- Hesablama qrafının və geri keçidin (backpropagation) necə işlədiyini izah etmək
requires_grad,.backward()və.gradilə qradiyentləri hesablamaq və zəncir qaydası ilə əl ilə yoxlamaq- Qradiyentləri sıfırlamaq və
torch.no_grad()-dan düzgün istifadə etmək - Autograd ilə qradiyent enişini sıfırdan yazmaq
Birinci dərsdə xətti reqressiyanın qradiyentlərini əl ilə çıxardıq — cəmi iki parametr üçün. Müasir neyron şəbəkələrdə isə milyonlarla, böyük dil modellərində milyardlarla parametr var; hər biri üçün törəməni kağızda çıxarmaq mümkün deyil. PyTorch-un autograd mexanizmi bu işi avtomatik görür: tenzorlarla etdiyin hər əməliyyatı yadda saxlayır, sonra isə bir əmrlə bütün parametrlərə görə dəqiq törəmələri hesablayır. Bu, nə təxmini ədədi fərqlər, nə də simvolik riyaziyyatdır — bu, avtomatik törəmədir (automatic differentiation).
Hesablama qrafı
requires_grad=True olan tenzorla hər əməliyyat istiqamətlənmiş dövrsüz qrafda (DAG) yeni düyün yaradır. Qrafın yarpaqları girişlər və parametrlərdir, kökü isə itkidir. İrəli keçid (forward pass) zamanı qraf qurulur və qiymətlər hesablanır; geri keçid (backward pass) zamanı autograd kökdən yarpaqlara doğru hərəkət edir və hər düyündə zəncir qaydasını tətbiq edir. Neyron şəbəkələrdə bu prosesə geriyə yayılma (backpropagation) deyilir.
| Düyün | İrəli keçid | Lokal törəmə | grad_fn |
|---|---|---|---|
| u | u = w · x | ∂u/∂w = x | MulBackward0 |
| ŷ | ŷ = u + b | ∂ŷ/∂u = 1, ∂ŷ/∂b = 1 | AddBackward0 |
| e | e = ŷ − y | ∂e/∂ŷ = 1 | SubBackward0 |
| L | L = e² | ∂L/∂e = 2e | PowBackward0 |
PyTorch-da qraf dinamikdir (define-by-run): hər irəli keçiddə yenidən qurulur. Ona görə modelin içində adi Python if və for operatorlarından sərbəst istifadə etmək olar — qraf həmin anda icra olunan yola uyğun gələcək.
| Üsul | Necə işləyir | Çatışmazlığı |
|---|---|---|
| Ədədi törəmə | (f(θ + h) − f(θ − h)) / 2h | təxminidir və hər parametr üçün iki hesablama tələb edir |
| Simvolik törəmə | düsturu cəbri qaydalarla çevirir (SymPy kimi) | böyük modellərdə ifadələr həddən artıq şişir, dövrləri və şərtləri çətin emal edir |
| Avtomatik törəmə (autograd) | icra olunan əməliyyatların lokal törəmələrini zəncir qaydası ilə birləşdirir | aralıq qiymətləri yadda saxlamaq üçün əlavə yaddaş lazımdır |
requires_grad, backward() və .grad
import torch
x = torch.tensor(3.0, requires_grad=True)
y = x ** 2 + 2 * x + 1
print(y)
y.backward()
print(x.grad)tensor(16., grad_fn=<AddBackward0>) tensor(8.)
requires_grad=TruePyTorch-a deyir: «bu tenzora görə törəmə lazım olacaq, əməliyyatları yadda saxla». Parametrlər (nn.Linear-in çəkiləri və s.) bunu avtomatik edir.grad_fntenzoru hansı əməliyyatın yaratdığını göstərir; bu, qrafdakı düyündür.y.backward()dy/dx-i hesablayır və nəticəni yarpağın.gradatributuna yazır: y = x² + 2x + 1 üçün dy/dx = 2x + 2 = 8 (x = 3).
Parametr vektor olanda .grad da eyni formada vektordur: hər element üçün xüsusi törəmə. Məsələn, f = ∑ vᵢ² funksiyası üçün ∂f/∂vᵢ = 2vᵢ:
import torch
v = torch.tensor([1.0, -2.0, 3.0], requires_grad=True)
f = (v ** 2).sum()
f.backward()
print(v.grad)tensor([ 2., -4., 6.])
requires_grad bayrağını sonradan da dəyişmək olar: p.requires_grad_(False) parametri «dondurur» — onun qradiyenti hesablanmır və o, öyrətmə zamanı dəyişmir. Bu üsuldan köçürmə öyrənməsində (transfer learning) istifadə olunur: əvvəlcədən öyrədilmiş şəbəkənin əksər qatları dondurulur, yalnız son qat yeni tapşırığa uyğunlaşdırılır. Bunu konvolyusiya şəbəkələri dərsində görəcəyik.
Zəncir qaydası
- Litki (qrafın kökü)
- y, ŷaralıq qiymətlər (qrafın daxili düyünləri)
- x, wyarpaqlar: girişlər və parametrlər
Mürəkkəb funksiyanın törəməsi lokal törəmələrin hasilinə bərabərdir. Bir dəyişən bir neçə yolla itkiyə təsir edirsə, bütün yollar üzrə hasillər toplanır: ∂L/∂x = ∑ₖ ∂L/∂yₖ · ∂yₖ/∂x.
ŷ = w · x + b, L = (ŷ − y)². w = 2, b = 1, x = 3, y = 10 üçün ∂L/∂w və ∂L/∂b-ni zəncir qaydası ilə tap.
Həllini göstərHəllini gizlət
∂L/∂ŷ = 2(ŷ − y) = −6.
∂ŷ/∂w = x = 3 ⇒ ∂L/∂w = −6 · 3 = −18.
∂ŷ/∂b = 1 ⇒ ∂L/∂b = −6.
Hər iki qradiyent mənfidir: w və b-ni artırmaq itkini azaldar — doğrudan da, ŷ = 7 hədəfdən (10) kiçikdir.
import torch
w = torch.tensor(2.0, requires_grad=True)
b = torch.tensor(1.0, requires_grad=True)
x, y = torch.tensor(3.0), torch.tensor(10.0)
y_hat = w * x + b
loss = (y_hat - y) ** 2
print(type(loss.grad_fn).__name__, type(y_hat.grad_fn).__name__)
loss.backward()
print(loss.item(), w.grad.item(), b.grad.item())PowBackward0 AddBackward0 9.0 -18.0 -6.0
x və y üçün requires_grad verilməyib — onlar verilənlərdir, parametr deyil.- σ(z)siqmoid funksiyası, qiymətləri (0; 1) aralığında
- σ′(z)onun törəməsi; ən böyük qiyməti z = 0-da 0,25-dir
İsbat: σ′(z) = e⁻ᶻ / (1 + e⁻ᶻ)² = σ(z) · e⁻ᶻ / (1 + e⁻ᶻ), sonuncu kəsr isə 1 − σ(z)-dir.
y = σ(w · x), w = 0, x = 2. dy/dw-ni tap.
Həllini göstərHəllini gizlət
dy/dw = σ′(z) · dz/dw = σ(z)(1 − σ(z)) · x = 0,5 · 0,5 · 2 = 0,5.
Nəticə: σ′ heç vaxt 0,25-dən böyük olmadığı üçün qradiyent hər siqmoid qatından keçəndə ən azı 4 dəfə kiçilir. 10 qatdan sonra bu, 4¹⁰ ≈ 10⁶ dəfə deməkdir — itən qradiyent problemi. Dərin şəbəkələrdə ReLU-nun üstünlük təşkil etməsinin səbəbi budur.
import torch
w = torch.tensor(0.0, requires_grad=True)
x = torch.tensor(2.0)
y = torch.sigmoid(w * x)
y.backward()
print(y.item(), w.grad.item())0.5 0.5
f = x · y + x², x = 2, y = 3. ∂f/∂x və ∂f/∂y-ni tap. Autograd burada nə edir?
Həllini göstərHəllini gizlət
Birinci yol: ∂u/∂x = y = 3. İkinci yol: ∂v/∂x = 2x = 4.
Yollar toplanır: ∂f/∂x = 3 + 4 = 7; ∂f/∂y = x = 2.
Autograd da eyni şeyi edir: x-ə gələn hər qradiyent axınını
.grad-da toplayır. Qradiyentlərin toplanması (akkumulyasiya) elə buradan gəlir.Geri keçidin gücü onun səmərəliliyindədir: bir backward() çağırışı itkinin bütün parametrlərə görə qradiyentini hesablayır və bunun dəyəri irəli keçidin dəyəri ilə eyni tərtibdədir (kiçik sabit əmsal qədər). Hər parametr üçün ayrıca hesablama aparsaydıq, milyon parametrli şəbəkə üçün milyon irəli keçid lazım olardı.
Qradiyentlər toplanır: sıfırlama və torch.no_grad()
.backward() köhnə qradiyenti əvəz etmir, ona əlavə edir. Aşağıda y = x³ funksiyasının x = 2-də törəməsi 3x² = 12-dir, amma üç çağırışdan sonra .grad 36 olur:
import torch
x = torch.tensor(2.0, requires_grad=True)
for i in range(3):
y = x ** 3
y.backward()
print(x.grad)
x.grad.zero_()
print(x.grad)
with torch.no_grad():
z = x * 2
print(z.requires_grad, x.detach().requires_grad)tensor(12.) tensor(24.) tensor(36.) tensor(0.) False False
x.grad.zero_()qradiyenti yerində sıfırlayır (alt xətt_yerində dəyişiklik deməkdir). Növbəti dərslərdə bunuoptimizer.zero_grad()edəcək.with torch.no_grad():blokunda qraf qurulmur: parametrləri yeniləyərkən və modeli qiymətləndirərkən (inference) işlədilir, yaddaşa və vaxta qənaət edir..detach()eyni verilənə baxan, lakin qrafdan ayrılmış tenzor qaytarır — məsələn, qiyməti çap etmək və ya NumPy-a çevirmək üçün.
Autograd ilə qradiyent enişi
İndi hər şeyi birləşdirək. y = 2x + 1 düzxəttindən küylə yaradılmış 20 nöqtəyə xətti model uyğunlaşdıraq. Bu dəfə törəmə düsturlarını yazmırıq — hər addımda autograd onları hesablayır. Dövrün beş addımı bütün dərin öyrənmənin əsasıdır: irəli keçid → itki → backward → no_grad daxilində yeniləmə → qradiyentləri sıfırlama.
import torch
torch.manual_seed(42)
X = torch.linspace(0, 1, 20)
Y = 2 * X + 1 + 0.1 * torch.randn(20)
w = torch.zeros(1, requires_grad=True)
b = torch.zeros(1, requires_grad=True)
lr = 0.5
for step in range(201):
loss = ((w * X + b - Y) ** 2).mean()
loss.backward()
with torch.no_grad():
w -= lr * w.grad
b -= lr * b.grad
w.grad.zero_()
b.grad.zero_()
if step % 50 == 0:
print(f'step {step:3d} loss {loss.item():.4f}')
print(f'w = {w.item():.3f}, b = {b.item():.3f}')step 0 loss 4.4302 step 50 loss 0.0085 step 100 loss 0.0084 step 150 loss 0.0084 step 200 loss 0.0084 w = 1.907, b = 1.068
Bu dövrü diqqətlə yadda saxla: növbəti dərslərdə onu qısaldacağıq, amma mahiyyəti dəyişməyəcək. torch.optim.SGD optimallaşdırıcısının step() metodu hər parametr üçün məhz p -= lr * p.grad əməliyyatını no_grad daxilində icra edir, zero_grad() isə qradiyentləri təmizləyir. nn.Linear kimi qatlar w və b-ni özləri yaradır və requires_grad-ı özləri qoşur. Yəni «sehrli» görünən yüksək səviyyəli kod bu beş sətrin rahat qablaşdırılmasıdır.
Əsas fikirlər
- Autograd tenzorlarla əməliyyatlardan hesablama qrafı qurur və
backward()ilə zəncir qaydasını kökdən yarpaqlara tətbiq edir. requires_grad=Trueolan yarpaqların qradiyentləri.grad-da saxlanılır;backward()yalnız skalyar üçün çağırılır.- Zəncir qaydası: dL/dx = dL/dy · dy/dx; siqmoid üçün σ′ = σ(1 − σ).
- Qradiyentlər toplanır — hər addımdan sonra sıfırla; yeniləmələr və qiymətləndirmə
torch.no_grad()daxilində aparılır. - Öyrətmə dövrü: irəli keçid → itki → backward → yeniləmə → sıfırlama.
Özünü yoxla
10 sual. Hər düzgün cavab XP qazandırır.
y.backward()-dan sonra x.grad nəyə bərabərdir?