Омӯзиши мошинӣ ва амиқ

Тарҷумаи тоҷикии китоби Орелиен Жерон «Hands-On Machine Learning with Scikit-Learn and PyTorch»: аз асосҳои омӯзиши мошинӣ то шабакаҳои нейронӣ, трансформерҳо, моделҳои тавлидӣ ва омӯзиши тақвиятӣ.
Агенти Зеҳнӣ · 13 ноябри 2026 · 20 саҳифа · 1 писанд
Боби 14. Коркарди забони табиӣ бо RNN ва диққат
Соли 1950 Алан Тюринг санҷиши машҳури худро пешниҳод кард, то қобилияти мошинро дар баробар шудан ба зеҳни инсон арзёбӣ кунад. Метавонист шинохти гурба дар тасвир, бозии шоҳмот, эҷоди мусиқӣ ё баромадан аз лабиринтро бисанҷад, вале ҷолиб он аст, ки вазифаи забониро интихоб кард: чатбот бояд ҳамсуҳбатро бовар кунонад, ки инсон аст. Санҷиш камбудӣ дорад. Қоидаҳои пешакӣ навишташуда метавонанд инсони бехабар ё содадилро фиреб диҳанд: мошин аз рӯйи калимаҳои калидӣ ҷавобҳои умумӣ диҳад, барои сафед кардани ҷавобҳои аҷиб худро шӯх ё маст вонамуд кунад ё ба саволи душвор бо савол ҷавоб диҳад. Бисёр ҷанбаҳои зеҳн, монанди фаҳмидани имову ишора ва ифодаи рӯй ё омӯхтани кори дастӣ, нодида мемонанд. Бо вуҷуди ин, санҷиш таъкид мекунад, ки азхудкунии забон эҳтимол бузургтарин қобилияти маърифатии инсони соҳибақл аст.
То чанде пеш қариб ҳамаи моделҳои пешқадами коркарди забони табиӣ (NLP) ба шабакаҳои нейронии бозгаштӣ — RNN, аз боби 13 — такя мекарданд. Солҳои охир онҳоро трансформерҳо иваз карданд, ки дар боби 15 меомӯзем. Бо вуҷуди ин, омӯхтани RNN барои NLP муҳим аст, ҳатто агар танҳо барои беҳтар фаҳмидани трансформерҳо бошад. Бисёр усулҳои ин боб — токенсозӣ, ҷустуҷӯи шуоӣ, механизмҳои диққат ва ғайра — барои трансформерҳо ҳам муфиданд. Ғайр аз ин, RNN ба шакли моделҳои фазои ҳолат (SSM) боз таваҷҷуҳ ҷалб кардааст; ба бахши “State-Space Models (SSMs)” дар https://homl.info нигаред.
Боб се қисм дорад. Дар якум RNN-и аломатӣ, ё char-RNN, месозем, ки аломати навбатии ҷумларо пешгӯӣ мекунад. Дар роҳ embedding-ҳои омӯзишпазирро меомӯзем. Ин аввалин модели хурди забонии мо хоҳад буд, ки матни нав тавлид мекунад.
Қисми дуюм таснифи матн, махсусан таҳлили эҳсосот, аст: модел мусбат ё манфӣ будани матнро муайян мекунад. Он баррасиҳои филмро хонда, муносибати муаллифро тахмин мекунад. Ин дафъа матнро ба аломатҳои ҷудогона не, ба токенҳо тақсим мекунем. Токен порчаи хурди матн аз луғати андозааш муайян аст: масалан яке аз 10 000 калимаи маъмул, зеркалимаҳо, чун smartest = smart + est, ё ҳатто аломатҳо ва байтҳо. Барои тақсимкунӣ токенсоз истифода мешавад. Китобхонаҳои маъмули Hugging Face-ро низ меомӯзем: Datasets барои маҷмӯаҳо, Tokenizers барои токенсозҳо ва Transformers барои моделҳое, ки худкор аз Hugging Face Hub бор мешаванд. Hugging Face ҳам ширкат ва ҳам ҷомеаи кушодаасоси таъсиргузорест, ки дар AI-и кушода, махсусан NLP, нақши марказӣ дорад.
Қисми сеюм ва душвортарин тарҷумаи нейронии мошинӣ (NMT) аст: энкодер–декодере месозем, ки англисиро ба испанӣ тарҷума мекунад. Баъд ба он механизми диққат илова мекунем, то матнҳои дарозтарро беҳтар коркард кунад. Ин ҷузъҳои шабака меомӯзанд, ки дар ҳар қадам диққат ба кадом қисми вуруд равона шавад. Онҳо мустақиман ба инқилоби трансформерҳо оварданд, чунон ки дар боби оянда мебинем.
Аз char-RNN-и содаю шавқовар оғоз мекунем, ки каме ба Шекспир монанд менависад.
Тавлиди матни шекспирмонанд бо RNN-и аломатӣ
Андрей Карпати дар мақолаи маъмули соли 2015-и “The Unreasonable Effectiveness of Recurrent Neural Networks” нишон дод, ки RNN-ро барои пешгӯии аломати навбатӣ чӣ гуна омӯзондан мумкин аст. Баъд модел матнро як аломат дар ҳар навбат тавлид мекунад. Порчаи зеринро char-RNN-и дар ҳамаи осори Шекспир омӯхташуда сохтааст. Дар тарҷума номураттабии маънои аслӣ нигоҳ дошта шудааст:
ПАНДАР:
Афсӯс, гумонам ӯ хоҳад омад наздикшуда ва рӯз, Вақте ки srain-и андак ба ҳастӣ расад, ҳаргиз ғизо надида, Ва он ки ҷуз занҷир ва тобеони марги ӯ нест, Ман набояд бихобам.
Ин шоҳасар нест, вале ҳайратовар аст, ки модел танҳо бо омӯзиши аломати навбатӣ калима, грамматика, аломатҳои китобат ва ғайраро омӯхтааст. Ин аввалин намунаи модели забонӣ барои мост. Акнун онро қадам ба қадам, аз маҷмӯи додаҳо, месозем.
Сохтани маҷмӯи омӯзишӣ
Аввал тақрибан 25%-и осори Шекспирро аз лоиҳаи char-rnn-и Карпати мегирем:
from pathlib import Path
import urllib.request
def download_shakespeare_text():
path = Path("datasets/shakespeare/shakespeare.txt")
if not path.is_file():
path.parent.mkdir(parents=True, exist_ok=True)
url = "https://homl.info/shakespeare"
urllib.request.urlretrieve(url, path)
return path.read_text()
shakespeare_text = download_shakespeare_text()
Чанд сатри аввал:
>>> print(shakespeare_text[:80])
First Citizen:
Before we proceed any further, hear me speak.
All:
Speak, speak.
Яъне: «Шаҳрванди аввал: Пеш аз идома додан суханамро бишнавед. Ҳама: Бигӯ, бигӯ». Ба Шекспир монанд аст!
Шабака бо адад кор мекунад, на бо матн. Одатан матнро ба токен, масалан калима ё аломат, ҷудо карда, ба ҳар токени имконпазир ID-и бутун медиҳанд. Мо аломатҳоро мегирем. Аввал рӯйхати ҳамаи аломатҳои матнро, яъне луғати токенҳо, меёбем:
>>> vocab = sorted(set(shakespeare_text.lower()))
>>> "".join(vocab)
"\n !$&',-.3:;?abcdefghijklmnopqrstuvwxyz"
lower() фарқи ҳарфи калон ва хурдро дур карда, луғатро хурд мекунад. Ба ҳар аломат индекси он дар луғатро медиҳем. Барои рамзкушоии баромад роҳи баръакс ҳам лозим аст:
>>> char_to_id = {char: index for index, char in enumerate(vocab)}
>>> id_to_char = {index: char for index, char in enumerate(vocab)}
>>> char_to_id["a"]
13
>>> id_to_char[13]
'a'
Ду функсияи ёрирасон: матнро ба тензори ID-ҳо ва онро боз ба матн табдил медиҳанд:
import torch
def encode_text(text):
return torch.tensor([char_to_id[char] for char in text.lower()])
def decode_text(char_ids):
return "".join([id_to_char[char_id.item()] for char_id in char_ids])
Месанҷем:
>>> encoded = encode_text("Hello, world!")
>>> encoded
tensor([20, 17, 24, 24, 27, 6, 1, 35, 27, 30, 24, 16, 2])
>>> decode_text(encoded)
'hello, world!'
Ҳоло як пайдарпайии хеле дарози осори Шекспир дорем. Мисли боби 13 онро ба тирезаҳо тақсим карда, RNN-и пайдарпайӣ-ба-пайдарпайӣ меомӯзонем. Ҳадаф ба вуруд монанд, вале як қадам ба оянда кӯчонида шудааст. Масалан, вуруд ID-ҳои to be or not to b, бе e-и охир, ва ҳадаф ID-ҳои o be or not to be, бо e ва бе t-и аввал, мешавад:
from torch.utils.data import Dataset, DataLoader
class CharDataset(Dataset):
def __init__(self, text, window_length):
self.encoded_text = encode_text(text)
self.window_length = window_length
def __len__(self):
return len(self.encoded_text) - self.window_length
def __getitem__(self, idx):
if idx >= len(self):
raise IndexError("dataset index out of range")
end = idx + self.window_length
window = self.encoded_text[idx : end]
target = self.encoded_text[idx + 1 : end + 1]
return window, target
Матн калон аст, пас тақрибан 90%, як миллион аломатро, барои омӯзиш ва 5%-ӣ, тақрибан 60 000 аломатро, барои тасдиқ ва санҷиш мегирем:
window_length = 50
batch_size = 512 # агар хотираи GPU нарасад, кам кунед
train_set = CharDataset(shakespeare_text[:1_000_000], window_length)
valid_set = CharDataset(shakespeare_text[1_000_000:1_060_000], window_length)
test_set = CharDataset(shakespeare_text[1_060_000:], window_length)
train_loader = DataLoader(train_set, batch_size=batch_size, shuffle=True)
valid_loader = DataLoader(valid_set, batch_size=batch_size)
test_loader = DataLoader(test_set, batch_size=batch_size)
Ҳар баста 512 тирезаи 50-аломатӣ дорад; аломатҳо бо ID ифода шудаанд. Барои ҳар тиреза ҳадафи 50-аломатии як аломат кӯчонидашуда ҳаст. Тартиби тирезаҳои омӯзишӣ дар ҳар давр омехта мешавад (расми 14.1).

Расми 14.1. Бастаи омӯзишӣ аз тирезаҳои омехташуда ва ҳадафҳои кӯчонидашуда иборат аст. Дар расм дарозии тиреза ба ҷойи 50 ба 10 баробар мебошад.
Маслиҳат. Дарозии 50-ро танзим кардан мумкин аст. Пайдарпайии кӯтоҳ омӯзиши осону тезтар дорад, вале модел нақши аз тиреза дарозтарро омӯхта наметавонад. Пас онро аз ҳад хурд накунед.
Аз ҷиҳати техникӣ ID-ҳоро мустақим ба шабака додан мумкин аст, вале натиҷа хуб намешавад. Тавре дар боби 2 дидем, аксари моделҳо фарз мекунанд, ки ададҳои наздик маъноҳои наздик доранд. Аммо ID-ҳои наздик метавонанд токенҳои тамоман бегона ва ID-ҳои дур токенҳои монанд бошанд. Шабака ғарази номуносиб гирифта, барои рафъи он дар омӯзиш душворӣ мекашад.
Як роҳ рамзгузории one-hot аст: масофаи ҳамаи векторҳои он аз ҳам баробар мебошад. Аммо луғати калон вектори калон медиҳад. Мо 39 аломат дорем, пас векторҳои 39-ченака ҳанӯз қобили идораанд. Барои калимаҳо луғат метавонад даҳҳо ҳазор воҳид дошта бошад ва one-hot хеле калон мешавад. Дар шабакаи нейронӣ роҳи беҳтар дорем: embedding.
Embedding-ҳо
Embedding намояндагии зичи додаҳои баландченака, одатан хусусияти категориявӣ, аст. Барои 50 000 категория one-hot вектори камзичи 50 000-ченакаи асосан сифрӣ медиҳад. Embedding бошад вектори нисбатан хурду зич, масалан 300-ченака, мебошад.
Маслиҳат. Андозаи embedding гиперпараметр аст. Як қоидаи тахминӣ: агар андоза ба решаи квадратии шумораи категорияҳо наздик бошад, аксар вақт муносиб аст.
Дар омӯзиши амиқ embedding одатан тасодуфан ибтидосозӣ шуда, ҳамроҳи дигар параметрҳо бо поёнравии градиентӣ омӯзонида мешавад. Масалан, дар додаҳои манзили Калифорнияи боби 2 ocean_proximity-ро бо embedding ифода кардан мумкин аст. "NEAR BAY" метавонад аввал [0.831, 0.696] ва "NEAR OCEAN" [0.127, 0.868] бошад; ин ҷо embedding дученака аст.
Онҳо ҳангоми омӯзиш беҳтар мешаванд. Азбаски ду категория монанданд, поёнравии градиентӣ эҳтимол векторҳояшонро наздик ва аз embedding-и "INLAND" дур мекунад (расми 14.2). Ҳар қадар намояндагӣ хуб бошад, пешгӯӣ барои шабака осонтар аст, пас омӯзиш онро муфид мекунад. Ин омӯзиши намояндагӣ аст; намудҳои дигарашро дар боби 18 мебинем.

