IlmHamroh
Data Science va sun'iy intellekt/PyTorch6/12-dars22 daqiqa
Mundarija (21)

21.6-dars: Qurilmalar va aniqlik

21-QISM — PYTORCH · 6-dars


1. Kirish va motivatsiya

Shu paytgacha hamma narsa CPU da, float32 da ishladi. Kichik tarmoqlar uchun bu yetarli. Lekin rasm, matn va katta modellarga o'tganimizda ikki savol paydo bo'ladi: qayerda hisoblash (CPU, GPU) va qanday aniqlikda (float32, float16, bfloat16).

Birinchi savol kodning tuzilishiga ta'sir qiladi. Model va ma'lumot bir qurilmada bo'lishi shart; aks holda RuntimeError. Shuning uchun kod boshidanoq qurilmadan mustaqil yozilishi kerak: GPU bor joyda GPU da, yo'q joyda CPU da, bitta o'zgartirishsiz ishlasin.

Ikkinchi savol — tezlik va xotira bilan raqamli aniqlik orasidagi savdo. float16 xotirani ikki barobar kamaytiradi va zamonaviy GPU da bir necha barobar tez hisoblaydi — lekin u 65504 dan katta sonni saqlay olmaydi va juda kichik gradientlarni nolga aylantiradi. Aralash aniqlik (AMP) bu muammoni hal qiladi: xavfli amallar float32 da, qolganlari past aniqlikda bajariladi.

Bu kursda misollar CPU da ishlaydi, shuning uchun GPU ning o'ziga xos tezligini o'lchay olmaymiz. Lekin qurilmadan mustaqil kodni, dtype farqlarini, float16 ning toshib ketishini va autocast mexanizmini haqiqiy sonlar bilan ko'ramiz — ular GPU da ham aynan shunday ishlaydi.

Real vaziyat. Model noutbukda ishlardi, serverga ko'chirilganda Expected all tensors to be on the same device xatosi chiqdi. Sabab: forward ichida torch.zeros(n) yaratilgan — u doim CPU da tug'iladi. torch.zeros(n, device=x.device) bitta o'zgartirish muammoni yopdi.

Bu darsda qurilmadan mustaqil va aniqlikni tushunadigan kod yozamiz.

Bu darsda:

  • Qurilma va .to()
  • Qurilmadan mustaqil kod
  • dtype va raqamli aniqlik
  • float16 va bfloat16
  • Aralash aniqlik (autocast, GradScaler)
  • Tuzoqlar
  • Amaliy: to'liq sikl qurilma bilan

ℹ Misollar real torch bilan (Python 3.14, torch 2.14 CPU). GPU bo'lmagani uchun hamma misollar CPU da ishlaydi, lekin kod o'zgarishsiz GPU da ham ishlaydi.


2. Nazariya — chuqur tushuntirish

2.1. Qurilma

text
QURILMA (device):
  torch.device("cpu")
  torch.device("cuda")      NVIDIA GPU  (cuda:0, cuda:1, ...)
  torch.device("mps")       Apple Silicon

TANLASH:
  qurilma = torch.device("cuda" if torch.cuda.is_available() else "cpu")

KO'CHIRISH:
  model.to(qurilma)         JOYIDA (model qaytariladi, lekin o'zi o'zgaradi)
  x = x.to(qurilma)         YANGI tensor qaytaradi - QAYTA TAYINLASH SHART

QOIDA: bitta amaldagi hamma tensor BIR qurilmada bo'lishi shart
  aks holda: RuntimeError: Expected all tensors to be on the same device

x.device       -> tensor qayerda
next(model.parameters()).device -> model qayerda

model.to() joyida o'zgartiradi, x.to() yangi tensor qaytaradi — ikkinchisini qayta tayinlash shart.

2.2. Qurilmadan mustaqil kod

text
1. QURILMANI BIR JOYDA TANLANG
   qurilma = torch.device("cuda" if torch.cuda.is_available() else "cpu")

2. MODEL VA BATCH NI KO'CHIRING
   model.to(qurilma)
   for x, y in dl:
       x, y = x.to(qurilma), y.to(qurilma)

3. FORWARD ICHIDA YANGI TENSOR - kirish qurilmasida
   torch.zeros(n, device=x.device)        ✅
   torch.zeros_like(x)                    ✅ (qurilma va dtype meros)
   torch.zeros(n)                         ⚠️ doim CPU da

4. DOIMIY TENSOR - bufer sifatida
   self.register_buffer("maska", ...)     ✅ model bilan ko'chadi
   self.maska = torch.tensor(...)          ⚠️ CPU da qoladi

5. NUMPY GA QAYTISH
   t.detach().cpu().numpy()               ✅
   t.numpy()                              ⚠️ GPU tensorida xato

6. YUKLASH
   torch.load(yol, map_location=qurilma)

forward ichida yangi tensor — device=x.device bilan; qolgani bir marta .to().

2.3. dtype va raqamli aniqlik

text
TUR         BAYT  EPS (1 dan keyingi qadam)  MAKS         DIAPAZON
float64     8     2.2e-16                   1.8e308      juda keng
float32     4     1.2e-7                    3.4e38       keng
bfloat16    2     7.8e-3                    3.4e38       float32 BILAN BIR XIL
float16     2     9.8e-4                    65504        TOR!

EPS - nisbiy aniqlik: 1.0 + eps/2 == 1.0 (yaxlitlanadi)

bfloat16:  diapazon float32 niki, aniqlik past  -> toshmaydi
float16:   aniqlik bfloat16 dan yaxshi, diapazon tor -> TOSHADI

TOSHISH (overflow):  float16 da 70000 -> inf
YO'QOLISH (underflow): float16 da 1e-8 -> 0  (kichik gradientlar!)

bfloat16 diapazonni, float16 aniqlikni saqlaydi — o'rgatish uchun odatda bfloat16 xavfsizroq.

2.4. Nima uchun past aniqlik

