Məzmuna keç
Educora
Universitet22 dəq39 / 42

PyTorch: neyron şəbəkələrin qurulması

Süni neyrondan çoxqatlı şəbəkəyə: z = w·x + b, aktivasiya funksiyaları (ReLU, siqmoid, tanh, softmax), nn.Linear qatları, nn.Module və nn.Sequential, irəli keçid və parametrlərin sayı.

Özünü yoxla
Bu dərsdə öyrənəcəksən
  • Neyronun və tam əlaqəli qatın riyazi modelini yazmaq
  • ReLU, siqmoid, tanh və softmax funksiyalarını hesablamaq və harada işlədildiyini bilmək
  • nn.Module və nn.Sequential ilə şəbəkə qurmaq və onun parametrlərini saymaq

Əl ilə yazılmış rəqəmi tanımaq istəyirik: şəkil 28 × 28 = 784 pikseldir, cavab isə 0-dan 9-a qədər on rəqəmdən biridir. Hansı funksiya 784 ədədi on ehtimala çevirə bilər? Neyron şəbəkə bu funksiyanı çoxlu sadə hissədən qurur: xətti çevirmələr və onların arasında qeyri-xətti «əyilmələr». Adı beyindən ilhamlanıb, amma əslində bu, parametrləri qradiyent enişi ilə seçilən riyazi funksiyadır — məhz əvvəlki dərslərdə gördüyümüz kimi.

Süni neyron

z = w · x + b = ∑ᵢ₌₁ⁿ wᵢxᵢ + b a = φ(z)
burada:
  • xgiriş vektoru (n əlamət)
  • wçəkilər — hər girişin əhəmiyyəti
  • bsürüşmə (bias) — aktivləşmə həddini sürüşdürür
  • φaktivasiya funksiyası (ReLU, siqmoid…)
  • aneyronun çıxışı (aktivasiya)

Neyron girişlərin çəkili cəmini hesablayır, ona sürüşməni əlavə edir və nəticəni aktivasiya funksiyasından keçirir. Aşağıdakı nümunədə z = 0,4 · 0,5 + 0,3 · (−1) + (−0,2) · 2 + 0,1 = −0,4 alınır: siqmoid bunu 0,4013 ehtimalına, ReLU isə 0-a çevirir.

Python
import torch

x = torch.tensor([0.5, -1.0, 2.0])
w = torch.tensor([0.4, 0.3, -0.2])
b = torch.tensor(0.1)
z = w @ x + b
print(round(z.item(), 4))
print(round(torch.sigmoid(z).item(), 4), torch.relu(z).item())
Gözlənilən nəticə
-0.4
0.4013 0.0

Aktivasiya funksiyaları

Aktivasiyalar olmasa, neçə qat qoysaq da şəbəkə xətti qalardı. İsbatı qısadır: iki qat ardıcıl tətbiq olunanda W₂(W₁x + b₁) + b₂ = (W₂W₁)x + (W₂b₁ + b₂) alınır — bu, W = W₂W₁ matrisi və b = W₂b₁ + b₂ sürüşməsi olan bir xətti qatdır. Qeyri-xətti φ funksiyası qatları «bükür» və şəbəkəyə mürəkkəb, əyri sərhədləri təsvir etməyə imkan verir.

ReLU(z) = max(0, z) σ(z) = 1 / (1 + e⁻ᶻ) tanh(z) = (eᶻ − e⁻ᶻ) / (eᶻ + e⁻ᶻ)ReLU(z) = max(0, z) σ(z) = 1 / (1 + e⁻ᶻ) tanh(z) = (eᶻ − e⁻ᶻ) / (eᶻ + e⁻ᶻ)
burada:
  • ReLUmənfi qiymətləri sıfırlayır; törəməsi z > 0 üçün 1, z < 0 üçün 0
  • σsiqmoid: qiymətlər (0; 1) aralığında, ehtimal kimi şərh olunur
  • tanhhiperbolik tangens: qiymətlər (−1; 1) aralığında, sıfıra görə simmetrik