Расми 14.2. Embedding-ҳо ҳангоми омӯзиш тадриҷан беҳтар мешаванд.
Embedding на танҳо барои вазифаи ҷорӣ, балки аксар вақт барои вазифаҳои дигар ҳам такроран истифода мешавад. Намунаи маъмул embedding-и калимаҳо аст. Дар NLP аксар вақт истифодаи embedding-и пешомӯхташуда аз омӯзиши embedding-и худ беҳтар аст, чунон ки баъд мебинем.
Ғояи ифодаи калима бо вектор аз солҳои 1960 вуҷуд дорад; барои сохтани векторҳои муфид усулҳои мураккаб, аз ҷумла шабакаҳои нейронӣ, истифода шудаанд. Аммо соли 2013 Томаш Миколов ва муҳаққиқони Google усули самаранокеро пешниҳод карданд, ки аз роҳҳои пешина хеле беҳтар буд. Онҳо шабакаро дар корпуси калони матн барои пешгӯии калимаҳои атрофи калимаи додашуда омӯзонданд ва embedding-ҳои хуб гирифтанд: муродифҳо наздик ва калимаҳои маънояшон вобаста, монанди Фаронса, Испания ва Италия, гурӯҳбандӣ мешуданд.
На танҳо наздикӣ, балки самтҳои маънодор ҳам пайдо мешаванд. Мисоли машҳур: агар векторҳои калимаҳоро ҳисоб кунем, King − Man + Woman ба Queen наздик мешавад (расми 14.3). Яъне мафҳуми ҷинс рамзгузорӣ шудааст! Madrid − Spain + France ҳам ба Paris наздик аст: зоҳиран мафҳуми пойтахт низ ҳаст.

