在深度学习的路上,从头开始了解一下各项技术。本人是DL小白,连续记录我自己看的一些东西,大家可以互相交流。
之前写过一些关于深度学习的简单概念,最近这段时间会看一些代码相关的东西。
本文参考:
https://github.com/lawlite19/Blog-Back-Up/blob/master/code/rnn/rnn_implement.py
一、前言
RNN擅长于处理序列化的问题,本片是基于TensorFlow框架的实现,用于处理一个二进制序列的预测。
二、数据描述
- 输入数据
X
是二进制的一串序列, 在t
时刻,有50%
的概率是1
,50%
的概率是0
,比如:X=[1,1,0,0,1,0.....]
- 输出数据
Y
:- 在时刻
t
,50%
的概率是1
,50%
的概率是0
; - 如果Xt−3是
1
,则Yt100%
是1
(增加50%
); - 如果Xt−8是
1
,则Yt25%
是1
(减少25%
);- 所以如果Xt−3和Xt−8都是
1
,则Yt50%+50%-25%=75%
的概率是1
- 所以如果Xt−3和Xt−8都是
- 在时刻
针对于如上数据,各个时间步之间有依赖关系,但是也存在随机性,可以模拟语言处理的简单情况。
具体的实现步骤和注释见具体代码。
三、代码实现
import numpy as np
import tensorflow as tf
from tensorflow.python import debug as tf_debug
import matplotlib.pyplot as plt
#hyperparameters
#只能记忆10步,误差只往前传播10步,至少8步才能学到第二个依赖
#每10个RNN cell一个截断
num_steps = 10
#每200个训练样本一批
batch_size = 200
#最后分为2类
num_classes = 2
#RNN中的unit个数-16个
state_size = 16
learning_rate = 0.1
#生成数据
#输入数据X是二进制的一串序列, 在t时刻,有50%的概率是1,50%的概率是0,比如:X=[1,1,0,0,1,0.....]
#输出数据Y:
#在时刻t,50%的概率是1,50%的概率是0;
#如果Xt−3是1,则Yt 100%是1(增加50%);
#如果Xt−8是1,则Yt 25%是1(减少25%);
#所以如果Xt−3和Xt−8都是1,则Yt 50%+50%-25%=75%的概率是1
#所以,输出数据是有两个依赖关系的
def gen_data(size=1000000):
#返回一个列表的随即项
#生成size大小个数列
X = np.array(np.random.choice(2, size=(size,)))
Y = []
#根据对应X生成Y
for i in range(size):
threshold = 0.5
if X[i-3] == 1:
threshold += 0.5
if X[i-8] == 1:
threshold -= 0.25
if np.random.rand() > threshold:
Y.append(0)
else:
Y.append(1)
return X, np.array(Y)
#生成batch数据,raw_data是全部数据
#实现从全部数据,到(x, y)的转化
def gen_batch(raw_data, batch_size, num_steps):
raw_x, raw_y = raw_data
#数据总长度
data_length = len(raw_x) #1000000
#每个batch的长度,一共1000000个数据,batch_size为200,分成5000个200大小的批
batch_patition_length = data_length // batch_size # 5000
#先用0填充矩阵,初始化
data_x = np.zeros([batch_size, batch_patition_length], dtype=np.int32) # (200, 5000)
data_y = np.zeros([batch_size, batch_patition_length], dtype=np.int32) # (200, 5000)
#填充内容到对应位置,生成data_x, data_y
for i in range(batch_size):
data_x[i] = raw_x[batch_patition_length*i: batch_patition_length*(i+1)]#raw_x[start: end]从开始到最后分开
data_y[i] = raw_y[batch_patition_length*i: batch_patition_length*(i+1)]
epoch_size = batch_patition_length // num_steps # 每一个RNN cell的大小
#抽取data_x, data_y,生成x,y的生成器
for i in range(epoch_size): # 抽取epoch_size 个数据,数据生成器
x = data_x[:, i * num_steps: (i+1) * num_steps] # (200, 5)
y = data_y[:, i * num_steps: (i+1) * num_steps]
yield (x, y)
def gen_epochs(n, num_steps):
for i in range(n):
yield gen_batch(gen_data(), batch_size, num_steps)
#创建placeholder
x = tf.placeholder(tf.int32, [batch_size, num_steps], name = 'x')
y = tf.placeholder(tf.int32, [batch_size, num_steps], name = 'y')
#a向量,初始化为0向量(200, 16)
init_state = tf.zeros([batch_size, state_size])
#RNN输入
x_one_hot = tf.one_hot(x, num_classes)
#unstack()矩阵分解的函数
rnn_inputs = tf.unstack(x_one_hot, axis=1)
#定义RNN的cell
"""""
name_scope 和 variable_scope的用法,类似与tf.Variable,也是用于定义变量
"""""
#创建rnn_cell的参数
with tf.variable_scope('rnn_cell'):
W = tf.get_variable('W', [num_classes + state_size, state_size])
b = tf.get_variable('b', [state_size], initializer=tf.constant_initializer(0.0))
#创建rnn_cell
def rnn_cell(rnn_input, state):
#取出参数
with tf.variable_scope('rnn_cell', reuse=True):
W = tf.get_variable('W', [num_classes + state_size, state_size])
b = tf.get_variable('b', [state_size], initializer=tf.constant_initializer(0.0))
return tf.tanh(tf.matmul(tf.concat([rnn_input, state], 1), W) + b)
#把rnn cell添加进计算图
state = init_state
rnn_outputs = []
for rnn_input in rnn_inputs:
state = rnn_cell(rnn_input, state)
rnn_outputs.append(state)
final_state = rnn_outputs[-1] # [-1]最后一个元素
#预测
#创建softmax的参数
with tf.variable_scope('softmax'):
W = tf.get_variable('W', [state_size, num_classes])
b = tf.get_variable('b', [num_classes], initializer=tf.constant_initializer(0.0))
#输入值,即为rnn的输出值
logits = [tf.matmul(rnn_output, W) + b for rnn_output in rnn_outputs]
predictions = [tf.nn.softmax(logit) for logit in logits]
#y为(200, 5)拆分为(200, ) * 5
y_as_list = tf.unstack(y, num=num_steps, axis=1)
#计算损失和优化
losses = [tf.nn.sparse_softmax_cross_entropy_with_logits(labels=label, logits=logit) for logit, label in zip(logits, y_as_list)]
total_loss = tf.reduce_mean(losses)
train_step = tf.train.AdagradOptimizer(learning_rate).minimize(total_loss)
#构建RNN网络训练
def train_rnn(num_epochs, num_steps, state_size = 16, verbose = True):
with tf.Session() as sess:
sess.run(tf.global_variables_initializer())
#训练损失统计参数
training_losses = []
# yield gen_batch(gen_data(), batch_size, num_steps)
#enumerate()将一个可遍历的数据对象(如列表、元组或字符串)组合为一个索引序列,同时列出数据和数据下标,一般用在 for 循环当中
#idx: 每一截断的序列号, epoch: 截断的数据
for idx, epoch in enumerate(gen_epochs(num_epochs, num_steps)):
training_loss = 0
#初始化状态向量为0向量,形状为(200, 16)
training_state = np.zeros((batch_size, state_size))
#verbose是否打印
if verbose:
print('\nepoch', idx)
for step, (X, Y) in enumerate(epoch):
tr_losses, training_loss_, training_state, _ = \
sess.run([losses, total_loss, final_state, train_step],
feed_dict={x: X, y: Y, init_state: training_state})
training_loss += training_loss_
if step % 100 == 0 and step > 0:
if verbose:
print('第 {0} 步的平均损失 {1}'.format(step, training_loss / 100))
training_losses.append(training_loss / 100)
training_loss = 0
return training_losses
training_losses = train_rnn(num_epochs=5, num_steps=num_steps, state_size=state_size)
print(training_losses[0])
plt.plot(training_losses)
plt.show()