Model Seq2seq (zaporedje v zaporedje) s PyTorch

Kazalo:

Anonim

Kaj je NLP?

NLP ali obdelava naravnega jezika je ena izmed priljubljenih vej umetne inteligence, ki pomaga računalnikom razumeti, manipulirati ali se odzvati na človeka v njegovem naravnem jeziku. NLP je motor za Google Translate, ki nam pomaga razumeti druge jezike.

Kaj je Seq2Seq?

Seq2Seq je metoda strojnega prevajanja in obdelave jezika, ki temelji na kodirniku-dekoderju, ki preslika vhod zaporedja v izhod zaporedja z oznako in vrednostjo pozornosti. Ideja je uporabiti 2 RNN, ki bosta delovala skupaj s posebnim žetonom in poskušala napovedati naslednje zaporedje stanja iz prejšnjega zaporedja.

1. korak) Nalaganje naših podatkov

Za naš nabor podatkov boste uporabili nabor podatkov iz dvojezičnih dvojic stavkov, ločenih s tabulatorji. Tu bom uporabil nabor podatkov od angleščine do indonezije. Izberete lahko karkoli, vendar ne pozabite spremeniti imena datoteke in imenika v kodi.

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. korak) Priprava podatkov

Nabora podatkov ne morete uporabiti neposredno. Stavke morate razdeliti na besede in jih pretvoriti v One-Hot Vector. Vsaka beseda bo v razredu Lang enolično indeksirana za izdelavo slovarja. Razred Lang bo shranil vsak stavek in ga razdelil besedo za besedo z addSentence. Nato ustvarite slovar, tako da vsako neznano besedo za zaporedje indeksirate na zaporedne 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

Razred jezika je razred, ki nam bo pomagal izdelati slovar. Za vsak jezik bo vsak stavek razdeljen na besede in nato dodan v vsebnik. Vsak vsebnik bo besede shranil v ustrezen indeks, preštel besedo in dodal indeks besede, da ga bomo lahko uporabili za iskanje indeksa besede ali iskanje besede iz njenega indeksa.

Ker so naši podatki ločeni s TAB, morate za nalaganje podatkov uporabiti pande. Pandas bo naše podatke prebral kot dataFrame in jih razdelil na naš izvorni in ciljni stavek. Za vsak stavek, ki ga imate,

  • normalizirali boste z malimi črkami,
  • odstrani vse neznakovne znake
  • pretvori v ASCII iz Unicode
  • razdelite stavke, tako da imate v sebi vsako besedo.
#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

Druga uporabna funkcija, ki jo boste uporabili, je pretvorba parov v Tensor. To je zelo pomembno, ker naše omrežje bere samo podatke tipa tenzorja. Pomembno je tudi zato, ker je to del, na katerem je na vsakem koncu stavka žeton, ki omrežju sporoča, da je vnos končan. Za vsako besedo v stavku bo dobil kazalo ustrezne besede v slovarju in dodal žeton na koncu stavka.

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)

Model Seq2Seq

Vir: Seq2Seq

Model PyTorch Seq2seq je nekakšen model, ki na vrhu modela uporablja dekodirnik dajalnika PyTorch. Dajalnik bo stavčno besedo kodiral po besedah ​​v indeksiranem besednjaku ali znanih besedah ​​z indeksom, dekodirnik pa bo napovedal izhod kodiranega vhoda z dekodiranjem vnosa v zaporedju in poskušal uporabiti zadnji vhod kot naslednji vhod, če mogoče je. S to metodo je mogoče predvideti tudi naslednji vnos za ustvarjanje stavka. Vsakemu stavku bo dodeljen žeton, ki označuje konec zaporedja. Na koncu napovedi bo tudi žeton, ki označuje konec izhoda. Torej bo iz dajalnika prešlo stanje v dekoder, da bo napovedal izhod.

Vir: Model Seq2Seq

Dajalnik bo naš vnosni stavek zaporedno kodiral besedo za besedo in na koncu bo žeton, ki označuje konec stavka. Dajalnik je sestavljen iz plasti za vdelavo in plasti GRU. Sloj za vdelavo je iskalna tabela, ki shrani vdelavo našega vnosa v slovar besed z določeno velikostjo. Prenesena bo na plast GRU. Sloj GRU je zaprta ponavljajoča se enota, ki je sestavljena iz večplastne vrste RNN, ki bo izračunala zaporedni vhod. Ta plast bo izračunala skrito stanje iz prejšnje in posodobila ponastavitev, posodobitev in nova vrata.

Vir: Seq2Seq

Dekoder bo dekodiral vhod iz izhoda dajalnika. Poskusil bo napovedati naslednji izhod in ga poskusiti uporabiti kot naslednji vhod, če je to mogoče. Dekoder je sestavljen iz vdelane plasti, plasti GRU in linearne plasti. Plast za vdelavo bo ustvarila iskalno tabelo za izhod in jo prenesla v plast GRU za izračun predvidenega izhodnega stanja. Po tem bo Linearni sloj pomagal izračunati aktivacijsko funkcijo za določitev resnične vrednosti predvidenega izhoda.

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. korak) Usposabljanje za model

Proces treninga v modelih Seq2seq se začne s pretvorbo vsakega para stavkov v tenzorje iz njihovega Langovega indeksa. Naš model zaporedja do zaporedja bo uporabil SGD kot optimizator in funkcijo NLLLoss za izračun izgub. Proces treninga se začne s podajanjem para stavka modelu, da napove pravilen rezultat. Na vsakem koraku se izhod iz modela izračuna z resničnimi besedami, da se najdejo izgube in posodobijo parametri. Ker boste uporabili 75000 ponovitev, bo naš model zaporedja do zaporedja iz našega nabora podatkov ustvaril naključnih 75000 parov.

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. korak) Preizkusite model

Postopek ocenjevanja Seq2seq PyTorch je preveriti izhodne podatke modela. Vsak par zaporednih modelov se vstavi v model in ustvari predvidene besede. Po tem boste na vsakem izhodu videli najvišjo vrednost, da boste našli pravi indeks. In na koncu boste primerjali, da boste videli naš model napovedi z resničnim stavkom

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

Zdaj pa začnimo s treningom od Seq do Seq, s številom ponovitev 75000 in številom RNN plasti 1 s skrito velikostjo 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)

Kot lahko vidite, se naš predvideni stavek ne ujema zelo dobro, zato morate za večjo natančnost trenirati z veliko več podatkov in poskusiti dodati več ponovitev in število slojev z uporabo Sequence za zaporedno učenje.

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