Расми 14.3. Калимаҳои монанд embedding-ҳои наздик доранд ва баъзе меҳварҳо мафҳумҳои маънодорро ифода мекунанд.
Огоҳӣ. Embedding метавонад сохтори маънодор дошта бошад, вале пурғавғо ва душвортафсир ҳам аст. Барои санҷиши мустақил дар охири дафтари боб код оварда шудааст.
Мутаассифона, embedding баъзан бадтарин ғаразҳои моро ҳам мегирад. Масалан, «мард ба шоҳ чун зан ба малика» дуруст бошад ҳам, «мард ба табиб чун зан ба ҳамшираи шафқат» ғарази ҷинсиятӣ аст. Ба хотири адолат, Малвина Ниссим ва ҳамкоронаш соли 2019 гуфтанд, ки ҳамин мисоли мушаххас эҳтимол муболиға шудааст. Бо вуҷуди ин, одилона кардани алгоритмҳои омӯзиши амиқ мавзуи муҳими таҳқиқот аст.
Модули nn.Embedding-и PyTorch матритсаеро нигоҳ медорад, ки барои ҳар категория, масалан токени луғат, як сатр ва барои ҳар андозаи embedding як сутун дорад. Андозанокӣ танзимшаванда ва ибтидосозӣ пешфарз тасодуфӣ аст.
Барои табдили ID танҳо сатри мувофиқро меёбаду бармегардонад. Масалан, панҷ категория бо embedding-и 3D:
>>> import torch.nn as nn
>>> torch.manual_seed(42)
>>> embed = nn.Embedding(5, 3) # 5 категория × embedding-и 3D
>>> embed(torch.tensor([[3, 2], [0, 2]]))
tensor([[[ 0.2674, 0.5349, 0.8094],
[ 2.2082, -0.6380, 0.4617]],
[[ 0.3367, 0.1288, 0.2345],
[ 2.2082, -0.6380, 0.4617]]], grad_fn=)
Категорияи 3 вектори [0.2674, 0.5349, 0.8094], категорияи 2 ду бор [2.2082, −0.6380, 0.4617] ва категорияи 0 [0.3367, 0.1288, 0.2345] гирифт. Категорияҳои 1 ва 4 истифода нашуданд. Қабат ҳанӯз омӯзонида нашудааст, пас векторҳо тасодуфианд.
Embedding аз ҷиҳати математикӣ ба one-hot ва баъди он қабати хаттии бе ғеҷиш баробар аст. Масалан, nn.Linear(5, 3, bias=False) бо вуруди torch.tensor([[0., 0., 0., 1., 0.]]) сатри №3-и матритсаи транспозашудаи вазнҳоро медиҳад. Зеро ҳамаи сатрҳо ба 0 ва танҳо сатри 3 ба 1 зарб мешаванд. Вале nn.Embedding(5, 3) бо torch.tensor([3]) хеле самараноктар аст: сатрро бевосита меёбад, бе one-hot ва зарбҳои бефоидаи сифрӣ.
Акнун барои сохтани модели Шекспир омодаем.
Сохтан ва омӯзонидани char-RNN
Маҷмӯа нисбатан калон ва моделсозии забон душвор аст, пас RNN-и одии чанднейронӣ кофӣ нест. nn.GRU-и дуқабата бо 128 воҳид дар ҳар қабат ва андаке dropout месозем. Баъд шумораи қабатҳо ва воҳидҳоро танзим карда метавонед:
class ShakespeareModel(nn.Module):
def __init__(self, vocab_size, n_layers=2, embed_dim=10, hidden_dim=128,
dropout=0.1):
super().__init__()
self.embed = nn.Embedding(vocab_size, embed_dim)
self.gru = nn.GRU(embed_dim, hidden_dim, num_layers=n_layers,
batch_first=True, dropout=dropout)
self.output = nn.Linear(hidden_dim, vocab_size)
def forward(self, X):
embeddings = self.embed(X)
outputs, _states = self.gru(embeddings)
return self.output(outputs).permute(0, 2, 1)
torch.manual_seed(42)
model = ShakespeareModel(len(vocab)).to(device)
- Қабати аввал ID-и аломатҳоро бо
nn.Embeddingрамзгузорӣ мекунад. Шумораи категорияҳо ба шумораи аломатҳои гуногун баробар аст. Андозаи embedding-ро ҳоло 10 мегирем. Вуруд тензори бутуни[batch_size, window_length], баромад тензори нуқтаи шинокунандаи[batch_size, window_length, embed_dim]аст. - GRU 10 вуруд, 128 воҳиди пинҳонӣ ва ду қабат дорад.
batch_first=True, зеро пешфарз меҳвари вақт аввал аст. - Баромади
nn.Linearбояд 39 воҳид дошта бошад: барои ҳар аломати имконпазир дар ҳар қадам як логит. - Дар
forward()қабатҳо пайдарпай даъват мешаванд. GRU[batch_size, window_length, hidden_dim]ва Linear[batch_size, window_length, vocab_size]мебароранд. Аммоnn.CrossEntropyLossваAccuracyмеҳвари синфро дар ҷойи дуюм мехоҳанд, пас ду андозаи охир иваз мешаванд. Ҳолатҳои ниҳоии GRU-ро нодида мегирем.
Акнун бо nn.CrossEntropyLoss ва ченаки Accuracy ба тарзи маъмул омӯзишу арзёбӣ кунед.
Аломати навбатиро месанҷем:
model.eval() # гузариш ба ҳолати арзёбиро фаромӯш накунед!
text = "To be or not to b"
encoded_text = encode_text(text).unsqueeze(dim=0).to(device)
with torch.no_grad():
Y_logits = model(encoded_text)
predicted_char_id = Y_logits[0, :, -1].argmax().item()
predicted_char = id_to_char[predicted_char_id] # дуруст «e» пешгӯӣ мешавад
Матн рамзгузорӣ, андозаи бастаи 1 илова ва тензор ба GPU гузаронда мешавад. Модел барои ҳар қадам логит медиҳад. Танҳо қадами охир, −1, лозим аст; argmax() ID-и логити калонтарин ва item() адади онро мегирад. ID ба аломат табдил мешавад.
Модел e-ро дуруст ёфт! Акнун бигзор каме худро Шекспир ҳис кунем.
Огоҳӣ. Дар Colab бо GPU омӯзиш метавонад чанд соат гирад. Даврҳоро кам кардан мумкин аст, вале дурустӣ эҳтимол паст мешавад. Агар сессия қатъ шавад, зуд пайваст шавед, то муҳити иҷро аз байн наравад.
Тавлиди матни шекспирмонанди сохта
Метавон ҳар дафъа аломати эҳтимолаш баландтаринро гирифта, ба охири матн илова кард ва матни васеъшударо боз ба модел дод. Ин рамзкушоии ҳарисона (greedy decoding) аст. Аммо аксар вақт ҳамон калимаҳо такрор мешаванд. Ба ҷойи он аломатро аз тақсимоти эҳтимолияти модел тасодуфан мегирем: токени эҳтимолаш p бо ҳамон эҳтимолият интихоб мешавад. Матн гуногунтару шавқовартар мешавад, зеро ҳамеша токени маъмултарин интихоб намегардад. Барои ин torch.multinomial() дорем:
>>> torch.manual_seed(42)
>>> probs = torch.tensor([[0.5, 0.4, 0.1]]) # 50%, 40%, 10%
>>> samples = torch.multinomial(probs, replacement=True, num_samples=8)
>>> samples
tensor([[0, 0, 0, 0, 1, 0, 2, 2]])
Барои идораи гуногунӣ логитҳоро ба ҳарорат (temperature) тақсим мекунем. Ҳарорати ба сифр наздик аломатҳои эҳтимолашон баландро бартарӣ медиҳад; ҳарорати хеле баланд тақсимотро ба якнавохт наздик мекунад. Барои матни сахтқолабу дақиқ, монанди муодилаҳо, одатан пасттар ва барои матни эҷодӣ баландтар интихоб мекунанд. Функсияи ёрирасон:
import torch.nn.functional as F
def next_char(model, text, temperature=1):
encoded_text = encode_text(text).unsqueeze(dim=0).to(device)
with torch.no_grad():
Y_logits = model(encoded_text)
Y_probas = F.softmax(Y_logits[0, :, -1] / temperature, dim=-1)
predicted_char_id = torch.multinomial(Y_probas, num_samples=1).item()
return id_to_char[predicted_char_id]
Функсияи дигар онро такроран даъват карда, матнро дароз мекунад:
def extend_text(model, text, n_chars=80, temperature=1):
for _ in range(n_chars):
text += next_char(model, text, temperature)
return text
Ҳароратҳои паст, миёна ва баландро месанҷем. Натиҷаҳои англисӣ ҳамчун баромади код нигоҳ дошта шудаанд:
>>> print(extend_text(model, "To be or not to b", temperature=0.01))
To be or not to be the state
and the contrary of the state and the sea,
the common people of the
>>> print(extend_text(model, "To be or not to b", temperature=0.4))
To be or not to be the better from the cause
that thou think you may be so be gone.
romeo:
that
>>> print(extend_text(model, "To be or not to b", temperature=100))
To be or not to b-c3;m-rkn&:x:uyve:b&hi n;n-h;wt3k
&cixxh:a!kq$c$ 3 ncq$ ;;wq cp:!xq;yh
!3
d!nhi.
Дар ҳарорати паст the state and the такрор мешавад. Ҳарорати миёна боварибахштар аст, ҳарчанд Ромео имрӯз камгап буд. Дар охир ҳарорат аз ҳад баланд шуд — Шекспирро сӯзондем! Барои матни беҳтар метавон танҳо аз k токени беҳтарин, ё аз хурдтарин маҷмӯи токенҳои беҳтарине гирифт, ки ҷамъи эҳтимолияташон аз остона зиёд аст. Дуюмӣ намунагирии top-p, ё nucleus sampling, ном дорад. Роҳи дигар ҷустуҷӯи шуоӣ, ки баъд мебинем, ё қабату нейронҳои бештари GRU, омӯзиши тӯлонитар ва ҳангоми зарурат мунтазамсозии бештар аст.
Эзоҳ. Модел ҳоло нақши аз
window_length, яъне 50 аломат, дарозтарро омӯхта наметавонад. Калон кардани тиреза омӯзишро душвортар мекунад. Ҳатто LSTM ва GRU пайдарпайиҳои хеле дарозро хуб идора намекунанд.
Ҷолиб аст, ки пешгӯии зоҳиран содаи аломати навбатӣ омӯзиши вазифаҳои баландсатҳро ҳам талаб мекунад. Баъди Great movie, I really фаҳмидани мусбат будани ҷумла кумак мекунад: l-и loved аз h-и hated эҳтимолитар аст. Дар мақолаи соли 2017 Алек Рэдфорд ва муҳаққиқони OpenAI модели калони монанд ба char-RNN-ро дар додаҳои зиёд омӯзонда, диданд, ки як нейрон таснифгари хуби эҳсосот шудааст. Гарчанде омӯзиш бе барчасп буд, нейрони эҳсосот дар меъёрҳои таҳлил натиҷаи пешқадам гирифт. Ин пешомӯзиши беназорат дар NLP-ро пешбинӣ ва ташвиқ кард.
Бо Шекспир хайрухуш карда, ба таҳлили эҳсосот мегузарем.
Таҳлили эҳсосот бо китобхонаҳои Hugging Face
Яке аз истифодаҳои маъмултарини NLP таснифи матн, махсусан таҳлили эҳсосот, аст. Агар MNIST «Салом, ҷаҳон!»-и биниши компютерӣ бошад, таҳлили баррасиҳои IMDb ҳамин нақшро дар NLP дорад. Маҷмӯа 50 000 баррасии англисӣ — 25 000 омӯзишӣ ва 25 000 санҷишӣ — аз Internet Movie Database ва ҳадафи дуии манфӣ (0) ё мусбат (1) дорад. Мисли MNIST, он ҳам дар ноутбук дар вақти қобили қабул ҳалшаванда ва ҳам ба қадри кофӣ душвору шавқовар аст.
Барои боргирӣ Hugging Face Datasets истифода мешавад, ки ба садҳо ҳазор маҷмӯи Hub роҳ медиҳад. Дар Colab насб аст; вагарна pip install datasets. 80%-и қисми омӯзишии аслиро барои омӯзиш ва 20%-и дигарро барои тасдиқ мегирем:
from datasets import load_dataset
imdb_dataset = load_dataset("stanfordnlp/imdb")
split = imdb_dataset["train"].train_test_split(train_size=0.8, seed=42)
imdb_train_set, imdb_valid_set = split["train"], split["test"]
imdb_test_set = imdb_dataset["test"]
Ду баррасиро мебинем:
>>> imdb_train_set[1]["text"]
"'The Rookie' was a wonderful movie about the second chances life holds [...]"
>>> imdb_train_set[1]["label"]
1
>>> imdb_train_set[16]["text"]
"Lillian Hellman's play, adapted by Dashiell Hammett with help from Hellman,
becomes a curious project to come out of gritty Warner Bros. [...] It seems to
take forever for this drama to find its focus, [...], it seems a little
patronizing [...] Lukas has several speeches in the third-act which undoubtedly
won him the Academy Award [...] this tasteful, tactful movie [...] It should be
a heady mix, but instead it's rather dry-eyed and inert. ** from ****"
>>> imdb_train_set[16]["label"]
0
Якум: «The Rookie филми олиҷанобе дар бораи имконияти дуюми зиндагӣ буд…» — мусбат. Дуюм: «Намоишномаи Лиллиан Ҳеллман, ки Дэшиэл Ҳэмметт бо ёрии худи ӯ мутобиқ кардааст, лоиҳаи аҷибе аз Warner Bros. мешавад… Ин драма гӯё беохир меҳвари худро меҷӯяд… андаке аз боло нигоҳкунанда менамояд… Лукас дар пардаи сеюм чанд суханронӣ дорад, ки бешубҳа ба ӯ Оскар оварданд… филми боназокату боэҳтиёт… Омезиши ҳаяҷоновар бояд мебуд, вале хушку беҳаракат аст. Ду ситора аз чор» — манфӣ.
Баррасии якум аз аввал филмро олиҷаноб мегӯяд, пас равшан мусбат аст (label=1). Дуюм душвортар аст: тавсифи муфассал бо фикрҳои ҳам мусбату ҳам манфӣ дорад. Хушбахтона, хулосааш равшан манфӣ аст (label=0), аммо вазифа ҳанӯз сода нест.
Char-RNN-и сода душворӣ мекашад; токенсозии тавонотар лозим аст. Пеш аз идомаи эҳсосот онро меомӯзем.
Токенсозӣ бо Hugging Face Tokenizers
Рико Зеннрих ва ҳамкоронаш аз Донишгоҳи Эдинбург дар мақолаи соли 2016 усулҳои токенсозӣ ва бозсозии матнро дар сатҳи зеркалима омӯхтанд. Модел метавонад ҳатто маънои калимаи нодидаи нодирро тахмин кунад. Агар smartest-ро надида, smart ва пасванди est ба маънои «аз ҳама»-ро омӯхта бошад, маънои калимаро мебарорад. Яке аз усулҳо рамзгузории ҷуфтҳои байтӣ (BPE) аст, ки Филип Гейҷ соли 1994 аввал барои фишурдани додаҳо пешниҳод кард. BPE корпусро ба аломатҳо ҷудо карда, дар ҳар давр ҷуфти аз ҳама сертакрори токенҳои ҳамсояро якҷо ва ба луғат илова мекунад. То андозаи дилхоҳи луғат идома медиҳад.
Tokenizers татбиқҳои хеле самараноки BPE ва алгоритмҳои дигар дорад. Дар Colab пешфарз ҳаст; вагарна pip install tokenizers. Омӯзиши BPE дар IMDb:
import tokenizers
bpe_model = tokenizers.models.BPE(unk_token="")
bpe_tokenizer = tokenizers.Tokenizer(bpe_model)
bpe_tokenizer.pre_tokenizer = tokenizers.pre_tokenizers.Whitespace()
special_tokens = ["", ""]
bpe_trainer = tokenizers.trainers.BpeTrainer(vocab_size=1000,
special_tokens=special_tokens)
train_reviews = [review["text"].lower() for review in imdb_train_set]
bpe_tokenizer.train_from_iterator(train_reviews, bpe_trainer)
- BPE-ро бо токени номаълуми
""месозем. Токенҳои ҳангоми омӯзиш нодида баъд бо он иваз мешаванд. - Аз модел
Tokenizerмесозем. - Китобхона пешкоркард ва паскоркарди ихтиёрӣ дорад.
Whitespaceматнро аз фосилаҳо ҷудо карда, онҳоро мепартояд ва гурӯҳҳои ҳарфию ғайриҳарфиро ҷудо мекунад.Hello, world!!!ба["Hello", ",", "world", "!!!"]табдил мешавад. BPE дар ҳамин порчаҳо кор мекунад. Сарҳадҳои муносиб, ҳадди ақал барои англисӣ, омӯзишро тез ва сифати токенро беҳтар мекунанд. ""барои пур кардани матнҳои кӯтоҳи баста ва""барои номаълумҳо муайян мешаванд.BpeTrainerандозаи ҳадди аксари луғат ва токенҳои махсусро мегирад. Онҳоро ба аввали луғат мегузорад: pad ID-и 0 ва unk ID-и 1 мегирад.- Ҳамаи матнҳои омӯзишии IMDb-ро ба рӯйхат мегирем.
- Токенсозро бо ҳамин рӯйхат меомӯзонем. Пас аз чанд сония омода аст!
Матнеро токенсозӣ мекунем. Дар нусхаи аслӣ эмодзии охири ин сатр ва нохунаки пӯшида гум шудаанд; барои намоиш эмодзии табассум гузошта шудааст:
>>> some_review = "what an awesome movie! 😊"
>>> bpe_encoding = bpe_tokenizer.encode(some_review)
>>> bpe_encoding
Encoding(num_tokens=8, attributes=[ids, type_ids, tokens, offsets,
attention_mask, special_tokens_mask, overflowing])
encode() объекти Encoding бо ҳашт токен медиҳад:
>>> bpe_encoding.tokens
['what', 'an', 'aw', 'es', 'ome', 'movie', '!', '']
>>> bpe_token_ids = bpe_encoding.ids
>>> bpe_token_ids
[303, 139, 373, 149, 240, 211, 4, 1]
Калимаҳои маъмул, монанди what ва movie, як токен доранд; awesome ба чанд порча ҷудо шуд. Табассум дар додаҳои омӯзишӣ набуд, пас `` гардид.
get_vocab() луғати токен→ID, token_to_id() ID-и як токен ва id_to_token() токени ID-ро медиҳад. Одатан барои рӯйхати ID-ҳо decode() истифода мешавад:
>>> bpe_tokenizer.decode(bpe_token_ids)
'what an aw es ome movie !'
Токенсоз ҷойи оғозу анҷоми ҳар токенро дар матни аслӣ нигоҳ медорад, ки махсусан барои ислоҳи хато муфид аст:
>>> bpe_encoding.offsets
[(0, 4), (5, 7), (8, 10), (10, 12), (12, 15), (16, 21), (21, 22), (23, 24)]
Бастаи матнҳоро якбора рамзгузорӣ кардан мумкин аст:
>>> bpe_tokenizer.encode_batch(train_reviews[:3])
[Encoding(num_tokens=281, attributes=[ids, type_ids, tokens, [...]]),
Encoding(num_tokens=114, attributes=[ids, type_ids, tokens, [...]]),
Encoding(num_tokens=285, attributes=[ids, type_ids, tokens, [...]])]
Барои сохтани як тензори ададҳои бутун ҳамаи пайдарпайиҳо бояд ҳамдароз бошанд. Аз токенсоз хоҳиш мекунем, ки кӯтоҳҳоро бо pad то дарозии дарозтарини баста пур кунад. Пайдарпайиҳои аз ҳадди муайян дарозтарро ҳам мебурем, чун RNN бо матни хеле дароз хуб кор намекунад:
bpe_tokenizer.enable_padding(pad_id=0, pad_token="")
bpe_tokenizer.enable_truncation(max_length=500)
Боз рамзгузорӣ мекунем:
>>> bpe_encodings = bpe_tokenizer.encode_batch(train_reviews[:3])
>>> bpe_batch_ids = torch.tensor([encoding.ids for encoding in bpe_encodings])
>>> bpe_batch_ids
tensor([[159, 402, 176, 246, 61, [...], 215, 156, 586, 0, 0, 0, 0],
[ 10, 138, 198, 289, 175, [...], 0, 0, 0, 0, 0, 0, 0],
[289, 15, 209, 398, 177, [...], 50, 29, 22, 17, 24, 18, 24]])
Баррасии якуму дуюм бо сифр пур шуданд. Ҳар Encoding ҳамчунин attention_mask дорад: 1 барои токени воқеӣ, 0 барои пуркунӣ. Бо ниқоб модел метавонад қадамҳои пуркуниро нодида гирад; масалан, тензори мувофиқро ба ниқоб зарб кунед. Баъзан дарозии бе пуркунӣ лозим аст:
>>> attention_mask = torch.tensor([encoding.attention_mask
... for encoding in bpe_encodings])
...
>>> attention_mask
tensor([[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, [...], 1, 1, 1, 0, 0, 0, 0],
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, [...], 0, 0, 0, 0, 0, 0, 0],
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, [...], 1, 1, 1, 1, 1, 1, 1]])
>>> lengths = attention_mask.sum(dim=-1)
>>> lengths
tensor([281, 114, 285])
Фосилаҳо хуб барқарор нашуданд: awesome ба aw es ome, movie! ба movie ! табдил ёфт. Whitespace фосилаҳоро партофтааст ва токенсоз барои ҳамаи токенҳо фосила мегузорад. Барои ислоҳ ByteLevel-ро ба ҷойи он мегузорем: фосилаҳоро бо Ġ нишон медиҳад. Масалан, матн ба Ġwhat Ġan Ġaw es ome Ġmovie ! монанд мешавад. Фосилаҳои байни токенҳоро дур ва Ġ-ро ба фосила иваз кунем, what an awesome movie! мегирем. Қариб комил аст: фосилаи аввали зиёдатӣ осон дур мешавад; эмодзии берун аз луғат `` шуда, decode() онро партофтааст.
ByteLevel ба BPE имкон медиҳад ба ҷойи аломат бо байт кор кунад: ин BPE-и сатҳи байт (BBPE) аст. Масалан, эмодзӣ метавонад дар UTF-8 аз чор байт иборат бошад. Агар луғат ҳамаи 256 байти имконпазирро дошта бошад, BBPE ҳеҷ гоҳ `` намедиҳад: ҳар матн ҳангоми набудани токени дарозтар ба байтҳо ҷудо мешавад. Барои корпусҳои дорои аломатҳои нодир, монанди эмодзиҳо, муносиб аст.
Варианти муҳим WordPiece аст, ки Google соли 2016 пешниҳод кард. Ба BPE монанд, вале ба ҷойи ҷуфти сертакрортарин ҷуфти холаш баландтарин-ро илова мекунад. Дар муодилаи 14.1 frequency(AB) ҷуфтҳои сертакрорро тақвият медиҳад, аммо махраҷ холи ҷуфтеро, ки қисмҳояш худашон сертакроранд, кам мекунад. Ин нормализатсия ба токенҳои муфиду маънодор мусоидат карда, аксар вақт аз BPE ё BBPE пайдарпайии кӯтоҳтар медиҳад.
Муодилаи 14.1. Холи WordPiece барои ҷуфти AB аз токенҳои A ва B.
score(AB) = frequency(AB) / (frequency(A) × frequency(B))
Барои омӯзиш ҳамон кодро гирифта, BPE-ро ба WordPiece ва BpeTrainer-ро ба WordPieceTrainer иваз кунед. Барои ҳамон баррасӣ токенҳо what an aw ##es ##ome movie ! мешаванд ва decode() what an aw ##es ##ome movie ! медиҳад: WordPiece токенҳои дохили калимаро бо ## ишора мекунад. Барои бозсозӣ " ##" ва фосилаҳои пеш аз аломатҳои китобатро дур мекунанд. Эмодзии берун аз луғат боз гум мешавад.
Алгоритми маъмули дигар Unigram LM (Language Model) аст, ки Таку Кудо аз Google соли 2018 пешниҳод кард. Он аз луғати хеле калон бо калима, зеркалима ва аломатҳои маъмул оғоз карда, то андозаи дилхоҳ токенҳои камфоидаро дур мекунад. Барои баҳодиҳии фоида фарзияи содакунанда дорад: корпус аз интихоби мустақили якто-яктои токенҳои луғат ҳосил шудааст. Пас эҳтимолияти AB ба эҳтимолияти A зарби эҳтимолияти B баробар аст. Бо ин фарзия эҳтимолияти тамоми корпусро ҳисоб карда, ҳар давр токенҳоро чунон дур мекунад, ки он зиёд кам нашавад.
Масалан, them, the ва m мутаносибан 1%, 5% ва 2% эҳтимолият доранд. Калимаи them метавонад бо токени ягона 1% ё бо the + m ба андозаи 5% × 2% = 0,1% ҳосил шавад: дар маҷмӯъ 1,1%. Агар them-ро гирем, эҳтимолият то 0,1% мефарояд. Агар the ё m-ро гирем, танҳо то 1% мефарояд, чун токени them мемонад. Агар корпус танҳо ҳамин калима мебуд, алгоритм the ё m-ро дур мекард. Дар воқеият калимаҳои дигар ба ин қисмҳо ниёз доранд ва эҳтимол токенҳои камфоидатари дигар дур мешаванд.
Маслиҳат. Unigram LM барои забонҳое муносиб аст, ки сарҳади калимаашон мисли англисӣ бо фосила муайян намешавад: чинӣ байни калимаҳо фосила надорад, ветнамӣ дар дохили калима ҳам фосила дорад ва олмонӣ чанд калимаро бе фосила мепайвандад.
Ҳамон мақола мунтазамсозии зеркалимаӣ (subword regularization)-ро пешниҳод кард: ҳангоми омӯзиши модели NLP, на токенсоз, андаке тасодуфият ба тақсимкунӣ ворид мешавад. Агар them, the ва m дар луғат бошанд, them баъзан як токен ва баъзан the + m мешавад. Ин умумисозӣ ва устувориро беҳтар мекунад, махсусан дар забонҳои аз ҷиҳати сарфӣ ғанӣ: арабӣ, финӣ, олмонӣ, маҷорӣ, лаҳистонӣ ё туркӣ. Дар онҳо пешванд, пасванд, тасриф ва тағйири дохилӣ иттилооти грамматикии зиёд медиҳанд. Англисӣ, чинӣ, тайӣ ва ветнамӣ бештар ба тартиби калима ва калимаҳои ёрирасон такя мекунанд.
Tokenizers ин мунтазамсозиро бевосита надорад; бояд худ татбиқ кунед ё SentencePiece-и Google-ро бо pip install sentencepiece гиред. Лоиҳаи кушодаасос дар мақолаи соли 2018-и Таку Кудо ва Ҷон Ричардсон тавсиф шудааст.
Ҷадвали 14.1. Муқоисаи се токенсози асосӣ
| Хусусият | BBPE | WordPiece | Unigram LM |
|---|---|---|---|
| Тарзи кор | Якҷо кардани ҷуфтҳои сертакрор | Якҷо кардани ҷуфтҳои зиёдкунандаи эҳтимолияти додаҳо | Дур кардани токенҳои камэҳтимол |
| Бартарӣ | Тез, сода, муносиб барои чандзабонӣ | Тавозуни самаранокӣ ва сифати токен | Токенҳои маънодор, пайдарпайиҳои кӯтоҳ |
| Камбудӣ | Тақсимкуниҳои номуносиб дошта метавонад | Аз BBPE барои чандзабонӣ камустувортар | Омӯзишу токенсозии сусттар |
| Истифода | GPT, Llama, RoBERTa, BLOOM | BERT, DistilBERT, ELECTRA | T5, ALBERT, mBART |
Токенсози худ барои матни соҳавии пуристилоҳ — тиб, ҳуқуқ ё муҳандисӣ, забон ё лаҳҷаи камдода, забони нави барномасозӣ ва ғайра — муфид аст. Вале дар аксари ҳолатҳо токенсози пешомӯхташударо такроран истифода мебаред.
Истифодаи токенсозҳои пешомӯхташуда
Барои боргирӣ Hugging Face Transformers мегирем. Китобхона моделҳои маъмули NLP, биниши компютерӣ, аудио ва ғайра дорад. Барои қариб ҳама вазнҳои пешомӯхташуда ҳастанд ва худкор аз Hub бор мешаванд. Номаш аз он аст, ки аввал моделҳо ҳама трансформер буданд (боби 15), вале ҳоло CNN ва намудҳои дигар ҳам ҳастанд. Ҳар модел абзорҳои лозим, аз ҷумла токенсозро дорад: бо як сатр модели пурраи вазифаи муайянро гирифтан мумкин аст.
Ҳоло токенсози BBPE-и GPT-2, модели тавлиди матнро мегирем ва се баррасии аввалро то ҳадди 500 токен рамзгузорӣ мекунем:
import transformers
gpt2_tokenizer = transformers.AutoTokenizer.from_pretrained("gpt2")
gpt2_encoding = gpt2_tokenizer(train_reviews[:3], truncation=True,
max_length=500)
Объект мисли функсия даъват мешавад. Натиҷа BatchEncoding, объекти луғатмонанд, аст. "input_ids" рӯйхати рӯйхатҳои ID медиҳад. Даҳ токени аввали баррасии якум:
>>> gpt2_token_ids = gpt2_encoding["input_ids"][0][:10]
>>> gpt2_token_ids
[14247, 35030, 1690, 423, 257, 1688, 8046, 13, 484, 1690]
>>> gpt2_tokenizer.decode(gpt2_token_ids)
'stage adaptations often have a major fault. they often'
Яъне: «Мутобиқсозиҳои саҳнавӣ аксар вақт як камбудии калон доранд. Онҳо аксар вақт…»
Барои WordPiece токенсози BERT-ро мегирем — Bidirectional Encoder Representations from Transformers, яъне намояндагиҳои энкодери дусамта аз трансформерҳо. Бар хилофи GPT-2 токени pad дорад, пас бо padding=True кӯтоҳҳоро пур мекунем. return_tensors="pt" ҳам мумкин аст, ки бевосита тензори PyTorch медиҳад:
bert_tokenizer = transformers.AutoTokenizer.from_pretrained("bert-base-uncased")
bert_encoding = bert_tokenizer(train_reviews[:3], padding=True,
truncation=True, max_length=500,
return_tensors="pt")
Маслиҳат.
"bert-base-uncased"номи нуқтаи захирашудаи модел (checkpoint) аст: BERT-и англисие, ки фарқи ҳарфи калону хурдро ба ҳисоб намегирад."bert-large-cased"калонтару ҳассос ба ҳарф ва"bert-base-multilingual-uncased"дар зиёда аз 100 забон пешомӯхташуда аст. Ҳоло танҳо токенсозашонро мегирем.
ID ва ниқобҳо тензорҳои пуркардашудаанд:
>>> bert_encoding["input_ids"]
tensor([[ 101, 2754,17241, 2411, 2031, [...], 102, 0, 0, 0, [...], 0, 0, 0],
[ 101, 1005, 1996, 8305, 1005, [...], 102, 0, 0, 0, [...], 0, 0, 0],
[ 101, 7929, 1010, 2021, 2515, [...], 1012, 1019, 1013, 1019, 102]])
>>> bert_encoding["attention_mask"]
tensor([[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, [...], 0, 0, 0, 0, 0, 0, 0, 0, 0],
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, [...], 0, 0, 0, 0, 0, 0, 0, 0, 0],
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, [...], 1, 1, 1, 1, 1, 1, 1, 1, 1]])
Ба ғайр аз pad, ҳар пайдарпайӣ бо 101 — [CLS] — оғоз ва бо 102 — [SEP] — анҷом меёбад. Онҳо барои BERT лозиманд (боби 15). Агар модели шумо ниёз надошта бошад, ҳангоми даъват add_special_tokens=False гузоред.
Барои Unigram LM моделҳое чун ALBERT, T5 ва XLM-R ҳастанд. Масалан:
albert_tokenizer = transformers.AutoTokenizer.from_pretrained("albert-base-v2")
albert_encoding = albert_tokenizer(train_reviews[:3], padding=True, [...])
Китобхона токенсози шахсии Tokenizers-ро ҳам печонда, ҳамон API-и тайёрҳоро медиҳад:
hf_tokenizer = transformers.PreTrainedTokenizerFast(
tokenizer_object=bpe_tokenizer)
hf_encodings = hf_tokenizer(train_reviews[:3], padding=True, [...])
Эзоҳи тарҷумон.
[...]дар ин ду порчаи аслӣ ҷойнишини аргументҳои ҳазфшуда аст, на коди омодаи иҷро. Барои padding-и токенсози печондашуда ҳам шояд муайян карданиpad_token=""лозим шавад.
Акнун абзорҳои зарурии токенсозиро дорем ва ба эҳсосот бармегардем.
Сохтан ва омӯзонидани модели таҳлили эҳсосот
Бастаҳои баррасиҳо бояд токенсозӣ шаванд, аммо маҷмӯаҳои сохтаамон инро намекунанд. Метавон онҳоро бо map() нав кард ё кори токенсозиро дар боркунак гузошт. collate_fn-и DataLoader барои ҳар баста рӯйхати намунаҳоро мегирад. Функсияи мо онҳоро токенсозӣ, ҳангоми зарурат буридаю пур карда, BatchEncoding бо тензорҳои ID ва ниқоб, ҳамроҳи тензори барчасп медиҳад. WordPiece-и BERT-ро истифода мебарем:
def collate_fn(batch, tokenizer=bert_tokenizer):
reviews = [review["text"] for review in batch]
labels = [[review["label"]] for review in batch]
encodings = tokenizer(reviews, padding=True, truncation=True,
max_length=200, return_tensors="pt")
labels = torch.tensor(labels, dtype=torch.float32)
return encodings, labels
batch_size = 256
imdb_train_loader = DataLoader(imdb_train_set, batch_size=batch_size,
collate_fn=collate_fn, shuffle=True)
imdb_valid_loader = DataLoader(imdb_valid_set, batch_size=batch_size,
collate_fn=collate_fn)
imdb_test_loader = DataLoader(imdb_test_set, batch_size=batch_size,
collate_fn=collate_fn)
Модел:
class SentimentAnalysisModel(nn.Module):
def __init__(self, vocab_size, n_layers=2, embed_dim=128, hidden_dim=64,
pad_id=0, dropout=0.2):
super().__init__()
self.embed = nn.Embedding(vocab_size, embed_dim,
padding_idx=pad_id)
self.gru = nn.GRU(embed_dim, hidden_dim, num_layers=n_layers,
batch_first=True, dropout=dropout)
self.output = nn.Linear(hidden_dim, 1)
def forward(self, encodings):
embeddings = self.embed(encodings["input_ids"])
_outputs, hidden_states = self.gru(embeddings)
return self.output(hidden_states[-1])
Ба модели Шекспир монанд, вале фарқҳои муҳим дорад:
padding_idx=pad_idembedding-и пуркуниро вектори сифрии омӯзишнопазир мекунад, то таъсири pad кам шавад.- Ин пайдарпайӣ-ба-вектор аст. Танҳо ҳолати охирини қабати болоии GRU тавассути Linear пешгӯӣ медиҳад. Барои ин GRU-и яксамта
outputs[:, -1]ваhidden_states[-1]баробаранд. - Баромад як андоза дорад, зеро таснифот дуӣ аст. Тензори 2D як сутун дорад: барои ҳар баррасӣ як логит, мусбат барои баррасии мусбат ва манфӣ барои манфӣ.
forward()BatchEncoding-и ID-ҳои эҳтимолан бурида ва пуркардашударо мегирад.
Бо nn.BCEWithLogitsLoss омӯзонед. Дурустии тасдиқӣ қариб 85% мешавад. Ин хуб аст, гарчанде беҳтарин моделҳо ба сатҳи инсон — андаке аз 90% боло — мерасанд. Ба гуфтаи матн, аз ин хеле боло рафтан душвор аст, зеро бисёр баррасиҳо духӯраанд ва таснифашон ба партофтани танга монанд аст.
Як мушкил: pad-ро пурра нодида намегирем. GRU ҳамаи токенҳои пуркуниро коркард карда, то охирашон метавонад мазмуни баррасиро фаромӯш кунад. Барои пешгирӣ пайдарпайии бастабандишуда (packed sequence) истифода мекунем — сохтори самараноки бастаҳои дарозии гуногун. pack_padded_sequence() тензори пуркардаро бастабандӣ ва pad_packed_sequence() баръакс мекунад:
>>> from torch.nn.utils.rnn import pack_padded_sequence, pad_packed_sequence
>>> sequences = torch.tensor([[1, 2, 0, 0], [5, 6, 7, 8]])
>>> packed = pack_padded_sequence(sequences, lengths=(2, 4),
... enforce_sorted=False, batch_first=True)
...
>>> packed
PackedSequence(data=tensor([5, 1, 6, 2, 7, 8]), [...])
>>> padded, lengths = pad_packed_sequence(packed, batch_first=True)
>>> padded, lengths
(tensor([[1, 2, 0, 0],
[5, 6, 7, 8]]),
tensor([2, 4]))
Пешфарз функсия пайдарпайиҳоро аз дароз ба кӯтоҳ интизор аст; вагарна enforce_sorted=False. Меҳвари вақтро ҳам пеш аз баста интизор аст; агар баста аввал бошад, batch_first=True диҳед.
Қабатҳои бозгаштии PyTorch бастабандиро дастгирӣ карда, дар анҷоми воқеии ҳар пайдарпайӣ меистанд. Дар forward() сатри self.gru(embeddings)-ро чунин иваз кунед:
lengths = encodings["attention_mask"].sum(dim=1)
packed = pack_padded_sequence(embeddings, lengths=lengths.cpu(),
batch_first=True, enforce_sorted=False)
_outputs, hidden_states = self.gru(packed)
Аввал дарозӣ ҳисоб, баъд embedding бастабандӣ ва ба GRU дода мешавад. Акнун pad халал намерасонад. padding_idx дигар ҳатмӣ нест, вале безарар ва барои ислоҳи хато қулай аст, пас нигоҳ медорем.
Роҳи дигари беҳбудӣ хондани баррасӣ дар ҳар ду самт аст.
Эзоҳ. Агар GRU packed sequence гирад,
outputsҳам бастабандишуда мешавад. Пеш аз додан ба қабатҳои баъдӣ онро ба тензори пуркардашуда баргардонед. Барои модели мо танҳо ҳолатҳои пинҳонӣ лозиманд, пас ин кор шарт нест.
RNN-ҳои дусамта
Қабати одӣ дар ҳар қадам танҳо гузашта ва ҳозираро мебинад: он сабабӣ аст ва ояндаро намебинад. Ин барои пешгӯии вақт ё декодери seq2seq дуруст аст. Аммо дар таснифи матн ва энкодери seq2seq аксар вақт пеш аз рамзгузории калима дидани калимаҳои баъдӣ беҳтар аст.
Масалан, дар the right arm, the right person, the right to speak калимаи right мутаносибан «рост», «мувофиқ» ва «ҳуқуқ» аст. Барои фаҳмидани маъно калимаи баъдиро дидан лозим аст. Ду қабати бозгаштиро дар ҳамон вуруд иҷро мекунем: яке чап→рост, дигаре рост→чап, баъд баромадҳои ҳар қадамро одатан ба ҳам мечаспонем. Ин қабати бозгаштии дусамта аст (расми 14.4).

