机器学习100天-Day10 Tensorflow实现RNN算法

本例是为了配合NLP学习中的RNN网络,斯坦福CS224n课程里面使用的是Tensorflow进行,所以提前熟悉一下,使用Tensorflow生成一个echo-rnn。
说实话,这个例子是照着教程敲出来的,仅仅实现了,但是没有对后面的原理进行分析,目前还是在一步一步往前推。
代码同样更新在github:https://github.com/jwc19890114/-02-learning-file-100days

什么是RNN?

RNN是循环神经网络(Recurrent Neural Network)的英文缩写,它能结合数据点之间的特定顺序和幅值大小等多个特征,来处理序列数据。更重要的是,这种网络的输入序列可以是任意长度的。
举一个简单的例子:数字时间序列,具体任务是根据先前值来预测后续值。在每个时间步中,循环神经网络的输入是当前值,以及一个表征该网络在之前的时间步中已经获得信息的状态向量。该状态向量是RNN网络的编码记忆单元,在训练网络之前初始化为零向量。

RNN和CNN、DNN

  • CNN 专门解决图像问题的,可用把它看作特征提取层,放在输入层上,最后用MLP 做分类。对于输入数据的维度约束是比较严重的,其训练和预测的输入数据都必须完全相同,但是如果用CNN去做一个智能问答系统,CNN会需要所有的问答数据都是固定的长度,这就很可怕了,这种模型会让问答变成对对子,必须每句话长短固定。而RNN没有这种约束。
  • RNN 专门解决时间序列问题的,用来提取时间序列信息,放在特征提取层(如CNN)之后。RNN更多的考虑了神经元之间的联系,例如训练机器翻译,那么对于一个短语的翻译一定要考虑前因后果,这就需要模型对于数据输入的前后因素都要考虑
  • DNN 说白了就是 多层网络,只是用了很多技巧,让它能够 deep 。

RNN和LSTM

为了解决原始RNN网络结构存在的梯度消失(vanishing gradient)问题,设计了LSTM这种新的网络结构。但从本质上来讲,LSTM是一种特殊的循环神经网络。
其和RNN的区别在于,对于特定时刻t,隐藏层输出st的计算方式不同。故对LSTM网络的训练的思路与RNN类似,仅前向传播关系式不同。
本文借鉴Erik Hallström教程

引入所需库

from __future__ import print_function, division
import numpy as np
import tensorflow as tf
import matplotlib.pyplot as plt
import os

tensorflow警告记录

os.environ['TF_CPP_MIN_LOG_LEVEL'] = '2'
old_v = tf.logging.get_verbosity()
tf.logging.set_verbosity(tf.logging.ERROR)

构建一个回声状态网络Echo-RNN,能记忆输入数据信息,在若干时间步后将其回传。

num_epchos = 100
total_series_length = 50000
truncated_backprop_length = 15
state_size = 4
num_classes = 2
echo_step = 3
batch_size = 5
num_batches = total_series_length // batch_size // truncated_backprop_length

生成随机训练数据,输入为一个随机的二元向量,在echo_step(3)个时间步后,得到输入的回声

def generateData():
    x = np.array(np.random.choice(2, total_series_length, p=[0.5, 0.5]))
    y = np.roll(x, echo_step)
    y[0:echo_step] = 0
    '''
    将数据重构为新矩阵。神经网络的训练,需要利用小批次数据(mini-batch),
    来近似得到关于神经元权重的损失函数梯度。
    在训练过程中,随机批次操作能防止过拟合和降低硬件压力。
    整个数据集通过数据重构转化为一个矩阵,并将其分解为多个小批次数据。
    '''
    x = x.reshape((batch_size, -1))
    y = y.reshape((batch_size, -1))
    # print(x)
    # print("**************")
    # print(y)
    return x, y

构建计算视图

# 占位符是计算图的“起始节点”。在运行每个计算图时,批处理数据被传递到占位符中。
# 另外,RNN状态向量也是存储在占位符中,在每一次运行后更新输出。
batchX_placeholder = tf.placeholder(tf.float32, [batch_size, truncated_backprop_length])
batchY_placeholder = tf.placeholder(tf.int32, [batch_size, truncated_backprop_length])
init_state = tf.placeholder(tf.float32, [batch_size, state_size])

