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

21.3-dars: Dataset va DataLoader

21-QISM — PYTORCH · 3-dars


1. Kirish va motivatsiya

20-qismda batchlarni qo'lda yig'dik: tartib = torch.randperm(N), keyin for boshi in range(0, N, batch). Kichik ma'lumotda bu yetarli edi.

Lekin real loyihada ma'lumot xotiraga sig'maydi (million rasm), o'qishda o'zgartiriladi (augmentatsiya), turli uzunlikda keladi (matnlar) yoki nomutanosib bo'ladi (1% musbat sinf). Qo'lda yozilgan sikl har holat uchun qaytadan yoziladi va har safar yangi xato bilan.

PyTorch bu vazifani ikki qismga ajratadi. Dataset — "i-chi namunani qanday olish" savoliga javob beradi. DataLoader — "namunalarni qanday tartibda, qanday guruhlab va qancha parallel olish" savoliga javob beradi. Bu ajratish tufayli bir xil Dataset ni o'quv (aralashtirilgan, augmentatsiyali) va validatsiya (tartibli, toza) uchun turli DataLoader bilan ishlatish mumkin.

Bu darsda o'z Dataset imizni yozamiz, DataLoader parametrlarini ko'ramiz, turli uzunlikdagi ma'lumot uchun collate_fn yozamiz va nomutanosib sinflar uchun namuna olish strategiyasini qo'llaymiz.

Real vaziyat. Jamoa 2 million qatorli CSV ni bitta tensorga yuklamoqchi bo'ldi — 14 GB xotira kerak bo'ldi va jarayon o'ldi. Dataset ga o'tkazib, qatorlarni kerak bo'lganda o'qiydigan qilishdi — xotira sarfi 300 MB ga tushdi.

Bu darsda ma'lumotni tarmoqqa uzatishning standart yo'lini o'rganamiz.

Bu darsda:

  • Dataset: __len__ va __getitem__
  • DataLoader parametrlari
  • collate_fn
  • Namuna olish strategiyalari
  • Bo'lish va takrorlanuvchanlik
  • Tuzoqlar
  • Amaliy: to'liq ma'lumot quvuri

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


2. Nazariya — chuqur tushuntirish

2.1. Dataset

text
MAP-STYLE DATASET (eng keng tarqalgan):
  class MeningDataset(Dataset):
      def __len__(self):            # nechta namuna
          return self.n
      def __getitem__(self, i):     # i-chi namuna
          return x_i, y_i

  SHARTNOMA: dataset[i] har chaqiruvda BIR XIL narsa qaytarsin
             (augmentatsiyadan tashqari)

ITERABLE DATASET:
  __iter__ ni yozasiz, indeks yo'q
  oqimli ma'lumot uchun (log, tarmoqdan kelayotgan)

TAYYOR SINFLAR:
  TensorDataset(X, y)        tensorlar xotirada bo'lsa
  Subset(ds, indekslar)      qism olish
  ConcatDataset([ds1, ds2])  birlashtirish
  random_split(ds, [0.8, 0.2], generator=g)

MUHIM: __getitem__ BITTA namunani qaytaradi
       batch yig'ish - DataLoader ishi

Dataset faqat "i-chi namuna" ni biladi — tartib va batch haqida hech narsa bilmaydi.

2.2. DataLoader

text
DataLoader(dataset,
           batch_size=64,
           shuffle=True,          # har davrda aralashtirish
           drop_last=False,       # oxirgi kichik batchni tashlash
           num_workers=0,         # parallel o'qish jarayonlari
           collate_fn=None,       # namunalarni batchga yig'ish
           sampler=None,          # indeks tartibi
           generator=g,           # takrorlanuvchanlik
           pin_memory=False)      # GPU ga tez ko'chirish

NIMA QILADI:
  1. sampler dan indekslar oladi
  2. har indeks uchun dataset[i] chaqiradi
  3. collate_fn bilan batchga yig'adi
  4. (num_workers > 0 bo'lsa) buni parallel qiladi

O'QUV:       shuffle=True,  drop_last=True (BatchNorm bo'lsa)
VALIDATSIYA: shuffle=False, drop_last=False

O'quv va validatsiya uchun alohida DataLoader — sozlamalari farq qiladi.

2.3. collate_fn

text
SUKUT collate:
  [(x0, y0), (x1, y1), ...] -> (stack(x), stack(y))
  SHART: hamma x BIR XIL shaklda

TURLI UZUNLIK (matn, ketma-ketlik):
  stack ishlamaydi -> o'z collate_fn

  def toldir(batch):
      xlar, ylar = zip(*batch)
      uzunliklar = [len(x) for x in xlar]
      L = max(uzunliklar)
      X = torch.zeros(len(xlar), L)
      for i, x in enumerate(xlar):
          X[i, :len(x)] = x
      maska = torch.arange(L) < torch.tensor(uzunliklar)[:, None]
      return X, torch.tensor(ylar), maska

LUG'AT QAYTARISH ham mumkin:
  return {"x": X, "y": y, "maska": maska}

Turli uzunlikdagi namunalar uchun collate_fn + maska — padding ni model bilishi kerak.

2.4. Namuna olish strategiyalari

text
SAMPLER - qaysi indekslar qaysi tartibda

