keras來機器翻譯怎麼樣?!
本文根據吳恩達 Coursera 課程 DeepLearning.ai 序列模型和注意力機制的機器翻譯
您將構建一個神經機器翻譯(NMT)模型,將人類可讀日期(「2009年6月25日」)翻譯成機器可讀日期(「2009-06-25」)。 您將使用注意模型執行此操作,注意模型是序列模型中最複雜的序列之一。
本文配套的所有數據集和代碼在
0.導入庫
from keras.layers import Bidirectional, Concatenate, Permute, Dot, Input, LSTM, Multiply
from keras.layers import RepeatVector, Dense, Activation, Lambda
from keras.optimizers import Adam
from keras.utils import to_categorical
from keras.models import load_model, Model
import keras.backend as K
import numpy as np
from faker import Faker
import random
from tqdm import tqdm
from babel.dates import format_date
from nmt_utils import *
import matplotlib.pyplot as plt
%matplotlib inline
需要pip安裝faker,tqdm,babel
1 - 將人類可讀日期翻譯成機器可讀日期
您將在此處構建的模型可用於從一種語言翻譯成另一種語言,例如從英語翻譯成印地語。 但是,語言翻譯需要大量數據集,並且通常需要數天的GPU培訓。 為了讓您在不使用大量數據集的情況下嘗試使用這些模型,我們將使用更簡單的「日期轉換」任務。 網路將輸入以各種可能格式(例如「1958年8月29日」,「03/30/1968」,「1987年6月24日」)編寫的日期,並將其翻譯成標準化的機器可讀日期(例如「1958年」) -08-29「,」1968-03-30「,」1987-06-24「)。 我們將讓網路學習以通用機器可讀格式YYYY-MM-DD輸出日期
1.1數據集
- dataset:元組列表(human readable date, machine readable date)
- human_vocab:將人類可讀日期中使用的所有字元映射到整數值索引的python字典
- machine_vocab:python字典映射 機器可讀日期中使用的所有字元都是整數值索引。這些指數不一定與human_vocab一致。
- inv_machine_vocab:machine_vocab的逆字典,從索引到字元的映射。
m = 10000
dataset, human_vocab, machine_vocab, inv_machine_vocab = load_dataset(m)
100%|██████████| 10000/10000 [00:00<00:00, 12716.16it/s]
dataset[:10]
[(9 may 1998, 1998-05-09),
(10.09.70, 1970-09-10),
(4/28/90, 1990-04-28),
(thursday january 26 1995, 1995-01-26),
(monday march 7 1983, 1983-03-07),
(sunday may 22 1988, 1988-05-22),
(tuesday july 8 2008, 2008-07-08),
(08 sep 1999, 1999-09-08),
(1 jan 1981, 1981-01-01),
(monday may 22 1995, 1995-05-22)]
1.2數據預處理
我們預處理數據並將原始文本數據映射到索引值。 我們還將使用Tx = 30(我們假設是人類可讀日期的最大長度;如果我們得到更長的輸入,我們將不得不截斷它)並且Ty = 10(因為「YYYY-MM-DD」是10 長字元)。
Tx = 30
Ty = 10
X, Y, Xoh, Yoh = preprocess_data(dataset, human_vocab, machine_vocab, Tx, Ty)
print("X.shape:", X.shape)
print("Y.shape:", Y.shape)
print("Xoh.shape:", Xoh.shape)
print("Yoh.shape:", Yoh.shape)
輸出
X.shape: (10000, 30)
Y.shape: (10000, 10)
Xoh.shape: (10000, 30, 37)
Yoh.shape: (10000, 10, 11)
- Xoh:X的獨熱版本 - Yoh:Y的獨熱版本
數據實例
index = 0
print("Source date:", dataset[index][0])
print("Target date:", dataset[index][1])
print()
print("Source after preprocessing (indices):", X[index])
print("Target after preprocessing (indices):", Y[index])
print()
print("Source after preprocessing (one-hot):", Xoh[index])
print("Target after preprocessing (one-hot):", Yoh[index])
輸出,能夠更為直觀地看出x和y的轉換
Source date: 9 may 1998
Target date: 1998-05-09
Source after preprocessing (indices): [12 0 24 13 34 0 4 12 12 11 36 36 36 36 36 36 36 36 36 36 36 36 36 36 36
36 36 36 36 36]
Target after preprocessing (indices): [ 2 10 10 9 0 1 6 0 1 10]
Source after preprocessing (one-hot): [[ 0. 0. 0. ..., 0. 0. 0.]
[ 1. 0. 0. ..., 0. 0. 0.]
[ 0. 0. 0. ..., 0. 0. 0.]
...,
[ 0. 0. 0. ..., 0. 0. 1.]
[ 0. 0. 0. ..., 0. 0. 1.]
[ 0. 0. 0. ..., 0. 0. 1.]]
Target after preprocessing (one-hot): [[ 0. 0. 1. 0. 0. 0. 0. 0. 0. 0. 0.]
[ 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 1.]
[ 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 1.]
[ 0. 0. 0. 0. 0. 0. 0. 0. 0. 1. 0.]
[ 1. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]
[ 0. 1. 0. 0. 0. 0. 0. 0. 0. 0. 0.]
[ 0. 0. 0. 0. 0. 0. 1. 0. 0. 0. 0.]
[ 1. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]
[ 0. 1. 0. 0. 0. 0. 0. 0. 0. 0. 0.]
[ 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 1.]]
2.帶注意力機制的機器翻譯
如果你必須將一本書的段落從法語翻譯成英語,你不會閱讀整段,然後關閉書籍並翻譯。 即使在翻譯過程中,您也會閱讀或者重新閱讀並專註於與您所寫的英語部分相對應的法文段落部分。 注意機制的作用就是告訴神經機器翻譯模型,它應該在任何步驟注意。
下圖中左側的圖表顯示了注意力模型。 右邊的圖表顯示了一個「注意」步驟用於計算注意變數α?t,t?的內容,它們用於計算上下文變數context?t?、 輸出中的每個時間步長(t = 1,...,Ty)。
以下是您可能會注意到的模型的一些屬性:此模型中有兩個單獨的LSTM(請參見左側的圖表)。因為圖片底部的那個是雙向LSTM並且在注意機制之前,我們將其稱為預注意力Bi-LSTM。圖表頂部的LSTM位於注意機制之後,因此我們將其稱為後注意LSTM。預注意力Bi-LSTM經歷Tx時間步長;後注意力LSTM經歷了Ty的時間步驟。後注意力LSTM將s?t?,c?t?從一個時間步傳遞到下一個時間步。
與之前的文本生成不同,在此模型中,時間t處的激活後LSTM不會將特定生成的y?t-1?作為輸入;它只需要s?t?和c?t?作為輸入。我們以這種方式設計了模型,因為(與相鄰字元高度相關的語言生成不同),YYYY-MM-DD日期中前一個字元與下一個字元之間的依賴性不強。
我們使用
來表示前向兩個激活的連接前注意力Bi-LSTM的後向和後向。
上面的圖使用RepeatVector節點複製s?t-1?的值Tx次,然後Concatenation連接層以連接s?t-1?和a?t?計算e?t,t,然後通過softmax計算α?t,t?,我們將在下面解釋如何在Keras中使用RepeatVector和Concatenation。
2.1函數實現
one_step_attention():
在步驟t中,給定Bi-LSTM的所有隱藏狀態([a <1>,a <2>,...,a <Tx>] )和第二個LSTM的先前隱藏狀態(s <t-1> ),one_step_attention()將計算注意權重([α<t, 1>,α<t,2>,...,α<t,Tx>] )和輸出上下文向量:
model():實現整個模型。它首先通過Bi-LSTM運行輸入以返回[a <1>,a <2>,...,a <Tx>] 。然後,它調用one_step_attention()Ty次數(for循環)。在該循環的每次迭代中,它將計算的上下文向量c <t> c <t>給予第二個LSTM,並且通過具有softmax激活的密集層運行LSTM的輸出以生成預測?<t>。
函數model()將使用for循環調用one_step_attention()Ty中的圖層,並且所有Ty副本具有相同的權重非常重要。即,它不應該每次重新初始化權重。換句話說,所有Ty步驟都應該具有共享權重。
而如何在Keras中實現具有可共享權重的圖層:
1。定義圖層對象(作為示例的全局變數)。
2.傳播輸入時調用這些對象。我們已經將您需要的層定義為全局變數。請運行以下單元格來創建它們。
請查看Keras文檔以確保您了解這些層是什麼:RepeatVector(),Concatenate(),
這裡有一個axis參數可以在下面的文章參考一下
# 將共享層定義為全局變數
repeator = RepeatVector(Tx)
concatenator = Concatenate(axis=-1)
densor1 = Dense(10, activation = "tanh")
densor2 = Dense(1, activation = "relu")
activator = Activation(softmax, name=attention_weights) # We are using a custom softmax(axis = 1) loaded in this notebook
dotor = Dot(axes = 1)
你可以使用這些層來實現one_step_attention()。 為了通過這些層中的一個傳播Keras張量對象X,如果它需要多個輸入,則使用layer(X)(或layer([X,Y])。 densor(X)將通過上面定義的Dense(1)層傳播X.很巧妙的思想!
# GRADED FUNCTION: one_step_attention
def one_step_attention(a, s_prev):
"""
Performs one step of attention: Outputs a context vector computed as a dot product of the attention weights
"alphas" and the hidden states "a" of the Bi-LSTM.
Arguments:
a -- hidden state output of the Bi-LSTM, numpy-array of shape (m, Tx, 2*n_a)
s_prev -- previous hidden state of the (post-attention) LSTM, numpy-array of shape (m, n_s)
Returns:
context -- context vector, input of the next (post-attetion) LSTM cell
"""
### START CODE HERE ###
# Use repeator to repeat s_prev to be of shape (m, Tx, n_s) so that you can concatenate it with all hidden states "a" (≈ 1 line)
s_prev = repeator(s_prev)
# Use concatenator to concatenate a and s_prev on the last axis (≈ 1 line)
concat = concatenator([a, s_prev])
# Use densor1 to propagate concat through a small fully-connected neural network to compute the "intermediate energies" variable e. (≈1 lines)
e = densor1(concat)
# Use densor2 to propagate e through a small fully-connected neural network to compute the "energies" variable energies. (≈1 lines)
energies = densor2(e)
# Use "activator" on "energies" to compute the attention weights "alphas" (≈ 1 line)
alphas = activator(energies)
# Use dotor together with "alphas" and "a" to compute the context vector to be given to the next (post-attention) LSTM-cell (≈ 1 line)
context = dotor([alphas, a])
### END CODE HERE ###
return context
定義model()函數的全局變數
n_a = 32
n_s = 64
#
post_activation_LSTM_cell = LSTM(n_s, return_state = True)
output_layer = Dense(len(machine_vocab), activation=softmax)
Kears LSTM API 中給出的兩個參數描述
return_sequences:默認 False。在輸出序列中,返回單個 hidden state值還是返回全部time step 的 hidden state值。 False 返回單個, true 返回全部。
return_state:默認 False。是否返回除輸出之外的最後一個狀態。
現在,您可以在for循環中使用這些層Ty次來生成輸出,並且它們的參數將不會重新初始化。 您必須執行以下步驟:
- 將輸入傳播到雙向LSTM迭代,t = 0,...,Ty-1:
- 在[α<t,1>上調用one_step_attention(), α<t,2>,...,α<t,Tx>] 和s <t-1 >獲取上下文向量context <t>。
- 將上下文context <t> 提供給關注後LSTM單元格。 請記住使用initial_state = [之前的隱藏狀態,之前的單元狀態]傳入此LSTM的先前隱藏狀態s?t-1?和單元狀態c?t-1?。 返回新隱藏狀態s <t> 和新單元狀態c <t>,不清楚的可以由上文的圖來進行聯想。
- 將softmax圖層應用於s <t> ,得到輸出。 通過將輸出添加到輸出列表來保存輸出。
- 創建您的Keras模型實例,它應該有三個輸入層(「輸入」,s <0> 和c <0> )並輸出「輸出」列表。
# GRADED FUNCTION: model
def model(Tx, Ty, n_a, n_s, human_vocab_size, machine_vocab_size):
"""
Arguments:
Tx -- length of the input sequence
Ty -- length of the output sequence
n_a -- hidden state size of the Bi-LSTM
n_s -- hidden state size of the post-attention LSTM
human_vocab_size -- size of the python dictionary "human_vocab"
machine_vocab_size -- size of the python dictionary "machine_vocab"
Returns:
model -- Keras model instance
"""
# Define the inputs of your model with a shape (Tx,)
# Define s0 and c0, initial hidden state for the decoder LSTM of shape (n_s,)
X = Input(shape=(Tx, human_vocab_size))
s0 = Input(shape=(n_s,), name=s0)
c0 = Input(shape=(n_s,), name=c0)
s = s0
c = c0
# Initialize empty list of outputs
outputs = []
### START CODE HERE ###
# Step 1: Define your pre-attention Bi-LSTM. Remember to use return_sequences=True. (≈ 1 line)
a = Bidirectional(LSTM(n_a, return_sequences=True))(X)
# Step 2: Iterate for Ty steps
for t in range(Ty):
# Step 2.A: Perform one step of the attention mechanism to get back the context vector at step t (≈ 1 line)
context = one_step_attention(a, s)
# Step 2.B: Apply the post-attention LSTM cell to the "context" vector.
# Dont forget to pass: initial_state = [hidden state, cell state] (≈ 1 line)
s, _, c = post_activation_LSTM_cell(context, initial_state = [s, c])
# Step 2.C: Apply Dense layer to the hidden state output of the post-attention LSTM (≈ 1 line)
out = output_layer(s)
# Step 2.D: Append "out" to the "outputs" list (≈ 1 line)
outputs.append(out)
# Step 3: Create model instance taking three inputs and returning the list of outputs. (≈ 1 line)
model = Model(inputs=[X,s0,c0],outputs=outputs)
### END CODE HERE ###
return model
針對這一句s, _, c = post_activation_LSTM_cell(context, initial_state = [s, c]) 我想我們需要額外的資料來理解返回的參數
當return_sequences=True && return_state=True
lstm1, state_h, state_c = LSTM(1, return_sequences=True, return_state=True)
此時,我們既要輸出全部時間步的 hidden state ,又要輸出 cell state。
lstm1 存放的就是全部時間步的 hidden state。
state_h 存放的是最後一個時間步的 hidden state
state_c 存放的是最後一個時間步的 cell state
理解LSTM在keras API中參數return_sequences和return_statemodel = model(Tx, Ty, n_a, n_s, len(human_vocab), len(machine_vocab))
model.summary()
model.summary() 輸出模型結構 ,由於結構太大這裡不貼出來,直接在源碼中就可以看到
Here is the summary you should see
**Total params:** 52,960
**Trainable params:** 52,960
**Non-trainable params:** 0
**bidirectional_1』s output shape ** (None, 30, 64)
**repeat_vector_1』s output shape ** (None, 30, 64)
**concatenate_1』s output shape ** (None, 30, 128)
**attention_weights』s output shape ** (None, 30, 1)
**dot_1』s output shape ** (None, 1, 64)
**dense_3』s output shape ** (None, 11)
像往常一樣,在Keras中創建模型後,您需要對其進行編譯並定義您想要使用的損失,優化程序和指標。 使用categorical_crossentropy loss,自定義Adam優化器(學習率= 0.005,β1=0.9,β2=0.999,decay= 0.01)和[accuracy]指標編譯模型:
### START CODE HERE ### (≈2 lines)
opt = Adam(lr=0.0005, beta_1=0.9, beta_2=0.999, decay=0.01)
model.compile(loss = categorical_crossentropy,optimizer=opt, metrics = [accuracy])
### END CODE HERE ###
最後一步是定義所有輸入和輸出以適合模型
- 您已經擁有包含訓練樣例X,其形狀為(m = 10000,Tx = 30)。
- 您需要創建s0和c0以使用0初始化post_activation_LSTM_cell。
- 給定您編碼的模型(),您需要「輸出」為11個形狀元素(m,T_y)的列表。
因此:outputs [i] [0],...,outputs [i] [Ty]表示與第i次訓練示例(X [i])對應的真實標籤(字元)。 更一般地,outputs[i][j]是第i次訓練示例中第j個字元的真正標籤。
numpy.swapaxes交換矩陣的軸,旋轉維度
s0 = np.zeros((m, n_s))
c0 = np.zeros((m, n_s))
outputs = list(Yoh.swapaxes(0,1))
運行模型
model.fit([Xoh, s0, c0], outputs, epochs=1, batch_size=100)
Epoch 1/1 10000/10000 [==============================] - 30s 3ms/step - loss: 21.9939 - dense_3_loss: 2.7849 - dense_3_acc: 6.0000e-04 - dense_3_acc_1: 0.2884 - dense_3_acc_2: 0.1321 - dense_3_acc_3: 0.0548 - dense_3_acc_4: 0.7795 - dense_3_acc_5: 0.1270 - dense_3_acc_6: 0.0201 - dense_3_acc_7: 0.8298 - dense_3_acc_8: 0.0381 - dense_3_acc_9: 0.0138 Out[20]:
<keras.callbacks.History at 0x7f30c68517b8>
dense_3_acc_8: 0.0381 means that you are predicting the 7th character of the output correctly 89% of the time in the current batch of data.
2.2 載入模型
我們運行此模型的時間更長,並保存了權重。 運行下一個單元格以載入我們的權重。 (通過訓練模型幾分鐘,您應該能夠獲得類似精度的模型,但載入我們的模型將節省您的時間。)
model.load_weights(models/model.h5)
您現在可以在新示例中看到結果(原網站給出的代碼有錯誤,下面是正確的代碼)
EXAMPLES = [3 May 1979, 5 April 09, 21th of August 2016, Tue 10 Jul 2007, Saturday May 9 2018, March 3 2001, March 3rd 2001, 1 March 2001]
for example in EXAMPLES:
source = string_to_int(example, Tx, human_vocab)
source = np.array(list(map(lambda x: to_categorical(x, num_classes=len(human_vocab)), source)))
source = source[np.newaxis, :]
prediction = model.predict([source, s0, c0])
prediction = np.argmax(prediction, axis = -1)
output = [inv_machine_vocab[int(i)] for i in prediction]
print("source:", example)
print("output:", .join(output))
輸出
source: 3 May 1979
output: 1979-05-03
source: 5 April 09
output: 2009-05-05
source: 21th of August 2016
output: 2016-08-21
source: Tue 10 Jul 2007
output: 2007-07-10
source: Saturday May 9 2018
output: 2018-05-09
source: March 3 2001
output: 2001-03-03
source: March 3rd 2001
output: 2001-03-03
source: 1 March 2001
output: 2001-03-01
您還可以更改這些示例以使用您自己的示例進行測試。 下一部分將讓您更好地了解注意機制正在做什麼 - 即,在生成特定輸出字元時,網路正在關注的輸入部分。
3.可視化Attention
由於問題具有10的固定輸出長度,因此還可以使用10個不同的softmax單元來執行該任務以生成輸出的10個字元。 但注意模型的一個優點是輸出的每個部分(比如月份)都知道它只需要依賴於輸入的一小部分(輸入中給出月份的字元)。 我們可以看到輸出的哪個部分正在查看輸入的哪個部分。 考慮將「2018年5月9日星期六」翻譯為「2018-05-09」的任務。 如果我們想像計算出的α?t,t?我們得到這個:
注意:
輸出如何忽略輸入的「Saturday」部分。
輸出時間步長都沒有注意到輸入的那部分。
我們還看到9已翻譯為09並且May已被正確翻譯為05,
輸出時要注意進行翻譯所需的輸入部分。 這一年主要要求它注意輸入的「18」以產生「2018」。
3.1 從網路中獲得激活值
現在讓我們可視化您網路中的注意力值。 我們將通過網路傳播一個例子,然後可視化α?t,t?α?t,t?的值。 為了確定注意力值的位置,讓我們首先列印模型的摘要。
model.summary()
輸出詳細在代碼上
瀏覽上面的model.summary()輸出。 您可以看到名為attention_weights的圖層在dot_1計算每個時間步t = 0,...,Ty-1的上下文向量之前輸出形狀(m,30,1)的alpha。 讓我們從這一層獲得激活。 函數attention_map()從模型中提取注意值並繪製它們。
The functionattention_map()
pulls out the attention values from your model and plots them.
attention_map = plot_attention_map(model, human_vocab, inv_machine_vocab, "Tuesday 09 Oct 1993", num = 7, n_s = 64)
在生成的圖上,您可以觀察預測輸出的每個字元的注意權重值。 檢查此圖並檢查網路注意的位置對您有意義。 在日期翻譯應用程序中,您將觀察到大多數時間注意力有助於預測年份,並且對預測日期/月份沒有太大影響。
您現在可以實現注意模型並使用它來學習從一個序列到另一個序列的複雜映射。
happy hacking!
推薦閱讀: