十分钟教程:用Keras实现seq2seq学习

2017 年 10 月 11 日 全球人工智能

“全球人工智能”拥有十多万AI产业用户,10000多名AI技术专家。主要来自:北大,清华,中科院,麻省理工,卡内基梅隆,斯坦福,哈佛,牛津,剑桥...以及谷歌,腾讯,百度,脸谱,微软,阿里,海康威视,英伟达......等全球名校和名企。


——免费加入AI技术专家社群>>

——免费加入AI高管投资者群>>

——申请成为AI高校推广大使>>

摘要: 序列到序列学习(seq2seq)是一种把序列从一个域(例如英语中的句子)转换为另一个域中的序列(例如把相同的句子翻译成法语)的模型训练方法。目前有多种方法可以用来处理这个任务,可以使用RNN,也可以使用一维卷积网络。这里,我们将重点介绍RNN。


看到有很多人问这个问题:如何在Keras中实现RNN序列到序列(seq2seq)学习?本文将对此做一个简单的介绍。

请注意,要读懂本文,你需要具备循环网络和Keras方面的相关经验。

什么是seq2seq学习?

序列到序列学习(seq2seq)是一种把序列从一个域(例如英语中的句子)转换为另一个域中的序列(例如把相同的句子翻译成法语)的模型训练方法。

"the cat sat on the mat" -> [Seq2Seq model] -> "le chat etait assit sur le tapis"

这可以用于机器翻译或免费问答(对于自然语言的问题,产生自然语言的答案)。一般来说,它适用于任何需要生成文本的场景。

目前有多种方法可以用来处理这个任务,可以使用RNN,也可以使用一维卷积网络。这里,我们将重点介绍RNN。

一个简单的例子:当输入和输出序列的长度相同时

当输入序列和输出序列具有相同长度的时候,你可以使用Keras LSTM或GRU层(或其堆叠)很轻松地实现这样地模型。这个示例脚本就是一个例子,它展示了如何教RNN计算加法,并编码为字符串:

对于这个方法有一点要注意:我们假定了对于给定的input[...t]是可以生成target[...t]的。这在某些情况下有效(例如,数字字符串的加法),但在大多数情况下都无效。在一般情况下,要生成目标序列,必须要有输入序列的完整信息。

一般情况:标准的序列到序列

一般来说,输入序列和输出序列的长度是不同的(例如机器翻译),并且需要有完整的输入序列才能开始预测目标。这需要一个更高级的设置,这就是人们在“序列到序列模型”时经常提及的没有上下文。下面是它的工作原理:

  • 有一个RNN层(或其堆叠)作为“编码器”:它负责处理输入序列并返回其自身的内部状态。注意,我们将丢弃编码器RNN的输出,只恢复状态。该状态将在下一步骤中用作解码器的“上下文”或“环境”。

  • 另外还有一个RNN层(或其堆叠)作为“解码器”:在给定目标序列前一个字符的情况下,对其进行训练以预测目标序列的下一个字符。具体来说,就是训练该层使其能够将目标序列转换成向将来偏移了一个时间步长的同一个序列,这种训练过程被称为“teacher forcing(老师强迫)”。有一点很重要,解码器将来自编码器的状态向量作为初始状态,这样,解码器就知道了它应该产生什么样的信息。实际上就是解码器以输入序列为条件,对于给定的targets[...t]学习生成targets[t+1...],。

在推理模式下,即当我们要解码未知输入序列时,过程稍稍会有些不同:

  1. 将输入序列编码为状态向量。

  2. 以大小为1的目标序列开始。

  3. 将状态向量和一个字符的目标序列提供给解码器,以产生下一个字符的预测。

  4. 使用这些预测对下一个字符进行采样(我们简单地使用argmax)。

  5. 将采样的字符添加到目标序列上

  6. 重复上述步骤,直到生成序列结束字符,或者达到字符数限制。

也可以在没有“teacher forcing”的情况下使用相同的过程来训练Seq2Seq网络,例如,通过将解码器的预测重新注入到解码器中。

一个Keras的例子

下面我们用代码来实现上面那些想法。

对于这个例程,我们将使用英文句子和对应的法语翻译数据集,可以从manythings.org/anki下载。下载的文件名为fra-eng.zip。我们将实现一个字符级别的序列到序列模型,处理逐个字符输入并逐个字符的生成输出。我们也可以实现一个单词级别的模型,这对于机器翻译而言更常见。在本文的最后,你能找到一些使用Embedding层把字符级别的模型变成单词级别模型的信息。

完整例程可以在GitHub上找到。

下面简单介绍一下处理过程:

  1. 将句子转换为3个Numpy数组,encoder_input_datadecode_input_datadecode_target_data:    - encoder_input_data是一个三维数组(num_pairsmax_english_sentence_lengthnum_english_characters),包含英文句子的独热向量化。    - decoder_input_data是一个三维数组(num_pairsmax_french_sentence_lengthnum_french_characters),包含法语句子的独热向量化。    - decoder_target_datadecoder_input_data相同但偏移一个时间步长。 decoder_target_data[:, t, :]将与decoder_input_data[:, t + 1, :]相同

  2. 训练一个基于LSTM的基本的Seq2Seq模型来预测encoder_input_datadecode_input_datadecode_target_data。模型使用了“teacher forcing”。

  3. 解码一些句子以检查模型是否正常工作(即将encoder_input_data中的样本从decoder_target_data转换为相应的样本)。

由于训练过程和推理过程(译码句)是完全不同的,所以我们要使用不同的模型,尽管它们都是利用相同的内部层。

这是我们的训练模型。它利用了Keras RNN的三个主要功能:

  • return_state contructor参数,配置一个RNN层返回第一个条目是输出,下一个条目是内部RNN状态的列表。用于恢复编码器的状态。

  • inital_state参数,指定RNN的初始状态。用于将编码器状态传递到解码器作为初始状态。

  • return_sequences构造函数参数,配置RNN返回其完整的输出序列。在解码器中使用。

from keras.models import Model
from keras.layers import Input, LSTM, Dense# Define an input sequence and process it.encoder_inputs = Input(shape=(None, num_encoder_tokens))encoder = LSTM(latent_dim, return_state=True)encoder_outputs, state_h, state_c = encoder(encoder_inputs)# We discard `encoder_outputs` and only keep the states.encoder_states = [state_h, state_c]# Set up the decoder, using `encoder_states` as initial state.decoder_inputs = Input(shape=(None, num_decoder_tokens))decoder_lstm = LSTM(latent_dim, return_sequences=True)decoder_outputs = decoder_lstm(decoder_inputs, initial_state=encoder_states)decoder_dense = Dense(num_decoder_tokens, activation='softmax')decoder_outputs = decoder_dense(decoder_outputs)# Define the model that will turn# `encoder_input_data` & `decoder_input_data` into `decoder_target_data`model = Model([encoder_inputs, decoder_inputs], decoder_outputs)

我们对模型进行了训练,同时监测到了20%的样本损失。

# Run trainingmodel.compile(optimizer='rmsprop', loss='categorical_crossentropy')model.fit([encoder_input_data, decoder_input_data], decoder_target_data,          batch_size=batch_size,
          epochs=epochs,
          validation_split=0.2)

在MacBook CPU上,经过一个小时左右的时间,就可以开始推断了。 要解码一个测试语句,要重复这几个步骤:

  1. 对输入的句子进行编码并获取初始解码器的状态

  2. 以该初始状态和“序列开始”令牌为目标,执行解码器的一个步骤。 输出是下一个目标字符。

  3. 添加预测到的目标字符并重复上述步骤。

这是我们的推理设置:

