首页 > 其他分享 >seq2seq项目详解

seq2seq项目详解

时间:2024-03-19 20:22:39浏览次数:25  
标签:target 项目 int batch rnn seq2seq source 详解 size

一、seq2seq和encoder-decoder关系

 seq2seq是从解决问题的目的角度来说的,利用的框架是encoder-decoder

 

二、项目例子

 比如我们有两个文件letters_source.txtletters_target.txt,他们行数一致,也就是我们的训练集合,他们每一行互应(这两个文件同一行彼此长度可以不一致:比如中英互译

 我们只展示前10行(我们该项目的目的是训练一个seq2seq模型,最终输入一个原序列,给出对原序列排序的目标序列

'''
原序列	目标序列
bsaqq	abqqs
npy	    npy
lbwuj	bjluw
bqv	    bqv
kial	aikl
tddam	addmt
edxpjpg	degjppx
nspv	npsv
huloz	hlouz
kmclq	cklmq
.............
'''

  1)读取文件

'''
只展示前十个序列
source_seq: ['bsaqq', 'npy', 'lbwuj', 'bqv', 'kial', 'tddam', 'edxpjpg', 'nspv', 'huloz', 'kmclq']
target_seq: ['abqqs', 'npy', 'bjluw', 'bqv', 'aikl', 'addmt', 'degjppx', 'npsv', 'hlouz', 'cklmq']
'''

 2)针对source和target中的所有字符,我们建立一个字典,将字符变成数字

'''
为每个char进行int转换:(这里为了每次转换保持一致,已经进行了字符列表的排序)
source_int_to_letter {0: '<PAD>', 1: '<UNK>', 2: '<GO>', 3: '<EOS>', 4: 'a', 5: 'b', 6: 'c', 7: 'd', 8: 'e', 9: 'f', 10: 'g', 11: 'h', 12: 'i', 13: 'j', 14: 'k', 15: 'l', 16: 'm', 17: 'n', 18: 'o', 19: 'p', 20: 'q', 21: 'r', 22: 's', 23: 't', 24: 'u', 25: 'v', 26: 'w', 27: 'x', 28: 'y', 29: 'z'}
source_letter_to_int {'<PAD>': 0, '<UNK>': 1, '<GO>': 2, '<EOS>': 3, 'a': 4, 'b': 5, 'c': 6, 'd': 7, 'e': 8, 'f': 9, 'g': 10, 'h': 11, 'i': 12, 'j': 13, 'k': 14, 'l': 15, 'm': 16, 'n': 17, 'o': 18, 'p': 19, 'q': 20, 'r': 21, 's': 22, 't': 23, 'u': 24, 'v': 25, 'w': 26, 'x': 27, 'y': 28, 'z': 29}

target_int_to_letter {0: '<PAD>', 1: '<UNK>', 2: '<GO>', 3: '<EOS>', 4: 'a', 5: 'b', 6: 'c', 7: 'd', 8: 'e', 9: 'f', 10: 'g', 11: 'h', 12: 'i', 13: 'j', 14: 'k', 15: 'l', 16: 'm', 17: 'n', 18: 'o', 19: 'p', 20: 'q', 21: 'r', 22: 's', 23: 't', 24: 'u', 25: 'v', 26: 'w', 27: 'x', 28: 'y', 29: 'z'}
target_letter_to_int {'<PAD>': 0, '<UNK>': 1, '<GO>': 2, '<EOS>': 3, 'a': 4, 'b': 5, 'c': 6, 'd': 7, 'e': 8, 'f': 9, 'g': 10, 'h': 11, 'i': 12, 'j': 13, 'k': 14, 'l': 15, 'm': 16, 'n': 17, 'o': 18, 'p': 19, 'q': 20, 'r': 21, 's': 22, 't': 23, 'u': 24, 'v': 25, 'w': 26, 'x': 27, 'y': 28, 'z': 29}

由于原序列和目标序列都是26个英语字母,如果是其他情况,如翻译,他们就是分别是单词或者中文文字库
'''

  3)source和target序列进行数值转换

