Seq2seq (secvență la secvență) Model cu PyTorch

Cuprins:

Anonim

Ce este NLP?

NLP sau Procesarea limbajului natural este una dintre ramurile populare ale inteligenței artificiale care ajută computerele să înțeleagă, să manipuleze sau să răspundă la un om în limbajul lor natural. NLP este motorul din spatele Google Translate care ne ajută să înțelegem alte limbi.

Ce este Seq2Seq?

Seq2Seq este o metodă de traducere automată bazată pe codificator-decodor și procesare a limbajului care mapează o intrare a secvenței la o ieșire a secvenței cu o etichetă și o valoare de atenție. Ideea este de a utiliza 2 RNN-uri care vor funcționa împreună cu un simbol special și vor încerca să prezică următoarea secvență de stare din secvența anterioară.

Pasul 1) Încărcarea datelor noastre

Pentru setul nostru de date, veți utiliza un set de date din perechi de propoziții bilingve delimitate de tab-uri. Aici voi folosi setul de date din engleză în indoneziană. Puteți alege orice doriți, dar nu uitați să schimbați numele fișierului și directorul din cod.

from __future__ import unicode_literals, print_function, divisionimport torchimport torch.nn as nnimport torch.optim as optimimport torch.nn.functional as Fimport numpy as npimport pandas as pdimport osimport reimport randomdevice = torch.device("cuda" if torch.cuda.is_available() else "cpu")

Pasul 2) Pregătirea datelor

Nu puteți utiliza setul de date direct. Trebuie să împărțiți propozițiile în cuvinte și să le convertiți în One-Hot Vector. Fiecare cuvânt va fi indexat în mod unic în clasa Lang pentru a crea un dicționar. Clasa Lang va stoca fiecare propoziție și o va împărți cuvânt cu cuvânt cu addSentence. Apoi creați un dicționar prin indexarea fiecărui cuvânt necunoscut pentru Secvență în ordine de modele.

SOS_token = 0EOS_token = 1MAX_LENGTH = 20#initialize Lang Classclass Lang:def __init__(self):#initialize containers to hold the words and corresponding indexself.word2index = {}self.word2count = {}self.index2word = {0: "SOS", 1: "EOS"}self.n_words = 2 # Count SOS and EOS#split a sentence into words and add it to the containerdef addSentence(self, sentence):for word in sentence.split(' '):self.addWord(word)#If the word is not in the container, the word will be added to it,#else, update the word counterdef addWord(self, word):if word not in self.word2index:self.word2index[word] = self.n_wordsself.word2count[word] = 1self.index2word[self.n_words] = wordself.n_words += 1else:self.word2count[word] += 1

Clasa Lang este o clasă care ne va ajuta să facem un dicționar. Pentru fiecare limbă, fiecare propoziție va fi împărțită în cuvinte și apoi adăugată în container. Fiecare container va stoca cuvintele în indexul corespunzător, va număra cuvântul și va adăuga indexul cuvântului, astfel încât să îl putem folosi pentru a găsi indexul unui cuvânt sau pentru a găsi un cuvânt din indexul acestuia.

Deoarece datele noastre sunt separate prin TAB, trebuie să utilizați panda ca încărcător de date. Pandele vor citi datele noastre ca dateFrame și le vor împărți în propoziția sursă și țintă. Pentru fiecare propoziție pe care o ai,

  • îl vei normaliza cu litere mici,
  • eliminați toate cele fără caracter
  • convertiți în ASCII din Unicode
  • împarte frazele, astfel încât să ai fiecare cuvânt în el.
#Normalize every sentencedef normalize_sentence(df, lang):sentence = df[lang].str.lower()sentence = sentence.str.replace('[^A-Za-z\s]+', '')sentence = sentence.str.normalize('NFD')sentence = sentence.str.encode('ascii', errors='ignore').str.decode('utf-8')return sentencedef read_sentence(df, lang1, lang2):sentence1 = normalize_sentence(df, lang1)sentence2 = normalize_sentence(df, lang2)return sentence1, sentence2def read_file(loc, lang1, lang2):df = pd.read_csv(loc, delimiter='\t', header=None, names=[lang1, lang2])return dfdef process_data(lang1,lang2):df = read_file('text/%s-%s.txt' % (lang1, lang2), lang1, lang2)print("Read %s sentence pairs" % len(df))sentence1, sentence2 = read_sentence(df, lang1, lang2)source = Lang()target = Lang()pairs = []for i in range(len(df)):if len(sentence1[i].split(' ')) < MAX_LENGTH and len(sentence2[i].split(' ')) < MAX_LENGTH:full = [sentence1[i], sentence2[i]]source.addSentence(sentence1[i])target.addSentence(sentence2[i])pairs.append(full)return source, target, pairs

O altă funcție utilă pe care o veți folosi este convertirea perechilor în tensor. Acest lucru este foarte important deoarece rețeaua noastră citește doar date de tip tensor. De asemenea, este important, deoarece aceasta este partea în care la fiecare capăt al propoziției va exista un simbol pentru a spune rețelei că intrarea este terminată. Pentru fiecare cuvânt din propoziție, va primi indexul din cuvântul corespunzător din dicționar și va adăuga un indicativ la sfârșitul propoziției.

def indexesFromSentence(lang, sentence):return [lang.word2index[word] for word in sentence.split(' ')]def tensorFromSentence(lang, sentence):indexes = indexesFromSentence(lang, sentence)indexes.append(EOS_token)return torch.tensor(indexes, dtype=torch.long, device=device).view(-1, 1)def tensorsFromPair(input_lang, output_lang, pair):input_tensor = tensorFromSentence(input_lang, pair[0])target_tensor = tensorFromSentence(output_lang, pair[1])return (input_tensor, target_tensor)

Seq2Seq Model

Sursa: Seq2Seq

Modelul PyTorch Seq2seq este un fel de model care folosește decodificatorul de codificare PyTorch deasupra modelului. Codificatorul va codifica cuvântul propoziției prin cuvinte într-un indexat de vocabular sau cuvinte cunoscute cu index, iar decodificatorul va prezice ieșirea intrării codificate decodificând intrarea în ordine și va încerca să utilizeze ultima intrare ca intrare următoare dacă este posibil. Cu această metodă, este de asemenea posibil să se prezică următoarea intrare pentru a crea o propoziție. Fiecărei propoziții i se va atribui un simbol pentru a marca sfârșitul secvenței. La sfârșitul predicției, va exista și un simbol pentru a marca sfârșitul ieșirii. Deci, de la codificator, va trece o stare către decodor pentru a prezice ieșirea.

Sursa: Seq2Seq Model

Codificatorul va codifica propoziția de intrare cuvânt cu cuvânt în ordine și la final va exista un simbol pentru a marca sfârșitul unei propoziții. Codificatorul este format dintr-un strat de încorporare și un strat GRU. Stratul de încorporare este un tabel de căutare care stochează încorporarea intrărilor noastre într-un dicționar de cuvinte de dimensiuni fixe. Acesta va fi trecut la un strat GRU. Stratul GRU este o unitate recurentă Gated care constă dintr-un tip de strat multiplu de RNN care va calcula intrarea secvențiată. Acest strat va calcula starea ascunsă din cel anterior și va actualiza resetarea, actualizarea și porțile noi.

Sursa: Seq2Seq

Decodorul va decoda intrarea de la ieșirea codificatorului. Se va încerca să prezică următoarea ieșire și să încerce să o folosească ca intrare următoare, dacă este posibil. Decodorul constă dintr-un strat de încorporare, un strat GRU și un strat liniar. Stratul de încorporare va face un tabel de căutare pentru ieșire și va fi trecut într-un strat GRU pentru a calcula starea de ieșire prevăzută. După aceea, un strat liniar va ajuta la calcularea funcției de activare pentru a determina adevărata valoare a ieșirii prezise.