encoder_model = Model(encoder_inputs, encoder_states)decoder_state_input_h = Input(shape=(latent_dim,))decoder_state_input_c = Input(shape=(latent_dim,))decoder_states = [decoder_state_input_h, decoder_state_input_c]decoder_outputs = decoder_lstm(decoder_inputs,                               initial_state=decoder_states)decoder_outputs = decoder_dense(decoder_outputs)decoder_model = Model(
    [decoder_inputs] + decoder_states,
    decoder_outputs)

我们用它来实现上述推理循环:

def decode_sequence(input_seq):
    # Encode the input as state vectors.
    states_value = encoder_model.predict(input_seq)    # Generate empty target sequence of length 1.
    target_seq = np.zeros((1, 1, num_decoder_tokens))    # Populate the first character of target sequence with the start character.
    target_seq[0, 0, target_token_index['\t']] = 1.

    # Sampling loop for a batch of sequences
    # (to simplify, here we assume a batch of size 1).
    stop_condition = False
    decoded_sentence = ''
    while not stop_condition:
        output_tokens = decoder_model.predict([target_seq] + states_value)        # Sample a token
        sampled_token_index = np.argmax(output_tokens[0, -1, :])
        sampled_char = reverse_target_char_index[sampled_token_index]
        decoded_sentence += sampled_char        # Exit condition: either hit max length
        # or find stop character.
        if (sampled_char == '\n' or
           len(decoded_sentence) > max_decoder_seq_length):
            stop_condition = True

        # Add the sampled character to the sequence
        char_vector = np.zeros((1, 1, num_decoder_tokens))
        char_vector[0, 0, sampled_token_index] = 1.

        target_seq = np.concatenate([target_seq, char_vector], axis=1)    return decoded_sentence

我们得到了一些不错的结果。由于我们是从训练测试集中抽取的样本,所以这并不奇怪。

Input sentence: Be nice.Decoded sentence: Soyez gentil !-Input sentence: Drop it!Decoded sentence: Laissez tomber !-Input sentence: Get out!Decoded sentence: Sortez !

有关Keras的序列到序列模型的十分钟介绍已经结束了。 请注意:完整的代码可在GitHub上找到。

参考资料

使用神经网络进行序列到序列的学习
使用用于统计机器翻译的RNN编码器-解码器来学习短语的表达

常见问题

如果我想使用GRU层而不是LSTM该怎么办?

这实际上更简单,因为GRU只有一个状态,而LSTM有两个状态。 以下代码展示了如何让训练模型适应使用GRU层:

encoder_inputs = Input(shape=(None, num_encoder_tokens))encoder = GRU(latent_dim, return_state=True)encoder_outputs, state_h = encoder(encoder_inputs)decoder_inputs = Input(shape=(None, num_decoder_tokens))decoder_gru = GRU(latent_dim, return_sequences=True)decoder_outputs = decoder_gru(decoder_inputs, initial_state=state_h)decoder_dense = Dense(num_decoder_tokens, activation='softmax')decoder_outputs = decoder_dense(decoder_outputs)model = Model([encoder_inputs, decoder_inputs], decoder_outputs)

如果我想对整数序列的输入使用单词级模型该怎么办?

如果输入是整数序列,该怎么办呢? 通过嵌入层嵌入这些整数令牌即可。 就是这样:

# Define an input sequence and process it.encoder_inputs = Input(shape=(None,))x = Embedding(num_encoder_tokens, latent_dim)(encoder_inputs)
x, state_h, state_c = LSTM(latent_dim,                           return_state=True)(x)encoder_states = [state_h, state_c]# Set up the decoder, using `encoder_states` as initial state.decoder_inputs = Input(shape=(None,))x = Embedding(num_decoder_tokens, latent_dim)(decoder_inputs)x = LSTM(latent_dim, return_sequences=True)(x, initial_state=encoder_states)decoder_outputs = Dense(num_decoder_tokens, activation='softmax')(x)# Define the model that will turn# `encoder_input_data` & `decoder_input_data` into `decoder_target_data`model = Model([encoder_inputs, decoder_inputs], decoder_outputs)# Compile & run trainingmodel.compile(optimizer='rmsprop', loss='categorical_crossentropy')# Note that `decoder_target_data` needs to be one-hot encoded,# rather than sequences of integers like `decoder_input_data`!model.fit([encoder_input_data, decoder_input_data], decoder_target_data,          batch_size=batch_size,
          epochs=epochs,
          validation_split=0.2)