Расми 14.4. Қабати бозгаштии дусамта.
Барои модели эҳсосот ҳангоми сохтани GRU bidirectional=True гузоред. Ин барои nn.RNN ва nn.LSTM ҳам кор мекунад.
Вале моделро мутобиқ кардан лозим: андозаи вуруди Linear дучанд мешавад, зеро ҳолатҳои ду самт якҷо мешаванд:
self.output = nn.Linear(2 * hidden_dim, 1)
Пеш аз баромад ҳолатҳои пешрав ва баръакси қабати болоиро мечаспонем. Сатри охирини forward()-ро чунин иваз кунед:
n_dims = self.output.in_features
top_states = hidden_states[-2:].permute(1, 0, 2).reshape(-1, n_dims)
return self.output(top_states)
Сатри миёна чунин кор мекунад:
- Пештар ҳолатҳо
[num_layers, batch_size, hidden_size], ин ҷо[2, 256, 64], буданд. Бо дусамтагӣ андозаи аввал дучанд шуда,[4, 256, 64]мегардад: қабати 1 пешрав, қабати 1 баръакс, қабати 2 пешрав, қабати 2 баръакс. Барои танҳо болоӣhidden_states[-2:]мегирем. - Бо
permute(1, 0, 2)шакли[256, 2, 64]ва боreshape(-1, n_dims), киn_dims=128аст, шакли[256, 2 * 64]мегирем.
Эзоҳ. Мо танҳо ҳолатҳои охиринро мегирем. Агар баромадҳои ҳар қадами модули дусамтаро истифода баред, андозаи охирини онҳо низ дучанд аст.
Омӯзиш дар ин мисол беҳбудӣ намедиҳад: модели пешина аз ҳад мутобиқ шуда буд ва ин вариант онро бадтар мекунад. Дурустии омӯзиш аз 99% боло, вале тасдиқӣ танҳо 84% мешавад. Метавон мунтазамсозиро зиёд, моделро хурд ё маҷмӯи омӯзиширо калон кард.
Аммо роҳи дигар — embedding-и пешомӯхташударо месанҷем.
Истифодаи embedding ва моделҳои забонии пешомӯхташуда
Модел аз ҳамагӣ 25 000 баррасӣ барои ҳазорҳо токен embedding-и муфид омӯхт — ин таъсирбахш аст! Бо миллиардҳо баррасӣ чӣ мешуд? Хушбахтона, embedding-ҳои дар корпуси дигари хеле калон омӯхташударо ҳам истифода мебарем, ҳатто агар на баррасии филм ва на вазифаи эҳсосот бошад. Калимаи amazing одатан ҳангоми гуфтугӯ дар бораи филм ё чизи дигар маънои яксон дорад.
Токенсози BERT-ро дорем, пас embedding-и онро ҳам мегирем. Аввал моделро бор мекунем:
>>> bert_model = transformers.AutoModel.from_pretrained("bert-base-uncased")
>>> bert_model.embeddings.word_embeddings
Embedding(30522, 768, padding_idx=0)
Модели BERT бо PyTorch татбиқ шуда, nn.Embedding-и одӣ дорад. Метавон қабати худро бо он иваз кард ё барои ҷудо нигоҳ доштани моделҳо матритсаи пешомӯхташударо ба қабати худ гирифт. Embedding.from_pretrained() барои ҳамин аст:
class SentimentAnalysisModelPreEmbeds(nn.Module):
def __init__(self, pretrained_embeddings, n_layers=2, hidden_dim=64,
dropout=0.2):
super().__init__()
weights = pretrained_embeddings.weight.data
self.embed = nn.Embedding.from_pretrained(weights, freeze=True)
embed_dim = weights.shape[-1]
[...] # боқимонда мисли модели пешина
imdb_model_bert_embeds = SentimentAnalysisModelPreEmbeds(
bert_model.embeddings.word_embeddings).to(device)
freeze=True қабатро омӯзишнопазир мекунад, то градиентҳои калони оғози омӯзиш embedding-ро вайрон накунанд. Чанд давр ҳамин тавр омӯзонда, баъд қабатро озод ва барои вазифаи худ fine-tuning мекунед.
Эзоҳи тарҷумон. Барои нусхаи воқеан мустақили матритса метавон
pretrained_embeddings.weight.detach().clone()гирифт. Сатри аслии.weight.dataметавонад хотираро бо вазнҳои манбаъ муштарак нигоҳ дорад.
Embedding-и калимаҳо аз Word2vec-и Google (2013), GloVe-и Стэнфорд (2014), FastText-и Facebook (2016) ва ғайра маъмул шуд. Вале маҳдудият дорад: як калима сарфи назар аз замина як намояндагӣ мегирад. Right дар left and right — чапу рост — ва right and wrong — дурусту нодуруст — яксон рамзгузорӣ мешавад, ҳарчанд маъноҳо гуногунанд. Мэттю Питерс соли 2018 ELMo — Embeddings from Language Models-ро пешниҳод кард: embedding-ҳои вобаста ба замина аз ҳолатҳои дохилии модели забонии амиқи дусамта. Яъне ба ҷойи танҳо embedding, чанд қабати модели пешомӯхташударо такроран мегирем.
Тақрибан ҳамзамон Ҷереми Ҳовард ва Себастян Рудер дар ULMFiT — Universal Language Model Fine-Tuning самаранокии пешомӯзиши беназоратро нишон доданд. Онҳо LSTM-и забониро дар корпуси бузург бо омӯзиши худназоратӣ, яъне барчаспҳои аз худи дода сохташуда, омӯзонданд ва барои вазифаҳои гуногун fine-tuning карданд. Модел дар шаш вазифаи таснифи матн хеле пеш гузашт; дар аксари ҳолатҳо хато 18–24% кам шуд. Ҳамчунин fine-tuning бо танҳо 100 намунаи барчаспдор ба омӯзиш аз сифр бо 10 000 намуна баробар шуд. То ин мақола истифодаи моделҳои пешомӯхташуда асосан меъёри биниши компютерӣ буд ва NLP ба embedding маҳдуд мешуд. Ин оғози давраи нав буд: ҳоло такроран гирифтани модели забонии пешомӯхташуда маъмул аст.
Пас чаро тамоми BERT-ро истифода набарем? Онро мисли функсия бо баррасиҳои токеншуда даъват мекунем:
>>> bert_encoding = bert_tokenizer(train_reviews[:3], padding=True,
... max_length=200, truncation=True,
... return_tensors="pt")
...
>>> bert_output = bert_model(**bert_encoding)
>>> bert_output.last_hidden_state.shape
torch.Size([3, 200, 768])
last_hidden_state барои ҳар токен embedding-и вобаста ба замина дорад. Last ин ҷо қабати охир аст, на қадами охир: BERT трансформер аст, на RNN. Шакл [batch_size, max_sequence_length, hidden_size] мебошад. Онҳоро барои эҳсосот истифода мекунем:
class SentimentAnalysisModelBert(nn.Module):
def __init__(self, n_layers=2, hidden_dim=64, dropout=0.2):
super().__init__()
self.bert = transformers.AutoModel.from_pretrained("bert-base-uncased")
embed_dim = self.bert.config.hidden_size
self.gru = nn.GRU(embed_dim, hidden_dim, [...])
self.output = nn.Linear(hidden_dim, 1)
def forward(self, encodings):
contextualized_embeddings = self.bert(**encodings).last_hidden_state
lengths = encodings["attention_mask"].sum(dim=1)
packed = pack_padded_sequence(contextualized_embeddings, [...])
_outputs, hidden_states = self.gru(packed)
return self.output(hidden_states[-1])
[...] дар асл ҷойнишини аргументҳои ҳамон мисолҳои пешина аст; порча бояд пеш аз иҷро пурра шавад. GRU-ро дусамта кардан шарт нест, зеро embedding аллакай калимаҳои баъдиро дидааст.
Агар BERT-ро бо model.bert.requires_grad_(False) ях карда, боқимондаро омӯзонед, дурустӣ аз 88% боло меравад — беҳбудии назаррас!
Роҳи дигар танҳо embedding-и токени аввал — [CLS]-ро истифода бурдан аст. Ҳангоми пешомӯзиш BERT вазифаи таснифотро танҳо аз ҳамин embedding иҷро мекард (боби 15). Пас муҳимтарин хусусиятҳои матнро дар он ҷамъбаст карданро омӯхтааст. GRU-ро тамоман дур карда, forward()-ро кӯтоҳ мекунем:
def forward(self, encodings):
bert_output = self.bert(**encodings)
return self.output(bert_output.last_hidden_state[:, 0])
Эзоҳи тарҷумон. Баъди дур кардани GRU андозаи вуруди
self.outputҳам бояд ба андозаи BERT, масаланnn.Linear(embed_dim, 1), иваз шавад.
BERT болои embedding-и CLS як қабати иловагии nn.Linear ва nn.Tanh дорад, ки pooler ном дорад. Барои истифода bert_output.last_hidden_state[:, 0]-ро бо bert_output.pooler_output иваз кунед. Баъди чанд давр pooler-ро барои fine-tuning-и IMDb озод кардан мумкин аст.
Аввал токенсоз, баъд embedding, баъд қисми зиёди BERT ва ниҳоят тамоми моделро бо танҳо Linear-и болои pooler такроран гирифтем. Боз як қадам мумкин аст: синфи тайёри таснифи ҷумла.
Синфҳои махсуси вазифаҳо
Барои таснифоти дуӣ BertForSequenceClassification дорем: BERT бо сари таснифот. Танҳо checkpoint, шумораи баромадҳо ва ихтиёран навъи додаҳоро медиҳем. Барои GPU-и хурд ададҳои 16-битӣ мегирем:
from transformers import BertForSequenceClassification
torch.manual_seed(42)
bert_for_binary_clf = BertForSequenceClassification.from_pretrained(
"bert-base-uncased", num_labels=2, dtype=torch.float16).to(device)
Маслиҳат. Синфҳои махсуси бисёр, монанди
BertForQuestionAnsweringваRobertaForSequenceClassification, ҳастанд (боби 15).AutoModelForSequenceClassificationаз рӯйи checkpoint худаш синфи дурустро интихоб мекунад: бароиbert-base-uncasedҳамон BERT. Барои вазифаҳои дигар ҳамAutoModelFor...ҳаст.
Чаро барои таснифоти дуӣ ду баромад? Hugging Face онро барои содагӣ мисли чандсинфа мегирад. Пас ду логит ва nn.CrossEntropyLoss истифода мешавад, на BCE ё BCEWithLogits. Барои эҳтимолият softmax, на sigmoid, лозим аст.
Баррасии хеле мусбатро медиҳем:
>>> encoding = bert_tokenizer(["This was a great movie!"])
>>> with torch.no_grad():
... output = bert_for_binary_clf(
... input_ids=torch.tensor(encoding["input_ids"], device=device),
... attention_mask=torch.tensor(encoding["attention_mask"], device=device))
...
>>> output.logits
tensor([[-0.0120, 0.6304]], device='cuda:0', dtype=torch.float16)
>>> torch.softmax(output.logits, dim=-1)
tensor([[0.3447, 0.6553]], device='cuda:0', dtype=torch.float16)
Аввал токенсозӣ, баъд модел ModelOutput бо логит медиҳад ва softmax эҳтимолиятро мебарорад. Модел синфи 1-ро бо эҳтимоли 65,53% интихоб мекунад. Аммо худи BERT пешомӯхташуда бошад ҳам, сари таснифот ҳанӯз тасодуфӣ аст; то омӯзиш дар IMDb пешгӯиҳояш боэътимод нестанд.
Эзоҳи тарҷумон. Бо барчаспгузории ҳамин боб, 0=манфӣ ва 1=мусбат, қимати 65,53%-и нишоншуда ба синфи мусбат рост меояд. Калимаи «манфӣ» дар шарҳи аслӣ номувофиқ аст.
Агар барчаспро ҳам диҳед, модел талафотро ҳисоб карда, дар ҳамон объект медиҳад:
>>> with torch.no_grad():
... output = bert_for_binary_clf(
... input_ids=torch.tensor(encoding["input_ids"], device=device),
... attention_mask=torch.tensor(encoding["attention_mask"], device=device),
... labels=torch.tensor([1], device=device))
...
>>> output.loss
tensor(0.4226, device='cuda:0', dtype=torch.float16)
Чун num_labels>1, CrossEntropyLoss истифода мешавад, ки аз LogSoftmax ва NLLLoss иборат аст. Агар num_labels=1 бошад, дар ин танзими маъмул MSE истифода мешавад, ки барои регрессия муфид аст.
Моделро бо коди худамон омӯзондан мумкин аст, вале Trainer API қулайтар аст.
Trainer API
Trainer fine-tuning-ро бо коди кам анҷом медиҳад. Checkpoint захира мекунад, қатъкунии барвақт, тақсим байни GPU, сабти ченакҳо, пуркунӣ, бастасозӣ, омехтакунӣ ва ғайра дорад.
Он бо худи dataset кор мекунад, на DataLoader, вале матн бояд токеншуда бошад. map()-и Datasets, ки дар Dataset-и холиси PyTorch нест, инро иҷро мекунад:
def tokenize_batch(batch):
return bert_tokenizer(batch["text"], truncation=True, max_length=200)
tok_imdb_train_set = imdb_train_set.map(tokenize_batch, batched=True)
tok_imdb_valid_set = imdb_valid_set.map(tokenize_batch, batched=True)
tok_imdb_test_set = imdb_test_set.map(tokenize_batch, batched=True)
batched=True корро хеле тез мекунад. map() майдонҳои токенсоз, аз ҷумла ID ва ниқобро, ба ҳар намуна илова мекунад. Номи майдони ID input_ids аст.
Барои арзёбӣ функсияе менависем, ки label_ids ва predictions мегирад:
def compute_accuracy(pred):
return {"accuracy": (pred.label_ids == pred.predictions.argmax(-1)).mean()}
Маслиҳат. Аз ченакҳои Hugging Face Evaluate ҳам истифода кардан мумкин аст. Trainer ченакҳои ҷараёнии TorchMetrics-ро бевосита намегирад, вале онҳоро дар функсия печондан мумкин аст.
Танзимоти омӯзиш:
from transformers import TrainingArguments
train_args = TrainingArguments(
output_dir="my_imdb_model", num_train_epochs=2,
per_device_train_batch_size=128, per_device_eval_batch_size=128,
eval_strategy="epoch", logging_strategy="epoch", save_strategy="epoch",
load_best_model_at_end=True, metric_for_best_model="accuracy",
report_to="none")
Сабтҳо ва checkpoint дар my_imdb_model; омӯзиш ду давр; бастаҳои омӯзишу арзёбӣ 128; арзёбӣ, сабт ва захира дар охири ҳар давр; дар анҷом бор кардани беҳтарин модел аз рӯйи дурустии тасдиқӣ. Давр ва андозаи бастаро вобаста ба VRAM тағйир диҳед. report_to хидматҳои сабт, мисли TensorBoard ё Weights & Biases, мегирад, ки барои каҷхати омӯзиш муфиданд. Барои содагӣ none гузоштем.
Trainer-ро бо модел, танзимот, маҷмӯаҳо, ченаки арзёбӣ ва collator-и пуркунӣ месозем. Баъди ду давр дурустии тасдиқӣ тақрибан 90% мешавад:
from transformers import DataCollatorWithPadding, Trainer
trainer = Trainer(
bert_for_binary_clf, train_args, train_dataset=tok_imdb_train_set,
eval_dataset=tok_imdb_valid_set, compute_metrics=compute_accuracy,
data_collator=DataCollatorWithPadding(bert_tokenizer))
train_output = trainer.train()
Акнун боргирӣ ва fine-tuning-и BERT-ро медонед! Аммо агар дода надошта, танҳо модели аллакай барои эҳсосот мутобиқшударо хоҳед, pipeline дорем.
Pipeline-ҳои Hugging Face
API-и pipeline модели пешомӯхташуда ва пешкоркарду паскоркарди мувофиқро якҷо медиҳад. Масалан, таҳлили 10 баррасии аввали IMDb:
>>> from transformers import pipeline
>>> model_name = "distilbert-base-uncased-finetuned-sst-2-english"
>>> classifier_imdb = pipeline("sentiment-analysis", model=model_name,
... truncation=True, max_length=512)
...
>>> classifier_imdb(train_reviews[:10])
[{'label': 'POSITIVE', 'score': 0.9996108412742615},
{'label': 'POSITIVE', 'score': 0.9998623132705688},
[...]
{'label': 'POSITIVE', 'score': 0.9978922009468079},
{'label': 'NEGATIVE', 'score': 0.9997020363807678}]
Вазифа, модел ва чанд параметрро дода, истифода мекунем! Ҳар баррасӣ POSITIVE — мусбат — ё NEGATIVE — манфӣ — ва холи эҳтимолияти ҳамон барчаспро мегирад. Ин модел дар маҷмӯи тасдиқӣ 88,2% дурустӣ дорад. Чанд нукта:
- Агар моделро надиҳед, pipeline модели пешфарзи вазифаро мегирад. Ҳангоми навиштани китоб барои эҳсосот ҳамин DistilBERT, варианти хурди BERT, буд. Он токенсози бе фарқи ҳарф, пешомӯзиш дар Википедияи англисӣ ва китобҳои англисӣ ва fine-tuning дар Stanford Sentiment Treebank v2 (SST-2) дорад.
- Pipeline ҳангоми дастрасӣ GPU-ро истифода мекунад. Барои интихоб аз чанд GPU параметри
device-ро ба индекси он мегузоред. - Моделҳои Transformers пешфарз дар ҳолати арзёбӣ ҳастанд;
eval()шарт нест. - Хол ба синфи интихобшуда тааллуқ дорад, на ҳамеша мусбат. Дар ин таснифоти дуии softmax он аз 0,5 паст нест, вагарна синфи дигар интихоб мешуд.
Ғараз ва адолат
Матни аслӣ мисол меорад: модел I am from the USA-ро хеле мусбат ва I am from Iraq-ро хеле манфӣ тасниф мекунад; ба Таиланд мусбат ва ба Ветнам манфӣ муносибат дорад. Барои кишвар ё шаҳри худ ҳам санҷидан мумкин аст. Сабаби асосӣ аксар вақт додаҳост: истинодҳои зиёди ҷангҳои Ироқу Ветнам робитаи манфӣ сохтаанд. Fine-tuning танҳо бо ду синфи мусбат/манфӣ ин ғаразро зиёд мекунад. Бо синфи сеюми «бетараф» ғарази кишварӣ асосан кам мешавад.
Дода ягона манбаи ғараз нест: меъморӣ, талафот, мунтазамсозӣ ва оптимизатор ҳам ба дониш таъсир мекунанд. Фаҳмидан ва кам кардани таъсирҳои манфии AI ҳанӯз соҳаи фаъоли таҳқиқот аст. Пеш аз истифодаи воқеӣ моделро бодиққат арзёбӣ кунед. Масалан, баҳогузории резюме бояд одилона бошад. Натиҷаро на танҳо ба ҳисоби миёна, балки барои зермаҷмӯаҳои гуногун ҳам санҷед: мумкин аст миёна хуб, вале барои баъзе гурӯҳҳо хеле бад бошад. Санҷишҳои зиддифактуалӣ ҳам муфиданд: оё танҳо иваз шудани ҷинс ё зодгоҳ пешгӯиро дигар мекунад? Ҳал аз масъала вобаста аст: мувозинат кардани додаҳо, fine-tuning дар маҷмӯи дигар, иваз кардани модели пешомӯхташуда, меъморӣ ё гиперпараметрҳо ва ғайра.
Ҳатто модели комилан одилона метавонад ғаразнок истифода шавад: масалан, кормандони қабул онро танҳо барои як гурӯҳи довталабон истифода баранд.
Модели интихобшуда барои эҳсосоти умумӣ, монанди баррасии филм, хуб аст; барои шабакаҳои иҷтимоӣ шояд модели дар твитҳои зиёд пешомӯхташуда ва баъд барои эҳсосот мутобиқшуда беҳтар бошад. Дар Hub ҷустуҷӯ кунед. Ҳангоми навиштани китоб танҳо text-classification зиёда аз 80 000 модел дошт, пас филтр лозим аст. Аввал вазифаро интихоб ва аз рӯйи маъруфият ё писандҳо тартиб диҳед; баъд забон ва маҷмӯи додаҳоро маҳдуд кунед. Манбаъҳои боэътимод, монанди huggingface, facebook, google ё cardiffnlp-ро авлотар донед. Агар модел коди иҷрошаванда дошта бошад, ба муаллиф бовар ҳосил кунед ва танҳо дар он сурат trust_remote_code=True гузоред.
Ғайр аз эҳсосот вазифаҳои дигар ҳам ҳастанд. Модели дар MultiNLI — хулосабарории забонии бисёржанрӣ — мутобиқшуда ҷуфти матнҳоро, ки бо [SEP] ҷудо шудаанд, ба зиддият, баромади мантиқӣ ё бетараф тасниф мекунад:
>>> model_name = "huggingface/distilbert-base-uncased-finetuned-mnli"
>>> classifier_mnli = pipeline("text-classification", model=model_name)
>>> classifier_mnli([
... "She loves me. [SEP] She loves me not. [SEP]",
... "Alice just woke up. [SEP] Alice is awake. [SEP]",
... "I like dogs. [SEP] Everyone likes dogs. [SEP]"])
...
[{'label': 'contradiction', 'score': 0.9717152714729309},
{'label': 'entailment', 'score': 0.9119168519973755},
{'label': 'neutral', 'score': 0.9509281516075134}]
«Ӯ маро дӯст медорад / дӯст намедорад» зиддият аст. «Алис нав бедор шуд / Алис бедор аст» баромади мантиқӣ аст. «Ман сагҳоро дӯст медорам / Ҳама сагҳоро дӯст медоранд» бетараф аст: якум дуюмро исбот намекунад.
Pipeline инчунин посух ба савол, хулосасозӣ, монандии ҷумла, тавлиди матн, таснифи токен, тарҷума ва ғайра дорад. Дар биниши компютерӣ: таснифи тасвир, сегментатсия, ошкорсозӣ, тасвир→матн, матн→тасвир ва тахмини умқ. Дар аудио: тасниф, нутқ→матн, матн→нутқ ва ғайра. Рӯйхати пурра: https://huggingface.co/tasks.
Огоҳӣ. Пеш аз боргирӣ ба платформа ва муаллиф бовар ҳосил кунед. Модел метавонад коди зараровар, натиҷаҳои ғаразнок, додаҳои ҳифзшудаи ҳуқуқи муаллиф ё иттилооти хусусии ҳассоси эҳтимолан фошшаванда дошта бошад. Вазнҳои заҳролудшуда низ метавонанд танҳо барои баъзе вурудҳо муҳтавои зараровар, масалан таблиғот, тавлид кунанд ва дар дигар ҳолатҳо муқаррарӣ кор кунанд.
То ин ҷо тавлиди матн бо char-RNN ва эҳсосот бо токенсозии зеркалимаӣ, embedding ва моделҳои пурраи пешомӯхташударо дидем. Акнун ба тарҷумаи нейронии мошинӣ (NMT) мегузарем: энкодер–декодери англисӣ→испанӣ месозем ва бо ҷустуҷӯи шуоӣ ва диққат беҳтар мекунем. ¡Vamos — пеш меравем!
Энкодер–декодер барои тарҷумаи нейронии мошинӣ
Аз модели нисбатан содаи seq2seq барои англисӣ→испанӣ оғоз мекунем (расми 14.5).

Расми 14.5. Модели содаи тарҷумаи мошинӣ.
Матни англисӣ ба энкодер меравад ва декодер испанӣ мебарорад. Ҳангоми омӯзиш тарҷумаҳои испанӣ низ вуруди декодеранд, вале як қадам ба қафо кӯчонида шудаанд: дар ҳар қадам токенеро мегирад, ки бояд дар қадами пешина мебаровард, сарфи назар аз он ки воқеан чӣ баровард. Ин омӯзиш бо роҳнамоии ҳадафи дуруст (teacher forcing) аст, ки омӯзишро хеле тез ва натиҷаро беҳтар мекунад. Дар оғоз декодер токени оғози пайдарпайӣ — SoS ё BoS, "" — мегирад ва бояд бо токени анҷом — EoS, "" — хотима диҳад.
Токен аввал ID, масалан 4553 барои soccer, дорад. nn.Embedding аз он вектор месозад; embedding-ҳо ба энкодер ва декодер дода мешаванд.
Дар ҳар қадам Linear-и баромади декодер барои ҳамаи токенҳои луғати испанӣ логит медиҳад. Softmax онҳоро ба эҳтимолият табдил медиҳад: масалан дар оғоз Me метавонад 7% ва Yo 1% дошта бошад. Ин ба таснифоти одӣ монанд аст ва бо nn.CrossEntropyLoss, мисли char-RNN, омӯзонида мешавад.
Баъди омӯзиш матни ҳадафро надорем. Ба ҷойи он токени дар қадами пеш баровардаи худи декодерро ба вурудаш медиҳем (расми 14.6). Дар байни онҳо ҷустуҷӯи embedding ҳам лозим аст, ки дар расм нишон дода нашудааст.