text
XOTIRA:  float16/bfloat16 - 2 bayt, float32 - 4 bayt
  parametrlar, gradientlar, aktivatsiyalar - hammasi 2x kichik
  -> kattaroq model yoki kattaroq batch sig'adi

TEZLIK (GPU da):
  Tensor Core lar float16/bfloat16 matmul ni float32 dan
  bir necha barobar tez bajaradi
  CPU da odatda TEZLIK yutug'i YO'Q yoki kam

NARXI:
  yig'indilar va loss da aniqlik yo'qoladi
  softmax, log, exp - past aniqlikda xavfli
  -> "hamma narsani float16 ga" - YAXSHI FIKR EMAS

Past aniqlik — xotira va GPU tezligi uchun; CPU da odatda tezlik bermaydi.

2.5. Aralash aniqlik (AMP)

text
G'OYA: har amalni O'ZIGA MOS aniqlikda bajarish

with torch.autocast(device_type="cuda", dtype=torch.float16):
    chiqish = model(x)          # matmul/conv -> float16
    loss = kriteriy(chiqish, y)  # softmax/loss -> float32 (avtomatik)

autocast QOIDALARI (torch ichida):
  past aniqlikka: Linear, matmul, conv      (tez, xavfsiz)
  float32 da:     softmax, log, loss        (aniqlik kerak)
  DIQQAT: ro'yxat QURILMAGA bog'liq - CUDA da layer_norm float32,
          CPU da esa past aniqlikda ham bo'lishi mumkin

PARAMETRLAR float32 DA QOLADI ("master weights")
  -> yangilash aniq, kichik qadamlar yo'qolmaydi

GRADIENT SCALER (faqat float16 uchun):
  loss ni katta songa ko'paytiradi -> kichik gradientlar 0 bo'lmaydi
  step dan oldin qaytarib bo'ladi, inf/NaN bo'lsa qadamni o'tkazadi

  scaler = torch.amp.GradScaler("cuda")
  scaler.scale(loss).backward()
  scaler.step(opt)
  scaler.update()

bfloat16 da scaler KERAK EMAS (diapazon keng)

autocast + master weights + (float16 da) GradScaler — aralash aniqlikning uch qismi.

2.6. Amaliy tanlov

text
CPU:
  float32 - sukut, deyarli har doim to'g'ri
  bfloat16 autocast - xotira kerak bo'lsa, tezlik kutilmasin

ZAMONAVIY GPU (Ampere+ : A100, RTX 30xx/40xx, H100):
  bfloat16 autocast, scaler siz - eng oddiy va barqaror

ESKIROQ GPU (V100, T4, RTX 20xx):
  float16 autocast + GradScaler

INFERENCE:
  float16/bfloat16 ga to'liq o'tkazish ko'pincha xavfsiz
  lekin natijani float32 bilan TAQQOSLANG