class Encoder(nn.Module):def __init__(self, input_dim, hidden_dim, embbed_dim, num_layers):super(Encoder, self).__init__()#set the encoder input dimesion , embbed dimesion, hidden dimesion, and number of layersself.input_dim = input_dimself.embbed_dim = embbed_dimself.hidden_dim = hidden_dimself.num_layers = num_layers#initialize the embedding layer with input and embbed dimentionself.embedding = nn.Embedding(input_dim, self.embbed_dim)#intialize the GRU to take the input dimetion of embbed, and output dimention of hidden and#set the number of gru layersself.gru = nn.GRU(self.embbed_dim, self.hidden_dim, num_layers=self.num_layers)def forward(self, src):embedded = self.embedding(src).view(1,1,-1)outputs, hidden = self.gru(embedded)return outputs, hiddenclass Decoder(nn.Module):def __init__(self, output_dim, hidden_dim, embbed_dim, num_layers):super(Decoder, self).__init__()#set the encoder output dimension, embed dimension, hidden dimension, and number of layersself.embbed_dim = embbed_dimself.hidden_dim = hidden_dimself.output_dim = output_dimself.num_layers = num_layers# initialize every layer with the appropriate dimension. For the decoder layer, it will consist of an embedding, GRU, a Linear layer and a Log softmax activation function.self.embedding = nn.Embedding(output_dim, self.embbed_dim)self.gru = nn.GRU(self.embbed_dim, self.hidden_dim, num_layers=self.num_layers)self.out = nn.Linear(self.hidden_dim, output_dim)self.softmax = nn.LogSoftmax(dim=1)def forward(self, input, hidden):# reshape the input to (1, batch_size)input = input.view(1, -1)embedded = F.relu(self.embedding(input))output, hidden = self.gru(embedded, hidden)prediction = self.softmax(self.out(output[0]))return prediction, hiddenclass Seq2Seq(nn.Module):def __init__(self, encoder, decoder, device, MAX_LENGTH=MAX_LENGTH):super().__init__()#initialize the encoder and decoderself.encoder = encoderself.decoder = decoderself.device = devicedef forward(self, source, target, teacher_forcing_ratio=0.5):input_length = source.size(0) #get the input length (number of words in sentence)batch_size = target.shape[1]target_length = target.shape[0]vocab_size = self.decoder.output_dim#initialize a variable to hold the predicted outputsoutputs = torch.zeros(target_length, batch_size, vocab_size).to(self.device)#encode every word in a sentencefor i in range(input_length):encoder_output, encoder_hidden = self.encoder(source[i])#use the encoder’s hidden layer as the decoder hiddendecoder_hidden = encoder_hidden.to(device)#add a token before the first predicted worddecoder_input = torch.tensor([SOS_token], device=device) # SOS#topk is used to get the top K value over a list#predict the output word from the current target word. If we enable the teaching force, then the #next decoder input is the next word, else, use the decoder output highest value.for t in range(target_length):decoder_output, decoder_hidden = self.decoder(decoder_input, decoder_hidden)outputs[t] = decoder_outputteacher_force = random.random() < teacher_forcing_ratiotopv, topi = decoder_output.topk(1)input = (target[t] if teacher_force else topi)if(teacher_force == False and input.item() == EOS_token):breakreturn outputs

Pasul 3) Instruirea modelului

Procesul de instruire în modelele Seq2seq este început cu conversia fiecărei perechi de propoziții în tensori din indexul lor Lang. Modelul nostru de secvență la secvență va folosi SGD ca optimizator și funcția NLLLoss pentru a calcula pierderile. Procesul de instruire începe cu alimentarea perechii de propoziții către model pentru a prezice rezultatul corect. La fiecare pas, ieșirea din model va fi calculată cu cuvintele adevărate pentru a găsi pierderile și a actualiza parametrii. Deci, deoarece veți utiliza 75000 de iterații, modelul nostru de secvență la secvență va genera 75000 de perechi aleatorii din setul nostru de date.

teacher_forcing_ratio = 0.5def clacModel(model, input_tensor, target_tensor, model_optimizer, criterion):model_optimizer.zero_grad()input_length = input_tensor.size(0)loss = 0epoch_loss = 0# print(input_tensor.shape)output = model(input_tensor, target_tensor)num_iter = output.size(0)print(num_iter)#calculate the loss from a predicted sentence with the expected resultfor ot in range(num_iter):loss += criterion(output[ot], target_tensor[ot])loss.backward()model_optimizer.step()epoch_loss = loss.item() / num_iterreturn epoch_lossdef trainModel(model, source, target, pairs, num_iteration=20000):model.train()optimizer = optim.SGD(model.parameters(), lr=0.01)criterion = nn.NLLLoss()total_loss_iterations = 0training_pairs = [tensorsFromPair(source, target, random.choice(pairs))for i in range(num_iteration)]for iter in range(1, num_iteration+1):training_pair = training_pairs[iter - 1]input_tensor = training_pair[0]target_tensor = training_pair[1]loss = clacModel(model, input_tensor, target_tensor, optimizer, criterion)total_loss_iterations += lossif iter % 5000 == 0:avarage_loss= total_loss_iterations / 5000total_loss_iterations = 0print('%d %.4f' % (iter, avarage_loss))torch.save(model.state_dict(), 'mytraining.pt')return model

Pasul 4) Testați modelul