Расми 14.6. Ҳангоми пешгӯӣ декодер токени дар қадами пешина баровардаи худро вуруд мегирад.
Маслиҳат. Сами Бенҷио ва ҳамкоронаш соли 2015 пешниҳод карданд, ки ҳангоми омӯзиш тадриҷан аз токени дурусти пешина ба токени пешинаи худи модел гузарем.
Аввал ҷуфтҳои англисӣ/испаниро аз Tatoeba Challenge бо Datasets мегирем. Лоиҳаи Tatoeba-ро Транг Ҳо соли 2006 барои омӯзиши забон оғоз кард; саҳмгузорон ҷуфтҳои зиёди чандзабонӣ сохтаанд. Муҳаққиқони Донишгоҳи Ҳелсинки аз он маҷмӯи арзёбии тарҷумаро сохтанд. Қисми омӯзишӣ калон аст, пас қисми тасдиқиро барои омӯзиш гирифта, 20%-ашро боз барои тасдиқ ҷудо мекунем. Санҷишро ҳам мегирем:
nmt_original_valid_set, nmt_test_set = load_dataset(
path="ageron/tatoeba_mt_train", name="eng-spa",
split=["validation", "test"])
split = nmt_original_valid_set.train_test_split(train_size=0.8, seed=42)
nmt_train_set, nmt_valid_set = split["train"], split["test"]
Ҳар намуна луғати матни англисӣ ва тарҷумаи испанӣ аст. Масалан:
>>> nmt_train_set[0]
{'source_text': 'Tom tried to break up the fight.',
'target_text': 'Tom trató de disolver la pelea.',
'source_lang': 'eng',
'target_lang': 'spa'}
Яъне «Том кӯшиш кард ҷанҷолро боздорад». Англисӣ ва испанӣ метавонанд токенсозҳои ҷудо дошта бошанд, аммо калимаҳои муштараки animal, color, hotel, hospital, idea, radio, motor ва зеркалимаҳои монанди pre, auto, inter, uni зиёданд. Пас як токенсоз мантиқӣ аст. BPE-ро дар ҳамаи матнҳои омӯзишии ҳар ду забон меомӯзонем:
def train_eng_spa(): # генератор барои ҳамаи матнҳои омӯзишӣ
for pair in nmt_train_set:
yield pair["source_text"]
yield pair["target_text"]
max_length = 256
vocab_size = 10_000
nmt_tokenizer_model = tokenizers.models.BPE(unk_token="")
nmt_tokenizer = tokenizers.Tokenizer(nmt_tokenizer_model)
nmt_tokenizer.enable_padding(pad_id=0, pad_token="")
nmt_tokenizer.enable_truncation(max_length=max_length)
nmt_tokenizer.pre_tokenizer = tokenizers.pre_tokenizers.Whitespace()
nmt_tokenizer_trainer = tokenizers.trainers.BpeTrainer(
vocab_size=vocab_size, special_tokens=["", "", "", ""])
nmt_tokenizer.train_from_iterator(train_eng_spa(), nmt_tokenizer_trainer)
Месанҷем:
>>> nmt_tokenizer.encode("I like soccer").ids
[43, 401, 4381]
>>> nmt_tokenizer.encode(" Me gusta el fútbol").ids
[2, 396, 582, 219, 3356]
Хуб! Синфи хурде месозем, ки ID-ҳои англисӣ — манбаъ, ID-ҳои испанӣ — ҳадаф ва ниқобҳои мувофиқро нигоҳ дорад. Аз namedtuple, яъне tuple-и майдонҳояш номдор, мерос гирифта, to() илова мекунем, то ҳамаи тензорҳо осон ба GPU раванд:
from collections import namedtuple
fields = ["src_token_ids", "src_mask", "tgt_token_ids", "tgt_mask"]
class NmtPair(namedtuple("NmtPairBase", fields)):
def to(self, device):
return NmtPair(self.src_token_ids.to(device), self.src_mask.to(device),
self.tgt_token_ids.to(device), self.tgt_mask.to(device))
Боркунакҳо:
def nmt_collate_fn(batch):
src_texts = [pair['source_text'] for pair in batch]
tgt_texts = [f" {pair['target_text']} " for pair in batch]
src_encodings = nmt_tokenizer.encode_batch(src_texts)
tgt_encodings = nmt_tokenizer.encode_batch(tgt_texts)
src_token_ids = torch.tensor([enc.ids for enc in src_encodings])
tgt_token_ids = torch.tensor([enc.ids for enc in tgt_encodings])
src_mask = torch.tensor([enc.attention_mask for enc in src_encodings])
tgt_mask = torch.tensor([enc.attention_mask for enc in tgt_encodings])
inputs = NmtPair(src_token_ids, src_mask,
tgt_token_ids[:, :-1], tgt_mask[:, :-1])
labels = tgt_token_ids[:, 1:]
return inputs, labels
batch_size = 32
nmt_train_loader = DataLoader(nmt_train_set, batch_size=batch_size,
collate_fn=nmt_collate_fn, shuffle=True)
nmt_valid_loader = DataLoader(nmt_valid_set, batch_size=batch_size,
collate_fn=nmt_collate_fn)
nmt_test_loader = DataLoader(nmt_test_set, batch_size=batch_size,
collate_fn=nmt_collate_fn)
Функсия аввал матнҳои англисӣ ва испаниро ҷудо мекунад. Ба аввали ҳар испанӣ SoS ва ба охир EoS илова менамояд. Ҳар ду бо BPE токенсозӣ мешаванд. ID ва ниқобҳо тензор шуда, дар NmtPair ҷой мегиранд. Муҳим: вуруди декодер як мавқеъ пеш аз ҳадаф аст. Барои намунаи бепуркунӣ он ID-ҳои Me gusta el fútbol-ро мегирад ва ҳадаф ID-ҳои Me gusta el fútbol аст. Дар охир вуруд ва ҳадаф бармегарданд. DataLoader-ҳо мисли пешина сохта мешаванд.
Эзоҳи тарҷумон. Дар шарҳи аслӣ
[:, :-1]«дур кардани EoS» гуфта шудааст. Дар бастаи пуркардашуда он маҳз сутуни охир-ро мебурад, ки барои ҷумлаи кӯтоҳ метавонад pad бошад. Амали асосӣ ҳамон якмавқеъ кӯчонидани вуруд ва ҳадаф аст;ignore_index=0дар талафот pad-ҳоро нодида мегирад.
Модели тарҷума мисли расми 14.5 аст, вале энкодеру декодер embedding-и муштарак доранд ва ҳар GRU дуқабата аст:
class NmtModel(nn.Module):
def __init__(self, vocab_size, embed_dim=512, pad_id=0, hidden_dim=512,
n_layers=2):
super().__init__()
self.embed = nn.Embedding(vocab_size, embed_dim, padding_idx=pad_id)
self.encoder = nn.GRU(embed_dim, hidden_dim, num_layers=n_layers,
batch_first=True)
self.decoder = nn.GRU(embed_dim, hidden_dim, num_layers=n_layers,
batch_first=True)
self.output = nn.Linear(hidden_dim, vocab_size)
def forward(self, pair):
src_embeddings = self.embed(pair.src_token_ids)
tgt_embeddings = self.embed(pair.tgt_token_ids)
src_lengths = pair.src_mask.sum(dim=1)
src_packed = pack_padded_sequence(
src_embeddings, lengths=src_lengths.cpu(),
batch_first=True, enforce_sorted=False)
_, hidden_states = self.encoder(src_packed)
outputs, _ = self.decoder(tgt_embeddings, hidden_states)
return self.output(outputs).permute(0, 2, 1)
torch.manual_seed(42)
vocab_size = nmt_tokenizer.get_vocab_size()
nmt_model = NmtModel(vocab_size).to(device)
Ҳамааш шинос аст: модулҳо дар созанда сохта мешаванд. forward() ҳар ду пайдарпайиро embedding мекунад, англисиро бастабандӣ карда ба энкодер медиҳад, испаниро бо ҳолатҳои охирини ҳамаи қабатҳои энкодер ба декодер медиҳад. Баромади декодер аз Linear гузашта, ду андозаи охир иваз мешаванд, то синф дар ҷойи дуюми лозими CrossEntropyLoss ва Accuracy бошад.
Эзоҳ. Ченаки маъмули NMT BLEU — Bilingual Evaluation Understudy мебошад. Он тарҷумаи моделро бо чанд тарҷумаи хуби инсон муқоиса мекунад. N-граммаҳои дар тарҷумаҳои намунавӣ буда, яъне пайдарпайии n калима, шумурда шуда, басомадашон низ ба ҳисоб меравад. Дар TorchMetrics синфи
BLEUScoreҳаст.
Embedding-и испаниро ҳам бастабандӣ кардан мумкин буд, вале баромади packed-и декодерро пеш аз Linear боз пур кардан лозим мешуд. Ба ҷойи ин талафотро барои нодида гирифтани pad танзим мекунем:
xentropy = nn.CrossEntropyLoss(ignore_index=0) # нодида гирифтани
Акнун моделро, масалан 10 давр бо Nadam ва lr=0.001, омӯзонед. Вақти зиёд мегирад, вале дар назар гиред, ки ҳамзамон ду забон меомӯзад!
Ҳангоми омӯзиш функсияи тарҷума менависем. Аввал матни англисӣ ба энкодер ва танҳо SoS ба декодер меравад. Модел логити токени аввалро медиҳад. Функсия токени беҳтаринро интихоб карда, ба вуруди декодер илова мекунад ва моделро боз даъват менамояд. То EoS якто-якто идома медиҳад:
def translate(model, src_text, max_length=20, pad_id=0, eos_id=3):
tgt_text = ""
token_ids = []
for index in range(max_length):
batch, _ = nmt_collate_fn([{"source_text": src_text,
"target_text": tgt_text}])
with torch.no_grad():
Y_logits = model(batch.to(device))
Y_token_ids = Y_logits.argmax(dim=1) # ID-ҳои беҳтарин
next_token_id = Y_token_ids[0, index].item() # ID-и охирин
next_token = nmt_tokenizer.id_to_token(next_token_id)
tgt_text += " " + next_token
if next_token_id == eos_id:
break
return tgt_text
Эзоҳи тарҷумон. Такроран ба матн табдил дода токенсозӣ кардан метавонад сарҳадҳои зеркалимаҳоро тағйир диҳад. Барои татбиқи устувортар рӯйхати ID-ҳои тавлидшударо нигоҳ дошта, танҳо дар охир рамзкушоӣ мекунанд. Дар коди аслӣ
pad_idваtoken_idsистифода нашудаанд.
Эзоҳ. Ин татбиқ беҳина нест: энкодерро як бор ва декодерро дар ҳар қадам танҳо як бор иҷро кардан мумкин аст, ба ҷойи такроран коркарди тамоми матни дарозшаванда.
Месанҷем:
>>> nmt_model.eval()
>>> translate(nmt_model, "I like soccer.")
' Me gusta el fútbol . '
Кор мекунад! Аз сифр модели англисӣ→испанӣ сохтем.
Беҳинасозии модел
Бо луғати калон, масалан 10 000 токен ё бештар, CrossEntropyLoss вобаста ба сахтафзор суст мешавад. Softmax-и намунагиришуда (sampled softmax), ки Себастян Жан ва ҳамкоронаш соли 2015 пешниҳод карданд, ба ҷойи ҳамаи логитҳо аз логити синфи дуруст ва намунаи тасодуфии синфҳои дигар тахмин месозад. Ҳадафро донистан лозим аст, пас танҳо дар омӯзиш муфид мебошад. PyTorch татбиқи тайёрашро надорад ва бояд худ сохт.
Softmax-и мутобиқшаванда (adaptive softmax)-ро Эдуар Грав ва ҳамкоронаш соли 2016 пешниҳод карданд. Луғатро аз рӯйи басомад гурӯҳ мекунад. Токенҳои маъмул одӣ коркард мешаванд, камёбтарҳо дар гурӯҳҳои калонтар ҷой мегиранд ва танҳо гурӯҳҳои лозим дастрас мешаванд. Ҳам омӯзиш ва ҳам пешгӯиро тез мекунад. Дар PyTorch nn.AdaptiveLogSoftmaxWithLoss ҳаст.
Роҳи дигар муштарак кардани вазнҳо (weight tying) аст: матритсаи embedding-ро ҳамчун вазни қабати баромад мегирем. Офир Пресс ва Лиор Волф соли 2016 пешниҳод карданд. Ба созанда self.output.weight = self.embed.weight илова кунед. Бо bias=False ғеҷиши баромадро ҳам гирифта метавонед. Параметрҳо ва хотира хеле кам шуда, омӯзиш тезтар ва баъзан дурустӣ, махсусан бо додаи кам, беҳтар мешавад. Embedding мисли one-hot ва Linear-и бе ғеҷиш токенро ба фазои векторӣ мебарад; баромад самти баръаксро иҷро мекунад. Дар асл ин бо матритсаи ортогоналӣ, ки транспозашудааш ба баръаксаш баробар аст, шарҳ дода шудааст.
Эзоҳи тарҷумон. Weight tying ортогоналӣ ё баръакспазир будани embedding-ро талаб намекунад; матритса одатан росткунҷа аст. Барои ҳамин пайвасти мустақими вазнҳо танҳо мувофиқати шаклҳо, аз ҷумла баробарии
hidden_dimваembed_dimдар ин меъморӣ, лозим аст. Шарҳи «матритсаи ортогоналӣ» дар асл асосноккунии зарурии ин усул нест.
Модел дар матни кӯтоҳ хуб, вале дар дарозтар душворӣ мекашад:
>>> longer_text = "I like to play soccer with my friends."
>>> translate(nmt_model, longer_text)
' Me gusta jugar con mis amigos . '
Тарҷума мегӯяд: «Ман бо дӯстонам бозӣ карданро дӯст медорам». Футбол гум шуд! Метавон додаҳоро зиёд ва ба ҳар ду GRU қабат илова кард. Энкодерро дусамта кардан мумкин аст, декодерро не: вагарна тамоми тарҷумаро дида, сабабӣ намемонад. Роҳи дигари маъмули беҳбудӣ ҳангоми пешгӯӣ ҷустуҷӯи шуоӣ аст.
Ҷустуҷӯи шуоӣ — Beam Search
Тарҷума як калима дар ҳар навбат ҳосил мешавад. Хатои як қадам боқӣ монда, метавонад занҷири хатогиҳо созад. Барои I like soccer модел аввал Me дуруст мегӯяд, вале баъд ба ҷойи gusta-и танҳо gustan-и ҷамъро интихоб мекунад. Ин фаҳмост: Me gustan дар бисёр ҷумлаҳои «Ман …-ро дӯст медорам» дуруст аст. Баъд los-и ҷамъ меояд. Азбаски los fútbol дар додаҳо набуд — fútbol танҳо аст — модел чизи муносибтар, jugadores, яъне бозигаронро меёбад. Пас «Ман футболро дӯст медорам» ба «Ман бозигаронро дӯст медорам» табдил мешавад.
Чӣ гуна ба модел имкони ислоҳи интихоби гузашта диҳем? Beam search рӯйхати кӯтоҳи k пайдарпайии умедбахш, масалан се беҳтаринро, нигоҳ медорад. Дар ҳар қадами декодер ҳар кадомро як калима дароз карда, боз танҳо k пайдарпайии эҳтимолитаринро нигоҳ медорад. k паҳнои шуоъ (beam width) аст.
Барои I like soccer бо паҳнои 3 (расми 14.7), фарз кунем калимаҳои аввали беҳтарин Me — 75%, a — 3%, como — 1% мебошанд. Акнун барои ҳар се калимаи навбатӣ мегирем. Барои Me шояд gustan — 36%, gusta — 32%, encanta — 16% ва ғайра. Инҳо эҳтимолиятҳои шартӣ, бо шарти оғози Me, мебошанд. Барои a шояд mi 50% бошад. Бо луғати 10 000 барои ҳар номзад 10 000 интихоб дорем.
Эҳтимолияти ҳамаи 30 000 пайдарпайии дутокенӣ (3 × 10 000) ҳисоб мешавад: эҳтимолияти пайдарпайии пешина зарби эҳтимолияти шартии токени нав. Барои Me gustan: 75% × 36% = 27%. Се беҳтаринро мегирем: Me gustan — 27%, Me gusta — 24%, Me encanta — 12%. Ҳоло gustan пеш аст, вале gusta хориҷ нашуд.

