Τι είναι το NLP;
Το NLP ή η Επεξεργασία Φυσικής Γλώσσας είναι ένας από τους δημοφιλείς κλάδους της Τεχνητής Νοημοσύνης που βοηθά τους υπολογιστές να κατανοήσουν, να χειριστούν ή να ανταποκριθούν σε έναν άνθρωπο στη φυσική του γλώσσα. Το NLP είναι ο κινητήρας πίσω από τη Μετάφραση Google που μας βοηθά να κατανοήσουμε άλλες γλώσσες.
Τι είναι το Seq2Seq;
Το Seq2Seq είναι μια μέθοδος μηχανικής μετάφρασης και επεξεργασίας γλώσσας που βασίζεται σε κωδικοποιητή-αποκωδικοποιητή που χαρτογραφεί μια είσοδο ακολουθίας σε μια έξοδο ακολουθίας με τιμή ετικέτας και προσοχής. Η ιδέα είναι να χρησιμοποιήσετε 2 RNN που θα λειτουργούν μαζί με ένα ειδικό διακριτικό και θα προσπαθούν να προβλέψουν την επόμενη ακολουθία κατάστασης από την προηγούμενη ακολουθία.
Βήμα 1) Φόρτωση των δεδομένων μας
Για το σύνολο δεδομένων μας, θα χρησιμοποιήσετε ένα σύνολο δεδομένων από ζεύγη δίγλωσσης φράσεων οριοθετημένων με καρτέλες. Εδώ θα χρησιμοποιήσω το σύνολο δεδομένων από Αγγλικά σε Ινδονησία. Μπορείτε να επιλέξετε οτιδήποτε θέλετε, αλλά θυμηθείτε να αλλάξετε το όνομα αρχείου και τον κατάλογο στον κώδικα.
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) Προετοιμασία δεδομένων
Δεν μπορείτε να χρησιμοποιήσετε το σύνολο δεδομένων απευθείας. Πρέπει να χωρίσετε τις προτάσεις σε λέξεις και να τις μετατρέψετε σε One-Hot Vector. Κάθε λέξη θα ευρετηριαστεί μοναδικά στην τάξη Lang για να δημιουργήσει λεξικό. Το Lang Class θα αποθηκεύει κάθε πρόταση και θα τη χωρίζει λέξη προς λέξη με το addSentence. Στη συνέχεια, δημιουργήστε ένα λεξικό με ευρετηρίαση κάθε άγνωστης λέξης για ακολουθία σε μοντέλα ακολουθίας.
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 είναι μια τάξη που θα μας βοηθήσει να φτιάξουμε λεξικό. Για κάθε γλώσσα, κάθε πρόταση χωρίζεται σε λέξεις και στη συνέχεια προστίθεται στο κοντέινερ. Κάθε κοντέινερ θα αποθηκεύσει τις λέξεις στο κατάλληλο ευρετήριο, θα μετρήσει τη λέξη και θα προσθέσει το ευρετήριο της λέξης, ώστε να μπορούμε να τη χρησιμοποιήσουμε για να βρούμε το ευρετήριο μιας λέξης ή να βρούμε μια λέξη από το ευρετήριό της.
Επειδή τα δεδομένα μας διαχωρίζονται από το TAB, πρέπει να χρησιμοποιήσετε τα pandas ως φορτωτές δεδομένων μας. Οι Pandas θα διαβάσουν τα δεδομένα μας ως dataFrame και θα τα χωρίσουν στην πρόταση και την πρόταση-στόχο μας. Για κάθε πρόταση που έχετε,
- θα το ομαλοποιήσετε σε πεζά,
- κατάργηση όλων των μη χαρακτήρων
- μετατροπή σε ASCII από Unicode
- χωρίστε τις προτάσεις, ώστε να έχετε κάθε λέξη σε αυτήν.
#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
Μια άλλη χρήσιμη λειτουργία που θα χρησιμοποιήσετε είναι τα ζεύγη μετατροπής σε Tensor. Αυτό είναι πολύ σημαντικό επειδή το δίκτυό μας διαβάζει μόνο δεδομένα τύπου τανυστή. Είναι επίσης σημαντικό γιατί αυτό είναι το μέρος που σε κάθε τέλος της πρότασης θα υπάρχει ένα διακριτικό για να πείτε στο δίκτυο ότι η είσοδος έχει ολοκληρωθεί. Για κάθε λέξη στην πρόταση, θα λαμβάνει το ευρετήριο από την κατάλληλη λέξη στο λεξικό και θα προσθέσει ένα διακριτικό στο τέλος της πρότασης.
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
Πηγή: Seq2Seq
Το μοντέλο PyTorch Seq2seq είναι ένα είδος μοντέλου που χρησιμοποιεί αποκωδικοποιητή κωδικοποιητή PyTorch στην κορυφή του μοντέλου. Ο κωδικοποιητής θα κωδικοποιήσει τη λέξη πρότασης με λέξεις σε ευρετήριο λεξιλογίου ή γνωστές λέξεις με ευρετήριο και ο αποκωδικοποιητής θα προβλέψει την έξοδο της κωδικοποιημένης εισόδου αποκωδικοποιώντας την είσοδο διαδοχικά και θα προσπαθήσει να χρησιμοποιήσει την τελευταία είσοδο ως την επόμενη είσοδο εάν είναι δυνατό. Με αυτήν τη μέθοδο, είναι επίσης δυνατό να προβλεφθεί η επόμενη εισαγωγή για να δημιουργήσετε μια πρόταση. Σε κάθε πρόταση θα εκχωρηθεί ένα διακριτικό για να σημειωθεί το τέλος της ακολουθίας. Στο τέλος της πρόβλεψης, θα υπάρχει επίσης ένα σύμβολο για το τέλος της εξόδου. Έτσι, από τον κωδικοποιητή, θα περάσει μια κατάσταση στον αποκωδικοποιητή για να προβλέψει την έξοδο.
Πηγή: Seq2Seq Model
Ο κωδικοποιητής θα κωδικοποιήσει τη φράση εισαγωγής μας λέξη προς λέξη στη σειρά και στο τέλος θα υπάρχει ένα σύμβολο για να σηματοδοτήσει το τέλος μιας πρότασης. Ο κωδικοποιητής αποτελείται από στρώματα Ενσωμάτωσης και στρώματα GRU. Το επίπεδο ενσωμάτωσης είναι ένας πίνακας αναζήτησης που αποθηκεύει την ενσωμάτωση της εισόδου μας σε ένα λεξικό λέξεων σταθερού μεγέθους. Θα περάσει σε επίπεδο GRU. Το επίπεδο GRU είναι ένα Gated Recurrent Unit που αποτελείται από πολλαπλά επίπεδα τύπου RNN που θα υπολογίσει την ακολουθία εισόδου. Αυτό το επίπεδο θα υπολογίσει την κρυφή κατάσταση από την προηγούμενη και θα ενημερώσει την επαναφορά, την ενημέρωση και τις νέες πύλες.
Πηγή: Seq2Seq
Ο αποκωδικοποιητής θα αποκωδικοποιήσει την είσοδο από την έξοδο του κωδικοποιητή. Θα προσπαθήσει να προβλέψει την επόμενη έξοδο και να την χρησιμοποιήσει ως την επόμενη είσοδο εάν είναι δυνατόν. Ο αποκωδικοποιητής αποτελείται από ένα στρώμα ενσωμάτωσης, ένα επίπεδο GRU και ένα γραμμικό επίπεδο. Το επίπεδο ενσωμάτωσης θα κάνει έναν πίνακα αναζήτησης για την έξοδο και θα περάσει σε ένα επίπεδο GRU για να υπολογίσει την προβλεπόμενη κατάσταση εξόδου. Μετά από αυτό, ένα γραμμικό επίπεδο θα βοηθήσει στον υπολογισμό της λειτουργίας ενεργοποίησης για να προσδιορίσει την πραγματική τιμή της προβλεπόμενης εξόδου.
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) Εκπαίδευση του Μοντέλου
Η διαδικασία εκπαίδευσης σε μοντέλα Seq2seq ξεκινά με τη μετατροπή κάθε ζεύγους προτάσεων σε Tensors από το ευρετήριο Lang τους. Το μοντέλο ακολουθίας σε ακολουθία θα χρησιμοποιήσει το SGD ως λειτουργία βελτιστοποίησης και NLLLoss για τον υπολογισμό των απωλειών. Η εκπαιδευτική διαδικασία ξεκινά με την τροφοδοσία του ζεύγους μιας πρότασης στο μοντέλο για να προβλέψει τη σωστή έξοδο. Σε κάθε βήμα, η έξοδος από το μοντέλο θα υπολογιστεί με τις πραγματικές λέξεις για να βρείτε τις απώλειες και να ενημερώσετε τις παραμέτρους. Έτσι, επειδή θα χρησιμοποιήσετε 75000 επαναλήψεις, το μοντέλο ακολουθίας σε ακολουθία θα δημιουργήσει τυχαία 75000 ζεύγη από το σύνολο δεδομένων μας.
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) Δοκιμάστε το μοντέλο
Η διαδικασία αξιολόγησης του Seq2seq PyTorch είναι να ελέγξετε την έξοδο του μοντέλου. Κάθε ζεύγος μοντέλων ακολουθίας σε ακολουθία θα τροφοδοτείται στο μοντέλο και θα δημιουργεί τις προβλεπόμενες λέξεις. Μετά από αυτό θα δείτε την υψηλότερη τιμή σε κάθε έξοδο για να βρείτε το σωστό ευρετήριο. Και στο τέλος, θα συγκρίνετε για να δείτε την πρόβλεψη του μοντέλου μας με την πραγματική πρόταση
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))
Τώρα, ας ξεκινήσουμε την εκπαίδευσή μας με Seq to Seq, με τον αριθμό των επαναλήψεων 75000 και τον αριθμό του επιπέδου RNN του 1 με το κρυφό μέγεθος 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)
Όπως μπορείτε να δείτε, η πρόβλεψη της πρότασής μας δεν ταιριάζει πολύ καλά, οπότε για να αποκτήσετε μεγαλύτερη ακρίβεια, πρέπει να εκπαιδεύσετε με πολύ περισσότερα δεδομένα και να προσπαθήσετε να προσθέσετε περισσότερες επαναλήψεις και τον αριθμό των επιπέδων χρησιμοποιώντας την ακολουθία στην εκμάθηση ακολουθιών.
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