'''
有了上面的序列字符组成的字符库,我们需要对原序列和目标序列转换成对应的数字序列,这里分别列出前十个:
source_int = [[5, 22, 4, 20, 20], [17, 19, 28], [15, 5, 26, 24, 13], [5, 20, 25], [14, 12, 4, 15], [23, 7, 7, 4, 16], [8, 7, 27, 19, 13, 19, 10], [17, 22, 19, 25], [11, 24, 15, 18, 29], [14, 16, 6, 15, 20], ......](如序列bsaqq对应的数值序列为[5, 22, 4, 20, 20])
target_int = [[4, 5, 20, 20, 22, 3], [17, 19, 28, 3], [5, 13, 15, 24, 26, 3], [5, 20, 25, 3], [4, 12, 14, 15, 3], [4, 7, 7, 16, 23, 3], [7, 8, 10, 13, 19, 19, 27, 3], [17, 19, 22, 25, 3],  [11, 15, 18, 24, 29, 3], [6, 14, 15, 16, 20, 3], ......](target序列abqqs对应的数值序列就是排序[4, 5, 20, 20, 22],我们为每个target序列加上EOS,也就是数字3)
综上,原序列和target序列就被数值化
'''

 

  下面我们讲几个概念:

  • Epoch(时期):
    当一个完整的数据集通过了神经网络一次并且返回了一次,这个过程称为一次epoch。(也就是说,所有训练样本在神经网络中进行了一次正向传播一次反向传播
    再通俗一点,一个Epoch就是将所有训练样本训练一次的过程。

  然而,当一个Epoch的样本(也就是所有的训练样本)数量可能太过庞大(对于计算机而言),就需要把它分成多个小块,也就是就是分成多个Batch 来进行训练

  • Batch(批 / 一批样本):
    将整个训练样本分成若干个Batch

  • Batch_Size(批大小):
    每批样本的大小

  • Iteration(一次迭代):
    训练一个Batch就是一次Iteration(这个概念跟程序语言中的迭代器相似)

  这里我们把epoch设置为60,batch_size=128

# 将数据集分割为train和validation
train_source = source_int[batch_size:]
train_target = target_int[batch_size:]
# 留出一个batch进行验证
valid_source = source_int[:batch_size]
valid_target = target_int[:batch_size]

  这样我们把source序列和target(分别有10000条序列)拆分为训练集和验证集(前128条为验证序列

 

 4)encoder层

  下面我们看看encoder部分干了什么事情:

# Encoder                                                                                                                          
# 在Encoder端,我们需要进行两步,第一步要对我们的输入进行Embedding,再把Embedding以后的向量传给RNN进行处理                         
# 在Embedding中,我们使用tf.contrib.layers.embed_sequence,它会对每个batch执行embedding操作                                        
def get_encoder_layer(input_data, rnn_size, num_layers, source_sequence_length, source_vocab_size, encoding_embedding_size): 
	"""                                                                                                                        
	构造Encoder层                                                                                                              
	参数说明:                                                                                                                 
	- input_data: 输入tensor                                                                                                   
	- rnn_size: rnn隐层结点数量                                                                                                
	- num_layers: 堆叠的rnn cell数量                                                                                           
	- source_sequence_length: 源数据的序列长度,也就是有效长度                                                                 
	- source_vocab_size: 源数据的词典大小                                                                                      
	- encoding_embedding_size: embedding的大小                                                                                 
	"""                                                                                                                        
	# Encoder embedding,这里进行embedding原理是什么呢?我只提供了序列数字,以及词汇数量,就完成了向量化                       
	encoder_embed_input = tf.contrib.layers.embed_sequence(input_data, source_vocab_size, encoding_embedding_size)             
                                                                                                                                   
	# RNN cell                                                                                                                 
	def get_lstm_cell(rnn_size):                                                                                               
		lstm_cell = tf.contrib.rnn.LSTMCell(rnn_size, initializer=tf.random_uniform_initializer(-0.1, 0.1, seed=2))        
		return lstm_cell                                                                                                   
                                                                                                                                   
	cell = tf.contrib.rnn.MultiRNNCell([get_lstm_cell(rnn_size) for _ in range(num_layers)])                                   
	encoder_output, encoder_state = tf.nn.dynamic_rnn(                                                                         
		cell, encoder_embed_input, sequence_length=source_sequence_length, dtype=tf.float32)                               
                                                                                                                                   
	return encoder_output, encoder_state                                                                                       

  我们假如输入的一个batch是128 * 7的tensor(即:128个句子样本,每个样本句子长度为7),rnn_size = 60,num_layers = 2,source_sequence_length为一个list,保留了128个句子样本中每个句子样本的原长,encoding_embedding_size = 15,即每个字符嵌入向量长度15,我们演示上面的encoder过程,别眨眼啊:

  我们可以根据每个batch中最长序列的长度,对其他序列进行pad填充:

'''只展示前六个
valid_sources_batch:
[[5,22,4,20,20,0,0], [17,19,28,0,0,0,0], [15,5,26,24,13,0,0], [,5,20,25,0,0,0,0],[14,12,4,15,0,0,0], [23,7,7,4,16,0,0]]

valid_targets_batch:
[[4,5,20,20,22,3,0,0], [17,19,28,3,0,0,0,0], [5,13,15,24,26,3,0,0], [,5,20,25,3,0,0,0,0], [4,12,14,15,3,0,0,0], [4,7,7,16,23,3,0,0]]

valid_targets_lengths: [6, 4, 6, 4, 5, 6]
valid_sources_lengths: [5, 3, 5, 3, 4, 5]
'''

  上面是验证集的展示,训练集从129行开始

  同理,每个batch处理方式相同,我们以第一个epoch的第一个batch为例,讲述训练过程:其中第一个batch是从source_int[batch_size:256]和target_int[batch_size:256],也就说source_int和target_int的下标:128到256,以前几行为例:

  我们看一下转数字并pading之后:sources_batch[0:10]:这里是训练集的第一batch,也就说从原序列129行开始(前128行是验证集),这里只是展示129-138行的序列

'''
sources_batch[0:10]
[[24 9 17 0 0 0 0]  ufn对应的数字进行pad后
[14 13 0 0 0 0 0]  kj对应的数字进行pad后
[20 26 23 19 10 20 0] qwtpgq对应的数字进行pad后
[26 23 15 0 0 0 0]
[11 25 0 0 0 0 0]
[17 25 17 0 0 0 0]
[13 6 22 0 0 0 0]
[21 29 0 0 0 0 0]
[ 8 29 27 21 0 0 0]
[23 13 8 0 0 0 0]] tje对应的数字进行pad后

[3, 2, 6, 3, 2, 3, 3, 2, 4, 3]这个保存的是他们未进行pad时各自序列长度

targets_batch[0:10]:
[[ 9 17 24 3 0 0 0 0]
[13 14 3 0 0 0 0 0]
[10 19 20 20 23 26 3 0]
[15 23 26 3 0 0 0 0]
[11 25 3 0 0 0 0 0]
[17 17 25 3 0 0 0 0]
[ 6 13 22 3 0 0 0 0]
[21 29 3 0 0 0 0 0]
[ 8 21 27 29 3 0 0 0]
[ 8 13 23 3 0 0 0 0]]

[4, 3, 7, 4, 3, 4, 4, 3, 5, 4] 与上面的sources_batch对应,注意,每个target序列都进行了EOS(数字3)的添加,所以每个target序列都比原序列长度多1
'''

  OK,我们至此已经有了一个batch 的input(128个序列),其中第一对训练序列如:source-[24 9 17 0 0 0 0]长度为3,已经pad处理,target-[ 9 17 24 3 0 0 0 0]长度为4,加了EOS和PAD

  其实我们每一batch的source是二维矩阵:$128 * 7$,这里的7不是固定的,因为第一batch中source最长是7,所以这里是7,这个列数由每一批中序列最长的序列长度决定(但要注意这个序列不会太长哦)

  然后我们可以对其进行encode:

  在Encoder端,我们需要进行两步,第一步要对我们的输入进行Embedding,再把Embedding以后的向量传给LSTM进行处理。

  在Embedding中,我们使用tf.contrib.layers.embed_sequence,它会对每个batch执行embedding操作,我们设置encoding_embedding_size = 15,也就说每个序列中的每个字符(也就是每个字母)从一维扩张到15维,我们的sources_batch也就是我们的input,会被embedding:

  sources_batch:一batch为128,每个序列长度为7(比如哈,可能其他batch中有条序列长度大于7,那么该批次就每个序列长度pad后就为最长序列长度)

  同时,我们要知道我们的source序列总共有多少种字符(本项目中为26个字母外加四种特殊字符,也就是30种)

  我们的source_batch本来是128 * 7,被embeding后就变成了128 * 7 * 15,我们把第一batch训练集的第一个序列[24 9 17 0 0 0 0]ufn进行embeding后展示出来:(7*15)

# Encoder embedding
encoder_embed_input = tf.contrib.layers.embed_sequence(input_data, source_vocab_size, encoding_embedding_size)

#下面是一行embeding后结果展示
'''
[[-0.36065042 -0.17114215 -0.05008167  0.21896338 -0.18541089  0.19533116
  -0.12902524  0.07539368 -0.23459466  0.21035886 -0.18695556  0.11775276
  -0.0019387  -0.12290165  0.02873832]
 [-0.25419667 -0.10173807  0.22848481 -0.0533388  -0.00930399 -0.02172154
   0.13963962  0.02923623  0.09819978 -0.12081417 -0.23042265 -0.28695342
   0.1045948   0.04559454  0.09481269]
 [ 0.19500643  0.28019369  0.10704392  0.03675491  0.35994822  0.08842304
  -0.15914266  0.33308589 -0.12992106 -0.13675191 -0.17976578 -0.20379627
  -0.29855976  0.03020796 -0.30255833]
 [-0.06587648 -0.05019096 -0.10603911  0.33219868  0.36303657  0.16047823
  -0.0563249  -0.13622303 -0.24733457 -0.30284318  0.03862795 -0.07534647
   0.28796619 -0.07870796  0.16098654]
 [-0.06587648 -0.05019096 -0.10603911  0.33219868  0.36303657  0.16047823
  -0.0563249  -0.13622303 -0.24733457 -0.30284318  0.03862795 -0.07534647
   0.28796619 -0.07870796  0.16098654]
 [-0.06587648 -0.05019096 -0.10603911  0.33219868  0.36303657  0.16047823
  -0.0563249  -0.13622303 -0.24733457 -0.30284318  0.03862795 -0.07534647
   0.28796619 -0.07870796  0.16098654]
 [-0.06587648 -0.05019096 -0.10603911  0.33219868  0.36303657  0.16047823
  -0.0563249  -0.13622303 -0.24733457 -0.30284318  0.03862795 -0.07534647
   0.28796619 -0.07870796  0.16098654]]
'''

  这样我们一个batch的Input就是128*7*15,也就是我们已经获得encoder_embed_input,下面我们可以进行encode了,我们先定义一个LSTM单元

# RNN cell
rnn_size = 50
def get_lstm_cell(rnn_size):
    lstm_cell = tf.contrib.rnn.LSTMCell(rnn_size, initializer=tf.random_uniform_initializer(-0.1, 0.1, seed=2))
    return lstm_cell

  我们先说说这个rnn_size = 50是什么意思,假设我们当前x输入是某个字符-15维向量,那么这个rnn_size也就是num_units就说下面的h,即就是一个50维的向量,h和x进行concatenate,就得到了65维的向量作为input

 

  下面我们参考下面的链接,继续讲解,在使用Tensorflow跑LSTM的试验中, 有个num_units(也就是上面所说的rnn_size)的参数,这个参数是什么意思呢?

  先总结一下,num_units这个参数的大小就是LSTM输出结果的维度。例如num_units=128(上面的例子rnn_size=50), 那么LSTM网络最后输出就是一个128维的向量

 

  我们先换个角度举个例子,最后再用公式来说明。

  假设在我们的训练数据中,每一个样本 x(在这里指每一行序列) 是 $7*15$(上面的例子是7*15:7表示一个序列长度,15表示每个元素(这里为一个字符)被embeding成15维向量) 维的一个矩阵,那么将这个样本的每一行当成一个输入,通过7个时间步骤展开LSTM,在每一个LSTM单元,我们输入一行维度为15的向量,如下图所示(修改别人的图片了,自己PS的,不要被row image字样影响,就当作一个向量就行)

  那么,对每一个LSTM单元,参数num_units=50的话,就是每一个单元的输出为 50*1 的向量,在展开的网络维度来看,如下图所示,对于每一个输入15维的向量,LSTM单元都把它映射到50维的维度, 在下一个LSTM单元时,LSTM会接收上一个50维的输出,和新的15维的输入,处理之后再映射成一个新的50维的向量输出,就这么一直处理下去,知道网络中最后一个LSTM单元,输出一个50维的向量

  从LSTM的公式的角度看是什么原理呢?我们先看一下LSTM的结构和公式:

  参数num_units=50的话,(在机器学习领域,我们说的向量都是列向量)

  对于公式 (1) ,h=50∗1维, x=15∗1维,[h,x]便等于65∗1维,(由于h要输出同样是50维),W=50*65维,W∗[h,x]=50∗65∗65∗1=50∗1维,b=50*1维,所以f=50*1维

  对于公式 (2) 和 (3),同上可分析得 i=50∗1维,$\hat C$=50*1维

  对于公式(4),f(t)=50*1维,C(t−1)=50∗1,f(t).∗C(t−1) = 50*1 点乘 50*1 = 50*1,i点乘$\hat C$=50*1点乘50*1=50*1维,所以$C_t$=50*1维

  对于公式 (5) 和 (6) , 同理可得 O=50∗1=50∗1 维,h=O点乘tanh(C)=50∗1

  所以最后LSTM单元输出的h就是 50*1 的向量

  以上就是 num_units 参数的含义

 

  我们接着讲:上面我们只是讲了单隐层的LSTM,比如现在输入的是ufn序列中的u(u已经被embeding成15维向量),我们希望对应的target为fnu,即希望输出f(u -> f),状态向量继续流入下一个LSTM单元,同时输出也流入下一个单元与新的输入f合并成新的65维向量,继续进行上面公式(1)到(6)的操作,请注意:我们目前只是单隐层的LSTM单元前后连接,我们也可以创建多隐层的LSTM,上下叠加(这里前后和上下叠加是形象化解释,便于理解),请参考

  我们使用tensorflow可以轻松的创建多隐藏的LSTM:

# RNN cell,这里rnn_size=50,num_layers=2,就是说我们创建的是双隐层的LISTM
#这里注意:我们的两个隐层,他们的rnn_size都是50,我们也可以为每个隐层设置不同的rnn_size
#这里的rnn_size,就说tensorflow该函数官方参数num_units
def get_lstm_cell(rnn_size):
    lstm_cell = tf.contrib.rnn.LSTMCell(rnn_size, initializer=tf.random_uniform_initializer(-0.1, 0.1, seed=2))
    return lstm_cell

cell = tf.contrib.rnn.MultiRNNCell([get_lstm_cell(rnn_size) for _ in range(num_layers)])

  

  目前,我们已经可以为每一batch(128个样本,也就是128行序列)数据进行embeding(从2维变成了3维:如输入Input是128*7,字嵌入后变成128*7*15),并且已经创建了双隐层的LSTM,而且还已经知道了每个序列的长度(未进行PAD的长度,也就是我们序列的有效长度,因为进行PAD后长度都是7了),我们可以利用tf.nn.dynamic_rnn()获取output和state

def get_encoder_layer(input_data, rnn_size, num_layers,
                   source_sequence_length, source_vocab_size, 
                   encoding_embedding_size):

    '''
    构造Encoder层
    
    参数说明:
    - input_data: 输入tensor
    - rnn_size: rnn隐层结点数量
    - num_layers: 堆叠的rnn cell数量
    - source_sequence_length: 源数据的序列长度
    - source_vocab_size: 源数据的词典大小
    - encoding_embedding_size: embedding的大小
    '''
    # Encoder embedding
    encoder_embed_input = tf.contrib.layers.embed_sequence(input_data, source_vocab_size, encoding_embedding_size)

    # RNN cell
    def get_lstm_cell(rnn_size):
        lstm_cell = tf.contrib.rnn.LSTMCell(rnn_size, initializer=tf.random_uniform_initializer(-0.1, 0.1, seed=2))
        return lstm_cell

    cell = tf.contrib.rnn.MultiRNNCell([get_lstm_cell(rnn_size) for _ in range(num_layers)])
    
    encoder_output, encoder_state = tf.nn.dynamic_rnn(cell, encoder_embed_input, 
                                                      sequence_length=source_sequence_length, dtype=tf.float32)
    
    return encoder_output, encoder_state

  这里需要记录一下encoder_output和encoder_state的shape,划重点了,我们的Input是一个128* 7 * 15的矩阵,我们开始讲解上面的encoder过程:如下图

  有了上面的图,我们可以测试一下 encoder_output, encoder_state这两个结果的维度,看是否和上图维度一致(是一致的)

  这里的data下载,请查看下载链接,提取码:9e6d

import sys
import numpy as np
import os
os.environ['TF_CPP_MIN_LOG_LEVEL'] = '2'

import tensorflow as tf


class Processing:
	def __init__(self, source_file='data/letters_source.txt', target_file='data/letters_target.txt'):
		self.source_data = self.safe_open(source_file)
		self.target_data = self.safe_open(target_file)

		# 构造映射表
		self.source_int_to_letter, self.source_letter_to_int = self.extract_character_vocab(self.source_data)
		self.target_int_to_letter, self.target_letter_to_int = self.extract_character_vocab(self.target_data)

		# 对字母进行转换
		self.source_int = self.char_to_int('source')
		self.target_int = self.char_to_int('target')

	def safe_open(self, file_name):
		with open(file_name, 'r', encoding='utf-8') as f:
			data = f.read().strip()
		return data

	def extract_character_vocab(self, data):
		"""
		构造映射表
		< PAD>: 补全字符。
		< EOS>: 解码器端的句子结束标识符。
		< UNK>: 低频词或者一些未遇到过的词等。
		< GO>: 解码器端的句子起始标识符
		"""
		special_words = ['<PAD>', '<UNK>', '<GO>', '<EOS>']
		set_words = sorted(list(set([character for line in data.split('\n') for character in line])))
		# 这里要把四个特殊字符添加进词典
		int_to_vocab = {idx: word for idx, word in enumerate(special_words + set_words)}
		vocab_to_int = {word: idx for idx, word in int_to_vocab.items()}
		return int_to_vocab, vocab_to_int

	def char_to_int(self, sequence):
		if sequence == 'source':
			return [[self.source_letter_to_int.get(letter, self.source_letter_to_int['<UNK>']) for letter in line]
					for line in self.source_data.split('\n')]
		elif sequence == 'target':
			return [[self.target_letter_to_int.get(letter, self.target_letter_to_int['<UNK>']) for letter in line] + [self.target_letter_to_int['<EOS>']]
					for line in self.target_data.split('\n')]
		else:
			print('Please input the sequence you  want to convert into int!')
			sys.exit()

	def pad_sentence_batch(self, sentence_batch, pad_int):
		"""
		对batch中的序列进行补全,保证batch中的每行都有相同的sequence_length
		参数:
		- sentence batch
		- pad_int: <PAD>对应索引号
		"""
		max_sentence = max([len(sentence) for sentence in sentence_batch])
		return [sentence + [pad_int] * (max_sentence - len(sentence)) for sentence in sentence_batch]

	def get_batches(self, targets, sources, batch_size, source_pad_int, target_pad_int):
		"""
		定义生成器,用来获取batch
		"""
		print('sources length in get_batches:', len(sources), len(sources) // batch_size)
		for batch_i in range(0, len(sources) // batch_size):
			start_i = batch_i * batch_size
			sources_batch = sources[start_i:start_i + batch_size]
			targets_batch = targets[start_i:start_i + batch_size]
			# 补全序列
			pad_sources_batch = np.array(self.pad_sentence_batch(sources_batch, source_pad_int))
			pad_targets_batch = np.array(self.pad_sentence_batch(targets_batch, target_pad_int))

			# 记录每条记录的长度
			targets_lengths = []
			for target in targets_batch:
				targets_lengths.append(len(target))

			source_lengths = []
			for source in sources_batch:
				source_lengths.append(len(source))

			yield pad_targets_batch, pad_sources_batch, targets_lengths, source_lengths



class EncodeDecodeModelBuild:
	def __init__(self):
		pass

	# Encoder
	# 在Encoder端,我们需要进行两步,第一步要对我们的输入进行Embedding,再把Embedding以后的向量传给RNN进行处理
	# 在Embedding中,我们使用tf.contrib.layers.embed_sequence,它会对每个batch执行embedding操作
	def get_encoder_layer(self, input_data, rnn_size, num_layers, source_sequence_length, source_vocab_size, encoding_embedding_size):
		"""
		构造Encoder层
		参数说明:
		- input_data: 输入tensor
		- rnn_size: rnn隐层结点数量
		- num_layers: 堆叠的rnn cell数量
		- source_sequence_length: 源数据的序列长度,也就是有效长度
		- source_vocab_size: 源数据的词典大小
		- encoding_embedding_size: embedding的大小
		"""
		# Encoder embedding,这里进行embedding原理是什么呢?我只提供了序列数字,以及词汇数量,就完成了向量化
		encoder_embed_input = tf.contrib.layers.embed_sequence(input_data, source_vocab_size, encoding_embedding_size)

		# RNN cell
		def get_lstm_cell(rnn_size):
			lstm_cell = tf.contrib.rnn.LSTMCell(rnn_size, initializer=tf.random_uniform_initializer(-0.1, 0.1, seed=2))
			return lstm_cell

		cell = tf.contrib.rnn.MultiRNNCell([get_lstm_cell(rnn_size) for _ in range(num_layers)])
		encoder_output, encoder_state = tf.nn.dynamic_rnn(
			cell, encoder_embed_input, sequence_length=source_sequence_length, dtype=tf.float32)

		return encoder_output, encoder_state

if __name__ == '__main__':
	process = Processing()
	print('source_seq:', process.source_data.split('\n')[0:10])
	print('target_seq:', process.target_data.split('\n')[0:10])

	print('source_int_to_letter', process.source_int_to_letter)
	print('source_letter_to_int', process.source_letter_to_int)

	print('target_int_to_letter', process.target_int_to_letter)
	print('target_letter_to_int', process.target_letter_to_int)

	source_letter_to_int = process.source_letter_to_int
	target_letter_to_int = process.target_letter_to_int

	encode_decode = EncodeDecodeModelBuild()  # 实例化,没什么初始化操作

	# 超参数
	# Batch Size
	batch_size = 128
	# RNN Size
	rnn_size = 50
	# Number of Layers
	num_layers = 2
	# Embedding Size
	encoding_embedding_size = 15

	valid_source = process.source_int[:batch_size]
	valid_target = process.target_int[:batch_size]
	print('valid_source[0:10]:', valid_source[0:10])
	print('valid_target[0:10]:', valid_target[0:10])

	(valid_targets_batch, valid_sources_batch, valid_targets_lengths, valid_sources_lengths) = next(
		process.get_batches(
			valid_target, valid_source, batch_size,
			process.source_letter_to_int['<PAD>'],
			process.target_letter_to_int['<PAD>'])
		)

	print('valid_sources_batch[0:10]:', valid_sources_batch[0:10])
	print('valid_targets_batch[0:10]:', valid_targets_batch[0:10])
	print('valid_sources_lengths[0:10]:', valid_sources_lengths[0:10])
	print('valid_targets_lengths[0:10]:', valid_targets_lengths[0:10])

	input_data = valid_sources_batch
	targets = valid_targets_batch

	source_sequence_length = valid_sources_lengths
	target_sequence_length = valid_targets_lengths

	max_target_sequence_length = 7
	source_vocab_size = len(source_letter_to_int)

	encoder_output, encoder_state = encode_decode.get_encoder_layer(
		input_data, rnn_size, num_layers, source_sequence_length, source_vocab_size, encoding_embedding_size)


	with tf.Session() as sess:
		sess.run(tf.global_variables_initializer())
	
		print('encoder_output.shape:', sess.run(encoder_output).shape)
		print('\n')
		print(sess.run(encoder_state[0]))
		print('0-c:', sess.run(encoder_state[0][0]).shape)
		print('0-h:', sess.run(encoder_state[0][1]).shape)
	
		print('\n')
		print(sess.run(encoder_state[1]))
		print('1-c:', sess.run(encoder_state[1][0]).shape)
		print('1-h:', sess.run(encoder_state[1][1]).shape)

'''
encoder_output.shape: (128, 7, 50)
LSTMStateTuple(c=array([[-0.04304752, -0.03509561,  0.08023848, ...,  0.00869391,
        -0.05691399, -0.0037792 ],
       [ 0.00383653, -0.00961697, -0.00571259, ..., -0.06620879,
        -0.03612325, -0.05058336],
       [-0.01471607, -0.00054711, -0.00437702, ..., -0.04058436,
        -0.08962905, -0.00147169],
       ...,
       [ 0.05962741,  0.03080438, -0.01021096, ...,  0.03458264,
        -0.01859237, -0.00854286],
       [ 0.10512877,  0.05675253, -0.05238747, ...,  0.08558378,
         0.03848773,  0.0153399 ],
       [-0.00535806, -0.00980609, -0.00647817, ...,  0.01386383,
         0.00361691, -0.01494723]], dtype=float32), h=array([[-0.02223058, -0.01775076,  0.03938275, ...,  0.00433571,
        -0.02808682, -0.00186137],
       [ 0.00186763, -0.00502355, -0.00283817, ..., -0.0320057 ,
        -0.01752974, -0.02522181],
       [-0.00723557, -0.00028466, -0.00215382, ..., -0.02022631,
        -0.04316105, -0.00071936],
       ...,
       [ 0.02942469,  0.01522003, -0.00491344, ...,  0.01708112,
        -0.00908282, -0.00431359],
       [ 0.0540871 ,  0.02825295, -0.02745056, ...,  0.04257125,
         0.01909489,  0.00735466],
       [-0.00273725, -0.00494143, -0.00335784, ...,  0.00702268,
         0.00177948, -0.00720975]], dtype=float32))
0-c: (128, 50)
0-h: (128, 50)


LSTMStateTuple(c=array([[-1.5820017e-02, -2.6942336e-03, -1.0050132e-03, ...,
         8.5409246e-03,  6.6550612e-03,  8.1391260e-03],
       [ 7.7295029e-03,  4.1437177e-03,  4.6428009e-03, ...,
         1.1356347e-02,  8.1963371e-05, -7.8776069e-03],
       [ 1.0825472e-02,  4.3439609e-03,  1.2341643e-02, ...,
         1.4018469e-02,  1.0256685e-02,  8.7942984e-03],
       ...,
       [-3.0825241e-03, -2.2337481e-05, -6.5338850e-04, ...,
        -6.1642872e-03, -1.1415123e-03,  7.1417345e-03],
       [-1.2705816e-02, -1.6099531e-03,  2.2869529e-02, ...,
        -2.3452278e-02, -7.6105753e-03,  7.6716589e-03],
       [-1.0074907e-03,  3.4129494e-05, -1.8248253e-03, ...,
        -5.8616372e-04, -1.6492521e-03, -8.0834865e-04]], dtype=float32), h=array([[-7.8813164e-03, -1.3356666e-03, -5.0444109e-04, ...,
         4.2508096e-03,  3.3232903e-03,  4.0544183e-03],
       [ 3.8475350e-03,  2.0742973e-03,  2.3326257e-03, ...,
         5.6687519e-03,  4.0923649e-05, -3.9418442e-03],
       [ 5.4338807e-03,  2.1625140e-03,  6.1921040e-03, ...,
         6.9652013e-03,  5.1008761e-03,  4.3697306e-03],
       ...,
       [-1.5396455e-03, -1.1132359e-05, -3.2613808e-04, ...,
        -3.0813138e-03, -5.6903326e-04,  3.5529216e-03],
       [-6.3411035e-03, -8.0282643e-04,  1.1467443e-02, ...,
        -1.1772511e-02, -3.8120230e-03,  3.8370693e-03],
       [-5.0305878e-04,  1.7079228e-05, -9.1262913e-04, ...,
        -2.9348588e-04, -8.2494150e-04, -4.0417191e-04]], dtype=float32))
1-c: (128, 50)
1-h: (128, 50)
'''

 

  说完了encode,我们说一下decode,先对target进行处理

# Decoder
# 先对target数据进行预处理
def process_decoder_input(self, data, vocab_to_int, batch_size):
        """
        补充<GO>,并移除最后一个字符
        """
        # cut掉最后一个字符
        data = tf.constant(data, dtype=tf.int32)
        ending = tf.strided_slice(data, [0, 0], [batch_size, -1], [1, 1])
        #  vocab_to_int['<GO>']在本例中是2,经过在列维度上的合并,每个序列都是以GO(对应数值为2)开头
        decoder_input = tf.concat([tf.fill([batch_size, 1], vocab_to_int['<GO>']), ending], 1)

        return decoder_input

 

  前面我们已经有了该batch的targets序列,target_letter_to_int以及batch_size=128,那么我们看看上面的process_decoder_input做了什么骚操作:

'''
valid_targets_batch[0:10]: 
[[ 4  5 20 20 22  3  0  0]
 [17 19 28  3  0  0  0  0]
 [ 5 13 15 24 26  3  0  0]
 [ 5 20 25  3  0  0  0  0]
 [ 4 12 14 15  3  0  0  0]
 [ 4  7  7 16 23  3  0  0]
 [ 7  8 10 13 19 19 27  3]
 [17 19 22 25  3  0  0  0]
 [11 15 18 24 29  3  0  0]
 [ 6 14 15 16 20  3  0  0]]

经过process_decoder_input(targets, target_letter_to_int, batch_size)骚操作后
就在target首位添加了数字2:'GO',并去掉了原来target序列补充的一个数字0

decoder_input: 
[[ 2  4  5 20 20 22  3  0]
 [ 2 17 19 28  3  0  0  0]
 [ 2  5 13 15 24 26  3  0]
 [ 2  5 20 25  3  0  0  0]
 [ 2  4 12 14 15  3  0  0]
 [ 2  4  7  7 16 23  3  0]
 [ 2  7  8 10 13 19 19 27]
 [ 2 17 19 22 25  3  0  0]
 [ 2 11 15 18 24 29  3  0]
 [ 2  6 14 15 16 20  3  0]]

'''

  对tf.strided_slice,tf.fill,tf.concat感兴趣的可以参考这个例子

 

  我们先进行embedding,即对decoder_input进行embedding,这样我们就获得了一个三维矩阵:128 * 8 * 15

# 1. Embedding
target_vocab_size = len(target_letter_to_int)
decoder_embeddings = tf.Variable(tf.random_uniform([target_vocab_size, decoding_embedding_size]))
decoder_embed_input = tf.nn.embedding_lookup(decoder_embeddings, decoder_input)

  如何理解呢?不难,请参考

  然后构建decoder中RNN单元,代码和encoder一样,不做解释

# 1. Embedding
target_vocab_size = len(target_letter_to_int)
# 创建一个shape为[target_vocab_size, decoding_embedding_size]的矩阵变量,这里为[30, 15]
decoder_embeddings = tf.Variable(tf.random_uniform([target_vocab_size, decoding_embedding_size]))
decoder_embed_input = tf.nn.embedding_lookup(decoder_embeddings, decoder_input)

# 2. 构造Decoder中的RNN单元
def get_decoder_cell(rnn_size):
        decoder_cell = tf.contrib.rnn.LSTMCell(rnn_size, initializer=tf.random_uniform_initializer(-0.1, 0.1, seed=  2      
        return decoder_cell

cell = tf.contrib.rnn.MultiRNNCell([get_decoder_cell(rnn_size) for _ in range(num_layers)])

 

  然后构建输出全连接层(获得每个字符的输出概率)

from tensorflow.python.layers.core import Dense
output_layer = Dense(target_vocab_size,
    kernel_initializer = tf.truncated_normal_initializer(mean = 0.0, stddev=0.1))

  全连接层例子,自己没找到合适的Dense()函数例子,但可以参考这个理解

 

 

标签:target,项目,int,batch,rnn,seq2seq,source,详解,size
From: https://www.cnblogs.com/always-fight/p/12374380.html

相关文章

  • Django项目记录
    在线教育平台Django项目#manage.pyos.environ.setdefault("DJANGO_SETTINGS_MODULE","mxonline.settings")是什么意思?这行代码是用于设置Django项目的配置模块。在Django项目中,有一个名为settings.py的文件,其中定义了项目的各种配置选项。DJANGO_SETTINGS_MODULE是一个环境变......
  • 生信项目之生信名词解释
    目录一、高通量测序与Hi-C测序二、宏基因组三、启动子,终止子,起始密码子,终止密码子四、Read,Reads,Contig,Contigs五、CDS与ORF六、测序深度与覆盖度七、进化树,分子树,系统发生树一、高通量测序与Hi-C测序高通量测序技术(High-throughputsequencing)又称“下一代”测序技术......
  • js数组方法详解
    数组是js中最常用到的数据集合,其内置的方法有很多,熟练掌握这些方法,可以有效的提高我们的工作效率,同时对我们的代码质量也是有很大影响。 创建数组一.字面量方式constarray=[1,2,3,4,5]; 二.使用Array构造方法1.无参构造-创建一个长度为0的空数组constarray......
  • 项目管理工具JIRA--使用方法
    1.1项目(缺陷)管理工具禅道(ZenTao)、Mantis、BugFree、Bugzilla、QualityCenter、jira(鸡爪子)、Redmine、Effevo(搜狗)1.2功能自动化工具QTP:QuickTestProfessionalSelenium(Java、Python)1.3性能自动化工具LR:LoadRunnerJMeter1.4白盒工具Junit、JTest、C++TestJIRA是......
  • 深入了解鸿鹄工程项目管理系统源码:功能清单与项目模块的深度解析
    工程项目管理软件是现代项目管理中不可或缺的工具,它能够帮助项目团队更高效地组织和协调工作。本文将介绍一款功能强大的工程项目管理软件,该软件采用先进的Vue、Uniapp、Layui等技术框架,涵盖了项目策划决策、规划设计、施工建设到竣工交付、总结评估、运维运营等全过程。通过该......
  • vue2项目升级vue3的小变化
    1、template的slot写法变化vue2:<templateslot-scope="{row,index}"slot="action">vue3:<template#action="{row,index}">2、路由页面缓存的写法变化vue2:<keep-alive:include="cacheList"><router-view/>......
  • electron 项目 代码片段工具
    文章目录概要项目目录技术栈安装效果添加代码代码中心配置概要electron实战项目,一个助力编程的代码片段工具。下载地址:https://github.com/QAQDFAFD/save-code项目目录技术栈electronvitevue3tailwindcsspiniabytemdantdvvue-routervue-codemirrorpinia-plug......
  • 访问数据库-BotBattle项目
    目录访问数据库流程Java框架各层级4层对象的调用流程耦合性与分层访问数据库流程flowchartLR开发人员-->C(连接数据库JDBC)C-->D(MyBatis-Plus映射和执行SQL)D-->E(数据库被访问)flowchartRLA(数据库)--查询结果-->B(MyBatis-Plus)B--将这些数据转换成Java......
  • 开源项目&免费接口:高频词提取挖掘文本核心价值
     一、开源项目介绍一款多模态AI能力引擎,专注于提供自然语言处理(NLP)、情感分析、实体识别、图像识别与分类、OCR识别和语音识别等接口服务。该平台功能强大,支持本地化部署,并鼓励用户体验和开发者共同完善,以实现开源共享。可以本地化部署,也可以云端SaaS调用,微信扫码即可登录。......
  • 基于Java中的SSM框架实现宝康药房销售管理系统项目【项目源码+论文说明】
    基于Java中的SSM框架实现宝康药房销售管理系统演示摘要随着我国市场经济的蓬勃发展和人们对医药产品需求的迅速增加,医药销售行业正处于一个高速发展的时期。行业的快速发展必然导致竞争的加剧,面对药品销售业日益严酷的竟争现实,加强管理、提高工作效率和改善服务质量成了急......