背景
本系列从第十二期开启 神经网络翻译NMT及聊天机器人Chatbot 的模型,已经涉及Seq2Seq,Attention Mechanism,Beam Search 等模型。前期基本上都是从抽象的角度出发进行总结。
本期将给大家带来Seq2Seq模型代码的讲解,代码来来自于 Toward Datascience 的Ravindra Kompella。并加上自己的理解。
本期带来的模型是基础的Seq2Seq 模型,并不涉及Attention Mechanism。对于Seq2Seq 模型的抽象原理讲解,可以参考本系列第十三期。
Seq2Seq模型简介
Seq2Seq 模型其实就是 Encoder-Decoder模型 加上一个 循环神经网络。其思路是将 一段句子 encoder 成一个向量,再将此向量“解压”成目标语句。
关于此模型的优缺点和其他的理解,详细的可以参考本系列第十二期。
Seq2Seq训练
本次建立的模型将会基于下图进行讲解,
首先,我们拥有的数据集是10000个英文句子 与其 对应的法语句子。我们的训练样本是10000个
总体的训练步骤:
对于所有的英文和法语句子,建立一个以 字母 为基础的one-hot 向量。这些向量将作为Encoder和Decoder的输入值;
个人解释:需要解释下 以 字母为基础。很多NLP的模型会使用Word Embedding层或者借用 Global Vector 等等。但是今天的例子,采用的是one-hot向量。主要是因为我们是以字母为基础,毕竟英文只有26个字母,不会造成Sparsity的问题(详情请见本系列第三期)
逐个将每个英文字母,输入Encoder 直到英文句子的末尾;
获取Encoder 最终的 States(hidden states和 cell states),然后将这个最终的states导入Decoder 作为初始状态;
Decoder每次都会有三个输入值,两个由Encoder发起的States 和 法语的逐字母输入;
在每一个Decoder输出值,输出值导入到Softmax层 与 实际的目标值进行比较并计算loss
详细的代码实践:
第一步.
处理原始数据,并且搜集所有的英文 和 法语 字母。并且将英文与法语字母转换为Dictionary
# Process english and french sentences
for line in range(nb_samples):
eng_line = str(lines[line]).split('')[0]
# Append '' for start of the sentence and '' to signify end of the sentence
fra_line = '' + str(lines[line]).split('')[1] + ''
eng_sent.append(eng_line)
fra_sent.append(fra_line)
for ch in eng_line:
if (ch not in eng_chars):
eng_chars.add(ch)
for ch in fra_line:
if (ch not in fra_chars):
fra_chars.add(ch)
第二步,建立起以字母为基础的one-hot vector
比如字母a 为变成【1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0.....】共26位
tokenized_eng_sentences = np.zeros(shape = (nb_samples,max_len_eng_sent,len(eng_chars)), dtype='float32')
tokenized_fra_sentences = np.zeros(shape = (nb_samples,max_len_fra_sent,len(fra_chars)), dtype='float32')
target_data = np.zeros((nb_samples, max_len_fra_sent, len(fra_chars)),dtype='float32')
# Vectorize the english and french sentences
for i in range(nb_samples):
for k, ch in enumerate(eng_sent[i]):
tokenized_eng_sentences[i, k, eng_char_to_index_dict[ch]] = 1
for k, ch in enumerate(fra_sent[i]):
tokenized_fra_sentences[i, k, fra_char_to_index_dict[ch]] = 1
# decoder_target_data will be ahead by one timestep and will not include the start character.
if k > 0:
target_data[i, k - 1, fra_char_to_index_dict[ch]] = 1
第三步
建立Encoder模型,对于encoder中的LSTM模型,我们将return state 设置为True,另外Return Sequence 模型为False。这意味着,虽然我们是逐字母输入,但是LSTM不会逐字母输出sequence,我们只输出最终的states。因为基础的Seq2Seq模型只使用Encoder输出的最终Vector
# Encoder model
encoder_input = Input(shape=(None,len(eng_chars)))
encoder_LSTM = LSTM(256,return_state = True)
encoder_outputs, encoder_h, encoder_c = encoder_LSTM (encoder_input)
encoder_states = [encoder_h, encoder_c]
第四步
Decoder 的输入值,为逐字母的法语字母, 和 上一个 神经元的States。另外,Decoder 的初始States 是来源于 Encoder, 大家能看到第四行的initial_state=encoder_states
# Decoder model
decoder_input = Input(shape=(None,len(fra_chars)))
decoder_LSTM = LSTM(256,return_sequences=True, return_state = True)
decoder_out, _ , _ = decoder_LSTM(decoder_input, initial_state=encoder_states)
decoder_dense = Dense(len(fra_chars),activation='softmax')
decoder_out = decoder_dense (decoder_out)
第五步
最终集合该模型,loss 类型选择为Categorical Crossentropy
model = Model(inputs=[encoder_input, decoder_input],outputs=[decoder_out])
# Run training
model.compile(optimizer='rmsprop', loss='categorical_crossentropy')
model.fit(x=[tokenized_eng_sentences,tokenized_fra_sentences],
y=target_data,
batch_size=64,
epochs=50,
validation_split=0.2)
开始训练
因为模型训练设置成50轮,下图展现的是每次的loss 和 validation loss。大家能看到validation loss在14轮开始就没有显著下降,很可能表明已经出现overfitting。
不过,此模型的目的是简单的展示一个Seq2Seq模型
下期预告
好像Beam Search还没完结
领取专属 10元无门槛券
私享最新 技术干货