精华内容
下载资源
问答
  • 利用机器翻译领域的Seq2Seq模型,对影响港口进出货物量的多种因素进行建模.Seq2Seq模型可以反映进出口货物量在时间维度上的变化规律,并且可以刻画天气、节假日等外部因素的影响,从而进行精准预测.Seq2Seq模型包含...
  • seq2seq 这是Tensorflow 2seq2seq模型结构。 有三种模型架构,RNNSeq2Seq,RNNSeq2SeqWithAttention,TransformerSeq2Seq。 该存储库包含训练,评估,推断,转换为保存的模型格式脚本。 火车 例子 您可以通过运行...
  • Keras实现Seq2Seq预测模型

    千次阅读 热门讨论 2019-04-17 14:55:36
    一个基于keras实现seq2seq(Encoder-Decoder)的序列预测例子 序列预测问题描述: 输入序列为随机产生的整数序列,目标序列是对输入序列前三个元素进行反转后的序列,当然这只是我们自己定义的一种形式,可以...

    一个基于keras实现seq2seq(Encoder-Decoder)的序列预测例子

    NLP中常见任务的开源解决方案、数据集、工具、学习资料

    • 序列预测问题描述:
    • 输入序列为随机产生的整数序列,目标序列是对输入序列前三个元素进行反转后的序列,当然这只是我们自己定义的一种形式,可以自定义更复杂的场景。
    输入序列		                目标序列
    [13, 28, 18, 7, 9, 5]		[18, 28, 13]
    [29, 44, 38, 15, 26, 22]	[38, 44, 29]
    [27, 40, 31, 29, 32, 1]		[31, 40, 27]
    ...
    
    • 输入序列与目标序列的构造及向量化
    from numpy import array
    from numpy import argmax
    from keras.utils import to_categorical
     
    # 随机产生在(1,n_features)区间的整数序列,序列长度为n_steps_in
    def generate_sequence(length, n_unique):
    	return [randint(1, n_unique-1) for _ in range(length)]
     
    # 构造LSTM模型输入需要的训练数据
    def get_dataset(n_in, n_out, cardinality, n_samples):
    	X1, X2, y = list(), list(), list()
    	for _ in range(n_samples):
    		# 生成输入序列
    		source = generate_sequence(n_in, cardinality)
    		# 定义目标序列,这里就是输入序列的前三个数据
    		target = source[:n_out]
    		target.reverse()
    		# 向前偏移一个时间步目标序列
    		target_in = [0] + target[:-1]
    		# 直接使用to_categorical函数进行on_hot编码
    		src_encoded = to_categorical(source, num_classes=cardinality)
    		tar_encoded = to_categorical(target, num_classes=cardinality)
    		tar2_encoded = to_categorical(target_in, num_classes=cardinality)
    		
    		X1.append(src_encoded)
    		X2.append(tar2_encoded)
    		y.append(tar_encoded)
    	return array(X1), array(X2), array(y)
     
    # one_hot解码
    def one_hot_decode(encoded_seq):
    	return [argmax(vector) for vector in encoded_seq]
     
    # 输入参数
    n_features = 50 + 1
    n_steps_in = 6
    n_steps_out = 3
    # 生成处理后的输入序列与目标序列,这里测试产生了一个序列样本
    X1, X2, y = get_dataset(n_steps_in, n_steps_out, n_features, 1)
    print(X1.shape, X2.shape, y.shape)
    print('X1=%s, X2=%s, y=%s' % (one_hot_decode(X1[0]), one_hot_decode(X2[0]), one_hot_decode(y[0])))
    
    # 结果如下:
    (1, 6, 51) (1, 3, 51) (1, 3, 51)
    X1=[32, 16, 12, 34, 25, 24], X2=[0, 12, 16], y=[12, 16, 32]
    
    • 构造Seq2Seq模型,参考官网 github
    # 构造Seq2Seq训练模型model, 以及进行新序列预测时需要的的Encoder模型:encoder_model 与Decoder模型:decoder_model
    def define_models(n_input, n_output, n_units):
    	# 训练模型中的encoder
    	encoder_inputs = Input(shape=(None, n_input))
    	encoder = LSTM(n_units, return_state=True)
    	encoder_outputs, state_h, state_c = encoder(encoder_inputs)
    	encoder_states = [state_h, state_c]   #仅保留编码状态向量
    	# 训练模型中的decoder
    	decoder_inputs = Input(shape=(None, n_output))
    	decoder_lstm = LSTM(n_units, return_sequences=True, return_state=True)
    	decoder_outputs, _, _ = decoder_lstm(decoder_inputs, initial_state=encoder_states)
    	decoder_dense = Dense(n_output, activation='softmax')
    	decoder_outputs = decoder_dense(decoder_outputs)
    	model = Model([encoder_inputs, decoder_inputs], decoder_outputs)
    	# 新序列预测时需要的encoder
    	encoder_model = Model(encoder_inputs, encoder_states)
    	# 新序列预测时需要的decoder
    	decoder_state_input_h = Input(shape=(n_units,))
    	decoder_state_input_c = Input(shape=(n_units,))
    	decoder_states_inputs = [decoder_state_input_h, decoder_state_input_c]
    	decoder_outputs, state_h, state_c = decoder_lstm(decoder_inputs, initial_state=decoder_states_inputs)
    	decoder_states = [state_h, state_c]
    	decoder_outputs = decoder_dense(decoder_outputs)
    	decoder_model = Model([decoder_inputs] + decoder_states_inputs, [decoder_outputs] + decoder_states)
        # 返回需要的三个模型
    	return model, encoder_model, decoder_model
    
    • 序列预测过程
    def predict_sequence(infenc, infdec, source, n_steps, cardinality):
    	# 输入序列编码得到编码状态向量
    	state = infenc.predict(source)
    	# 初始目标序列输入:通过开始字符计算目标序列第一个字符,这里是0
    	target_seq = array([0.0 for _ in range(cardinality)]).reshape(1, 1, cardinality)
    	# 输出序列列表
    	output = list()
    	for t in range(n_steps):
    		# predict next char
    		yhat, h, c = infdec.predict([target_seq] + state)
    		# 截取输出序列,取后三个
    		output.append(yhat[0,0,:])
    		# 更新状态
    		state = [h, c]
    		# 更新目标序列(用于下一个词预测的输入)
    		target_seq = yhat
    	return array(output)
    
    • 模型结果评估
    # 评估模型效果
    total, correct = 100, 0
    for _ in range(total):
    	X1, X2, y = get_dataset(n_steps_in, n_steps_out, n_features, 1)
    	target = predict_sequence(infenc, infdec, X1, n_steps_out, n_features)
    	if array_equal(one_hot_decode(y[0]), one_hot_decode(target)):
    		correct += 1
    print('Accuracy: %.2f%%' % (float(correct)/float(total)*100.0))
    
    
    • 模型完整代码
    from numpy import array
    from numpy import argmax
    from numpy import array_equal
    from keras.utils import to_categorical
    from keras.models import Model
    from keras.layers import Input
    from keras.layers import LSTM
    from keras.layers import Dense
     
    # 随机产生在(1,n_features)区间的整数序列,序列长度为n_steps_in
    def generate_sequence(length, n_unique):
    	return [randint(1, n_unique-1) for _ in range(length)]
     
    # 构造LSTM模型输入需要的训练数据
    def get_dataset(n_in, n_out, cardinality, n_samples):
    	X1, X2, y = list(), list(), list()
    	for _ in range(n_samples):
    		# 生成输入序列
    		source = generate_sequence(n_in, cardinality)
    		# 定义目标序列,这里就是输入序列的前三个数据
    		target = source[:n_out]
    		target.reverse()
    		# 向前偏移一个时间步目标序列
    		target_in = [0] + target[:-1]
    		# 直接使用to_categorical函数进行on_hot编码
    		src_encoded = to_categorical(source, num_classes=cardinality)
    		tar_encoded = to_categorical(target, num_classes=cardinality)
    		tar2_encoded = to_categorical(target_in, num_classes=cardinality)
    		
    		X1.append(src_encoded)
    		X2.append(tar2_encoded)
    		y.append(tar_encoded)
    	return array(X1), array(X2), array(y)
     
    # 构造Seq2Seq训练模型model, 以及进行新序列预测时需要的的Encoder模型:encoder_model 与Decoder模型:decoder_model
    def define_models(n_input, n_output, n_units):
    	# 训练模型中的encoder
    	encoder_inputs = Input(shape=(None, n_input))
    	encoder = LSTM(n_units, return_state=True)
    	encoder_outputs, state_h, state_c = encoder(encoder_inputs)
    	encoder_states = [state_h, state_c]   #仅保留编码状态向量
    	# 训练模型中的decoder
    	decoder_inputs = Input(shape=(None, n_output))
    	decoder_lstm = LSTM(n_units, return_sequences=True, return_state=True)
    	decoder_outputs, _, _ = decoder_lstm(decoder_inputs, initial_state=encoder_states)
    	decoder_dense = Dense(n_output, activation='softmax')
    	decoder_outputs = decoder_dense(decoder_outputs)
    	model = Model([encoder_inputs, decoder_inputs], decoder_outputs)
    	# 新序列预测时需要的encoder
    	encoder_model = Model(encoder_inputs, encoder_states)
    	# 新序列预测时需要的decoder
    	decoder_state_input_h = Input(shape=(n_units,))
    	decoder_state_input_c = Input(shape=(n_units,))
    	decoder_states_inputs = [decoder_state_input_h, decoder_state_input_c]
    	decoder_outputs, state_h, state_c = decoder_lstm(decoder_inputs, initial_state=decoder_states_inputs)
    	decoder_states = [state_h, state_c]
    	decoder_outputs = decoder_dense(decoder_outputs)
    	decoder_model = Model([decoder_inputs] + decoder_states_inputs, [decoder_outputs] + decoder_states)
        # 返回需要的三个模型
    	return model, encoder_model, decoder_model
     
    def predict_sequence(infenc, infdec, source, n_steps, cardinality):
    	# 输入序列编码得到编码状态向量
    	state = infenc.predict(source)
    	# 初始目标序列输入:通过开始字符计算目标序列第一个字符,这里是0
    	target_seq = array([0.0 for _ in range(cardinality)]).reshape(1, 1, cardinality)
    	# 输出序列列表
    	output = list()
    	for t in range(n_steps):
    		# predict next char
    		yhat, h, c = infdec.predict([target_seq] + state)
    		# 截取输出序列,取后三个
    		output.append(yhat[0,0,:])
    		# 更新状态
    		state = [h, c]
    		# 更新目标序列(用于下一个词预测的输入)
    		target_seq = yhat
    	return array(output)
     
    # one_hot解码
    def one_hot_decode(encoded_seq):
    	return [argmax(vector) for vector in encoded_seq]
     
    # 参数设置
    n_features = 50 + 1
    n_steps_in = 6
    n_steps_out = 3
    # 定义模型
    train, infenc, infdec = define_models(n_features, n_features, 128)
    train.compile(optimizer='adam', loss='categorical_crossentropy', metrics=['acc'])
    # 生成训练数据
    X1, X2, y = get_dataset(n_steps_in, n_steps_out, n_features, 100000)
    print(X1.shape,X2.shape,y.shape)
    # 训练模型
    train.fit([X1, X2], y, epochs=1)
    # 评估模型效果
    total, correct = 100, 0
    for _ in range(total):
    	X1, X2, y = get_dataset(n_steps_in, n_steps_out, n_features, 1)
    	target = predict_sequence(infenc, infdec, X1, n_steps_out, n_features)
    	if array_equal(one_hot_decode(y[0]), one_hot_decode(target)):
    		correct += 1
    print('Accuracy: %.2f%%' % (float(correct)/float(total)*100.0))
    # 查看预测结果
    for _ in range(10):
    	X1, X2, y = get_dataset(n_steps_in, n_steps_out, n_features, 1)
    	target = predict_sequence(infenc, infdec, X1, n_steps_out, n_features)
    	print('X=%s y=%s, yhat=%s' % (one_hot_decode(X1[0]), one_hot_decode(y[0]), one_hot_decode(target)))
    
    • 结果展示
    100000/100000 [==============================] - 28s 284us/step - loss: 0.6441 - acc: 0.7941
    Accuracy: 99.00%
    X=[16, 14, 28, 22, 9, 17] y=[28, 14, 16], yhat=[28, 14, 16]
    X=[48, 36, 32, 49, 13, 5] y=[32, 36, 48], yhat=[32, 36, 48]
    X=[27, 37, 6, 50, 30, 16] y=[6, 37, 27], yhat=[6, 37, 27]
    X=[7, 7, 4, 45, 6, 16] y=[4, 7, 7], yhat=[4, 7, 7]
    X=[4, 25, 33, 34, 21, 12] y=[33, 25, 4], yhat=[33, 25, 4]
    X=[44, 44, 9, 35, 5, 16] y=[9, 44, 44], yhat=[9, 44, 44]
    X=[20, 29, 3, 7, 17, 29] y=[3, 29, 20], yhat=[3, 29, 20]
    X=[38, 45, 5, 35, 46, 21] y=[5, 45, 38], yhat=[5, 45, 38]
    X=[16, 10, 48, 27, 39, 3] y=[48, 10, 16], yhat=[48, 10, 16]
    X=[16, 45, 7, 45, 29, 25] y=[7, 45, 16], yhat=[45, 45, 16]
    
    • 参考文献
    • https://blog.keras.io/a-ten-minute-introduction-to-sequence-to-sequence-learning-in-keras.html
    • https://machinelearningmastery.com/define-encoder-decoder-sequence-sequence-model-neural-machine-translation-keras/
    • https://machinelearningmastery.com/return-sequences-and-return-states-for-lstms-in-keras/
    • https://machinelearningmastery.com/develop-encoder-decoder-model-sequence-sequence-prediction-keras/
    展开全文
  • 使用seq2seq预测当天的股票价格

    千次阅读 热门讨论 2018-09-28 19:56:52
    1.tensorflow中的seq2seq 在tensorflow中有两套seq2seq的接口。一套是tensorflow1.0版本之前的旧接口,在tf.contrib.legacy_seq2seq下;另一套为tensorflow1.0版本之后推出的新接口,在tf.contrib.seq2seq下。本文...

    1.tensorflow中的seq2seq

    在tensorflow中有两套seq2seq的接口。一套是tensorflow1.0版本之前的旧接口,在tf.contrib.legacy_seq2seq下;另一套为tensorflow1.0版本之后推出的新接口,在tf.contrib.seq2seq下。本文使用的是seq2seq的旧接口,旧接口的功能相对简单,是静态的展开模式,新接口功能更加强大,使用的是动态展开网络模型。想要了解新接口的使用情况,推荐这个网址https://blog.csdn.net/thriving_fcl/article/details/74165062

    2.实力描述

    使用seq2seq模式对某个股票前段时间的数据训练学习,拟合特征,从而达到第二天预测股票价格的效果。

    3.导入股票数据

    首先得准备一个股票数据,本人使用的是参考书中所提供的数据,想要数据集的可以在私信我。

    添加股票载入函数loadstock,实例中将收盘价格载入内存做样本生成。

    import random
    import math
            
    import tensorflow as tf 
    import numpy as np
    import matplotlib.pyplot as plt  
    
    
    import pandas as pd
    pd.options.mode.chained_assignment = None  # default='warn'
    def loadstock(window_size):
        names = ['date',
             'code',
             'name',
             'Close',
             'top_price',
             'low_price',
             'opening_price',
             'bef_price',
             'floor_price',
             'floor',
             'exchange',
             'Volume',
             'amount',
             '总市值',
             '流通市值']
        data = pd.read_csv('600000.csv', names=names, header=None,encoding = "gbk")
        #predictor_names = ["Close",'top_price',"low_price","opening_price"]
        predictor_names = ["Close"]
        training_features = np.asarray(data[predictor_names], dtype = "float32")
        kept_values = training_features[1000:]
    
        X = []
        Y = []
        for i in range(len(kept_values) - window_size * 2):#  x ;前window_size,y后window_size
            X.append(kept_values[i:i + window_size])
            Y.append(kept_values[i + window_size:i + window_size * 2])
    
        X = np.reshape(X,[-1,window_size,len(predictor_names)])
        Y = np.reshape(Y,[-1,window_size,len(predictor_names)])
        print(np.shape(X))
    
        return X, Y

    3.生成样本

    X_train = []
    Y_train = []
    X_test = []
    Y_test = []
    
    
    def generate_data(isTrain, batch_size):        
        # 用前40个样本预测后40个样本.
        
        seq_length = 40   
        seq_length_test = 80
    
        global Y_train
        global X_train
        global X_test
        global Y_test
        # First load, with memoization:
        if len(Y_train) == 0:       
            X, Y= loadstock( window_size=seq_length)
            #X, Y = normalizestock(X, Y)
    
            # Split 80-20:
            X_train = X[:int(len(X) * 0.8)]
            Y_train = Y[:int(len(Y) * 0.8)]
    
    
        if len(Y_test) == 0:
            X, Y  = loadstock( window_size=seq_length_test)
            #X, Y = normalizestock(X, Y)
    
            # Split 80-20:
            X_test = X[int(len(X) * 0.8):]
            Y_test = Y[int(len(Y) * 0.8):]
    
        if isTrain:
            return do_generate_x_y(X_train, Y_train, batch_size)
        else:
            return do_generate_x_y(X_test,  Y_test,  batch_size)
    
    
    def do_generate_x_y(X, Y, batch_size):
        assert X.shape == Y.shape, (X.shape, Y.shape)
        idxes = np.random.randint(X.shape[0], size=batch_size)
        X_out = np.array(X[idxes]).transpose((1, 0, 2))
        Y_out = np.array(Y[idxes]).transpose((1, 0, 2))
        return X_out, Y_out

    4.设置参数及网络结构

    网络模型定义为2层循环网络,每层12个GRUcell,使用MultiRNNcell将多个RNN连到一块,一起传入basic_rnn_seq2seq中。生成结果每时刻有12个grucell输出,所以还需要通过循环在每个时刻下加一个全连接层,将其转为输出维度为1的节点。

    sample_now, sample_f = generate_data(isTrain=True, batch_size=3)
    print("training examples : ")
    print(sample_now.shape)
    print("(seq_length, batch_size, output_dim)")
    
    
    seq_length = sample_now.shape[0]
    batch_size = 100
    
    output_dim = input_dim = sample_now.shape[-1]
    hidden_dim = 12 #12个grucell
    layers_num = 2#两层循环网络
    
    # Optmizer:
    learning_rate =0.04#学习率设为0.04
    #nb_iters = 100
    nb_iters = 100000
    lambda_l2_reg = 0.003  # L2调节因子设为0.003
    
    tf.reset_default_graph()
    
    
    
    encoder_input = []
    expected_output = []
    decode_input =[]
    for i in range(seq_length):
        encoder_input.append( tf.placeholder(tf.float32, shape=( None, input_dim)) )
        expected_output.append( tf.placeholder(tf.float32, shape=( None, output_dim)) )
        decode_input.append( tf.placeholder(tf.float32, shape=( None, input_dim)) )
    
        
    tcells = []
    for i in range(layers_num):
        tcells.append(tf.contrib.rnn.GRUCell(hidden_dim))
    Mcell = tf.contrib.rnn.MultiRNNCell(tcells)
    
    dec_outputs, dec_memory = tf.contrib.legacy_seq2seq.basic_rnn_seq2seq(encoder_input,decode_input,Mcell)
    
    reshaped_outputs = []
    for ii in dec_outputs :
        reshaped_outputs.append( tf.contrib.layers.fully_connected(ii,output_dim,activation_fn=None))
    
    
    # 计算l2的loss值
    output_loss = 0
    for _y, _Y in zip(reshaped_outputs, expected_output):
        output_loss += tf.reduce_mean( tf.pow(_y - _Y, 2) )
       
    # 求正则化损失
    reg_loss = 0
    for tf_var in tf.trainable_variables():
        if not ("fully_connected" in tf_var.name ):
            #print(tf_var.name)
            reg_loss += tf.reduce_mean(tf.nn.l2_loss(tf_var))
    
    loss = output_loss + lambda_l2_reg * reg_loss
    train_op = tf.train.AdamOptimizer(learning_rate).minimize(loss)   
    
    

    4.训练样本

    在session中将训练和测试单独封装成了两个函数,在train_batch函数里先取指定批次数据,通过循环增来将数据添加到encoder_input和expect_output列表中。

    sess = tf.InteractiveSession()
            
    def train_batch(batch_size):
    
        X, Y = generate_data(isTrain=True, batch_size=batch_size)
        feed_dict = {encoder_input[t]: X[t] for t in range(len(encoder_input))}
        feed_dict.update({expected_output[t]: Y[t] for t in range(len(expected_output))})
    
        c =np.concatenate(( [np.zeros_like(Y[0])],Y[:-1]),axis = 0)
    
        feed_dict.update({decode_input[t]: c[t] for t in range(len(c))})
    
        _, loss_t = sess.run([train_op, loss], feed_dict)
        return loss_t
    
    
    def test_batch(batch_size):
        X, Y = generate_data(isTrain=True, batch_size=batch_size)
        feed_dict = {encoder_input[t]: X[t] for t in range(len(encoder_input))}
        feed_dict.update({expected_output[t]: Y[t] for t in range(len(expected_output))})
        c =np.concatenate(( [np.zeros_like(Y[0])],Y[:-1]),axis = 0)#来预测最后一个序列
        feed_dict.update({decode_input[t]: c[t] for t in range(len(c))})    
        output_lossv,reg_lossv,loss_t = sess.run([output_loss,reg_loss,loss], feed_dict)
        print("-----------------")    
        print(output_lossv,reg_lossv)
        return loss_t
    
    
    # Training
    train_losses = []
    test_losses = []
    
    sess.run(tf.global_variables_initializer())
    for t in range(nb_iters + 1):
        train_loss = train_batch(batch_size)
        train_losses.append(train_loss)
        if t % 50 == 0:
            test_loss = test_batch(batch_size)
            test_losses.append(test_loss)
            print("Step {}/{}, train loss: {}, \tTEST loss: {}".format(t,nb_iters, train_loss, test_loss))
    print("Fin. train loss: {}, \tTEST loss: {}".format(train_loss, test_loss))        
            

    5.准备可视化数据

    在可视化部分,取时间序列2倍样本,前一部分用于输入模型,产生最后一天的预测值,后一部分用于数据显示,对比真实值与预测值之间差距。

    # 测试
    nb_predictions = 5
    print("visualize {} predictions data:".format(nb_predictions))
    
    preout =[]
    X, Y = generate_data(isTrain=False, batch_size=nb_predictions)
    print(np.shape(X),np.shape(Y))
    for tt in  range(seq_length):
        feed_dict = {encoder_input[t]: X[t+tt] for t in range(seq_length)}
        feed_dict.update({expected_output[t]: Y[t+tt] for t in range(len(expected_output))})
        c =np.concatenate(( [np.zeros_like(Y[0])],Y[tt:seq_length+tt-1]),axis = 0)  #从前15个的最后一个开始预测  
    
        feed_dict.update({decode_input[t]: c[t] for t in range(len(c))})
        outputs = np.array(sess.run([reshaped_outputs], feed_dict)[0])
        preout.append(outputs[-1])
    
    print(np.shape(preout))#将每个未知预测值收集起来准备显示出来。
    preout =np.reshape(preout,[seq_length,nb_predictions,output_dim])
    

    6.图画显示数据

    for j in range(nb_predictions):
        plt.figure(figsize=(12, 3))
    
        for k in range(output_dim):
            past = X[:, j, k]
            expected = Y[seq_length-1:, j, k]#对应预测值的打印
    
            pred = preout[:, j, k]
    
            label1 = "past" if k == 0 else "_nolegend_"
            label2 = "future" if k == 0 else "_nolegend_"
            label3 = "Pred" if k == 0 else "_nolegend_"
            plt.plot(range(len(past)), past, "o--b", label=label1)
            plt.plot(range(len(past), len(expected) + len(past)),
                     expected, "x--b", label=label2)
            plt.plot(range(len(past), len(pred) + len(past)),
                     pred, "o--y", label=label3)
    
        plt.legend(loc='best')
        plt.title("Predictions vs. future")
        plt.show()

    7.结果显示

                                                                                   loss函数图

                                                                            股票示例结果图

    序列80—120之间的点蓝色的*代表真实值,灰色的点代表预测值,总体预测结果与真实值相差不大

    参考书籍:  李金洪老师的 深度学习之tensorflow

    展开全文
  • 最近有个任务:利用 RNN...本文使用了 Seq2Seq 模型,输入为5个中文字词,输出为一个中文字词。 目录 关于RNN 语料预处理 搭建数据集 搭建模型 训练模型 测试模型 保存/加载模型 1.关于R...

    最近有个任务:利用 RNN 进行句子补全,即给定一个不完整的句子,预测其后续的字词。
    本文使用了 Seq2Seq 模型,输入为5个中文字词,输出为一个中文字词。

    目录

    1. 关于RNN

    2. 语料预处理

    3. 搭建数据集

    4. 搭建模型

    5. 训练模型

    6. 测试模型

    7. 保存/加载模型

     

    1.关于RNN

    自被提出以来,循环神经网络(Recurrent Neural Networks,RNN) 在 NLP 领域取得了巨大的成功与广泛的应用,也由此催生出了许多新的变体与网络结构。由于网上有众多资料,在此我也只做简单的讲解了。
    首先,讲讲 RNN cell 及其变体:
    (1) vallina RNN cell

    不同于常见的神经网络结构,RNN 的输入为时序输入,每一时刻的输入对神经元的隐状态产生影响,从而影响后续所有时刻的输出。
    其中,隐藏层的公式如下所示:
    O_{t} = g(V* S_t), S_t = f(U*X_t+W*S_{t-1})Ot=g(V∗St),St=f(U∗Xt+W∗St−1)

    (2) LSTM cell

    LSTM(Long short-term memory,长短期记忆)极大程度的解决了长序列训练过程中的梯度消失和梯度爆炸问题。

    (3) GRU cell
    GRU(Gate Recurrent Unit)与 LSTM 一样,也极大程度的解决了长序列训练过程中的梯度消失和梯度爆炸问题。但是,与 LSTM 相比,GRU 所需要的计算资源更小,往往工程实现时更倾向于使用 GRU。

    接着,讲讲网络结构:

    (1) 常见结构:

    (2) Bi-directional RNN

    (3) Deep Bi-directional RNN

    (4) Seq2Seq

    (5) Attention

    参考资料:

     

    2.语料预处理

    由于这次使用的语料为中文语料,自然需要对其进行分词,并构造词典。
    首先,收集所用的句子,利用 jieba 库,对每个句子进行分词,并将所得结果加入 word_set 中。
    接着,对 word_set 中的所有字词构建统计词典。

    代码:

    import osimport jsonimport jiebaimport numpy as npimport pandas as pdimport matplotlib.pyplot as pltimport torchimport torch.nn as nnimport torch.nn.functional as Fimport torch.optim as optimfrom torch.autograd import Variableimport torchvisionimport torchvision.datasets as datasetsimport torchvision.transforms as transformsfrom torch.utils.data import DataLoader,Dataset# Set Hyper ParametersLR = 0.005EPOCH = 100BATCH_SIZE = 1Sentence_Num = 100Embedding_Dim = None# Bulid Vocabsentence_set = []								# 收集所用到的文本句子for index in range(Sentence_Num):
        with open('../../Corpus/CAIL2018/'+str(index)+'.txt','r',encoding='UTF-8') as f:
            sentence_set.append(f.read().replace('\n', '').replace('\r', '').replace(',', ' ').replace('。', ' ').replace(':', ' ').replace('  ', ' ').lower())
            word_set = set()								# 利用jieba库进行中文分词for sentence in sentence_set:
        words = jieba.lcut(sentence)
        word_set.update(words)
        word_to_ix = {'SOS':0, 'EOS':1, 'UNK':2}		# 'SOS': start of sentencexix_to_word = {0:'SOS', 1:'EOS', 2:'UNK'}		# 'EOS': end of sentence
    												# 'UNK': unknown tokenfor word in word_set:							# 构建词典,注意:word_to_ix用于对字词进行编号,ix_to_word用于将模型的输出转化为字词
        if word not in word_to_ix:
            word_to_ix[word] = len(word_to_ix)
            ix_to_word[len(ix_to_word)] = word
    
    Embedding_Dim = len(word_to_ix)        
            with open('./Vocab.txt','w',encoding='UTF-8') as f: # 保存词典
        for vocab in word_to_ix.items():
            f.write(vocab[0]+'   '+str(vocab[1])+'\n')as npimport pandas as pdimport matplotlib.pyplot as pltimport torchimport torch.nn as nnimport torch.nn.functional as Fimport torch.optim as optimfrom torch.autograd import Variableimport torchvisionimport torchvision.datasets as datasetsimport torchvision.transforms as transformsfrom torch.utils.data import DataLoader,Dataset# Set Hyper ParametersLR = 0.005EPOCH = 100BATCH_SIZE = 1Sentence_Num = 100Embedding_Dim = None# Bulid Vocabsentence_set = []								# 收集所用到的文本句子for index in range(Sentence_Num):
        with open('../../Corpus/CAIL2018/'+str(index)+'.txt','r',encoding='UTF-8') as f:
            sentence_set.append(f.read().replace('\n', '').replace('\r', '').replace(',', ' ').replace('。', ' ').replace(':', ' ').replace('  ', ' ').lower())
            word_set = set()								# 利用jieba库进行中文分词for sentence in sentence_set:
        words = jieba.lcut(sentence)
        word_set.update(words)
        word_to_ix = {'SOS':0, 'EOS':1, 'UNK':2}		# 'SOS': start of sentencexix_to_word = {0:'SOS', 1:'EOS', 2:'UNK'}		# 'EOS': end of sentence
    												# 'UNK': unknown tokenfor word in word_set:							# 构建词典,注意:word_to_ix用于对字词进行编号,ix_to_word用于将模型的输出转化为字词
        if word not in word_to_ix:
            word_to_ix[word] = len(word_to_ix)
            ix_to_word[len(ix_to_word)] = word
    
    Embedding_Dim = len(word_to_ix)        
            with open('./Vocab.txt','w',encoding='UTF-8') as f: # 保存词典
        for vocab in word_to_ix.items():
            f.write(vocab[0]+'   '+str(vocab[1])+'\n')

    参考资料:

     

    3.搭建数据集

    由于所使用的中文文本并无数据集格式,故我们需要自己制作数据集。
    注意,代码中的 bulid_one_hot 并非生成 one-hot 向量。这是因为模型中使用了 nn.Embedding() ,它会初始一个矩阵,相当于我们模型再训练过程中,顺便训练了一个 word embedding matrix。
    至于如何使用该函数进行 word embedding ,大家可以查阅本小节的参考资料。

    代码:

    # Bulid Datasetdef bulid_one_hot(word,word_dict):
        if word in word_dict:
            return torch.LongTensor([word_dict[word]])
        return torch.LongTensor([word_dict['UNK']])
    
       class MyDataset(Dataset):
        def __init__(self, words, labels, transform=None, target_transform=None): 
            self.words = words
            self.labels = labels
            self.transform = transform
            self.target_transform = target_transform        
        def __getitem__(self, index):
            words, labels = self.words[index], self.labels[index]
            if self.transform is not None:
                words = [self.transform(word) for word in words]
            if self.target_transform is not None:
                labels = self.target_transform(labels)
            return words, labels    def __len__(self): 
            return len(self.labels)train_words, train_labels, test_words, test_labels = [], [], [], []for i in range(int(0.9*Sentence_Num)):
        sentence = sentence_set[i] 
        words =  jieba.lcut(sentence)
        words.insert(0,'SOS')
        words.append('EOS')
        words = [bulid_one_hot(word,word_to_ix) for word in words]
    
        for j in range(0,len(words),6):
            if j+6 >= len(words):
                break
            train_words.append(words[j:j+5])
            train_labels.append(words[j+5])
        for i in range(int(0.9*Sentence_Num),Sentence_Num):
        sentence = sentence_set[i]
        words =  jieba.lcut(sentence)
        words.insert(0,'SOS')
        words.append('EOS')
        words = [bulid_one_hot(word,word_to_ix) for word in words]
        
        for j in range(0,len(words),6):
            if j+6 >= len(words):
                break
            test_words.append(words[j:j+5])
            test_labels.append(words[j+5])
        trans, target_trans = None, None # transforms.ToTensor(), transforms.ToTensor()train_set = MyDataset(train_words, train_labels, trans, target_trans)train_loader = DataLoader(dataset=train_set, batch_size=BATCH_SIZE)test_set = MyDataset(test_words, test_labels, trans, target_trans)test_loader = DataLoader(dataset=test_set, batch_size=BATCH_SIZE)if word in word_dict:
            return torch.LongTensor([word_dict[word]])
        return torch.LongTensor([word_dict['UNK']])
    
       class MyDataset(Dataset):
        def __init__(self, words, labels, transform=None, target_transform=None): 
            self.words = words
            self.labels = labels
            self.transform = transform
            self.target_transform = target_transform        
        def __getitem__(self, index):
            words, labels = self.words[index], self.labels[index]
            if self.transform is not None:
                words = [self.transform(word) for word in words]
            if self.target_transform is not None:
                labels = self.target_transform(labels)
            return words, labels    def __len__(self): 
            return len(self.labels)train_words, train_labels, test_words, test_labels = [], [], [], []for i in range(int(0.9*Sentence_Num)):
        sentence = sentence_set[i] 
        words =  jieba.lcut(sentence)
        words.insert(0,'SOS')
        words.append('EOS')
        words = [bulid_one_hot(word,word_to_ix) for word in words]
    
        for j in range(0,len(words),6):
            if j+6 >= len(words):
                break
            train_words.append(words[j:j+5])
            train_labels.append(words[j+5])
        for i in range(int(0.9*Sentence_Num),Sentence_Num):
        sentence = sentence_set[i]
        words =  jieba.lcut(sentence)
        words.insert(0,'SOS')
        words.append('EOS')
        words = [bulid_one_hot(word,word_to_ix) for word in words]
        
        for j in range(0,len(words),6):
            if j+6 >= len(words):
                break
            test_words.append(words[j:j+5])
            test_labels.append(words[j+5])
        trans, target_trans = None, None # transforms.ToTensor(), transforms.ToTensor()train_set = MyDataset(train_words, train_labels, trans, target_trans)train_loader = DataLoader(dataset=train_set, batch_size=BATCH_SIZE)test_set = MyDataset(test_words, test_labels, trans, target_trans)test_loader = DataLoader(dataset=test_set, batch_size=BATCH_SIZE)

    参考资料:

     

    4.搭建模型

    采用 GRU 结构构建 Seq2Seq 模型,其中,loss function 为 nn.CrossEntropyLoss(), optimizer 为 optim.SGD()。
    注意,pytorch 中采用 nn.CrossEntropyLoss(),对输入与输出有格式要求,请查阅本小节的参考资料。

    代码:

    # Bulid Seq2Seq Modelclass Encoder(nn.Module):
        def __init__(self, input_size, hidden_size):
            super(Encoder, self).__init__()
            self.hidden_size = hidden_size
            self.embedding = nn.Embedding(input_size, hidden_size)     # 将one-hot向量embedding为词向量
            self.gru = nn.GRU(hidden_size, hidden_size)                # GRU的hidden layer的size与词向量的size一样,并非必须 
    
        def forward(self, input, hidden):
            embedded = self.embedding(input).view(1, 1, -1)            # RNN的输入格式为 (seq_len, batch, input_size)
            output = embedded
            output, hidden = self.gru(output, hidden)
            return output, hidden    def initHidden(self):
            return torch.zeros(1, 1, self.hidden_size)                 # 初始化Encoder的隐状态 class Decoder(nn.Module):
        def __init__(self, input_size, hidden_size, output_size):
            super(Decoder, self).__init__()
            self.hidden_size = hidden_size
            self.embedding = nn.Embedding(input_size, hidden_size)
            self.gru = nn.GRU(hidden_size, hidden_size)
            self.out = nn.Linear(hidden_size, output_size)
    
        def forward(self, input, hidden):
            output = self.embedding(input).view(1, 1, -1)
            output = F.relu(output)
            output, hidden = self.gru(output, hidden)
            output = self.out(output[0])
            return output, hidden    def initHidden(self):
            return torch.zeros(1, 1, self.hidden_size)class Seq2Seq(nn.Module):
        def __init__(self, encoder, decoder):
            super(Seq2Seq, self).__init__()
            self.encoder = encoder 
            self.decoder = decoder    def forward(self, inputs):
            encoder_hidden = self.encoder.initHidden()
            if torch.cuda.is_available():
                encoder_hidden = encoder_hidden.cuda()
    
            # encode
            for word in inputs:
                encoder_out, encoder_hidden = self.encoder(word, encoder_hidden)
            
            # decode
            decoder_hidden = encoder_hidden
            pred, decoder_hidden = self.decoder(inputs[-1], decoder_hidden)
            
            return pred
    
        
    encoder = Encoder(Embedding_Dim,1000)decoder = Decoder(Embedding_Dim,1000,Embedding_Dim)if torch.cuda.is_available():
        encoder = encoder.cuda()
        decoder = decoder.cuda()
        seq2seq = Seq2Seq(encoder,decoder)if torch.cuda.is_available():
        seq2seq = seq2seq.cuda()
        # Bulid loss function and optimizer loss_func = nn.CrossEntropyLoss()#encoder_optimizer = optim.SGD(encoder.parameters(), lr=LR, momentum=0.9)#decoder_optimizer = optim.SGD(decoder.parameters(), lr=LR, momentum=0.9)seq2seq_optimizer = optim.SGD(seq2seq.parameters(), lr=LR, momentum=0.9)def __init__(self, input_size, hidden_size):
            super(Encoder, self).__init__()
            self.hidden_size = hidden_size
            self.embedding = nn.Embedding(input_size, hidden_size)     # 将one-hot向量embedding为词向量
            self.gru = nn.GRU(hidden_size, hidden_size)                # GRU的hidden layer的size与词向量的size一样,并非必须 
    
        def forward(self, input, hidden):
            embedded = self.embedding(input).view(1, 1, -1)            # RNN的输入格式为 (seq_len, batch, input_size)
            output = embedded
            output, hidden = self.gru(output, hidden)
            return output, hidden    def initHidden(self):
            return torch.zeros(1, 1, self.hidden_size)                 # 初始化Encoder的隐状态 class Decoder(nn.Module):
        def __init__(self, input_size, hidden_size, output_size):
            super(Decoder, self).__init__()
            self.hidden_size = hidden_size
            self.embedding = nn.Embedding(input_size, hidden_size)
            self.gru = nn.GRU(hidden_size, hidden_size)
            self.out = nn.Linear(hidden_size, output_size)
    
        def forward(self, input, hidden):
            output = self.embedding(input).view(1, 1, -1)
            output = F.relu(output)
            output, hidden = self.gru(output, hidden)
            output = self.out(output[0])
            return output, hidden    def initHidden(self):
            return torch.zeros(1, 1, self.hidden_size)class Seq2Seq(nn.Module):
        def __init__(self, encoder, decoder):
            super(Seq2Seq, self).__init__()
            self.encoder = encoder 
            self.decoder = decoder    def forward(self, inputs):
            encoder_hidden = self.encoder.initHidden()
            if torch.cuda.is_available():
                encoder_hidden = encoder_hidden.cuda()
    
            # encode
            for word in inputs:
                encoder_out, encoder_hidden = self.encoder(word, encoder_hidden)
            
            # decode
            decoder_hidden = encoder_hidden
            pred, decoder_hidden = self.decoder(inputs[-1], decoder_hidden)
            
            return pred
    
        
    encoder = Encoder(Embedding_Dim,1000)decoder = Decoder(Embedding_Dim,1000,Embedding_Dim)if torch.cuda.is_available():
        encoder = encoder.cuda()
        decoder = decoder.cuda()
        seq2seq = Seq2Seq(encoder,decoder)if torch.cuda.is_available():
        seq2seq = seq2seq.cuda()
        # Bulid loss function and optimizer loss_func = nn.CrossEntropyLoss()#encoder_optimizer = optim.SGD(encoder.parameters(), lr=LR, momentum=0.9)#decoder_optimizer = optim.SGD(decoder.parameters(), lr=LR, momentum=0.9)seq2seq_optimizer = optim.SGD(seq2seq.parameters(), lr=LR, momentum=0.9)

    参考资料:

     

    5.训练模型

    代码:

    # Train Seq2Seq Modelfor epoch in range(EPOCH):  
        loss_sum = 0
        for step, (inputs, labels) in enumerate(train_loader):   
            # encoder_hidden = encoder.initHidden()
            label = torch.LongTensor((1,))
            label[0] = int(labels.data.numpy()[0])
    
            if torch.cuda.is_available():
                inputs = [word.cuda() for word in inputs]
                label = label.cuda()
                # encoder_hidden = encoder_hidden.cuda() 
                
            # forward
            pred = seq2seq(inputs)
            loss = loss_func(pred,label)
            
            # backward
            seq2seq_optimizer.zero_grad()
            loss.backward()
            seq2seq_optimizer.step()
            
            '''
            for word in inputs:
                encoder_out, encoder_hidden = encoder(word, encoder_hidden)
            decoder_hidden = encoder_hidden
            decoder_out, decoder_hidden = decoder(inputs[-1], decoder_hidden)
    
            loss = loss_func(decoder_out,label)
            
            #backward
            encoder_optimizer.zero_grad()
            decoder_optimizer.zero_grad()   
            loss.backward()                     
            encoder_optimizer.step()                  
            decoder_optimizer.step()
            '''
        
            loss_sum+= loss.data[0]
        print('Epoch: %2d train loss: %.4f' % (epoch, loss_sum))0
        for step, (inputs, labels) in enumerate(train_loader):   
            # encoder_hidden = encoder.initHidden()
            label = torch.LongTensor((1,))
            label[0] = int(labels.data.numpy()[0])
    
            if torch.cuda.is_available():
                inputs = [word.cuda() for word in inputs]
                label = label.cuda()
                # encoder_hidden = encoder_hidden.cuda() 
                
            # forward
            pred = seq2seq(inputs)
            loss = loss_func(pred,label)
            
            # backward
            seq2seq_optimizer.zero_grad()
            loss.backward()
            seq2seq_optimizer.step()
            
            '''
            for word in inputs:
                encoder_out, encoder_hidden = encoder(word, encoder_hidden)
            decoder_hidden = encoder_hidden
            decoder_out, decoder_hidden = decoder(inputs[-1], decoder_hidden)
    
            loss = loss_func(decoder_out,label)
            
            #backward
            encoder_optimizer.zero_grad()
            decoder_optimizer.zero_grad()   
            loss.backward()                     
            encoder_optimizer.step()                  
            decoder_optimizer.step()
            '''
        
            loss_sum+= loss.data[0]
        print('Epoch: %2d train loss: %.4f' % (epoch, loss_sum))

    结果:

     

    6.测试模型

    代码:

    # Test Seq2Seq Modelfor step, (inputs, labels) in enumerate(test_loader):   
        # encoder_hidden = encoder.initHidden()
        label = torch.LongTensor((1,))
        label[0] = int(labels.data.numpy()[0])
        
        if torch.cuda.is_available():
            inputs = [word.cuda() for word in inputs]
            label = label.cuda()
            # encoder_hidden = encoder_hidden.cuda() 
            
        decoder_output = seq2seq(inputs)
        
        '''
         # forward
        for word in inputs:
            encoder_out, encoder_hidden = encoder(word, encoder_hidden)
        decoder_hidden = encoder_hidden
        decoder_out, decoder_hidden = decoder(inputs[-1], decoder_hidden)
        '''        
            
        # output
        ans = ''
        pred = ''
            
        for word in inputs:
            ix = word.cpu().data.numpy()[0][0]
            ans+=ix_to_word[ix]
            pred+=ix_to_word[ix]
    
        ans+=ix_to_word[int(labels.data.numpy()[0])]
        pred+=ix_to_word[np.argmax(decoder_output.cpu().data.numpy())]
            
        print('Answer: %s' % ans)
        print('Prediction: %s' % pred)# encoder_hidden = encoder.initHidden()
        label = torch.LongTensor((1,))
        label[0] = int(labels.data.numpy()[0])
        
        if torch.cuda.is_available():
            inputs = [word.cuda() for word in inputs]
            label = label.cuda()
            # encoder_hidden = encoder_hidden.cuda() 
            
        decoder_output = seq2seq(inputs)
        
        '''
         # forward
        for word in inputs:
            encoder_out, encoder_hidden = encoder(word, encoder_hidden)
        decoder_hidden = encoder_hidden
        decoder_out, decoder_hidden = decoder(inputs[-1], decoder_hidden)
        '''        
            
        # output
        ans = ''
        pred = ''
            
        for word in inputs:
            ix = word.cpu().data.numpy()[0][0]
            ans+=ix_to_word[ix]
            pred+=ix_to_word[ix]
    
        ans+=ix_to_word[int(labels.data.numpy()[0])]
        pred+=ix_to_word[np.argmax(decoder_output.cpu().data.numpy())]
            
        print('Answer: %s' % ans)
        print('Prediction: %s' % pred)

    结果:

    上述结果是全部结果中效果不错的,可以观察到:虽然模型无法完全预测后续字词,但是能依照句子的前部分继续生成意思完整的句子。
    不过,整体来看模型效果较差,我认为有以下几个原因:

    1. 所用文本数量少,仅用了100个句子进行训练。

    2. 构造的词库小,词典中仅有3000+字词,其中包括许多无意义的字词。

    3. 未对超参数进行微调。

     

    7.保存/加载模型

    往往大家保存和加载模型都是用的最简单的方法:torch.save(model,path),torch.load(path)
    这样的方法不仅将模型的参数保存了下来,还将模型的结构保存了下来。

    有时我们只需要保存模型的参数,我们可以采用这样的方法:torch.save(model.state_dict(),path),torch.load_state_dict(torch.load(path))

    当然,还有许多复杂的方法可以选择,大家可以查阅参考资料进一步了解。

    代码:

    # Save Seq2Seq Model'''
    torch.save(encoder.state_dict(),'../../Model/Seq2Seq/encoder_params.pkl')
    torch.save(decoder.state_dict(),'../../Model/Seq2Seq/decoder_params.pkl')
    
    torch.save(encoder,'../../Model/Seq2Seq/encoder.pkl')
    torch.save(decoder,'../../Model/Seq2Seq/decoder.pkl')
    '''torch.save(seq2seq.state_dict(),'../../Model/Seq2Seq/seq2seq_params.pkl')torch.save(seq2seq,'../../Model/Seq2Seq/seq2seq.pkl')# Load Seq2Seq Model# encoder.load_state_dict(torch.load('../../Model/Seq2Seq/encoder_params.pkl'))# decoder.load_state_dict(torch.load('../../Model/Seq2Seq/decoder_params.pkl'))seq2seq = torch.load('../../Model/Seq2Seq/seq2seq.pkl')'../../Model/Seq2Seq/encoder_params.pkl')
    torch.save(decoder.state_dict(),'../../Model/Seq2Seq/decoder_params.pkl')
    
    torch.save(encoder,'../../Model/Seq2Seq/encoder.pkl')
    torch.save(decoder,'../../Model/Seq2Seq/decoder.pkl')
    '''torch.save(seq2seq.state_dict(),'../../Model/Seq2Seq/seq2seq_params.pkl')torch.save(seq2seq,'../../Model/Seq2Seq/seq2seq.pkl')# Load Seq2Seq Model# encoder.load_state_dict(torch.load('../../Model/Seq2Seq/encoder_params.pkl'))# decoder.load_state_dict(torch.load('../../Model/Seq2Seq/decoder_params.pkl'))seq2seq = torch.load('../../Model/Seq2Seq/seq2seq.pkl')
    展开全文
  • Keras实现带attention的seq2seq预测模型

    千次阅读 2019-04-16 16:44:41
    例如,输入序列可以是[1,6,2,7,3],预期的输出序列可以是序列[1,6]中的前两个随机整数。 我们将定义问题,使输入和输出序列的长度相同,并根据需要用“0”值填充输出序列。 首先,我们需要一个函数来生成随机...

    参考:https://machinelearningmastery.com/encoder-decoder-attention-sequence-to-sequence-prediction-keras/

    转自:https://blog.csdn.net/baidu_33718858/article/details/85988935

    目录

    带有注意力机制的编码器-解码器(Encoder-Decoder with Attention)

    一个测试注意力机制的问题(Test Problem for Attention)

    没有注意力机制的编码-解码(Encoder-Decoder Without Attention)

    自定义Keras中的Attention层(Custom Keras Attention Layer)

    带有注意力机制的编码器-解码器(Encoder-Decoder With Attention)

    模型比较(Comparison of Models)


    事实证明,递归神经网络的编码器-解码器体系结构在自然语言处理领域的一系列序列到序列预测问题上非常强大。
    注意是一种机制,它解决了编码器-解码器体系结构在长序列上的局限性,并且通常加快了学习速度,提高了序列到序列预测问题模型的技能。

    在本教程中,您将了解如何在Keras中实现一个带有注意力机制的编码器-解码器卷积神经网络。

    完成本教程后,您将知道:

    • 如何设计一个小而可配置的问题来评估有无注意机制的编码器-解码器循环神经网络。
    • 针对序列预测问题,如何设计和评价一个有无注意机制的编码器-解码器网络。
    • 如何在有无注意机制的情况下,鲁棒比较编码器-解码器网络的性能。

    带有注意力机制的编码器-解码器(Encoder-Decoder with Attention)

    循环神经网络中的编码器-解码器模型是一种序列到序列预测问题的体系结构。
    它由两个子模型组成,顾名思义:

    • 编码器:编码器负责单步执行输入时间步骤,并将整个序列编码为一个固定长度的向量,称为上下文向量(context vector)。
    • 解码器:解码器负责逐步执行输出时间步骤,并从上下文向量读取信息。

    该体系结构的一个问题是,长输入或输出序列的性能较差。原因被认为是编码器使用了固定尺寸的上下文向量进行内部表示。
    注意力机制是对解决这个限制的体系结构的扩展。它首先提供从编码器到解码器的更丰富的上下文,以及一种学习机制,在该机制中,解码器可以在预测输出序列中的每个时间步的输出时,在更丰富的编码中学习应注意的位置。
    有关编码器-解码器体系结构的更多注意事项,请参阅以下文章:


    一个测试注意力机制的问题(Test Problem for Attention)

    在我们开发带有注意力机制的模型之前,我们首先定义一个人为的可伸缩的测试问题,我们可以用它来确定注意力机制是否带来了任何好处。
    在这个问题中,我们将生成随机整数序列作为输入并匹配输出序列,输出序列由输入序列中整数的子集组成。
    例如,输入序列可以是[1,6,2,7,3],预期的输出序列可以是序列[1,6]中的前两个随机整数。
    我们将定义问题,使输入和输出序列的长度相同,并根据需要用“0”值填充输出序列。
    首先,我们需要一个函数来生成随机整数序列。我们将使用python的randint()函数生成介于0和最大值之间的随机整数,并将此范围用作问题的基数(例如,特征数)。

    下面的generate_sequence()函数将生成一个具有固定长度和指定基数的随机整数序列。

    
     
    1. from random import randint
    2. # generate a sequence of random integers
    3. def generate_sequence(length, n_unique):
    4. return [randint( 0, n_unique -1) for _ in range(length)]
    5. # generate random sequence
    6. sequence = generate_sequence( 5, 50)
    7. print(sequence)

    运行此示例将生成一个具有5个时间步的序列,其中序列中的每个值都是0到49之间的随机整数。

    [43, 3, 28, 34, 33]
     

    接下来,我们需要一个函数将离散整数值独热编码为二进制向量。
    如果使用的基数为50,则每个整数将由0和指定整数值索引处的1表示的50-元素的向量。
    下面的one_hot_encode()函数将对给定的整数序列进行一次独热编码。

    
     
    1. # one hot encode sequence
    2. def one_hot_encode(sequence, n_unique):
    3. encoding = list()
    4. for value in sequence:
    5. vector = [ 0 for _ in range(n_unique)]
    6. vector[value] = 1
    7. encoding.append(vector)
    8. return array(encoding)

    我们还需要能够解码独热编码后的序列。这将需要把从模型的预测或独热编码的预期序列转换回我们可以读取和计算的整数序列。

    下面的one_hot_decode()函数将把一个独热编码序列解码回一个整数序列。

    
     
    1. # decode a one hot encoded string
    2. def one_hot_decode(encoded_seq):
    3. return [argmax(vector) for vector in encoded_seq]

    我们可以在下面的示例中测试这些操作。

    
     
    1. from random import randint
    2. from numpy import array
    3. from numpy import argmax
    4. # generate a sequence of random integers
    5. def generate_sequence(length, n_unique):
    6. return [randint( 0, n_unique -1) for _ in range(length)]
    7. # one hot encode sequence
    8. def one_hot_encode(sequence, n_unique):
    9. encoding = list()
    10. for value in sequence:
    11. vector = [ 0 for _ in range(n_unique)]
    12. vector[value] = 1
    13. encoding.append(vector)
    14. return array(encoding)
    15. # decode a one hot encoded string
    16. def one_hot_decode(encoded_seq):
    17. return [argmax(vector) for vector in encoded_seq]
    18. # argmax()为返回沿轴axis最大值的索引,也就是独热编码后1所在的索引,即原整数数值
    19. # generate random sequence
    20. sequence = generate_sequence( 5, 50)
    21. print(sequence)
    22. # one hot encode
    23. encoded = one_hot_encode(sequence, 50)
    24. print(encoded)
    25. # decode
    26. decoded = one_hot_decode(encoded)
    27. print(decoded)

    运行这个示例首先打印一个随机生成的序列,然后打印一个独热编码的版本,最后再次打印解码的序列。

    
     
    1. [ 3, 18, 32, 11, 36]
    2. [[ 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0]
    3. [ 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 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0]
    4. [ 0 0 0 0 0 0 0 0 0 0 0 0 0 0 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 0 0 0 0 0 0 0]
    5. [ 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 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0]
    6. [ 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 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 0 0 0]]
    7. [ 3, 18, 32, 11, 36]

    最后,我们需要一个可以创建输入和输出序列对的函数来训练和评估模型。
    下面名为get_pair()的函数将返回一个给定了输入长度、输出长度和基数的,输入-输出序列对。输入序列和输出序列的长度相同,即等于输入序列的长度,但输出序列将表示为输入序列的前N个字符,并用零值填充到所需的长度。
    然后对整数序列进行独热编码,然后将其reshape为循环神经网络所需的三维格式,其尺寸为:样本、时间步和特征。在这种情况下,样本总是1,因为我们只生成一个输入-输出对,时间步数是输入序列长度,特征是每个时间步数的基数。

    
     
    1. # prepare data for the LSTM
    2. def get_pair(n_in, n_out, n_unique):
    3. # generate random sequence
    4. sequence_in = generate_sequence(n_in, n_unique)
    5. sequence_out = sequence_in[:n_out] + [ 0 for _ in range(n_in-n_out)]
    6. # one hot encode
    7. X = one_hot_encode(sequence_in, n_unique)
    8. y = one_hot_encode(sequence_out, n_unique)
    9. # reshape as 3D
    10. X = X.reshape(( 1, X.shape[ 0], X.shape[ 1]))
    11. y = y.reshape(( 1, y.shape[ 0], y.shape[ 1]))
    12. return X,y

    我们可以把这些放在一起并演示数据准备部分的代码。

    
     
    1. from random import randint
    2. from numpy import array
    3. from numpy import argmax
    4. # generate a sequence of random integers
    5. def generate_sequence(length, n_unique):
    6. return [randint( 0, n_unique -1) for _ in range(length)]
    7. # one hot encode sequence
    8. def one_hot_encode(sequence, n_unique):
    9. encoding = list()
    10. for value in sequence:
    11. vector = [ 0 for _ in range(n_unique)]
    12. vector[value] = 1
    13. encoding.append(vector)
    14. return array(encoding)
    15. # decode a one hot encoded string
    16. def one_hot_decode(encoded_seq):
    17. return [argmax(vector) for vector in encoded_seq]
    18. # prepare data for the LSTM
    19. def get_pair(n_in, n_out, n_unique):
    20. # generate random sequence
    21. sequence_in = generate_sequence(n_in, n_unique)
    22. sequence_out = sequence_in[:n_out] + [ 0 for _ in range(n_in-n_out)]
    23. # one hot encode
    24. X = one_hot_encode(sequence_in, n_unique)
    25. y = one_hot_encode(sequence_out, n_unique)
    26. # reshape as 3D
    27. X = X.reshape(( 1, X.shape[ 0], X.shape[ 1]))
    28. y = y.reshape(( 1, y.shape[ 0], y.shape[ 1]))
    29. return X,y
    30. # generate random sequence
    31. X, y = get_pair( 5, 2, 50)
    32. print(X.shape, y.shape)
    33. print( 'X=%s, y=%s' % (one_hot_decode(X[ 0]), one_hot_decode(y[ 0])))

    运行该示例将生成一个输入输出对,并打印两个数组的形状。
    然后以解码的形式打印生成的对,我们可以看到序列的前两个整数在输出序列中被复制,后面是零值的填充。

    
     
    1. ( 1, 5, 50) ( 1, 5, 50)
    2. X=[ 12, 20, 36, 40, 12], y=[ 12, 20, 0, 0, 0]

    没有注意力机制的编码-解码(Encoder-Decoder Without Attention)

    在这一部分中,我们将开发一个解决上述问题的编码器-解码器模型作为评估的基准。
    首先,我们将重复问题的定义:5个时间步长的输入和输出序列,输出序列中的值为输入序列的前2个元素,以50为基数(特征的个数)。

    
     
    1. # configure problem
    2. n_features = 50
    3. n_timesteps_in = 5
    4. n_timesteps_out = 2

    我们可以通过从编码器lstm模型中获取输出编码向量,并按照输出序列中的时间步数重复输入编码向量n次,然后使用解码器预测输出序列,从而在keras中开发一个简单的编码器-解码器模型。
    有关如何在Keras中定义编码器-解码器体系结构的详细信息,请参阅以下文章:

    我们将使用相同数量的单元(units,代表循环神经网络中隐藏层的单元个数,也代表了循环神经网络的输出维度)配置编码器和解码器,在本例中为150。我们将利用Adam有效实现梯度下降,并优化分类交叉熵损失函数,前提是该问题在技术上是一个多分类问题。
    模型的配置是经过一点尝试和错误后发现的,决不是最优化的。
    下面列出了keras中编码器-解码器体系结构的代码。

    
     
    1. # define model
    2. model = Sequential()
    3. model.add(LSTM( 150, input_shape=(n_timesteps_in, n_features)))
    4. model.add(RepeatVector(n_timesteps_in))
    5. model.add(LSTM( 150, return_sequences= True))
    6. model.add(TimeDistributed(Dense(n_features, activation= 'softmax')))
    7. model.compile(loss= 'categorical_crossentropy', optimizer= 'adam', metrics=[ 'acc'])

    我们将在5000个整数序列的随机输入输出对上训练模型。

    
     
    1. # train LSTM
    2. for epoch in range( 5000):
    3. # generate new random sequence
    4. X,y = get_pair(n_timesteps_in, n_timesteps_out, n_features)
    5. # fit model for one epoch on this sequence
    6. model.fit(X, y, epochs= 1, verbose= 2)

    经过训练后,我们将在100个新的随机生成的整数序列上评估模型,并且仅在整个输出序列与预期值匹配时标记预测正确。

    
     
    1. # evaluate LSTM
    2. total, correct = 100, 0
    3. for _ in range(total):
    4. X,y = get_pair(n_timesteps_in, n_timesteps_out, n_features)
    5. yhat = model.predict(X, verbose= 0)
    6. if array_equal(one_hot_decode(y[ 0]), one_hot_decode(yhat[ 0])):
    7. correct += 1
    8. print( 'Accuracy: %.2f%%' % (float(correct)/float(total)* 100.0))

    最后,我们将打印10个期望输出序列和模型预测序列的例子。
    将所有这些放在一起,下面列出了完整的示例。

    
     
    1. from random import randint
    2. from numpy import array
    3. from numpy import argmax
    4. from numpy import array_equal
    5. from keras.models import Sequential
    6. from keras.layers import LSTM
    7. from keras.layers import Dense
    8. from keras.layers import TimeDistributed
    9. from keras.layers import RepeatVector
    10. # generate a sequence of random integers
    11. def generate_sequence(length, n_unique):
    12. return [randint( 0, n_unique -1) for _ in range(length)]
    13. # one hot encode sequence
    14. def one_hot_encode(sequence, n_unique):
    15. encoding = list()
    16. for value in sequence:
    17. vector = [ 0 for _ in range(n_unique)]
    18. vector[value] = 1
    19. encoding.append(vector)
    20. return array(encoding)
    21. # decode a one hot encoded string
    22. def one_hot_decode(encoded_seq):
    23. return [argmax(vector) for vector in encoded_seq]
    24. # prepare data for the LSTM
    25. def get_pair(n_in, n_out, cardinality):
    26. # generate random sequence
    27. sequence_in = generate_sequence(n_in, cardinality)
    28. sequence_out = sequence_in[:n_out] + [ 0 for _ in range(n_in-n_out)]
    29. # one hot encode
    30. X = one_hot_encode(sequence_in, cardinality)
    31. y = one_hot_encode(sequence_out, cardinality)
    32. # reshape as 3D
    33. X = X.reshape(( 1, X.shape[ 0], X.shape[ 1]))
    34. y = y.reshape(( 1, y.shape[ 0], y.shape[ 1]))
    35. return X,y
    36. # configure problem
    37. n_features = 50
    38. n_timesteps_in = 5
    39. n_timesteps_out = 2
    40. # define model
    41. model = Sequential()
    42. model.add(LSTM( 150, input_shape=(n_timesteps_in, n_features)))
    43. model.add(RepeatVector(n_timesteps_in))
    44. model.add(LSTM( 150, return_sequences= True))
    45. model.add(TimeDistributed(Dense(n_features, activation= 'softmax')))
    46. model.compile(loss= 'categorical_crossentropy', optimizer= 'adam', metrics=[ 'acc'])
    47. # train LSTM
    48. for epoch in range( 5000):
    49. # generate new random sequence
    50. X,y = get_pair(n_timesteps_in, n_timesteps_out, n_features)
    51. # fit model for one epoch on this sequence
    52. model.fit(X, y, epochs= 1, verbose= 2)
    53. # evaluate LSTM
    54. total, correct = 100, 0
    55. for _ in range(total):
    56. X,y = get_pair(n_timesteps_in, n_timesteps_out, n_features)
    57. yhat = model.predict(X, verbose= 0)
    58. if array_equal(one_hot_decode(y[ 0]), one_hot_decode(yhat[ 0])):
    59. correct += 1
    60. print( 'Accuracy: %.2f%%' % (float(correct)/float(total)* 100.0))
    61. # spot check some examples
    62. for _ in range( 10):
    63. X,y = get_pair(n_timesteps_in, n_timesteps_out, n_features)
    64. yhat = model.predict(X, verbose= 0)
    65. print( 'Expected:', one_hot_decode(y[ 0]), 'Predicted', one_hot_decode(yhat[ 0]))

    在CPU上运行这个例子不会花费很长时间,可能几分钟,不需要GPU。
    根据实验该模型的准确度略低于20%。考虑到神经网络的随机性,您的结果会有所不同;可以运行示例几次并取平均值。

    Accuracy: 19.00%
     

    我们可以从样本输出中看到,模型在输出序列中得到的第一个数字对于大多数或所有情况都是正确的,并且只与第二个数字发生冲突。所有零填充值都是正确预测的。

    
     
    1. Expected: [ 47, 0, 0, 0, 0] Predicted [ 47, 47, 0, 0, 0]
    2. Expected: [ 43, 31, 0, 0, 0] Predicted [ 43, 31, 0, 0, 0]
    3. Expected: [ 14, 22, 0, 0, 0] Predicted [ 14, 14, 0, 0, 0]
    4. Expected: [ 39, 31, 0, 0, 0] Predicted [ 39, 39, 0, 0, 0]
    5. Expected: [ 6, 4, 0, 0, 0] Predicted [ 6, 4, 0, 0, 0]
    6. Expected: [ 47, 0, 0, 0, 0] Predicted [ 47, 47, 0, 0, 0]
    7. Expected: [ 39, 33, 0, 0, 0] Predicted [ 39, 39, 0, 0, 0]
    8. Expected: [ 23, 2, 0, 0, 0] Predicted [ 23, 23, 0, 0, 0]
    9. Expected: [ 19, 28, 0, 0, 0] Predicted [ 19, 3, 0, 0, 0]
    10. Expected: [ 32, 33, 0, 0, 0] Predicted [ 32, 32, 0, 0, 0]

    自定义Keras中的Attention层(Custom Keras Attention Layer)

    现在我们需要将注意力机制引入编码器-解码器模型。
    在Keras发布正式的注意力机制之前(查Keras的官网发现了一段模糊的话,没看到专门的介绍attention的文档,如果已经有官方实现了,欢迎留言告诉我),我们可以开发自己的实施方案,也可以使用现有的第三方实施方案。


    为了加快速度,让我们使用现有的第三方实现。
    Zafarali Ahmed ,一位在数据日志网站上的实习生,在2017年发表的题为“How to Visualize Your Recurrent Neural Network with Attention in Keras”的文章和名为“Keras attention”的Github项目中,为Keras开发了一个定制层,为attention提供支持。
    自定义关注层称为AttentionDecoder,可在github项目中的custom_recurrents.py文件中找到。我们可以在项目的GNU affero通用公共许可v3.0许可证下重用此代码。
    为了完整起见,下面列出了自定义层的副本。复制并粘贴到当前工作目录中名为“attention_decoder.py”的新的单独文件中。

    
     
    1. import tensorflow as tf
    2. from keras import backend as K
    3. from keras import regularizers, constraints, initializers, activations
    4. from keras.layers.recurrent import Recurrent, _time_distributed_dense
    5. from keras.engine import InputSpec
    6. tfPrint = lambda d, T: tf.Print(input_=T, data=[T, tf.shape(T)], message=d)
    7. class AttentionDecoder(Recurrent):
    8. def __init__(self, units, output_dim,
    9. activation='tanh',
    10. return_probabilities=False,
    11. name='AttentionDecoder',
    12. kernel_initializer='glorot_uniform',
    13. recurrent_initializer='orthogonal',
    14. bias_initializer='zeros',
    15. kernel_regularizer=None,
    16. bias_regularizer=None,
    17. activity_regularizer=None,
    18. kernel_constraint=None,
    19. bias_constraint=None,
    20. **kwargs):
    21. """
    22. Implements an AttentionDecoder that takes in a sequence encoded by an
    23. encoder and outputs the decoded states
    24. :param units: dimension of the hidden state and the attention matrices
    25. :param output_dim: the number of labels in the output space
    26. references:
    27. Bahdanau, Dzmitry, Kyunghyun Cho, and Yoshua Bengio.
    28. "Neural machine translation by jointly learning to align and translate."
    29. arXiv preprint arXiv:1409.0473 (2014).
    30. """
    31. self.units = units
    32. self.output_dim = output_dim
    33. self.return_probabilities = return_probabilities
    34. self.activation = activations.get(activation)
    35. self.kernel_initializer = initializers.get(kernel_initializer)
    36. self.recurrent_initializer = initializers.get(recurrent_initializer)
    37. self.bias_initializer = initializers.get(bias_initializer)
    38. self.kernel_regularizer = regularizers.get(kernel_regularizer)
    39. self.recurrent_regularizer = regularizers.get(kernel_regularizer)
    40. self.bias_regularizer = regularizers.get(bias_regularizer)
    41. self.activity_regularizer = regularizers.get(activity_regularizer)
    42. self.kernel_constraint = constraints.get(kernel_constraint)
    43. self.recurrent_constraint = constraints.get(kernel_constraint)
    44. self.bias_constraint = constraints.get(bias_constraint)
    45. super(AttentionDecoder, self).__init__(**kwargs)
    46. self.name = name
    47. self.return_sequences = True # must return sequences
    48. def build(self, input_shape):
    49. """
    50. See Appendix 2 of Bahdanau 2014, arXiv:1409.0473
    51. for model details that correspond to the matrices here.
    52. """
    53. self.batch_size, self.timesteps, self.input_dim = input_shape
    54. if self.stateful:
    55. super(AttentionDecoder, self).reset_states()
    56. self.states = [ None, None] # y, s
    57. """
    58. Matrices for creating the context vector
    59. """
    60. self.V_a = self.add_weight(shape=(self.units,),
    61. name= 'V_a',
    62. initializer=self.kernel_initializer,
    63. regularizer=self.kernel_regularizer,
    64. constraint=self.kernel_constraint)
    65. self.W_a = self.add_weight(shape=(self.units, self.units),
    66. name= 'W_a',
    67. initializer=self.kernel_initializer,
    68. regularizer=self.kernel_regularizer,
    69. constraint=self.kernel_constraint)
    70. self.U_a = self.add_weight(shape=(self.input_dim, self.units),
    71. name= 'U_a',
    72. initializer=self.kernel_initializer,
    73. regularizer=self.kernel_regularizer,
    74. constraint=self.kernel_constraint)
    75. self.b_a = self.add_weight(shape=(self.units,),
    76. name= 'b_a',
    77. initializer=self.bias_initializer,
    78. regularizer=self.bias_regularizer,
    79. constraint=self.bias_constraint)
    80. """
    81. Matrices for the r (reset) gate
    82. """
    83. self.C_r = self.add_weight(shape=(self.input_dim, self.units),
    84. name= 'C_r',
    85. initializer=self.recurrent_initializer,
    86. regularizer=self.recurrent_regularizer,
    87. constraint=self.recurrent_constraint)
    88. self.U_r = self.add_weight(shape=(self.units, self.units),
    89. name= 'U_r',
    90. initializer=self.recurrent_initializer,
    91. regularizer=self.recurrent_regularizer,
    92. constraint=self.recurrent_constraint)
    93. self.W_r = self.add_weight(shape=(self.output_dim, self.units),
    94. name= 'W_r',
    95. initializer=self.recurrent_initializer,
    96. regularizer=self.recurrent_regularizer,
    97. constraint=self.recurrent_constraint)
    98. self.b_r = self.add_weight(shape=(self.units, ),
    99. name= 'b_r',
    100. initializer=self.bias_initializer,
    101. regularizer=self.bias_regularizer,
    102. constraint=self.bias_constraint)
    103. """
    104. Matrices for the z (update) gate
    105. """
    106. self.C_z = self.add_weight(shape=(self.input_dim, self.units),
    107. name= 'C_z',
    108. initializer=self.recurrent_initializer,
    109. regularizer=self.recurrent_regularizer,
    110. constraint=self.recurrent_constraint)
    111. self.U_z = self.add_weight(shape=(self.units, self.units),
    112. name= 'U_z',
    113. initializer=self.recurrent_initializer,
    114. regularizer=self.recurrent_regularizer,
    115. constraint=self.recurrent_constraint)
    116. self.W_z = self.add_weight(shape=(self.output_dim, self.units),
    117. name= 'W_z',
    118. initializer=self.recurrent_initializer,
    119. regularizer=self.recurrent_regularizer,
    120. constraint=self.recurrent_constraint)
    121. self.b_z = self.add_weight(shape=(self.units, ),
    122. name= 'b_z',
    123. initializer=self.bias_initializer,
    124. regularizer=self.bias_regularizer,
    125. constraint=self.bias_constraint)
    126. """
    127. Matrices for the proposal
    128. """
    129. self.C_p = self.add_weight(shape=(self.input_dim, self.units),
    130. name= 'C_p',
    131. initializer=self.recurrent_initializer,
    132. regularizer=self.recurrent_regularizer,
    133. constraint=self.recurrent_constraint)
    134. self.U_p = self.add_weight(shape=(self.units, self.units),
    135. name= 'U_p',
    136. initializer=self.recurrent_initializer,
    137. regularizer=self.recurrent_regularizer,
    138. constraint=self.recurrent_constraint)
    139. self.W_p = self.add_weight(shape=(self.output_dim, self.units),
    140. name= 'W_p',
    141. initializer=self.recurrent_initializer,
    142. regularizer=self.recurrent_regularizer,
    143. constraint=self.recurrent_constraint)
    144. self.b_p = self.add_weight(shape=(self.units, ),
    145. name= 'b_p',
    146. initializer=self.bias_initializer,
    147. regularizer=self.bias_regularizer,
    148. constraint=self.bias_constraint)
    149. """
    150. Matrices for making the final prediction vector
    151. """
    152. self.C_o = self.add_weight(shape=(self.input_dim, self.output_dim),
    153. name= 'C_o',
    154. initializer=self.recurrent_initializer,
    155. regularizer=self.recurrent_regularizer,
    156. constraint=self.recurrent_constraint)
    157. self.U_o = self.add_weight(shape=(self.units, self.output_dim),
    158. name= 'U_o',
    159. initializer=self.recurrent_initializer,
    160. regularizer=self.recurrent_regularizer,
    161. constraint=self.recurrent_constraint)
    162. self.W_o = self.add_weight(shape=(self.output_dim, self.output_dim),
    163. name= 'W_o',
    164. initializer=self.recurrent_initializer,
    165. regularizer=self.recurrent_regularizer,
    166. constraint=self.recurrent_constraint)
    167. self.b_o = self.add_weight(shape=(self.output_dim, ),
    168. name= 'b_o',
    169. initializer=self.bias_initializer,
    170. regularizer=self.bias_regularizer,
    171. constraint=self.bias_constraint)
    172. # For creating the initial state:
    173. self.W_s = self.add_weight(shape=(self.input_dim, self.units),
    174. name= 'W_s',
    175. initializer=self.recurrent_initializer,
    176. regularizer=self.recurrent_regularizer,
    177. constraint=self.recurrent_constraint)
    178. self.input_spec = [
    179. InputSpec(shape=(self.batch_size, self.timesteps, self.input_dim))]
    180. self.built = True
    181. def call(self, x):
    182. # store the whole sequence so we can "attend" to it at each timestep
    183. self.x_seq = x
    184. # apply the a dense layer over the time dimension of the sequence
    185. # do it here because it doesn't depend on any previous steps
    186. # thefore we can save computation time:
    187. self._uxpb = _time_distributed_dense(self.x_seq, self.U_a, b=self.b_a,
    188. input_dim=self.input_dim,
    189. timesteps=self.timesteps,
    190. output_dim=self.units)
    191. return super(AttentionDecoder, self).call(x)
    192. def get_initial_state(self, inputs):
    193. # apply the matrix on the first time step to get the initial s0.
    194. s0 = activations.tanh(K.dot(inputs[:, 0], self.W_s))
    195. # from keras.layers.recurrent to initialize a vector of (batchsize,
    196. # output_dim)
    197. y0 = K.zeros_like(inputs) # (samples, timesteps, input_dims)
    198. y0 = K.sum(y0, axis=( 1, 2)) # (samples, )
    199. y0 = K.expand_dims(y0) # (samples, 1)
    200. y0 = K.tile(y0, [ 1, self.output_dim])
    201. return [y0, s0]
    202. def step(self, x, states):
    203. ytm, stm = states
    204. # repeat the hidden state to the length of the sequence
    205. _stm = K.repeat(stm, self.timesteps)
    206. # now multiplty the weight matrix with the repeated hidden state
    207. _Wxstm = K.dot(_stm, self.W_a)
    208. # calculate the attention probabilities
    209. # this relates how much other timesteps contributed to this one.
    210. et = K.dot(activations.tanh(_Wxstm + self._uxpb),
    211. K.expand_dims(self.V_a))
    212. at = K.exp(et)
    213. at_sum = K.sum(at, axis= 1)
    214. at_sum_repeated = K.repeat(at_sum, self.timesteps)
    215. at /= at_sum_repeated # vector of size (batchsize, timesteps, 1)
    216. # calculate the context vector
    217. context = K.squeeze(K.batch_dot(at, self.x_seq, axes= 1), axis= 1)
    218. # ~~~> calculate new hidden state
    219. # first calculate the "r" gate:
    220. rt = activations.sigmoid(
    221. K.dot(ytm, self.W_r)
    222. + K.dot(stm, self.U_r)
    223. + K.dot(context, self.C_r)
    224. + self.b_r)
    225. # now calculate the "z" gate
    226. zt = activations.sigmoid(
    227. K.dot(ytm, self.W_z)
    228. + K.dot(stm, self.U_z)
    229. + K.dot(context, self.C_z)
    230. + self.b_z)
    231. # calculate the proposal hidden state:
    232. s_tp = activations.tanh(
    233. K.dot(ytm, self.W_p)
    234. + K.dot((rt * stm), self.U_p)
    235. + K.dot(context, self.C_p)
    236. + self.b_p)
    237. # new hidden state:
    238. st = ( 1-zt)*stm + zt * s_tp
    239. yt = activations.softmax(
    240. K.dot(ytm, self.W_o)
    241. + K.dot(stm, self.U_o)
    242. + K.dot(context, self.C_o)
    243. + self.b_o)
    244. if self.return_probabilities:
    245. return at, [yt, st]
    246. else:
    247. return yt, [yt, st]
    248. def compute_output_shape(self, input_shape):
    249. """
    250. For Keras internal compatability checking
    251. """
    252. if self.return_probabilities:
    253. return ( None, self.timesteps, self.timesteps)
    254. else:
    255. return ( None, self.timesteps, self.output_dim)
    256. def get_config(self):
    257. """
    258. For rebuilding models on load time.
    259. """
    260. config = {
    261. 'output_dim': self.output_dim,
    262. 'units': self.units,
    263. 'return_probabilities': self.return_probabilities
    264. }
    265. base_config = super(AttentionDecoder, self).get_config()
    266. return dict(list(base_config.items()) + list(config.items()))

    我们可以在项目中使用这个自定义层,通过用如下的方式导入它:

    from attention_decoder import AttentionDecoder
     

    该层实现了Bahdanau等人在他们的“Neural Machine Translation by Jointly Learning to Align and Translate.”论文中所描述的注意力机制。
    这段代码在文章中解释得很好,并且与LSTM和注意力方程式都有关联。
    这种实现的一个限制是,它必须输出与输入序列长度相同的序列,而这是编码器-解码器体系结构设计要克服的特定限制。(确实是这样,如果输入和输出序列长度不一样,可以采用补0措施,将短的序列补为和长的一样的;或者在得到这层输出之后,再通过一层GRU改变序列长度)
    重要的是,该层不但避免了第二个LSTM层作为解码器的代码重复,还在这个编码器-解码器模型中通过不加注意机制的Dense层得到SoftMax后的输出。这大大简化了模型的代码。
    需要注意的是,自定义层是建立在Keras中的循环层(继承了Recurrent)之上的,在编写时,它被标记为遗留代码,并且可能会在某个时刻从项目中删除。


    带有注意力机制的编码器-解码器(Encoder-Decoder With Attention)

    既然我们已经有了可以使用的注意力实现,那么我们就可以为我们设计的序列预测问题开发一个有注意力的编码器-解码器模型。
    下面定义了具有注意层的模型。我们可以看到,该层能处理编码器-解码器模型本身的一些机制,使得定义模型更简单。

    
     
    1. # define model
    2. model = Sequential()
    3. model.add(LSTM( 150, input_shape=(n_timesteps_in, n_features), return_sequences= True))
    4. model.add(AttentionDecoder( 150, n_features))
    5. model.compile(loss= 'categorical_crossentropy', optimizer= 'adam', metrics=[ 'acc'])

    就是这样。其余的例子是相同的。
    下面列出了完整的示例。

    
     
    1. from random import randint
    2. from numpy import array
    3. from numpy import argmax
    4. from numpy import array_equal
    5. from keras.models import Sequential
    6. from keras.layers import LSTM
    7. from attention_decoder import AttentionDecoder
    8. # generate a sequence of random integers
    9. def generate_sequence(length, n_unique):
    10. return [randint( 0, n_unique -1) for _ in range(length)]
    11. # one hot encode sequence
    12. def one_hot_encode(sequence, n_unique):
    13. encoding = list()
    14. for value in sequence:
    15. vector = [ 0 for _ in range(n_unique)]
    16. vector[value] = 1
    17. encoding.append(vector)
    18. return array(encoding)
    19. # decode a one hot encoded string
    20. def one_hot_decode(encoded_seq):
    21. return [argmax(vector) for vector in encoded_seq]
    22. # prepare data for the LSTM
    23. def get_pair(n_in, n_out, cardinality):
    24. # generate random sequence
    25. sequence_in = generate_sequence(n_in, cardinality)
    26. sequence_out = sequence_in[:n_out] + [ 0 for _ in range(n_in-n_out)]
    27. # one hot encode
    28. X = one_hot_encode(sequence_in, cardinality)
    29. y = one_hot_encode(sequence_out, cardinality)
    30. # reshape as 3D
    31. X = X.reshape(( 1, X.shape[ 0], X.shape[ 1]))
    32. y = y.reshape(( 1, y.shape[ 0], y.shape[ 1]))
    33. return X,y
    34. # configure problem
    35. n_features = 50
    36. n_timesteps_in = 5
    37. n_timesteps_out = 2
    38. # define model
    39. model = Sequential()
    40. model.add(LSTM( 150, input_shape=(n_timesteps_in, n_features), return_sequences= True))
    41. model.add(AttentionDecoder( 150, n_features))
    42. model.compile(loss= 'categorical_crossentropy', optimizer= 'adam', metrics=[ 'acc'])
    43. # train LSTM
    44. for epoch in range( 5000):
    45. # generate new random sequence
    46. X,y = get_pair(n_timesteps_in, n_timesteps_out, n_features)
    47. # fit model for one epoch on this sequence
    48. model.fit(X, y, epochs= 1, verbose= 2)
    49. # evaluate LSTM
    50. total, correct = 100, 0
    51. for _ in range(total):
    52. X,y = get_pair(n_timesteps_in, n_timesteps_out, n_features)
    53. yhat = model.predict(X, verbose= 0)
    54. if array_equal(one_hot_decode(y[ 0]), one_hot_decode(yhat[ 0])):
    55. correct += 1
    56. print( 'Accuracy: %.2f%%' % (float(correct)/float(total)* 100.0))
    57. # spot check some examples
    58. for _ in range( 10):
    59. X,y = get_pair(n_timesteps_in, n_timesteps_out, n_features)
    60. yhat = model.predict(X, verbose= 0)
    61. print( 'Expected:', one_hot_decode(y[ 0]), 'Predicted', one_hot_decode(yhat[ 0]))

    运行这个示例可以在100个随机生成的输入输出对进行训练来评估模型的性能。在相同的资源和相同的训练量下,具有注意力的模型表现得更好。
    考虑到神经网络的随机性,你的结果可能会有所不同。尝试运行该示例几次。

    Accuracy: 95.00%
     

    抽查一些样本输出和预测序列,我们可以看到即使在前两个元素中有零值的情况下,都很少有错误。

    
     
    1. Expected: [ 48, 47, 0, 0, 0] Predicted [ 48, 47, 0, 0, 0]
    2. Expected: [ 7, 46, 0, 0, 0] Predicted [ 7, 46, 0, 0, 0]
    3. Expected: [ 32, 30, 0, 0, 0] Predicted [ 32, 2, 0, 0, 0]
    4. Expected: [ 3, 25, 0, 0, 0] Predicted [ 3, 25, 0, 0, 0]
    5. Expected: [ 45, 4, 0, 0, 0] Predicted [ 45, 4, 0, 0, 0]
    6. Expected: [ 49, 9, 0, 0, 0] Predicted [ 49, 9, 0, 0, 0]
    7. Expected: [ 22, 23, 0, 0, 0] Predicted [ 22, 23, 0, 0, 0]
    8. Expected: [ 29, 36, 0, 0, 0] Predicted [ 29, 36, 0, 0, 0]
    9. Expected: [ 0, 29, 0, 0, 0] Predicted [ 0, 29, 0, 0, 0]
    10. Expected: [ 11, 26, 0, 0, 0] Predicted [ 11, 26, 0, 0, 0]

    模型比较(Comparison of Models)

    尽管我们在有注意力机制的情况下从模型中得到了更好的结果,但是那只是从模型的一次运行中得到的结果。
    在这种情况下,我们通过多次重复对每个模型的评估,并报告这些运行的平均性能,来寻求更可靠的模型评估结果。有关评估神经网络模型的这种稳健方法的更多信息,请参阅以下文章:

    我们可以定义一个函数来创建每种类型的模型,如下所示。

    
     
    1. # define the encoder-decoder model
    2. def baseline_model(n_timesteps_in, n_features):
    3. model = Sequential()
    4. model.add(LSTM( 150, input_shape=(n_timesteps_in, n_features)))
    5. model.add(RepeatVector(n_timesteps_in))
    6. model.add(LSTM( 150, return_sequences= True))
    7. model.add(TimeDistributed(Dense(n_features, activation= 'softmax')))
    8. model.compile(loss= 'categorical_crossentropy', optimizer= 'adam', metrics=[ 'acc'])
    9. return model
    10. # define the encoder-decoder with attention model
    11. def attention_model(n_timesteps_in, n_features):
    12. model = Sequential()
    13. model.add(LSTM( 150, input_shape=(n_timesteps_in, n_features), return_sequences= True))
    14. model.add(AttentionDecoder( 150, n_features))
    15. model.compile(loss= 'categorical_crossentropy', optimizer= 'adam', metrics=[ 'acc'])
    16. return model

    然后我们可以定义一个函数来拟合和评估模型的精度,并返回精度分数。

    
     
    1. # train and evaluate a model, return accuracy
    2. def train_evaluate_model(model, n_timesteps_in, n_timesteps_out, n_features):
    3. # train LSTM
    4. for epoch in range( 5000):
    5. # generate new random sequence
    6. X,y = get_pair(n_timesteps_in, n_timesteps_out, n_features)
    7. # fit model for one epoch on this sequence
    8. model.fit(X, y, epochs= 1, verbose= 0)
    9. # evaluate LSTM
    10. total, correct = 100, 0
    11. for _ in range(total):
    12. X,y = get_pair(n_timesteps_in, n_timesteps_out, n_features)
    13. yhat = model.predict(X, verbose= 0)
    14. if array_equal(one_hot_decode(y[ 0]), one_hot_decode(yhat[ 0])):
    15. correct += 1
    16. return float(correct)/float(total)* 100.0

    综合起来,我们可以多次重复创建、训练和评估每种模型的过程,并报告多次评估的平均准确性。为了减少运行时间,我们将重复评估每个模型10次,如果您有资源,您可以将其增加到30或100次。
    下面列出了完整的示例。

    
     
    1. from random import randint
    2. from numpy import array
    3. from numpy import argmax
    4. from numpy import array_equal
    5. from keras.models import Sequential
    6. from keras.layers import LSTM
    7. from keras.layers import Dense
    8. from keras.layers import TimeDistributed
    9. from keras.layers import RepeatVector
    10. from attention_decoder import AttentionDecoder
    11. # generate a sequence of random integers
    12. def generate_sequence(length, n_unique):
    13. return [randint( 0, n_unique -1) for _ in range(length)]
    14. # one hot encode sequence
    15. def one_hot_encode(sequence, n_unique):
    16. encoding = list()
    17. for value in sequence:
    18. vector = [ 0 for _ in range(n_unique)]
    19. vector[value] = 1
    20. encoding.append(vector)
    21. return array(encoding)
    22. # decode a one hot encoded string
    23. def one_hot_decode(encoded_seq):
    24. return [argmax(vector) for vector in encoded_seq]
    25. # prepare data for the LSTM
    26. def get_pair(n_in, n_out, cardinality):
    27. # generate random sequence
    28. sequence_in = generate_sequence(n_in, cardinality)
    29. sequence_out = sequence_in[:n_out] + [ 0 for _ in range(n_in-n_out)]
    30. # one hot encode
    31. X = one_hot_encode(sequence_in, cardinality)
    32. y = one_hot_encode(sequence_out, cardinality)
    33. # reshape as 3D
    34. X = X.reshape(( 1, X.shape[ 0], X.shape[ 1]))
    35. y = y.reshape(( 1, y.shape[ 0], y.shape[ 1]))
    36. return X,y
    37. # define the encoder-decoder model
    38. def baseline_model(n_timesteps_in, n_features):
    39. model = Sequential()
    40. model.add(LSTM( 150, input_shape=(n_timesteps_in, n_features)))
    41. model.add(RepeatVector(n_timesteps_in))
    42. model.add(LSTM( 150, return_sequences= True))
    43. model.add(TimeDistributed(Dense(n_features, activation= 'softmax')))
    44. model.compile(loss= 'categorical_crossentropy', optimizer= 'adam', metrics=[ 'acc'])
    45. return model
    46. # define the encoder-decoder with attention model
    47. def attention_model(n_timesteps_in, n_features):
    48. model = Sequential()
    49. model.add(LSTM( 150, input_shape=(n_timesteps_in, n_features), return_sequences= True))
    50. model.add(AttentionDecoder( 150, n_features))
    51. model.compile(loss= 'categorical_crossentropy', optimizer= 'adam', metrics=[ 'acc'])
    52. return model
    53. # train and evaluate a model, return accuracy
    54. def train_evaluate_model(model, n_timesteps_in, n_timesteps_out, n_features):
    55. # train LSTM
    56. for epoch in range( 5000):
    57. # generate new random sequence
    58. X,y = get_pair(n_timesteps_in, n_timesteps_out, n_features)
    59. # fit model for one epoch on this sequence
    60. model.fit(X, y, epochs= 1, verbose= 0)
    61. # evaluate LSTM
    62. total, correct = 100, 0
    63. for _ in range(total):
    64. X,y = get_pair(n_timesteps_in, n_timesteps_out, n_features)
    65. yhat = model.predict(X, verbose= 0)
    66. if array_equal(one_hot_decode(y[ 0]), one_hot_decode(yhat[ 0])):
    67. correct += 1
    68. return float(correct)/float(total)* 100.0
    69. # configure problem
    70. n_features = 50
    71. n_timesteps_in = 5
    72. n_timesteps_out = 2
    73. n_repeats = 10
    74. # evaluate encoder-decoder model
    75. print( 'Encoder-Decoder Model')
    76. results = list()
    77. for _ in range(n_repeats):
    78. model = baseline_model(n_timesteps_in, n_features)
    79. accuracy = train_evaluate_model(model, n_timesteps_in, n_timesteps_out, n_features)
    80. results.append(accuracy)
    81. print(accuracy)
    82. print( 'Mean Accuracy: %.2f%%' % (sum(results)/float(n_repeats)))
    83. # evaluate encoder-decoder with attention model
    84. print( 'Encoder-Decoder With Attention Model')
    85. results = list()
    86. for _ in range(n_repeats):
    87. model = attention_model(n_timesteps_in, n_features)
    88. accuracy = train_evaluate_model(model, n_timesteps_in, n_timesteps_out, n_features)
    89. results.append(accuracy)
    90. print(accuracy)
    91. print( 'Mean Accuracy: %.2f%%' % (sum(results)/float(n_repeats)))

    运行这个示例可以打印每个模型重复的精度,从而让您了解运行的进度。

    
     
    1. Encoder-Decoder Model
    2. 20.0
    3. 23.0
    4. 23.0
    5. 18.0
    6. 28.000000000000004
    7. 28.999999999999996
    8. 23.0
    9. 26.0
    10. 21.0
    11. 20.0
    12. Mean Accuracy: 23.10%
    13. Encoder-Decoder With Attention Model
    14. 98.0
    15. 91.0
    16. 94.0
    17. 93.0
    18. 96.0
    19. 99.0
    20. 97.0
    21. 94.0
    22. 99.0
    23. 96.0
    24. Mean Accuracy: 95.70%

    我们可以看到,即使运行10次,注意力模型仍然比没有注意力的编码器-解码器模型表现出更好的性能。
    对这种评估的一个很好的扩展是捕获每个模型的每个epoch的模型损失,取平均值,并比较有或无注意力机制的模型中损失随时间的变化情况。我希望这个跟踪会显示出注意力比非注意力模型更快地获得更好的技能,进一步突出了该方法的好处。

     

    展开全文
  • PyTorch中的Seq2seq代码 根据和 数据预处理: 我使用以下步骤 > config/WMT14/download.sh # download WMT14 data into raw_data/WMT14 > config/WMT14/prepare.sh # preprocess the data, and copy the files to ...
  • PyTorch中seq2seq模型的一个框架
  • 这里有一个很好的专栏:https://www.dazhuanlan.com/2020/03/30/5e81581e3c05a/
  • PyTorch中的Seq2Seq 这是用于在训练序列到序列模型的完整套件。 它由几个模型和代码组成,可以训练和推断使用它们。 使用此代码,您可以训练: 神经机器翻译(NMT)模型 语言模型 图像到字幕的生成 跳过思维的...
  • 最近学习了seq2seq模型的内容,发现seq2seq模型实际上训练过程和输出过程是一个分离的过程,这里我们采用解决小学生数学问题的数据例子作为一个示例,来详细地讲解seq2seq通过小学生数学问题的句子去预测对应的数学...
  • RNN之seq2seq模型

    千次阅读 2019-03-09 19:03:28
    3. seq2seq预测股票 我们利用seq2seq对股票收盘价进行了预测,关于代码的详细说明,参见注释,需要数据的同学可以加我qq,请备注csdn_seq2seq。 # -*- coding: utf-8 -*- """ seq2seq stock """ import ...
  • 基于seq2seq的时间序列预测实验(转) https://zhuanlan.zhihu.com/p/39140472 本文使用seq2seq模型来做若干组时间序列的预测任务,目的是验证RNN这种网络结构对时间序列数据的pattern的发现能力,并在小范围内...
  • 您可能会在其他地方找到更好的seq2seq教程/实现。 seq2seq_chatbot 张量流中Seq2seq聊天机器人的实现。 特征 带智能加载程序的动态rnn (无填充) 预测中的波束搜索(全局最优快速近似) 解码器的信号指示器...
  • keras seq2seqDo you want to try some other methods to solve your forecasting problem rather than traditional regression? There are many neural network architectures, which are frequently applied in NL...
  • 时间序列预测可以根据短期预测,长期预测,以及具体场景选用不同的方法,如ARMA、ARIMA...本文通过生成的随机数利用tensorflow的seq2seq模型进行单变量时间序列预测实验,目的是理解seq2seq的模型基础架构以及验证模...
  • TF-seq2seq 使用TensorFlow进行序列到序列(seq2seq)学习。 核心构建块是RNN编码器-解码器体系结构和注意机制。 该软件包主要使用最新的(1.2)tf.contrib.seq2seq模块实现 注意包装 解码器 基本解码器 ...
  • Seq2seq模型 当输入和输出都是不定长序列时,可以使⽤编码器—解码器(encoder-decoder) 或者seq2seq模型。这两个模型本质上都⽤到了两个循环神经⽹络,分别叫做编码器和解码器。编码器⽤来分析输⼊序列,解码器⽤...
  • 该存储库实现了时间序列预测的常用方法,尤其是TensorFlow2中的深度学习方法。 如果您有更好的主意,欢迎您贡献力量,只需创建PR。 如有任何疑问,请随时提出问题。 正在进行的项目,我将继续进行改进,因此您可能...
  • 求教,在使用tensorflow中的tf.contrib.legacy_seq2seq.basic_rnn_seq2seq进行预测时, 应将上一步输出的结果作为decoder_input的输入。可传入参数过程中,decoder_input需 一次传完。怎么才能将上一步的结果传入呢?
  • Seq2Seq的PyTorch实现

    千次阅读 2020-07-02 09:11:11
    本文介绍一下如何使用 PyTorch 复现 Seq2Seq,实现简单的机器翻译应用,请先简单阅读论文Learning Phrase Representations using RNN Encoder–Decoder for Statistical Machine Translation(2014),了解清楚Seq2Seq...
  • Seq2Seq /注意 WaveNet 变压器/变压器 快速开始 from deepseries . models import Wave2Wave , RNN2RNN from deepseries . train import Learner from deepseries . data import Value , create_seq2seq_data_...
  • seq2seq 预测/测试阶段decoder的输入

    千次阅读 2017-12-01 09:23:24
    训练阶段输入 encoder_input decoder_input decoder_target 其中decoder_target是decoder_input的shift一位(如图) ...decoder_input:前一decoder的step的预测结果就会作为下一step的输入,只用输入一个(如图)
  • 本文使用了 Seq2Seq 模型,输入为 5 个中文字词,输出为 1 个中文字词。目录 关于RNN 语料预处理 搭建数据集 搭建模型 训练模型 测试模型 保存/加载模型 1.关于RNN 自被提出以来,循环神经网络(Recurrent Neural ...
  • Seq2Seq-PyTorch 使用PyTorch的序列到序列实现 安装 克隆项目,进入项目目录并执行 python setup.py install 或者 pip install ./ 或简单地复制源代码。 推荐使用pip install ./ ,因为您可以先激活虚拟环境,...
  • 本文将结合交通流量预测分析《Bike Flow Prediction with Multi-Graph Convolutional Networks》中的模型。 参考的这篇博客:https://blog.csdn.net/zuiyishihefang/article/details/96978243 图卷积网络 图网络可以...
  • seq2seq seq2seq-pytorch是一个框架,用于在实现的基于注意力的序列到序列模型。 该框架具有用于seq2seq模型,训练,推理,检查点等的模块化和可扩展组件。 介绍 Seq2seq将一个序列转换为另一序列。 它通过使用递归...
  • 1文章信息《SeqST-GAN: Seq2Seq Generative Adversarial Nets for Multi-step Urban Crowd Flow Predictio...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 19,340
精华内容 7,736
关键字:

seq2seq预测