本篇文章7881字,读完约20分钟
雷锋。这篇文章的作者是刘冲。原文包含在作者的个人博客中。雷锋。com已被授权。
最近,我们正在研究rnn模型。为简单起见,本文采用简单的二进制序列作为训练数据,而不是实现特定的纸张模拟。主要目的是理解rnn的原理以及如何在张量流中构造一个简单的基本模型结构。代码指的是这个博客。
数据集首先,让我们看看实验数据的结构:
输入数据x:在时间t,xt的值有50%的概率为1,50%的概率为0;
输出数据y:在实践t中,yt的值有50%的概率为1,50%的概率为0。此外,如果“XT-3 = = 1 ”, yt为1的概率增加50%,如果“XT-8 = = 1 ”, yt为1的概率减少25%。如果同时满足上述两个条件,。
可以看出,y和x有两个依赖性,一个是t-3,另一个是t-8。我们实验的目的是测试rnn是否能够捕捉到Y和x之间的这两种依赖关系。以交叉熵作为评价标准,实验得到了以下三个理想的实验结果:
如果rnn没有学习到任何相关性,那么yt为1的概率是0.625(0.5+0.5*0.5-0.5*0.25),因此所获得的交叉熵应该是0.66(-(0.625 * NP . log(0.625)+0.375 * NP . log(0.375)
如果rnn知道第一个相关性,也就是说,当xt-3为1时,yt必须为1。那么,最终的交叉熵应该是0.52(-0.5 *(0.875 * NP . log(0.875)+0.125 * NP . log(0.125))-0.5 *(0.625 * NP . log(0.625)+0.375 * NP。
如果rnn学习了两个依赖项,那么概率为0.25,0.5的概率为75%,0.25的概率为0.5。因此,交叉熵为0.45(-0.50 *(0.75 * NP . log(0.75)+0.25 * NP . log(0.25))-0.25 *(2 * 0.50 * NP . log(0.50))-0.25 *(0))
数据预处理的主要部分是生成实验数据,实验数据根据rnn模型的输入格式进行分段和批处理。代码输入:
1、生成实验数据:
def gen_data(大小=100000):
x = np.array(np.random.choice(2,size=(size,))
y = []
对于范围内的I(尺寸):
阈值= 0.5
#判断x[i-3]和x[i-8]是否为1,并修改阈值
如果x[i-3] == 1:
阈值+= 0.5
如果x[i-8] == 1:
阈值-= 0.25
#生成随机数,并以阈值为阈值分配yi
如果NP . rand . rand()>阈值:
y.append(0)
否则:
y.append(1)
返回x,np.array(y)
接下来,根据模型参数设置分割生成的数据。这里需要的参数主要包括:批量大小和步数,即RNN每层的批量数据大小和rnn信元周期数,即下图中sn中N的大小。代码输入:
def gen_batch(原始数据、批次大小、步骤数):
#raw_data是使用gen_data()函数生成的数据,分别为x和y
原始x,原始y =原始数据
data _ len = len(raw _ x)
#首先,将数据分成批次大小,0-批次大小,批次大小-2 *批次大小。。。
批处理分区长度=数据长度//批处理大小
data _ x = NP . zeross([batch _ size,batch_partition_length],dt type = NP . int 32)
data _ y = NP . zeross([batch _ size,batch_partition_length],dt type = NP . int 32)
对于范围内的I(批次大小):
data _ x[I]= raw _ x[batch _ partition _ length * I:batch _ partition _ length *(I+1)]
data _ y[I]= raw _ y[batch _ partition _ length * I:batch _ partition _ length *(I+1)]
#因为rnn模型一次只处理num_steps数据,所以每个batch_size都被划分为epoch_size,并且每个批处理都有num_steps数据。注意,这里的历元大小不同于模型训练过程中的历元。
epoch _ size = batch _ partition _ length//num _ steps
#x是0-num_steps,batch _ partition _ length-batch _ partition _ length+num _ steps。。。总批量_大小
对于范围内的I(纪元大小):
x = data_x[:,i * num_steps:(i + 1) * num_steps]
y = data_y[:,i * num_steps:(i + 1) * num_steps]
产量(x,y)
# n这里是训练过程中使用的历元,即样本尺度上的周期数
定义世代(n,num_steps):
对于范围(n)中的I:
生成gen_batch(gen_data(),批次大小,步骤数)
根据上面的代码,我们可以看到,这里的数据划分并不完全遵循原始的数据顺序,而是每隔一段时间取num_steps数据,因此该批被训练= =是为了省事还是为了其他原因,这有待在后面的研究中验证。
我们不再重复用该模型构建rnn的具体原理,主要是计算新的隐藏层状态,并将隐藏层状态与输入连接后输出。这里使用单层rnn。公式和原理图如下:
st=tanh(w(xt @ st?1)+bs)
pt=softmax(ust+bp)
对于用张量流构造rnn模型,主要是定义rnn_cell类型并重用它。代码如下:
x = tf.placeholder(tf.int32,[batch_size,num_steps],name= input_placeholder)
y = tf.placeholder(tf.int32,[batch_size,num_steps],name= labels_placeholder)
#rnn初始化状态,全部设置为零。请注意,状态与输入是一致的,接下来是连接操作,所以这里应该有批处理的维度。也就是说,每个样本必须有一个隐藏层状态
init _ state = TF . zeross([batch _ size,state_size])
#将输入转换为一个热门代码,两个类别。[批处理大小,步骤数,类数]
x_one_hot = tf.one_hot(x,num _ classes)
#输入unstack,即unbind num_steps,这对于每个周期单位都很方便。这里可以看到,rnn的每个单元处理一个批处理输入(即批处理二进制样本输入)
rnn _ inputs = TF . un stack(x _ one _ hot,axis=1)
#定义rnn_cell的权重参数,
使用tf.variable_scope( rnn_cell):
w = tf.get_variable( w,[num _ class+state _ size,state_size])
b = tf.get_variable( b,[state_size],初始值设定项= tf.constant _初始值设定项(0.0))
#将其定义为重用模式,回收它,并保持参数不变
定义rnn_cell(rnn_input,state):
tf.variable_scope( rnn_cell,重用=true):
w = tf.get_variable( w,[num _ class+state _ size,state_size])
b = tf.get_variable( b,[state_size],初始值设定项= tf.constant _初始值设定项(0.0))
#定义rnn_cell的具体操作。这里使用最简单的rnn,而不是lstm
返回TF . tanh(TF . mat mul(TF . concat([rnn _ input,state],1),w) + b)
状态= init_state
rnn_outputs = []
#循环次数_步次,即输入一个序列到rnn模型
对于rnn_inputs中的rnn_input:
状态= rnn_cell(rnn_input,state)
rnn_outputs.append(状态)
final_state = rnn_outputs[-1]
#定义softmax层
使用tf.variable_scope( softmax):
w = tf.get_variable( w,[state_size,num _ classes)
b = tf.get_variable( b,[num _ class],初始值设定项= tf.constant _初始值设定项(0.0))
#注意,这里我们将分别计算所有的num_steps输出,然后使用softmax进行预测
logit =[TF . mat mul(rnn_output,w)+b(rnn _ output中的rnn _ output)
预测=[TF . nn . soft max(logit)for logit in logit]
#将我们的y占位符变成标签列表
y_as_list = tf.unstack(y,num=num_steps,axis=1)
#损失和训练_步骤
loss =[TF . nn . sparse _ soft max _ cross _熵_ with _ logit(labels = label,logit = logit)for \
logit,zip中的标签(logit,y_as_list)]
total_loss = tf.reduce_mean(损失)
train _ step = TF . train . adagrad optimizer(学习速率)。最小化(总损失)
在模型训练定义了我们的模型之后,下一步是传入数据,然后训练并编码:
def train _ network(num _ epoch,num_steps,state_size=4,verbose=true):
tf.session()作为sess:
sess . run(TF . global _ variables _ initializer())
培训_损失= []
#获取数据,因为num _ epochs = =,所以外部循环只执行一次
对于idx,枚举中的纪元(gen _ epoch(num _ epoch,num_steps)):
培训_损失= 0
#保存每次执行后的最终状态,并将其分配给下一次执行
training _ state = NP . zeros((batch _ size,state_size))
如果冗长:
打印(idx)
#这是具体的数据采集部分,应该执行1000000//200//5 = 1000次,也就是说,在每次执行中要传输batch_size*num_steps (1000个),所以每个num _ epochs需要执行1000次。
对于枚举(纪元)中的步骤(x,y):
tr _ losses,training_loss_,training_state,_ = \
sess . run([损失,
全损,
最终状态,
train_step],
feed_dict={x:x,y:y,init_state:training_state})
培训_损失+=培训_损失\u
如果步骤% 100 == 0且步骤> 0:
如果冗长:
打印(“步骤平均损失”,步骤,
"对于最后250步:",training_loss/100)
培训_损失.追加(培训_损失/100)
培训_损失= 0
返回培训_损失
训练损失=训练网络(1,步数)
plt.plot(培训_损失)
plt.show()
实验结果如下:
从上图可以看出,交叉熵最终稳定在0。52.根据以上分析,我们可以知道rnn模型成功地学习了第一个依赖关系。因为我们的循环步长是5,他只能学习t-3的第一个依赖性,而不能学习t-8的第二个依赖性。
接下来,尝试num_steps==10来捕获第二个依赖项。最终结果如下:
从上图可以看出,我们的rnn模型已经成功地学习了两种依赖关系。最终的交叉熵不确定在0.46左右。
一些改进。首先,在上面的代码中,为了尽可能详细地解释张量流中rnn模型的构造方法,详细地编写了rnn_cell的定义。事实上,这些任务tf已经打包好了,我们只需要一行命令来实现它们,所以首先要改进的是简化rnn_cell定义和循环部分的代码:
#定义rnn_cell的权重参数,
使用tf.variable_scope( rnn_cell):
w = tf.get_variable( w,[num _ class+state _ size,state_size])
b = tf.get_variable( b,[state_size],初始值设定项= tf.constant _初始值设定项(0.0))
#将其定义为重用模式,回收它,并保持参数不变
定义rnn_cell(rnn_input,state):
tf.variable_scope( rnn_cell,重用=true):
w = tf.get_variable( w,[num _ class+state _ size,state_size])
b = tf.get_variable( b,[state_size],初始值设定项= tf.constant _初始值设定项(0.0))
#定义rnn_cell的具体操作。这里使用最简单的rnn,而不是lstm
返回TF . tanh(TF . mat mul(TF . concat([rnn _ input,state],1),w) + b)
状态= init_state
rnn_outputs = []
#循环次数_步次,即输入一个序列到rnn模型
对于rnn_inputs中的rnn_input:
状态= rnn_cell(rnn_input,state)
rnn_outputs.append(状态)
final_state = rnn_outputs[-1]
# -
cell = TF . contrib . rnn . basicrncell(state _ size)
rnn_outputs,final _ state = TF . contrib . rnn . static _ rnn(cell,rnn_inputs,initial_state=init_state)
2.使用动态rnn模型,在上面的模型中,我们以列表的形式表示输入,也就是说,rnn_inputs是一个具有num_steps长度的列表,其中每个元素都是[batch_size,features](即每个rnn_cell要处理的数据)的张量,这很难做到。我们还可以使用tf提供的dynamic_rnn函数,它不仅在使用dynamic_rnn时,我们可以将输入直接表示为[批量大小、步长数、特征]的三维张量。最终的动态rnn模型代码如下:
x = tf.placeholder(tf.int32,[batch_size,num_steps],name= input_placeholder)
y = tf.placeholder(tf.int32,[batch_size,num_steps],name= labels_placeholder)
init _ state = TF . zeross([batch _ size,state_size])
rnn_inputs = tf.one_hot(x,num _ classes)
#请注意,这一行代码在这里已经被删除了,因为我们不需要将它表示为一个列表并使用循环来完成它。
# rnn _ inputs = TF . un stack(x _ one _ hot,axis=1)
cell = TF . contrib . rnn . basicrncell(state _ size)
#使用dynamic_rnn函数动态构建rnn模型
rnn_outputs,final _ state = TF . nn . dynamic _ rnn(cell,rnn_inputs,initial_state=init_state)
使用tf.variable_scope( softmax):
w = tf.get_variable( w,[state_size,num _ classes)
b = tf.get_variable( b,[num _ class],初始值设定项= tf.constant _初始值设定项(0.0))
logit = TF . resform(
TF . mat mul(TF . resform(rnn _ outputs,[-1,state_size]),w) + b,
[批处理大小,步数,类数]
预测= TF . nn . soft max(logit)
损耗= tf.nn .稀疏_ softmax _ cross _熵_ with _ logits(标签=y,logits = logits)
total_loss = tf.reduce_mean(损失)
train _ step = TF . train . adagrad optimizer(学习速率)。最小化(总损失)
至此,我们已经实现了一个非常简单的rnn模型的构建。在这个过程中,我们应该注意以下三点:
在将数据转换为rnn可接受的输入格式时,应注意批量大小和步数之间的关系。
为了定义rnn_cell,这里使用了以下命令:cell = TF . contrib . rnn . basicnncell(state _ size)
要定义rnn模型,可以使用以下两个命令静态和动态地构建它:
rnn_outputs,final _ state = TF . contrib . rnn . static _ rnn(cell,rnn_inputs,initial_state=init_state)
rnn_outputs,final _ state = TF . nn . dynamic _ rnn(cell,rnn_inputs,initial_state=init_state)
雷锋(公开号:雷锋)(公开号:雷锋)相关阅读:
从原则到实战,英伟达教你用pytorch(1)构建rnn
从原则到实战,英伟达教你用pytorch构建rnn(第二部分)
雷锋文章版权所有。严禁擅自转载。详情请参考转载说明。
来源:搜狐微门户
标题:简单实用的 TensorFlow 实现 RNN 入门教程
地址:http://www.shwmhw.com/shxw/63217.html