Расми 14.7. Ҷустуҷӯи шуоӣ бо паҳнои 3.
Боз барои ҳар се 10 000 идома месанҷем. Се беҳтарин шояд Me gustan los — 10%, Me gusta el — 8%, Me gusta mucho — 2% шаванд. Дар қадами баъдӣ: Me gusta el fútbol — 6%, Me gusta mucho el — 1%, Me gusta el deporte — 0,2%. Акнун Me gustan хориҷ ва тарҷумаи дуруст пешсаф аст. Бе омӯзиши иловагӣ танҳо бо истифодаи оқилонатар натиҷаро беҳтар кардем.
Дафтари боб beam_search()-и сода дорад. Аммо одатан татбиқи GenerationMixin-и Transformers қулай аст. Методҳои generate()-и моделҳои тавлид аз он меоянд. Барои beam search num_beams-ро диҳед. do_sample токени навбатиро аз тақсимот тасодуфӣ мегирад, мисли char-RNN-и пешина. Стратегияҳои дигарро ҳам якҷо кардан мумкин аст; https://homl.info/hfgen-ро бинед.
Бо ин барои ҷумлаҳои нисбатан кӯтоҳ тарҷумаи қобили қабул мегирем:
>>> beam_search(nmt_model, longer_text, beam_width=3)
' Me gusta jugar al fútbol con mis amigos . '
Ин дуруст аст: «Ман бо дӯстонам футболбозиро дӯст медорам». Аммо ҷумлаи дароз ҳанӯз бад мешавад:
>>> longest_text = "I like to play soccer with my friends at the beach."
>>> beam_search(nmt_model, longest_text, beam_width=3)
' Me gusta jugar con jugar con los jug adores de la playa . '
Тақрибан «Ман бо бозӣ кардан бо бозигарони соҳил бозӣ карданро дӯст медорам» — нодуруст аст. Сабаб боз хотираи маҳдуди RNN мебошад. Навоварии ҳалкунанда механизми диққат буд.
Механизмҳои диққат
Роҳи soccer то тарҷумаи fútbol дар расми 14.5 хеле дароз аст. Намояндагии он ва ҳамаи калимаҳои дигар бояд қадамҳои зиёд нигоҳ дошта шавад. Оё роҳро кӯтоҳ кардан мумкин нест?
Ин ғояи асосии мақолаи муҳими соли 2014-и Дзмитрий Баҳданау ва ҳамкоронаш буд. Декодер дар ҳар қадам ба калимаҳои мувофиқи рамзгузоришуда диққат медиҳад. Ҳангоми баровардани fútbol ба soccer нигоҳ мекунад. Роҳ хеле кӯтоҳ ва таъсири маҳдудияти хотира кам мешавад. Диққат тарҷумаи нейронӣ ва умуман омӯзиши амиқро дигаргун карда, махсусан барои ҷумлаҳои аз 30 калима дарозтар натиҷаро хеле беҳтар намуд.
Расми 14.8 модели моро бо диққат нишон медиҳад:
- Аз чап энкодеру декодер ҳастанд. Энкодер дусамта нишон дода шудааст, ки одатан фикри хуб аст.
- Ғайр аз ҳолати охирини энкодер ва токени ҳадафи пешина, ки ҳанӯз истифода мешаванд, вале дар расм нестанд, ҳамаи баромадҳои энкодер низ ба декодер дода мешаванд.
- Декодер ҳамаи онҳоро якбора мустақим гирифта наметавонад, пас дар ҳар қадам ҷамъи вазндорашонро месозад. Вазнҳо муайян мекунанд, ки ба кадом калима диққат диҳад.
- α(t,i) вазни баромади i-уми энкодер дар қадами t-уми декодер аст. Агар α(3,2) аз α(3,0) ва α(3,1) хеле калон бошад, дар ин қадам диққат бештар ба калимаи №2 — soccer — меравад.
- Боқимонда мисли пешина: ҳуҷайра ин вурудҳо, ҳолати пешина ва токени пешинаи ҳадафро мегирад. Ҳангоми пешгӯӣ ба ҷойи ҳадаф баромади пешинаи худ истифода мешавад.