如果我不想用“teacher forcing”训练该怎么办?

在某些案例中,由于无法访问完整的目标序列,可能导致无法使用“teacher forcing”。例如 如果需要对一个很长的序列做在线训练,那么缓冲完整的输入几乎是不可能的。 在这种情况下,你可能希望通过将解码器的预测重新注入到解码器的输入中来进行训练,就像我们在推理中做的那样。

你可以通过构建一个硬编码输出重新注入回路的模型来实现这个目的:

from keras.layers import Lambda
from keras import backend as K# The first part is unchangedencoder_inputs = Input(shape=(None, num_encoder_tokens))encoder = LSTM(latent_dim, return_state=True)encoder_outputs, state_h, state_c = encoder(encoder_inputs)states = [state_h, state_c]# Set up the decoder, which will only process one timestep at a time.decoder_inputs = Input(shape=(1, num_decoder_tokens))decoder_lstm = LSTM(latent_dim, return_sequences=True, return_state=True)decoder_dense = Dense(num_decoder_tokens, activation='softmax')all_outputs = []inputs = decoder_inputs
for _ in range(max_decoder_seq_length):    # Run the decoder on one timestep
    outputs, state_h, state_c = decoder_lstm(inputs,                                             initial_state=states)
    outputs = decoder_dense(outputs)    # Store the current prediction (we will concatenate all predictions later)
    all_outputs.append(outputs)    # Reinject the outputs as inputs for the next loop iteration
    # as well as update the states
    inputs = outputs    states = [state_h, state_c]# Concatenate all predictionsdecoder_outputs = Lambda(lambda x: K.concatenate(x, axis=1))(all_outputs)# Define and compile model as previouslymodel = Model([encoder_inputs, decoder_inputs], decoder_outputs)
model.compile(optimizer='rmsprop', loss='categorical_crossentropy')# Prepare decoder input data that just contains the start character# Note that we could have made it a constant hard-coded in the modeldecoder_input_data = np.zeros((num_samples, 1, num_decoder_tokens))
decoder_input_data[:, 0, target_token_index['\t']] = 1.# Train model as previouslymodel.fit([encoder_input_data, decoder_input_data], decoder_target_data,          batch_size=batch_size,
          epochs=epochs,
          validation_split=0.2)

原文:https://blog.keras.io/a-ten-minute-introduction-to-sequence-to-sequence-learning-in-keras.html?spm=5176.100239.blogcont221673.19.dH9uEa

系统学习,进入全球人工智能学院

热门文章推荐

未来 3~5 年内,哪个方向的机器学习人才最紧缺?

中科院步态识别技术:不看脸 50米内在人群中认出你!

厉害|黄仁勋狂怼CPU:摩尔定律已死 未来属于GPU!

干货|7步让你从零开始掌握Python机器学习!

华裔女科学家钱璐璐,发明仅20纳米的DNA机器人!

Geoffrey Hinton提出capsule 概念,推翻反向传播!

2017年7大最受欢迎的AI编程语言:Python第一!

重磅|中国首家人工智能技术学院在京揭牌开学!

厉害 | 南京大学周志华教授当选欧洲科学院外籍院士!

5个月市值涨了1200亿,首次突破3100亿市值!

登录查看更多
2

相关内容

