Seq2seq (Sequence to Sequence) modelis ar PyTorch

Satura rādītājs:

Anonim

Kas ir NLP?

NLP jeb dabiskās valodas apstrāde ir viena no populārākajām mākslīgā intelekta nozarēm, kas palīdz datoriem saprast, manipulēt vai reaģēt uz viņu viņu dabiskajā valodā. NLP ir Google tulkotāja motors, kas palīdz mums saprast citas valodas.

Kas ir Seq2Seq?

Seq2Seq ir kodētāja-dekodera mašīntulkošanas un valodas apstrādes metode, kas secības ievadi kartē ar secības izvadi ar tagu un uzmanības vērtību. Ideja ir izmantot 2 RNN, kas darbosies kopā ar īpašu marķieri, un mēģināt paredzēt nākamo stāvokļu secību no iepriekšējās secības.

1. darbība. Ielādējiet mūsu datus

Mūsu datu kopai izmantosiet datu kopu no tabulētā atdalītajiem divvalodu teikumu pāriem. Šeit es izmantošu angļu - indonēziešu datu kopu. Jūs varat izvēlēties visu, kas jums patīk, bet neaizmirstiet kodā mainīt faila nosaukumu un direktoriju.

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")

2. solis) Datu sagatavošana

Jūs nevarat tieši izmantot datu kopu. Jums jāsadala teikumi vārdos un jāpārvērš par One-Hot Vector. Lai izveidotu vārdnīcu, Lang klasē katrs vārds tiks unikāli indeksēts. Lang klase glabās katru teikumu un sadalīs to pa vārdam ar addSentence. Pēc tam izveidojiet vārdnīcu, indeksējot visus secības nezināmos vārdus secības modeļos.

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

Langa klase ir klase, kas mums palīdzēs izveidot vārdnīcu. Katrai valodai katrs teikums tiks sadalīts vārdos un pēc tam pievienots konteineram. Katrs konteiners glabās vārdus attiecīgajā rādītājā, saskaitīs vārdu un pievienos vārda indeksu, lai mēs to varētu izmantot, lai atrastu vārda indeksu vai atrastu vārdu no tā rādītāja.

Tā kā mūsu datus atdala TAB, jums kā datu ielādētājs jāizmanto pandas. Pandas lasīs mūsu datus kā dataFrame un sadalīs tos mūsu avota un mērķa teikumā. Par katru teikumu, kas jums ir,

  • jūs to normalizēsit ar mazajiem burtiem,
  • noņemt visus bez rakstzīmes
  • pārveidot par ASCII no Unicode
  • sadaliet teikumus, tāpēc jums ir katrs vārds.
#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

Vēl viena noderīga funkcija, kuru izmantosit, ir pāru konvertēšana Tensorā. Tas ir ļoti svarīgi, jo mūsu tīklā tiek lasīti tikai tenzora tipa dati. Tas ir svarīgi arī tāpēc, ka šī ir tā daļa, kurā katrā teikuma galā būs marķieris, kas paziņo tīklam, ka ievade ir pabeigta. Katram vārdam teikumā tas iegūs indeksu no attiecīgā vārdnīcas vārda un teikuma beigās pievienos marķieri.

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 modelis

Avots: Seq2Seq

PyTorch Seq2seq modelis ir sava veida modelis, kas modeļa augšpusē izmanto PyTorch kodētāja dekodētāju. Kodētājs kodēs teikuma vārdu pēc vārdiem vārdnīcas indeksā vai zināmos vārdos ar indeksu, un dekodētājs prognozēs kodētās ievades iznākumu, dekodējot ievadi secīgi, un mēģinās izmantot pēdējo ievadi kā nākamo ievadi, ja tas ir iespējams. Izmantojot šo metodi, ir iespējams arī paredzēt nākamo ievadi, lai izveidotu teikumu. Katram teikumam tiks piešķirts marķējums, lai atzīmētu secības beigas. Prognozēšanas beigās būs arī marķējums, kas atzīmēs izejas beigas. Tātad, no kodētāja tas nodos stāvokli dekodētājam, lai prognozētu izvadi.

Avots: Seq2Seq modelis

Kodētājs kodēs mūsu ievadīto teikumu vārdu pa vārdam pēc kārtas un beigās būs marķieris, kas atzīmēs teikuma beigas. Kodētājs sastāv no slāņa Iegulšana un GRU slāņiem. Iegulšanas slānis ir uzmeklēšanas tabula, kurā tiek glabāts mūsu ieguldījums fiksēta lieluma vārdu vārdnīcā. Tas tiks nodots GRU slānim. GRU slānis ir Gated Recurrent Unit, kas sastāv no vairāku slāņu tipa RNN, kas aprēķinās secīgo ievadi. Šis slānis aprēķinās slēpto stāvokli no iepriekšējā un atjauninās atiestatīšanas, atjaunināšanas un jaunos vārtus.

Avots: Seq2Seq

Dekoders atšifrēs ievadi no kodētāja izejas. Tas mēģinās paredzēt nākamo izvadi un mēģinās to izmantot kā nākamo ievadi, ja tas ir iespējams. Dekoders sastāv no iegremdēšanas slāņa, GRU slāņa un lineārā slāņa. Iegulšanas slānis izveidos izejas uzmeklēšanas tabulu un pārsūtīs GRU slānī, lai aprēķinātu paredzamo izvades stāvokli. Pēc tam lineārais slānis palīdzēs aprēķināt aktivizācijas funkciju, lai noteiktu paredzamās izejas patieso vērtību.

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

3. solis. Modeļa apmācība

Apmācības process Seq2seq modeļos tiek uzsākts ar katra teikuma pāra pārveidošanu Tensoros no viņu Lang indeksa. Mūsu secības pēc kārtas modelis izmantos SGD kā optimizētāju un NLLLoss funkciju, lai aprēķinātu zaudējumus. Apmācības process sākas ar teikuma pāra padošanu modelim, lai prognozētu pareizo iznākumu. Katrā solī modeļa izlaide tiks aprēķināta ar patiesiem vārdiem, lai atrastu zaudējumus un atjauninātu parametrus. Tā kā jūs izmantosiet 75000 atkārtojumu, mūsu secības modelēšanas secība no mūsu datu kopas ģenerēs nejaušus 75000 pārus.

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

4. solis. Pārbaudiet modeli

Seq2seq PyTorch novērtēšanas process ir modeļa izejas pārbaude. Katrs secības modeļa pāris tiks ievadīts modelī un ģenerēs paredzētos vārdus. Pēc tam katrā izvadē jūs meklēsit visaugstāko vērtību, lai atrastu pareizo indeksu. Galu galā jūs salīdzināsiet, lai redzētu mūsu modeļa prognozi ar patieso teikumu

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))

Tagad sāksim apmācību ar Seq to Seq ar atkārtojumu skaitu 75000 un RNN slāņa skaitu 1 ar slēpto izmēru 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)

Kā redzat, mūsu pareģotais teikums nav ļoti labi saskaņots, tāpēc, lai iegūtu lielāku precizitāti, jums jāapmāca ar daudz vairāk datu un jāmēģina pievienot vairāk atkārtojumu un slāņu skaita, izmantojot secību secības apguvei.

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