17-bo‘lim
Matn bilan ishlash
Tokenizatsiya, lug'at qurish, padding, nn.Embedding, padding_idx va niqob (mask) bilan o'rtacha olish.
Ushbu bo‘lim mundarijasi
Model sonlar bilan ishlaydi, matn esa harflardan iborat. Bu bo'limda matnni modelga tushunarli shaklga keltiramiz.
Uch qadam #
Matnni tenzorga aylantirish har doim shu uch qadamdan iborat:
- Tokenizatsiya - matnni so'zlarga ajratish.
- Lug'at - har so'zga raqam berish.
- Padding - barcha jumlalarni bir xil uzunlikka keltirish.
import torch
import torch.nn as nn
matnlar = [
("bu film juda zor edi", 1), ("ajoyib asar tavsiya qilaman", 1),
("juda yoqdi yana koraman", 1), ("zor aktyorlar zor syujet", 1),
("vaqtim bekor ketdi", 0), ("juda zerikarli film", 0),
("umuman yoqmadi", 0), ("bekor pul sarfladim", 0),
]
lugat = {"<pad>": 0, "<unk>": 1}
for m, _ in matnlar:
for s in m.split():
if s not in lugat:
lugat[s] = len(lugat)
print("lug'at hajmi:", len(lugat))
print("birinchi 8 ta:", dict(list(lugat.items())[:8]))
lug'at hajmi: 24
birinchi 8 ta: {'<pad>': 0, '<unk>': 1, 'bu': 2, 'film': 3, 'juda': 4, 'zor': 5, 'edi': 6, 'ajoyib': 7}
| Maxsus token | Raqami | Vazifasi |
|---|---|---|
<pad> | 0 | Qisqa jumlalarni to'ldirish uchun |
<unk> | 1 | Lug'atda yo'q so'z uchun |
<pad> aynan 0Bu kelishuv, lekin juda foydali: keyinroq nn.Embedding(..., padding_idx=0)
va (x != 0) niqobi aynan shu raqamga tayanadi. Boshqa raqam tanlasangiz,
kodning hamma joyida uni ko'rsatishingiz kerak bo'ladi.
Kodlash va padding #
def kodla(matn, uzunlik=6):
id_lar = [lugat.get(s, 1) for s in matn.split()][:uzunlik]
return id_lar + [0] * (uzunlik - len(id_lar))
print("matn :", matnlar[0][0])
print("id lar :", kodla(matnlar[0][0]))
print("qisqa matn:", matnlar[6][0], "->", kodla(matnlar[6][0]))
matn : bu film juda zor edi
id lar : [2, 3, 4, 5, 6, 0]
qisqa matn: umuman yoqmadi -> [20, 21, 0, 0, 0, 0]
lugat.get(s, 1) - agar so'z lug'atda bo'lmasa, <unk> (1) qaytaradi.
Shu tufayli model hech qachon notanish so'zdan yiqilmaydi.
nn.Embedding #
Har so'z raqamini vektorga aylantiradigan qatlam:
qatlam = nn.Embedding(num_embeddings=len(lugat), embedding_dim=8, padding_idx=0)
print("Embedding og'irligi:", tuple(qatlam.weight.shape))
x = torch.tensor([kodla(matnlar[0][0])])
print("kirish:", tuple(x.shape), "-> chiqish:", tuple(qatlam(x).shape))
print("padding vektori:", qatlam.weight[0].abs().sum().item())
Embedding og'irligi: (24, 8)
kirish: (1, 6) -> chiqish: (1, 6, 8)
padding vektori: 0.0
Kirish (1, 6) edi - 1 ta jumla, 6 ta so'z. Chiqish (1, 6, 8) - har
so'z 8 o'lchamli vektorga aylandi.
padding_idx=0 tufayli <pad> vektori aynan nol va u hech qachon
o'qitilmaydi.
nn.Embedding sehrli narsa emas: u (lug'at_hajmi, o'lcham) shaklidagi
jadval, x esa undan qator tanlaydi.
Farqi shundaki, bu jadval o'qitiladi: o'xshash ma'noli so'zlar o'qitish davomida bir-biriga yaqin vektorlarga ega bo'ladi.
Niqob (mask) bilan o'rtacha #
Jumlani bitta vektorga aylantirish uchun so'z vektorlarining o'rtachasini
olamiz. Lekin <pad> larni hisobga qo'shmaslik kerak:
class MatnModel(nn.Module):
def __init__(self, lugat_hajmi, olcham=16):
super().__init__()
self.emb = nn.Embedding(lugat_hajmi, olcham, padding_idx=0)
self.chiqish = nn.Linear(olcham, 2)
def forward(self, x):
v = self.emb(x) # (paket, uzunlik, olcham)
niqob = (x != 0).unsqueeze(-1).float() # (paket, uzunlik, 1)
ortacha = (v * niqob).sum(1) / niqob.sum(1).clamp(min=1)
return self.chiqish(ortacha)
v.mean(1) deb yozsangiz, nol vektorlar ham hisobga kiradi.
«umuman yoqmadi» jumlasida 2 ta so'z va 4 ta padding bor. Niqobsiz o'rtacha 6 ga bo'linadi - natijada haqiqiy so'zlarning ta'siri uch barobar susayadi.
Uzun jumlalar kuchli, qisqalari zaif bo'lib qoladi. Xato xabari chiqmaydi, model esa yomon o'qiydi.
O'qitamiz #
X = torch.tensor([kodla(m) for m, _ in matnlar])
y = torch.tensor([e for _, e in matnlar])
print("X:", tuple(X.shape), " y:", tuple(y.shape))
X: (8, 6) y: (8,)
1-davr: yo'qotish=0.4738 aniqlik=100%
20-davr: yo'qotish=0.0001 aniqlik=100%
60-davr: yo'qotish=0.0000 aniqlik=100%
Natijani sinaymiz #
model.eval()
for t in ["juda zor film", "vaqtim bekor ketdi", "notanish sozlar bu yerda"]:
with torch.no_grad():
p = torch.softmax(model(torch.tensor([kodla(t)])), 1)[0]
print(f" '{t}' -> {'ijobiy' if p[1]>p[0] else 'salbiy'} ({max(p).item():.0%})")
'juda zor film' -> ijobiy (100%)
'vaqtim bekor ketdi' -> salbiy (100%)
'notanish sozlar bu yerda' -> ijobiy (100%)
Birinchi ikkitasi to'g'ri. Uchinchisiga diqqat qiling.
8 ta namuna, 24 so'zlik lug'at, yo'qotish 0.0000 - bu 11-bo'limdagi yodlashning aynan o'zi.
Eng aniq dalil - uchinchi qator: butunlay notanish so'zlardan iborat
jumlaga model 100% ishonch bilan «ijobiy» dedi. Aslida u barcha
so'zlarni <unk> deb ko'rdi va ma'nosiz vektorga baho berdi.
18-bo'limda aytilgan kalibrlanmagan model shu. Haqiqiy matn modeli uchun minglab namuna kerak.
Keyingi qadamlar #
| Yondashuv | Qachon |
|---|---|
| O'rtacha embedding (shu bo'lim) | Oddiy tasniflash, boshlash uchun |
nn.LSTM / nn.GRU | So'z tartibi muhim bo'lganda |
nn.TransformerEncoder | Uzoq bog'liqliklar, katta to'plam |
| Tayyor model (BERT va boshqalar) | Amalda eng ko'p ishlatiladigan yo'l |
Bizning usul so'z tartibini e'tiborga olmaydi: «yaxshi emas» va «emas yaxshi» bir xil natija beradi. LSTM va Transformer aynan shu muammoni hal qiladi.
16-bo'limdagi g'oya matnda ham ishlaydi - va u yerda ta'siri ancha
kuchliroq. transformers kutubxonasi orqali tayyor modelni olib, o'z
ma'lumotingizda sozlash hozirgi standart yondashuv.
- O'z jumlalaringizdan lug'at tuzing va hajmini chop eting.
<pad>va<unk>nima uchun kerakligini tushuntiring.kodlafunksiyasini yozib, qisqa va uzun jumlada sinang.- Uzunlikdan oshib ketgan jumla nima bo'lishini tekshiring.
nn.Embeddingyaratib, kirish va chiqish shakllarini solishtiring.padding_idx=0bilan va usizweight[0]ni tekshiring.- Niqob bilan va niqobsiz o'rtachani bitta jumlada hisoblab solishtiring.
- Modelni o'qitib, yo'qotishning 0 ga tushishini kuzating.
- Notanish so'zlardan jumla berib, model ishonchini yozing.
- Namunalar sonini 8 dan 100 ga oshirib, natija qanday o'zganini tekshiring.
Xulosa #
- Matnni modelga berish uch qadamdan iborat: tokenizatsiya, lug'at, padding.
<pad>odatda 0,<unk>esa 1 raqamini oladi.lugat.get(so'z, 1)notanish so'zni<unk>ga aylantiradi.nn.Embedding- o'qitiladigan jadval: har so'z raqamini vektorga aylantiradi.padding_idx=0<pad>vektorini nol holatda saqlaydi.- Jumlani bitta vektorga aylantirishda niqob ishlatish shart.
- Niqobsiz o'rtacha qisqa jumlalarni zaiflashtiradi - xato xabari chiqmaydi.
- 8 ta namunada model yodlab oladi: yo'qotish 0.0000 ga tushadi.
- Notanish jumlaga 100% ishonch bildirish - kalibrlanmagan modelning belgisi.
- Bu usul so'z tartibini hisobga olmaydi; buning uchun LSTM yoki Transformer kerak.
Keyingi bo'limda modelni ishlab chiqarishga tayyorlaymiz.
O‘qish tarixini saqlamoqchimisiz?
Tizimga kirsangiz, tugatgan bo‘limlaringiz saqlanadi va qoldirgan joyingizdan davom etasiz.
Xatolik topdingizmi?
Imlo xatosi, ishlamaydigan kod yoki noto‘g‘ri ma‘lumotni ko‘rsangiz - bizga xabar bering. Har bir xabar administrator tomonidan ko‘rib chiqiladi.