softmax(z)ᵢ = eᶻⁱ / ∑ⱼ₌₁ᴷ eᶻʲsoftmax(z)ᵢ = eᶻⁱ / ∑ⱼ₌₁ᴷ eᶻʲ
burada:
  • zK sinif üçün xam ballar (logitlər)
  • softmax(z)ᵢi sinfinin ehtimalı; hamısı müsbətdir və cəmi 1-dir

Softmax çoxsinifli təsnifatda son qatın logitlərini ehtimallar paylanmasına çevirir.

FunksiyaQiymətlər çoxluğuHarada işlədilir
ReLU[0; ∞)gizli qatlarda standart seçim: sadə, sürətli, qradiyent itmir
Siqmoid(0; 1)ikili təsnifatda çıxış, «qapılar» (LSTM)
tanh(−1; 1)rekurrent şəbəkələr, sıfır ətrafında mərkəzləşmiş çıxış lazım olanda
softmax(0; 1), cəmi 1çoxsinifli təsnifatda son qat

ReLU-nun bir zəif cəhəti var: neyronun girişi bütün nümunələr üçün mənfi olarsa, onun qradiyenti həmişə sıfırdır və neyron bir daha öyrənmir («ölü ReLU»). Buna qarşı LeakyReLU mənfi tərəfdə kiçik meyl saxlayır (məsələn, 0,01z). Transformerlərdə isə ReLU-nun hamar variantı olan GELU geniş işlədilir. PyTorch-da hamısı hazırdır: nn.LeakyReLU(), nn.GELU(), nn.Tanh(), nn.Sigmoid().

Python
import torch

z = torch.tensor([-2.0, 0.0, 1.0, 3.0])
print(torch.relu(z))
print(torch.sigmoid(z).round(decimals=4))
print(torch.tanh(z).round(decimals=4))
p = torch.softmax(z, dim=0)
print(p.round(decimals=4), round(p.sum().item(), 4))
Gözlənilən nəticə
tensor([0., 0., 1., 3.])
tensor([0.1192, 0.5000, 0.7311, 0.9526])
tensor([-0.9640,  0.0000,  0.7616,  0.9951])
tensor([0.0057, 0.0418, 0.1135, 0.8390]) 1.0
Nümunə 1: softmax-ı əl ilə hesablayaq

Şəbəkə üç sinif üçün z = (2; 1; 0) logitlərini verib. Ehtimalları tap.

Həllini göstər
e² ≈ 7,389; e¹ ≈ 2,718; e⁰ = 1. Cəm ≈ 11,107.
p ≈ (7,389 / 11,107; 2,718 / 11,107; 1 / 11,107) ≈ (0,665; 0,245; 0,090).
Logitlər arasındakı fərq cəmi 1 olsa da, ehtimallar təxminən e ≈ 2,72 dəfə fərqlənir: softmax ən böyük balı «gücləndirir».
Python
import torch

print(torch.softmax(torch.tensor([2.0, 1.0, 0.0]), dim=0).round(decimals=3))
Gözlənilən nəticə
tensor([0.6650, 0.2450, 0.0900])

Qatlar: nn.Linear

Bir neçə neyron eyni girişə baxanda onların çəki vektorları bir W matrisinin sətirləri olur və qatın bütün çıxışları bir matris hasili ilə hesablanır. Paket şəklində gələn girişlər (N, nin) formasındadır, çıxışlar isə (N, nout).

Y = X · Wᵀ + b params = (nin + 1) · nout
burada:
  • Xgirişlər paketi, forma (N, nin)
  • Wçəkilər, forma (nout, nin) — layer.weight
  • bsürüşmələr, forma (nout,) — layer.bias
  • Yçıxışlar, forma (N, nout)