数学上,序列是被排成一列的对象(或事件);这样每个元素不是在其他元素之前,就是在其他元素之后。这里,元素之间的顺序非常重要。
一份循环神经网络RNNs简明教程,37页ppt
专知会员服务
168+阅读 · 2020年5月6日
Transformer文本分类代码
专知会员服务
116+阅读 · 2020年2月3日
【MIT深度学习课程】深度序列建模,Deep Sequence Modeling
专知会员服务
76+阅读 · 2020年2月3日
【书籍】深度学习框架:PyTorch入门与实践(附代码)
专知会员服务
160+阅读 · 2019年10月28日
注意力机制介绍,Attention Mechanism
专知会员服务
166+阅读 · 2019年10月13日
初学者的 Keras:实现卷积神经网络
Python程序员
24+阅读 · 2019年9月8日
PyTorch:60分钟入门学习
全球人工智能
13+阅读 · 2018年5月18日
教程 | 基于Keras的LSTM多变量时间序列预测
机器之心
20+阅读 · 2017年10月30日
深度学习入门篇--手把手教你用 TensorFlow 训练模型
全球人工智能
4+阅读 · 2017年10月21日
10分钟搞懂Tensorflow 逻辑回归实现手写识别
全球人工智能
5+阅读 · 2017年10月19日
如何为LSTM重新构建输入数据(Keras)
全球人工智能
6+阅读 · 2017年10月13日
十分钟掌握Keras实现RNN的seq2seq学习
机器学习研究会
10+阅读 · 2017年10月13日
用Python实现CNN长短期记忆网络!
全球人工智能
9+阅读 · 2017年8月22日
Arxiv
4+阅读 · 2018年10月31日
The Matrix Calculus You Need For Deep Learning
Arxiv
12+阅读 · 2018年7月2日
Arxiv
6+阅读 · 2018年6月20日
Arxiv
11+阅读 · 2018年1月11日
Arxiv
26+阅读 · 2017年12月6日
Arxiv
4+阅读 · 2017年7月25日
VIP会员
相关VIP内容
一份循环神经网络RNNs简明教程,37页ppt
专知会员服务
168+阅读 · 2020年5月6日
Transformer文本分类代码
专知会员服务
116+阅读 · 2020年2月3日
【MIT深度学习课程】深度序列建模,Deep Sequence Modeling
专知会员服务
76+阅读 · 2020年2月3日
【书籍】深度学习框架:PyTorch入门与实践(附代码)
专知会员服务
160+阅读 · 2019年10月28日
注意力机制介绍,Attention Mechanism
专知会员服务
166+阅读 · 2019年10月13日
相关资讯
初学者的 Keras:实现卷积神经网络
Python程序员
24+阅读 · 2019年9月8日
PyTorch:60分钟入门学习
全球人工智能
13+阅读 · 2018年5月18日
教程 | 基于Keras的LSTM多变量时间序列预测
机器之心
20+阅读 · 2017年10月30日
深度学习入门篇--手把手教你用 TensorFlow 训练模型
全球人工智能
4+阅读 · 2017年10月21日
10分钟搞懂Tensorflow 逻辑回归实现手写识别
全球人工智能
5+阅读 · 2017年10月19日
如何为LSTM重新构建输入数据(Keras)
全球人工智能
6+阅读 · 2017年10月13日
十分钟掌握Keras实现RNN的seq2seq学习
机器学习研究会
10+阅读 · 2017年10月13日
用Python实现CNN长短期记忆网络!
全球人工智能
9+阅读 · 2017年8月22日
相关论文
Arxiv
4+阅读 · 2018年10月31日
The Matrix Calculus You Need For Deep Learning
Arxiv
12+阅读 · 2018年7月2日
Arxiv
6+阅读 · 2018年6月20日
Arxiv
11+阅读 · 2018年1月11日
Arxiv
26+阅读 · 2017年12月6日
Arxiv
4+阅读 · 2017年7月25日
Top
微信扫码咨询专知VIP会员