当前位置: 首页 > 科技观察

十分钟搞定Keras序列到序列学习(附代码实现)

时间:2023-03-17 12:23:39 科技观察

在Keras中如何实现RNN序列到序列学习?在本文中,笔者将尝试对这个问题做出简短的回答;本文假定您已经对网络和Keras有一定的循环体验。GitHub:https://github.com/fchollet/keras/blob/master/examples/lstm_seq2seq.py什么是sequence-to-sequence学习?序列到序列学习(Seq2Seq)是指训练一个模型,将一个领域的序列(比如英语句子)转换成另一个领域的序列(比如对应的法语句子)。“thecatsatonthemat”->[Seq2Seqmodel]->“lechatetaitassissurletapis”Seq2Seq可用于机器翻译或保存问答——一般来说,它可以动态生成文本。有很多方法可以完成这个任务,例如RNN或1D卷积。本文仅涵盖RNN。次要情况:当输入序列和输出序列长度相同时当输入序列和输出序列长度相同时,您可以简单地用KerasLSTM或GRU层(或它们的堆栈)实现模型。此示例脚本中的示例展示了如何教RNN学习将编码为字符串的数字相加:一般示例:标准Seq2Seq通常,输入序列和输出序列具有不同的长度(例如机器翻译)。这需要更高级的设置,尤其是在没有更多上下文的“序列到序列模型”中。它的工作原理如下:RNN层(或其中的堆栈)充当“编码器”:它处理输入序列并反馈其内部状态。请注意,我们丢弃了编码器RNN的输出,只恢复了它的状态。该状态在下一步中充当解码器的“上下文”。另一个RNN层充当“解码器”:它被训练为在给定目标序列的前一个字母的情况下预测目标序列的下一个字符。具体来说,它被训练将目标序列变换成相同的序列,但随后偏移一个时间步长,这个训练过程在上下文中被称为“teacherforcing”。更重要的是,编码器使用它的状态向量作为初始状态,因此编码器拥有它将生成的信息。实际上,给定targets[...t],解码器会根据输入序列学习生成targets[t+1...]。在推理模式下,即当一个未知的输入序列要被解码时,我们做一个稍微不同的过程:将输入序列编码成一个状态向量,从大小为1的目标序列开始,并将状态向量和1个字符的目标序列提供给解码器生成器因此生成下一个字符的预测通过这些预测采样下一个字符(我们使用argmax)将采样的字符附加到目标序列并重复直到我们生成序列的最后一个字符或达到字符限制同样的过程可以用于在没有“教师强制”的情况下训练Seq2Seq网络,即将解码器的预测注入解码器。Keras示例让我们用真实代码来演示这些想法。对于示例实现,我们将使用一对英语句子及其法语翻译的数据集,您可以从http://www.manythings.org/anki/下载,文件名为fra-eng.zip。我们将实现一个字符级的序列到序列模型,该模型逐个字符地处理这些输入并生成输出。另一种选择是词级模型,它更常用于机器学习。在本文的最后,您将找到一些注释,用于通过嵌入层将我们的模型转换为词级模型。这是示例的完整脚本:https://github.com/fchollet/keras/blob/master/examples/lstm_seq2seq.py。下面是这个过程的总结:1.将句子转化为三个Numpy数组encoder_input_data,decoder_input_data,decoder_target_data:encoder_input_data是一个shape为(num_pairs,max_english_sentence_length,num_english_characters)的3D数组,包含一个英文句子的one-hotvectorization.decoder_input_data是一个形状为(num_pairs,max_french_sentence_length,num_french_characters)的3D数组,包含法语句子的单热矢量化。decoder_target_data与decoder_input_data相同,但偏移一个时间步长。decoder_target_data[:,t,:]与decoder_input_data[:,t+1,:]相同。2.给定encoder_input_data和decoder_input_data,训练一个基本的基于LSTM的Seq2Seq模型来预测decoder_target_data。我们的模型使用教师强迫。3.解码一些语句以检查模型是否正常工作。由于训练过程和推理过程(解码句子)有很大不同,我们使用不同的模型,尽管它们具有相同的内层。这是我们的模型,它利用了KerasRNN的3个关键特性:return_state构造函数参数配置一个RNN层以反馈一个列表,其中第一个是它的输出,下一个是内部RNN状态。这用于恢复编码器的状态。inital_state调用参数指定了RNN的初始状态,用于将编码器状态作为初始状态传递给解码器。return_sequences构造函数参数配置RNN的完整反馈输出序列。这在解码器中使用。fromkeras.modelsimportModelfromkeras.layersimportInput,LSTM,Dense#Defineaninputsequenceandprocessit.encoder_inputs=Input(shape=(None,num_encoder_tokens))encoder=LSTM(latent_dim,return_state=True)encoder_outputs,state_h,state_c=编码器(encoder_inputs)#Wediscard`encoder_onlystateoutputs`和.encoder_states=[state_h,state_c]#Setupthedecoder,using`encoder_states`asinitialstate.decoder_inputs=Input(shape=(None,num_decoder_tokens))#Wesetupourdecodertoreturnfulloutputsequences,#andtoreturninternalstatesaswell.Wedon'tusethe#returnstatesinthetrainingmodel,butwewillusethemininference.decoder_lstm=LSTM(latent_dim,return_sequences=True,return_state=True)decoder_outputs,_,_=decoder_lstm(decoder_inputs,initial_state=encoder_states)decoder_dense=密集(num_decoder_tokens,activation='softmax')decoder_outputs=decoder_dense(decoder_outputs)#Definethemodelthatwillturn#`encoder_input_data`input_data&`decoder`decoder_target_data`model=模型([encoder_inputs,decoder_inputs],decoder_outputs)我们使用这两行代码来训练模型,同时监控保留的20%样本集中的损失#Runtrainingmodel.compile(optimizer='rmsprop',loss='categorical_crossentropy')model.fit([encoder_input_data,decoder_input_data],decoder_target_data,batch_sizebatch_size=batch_size,epochsepochs=epochs,validation_split=0.2)在MacBookCPU上运行大约1小时后,我们就可以进行推理了。为了解码测试句子,我们将重复:对输入句子进行编码,检索初始解码器状态。使用初始状态运行单步解码器,目标是“sequencestart”。输出是下一个目标字符。附加预测的目标字符并重复。这是我们的预测设置: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_inputs=[decoder_state_input_h,decoder_c,state_c]decoder_c,state_inputdecoder_lstm(decoder_inputs,initial_state=decoder_states_inputs)desoder_states=[state_h,state_c]desoder_outputs=decoder_dense(decoder_outputs)decoder_model_model=decoder_modes_mode_[decoder_inputs]+decodestates+decodestates使用ddecites上述ddecites](input_seq):#Encodetheinputasstatevectors.states_value=encoder_model.predict(input_seq)#Generateemptytargetsequenceoflength1.target_seq=np.zeros((1,1,num_decoder_tokens))#Populatethefirstcharacteroftargetsequencewiththestartcharacter.target_seq[0,0,target_token_index['t']]=1目标序列.#Samplingloopforabatchofsequences#(tosimplify,hereweassumeabatchofsize1).stop_condition=Falsedecoded_sentence=''whilenotstop_condition:output_tokens,h,c=decoder_model.predict([target_seq]+states_value)#Sampleatokensampled_token_index=np.argmax(output_tokens[0,-1,:])sampled_char=reverse_target_char_index[sampled_token_index]decoded_sentenceds+=sampled_char#Exitiferactlefinance:chareithorhit(采样字符=='n'orlen(decoded_sentence)>max_decoder_seq_length):stop_condition=True#Updatethetargetsequence(oflength1).target_seq=np.zeros((1,1,num_decoder_tokens))target_seq[0,0,sampled_token_index]=1.#Updatetestatesstates_value=[h,c]returndecoded_sentence我们得到了一些好的结果——这是意料之中的,因为我们解码的样本来自训练测试:出去!解码句子:Sortez!我们的10分钟介绍性Keras序列到序列模型教程到此结束。完整代码见GitHub:https://github.com/fchollet/keras/blob/master/examples/lstm_seq2seq.py。FAQ1.我想使用GRU层而不是LSTM,我应该怎么做?这个其实更简单,因为GRU只有一个状态,而LSTM有两个状态。以下是如何使用GRU层拟合经过训练的模型:,num_decoder_tokens))decoder_gru=GRU(latent_dim,return_sequences=True)decoder_outputs=decoder_gru(decoder_inputs,initial_state=state_h)decoder_dense=密集(num_decoder_tokens,activation='softmax')decoder_outputs=decoder_dense(decoder_outputs[enputs]codermodel(decoder_outputs)codermodel(],decoder_outputs)2.我想使用整数序列的词级模型,怎么办?如果你的输入是一个整数序列(比如由字典索引编码的单词序列),你可以通过Embedding层嵌入这些整数标记。方法如下:#Defineaninputsequenceandprocessit.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]#Setupthedecoder,使用`encoder_states`asinitialstate.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)#Definethemodelthatwillturn#`encoder_input_data`&`decoder_input_data`进入`decoder_target_data`model=Model([encoder_inputs,decoder_inputs],decoder_outputs)#Compile&runtrainingmodel.compile(optimizer='rmsprop',loss='categorical_crossentropy')#Notethat`decoder_target_data`needstobeone-hotencoded,#ratherthansequencesofintegerslike`decoder_input_data`!model.fit([encoder_input_data,decoder_input_data],decoder_target_data,batch_sizebatch_size=batch_size,epochsepochs=epochs,validation_split=0.2)3.不想使用“teacherforcing”怎么办?在某些情况下,teacherforcing可能无法使用,因为您无法获得完整的目标序列,例如,在线训练非常长的句子,则无法对输入-目标语言对进行缓冲。在这种情况下,您可以通过将解码器的预测重新注入解码器输入来进行训练,就像我们为推理所做的那样。你你可以通过硬编码输出再(输出重新注册循环)的的模型达到达到达到:fromkeras.layersimportlambdafromkerasimportbackendask#firstpartischinsungheNgedEncoder_Inputs=input=input(none,nume_encent_encent_encent_encent_Encent_Eccens_Eccens),state_c=编码器(encoder_inputs)states=[state_h,state_c]#Setupthedecoder,whichwillonlyprocessonetimestepatatime.decoder_inputs=Input(shape=(1,num_decoder_tokens))decoder_lstm=LSTM(latent_dim,return_sequences=True,return_state=True)decoder_dense=Dense_coderen(kentonum_de,activation='softmax')all_outputs=[]inputs=decoder_inputsfor_inrange(max_decoder_seq_length):#Runthedecoderononetimestepoutputs,state_h,state_c=decoder_lstm(inputs,initial_state=states)outputs=decoder_dense(输出)#Storethecurrentprediction(wewillconcatenateallpredictionslater)endall(outputs).app#Reinjecttheoutputsasinputsforthenextloopiteration#aswellasupdatethestatesinputs=outputsstates=[state_h,state_c]#Concatenateallpredictionsdecoder_outputs=Lambda(lambdax:K.concatenate(x,axis=1))(all_outputs)#Defineandcompilemodelaspreviouslymodel=模型([encoder_inputs,decoder_inputs],decoder_outputs)model.compile(optimizer='rmsprop',loss='categorical_crossentropy')#准备只包含起始字符的解码器输入数据#注意我们本可以使模型保持不变硬编码decoder_input_data=np.zeros((num_samples,1,num_decoder_tokens))decoder_input_data[:,0,target_token_index['t']]=1。,epochsepochs=epochs,validation_split=0.2)原文:https://blog.keras.io/a-ten-minute-introduction-to-sequence-to-sequence-learning-in-keras.html【本文是一篇专栏组织《机器之心》原译,微信公众号“机器之心(id:almosthuman2014)”】点此查看作者更多好文