Apa itu NLP?
NLP atau Pemprosesan Bahasa Asli adalah salah satu cabang Kecerdasan Buatan yang popular yang membantu komputer memahami, memanipulasi atau bertindak balas terhadap manusia dalam bahasa semula jadi mereka. NLP adalah mesin di sebalik Terjemahan Google yang membantu kita memahami bahasa lain.
Apa itu Seq2Seq?
Seq2Seq adalah kaedah terjemahan mesin dan pemprosesan bahasa berdasarkan encoder-decoder yang memetakan input urutan ke output urutan dengan tag dan nilai perhatian. Ideanya adalah menggunakan 2 RNN yang akan berfungsi bersama dengan token khas dan cuba meramalkan urutan keadaan seterusnya dari urutan sebelumnya.
Langkah 1) Memuatkan Data kami
Untuk set data kami, anda akan menggunakan set data dari Pasangan Bilingual Sentence Pas-delimited. Di sini saya akan menggunakan dataset Bahasa Inggeris ke Indonesia. Anda boleh memilih apa sahaja yang anda suka tetapi ingat untuk menukar nama fail dan direktori dalam kod.
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")
Langkah 2) Penyediaan Data
Anda tidak boleh menggunakan set data secara langsung. Anda perlu membahagikan ayat menjadi perkataan dan menukarnya menjadi One-Hot Vector. Setiap perkataan akan diindeks secara unik di kelas Lang untuk membuat kamus. Kelas Lang akan menyimpan setiap ayat dan membahagikannya demi satu perkataan dengan addSentence. Kemudian buat kamus dengan mengindeks setiap perkataan yang tidak diketahui untuk model Urutan ke urutan.
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
Kelas Lang adalah kelas yang akan membantu kami membuat kamus. Untuk setiap bahasa, setiap ayat akan dibahagikan kepada perkataan dan kemudian ditambahkan ke dalam wadah. Setiap bekas akan menyimpan kata-kata dalam indeks yang sesuai, mengira kata, dan menambahkan indeks kata sehingga kita dapat menggunakannya untuk mencari indeks kata atau mencari kata dari indeksnya.
Oleh kerana data kami dipisahkan oleh TAB, anda perlu menggunakan panda sebagai pemuat data kami. Pandas akan membaca data kami sebagai dataFrame dan membaginya menjadi sumber dan ayat sasaran kami. Untuk setiap ayat yang anda ada,
- anda akan menormalkannya menjadi huruf kecil,
- buang semua bukan watak
- tukar ke ASCII dari Unicode
- bahagikan ayat, jadi anda mempunyai setiap perkataan di dalamnya.
#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
Fungsi lain yang berguna yang akan anda gunakan ialah menukar pasangan menjadi Tensor. Ini sangat penting kerana rangkaian kami hanya membaca data jenis tensor. Ini juga penting kerana ini adalah bahagian bahawa pada setiap akhir ayat akan ada tanda untuk memberitahu rangkaian bahawa input sudah selesai. Untuk setiap perkataan dalam kalimat, ia akan mendapat indeks dari kata yang sesuai dalam kamus dan menambahkan token di akhir ayat.
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
Sumber: Seq2Seq
Model PyTorch Seq2seq adalah sejenis model yang menggunakan penyahkod PyTorch encoder di atas model. Encoder akan menyandikan kata kalimat dengan kata-kata ke dalam kosa kata yang diindeks atau kata-kata yang diketahui dengan indeks, dan penyahkod akan meramalkan output input kod dengan menyahkod input secara berurutan dan akan cuba menggunakan input terakhir sebagai input seterusnya jika ada kemungkinan. Dengan kaedah ini, juga dimungkinkan untuk meramalkan input seterusnya untuk membuat ayat. Setiap ayat akan diberikan tanda untuk menandakan akhir urutan. Pada akhir ramalan, akan ada tanda untuk menandakan akhir output. Jadi, dari pengekod, ia akan meneruskan keadaan ke penyahkod untuk meramalkan outputnya.
Sumber: Model Seq2Seq
Encoder akan menyandikan kalimat input kami dengan kata demi kata mengikut urutan dan pada akhirnya akan ada tanda untuk menandakan akhir ayat. Pengekod terdiri daripada lapisan Embedding dan lapisan GRU. Lapisan Embedding adalah jadual pencarian yang menyimpan penyisipan input kami ke kamus kata berukuran tetap. Ia akan diteruskan ke lapisan GRU. Lapisan GRU adalah Gated Recurrent Unit yang terdiri daripada pelbagai jenis RNN yang akan mengira input yang diuraikan. Lapisan ini akan mengira keadaan tersembunyi dari yang sebelumnya dan mengemas kini tetapan semula, kemas kini, dan gerbang baru.
Sumber: Seq2Seq
Decoder akan menyahkod input dari output pengekod. Ia akan cuba meramalkan keluaran seterusnya dan mencuba menggunakannya sebagai input seterusnya jika mungkin. Decoder terdiri daripada lapisan Embedding, lapisan GRU, dan lapisan Linear. Lapisan penyisipan akan membuat jadual pencarian untuk output dan diteruskan ke lapisan GRU untuk mengira keadaan output yang diramalkan. Selepas itu, lapisan Linear akan membantu mengira fungsi pengaktifan untuk menentukan nilai sebenar output yang diramalkan.
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
Langkah 3) Melatih Model
Proses latihan dalam model Seq2seq dimulakan dengan menukar setiap pasangan ayat menjadi Tensor dari indeks Lang mereka. Model urutan ke urutan kami akan menggunakan SGD sebagai pengoptimum dan fungsi NLLLoss untuk mengira kerugian. Proses latihan dimulakan dengan memberi pasangan ayat kepada model untuk meramalkan hasil yang betul. Pada setiap langkah, output dari model akan dihitung dengan kata-kata sebenarnya untuk mencari kerugian dan mengemas kini parameter. Oleh kerana anda akan menggunakan 75000 iterasi, model urutan ke urutan kami akan menghasilkan 75000 pasangan rawak dari set data kami.
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
Langkah 4) Uji Model
Proses penilaian Seq2seq PyTorch adalah untuk memeriksa output model. Setiap pasangan model Urutan ke urutan akan dimasukkan ke dalam model dan menghasilkan perkataan yang diramalkan. Selepas itu anda akan melihat nilai tertinggi pada setiap output untuk mencari indeks yang betul. Dan pada akhirnya, anda akan membandingkan untuk melihat ramalan model kami dengan ayat yang benar
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))
Sekarang, mari mulakan latihan kami dengan Seq ke Seq, dengan bilangan lelaran 75000 dan bilangan lapisan RNN 1 dengan ukuran tersembunyi 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)
Seperti yang anda lihat, ayat yang diramalkan kami tidak sesuai, jadi untuk mendapatkan ketepatan yang lebih tinggi, anda perlu berlatih dengan lebih banyak data dan cuba menambahkan lebih banyak lelaran dan bilangan lapisan menggunakan Urutan untuk pembelajaran urutan.
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