Mundarija (21)
- 1. Kirish va motivatsiya
- 2. Nazariya — chuqur tushuntirish
- 2.1. Embedding nima
- 2.2. Embedding va one-hot
- 2.3. Son va kategoriyani birlashtirish
- 2.4. Embedding o'lchamini tanlash
- 2.5. Noma'lum va kam uchraydigan kategoriyalar
- 2.6. Halol taqqoslash
- 2.7. Tuzoqlar
- 3. Tez ma'lumotnoma
- 4. Batafsil misollar
- Misol 1 — Embedding ichidan
- Misol 2 — Lug'at, noma'lum va kam uchraydigan kategoriyalar
- Misol 3 — Embedding va one-hot o'rgatishda
- Misol 4 — To'liq tabular tarmoq va HistGradientBoosting
- 5. To'g'ri va noto'g'ri tushunishlar
- 6. Keng tarqalgan xatolar va yechimlari
- 7. Integratsiya — bu bilim qayerda kerak bo'ladi
- 8. Eng yaxshi amaliyotlar
- 9. Amaliy topshiriq
- Xulosa
21.10-dars: Tabular ma'lumot va embedding
21-QISM — PYTORCH · 10-dars
1. Kirish va motivatsiya
20.12-darsda ko'rdik: tabular ma'lumotda tarmoq avtomatik tanlov emas, HistGradientBoosting ko'pincha undan yaxshi. Lekin bitta holat bor, bu yerda tarmoq o'z kuchini ko'rsata oladi: ko'p darajali kategoriyalar.
Mahsulot kodi (50 000 xil), foydalanuvchi ID si, pochta indeksi, do'kon raqami — bunday belgilarni one-hot bilan kodlash 50 000 ustunli siyrak matritsa beradi. target encoding (17-qism) ularni bitta songa siqadi va ma'lumotni yo'qotadi. Embedding esa har kategoriyaga kichik o'rganiladigan vektor beradi: 50 000 kategoriya × 16 o'lchov. Bu vektorlar o'rgatish davomida shunday joylashadiki, o'xshash ta'sirli kategoriyalar yaqin bo'lib qoladi.
nn.Embedding — texnik jihatdan juda oddiy narsa: bu indeks bo'yicha satr oladigan jadval. Embedding(n, d) ning og'irligi (n, d) matritsa, emb(torch.tensor([5])) esa uning 5-satri. Lekin shu oddiy vosita tarmoqqa kategoriyalar orasidagi o'xshashlikni o'zi topish imkonini beradi.
Bu darsda Embedding ni ichidan ko'ramiz, uni one-hot bilan taqqoslaymiz, son va kategoriyali belgilarni bitta modelda birlashtiramiz va natijani HistGradientBoosting bilan halol solishtiramiz.
Real vaziyat. Chakana savdo kompaniyasi 3 000 do'kon uchun sotuvni bashorat qilardi. one-hot bilan model 3 000 ustunli bo'ldi va kam sotuvli do'konlarda yomon ishladi. Do'konlarga 8 o'lchovli embedding berilgach, model o'xshash do'konlarni o'zi guruhladi — va kam ma'lumotli do'konlar "qo'shnilari" dan foydalana boshladi.
Bu darsda kategoriyalar uchun embedding ni o'rganamiz.
Bu darsda:
-
nn.Embeddingnima - Embedding va one-hot
- Son va kategoriyali belgilarni birlashtirish
- Embedding o'lchamini tanlash
- Noma'lum kategoriya
- Tuzoqlar
- Amaliy: tabular tarmoq
ℹ Misollar real torch/sklearn bilan (Python 3.14, torch 2.14 CPU).
2. Nazariya — chuqur tushuntirish
2.1. Embedding nima
nn.Embedding(n_kategoriya, d)
og'irlik: (n_kategoriya, d) matritsa
kirish: butun sonlar (indekslar), shakl (B,) yoki (B, L)
chiqish: (B, d) yoki (B, L, d)
emb(torch.tensor([3, 0, 3])) -> og'irlikning 3-, 0-, 3-satrlari
MATEMATIK JIHATDAN:
embedding = one_hot(i) @ W
lekin one-hot matritsa HECH QACHON quriladi - to'g'ridan-to'g'ri satr
-> xotira va hisob tejaladi
GRADIENT:
faqat batchda UCHRAGAN satrlar yangilanadi
(siyrak gradient)
KIRISH TURI: torch.long (int64) - float EMAS Embedding — one_hot @ W ning tejamkor ko'rinishi; natija bir xil, xarajat keskin kam.
2.2. Embedding va one-hot
ONE-HOT EMBEDDING
o'lcham n ustun d ustun (d << n)
parametrlar n * yashirin n * d (+ d * yashirin)
o'xshashlik yo'q (hamma teng) O'RGANILADI
yangi kategoriya hamma nol maxsus indeks 2.5-bob
kam uchraydigan o'z og'irligi, kam qo'shnilarga yaqinlashadi
ma'lumot bilan
QACHON ONE-HOT:
kategoriya kam (< 10-20)
chiziqli model / daraxt
QACHON EMBEDDING:
kategoriya ko'p (> 50)
tarmoq
kategoriyalar orasida yashirin o'xshashlik bor Kam kategoriyada one-hot, ko'pida embedding — chegara taxminan 20-50 daraja.
2.3. Son va kategoriyani birlashtirish
class TabularTarmoq(nn.Module):
def __init__(self, n_son, kat_hajmlari, d_emb):
self.embler = nn.ModuleList(
[nn.Embedding(n, d) for n, d in zip(kat_hajmlari, d_emb)])
self.tarmoq = nn.Sequential(
nn.Linear(n_son + sum(d_emb), 128), ...)
def forward(self, son, kat): # kat: (B, n_kat) long
e = [emb(kat[:, i]) for i, emb in enumerate(self.embler)]
return self.tarmoq(torch.cat([son] + e, dim=1))
TARTIB:
son belgilar - masshtablangan (StandardScaler)
kategoriyalar - 0..n-1 indekslarga aylantirilgan
embedding chiqishlari son belgilar bilan CONCAT Har kategoriyali ustunga o'z Embedding, keyin hammasi son belgilar bilan birlashtiriladi.
2.4. Embedding o'lchamini tanlash
EMPIRIK QOIDALAR:
d = min(50, (n + 1) // 2) (fast.ai)
d = round(1.6 * n ** 0.56) (fast.ai yangi)
AMALDA:
n = 10 -> d = 4..5
n = 100 -> d = 8..16
n = 10000 -> d = 32..64
KATTA d:
ko'proq parametr, yodlash xavfi (kam uchraydigan kategoriyalarda)
KICHIK d:
kategoriyalar orasidagi farqni ifodalay olmaydi
QOIDA: formulani boshlang'ich nuqta sifatida oling, validatsiyada tekshiring d — giperparametr; formulalar faqat boshlang'ich nuqta.
2.5. Noma'lum va kam uchraydigan kategoriyalar
MUAMMO: o'quvda ko'rilmagan kategoriya -> indeks yo'q -> IndexError
YECHIM: maxsus "noma'lum" indeksi
kodlash: {kat: i + 1 for i, kat in enumerate(oquv_kategoriyalar)}
noma'lum -> 0
nn.Embedding(n + 1, d)
KAM UCHRAYDIGAN KATEGORIYALAR:
5 martadan kam uchraganlarni ham 0 ga (yoki "boshqa" ga) birlashtirish
-> embedding ma'lumotsiz satrlarni yodlamaydi
KODLASH FAQAT O'QUV TO'PLAMIDA:
lug'at o'quvda quriladi, val/test da faqat qo'llanadi
(aks holda leakage - 17-qism)
padding_idx=0: shu indeksning gradienti nol, vektori o'zgarmaydi
(ketma-ketliklarda padding uchun - 25-qism)Indeks 0 — "noma'lum" uchun; lug'at faqat o'quv to'plamida quriladi.
2.6. Halol taqqoslash
TABULAR + KO'P KATEGORIYA - TARMOQ UCHUN ENG QULAY HOLAT
lekin baribir bazaviy kerak:
HistGradientBoosting(categorical_features=[...])
kategoriyalarni o'zi qayta ishlaydi (ichki kodlash)
max 255 daraja (ko'pida - target encoding yoki ordinal)
TAQQOSLASH:
bir xil bo'linish, bir xil metrika
bir necha seed (tarmoq uchun)
vaqt va murakkablik ham hisobga olinadi
KO'PINCHA NATIJA:
farq kichik, ba'zan boosting, ba'zan tarmoq ustun
TARMOQNING QO'SHIMCHA FOYDASI: embedding vektorlarining o'zi
(qidiruv, klasterlash, boshqa modelga belgi sifatida)Embedding ning qo'shimcha qiymati — vektorlarning o'zi; ular boshqa vazifalarda ham ishlatiladi.
2.7. Tuzoqlar
Asosiy tuzoqlar: embedding ga float indeks berish; noma'lum kategoriya uchun joy qoldirmaslik; lug'atni butun ma'lumotda qurish (leakage); kam uchraydigan kategoriyalarni alohida qoldirib, yodlashga yo'l qo'yish; d ni juda katta tanlash; son belgilarni masshtablamaslik; boosting bazaviysiz tarmoqni "g'olib" deb e'lon qilish; kategoriya indekslariga son sifatida qarash (nn.Linear ga to'g'ridan-to'g'ri).
3. Tez ma'lumotnoma
import torch
import torch.nn as nn
def lugat_qur(qiymatlar, min_soni=5):
"""0 - noma'lum/kam uchraydigan uchun ajratilgan."""
uniq, soni = np.unique(qiymatlar, return_counts=True)
return {k: i + 1 for i, k in enumerate(uniq[soni >= min_soni])}
def kodla(qiymatlar, lugat):
return np.array([lugat.get(q, 0) for q in qiymatlar])
class TabularTarmoq(nn.Module):
def __init__(self, n_son, kat_hajmlari, d_emb, yashirin=128):
super().__init__()
self.embler = nn.ModuleList(
[nn.Embedding(n, d) for n, d in zip(kat_hajmlari, d_emb)])
self.tarmoq = nn.Sequential(
nn.Linear(n_son + sum(d_emb), yashirin), nn.ReLU(),
nn.Linear(yashirin, 1))
def forward(self, son, kat):
e = [emb(kat[:, i]) for i, emb in enumerate(self.embler)]
return self.tarmoq(torch.cat([son] + e, dim=1)).squeeze(-1)Embedding xulosasi
Embedding(n, d): indeks -> satr, one_hot @ W bilan teng
kirish: long; noma'lum -> 0
har kat ustunga o'z Embedding, keyin concat
d ~ min(50, (n+1)//2) boshlang'ich nuqta
bazaviy: HistGradientBoosting(categorical_features=...)4. Batafsil misollar
Misollar real torch/sklearn bilan (Python 3.14, torch 2.14 CPU).
Misol 1 — Embedding ichidan
"""Embedding = one_hot @ W, siyrak gradient (real torch)."""
import torch
import torch.nn as nn
def main() -> None:
torch.manual_seed(0)
n, d = 6, 3
emb = nn.Embedding(n, d)
print("=== 1. Og'irlik jadvali ===")
print(f" shakl: {tuple(emb.weight.shape)} (kategoriya, o'lcham)")
for i in range(n):
print(f" satr {i}: {emb.weight[i].detach().numpy().round(4)}")
print("\n=== 2. Indeks bo'yicha satr olish ===")
idx = torch.tensor([3, 0, 3])
chiqish = emb(idx)
print(f" emb([3, 0, 3]) shakli: {tuple(chiqish.shape)}")
print(f" 0-chiqish == 3-satr: {torch.equal(chiqish[0], emb.weight[3])}")
print(f" 2-chiqish == 3-satr: {torch.equal(chiqish[2], emb.weight[3])}")
print("\n=== 3. one_hot @ W bilan tenglik ===")
oh = nn.functional.one_hot(idx, n).float()
print(f" one-hot shakli: {tuple(oh.shape)}")
print(f" one_hot @ W == emb(idx): "
f"{torch.allclose(oh @ emb.weight, chiqish)}")
print(" natija bir xil, lekin embedding one-hot matritsani QURMAYDI")
print("\n=== 4. Siyrak gradient ===")
emb.zero_grad()
emb(torch.tensor([1, 4, 4])).sum().backward()
g = emb.weight.grad
print(f" {'satr':>5} {'grad normasi':>14}")
for i in range(n):
print(f" {i:>5} {g[i].norm().item():>14.4f}")
print(" faqat batchda uchragan satrlar (1 va 4) gradient oldi")
print(" 4-satr ikki marta uchragani uchun gradienti ikki barobar")
print("\n=== 5. Kirish turi ===")
try:
emb(torch.tensor([1.0, 2.0]))
print(" float indeks ishladi (kutilmagan)")
except RuntimeError as xato:
print(f" float indeks: RuntimeError - {str(xato)[:44]}")
try:
emb(torch.tensor([6]))
print(" indeks 6 ishladi (kutilmagan)")
except IndexError as xato:
print(f" indeks 6 (n=6): IndexError - {str(xato)[:40]}")
print("\n=== 6. Parametrlar: embedding va one-hot + Linear ===")
print(f" {'n':>7} {'d':>4} {'one-hot + Linear(n,64)':>24} "
f"{'Emb(n,d) + Linear(d,64)':>26}")
for n_, d_ in [(10, 4), (1000, 16), (50000, 32)]:
oh_param = n_ * 64 + 64
emb_param = n_ * d_ + d_ * 64 + 64
print(f" {n_:>7} {d_:>4} {oh_param:>24} {emb_param:>26}")
print(" ⭐ Ko'p kategoriyada embedding kamroq parametr bilan ishlaydi")
if __name__ == "__main__":
main()Natijaning muhim qismi:
=== 1. Og'irlik jadvali ===
shakl: (6, 3) (kategoriya, o'lcham)
satr 0: [-1.1258 -1.1524 0.5667]
satr 1: [ 0.7935 0.5988 -1.5551]
satr 2: [-0.3414 1.853 0.4681]
satr 3: [-0.1577 -0.1734 0.1835]
satr 4: [1.3894 1.5863 0.9463]
satr 5: [-0.8437 0.9318 1.259 ]
=== 2. Indeks bo'yicha satr olish ===
emb([3, 0, 3]) shakli: (3, 3)
0-chiqish == 3-satr: True
2-chiqish == 3-satr: True
=== 3. one_hot @ W bilan tenglik ===
one-hot shakli: (3, 6)
one_hot @ W == emb(idx): True
natija bir xil, lekin embedding one-hot matritsani QURMAYDI
=== 4. Siyrak gradient ===
satr grad normasi
0 0.0000
1 1.7321
2 0.0000
3 0.0000
4 3.4641
5 0.0000
faqat batchda uchragan satrlar (1 va 4) gradient oldi
4-satr ikki marta uchragani uchun gradienti ikki barobar
=== 5. Kirish turi ===
float indeks: RuntimeError - Expected tensor for argument #1 'indices' to
indeks 6 (n=6): IndexError - index out of range in self
=== 6. Parametrlar: embedding va one-hot + Linear ===
n d one-hot + Linear(n,64) Emb(n,d) + Linear(d,64)
10 4 704 360
1000 16 64064 17088
50000 32 3200064 1602112
⭐ Ko'p kategoriyada embedding kamroq parametr bilan ishlaydiNima ko'rsatdi: 2.1, 2.2-bo'limlar.
Misol 2 — Lug'at, noma'lum va kam uchraydigan kategoriyalar
"""Kategoriyalarni indeksga aylantirish - faqat o'quv to'plamida."""
import numpy as np
import torch
import torch.nn as nn
def lugat_qur(qiymatlar, min_soni=1):
uniq, soni = np.unique(qiymatlar, return_counts=True)
return {k: i + 1 for i, k in enumerate(uniq[soni >= min_soni])}
def kodla(qiymatlar, lugat):
return np.array([lugat.get(q, 0) for q in qiymatlar], dtype=np.int64)
def main() -> None:
oquv = np.array(["A", "B", "A", "C", "A", "B", "D", "A", "B", "C"])
test = np.array(["A", "E", "C", "F", "B"])
print("=== 1. Lug'at faqat o'quvda ===")
lugat = lugat_qur(oquv)
print(f" lug'at: {lugat}")
print(f" o'quv kodlari: {kodla(oquv, lugat).tolist()}")
print(f" test kodlari: {kodla(test, lugat).tolist()}")
print(" E va F o'quvda yo'q -> 0 (noma'lum)")
print("\n=== 2. Embedding n + 1 bilan ===")
torch.manual_seed(0)
emb = nn.Embedding(len(lugat) + 1, 3)
print(f" Embedding({len(lugat) + 1}, 3) - 0-satr noma'lum uchun")
chiqish = emb(torch.tensor(kodla(test, lugat)))
print(f" test chiqishi shakli: {tuple(chiqish.shape)}")
print(f" E va F bir xil vektor oldi: "
f"{torch.equal(chiqish[1], chiqish[3])}")
print("\n=== 3. 0-indekssiz nima bo'ladi ===")
notogri = {k: i for i, k in enumerate(np.unique(oquv))}
emb2 = nn.Embedding(len(notogri), 3)
try:
emb2(torch.tensor([notogri[q] for q in test]))
print(" xato chiqmadi (kutilmagan)")
except KeyError as xato:
print(f" KeyError: {xato} - test da yangi kategoriya")
print("\n=== 4. Kam uchraydigan kategoriyalar ===")
rng = np.random.default_rng(0)
chastota = rng.zipf(1.6, 2000)
chastota = chastota[chastota < 400]
uniq, soni = np.unique(chastota, return_counts=True)
print(f" noyob kategoriyalar: {len(uniq)}")
for m in [1, 3, 5, 10]:
print(f" {m:>2} martadan kam uchraganlar: "
f"{int((soni < m).sum()):>4} ta "
f"({int(soni[soni < m].sum())} qator)")
lug5 = lugat_qur(chastota, min_soni=5)
kod = kodla(chastota, lug5)
print(f" min_soni=5 bilan lug'at: {len(lug5)} + 1 (noma'lum)")
print(f" 0 ga tushgan qatorlar: {(kod == 0).mean():.1%}")
print(" kam ma'lumotli satrlar yodlanmaydi - ular umumiy 0 ni oladi")
print("\n=== 5. Butun ma'lumotda lug'at qurish - leakage ===")
hammasi = np.concatenate([oquv, test])
lug_xato = lugat_qur(hammasi)
print(f" to'g'ri lug'at hajmi: {len(lugat)}")
print(f" xato lug'at hajmi: {len(lug_xato)}")
print(" test kategoriyalari 'ma'lum' bo'lib qoladi, lekin ularning")
print(" embedding satrlari HECH QACHON o'rgatilmaydi - tasodifiy")
print(" ⭐ Lug'at o'quvda, 0 - noma'lum uchun")
if __name__ == "__main__":
main()Natijaning muhim qismi:
=== 1. Lug'at faqat o'quvda ===
lug'at: {np.str_('A'): 1, np.str_('B'): 2, np.str_('C'): 3, np.str_('D'): 4}
o'quv kodlari: [1, 2, 1, 3, 1, 2, 4, 1, 2, 3]
test kodlari: [1, 0, 3, 0, 2]
E va F o'quvda yo'q -> 0 (noma'lum)
=== 2. Embedding n + 1 bilan ===
Embedding(5, 3) - 0-satr noma'lum uchun
test chiqishi shakli: (5, 3)
E va F bir xil vektor oldi: True
=== 3. 0-indekssiz nima bo'ladi ===
KeyError: np.str_('E') - test da yangi kategoriya
=== 4. Kam uchraydigan kategoriyalar ===
noyob kategoriyalar: 122
1 martadan kam uchraganlar: 0 ta (0 qator)
3 martadan kam uchraganlar: 81 ta (99 qator)
5 martadan kam uchraganlar: 91 ta (133 qator)
10 martadan kam uchraganlar: 104 ta (221 qator)
min_soni=5 bilan lug'at: 31 + 1 (noma'lum)
0 ga tushgan qatorlar: 6.8%
kam ma'lumotli satrlar yodlanmaydi - ular umumiy 0 ni oladi
=== 5. Butun ma'lumotda lug'at qurish - leakage ===
to'g'ri lug'at hajmi: 4
xato lug'at hajmi: 6
test kategoriyalari 'ma'lum' bo'lib qoladi, lekin ularning
embedding satrlari HECH QACHON o'rgatilmaydi - tasodifiy
⭐ Lug'at o'quvda, 0 - noma'lum uchunNima ko'rsatdi: 2.5-bo'lim.
Misol 3 — Embedding va one-hot o'rgatishda
"""Ko'p darajali kategoriya: one-hot va embedding (real torch)."""
import numpy as np
import torch
import torch.nn as nn
from sklearn.metrics import mean_squared_error
def yarat(n=6000, n_dokon=400, seed=0):
"""Sotuv = dokon ta'siri + son belgilar. Do'konlar 8 ta yashirin
guruhga bo'lingan - o'xshash do'konlar o'xshash ta'sirga ega."""
rng = np.random.default_rng(seed)
guruh = rng.integers(0, 8, n_dokon)
guruh_tasiri = rng.normal(0, 1.5, 8)
dokon_tasiri = guruh_tasiri[guruh] + rng.normal(0, 0.3, n_dokon)
# do'konlar notekis: p ~ 1/rang, ko'pchiligi kam uchraydi
p = rng.permutation(1.0 / np.arange(1, n_dokon + 1))
p = p / p.sum()
dokon = rng.choice(n_dokon, n, p=p)
son = rng.normal(0, 1, (n, 4)).astype(np.float32)
y = (dokon_tasiri[dokon] + son[:, 0] - 0.5 * son[:, 1]
+ rng.normal(0, 0.5, n)).astype(np.float32)
return son, dokon, y, guruh
class OneHotModel(nn.Module):
def __init__(self, n_dokon):
super().__init__()
self.n = n_dokon
self.tarmoq = nn.Sequential(nn.Linear(4 + n_dokon, 64), nn.ReLU(),
nn.Linear(64, 1))
def forward(self, son, kat):
oh = nn.functional.one_hot(kat, self.n).float()
return self.tarmoq(torch.cat([son, oh], 1)).squeeze(-1)
class EmbModel(nn.Module):
def __init__(self, n_dokon, d):
super().__init__()
self.emb = nn.Embedding(n_dokon, d)
self.tarmoq = nn.Sequential(nn.Linear(4 + d, 64), nn.ReLU(),
nn.Linear(64, 1))
def forward(self, son, kat):
return self.tarmoq(torch.cat([son, self.emb(kat)], 1)).squeeze(-1)
def orgat(model, son, kat, y, davrlar=40, seed=0):
opt = torch.optim.AdamW(model.parameters(), lr=0.01, weight_decay=1e-4)
g = torch.Generator().manual_seed(seed)
for _ in range(davrlar):
model.train()
tartib = torch.randperm(len(y), generator=g)
for i in range(0, len(y), 128):
idx = tartib[i:i + 128]
opt.zero_grad()
nn.functional.mse_loss(model(son[idx], kat[idx]),
y[idx]).backward()
opt.step()
return model
def main() -> None:
son, dokon, y, guruh = yarat()
n_dokon = 400
son_t = torch.tensor(son)
kat_t = torch.tensor(dokon)
y_t = torch.tensor(y)
tr, te = slice(0, 4500), slice(4500, None)
soni = np.bincount(dokon[:4500], minlength=n_dokon)
print(f" {len(y)} qator, {n_dokon} do'kon")
print(f" o'quvda 10 martadan kam uchragan do'konlar: "
f"{int(((soni > 0) & (soni < 10)).sum())}")
print("\n=== 1. Test MSE ===")
print(f" {'model':<18} {'parametr':>10} {'test MSE':>10} "
f"{'kam do_kon MSE':>15}")
kam = np.isin(dokon[te], np.where(soni < 10)[0])
modellar = {}
for nom, yasa in [("one-hot", lambda: OneHotModel(n_dokon)),
("embedding d=4", lambda: EmbModel(n_dokon, 4)),
("embedding d=16", lambda: EmbModel(n_dokon, 16))]:
torch.manual_seed(0)
m = orgat(yasa(), son_t[tr], kat_t[tr], y_t[tr])
m.eval()
with torch.no_grad():
p = m(son_t[te], kat_t[te]).numpy()
modellar[nom] = m
param = sum(q.numel() for q in m.parameters())
print(f" {nom:<18} {param:>10} "
f"{mean_squared_error(y[te], p):>10.4f} "
f"{mean_squared_error(y[te][kam], p[kam]):>15.4f}")
print("\n=== 2. Embedding yashirin guruhlarni topdimi ===")
m = modellar["embedding d=4"]
E = m.emb.weight.detach().numpy()
kop = soni >= 20
ichki, tashqi = [], []
idx = np.where(kop)[0]
for a in range(len(idx)):
for b in range(a + 1, len(idx)):
i, j = idx[a], idx[b]
d = np.linalg.norm(E[i] - E[j])
(ichki if guruh[i] == guruh[j] else tashqi).append(d)
print(f" ko'p uchragan do'konlar: {kop.sum()}")
print(f" bir guruhdagi juftlar o'rtacha masofasi: "
f"{np.mean(ichki):.3f}")
print(f" boshqa guruhdagi juftlar o'rtacha masofasi: "
f"{np.mean(tashqi):.3f}")
nisbat = np.mean(tashqi) / np.mean(ichki)
print(f" nisbat: {nisbat:.2f}")
print(f" bir guruhdagilar o'rtacha {100 * (1 - 1 / nisbat):.0f}% "
f"yaqinroq")
print(" guruh ma'lumoti modelga BERILMAGAN - signal zaif, lekin bor")
print(" ⭐ Embedding o'xshashlikni qisman o'zi topadi")
if __name__ == "__main__":
main()Natijaning muhim qismi:
6000 qator, 400 do'kon
o'quvda 10 martadan kam uchragan do'konlar: 300
=== 1. Test MSE ===
model parametr test MSE kam do_kon MSE
one-hot 25985 0.3682 0.4908
embedding d=4 2241 0.3158 0.3881
embedding d=16 7809 0.3836 0.5839
=== 2. Embedding yashirin guruhlarni topdimi ===
ko'p uchragan do'konlar: 35
bir guruhdagi juftlar o'rtacha masofasi: 2.616
boshqa guruhdagi juftlar o'rtacha masofasi: 2.910
nisbat: 1.11
bir guruhdagilar o'rtacha 10% yaqinroq
guruh ma'lumoti modelga BERILMAGAN - signal zaif, lekin bor
⭐ Embedding o'xshashlikni qisman o'zi topadiNima ko'rsatdi: 2.2, 2.4-bo'limlar.
Misol 4 — To'liq tabular tarmoq va HistGradientBoosting
"""Son + bir necha kategoriya: tarmoq va boosting halol taqqoslash."""
import numpy as np
import pandas as pd
import torch
import torch.nn as nn
from sklearn.ensemble import HistGradientBoostingClassifier
from sklearn.metrics import roc_auc_score
from sklearn.model_selection import StratifiedKFold
from sklearn.preprocessing import StandardScaler
def yarat(n=8000, seed=0):
rng = np.random.default_rng(seed)
shahar = rng.integers(0, 60, n)
kasb = rng.integers(0, 25, n)
kanal = rng.integers(0, 4, n)
shahar_t = rng.normal(0, 0.8, 60)
kasb_t = rng.normal(0, 0.8, 25)
kanal_t = np.array([0.0, 0.4, -0.3, 0.8])
son = rng.normal(0, 1, (n, 5))
kuch = (shahar_t[shahar] + kasb_t[kasb] + kanal_t[kanal]
+ 0.8 * son[:, 0] - 0.6 * son[:, 1] + 0.5 * son[:, 2] * son[:, 3]
- 1.0)
y = (kuch + rng.logistic(0, 1, n) > 0).astype(np.int64)
df = pd.DataFrame(son, columns=[f"s{i}" for i in range(5)])
df["shahar"], df["kasb"], df["kanal"] = shahar, kasb, kanal
return df, y
KAT = ["shahar", "kasb", "kanal"]
SON = [f"s{i}" for i in range(5)]
class TabularTarmoq(nn.Module):
def __init__(self, kat_hajmlari, d_emb, yashirin=64):
super().__init__()
self.embler = nn.ModuleList(
[nn.Embedding(n, d) for n, d in zip(kat_hajmlari, d_emb)])
self.tarmoq = nn.Sequential(
nn.Linear(len(SON) + sum(d_emb), yashirin), nn.ReLU(),
nn.Dropout(0.1), nn.Linear(yashirin, 2))
def forward(self, son, kat):
e = [emb(kat[:, i]) for i, emb in enumerate(self.embler)]
return self.tarmoq(torch.cat([son] + e, dim=1))
def tarmoq_bahola(df, y, tr, va, seed):
sc = StandardScaler().fit(df.iloc[tr][SON])
lugatlar = [{k: i + 1 for i, k in
enumerate(np.unique(df.iloc[tr][c]))} for c in KAT]
def tayyorla(idx):
s = torch.tensor(sc.transform(df.iloc[idx][SON]),
dtype=torch.float32)
k = torch.tensor(np.column_stack(
[[l.get(q, 0) for q in df.iloc[idx][c]]
for c, l in zip(KAT, lugatlar)]), dtype=torch.int64)
return s, k
s_tr, k_tr = tayyorla(tr)
s_va, k_va = tayyorla(va)
y_tr = torch.tensor(y[tr])
hajmlar = [len(l) + 1 for l in lugatlar]
d_emb = [min(16, (n + 1) // 2) for n in hajmlar]
torch.manual_seed(seed)
model = TabularTarmoq(hajmlar, d_emb)
opt = torch.optim.AdamW(model.parameters(), lr=0.005,
weight_decay=1e-4)
g = torch.Generator().manual_seed(seed)
for _ in range(25):
model.train()
tartib = torch.randperm(len(y_tr), generator=g)
for i in range(0, len(y_tr), 128):
idx = tartib[i:i + 128]
opt.zero_grad()
nn.functional.cross_entropy(model(s_tr[idx], k_tr[idx]),
y_tr[idx]).backward()
opt.step()
model.eval()
with torch.no_grad():
p = torch.softmax(model(s_va, k_va), 1)[:, 1].numpy()
return roc_auc_score(y[va], p), hajmlar, d_emb
def main() -> None:
df, y = yarat()
print(f" {len(df)} qator, son belgilar {len(SON)}, kategoriyalar "
f"{[int(df[c].nunique()) for c in KAT]}")
print(f" musbat ulush: {y.mean():.3f}")
cv = StratifiedKFold(4, shuffle=True, random_state=0)
natija = {"HistGB": [], "Tarmoq": []}
for k, (tr, va) in enumerate(cv.split(df, y)):
gb = HistGradientBoostingClassifier(
max_iter=200, learning_rate=0.05, categorical_features=KAT,
early_stopping=False, random_state=0)
gb.fit(df.iloc[tr], y[tr])
natija["HistGB"].append(
roc_auc_score(y[va], gb.predict_proba(df.iloc[va])[:, 1]))
auc, hajmlar, d_emb = tarmoq_bahola(df, y, tr, va, seed=k)
natija["Tarmoq"].append(auc)
print("\n=== 1. Embedding o'lchamlari ===")
for c, n, d in zip(KAT, hajmlar, d_emb):
print(f" {c:<8} {n:>3} daraja (+0 noma'lum) -> d = {d}")
print("\n=== 2. Juftlashgan CV (4 fold) ===")
print(f" {'fold':>5} {'HistGB':>9} {'Tarmoq':>9}")
for k in range(4):
print(f" {k + 1:>5} {natija['HistGB'][k]:>9.4f} "
f"{natija['Tarmoq'][k]:>9.4f}")
a = np.array(natija["HistGB"])
b = np.array(natija["Tarmoq"])
d = b - a
se = d.std(ddof=1) / np.sqrt(len(d))
print(f" o'rtacha: HistGB {a.mean():.4f}, Tarmoq {b.mean():.4f}")
print(f" farq (Tarmoq - HistGB): {d.mean():+.4f}, SE {se:.4f}")
if abs(d.mean()) <= 2 * se:
xulosa = "farq 2*SE ichida - soddaroq model (HistGB) tanlanadi"
elif d.mean() > 0:
xulosa = "tarmoq sezilarli ustun"
else:
xulosa = "HistGB sezilarli ustun"
print(f" QAROR: {xulosa}")
print(" ⭐ Qarorni farq va SE belgilaydi, bitta raqam emas")
if __name__ == "__main__":
main()Natijaning muhim qismi:
8000 qator, son belgilar 5, kategoriyalar [60, 25, 4]
musbat ulush: 0.387
=== 1. Embedding o'lchamlari ===
shahar 61 daraja (+0 noma'lum) -> d = 16
kasb 26 daraja (+0 noma'lum) -> d = 13
kanal 5 daraja (+0 noma'lum) -> d = 3
=== 2. Juftlashgan CV (4 fold) ===
fold HistGB Tarmoq
1 0.7793 0.7785
2 0.7837 0.7843
3 0.7916 0.7719
4 0.7850 0.7763
o'rtacha: HistGB 0.7849, Tarmoq 0.7777
farq (Tarmoq - HistGB): -0.0071, SE 0.0047
QAROR: farq 2*SE ichida - soddaroq model (HistGB) tanlanadi
⭐ Qarorni farq va SE belgilaydi, bitta raqam emasNima ko'rsatdi: 2.3, 2.6-bo'limlar.
5. To'g'ri va noto'g'ri tushunishlar
| Noto'g'ri fikr | To'g'risi |
|---|---|
| "Embedding murakkab qatlam" | Indeks bo'yicha satr jadvali |
| "Embedding va one-hot boshqa natija beradi" | one_hot @ W bilan bir xil |
| "Hamma satr har qadamda yangilanadi" | Faqat uchragan satrlar |
| "Kategoriya indeksini son sifatida berish mumkin" | Tartib ma'nosiz |
| "Lug'atni butun ma'lumotda qurish qulay" | Leakage va o'rgatilmagan satrlar |
| "Noma'lum kategoriya kam uchraydi" | Ishlab chiqarishda albatta chiqadi |
"Katta d — yaxshi" |
Kam ma'lumotda yodlaydi |
| "Embedding bilan tarmoq boosting dan yaxshi" | O'lchab ko'rish kerak |
6. Keng tarqalgan xatolar va yechimlari
1. Float indeks
emb(torch.tensor([1.0, 2.0])) # ⚠️
emb(torch.tensor([1, 2])) # ✅ long2. Noma'lum uchun joy yo'q
lugat = {k: i for i, k in enumerate(uniq)} # ⚠️ KeyError
lugat = {k: i + 1 ...}; lugat.get(q, 0) # ✅3. Lug'at butun ma'lumotda
lugat_qur(np.concatenate([oquv, test])) # ⚠️
lugat_qur(oquv) # ✅4. Indeksni son sifatida
torch.cat([son, kat.float()], 1) # ⚠️ 5 > 3 ma'nosiz
torch.cat([son, emb(kat)], 1) # ✅5. Kam uchraydiganlarni qoldirish
lugat_qur(qiymatlar, min_soni=1) # 1 martalik ham # ⚠️ yodlash
lugat_qur(qiymatlar, min_soni=5) # ✅6. Embedding o'lchami kategoriyadan katta
nn.Embedding(4, 64) # ⚠️
nn.Embedding(4, 2) # ✅ yoki one-hot7. Bazaviysiz
# faqat tarmoq natijasini hisobot qilish # ⚠️
HistGradientBoostingClassifier(categorical_features=[...]) # ✅7. Integratsiya — bu bilim qayerda kerak bo'ladi
- 17-qism (o'tilgan): Kategoriyali kodlash va target encoding
- 20.12-dars (o'tilgan): Tarmoq va boosting taqqoslash
- 21.5-dars (o'tilgan): Ko'p kirishli Trainer
- 25-qism: So'z embeddinglari
- 27-qism: Embedding va qidiruv (RAG)
8. Eng yaxshi amaliyotlar
Ko'p darajali kategoriyaga embedding.
Lug'atni faqat o'quvda quring.
Indeks 0 — noma'lum.
Kam uchraydiganlarni birlashtiring.
dni formuladan boshlab validatsiyada sozlang.Son belgilarni masshtablang.
HistGradientBoostingbilan taqqoslang.Embedding vektorlarini tahlil qiling.
9. Amaliy topshiriq
Vazifa 1: Bashorat qiling
1. # Embedding(n, d) og'irligi shakli?
2. # emb(idx) nimaga teng?
3. # qaysi satrlar gradient oladi?
4. # kirish turi?
5. # qachon one-hot?
6. # qachon embedding?
7. # noma'lum kategoriya qanday kodlanadi?
8. # lug'at qayerda quriladi?
9. # kam uchraydiganlar bilan nima qilinadi?
10. # d ni tanlash formulasi?
11. # son va kategoriya qanday birlashtiriladi?
12. # embeddingning qo'shimcha foydasi?Javoblar
(n, d)one_hot(idx) @ W- Faqat batchda uchraganlar
long(int64)- Kategoriya kam (< 20)
- Kategoriya ko'p, tarmoq
- 0
- Faqat o'quvda
- 0 ga birlashtiriladi
min(50, (n + 1) // 2)torch.cat([son] + embeddinglar)- Vektorlarning o'zi (qidiruv, klaster)
Vazifa 2: Xatolarni tuzating
1. emb(torch.tensor([1.0, 2.0]))
2. lugat = {k: i for i, k in enumerate(uniq)} # test da yangi kategoriya
3. lugat_qur(np.concatenate([oquv, test]))
4. torch.cat([son, kat.float()], 1)
5. nn.Embedding(4, 64)Javoblar
1. emb(torch.tensor([1, 2]))
2. lugat = {k: i + 1 for i, k in enumerate(uniq)}; lugat.get(q, 0)
3. lugat_qur(oquv)
4. torch.cat([son, emb(kat)], 1)
5. nn.Embedding(4, 2) # yoki one-hotVazifa 3: Ichidan
Modellang:
- Jadval
- Satr olish
- one-hot tenglik
- Siyrak gradient
Vazifa 4: Lug'at
Modellang:
- O'quvda
- n + 1
- 0 siz
- Kam uchraydigan
Vazifa 5: O'rgatish
Modellang:
- One-hot
- Embedding
- Kam do'konlar
- Yashirin guruhlar
Vazifa 6: Taqqoslash
Modellang:
- O'lchamlar
- Juftlashgan CV
- Farq va SE
- Qaror
Vazifa 7: O'ylash
Siz mahsulotlar uchun embedding o'rgatdingiz. Mahsulot menejeri so'radi: "Bu vektorlardan 'o'xshash mahsulotlar' ro'yxatini chiqarsa bo'ladimi?" Nima deysiz?
Javob
Qisqa javob: ha, lekin "o'xshash" bu yerda "model uchun bir xil ta'sirga ega" degan ma'noni bildiradi — "mijozga o'xshash ko'rinadi" degani emas.
Qanday qilinadi:
E = model.emb.weight.detach() # (n, d)
E = torch.nn.functional.normalize(E, dim=1) # kosinus uchun
oxshash = E @ E[mahsulot_id] # (n,)
top = oxshash.topk(11).indices[1:] # o'zidan tashqari 10 taMuhim cheklovlar:
1. Embedding faqat o'rgatilgan vazifani biladi. Sotuv hajmini bashorat qilish uchun o'rgatilgan bo'lsa, "o'xshash" = "sotuv hajmiga o'xshash ta'sir qiladi". Masalan, qishki kurtka va yangi yil bezagi bir-biriga yaqin chiqishi mumkin — chunki ikkalasi ham dekabrda sotiladi, lekin ular o'xshash mahsulot emas.
2. Kam uchragan mahsulotlarning vektorlari ishonchsiz. 3-misolda ko'rdik: faqat ko'p uchragan kategoriyalar uchun masofa tahlili ma'noga ega. Kam uchraganlar (yoki min_soni bo'yicha 0 ga birlashtirilganlar) tasodifiy yoki umumiy vektorda qoladi.
soni = np.bincount(oquv_kodlar, minlength=n)
ishonchli = soni >= 50 # faqat shular uchun tavsiya bering3. Masofa o'lchovi muhim. Kosinus o'xshashlik odatda Evklid masofasidan yaxshiroq, chunki vektor uzunligi ko'pincha kategoriyaning chastotasini aks ettiradi, yo'nalish esa — ma'nosini.
4. Tekshiruv kerak.
# 20 ta mahsulotni tanlab, eng yaqin 5 tasini ko'rib chiqing
# mutaxassis bilan: "bular haqiqatan o'xshashmi?"Nima taklif qilish kerak:
| Maqsad | Yondashuv |
|---|---|
| "Birga sotib olinadigan" | Savat ma'lumotida alohida embedding (item2vec) |
| "Tashqi ko'rinishi o'xshash" | Rasm embeddingi (24-qism) |
| "Tavsifi o'xshash" | Matn embeddingi (25, 27-qism) |
| "Sotuv dinamikasi o'xshash" | Hozirgi embedding — mos |
Menejerga javob:
"Ha, bu vektorlardan o'xshashlik ro'yxatini chiqarish mumkin. Lekin ular sotuv bashorati uchun o'rgatilgan, shuning uchun 'o'xshash' = 'sotuvga o'xshash ta'sir qiladi'. Agar sizga 'mijoz uchun o'xshash' kerak bo'lsa, xarid tarixidan alohida embedding o'rgatishni taklif qilaman. Hozirgisini faqat ko'p sotilgan mahsulotlar uchun va mutaxassis tekshiruvi bilan ishlatish mumkin."
Nimani mustahkamlaydi: 2.2, 2.6-bo'limlar.
Xulosa
Bu darsda embedding va tabular tarmoqni ko'rdik.
Eng muhim uch fikr:
Embedding — indeks bo'yicha satr jadvali.
nn.Embedding(n, d)ning chiqishione_hot(idx) @ Wbilan aynan teng, lekinone-hotmatritsa hech qachon qurilmaydi. Gradient faqat batchda uchragan satrlarga keladi. Ko'p kategoriyada buone-hot+Linearga nisbatan parametrlarni kamaytiradi — 1-misolda 50 000 kategoriyada taxminan ikki barobar.Lug'at o'quvda, indeks 0 — noma'lum. Test yoki ishlab chiqarishda yangi kategoriya albatta paydo bo'ladi; uning uchun ajratilgan 0-indeks bo'lmasa, kod
KeyErroryokiIndexErrorbilan to'xtaydi. Lug'atni butun ma'lumotda qurish esa leakage va hech qachon o'rgatilmagan tasodifiy satrlarni beradi. Kam uchraydigan kategoriyalarni 0 ga birlashtirish ularning yodlanishini oldini oladi.Embedding o'xshashlikni o'zi o'rganadi — lekin baribir o'lchang. 3-misolda
d = 4li embeddingone-hotdan past xato berdi,d = 16esa kam uchraydigan do'konlarni yodlab yomonlashdi. Guruh ma'lumoti modelga berilmagan bo'lsa ham, bir guruhdagi do'konlarning embeddinglari o'rtacha ~10% yaqinroq joylashdi — zaif, lekin haqiqiy signal. Shunga qaramay tarmoqniHistGradientBoosting(categorical_features=...)bilan juftlashgan CV da taqqoslash shart: 4-misolda qaror bitta raqam bilan emas, farq va uningSEsi bilan qabul qilindi.
Keyingi darsda loyiha tuzilishi va takrorlanuvchanlik: torch loyihasini papkalarga ajratish, konfiguratsiya fayli, seed boshqaruvi va tajribalarni qayd qilish.
Izohlar (0)
Izoh yozish uchun kiring.
- Hozircha izoh yo'q. Birinchi bo'ling!