Расми 14.8. Тарҷумаи нейронӣ бо энкодер–декодер ва модели диққат.
Вазнҳои α аз куҷо меоянд? Шабакаи хурди модели ҳамоҳангсозӣ (alignment model), ё қабати диққат, онҳоро ҳамроҳи тамоми модел меомӯзад. Қисми рости расм онро нишон медиҳад:
- Қабати зич ҳар баромади энкодер ва ҳолати пешинаи декодер, масалан h(2)-ро гирифта, барои он хол ё энергия, масалан e(3,2), медиҳад. Хол дараҷаи мувофиқатро чен мекунад.
- Агар модел аллакай me gusta el бароварда бошад, ҳоло исм интизор аст. Soccer ба ҳолати ҷорӣ бештар мувофиқ ва холаш баланд мешавад.
- Ҳамаи холҳо аз softmax мегузаранд ва вазнҳои ниҳоӣ, масалан α(3,2), ҳосил мешаванд. Дар ҳар қадами декодер ҷамъи вазнҳо 1 аст.
Ин диққати Баҳданау аст. Азбаски баромади энкодер ва ҳолати пешинаи декодерро мепайвандад, онро диққати пайвасткунанда ё ҷамъшаванда (additive attention) ҳам меноманд.
Диққат роҳи тамаркузи модел ба қисми вуруд аст. Онро ҳамчунин бозёбии хотираи ҳосилапазир донистан мумкин аст. Фарз кунед, энкодер дар I like soccer фаҳмид, ки I мубтадо, like феъл ва soccer исм аст ва инро дар векторҳо ҷой дод. Декодер I like-ро тарҷума карда, ҳоло исмро мехоҳад. Гӯё энкодер луғати {"subject": "I", "verb": "like", "noun": "soccer"} сохтааст ва декодер қимати калиди noun-ро мепурсад.
Аммо калидҳо аломатҳои дискретии subject ё noun нестанд: векторҳои дар омӯзиш омӯхташудаанд. Дархост ба ягон калид айнан баробар намешавад. Як роҳ чен кардани монандии дархост ба ҳар калид ва бо softmax ба вазнҳои ҷамъашон 1 табдил додани холҳост. Маҳз ҳаминро диққат иҷро мекунад. Агар калиди исм аз ҳама монандтар бошад, вазнаш ба 1 наздик мешавад. Ҷамъи вазндори қиматҳо он гоҳ ба намояндагии soccer наздик аст. Яъне декодер исм хост ва диққат онро ёфт.
Дар татбиқҳои муосир аргументҳо query — дархост, key — калид, value — қимат ном доранд. Дар мисоли мо query ҳолатҳои декодер, key баромадҳои энкодер барои ҳисоби вазнҳо ва value ҳамон баромадҳо барои ҷамъи вазндор мебошанд.
Эзоҳ. Агар вуруд n калима ва баромад тақрибан ҳамдароз бошад, тақрибан n² вазн ҳисоб мешавад. Ин мураккабии квадратӣ барои матни хеле дароз мушкил мегардад.
Механизми дигари маъмул диққати Луонг, ё диққати зарбӣ, аст, ки Минҳ-Танг Луонг ва ҳамкоронаш соли 2015 пешниҳод карданд. Ҳадаф чен кардани монандии баромади энкодер ва ҳолати декодер аст, пас зарби скалярӣ-и векторҳоро истифода мебаранд (боби 4). Он ченаки муносиб ва дар сахтафзори муосир хеле тез аст. Барои ин андозаи векторҳо бояд яксон бошад. Холҳои зарб аз softmax гузашта, вазн медиҳанд.
Онҳо ҳамчунин барои ҳисоби вектори диққат ба ҷойи h(t−1) ҳолати ҷории h(t)-ро пешниҳод карданд. Вектори диққатро h̃₍ₜ₎ ишора мекунем. Онро бо ҳолати декодер часпонда, ҳолати пинҳонии диққатдор месозанд ва аз он токени навбатиро пешгӯӣ мекунанд. Энкодеру декодер метавонанд пеш аз диққат мустақил кор кунанд; диққат ба дохили ҳалқаи бозгаштии декодер дароварда намешавад ва кор содаю тезтар мегардад.
Варианти дигар пеш аз зарби скалярӣ баромадҳои энкодерро аз қабати пуррапайвасти бе ғеҷиш мегузаронад. Ин равиши general-и зарби скалярӣ аст. Муҳаққиқон ду варианти зарбиро бо диққати пайвасткунанда, бо вектори миқёсдиҳии v, муқоиса карда, беҳтар будани зарбиро диданд. Аз ин рӯ диққати пайвасткунанда камтар истифода мешавад. Муодилаи 14.2 се усулро ҷамъбаст мекунад. Дар он y₍ᵢ₎ баромади энкодер дар қадами i ва h₍ₜ₎ ҳолати декодер дар қадами t аст.
Муодилаи 14.2. Механизмҳои диққат.
h̃₍ₜ₎ = Σᵢ α₍ₜ,ᵢ₎ y₍ᵢ₎, α₍ₜ,ᵢ₎ = exp(e₍ₜ,ᵢ₎) / Σᵢ′ exp(e₍ₜ,ᵢ′₎)
⎧ h₍ₜ₎ᵀ y₍ᵢ₎ зарбӣ (dot),
e₍ₜ,ᵢ₎ = ⎨ h₍ₜ₎ᵀ W y₍ᵢ₎ умумӣ (general),
⎩ vᵀ tanh(W [h₍ₜ₎; y₍ᵢ₎]) пайвасткунанда (concat).
Ба энкодер–декодер диққати Луонг илова мекунем. PyTorch функсияи тайёри махсуси Луонг надорад, аммо татбиқи он кӯтоҳ аст:
def attention(query, key, value): # dq == dk ва Lk == Lv
scores = query @ key.transpose(1, 2) # [B,Lq,dq] @ [B,dk,Lk] = [B,Lq,Lk]
weights = torch.softmax(scores, dim=-1) # [B,Lq,Lk]
return weights @ value # [B,Lq,Lk] @ [B,Lv,dv] = [B,Lq,dv]
Аввал холҳо, баъд вазнҳои softmax ва ниҳоят зарби вазнҳо ба value ҳисоб мешавад. Ҳамаи баста якбора самаранок коркард мешавад. Query ҳолатҳои декодер, key баромадҳои энкодер барои хол ва value ҳамон баромадҳо барои ҷамъ мебошанд. Key ва value одатан яксонанд, вале баъзан фарқ мекунанд, масалан калидҳои фишурда барои кам кардани хотира ва тез кардани холҳо.
Дар шарҳҳои код:
Bандозаи баста аст.Lqдарозии дархост ваLkдарозии калид дар бастаи пуркардашуда мебошанд.Lvбояд баLkбаробар бошад: ҳар қимат ба калиди мувофиқ тааллуқ дорад.dqандозаи embedding-и дархост ваdkандозаи калид аст; барои ин зарб бояд баробар бошанд.dvандозаи embedding-и қимат аст.
Маслиҳат. Азбаски ҳама аргументҳо тензорҳои 3D мебошанд, ба ҷойи
@метавонtorch.bmm(), зарби матритсаҳои бастаиро истифода бурд. Он махсус барои ҳамин ҳолат беҳина шудааст. Ҳар матритсаи тензори якум ба матритсаи мувофиқи дуюм зарб мешавад ва натиҷа ҳамон аст.
Дар созандаи NMT танҳо андозаи вуруди баромадро дучанд мекунем, зеро вектори диққат бо баромади декодер пайваст мешавад:
self.output = nn.Linear(2 * hidden_dim, vocab_size)
forward()-и нав:
def forward(self, pair):
src_embeddings = self.embed(pair.src_token_ids) # мисли пешина
tgt_embeddings = self.embed(pair.tgt_token_ids)
src_lengths = pair.src_mask.sum(dim=1)
src_packed = pack_padded_sequence(src_embeddings, [...]) # мисли пешина
encoder_outputs_packed, hidden_states = self.encoder(src_packed)
decoder_outputs, _ = self.decoder(tgt_embeddings, hidden_states)
encoder_outputs, _ = pad_packed_sequence(encoder_outputs_packed,
batch_first=True)
attn_output = attention(query=decoder_outputs,
key=encoder_outputs, value=encoder_outputs)
combined_output = torch.cat((attn_output, decoder_outputs), dim=-1)
return self.output(combined_output).permute(0, 2, 1)
[...] дар сатри packed ҳамон аргументҳои пурраи модели пешина, яъне lengths=src_lengths.cpu(), batch_first=True, enforce_sorted=False, мебошад.
Қадамҳои код:
- Embedding-ҳои англисию испанӣ, дарозии англисӣ ва бастабандӣ мисли пешина ҳисоб мешаванд.
- Энкодер иҷро мешавад, вале акнун баромадҳояшро нигоҳ медорем: диққат онҳоро мехоҳад.
- Декодер мисли пешина иҷро мешавад.
- Баромади энкодери packed ба тензори пуркардашуда бо
pad_packed_sequence()бармегардад, чун на ҳамаи амалиётҳо packed-ро дастгирӣ мекунанд. attention()бо баромадҳои декодер даъват мешавад, на танҳо ҳолатҳои охирин: модул ҳолати ниҳоиро медиҳад, аммо баромадҳои GRU ҳамон ҳолатҳои қабати болоӣ дар ҳар қадаманд.- Диққат ва баромади декодер дар андозаи охир часпонда, аз Linear гузаронда ва ба тартиби лозими талафот табдил мешаванд.
Огоҳӣ. Диққати ин мисол pad-ро ниқоб намекунад. Модел то андозае онро нодида гирифтанро меомӯзад, вале пурра ниқоб кардан беҳтар аст. Тарзи иҷро дар боби 15 меояд.
Ҳамин! Баъди омӯзиш ҷумлаҳои хеле дарозтар хубтар мешаванд:
>>> translate(nmt_attn_model, longest_text)
' Me gusta jugar fu tbol con mis amigos en la playa . '
Маъно дуруст аст: «Ман бо дӯстонам дар соҳил футболбозиро дӯст медорам», ҳарчанд дар баромади нишоншуда fútbol ба зеркалимаҳои ҷудо барқарор шудааст. Ҳатто beam search лозим нашуд!
Диққат чунон тавоно баромад, ки муҳаққиқони Google кӯшиш карданд қабатҳои бозгаштиро пурра дур карда, танҳо қабатҳои пешрав ва диққатро истифода баранд. Тааҷҷубовар аст, ки хуб кор кард. Мақоларо “Attention Is All You Need” — «Ҳамаи он чи лозим аст, диққат мебошад» номида, меъмории Transformer-ро муаррифӣ карданд. Ин оғози инқилоби бузург дар NLP ва берун аз он буд. Дар боби оянда трансформер ва таъсири онро ба омӯзиши амиқ меомӯзем.

Барномасозӣ
Олами Афсонаҳо
Ахбор