HECH QACHON:
  float16 da loss ni hisoblash (autocast buni o'zi hal qiladi)
  parametrlarni float16 ga o'tkazib, shunda o'rgatish

Yangi GPU da bfloat16, eskisida float16 + GradScaler, CPU da float32.

2.7. Tuzoqlar

Asosiy tuzoqlar: x.to() natijasini tayinlamaslik; forward da device siz tensor yaratish; doimiy tensorni bufer o'rniga oddiy atribut qilish; GPU tensorida .numpy(); map_location siz yuklash; optimizatorni model ko'chirilishidan oldin yaratish (ba'zi holatlarda); parametrlarni to'liq float16 ga o'tkazib o'rgatish; float16 da GradScaler siz o'rgatish; aniqlik o'zgargandan keyin natijani float32 bilan taqqoslamaslik.


3. Tez ma'lumotnoma

python
import torch

qurilma = torch.device("cuda" if torch.cuda.is_available() else "cpu")
model = Model().to(qurilma)
opt = torch.optim.AdamW(model.parameters(), lr=1e-3)

kichik_aniqlik = torch.bfloat16
for x, y in dl:
    x, y = x.to(qurilma), y.to(qurilma)
    opt.zero_grad()
    with torch.autocast(device_type=qurilma.type, dtype=kichik_aniqlik):
        loss = kriteriy(model(x), y)
    loss.backward()
    opt.step()

# forward ichida
tozalik = torch.zeros(x.shape[0], device=x.device, dtype=x.dtype)

# numpy ga
p = chiqish.detach().float().cpu().numpy()

# yuklash
holat = torch.load(yol, map_location=qurilma, weights_only=True)

Qurilma xulosasi

qurilma bir joyda tanlanadi
model.to() joyida, x = x.to() qayta tayinlash
forward ichida device=x.device
bfloat16: diapazon keng, aniqlik past
float16: diapazon tor (65504), GradScaler kerak
autocast: xavfli amallar float32 da qoladi

4. Batafsil misollar

Misollar real torch bilan (Python 3.14, torch 2.14 CPU).

Misol 1 — Qurilma va ko'chirish

python
"""device, .to() va 'bir qurilma' qoidasi (real torch)."""

import torch
import torch.nn as nn


def main() -> None:
    torch.manual_seed(0)

    print("=== 1. Mavjud qurilmalar ===")
    print(f"  cuda mavjudmi: {torch.cuda.is_available()}")
    mps = (hasattr(torch.backends, "mps")
           and torch.backends.mps.is_available())
    print(f"  mps mavjudmi:  {mps}")
    qurilma = torch.device("cuda" if torch.cuda.is_available() else "cpu")
    print(f"  tanlangan qurilma: {qurilma}")
    print(f"  qurilma turi: {qurilma.type}")

    print("\n=== 2. model.to() va x.to() farqi ===")
    model = nn.Linear(4, 2)
    natija = model.to(qurilma)
    print(f"  model.to() o'sha obyektni qaytaradimi: {natija is model}")
    x = torch.randn(3, 4)
    y = x.to(torch.float64)
    print(f"  x.to(float64) yangi tensormi: {y is not x}")
    print(f"  x ning turi o'zgarmadi: {x.dtype}")
    print(f"  y ning turi: {y.dtype}")
    x.to(torch.float64)
    print(f"  tayinlashsiz x.to() dan keyin x: {x.dtype}")
    print("  x = x.to(...) - QAYTA TAYINLASH shart")

    print("\n=== 3. Qayerda joylashgan ===")
    print(f"  x.device: {x.device}")
    print(f"  model qurilmasi: {next(model.parameters()).device}")
    print(f"  model dtype:     {next(model.parameters()).dtype}")

    print("\n=== 4. Tur mos kelmasa (qurilma kabi xato) ===")
    try:
        model(x.to(torch.float64))
        print("  xato chiqmadi (kutilmagan)")
    except RuntimeError as xato:
        print(f"  RuntimeError: {str(xato)[:60]}")
    print("  qurilma mos kelmasa ham xuddi shunday xato chiqadi")

    print("\n=== 5. model.to() dtype ni ham o'zgartiradi ===")
    m64 = nn.Linear(4, 2).to(torch.float64)
    print(f"  parametr turi: {m64.weight.dtype}")
    print(f"  float64 kirish bilan: "
          f"{m64(torch.randn(2, 4, dtype=torch.float64)).dtype}")

    print("\n=== 6. Tensor yaratishda qurilma va tur ===")
    namuna = torch.randn(2, 3, dtype=torch.float64)
    variantlar = {
        "torch.zeros(3)": torch.zeros(3),
        "torch.zeros(3, device=, dtype=)": torch.zeros(
            3, device=namuna.device, dtype=namuna.dtype),
        "torch.zeros_like(namuna)": torch.zeros_like(namuna),
        "namuna.new_zeros(3)": namuna.new_zeros(3),
    }
    print(f"  {'ifoda':<34} {'qurilma':>8} {'dtype':>15}")
    for nom, t in variantlar.items():
        print(f"  {nom:<34} {str(t.device):>8} {str(t.dtype):>15}")
    print("  ⭐ _like va new_ - qurilma va turni meros oladi")


if __name__ == "__main__":
    main()

Natijaning muhim qismi:

text
=== 1. Mavjud qurilmalar ===
  cuda mavjudmi: False
  mps mavjudmi:  False
  tanlangan qurilma: cpu
  qurilma turi: cpu

=== 2. model.to() va x.to() farqi ===
  model.to() o'sha obyektni qaytaradimi: True
  x.to(float64) yangi tensormi: True
  x ning turi o'zgarmadi: torch.float32
  y ning turi: torch.float64
  tayinlashsiz x.to() dan keyin x: torch.float32
  x = x.to(...) - QAYTA TAYINLASH shart

=== 3. Qayerda joylashgan ===
  x.device: cpu
  model qurilmasi: cpu
  model dtype:     torch.float32

=== 4. Tur mos kelmasa (qurilma kabi xato) ===
  RuntimeError: mat1 and mat2 must have the same dtype, but got Double and F
  qurilma mos kelmasa ham xuddi shunday xato chiqadi

=== 5. model.to() dtype ni ham o'zgartiradi ===
  parametr turi: torch.float64
  float64 kirish bilan: torch.float64

=== 6. Tensor yaratishda qurilma va tur ===
  ifoda                               qurilma           dtype
  torch.zeros(3)                          cpu   torch.float32
  torch.zeros(3, device=, dtype=)         cpu   torch.float64
  torch.zeros_like(namuna)                cpu   torch.float64
  namuna.new_zeros(3)                     cpu   torch.float64
  ⭐ _like va new_ - qurilma va turni meros oladi

Nima ko'rsatdi: 2.1-bo'lim.

Misol 2 — Qurilmadan mustaqil model

python
"""forward ichidagi yangi tensorlar va buferlar (real torch)."""

import shutil
import tempfile
from pathlib import Path

import torch
import torch.nn as nn


class Yomon(nn.Module):
    def __init__(self, d):
        super().__init__()
        self.fc = nn.Linear(d, d)
        self.masshtab = torch.full((d,), 0.5)          # ODDIY atribut

    def forward(self, x):
        shovqin = torch.zeros(x.shape[0], x.shape[1])  # device yo'q
        self.ichki = (shovqin.dtype, self.masshtab.dtype)
        return self.fc(x + shovqin) * self.masshtab


class Yaxshi(nn.Module):
    def __init__(self, d):
        super().__init__()
        self.fc = nn.Linear(d, d)
        self.register_buffer("masshtab", torch.full((d,), 0.5))

    def forward(self, x):
        shovqin = torch.zeros_like(x)                  # x dan meros
        self.ichki = (shovqin.dtype, self.masshtab.dtype)
        return self.fc(x + shovqin) * self.masshtab


def main() -> None:
    torch.manual_seed(0)

    print("=== 1. Ikki model .to(float64) dan keyin ===")
    print("  GPU yo'q, shuning uchun .to() ni dtype bilan sinaymiz")
    print(f"  {'model':<8} {'chiqish':>15} {'shovqin':>15} "
          f"{'masshtab':>15}")
    for sinf in [Yomon, Yaxshi]:
        m = sinf(4).to(torch.float64)
        chiqish = m(torch.randn(3, 4, dtype=torch.float64))
        print(f"  {sinf.__name__:<8} {str(chiqish.dtype):>15} "
              f"{str(m.ichki[0]):>15} {str(m.ichki[1]):>15}")
    print("  ikkalasi ham ISHLADI: torch turlarni avtomatik ko'taradi")
    print("  (float32 * float64 -> float64), lekin Yomon ichida")
    print("  ikki tensor float32 da QOLIB KETDI")

    print("\n=== 1b. Qurilmada avtomatik ko'tarish YO'Q ===")
    try:
        torch.ones(2) + torch.ones(2, device="meta")
        print("  xato chiqmadi (kutilmagan)")
    except RuntimeError as xato:
        print(f"  cpu + meta: RuntimeError - {str(xato)[:46]}")
    print("  'meta' - ma'lumotsiz sinov qurilmasi; cuda bilan ham")
    print("  AYNAN shu xato chiqadi - Yomon model GPU da ISHLAMAYDI")

    print("\n=== 2. Nima ko'chdi, nima ko'chmadi ===")
    y = Yomon(4).to(torch.float64)
    z = Yaxshi(4).to(torch.float64)
    print(f"  {'model':<8} {'fc.weight':>15} {'masshtab':>15}")
    print(f"  {'Yomon':<8} {str(y.fc.weight.dtype):>15} "
          f"{str(y.masshtab.dtype):>15}")
    print(f"  {'Yaxshi':<8} {str(z.fc.weight.dtype):>15} "
          f"{str(z.masshtab.dtype):>15}")
    print("  oddiy atribut .to() bilan KO'CHMAYDI")

    print("\n=== 3. state_dict da ham farq ===")
    print(f"  Yomon:  {list(Yomon(4).state_dict())}")
    print(f"  Yaxshi: {list(Yaxshi(4).state_dict())}")

    print("\n=== 4. Qurilmadan mustaqil sikl ===")
    qurilma = torch.device("cuda" if torch.cuda.is_available() else "cpu")
    model = Yaxshi(4).to(qurilma)
    opt = torch.optim.AdamW(model.parameters(), lr=0.01)
    X = torch.randn(64, 4)
    Y = X * 2.0
    for _ in range(100):
        x, t = X.to(qurilma), Y.to(qurilma)
        opt.zero_grad()
        loss = ((model(x) - t) ** 2).mean()
        loss.backward()
        opt.step()
    print(f"  qurilma: {qurilma}, yakuniy loss: {loss.item():.6f}")
    p = model(X.to(qurilma)).detach().cpu().numpy()
    print(f"  numpy ga: shakl {p.shape}, tur {p.dtype}")
    print("  .detach().cpu().numpy() - har qanday qurilmada ishlaydi")

    print("\n=== 5. map_location bilan yuklash ===")
    papka = Path(tempfile.mkdtemp(prefix="torch_qurilma_"))
    try:
        yol = papka / "m.pt"
        torch.save(model.state_dict(), yol)
        holat = torch.load(yol, map_location="cpu", weights_only=True)
        print(f"  {'kalit':<12} {'qurilma':>8}")
        for k, v in holat.items():
            print(f"  {k:<12} {str(v.device):>8}")
        print("  GPU da saqlangan fayl GPU siz mashinada ham ochiladi")
        print("  ⭐ map_location - har doim bering")
    finally:
        shutil.rmtree(papka, ignore_errors=True)


if __name__ == "__main__":
    main()

Natijaning muhim qismi:

text
=== 1. Ikki model .to(float64) dan keyin ===
  GPU yo'q, shuning uchun .to() ni dtype bilan sinaymiz
  model            chiqish         shovqin        masshtab
  Yomon      torch.float64   torch.float32   torch.float32
  Yaxshi     torch.float64   torch.float64   torch.float64
  ikkalasi ham ISHLADI: torch turlarni avtomatik ko'taradi
  (float32 * float64 -> float64), lekin Yomon ichida
  ikki tensor float32 da QOLIB KETDI

=== 1b. Qurilmada avtomatik ko'tarish YO'Q ===
  cpu + meta: RuntimeError - Tensor on device meta is not on the expected d
  'meta' - ma'lumotsiz sinov qurilmasi; cuda bilan ham
  AYNAN shu xato chiqadi - Yomon model GPU da ISHLAMAYDI

=== 2. Nima ko'chdi, nima ko'chmadi ===
  model          fc.weight        masshtab
  Yomon      torch.float64   torch.float32
  Yaxshi     torch.float64   torch.float64
  oddiy atribut .to() bilan KO'CHMAYDI

=== 3. state_dict da ham farq ===
  Yomon:  ['fc.weight', 'fc.bias']
  Yaxshi: ['masshtab', 'fc.weight', 'fc.bias']

=== 4. Qurilmadan mustaqil sikl ===
  qurilma: cpu, yakuniy loss: 2.578763
  numpy ga: shakl (64, 4), tur float32
  .detach().cpu().numpy() - har qanday qurilmada ishlaydi

=== 5. map_location bilan yuklash ===
  kalit         qurilma
  masshtab          cpu
  fc.weight         cpu
  fc.bias           cpu
  GPU da saqlangan fayl GPU siz mashinada ham ochiladi
  ⭐ map_location - har doim bering

Nima ko'rsatdi: 2.2-bo'lim.

Misol 3 — dtype, toshish va yo'qolish

python
"""float64 / float32 / bfloat16 / float16 ni son bilan (real torch)."""

import torch


def main() -> None:
    turlar = [torch.float64, torch.float32, torch.bfloat16, torch.float16]

    print("=== 1. Asosiy xossalar ===")
    print(f"  {'tur':<16} {'bayt':>5} {'eps':>12} {'maks':>12} "
          f"{'eng kichik':>12}")
    for t in turlar:
        f = torch.finfo(t)
        print(f"  {str(t):<16} {f.bits // 8:>5} {f.eps:>12.2e} "
              f"{f.max:>12.3e} {f.tiny:>12.2e}")

    print("\n=== 2. 1 + kichik son ===")
    print(f"  {'tur':<16}", end="")
    kichiklar = [1e-2, 1e-3, 1e-4, 1e-8]
    for k in kichiklar:
        print(f" {f'1+{k:g}':>10}", end="")
    print()
    for t in turlar:
        print(f"  {str(t):<16}", end="")
        for k in kichiklar:
            q = (torch.tensor(1.0, dtype=t) + torch.tensor(k, dtype=t))
            print(f" {q.item():>10.6f}", end="")
        print()
    print("  bfloat16 da 1 + 0.001 = 1 (aniqlik YO'QOLDI)")

    print("\n=== 3. Toshish (overflow) ===")
    print(f"  {'son':>10}", end="")
    for t in turlar:
        print(f" {str(t).split('.')[1]:>10}", end="")
    print()
    for son in [1000.0, 65000.0, 70000.0, 1e10]:
        print(f"  {son:>10.0e}", end="")
        for t in turlar:
            print(f" {torch.tensor(son).to(t).item():>10.3g}", end="")
        print()
    print("  float16 da 65504 dan kattasi - inf")

    print("\n=== 4. Yo'qolish (underflow) - kichik gradientlar ===")
    gradient = torch.tensor([1e-3, 1e-5, 1e-7, 1e-8])
    print(f"  asl (float32): {gradient.tolist()}")
    for t in [torch.bfloat16, torch.float16]:
        q = gradient.to(t).float()
        nol = int((q == 0).sum())
        print(f"  {str(t):<16}: {[f'{v:.2e}' for v in q.tolist()]}  "
              f"nol: {nol}")
    masshtablangan = (gradient * 1024).to(torch.float16).float() / 1024
    print(f"  float16, 1024 ga ko'paytirilgan: "
          f"{[f'{v:.2e}' for v in masshtablangan.tolist()]}")
    print("  GradScaler aynan shunday qiladi")

    print("\n=== 5. Matritsa ko'paytmasi aniqligi ===")
    torch.manual_seed(0)
    a = torch.randn(128, 128, dtype=torch.float64)
    b = torch.randn(128, 128, dtype=torch.float64)
    etalon = a @ b
    print(f"  {'tur':<16} {'maks xato':>12} {'nisbiy xato':>13}")
    for t in turlar:
        c = (a.to(t) @ b.to(t)).double()
        xato = (c - etalon).abs().max().item()
        nisbiy = xato / etalon.abs().max().item()
        print(f"  {str(t):<16} {xato:>12.3e} {nisbiy:>13.3e}")

    print("\n=== 6. Uzun yig'indida aniqlik ===")
    son = torch.full((100000,), 0.1)
    print(f"  {'tur':<16} {'torch.sum':>11} {'qo_lda 5000':>13} "
          f"{'haqiqiy':>9}")
    for t in turlar:
        s = son.to(t).sum().item()
        jami = torch.tensor(0.0, dtype=t)
        qadam = torch.tensor(0.1, dtype=t)
        for _ in range(5000):
            jami = jami + qadam
        print(f"  {str(t):<16} {s:>11.1f} {jami.item():>13.2f} "
              f"{'10000 / 500':>9}")
    print("  torch.sum ichkarida aniqroq to'playdi, lekin QO'LDA")
    print("  past aniqlikda yig'ish to'xtab qoladi: jami katta")
    print("  bo'lganda 0.1 qo'shilishi yaxlitlashda yo'qoladi")
    print("  ⭐ Yig'indi va loss ni past aniqlikda to'plamang")


if __name__ == "__main__":
    main()

Natijaning muhim qismi:

text
=== 1. Asosiy xossalar ===
  tur               bayt          eps         maks   eng kichik
  torch.float64        8     2.22e-16   1.798e+308    2.23e-308
  torch.float32        4     1.19e-07    3.403e+38     1.18e-38
  torch.bfloat16       2     7.81e-03    3.390e+38     1.18e-38
  torch.float16        2     9.77e-04    6.550e+04     6.10e-05

=== 2. 1 + kichik son ===
  tur                  1+0.01    1+0.001   1+0.0001    1+1e-08
  torch.float64      1.010000   1.001000   1.000100   1.000000
  torch.float32      1.010000   1.001000   1.000100   1.000000
  torch.bfloat16     1.007812   1.000000   1.000000   1.000000
  torch.float16      1.009766   1.000977   1.000000   1.000000
  bfloat16 da 1 + 0.001 = 1 (aniqlik YO'QOLDI)

=== 3. Toshish (overflow) ===
         son    float64    float32   bfloat16    float16
       1e+03      1e+03      1e+03      1e+03      1e+03
       6e+04    6.5e+04    6.5e+04    6.5e+04    6.5e+04
       7e+04      7e+04      7e+04   7.01e+04        inf
       1e+10      1e+10      1e+10      1e+10        inf
  float16 da 65504 dan kattasi - inf

=== 4. Yo'qolish (underflow) - kichik gradientlar ===
  asl (float32): [0.0010000000474974513, 9.999999747378752e-06, 1.0000000116860974e-07, 9.99999993922529e-09]
  torch.bfloat16  : ['9.99e-04', '1.00e-05', '1.00e-07', '1.00e-08']  nol: 0
  torch.float16   : ['1.00e-03', '1.00e-05', '1.19e-07', '0.00e+00']  nol: 1
  float16, 1024 ga ko'paytirilgan: ['1.00e-03', '1.00e-05', '1.00e-07', '1.00e-08']
  GradScaler aynan shunday qiladi

=== 5. Matritsa ko'paytmasi aniqligi ===
  tur                 maks xato   nisbiy xato
  torch.float64       0.000e+00     0.000e+00
  torch.float32       1.643e-05     3.745e-07
  torch.bfloat16      1.524e-01     3.474e-03
  torch.float16       2.267e-02     5.169e-04

=== 6. Uzun yig'indida aniqlik ===
  tur                torch.sum   qo_lda 5000   haqiqiy
  torch.float64        10000.0        500.00 10000 / 500
  torch.float32        10000.0        500.02 10000 / 500
  torch.bfloat16        9984.0         32.00 10000 / 500
  torch.float16        10000.0        256.00 10000 / 500
  torch.sum ichkarida aniqroq to'playdi, lekin QO'LDA
  past aniqlikda yig'ish to'xtab qoladi: jami katta
  bo'lganda 0.1 qo'shilishi yaxlitlashda yo'qoladi
  ⭐ Yig'indi va loss ni past aniqlikda to'plamang

Nima ko'rsatdi: 2.3, 2.4-bo'limlar.

Misol 4 — autocast va aralash aniqlik

python
"""autocast qaysi amalni qaysi aniqlikda bajaradi (real torch)."""

import torch
import torch.nn as nn
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)
    qurilma = torch.device("cuda" if torch.cuda.is_available() else "cpu")
    past = torch.bfloat16

    print("=== 1. autocast ichida turlar ===")
    model = nn.Sequential(nn.Linear(16, 32), nn.LayerNorm(32),
                          nn.ReLU(), nn.Linear(32, 4)).to(qurilma)
    x = torch.randn(8, 16, device=qurilma)
    y = torch.randint(0, 4, (8,), device=qurilma)
    turlar = {}
    tutqichlar = [m.register_forward_hook(
        lambda mod, k, c, nom=type(m).__name__ + str(i):
        turlar.__setitem__(nom, c.dtype))
        for i, m in enumerate(model)]
    with torch.autocast(device_type=qurilma.type, dtype=past):
        chiqish = model(x)
        loss = nn.functional.cross_entropy(chiqish, y)
    for t in tutqichlar:
        t.remove()
    print(f"  {'qatlam':<14} {'chiqish turi':>16}")
    for nom, t in turlar.items():
        print(f"  {nom:<14} {str(t):>16}")
    print(f"  {'loss':<14} {str(loss.dtype):>16}")
    print(f"  parametrlar turi: {model[0].weight.dtype}")
    print("  CPU autocast da LayerNorm ham past aniqlikda chiqdi;")
    print("  CUDA ro'yxatida layer_norm float32 da. Loss IKKALASIDA")
    print("  float32 - bu eng muhimi")

    print("\n=== 2. Gradientlar float32 da ===")
    loss.backward()
    print(f"  model[0].weight.grad turi: {model[0].weight.grad.dtype}")
    print("  master weights va ularning gradienti - float32")

    print("\n=== 3. To'liq o'rgatish: float32 va autocast ===")
    X, t = make_classification(n_samples=3000, n_features=16,
                               n_informative=8, n_classes=4,
                               flip_y=0.1, class_sep=1.0, random_state=0)
    Xtr, Xva, ttr, tva = train_test_split(X, t, test_size=0.3,
                                          random_state=0, stratify=t)
    sc = StandardScaler().fit(Xtr)
    Xtr = torch.tensor(sc.transform(Xtr), dtype=torch.float32)
    Xva = torch.tensor(sc.transform(Xva), dtype=torch.float32)
    ttr, tva = torch.tensor(ttr), torch.tensor(tva)

    def orgat(autocast):
        torch.manual_seed(0)
        m = nn.Sequential(nn.Linear(16, 64), nn.ReLU(),
                          nn.Linear(64, 64), nn.ReLU(),
                          nn.Linear(64, 4)).to(qurilma)
        o = torch.optim.AdamW(m.parameters(), lr=0.005)
        g = torch.Generator().manual_seed(0)
        for _ in range(20):
            m.train()
            tartib = torch.randperm(len(ttr), generator=g)
            for i in range(0, len(ttr), 128):
                idx = tartib[i:i + 128]
                xb, yb = Xtr[idx].to(qurilma), ttr[idx].to(qurilma)
                o.zero_grad()
                with torch.autocast(device_type=qurilma.type, dtype=past,
                                    enabled=autocast):
                    l = nn.functional.cross_entropy(m(xb), yb)
                l.backward()
                o.step()
        m.eval()
        with torch.no_grad():
            ch = m(Xva.to(qurilma)).float()
            va_loss = nn.functional.cross_entropy(ch, tva.to(qurilma))
            aniq = (ch.argmax(1) == tva.to(qurilma)).float().mean()
        return va_loss.item(), aniq.item(), m

    print(f"  {'rejim':<18} {'val loss':>10} {'val aniqlik':>13}")
    natijalar = {}
    for nom, a in [("float32", False), ("autocast bf16", True)]:
        vl, va, m = orgat(a)
        natijalar[nom] = m
        print(f"  {nom:<18} {vl:>10.4f} {va:>13.4f}")
    print("  natija deyarli bir xil - aniqlik yo'qotilmadi")

    print("\n=== 4. Model xotirasi ===")
    m = natijalar["float32"]
    print(f"  {'tur':<16} {'bayt':>8}")
    for t in [torch.float32, torch.bfloat16, torch.float16]:
        bayt = sum(p.numel() * torch.finfo(t).bits // 8
                   for p in m.parameters())
        print(f"  {str(t):<16} {bayt:>8}")

    print("\n=== 5. Inference ni to'liq bf16 ga o'tkazish ===")
    m32 = natijalar["float32"]
    m16 = nn.Sequential(nn.Linear(16, 64), nn.ReLU(), nn.Linear(64, 64),
                        nn.ReLU(), nn.Linear(64, 4)).to(qurilma)
    m16.load_state_dict(m32.state_dict())
    m16 = m16.to(torch.bfloat16).eval()
    with torch.no_grad():
        p32 = m32(Xva.to(qurilma)).argmax(1)
        p16 = m16(Xva.to(qurilma).to(torch.bfloat16)).argmax(1)
    print(f"  bashoratlar mos kelishi: "
          f"{(p32 == p16).float().mean().item():.4f}")
    print("  ⭐ Past aniqlikdan keyin natijani float32 bilan solishtiring")

    print("\n=== 6. GradScaler (float16 uchun) ===")
    scaler = torch.amp.GradScaler(qurilma.type, init_scale=1024.0)
    print(f"  boshlang'ich masshtab: {scaler.get_scale()}")
    print("  float16 da:  scaler.scale(loss).backward()")
    print("               scaler.step(opt); scaler.update()")
    print("  bfloat16 da: scaler KERAK EMAS")


if __name__ == "__main__":
    main()

Natijaning muhim qismi:

text
=== 1. autocast ichida turlar ===
  qatlam             chiqish turi
  Linear0          torch.bfloat16
  LayerNorm1       torch.bfloat16
  ReLU2            torch.bfloat16
  Linear3          torch.bfloat16
  loss              torch.float32
  parametrlar turi: torch.float32
  CPU autocast da LayerNorm ham past aniqlikda chiqdi;
  CUDA ro'yxatida layer_norm float32 da. Loss IKKALASIDA
  float32 - bu eng muhimi

=== 2. Gradientlar float32 da ===
  model[0].weight.grad turi: torch.float32
  master weights va ularning gradienti - float32

=== 3. To'liq o'rgatish: float32 va autocast ===
  rejim                val loss   val aniqlik
  float32                0.8186        0.7378
  autocast bf16          0.8169        0.7467
  natija deyarli bir xil - aniqlik yo'qotilmadi

=== 4. Model xotirasi ===
  tur                  bayt
  torch.float32       22032
  torch.bfloat16      11016
  torch.float16       11016

=== 5. Inference ni to'liq bf16 ga o'tkazish ===
  bashoratlar mos kelishi: 1.0000
  ⭐ Past aniqlikdan keyin natijani float32 bilan solishtiring

=== 6. GradScaler (float16 uchun) ===
  boshlang'ich masshtab: 1024.0
  float16 da:  scaler.scale(loss).backward()
               scaler.step(opt); scaler.update()
  bfloat16 da: scaler KERAK EMAS

Nima ko'rsatdi: 2.5, 2.6-bo'limlar.


5. To'g'ri va noto'g'ri tushunishlar

Noto'g'ri fikr To'g'risi
"x.to() joyida o'zgartiradi" Yangi tensor qaytaradi
"torch.zeros(n) kirish qurilmasida" Doim CPU da
"Oddiy atribut .to() bilan ko'chadi" Faqat parametr va bufer
"float16 va bfloat16 bir xil" Biri aniqlikni, biri diapazonni saqlaydi
"Past aniqlik doim tez" CPU da odatda yo'q
"autocast hamma narsani float16 qiladi" Xavfli amallar float32 da
"bfloat16 ga GradScaler kerak" Kerak emas
"Aniqlik o'zgarsa natija o'zgarmaydi" Taqqoslab tekshiring

6. Keng tarqalgan xatolar va yechimlari

1. Tayinlashsiz .to()

python
x.to(qurilma)                                    # ⚠️ x o'zgarmaydi
x = x.to(qurilma)                                # ✅

2. forward da qurilmasiz tensor

python
maska = torch.zeros(n)                           # ⚠️
maska = torch.zeros(n, device=x.device)          # ✅

3. Doimiy tensor atribut sifatida

python
self.k = torch.tensor([...])                     # ⚠️ ko'chmaydi
self.register_buffer("k", torch.tensor([...]))   # ✅

4. GPU tensoridan numpy

python
chiqish.numpy()                                  # ⚠️
chiqish.detach().cpu().numpy()                   # ✅

5. map_location siz

python
torch.load("gpu_model.pt")                       # ⚠️ GPU siz mashinada
torch.load("gpu_model.pt", map_location="cpu")   # ✅

6. Parametrlarni float16 ga

python
model.half(); orgat(...)                     # ⚠️ beqaror
with torch.autocast(..., dtype=torch.float16):   # ✅ + GradScaler

7. Past aniqlikda loss yig'indisi

python
jami = jami + loss.to(torch.float16)             # ⚠️ aniqlik yo'qoladi
jami += loss.item()                              # ✅

7. Integratsiya — bu bilim qayerda kerak bo'ladi

  • 20.9-dars (o'tilgan): Tensor va dtype
  • 21.5-dars (o'tilgan): Trainer — endi qurilma bilan
  • 21.7-dars: Checkpoint va map_location
  • 24-qism: Rasm modellari GPU da
  • 27-qism: Katta modellar va bfloat16

8. Eng yaxshi amaliyotlar

  1. Qurilmani bir joyda tanlang.

  2. x = x.to(qurilma) — tayinlash bilan.

  3. forward da device=x.device yoki _like.

  4. Doimiy tensorlarni bufer qiling.

  5. .detach().cpu().numpy().

  6. map_location bering.

  7. Yangi GPU da bfloat16 autocast.

  8. Past aniqlikdan keyin natijani taqqoslang.


9. Amaliy topshiriq

Vazifa 1: Bashorat qiling

python
1.  # model.to() nima qaytaradi?
2.  # x.to() nima qaytaradi?
3.  # torch.zeros(n) qayerda yaratiladi?
4.  # oddiy atribut .to() bilan ko'chadimi?
5.  # float16 maksimumi?
6.  # bfloat16 ning afzalligi?
7.  # underflow nima?
8.  # GradScaler nima qiladi?
9.  # autocast da softmax qaysi turda?
10. # parametrlar qaysi turda qoladi?
11. # CPU da past aniqlik tez-mi?
12. # GPU fayl CPU da qanday ochiladi?
Javoblar
  1. O'sha modelni (joyida o'zgaradi)
  2. Yangi tensor
  3. CPU da
  4. Yo'q
  5. 65504
  6. Diapazon float32 niki
  7. Kichik son nolga aylanishi
  8. Loss ni masshtablab, underflow ni oldini oladi
  9. float32
  10. float32
  11. Odatda yo'q
  12. map_location="cpu"

Vazifa 2: Xatolarni tuzating

python
1.  x.to(qurilma)

2.  maska = torch.zeros(n)            # forward ichida

3.  self.k = torch.tensor([1.0, 2.0])

4.  chiqish.numpy()                   # GPU tensor

5.  model.half(); orgat(...)
Javoblar
python
1.  x = x.to(qurilma)

2.  maska = torch.zeros(n, device=x.device)

3.  self.register_buffer("k", torch.tensor([1.0, 2.0]))

4.  chiqish.detach().cpu().numpy()

5.  with torch.autocast(device_type="cuda", dtype=torch.float16): ...
    # + GradScaler

Vazifa 3: Qurilma

Modellang:

  1. Mavjudlik
  2. .to() farqi
  3. Joylashuv
  4. Yaratish

Vazifa 4: Mustaqil kod

Modellang:

  1. Yomon va yaxshi
  2. Ko'chish
  3. state_dict
  4. map_location

Vazifa 5: dtype

Modellang:

  1. Xossalar
  2. Eps
  3. Toshish
  4. Yo'qolish

Vazifa 6: autocast

Modellang:

  1. Turlar
  2. Gradient
  3. O'rgatish
  4. Inference

Vazifa 7: O'ylash

float16 autocast bilan o'rgatishda loss bir necha yuz qadamdan keyin NaN ga aylandi, float32 da esa hammasi yaxshi. Sabablari va yechimlari?

Javob

Asosiy shubha: float16 diapazoni. U 65504 dan katta sonni saqlay olmaydi — 3-misolda 70000 darhol inf ga aylandi. inf - inf = NaN, va NaN butun tarmoqqa tarqaladi.

1. GradScaler bormi

python
scaler = torch.amp.GradScaler("cuda")
scaler.scale(loss).backward()
scaler.step(opt)          # inf/NaN gradientli qadamni O'TKAZADI
scaler.update()           # masshtabni kamaytiradi

GradScaler ikki ish qiladi: kichik gradientlarni yo'qolishdan saqlaydi va inf chiqsa qadamni o'tkazib, masshtabni kamaytiradi. Usiz birinchi inf modelni buzadi.

2. Qaysi qatlamda inf paydo bo'ladi

python
for nom, m in model.named_modules():
    m.register_forward_hook(
        lambda mod, k, c, n=nom: print(n, c.abs().max().item())
        if torch.is_tensor(c) and not torch.isfinite(c).all() else None)

Eng ko'p uchraydigan joylar:

Joy Sabab Yechim
Attention ballari Q @ K.T katta 1/sqrt(d) masshtab, softmax float32 da
Oxirgi Linear Logits juda katta Clipping, lr ni kamaytirish
O'z exp/log ingiz autocast ro'yxatida yo'q Qo'lda .float()
Normalizatsiyasiz chuqur tarmoq Aktivatsiyalar o'sadi LayerNorm, residual

3. O'z amallaringiz

autocast faqat o'zi biladigan amallarni float32 ga o'tkazadi. Agar siz torch.exp(x) yoki x.pow(2).sum() ni o'zingiz yozgan bo'lsangiz — u float16 da qoladi:

python
with torch.autocast("cuda", dtype=torch.float16):
    z = model(x)
    energiya = z.float().pow(2).sum()      # float32 ga o'tkazib hisoblash

4. Eng oddiy yechim — bfloat16

Agar GPU qo'llasa (Ampere va yangiroq), bfloat16 ga o'ting. Uning diapazoni float32 bilan bir xil — toshish muammosi deyarli yo'qoladi va GradScaler kerak bo'lmaydi.

python
if torch.cuda.is_bf16_supported():
    past = torch.bfloat16      # scaler kerak emas
else:
    past = torch.float16       # scaler SHART

5. Boshqa himoyalar

  • Gradient clipping: scaler.unscale_(opt) dan keyin clip_grad_norm_
  • lr ni kamaytirish yoki warmup qo'shish
  • eps ni Adam da kattaroq qilish (1e-8 → 1e-6) — float16 da 1e-8 nolga aylanishi mumkin

Tashxis tartibi: avval GradScaler bormi → keyin bfloat16 ni sinang → keyin inf paydo bo'lgan qatlamni hook bilan toping.

Nimani mustahkamlaydi: 2.3, 2.5-bo'limlar.


Xulosa

Bu darsda qurilmalar va raqamli aniqlikni ko'rdik.

Eng muhim uch fikr:

  1. Qurilmadan mustaqil kod — boshidanoq. Qurilma bir joyda tanlanadi, model model.to() bilan (joyida), batch esa x = x.to() bilan (qayta tayinlab) ko'chiriladi. 2-misolda forward ichida device siz yaratilgan tensor va oddiy atribut sifatida saqlangan doimiy — ikkalasi ham model ko'chirilganda orqada qoldi. dtype da torch turlarni avtomatik ko'tarib, buni yashirdi; qurilmada esa bunday ko'tarish yo'q va aynan shu kod GPU da RuntimeError beradi. zeros_like, device=x.device va register_buffer bu muammoni butunlay yopadi.

  2. bfloat16 diapazonni, float16 aniqlikni saqlaydi. 3-misolda float16 da 70000 inf ga aylandi va 1e-8 kabi kichik gradientlar nolga tushdi; bfloat16 esa katta sonlarni saqladi, lekin 1 + 0.001 ni 1 ga yaxlitladi. Uzun yig'indilar past aniqlikda to'planmasligi kerak — 3-misolda 0.1 ni qo'lda 5000 marta qo'shganda past aniqlikdagi jami haqiqiy 500 ga yetmasdan to'xtab qoldi, chunki jami kattalashgach 0.1 yaxlitlashda yo'qoladi; torch.sum esa ichkarida aniqroq to'plagani uchun bu muammoni chetlab o'tdi.

  3. Aralash aniqlik — har amalga o'z aniqligi. autocast ichida Linear past aniqlikda ishladi, loss esa float32 da qoldi; parametrlar va ularning gradientlari ham float32 da. Qaysi amal qaysi aniqlikda bajarilishi qurilmaga bog'liq — CPU da LayerNorm ham past aniqlikda chiqdi. 4-misolda bfloat16 autocast bilan o'rgatilgan model float32 bilan deyarli bir xil natija berdi. float16 da esa GradScaler majburiy.

Keyingi darsda modelni saqlash va checkpoint: to'liq holatni saqlash, davom ettirish, eng yaxshi va oxirgi checkpoint, versiya va ko'chirish muammolari.

Ulashish:Telegram'da

Izohlar (0)

Izoh yozish uchun kiring.

  • Hozircha izoh yo'q. Birinchi bo'ling!
21.6-dars: Qurilmalar va aniqlik — IlmHamroh