Kas yra NLP?
NLP arba natūralios kalbos apdorojimas yra viena iš populiariausių dirbtinio intelekto šakų, padedanti kompiuteriams suprasti, manipuliuoti ar atsakyti į žmogų jų natūralia kalba. NLP yra „Google“ vertėjo variklis, padedantis suprasti kitas kalbas.
Kas yra Seq2Seq?
„Seq2Seq“ yra kodavimo-dekodavimo mašininio vertimo ir kalbos apdorojimo metodas, kuris sekos įvestį susieja su sekos išvestimi su žyme ir dėmesio verte. Idėja yra naudoti 2 RNN, kurie veiks kartu su specialiu žetonu ir bandys nuspėti kitą būsenos seką iš ankstesnės sekos.
1 žingsnis) Įkeliame mūsų duomenis
Mūsų duomenų rinkiniui naudosite duomenų rinkinį iš tabuliatorių atskirtų dvikalbių sakinių porų. Čia naudosiu anglų - indoneziečių duomenų rinkinį. Galite pasirinkti viską, kas jums patinka, bet nepamirškite pakeisti failo pavadinimo ir katalogo.
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 žingsnis) Duomenų paruošimas
Negalite tiesiogiai naudoti duomenų rinkinio. Jūs turite padalinti sakinius į žodžius ir paversti juos „One-Hot Vector“. Kiekvienas žodis bus unikaliai indeksuojamas „Lang“ klasėje, kad būtų sukurtas žodynas. „Lang“ klasė išsaugos kiekvieną sakinį ir padalins žodį po žodžio su „addSentence“. Tada sukurkite žodyną, indeksuodami kiekvieną nežinomą sekos žodį į sekos modelius.
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
„Lang Class“ yra klasė, kuri padės mums sukurti žodyną. Kiekvienai kalbai kiekvienas sakinys bus padalintas į žodžius ir pridėtas prie talpyklos. Kiekvienas konteineris išsaugos žodžius atitinkamoje rodyklėje, suskaičiuos žodį ir pridės žodžio rodyklę, kad galėtume jį naudoti norėdami rasti žodžio rodyklę arba surasti žodį iš jo indekso.
Kadangi mūsų duomenis skiria TAB, duomenų rinktuvu turite naudoti pandas. Pandos skaitys mūsų duomenis kaip „dataFrame“ ir padalins juos į šaltinį ir tikslinį sakinį. Už kiekvieną turimą sakinį
- normalizuosite ją mažosiomis raidėmis,
- pašalinti visus ne simbolius
- konvertuoti į ASCII iš „Unicode“
- padalykite sakinius, kad turėtumėte kiekvieną žodį.
#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
Kita naudinga funkcija, kurią naudosite, yra porų konvertavimas į „Tensor“. Tai labai svarbu, nes mūsų tinklas skaito tik tenzoriaus tipo duomenis. Tai taip pat svarbu, nes tai yra ta dalis, kurioje kiekviename sakinio gale bus žetonas, nurodantis tinklui, kad įvestis baigta. Už kiekvieną sakinio žodį jis gaus indeksą iš atitinkamo žodyno žodžio ir sakinio pabaigoje pridės žetoną.
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
Šaltinis: Seq2Seq
„PyTorch Seq2seq“ modelis yra tam tikro tipo modelis, kurio viršuje naudojamas „PyTorch“ kodavimo įrenginio dekoderis. Koduotojas užkoduos sakinio žodį žodžiais į indeksuotą žodyną arba žinomus žodžius su indeksu, o dekoderis nuspės užkoduoto įvesties išvestį dekoduodamas įvestį nuosekliai ir bandys naudoti paskutinį įvestį kaip kitą įvestį, jei tai įmanoma. Taikant šį metodą taip pat galima numatyti kitą įvestį sakiniui sukurti. Kiekvienam sakiniui bus paskirtas ženklas, kuris žymės sekos pabaigą. Spėjimo pabaigoje taip pat bus ženklas, žymintis išvesties pabaigą. Taigi, iš kodavimo priemonės jis perduos būseną dekoderiui, kad numatytų išvestį.
Šaltinis: „Seq2Seq“ modelis
Koduotojas užkoduos mūsų įvestą sakinį žodis po žodžio iš eilės ir galų gale bus žetonas sakinio pabaigai pažymėti. Koderis susideda iš įdėjimo ir GRU sluoksnių. Įterpimo sluoksnis yra paieškos lentelė, kurioje saugomas mūsų įvesties įdėjimas į fiksuoto dydžio žodžių žodyną. Jis bus perduotas GRU sluoksniui. GRU sluoksnis yra uždaras pasikartojantis vienetas, susidedantis iš kelių sluoksnių tipo RNN, kuris apskaičiuos sekos įvestį. Šis sluoksnis apskaičiuos paslėptą būseną iš ankstesnės ir atnaujins atstatymo, atnaujinimo ir naujus vartus.
Šaltinis: Seq2Seq
Dekoderis dekoduos įvestį iš kodavimo įrenginio išvesties. Ji bandys numatyti kitą išvestį ir bandys ją naudoti kaip kitą įvestį, jei tai įmanoma. Dekoderį sudaro įdėjimo sluoksnis, GRU sluoksnis ir linijinis sluoksnis. Įterpimo sluoksnis sudarys išvesties paieškos lentelę ir perkels į GRU sluoksnį, kad apskaičiuotų numatomą išvesties būseną. Po to linijinis sluoksnis padės apskaičiuoti aktyvavimo funkciją, kad būtų galima nustatyti tikrąją numatomos išvesties vertę.
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 žingsnis. Mokykite modelį
Mokymo procesas „Seq2seq“ modeliuose pradedamas konvertuojant kiekvieną sakinių porą į „Tensors“ iš jų „Lang“ indekso. Mūsų sekos į sekos modelį nuostoliams apskaičiuoti bus naudojamas SGD kaip optimizatorius ir NLLLoss funkcija. Mokymo procesas prasideda sakinio poros padavimu modeliui, kad būtų galima numatyti teisingą išvestį. Kiekviename etape modelio išvestis bus apskaičiuojama tikrais žodžiais, kad būtų galima rasti nuostolius ir atnaujinti parametrus. Kadangi naudosite 75000 pakartojimų, mūsų seka modeliuojant sukurs atsitiktines 75000 porų iš mūsų duomenų rinkinio.
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 žingsnis) Išbandykite modelį
„Seq2seq PyTorch“ vertinimo procesas yra patikrinti modelio išvestį. Kiekviena sekos į seką modelių pora bus įtraukta į modelį ir sugeneruos numatomus žodžius. Po to kiekvienoje išvestyje atrodysite didžiausią vertę, kad rastumėte teisingą indeksą. Galų gale, jūs palyginsite, kad pamatytumėte mūsų modelio prognozę su tikru sakiniu
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))
Pradėkime treniruotes nuo Seq to Seq, pasikartojimų skaičiumi 75000 ir RNN 1 sluoksnio skaičiumi, kurio paslėptas dydis yra 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)
Kaip matote, mūsų numatomas sakinys nėra labai derinamas, todėl norint gauti didesnį tikslumą, turite treniruotis su daug daugiau duomenų ir pabandyti pridėti daugiau iteracijų ir sluoksnių skaičiaus, naudodami seką mokydamiesi sekos.
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