Hər çıxış neyronunun nin çəkisi və bir sürüşməsi var, buna görə tam əlaqəli qatda (nin + 1) · nout parametr olur.

Python
import torch
from torch import nn

torch.manual_seed(42)
layer = nn.Linear(in_features=3, out_features=2)
print(layer.weight.shape, layer.bias.shape)
x = torch.randn(4, 3)
print(layer(x).shape)
print(sum(p.numel() for p in layer.parameters()))
Gözlənilən nəticə
torch.Size([2, 3]) torch.Size([2])
torch.Size([4, 2])
8

Çəkilərə başlanğıcda təsadüfi kiçik qiymətlər verilir: nn.Linear onları susmaya görə (−1/√nin; 1/√nin) aralığından bərabər paylanma ilə seçir. Təsadüfilik vacibdir — bütün çəkilər eyni olsaydı, qatdakı bütün neyronlar eyni qradiyenti alar və eyni şeyi öyrənərdi (simmetriya problemi).

nn.Module və nn.Sequential ilə şəbəkə

PyTorch-da hər model nn.Module-un varisidir. __init__ metodunda qatlar yaradılır — onların parametrləri avtomatik qeydə alınır; forward metodu isə girişdən çıxışa hesablamanı təsvir edir. Modeli model(x) kimi çağırırıq: PyTorch özü forward-u işə salır.

Python
import torch
from torch import nn

class MLP(nn.Module):
    def __init__(self, n_in, n_hidden, n_out):
        super().__init__()
        self.fc1 = nn.Linear(n_in, n_hidden)
        self.fc2 = nn.Linear(n_hidden, n_out)

    def forward(self, x):
        h = torch.relu(self.fc1(x))
        return self.fc2(h)

torch.manual_seed(42)
model = MLP(784, 128, 10)
print(model)
x = torch.randn(32, 784)
print(model(x).shape)
print(sum(p.numel() for p in model.parameters()))
Gözlənilən nəticə
MLP(
  (fc1): Linear(in_features=784, out_features=128, bias=True)
  (fc2): Linear(in_features=128, out_features=10, bias=True)
)
torch.Size([32, 10])
101770
32 şəkildən ibarət paket (32, 784) → gizli qat (32, 128) → logitlər (32, 10). Parametrlər: 785 · 128 + 129 · 10 = 100 480 + 1290 = 101 770.
  • model.parameters() — bütün öyrədilən parametrlər; onları optimallaşdırıcıya veririk.
  • model.to(device) — bütün parametrləri bir əmrlə CPU-dan GPU-ya (və ya əksinə) köçürür.
  • model.train() və model.eval() — öyrətmə və qiymətləndirmə rejimləri (Dropout və BatchNorm qatları üçün vacibdir).
  • model.state_dict() — parametrlərin lüğəti; modeli fayla yazmaq üçün növbəti dərsdə işlədəcəyik.
Tərif
Çoxqatlı perseptron (MLP) və logitlər

Tam əlaqəli qatlardan və onların arasındakı aktivasiyalardan ibarət şəbəkəyə çoxqatlı perseptron deyilir. Onun son qatının normallaşdırılmamış çıxışları — logitlər — istənilən həqiqi ədəd ola bilər. Proqnoz üçün ən böyük logitin indeksi (logits.argmax(dim=1)), ehtimallar üçün isə torch.softmax(logits, dim=1) götürülür.

Arxitekturanı necə seçmək olar? Cədvəl verilənləri üçün 1–3 gizli qat və 32–256 neyron adətən kifayətdir; kiçik modeldən başla, validasiya itkisi imkan verdikcə böyüt. Şəkillər üçün isə tam əlaqəli qatlar çox bahalıdır — 224 × 224 rəngli şəkildən 1000 neyronlu qata keçid artıq 150 milyondan çox parametr tələb edir. Bu problemi konvolyusiya şəbəkələri həll edir.