RandomSampler         shuffle=True bilan bir xil
SequentialSampler     shuffle=False bilan bir xil
SubsetRandomSampler   faqat berilgan indekslardan
WeightedRandomSampler og'irlik bo'yicha, qaytarib (replacement)

NOMUTANOSIB SINFLAR:
  og'irlik_i = 1 / sinf_chastotasi[y_i]
  sampler = WeightedRandomSampler(og'irliklar, num_samples=N,
                                  replacement=True, generator=g)
  -> har batchda sinflar TAXMINAN teng

DIQQAT:
  sampler va shuffle=True BIRGA ISHLATILMAYDI (xato)
  WeightedRandomSampler kam sinfni TAKRORLAYDI -> yodlash xavfi
  loss og'irligi 20.5-bob bilan BIRGA qo'llamang - ikki marta

WeightedRandomSampler — ma'lumot darajasidagi muvozanatlash; loss og'irligi bilan birga ishlatmang.

2.5. Bo'lish va takrorlanuvchanlik

text
BO'LISH:
  g = torch.Generator().manual_seed(42)
  oquv, val = random_split(ds, [0.8, 0.2], generator=g)

  DIQQAT: guruh/vaqt tuzilmasi bo'lsa random_split YAROQSIZ
          -> indekslarni sklearn bilan hisoblab, Subset ishlating
          (GroupKFold, TimeSeriesSplit - 18-qism)

TAKRORLANUVCHANLIK:
  DataLoader(..., shuffle=True, generator=g)
  g ni har yurishda QAYTA seed qiling

  num_workers > 0 bo'lsa har ishchi o'z seed ini oladi:
    worker_init_fn=lambda i: np.random.seed(asos + i)

AUGMENTATSIYA TASODIFIYLIGI:
  __getitem__ ichidagi tasodif ham seed ga bog'lanishi kerak

Guruhli ma'lumotda random_split leakage beradi — indekslarni sklearn bilan hisoblang.

2.6. Xotira va parallellik

text
XOTIRAGA SIG'SA:
  TensorDataset - eng tez, eng oddiy

SIG'MASA (lazy loading):
  __init__ da faqat YO'LLAR/indekslarni saqlang
  __getitem__ da faylni O'QING
  -> xotira ~ bitta batch hajmi

