Mundarija (22)
- 1. Kirish va motivatsiya
- 2. Nazariya — chuqur tushuntirish
- 2.1. BERT g'oyasi: ikki tomonlama encoder
- 2.2. MLM: 15% va 80/10/10 qoidasi
- 2.3. Kirish formati: [CLS], [SEP], segment va pozitsiya
- 2.4. NSP va nega undan voz kechildi
- 2.5. MLM ni baholash: bazaviy modellar va noaniqlik
- 2.6. Kontekstli vektorlar
- 2.7. BERT qayerda ishlatiladi
- 2.8. Tuzoqlar
- 3. Tez ma'lumotnoma
- 4. Batafsil misollar
- Misol 1 — MLM maskalash noldan
- Misol 2 — Kichik BERT: pretraining va halol baholash
- Misol 3 — Juftliklar: [CLS], [SEP], segment va NSP
- Misol 4 — Kontekstli vektorlar: bir so'z, ikki ma'no
- 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
24.8-dars: BERT — maskali til modeli
24-QISM — TRANSFORMERLAR · 8-dars
1. Kirish va motivatsiya
Oldingi darsda kauzal niqob encoderni decoderga aylantirishini ko'rdik: har pozitsiya faqat o'tmishni ko'radi va keyingi tokenni bashorat qiladi. Bu generatsiya uchun zarur. Lekin ko'p NLP vazifalarida matn tayyor: sharhning tonalligini aniqlash, jumladagi ismlarni topish, savolga javobni matndan ajratib olish. Bu yerda so'zni tushunish uchun uning ikkala tomoni ham kerak. "anvar bozor__ ertalab bordi" jumlasida bo'sh joyga -ga yoki -dan kelishini chap kontekst aytmaydi — hal qiluvchi so'z (bordi) o'ngda.
Muammo: encoderni qanday o'z-o'zidan (belgisiz matnda) o'rgatish mumkin? Kauzal til modeli maqsadi ("keyingi tokenni bashorat qil") ikki tomonlama modelga to'g'ri kelmaydi — model keyingi tokenni to'g'ridan-to'g'ri ko'rib turadi (24.7-darsdagi sizish). 2018-yilda Google taklif qilgan BERT (Bidirectional Encoder Representations from Transformers) buni maskali til modeli (MLM) bilan hal qildi: tokenlarning bir qismi [MASK] bilan yashiriladi va model ularni ikki tomonlama kontekstdan tiklaydi. Bu maktabdagi "bo'sh joyni to'ldiring" mashqining o'zi (psixologiyada — Cloze testi).
Real vaziyat. Jamoa o'zbekcha matnlar uchun kichik BERT o'rgatdi va hisobotga "MLM aniqligi 0.53" deb yozdi. Rahbar: "Model har ikkinchi so'zni xato topadi — yaroqsiz." Ammo raqamni bo'lib ko'rilganda boshqa manzara chiqdi: qo'shimchalarda aniqlik 0.98, fe'llarda 0.97, ismlarda esa 0.03 — tasodif darajasida, chunki "kim bozorga bordi" degan savolga matnning o'zida javob yo'q. Eng yaxshi oddiy bazaviy model 0.28 bergan edi. Bu darsning 2-misoli aynan shu tahlilni qiladi.
Bu darsda MLM maskalashni noldan yozamiz, kichik BERT ni sintetik o'zbekcha korpusda o'rgatamiz, uni bazaviy modellar va "faqat chap kontekstli" varianti bilan halol solishtiramiz, [CLS]/[SEP]/segment embedding va NSP ni sinaymiz va o'rganilgan kontekstli vektorlarni tekshiramiz.
Bu darsda:
- BERT g'oyasi: ikki tomonlama encoder
- MLM: 15% va 80/10/10 qoidasi
- Kirish formati:
[CLS],[SEP], segment va pozitsiya - NSP va nega undan voz kechildi
- MLM ni baholash: bazaviy modellar va kamaytirib bo'lmaydigan noaniqlik
- Kontekstli vektorlar
- Tuzoqlar
ℹ Misollar real torch/numpy bilan (Python 3.14, torch 2.14 CPU).
2. Nazariya — chuqur tushuntirish
2.1. BERT g'oyasi: ikki tomonlama encoder
UCH OILA (bitta Transformer blokidan):
encoder-only (BERT) - ikki tomonlama, MLM bilan o'rgatiladi
tushunish: klassifikatsiya, teglash, qidiruv
decoder-only (GPT) - kauzal, keyingi token bilan o'rgatiladi
generatsiya
encoder-decoder (asl, T5) - kirish -> chiqish (tarjima, qisqartirish)
NEGA ODDIY TIL MODELI IKKI TOMONLAMA BO'LA OLMAYDI:
kauzal LM: nishon y_t = x_{t+1}
ikki tomonlama model x_{t+1} ni kirishda ko'radi -> shunchaki ko'chiradi
(24.7, 2-misol: loss 0.045, halol PPL 453)
ELMo (2018) ikki alohida LSTM (chap-o'ng va o'ng-chap) ni birlashtirgan -
lekin har biri bir tomonlama, "chuqur" ikki tomonlama emas
MLM YECHIMI:
nishonni kirishdan OLIB TASHLASH: x_t -> [MASK]
model x_t ni ikkala tomondagi kontekstdan tiklaydi
P(x_t | x_1 ... x_{t-1}, [MASK], x_{t+1} ... x_T)
ARXITEKTURA:
24.6-darsdagi encoder + MLM boshi (Linear d -> V)
BERT-base: 12 qatlam, d = 768, 12 bosh, 110M parametr
bizniki: 2 qatlam, d = 64, 4 bosh, ~82 ming parametrBERT = Transformer encoder + "bo'sh joyni to'ldir" maqsadi; kauzal niqob yo'q, shuning uchun har token ikkala tomonni ko'radi.
2.2. MLM: 15% va 80/10/10 qoidasi
1. TANLASH: har oddiy token 15% ehtimol bilan tanlanadi
maxsus tokenlar ([CLS], [SEP], [PAD]) HECH QACHON tanlanmaydi
2. TANLANGANLARNI ALMASHTIRISH:
80% -> [MASK]
10% -> lug'atdan tasodifiy token
10% -> o'zgarishsiz qoladi
3. LOSS: faqat tanlangan pozitsiyalarda
nishon = asl token, qolgan joylarda -100 (ignore_index)
NEGA 80/10/10, 100% [MASK] EMAS:
fine-tuning va real ishlatishda [MASK] tokeni UCHRAMAYDI
100% [MASK] bo'lsa: model faqat [MASK] ko'rgan joyda "o'ylaydi",
qolgan tokenlar vakilligiga e'tibor bermasligi mumkin
10% tasodifiy: model ko'rinib turgan tokenga ham to'liq ishonmaydi
10% o'zgarmagan: to'g'ri token ham baholanadi - vakillik real kirishga moslashadi
1-MISOL STATISTIKASI (20000 jumla):
tanlangan 0.1491; ulardan [MASK] 0.8009, tasodifiy 0.1012, o'zgarmagan 0.0979
tasodifiy token ba'zan asl tokenga teng chiqadi: 0.0084 (~1/105)
DINAMIK MASKALASH (RoBERTa):
har epoxada yangi niqob: 10 epoxada tokenlarning 0.8029 qismi
kamida bir marta nishon bo'ladi (1 - 0.85^10 = 0.8031)
statik niqob (asl BERT - ma'lumotni oldindan maskalash): doim o'sha 15%
NARXI - SIGNAL ZICHLIGI:
kauzal LM: har token nishon (bizda jumlaga 6.34 ta bashorat)
MLM: faqat 15% (0.95 ta) - bir xil hisobda kamroq signal
evaziga: har bashorat ikki tomonlama kontekst bilan
(ELECTRA kabi keyingi modellar aynan shu kamchilikni tuzatadi) MLM qoidasi: 15% tanlash → 80% [MASK], 10% tasodifiy, 10% o'zgarmagan; loss faqat tanlanganlarda — bu uchta raqamni va ularning sababini bilish kerak.
2.3. Kirish formati: [CLS], [SEP], segment va pozitsiya
BIR JUMLA: [CLS] anvar bozor ##ga ertalab bordi . [SEP]
JUFTLIK: [CLS] A jumla [SEP] B jumla [SEP]
EMBEDDING = token + pozitsiya + segment (uchalasi QO'SHILADI)
token: WordPiece lug'ati (23.3-dars): "##ga" - so'z davomi
pozitsiya: O'RGATILADIGAN embedding (24.4-darsdagi sinusoidal emas),
BERT da maksimal uzunlik 512
segment: A -> 0, B -> 1 (ikki vektorli jadval)
3-MISOLDAGI KO'RINISH:
[CLS] dilshod kutubxona ##ga bugun bordi . [SEP] u kutubxona ...
0 0 0 0 0 0 0 0 1 1 <- segment
0 1 2 3 4 5 6 7 8 9 <- pozitsiya
[CLS]:
ma'nosiz token, lekin self-attention orqali butun ketma-ketlikni ko'radi
uning oxirgi vektori - "butun kirish vektori" (NSP va klassifikatsiya uchun)
diqqat: o'rgatilmagan [CLS] vektori hali yaxshi "jumla vektori" EMAS
[SEP]: jumla chegarasi; [PAD]: attention da maskalanadi (kalit sifatida)
SEGMENT NIMA BERADI (3-misol, 4-bo'lim):
hamma segmentni 0 qilsak: MLM 0.6113 -> 0.5718, B qismida 0.6679 -> 0.6163
model segmentdan "qaysi jumlada turibman" ni biladi BERT kirishi = token + pozitsiya + segment embedding yig'indisi; [CLS] — butun ketma-ketlik uchun vektor, [SEP] — chegara.
2.4. NSP va nega undan voz kechildi
NSP (Next Sentence Prediction, asl BERT):
50%: B - A dan keyin haqiqatan kelgan jumla (IsNext = 1)
50%: B - korpusdan tasodifiy jumla (IsNext = 0)
[CLS] vektoridan ikki sinfli klassifikator; loss = MLM + NSP
MAQSAD: jumlalar orasidagi munosabatni o'rgatish (savol-javob, NLI uchun)
MUAMMO - VAZIFA JUDA OSON VA "YORLIQLI":
tasodifiy B odatda BOSHQA MAVZUDA -> mavzu/so'z o'xshashligi yetarli
3-misol: "A va B da umumiy so'z bormi" qoidasi - 0.9647 aniqlik
BERT ning NSP boshi - 0.83 atrofida (qoidadan ham past)
MLM aniqligiga ta'siri: +0.0033, SE 0.0024 - sezilarli emas
KEYINGI TADQIQOTLAR:
RoBERTa (2019): NSP ni olib tashladi, uzun ketma-ket matn bilan
natija yomonlashmadi, ba'zi vazifalarda yaxshilandi
ALBERT: NSP o'rniga SOP (jumlalar tartibi to'g'rimi) - mavzu yorlig'i
ishlamaydi, chunki ikkala jumla bir matndan
xulosa: jumlalararo munosabat MLM ning o'zidan ham o'rganiladiNSP — tarixiy qism: u vazifani mavzu o'xshashligi orqali "aldab" yechish mumkin bo'lgani uchun keyingi modellarda olib tashlangan yoki o'zgartirilgan.
2.5. MLM ni baholash: bazaviy modellar va noaniqlik
O'LCHOV: [MASK] qo'yilgan pozitsiyalarda top-1 aniqlik
"o'zgarmagan" 10% ni aralashtirmang - token ko'rinib turibdi, aniqlik oshadi
(2-misol: [MASK] da 0.5224, o'zgarmaganda 0.7021)
BAZAVIY MODELLAR (2-misol):
eng ko'p token ('.') 0.1713
unigram namuna olish 0.0412
pozitsiya bo'yicha eng ko'p 0.2823 <- eng kuchlisi
BERT (3 seed o'rtachasi) 0.5261
KAMAYTIRIB BO'LMAYDIGAN NOANIQLIK:
sintetik korpusda ism, joy, narsa TASODIFIY tanlanadi
"[MASK] bozor ##ga bordi" - qaysi ism? 28 tadan istalgani
ism aniqligi 0.0291 ~ tasodif 1/28 = 0.0357
demak umumiy aniqlikning "shipi" 1 dan ancha past
TOKEN TURI BO'YICHA (BERT va faqat chap kontekst):
qo'shimcha 0.9836 vs 0.7676 (+0.2160) - o'ngdagi fe'l hal qiladi
fe'l 0.9698 vs 0.9561 (+0.0137)
ism 0.0291 vs 0.0213 (sezilarli emas - ikkalasi tasodif)
umumiy 0.5261 vs 0.4859 (+0.0402, SE 0.0032)
XULOSA:
bitta umumiy raqam emas - token turlari bo'yicha va bazaviy bilan
ikki tomonlama kontekst foydasi aynan o'ng kontekst hal qiladigan joydaMLM aniqligini doim bazaviy model va token turlari bilan birga ko'ring; past umumiy raqam matnning o'z noaniqligidan bo'lishi mumkin.
2.6. Kontekstli vektorlar
STATIK EMBEDDING (23.6-dars, word2vec):
bitta so'z - bitta vektor
"yuz" (100) va "yuz" (bet) - AYNAN bir xil vektor
KONTEKSTLI (BERT):
h_t = f(butun jumla) - bir so'z har jumlada boshqa vektor
qaysi qatlamdan olish: odatda oxirgi yoki bir necha oxirgi qatlam
4-MISOL (ikki ma'noli so'z doim 2-o'rinda - pozitsiya yordam bermaydi):
token embedding va (token + pozitsiya): ma'nolar farqi 0.000
2-blok: 'yuz' bir ma'no ichida 0.853, ma'nolar orasida 0.759
markazga eng yaqin (o'quvdagi shablonlar): 2-blokda 1.000 va 1.000
HALOL CHEKLOV (4-misol, B ustun):
markazlar faqat 'a' shablonlaridan, test boshqa shablonlarda:
'yuz' 0.018, 'kun' 0.667 - YANGI kontekst turiga umumlashmadi
kichik korpusda "ma'no" = ko'rilgan kontekstlar to'plami
real BERT milliardlab so'zda o'rganadi - kontekstlar xilma-xil
ANIZOTROPIYA:
kontekstli vektorlar odatda bir-biriga o'xshash (kosinus 0.76-0.93)
farq kichik, lekin izchil; o'rtachani ayirish ko'pincha yordam beradiBERT vektori — so'zning o'zi emas, so'z + kontekst; lekin ma'noni qanchalik umumlashtirishi ma'lumotning xilma-xilligiga bog'liq.
2.7. BERT qayerda ishlatiladi
FINE-TUNING (Pretraining va fine-tuning darsida batafsil):
jumla klassifikatsiyasi: [CLS] vektori -> Linear -> sinf
token teglash (NER): har token vektori -> Linear -> teg
savol-javob: har token uchun "javob boshi/oxiri" ehtimoli
juftlik (NLI): [CLS] A [SEP] B [SEP] -> [CLS] -> sinf
FEATURE EXTRACTION:
BERT ni muzlatib, vektorlarni oddiy klassifikatorga berish
GENERATSIYAGA YARAMAYDI:
MLM chapdan o'ngga yozishni o'rgatmaydi - buning uchun GPT (keyingi dars)
AVLODLAR (qisqa):
RoBERTa - NSP siz, dinamik niqob, ko'p ma'lumot
ALBERT - parametrlarni ulashish, SOP
DistilBERT - distillyatsiya bilan kichraytirilgan
ELECTRA - "almashtirilgan tokenni aniqlash": har token signal beradi
ko'p tilli (mBERT, XLM-R) - o'zbek tili ham borBERT — "tushunish" uchun asos model: uni o'z vazifangizga fine-tuning qilasiz yoki vektorlarini xususiyat sifatida ishlatasiz.
2.8. Tuzoqlar
Asosiy tuzoqlar: maxsus tokenlarni ([CLS], [SEP], [PAD]) maskalash; loss ni barcha pozitsiyalarda hisoblash (-100 / ignore_index ni unutish); 80/10/10 o'rniga 100% [MASK] (pretraining va fine-tuning nomuvofiqligi); tasodifiy tokenni maxsus tokenlar orasidan tanlash; MLM aniqligini "o'zgarmagan" pozitsiyalar bilan birga hisoblab, raqamni sun'iy oshirish; bazaviy modelsiz aniqlikni talqin qilish; [PAD] ni attention da kalit sifatida maskalamaslik; [CLS] vektorini o'rgatilmagan holda "jumla vektori" deb ishlatish; statik niqob (bir marta maskalab, har epoxada qayta ishlatish); segment id larni noto'g'ri qo'yish (B jumlaning [SEP] i ham segment 1); NSP ning yuqori aniqligini "model jumlalar munosabatini tushundi" deb talqin qilish; kontekstli vektorlarni pozitsiya bilan chalkashgan tajribada tekshirish.
3. Tez ma'lumotnoma
import torch
import torch.nn.functional as F
PAD, CLS, SEP, MASK = 0, 1, 2, 3
# MLM maskalash: 15%, keyin 80/10/10
oddiy = X > MASK # maxsus tokenlar emas
tanlandi = (torch.rand(X.shape, generator=g) < 0.15) & oddiy
u = torch.rand(X.shape, generator=g)
kirish = X.clone()
kirish[tanlandi & (u < 0.8)] = MASK
tasodifiy = tanlandi & (u >= 0.8) & (u < 0.9)
kirish[tasodifiy] = torch.randint(MASK + 1, V, X.shape, generator=g)[tasodifiy]
nishon = torch.where(tanlandi, X, torch.full_like(X, -100))
# loss faqat tanlanganlarda
loss = F.cross_entropy(model(kirish).reshape(-1, V), nishon.reshape(-1),
ignore_index=-100)
# kirish: token + pozitsiya + segment
h = emb(x) + poz(torch.arange(T)) + seg(s) # s: 0 - A, 1 - B
ruxsat = (x != PAD)[:, None, None, :] # sdpa: True - ruxsat
o = F.scaled_dot_product_attention(q, k, v, attn_mask=ruxsat)
# NSP (ixtiyoriy, tarixiy)
nsp_logit = nsp_bosh(h[:, 0]) # [CLS] vektori
# baholash: faqat [MASK] qo'yilgan joylar
aniq = (model(kirish).argmax(-1) == X)[kirish == MASK].float().mean()BERT xulosasi
encoder-only, ikki tomonlama; kauzal niqob yo'q
MLM: 15% tanlash, 80% [MASK] / 10% tasodifiy / 10% o'zgarmagan
loss faqat tanlanganlarda (-100); maxsus tokenlar maskalanmaydi
kirish: token + pozitsiya + segment; [CLS] ... [SEP] ... [SEP]
NSP - oson "yorliqli" vazifa, keyingi modellarda olib tashlangan
baholash: [MASK] joylarida, bazaviy va token turlari bilan
kontekstli vektor: bir so'z - turli kontekstda turli vektor4. Batafsil misollar
Misollar real torch/numpy bilan (Python 3.14, torch 2.14 CPU).
Misol 1 — MLM maskalash noldan
"""MLM maskalash noldan: 15% tanlash, 80/10/10 qoidasi, maxsus tokenlar va dinamik niqob."""
import numpy as np
import torch
ISMLAR = ("anvar dilnoza bobur malika sardor nodira jasur zarina otabek madina "
"sherzod gulnora farhod laylo rustam sevara aziz kamola bahrom "
"shahlo temur nigora dilshod feruza jamshid mohira sanjar yulduz").split()
VAQTLAR = "ertalab kechqurun bugun kecha tushda yakshanba erta indinga".split()
JOYLAR = ("bozor maktab bog' shahar qishloq kutubxona do'kon universitet "
"choyxona dala bekat kasalxona stadion muzey teatr vokzal "
"ofis zavod").split()
NARSALAR = ("kitob olma non qalam daftar gul choy sut anor uzum ruchka gazeta "
"qovun tarvuz sabzi guruch go'sht shakar asal pishloq ko'ylak "
"do'ppi soat telefon").split()
SIFATLAR = ("katta kichik yangi eski chiroyli shirin qizil oq arzon qimmat "
"yashil sariq issiq sovuq mazali").split()
QOSHIMCHALAR = ["##ga", "##dan", "##da"]
FELLAR = "bordi qaytdi sotib oldi bor berdi olib keldi".split()
MAXSUS = ["[PAD]", "[CLS]", "[SEP]", "[MASK]"]
PAD, CLS, SEP, MASK = 0, 1, 2, 3
LUGAT = MAXSUS + sorted(set(ISMLAR + VAQTLAR + JOYLAR + NARSALAR + SIFATLAR
+ QOSHIMCHALAR + FELLAR + ["."]))
s2i = {s: i for i, s in enumerate(LUGAT)}
V = len(LUGAT)
L = 12
def jumla(rng):
t = rng.integers(0, 6)
ism, ism2 = rng.choice(ISMLAR, 2, replace=False)
vaqt, joy = str(rng.choice(VAQTLAR)), str(rng.choice(JOYLAR))
narsa, sifat = str(rng.choice(NARSALAR)), str(rng.choice(SIFATLAR))
if t == 0:
return [ism, joy, "##ga", vaqt, "bordi", "."]
if t == 1:
return [ism, joy, "##dan", vaqt, "qaytdi", "."]
if t == 2:
return [ism, sifat, narsa, "sotib", "oldi", "."]
if t == 3:
return [joy, "##da", sifat, narsa, "bor", "."]
if t == 4:
return [ism, ism2, "##ga", sifat, narsa, "berdi", "."]
return [ism, joy, "##dan", narsa, "olib", "keldi", "."]
def tensorla(jumlalar):
X = torch.full((len(jumlalar), L), PAD)
for i, j in enumerate(jumlalar):
ids = [CLS] + [s2i[str(w)] for w in j] + [SEP]
X[i, :len(ids)] = torch.tensor(ids)
return X
def maskala(X, g, p=0.15):
"""Qaytaradi: model kirishi, nishon (-100 - loss yo'q) va tanlov turi.
tur: 0 - tanlanmagan, 1 - [MASK], 2 - tasodifiy token, 3 - o'zgarmagan
"""
oddiy = X > MASK # maxsus tokenlar tanlanmaydi
tanlandi = (torch.rand(X.shape, generator=g) < p) & oddiy
u = torch.rand(X.shape, generator=g)
tur = torch.zeros_like(X)
tur[tanlandi & (u < 0.8)] = 1
tur[tanlandi & (u >= 0.8) & (u < 0.9)] = 2
tur[tanlandi & (u >= 0.9)] = 3
kirish = X.clone()
kirish[tur == 1] = MASK
tasodifiy = torch.randint(MASK + 1, V, X.shape, generator=g)
kirish[tur == 2] = tasodifiy[tur == 2]
nishon = torch.where(tanlandi, X, torch.full_like(X, -100))
return kirish, nishon, tur
def korsat(qator):
return " ".join(LUGAT[i] for i in qator if i != PAD)
def main() -> None:
rng = np.random.default_rng(0)
X = tensorla([jumla(rng) for _ in range(20000)])
g = torch.Generator().manual_seed(0)
print("=== 1. Maxsus tokenlar va kirish ===")
print(f" lug'at: {V} token, shundan maxsus: {MAXSUS}")
print(f" jumla: {korsat(X[0].tolist())}")
print(f" id lar: {X[0].tolist()}")
print(" [CLS] - boshida (butun ketma-ketlik vektori), [SEP] - jumla oxiri,"
" [PAD] - to'ldirish")
print("\n=== 2. Bitta jumlani maskalash (bir necha urinish) ===")
kichik = X[:1].repeat(4, 1)
kir, nish, tur = maskala(kichik, torch.Generator().manual_seed(3), p=0.3)
belgi = {0: "", 1: " <- [MASK]", 2: " <- tasodifiy", 3: " <- o'zgarmagan"}
for r in range(4):
print(f" {r + 1}) {korsat(kir[r].tolist())}")
for j in torch.where(tur[r] > 0)[0].tolist():
print(f" poz {j}: nishon {LUGAT[nish[r, j]]!r}{belgi[int(tur[r, j])]}")
print(" (bu yerda ko'rinishi uchun p = 0.3; BERT da p = 0.15)")
print("\n=== 3. Statistika: 20000 jumla, p = 0.15 ===")
kir, nish, tur = maskala(X, g)
oddiy = X > MASK
tanlandi = tur > 0
print(f" oddiy tokenlar: {int(oddiy.sum())}, tanlangan: {int(tanlandi.sum())} "
f"({tanlandi.sum().item() / oddiy.sum().item():.4f})")
for k, nom in [(1, "[MASK]"), (2, "tasodifiy"), (3, "o'zgarmagan")]:
ulush = (tur == k).sum().item() / tanlandi.sum().item()
print(f" {nom:<12} {ulush:.4f}")
print(f" maxsus tokenlardan tanlangani: {int((tanlandi & ~oddiy).sum())}")
teng = ((tur == 2) & (kir == X)).sum().item() / (tur == 2).sum().item()
print(f" tasodifiy token asl token bilan tasodifan teng: {teng:.4f} "
f"(kutilgan 1/{V - 4} = {1 / (V - 4):.4f})")
print(f" loss hisoblanadigan pozitsiyalar (nishon != -100): "
f"{(nish != -100).float().mean().item():.4f} barcha {L} pozitsiyadan (PAD bilan)")
print(f" kirishda asl tokeni ko'rinmaydigan (o'zgartirilgan) pozitsiyalar: "
f"{((tur == 1) | ((tur == 2) & (kir != X))).sum().item() / oddiy.sum().item():.4f}")
print("\n=== 4. Dinamik maskalash: har epoxada yangi niqob ===")
_, n1, _ = maskala(X, torch.Generator().manual_seed(10))
_, n2, _ = maskala(X, torch.Generator().manual_seed(11))
a, b = n1 != -100, n2 != -100
print(f" ikki epoxada ikkalasida ham tanlangan: {(a & b).sum().item() / a.sum().item():.4f}"
f" (kutilgan 0.15)")
epoxa = 10
korilgan = torch.zeros_like(a)
for e in range(epoxa):
_, n, _ = maskala(X, torch.Generator().manual_seed(100 + e))
korilgan |= n != -100
print(f" {epoxa} epoxada kamida bir marta nishon bo'lgan tokenlar: "
f"{korilgan.sum().item() / oddiy.sum().item():.4f} "
f"(1 - 0.85^{epoxa} = {1 - 0.85 ** epoxa:.4f})")
print(" statik niqob: har epoxada AYNAN 15% - qolgan 85% hech qachon nishon emas")
print("\n=== 5. Signal zichligi: MLM va kauzal til modeli ===")
uz = oddiy.sum(1).float().mean().item()
print(f" o'rtacha jumla: {uz:.2f} ta oddiy token")
print(f" kauzal LM: {uz:.2f} ta bashorat (har token keyingisini)")
print(f" MLM (15%): o'rtacha {0.15 * uz:.2f} ta bashorat, lekin har biri IKKI "
f"tomonlama kontekst bilan")
print(" ⭐ MLM qoidasi: 15% tanlash, 80% [MASK], 10% tasodifiy, 10% o'zgarmagan")
if __name__ == "__main__":
main()Natijaning muhim qismi:
=== 1. Maxsus tokenlar va kirish ===
lug'at: 109 token, shundan maxsus: ['[PAD]', '[CLS]', '[SEP]', '[MASK]']
jumla: [CLS] rustam bozor ##dan olma olib keldi . [SEP]
id lar: [1, 79, 20, 5, 68, 67, 50, 7, 2, 0, 0, 0]
[CLS] - boshida (butun ketma-ketlik vektori), [SEP] - jumla oxiri, [PAD] - to'ldirish
=== 2. Bitta jumlani maskalash (bir necha urinish) ===
1) [CLS] rustam [MASK] [MASK] olma olib keldi . [SEP]
poz 1: nishon 'rustam' <- o'zgarmagan
poz 2: nishon 'bozor' <- [MASK]
poz 3: nishon '##dan' <- [MASK]
poz 5: nishon 'olib' <- o'zgarmagan
2) [CLS] rustam [MASK] ##dan [MASK] olib keldi . [SEP]
poz 2: nishon 'bozor' <- [MASK]
poz 4: nishon 'olma' <- [MASK]
3) [CLS] [MASK] ##ga ##dan [MASK] olib [MASK] . [SEP]
poz 1: nishon 'rustam' <- [MASK]
poz 2: nishon 'bozor' <- tasodifiy
poz 4: nishon 'olma' <- [MASK]
poz 5: nishon 'olib' <- o'zgarmagan
poz 6: nishon 'keldi' <- [MASK]
4) [CLS] [MASK] [MASK] ##dan olma olib keldi [MASK] [SEP]
poz 1: nishon 'rustam' <- [MASK]
poz 2: nishon 'bozor' <- [MASK]
poz 7: nishon '.' <- [MASK]
(bu yerda ko'rinishi uchun p = 0.3; BERT da p = 0.15)
=== 3. Statistika: 20000 jumla, p = 0.15 ===
oddiy tokenlar: 126702, tanlangan: 18893 0.1491-bob
[MASK] 0.8009
tasodifiy 0.1012
o'zgarmagan 0.0979
maxsus tokenlardan tanlangani: 0
tasodifiy token asl token bilan tasodifan teng: 0.0084 (kutilgan 1/105 = 0.0095)
loss hisoblanadigan pozitsiyalar (nishon != -100): 0.0787 barcha 12 pozitsiyadan (PAD bilan)
kirishda asl tokeni ko'rinmaydigan (o'zgartirilgan) pozitsiyalar: 0.1344
=== 4. Dinamik maskalash: har epoxada yangi niqob ===
ikki epoxada ikkalasida ham tanlangan: 0.1494 (kutilgan 0.15)
10 epoxada kamida bir marta nishon bo'lgan tokenlar: 0.8029 (1 - 0.85^10 = 0.8031)
statik niqob: har epoxada AYNAN 15% - qolgan 85% hech qachon nishon emas
=== 5. Signal zichligi: MLM va kauzal til modeli ===
o'rtacha jumla: 6.34 ta oddiy token
kauzal LM: 6.34 ta bashorat (har token keyingisini)
MLM (15%): o'rtacha 0.95 ta bashorat, lekin har biri IKKI tomonlama kontekst bilan
⭐ MLM qoidasi: 15% tanlash, 80% [MASK], 10% tasodifiy, 10% o'zgarmaganNima ko'rsatdi: korpus WordPiece uslubida tokenlangan: bozor ##dan — so'z va uning davomi alohida tokenlar, lug'at 109 token (4 maxsus). Bir jumlani to'rt marta maskalaganda har safar boshqa pozitsiyalar tanlanadi va ular uch xil taqdirga uchraydi: [MASK], tasodifiy token (3-urinishda bozor o'rniga ##ga), yoki o'zgarishsiz qoladi — lekin baribir nishon. 20000 jumlada tanlangan ulush 0.1491, ulardan 0.8009 / 0.1012 / 0.0979 — 80/10/10 ga mos; maxsus tokenlardan birortasi ham tanlanmadi. Tasodifiy token 0.0084 holatda asl token bilan tasodifan teng chiqdi (nazariy 1/105 = 0.0095) — bu zararsiz. Kirishda asl tokeni yashiringan pozitsiyalar oddiy tokenlarning atigi 0.1344 qismi. Dinamik maskalashda ikki epoxa niqoblarining kesishmasi 0.1494 — mustaqil; 10 epoxada tokenlarning 0.8029 qismi kamida bir marta nishon bo'ldi (formula 0.8031). Signal zichligi: jumlada o'rtacha 6.34 oddiy token — kauzal LM shuncha bashorat qiladi, MLM esa atigi 0.95 ta. Bog'liq bo'limlar: 2.2, 2.3.
Misol 2 — Kichik BERT: pretraining va halol baholash
"""Kichik BERT: MLM pretraining, bazaviy modellar va faqat chap kontekstli variant."""
import math
from collections import Counter
import numpy as np
import torch
import torch.nn as nn
import torch.nn.functional as F
ISMLAR = ("anvar dilnoza bobur malika sardor nodira jasur zarina otabek madina "
"sherzod gulnora farhod laylo rustam sevara aziz kamola bahrom "
"shahlo temur nigora dilshod feruza jamshid mohira sanjar yulduz").split()
VAQTLAR = "ertalab kechqurun bugun kecha tushda yakshanba erta indinga".split()
JOYLAR = ("bozor maktab bog' shahar qishloq kutubxona do'kon universitet "
"choyxona dala bekat kasalxona stadion muzey teatr vokzal "
"ofis zavod").split()
NARSALAR = ("kitob olma non qalam daftar gul choy sut anor uzum ruchka gazeta "
"qovun tarvuz sabzi guruch go'sht shakar asal pishloq ko'ylak "
"do'ppi soat telefon").split()
SIFATLAR = ("katta kichik yangi eski chiroyli shirin qizil oq arzon qimmat "
"yashil sariq issiq sovuq mazali").split()
QOSHIMCHALAR = ["##ga", "##dan", "##da"]
FELLAR = "bordi qaytdi sotib oldi bor berdi olib keldi".split()
MAXSUS = ["[PAD]", "[CLS]", "[SEP]", "[MASK]"]
PAD, CLS, SEP, MASK = 0, 1, 2, 3
LUGAT = MAXSUS + sorted(set(ISMLAR + VAQTLAR + JOYLAR + NARSALAR + SIFATLAR
+ QOSHIMCHALAR + FELLAR + ["."]))
s2i = {s: i for i, s in enumerate(LUGAT)}
V = len(LUGAT)
L = 12
def jumla(rng):
"""So'z + WordPiece uslubidagi qo'shimcha (##ga, ##dan, ##da) tokenlari."""
t = rng.integers(0, 6)
ism, ism2 = rng.choice(ISMLAR, 2, replace=False)
vaqt, joy = str(rng.choice(VAQTLAR)), str(rng.choice(JOYLAR))
narsa, sifat = str(rng.choice(NARSALAR)), str(rng.choice(SIFATLAR))
if t == 0:
return [ism, joy, "##ga", vaqt, "bordi", "."]
if t == 1:
return [ism, joy, "##dan", vaqt, "qaytdi", "."]
if t == 2:
return [ism, sifat, narsa, "sotib", "oldi", "."]
if t == 3:
return [joy, "##da", sifat, narsa, "bor", "."]
if t == 4:
return [ism, ism2, "##ga", sifat, narsa, "berdi", "."]
return [ism, joy, "##dan", narsa, "olib", "keldi", "."]
def tensorla(jumlalar):
X = torch.full((len(jumlalar), L), PAD)
for i, j in enumerate(jumlalar):
ids = [CLS] + [s2i[str(w)] for w in j] + [SEP]
X[i, :len(ids)] = torch.tensor(ids)
return X
def maskala(X, g, p=0.15):
"""BERT qoidasi: 15% tanlanadi; ulardan 80% [MASK], 10% tasodifiy, 10% o'zgarmaydi."""
oddiy = X > MASK
tanlandi = (torch.rand(X.shape, generator=g) < p) & oddiy
u = torch.rand(X.shape, generator=g)
kirish = X.clone()
kirish[tanlandi & (u < 0.8)] = MASK
tasodifiy = tanlandi & (u >= 0.8) & (u < 0.9)
kirish[tasodifiy] = torch.randint(MASK + 1, V, X.shape, generator=g)[tasodifiy]
nishon = torch.where(tanlandi, X, torch.full_like(X, -100))
return kirish, nishon, tanlandi & (u < 0.8)
class Blok(nn.Module):
def __init__(self, d, boshlar):
super().__init__()
self.boshlar = boshlar
self.ln1, self.ln2 = nn.LayerNorm(d), nn.LayerNorm(d)
self.qkv = nn.Linear(d, 3 * d)
self.proj = nn.Linear(d, d)
self.ffn = nn.Sequential(nn.Linear(d, 2 * d), nn.GELU(), nn.Linear(2 * d, d))
def forward(self, x, ruxsat):
B, T, d = x.shape
q, k, v = self.qkv(self.ln1(x)).split(d, dim=2)
q, k, v = [t.view(B, T, self.boshlar, -1).transpose(1, 2) for t in (q, k, v)]
o = F.scaled_dot_product_attention(q, k, v, attn_mask=ruxsat) # True - ruxsat
x = x + self.proj(o.transpose(1, 2).reshape(B, T, d))
return x + self.ffn(self.ln2(x))
class KichikBERT(nn.Module):
"""faqat_chap=True - xuddi shu model, lekin kauzal niqob bilan (faqat chap kontekst)."""
def __init__(self, faqat_chap=False, d=64, boshlar=4, qatlamlar=2):
super().__init__()
self.faqat_chap = faqat_chap
self.emb = nn.Embedding(V, d)
self.poz = nn.Embedding(L, d)
self.bloklar = nn.ModuleList(Blok(d, boshlar) for _ in range(qatlamlar))
self.ln = nn.LayerNorm(d)
self.mlm = nn.Linear(d, V)
def forward(self, x):
ruxsat = (x != PAD)[:, None, None, :] # pad kalitlarga qaralmaydi
if self.faqat_chap:
ruxsat = ruxsat & torch.ones(L, L, dtype=torch.bool).tril()
h = self.emb(x) + self.poz(torch.arange(x.shape[1]))
for b in self.bloklar:
h = b(h, ruxsat)
return self.mlm(self.ln(h))
def orgat(X, faqat_chap, seed, qadamlar=300, B=64):
torch.manual_seed(seed)
model = KichikBERT(faqat_chap)
opt = torch.optim.AdamW(model.parameters(), lr=3e-3, weight_decay=0.01)
g = torch.Generator().manual_seed(seed)
for _ in range(qadamlar):
idx = torch.randint(0, len(X), (B,), generator=g)
kirish, nishon, _ = maskala(X[idx], g)
loss = F.cross_entropy(model(kirish).reshape(-1, V), nishon.reshape(-1),
ignore_index=-100)
opt.zero_grad()
loss.backward()
nn.utils.clip_grad_norm_(model.parameters(), 1.0)
opt.step()
model.eval()
return model
def turi(i):
s = LUGAT[i]
if s in QOSHIMCHALAR:
return "qo'shimcha"
if s in ISMLAR:
return "ism"
if s in FELLAR:
return "fe'l"
return "boshqa"
def main() -> None:
torch.set_num_threads(1)
rng = np.random.default_rng(0)
X = tensorla([jumla(rng) for _ in range(6000)])
Xv = tensorla([jumla(rng) for _ in range(1500)])
kirish_v, nishon_v, mask_v = maskala(Xv, torch.Generator().manual_seed(123))
tanlangan = nishon_v != -100
y = Xv[mask_v] # baholash: faqat [MASK] qo'yilgan joylar
print("=== 1. Ma'lumot ===")
print(f" lug'at V = {V} (4 maxsus token), o'quv 6000, val 1500 jumla")
print(f" misol: {' '.join(LUGAT[i] for i in X[0].tolist() if i != PAD)}")
print(f" val: tanlangan {int(tanlangan.sum())} pozitsiya, ulardan [MASK] - "
f"{int(mask_v.sum())} (baholash shularda)")
print("\n=== 2. Bazaviy modellar (val MLM aniqligi) ===")
sanoq = Counter(X[X > MASK].tolist())
eng_kop = max(sanoq, key=sanoq.get)
jami = sum(sanoq.values())
unigram = sum(sanoq[t] / jami for t in y.tolist()) / len(y)
poz = {}
for p in range(L):
c = Counter(X[:, p][X[:, p] > MASK].tolist())
poz[p] = max(c, key=c.get) if c else PAD
poz_bash = torch.tensor([poz[p] for p in torch.where(mask_v)[1].tolist()])
bazaviy = {
f"eng ko'p token ({LUGAT[eng_kop]!r})": (y == eng_kop).float().mean().item(),
"unigram (namuna olish)": unigram,
"pozitsiya bo'yicha eng ko'p": (poz_bash == y).float().mean().item(),
}
for nom, a in bazaviy.items():
print(f" {nom:<30} {a:.4f}")
print("\n=== 3. BERT (ikki tomonlama) va faqat chap kontekst, 3 seed ===")
natija = {False: [], True: []}
tur = np.array([turi(i) for i in y.tolist()])
for s in range(3):
for faqat_chap in (False, True):
m = orgat(X, faqat_chap, s)
with torch.no_grad():
bash = m(kirish_v)[mask_v].argmax(1)
togri = (bash == y).numpy()
natija[faqat_chap].append({t: togri[tur == t].mean() for t in
["qo'shimcha", "ism", "fe'l", "boshqa"]}
| {"hammasi": togri.mean()})
if s == 0 and not faqat_chap:
bert0 = m
n_par = sum(p.numel() for p in bert0.parameters())
print(f" parametrlar: {n_par}, 300 qadam, batch 64")
print(f" {'token turi':<11} {'ulush':>6} {'BERT':>7} {'chap':>7} {'farq':>8} "
f"{'SE':>7} {'sezilarli':>10}")
for t in ["hammasi", "qo'shimcha", "fe'l", "ism", "boshqa"]:
a = np.array([r[t] for r in natija[False]])
b = np.array([r[t] for r in natija[True]])
farq = a - b
se = farq.std(ddof=1) / math.sqrt(len(farq))
ulush = 1.0 if t == "hammasi" else (tur == t).mean()
print(f" {t:<11} {ulush:>6.1%} {a.mean():>7.4f} {b.mean():>7.4f} "
f"{farq.mean():>+8.4f} {se:>7.4f} {str(abs(farq.mean()) > 2 * se):>10}")
print(f" ism uchun tasodif darajasi: 1/{len(ISMLAR)} = {1 / len(ISMLAR):.4f}")
print("\n=== 4. 80/10/10: tanlangan pozitsiya turi bo'yicha (BERT, seed 0) ===")
with torch.no_grad():
bash_hammasi = bert0(kirish_v).argmax(2)
for nom, joy in [("[MASK] qo'yilgan", mask_v),
("tasodifiy token", tanlangan & ~mask_v & (kirish_v != Xv)),
("o'zgarmagan", tanlangan & ~mask_v & (kirish_v == Xv))]:
a = (bash_hammasi[joy] == Xv[joy]).float().mean().item()
print(f" {nom:<17} {int(joy.sum()):>5} ta aniqlik {a:.4f}")
oddiy = (Xv > MASK) & ~tanlangan
a = (bash_hammasi[oddiy] == Xv[oddiy]).float().mean().item()
print(f" {'tanlanmagan':<17} {int(oddiy.sum()):>5} ta aniqlik {a:.4f} (loss yo'q)")
print("\n=== 5. [MASK] ni to'ldirish (BERT, seed 0) ===")
for gap in [["anvar", "bozor", "[MASK]", "ertalab", "bordi", "."],
["anvar", "bozor", "[MASK]", "ertalab", "qaytdi", "."],
["bog'", "[MASK]", "katta", "olma", "bor", "."]]:
x = torch.full((1, L), PAD)
ids = [CLS] + [s2i[w] for w in gap] + [SEP]
x[0, :len(ids)] = torch.tensor(ids)
j = ids.index(MASK)
with torch.no_grad():
p = torch.softmax(bert0(x)[0, j], 0)
top = p.topk(3)
print(f" {' '.join(gap):<36} -> " + ", ".join(
f"{LUGAT[i]} {v:.3f}" for v, i in zip(top.values.tolist(), top.indices.tolist())))
print(" ⭐ MLM: ikki tomonlama kontekst o'ngdagi fe'lga qarab qo'shimchani tanlaydi")
if __name__ == "__main__":
main()Natijaning muhim qismi:
=== 1. Ma'lumot ===
lug'at V = 109 (4 maxsus token), o'quv 6000, val 1500 jumla
misol: [CLS] rustam bozor ##dan olma olib keldi . [SEP]
val: tanlangan 1444 pozitsiya, ulardan [MASK] - 1162 (baholash shularda)
=== 2. Bazaviy modellar (val MLM aniqligi) ===
eng ko'p token ('.') 0.1713
unigram (namuna olish) 0.0412
pozitsiya bo'yicha eng ko'p 0.2823
=== 3. BERT (ikki tomonlama) va faqat chap kontekst, 3 seed ===
parametrlar: 81901, 300 qadam, batch 64
token turi ulush BERT chap farq SE sezilarli
hammasi 100.0% 0.5261 0.4859 +0.0402 0.0032 True
qo'shimcha 12.2% 0.9836 0.7676 +0.2160 0.0300 True
fe'l 20.9% 0.9698 0.9561 +0.0137 0.0036 True
ism 14.8% 0.0291 0.0213 +0.0078 0.0084 False
boshqa 52.1% 0.3818 0.3631 +0.0187 0.0024 True
ism uchun tasodif darajasi: 1/28 = 0.0357
=== 4. 80/10/10: tanlangan pozitsiya turi bo'yicha (BERT, seed 0) ===
[MASK] qo'yilgan 1162 ta aniqlik 0.5224
tasodifiy token 141 ta aniqlik 0.5461
o'zgarmagan 141 ta aniqlik 0.7021
tanlanmagan 8095 ta aniqlik 0.6566 (loss yo'q)
=== 5. [MASK] ni to'ldirish (BERT, seed 0) ===
anvar bozor [MASK] ertalab bordi . -> ##ga 0.999, ##dan 0.001, . 0.000
anvar bozor [MASK] ertalab qaytdi . -> ##dan 0.996, ##ga 0.001, sotib 0.000
bog' [MASK] katta olma bor . -> ##da 0.992, . 0.002, mazali 0.001
⭐ MLM: ikki tomonlama kontekst o'ngdagi fe'lga qarab qo'shimchani tanlaydiNima ko'rsatdi: val da 1444 pozitsiya tanlangan, baholash faqat [MASK] qo'yilgan 1162 tasida. Eng kuchli bazaviy model — "shu pozitsiyada eng ko'p uchraydigan token" (0.2823); BERT uch seed da o'rtacha 0.5261. Xuddi shu model faqat chap kontekst bilan (kauzal niqob, boshqa hamma narsa bir xil) — 0.4859; juftlashgan farq +0.0402, SE 0.0032 — sezilarli. Farq qayerdan kelishini token turlari ko'rsatadi: qo'shimchalarda 0.9836 va 0.7676 (+0.2160) — ##ga yoki ##dan ni o'ngdagi bordi/qaytdi hal qiladi, chap kontekstli model esa faqat taxmin qila oladi. Ismlarda ikkalasi ham tasodif darajasida (0.0291 va 0.0213, tasodif 0.0357) — bu matnning o'z noaniqligi, farq sezilarli emas. 4-bo'lim 80/10/10 ning ta'sirini ko'rsatadi: [MASK] joylarida 0.5224, tasodifiy token joylarida 0.5461 (model ko'rinib turgan "begona" tokenga ishonmaydi), o'zgarmagan joylarda 0.7021. Tanlanmagan 8095 pozitsiyada chiqish asl tokenga atigi 0.6566 holatda teng — MLM boshi bu joylarda o'rgatilmaydi, shuning uchun u "nusxa ko'chiruvchi" emas. 5-bo'limda model bordi ni ko'rib ##ga ga 0.999, qaytdi ni ko'rib ##dan ga 0.996 ehtimol berdi. Bog'liq bo'limlar: 2.1, 2.5.
Misol 3 — Juftliklar: [CLS], [SEP], segment va NSP
"""Jumla juftliklari: [CLS] A [SEP] B [SEP], segment embedding va NSP."""
import math
import numpy as np
import torch
import torch.nn as nn
import torch.nn.functional as F
ISMLAR = ("anvar dilnoza bobur malika sardor nodira jasur zarina otabek madina "
"sherzod gulnora farhod laylo rustam sevara aziz kamola bahrom "
"shahlo temur nigora dilshod feruza jamshid mohira sanjar yulduz").split()
VAQTLAR = "ertalab kechqurun bugun kecha tushda yakshanba erta indinga".split()
JOYLAR = ("bozor maktab bog' shahar qishloq kutubxona do'kon universitet "
"choyxona dala bekat kasalxona stadion muzey teatr vokzal "
"ofis zavod").split()
NARSALAR = ("kitob olma non qalam daftar gul choy sut anor uzum ruchka gazeta "
"qovun tarvuz sabzi guruch go'sht shakar asal pishloq ko'ylak "
"do'ppi soat telefon").split()
SIFATLAR = ("katta kichik yangi eski chiroyli shirin qizil oq arzon qimmat "
"yashil sariq issiq sovuq mazali").split()
BOSHQA = "##ga ##dan ##da ##ni u bordi qaytdi sotib oldi berdi olib keldi yedi .".split()
MAXSUS = ["[PAD]", "[CLS]", "[SEP]", "[MASK]"]
PAD, CLS, SEP, MASK = 0, 1, 2, 3
LUGAT = MAXSUS + sorted(set(ISMLAR + VAQTLAR + JOYLAR + NARSALAR + SIFATLAR + BOSHQA))
s2i = {s: i for i, s in enumerate(LUGAT)}
V = len(LUGAT)
L = 20
def hikoya(rng):
"""Ikki bog'liq jumla: B jumla A dagi joy yoki narsani takrorlaydi."""
ism, ism2 = (str(s) for s in rng.choice(ISMLAR, 2, replace=False))
vaqt, joy = str(rng.choice(VAQTLAR)), str(rng.choice(JOYLAR))
narsa, sifat = str(rng.choice(NARSALAR)), str(rng.choice(SIFATLAR))
t = rng.integers(0, 3)
if t == 0:
return ([ism, joy, "##ga", vaqt, "bordi", "."],
["u", joy, "##da", sifat, narsa, "sotib", "oldi", "."])
if t == 1:
return ([ism, joy, "##dan", narsa, "olib", "keldi", "."],
["u", narsa, "##ni", ism2, "##ga", "berdi", "."])
return ([ism, sifat, narsa, "sotib", "oldi", "."],
["u", vaqt, narsa, "##ni", "yedi", "."])
def juftliklar(n, rng):
"""50% - haqiqiy davom (IsNext=1), 50% - boshqa hikoyadan tasodifiy B (0)."""
hik = [hikoya(rng) for _ in range(n)]
A, B, y = [], [], []
for i, (a, b) in enumerate(hik):
A.append(a)
if rng.random() < 0.5:
B.append(b)
y.append(1)
else:
j = int(rng.integers(0, n - 1))
j += j >= i # o'zidan boshqa hikoya
B.append(hik[j][1])
y.append(0)
return A, B, torch.tensor(y)
def tensorla(A, B):
X = torch.full((len(A), L), PAD)
S = torch.zeros((len(A), L), dtype=torch.long)
for i, (a, b) in enumerate(zip(A, B)):
ids = [CLS] + [s2i[w] for w in a] + [SEP] + [s2i[w] for w in b] + [SEP]
X[i, :len(ids)] = torch.tensor(ids)
S[i, len(a) + 2:len(ids)] = 1 # B jumla va uning [SEP] i
return X, S
def maskala(X, g, p=0.15):
oddiy = X > MASK
tanlandi = (torch.rand(X.shape, generator=g) < p) & oddiy
u = torch.rand(X.shape, generator=g)
kirish = X.clone()
kirish[tanlandi & (u < 0.8)] = MASK
tasodifiy = tanlandi & (u >= 0.8) & (u < 0.9)
kirish[tasodifiy] = torch.randint(MASK + 1, V, X.shape, generator=g)[tasodifiy]
return kirish, torch.where(tanlandi, X, torch.full_like(X, -100)), tanlandi & (u < 0.8)
class Blok(nn.Module):
def __init__(self, d, boshlar):
super().__init__()
self.boshlar = boshlar
self.ln1, self.ln2 = nn.LayerNorm(d), nn.LayerNorm(d)
self.qkv = nn.Linear(d, 3 * d)
self.proj = nn.Linear(d, d)
self.ffn = nn.Sequential(nn.Linear(d, 2 * d), nn.GELU(), nn.Linear(2 * d, d))
def forward(self, x, ruxsat):
B, T, d = x.shape
q, k, v = self.qkv(self.ln1(x)).split(d, dim=2)
q, k, v = [t.view(B, T, self.boshlar, -1).transpose(1, 2) for t in (q, k, v)]
o = F.scaled_dot_product_attention(q, k, v, attn_mask=ruxsat)
x = x + self.proj(o.transpose(1, 2).reshape(B, T, d))
return x + self.ffn(self.ln2(x))
class BERT(nn.Module):
def __init__(self, d=64, boshlar=4, qatlamlar=2):
super().__init__()
self.emb = nn.Embedding(V, d)
self.poz = nn.Embedding(L, d)
self.seg = nn.Embedding(2, d) # A = 0, B = 1
self.bloklar = nn.ModuleList(Blok(d, boshlar) for _ in range(qatlamlar))
self.ln = nn.LayerNorm(d)
self.mlm = nn.Linear(d, V)
self.nsp = nn.Linear(d, 2) # [CLS] vektoridan
def forward(self, x, s):
ruxsat = (x != PAD)[:, None, None, :]
h = self.emb(x) + self.poz(torch.arange(x.shape[1])) + self.seg(s)
for b in self.bloklar:
h = b(h, ruxsat)
h = self.ln(h)
return self.mlm(h), self.nsp(h[:, 0])
def orgat(X, S, Y, nsp_bilan, seed, qadamlar=200, B=64):
torch.manual_seed(seed)
model = BERT()
opt = torch.optim.AdamW(model.parameters(), lr=3e-3, weight_decay=0.01)
g = torch.Generator().manual_seed(seed)
for _ in range(qadamlar):
idx = torch.randint(0, len(X), (B,), generator=g)
kirish, nishon, _ = maskala(X[idx], g)
mlm, nsp = model(kirish, S[idx])
loss = F.cross_entropy(mlm.reshape(-1, V), nishon.reshape(-1), ignore_index=-100)
if nsp_bilan:
loss = loss + F.cross_entropy(nsp, Y[idx])
opt.zero_grad()
loss.backward()
nn.utils.clip_grad_norm_(model.parameters(), 1.0)
opt.step()
model.eval()
return model
def main() -> None:
torch.set_num_threads(1)
rng = np.random.default_rng(0)
A, B, Y = juftliklar(6000, rng)
Av, Bv, Yv = juftliklar(1500, rng)
X, S = tensorla(A, B)
Xv, Sv = tensorla(Av, Bv)
print("=== 1. Kirish formati: tokenlar, segmentlar, pozitsiyalar ===")
ids = [i for i in X[0].tolist() if i != PAD]
print(" " + " ".join(f"{LUGAT[i]:>7}" for i in ids[:10]))
print(" " + " ".join(f"{s:>7}" for s in S[0, :10].tolist()) + " <- segment")
print(" " + " ".join(f"{p:>7}" for p in range(10)) + " <- pozitsiya")
print(f" IsNext = {int(Y[0])}; o'quvda IsNext ulushi {Y.float().mean().item():.3f}")
print(f" embedding = token + pozitsiya + segment; V = {V}, L = {L}")
print("\n=== 2. NSP uchun 'yorliq' qoida: A va B da umumiy so'z bormi ===")
xizmat = {"u", ".", "##ga", "##dan", "##da", "##ni", "sotib", "oldi"}
def umumiy(a, b):
return int(len((set(a) - xizmat) & (set(b) - xizmat)) > 0)
qoida = torch.tensor([umumiy(a, b) for a, b in zip(Av, Bv)])
print(f" qoida aniqligi (val): {(qoida == Yv).float().mean().item():.4f}")
print(f" tasodifiy B da umumiy so'z: {qoida[Yv == 0].float().mean().item():.4f}")
print("\n=== 3. MLM + NSP va faqat MLM (200 qadam, 3 seed) ===")
kirish_v, _, mask_v = maskala(Xv, torch.Generator().manual_seed(7))
natija = {True: [], False: []}
nsp_aniq = []
for s in range(3):
for nsp_bilan in (True, False):
m = orgat(X, S, Y, nsp_bilan, s)
with torch.no_grad():
mlm, nsp = m(kirish_v, Sv)
natija[nsp_bilan].append((mlm.argmax(2) == Xv)[mask_v].float().mean().item())
if nsp_bilan:
nsp_aniq.append((nsp.argmax(1) == Yv).float().mean().item())
if s == 0:
model0 = m
print(" NSP aniqligi ([CLS] boshi): " + ", ".join(f"{a:.4f}" for a in nsp_aniq))
for nsp_bilan, nom in [(True, "MLM + NSP"), (False, "faqat MLM")]:
print(f" {nom:<10} MLM aniqligi: "
+ ", ".join(f"{a:.4f}" for a in natija[nsp_bilan])
+ f" o'rtacha {np.mean(natija[nsp_bilan]):.4f}")
farq = np.array(natija[True]) - np.array(natija[False])
se = farq.std(ddof=1) / math.sqrt(len(farq))
xulosa = ("NSP MLM ni sezilarli yaxshiladi" if farq.mean() > 2 * se else
"NSP MLM ni sezilarli yomonlashtirdi" if farq.mean() < -2 * se else
"MLM aniqligida sezilarli farq yo'q")
print(f" MLM farqi (NSP bilan - NSP siz): {farq.mean():+.4f}, SE {se:.4f} -> {xulosa}")
if np.mean(nsp_aniq) < (qoida == Yv).float().mean().item():
print(" NSP boshi oddiy 'umumiy so'z' qoidasidan ham past")
print("\n=== 4. Segment embedding nima beradi (MLM + NSP, seed 0) ===")
with torch.no_grad():
mlm0, nsp0 = model0(kirish_v, Sv)
mlm1, nsp1 = model0(kirish_v, torch.zeros_like(Sv))
for nom, mlm, nsp in [("to'g'ri segmentlar", mlm0, nsp0),
("hammasi segment 0", mlm1, nsp1)]:
a = (mlm.argmax(2) == Xv)[mask_v]
b_qism = a[(Sv == 1)[mask_v]].float().mean().item()
print(f" {nom:<19} MLM {a.float().mean().item():.4f} (B qismida {b_qism:.4f}),"
f" NSP {(nsp.argmax(1) == Yv).float().mean().item():.4f}")
print(f" segment vektorlari orasidagi kosinus: "
f"{F.cosine_similarity(model0.seg.weight[0], model0.seg.weight[1], 0).item():.3f}")
print(" ⭐ [CLS] - butun juftlik vektori; segment - qaysi jumlaga tegishlilik belgisi")
if __name__ == "__main__":
main()Natijaning muhim qismi:
=== 1. Kirish formati: tokenlar, segmentlar, pozitsiyalar ===
[CLS] dilshod kutubxona ##ga bugun bordi . [SEP] u kutubxona
0 0 0 0 0 0 0 0 1 1 <- segment
0 1 2 3 4 5 6 7 8 9 <- pozitsiya
IsNext = 1; o'quvda IsNext ulushi 0.502
embedding = token + pozitsiya + segment; V = 111, L = 20
=== 2. NSP uchun 'yorliq' qoida: A va B da umumiy so'z bormi ===
qoida aniqligi (val): 0.9647
tasodifiy B da umumiy so'z: 0.0707
=== 3. MLM + NSP va faqat MLM (200 qadam, 3 seed) ===
NSP aniqligi ([CLS] boshi): 0.8367, 0.8287, 0.8307
MLM + NSP MLM aniqligi: 0.6113, 0.6093, 0.6117 o'rtacha 0.6108
faqat MLM MLM aniqligi: 0.6034, 0.6089, 0.6101 o'rtacha 0.6075
MLM farqi (NSP bilan - NSP siz): +0.0033, SE 0.0024 -> MLM aniqligida sezilarli farq yo'q
NSP boshi oddiy 'umumiy so'z' qoidasidan ham past
=== 4. Segment embedding nima beradi (MLM + NSP, seed 0) ===
to'g'ri segmentlar MLM 0.6113 (B qismida 0.6679), NSP 0.8367
hammasi segment 0 MLM 0.5718 (B qismida 0.6163), NSP 0.8353
segment vektorlari orasidagi kosinus: 0.106
⭐ [CLS] - butun juftlik vektori; segment - qaysi jumlaga tegishlilik belgisiNima ko'rsatdi: juftlik [CLS] A [SEP] B [SEP] ko'rinishida, segment A uchun 0, B va uning [SEP] i uchun 1; IsNext ulushi 0.502. Haqiqiy davomda B jumla A dagi joy yoki narsani takrorlaydi — shuning uchun "A va B da umumiy mazmunli so'z bormi" degan bir qatorli qoida NSP ni 0.9647 aniqlik bilan yechadi (tasodifiy B da umumiy so'z atigi 0.0707 holatda). BERT ning NSP boshi 200 qadamda uch seed da 0.8287–0.8367 ga yetdi — oddiy qoidadan ham past. NSP qo'shilganda MLM aniqligi 0.6108 va NSP siz 0.6075: farq +0.0033, SE 0.0024 — sezilarli emas, ya'ni bu tajribada NSP MLM ga yordam ham, zarar ham bermadi. Bu RoBERTa ning kuzatuviga mos: NSP ni olib tashlash natijani yomonlashtirmaydi. 4-bo'lim segment embedding rolini ko'rsatadi: o'rgatilgan modelga hamma pozitsiyada segment 0 berilsa, MLM aniqligi 0.6113 dan 0.5718 ga tushdi (B qismida 0.6679 → 0.6163), NSP esa deyarli o'zgarmadi (0.8367 → 0.8353). Ikki segment vektori deyarli ortogonal (kosinus 0.106). Bog'liq bo'limlar: 2.3, 2.4.
Misol 4 — Kontekstli vektorlar: bir so'z, ikki ma'no
"""Kontekstli vektorlar: bir xil so'z ('yuz', 'kun') turli ma'noda - BERT qatlamlarida."""
import numpy as np
import torch
import torch.nn as nn
import torch.nn.functional as F
ISMLAR = ("anvar dilnoza bobur malika sardor nodira jasur zarina otabek madina "
"sherzod gulnora farhod laylo rustam sevara aziz kamola bahrom "
"shahlo temur nigora dilshod feruza jamshid mohira sanjar yulduz").split()
VAQTLAR = "ertalab kechqurun bugun kecha tushda yakshanba erta indinga".split()
JOYLAR = ("bozor maktab bog' shahar qishloq kutubxona do'kon universitet "
"choyxona dala bekat kasalxona stadion muzey teatr vokzal "
"ofis zavod").split()
NARSALAR = ("kitob olma non qalam daftar gul choy sut anor uzum ruchka gazeta "
"qovun tarvuz sabzi guruch go'sht shakar asal pishloq ko'ylak "
"do'ppi soat telefon").split()
SIFATLAR = ("katta kichik yangi eski chiroyli shirin qizil oq arzon qimmat "
"yashil sariq issiq sovuq mazali").split()
BOSHQA = ("##ga ##dan ##da ##ni ##ini ##i bordi qaytdi sotib oldi bor berdi olib "
"keldi . yuz ta so'm turadi qo'l yuvdi qizardi kun har bir chiqdi "
"tongda nuri yoritdi osmonda porladi").split()
MAXSUS = ["[PAD]", "[CLS]", "[SEP]", "[MASK]"]
PAD, CLS, SEP, MASK = 0, 1, 2, 3
LUGAT = MAXSUS + sorted(set(ISMLAR + VAQTLAR + JOYLAR + NARSALAR + SIFATLAR + BOSHQA))
s2i = {s: i for i, s in enumerate(LUGAT)}
V = len(LUGAT)
L = 12
def jumla(rng):
"""(tokenlar, so'z, ma'no, shablon). Ikki ma'noli so'z doim 2-o'rinda turadi -
pozitsiya ma'no haqida hech narsa aytmaydi, farqni faqat kontekst beradi."""
ism, ism2 = (str(s) for s in rng.choice(ISMLAR, 2, replace=False))
vaqt, joy = str(rng.choice(VAQTLAR)), str(rng.choice(JOYLAR))
narsa, sifat = str(rng.choice(NARSALAR)), str(rng.choice(SIFATLAR))
t = rng.integers(0, 14)
if t == 0:
return [ism, "yuz", "ta", narsa, "sotib", "oldi", "."], "yuz", 0, "a"
if t == 1:
return [narsa, "yuz", "so'm", "turadi", "."], "yuz", 0, "b"
if t == 2:
return [ism, "yuz", "qo'l", "##ini", "yuvdi", "."], "yuz", 1, "a"
if t == 3:
return [ism, "yuz", "##i", "qizardi", "."], "yuz", 1, "b"
if t == 4:
return ["har", "kun", ism, joy, "##ga", "bordi", "."], "kun", 0, "a"
if t == 5:
return ["bir", "kun", ism, joy, "##dan", "qaytdi", "."], "kun", 0, "b"
if t == 6:
return [vaqt, "kun", "chiqdi", "."], "kun", 1, "a"
if t == 7:
return ["tongda", "kun", "nuri", joy, "##ni", "yoritdi", "."], "kun", 1, "b"
if t == 8:
return [ism, joy, "##ga", vaqt, "bordi", "."], None, -1, ""
if t == 9:
return [ism, joy, "##dan", vaqt, "qaytdi", "."], None, -1, ""
if t == 10:
return [ism, sifat, narsa, "sotib", "oldi", "."], None, -1, ""
if t == 11:
return [joy, "##da", sifat, narsa, "bor", "."], None, -1, ""
if t == 12:
return [ism, ism2, "##ga", sifat, narsa, "berdi", "."], None, -1, ""
return ["osmonda", "kun", "porladi", "."], "kun", 1, "c"
def tensorla(jumlalar):
X = torch.full((len(jumlalar), L), PAD)
for i, (j, _, _, _) in enumerate(jumlalar):
ids = [CLS] + [s2i[w] for w in j] + [SEP]
X[i, :len(ids)] = torch.tensor(ids)
return X
def maskala(X, g, p=0.15):
oddiy = X > MASK
tanlandi = (torch.rand(X.shape, generator=g) < p) & oddiy
u = torch.rand(X.shape, generator=g)
kirish = X.clone()
kirish[tanlandi & (u < 0.8)] = MASK
tasodifiy = tanlandi & (u >= 0.8) & (u < 0.9)
kirish[tasodifiy] = torch.randint(MASK + 1, V, X.shape, generator=g)[tasodifiy]
return kirish, torch.where(tanlandi, X, torch.full_like(X, -100))
class Blok(nn.Module):
def __init__(self, d, boshlar):
super().__init__()
self.boshlar = boshlar
self.ln1, self.ln2 = nn.LayerNorm(d), nn.LayerNorm(d)
self.qkv = nn.Linear(d, 3 * d)
self.proj = nn.Linear(d, d)
self.ffn = nn.Sequential(nn.Linear(d, 2 * d), nn.GELU(), nn.Linear(2 * d, d))
def forward(self, x, ruxsat):
B, T, d = x.shape
q, k, v = self.qkv(self.ln1(x)).split(d, dim=2)
q, k, v = [t.view(B, T, self.boshlar, -1).transpose(1, 2) for t in (q, k, v)]
o = F.scaled_dot_product_attention(q, k, v, attn_mask=ruxsat)
x = x + self.proj(o.transpose(1, 2).reshape(B, T, d))
return x + self.ffn(self.ln2(x))
class KichikBERT(nn.Module):
def __init__(self, d=64, boshlar=4, qatlamlar=2):
super().__init__()
self.emb = nn.Embedding(V, d)
self.poz = nn.Embedding(L, d)
self.bloklar = nn.ModuleList(Blok(d, boshlar) for _ in range(qatlamlar))
self.ln = nn.LayerNorm(d)
self.mlm = nn.Linear(d, V)
def forward(self, x, holatlar=False):
ruxsat = (x != PAD)[:, None, None, :]
h = self.emb(x) + self.poz(torch.arange(x.shape[1]))
qatlam = {"token embedding": self.emb(x), "kirish (token + poz)": h}
for i, b in enumerate(self.bloklar):
h = b(h, ruxsat)
qatlam[f"{i + 1}-blok"] = h
return qatlam if holatlar else self.mlm(self.ln(h))
def orgat(X, seed=0, qadamlar=400, B=64):
torch.manual_seed(seed)
model = KichikBERT()
opt = torch.optim.AdamW(model.parameters(), lr=3e-3, weight_decay=0.01)
g = torch.Generator().manual_seed(seed)
for _ in range(qadamlar):
idx = torch.randint(0, len(X), (B,), generator=g)
kirish, nishon = maskala(X[idx], g)
loss = F.cross_entropy(model(kirish).reshape(-1, V), nishon.reshape(-1),
ignore_index=-100)
opt.zero_grad()
loss.backward()
nn.utils.clip_grad_norm_(model.parameters(), 1.0)
opt.step()
model.eval()
return model
@torch.no_grad()
def vektorlar(model, jumlalar, soz):
"""Har jumlada 'soz' pozitsiyasidagi vektor - har qatlam uchun."""
tanlov = [j for j in jumlalar if j[1] == soz]
X = tensorla(tanlov)
joy = torch.tensor([j[0].index(soz) + 1 for j in tanlov]) # +1: [CLS]
qatlam = model(X, holatlar=True)
return ({k: v[torch.arange(len(X)), joy] for k, v in qatlam.items()},
np.array([j[2] for j in tanlov]), np.array([j[3] for j in tanlov]))
def main() -> None:
torch.set_num_threads(1)
rng = np.random.default_rng(0)
oquv = [jumla(rng) for _ in range(8000)]
val = [jumla(rng) for _ in range(2000)]
model = orgat(tensorla(oquv))
print("=== 1. Ikki ma'noli so'zlar (val dan namunalar) ===")
for soz in ("yuz", "kun"):
for m in (0, 1):
j = next(j for j in val if j[1] == soz and j[2] == m)
print(f" {soz!r} ma'no {m}: {' '.join(j[0])}")
print(" token embedding (statik) ikkala ma'noda AYNAN bir xil vektor")
print("\n=== 2. Kosinus o'xshashlik: bir ma'no ichida va ma'nolar orasida ===")
natija = {}
for soz in ("yuz", "kun"):
v_o, m_o, sh_o = vektorlar(model, oquv, soz)
v_v, m_v, sh_v = vektorlar(model, val, soz)
natija[soz] = (v_o, m_o, sh_o, v_v, m_v, sh_v)
print(f" {soz!r} (val: {len(m_v)} ta)")
for k, v in v_v.items():
n = F.normalize(v, dim=1)
s = (n @ n.T).numpy()
bir = s[m_v[:, None] == m_v[None, :]].mean()
boshqa = s[m_v[:, None] != m_v[None, :]].mean()
print(f" {k:<21} bir ma'no {bir:.3f} boshqa ma'no {boshqa:.3f} "
f"farq {bir - boshqa:.3f}")
print("\n=== 3. Ma'noni aniqlash: markazga eng yaqin (val) ===")
print(" A: markazlar o'quvdagi barcha shablonlardan")
print(" B: markazlar faqat 'a' shablonlardan, test - boshqa shablonlarda")
print(f" {'qatlam':<21} {'yuz A':>7} {'yuz B':>7} {'kun A':>7} {'kun B':>7}")
qatorlar = {}
for soz in ("yuz", "kun"):
v_o, m_o, sh_o, v_v, m_v, sh_v = natija[soz]
for k in v_o:
for rejim in ("A", "B"):
tanla = np.ones(len(m_o), bool) if rejim == "A" else sh_o == "a"
test = np.ones(len(m_v), bool) if rejim == "A" else sh_v != "a"
markaz = torch.stack([v_o[k][torch.tensor(tanla & (m_o == c))].mean(0)
for c in (0, 1)])
o = F.cosine_similarity(v_v[k][torch.tensor(test)][:, None],
markaz[None], dim=2)
a = (o.argmax(1).numpy() == m_v[test]).mean()
qatorlar.setdefault(k, []).append(a)
for k, r in qatorlar.items():
print(f" {k:<21} " + " ".join(f"{a:>7.3f}" for a in r))
print(" tasodif ~0.5; token embedding - bir xil vektor, argmax doim bir sinf")
print("\n=== 4. Bitta so'z, to'rt kontekst (oxirgi blok, kosinus) ===")
misollar = [["anvar", "yuz", "ta", "olma", "sotib", "oldi", "."],
["non", "yuz", "so'm", "turadi", "."],
["anvar", "yuz", "qo'l", "##ini", "yuvdi", "."],
["malika", "yuz", "##i", "qizardi", "."]]
X = tensorla([(j, None, 0, "") for j in misollar])
joy = torch.tensor([j.index("yuz") + 1 for j in misollar])
with torch.no_grad():
h = model(X, holatlar=True)["2-blok"][torch.arange(4), joy]
n = F.normalize(h, dim=1)
s = n @ n.T
for i, j in enumerate(misollar):
print(f" {' '.join(j):<34} " + " ".join(f"{x:6.3f}" for x in s[i].tolist()))
print(" 1-2: 'yuz' = 100 (son), 3-4: 'yuz' = a'zo (bet)")
print(" ⭐ BERT vektori - so'zning O'ZI emas, so'z + uning konteksti")
if __name__ == "__main__":
main()Natijaning muhim qismi:
=== 1. Ikki ma'noli so'zlar (val dan namunalar) ===
'yuz' ma'no 0: shahlo yuz ta ruchka sotib oldi .
'yuz' ma'no 1: laylo yuz ##i qizardi .
'kun' ma'no 0: har kun sardor muzey ##ga bordi .
'kun' ma'no 1: osmonda kun porladi .
token embedding (statik) ikkala ma'noda AYNAN bir xil vektor
=== 2. Kosinus o'xshashlik: bir ma'no ichida va ma'nolar orasida ===
'yuz' (val: 549 ta)
token embedding bir ma'no 1.000 boshqa ma'no 1.000 farq 0.000
kirish (token + poz) bir ma'no 1.000 boshqa ma'no 1.000 farq 0.000
1-blok bir ma'no 0.904 boshqa ma'no 0.870 farq 0.034
2-blok bir ma'no 0.853 boshqa ma'no 0.759 farq 0.094
'kun' (val: 704 ta)
token embedding bir ma'no 1.000 boshqa ma'no 1.000 farq -0.000
kirish (token + poz) bir ma'no 1.000 boshqa ma'no 1.000 farq -0.000
1-blok bir ma'no 0.964 boshqa ma'no 0.920 farq 0.044
2-blok bir ma'no 0.934 boshqa ma'no 0.886 farq 0.048
=== 3. Ma'noni aniqlash: markazga eng yaqin (val) ===
A: markazlar o'quvdagi barcha shablonlardan
B: markazlar faqat 'a' shablonlardan, test - boshqa shablonlarda
qatlam yuz A yuz B kun A kun B
token embedding 0.554 0.536 0.385 0.328
kirish (token + poz) 0.554 0.464 0.385 0.328
1-blok 0.767 0.536 1.000 0.667
2-blok 1.000 0.018 1.000 0.667
tasodif ~0.5; token embedding - bir xil vektor, argmax doim bir sinf
=== 4. Bitta so'z, to'rt kontekst (oxirgi blok, kosinus) ===
anvar yuz ta olma sotib oldi . 1.000 0.558 0.753 0.878
non yuz so'm turadi . 0.558 1.000 0.605 0.717
anvar yuz qo'l ##ini yuvdi . 0.753 0.605 1.000 0.881
malika yuz ##i qizardi . 0.878 0.717 0.881 1.000
1-2: 'yuz' = 100 (son), 3-4: 'yuz' = a'zo (bet)
⭐ BERT vektori - so'zning O'ZI emas, so'z + uning kontekstiNima ko'rsatdi: korpusda ikki ma'noli so'zlar bor: yuz (100 / bet) va kun (sutka / quyosh), va ular doim jumlaning 2-o'rnida — ma'noni pozitsiyadan bilib bo'lmaydi. Token embedding va (token + pozitsiya) darajasida ikkala ma'no aynan bir xil vektor (farq 0.000). Transformer bloklaridan keyin vektorlar ajraladi: 2-blokda yuz uchun bir ma'no ichidagi o'rtacha kosinus 0.853, ma'nolar orasida 0.759; kun uchun 0.934 va 0.886. Farqlar kichik (anizotropiya — hamma vektorlar bir-biriga o'xshash), lekin markazga eng yaqin qoidasi o'quvdagi shablonlar bilan 2-blokda ikkala so'z uchun ham 1.000 va 1.000 aniqlik berdi. Halol cheklov (B ustun): markazlar faqat 'a' shablonlaridan olinib, boshqa shablonlarda tekshirilganda natija yiqildi — yuz uchun 0.018 (deyarli doim teskari), kun uchun 0.667. Ya'ni 8000 jumlalik korpusda model "ma'no" ni emas, ko'rilgan kontekst turlarini ajratadi; narsa yuz so'm turadi vektori anvar yuz ta olma dan ko'ra anvar yuz ##i qizardi ga yaqinroq chiqdi. 4-bo'limdagi to'rtta jumla ham shuni ko'rsatadi: 1-jumla (son) 4-jumlaga (bet) 0.878 bilan eng yaqin. Real BERT bu muammoni ma'lumot hajmi va xilma-xilligi bilan yengadi. Bog'liq bo'limlar: 2.6.
5. To'g'ri va noto'g'ri tushunishlar
| Noto'g'ri fikr | To'g'risi |
|---|---|
| "BERT — til modeli, demak matn yoza oladi" | MLM chapdan o'ngga yozishni o'rgatmaydi; generatsiya — GPT |
"Tanlangan tokenlarning hammasi [MASK] bo'ladi" |
80% [MASK], 10% tasodifiy, 10% o'zgarmagan |
| "Loss barcha pozitsiyalarda" | Faqat tanlangan 15% da; qolganlari -100 |
| "MLM aniqligi 0.53 — model yomon" | Bazaviy 0.28; ismlarda shipi tasodif; qo'shimchalarda 0.98 |
| "Ikki tomonlamalik hamma joyda yordam beradi" | 2-misolda ismlarda farq sezilarli emas; asosiy foyda qo'shimchalarda (+0.216) |
| "NSP aniqligi yuqori — model jumlalar munosabatini tushundi" | Oddiy "umumiy so'z" qoidasi 0.965 berdi |
| "NSP BERT ning muhim qismi" | RoBERTa uni olib tashladi; 3-misolda MLM ga sezilarli ta'sir yo'q |
| "Kontekstli vektor ma'noni avtomatik ajratadi" | 4-misolda yangi shablonlarga umumlashmadi (0.018 / 0.667) |
"[CLS] vektori tayyor jumla vektori" |
U maqsad (NSP yoki fine-tuning) bilan o'rgatilgandagina ma'noli |
6. Keng tarqalgan xatolar va yechimlari
1. Maxsus tokenlar maskalanadi
tanlandi = torch.rand(X.shape) < 0.15 # ⚠️
tanlandi = (torch.rand(X.shape, generator=g) < 0.15) & (X > MASK) # ✅2. Loss hamma pozitsiyada
loss = F.cross_entropy(logit.reshape(-1, V), X.reshape(-1)) # ⚠️
nishon = torch.where(tanlandi, X, torch.full_like(X, -100))
loss = F.cross_entropy(logit.reshape(-1, V), nishon.reshape(-1),
ignore_index=-100) # ✅3. Faqat [MASK]
kirish[tanlandi] = MASK # ⚠️
# 80% [MASK], 10% tasodifiy, 10% o'zgarmagan # ✅4. Tasodifiy token maxsuslar orasidan
torch.randint(0, V, X.shape) # [PAD], [CLS] ham # ⚠️
torch.randint(MASK + 1, V, X.shape, generator=g) # ✅5. Statik niqob
kirish, nishon = maskala(X, g) # bir marta, sikldan oldin # ⚠️
for _ in range(qadamlar):
kirish, nishon = maskala(X[idx], g) # har batchda yangi # ✅6. Aniqlikni barcha tanlanganlarda hisoblash
aniq = (bash == X)[nishon != -100].mean() # o'zgarmaganlar ham # ⚠️
aniq = (bash == X)[kirish == MASK].float().mean() # ✅7. [PAD] attention da ko'rinadi
F.scaled_dot_product_attention(q, k, v) # ⚠️
F.scaled_dot_product_attention(q, k, v,
attn_mask=(x != PAD)[:, None, None, :]) # ✅7. Integratsiya — bu bilim qayerda kerak bo'ladi
- 23.3, 23.6-darslar (o'tilgan): WordPiece/BPE subword tokenizatsiyasi va statik so'z embeddinglari
- 24.4–24.7-darslar (o'tilgan): pozitsion kodlash, Transformer bloki va encoder, kauzal niqob
- 18-qism (o'tilgan): bir necha seed, juftlashgan farq va SE
- Keyingi darslar: GPT — kauzal decoder va generatsiya; Pretraining va fine-tuning darsida BERT ni klassifikatsiyaga moslash; Katta til modellari qismida — encoder va decoder modellarning masshtabdagi o'rni
8. Eng yaxshi amaliyotlar
Maskalashni har batchda qayta qiling (dinamik) va
torch.Generatorbilan takrorlanadigan qiling.Maxsus tokenlarni tanlovdan va tasodifiy almashtirishdan chiqaring.
Maskalash statistikasini (15%, 80/10/10) katta namunada tekshiring.
MLM aniqligini faqat
[MASK]joylarida o'lchang va bazaviy modellar bilan birga ko'rsating.Aniqlikni token turlari bo'yicha bo'ling — kamaytirib bo'lmaydigan noaniqlikni ajrating.
Arxitektura tanlovini ablyatsiya bilan tekshiring (masalan, faqat chap kontekst), bir necha seed da.
NSP kabi yordamchi vazifalarni oddiy "yorliq" qoida bilan solishtiring.
Kontekstli vektorlarni pozitsiya va shablon ta'siri ajratilgan tajribada baholang.
9. Amaliy topshiriq
Vazifa 1: Bashorat qiling
1. # 1000 ta oddiy tokendan taxminan nechtasi tanlanadi?
2. # tanlanganlardan nechtasi [MASK] bo'ladi?
3. # tanlanmagan pozitsiyaning nishoni qanday?
4. # [CLS] tokeni maskalanishi mumkinmi?
5. # nega 10% tokenni o'zgarishsiz qoldiramiz?
6. # BERT kirish embeddingi qaysi uch qismdan iborat?
7. # [CLS] A [SEP] B [SEP] da ikkinchi [SEP] ning segmenti?
8. # NSP da IsNext ulushi qancha?
9. # nega RoBERTa NSP ni olib tashladi?
10. # 10 epoxa dinamik maskalashda token kamida bir marta nishon bo'lish ehtimoli?
11. # MLM aniqligini qaysi pozitsiyalarda o'lchash kerak?
12. # statik embeddingda "yuz" (100) va "yuz" (bet) vektorlari?Javoblar
- ~150 ta (15%)
- ~120 ta (80%)
-100— loss hisoblanmaydi- Yo'q, maxsus tokenlar tanlanmaydi
- Fine-tuning da
[MASK]bo'lmaydi; model ko'rinib turgan tokenlar uchun ham yaxshi vakillik o'rganishi kerak - Token + pozitsiya + segment
- 1 (B ga tegishli)
- 50%
- Vazifa mavzu o'xshashligi bilan oson yechiladi va MLM ga foyda bermadi
1 - 0.85^10 ≈ 0.803[MASK]qo'yilgan joylarda- Aynan bir xil
Vazifa 2: Xatolarni tuzating
1. tanlandi = torch.rand(X.shape) < 0.15
kirish = X.masked_fill(tanlandi, MASK)
2. loss = F.cross_entropy(model(kirish).reshape(-1, V), X.reshape(-1))
3. kirish[tasodifiy] = torch.randint(0, V, X.shape)[tasodifiy]
4. kirish, nishon = maskala(X, g)
for _ in range(1000):
loss = ce(model(kirish), nishon)
5. aniq = (model(kirish).argmax(-1) == X)[nishon != -100].float().mean()
print("MLM aniqligi:", aniq)Javoblar
1. tanlandi = (torch.rand(X.shape, generator=g) < 0.15) & (X > MASK)
u = torch.rand(X.shape, generator=g)
kirish = X.clone()
kirish[tanlandi & (u < 0.8)] = MASK
# 10% tasodifiy, 10% o'zgarmagan - 3-banddagi kabi
2. nishon = torch.where(tanlandi, X, torch.full_like(X, -100))
loss = F.cross_entropy(model(kirish).reshape(-1, V), nishon.reshape(-1),
ignore_index=-100)
3. kirish[tasodifiy] = torch.randint(MASK + 1, V, X.shape, generator=g)[tasodifiy]
4. for _ in range(1000):
idx = torch.randint(0, len(X), (B,), generator=g)
kirish, nishon = maskala(X[idx], g) # har qadamda yangi niqob
loss = ce(model(kirish), nishon)
5. aniq = (model(kirish).argmax(-1) == X)[kirish == MASK].float().mean()
print("MLM aniqligi ([MASK] joylarida):", aniq, "bazaviy:", bazaviy)Vazifa 3: Maskalash
Modellang:
- Maxsus tokenli lug'at
maskalafunksiyasi (80/10/10)- 20000 jumlada statistika
- Dinamik va statik niqob
Vazifa 4: Kichik BERT
Modellang:
- Encoder + MLM boshi
- Dinamik maskalash bilan o'rgatish
- Uch bazaviy model
- Faqat chap kontekstli ablyatsiya, 3 seed
Vazifa 5: Juftliklar va NSP
Modellang:
[CLS] A [SEP] B [SEP]va segment id lar- NSP boshi
- "Umumiy so'z" qoidasi
- NSP bilan va NSP siz MLM
Vazifa 6: Kontekstli vektorlar
Modellang:
- Ikki ma'noli so'zlar korpusi (pozitsiya nazorat qilingan)
- Qatlamlar bo'yicha vektorlar
- Kosinus o'xshashlik
- Yangi shablonlarga umumlashish
Vazifa 7: O'ylash
Hamkasbingiz aytdi: "Men BERT ni NSP bilan o'rgatdim, NSP aniqligi 0.97 — model jumlalar orasidagi mantiqiy bog'lanishni a'lo darajada tushunadi. Endi uni savol-javob tizimida ishlatamiz." Siz nima deysiz?
Javob
Qisqa javob: NSP aniqligi yuqoriligi "mantiqiy bog'lanishni tushunish" ni isbotlamaydi — vazifani yuzaki belgilar bilan ham yechish mumkin.
1. Oddiy qoida bilan solishtiring. 3-misolda "A va B da umumiy mazmunli so'z bormi" degan bir qatorli qoida NSP ni 0.9647 aniqlik bilan yechdi. Tasodifiy B odatda boshqa mavzuda bo'ladi — model mavzu o'xshashligini sezsa kifoya.
2. Tarixiy tajriba. RoBERTa mualliflari NSP ni olib tashlab, natija yomonlashmaganini ko'rsatishgan; ALBERT uni jumlalar tartibini aniqlash (SOP) bilan almashtirgan — u yerda ikkala jumla ham bir matndan, mavzu yorlig'i ishlamaydi. 3-misolda ham NSP MLM aniqligini sezilarli o'zgartirmadi (+0.0033, SE 0.0024).
3. Savol-javob uchun nima muhim. Savol-javob tizimi javobni matndan topishi kerak — buni NSP aniqligi emas, aynan shu vazifadagi fine-tuning natijasi ko'rsatadi.
4. Qiyinroq tekshiruv. NSP ni SOP ko'rinishida (to'g'ri va almashtirilgan tartib) sinab ko'ring — agar aniqlik tasodifga yaqin tushsa, model faqat mavzuni sezgan.
Tavsiya:
# 1. "Yorliq" bazaviy qoidalar: umumiy so'zlar, uzunlik, mavzu
# 2. SOP varianti: bir matndan ikki jumla, tartib almashtirilgan
# 3. Asosiy o'lchov: savol-javob to'plamida fine-tuning natijasi
# 4. MLM bilan va MLM + NSP bilan - bir necha seed, juftlashgan farqHamkasbga javob: "0.97 yaxshi raqam, lekin oddiy 'umumiy so'z' qoidasi ham shunga yaqin natija beradi. Keling, model aynan savol-javob vazifasida qanday ishlashini fine-tuning qilib o'lchaylik — va NSP ni SOP bilan tekshiraylik."
Nimani mustahkamlaydi: 2.2, 2.4, 2.5, 2.6-bo'limlar.
Xulosa
Bu darsda BERT ning maskali til modeli maqsadini noldan yozdik, kichik BERT ni o'rgatib halol baholadik, NSP va segment embeddingni sinadik va kontekstli vektorlarni tekshirdik.
Eng muhim uch fikr:
MLM — ikki tomonlama encoderni o'z-o'zidan o'qitish usuli. 15% token tanlanadi, ulardan 80%
[MASK], 10% tasodifiy, 10% o'zgarishsiz qoladi, loss faqat tanlanganlarda. 1-misolda 20000 jumlada ulushlar0.1491va0.8009 / 0.1012 / 0.0979chiqdi, maxsus tokenlardan birortasi tanlanmadi. Dinamik maskalashda 10 epoxada tokenlarning0.8029qismi kamida bir marta nishon bo'ldi. Narxi — signal zichligi: jumlaga o'rtacha 0.95 ta bashorat, kauzal LM da 6.34 ta.Ikki tomonlamalik o'ng kontekst hal qiladigan joyda yutadi — buni bazaviy va ablyatsiya bilan ko'rish kerak. 2-misolda kichik BERT (81 901 parametr)
[MASK]joylarida0.5261aniqlikka yetdi; eng kuchli bazaviy (pozitsiya bo'yicha eng ko'p token)0.2823. Xuddi shu model faqat chap kontekst bilan0.4859berdi: farq+0.0402,SE 0.0032. Farqning asosiy qismi qo'shimchalarda (0.9836va0.7676) —##ga/##danni o'ngdagi fe'l hal qiladi; ismlarda ikkala model ham tasodif darajasida (0.0291,0.0213, tasodif0.0357) — bu matnning o'z noaniqligi. 3-misolda NSP ni oddiy "umumiy so'z" qoidasi0.9647bilan yechdi, BERT ning NSP boshi esa0.83atrofida qoldi; NSP MLM aniqligiga sezilarli ta'sir qilmadi (+0.0033,SE 0.0024) — RoBERTa uni shuning uchun olib tashlagan. Segment embeddingni o'chirish MLM ni0.6113dan0.5718ga tushirdi.Kontekstli vektor — so'z + kontekst, lekin umumlashish ma'lumotga bog'liq. 4-misolda
yuzvakunso'zlari pozitsiyasi bir xil bo'lsa ham, 2-blokdan keyin ularning ma'nolari o'quvdagi kontekst turlarida1.000aniqlik bilan ajraldi, statik embeddingda esa vektorlar aynan bir xil edi. Ammo yangi shablonlarga umumlashish yiqildi (yuz—0.018,kun—0.667): kichik korpusda model ma'noni emas, ko'rilgan kontekst turlarini ajratadi. Real BERT bu muammoni milliardlab so'zli xilma-xil korpus bilan hal qiladi.
Keyingi darsda GPT — generatsiya: kauzal decoder-only til modelini o'rgatamiz, uni 23.10-darsdagi LSTM bilan perplexity bo'yicha halol solishtiramiz va generatsiya strategiyalarini (greedy, temperature, top-k, top-p) noldan yozib, takrorlanish muammosini o'lchaymiz.
Izohlar (0)
Izoh yozish uchun kiring.
- Hozircha izoh yo'q. Birinchi bo'ling!