Procesul de evaluare a Seq2seq PyTorch este de a verifica ieșirea modelului. Fiecare pereche de secvențe pentru a secvența modele va fi introdusă în model și va genera cuvintele prezise. După aceea, veți vedea cea mai mare valoare la fiecare ieșire pentru a găsi indexul corect. Și, în cele din urmă, veți compara pentru a vedea predicția modelului nostru cu propoziția adevărată

def evaluate(model, input_lang, output_lang, sentences, max_length=MAX_LENGTH):with torch.no_grad():input_tensor = tensorFromSentence(input_lang, sentences[0])output_tensor = tensorFromSentence(output_lang, sentences[1])decoded_words = []output = model(input_tensor, output_tensor)# print(output_tensor)for ot in range(output.size(0)):topv, topi = output[ot].topk(1)# print(topi)if topi[0].item() == EOS_token:decoded_words.append('')breakelse:decoded_words.append(output_lang.index2word[topi[0].item()])return decoded_wordsdef evaluateRandomly(model, source, target, pairs, n=10):for i in range(n):pair = random.choice(pairs)print(‘source {}’.format(pair[0]))print(‘target {}’.format(pair[1]))output_words = evaluate(model, source, target, pair)output_sentence = ' '.join(output_words)print(‘predicted {}’.format(output_sentence))

Acum, să începem antrenamentul nostru cu Seq to Seq, cu numărul de iterații de 75000 și numărul de strat RNN de 1 cu dimensiunea ascunsă de 512.

lang1 = 'eng'lang2 = 'ind'source, target, pairs = process_data(lang1, lang2)randomize = random.choice(pairs)print('random sentence {}'.format(randomize))#print number of wordsinput_size = source.n_wordsoutput_size = target.n_wordsprint('Input : {} Output : {}'.format(input_size, output_size))embed_size = 256hidden_size = 512num_layers = 1num_iteration = 100000#create encoder-decoder modelencoder = Encoder(input_size, hidden_size, embed_size, num_layers)decoder = Decoder(output_size, hidden_size, embed_size, num_layers)model = Seq2Seq(encoder, decoder, device).to(device)#print modelprint(encoder)print(decoder)model = trainModel(model, source, target, pairs, num_iteration)evaluateRandomly(model, source, target, pairs)

După cum puteți vedea, propoziția noastră prezisă nu se potrivește foarte bine, așa că, pentru a obține o precizie mai mare, trebuie să vă antrenați cu mai multe date și să încercați să adăugați mai multe iterații și număr de straturi folosind secvența pentru învățarea secvențială.

random sentence ['tom is finishing his work', 'tom sedang menyelesaikan pekerjaannya']Input : 3551 Output : 4253Encoder((embedding): Embedding(3551, 256)(gru): GRU(256, 512))Decoder((embedding): Embedding(4253, 256)(gru): GRU(256, 512)(out): Linear(in_features=512, out_features=4253, bias=True)(softmax): LogSoftmax())Seq2Seq((encoder): Encoder((embedding): Embedding(3551, 256)(gru): GRU(256, 512))(decoder): Decoder((embedding): Embedding(4253, 256)(gru): GRU(256, 512)(out): Linear(in_features=512, out_features=4253, bias=True)(softmax): LogSoftmax()))5000 4.090610000 3.912915000 3.817120000 3.836925000 3.819930000 3.795735000 3.803740000 3.809845000 3.753050000 3.711955000 3.726360000 3.693365000 3.684070000 3.705875000 3.7044> this is worth one million yen= ini senilai satu juta yen< tom sangat satu juta yen > she got good grades in english= dia mendapatkan nilai bagus dalam bahasa inggris< tom meminta nilai bagus dalam bahasa inggris > put in a little more sugar= tambahkan sedikit gula< tom tidak > are you a japanese student= apakah kamu siswa dari jepang< tom kamu memiliki yang jepang > i apologize for having to leave= saya meminta maaf karena harus pergi< tom tidak maaf karena harus pergi ke> he isnt here is he= dia tidak ada di sini kan< tom tidak > speaking about trips have you ever been to kobe= berbicara tentang wisata apa kau pernah ke kobe< tom tidak > tom bought me roses= tom membelikanku bunga mawar< tom tidak bunga mawar > no one was more surprised than tom= tidak ada seorangpun yang lebih terkejut dari tom< tom ada orang yang lebih terkejut > i thought it was true= aku kira itu benar adanya< tom tidak