Qatlar sadəcə ardıcıl düzülürsə, sinif yazmağa ehtiyac yoxdur: nn.Sequential onları sıra ilə tətbiq edir. Aktivasiya burada nn.ReLU() qatı kimi yazılır. named_parameters() hər parametrin adını və formasını göstərir — sazlama üçün çox faydalıdır:

Python
import torch
from torch import nn

model = nn.Sequential(
    nn.Linear(784, 128), nn.ReLU(),
    nn.Linear(128, 64), nn.ReLU(),
    nn.Linear(64, 10),
)
for name, p in model.named_parameters():
    print(name, tuple(p.shape))
print(sum(p.numel() for p in model.parameters()))
Gözlənilən nəticə
0.weight (128, 784)
0.bias (128,)
2.weight (64, 128)
2.bias (64,)
4.weight (10, 64)
4.bias (10,)
109386
Adlardakı 0, 2, 4 qatın Sequential daxilindəki nömrəsidir; 1 və 3 nömrəli ReLU qatlarının parametri yoxdur.
Nümunə 2: parametrləri əl ilə say

784 → 128 → 64 → 10 şəbəkəsində neçə parametr var? Onlar float32-də nə qədər yaddaş tutur?

Həllini göstər
1-ci qat: (784 + 1) · 128 = 100 480.
2-ci qat: (128 + 1) · 64 = 8256.
3-cü qat: (64 + 1) · 10 = 650.
Cəmi: 109 386 — kodun nəticəsi ilə eynidir.
Yaddaş: 109 386 · 4 bayt ≈ 0,44 MB. Parametrlərin 92%-i birinci qatdadır: böyük girişlər bahalıdır.
Nümunə 3: irəli keçid əl ilə

2 → 2 → 1 şəbəkəsi: W₁ = [[1; −1]; [0,5; 0,5]], b₁ = (0; −1), gizli qatda ReLU, W₂ = [[2; −3]], b₂ = 1. x = (1; 2) üçün çıxışı tap.

Həllini göstər
W₁x + b₁ = (1 − 2 + 0; 0,5 + 1 − 1) = (−1; 0,5).
ReLU: h = (0; 0,5) — birinci neyron «söndü».
y = 2 · 0 + (−3) · 0,5 + 1 = −0,5.
Diqqət et: bu x üçün birinci neyron çıxışa heç bir təsir etmir, ona görə geri keçiddə onun çəkilərinin qradiyenti də sıfır olacaq — ReLU qradiyenti yalnız aktiv neyronlardan keçirir.
Python
import torch

x = torch.tensor([1.0, 2.0])
W1 = torch.tensor([[1.0, -1.0], [0.5, 0.5]])
b1 = torch.tensor([0.0, -1.0])
W2 = torch.tensor([[2.0, -3.0]])
b2 = torch.tensor([1.0])
h = torch.relu(W1 @ x + b1)
y = W2 @ h + b2
print(h, y)
Gözlənilən nəticə
tensor([0.0000, 0.5000]) tensor([-0.5000])

Əsas fikirlər

  • Neyron: z = w · x + b, sonra aktivasiya a = φ(z); qat bunu matris şəklində edir: Y = X · Wᵀ + b.
  • Aktivasiyasız çoxqatlı şəbəkə bir xətti qata bərabərdir; gizli qatlarda adətən ReLU işlədilir.
  • Softmax logitləri cəmi 1 olan ehtimallara çevirir; nn.CrossEntropyLoss-a isə xam logitlər verilir.
  • Model nn.Module-dan törəyir: qatlar __init__-də, hesablama forward-da; sadə zəncir üçün nn.Sequential kifayətdir.
  • Tam əlaqəli qatda (nin + 1) · nout parametr var; nn.Linear(n_in, n_out).weight-in forması (nout, nin)-dir.

Özünü yoxla

10 sual. Hər düzgün cavab XP qazandırır.

1 / 10
nn.Linear(10, 5) qatında neçə parametr var?