Mundarija (21)
- 1. Kirish va motivatsiya
- 2. Nazariya — chuqur tushuntirish
- 2.1. Taqsimot gipotezasi
- 2.2. Birga uchrash matritsasi va PPMI
- 2.3. SVD bilan zich vektorlar
- 2.4. word2vec: skip-gram va negative sampling
- 2.5. Kosinus o'xshashlik, eng yaqin so'zlar va analogiya
- 2.6. Sifat korpus hajmiga bog'liq
- 2.7. Tuzoqlar
- 3. Tez ma'lumotnoma
- 4. Batafsil misollar
- Misol 1 — Birga uchrash matritsasi va PPMI
- Misol 2 — SVD bilan zich vektorlar
- Misol 3 — word2vec (skip-gram + negative sampling) noldan
- Misol 4 — Embedding sifati va korpus hajmi
- 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
23.6-dars: So'z embeddinglari
23-QISM — NLP VA KETMA-KETLIKLAR · 6-dars
1. Kirish va motivatsiya
23.4 va 23.5-darslarda har so'z lug'atdagi alohida ustun edi: "shifokor" va "vrach" — ikki xil, bir-biriga hech qanday aloqasi yo'q belgi. TF-IDF uchun "shifokor bemorni ko'rdi" va "vrach bemorni ko'rdi" gaplari faqat "bemorni ko'rdi" qismida o'xshash. Agar o'quv to'plamida faqat "shifokor" uchragan bo'lsa, model "vrach" haqida hech narsa bilmaydi.
So'z embeddingi bu muammoni hal qiladi: har so'zga kichik zich vektor (masalan 20–300 o'lchovli) beriladi, va bu vektorlar shunday joylashadiki, ma'nosi yaqin so'zlar yaqin bo'ladi. Qanday qilib "ma'no"ni o'lchaymiz? Javob 1950-yillardagi tilshunoslik g'oyasida: so'zni uning qo'shnilari orqali bilasiz. "Shifokor" ham, "vrach" ham "bemor", "dori", "tekshirdi" so'zlari yonida keladi — demak ular o'xshash.
Bu g'oyadan ikki yo'l bilan vektor olamiz. Birinchisi — sanash: qaysi so'z qaysi so'z yonida necha marta kelganini jadvalga yozamiz, uni PPMI bilan tozalaymiz va SVD bilan siqamiz. Ikkinchisi — bashorat: word2vec modeli so'zdan uning qo'shnilarini bashorat qilishni o'rganadi, va shu jarayonda vektorlar o'z-o'zidan hosil bo'ladi. Ikkalasini ham noldan, numpy va torch bilan quramiz.
Real vaziyat. Tibbiyot klinikasi bemorlar murojaatlarini saralaydi. Belgilangan murojaatlar atigi bir necha yuzta, lekin belgilanmagan matnlar — yuz minglab. Belgilanmagan matnlarda o'rgatilgan embeddinglar "vrach", "shifokor", "doktor" so'zlarini bir joyga qo'yadi — va keyingi darsda ko'ramizki, bu kam belgilangan ma'lumotda klassifikatorga yordam berishi mumkin. Lekin embedding sifati korpus hajmiga qattiq bog'liq — buni ham o'lchaymiz.
Bu darsda so'zlarni ma'noli zich vektorlarga aylantirishni o'rganamiz.
Bu darsda:
- Taqsimot gipotezasi
- Birga uchrash matritsasi va PPMI
- SVD bilan zich vektorlar
- word2vec: skip-gram va negative sampling
- Kosinus o'xshashlik, eng yaqin so'zlar va analogiya
- Sifat korpus hajmiga bog'liq
- Tuzoqlar
ℹ Misollar real numpy/torch bilan (Python 3.14, torch 2.14 CPU).
2. Nazariya — chuqur tushuntirish
2.1. Taqsimot gipotezasi
G'OYA (Harris 1954, Firth 1957):
"so'zni u bilan birga keladigan so'zlar orqali bilasiz"
bir xil kontekstlarda keladigan so'zlar - ma'nosi yaqin
MISOL:
shifokor bemorni tekshirdi vrach bemorni tekshirdi
shifokor dori yozib berdi vrach dori yozib berdi
-> "shifokor" va "vrach" kontekstlari deyarli bir xil
ONE-HOT 23.4-bob BILAN FARQ:
one-hot: har so'z alohida o'q, istalgan ikki so'z orasidagi
kosinus = 0 ("shifokor" va "vrach" ham, "shifokor" va "gol" ham)
embedding: d o'lchovli zich vektor (d = 20..300),
o'xshash so'zlar - kichik burchak
"O'XSHASH" NIMANI BILDIRADI:
sinonimlar (shifokor - vrach) yaqin
bir mavzudagi so'zlar (shifokor - bemor) o'rtacha yaqin
ANTONIMLAR ham ko'pincha yaqin (oshdi - tushdi) - kontekstlari bir xil!
-> embedding "almashtirsa bo'ladigan" so'zlarni topadi,
"bir xil ma'noli" so'zlarni emasEmbedding — kontekstlar statistikasining siqilgan ko'rinishi; u ma'noni emas, foydalanish o'xshashligini o'lchaydi.
2.2. Birga uchrash matritsasi va PPMI
BIRGA UCHRASH MATRITSASI:
C[w, c] = w so'zidan +-oyna masofada c so'zi necha marta kelgan
oyna = 2: "bugun shifokor bemor ko'rdi" da
shifokor uchun kontekstlar: bugun, bemor, ko'rdi
shakl: (V, V), V - lug'at hajmi; asosan nollardan iborat
XOM SANOQ MUAMMOSI:
ko'p uchraydigan so'zlar ("bugun", "juda", "bu") hamma joyda
-> har so'zning eng katta sanoqlari aynan shular
-> ikki so'z "bugun" bilan birga kelgani uchun o'xshash bo'lib qoladi
PMI (pointwise mutual information):
PMI(w, c) = log( P(w, c) / (P(w) * P(c)) )
= log( C[w,c] * N / (C[w,:].sum() * C[:,c].sum()) )
"tasodifdan qanchalik KO'P birga keladi"
PMI > 0 - tasodifdan ko'p, PMI < 0 - kam, C = 0 da -inf
PPMI (positive PMI):
PPMI = max(PMI, 0), C = 0 bo'lsa 0
manfiy qiymatlar ishonchsiz (kam ma'lumot) -> tashlanadi
natija: "bugun" kabi hamma joydagi so'zlarning og'irligi kichrayadiPPMI "tez-tez" emas, "kutilganidan ko'p" birga kelishni o'lchaydi — bu TF-IDF dagi IDF g'oyasining qarindoshi.
2.3. SVD bilan zich vektorlar
PPMI MATRITSASI: (V, V), siyrak, katta (V = 50 000 -> 2.5 mlrd katak)
QISQARTIRILGAN SVD (10-qism):
PPMI ~ U_k * S_k * V_k^T, k << V
so'z vektori: E = U_k * S_k (yoki U_k * sqrt(S_k))
shakl: (V, k), k = 20..300
NIMA BERADI:
1. zich va qisqa vektorlar
2. shovqin kamayadi: kichik singular qiymatlar tashlanadi
3. umumlashtirish: "shifokor" va "vrach" hech qachon bir gapda
kelmasa ham, ularning satrlari bir xil "yo'nalish"ga tushadi
k NI TANLASH:
juda kichik -> mavzular aralashadi (sport va salomatlik bir joyda)
juda katta -> shovqin qaytadi, siqish foydasi yo'qoladi
-> validatsiya vazifasida tanlanadi (bizda: sinonim topish)Sanash + PPMI + SVD — word2vec dan oldingi klassik yo'l, va kichik korpusda u hali ham raqobatbardosh.
2.4. word2vec: skip-gram va negative sampling
SKIP-GRAM VAZIFASI:
markaziy so'zdan uning qo'shnilarini bashorat qil
"bugun shifokor bemor ko'rdi", oyna=2:
(shifokor, bugun), (shifokor, bemor), (shifokor, ko'rdi), ...
har so'zning IKKI vektori: markaz v_w va kontekst u_c
TO'LIQ SOFTMAX (qimmat):
P(c | w) = exp(u_c . v_w) / sum_{c'} exp(u_c' . v_w)
maxraj - butun lug'at bo'yicha (V = 100 000 bo'lsa, har juftda 100 000)
NEGATIVE SAMPLING (SGNS):
ko'p klassli vazifa -> ikkilik vazifa:
haqiqiy juft (w, c) -> 1, tasodifiy k ta (w, n) -> 0
loss = -log sigmoid(u_c . v_w) - sum_{i=1..k} log sigmoid(-u_ni . v_w)
k = 5..20; manfiy so'zlar P(n) ~ count(n)^0.75 dan olinadi
(0.75 - kam uchraydigan so'zlarga biroz ko'proq imkon)
TORCH DA:
markaz = nn.Embedding(V, d); kontekst = nn.Embedding(V, d)
ijobiy = (markaz(w) * kontekst(c)).sum(1)
salbiy = bmm(kontekst(neg), markaz(w).unsqueeze(2))
o'rgatishdan keyin: markaz.weight - so'z vektorlari
SGNS VA PPMI BOG'LIQLIGI (Levy va Goldberg, 2014):
SGNS yashirin tarzda PMI - log(k) matritsasini faktorlaydi
-> ikki yondashuv bir xil statistikaning ikki ko'rinishiword2vec — "qo'shnini tanib ol" o'yini; vektorlar bu o'yinning qo'shimcha mahsuloti.
2.5. Kosinus o'xshashlik, eng yaqin so'zlar va analogiya
KOSINUS O'XSHASHLIK:
cos(a, b) = a . b / (|a| * |b|), -1 .. 1
vektor UZUNLIGI ko'pincha chastotani aks ettiradi -> uzunlik emas,
yo'nalish taqqoslanadi
ENG YAQIN SO'ZLAR:
En = E / |E| (satr bo'yicha normallash)
S = En @ En.T; S[w, w] = -inf; S[w].argsort()[::-1][:5]
SINONIMLARNI O'LCHASH (bizning korpusda):
top-1: sinonimi eng yaqin qo'shni chiqqan juftlar ulushi
cos(sinonimlar) ni cos(bir mavzudagi boshqa so'zlar) va
cos(boshqa mavzudagi so'zlar) bilan solishtiramiz
tasodifiy vektorlar bilan ham (o'rgatishdan OLDIN) - bazaviy
ANALOGIYA (nazariya):
"erkak : qirol = ayol : ?"
vec(qirol) - vec(erkak) + vec(ayol) ~ vec(malika)
ishlashi uchun: farq vektori ("erkak -> ayol") ko'p juftlarda
bir xil bo'lishi kerak - bu faqat JUDA KATTA korpusda (milliardlab
so'z) va muntazam munosabatlarda (poytaxt, rod, zamon) kuzatiladi
kichik korpusda analogiya ishonchsiz; va u embedding sifatining
yagona o'lchovi emas (natijalar test to'plamiga sezgir)Kosinus va eng yaqin qo'shnilar — embeddingni tekshirishning asosiy vositasi; analogiya — chiroyli, lekin nozik xossa.
2.6. Sifat korpus hajmiga bog'liq
NEGA:
so'z vektori uning kontekstlari statistikasidan quriladi
so'z 5 marta uchragan bo'lsa - 5 ta kontekst, statistikasi shovqinli
kam uchraydigan sinonim ("vrach" "shifokor"dan 3 barobar kam) -
eng birinchi zarar ko'radi
AMALDA:
word2vec ning klassik vektorlari milliardlab so'zda o'rgatilgan
o'n minglab gapda - faqat ko'p uchraydigan so'zlar ishonchli
min_count (odatda 5) - juda kam uchraganlar lug'atdan chiqariladi
HALOL TAJRIBA (4-misol):
bir xil generatordan 50, 150, 500, 2000 gap
bir xil model va bir xil qadamlar soni
-> farq faqat ma'lumot hajmidan
OLDINDAN O'RGATILGAN EMBEDDING:
katta umumiy korpusda bir marta o'rgatiladi, keyin kichik vazifada
ishlatiladi (keyingi dars) - kam ma'lumotli vazifaning asosiy yordamchisiEmbedding faqat korpus bilgan narsani biladi — kichik korpusda kam uchraydigan so'zlar vektori ishonchsiz.
2.7. Tuzoqlar
Asosiy tuzoqlar: xom sanoqlarga kosinus qo'llab "bugun" kabi so'zlar ta'sirida xulosa chiqarish; PMI da log(0) ni tozalamaslik; SVD k ni tekshirmasdan tanlash; skip-gram da kontekst vektorini ham markaz vektori deb olish yoki ularni aralashtirish; manfiy namunalarni tekis taqsimotdan olish; Evklid masofasini kosinus o'rniga ishlatish (uzunlik = chastota); eng yaqin qo'shnilar ro'yxatida so'zning o'zini qoldirish; bitta chiroyli analogiya asosida "embedding ma'noni tushunadi" deyish; antonimlar ham yaqin bo'lishini unutish; kichik korpusda kam uchraydigan so'zlar vektoriga ishonish.
3. Tez ma'lumotnoma
import numpy as np
import torch
import torch.nn as nn
# 1. Birga uchrash (oyna = 2)
C = np.zeros((V, V))
for gap in gaplar:
ids = [idx[s] for s in gap]
for i, w in enumerate(ids):
for j in range(max(0, i - 2), min(len(ids), i + 3)):
if j != i:
C[w, ids[j]] += 1
# 2. PPMI
N = C.sum()
pmi = np.log(C * N / (C.sum(1, keepdims=True) * C.sum(0, keepdims=True))
+ 1e-12)
ppmi = np.where(C > 0, np.maximum(pmi, 0), 0)
# 3. SVD
U, S, Vt = np.linalg.svd(ppmi)
E = U[:, :20] * S[:20]
# 4. Skip-gram + negative sampling
markaz, kontekst = nn.Embedding(V, d), nn.Embedding(V, d)
vm = markaz(w) # (B, d)
ijobiy = (vm * kontekst(c)).sum(1) # (B,)
salbiy = torch.bmm(kontekst(neg), vm.unsqueeze(2)).squeeze(2) # (B, k)
loss = -(nn.functional.logsigmoid(ijobiy).mean()
+ nn.functional.logsigmoid(-salbiy).sum(1).mean())
# 5. Eng yaqin so'zlar
En = E / np.linalg.norm(E, axis=1, keepdims=True)
S = En @ En.T
np.fill_diagonal(S, -np.inf)
yaqin = S[idx["shifokor"]].argsort()[::-1][:5]So'z embeddinglari xulosasi
taqsimot gipotezasi: kontekst o'xshash -> ma'no yaqin
sanash: C -> PPMI -> SVD (k ~ 20..300)
bashorat: skip-gram + negative sampling, P(neg) ~ count^0.75
kosinus, o'zini chiqarib tashlab eng yaqinlar
sifat: sinonimlar, mavzu, tasodifiy bazaviy bilan o'lchang
korpus kichik -> kam uchraydigan so'zlar ishonchsiz4. Batafsil misollar
Misollar real numpy/torch bilan (Python 3.14, torch 2.14 CPU).
Misol 1 — Birga uchrash matritsasi va PPMI
"""Mavzuli sintetik korpus: xom sanoq va PPMI qo'shnilari."""
import numpy as np
MAVZULAR = {
"sport": (["o'yin", "mavsum", "chempionat", "sport", "final"], [
[("futbolchi", "o'yinchi"), ("darvozaga",), ("gol",),
("urdi", "kiritdi")],
[("jamoa", "komanda"), ("turnirda", "musobaqada"), ("g'alaba",),
("qozondi",)],
[("murabbiy", "trener"), ("futbolchi", "o'yinchi"), ("maqtadi",)],
[("jamoa", "komanda"), ("stadionda", "maydonda"), ("mashq",),
("qildi",)]]),
"iqtisod": (["bozor", "iqtisodiyot", "foiz", "hukumat", "davlat"], [
[("narx", "baho"), ("bozorda",), ("oshdi", "ko'tarildi")],
[("bank",), ("kredit", "qarz"), ("berdi",)],
[("hukumat",), ("soliqni",), ("kamaytirdi", "pasaytirdi")],
[("kompaniya", "firma"), ("pul", "mablag'"), ("sarfladi",)],
[("investor", "sarmoyador"), ("kompaniya", "firma"),
("qo'llab-quvvatladi",)]]),
"texnologiya": (["raqamli", "texnologiya", "tizim", "onlayn",
"yangilanish"], [
[("dasturchi", "muhandis"), ("dastur", "ilova"),
("yozdi", "yaratdi")],
[("foydalanuvchi",), ("telefon", "smartfon"), ("sotib",), ("oldi",)],
[("internet", "tarmoq"), ("tezligi",), ("oshdi", "ko'tarildi")],
[("kompaniya", "firma"), ("dastur", "ilova"), ("chiqardi",)]]),
"salomatlik": (["sog'liq", "tibbiyot", "kasallik", "davolash",
"klinika"], [
[("shifokor", "vrach"), ("bemor", "kasal"), ("ko'rdi", "tekshirdi")],
[("shifokor", "vrach"), ("dori", "preparat"), ("yozib",),
("berdi",)],
[("bemor", "kasal"), ("kasalxonada", "shifoxonada"),
("davolandi",)],
[("bemor", "kasal"), ("dori", "preparat"), ("ichdi",)]]),
}
BOSH = ["bugun", "kecha", "shu", "bu", "o'tgan"]
OXIR = ["yana", "juda", "ham", "tez", "ko'p"]
YORDAMCHI = ["va", "bu", "ham", "endi", "esa"]
def gap_yarat(rng):
"""Mavzu -> freym (Zipf bo'yicha) -> sinonim (75% / 25%) -> fon va
yordamchi so'zlar (hamma mavzuda bir xil, tez-tez uchraydi)."""
fon, freymlar = MAVZULAR[list(MAVZULAR)[int(rng.integers(0, 4))]]
w = 1.0 / np.arange(1, len(freymlar) + 1)
f = freymlar[rng.choice(len(freymlar), p=w / w.sum())]
sozlar = [t[0] if len(t) == 1 or rng.random() < 0.75 else t[1]
for t in f]
for _ in range(int(rng.integers(1, 3))):
sozlar.insert(int(rng.integers(0, len(sozlar) + 1)),
str(rng.choice(fon)))
if rng.random() < 0.6:
sozlar.insert(0, str(rng.choice(BOSH)))
if rng.random() < 0.3:
sozlar.insert(len(sozlar) - 1, str(rng.choice(OXIR)))
for _ in range(int(rng.integers(0, 6))):
sozlar.insert(int(rng.integers(0, len(sozlar) + 1)),
str(rng.choice(YORDAMCHI)))
return sozlar
def birga_uchrash(gaplar, idx, oyna=2):
C = np.zeros((len(idx), len(idx)))
for g in gaplar:
ids = [idx[s] for s in g]
for i, w in enumerate(ids):
for j in range(max(0, i - oyna), min(len(ids), i + oyna + 1)):
if j != i:
C[w, ids[j]] += 1
return C
def ppmi(C):
N = C.sum()
kutilgan = C.sum(1, keepdims=True) * C.sum(0, keepdims=True) / N
with np.errstate(divide="ignore"):
pmi = np.log(C / kutilgan)
return np.where(C > 0, np.maximum(pmi, 0), 0.0)
def kos(a, b):
return float(a @ b / (np.linalg.norm(a) * np.linalg.norm(b)))
def main() -> None:
rng = np.random.default_rng(0)
gaplar = [gap_yarat(rng) for _ in range(1000)]
lugat = sorted({s for g in gaplar for s in g})
idx = {s: i for i, s in enumerate(lugat)}
C = birga_uchrash(gaplar, idx)
print("=== 1. Korpus ===")
print(f" gaplar: {len(gaplar)}, so'zlar: {sum(map(len, gaplar))}, "
f"lug'at: {len(lugat)}")
for g in gaplar[:3]:
print(f" {' '.join(g)}")
soni = {s: sum(g.count(s) for g in gaplar)
for s in ["shifokor", "vrach", "bugun", "gol"]}
print(f" chastotalar: {soni}")
print(f" C shakli: {C.shape}, nol bo'lmagan kataklar: "
f"{(C > 0).mean():.1%}")
print("\n=== 2. Matritsa bo'lagi (xom sanoq) ===")
qator = ["shifokor", "vrach", "bemor", "futbolchi"]
ustun = ["bemor", "dori", "tekshirdi", "gol", "bugun", "tibbiyot"]
print(" " + " " * 10 + "".join(f"{u:>10}" for u in ustun))
for q in qator:
print(f" {q:<10}" + "".join(f"{C[idx[q], idx[u]]:>10.0f}"
for u in ustun))
print(f" shifokor va vrach bir gapda: "
f"{C[idx['shifokor'], idx['vrach']]:.0f} marta")
P = ppmi(C)
print("\n=== 3. 'shifokor' ning eng kuchli kontekstlari ===")
for nom, M in [("xom sanoq", C), ("PPMI", P)]:
top = np.argsort(-M[idx["shifokor"]], kind="stable")[:6]
print(f" {nom:<10} " + ", ".join(
f"{lugat[i]} ({M[idx['shifokor'], i]:.1f})" for i in top))
print("\n=== 4. Kosinus: xom sanoq va PPMI satrlari ===")
juftlar = [("shifokor", "vrach"), ("shifokor", "bemor"),
("shifokor", "futbolchi"), ("narx", "baho"),
("narx", "gol")]
print(f" {'juft':<22} {'xom':>7} {'PPMI':>7}")
natija = {}
for a, b in juftlar:
x = kos(C[idx[a]], C[idx[b]])
p = kos(P[idx[a]], P[idx[b]])
natija[(a, b)] = (x, p)
print(f" {a + ' - ' + b:<22} {x:>7.3f} {p:>7.3f}")
x_s, p_s = natija[("shifokor", "vrach")]
x_b, p_b = natija[("shifokor", "futbolchi")]
print(f"\n sinonim / boshqa mavzu nisbati: xom {x_s / x_b:.1f}, "
f"PPMI {p_s / p_b:.1f}")
if p_s / p_b > x_s / x_b:
print(" PPMI sinonimni boshqa mavzudan yaxshiroq ajratdi")
else:
print(" PPMI ajratishni yaxshilamadi")
print(" ⭐ Sinonimlar bir gapda deyarli kelmaydi - lekin kontekstlari bir xil")
if __name__ == "__main__":
main()Natijaning muhim qismi:
=== 1. Korpus ===
gaplar: 1000, so'zlar: 8324, lug'at: 100
bu shifokor bemor davolash klinika tekshirdi endi bu
kecha shifokor bemor sog'liq yana tekshirdi
jamoa ham turnirda final esa bu g'alaba endi esa final qozondi
chastotalar: {'shifokor': 138, 'vrach': 45, 'bugun': 117, 'gol': 127}
C shakli: (100, 100), nol bo'lmagan kataklar: 29.0%
=== 2. Matritsa bo'lagi (xom sanoq) ===
bemor dori tekshirdi gol bugun tibbiyot
shifokor 58 21 3 0 10 25
vrach 21 7 2 0 5 7
bemor 0 15 15 0 8 32
futbolchi 0 0 0 22 9 0
shifokor va vrach bir gapda: 0 marta
=== 3. 'shifokor' ning eng kuchli kontekstlari ===
xom sanoq bemor 58.0-bob, esa 39.0-bob, bu 34.0-bob, endi 34.0-bob, ham 31.0-bob, va 30.0-bob
PPMI kasal 2.1-bob, preparat 1.9-bob, sog'liq 1.8-bob, bemor 1.8-bob, davolash 1.7-bob, tibbiyot 1.6-bob
=== 4. Kosinus: xom sanoq va PPMI satrlari ===
juft xom PPMI
shifokor - vrach 0.941 0.960
shifokor - bemor 0.711 0.585
shifokor - futbolchi 0.397 0.053
narx - baho 0.910 0.822
narx - gol 0.192 0.000
sinonim / boshqa mavzu nisbati: xom 2.4, PPMI 18.0
PPMI sinonimni boshqa mavzudan yaxshiroq ajratdi
⭐ Sinonimlar bir gapda deyarli kelmaydi - lekin kontekstlari bir xilNima ko'rsatdi: 2.1, 2.2-bo'limlar.
Misol 2 — SVD bilan zich vektorlar
"""PPMI -> SVD: o'lchov k, sinonim topish va eng yaqin so'zlar."""
import numpy as np
MAVZULAR = {
"sport": (["o'yin", "mavsum", "chempionat", "sport", "final"], [
[("futbolchi", "o'yinchi"), ("darvozaga",), ("gol",),
("urdi", "kiritdi")],
[("jamoa", "komanda"), ("turnirda", "musobaqada"), ("g'alaba",),
("qozondi",)],
[("murabbiy", "trener"), ("futbolchi", "o'yinchi"), ("maqtadi",)],
[("jamoa", "komanda"), ("stadionda", "maydonda"), ("mashq",),
("qildi",)]]),
"iqtisod": (["bozor", "iqtisodiyot", "foiz", "hukumat", "davlat"], [
[("narx", "baho"), ("bozorda",), ("oshdi", "ko'tarildi")],
[("bank",), ("kredit", "qarz"), ("berdi",)],
[("hukumat",), ("soliqni",), ("kamaytirdi", "pasaytirdi")],
[("kompaniya", "firma"), ("pul", "mablag'"), ("sarfladi",)],
[("investor", "sarmoyador"), ("kompaniya", "firma"),
("qo'llab-quvvatladi",)]]),
"texnologiya": (["raqamli", "texnologiya", "tizim", "onlayn",
"yangilanish"], [
[("dasturchi", "muhandis"), ("dastur", "ilova"),
("yozdi", "yaratdi")],
[("foydalanuvchi",), ("telefon", "smartfon"), ("sotib",), ("oldi",)],
[("internet", "tarmoq"), ("tezligi",), ("oshdi", "ko'tarildi")],
[("kompaniya", "firma"), ("dastur", "ilova"), ("chiqardi",)]]),
"salomatlik": (["sog'liq", "tibbiyot", "kasallik", "davolash",
"klinika"], [
[("shifokor", "vrach"), ("bemor", "kasal"), ("ko'rdi", "tekshirdi")],
[("shifokor", "vrach"), ("dori", "preparat"), ("yozib",),
("berdi",)],
[("bemor", "kasal"), ("kasalxonada", "shifoxonada"),
("davolandi",)],
[("bemor", "kasal"), ("dori", "preparat"), ("ichdi",)]]),
}
BOSH = ["bugun", "kecha", "shu", "bu", "o'tgan"]
OXIR = ["yana", "juda", "ham", "tez", "ko'p"]
YORDAMCHI = ["va", "bu", "ham", "endi", "esa"]
def gap_yarat(rng):
fon, freymlar = MAVZULAR[list(MAVZULAR)[int(rng.integers(0, 4))]]
w = 1.0 / np.arange(1, len(freymlar) + 1)
f = freymlar[rng.choice(len(freymlar), p=w / w.sum())]
sozlar = [t[0] if len(t) == 1 or rng.random() < 0.75 else t[1]
for t in f]
for _ in range(int(rng.integers(1, 3))):
sozlar.insert(int(rng.integers(0, len(sozlar) + 1)),
str(rng.choice(fon)))
if rng.random() < 0.6:
sozlar.insert(0, str(rng.choice(BOSH)))
if rng.random() < 0.3:
sozlar.insert(len(sozlar) - 1, str(rng.choice(OXIR)))
for _ in range(int(rng.integers(0, 6))):
sozlar.insert(int(rng.integers(0, len(sozlar) + 1)),
str(rng.choice(YORDAMCHI)))
return sozlar
def sinonimlar():
return sorted({t for _, fr in MAVZULAR.values() for f in fr for t in f
if len(t) == 2})
def birga_uchrash(gaplar, idx, oyna=2):
C = np.zeros((len(idx), len(idx)))
for g in gaplar:
ids = [idx[s] for s in g]
for i, w in enumerate(ids):
for j in range(max(0, i - oyna), min(len(ids), i + oyna + 1)):
if j != i:
C[w, ids[j]] += 1
return C
def ppmi(C):
N = C.sum()
kutilgan = C.sum(1, keepdims=True) * C.sum(0, keepdims=True) / N
with np.errstate(divide="ignore"):
pmi = np.log(C / kutilgan)
return np.where(C > 0, np.maximum(pmi, 0), 0.0)
def oxshashlik(E):
En = E / np.linalg.norm(E, axis=1, keepdims=True)
S = En @ En.T
np.fill_diagonal(S, -np.inf)
return S
def sinonim_top1(S, idx, juftlar):
return np.mean([S[idx[a]].argmax() == idx[b] for a, b in juftlar])
def main() -> None:
rng = np.random.default_rng(0)
gaplar = [gap_yarat(rng) for _ in range(1000)]
lugat = sorted({s for g in gaplar for s in g})
idx = {s: i for i, s in enumerate(lugat)}
juftlar = sinonimlar()
C = birga_uchrash(gaplar, idx)
P = ppmi(C)
U, S_, _ = np.linalg.svd(P)
print("=== 1. Singular qiymatlar ===")
print(" birinchi 8: " + " ".join(f"{s:.1f}" for s in S_[:8]))
energiya = np.cumsum(S_ ** 2) / np.sum(S_ ** 2)
for k in (5, 20, 50):
print(f" k = {k:>2}: energiyaning {energiya[k - 1]:.1%} i")
print(f"\n=== 2. Sinonim top-1 ({len(juftlar)} juft) ===")
print(f" {'vektorlar':<22} {'o_lcham':>8} {'top-1':>7}")
Uc, Sc, _ = np.linalg.svd(C)
satrlar = [("xom sanoq satri", C, len(lugat)),
("PPMI satri", P, len(lugat))]
for k in (2, 5, 10, 20, 50):
satrlar.append((f"PPMI + SVD k={k}", U[:, :k] * S_[:k], k))
satrlar.append(("xom sanoq + SVD k=20", Uc[:, :20] * Sc[:20], 20))
top1 = {}
for nom, E, d in satrlar:
top1[nom] = sinonim_top1(oxshashlik(E), idx, juftlar)
print(f" {nom:<22} {d:>8} {top1[nom]:>7.3f}")
eng_k = max((k for k in (2, 5, 10, 20, 50)),
key=lambda k: (top1[f"PPMI + SVD k={k}"], -k))
print(f" eng yaxshi k (teng bo'lsa kichigi): {eng_k}")
print("\n=== 3. Eng yaqin so'zlar (PPMI + SVD k=20) ===")
S = oxshashlik(U[:, :20] * S_[:20])
for s in ["shifokor", "narx", "dastur", "jamoa", "bugun"]:
yaqin = np.argsort(-S[idx[s]], kind="stable")[:4]
print(f" {s:<9} -> " + ", ".join(
f"{lugat[i]} ({S[idx[s], i]:.2f})" for i in yaqin))
print("\n=== 4. Xato qilingan sinonimlar (k=20) ===")
xatolar = [(a, b) for a, b in juftlar if S[idx[a]].argmax() != idx[b]]
for a, b in xatolar[:4]:
eng = lugat[int(S[idx[a]].argmax())]
print(f" {a:<10} sinonimi {b:<11} eng yaqin: {eng}")
if not xatolar:
print(" hamma sinonimlar topildi")
print(" ⭐ SVD siqadi va umumlashtiradi - lekin k ni o'lchab tanlang")
if __name__ == "__main__":
main()Natijaning muhim qismi:
=== 1. Singular qiymatlar ===
birinchi 8: 23.9 21.5 19.0 18.8 11.7 11.4 11.3 10.2
k = 5: energiyaning 49.0% i
k = 20: energiyaning 79.9% i
k = 50: energiyaning 97.3% i
=== 2. Sinonim top-1 (23 juft) ===
vektorlar o_lcham top-1
xom sanoq satri 100 0.870
PPMI satri 100 1.000
PPMI + SVD k=2 2 0.043
PPMI + SVD k=5 5 0.739
PPMI + SVD k=10 10 0.957
PPMI + SVD k=20 20 1.000
PPMI + SVD k=50 50 1.000
xom sanoq + SVD k=20 20 0.783
eng yaxshi k (teng bo'lsa kichigi): 20
=== 3. Eng yaqin so'zlar (PPMI + SVD k=20) ===
shifokor -> vrach 1.00-bob, ichdi 0.83-bob, ko'rdi 0.81-bob, tibbiyot 0.78-bob
narx -> baho 0.97-bob, bozorda 0.83-bob, ko'tarildi 0.74-bob, oshdi 0.70-bob
dastur -> ilova 0.98-bob, texnologiya 0.84-bob, yangilanish 0.82-bob, raqamli 0.78-bob
jamoa -> komanda 0.97-bob, qildi 0.73-bob, g'alaba 0.71-bob, qozondi 0.66-bob
bugun -> shu 0.96-bob, kecha 0.88-bob, o'tgan 0.84-bob, bu 0.68-bob
=== 4. Xato qilingan sinonimlar (k=20) ===
hamma sinonimlar topildi
⭐ SVD siqadi va umumlashtiradi - lekin k ni o'lchab tanlangNima ko'rsatdi: 2.3, 2.5-bo'limlar.
Misol 3 — word2vec (skip-gram + negative sampling) noldan
"""SGNS ni torch da noldan o'rgatish va sinonimlarni o'lchash."""
import numpy as np
import torch
import torch.nn as nn
MAVZULAR = {
"sport": (["o'yin", "mavsum", "chempionat", "sport", "final"], [
[("futbolchi", "o'yinchi"), ("darvozaga",), ("gol",),
("urdi", "kiritdi")],
[("jamoa", "komanda"), ("turnirda", "musobaqada"), ("g'alaba",),
("qozondi",)],
[("murabbiy", "trener"), ("futbolchi", "o'yinchi"), ("maqtadi",)],
[("jamoa", "komanda"), ("stadionda", "maydonda"), ("mashq",),
("qildi",)]]),
"iqtisod": (["bozor", "iqtisodiyot", "foiz", "hukumat", "davlat"], [
[("narx", "baho"), ("bozorda",), ("oshdi", "ko'tarildi")],
[("bank",), ("kredit", "qarz"), ("berdi",)],
[("hukumat",), ("soliqni",), ("kamaytirdi", "pasaytirdi")],
[("kompaniya", "firma"), ("pul", "mablag'"), ("sarfladi",)],
[("investor", "sarmoyador"), ("kompaniya", "firma"),
("qo'llab-quvvatladi",)]]),
"texnologiya": (["raqamli", "texnologiya", "tizim", "onlayn",
"yangilanish"], [
[("dasturchi", "muhandis"), ("dastur", "ilova"),
("yozdi", "yaratdi")],
[("foydalanuvchi",), ("telefon", "smartfon"), ("sotib",), ("oldi",)],
[("internet", "tarmoq"), ("tezligi",), ("oshdi", "ko'tarildi")],
[("kompaniya", "firma"), ("dastur", "ilova"), ("chiqardi",)]]),
"salomatlik": (["sog'liq", "tibbiyot", "kasallik", "davolash",
"klinika"], [
[("shifokor", "vrach"), ("bemor", "kasal"), ("ko'rdi", "tekshirdi")],
[("shifokor", "vrach"), ("dori", "preparat"), ("yozib",),
("berdi",)],
[("bemor", "kasal"), ("kasalxonada", "shifoxonada"),
("davolandi",)],
[("bemor", "kasal"), ("dori", "preparat"), ("ichdi",)]]),
}
BOSH = ["bugun", "kecha", "shu", "bu", "o'tgan"]
OXIR = ["yana", "juda", "ham", "tez", "ko'p"]
YORDAMCHI = ["va", "bu", "ham", "endi", "esa"]
def gap_yarat(rng):
fon, freymlar = MAVZULAR[list(MAVZULAR)[int(rng.integers(0, 4))]]
w = 1.0 / np.arange(1, len(freymlar) + 1)
f = freymlar[rng.choice(len(freymlar), p=w / w.sum())]
sozlar = [t[0] if len(t) == 1 or rng.random() < 0.75 else t[1]
for t in f]
for _ in range(int(rng.integers(1, 3))):
sozlar.insert(int(rng.integers(0, len(sozlar) + 1)),
str(rng.choice(fon)))
if rng.random() < 0.6:
sozlar.insert(0, str(rng.choice(BOSH)))
if rng.random() < 0.3:
sozlar.insert(len(sozlar) - 1, str(rng.choice(OXIR)))
for _ in range(int(rng.integers(0, 6))):
sozlar.insert(int(rng.integers(0, len(sozlar) + 1)),
str(rng.choice(YORDAMCHI)))
return sozlar
def sinonimlar():
return sorted({t for _, fr in MAVZULAR.values() for f in fr for t in f
if len(t) == 2})
def mavzu_sozlari():
"""So'z -> u tegishli mavzular to'plami (umumiy so'zlarsiz)."""
m = {}
for nom, (fon, fr) in MAVZULAR.items():
for s in fon + [s for f in fr for t in f for s in t]:
m.setdefault(s, set()).add(nom)
return m
def juftlar_top(gaplar, idx, oyna=2):
m, c = [], []
for g in gaplar:
ids = [idx[s] for s in g]
for i, w in enumerate(ids):
for j in range(max(0, i - oyna), min(len(ids), i + oyna + 1)):
if j != i:
m.append(w)
c.append(ids[j])
return torch.tensor(m), torch.tensor(c)
class SkipGram(nn.Module):
def __init__(self, v, d):
super().__init__()
self.markaz = nn.Embedding(v, d)
self.kontekst = nn.Embedding(v, d)
nn.init.uniform_(self.markaz.weight, -0.5 / d, 0.5 / d)
nn.init.zeros_(self.kontekst.weight)
def forward(self, m, c, neg):
vm = self.markaz(m) # (B, d)
ijobiy = (vm * self.kontekst(c)).sum(1) # (B,)
salbiy = torch.bmm(self.kontekst(neg),
vm.unsqueeze(2)).squeeze(2) # (B, k)
return -(nn.functional.logsigmoid(ijobiy).mean()
+ nn.functional.logsigmoid(-salbiy).sum(1).mean())
def baho(E, idx, juftlar, mavzu):
En = E / np.linalg.norm(E, axis=1, keepdims=True)
S = En @ En.T
np.fill_diagonal(S, -np.inf)
sin = {frozenset(j) for j in juftlar}
soz = sorted(s for s in mavzu if s in idx)
bir, boshqa = [], []
for i, a in enumerate(soz):
for b in soz[i + 1:]:
if frozenset((a, b)) in sin:
continue
(bir if mavzu[a] & mavzu[b] else boshqa).append(
S[idx[a], idx[b]])
return (np.mean([S[idx[a]].argmax() == idx[b] for a, b in juftlar]),
np.mean([S[idx[a], idx[b]] for a, b in juftlar]),
np.mean(bir), np.mean(boshqa), S)
def main() -> None:
rng = np.random.default_rng(0)
gaplar = [gap_yarat(rng) for _ in range(1000)]
lugat = sorted({s for g in gaplar for s in g})
idx = {s: i for i, s in enumerate(lugat)}
M, Cx = juftlar_top(gaplar, idx)
soni = np.bincount(M.numpy(), minlength=len(lugat)).astype(float)
P = torch.tensor(soni ** 0.75 / (soni ** 0.75).sum(), dtype=torch.float)
V, d, k = len(lugat), 20, 5
print("=== 1. O'quv juftlari va manfiy namunalar ===")
print(f" lug'at {V}, (markaz, kontekst) juftlari: {len(M)}")
print(f" birinchi gap: {' '.join(gaplar[0])}")
print(f" undan juftlar: " + ", ".join(
f"({lugat[M[i]]}, {lugat[Cx[i]]})" for i in range(4)))
ko, kam = int(soni.argmax()), int(soni.argmin())
for nom, i in [("eng ko'p", ko), ("eng kam", kam)]:
print(f" {nom} uchragan '{lugat[i]}': chastota ulushi "
f"{soni[i] / soni.sum():.4f}, count^0.75 ulushi {P[i]:.4f}")
torch.manual_seed(0)
model = SkipGram(V, d)
mavzu = mavzu_sozlari()
juftlar = sinonimlar()
oldin = baho(model.markaz.weight.detach().numpy().copy(), idx,
juftlar, mavzu)
print("\n=== 2. O'rgatish (Adam lr=0.01, batch 1024, k=5) ===")
opt = torch.optim.Adam(model.parameters(), lr=0.01)
g = torch.Generator().manual_seed(0)
qadam, jami, n = 0, 0.0, 0
while qadam < 600:
tartib = torch.randperm(len(M), generator=g)
for i in range(0, len(M), 1024):
b = tartib[i:i + 1024]
neg = torch.multinomial(P, len(b) * k, replacement=True,
generator=g).view(len(b), k)
opt.zero_grad()
loss = model(M[b], Cx[b], neg)
loss.backward()
opt.step()
jami, n, qadam = jami + loss.item(), n + 1, qadam + 1
if qadam in (1, 50, 150, 300, 600):
print(f" qadam {qadam:>4}: o'rtacha loss {jami / n:.4f}")
jami, n = 0.0, 0
if qadam >= 600:
break
print(f" boshlang'ich loss nazariyasi: (1 + k) * log 2 = "
f"{(1 + k) * np.log(2):.4f}")
E = model.markaz.weight.detach().numpy()
top1, sin, bir, boshqa, S = baho(E, idx, juftlar, mavzu)
print("\n=== 3. Eng yaqin so'zlar ===")
for s in ["shifokor", "narx", "dastur", "jamoa", "bugun"]:
yaqin = np.argsort(-S[idx[s]], kind="stable")[:4]
print(f" {s:<9} -> " + ", ".join(
f"{lugat[i]} ({S[idx[s], i]:.2f})" for i in yaqin))
print(f"\n=== 4. Sinonimlar ({len(juftlar)} juft) ===")
print(f" {'':<24} {'oldin':>8} {'keyin':>8}")
print(f" {'top-1 sinonim':<24} {oldin[0]:>8.3f} {top1:>8.3f}")
print(f" {'cos sinonimlar':<24} {oldin[1]:>8.3f} {sin:>8.3f}")
print(f" {'cos bir mavzu':<24} {oldin[2]:>8.3f} {bir:>8.3f}")
print(f" {'cos boshqa mavzu':<24} {oldin[3]:>8.3f} {boshqa:>8.3f}")
if sin > bir > boshqa:
print(" tartib: sinonim > bir mavzu > boshqa mavzu - "
"taqsimot gipotezasi ishladi")
else:
print(" kutilgan tartib buzildi")
print(" ⭐ Vektorlar faqat 'qo'shnini tanish' o'yinidan hosil bo'ldi")
if __name__ == "__main__":
main()Natijaning muhim qismi:
=== 1. O'quv juftlari va manfiy namunalar ===
lug'at 100, (markaz, kontekst) juftlari: 27296
birinchi gap: bu shifokor bemor davolash klinika tekshirdi endi bu
undan juftlar: (bu, shifokor), (bu, bemor), (shifokor, bu), (shifokor, bemor)
eng ko'p uchragan 'bu': chastota ulushi 0.0696, count^0.75 ulushi 0.0470
eng kam uchragan 'mablag'': chastota ulushi 0.0010, count^0.75 ulushi 0.0019
=== 2. O'rgatish (Adam lr=0.01, batch 1024, k=5) ===
qadam 1: o'rtacha loss 4.1589
qadam 50: o'rtacha loss 3.1764
qadam 150: o'rtacha loss 2.6449
qadam 300: o'rtacha loss 2.2980
qadam 600: o'rtacha loss 2.1538
boshlang'ich loss nazariyasi: (1 + k) * log 2 = 4.1589
=== 3. Eng yaqin so'zlar ===
shifokor -> vrach 0.99-bob, ichdi 0.84-bob, tibbiyot 0.80-bob, tekshirdi 0.80-bob
narx -> baho 0.93-bob, hukumat 0.77-bob, bozorda 0.75-bob, davlat 0.73-bob
dastur -> ilova 0.96-bob, dasturchi 0.73-bob, texnologiya 0.71-bob, muhandis 0.71-bob
jamoa -> komanda 0.97-bob, qozondi 0.78-bob, g'alaba 0.75-bob, musobaqada 0.75-bob
bugun -> kecha 0.92-bob, o'tgan 0.90-bob, shu 0.90-bob, bu 0.72-bob
=== 4. Sinonimlar (23 juft) ===
oldin keyin
top-1 sinonim 0.000 1.000
cos sinonimlar 0.043 0.969
cos bir mavzu 0.016 0.637
cos boshqa mavzu -0.001 0.261
tartib: sinonim > bir mavzu > boshqa mavzu - taqsimot gipotezasi ishladi
⭐ Vektorlar faqat 'qo'shnini tanish' o'yinidan hosil bo'ldiNima ko'rsatdi: 2.4, 2.5-bo'limlar.
Misol 4 — Embedding sifati va korpus hajmi
"""Bir xil model, bir xil qadamlar: 50 dan 2000 gapgacha korpus."""
import numpy as np
import torch
import torch.nn as nn
MAVZULAR = {
"sport": (["o'yin", "mavsum", "chempionat", "sport", "final"], [
[("futbolchi", "o'yinchi"), ("darvozaga",), ("gol",),
("urdi", "kiritdi")],
[("jamoa", "komanda"), ("turnirda", "musobaqada"), ("g'alaba",),
("qozondi",)],
[("murabbiy", "trener"), ("futbolchi", "o'yinchi"), ("maqtadi",)],
[("jamoa", "komanda"), ("stadionda", "maydonda"), ("mashq",),
("qildi",)]]),
"iqtisod": (["bozor", "iqtisodiyot", "foiz", "hukumat", "davlat"], [
[("narx", "baho"), ("bozorda",), ("oshdi", "ko'tarildi")],
[("bank",), ("kredit", "qarz"), ("berdi",)],
[("hukumat",), ("soliqni",), ("kamaytirdi", "pasaytirdi")],
[("kompaniya", "firma"), ("pul", "mablag'"), ("sarfladi",)],
[("investor", "sarmoyador"), ("kompaniya", "firma"),
("qo'llab-quvvatladi",)]]),
"texnologiya": (["raqamli", "texnologiya", "tizim", "onlayn",
"yangilanish"], [
[("dasturchi", "muhandis"), ("dastur", "ilova"),
("yozdi", "yaratdi")],
[("foydalanuvchi",), ("telefon", "smartfon"), ("sotib",), ("oldi",)],
[("internet", "tarmoq"), ("tezligi",), ("oshdi", "ko'tarildi")],
[("kompaniya", "firma"), ("dastur", "ilova"), ("chiqardi",)]]),
"salomatlik": (["sog'liq", "tibbiyot", "kasallik", "davolash",
"klinika"], [
[("shifokor", "vrach"), ("bemor", "kasal"), ("ko'rdi", "tekshirdi")],
[("shifokor", "vrach"), ("dori", "preparat"), ("yozib",),
("berdi",)],
[("bemor", "kasal"), ("kasalxonada", "shifoxonada"),
("davolandi",)],
[("bemor", "kasal"), ("dori", "preparat"), ("ichdi",)]]),
}
BOSH = ["bugun", "kecha", "shu", "bu", "o'tgan"]
OXIR = ["yana", "juda", "ham", "tez", "ko'p"]
YORDAMCHI = ["va", "bu", "ham", "endi", "esa"]
def gap_yarat(rng):
fon, freymlar = MAVZULAR[list(MAVZULAR)[int(rng.integers(0, 4))]]
w = 1.0 / np.arange(1, len(freymlar) + 1)
f = freymlar[rng.choice(len(freymlar), p=w / w.sum())]
sozlar = [t[0] if len(t) == 1 or rng.random() < 0.75 else t[1]
for t in f]
for _ in range(int(rng.integers(1, 3))):
sozlar.insert(int(rng.integers(0, len(sozlar) + 1)),
str(rng.choice(fon)))
if rng.random() < 0.6:
sozlar.insert(0, str(rng.choice(BOSH)))
if rng.random() < 0.3:
sozlar.insert(len(sozlar) - 1, str(rng.choice(OXIR)))
for _ in range(int(rng.integers(0, 6))):
sozlar.insert(int(rng.integers(0, len(sozlar) + 1)),
str(rng.choice(YORDAMCHI)))
return sozlar
def sinonimlar():
return sorted({t for _, fr in MAVZULAR.values() for f in fr for t in f
if len(t) == 2})
def juftlar_top(gaplar, idx, oyna=2):
m, c = [], []
for g in gaplar:
ids = [idx[s] for s in g]
for i, w in enumerate(ids):
for j in range(max(0, i - oyna), min(len(ids), i + oyna + 1)):
if j != i:
m.append(w)
c.append(ids[j])
return torch.tensor(m), torch.tensor(c)
class SkipGram(nn.Module):
def __init__(self, v, d):
super().__init__()
self.markaz = nn.Embedding(v, d)
self.kontekst = nn.Embedding(v, d)
nn.init.uniform_(self.markaz.weight, -0.5 / d, 0.5 / d)
nn.init.zeros_(self.kontekst.weight)
def forward(self, m, c, neg):
vm = self.markaz(m)
ijobiy = (vm * self.kontekst(c)).sum(1)
salbiy = torch.bmm(self.kontekst(neg), vm.unsqueeze(2)).squeeze(2)
return -(nn.functional.logsigmoid(ijobiy).mean()
+ nn.functional.logsigmoid(-salbiy).sum(1).mean())
def sgns(gaplar, idx, seed, d=20, k=5, qadamlar=300):
M, C = juftlar_top(gaplar, idx)
soni = np.bincount(M.numpy(), minlength=len(idx)).astype(float)
P = torch.tensor(soni ** 0.75 / (soni ** 0.75).sum(), dtype=torch.float)
torch.manual_seed(seed)
model = SkipGram(len(idx), d)
opt = torch.optim.Adam(model.parameters(), lr=0.02)
g = torch.Generator().manual_seed(seed)
qadam = 0
while qadam < qadamlar:
tartib = torch.randperm(len(M), generator=g)
for i in range(0, len(M), 1024):
b = tartib[i:i + 1024]
neg = torch.multinomial(P, len(b) * k, replacement=True,
generator=g).view(len(b), k)
opt.zero_grad()
model(M[b], C[b], neg).backward()
opt.step()
qadam += 1
if qadam >= qadamlar:
break
return model.markaz.weight.detach().numpy()
def top1(E, idx, juftlar):
En = E / np.linalg.norm(E, axis=1, keepdims=True)
S = En @ En.T
np.fill_diagonal(S, -np.inf)
return np.array([S[idx[a]].argmax() == idx[b] for a, b in juftlar])
def main() -> None:
hajmlar = [50, 150, 500, 2000]
seedlar = [0, 1, 2]
barcha = sinonimlar()
print("=== 1. Korpus hajmi va sinonim top-1 (3 seed) ===")
print(f" {'gaplar':>6} {'lug_at':>7} {'juftlar':>8} "
f"{'noyob sinonim':>14} {'top-1':>7} {'SE':>6}")
natija = {}
for n in hajmlar:
qiymat, noyob, v, nj = [], [], [], []
for s in seedlar:
rng = np.random.default_rng(100 + s)
gaplar = [gap_yarat(rng) for _ in range(n)]
lugat = sorted({w for g in gaplar for w in g})
idx = {w: i for i, w in enumerate(lugat)}
juftlar = [j for j in barcha if j[0] in idx and j[1] in idx]
v.append(len(lugat))
nj.append(len(juftlar))
soni = {w: sum(g.count(w) for g in gaplar) for w in lugat}
noyob.append(np.median([soni[b] for _, b in juftlar]))
E = sgns(gaplar, idx, seed=s)
t = top1(E, idx, juftlar)
qiymat.append(t.mean())
q = np.array(qiymat)
natija[n] = q
se = q.std(ddof=1) / np.sqrt(len(q))
print(f" {n:>6} {np.mean(v):>7.0f} {np.mean(nj):>5.1f}/{len(barcha)} "
f"{np.median(noyob):>14.0f} {q.mean():>7.3f} {se:>6.3f}")
print(" (lug'at, juftlar - 3 seed o'rtachasi; noyob sinonim - ikkinchi,")
print(" kam uchraydigan variantning mediana chastotasi)")
print("\n=== 2. Qo'shni hajmlar orasidagi farq ===")
for a, b in zip(hajmlar, hajmlar[1:]):
f = natija[b] - natija[a]
se = f.std(ddof=1) / np.sqrt(len(f))
belgi = "sezilarli" if f.mean() > 2 * se else "sezilarli emas"
print(f" {a:>5} -> {b:>5}: {f.mean():+.3f} SE {se:.3f} {belgi}")
print(" ⭐ Bir xil model - farq faqat ma'lumot hajmidan")
if __name__ == "__main__":
main()Natijaning muhim qismi:
=== 1. Korpus hajmi va sinonim top-1 (3 seed) ===
gaplar lug_at juftlar noyob sinonim top-1 SE
50 85 12.3/23 2 0.139 0.032
150 97 21.0/23 3 0.413 0.008
500 100 23.0/23 10 0.899 0.038
2000 100 23.0/23 46 1.000 0.000
(lug'at, juftlar - 3 seed o'rtachasi; noyob sinonim - ikkinchi,
kam uchraydigan variantning mediana chastotasi)
=== 2. Qo'shni hajmlar orasidagi farq ===
50 -> 150: +0.274 SE 0.024 sezilarli
150 -> 500: +0.486 SE 0.035 sezilarli
500 -> 2000: +0.101 SE 0.038 sezilarli
⭐ Bir xil model - farq faqat ma'lumot hajmidanNima ko'rsatdi: 2.6-bo'lim.
5. To'g'ri va noto'g'ri tushunishlar
| Noto'g'ri fikr | To'g'risi |
|---|---|
| "Embedding so'z ma'nosini tushunadi" | U kontekst statistikasini siqadi — foydalanish o'xshashligi |
| "Sinonimlar bir gapda birga keladi" | Deyarli hech qachon — ular bir xil kontekstlarda keladi |
| "Xom sanoq yetarli" | Ko'p uchraydigan so'zlar ustunlik qiladi — PPMI kerak |
| "SVD o'lchami qancha katta bo'lsa, shuncha yaxshi" | Kichik singular qiymatlar shovqin; k o'lchab tanlanadi |
| "word2vec va PPMI butunlay boshqa narsa" | SGNS yashirin tarzda siljitilgan PMI ni faktorlaydi |
| "Analogiya har embeddingda ishlaydi" | Faqat katta korpus va muntazam munosabatlarda |
| "Yaqin vektor — sinonim" | Antonimlar ham yaqin bo'lishi mumkin |
| "Kichik korpusda ham barcha so'zlar ishonchli" | Kam uchraydigan so'zlar vektori shovqinli |
6. Keng tarqalgan xatolar va yechimlari
1. log(0)
pmi = np.log(C * N / (qator * ustun)) # -inf va ogohlantirish # ⚠️
ppmi = np.where(C > 0, np.maximum(pmi, 0), 0) # errstate bilan # ✅2. Eng yaqinlar ichida so'zning o'zi
S[w].argsort()[::-1][:5] # birinchisi - o'zi # ⚠️
np.fill_diagonal(S, -np.inf); S[w].argsort()[::-1][:5] # ✅3. Evklid masofasi
np.linalg.norm(E - E[w], axis=1).argsort() # uzunlik = chastota # ⚠️
En = E / np.linalg.norm(E, axis=1, keepdims=True); En @ En[w] # ✅4. Tekis manfiy namunalar
neg = torch.randint(0, V, (B, k)) # ⚠️
neg = torch.multinomial(soni ** 0.75 / Z, B * k, replacement=True) # ✅5. Kontekst jadvalini so'z vektori deb olish
E = model.kontekst.weight # nol bilan boshlangan # ⚠️
E = model.markaz.weight # (yoki ikkalasi yig'indisi) # ✅6. Tasodifiy bazaviysiz baholash
print(cos(E[a], E[b])) # 0.7 - yaxshimi? # ⚠️
# o'rgatishdan OLDIN va boshqa mavzu juftlari bilan solishtiring # ✅7. Kam uchraydigan so'zlarga ishonish
yaqin(E, "noyob_soz") # 3 marta uchragan # ⚠️
if soni[w] >= 5: yaqin(E, w) # min_count # ✅7. Integratsiya — bu bilim qayerda kerak bo'ladi
- 10-qism (o'tilgan): SVD va past rangli yaqinlashish
- 21.10-dars (o'tilgan):
nn.Embedding— indeks bo'yicha satr jadvali - 23.4-dars (o'tilgan): TF-IDF va IDF g'oyasi — PPMI ning qarindoshi
- Keyingi dars: embedding bilan klassifikatsiya — o'zimiz o'rgatgan vektorlarni muzlatish va fine-tune
- Transformerlar qismida: kontekstga bog'liq embeddinglar — bir so'z turli gapda turli vektor
- Katta til modellari qismida: embedding bilan qidiruv va RAG
8. Eng yaxshi amaliyotlar
Xom sanoq o'rniga PPMI ishlating.
SVD
kni validatsiya vazifasida tanlang.Kosinus o'xshashlik va normallangan vektorlar bilan ishlang.
Manfiy namunalarni
count^0.75taqsimotidan oling.Embeddingni tasodifiy bazaviy va mavzu juftlari bilan solishtiring.
Eng yaqin qo'shnilarni ko'zdan kechiring — sonlar yetarli emas.
Kam uchraydigan so'zlarni
min_countbilan filtrlang.Korpus hajmi bo'yicha sifat egri chizig'ini o'lchang.
9. Amaliy topshiriq
Vazifa 1: Bashorat qiling
1. # one-hot da ikki xil so'z orasidagi kosinus?
2. # C[w, c] nimani sanaydi?
3. # PMI formulasi?
4. # nima uchun PPMI da manfiy qiymatlar tashlanadi?
5. # SVD dan keyingi so'z vektori?
6. # skip-gram nimani bashorat qiladi?
7. # negative sampling loss formulasi?
8. # manfiy namunalar taqsimoti?
9. # SGNS boshlang'ich loss i (k = 5)?
10. # nega kosinus, Evklid emas?
11. # analogiya formulasi?
12. # nega kam uchraydigan sinonim birinchi zarar ko'radi?Javoblar
- 0
cso'ziwdan±oynamasofada necha marta kelganilog(P(w, c) / (P(w) P(c)))- Ular kam ma'lumotda ishonchsiz; nol sanoq
-infberadi U[:, :k] * S[:k]- Markaziy so'zdan uning qo'shnilarini
-log sigmoid(u_c · v_w) - sum log sigmoid(-u_n · v_w)count^0.75ga proporsional(1 + k) · log 2 = 6 · 0.693 ≈ 4.159- Vektor uzunligi chastotani aks ettiradi, yo'nalish — ma'noni
vec(b) - vec(a) + vec(c) ≈ vec(d)- Uning kontekstlari kam — statistikasi shovqinli
Vazifa 2: Xatolarni tuzating
1. pmi = np.log(C * N / np.outer(C.sum(1), C.sum(0)))
ppmi = np.maximum(pmi, 0)
2. yaqin = (En @ En[w]).argsort()[::-1][:5]
3. neg = torch.randint(0, V, (B, 5))
4. E = model.kontekst.weight.detach().numpy()
5. masofa = np.linalg.norm(E - E[w], axis=1); yaqin = masofa.argsort()[:5]Javoblar
1. with np.errstate(divide="ignore"):
pmi = np.log(C * N / np.outer(C.sum(1), C.sum(0)))
ppmi = np.where(C > 0, np.maximum(pmi, 0), 0.0)
2. s = En @ En[w]; s[w] = -np.inf
yaqin = s.argsort()[::-1][:5]
3. P = torch.tensor(soni ** 0.75 / (soni ** 0.75).sum())
neg = torch.multinomial(P, B * 5, replacement=True).view(B, 5)
4. E = model.markaz.weight.detach().numpy()
5. En = E / np.linalg.norm(E, axis=1, keepdims=True)
s = En @ En[w]; s[w] = -np.inf; yaqin = s.argsort()[::-1][:5]Vazifa 3: Birga uchrash va PPMI
Modellang:
- Matritsa
- Xom kontekstlar
- PPMI kontekstlari
- Kosinus taqqoslash
Vazifa 4: SVD
Modellang:
- Singular qiymatlar
kbo'yicha top-1- Eng yaqin so'zlar
- Xatolar
Vazifa 5: word2vec
Modellang:
- Juftlar
- Manfiy taqsimot
- O'rgatish
- Oldin va keyin
Vazifa 6: Korpus hajmi
Modellang:
- Hajmlar
- Seedlar
- Top-1 va SE
- Qo'shni farqlar
Vazifa 7: O'ylash
Kompaniyangiz o'zbekcha yangiliklar arxividan (taxminan 20 000 gap) word2vec o'rgatdi. Mahsulot jamoasi so'radi: "Qidiruvda foydalanuvchi 'vrach' deb yozsa, 'shifokor' haqidagi maqolalar ham chiqsin — shu vektorlardan sinonim lug'ati tuzsak bo'ladimi?" Nima deysiz?
Javob
Qisqa javob: qisman ha — lekin avtomatik "sinonim lug'ati" sifatida emas, tekshiruvdan o'tgan nomzodlar ro'yxati sifatida.
1. Embedding "almashtirsa bo'ladigan" so'zlarni topadi. "vrach" ning eng yaqin qo'shnilari orasida "shifokor" bo'lishi ehtimoli katta. Lekin u yerda "bemor", "hamshira" yoki hatto antonim ma'noli so'zlar ham bo'ladi — ular ham bir xil kontekstlarda keladi. "narx oshdi" va "narx tushdi" — kontekstlari bir xil, ma'nosi qarama-qarshi. Qidiruvda "tushdi" so'rovi "oshdi" maqolalarini chiqarsa — xato.
2. Korpus hajmi. 20 000 gap — word2vec uchun kichik. 4-misolda ko'rdik: kichik korpusda kam uchraydigan sinonimlarning vektori ishonchsiz, top-1 sinonim topish ulushi hajm bilan keskin o'sdi. Arxivda "vrach" "shifokor" dan ancha kam uchrasa, uning vektori eng shovqinli bo'ladi.
soni = Counter(soz for gap in gaplar for soz in gap)
ishonchli = [w for w in lugat if soni[w] >= 50] # faqat shular uchun3. Qanday qilish kerak:
# 1. har so'z uchun top-5 qo'shni (kosinus > chegara)
# 2. faqat ikki tomonlama juftlar: a -> b va b -> a
# 3. muharrir tekshiruvi: nomzodlarni tasdiqlash / rad etish
# 4. qidiruvda: asl so'z to'liq og'irlik, sinonim - kamaytirilgan og'irlik4. O'lchash. Qidiruv sifatini sinonimlar qo'shilishidan oldin va keyin real so'rovlarda o'lchang: topilgan maqolalarning qanchasi haqiqatan tegishli (precision), qanchasi yangi topildi (recall).
5. Muqobil. Katta umumiy korpusda (millionlab gap) o'rgatilgan vektorlar yoki kontekstga bog'liq modellar (Transformerlar qismida) sinonimlarni ancha ishonchliroq ajratadi.
Jamoaga javob: "Vektorlardan sinonim nomzodlarini chiqaramiz, lekin ularni muharrir tasdiqlaydi — chunki embedding antonimlarni va bir mavzudagi so'zlarni ham yaqin qo'yadi. Faqat yetarlicha ko'p uchragan so'zlar uchun ishlatamiz va qidiruv sifatini o'lchab turib qo'shamiz."
Nimani mustahkamlaydi: 2.5, 2.6-bo'limlar.
Xulosa
Bu darsda so'z embeddinglarini ikki yo'l bilan noldan qurdik: sanash (PPMI + SVD) va bashorat (word2vec).
Eng muhim uch fikr:
Taqsimot gipotezasi — kontekst o'xshashligi ma'no o'xshashligini beradi. 1-misoldagi 1000 gaplik korpusda "shifokor" va "vrach" bir marta ham bir gapda kelmadi — lekin ularning birga uchrash satrlari orasidagi kosinus
0.941(PPMI da0.960) chiqdi: kontekstlari bir xil. Xom sanoqda "shifokor" ning eng kuchli kontekstlari "bemor" dan keyin "esa", "bu", "endi" kabi yordamchi so'zlar bo'ldi, shuning uchun hatto "shifokor" va "futbolchi" ham0.397o'xshashlik oldi. PPMI bu umumiy so'zlarni chetga surdi: "shifokor – futbolchi"0.053ga tushdi va sinonim bilan boshqa mavzu orasidagi nisbat2.4dan18.0ga oshdi.SVD va word2vec bir statistikani siqadi. 2-misolda PPMI satrlari 23 sinonim juftining hammasini topdi (top-1
1.000), xom sanoq satrlari —0.870. SVD bilan 100 o'lchovli satrlar 20 o'lchovga siqilganda ham top-11.000qoldi, lekink = 2da0.043,k = 5da0.739— juda kichikkmavzularni aralashtiradi. Xom sanoq ustidan SVD esak = 20da atigi0.783berdi: siqishdan oldin tozalash (PPMI) muhim. 3-misolda torch da noldan yozilgan skip-gram + negative sampling 600 qadamda loss ni nazariy4.1589dan2.15ga tushirdi va xuddi shunday top-11.000ga yetdi; o'rtacha kosinus sinonimlarda0.969, bir mavzudagi so'zlarda0.637, boshqa mavzularda0.261— o'rgatishdan oldin uchalasi ham nolga yaqin edi.Sifat korpus hajmiga bog'liq. 4-misolda bir xil model va bir xil qadamlar soni bilan faqat korpus hajmini o'zgartirdik. 50 gapda 23 juftdan o'rtacha faqat 12.3 tasining ikkala so'zi ham korpusda uchradi, ularning esa atigi
0.139qismida sinonim eng yaqin qo'shni chiqdi; 150 gapda0.413, 500 gapda0.899, 2000 gapda1.000. Har qadamdagi o'sish2 × SEdan katta. Sabab — kam uchraydigan sinonim varianti: 150 gapda u mediana bo'yicha 3 marta, 2000 gapda 46 marta uchradi. Real tilda lug'at minglab marta katta, shuning uchun word2vec klassik ravishda milliardlab so'zda o'rgatiladi — va keyingi darsda ko'ramizki, kichik vazifada tayyor embeddingdan foyda bor-yo'qligini ham o'lchash kerak.
Keyingi darsda embedding bilan klassifikatsiya: nn.Embedding va o'rtacha pooling, nn.EmbeddingBag, padding va maska, va o'zimiz o'rgatgan embeddingni muzlatish yoki fine-tune qilish — TF-IDF bazaviysi bilan halol taqqoslash.
Izohlar (0)
Izoh yozish uchun kiring.
- Hozircha izoh yo'q. Birinchi bo'ling!