权重

W = tf.Variable(np.random.rand(state_size + 1, state_size), dtype=tf.float32)
b = tf.Variable(np.zeros((1, state_size)), dtype=tf.float32)
W2 = tf.Variable(np.random.rand(state_size, num_classes), dtype=tf.float32)
b2 = tf.Variable(np.zeros((1, num_classes)), dtype=tf.float32)

拆分序列

# 开始构建RNN计算视图的下个部分,首先我们要以相邻的时间步分割批数据。
# 可以按批次分解各列,转成list格式文件。RNN会同时从不同位置开始训练时间序列
# 在我们的时间序列数据中,在三个位置同时开启训练,所以在前向传播时需要保存三个状态。我们在参数定义时就已经考虑到这一点了,
# 故将init_state设置为3,[batch_size, state_size]。
inputs_series = tf.unstack(batchX_placeholder, axis=1)
labels_series = tf.unstack(batchY_placeholder, axis=1)

前向传播

current_state=init_state
states_series=[]
for current_input in inputs_series:
    current_input=tf.reshape(current_input,[batch_size,1])
    input_and_state_concatenated=tf.concat([current_input, current_state],1)

    next_state = tf.tanh(tf.matmul(input_and_state_concatenated, W) + b)  # Broadcasted addition
    states_series.append(next_state)
    current_state = next_state

计算损失

# 这里调用的tosparse_softmax_cross_entropy_with_logits函数,能在内部算得softmax函数值后,继续计算交叉熵。
logits_series=[tf.matmul(state, W2)+b2 for state in states_series]
predictions_series=[tf.nn.softmax(logits) for logits in logits_series]
losses = [tf.nn.sparse_softmax_cross_entropy_with_logits(logits=logits, labels=labels) for logits, labels in zip(logits_series,labels_series)]
total_loss=tf.reduce_mean(losses)

train_step=tf.train.AdagradOptimizer(0.3).minimize(total_loss)

可视化结果

def plot(loss_list, predictions_series, batchX, batchY):
    plt.subplot(2, 3, 1)
    plt.cla()
    plt.plot(loss_list)

    for batch_series_idx in range(5):
        one_hot_output_series = np.array(predictions_series)[:, batch_series_idx, :]
        single_output_series = np.array([(1 if out[0] < 0.5 else 0) for out in one_hot_output_series])

        plt.subplot(2, 3, batch_series_idx + 2)
        plt.cla()
        plt.axis([0, truncated_backprop_length, 0, 2])
        left_offset = range(truncated_backprop_length)
        plt.bar(left_offset, batchX[batch_series_idx, :], width=1, color="blue")
        plt.bar(left_offset, batchY[batch_series_idx, :] * 0.5, width=1, color="red")
        plt.bar(left_offset, single_output_series * 0.3, width=1, color="green")

    plt.draw()
    plt.pause(0.0001)

with tf.Session() as sess:
    sess.run(tf.global_variables_initializer())
    plt.ion()
    plt.figure()
    plt.show()
    loss_list=[]

    for epoch_idx in range(num_epchos):
        x,y=generateData()
        _current_state=np.zeros((batch_size,state_size))
        print('New data, epoch', epoch_idx)

        for batch_idx in range(num_batches):
            start_idx = batch_idx * truncated_backprop_length
            end_idx = start_idx + truncated_backprop_length

            batchX = x[:, start_idx:end_idx]
            batchY = y[:, start_idx:end_idx]

            _total_loss, _train_step, _current_state, _predictions_series = sess.run(
                [total_loss, train_step, current_state, predictions_series],
                feed_dict={
                    batchX_placeholder: batchX,
                    batchY_placeholder: batchY,
                    init_state: _current_state
                })

            loss_list.append(_total_loss)

            if batch_idx % 100 == 0:
                print("Step", batch_idx, "Loss", _total_loss)
                plot(loss_list, _predictions_series, batchX, batchY)

plt.ioff()
plt.show()

你可能感兴趣的:(机器学习100天-Day10 Tensorflow实现RNN算法)