Mundarija (21)
- 1. Kirish va motivatsiya
- 2. Nazariya — chuqur tushuntirish
- 2.1. Tensor
- 2.2. autograd
- 2.3. Hisob grafi
- 2.4. no_grad va detach
- 2.5. Tensor amallari
- 2.6. Qo'lda va avtomatik
- 2.7. Tuzoqlar
- 3. Tez ma'lumotnoma
- 4. Batafsil misollar
- Misol 1 — Tensor asoslari
- Misol 2 — autograd qanday ishlaydi
- Misol 3 — Qo'lda yozilgan backprop va autograd
- Misol 4 — torch bilan to'liq o'rgatish
- 5. To'g'ri va noto'g'ri tushunishlar
- 6. Keng tarqalgan xatolar va yechimlari
- 7. Integratsiya — bu bilim qayerda kerak bo'ladi
- 8. Eng yaxshi amaliyotlar
- 9. Amaliy topshiriq
- Xulosa
20.9-dars: torch tensorlari va autograd
20-QISM — NEYRON TARMOQLAR · 9-dars
1. Kirish va motivatsiya
Shu paytgacha hammasini numpy bilan qildik: oldinga o'tish, loss, orqaga tarqalish, optimizator. Bu kerak edi — endi tarmoq ichida nima bo'layotganini bilamiz.
Lekin amaliyotda hech kim gradientni qo'lda yozmaydi. Buning ikki sababi bor. Birinchisi — xatolik: har yangi qatlam uchun to'rtta formulani qo'lda chiqarish va tekshirish soatlab vaqt oladi. Ikkinchisi — GPU: zamonaviy tarmoqlar numpy da amalda o'rgatilmaydi.
PyTorch ikkala muammoni ham yechadi. Uning asosi — tensor: numpy massiviga juda o'xshash, lekin ikki qo'shimcha qobiliyati bor — GPU da yashay oladi va o'zining tarixini eslaydi. Shu tarix orqali autograd gradientni avtomatik hisoblaydi.
Bu darsda tensorlar bilan tanishamiz va eng muhimi — o'zimiz yozgan backprop ni autograd bilan raqamma-raqam solishtiramiz. Bu solishtirish ikki narsani ko'rsatadi: bizning kodimiz to'g'ri edi, va autograd sehr emas — u aynan o'sha zanjir qoidasini qo'llaydi.
Real vaziyat. Muhandis numpy da tarmoq yozdi va yangi qatlam qo'shmoqchi bo'ldi. Gradientni chiqarishga ikki kun ketdi. torch ga ko'chirgandan keyin yangi qatlam qo'shish besh daqiqa oladigan ishga aylandi.
Bu darsda numpy dan torch ga o'tamiz.
Bu darsda:
- Tensor va numpy farqi
- autograd qanday ishlaydi
- Hisob grafi
- no_grad va detach
- Qo'lda yozilgan backprop bilan solishtirish
- Tuzoqlar
- Amaliy: torch bilan tarmoq
ℹ Misollar real torch/numpy bilan (Python 3.14, torch 2.14 CPU).
2. Nazariya — chuqur tushuntirish
2.1. Tensor
TENSOR = numpy massivi + ikki qo'shimcha xossa
1. QURILMA (device): cpu yoki cuda
2. GRADIENT KUZATUVI: requires_grad=True bo'lsa tarix yoziladi
YARATISH:
torch.tensor([1.0, 2.0]) ro'yxatdan
torch.zeros(3, 4) / torch.ones to'ldirilgan
torch.randn(3, 4) N(0,1)
torch.from_numpy(massiv) XOTIRANI BAHAM KO'RADI
torch.arange, torch.linspace
TURLAR: float32 (SUKUT), float64, int64, bool
DIQQAT: numpy sukut float64, torch sukut float32
numpy GA QAYTISH:
t.numpy() xotira baham, grad bo'lsa XATO
t.detach().numpy() to'g'ri usul torch sukut float32, numpy esa float64 — bu farq aniqlik taqqoslashda darhol seziladi.
2.2. autograd
requires_grad=True BO'LGAN TENSOR:
unga qo'llangan HAR amal grafga yoziladi
har natijada grad_fn saqlanadi
y.backward() CHAQIRILGANDA:
graf bo'ylab ORQAGA yuriladi
har barg tensorning .grad maydoniga gradient QO'SHILADI
MISOL:
x = torch.tensor([2.0], requires_grad=True)
y = x ** 3
y.backward()
x.grad -> 3*x^2 = 12
MUHIM 1: .grad YIG'ILADI, almashmaydi
-> har qadamdan oldin zero_grad() yoki grad = None
MUHIM 2: backward() grafni O'CHIRADI
-> ikki marta chaqirish uchun retain_graph=True
MUHIM 3: backward() faqat SKALYARDAN
-> vektor uchun gradient argumenti kerak .grad yig'iladi — bu torch dagi eng ko'p uchraydigan xato manbai.
2.3. Hisob grafi
z = (x * y + 3).sum()
x y
\ /
\ /
mul <- MulBackward
|
+3 <- AddBackward
|
sum <- SumBackward
|
z
DINAMIK GRAF: har oldinga o'tishda QAYTA quriladi
-> if/for/while bemalol ishlatiladi (TensorFlow 1.x da bo'lmagan)
-> debug oson: oddiy Python
BARG (leaf) TENSOR: foydalanuvchi yaratgan, grad_fn = None
faqat barglarda .grad saqlanadi
oraliq tensorda kerak bo'lsa: t.retain_grad() Graf dinamik: har forward da qayta quriladi, shuning uchun oddiy Python boshqaruvi ishlaydi.
2.4. no_grad va detach
torch.no_grad():
blok ichida graf QURILMAYDI
-> xotira tejaladi, tezroq
-> INFERENCE va parametr yangilashda SHART
t.detach():
grafdan UZILGAN nusxa (xotira baham)
-> gradient shu nuqtada to'xtaydi
QAYERDA KERAK:
bashorat: with torch.no_grad(): model(X)
metrika: loss.item() yoki loss.detach()
qo'lda yangilash: with torch.no_grad(): w -= lr * w.grad
numpy ga o'tish: t.detach().numpy()
torch.inference_mode(): no_grad dan ham tezroq (yangi API) no_grad — inference da majburiy; usiz xotira behuda sarflanadi.
2.5. Tensor amallari
numpy DAN FARQLAR:
massiv.reshape -> t.reshape yoki t.view
massiv.T -> t.T (2D), t.transpose(i,j), t.permute
np.concatenate -> torch.cat
np.stack -> torch.stack
massiv @ massiv -> bir xil (@ yoki torch.matmul)
axis= -> dim=
massiv.astype -> t.to(torch.float64) yoki t.float()
view VA reshape:
view - xotira uzluksiz bo'lishi SHART, nusxa olmaydi
reshape - kerak bo'lsa nusxa oladi (xavfsizroq)
BROADCASTING: numpy bilan bir xil qoidalar
JOYIDA (in-place) AMALLAR: t.add_(1), t.zero_()
DIQQAT: autograd ni buzishi mumkin dim= va axis= — ko'chirishda eng ko'p uchraydigan sintaksis farqi.
2.6. Qo'lda va avtomatik
BIZNING KOD (20.6-dars):
dZ = (P - Y) / N
dW = A.T @ dZ
db = dZ.sum(0)
dA = dZ @ W.T
TORCH:
loss.backward()
NATIJA: AYNAN BIR XIL sonlar (float aniqligi doirasida)
autograd SEHR EMAS:
u aynan o'sha zanjir qoidasini qo'llaydi
farq - uni SIZ emas, kutubxona yozgan
SHUNING UCHUN 20.6-dars kerak edi:
autograd xato bergan joyni faqat MEXANIZMNI bilgan odam topadi autograd — avtomatlashtirilgan 20.6-dars, boshqa hech narsa emas.
2.7. Tuzoqlar
Asosiy tuzoqlar: zero_grad() ni unutish; parametr yangilashni no_grad siz qilish; float64 va float32 ni aralashtirish; t.numpy() ni detach siz chaqirish; backward() ni ikki marta chaqirish; vektordan backward() qilish; view ni uzluksiz bo'lmagan tensorga qo'llash; item() o'rniga tensorni ro'yxatga yig'ish (xotira sizishi).
3. Tez ma'lumotnoma
import torch
x = torch.tensor([2.0, 3.0], requires_grad=True)
y = (x ** 2).sum()
y.backward()
print(x.grad) # 2*x = [4., 6.]
x.grad = None # yoki opt.zero_grad()
# qo'lda yangilash
with torch.no_grad():
w -= lr * w.grad
w.grad = None
# numpy bilan
t = torch.from_numpy(massiv) # xotira baham
m = t.detach().numpy()
# inference
with torch.no_grad():
bashorat = model(X)
torch.manual_seed(0) # takrorlanuvchanlikTensor xulosasi
tensor = massiv + device + grad tarixi
requires_grad -> graf yoziladi
backward() -> barglarning .grad iga QO'SHADI
no_grad / detach -> grafdan chiqish
sukut dtype: torch float32, numpy float644. Batafsil misollar
Misollar real torch/numpy bilan (Python 3.14, torch 2.14 CPU).
Misol 1 — Tensor asoslari
"""Tensor va numpy massivi: o'xshashlik va farqlar (real torch)."""
import numpy as np
import torch
def main() -> None:
torch.manual_seed(0)
print("=== 1. Yaratish usullari ===")
tensorlar = {
"tensor([1.,2.,3.])": torch.tensor([1.0, 2.0, 3.0]),
"zeros(2,3)": torch.zeros(2, 3),
"randn(2,3)": torch.randn(2, 3),
"arange(5)": torch.arange(5),
"linspace(0,1,5)": torch.linspace(0, 1, 5),
}
print(f" {'ifoda':<22} {'shakl':>10} {'dtype':>16}")
for nom, t in tensorlar.items():
print(f" {nom:<22} {str(tuple(t.shape)):>10} {str(t.dtype):>16}")
print("\n=== 2. Sukut turlari farqi ===")
m = np.array([1.0, 2.0])
t = torch.tensor([1.0, 2.0])
print(f" numpy sukut: {m.dtype}")
print(f" torch sukut: {t.dtype}")
print(f" from_numpy dan keyin: {torch.from_numpy(m).dtype}")
print(" float64 va float32 aralashsa - xato yoki aniqlik yo'qoladi")
print("\n=== 3. Xotirani baham ko'rish ===")
m = np.array([1.0, 2.0, 3.0])
t = torch.from_numpy(m)
m[0] = 99.0
print(f" numpy ni o'zgartirdik: {m}")
print(f" tensor ham o'zgardi: {t.numpy()}")
t2 = torch.tensor(m) # NUSXA oladi
m[1] = 88.0
print(f" torch.tensor() nusxa oladi: {t2.numpy()}")
print("\n=== 4. Amallar numpy bilan bir xil ===")
a_np = np.arange(6, dtype=np.float32).reshape(2, 3)
a_t = torch.arange(6, dtype=torch.float32).reshape(2, 3)
amallar = [
("sum", a_np.sum(), a_t.sum().item()),
("mean", a_np.mean(), a_t.mean().item()),
("max", a_np.max(), a_t.max().item()),
("std (ddof=1)", a_np.std(ddof=1), a_t.std().item()),
("sum(axis=0)[0]", a_np.sum(axis=0)[0], a_t.sum(dim=0)[0].item()),
]
print(f" {'amal':<16} {'numpy':>12} {'torch':>12} {'farq':>10}")
for nom, v1, v2 in amallar:
print(f" {nom:<16} {v1:>12.6f} {v2:>12.6f} "
f"{abs(v1 - v2):>10.2e}")
print("\n=== 5. Shakl o'zgartirish ===")
t = torch.arange(12).reshape(3, 4)
print(f" asl: {tuple(t.shape)}")
print(f" {'amal':<24} {'natija shakli':>16}")
for nom, natija in [
("reshape(4,3)", t.reshape(4, 3)),
("view(2,6)", t.view(2, 6)),
("T", t.T),
("unsqueeze(0)", t.unsqueeze(0)),
("flatten()", t.flatten()),
("permute(1,0)", t.permute(1, 0))]:
print(f" {nom:<24} {str(tuple(natija.shape)):>16}")
print("\n=== 6. view va reshape farqi ===")
t = torch.arange(12).reshape(3, 4)
tr = t.T
print(f" t uzluksizmi: {t.is_contiguous()}")
print(f" t.T uzluksizmi: {tr.is_contiguous()}")
try:
tr.view(12)
print(" t.T.view(12): ishladi")
except RuntimeError as xato:
print(f" t.T.view(12): RuntimeError - {str(xato)[:44]}")
print(f" t.T.reshape(12): ishladi, shakl "
f"{tuple(tr.reshape(12).shape)}")
print(" ⭐ Shubha bo'lsa reshape ishlating")
if __name__ == "__main__":
main()Natijaning muhim qismi:
=== 1. Yaratish usullari ===
ifoda shakl dtype
tensor([1.,2.,3.]) (3,) torch.float32
zeros(2,3) (2, 3) torch.float32
randn(2,3) (2, 3) torch.float32
arange(5) (5,) torch.int64
linspace(0,1,5) (5,) torch.float32
=== 2. Sukut turlari farqi ===
numpy sukut: float64
torch sukut: torch.float32
from_numpy dan keyin: torch.float64
float64 va float32 aralashsa - xato yoki aniqlik yo'qoladi
=== 3. Xotirani baham ko'rish ===
numpy ni o'zgartirdik: [99. 2. 3.]
tensor ham o'zgardi: [99. 2. 3.]
torch.tensor() nusxa oladi: [99. 2. 3.]
=== 4. Amallar numpy bilan bir xil ===
amal numpy torch farq
sum 15.000000 15.000000 0.00e+00
mean 2.500000 2.500000 0.00e+00
max 5.000000 5.000000 0.00e+00
std (ddof=1) 1.870829 1.870829 0.00e+00
sum(axis=0)[0] 3.000000 3.000000 0.00e+00
=== 5. Shakl o'zgartirish ===
asl: (3, 4)
amal natija shakli
reshape(4,3) (4, 3)
view(2,6) (2, 6)
T (4, 3)
unsqueeze(0) (1, 3, 4)
flatten() (12,)
permute(1,0) (4, 3)
=== 6. view va reshape farqi ===
t uzluksizmi: True
t.T uzluksizmi: False
t.T.view(12): RuntimeError - view size is not compatible with input tenso
t.T.reshape(12): ishladi, shakl (12,)
⭐ Shubha bo'lsa reshape ishlatingNima ko'rsatdi: 2.1, 2.5-bo'limlar.
Misol 2 — autograd qanday ishlaydi
"""Hisob grafi, backward va .grad (real torch)."""
import warnings
import torch
def main() -> None:
print("=== 1. Eng oddiy holat ===")
x = torch.tensor([2.0], requires_grad=True)
y = x ** 3
y.backward()
print(f" y = x^3, x = {x.item()}")
print(f" dy/dx = 3x^2 = {3 * 2.0 ** 2}")
print(f" x.grad = {x.grad.item()}")
print("\n=== 2. Graf tuzilishi ===")
a = torch.tensor([1.0, 2.0], requires_grad=True)
b = torch.tensor([3.0, 4.0], requires_grad=True)
c = a * b
d = c + 3
e = d.sum()
print(f" {'tensor':<8} {'grad_fn':<22} {'barg (leaf)':>12}")
for nom, t in [("a", a), ("b", b), ("c", c), ("d", d), ("e", e)]:
fn = type(t.grad_fn).__name__ if t.grad_fn else "None"
print(f" {nom:<8} {fn:<22} {str(t.is_leaf):>12}")
print("\n=== 3. Gradientlar ===")
e.backward()
print(f" e = sum(a*b + 3)")
print(f" de/da = b = {b.detach().tolist()}, "
f"a.grad = {a.grad.tolist()}")
print(f" de/db = a = {a.detach().tolist()}, "
f"b.grad = {b.grad.tolist()}")
with warnings.catch_warnings(record=True) as ogohlar:
warnings.simplefilter("always")
c_grad = c.grad
print(f" c.grad (barg emas): {c_grad}")
print(f" torch ogohlantirdi: {ogohlar[0].category.__name__}"
if ogohlar else " ogohlantirish yo'q")
print(" oraliq tensorda grad kerak bo'lsa: c.retain_grad()")
print("\n=== 4. .grad YIG'ILADI ===")
w = torch.tensor([1.0], requires_grad=True)
print(f" {'chaqiruv':>9} {'w.grad':>10}")
for i in range(1, 4):
(w * 2).sum().backward()
print(f" {i:>9} {w.grad.item():>10.1f}")
print(" har backward() gradientni QO'SHADI")
w.grad = None
(w * 2).sum().backward()
print(f" grad = None dan keyin: {w.grad.item()}")
print("\n=== 5. Vektordan backward ===")
v = torch.tensor([1.0, 2.0, 3.0], requires_grad=True)
u = v ** 2
try:
u.backward()
print(" ishladi (kutilmagan)")
except RuntimeError as xato:
print(f" RuntimeError: {str(xato)[:52]}")
u.backward(torch.ones_like(u))
print(f" backward(ones) bilan: {v.grad.tolist()} (= 2*v)")
print("\n=== 6. no_grad va detach ===")
p = torch.tensor([1.0, 2.0], requires_grad=True)
q1 = p * 2
with torch.no_grad():
q2 = p * 2
q3 = p.detach() * 2
print(f" {'holat':<22} {'requires_grad':>15} {'grad_fn':>14}")
for nom, t in [("oddiy", q1), ("no_grad ichida", q2),
("detach() dan keyin", q3)]:
fn = type(t.grad_fn).__name__ if t.grad_fn else "None"
print(f" {nom:<22} {str(t.requires_grad):>15} {fn:>14}")
print("\n=== 7. Dinamik graf ===")
def f(x, n):
natija = x
for _ in range(n): # oddiy Python sikli
natija = natija * x
return natija
print(f" {'n':>3} {'f(2,n)':>10} {'df/dx':>10} {'kutilgan':>10}")
for n in [1, 2, 3, 4]:
xx = torch.tensor([2.0], requires_grad=True)
f(xx, n).backward()
kutilgan = (n + 1) * 2.0 ** n
print(f" {n:>3} {2.0 ** (n + 1):>10.1f} {xx.grad.item():>10.1f} "
f"{kutilgan:>10.1f}")
print(" ⭐ Graf har chaqiruvda QAYTA quriladi - Python bemalol")
if __name__ == "__main__":
main()Natijaning muhim qismi:
=== 1. Eng oddiy holat ===
y = x^3, x = 2.0
dy/dx = 3x^2 = 12.0
x.grad = 12.0
=== 2. Graf tuzilishi ===
tensor grad_fn barg (leaf)
a None True
b None True
c MulBackward0 False
d AddBackward0 False
e SumBackward0 False
=== 3. Gradientlar ===
e = sum(a*b + 3)
de/da = b = [3.0, 4.0], a.grad = [3.0, 4.0]
de/db = a = [1.0, 2.0], b.grad = [1.0, 2.0]
c.grad (barg emas): None
torch ogohlantirdi: UserWarning
oraliq tensorda grad kerak bo'lsa: c.retain_grad()
=== 4. .grad YIG'ILADI ===
chaqiruv w.grad
1 2.0
2 4.0
3 6.0
har backward() gradientni QO'SHADI
grad = None dan keyin: 2.0
=== 5. Vektordan backward ===
RuntimeError: grad can be implicitly created only for scalar outpu
backward(ones) bilan: [2.0, 4.0, 6.0] (= 2*v)
=== 6. no_grad va detach ===
holat requires_grad grad_fn
oddiy True MulBackward0
no_grad ichida False None
detach() dan keyin False None
=== 7. Dinamik graf ===
n f(2,n) df/dx kutilgan
1 4.0 4.0 4.0
2 8.0 12.0 12.0
3 16.0 32.0 32.0
4 32.0 80.0 80.0
⭐ Graf har chaqiruvda QAYTA quriladi - Python bemalolNima ko'rsatdi: 2.2, 2.3, 2.4-bo'limlar.
Misol 3 — Qo'lda yozilgan backprop va autograd
"""20.6-darsdagi formulalar autograd bilan bir xilmi (real torch)."""
import numpy as np
import torch
def softmax_np(Z):
Z = Z - Z.max(axis=1, keepdims=True)
E = np.exp(Z)
return E / E.sum(axis=1, keepdims=True)
def main() -> None:
rng = np.random.default_rng(0)
N, d0, d1, K = 16, 8, 12, 4
X_np = rng.normal(0, 1, (N, d0))
y_np = rng.integers(0, K, N)
W1_np = rng.normal(0, np.sqrt(2 / d0), (d0, d1))
b1_np = np.zeros(d1)
W2_np = rng.normal(0, np.sqrt(2 / d1), (d1, K))
b2_np = np.zeros(K)
print("=== 1. numpy: qo'lda backprop ===")
Z1 = X_np @ W1_np + b1_np
A1 = np.maximum(0, Z1)
Z2 = A1 @ W2_np + b2_np
P = softmax_np(Z2)
loss_np = -np.mean(np.log(P[np.arange(N), y_np]))
Y = np.zeros((N, K))
Y[np.arange(N), y_np] = 1.0
dZ2 = (P - Y) / N
dW2_np = A1.T @ dZ2
db2_np = dZ2.sum(axis=0)
dZ1 = (dZ2 @ W2_np.T) * (Z1 > 0)
dW1_np = X_np.T @ dZ1
db1_np = dZ1.sum(axis=0)
print(f" loss: {loss_np:.10f}")
print(f" {'gradient':<8} {'shakl':>10} {'norma':>14}")
for nom, g in [("dW1", dW1_np), ("db1", db1_np),
("dW2", dW2_np), ("db2", db2_np)]:
print(f" {nom:<8} {str(g.shape):>10} "
f"{np.linalg.norm(g):>14.10f}")
print("\n=== 2. torch: autograd ===")
X = torch.tensor(X_np, dtype=torch.float64)
y = torch.tensor(y_np, dtype=torch.int64)
W1 = torch.tensor(W1_np, dtype=torch.float64, requires_grad=True)
b1 = torch.tensor(b1_np, dtype=torch.float64, requires_grad=True)
W2 = torch.tensor(W2_np, dtype=torch.float64, requires_grad=True)
b2 = torch.tensor(b2_np, dtype=torch.float64, requires_grad=True)
z1 = X @ W1 + b1
a1 = torch.relu(z1)
z2 = a1 @ W2 + b2
loss = torch.nn.functional.cross_entropy(z2, y)
loss.backward()
print(f" loss: {loss.item():.10f}")
print(f" {'gradient':<8} {'shakl':>10} {'norma':>14}")
for nom, t in [("dW1", W1.grad), ("db1", b1.grad),
("dW2", W2.grad), ("db2", b2.grad)]:
print(f" {nom:<8} {str(tuple(t.shape)):>10} "
f"{t.norm().item():>14.10f}")
print("\n=== 3. Taqqoslash ===")
juftlar = [("loss", np.array(loss_np), np.array(loss.item())),
("dW1", dW1_np, W1.grad.numpy()),
("db1", db1_np, b1.grad.numpy()),
("dW2", dW2_np, W2.grad.numpy()),
("db2", db2_np, b2.grad.numpy())]
print(f" {'nom':<8} {'maksimal farq':>16} {'bir xilmi':>11}")
for nom, a, b in juftlar:
farq = float(np.abs(a - b).max())
print(f" {nom:<8} {farq:>16.3e} "
f"{str(bool(np.allclose(a, b, atol=1e-12))):>11}")
print(" ⭐ Qo'lda yozganimiz autograd bilan AYNAN bir xil")
print("\n=== 4. float32 da aniqlik ===")
W1f = torch.tensor(W1_np, dtype=torch.float32, requires_grad=True)
b1f = torch.tensor(b1_np, dtype=torch.float32, requires_grad=True)
W2f = torch.tensor(W2_np, dtype=torch.float32, requires_grad=True)
b2f = torch.tensor(b2_np, dtype=torch.float32, requires_grad=True)
Xf = torch.tensor(X_np, dtype=torch.float32)
z2f = torch.relu(Xf @ W1f + b1f) @ W2f + b2f
torch.nn.functional.cross_entropy(z2f, y).backward()
print(f" {'nom':<8} {'float64 farqi':>16} {'float32 farqi':>16}")
for nom, a, b64, b32 in [
("dW1", dW1_np, W1.grad.numpy(), W1f.grad.numpy()),
("dW2", dW2_np, W2.grad.numpy(), W2f.grad.numpy())]:
print(f" {nom:<8} {np.abs(a - b64).max():>16.3e} "
f"{np.abs(a - b32).max():>16.3e}")
print(" float32 da farq ~1e-7 - bu NORMAL")
print("\n=== 5. Sonli gradient bilan ham tekshiramiz ===")
h = 1e-6
print(f" {'parametr':<12} {'qo_lda':>13} {'autograd':>13} "
f"{'sonli':>13}")
for i, j in [(0, 0), (3, 5), (7, 11)]:
asl = W1_np[i, j]
W1_np[i, j] = asl + h
l1 = -np.mean(np.log(softmax_np(
np.maximum(0, X_np @ W1_np + b1_np) @ W2_np
+ b2_np)[np.arange(N), y_np]))
W1_np[i, j] = asl - h
l2 = -np.mean(np.log(softmax_np(
np.maximum(0, X_np @ W1_np + b1_np) @ W2_np
+ b2_np)[np.arange(N), y_np]))
W1_np[i, j] = asl
print(f" {f'W1[{i},{j}]':<12} {dW1_np[i, j]:>13.9f} "
f"{W1.grad.numpy()[i, j]:>13.9f} "
f"{(l1 - l2) / (2 * h):>13.9f}")
print(" uchala usul ham bir xil javob beradi")
if __name__ == "__main__":
main()Natijaning muhim qismi:
=== 1. numpy: qo'lda backprop ===
loss: 2.3044637610
gradient shakl norma
dW1 (8, 12) 0.9779808180
db1 (12,) 0.5037569081
dW2 (12, 4) 1.2545012438
db2 (4,) 0.4210806805
=== 2. torch: autograd ===
loss: 2.3044637610
gradient shakl norma
dW1 (8, 12) 0.9779808180
db1 (12,) 0.5037569081
dW2 (12, 4) 1.2545012438
db2 (4,) 0.4210806805
=== 3. Taqqoslash ===
nom maksimal farq bir xilmi
loss 4.441e-16 True
dW1 2.776e-17 True
db1 5.551e-17 True
dW2 8.327e-17 True
db2 0.000e+00 True
⭐ Qo'lda yozganimiz autograd bilan AYNAN bir xil
=== 4. float32 da aniqlik ===
nom float64 farqi float32 farqi
dW1 2.776e-17 3.075e-08
dW2 8.327e-17 3.093e-08
float32 da farq ~1e-7 - bu NORMAL
=== 5. Sonli gradient bilan ham tekshiramiz ===
parametr qo_lda autograd sonli
W1[0,0] -0.138830884 -0.138830884 -0.138830884
W1[3,5] -0.093769873 -0.093769873 -0.093769874
W1[7,11] 0.068387340 0.068387340 0.068387340
uchala usul ham bir xil javob beradiNima ko'rsatdi: 2.6-bo'lim.
Misol 4 — torch bilan to'liq o'rgatish
"""Tensorlar va autograd bilan tarmoqni o'rgatish (real torch)."""
import numpy as np
import torch
from sklearn.datasets import make_classification
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
def main() -> None:
torch.manual_seed(0)
X, y = make_classification(n_samples=4000, n_features=20,
n_informative=10, n_redundant=4,
n_classes=4, flip_y=0.08, class_sep=1.1,
random_state=0)
Xtr, Xte, ytr, yte = train_test_split(X, y, test_size=0.3,
random_state=0, stratify=y)
sc = StandardScaler().fit(Xtr)
Xtr_t = torch.tensor(sc.transform(Xtr), dtype=torch.float32)
Xte_t = torch.tensor(sc.transform(Xte), dtype=torch.float32)
ytr_t = torch.tensor(ytr, dtype=torch.int64)
yte_t = torch.tensor(yte, dtype=torch.int64)
print("=== 1. Parametrlarni qo'lda yaratish ===")
olchamlar = [20, 64, 32, 4]
params = []
for a, b in zip(olchamlar[:-1], olchamlar[1:]):
W = (torch.randn(a, b) * np.sqrt(2.0 / a)).requires_grad_(True)
bb = torch.zeros(b, requires_grad=True)
params += [W, bb]
print(f" arxitektura: {' -> '.join(map(str, olchamlar))}")
print(f" {'parametr':<8} {'shakl':>12} {'requires_grad':>15}")
for i, p in enumerate(params):
nom = f"{'W' if i % 2 == 0 else 'b'}{i // 2 + 1}"
print(f" {nom:<8} {str(tuple(p.shape)):>12} "
f"{str(p.requires_grad):>15}")
def oldinga(X):
A = X
for i in range(0, len(params), 2):
Z = A @ params[i] + params[i + 1]
A = torch.relu(Z) if i < len(params) - 2 else Z
return A
def aniqlik(X, y):
with torch.no_grad():
return (oldinga(X).argmax(1) == y).float().mean().item()
print("\n=== 2. Qo'lda SGD ===")
print(f" {'davr':>6} {'o_quv loss':>12} {'test loss':>11} "
f"{'test aniqlik':>13}")
lr = 0.3
for davr in range(1, 401):
loss = torch.nn.functional.cross_entropy(oldinga(Xtr_t), ytr_t)
for p in params:
p.grad = None
loss.backward()
with torch.no_grad():
for p in params:
p -= lr * p.grad
if davr in (1, 25, 100, 200, 400):
with torch.no_grad():
te = torch.nn.functional.cross_entropy(oldinga(Xte_t),
yte_t)
print(f" {davr:>6} {loss.item():>12.4f} {te.item():>11.4f} "
f"{aniqlik(Xte_t, yte_t):>13.4f}")
print("\n=== 3. torch.optim bilan mini-batch ===")
torch.manual_seed(0)
params2 = []
for a, b in zip(olchamlar[:-1], olchamlar[1:]):
params2 += [(torch.randn(a, b) * np.sqrt(2.0 / a))
.requires_grad_(True),
torch.zeros(b, requires_grad=True)]
def oldinga2(X):
A = X
for i in range(0, len(params2), 2):
Z = A @ params2[i] + params2[i + 1]
A = torch.relu(Z) if i < len(params2) - 2 else Z
return A
opt = torch.optim.Adam(params2, lr=0.01)
g = torch.Generator().manual_seed(0)
print(f" {'davr':>6} {'o_quv loss':>12} {'test aniqlik':>13}")
for davr in range(1, 31):
tartib = torch.randperm(len(ytr_t), generator=g)
for boshi in range(0, len(ytr_t), 128):
idx = tartib[boshi:boshi + 128]
opt.zero_grad()
torch.nn.functional.cross_entropy(
oldinga2(Xtr_t[idx]), ytr_t[idx]).backward()
opt.step()
if davr in (1, 5, 10, 20, 30):
with torch.no_grad():
l = torch.nn.functional.cross_entropy(oldinga2(Xtr_t),
ytr_t).item()
a = (oldinga2(Xte_t).argmax(1)
== yte_t).float().mean().item()
print(f" {davr:>6} {l:>12.4f} {a:>13.4f}")
print("\n=== 4. no_grad ning ta'siri ===")
with torch.no_grad():
chiqish_yoq = oldinga2(Xte_t)
chiqish_bor = oldinga2(Xte_t)
print(f" {'holat':<20} {'requires_grad':>15} {'grad_fn':>14}")
for nom, t in [("no_grad ichida", chiqish_yoq),
("no_grad siz", chiqish_bor)]:
fn = type(t.grad_fn).__name__ if t.grad_fn else "None"
print(f" {nom:<20} {str(t.requires_grad):>15} {fn:>14}")
print(" natija bir xilmi: "
f"{torch.allclose(chiqish_yoq, chiqish_bor.detach())}")
print("\n=== 5. Qatlamlar statistikasi ===")
with torch.no_grad():
A = Xte_t
print(f" {'bosqich':<12} {'shakl':>12} {'std':>9} {'nol %':>8}")
print(f" {'kirish':<12} {str(tuple(A.shape)):>12} "
f"{A.std().item():>9.4f} "
f"{(A == 0).float().mean().item():>8.1%}")
for i in range(0, len(params2), 2):
Z = A @ params2[i] + params2[i + 1]
A = torch.relu(Z) if i < len(params2) - 2 else Z
print(f" {f'qatlam {i // 2 + 1}':<12} "
f"{str(tuple(A.shape)):>12} {A.std().item():>9.4f} "
f"{(A == 0).float().mean().item():>8.1%}")
print(" ⭐ 15 qator kod - butun o'rgatish sikli")
if __name__ == "__main__":
main()Natijaning muhim qismi:
=== 1. Parametrlarni qo'lda yaratish ===
arxitektura: 20 -> 64 -> 32 -> 4
parametr shakl requires_grad
W1 (20, 64) True
b1 (64,) True
W2 (64, 32) True
b2 (32,) True
W3 (32, 4) True
b3 (4,) True
=== 2. Qo'lda SGD ===
davr o_quv loss test loss test aniqlik
1 1.7904 1.5287 0.3075
25 0.9689 1.0212 0.5958
100 0.6675 0.8398 0.7100
200 0.5108 0.8031 0.7425
400 0.3714 0.8425 0.7542
=== 3. torch.optim bilan mini-batch ===
davr o_quv loss test aniqlik
1 0.8684 0.6458
5 0.4918 0.7600
10 0.3866 0.7742
20 0.2037 0.7600
30 0.1044 0.7533
=== 4. no_grad ning ta'siri ===
holat requires_grad grad_fn
no_grad ichida False None
no_grad siz True AddBackward0
natija bir xilmi: True
=== 5. Qatlamlar statistikasi ===
bosqich shakl std nol %
kirish (1200, 20) 0.9846 0.0%
qatlam 1 (1200, 64) 1.2708 53.2%
qatlam 2 (1200, 32) 2.2142 62.4%
qatlam 3 (1200, 4) 5.6926 0.0%
⭐ 15 qator kod - butun o'rgatish sikliNima ko'rsatdi: 2.2, 2.4-bo'limlar.
5. To'g'ri va noto'g'ri tushunishlar
| Noto'g'ri fikr | To'g'risi |
|---|---|
| "autograd sehrli" | Aynan 20.6-darsdagi zanjir qoidasi |
".grad almashadi" |
Yig'iladi — tozalash kerak |
"backward() ni qayta chaqirsa bo'ladi" |
Graf o'chadi, retain_graph kerak |
| "Tensor = numpy massivi" | Device va grad tarixi qo'shilgan |
"float32 va float64 farqi yo'q" |
Gradientda ~1e-7 farq |
"no_grad ixtiyoriy" |
Inference da xotira uchun zarur |
"view va reshape bir xil" |
view uzluksizlik talab qiladi |
| "Graf oldindan quriladi" | Har forward da qaytadan |
6. Keng tarqalgan xatolar va yechimlari
1. zero_grad ni unutish
loss.backward(); opt.step() # ⚠️ gradient yig'iladi
opt.zero_grad(); loss.backward(); opt.step() # ✅2. no_grad siz yangilash
w -= lr * w.grad # ⚠️ grafga qo'shiladi
with torch.no_grad(): w -= lr * w.grad # ✅3. detach siz numpy
t.numpy() # ⚠️ RuntimeError
t.detach().numpy() # ✅4. dtype aralashmasi
torch.from_numpy(X) @ W # ⚠️ float64 @ float32
torch.tensor(X, dtype=torch.float32) @ W # ✅5. Vektordan backward
u.backward() # u vektor # ⚠️
u.sum().backward() # ✅6. Loss ni ro'yxatga yig'ish
tarix.append(loss) # ⚠️ butun graf saqlanadi
tarix.append(loss.item()) # ✅7. view ni transpozitsiyadan keyin
t.T.view(-1) # ⚠️ RuntimeError
t.T.reshape(-1) # ✅7. Integratsiya — bu bilim qayerda kerak bo'ladi
- 20.4, 20.6-darslar (o'tilgan): Oldinga va orqaga o'tish
- 20.10-dars: Regularizatsiya
- 21-qism:
nn.Module,DataLoader, GPU - 23-qism va keyingilari: Barcha DL modellari
8. Eng yaxshi amaliyotlar
zero_gradni sikl boshida qiling.Yangilashni
no_gradichida.Metrikalarni
.item()bilan oling.dtype ni aniq belgilang.
Inference da
no_grad.Shubhada
reshape.manual_seedqo'ying.Gradientni sonli tekshiring.
9. Amaliy topshiriq
Vazifa 1: Bashorat qiling
1. # tensor numpy dan nimasi bilan farq qiladi?
2. # torch sukut dtype?
3. # requires_grad nima qiladi?
4. # .grad yig'iladimi?
5. # backward() nimadan chaqiriladi?
6. # grad_fn nima?
7. # barg tensor nima?
8. # no_grad nima beradi?
9. # detach nima qiladi?
10. # view va reshape farqi?
11. # graf qachon quriladi?
12. # numpy ga qanday o'tiladi?Javoblar
- Device va grad tarixi
float32- Amallar grafga yoziladi
- Ha
- Skalyardan
- Gradient funksiyasi
- Foydalanuvchi yaratgan
- Graf qurilmaydi
- Grafdan uzadi
viewuzluksizlik talab qiladi- Har
forwardda .detach().numpy()
Vazifa 2: Xatolarni tuzating
1. loss.backward(); opt.step()
2. w -= lr * w.grad
3. t.numpy() # requires_grad=True
4. u.backward() # u vektor
5. tarix.append(loss)Javoblar
1. opt.zero_grad(); loss.backward(); opt.step()
2. with torch.no_grad(): w -= lr * w.grad
3. t.detach().numpy()
4. u.sum().backward()
5. tarix.append(loss.item())Vazifa 3: Tensorlar
Modellang:
- Yaratish
- Turlar
- Xotira
- Shakllar
Vazifa 4: autograd
Modellang:
- Oddiy
- Graf
- Yig'ilish
- Dinamik
Vazifa 5: Taqqoslash
Modellang:
- numpy
- torch
- Farq
- Sonli
Vazifa 6: O'rgatish
Modellang:
- Parametrlar
- Qo'lda SGD
- Adam
- no_grad
Vazifa 7: O'ylash
O'rgatish sikli ishlayapti, lekin xotira sarfi har davr bilan o'sib boradi va 50-davrda jarayon o'ladi. Sabab nima?
Javob
Eng ehtimolli sabab: graf bilan bog'langan tensorlar saqlanib qolyapti.
Klassik holat:
tarix = []
for davr in range(davrlar):
loss = kriteriy(model(X), y)
tarix.append(loss) # ⚠️ TENSOR saqlanmoqdaloss — oddiy son emas, u butun hisob grafiga ulangan tensor. Uni ro'yxatga qo'shish o'sha davrning barcha oraliq aktivatsiyalarini xotirada tirik qoldiradi. 50 davrdan keyin xotirada 50 ta to'liq graf yotadi.
Yechim:
tarix.append(loss.item()) # ✅ oddiy float
# yoki
tarix.append(loss.detach()) # ✅ grafdan uzilganBoshqa sabablar:
| Sabab | Kod | Yechim |
|---|---|---|
| Metrikani tensorda yig'ish | jami += loss |
jami += loss.item() |
| Bashoratlarni saqlash | natijalar.append(chiqish) |
.detach() yoki .cpu() |
no_grad siz baholash |
model(X_val) |
with torch.no_grad(): |
retain_graph=True odat bo'lib qolgan |
Har backward da |
Faqat kerak bo'lganda |
| Yashirin holatni uzmaslik (RNN) | h davrdan davrga |
h = h.detach() |
Tashxis:
import gc
import torch
def tensor_soni():
n = 0
for obj in gc.get_objects():
try:
if torch.is_tensor(obj):
n += 1
except Exception:
pass
return n
for davr in range(davrlar):
...
if davr % 5 == 0:
print(f"davr {davr}: {tensor_soni()} ta tensor")Bu son o'sib borsa — sizish tasdiqlandi.
GPU da qo'shimcha:
print(torch.cuda.memory_allocated() / 1e9, "GB")
print(torch.cuda.max_memory_allocated() / 1e9, "GB")Profilaktika — uchta qoida:
- Sonni saqlayotgan bo'lsangiz
.item()qo'ying - Tensorni saqlayotgan bo'lsangiz
.detach()qo'ying - Baholashni har doim
with torch.no_grad():ichida qiling
Bu uchtasi xotira sizishining deyarli barcha holatini yopadi.
Nimani mustahkamlaydi: 2.4-bo'lim.
Xulosa
Bu darsda torch tensorlari va autograd ni ko'rdik.
Eng muhim uch fikr:
Tensor —
numpymassivi ustiga ikki qobiliyat. U qurilmani (cpu/cuda) biladi varequires_grad=Truebo'lganda o'ziga qo'llangan har amalni grafga yozadi. Sukut turifloat32(numpydafloat64) — bu farq gradientlarni taqqoslaganda~1e-7ko'rinishida chiqadi va bu normal.autogradsehr emas. 3-misoldanumpyda qo'lda yozilgandW1,db1,dW2,db2vafloat64dagiautogradnatijalari aynan mos keldi, sonli hosila ham o'sha javobni berdi.autograd— avtomatlashtirilgan 20.6-dars; shuning uchun o'sha darsni o'tganimiz bejiz emas..gradyig'iladi, graf esa dinamik. Harbackward()gradientni qo'shadi, shuning uchun har qadam boshidazero_grad()(yokip.grad = None) majburiy. Graf esa harforwardda qaytadan quriladi — shu sababliif,for,whilebemalol ishlatiladi va hisobno_gradbilan istalgan joyda to'xtatiladi.
Keyingi darsda regularizatsiya: dropout, weight decay va erta to'xtash; ular nimani hal qiladi, qachon qaysi biri kerak va ularni bir vaqtda ishlatish qanday natija beradi.
Izohlar (0)
Izoh yozish uchun kiring.
- Hozircha izoh yo'q. Birinchi bo'ling!