17-bo‘lim

Matn bilan ishlash

Tokenizatsiya, lug'at qurish, padding, nn.Embedding, padding_idx va niqob (mask) bilan o'rtacha olish.

🕑 9 daqiqa o‘qish 📄 679 so‘z 👁 0 marta ko‘rilgan
Ushbu bo‘lim mundarijasi
  1. Uch qadam
  2. Kodlash va padding
  3. nn.Embedding
  4. Niqob (mask) bilan o'rtacha
  5. O'qitamiz
  6. Natijani sinaymiz
  7. Keyingi qadamlar
  8. Xulosa

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:

  1. Tokenizatsiya - matnni so'zlarga ajratish.
  2. Lug'at - har so'zga raqam berish.
  3. Padding - barcha jumlalarni bir xil uzunlikka keltirish.
Python
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]))
Natija
lug'at hajmi: 24
birinchi 8 ta: {'<pad>': 0, '<unk>': 1, 'bu': 2, 'film': 3, 'juda': 4, 'zor': 5, 'edi': 6, 'ajoyib': 7}
Maxsus tokenRaqamiVazifasi
<pad>0Qisqa jumlalarni to'ldirish uchun
<unk>1Lug'atda yo'q so'z uchun
Nega <pad> aynan 0

Bu 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 #

Python
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]))
Natija
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:

Python
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())
Natija
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.

Embedding - bu oddiy jadval

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:

Python
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)
"umuman yoqmadi" -> bashorat "umuman yoqmadi" xom matn kodlash + padding 20 21 0 0 0 0 Embedding (2, 6, 16) vektor vektor nol vektorlar (padding_idx=0) Niqob bilan o'rtacha 2 ga bo'linadi, 6 ga emas Linear(16, 2) ijobiy / salbiy Niqobsiz o'rtacha nollar bilan suyultirilib, qisqa jumlalar zaiflashadi
Padding modelga kirmasligi kerak - niqob aynan shuni ta'minlaydi
Niqobsiz o'rtacha olish - jimgina xato

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 #

Python
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))
Natija
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 #

Python
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%})")
Natija
  'juda zor film' -> ijobiy (100%)
  'vaqtim bekor ketdi' -> salbiy (100%)
  'notanish sozlar bu yerda' -> ijobiy (100%)

Birinchi ikkitasi to'g'ri. Uchinchisiga diqqat qiling.

Bu model hech nima o'rganmagan

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 #

YondashuvQachon
O'rtacha embedding (shu bo'lim)Oddiy tasniflash, boshlash uchun
nn.LSTM / nn.GRUSo'z tartibi muhim bo'lganda
nn.TransformerEncoderUzoq 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.

Matnda ham transfer learning

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.

Amaliy topshiriq
  1. O'z jumlalaringizdan lug'at tuzing va hajmini chop eting.
  2. <pad> va <unk> nima uchun kerakligini tushuntiring.
  3. kodla funksiyasini yozib, qisqa va uzun jumlada sinang.
  4. Uzunlikdan oshib ketgan jumla nima bo'lishini tekshiring.
  5. nn.Embedding yaratib, kirish va chiqish shakllarini solishtiring.
  6. padding_idx=0 bilan va usiz weight[0] ni tekshiring.
  7. Niqob bilan va niqobsiz o'rtachani bitta jumlada hisoblab solishtiring.
  8. Modelni o'qitib, yo'qotishning 0 ga tushishini kuzating.
  9. Notanish so'zlardan jumla berib, model ishonchini yozing.
  10. 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.

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.