PyTorch: 序列到序列模型(Seq2Seq)實現機器翻譯實戰


版權聲明:博客文章都是作者辛苦整理的,轉載請注明出處,謝謝!http://blog.csdn.net/m0_37306360/article/details/79318644
簡介
在這個項目中,我們將使用PyTorch框架實現一個神經網絡,這個網絡實現法文翻譯成英文。這個項目是Sean Robertson寫的稍微復雜一點的教程,但對學習PyTorch還是有很大的幫助。

本文通過序列網絡的這種簡單而強大的思想來實現的,其中包括兩個循環神經網絡一起工作以將一個序列轉換為另一個序列。 編碼器網絡(Encode)將輸入序列壓縮成矢量,解碼器網絡(Decode)將該矢量展開為新的序列。為了改進這個模型,我們將使用一個注意機制,讓解碼器學習把注意力集中在輸入序列的特定范圍上。

 

關於這些技術,更多的學習資料可以在下面網址學習:http://pytorch.org/tutorials/intermediate/seq2seq_translation_tutorial.html

數據集
這個項目的數據是一組數以千計的英語到法語的翻譯對。作者選取了其中部分數據構建本文的訓練數據集(data / eng-fra.txt)。 該文件是一個制表符分隔的翻譯對列表(下載地址:https://download.pytorch.org/tutorial/data.zip):

 

我們才有one-hot vector初始化詞,與前面分類名詞不同的是,這里把單詞看作一個獨立的語言粒度:


我們需要每個單詞的唯一索引作為以后網絡的輸入(inputs)和目標(targets)。 為此,我們使用名為Lang的助手類,它具有詞→索引(word2index)和索引→詞(index2word)字典,以及每個單詞word2count的計數以用於稍后替換罕見詞語。

SOS_token = 0
EOS_token = 1


class Lang:
def __init__(self, name):
self.name = name
self.word2index = {}
self.word2count = {}
self.index2word = {0: "SOS", 1: "EOS"}
self.n_words = 2 # Count SOS and EOS

def addSentence(self, sentence):
for word in sentence.split(' '):
self.addWord(word)

def addWord(self, word):
if word not in self.word2index:
self.word2index[word] = self.n_words
self.word2count[word] = 1
self.index2word[self.n_words] = word
self.n_words += 1
else:
self.word2count[word] += 1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
要讀取數據文件,我們將文件分割成幾行,然后將行分成兩部分。 這些文件都是英文→其他語言,所以如果我們想從其他語言翻譯→英文,我添加了reverse標志來反轉對。

def readLangs(lang1, lang2, reverse=False):
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) for s in l.split('\t')] for l in lines]

# Reverse pairs, make Lang instances
if reverse:
pairs = [list(reversed(p)) for p in pairs]
input_lang = Lang(lang2)
output_lang = Lang(lang1)
else:
input_lang = Lang(lang1)
output_lang = Lang(lang2)

return input_lang, output_lang, pairs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
本文數據預處理過程是:
1.讀取文本文件並拆分成行,將行拆分成對
2.使文本標准化,按照長度和內容進行過濾
3.從成對的句子中構建單詞列表

Seq2Seq模型
Seq2Seq(Sequence to Sequence network or Encoder Decoder network)是由兩個稱為編碼器和解碼器的RNN組成的模型。 編碼器讀取輸入序列並輸出單個矢量,解碼器讀取該矢量以產生輸出序列。

與單個RNN的序列預測不同,每個輸入對應於一個輸出,seq2seq模型無需考慮序列長度和順序,這使得它成為兩種語言之間翻譯的理想選擇。使用seq2seq模型,編碼器會創建一個單一的矢量,在理想的情況下,將輸入序列的“含義”編碼為單個矢量 - 句子的N維空間中的單個點。

The Encoder
seq2seq網絡的編碼器是一個RNN,它為輸入句子中的每個單詞輸出一些值。 對於每個輸入單詞,編碼器輸出一個向量和一個隱藏狀態,這個隱藏狀態和下一個單詞構成下一步的輸入。

 