num_workers:
  0  - asosiy jarayonda (debug uchun eng yaxshi)
  >0 - parallel jarayonlar (I/O sekin bo'lsa foydali)
  Windows/macOS da spawn - Dataset pickle qilinishi SHART
  -> lambda, ochiq fayl tutqichlari muammo beradi

pin_memory=True: CPU->GPU ko'chirishni tezlashtiradi
persistent_workers=True: ishchilarni davrlar orasida saqlaydi

Debug da num_workers=0 — parallel jarayonlardagi xato izini o'qish qiyin.

2.7. Tuzoqlar

Asosiy tuzoqlar: __getitem__ da batch qaytarish; validatsiyada shuffle=True; sampler va shuffle ni birga berish; guruhli ma'lumotda random_split; generator siz aralashtirish; WeightedRandomSampler va loss og'irligini birga ishlatish; Windows da lambda bilan num_workers>0; collate_fn da maskani unutish; __init__ da butun katta faylni yuklash.


3. Tez ma'lumotnoma

python
import torch
from torch.utils.data import (DataLoader, Dataset, Subset, TensorDataset,
                              WeightedRandomSampler, random_split)


class JadvalDataset(Dataset):
    def __init__(self, X, y):
        self.X = torch.as_tensor(X, dtype=torch.float32)
        self.y = torch.as_tensor(y, dtype=torch.int64)

    def __len__(self):
        return len(self.y)

    def __getitem__(self, i):
        return self.X[i], self.y[i]


g = torch.Generator().manual_seed(42)
oquv_dl = DataLoader(JadvalDataset(Xtr, ytr), batch_size=64,
                     shuffle=True, drop_last=True, generator=g)
val_dl = DataLoader(JadvalDataset(Xva, yva), batch_size=256,
                    shuffle=False)

# nomutanosib sinflar
chastota = torch.bincount(ytr_t)
w = (1.0 / chastota)[ytr_t]
sampler = WeightedRandomSampler(w, num_samples=len(w),
                                replacement=True, generator=g)
DataLoader(ds, batch_size=64, sampler=sampler)

Ma'lumot xulosasi

Dataset:     __len__ + __getitem__ (bitta namuna)
DataLoader:  batch, tartib, parallellik
collate_fn:  turli uzunlik -> padding + maska
sampler:     tartib strategiyasi
generator:   takrorlanuvchanlik

4. Batafsil misollar

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

Misol 1 — O'z Dataset imiz

python
"""Dataset shartnomasi va TensorDataset (real torch)."""

import numpy as np
import torch
from torch.utils.data import ConcatDataset, Dataset, Subset, TensorDataset


class JadvalDataset(Dataset):
    """Son belgilar + kategoriya + maqsad."""

    def __init__(self, X_son, X_kat, y):
        self.X_son = torch.as_tensor(X_son, dtype=torch.float32)
        self.X_kat = torch.as_tensor(X_kat, dtype=torch.int64)
        self.y = torch.as_tensor(y, dtype=torch.int64)
        assert len(self.X_son) == len(self.X_kat) == len(self.y)

    def __len__(self):
        return len(self.y)

    def __getitem__(self, i):
        return {"son": self.X_son[i], "kat": self.X_kat[i],
                "y": self.y[i]}


def main() -> None:
    rng = np.random.default_rng(0)
    n = 100
    X_son = rng.normal(0, 1, (n, 4))
    X_kat = rng.integers(0, 5, (n, 2))
    y = rng.integers(0, 3, n)

    print("=== 1. Shartnoma ===")
    ds = JadvalDataset(X_son, X_kat, y)
    print(f"  len(ds): {len(ds)}")
    namuna = ds[7]
    print(f"  ds[7] turi: {type(namuna).__name__}")
    print(f"  {'kalit':<6} {'shakl':>8} {'dtype':>14}")
    for k, v in namuna.items():
        print(f"  {k:<6} {str(tuple(v.shape)):>8} {str(v.dtype):>14}")

    print("\n=== 2. Bir xil indeks - bir xil natija ===")
    a, b = ds[7], ds[7]
    print(f"  ds[7]['son'] ikki marta bir xilmi: "
          f"{torch.equal(a['son'], b['son'])}")
    print("  Dataset DETERMINISTIK bo'lishi kerak (augmentatsiyasiz)")

    print("\n=== 3. TensorDataset ===")
    td = TensorDataset(torch.tensor(X_son, dtype=torch.float32),
                       torch.tensor(y))
    x0, y0 = td[0]
    print(f"  len: {len(td)}, td[0] = (shakl {tuple(x0.shape)}, "
          f"y={int(y0)})")
    print(f"  tuple qaytaradi: {type(td[0]).__name__}")

    print("\n=== 4. Subset va ConcatDataset ===")
    birinchi = Subset(ds, range(0, 30))
    ikkinchi = Subset(ds, range(70, 100))
    birga = ConcatDataset([birinchi, ikkinchi])
    print(f"  Subset(0..30): {len(birinchi)}")
    print(f"  Subset(70..100): {len(ikkinchi)}")
    print(f"  Concat: {len(birga)}")
    print(f"  birga[30] == ds[70]: "
          f"{torch.equal(birga[30]['son'], ds[70]['son'])}")

    print("\n=== 5. Salbiy va chegara indekslari ===")
    print(f"  ds[-1]['y'] == ds[99]['y']: "
          f"{int(ds[-1]['y']) == int(ds[99]['y'])}")
    try:
        ds[100]
        print("  ds[100] xato bermadi (kutilmagan)")
    except IndexError as xato:
        print(f"  ds[100]: IndexError - {str(xato)[:40]}")

    print("\n=== 6. Uzunlik mos kelmasa ===")
    try:
        JadvalDataset(X_son, X_kat[:50], y)
        print("  xato chiqmadi (kutilmagan)")
    except AssertionError:
        print("  AssertionError: uzunliklar mos emas")
    print("  ⭐ __init__ da tekshiruv - keyingi xatolarni oldini oladi")


if __name__ == "__main__":
    main()

Natijaning muhim qismi:

text
=== 1. Shartnoma ===
  len(ds): 100
  ds[7] turi: dict
  kalit     shakl          dtype
  son        (4,)  torch.float32
  kat        (2,)    torch.int64
  y            ()    torch.int64

=== 2. Bir xil indeks - bir xil natija ===
  ds[7]['son'] ikki marta bir xilmi: True
  Dataset DETERMINISTIK bo'lishi kerak (augmentatsiyasiz)

=== 3. TensorDataset ===
  len: 100, td[0] = (shakl (4,), y=1)
  tuple qaytaradi: tuple

=== 4. Subset va ConcatDataset ===
  Subset(0..30): 30
  Subset(70..100): 30
  Concat: 60
  birga[30] == ds[70]: True

=== 5. Salbiy va chegara indekslari ===
  ds[-1]['y'] == ds[99]['y']: True
  ds[100]: IndexError - index 100 is out of bounds for dimension

=== 6. Uzunlik mos kelmasa ===
  AssertionError: uzunliklar mos emas
  ⭐ __init__ da tekshiruv - keyingi xatolarni oldini oladi

Nima ko'rsatdi: 2.1-bo'lim.

Misol 2 — DataLoader parametrlari

python
"""batch_size, shuffle, drop_last, generator (real torch)."""

import torch
from torch.utils.data import DataLoader, TensorDataset


def main() -> None:
    X = torch.arange(10, dtype=torch.float32).unsqueeze(1)
    y = torch.arange(10)
    ds = TensorDataset(X, y)

    print("=== 1. Tartibli (validatsiya) ===")
    dl = DataLoader(ds, batch_size=4, shuffle=False)
    for i, (xb, yb) in enumerate(dl):
        print(f"  batch {i}: y = {yb.tolist()}")
    print(f"  batchlar soni: {len(dl)}")

    print("\n=== 2. drop_last ===")
    for dl_ in [DataLoader(ds, batch_size=4, drop_last=False),
                DataLoader(ds, batch_size=4, drop_last=True)]:
        hajmlar = [len(yb) for _, yb in dl_]
        print(f"  drop_last={dl_.drop_last}: hajmlar {hajmlar}, "
              f"jami {sum(hajmlar)}")
    print("  drop_last=True oxirgi kichik batchni tashlaydi")

    print("\n=== 3. shuffle har davrda boshqa ===")
    g = torch.Generator().manual_seed(0)
    dl = DataLoader(ds, batch_size=10, shuffle=True, generator=g)
    for davr in range(3):
        _, yb = next(iter(dl))
        print(f"  davr {davr}: {yb.tolist()}")

    print("\n=== 4. generator bilan takrorlanuvchanlik ===")
    def tartib(seed):
        g = torch.Generator().manual_seed(seed)
        dl = DataLoader(ds, batch_size=10, shuffle=True, generator=g)
        return [next(iter(dl))[1].tolist() for _ in range(2)]

    a, b = tartib(42), tartib(42)
    print(f"  seed 42, 1-yurish: {a[0]}")
    print(f"  seed 42, 2-yurish: {b[0]}")
    print(f"  bir xilmi: {a == b}")
    print(f"  seed 7 bilan farqlimi: {tartib(7) != a}")

    print("\n=== 5. Har davrda har namuna bir marta ===")
    g = torch.Generator().manual_seed(1)
    dl = DataLoader(ds, batch_size=3, shuffle=True, generator=g)
    korilgan = sorted(v for _, yb in dl for v in yb.tolist())
    print(f"  bir davrda ko'rilgan: {korilgan}")
    print(f"  hammasi bir martadan: {korilgan == list(range(10))}")

    print("\n=== 6. sampler va shuffle birga ===")
    from torch.utils.data import SequentialSampler
    try:
        DataLoader(ds, batch_size=4, shuffle=True,
                   sampler=SequentialSampler(ds))
        print("  xato chiqmadi (kutilmagan)")
    except ValueError as xato:
        print(f"  ValueError: {str(xato)[:58]}")
    print("  ⭐ sampler berilsa shuffle ni bermang")

    print("\n=== 7. O'quv va validatsiya sozlamalari ===")
    jadval = [
        ("batch_size", "32-256", "kattaroq (512+)"),
        ("shuffle", "True", "False"),
        ("drop_last", "True (BN bo'lsa)", "False"),
        ("generator", "seed bilan", "kerak emas"),
    ]
    print(f"  {'parametr':<12} {'o_quv':<18} {'validatsiya'}")
    for a, b, c in jadval:
        print(f"  {a:<12} {b:<18} {c}")


if __name__ == "__main__":
    main()

Natijaning muhim qismi:

text
=== 1. Tartibli (validatsiya) ===
  batch 0: y = [0, 1, 2, 3]
  batch 1: y = [4, 5, 6, 7]
  batch 2: y = [8, 9]
  batchlar soni: 3

=== 2. drop_last ===
  drop_last=False: hajmlar [4, 4, 2], jami 10
  drop_last=True: hajmlar [4, 4], jami 8
  drop_last=True oxirgi kichik batchni tashlaydi

=== 3. shuffle har davrda boshqa ===
  davr 0: [3, 7, 5, 2, 0, 8, 1, 6, 9, 4]
  davr 1: [9, 2, 1, 7, 4, 0, 3, 5, 6, 8]
  davr 2: [4, 6, 7, 1, 2, 5, 0, 3, 9, 8]

=== 4. generator bilan takrorlanuvchanlik ===
  seed 42, 1-yurish: [6, 5, 4, 0, 8, 9, 2, 1, 3, 7]
  seed 42, 2-yurish: [6, 5, 4, 0, 8, 9, 2, 1, 3, 7]
  bir xilmi: True
  seed 7 bilan farqlimi: True

=== 5. Har davrda har namuna bir marta ===
  bir davrda ko'rilgan: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
  hammasi bir martadan: True

=== 6. sampler va shuffle birga ===
  ValueError: sampler option is mutually exclusive with shuffle
  ⭐ sampler berilsa shuffle ni bermang

=== 7. O'quv va validatsiya sozlamalari ===
  parametr     o_quv              validatsiya
  batch_size   32-256             kattaroq (512+)
  shuffle      True               False
  drop_last    True (BN bo'lsa)   False
  generator    seed bilan         kerak emas

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

Misol 3 — collate_fn va turli uzunlik

python
"""Turli uzunlikdagi ketma-ketliklarni batchga yig'ish (real torch)."""

import torch
from torch.utils.data import DataLoader, Dataset


class KetmaKetlik(Dataset):
    """Har namuna - turli uzunlikdagi sonlar ketma-ketligi."""

    def __init__(self, n=40, seed=0):
        g = torch.Generator().manual_seed(seed)
        self.uzunliklar = torch.randint(3, 12, (n,), generator=g)
        self.xlar = [torch.randn(int(L), generator=g)
                     for L in self.uzunliklar]
        self.ylar = [int(x.sum() > 0) for x in self.xlar]

    def __len__(self):
        return len(self.xlar)

    def __getitem__(self, i):
        return self.xlar[i], self.ylar[i]


def toldir(batch):
    xlar, ylar = zip(*batch)
    uzunliklar = torch.tensor([len(x) for x in xlar])
    L = int(uzunliklar.max())
    X = torch.zeros(len(xlar), L)
    for i, x in enumerate(xlar):
        X[i, :len(x)] = x
    maska = torch.arange(L)[None, :] < uzunliklar[:, None]
    return {"x": X, "maska": maska, "uzunlik": uzunliklar,
            "y": torch.tensor(ylar)}


def main() -> None:
    ds = KetmaKetlik()

    print("=== 1. Namunalar uzunligi ===")
    print(f"  birinchi 8 ta: {[len(ds[i][0]) for i in range(8)]}")

    print("\n=== 2. Sukut collate ishlamaydi ===")
    dl = DataLoader(ds, batch_size=4)
    try:
        next(iter(dl))
        print("  xato chiqmadi (kutilmagan)")
    except RuntimeError as xato:
        print(f"  RuntimeError: {str(xato).splitlines()[0][:56]}")

    print("\n=== 3. O'z collate_fn ===")
    dl = DataLoader(ds, batch_size=4, collate_fn=toldir)
    batch = next(iter(dl))
    print(f"  {'kalit':<9} {'shakl':>10} {'dtype':>14}")
    for k, v in batch.items():
        print(f"  {k:<9} {str(tuple(v.shape)):>10} {str(v.dtype):>14}")
    print(f"  uzunliklar: {batch['uzunlik'].tolist()}")

    print("\n=== 4. Maska ===")
    for i in range(4):
        m = batch["maska"][i].int().tolist()
        print(f"  namuna {i}: {''.join(map(str, m))}")
    print("  1 - haqiqiy qiymat, 0 - to'ldirilgan (padding)")

    print("\n=== 5. Maskasiz o'rtacha xato beradi ===")
    x = batch["x"]
    maska = batch["maska"]
    notogri = x.mean(dim=1)
    togri = (x * maska).sum(dim=1) / maska.sum(dim=1)
    haqiqiy = torch.tensor([ds[i][0].mean().item() for i in range(4)])
    print(f"  {'namuna':>7} {'maskasiz':>10} {'maska bilan':>12} "
          f"{'haqiqiy':>10}")
    for i in range(4):
        print(f"  {i:>7} {notogri[i].item():>10.4f} "
              f"{togri[i].item():>12.4f} {haqiqiy[i].item():>10.4f}")
    print("  padding nollari o'rtachani BUZADI")

    print("\n=== 6. Uzunlik bo'yicha saralash (bucketing) ===")
    tartib = sorted(range(len(ds)), key=lambda i: len(ds[i][0]))
    oddiy = DataLoader(ds, batch_size=8, collate_fn=toldir)
    saralangan = DataLoader(torch.utils.data.Subset(ds, tartib),
                            batch_size=8, collate_fn=toldir)
    for nom, dl_ in [("oddiy", oddiy), ("saralangan", saralangan)]:
        jami, haqiqiy_son = 0, 0
        for b in dl_:
            jami += b["maska"].numel()
            haqiqiy_son += int(b["maska"].sum())
        print(f"  {nom:<12} padding ulushi: "
              f"{1 - haqiqiy_son / jami:.1%}")
    print("  ⭐ O'xshash uzunliklarni bir batchga - kam padding")


if __name__ == "__main__":
    main()

Natijaning muhim qismi:

text
=== 1. Namunalar uzunligi ===
  birinchi 8 ta: [11, 3, 5, 9, 10, 9, 10, 4]

=== 2. Sukut collate ishlamaydi ===
  RuntimeError: stack expects each tensor to be equal size, but got [11]

=== 3. O'z collate_fn ===
  kalit          shakl          dtype
  x            (4, 11)  torch.float32
  maska        (4, 11)     torch.bool
  uzunlik         (4,)    torch.int64
  y               (4,)    torch.int64
  uzunliklar: [11, 3, 5, 9]

=== 4. Maska ===
  namuna 0: 11111111111
  namuna 1: 11100000000
  namuna 2: 11111000000
  namuna 3: 11111111100
  1 - haqiqiy qiymat, 0 - to'ldirilgan (padding)

=== 5. Maskasiz o'rtacha xato beradi ===
   namuna   maskasiz  maska bilan    haqiqiy
        0     0.2450       0.2450     0.2450
        1    -0.0525      -0.1926    -0.1926
        2    -0.2924      -0.6434    -0.6434
        3    -0.2707      -0.3308    -0.3308
  padding nollari o'rtachani BUZADI

=== 6. Uzunlik bo'yicha saralash (bucketing) ===
  oddiy        padding ulushi: 34.5%
  saralangan   padding ulushi: 10.0%
  ⭐ O'xshash uzunliklarni bir batchga - kam padding

Nima ko'rsatdi: 2.3-bo'lim.

Misol 4 — Nomutanosib sinflar va to'liq quvur

python
"""WeightedRandomSampler, guruhli bo'lish va o'rgatish (real torch)."""

import numpy as np
import torch
import torch.nn as nn
from sklearn.metrics import average_precision_score
from sklearn.model_selection import GroupShuffleSplit
from torch.utils.data import (DataLoader, Subset, TensorDataset,
                              WeightedRandomSampler)


def orgat(dl, Xva, yva, davrlar=25, seed=0):
    torch.manual_seed(seed)
    model = nn.Sequential(nn.Linear(10, 32), nn.ReLU(),
                          nn.Linear(32, 2))
    opt = torch.optim.AdamW(model.parameters(), lr=0.01)
    kriteriy = nn.CrossEntropyLoss()
    for _ in range(davrlar):
        model.train()
        for xb, yb in dl:
            opt.zero_grad()
            kriteriy(model(xb), yb).backward()
            opt.step()
    model.eval()
    with torch.no_grad():
        p = torch.softmax(model(Xva), dim=1)[:, 1].numpy()
    return p


def main() -> None:
    rng = np.random.default_rng(0)
    n = 4000
    guruh = rng.integers(0, 400, n)
    X = rng.normal(0, 1, (n, 10)).astype(np.float32)
    kuch = X[:, 0] * 1.5 + X[:, 1] - 3.8 + rng.normal(0, 0.8, n)
    y = (kuch > 0).astype(np.int64)
    print(f"  {n} namuna, musbat ulush: {y.mean():.2%}")

    print("\n=== 1. Guruhli bo'lish (random_split EMAS) ===")
    tr, va = next(GroupShuffleSplit(1, test_size=0.25, random_state=0)
                  .split(X, y, groups=guruh))
    ds = TensorDataset(torch.tensor(X), torch.tensor(y))
    oquv, val = Subset(ds, tr), Subset(ds, va)
    print(f"  o'quv: {len(oquv)}, validatsiya: {len(val)}")
    print(f"  guruh kesishishi: "
          f"{len(np.intersect1d(guruh[tr], guruh[va]))}")
    Xva = torch.tensor(X[va])
    yva = y[va]

    print("\n=== 2. Oddiy DataLoader da batch tarkibi ===")
    g = torch.Generator().manual_seed(0)
    dl_oddiy = DataLoader(oquv, batch_size=64, shuffle=True,
                          generator=g)
    ulushlar = [yb.float().mean().item() for _, yb in dl_oddiy]
    print(f"  batchdagi musbat ulushi: o'rtacha {np.mean(ulushlar):.3f}, "
          f"min {min(ulushlar):.3f}, max {max(ulushlar):.3f}")
    nol = sum(1 for u in ulushlar if u == 0)
    print(f"  birorta ham musbat yo'q batchlar: {nol}/{len(ulushlar)}")

    print("\n=== 3. WeightedRandomSampler ===")
    ytr = torch.tensor(y[tr])
    chastota = torch.bincount(ytr).float()
    ogirlik = (1.0 / chastota)[ytr]
    g = torch.Generator().manual_seed(0)
    sampler = WeightedRandomSampler(ogirlik, num_samples=len(ogirlik),
                                    replacement=True, generator=g)
    dl_w = DataLoader(oquv, batch_size=64, sampler=sampler)
    ulushlar = [yb.float().mean().item() for _, yb in dl_w]
    print(f"  batchdagi musbat ulushi: o'rtacha {np.mean(ulushlar):.3f}, "
          f"min {min(ulushlar):.3f}, max {max(ulushlar):.3f}")
    idx_royxat = list(iter(sampler))
    noyob = len(set(idx_royxat))
    print(f"  bir davrda noyob indekslar: {noyob}/{len(idx_royxat)}")
    musbat_idx = set(np.where(y[tr] == 1)[0].tolist())
    takror = sum(1 for i in idx_royxat if i in musbat_idx)
    print(f"  musbat namunalar {len(musbat_idx)} ta, "
          f"davrda {takror} marta tanlandi")
    print("  kam sinf TAKRORLANADI - yodlash xavfi")

    print("\n=== 4. O'rgatishga ta'siri ===")
    print(f"  {'variant':<22} {'val AP':>9} {'o_rt bashorat':>14}")
    g1 = torch.Generator().manual_seed(0)
    p1 = orgat(DataLoader(oquv, batch_size=64, shuffle=True,
                          generator=g1), Xva, yva)
    g2 = torch.Generator().manual_seed(0)
    s2 = WeightedRandomSampler(ogirlik, num_samples=len(ogirlik),
                               replacement=True, generator=g2)
    p2 = orgat(DataLoader(oquv, batch_size=64, sampler=s2), Xva, yva)
    for nom, p in [("oddiy", p1), ("WeightedRandomSampler", p2)]:
        print(f"  {nom:<22} {average_precision_score(yva, p):>9.4f} "
              f"{p.mean():>14.4f}")
    print(f"  haqiqiy musbat ulush: {yva.mean():.4f}")
    print("  sampler tartiblashni SAQLAYDI, lekin ehtimollikni SILJITADI")

    print("\n=== 5. Validatsiya uchun ===")
    dl_val = DataLoader(val, batch_size=512, shuffle=False)
    hajmlar = [len(yb) for _, yb in dl_val]
    print(f"  batchlar: {hajmlar}")
    print("  validatsiyada sampler ham, shuffle ham YO'Q")
    print("  ⭐ Muvozanatlash faqat O'QUV DataLoader ida")


if __name__ == "__main__":
    main()

Natijaning muhim qismi:

text
  4000 namuna, musbat ulush: 2.53%

=== 1. Guruhli bo'lish (random_split EMAS) ===
  o'quv: 2958, validatsiya: 1042
  guruh kesishishi: 0

=== 2. Oddiy DataLoader da batch tarkibi ===
  batchdagi musbat ulushi: o'rtacha 0.026, min 0.000, max 0.143
  birorta ham musbat yo'q batchlar: 13/47

=== 3. WeightedRandomSampler ===
  batchdagi musbat ulushi: o'rtacha 0.492, min 0.359, max 0.578
  bir davrda noyob indekslar: 1222/2958
  musbat namunalar 71 ta, davrda 1511 marta tanlandi
  kam sinf TAKRORLANADI - yodlash xavfi

=== 4. O'rgatishga ta'siri ===
  variant                   val AP  o_rt bashorat
  oddiy                     0.5258         0.0193
  WeightedRandomSampler     0.5618         0.0479
  haqiqiy musbat ulush: 0.0288
  sampler tartiblashni SAQLAYDI, lekin ehtimollikni SILJITADI

=== 5. Validatsiya uchun ===
  batchlar: [512, 512, 18]
  validatsiyada sampler ham, shuffle ham YO'Q
  ⭐ Muvozanatlash faqat O'QUV DataLoader ida

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


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

Noto'g'ri fikr To'g'risi
"__getitem__ batch qaytaradi" Bitta namuna
"Bitta DataLoader yetarli" O'quv va validatsiya alohida
"random_split har doim to'g'ri" Guruhli ma'lumotda leakage
"Padding zararsiz" Maskasiz hisobni buzadi
"WeightedRandomSampler bepul" Kam sinfni takrorlaydi
"Sampler + shuffle birga" ValueError
"num_workers ko'p — tez" Debug qiyin, Windows da muammoli
"Butun faylni __init__ da yuklash" Katta ma'lumotda xotira tugaydi

6. Keng tarqalgan xatolar va yechimlari

1. Validatsiyada aralashtirish

python
DataLoader(val_ds, shuffle=True)                    # ⚠️
DataLoader(val_ds, shuffle=False)                   # ✅

2. Guruhli ma'lumotda random_split

python
random_split(ds, [0.8, 0.2])                        # ⚠️ leakage
Subset(ds, tr_idx) # GroupShuffleSplit dan         # ✅

3. generator siz

python
DataLoader(ds, shuffle=True)                        # ⚠️ takrorlanmaydi
DataLoader(ds, shuffle=True, generator=g)           # ✅

4. Maskasiz o'rtacha

python
x.mean(dim=1)                                       # ⚠️ padding hisobga
(x * maska).sum(1) / maska.sum(1)                   # ✅

5. Sampler va shuffle

python
DataLoader(ds, sampler=s, shuffle=True)             # ⚠️
DataLoader(ds, sampler=s)                           # ✅

6. Ikki marta muvozanatlash

python
WeightedRandomSampler(...) + CrossEntropyLoss(weight=w)   # ⚠️
# bittasini tanlang                                        # ✅

7. Katta faylni to'liq yuklash

python
self.ma_lumot = pd.read_csv("20GB.csv")             # ⚠️
self.yollar = [...]  # __getitem__ da o'qish        # ✅

7. Integratsiya — bu bilim qayerda kerak bo'ladi

  • 20.11-dars (o'tilgan): Qo'lda batch sikli
  • 18-qism (o'tilgan): Guruhli va vaqt bo'linishi
  • 21.5-dars: Trainer
  • 24-qism: Rasm Dataset va augmentatsiya
  • 25-qism: Matn va padding

8. Eng yaxshi amaliyotlar

  1. Dataset faqat bitta namuna qaytarsin.

  2. O'quv va validatsiyaga alohida DataLoader.

  3. generator ni har doim bering.

  4. Guruhli ma'lumotda Subset + sklearn.

  5. Turli uzunlikda collate_fn + maska.

  6. Debug da num_workers=0.

  7. Muvozanatlashni bir joyda qiling.

  8. __init__ da uzunliklarni tekshiring.


9. Amaliy topshiriq

Vazifa 1: Bashorat qiling

python
1.  # Dataset qaysi ikki metodni talab qiladi?
2.  # __getitem__ nima qaytaradi?
3.  # batch kim yig'adi?
4.  # validatsiyada shuffle?
5.  # drop_last nima qiladi?
6.  # generator nima uchun?
7.  # turli uzunlik uchun nima?
8.  # maska nima uchun?
9.  # WeightedRandomSampler nima qiladi?
10. # uning xavfi?
11. # guruhli ma'lumotda bo'lish?
12. # debug da num_workers?
Javoblar
  1. __len__, __getitem__
  2. Bitta namuna
  3. DataLoader (collate_fn)
  4. Yo'q
  5. Oxirgi kichik batchni tashlaydi
  6. Takrorlanuvchanlik
  7. collate_fn
  8. Padding ni ajratish
  9. Og'irlik bo'yicha namuna oladi
  10. Kam sinfni takrorlaydi
  11. sklearn + Subset
  12. 0

Vazifa 2: Xatolarni tuzating

python
1.  DataLoader(val_ds, shuffle=True)

2.  random_split(ds, [0.8, 0.2])   # mijozlar takrorlanadi

3.  x.mean(dim=1)                   # padding bor

4.  DataLoader(ds, sampler=s, shuffle=True)

5.  DataLoader(ds, shuffle=True)    # takrorlanmaydi
Javoblar
python
1.  DataLoader(val_ds, shuffle=False)

2.  Subset(ds, tr_idx)   # GroupShuffleSplit dan

3.  (x * maska).sum(1) / maska.sum(1)

4.  DataLoader(ds, sampler=s)

5.  DataLoader(ds, shuffle=True, generator=g)

Vazifa 3: Dataset

Modellang:

  1. Shartnoma
  2. Determinizm
  3. TensorDataset
  4. Subset

Vazifa 4: DataLoader

Modellang:

  1. Tartibli
  2. drop_last
  3. shuffle
  4. generator

Vazifa 5: collate

Modellang:

  1. Uzunliklar
  2. Xato
  3. Padding
  4. Bucketing

Vazifa 6: Muvozanat

Modellang:

  1. Guruhli bo'lish
  2. Batch tarkibi
  3. Sampler
  4. Ta'sir

Vazifa 7: O'ylash

O'rgatish juda sekin: GPU 15% band, qolgan vaqt kutadi. DataLoader bilan bog'liq nimalarni tekshirasiz?

Javob

GPU kutayapti — demak u ma'lumotni kutayapti. Model tez, lekin ma'lumot sekin keladi.

1. O'lchang: vaqt qayerga ketyapti

python
import time
t_malumot, t_model = 0.0, 0.0
t0 = time.perf_counter()
for xb, yb in dl:
    t1 = time.perf_counter()
    t_malumot += t1 - t0
    xb, yb = xb.to(qurilma), yb.to(qurilma)
    loss = kriteriy(model(xb), yb); loss.backward(); opt.step()
    torch.cuda.synchronize()
    t0 = time.perf_counter()
    t_model += t0 - t1
print(f"ma'lumot {t_malumot:.1f}s, model {t_model:.1f}s")

Agar ma'lumot vaqti model vaqtidan katta bo'lsa — muammo DataLoader da.

2. Asosiy sabablar va yechimlar

Belgi Sabab Yechim
num_workers=0 Bitta jarayon o'qiydi num_workers=4..8
__getitem__ da fayl ochiladi Disk I/O Oldindan keshlash, tezroq format
Og'ir augmentatsiya CPU da Hisob Ishchilar sonini oshirish
Har davrda ishchilar qayta ishga tushadi Ishga tushirish xarajati persistent_workers=True
CPU -> GPU ko'chirish sekin Sahifalangan xotira pin_memory=True + non_blocking=True
Kichik batch Juda ko'p chaqiruv Batchni oshirish
__getitem__ da pandas iloc Sekin indekslash Numpy ga oldindan o'tkazish

3. Tipik sozlama

python
DataLoader(ds, batch_size=256, shuffle=True,
           num_workers=8, pin_memory=True,
           persistent_workers=True, prefetch_factor=4)
# o'rgatishda
xb = xb.to(qurilma, non_blocking=True)

4. __getitem__ ni profillang

python
t0 = time.perf_counter()
for i in range(1000):
    ds[i]
print((time.perf_counter() - t0) / 1000 * 1000, "ms / namuna")

1 ms / namuna va batch 256 bo'lsa — bitta batch uchun 256 ms. Bu model vaqtidan ko'p bo'lishi mumkin.

5. Eng samarali yechim — ma'lumotni oldindan tayyorlash

  • Rasmlarni kichik o'lchamga oldindan qisqartirish
  • CSV o'rniga parquet yoki numpy massivi
  • Matnni oldindan tokenlash va saqlash
  • Kichik ma'lumotni butunlay xotiraga yuklash (TensorDataset)

Muhim nuans: num_workers ni oshirish xotira sarfini ham oshiradi — har ishchi Dataset ning nusxasini oladi. Agar __init__ da katta massiv yuklangan bo'lsa, 8 ishchi = 8 nusxa.

Xulosa: avval o'lchang (ma'lumot va model vaqti), keyin num_workers va pin_memory ni sozlang, oxirida __getitem__ ni tezlashtiring.

Nimani mustahkamlaydi: 2.2, 2.6-bo'limlar.


Xulosa

Bu darsda Dataset va DataLoader ni ko'rdik.

Eng muhim uch fikr:

  1. Dataset — "i-chi namuna", DataLoader — "qanday tartibda va qancha". Bu ajratish bir xil ma'lumotni turli maqsadda ishlatish imkonini beradi: o'quv uchun aralashtirilgan va muvozanatlangan, validatsiya uchun tartibli va toza. __getitem__ bitta namuna qaytaradi, batch yig'ish esa collate_fn ning ishi.

  2. Turli uzunlikdagi ma'lumot — collate_fn va maska. Sukut collate bir xil shakl talab qiladi va turli uzunlikda RuntimeError beradi. O'z collate_fn padding qo'shadi, lekin maska ham qaytarishi shart: 3-misolda maskasiz o'rtacha qiymat haqiqiydan sezilarli farq qildi. Uzunlik bo'yicha saralash (bucketing) esa padding ulushini keskin kamaytiradi.

  3. Bo'lish va muvozanat — o'quv sifatiga to'g'ridan-to'g'ri ta'sir. Guruhli ma'lumotda random_split leakage beradi, shuning uchun indekslar sklearn bilan hisoblanib Subset ga beriladi. WeightedRandomSampler batchdagi sinflarni tenglashtiradi, lekin kam sinfni takrorlaydi va ehtimolliklarni siljitadi — uni loss og'irligi bilan birga ishlatmang.

Keyingi darsda torch.optim va jadvallar: optimizator ichi, parametr guruhlari, weight_decay ni bias dan ajratish, jadvallarni to'g'ri qadamlash va gradient clipping.

Ulashish:Telegram'da

Izohlar (0)

Izoh yozish uchun kiring.

  • Hozircha izoh yo'q. Birinchi bo'ling!
21.3-dars: Dataset va DataLoader — IlmHamroh