Mundarija (21)
- 1. Kirish va motivatsiya
- 2. Nazariya — chuqur tushuntirish
- 2.1. Konfiguratsiya
- 2.2. Takrorlanuvchanlik
- 2.3. Tajriba jurnali
- 2.4. Loyiha tuzilishi
- 2.5. Konfigdan ishga tushirish
- 2.6. Noutbuk va skript
- 2.7. Tuzoqlar
- 3. Tez ma'lumotnoma
- 4. Batafsil misollar
- Misol 1 — Konfiguratsiya obyekti
- Misol 2 — Takrorlanuvchanlik va uning chegaralari
- Misol 3 — Tajriba jurnali
- Misol 4 — Modullarga ajratilgan loyiha
- 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
21.11-dars: Loyiha tuzilishi va takrorlanuvchanlik
21-QISM — PYTORCH · 11-dars
1. Kirish va motivatsiya
Bu qismda ko'p bo'lak yozdik: Module, Dataset, Trainer, callback lar, checkpoint, embedding. Endi savol: ularni qanday tartiblash kerak, toki bir oydan keyin ham loyihani ochib, istalgan natijani qayta olish mumkin bo'lsin?
Chuqur o'rganish loyihalari tez tartibsizlikka tushadi. train_v2_final_REAL.py, noutbukda o'zgartirilgan va saqlanmagan lr, "o'tgan haftadagi eng yaxshi natija" — lekin qaysi kod bilan va qaysi sozlama bilan olingani noma'lum. Bu nafaqat noqulay, balki xavfli: siz hisobotda yozgan raqamni hech kim, hatto o'zingiz ham takrorlay olmaysiz.
Bu darsda uch narsani quramiz. Birinchisi — konfiguratsiya: tajribaning barcha sozlamalari bitta obyektda, faylga saqlanadigan va fayldan o'qiladigan. Ikkinchisi — takrorlanuvchanlik: seed_everything va uning chegaralari. Uchinchisi — tajriba jurnali: har yurish natijasi konfig bilan birga yoziladi va keyin jadval sifatida taqqoslanadi.
Oxirida loyihani modullarga ajratamiz va uni haqiqatan paket sifatida import qilib ishga tushiramiz — tuzilma qog'ozda emas, ishlayotgan kodda ko'rinsin.
Real vaziyat. Jamoa maqola uchun eng yaxshi natijani (0.912) qayta olishga urindi va ololmadi — 0.897 dan oshmadi. Uch kun izlashdan keyin topildi: noutbukda bir katakda lr qo'lda 3e-3 ga o'zgartirilgan, faylda esa 1e-3 edi. Konfig fayli natija bilan birga saqlanganida bu savol umuman tug'ilmasdi.
Bu darsda natijani istalgan vaqtda qayta olish mumkin bo'lgan loyiha tuzamiz.
Bu darsda:
- Konfiguratsiya obyekti
- Takrorlanuvchanlik va uning chegaralari
- Tajriba jurnali
- Loyiha tuzilishi
- Konfigdan ishga tushirish
- Tuzoqlar
- Amaliy: tartibli loyiha
ℹ Misollar real torch bilan (Python 3.14, torch 2.14 CPU).
2. Nazariya — chuqur tushuntirish
2.1. Konfiguratsiya
BARCHA SOZLAMALAR BIR JOYDA:
@dataclass
class Konfig:
seed: int = 42
lr: float = 3e-3
batch: int = 128
davrlar: int = 30
yashirin: int = 64
dropout: float = 0.1
weight_decay: float = 1e-4
NIMA BERADI:
kod ichida "sehrli son" yo'q
konfig JSON ga saqlanadi -> natija bilan birga
tajribalar orasidagi farq = konfiglar orasidagi farq
buyruq qatoridan o'zgartirish oson
QOIDA: noutbukda "bir martalik" o'zgartirish ham KONFIG orqaliKod ichida sozlama bo'lmasin — hamma narsa konfigda, konfig esa natija bilan birga saqlanadi.
2.2. Takrorlanuvchanlik
def seed_everything(seed):
random.seed(seed)
np.random.seed(seed)
torch.manual_seed(seed)
# GPU bo'lsa: torch.cuda.manual_seed_all(seed)
QO'SHIMCHA (to'liq determinizm, sekinroq):
torch.use_deterministic_algorithms(True)
torch.backends.cudnn.deterministic = True
torch.backends.cudnn.benchmark = False
CUBLAS_WORKSPACE_CONFIG=:4096:8 (CUDA muhit o'zgaruvchisi)
DataLoader:
generator=torch.Generator().manual_seed(seed)
CHEGARALAR:
bir xil mashina, bir xil versiya -> AYNAN bir xil
boshqa GPU / boshqa torch versiyasi -> biroz farq qilishi MUMKIN
ko'p oqimli CPU hisobi ham ba'zan farq beradi
SHUNING UCHUN: bitta seed natijasi emas, bir necha seed O'RTACHASISeed takrorlashni beradi, ishonchni emas — xulosa uchun bir necha seed kerak.
2.3. Tajriba jurnali
HAR YURISH -> BITTA YOZUV:
{"vaqt_belgisi": ..., "konfig": {...}, "natija": {...},
"git": "a1b2c3d", "versiyalar": {...}}
FORMAT: JSON Lines (har qator bitta JSON)
qo'shish oson (append)
pandas bilan o'qish: pd.read_json(yol, lines=True)
NIMA BERADI:
"qaysi sozlama eng yaxshi edi" - bitta so'rov
natijani qaysi kod versiyasi bergani - git xesh
tajribalar tarixini yo'qotmaslik
TAYYOR VOSITALAR: MLflow, Weights & Biases, TensorBoard
(29-qism) - xuddi shu g'oya, qo'shimcha interfeys bilanHar yurish jurnalga — konfig va natija bilan birga; xotiraga ishonmang.
2.4. Loyiha tuzilishi
loyiha/
konfig.py Konfig dataclass, yuklash/saqlash
malumot.py Dataset, lug'at, masshtab, DataLoader yasash
model.py nn.Module lar
orgatish.py Trainer, callback lar
utils.py seed_everything, jurnal, checkpoint
orgat.py KIRISH NUQTASI: konfig -> ma'lumot -> model -> fit
konfiglar/
asos.json
katta.json
natijalar/
jurnal.jsonl
<yurish_id>/ konfig.json, eng_yaxshi.pt, tarix.csv
QOIDALAR:
modul - bitta mas'uliyat
kirish nuqtasi - faqat yig'adi, mantiq yo'q
har yurish o'z papkasigaKirish nuqtasi faqat yig'adi — mantiq modullarda, shunda ular sinaladi va qayta ishlatiladi.
2.5. Konfigdan ishga tushirish
python orgat.py --konfig konfiglar/asos.json --lr 1e-3
1. konfig fayldan o'qiladi
2. buyruq qatori argumentlari ustiga yoziladi
3. yakuniy konfig natijalar papkasiga SAQLANADI
4. seed_everything(konfig.seed)
5. o'rgatish
6. jurnalga yozuv
NATIJA: har natija papkasida uni bergan TO'LIQ konfig bor
-> qayta ishga tushirish: python orgat.py --konfig natijalar/X/konfig.jsonYakuniy konfig natija papkasiga yoziladi — shu fayl bilan natijani istalgan vaqtda qayta olish mumkin.
2.6. Noutbuk va skript
NOUTBUK:
+ tahlil, vizualizatsiya, tez sinov
- holat yashirin (kataklar tartibi), versiyalash qiyin
SKRIPT + MODULLAR:
+ takrorlanadi, sinaladi, versiyalanadi
- interaktivlik kam
AMALIY TARTIB:
noutbukda g'oyani sinash
-> ishlagan kodni modulga ko'chirish
-> noutbuk modulni IMPORT qiladi
-> hisobotdagi raqamlar faqat SKRIPT orqali olinadiHisobotdagi har raqam skriptdan — noutbuk kashfiyot uchun, natija uchun emas.
2.7. Tuzoqlar
Asosiy tuzoqlar: sozlamalarni kod ichida yozish; noutbukda qo'lda o'zgartirilgan qiymat; konfigni natija bilan saqlamaslik; bitta seed natijasini hisobotga yozish; DataLoader generatorini seed siz qoldirish; jurnalsiz ishlash; kirish nuqtasida mantiq yozish; natijalar papkasini yurishlar orasida qayta yozish.
3. Tez ma'lumotnoma
import json
import random
from dataclasses import asdict, dataclass, replace
import numpy as np
import torch
@dataclass
class Konfig:
seed: int = 42
lr: float = 3e-3
davrlar: int = 30
def seed_everything(seed):
random.seed(seed)
np.random.seed(seed)
torch.manual_seed(seed)
def saqla(k, yol):
yol.write_text(json.dumps(asdict(k), indent=2), encoding="utf-8")
def yukla(yol):
return Konfig(**json.loads(yol.read_text(encoding="utf-8")))
def jurnalga(yol, konfig, natija):
with open(yol, "a", encoding="utf-8") as f:
f.write(json.dumps({"konfig": asdict(konfig),
"natija": natija}) + "\n")
k = replace(Konfig(), lr=1e-3) # o'zgartirilgan nusxaTuzilma xulosasi
Konfig dataclass -> JSON, natija bilan birga
seed_everything + DataLoader generator
jurnal: JSON Lines, har yurish bir qator
modullar: konfig / malumot / model / orgatish / utils
kirish nuqtasi faqat yig'adi4. Batafsil misollar
Misollar real torch bilan (Python 3.14, torch 2.14 CPU).
Misol 1 — Konfiguratsiya obyekti
"""dataclass konfig: saqlash, yuklash, o'zgartirish, tekshirish."""
import json
import shutil
import tempfile
from dataclasses import asdict, dataclass, fields, replace
from pathlib import Path
@dataclass(frozen=True)
class Konfig:
seed: int = 42
lr: float = 3e-3
batch: int = 128
davrlar: int = 30
yashirin: int = 64
dropout: float = 0.1
weight_decay: float = 1e-4
def __post_init__(self):
if not 0 <= self.dropout < 1:
raise ValueError(f"dropout [0, 1) oralig'ida: {self.dropout}")
if self.lr <= 0:
raise ValueError(f"lr musbat bo'lishi kerak: {self.lr}")
def main() -> None:
papka = Path(tempfile.mkdtemp(prefix="konfig_"))
try:
print("=== 1. Sukut konfig ===")
k = Konfig()
for f in fields(k):
print(f" {f.name:<14} {getattr(k, f.name)!r}")
print("\n=== 2. JSON ga saqlash va qaytarish ===")
yol = papka / "konfig.json"
yol.write_text(json.dumps(asdict(k), indent=2), encoding="utf-8")
print(f" fayl hajmi: {yol.stat().st_size} bayt")
k2 = Konfig(**json.loads(yol.read_text(encoding="utf-8")))
print(f" qaytarilgan konfig teng: {k == k2}")
print("\n=== 3. O'zgartirilgan nusxa ===")
katta = replace(k, yashirin=256, dropout=0.3)
farq = {f.name: (getattr(k, f.name), getattr(katta, f.name))
for f in fields(k)
if getattr(k, f.name) != getattr(katta, f.name)}
print(f" farqlar: {farq}")
print(f" asl o'zgarmadi: yashirin = {k.yashirin}")
print("\n=== 4. frozen - tasodifiy o'zgartirishdan himoya ===")
try:
k.lr = 1e-2
print(" o'zgardi (kutilmagan)")
except Exception as xato:
print(f" {type(xato).__name__}: konfig o'zgarmas")
print(" noutbukda 'bir martalik' o'zgartirish imkonsiz")
print("\n=== 5. Noto'g'ri qiymat darhol ushlanadi ===")
for kw in [{"dropout": 1.5}, {"lr": -0.01}]:
try:
Konfig(**kw)
print(f" {kw}: qabul qilindi (kutilmagan)")
except ValueError as xato:
print(f" {kw}: ValueError - {xato}")
print("\n=== 6. Noma'lum kalit ===")
try:
Konfig(**{"lr": 1e-3, "l_r": 5e-3})
print(" qabul qilindi (kutilmagan)")
except TypeError as xato:
print(f" TypeError: {str(xato)[:52]}")
print(" imlo xatosi JIM o'tib ketmaydi")
print(" ⭐ Konfig - tajribaning to'liq va tekshiriladigan ta'rifi")
finally:
shutil.rmtree(papka, ignore_errors=True)
if __name__ == "__main__":
main()Natijaning muhim qismi:
=== 1. Sukut konfig ===
seed 42
lr 0.003
batch 128
davrlar 30
yashirin 64
dropout 0.1
weight_decay 0.0001
=== 2. JSON ga saqlash va qaytarish ===
fayl hajmi: 134 bayt
qaytarilgan konfig teng: True
=== 3. O'zgartirilgan nusxa ===
farqlar: {'yashirin': (64, 256), 'dropout': (0.1, 0.3)}
asl o'zgarmadi: yashirin = 64
=== 4. frozen - tasodifiy o'zgartirishdan himoya ===
FrozenInstanceError: konfig o'zgarmas
noutbukda 'bir martalik' o'zgartirish imkonsiz
=== 5. Noto'g'ri qiymat darhol ushlanadi ===
{'dropout': 1.5}: ValueError - dropout [0, 1) oralig'ida: 1.5
{'lr': -0.01}: ValueError - lr musbat bo'lishi kerak: -0.01
=== 6. Noma'lum kalit ===
TypeError: Konfig.__init__() got an unexpected keyword argument
imlo xatosi JIM o'tib ketmaydi
⭐ Konfig - tajribaning to'liq va tekshiriladigan ta'rifiNima ko'rsatdi: 2.1-bo'lim.
Misol 2 — Takrorlanuvchanlik va uning chegaralari
"""seed_everything, generator va 'bitta seed' muammosi (real torch)."""
import random
import numpy as np
import torch
import torch.nn as nn
from torch.utils.data import DataLoader, TensorDataset
def seed_everything(seed):
random.seed(seed)
np.random.seed(seed)
torch.manual_seed(seed)
def yurish(seed, generator_bilan=True, davrlar=15):
seed_everything(seed)
X = torch.randn(800, 12)
y = (X[:, 0] + X[:, 1] * X[:, 2] + 0.5 * torch.randn(800) > 0).long()
model = nn.Sequential(nn.Linear(12, 64), nn.ReLU(), nn.Dropout(0.2),
nn.Linear(64, 2))
opt = torch.optim.AdamW(model.parameters(), lr=3e-3)
g = torch.Generator().manual_seed(seed) if generator_bilan else None
dl = DataLoader(TensorDataset(X[:600], y[:600]), batch_size=64,
shuffle=True, generator=g)
for _ in range(davrlar):
model.train()
for xb, yb in dl:
opt.zero_grad()
nn.functional.cross_entropy(model(xb), yb).backward()
opt.step()
model.eval()
with torch.no_grad():
return (model(X[600:]).argmax(1) == y[600:]).float().mean().item()
def main() -> None:
print("=== 1. Bir xil seed - aynan bir xil natija ===")
a, b = yurish(0), yurish(0)
print(f" 1-yurish: {a:.6f}")
print(f" 2-yurish: {b:.6f}")
print(f" aynan tengmi: {a == b}")
print("\n=== 2. Aralashtirish tartibi va oraliqdagi tasodif ===")
ds = TensorDataset(torch.arange(10))
def tartib(generator_bilan, oraliq_tasodif):
seed_everything(0)
g = (torch.Generator().manual_seed(0) if generator_bilan
else None)
dl = DataLoader(ds, batch_size=10, shuffle=True, generator=g)
if oraliq_tasodif:
torch.randn(3) # masalan, boshqa modul
return next(iter(dl))[0].tolist()
print(f" {'variant':<28} {'tartib'}")
for gen in [False, True]:
for oraliq in [False, True]:
nom = (f"{'generator' if gen else 'generatorsiz'}, "
f"{'oraliq tasodif' if oraliq else 'toza'}")
print(f" {nom:<28} {tartib(gen, oraliq)}")
print(" generatorsiz: oraliqdagi BIR randn tartibni o'zgartirdi")
print(" generator bilan: tartib boshqa koddan MUSTAQIL")
print("\n=== 3. Turli seed - turli natija ===")
ballar = np.array([yurish(s) for s in range(8)])
print(f" 8 seed: {ballar.round(4).tolist()}")
print(f" o'rtacha {ballar.mean():.4f}, std {ballar.std(ddof=1):.4f}")
print(f" min {ballar.min():.4f}, max {ballar.max():.4f}, "
f"oraliq {ballar.max() - ballar.min():.4f}")
print("\n=== 4. 'Eng yaxshi seed' ni tanlash - aldov ===")
eng = int(ballar.argmax())
print(f" eng yaxshi seed: {eng}, ball {ballar[eng]:.4f}")
print(f" o'rtachadan farq: {ballar[eng] - ballar.mean():+.4f}")
print(" bu 'yaxshilanish' - faqat tasodif")
print("\n=== 5. Deterministik algoritmlar ===")
torch.use_deterministic_algorithms(True)
e1, e2 = yurish(0), yurish(0)
torch.use_deterministic_algorithms(False)
print(f" use_deterministic_algorithms(True) bilan: {e1 == e2}")
print(" CPU da odatda o'zi deterministik; GPU da bu bayroq muhim")
print(" ⭐ Hisobotga: o'rtacha +- std, bir necha seed bo'yicha")
if __name__ == "__main__":
main()Natijaning muhim qismi:
=== 1. Bir xil seed - aynan bir xil natija ===
1-yurish: 0.825000
2-yurish: 0.825000
aynan tengmi: True
=== 2. Aralashtirish tartibi va oraliqdagi tasodif ===
variant tartib
generatorsiz, toza [6, 7, 1, 4, 2, 0, 9, 8, 3, 5]
generatorsiz, oraliq tasodif [7, 4, 5, 1, 9, 3, 8, 2, 0, 6]
generator, toza [3, 7, 5, 2, 0, 8, 1, 6, 9, 4]
generator, oraliq tasodif [3, 7, 5, 2, 0, 8, 1, 6, 9, 4]
generatorsiz: oraliqdagi BIR randn tartibni o'zgartirdi
generator bilan: tartib boshqa koddan MUSTAQIL
=== 3. Turli seed - turli natija ===
8 seed: [0.825, 0.83, 0.805, 0.86, 0.865, 0.76, 0.84, 0.8]
o'rtacha 0.8231, std 0.0344
min 0.7600, max 0.8650, oraliq 0.1050
=== 4. 'Eng yaxshi seed' ni tanlash - aldov ===
eng yaxshi seed: 4, ball 0.8650
o'rtachadan farq: +0.0419
bu 'yaxshilanish' - faqat tasodif
=== 5. Deterministik algoritmlar ===
use_deterministic_algorithms(True) bilan: True
CPU da odatda o'zi deterministik; GPU da bu bayroq muhim
⭐ Hisobotga: o'rtacha +- std, bir necha seed bo'yichaNima ko'rsatdi: 2.2-bo'lim.
Misol 3 — Tajriba jurnali
"""JSON Lines jurnali va tajribalarni taqqoslash (real torch)."""
import json
import random
import shutil
import tempfile
from dataclasses import asdict, dataclass, replace
from pathlib import Path
import numpy as np
import pandas as pd
import torch
import torch.nn as nn
@dataclass(frozen=True)
class Konfig:
seed: int = 0
lr: float = 3e-3
yashirin: int = 64
dropout: float = 0.1
davrlar: int = 60
def seed_everything(seed):
random.seed(seed)
np.random.seed(seed)
torch.manual_seed(seed)
def malumot():
g = torch.Generator().manual_seed(1000)
X = torch.randn(1000, 10, generator=g)
y = ((X[:, 0] * X[:, 1] + X[:, 2] + 0.5 * torch.randn(1000, generator=g))
> 0).long()
return X[:700], y[:700], X[700:], y[700:]
def orgat(k, Xtr, ytr, Xva, yva):
seed_everything(k.seed)
model = nn.Sequential(nn.Linear(10, k.yashirin), nn.ReLU(),
nn.Dropout(k.dropout), nn.Linear(k.yashirin, 2))
opt = torch.optim.AdamW(model.parameters(), lr=k.lr)
for _ in range(k.davrlar):
model.train()
opt.zero_grad()
nn.functional.cross_entropy(model(Xtr), ytr).backward()
opt.step()
model.eval()
with torch.no_grad():
return {"val_aniqlik": round(
(model(Xva).argmax(1) == yva).float().mean().item(), 4)}
def jurnalga(yol, k, natija):
with open(yol, "a", encoding="utf-8") as f:
f.write(json.dumps({"konfig": asdict(k), "natija": natija,
"torch": str(torch.__version__)}) + "\n")
def main() -> None:
papka = Path(tempfile.mkdtemp(prefix="jurnal_"))
try:
jurnal = papka / "jurnal.jsonl"
Xtr, ytr, Xva, yva = malumot()
asos = Konfig()
print("=== 1. Tajribalar ===")
variantlar = []
for yashirin in [16, 64, 256]:
for lr in [1e-3, 1e-2]:
for seed in range(3):
variantlar.append(replace(asos, yashirin=yashirin,
lr=lr, seed=seed))
for k in variantlar:
jurnalga(jurnal, k, orgat(k, Xtr, ytr, Xva, yva))
print(f" {len(variantlar)} yurish jurnalga yozildi")
print(f" jurnal qatorlari: "
f"{len(jurnal.read_text(encoding='utf-8').splitlines())}")
print("\n=== 2. Jurnalning bitta qatori ===")
birinchi = json.loads(jurnal.read_text(encoding="utf-8")
.splitlines()[0])
print(f" {json.dumps(birinchi, ensure_ascii=False)[:78]}...")
print("\n=== 3. Jadval sifatida ===")
df = pd.read_json(jurnal, lines=True)
jadval = pd.concat([pd.json_normalize(df["konfig"]),
pd.json_normalize(df["natija"])], axis=1)
print(f" shakl: {jadval.shape}")
print(f" ustunlar: {list(jadval.columns)}")
print("\n=== 4. Seedlar bo'yicha jamlash ===")
jam = (jadval.groupby(["yashirin", "lr"])["val_aniqlik"]
.agg(["mean", "std", "count"]).round(4)
.sort_values("mean", ascending=False))
print(jam.to_string())
print("\n=== 5. Eng yaxshisi va uning konfigi ===")
eng = jam.index[0]
print(f" yashirin={eng[0]}, lr={eng[1]}")
tanlangan = jadval[(jadval["yashirin"] == eng[0])
& (jadval["lr"] == eng[1])]
print(f" bu variantning yurishlari: {len(tanlangan)}")
qator = tanlangan.iloc[0]
# pandas butun sonlarni float qiladi - turini konfigdan tiklaymiz
qayta = Konfig(**{k: type(v)(qator[k])
for k, v in asdict(asos).items()})
print(f" qayta ishga tushirish uchun konfig: {qayta}")
natija2 = orgat(qayta, Xtr, ytr, Xva, yva)
print(f" jurnaldagi natija: {qator['val_aniqlik']}, "
f"qayta: {natija2['val_aniqlik']}")
print(" ⭐ Jurnal - 'qaysi sozlama yaxshi edi' ga javob")
finally:
shutil.rmtree(papka, ignore_errors=True)
if __name__ == "__main__":
main()Natijaning muhim qismi:
=== 1. Tajribalar ===
18 yurish jurnalga yozildi
jurnal qatorlari: 18
=== 2. Jurnalning bitta qatori ===
{"konfig": {"seed": 0, "lr": 0.001, "yashirin": 16, "dropout": 0.1, "davrlar":...
=== 3. Jadval sifatida ===
shakl: (18, 6)
ustunlar: ['seed', 'lr', 'yashirin', 'dropout', 'davrlar', 'val_aniqlik']
=== 4. Seedlar bo'yicha jamlash ===
mean std count
yashirin lr
16 0.010 0.8422 0.0039 3
64 0.010 0.8322 0.0084 3
256 0.010 0.8144 0.0020 3
0.001 0.7867 0.0100 3
64 0.001 0.7522 0.0139 3
16 0.001 0.6878 0.0568 3
=== 5. Eng yaxshisi va uning konfigi ===
yashirin=16, lr=0.01
bu variantning yurishlari: 3
qayta ishga tushirish uchun konfig: Konfig(seed=0, lr=0.01, yashirin=16, dropout=0.1, davrlar=60)
jurnaldagi natija: 0.84, qayta: 0.84
⭐ Jurnal - 'qaysi sozlama yaxshi edi' ga javobNima ko'rsatdi: 2.3-bo'lim.
Misol 4 — Modullarga ajratilgan loyiha
"""Loyihani papka va modullarga yozib, uni paket sifatida ishlatish."""
import importlib
import json
import shutil
import subprocess
import sys
import tempfile
from pathlib import Path
MODULLAR = {
"loyiha/__init__.py": "",
"loyiha/konfig.py": '''
import json
from dataclasses import asdict, dataclass
@dataclass(frozen=True)
class Konfig:
seed: int = 0
lr: float = 0.01
yashirin: int = 32
davrlar: int = 80
def saqla(self, yol):
yol.write_text(json.dumps(asdict(self), indent=2), encoding="utf-8")
@classmethod
def yukla(cls, yol):
return cls(**json.loads(yol.read_text(encoding="utf-8")))
''',
"loyiha/malumot.py": '''
import torch
def yasa(seed=1000):
g = torch.Generator().manual_seed(seed)
X = torch.randn(600, 8, generator=g)
y = (X[:, 0] - X[:, 1] + 0.3 * torch.randn(600, generator=g) > 0).long()
return X[:450], y[:450], X[450:], y[450:]
''',
"loyiha/model.py": '''
import torch.nn as nn
def yasa(kirish, yashirin, chiqish=2):
return nn.Sequential(nn.Linear(kirish, yashirin), nn.ReLU(),
nn.Linear(yashirin, chiqish))
''',
"loyiha/utils.py": '''
import random
import numpy as np
import torch
def seed_everything(seed):
random.seed(seed)
np.random.seed(seed)
torch.manual_seed(seed)
''',
"orgat.py": '''
import argparse
import json
import sys
from dataclasses import replace
from pathlib import Path
import torch
import torch.nn as nn
from loyiha import malumot, model
from loyiha.konfig import Konfig
from loyiha.utils import seed_everything
def main():
p = argparse.ArgumentParser()
p.add_argument("--konfig", type=Path)
p.add_argument("--lr", type=float)
p.add_argument("--chiqish", type=Path, required=True)
a = p.parse_args()
k = Konfig.yukla(a.konfig) if a.konfig else Konfig()
if a.lr is not None:
k = replace(k, lr=a.lr)
a.chiqish.mkdir(parents=True, exist_ok=True)
k.saqla(a.chiqish / "konfig.json")
seed_everything(k.seed)
Xtr, ytr, Xva, yva = malumot.yasa()
m = model.yasa(Xtr.shape[1], k.yashirin)
opt = torch.optim.AdamW(m.parameters(), lr=k.lr)
for _ in range(k.davrlar):
opt.zero_grad()
nn.functional.cross_entropy(m(Xtr), ytr).backward()
opt.step()
with torch.no_grad():
aniq = (m(Xva).argmax(1) == yva).float().mean().item()
natija = {"val_aniqlik": round(aniq, 4)}
(a.chiqish / "natija.json").write_text(json.dumps(natija))
print(json.dumps(natija))
if __name__ == "__main__":
main()
''',
}
def ishga_tushir(ildiz, *args):
r = subprocess.run([sys.executable, "orgat.py", *args], cwd=ildiz,
capture_output=True, text=True, timeout=120)
if r.returncode != 0:
raise RuntimeError(r.stderr.strip().splitlines()[-1])
return json.loads(r.stdout.strip().splitlines()[-1])
def main() -> None:
ildiz = Path(tempfile.mkdtemp(prefix="torch_loyiha_"))
try:
print("=== 1. Loyiha tuzilishi ===")
for nisbiy, matn in MODULLAR.items():
yol = ildiz / nisbiy
yol.parent.mkdir(parents=True, exist_ok=True)
yol.write_text(matn.lstrip(), encoding="utf-8")
for yol in sorted(ildiz.rglob("*.py")):
qatorlar = len(yol.read_text(encoding="utf-8").splitlines())
nisbiy = yol.relative_to(ildiz).as_posix()
print(f" {nisbiy:<22} {qatorlar:>3} qator")
print("\n=== 2. Modullar paket sifatida import qilinadi ===")
sys.path.insert(0, str(ildiz))
konfig_mod = importlib.import_module("loyiha.konfig")
print(f" Konfig sukut: {konfig_mod.Konfig()}")
sys.path.remove(str(ildiz))
print("\n=== 3. Kirish nuqtasi - buyruq qatoridan ===")
n1 = ishga_tushir(ildiz, "--chiqish", "natijalar/asos")
n2 = ishga_tushir(ildiz, "--lr", "0.001", "--chiqish",
"natijalar/kichik_lr")
print(f" asos: {n1}")
print(f" lr=0.001: {n2}")
print("\n=== 4. Har natija papkasida TO'LIQ konfig ===")
for nom in ["asos", "kichik_lr"]:
k = json.loads((ildiz / "natijalar" / nom / "konfig.json")
.read_text(encoding="utf-8"))
print(f" {nom:<10} {k}")
print("\n=== 5. Saqlangan konfigdan qayta ishga tushirish ===")
n3 = ishga_tushir(ildiz, "--konfig",
"natijalar/kichik_lr/konfig.json",
"--chiqish", "natijalar/qayta")
print(f" asl: {n2}")
print(f" qayta: {n3}")
print(f" aynan tengmi: {n2 == n3}")
print(" ⭐ Natija papkasidagi konfig - natijani qayta olish kaliti")
finally:
shutil.rmtree(ildiz, ignore_errors=True)
if __name__ == "__main__":
main()Natijaning muhim qismi:
=== 1. Loyiha tuzilishi ===
loyiha/__init__.py 0 qator
loyiha/konfig.py 17 qator
loyiha/malumot.py 8 qator
loyiha/model.py 6 qator
loyiha/utils.py 10 qator
orgat.py 42 qator
=== 2. Modullar paket sifatida import qilinadi ===
Konfig sukut: Konfig(seed=0, lr=0.01, yashirin=32, davrlar=80)
=== 3. Kirish nuqtasi - buyruq qatoridan ===
asos: {'val_aniqlik': 0.92}
lr=0.001: {'val_aniqlik': 0.86}
=== 4. Har natija papkasida TO'LIQ konfig ===
asos {'seed': 0, 'lr': 0.01, 'yashirin': 32, 'davrlar': 80}
kichik_lr {'seed': 0, 'lr': 0.001, 'yashirin': 32, 'davrlar': 80}
=== 5. Saqlangan konfigdan qayta ishga tushirish ===
asl: {'val_aniqlik': 0.86}
qayta: {'val_aniqlik': 0.86}
aynan tengmi: True
⭐ Natija papkasidagi konfig - natijani qayta olish kalitiNima ko'rsatdi: 2.4, 2.5-bo'limlar.
5. To'g'ri va noto'g'ri tushunishlar
| Noto'g'ri fikr | To'g'risi |
|---|---|
| "Sozlamani kodda yozish tezroq" | Keyin qaysi qiymat ishlatilgani noma'lum |
| "Seed qo'ysam natija ishonchli" | Takrorlanadi, lekin bitta namuna |
| "Eng yaxshi seed ni tanlash mumkin" | Bu tasodifni tanlash |
| "Jurnal ortiqcha ish" | Tajribalar tarixi yo'qolmaydi |
| "Noutbukdagi natija yetarli" | Holat yashirin, takrorlanmaydi |
| "Kirish nuqtasida mantiq qulay" | Sinab va qayta ishlatib bo'lmaydi |
| "Konfigni eslab qolaman" | Natija bilan birga faylga yozing |
| "Boshqa mashinada ham aynan teng" | Ba'zan biroz farq qiladi |
6. Keng tarqalgan xatolar va yechimlari
1. Kod ichidagi sozlama
opt = AdamW(params, lr=0.003) # ⚠️
opt = AdamW(params, lr=k.lr) # ✅2. Konfigni saqlamaslik
torch.save(model.state_dict(), "eng_yaxshi.pt") # ⚠️ qaysi sozlama?
k.saqla(papka / "konfig.json") # ✅3. Bitta seed
print("aniqlik:", yurish(seed=0)) # ⚠️
print(np.mean([yurish(s) for s in range(5)])) # ✅ + std4. Eng yaxshi seed ni tanlash
max(yurish(s) for s in range(20)) # ⚠️
# o'rtacha va std ni hisobot qiling # ✅5. Generatorsiz DataLoader
DataLoader(ds, shuffle=True) # ⚠️
DataLoader(ds, shuffle=True, generator=g) # ✅6. O'zgaruvchan konfig
k.lr = 1e-2 # noutbukda # ⚠️
k = replace(k, lr=1e-2) # frozen dataclass # ✅7. Natija papkasini qayta yozish
chiqish = "natijalar/" # ⚠️ har safar ustiga
chiqish = f"natijalar/{vaqt_belgisi}_{nom}/" # ✅7. Integratsiya — bu bilim qayerda kerak bo'ladi
- 19.7-dars (o'tilgan): sklearn da takrorlanuvchanlik
- 19.10-dars (o'tilgan): sklearn loyiha tuzilishi
- 21.5, 21.7-darslar (o'tilgan): Trainer va checkpoint
- 21.12-dars: To'liq amaliyot
- 29-qism: MLflow va tajribalarni kuzatish
8. Eng yaxshi amaliyotlar
Hamma sozlama konfigda.
Konfig
frozenva tekshiriladigan.Konfig natija papkasiga yoziladi.
seed_everything+DataLoadergeneratori.Bir necha seed, o'rtacha va std.
Har yurish jurnalga.
Mantiq modullarda, kirish nuqtasi yig'adi.
Hisobotdagi raqamlar skriptdan.
9. Amaliy topshiriq
Vazifa 1: Bashorat qiling
1. # konfig nima uchun kerak?
2. # frozen dataclass nima beradi?
3. # seed_everything nimalarni o'rnatadi?
4. # DataLoader uchun qo'shimcha nima?
5. # bir xil seed natijasi har joyda bir xilmi?
6. # hisobotga qancha seed?
7. # "eng yaxshi seed" nega aldov?
8. # jurnal formati?
9. # jurnalda nima bo'ladi?
10. # kirish nuqtasining vazifasi?
11. # natija qanday qayta olinadi?
12. # noutbuk nima uchun?Javoblar
- Hamma sozlama bir joyda va saqlanadi
- Tasodifiy o'zgartirishdan himoya
random,numpy,torchgenerator- Bir xil mashina/versiyada — ha
- Kamida 3-5
- Tasodifni tanlaydi
- JSON Lines
- Konfig, natija, versiya
- Faqat yig'ish
- Saqlangan konfig bilan
- Kashfiyot va tahlil
Vazifa 2: Xatolarni tuzating
1. opt = AdamW(params, lr=0.003)
2. torch.save(model.state_dict(), "eng_yaxshi.pt") # konfigsiz
3. print("aniqlik:", yurish(seed=0))
4. k.lr = 1e-2
5. DataLoader(ds, shuffle=True)Javoblar
1. opt = AdamW(params, lr=k.lr)
2. k.saqla(papka / "konfig.json"); torch.save(..., papka / "eng_yaxshi.pt")
3. ballar = [yurish(s) for s in range(5)]; print(np.mean(ballar), np.std(ballar))
4. k = replace(k, lr=1e-2)
5. DataLoader(ds, shuffle=True, generator=g)Vazifa 3: Konfig
Modellang:
- Sukut
- JSON
- replace
- Tekshiruv
Vazifa 4: Takrorlanuvchanlik
Modellang:
- Bir xil seed
- Generator
- Turli seed
- Eng yaxshi seed
Vazifa 5: Jurnal
Modellang:
- Yozish
- Qator
- Jadval
- Jamlash
Vazifa 6: Loyiha
Modellang:
- Tuzilma
- Import
- Buyruq qatori
- Qayta ishga tushirish
Vazifa 7: O'ylash
Maqolangizda "model 0.912 aniqlikka erishdi" deb yozilgan. Taqrizchi so'radi: "Bu natija qanchalik barqaror?" Sizda faqat bitta yurish bor. Nima qilasiz?
Javob
Qisqa javob: bitta yurish — bitta namuna. Taqrizchi haqli: 0.912 barqaror natijami yoki omadli seedmi — bilmaymiz.
1. Bir necha seed bilan qayta ishga tushiring
ballar = []
for seed in range(5):
k = replace(asl_konfig, seed=seed)
ballar.append(orgat(k)["test_aniqlik"])
print(f"{np.mean(ballar):.3f} ± {np.std(ballar, ddof=1):.3f}")Bu yerda saqlangan konfig hal qiluvchi: agar u yo'q bo'lsa, siz aynan o'sha sozlamani qayta tiklay olmaysiz — va natija 0.912 ga yaqin chiqmasa, sabab seed dami yoki sozlamadami, bilolmaysiz.
2. Natijani qanday yozish kerak
| Yomon | Yaxshi |
|---|---|
| "0.912 aniqlik" | "0.905 ± 0.006 (5 seed, o'rtacha ± std)" |
| "Eng yaxshi model 0.912" | "Eng yaxshi seed 0.912, o'rtacha 0.905" |
| Bazaviysiz | "Bazaviy HistGB: 0.896 ± 0.004" |
3. Agar o'rtacha 0.912 dan ancha past chiqsa
Bu og'riqli, lekin halol yo'l — maqoladagi raqamni tuzatish:
- o'rtacha va
stdni yozing 0.912ni "eng yaxshi yurish" sifatida qoldirish mumkin, lekin asosiy natija o'rtacha bo'lishi kerak- bazaviy bilan farq
2 × SEdan oshadimi — shuni tekshiring
4. Bazaviy ham bir necha seed bilan
Taqqoslash adolatli bo'lishi uchun bazaviy model ham xuddi shu seedlarda ishga tushiriladi. Juftlashgan farq va uning SE si hisobot qilinadi (18-qism).
5. Takrorlash ma'lumotlarini ilova qiling
- konfig fayli
- kod versiyasi (git xesh)
torch,numpyversiyalari- qurilma (CPU/GPU modeli)
- seedlar ro'yxati
Xulosa: bitta raqam — da'vo, bir necha seed bo'yicha o'rtacha va tarqoqlik — dalil. Konfig va jurnal bo'lsa, bu dalilni bir soatda tayyorlash mumkin; bo'lmasa — bir haftada ham qiyin.
Nimani mustahkamlaydi: 2.2, 2.3-bo'limlar.
Xulosa
Bu darsda loyiha tuzilishi va takrorlanuvchanlikni ko'rdik.
Eng muhim uch fikr:
Hamma sozlama — konfigda, konfig — natija bilan birga.
frozen dataclasstasodifiy o'zgartirishni imkonsiz qiladi,__post_init__noto'g'ri qiymatni darhol ushlaydi, noma'lum kalit esaTypeErrorberadi. 4-misolda har natija papkasida uni bergan to'liq konfig saqlandi va shu fayldan qayta ishga tushirilgan yurish aynan bir xil natija berdi.Seed takrorlashni beradi, ishonchni emas.
seed_everythingvaDataLoadergeneratori bilan bir xil seed bir xil mashinada aynan bir xil natija beradi. Lekin 2-misolda sakkiz seed orasidagi tarqoqlik sezilarli bo'ldi — "eng yaxshi seed" ni tanlash tasodifni tanlash degani. Hisobotga bir necha seed bo'yicha o'rtacha vastdyoziladi.Jurnal va modullar — tartibning ikki ustuni. Har yurish
JSON Linesjurnalga konfig va natija bilan yoziladi; keyinpandasbilan seedlar bo'yicha jamlab, qaysi sozlama yaxshiroq ekanini bitta so'rov bilan topish mumkin. Mantiq modullarda, kirish nuqtasi esa faqat konfigni o'qib, bo'laklarni yig'adi.
Keyingi darsda 21-qism amaliyoti: hamma bo'laklarni — Dataset, embedding li model, Trainer, callback lar, checkpoint, konfig va jurnal — bitta to'liq loyihada birlashtiramiz.
Izohlar (0)
Izoh yozish uchun kiring.
- Hozircha izoh yo'q. Birinchi bo'ling!