Pytorch English to Hindi Translation using Attention
·Ashutosh Mishra·22 min read
Using encoder-decoder model with Attention network to create a Langaue translation model
Language Translation
A English to Hindi Translation model.
Encoder-decoder model using attention.
Import modules
# util modules.
from__future__importunicode_literals,print_function,divisionfromioimportopenimportunicodedataimportstringimportreimportrandomimporttqdmfromtqdmimporttnrangeimporttorchimporttorch.nnasnnfromtorchimportoptimimporttorch.nn.functionalasF#use cuda if gpu is available else cpu.
device=torch.device("cuda"iftorch.cuda.is_available()else"cpu")%matplotlibinline
Data Preprocessing
# tokens for start of sentence(SOS) and end of sentence(EOS)
SOS_token=0EOS_token=1classLang:'''
class for word object, storing sentences, words and word counts.
'''def__init__(self,name):self.name=nameself.word2index={}self.word2count={}self.index2word={0:"SOS",1:"EOS"}self.n_words=2# Count SOS and EOS
defaddSentence(self,sentence):forwordinsentence.split(' '):self.addWord(word)defaddWord(self,word):ifwordnotinself.word2index:self.word2index[word]=self.n_wordsself.word2count[word]=1self.index2word[self.n_words]=wordself.n_words+=1else:self.word2count[word]+=1
# Turn a Unicode string to plain ASCII, thanks to
# https://stackoverflow.com/a/518232/2809427
defunicodeToAscii(s):return''.join(cforcinunicodedata.normalize('NFD',s)ifunicodedata.category(c)!='Mn')# Lowercase, trim, and remove non-letter characters
defnormalizeString(s):s=s.lower().strip()s=re.sub(r"([.!?])",r" \1",s)#s = re.sub(r"[^a-zA-Z.!?]+", r" ", s)
returns
defreadLangs(lang1,lang2,reverse=False):'''
Read lines, from text file.
lang1 - laguage as input,
lang2 - output language,
reverse - to reverse the languages as input and output.
'''print("Reading lines...")# Read the file and split into lines
lines=open('data/%s-%s.txt'%(lang1,lang2),encoding='utf-8').\
read().strip().split('\n')# Split every line into pairs and normalize
pairs=[[normalizeString(s)forsinl.split('\t')]forlinlines]# Reverse pairs, make Lang instances
ifreverse:pairs=[list(reversed(p))forpinpairs]input_lang=Lang(lang2)output_lang=Lang(lang1)else:input_lang=Lang(lang1)output_lang=Lang(lang2)returninput_lang,output_lang,pairs
MAX_LENGTH=15#max length of words in a sentence.
eng_prefixes=("i am ","i m ","he is","he s ","she is","she s ","you are","you re ","we are","we re ","they are","they re ")deffilterPair(p):returnlen(p[0].split(' '))<MAX_LENGTHand \
len(p[1].split(' '))<MAX_LENGTH#and \
#p[1].startswith(eng_prefixes)
deffilterPairs(pairs):return[pairforpairinpairsiffilterPair(pair)]
defprepareData(lang1,lang2,reverse=False):'''
prepare class objects for the languages.
'''input_lang,output_lang,pairs=readLangs(lang1,lang2,reverse)print("Read %s sentence pairs"%len(pairs))pairs=filterPairs(pairs)print("Trimmed to %s sentence pairs"%len(pairs))print("Counting words...")forpairinpairs:input_lang.addSentence(pair[0])output_lang.addSentence(pair[1])print("Counted words:")print(input_lang.name,input_lang.n_words)print(output_lang.name,output_lang.n_words)returninput_lang,output_lang,pairsinput_lang,output_lang,pairs=prepareData('eng','hin',False)print(random.choice(pairs))
Reading lines...
Read 2831 sentence pairs
Trimmed to 2787 sentence pairs
Counting words...
Counted words:
eng 2463
hin 3022
['he is angry with you .', 'वह तुमसे नाराज़ है।']
teacher_forcing_ratio=0.5deftrain(input_tensor,target_tensor,encoder,decoder,encoder_optimizer,decoder_optimizer,criterion,max_length=MAX_LENGTH):encoder_hidden=encoder.initHidden()encoder_optimizer.zero_grad()decoder_optimizer.zero_grad()input_length=input_tensor.size(0)target_length=target_tensor.size(0)encoder_outputs=torch.zeros(max_length,encoder.hidden_size,device=device)loss=0foreiinrange(input_length):encoder_output,encoder_hidden=encoder(input_tensor[ei],encoder_hidden)encoder_outputs[ei]=encoder_output[0,0]decoder_input=torch.tensor([[SOS_token]],device=device)decoder_hidden=encoder_hiddenuse_teacher_forcing=Trueifrandom.random()<teacher_forcing_ratioelseFalseifuse_teacher_forcing:# Teacher forcing: Feed the target as the next input
fordiinrange(target_length):decoder_output,decoder_hidden,decoder_attention=decoder(decoder_input,decoder_hidden,encoder_outputs)loss+=criterion(decoder_output,target_tensor[di])decoder_input=target_tensor[di]# Teacher forcing
else:# Without teacher forcing: use its own predictions as the next input
fordiinrange(target_length):decoder_output,decoder_hidden,decoder_attention=decoder(decoder_input,decoder_hidden,encoder_outputs)topv,topi=decoder_output.topk(1)decoder_input=topi.squeeze().detach()# detach from history as input
loss+=criterion(decoder_output,target_tensor[di])ifdecoder_input.item()==EOS_token:breakloss.backward()encoder_optimizer.step()decoder_optimizer.step()returnloss.item()/target_length
deftrainIters(encoder,decoder,n_iters,print_every=1000,plot_every=100,learning_rate=0.01):start=time.time()plot_losses=[]print_loss_total=0# Reset every print_every
plot_loss_total=0# Reset every plot_every
encoder_optimizer=optim.SGD(encoder.parameters(),lr=learning_rate)decoder_optimizer=optim.SGD(decoder.parameters(),lr=learning_rate)training_pairs=[tensorsFromPair(random.choice(pairs))foriinrange(n_iters)]criterion=nn.NLLLoss()foriintnrange(1,n_iters+1):training_pair=training_pairs[i-1]input_tensor=training_pair[0]target_tensor=training_pair[1]loss=train(input_tensor,target_tensor,encoder,decoder,encoder_optimizer,decoder_optimizer,criterion)print_loss_total+=lossplot_loss_total+=lossifi%print_every==0:print_loss_avg=print_loss_total/print_everyprint_loss_total=0print('%s (%d %d%%) %.4f'%(timeSince(start,i/n_iters),i,i/n_iters*100,print_loss_avg))ifi%plot_every==0:plot_loss_avg=plot_loss_total/plot_everyplot_losses.append(plot_loss_avg)plot_loss_total=0showPlot(plot_losses)
importmatplotlib.pyplotaspltplt.switch_backend('agg')importmatplotlib.tickerastickerimportnumpyasnpdefshowPlot(points):plt.figure()fig,ax=plt.subplots()# this locator puts ticks at regular intervals
loc=ticker.MultipleLocator(base=0.2)ax.yaxis.set_major_locator(loc)plt.plot(points)
defevaluate(encoder,decoder,sentence,max_length=MAX_LENGTH):withtorch.no_grad():input_tensor=tensorFromSentence(input_lang,sentence)input_length=input_tensor.size()[0]encoder_hidden=encoder.initHidden()encoder_outputs=torch.zeros(max_length,encoder.hidden_size,device=device)foreiinrange(input_length):encoder_output,encoder_hidden=encoder(input_tensor[ei],encoder_hidden)encoder_outputs[ei]+=encoder_output[0,0]decoder_input=torch.tensor([[SOS_token]],device=device)# SOS
decoder_hidden=encoder_hiddendecoded_words=[]decoder_attentions=torch.zeros(max_length,max_length)fordiinrange(max_length):decoder_output,decoder_hidden,decoder_attention=decoder(decoder_input,decoder_hidden,encoder_outputs)decoder_attentions[di]=decoder_attention.datatopv,topi=decoder_output.data.topk(1)iftopi.item()==EOS_token:decoded_words.append('<EOS>')breakelse:decoded_words.append(output_lang.index2word[topi.item()])decoder_input=topi.squeeze().detach()returndecoded_words,decoder_attentions[:di+1]
> does anyone here speak english ?
= यहाँ पर कोई अंग्रेज़ी बोलता है क्या ?
< यहाँ पर कोई अंग्रेज़ी बोलता है क्या ? <EOS>
> i have never come across such a stubborn person .
= इतने अड़ियल व्यक्ति से मेरी कभी भी मुलाकात नहीं हुई है।
< इतने अड़ियल व्यक्ति से मेरी कभी नहीं हुई है। <EOS>
> did you read it at all ?
= तुमने पूरा पढ़ लिया क्या ?
< तुमने पूरा पढ़ लिया क्या ? <EOS>
> canada is a large country .
= कनाडा एक बड़ा देश है ।
< कनाडा एक बड़ा देश है । <EOS>
> i have no time to see you .
= मेरे पास तुमसे मिलने के लिए समय नहीं है।
< मेरे पास तुमसे मिलने के लिए समय नहीं है। <EOS>
> hello !
= नमस्कार।
< नमस्कार। <EOS>
> cows are sacred to many people in india .
= भारत में कई लोग गाय को पूज्य मानते हैं।
< भारत में कई लोग गाय को पूज्य मानते हैं। <EOS>
> what are you going to do with this money ?
= तुम इन पैसों के साथ क्या करोगे ?
< तुम इन पैसों के साथ क्या करोगे ? <EOS>
> will the weather be good tomorrow ?
= कल मौसम अच्छा होगा क्या ?
< कल मौसम अच्छा होगा क्या ? <EOS>
> he pressed me for a prompt reply .
= उसने मुझसे जल्द-से-जल्द उत्तर देने के लिए कहा।
< उसने मुझसे जल्द-से-जल्द उत्तर देने के लिए कहा। <EOS>
#save the model
importtorchtorch.save(encoder1.state_dict,'encoder@40kepoch-512-0,36err.pth')torch.save(attn_decoder1.state_dict,'attndecoder@40kepoch-512-0,36err.pth')
output_words,attentions=evaluate(encoder1,attn_decoder1,"i explained the rule to him .")plt.matshow(attentions.numpy())
<matplotlib.image.AxesImage at 0x7f1ba0c524e0>
defshowAttention(input_sentence,output_words,attentions):# Set up figure with colorbar
fig=plt.figure()ax=fig.add_subplot(111)cax=ax.matshow(attentions.numpy(),cmap='bone')fig.colorbar(cax)# Set up axes
ax.set_xticklabels(['']+input_sentence.split(' ')+['<EOS>'],rotation=90)ax.set_yticklabels(['']+output_words)# Show label at every tick
ax.xaxis.set_major_locator(ticker.MultipleLocator(1))ax.yaxis.set_major_locator(ticker.MultipleLocator(1))plt.show()defevaluateAndShowAttention(input_sentence):output_words,attentions=evaluate(encoder1,attn_decoder1,input_sentence)print('input =',input_sentence)print('output =',' '.join(output_words))showAttention(input_sentence,output_words,attentions)
evaluateAndShowAttention("the thief cursed the police for finding him .")evaluateAndShowAttention("they gave us very little trouble .")evaluateAndShowAttention("the thief gave the police very little trouble .")
input = the thief cursed the police for finding him .
output = चोर ने चोर को उसको पकड़ने के लिए गाली दी। <EOS>
input = they gave us very little trouble .
output = उन्होंने हमको बहुत कम कष्ट दिया। <EOS>
input = the thief gave the police very little trouble .
output = उस आदमी को बहुत कम दिया। <EOS>