class EncoderRNN(nn.Module):
def __init__(self, input_size, hidden_size):
super(EncoderRNN, self).__init__()
self.hidden_size = hidden_size

self.embedding = nn.Embedding(input_size, hidden_size)
self.gru = nn.GRU(hidden_size, hidden_size)

def forward(self, input, hidden):
embedded = self.embedding(input).view(1, 1, -1)
output = embedded
output, hidden = self.gru(output, hidden)
return output, hidden

def initHidden(self):
result = Variable(torch.zeros(1, 1, self.hidden_size))
return result
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
The Decoder
解碼器是另一個RNN,它接收編碼器輸出向量並輸出一個字序列來創建翻譯。

在最簡單的seq2seq解碼器中,我們只使用編碼器的最后一個輸出。 這個最后的輸出有時被稱為上下文向量,因為它從整個序列編碼上下文。 該上下文向量被用作解碼器的初始隱藏狀態。如果僅在編碼器和解碼器之間傳遞上下文向量,則該單個向量承擔編碼整個句子的負擔。注意力(Attention Decoder)允許解碼器網絡針對解碼器自身輸出的每一步“聚焦”編碼器輸出的不同部分。首先我們計算一組注意力權重。 這些將被乘以編碼器輸出矢量以創建加權組合。

 

 

class AttnDecoderRNN(nn.Module):
def __init__(self, hidden_size, output_size, dropout_p=0.1, max_length=MAX_LENGTH):
super(AttnDecoderRNN, self).__init__()
self.hidden_size = hidden_size
self.output_size = output_size
self.dropout_p = dropout_p
self.max_length = max_length

self.embedding = nn.Embedding(self.output_size, self.hidden_size)
self.attn = nn.Linear(self.hidden_size * 2, self.max_length)
self.attn_combine = nn.Linear(self.hidden_size * 2, self.hidden_size)
self.dropout = nn.Dropout(self.dropout_p)
self.gru = nn.GRU(self.hidden_size, self.hidden_size)
self.out = nn.Linear(self.hidden_size, self.output_size)

def forward(self, input, hidden, encoder_outputs):
embedded = self.embedding(input).view(1, 1, -1)
embedded = self.dropout(embedded)
attn_weights = F.softmax(
self.attn(torch.cat((embedded[0], hidden[0]), 1)), dim=1)
attn_applied = torch.bmm(attn_weights.unsqueeze(0),
encoder_outputs.unsqueeze(0))
output = torch.cat((embedded[0], attn_applied[0]), 1)
output = self.attn_combine(output).unsqueeze(0)
output = F.relu(output)
output, hidden = self.gru(output, hidden)
output = F.log_softmax(self.out(output[0]), dim=1)
return output, hidden, attn_weights

def initHidden(self):
result = Variable(torch.zeros(1, 1, self.hidden_size))
return result
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
訓練和測試模型
loss 圖:


評估與訓練大部分相同,但沒有目標(target),因此我們只是將解碼器的預測反饋給每一步的自身。 每當它預測到一個單詞時,我們就會將它添加到輸出字符串中,並且當生成EOS字符就停止。 我們還存儲解碼器的注意力輸出以供稍后顯示。

可視化Attention,這個機制的一個有用特性是其高度可解釋的輸出。 因為它用於對輸入序列的特定編碼器輸出進行加權,所以我們可以想象在每個時間步驟中網絡最集中的位置。這里將注意力輸出顯示為矩陣,其中列是輸入步驟,行是輸出步驟:


更好的觀看體驗,我們額外用了幾個數據對:


注意:所以的代碼基本上為教程上的,我跑通的代碼稍微會上傳到github上。

參考:http://pytorch.org/tutorials/intermediate/seq2seq_translation_tutorial.html
---------------------
作者:yuquanle
來源:CSDN
原文:https://blog.csdn.net/m0_37306360/article/details/79318644
版權聲明:本文為博主原創文章,轉載請附上博文鏈接!


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM