[Python人工智能] 十九.Keras搭建循环神经网络分类案例及RNN原理详解

从本专栏开始,作者正式研究Python深度学习、神经网络及人工智能相关知识。前一篇文章分享了卷积神经网络CNN原理,并通过Keras编写CNN实现了MNIST分类学习案例。这篇文章将详细讲解循环神经网络RNN的原理知识,并采用Keras实现手写数字识别的RNN分类案例及可视化呈现。基础性文章,希望对您有所帮助!

在这里插入图片描述
在这里插入图片描述

本专栏主要结合作者之前的博客、AI经验和相关视频(强推"莫烦大神"视频)及论文介绍,后面随着深入会讲解更多的Python人工智能案例及应用。基础性文章,希望对您有所帮助,如果文章中存在错误或不足之处,还请海涵~作者作为人工智能的菜鸟,希望大家能与我在这一笔一划的博客中成长起来。写了这么多年博客,尝试第一个付费专栏,但更多博客尤其基础性文章,还是会继续免费分享,但该专栏也会用心撰写,望对得起读者,共勉!

华为云社区前文赏析:

补充一张深度学习的思维导图。

在这里插入图片描述



一.循环神经网络

在编写代码之前,我们需要介绍什么是RNN,RNN是怎样运行的以及RNN的结构。

1.RNN原理

循环神经网络英文是Recurrent Neural Networks,简称RNN。RNN的本质概念是利用时序信息,在传统神经网络中,假设所有的输入(以及输出)都各自独立。但是,对于很多任务而言,这非常局限。举个例子,假如你想根据一句没说完的话,预测下一个单词,最好的办法就是联系上下文的信息。而RNN(循环神经网络)之所以是“循环”,是因为它们对序列的每个元素执行相同的任务,而每次的结果都独立于之前的计算。

假设有一组数据data0、data1、data2、data3,使用同一个神经网络预测它们,得到对应的结果。如果数据之间是有关系的,比如做菜下料的前后步骤,英文单词的顺序,如何让数据之间的关联也被神经网络学习呢?这就要用到——RNN。

比如存在ABCD数字,需要预测下一个数字E,会根据前面ABCD顺序进行预测,这就称为记忆。预测之前,需要回顾以前的记忆有哪些,再加上这一步新的记忆点,最终输出output,循环神经网络(RNN)就利用了这样的原理。

在这里插入图片描述

首先,让我们想想人类是怎么分析事物之间的关联或顺序的。人类通常记住之前发生的事情,从而帮助我们后续的行为判断,那么是否能让计算机也记住之前发生的事情呢?

在分析data0时,我们把分析结果存入记忆Memory中,然后当分析data1时,神经网络(NN)会产生新的记忆,但此时新的记忆和老的记忆没有关联,如上图所示。在RNN中,我们会简单的把老记忆调用过来分析新记忆,如果继续分析更多的数据时,NN就会把之前的记忆全部累积起来

在这里插入图片描述

下面是一个典型的RNN结果模型,按照时间点t-1、t、t+1,每个时刻有不同的x,每次计算会考虑上一步的state和这一步的x(t),再输出y值。在该数学形式中,每次RNN运行完之后都会产生s(t),当RNN要分析x(t+1)时,此刻的y(t+1)是由s(t)和s(t+1)共同创造的,s(t)可看作上一步的记忆。多个神经网络NN的累积就转换成了循环神经网络,其简化图如下图的左边所示。例如,如果序列中的句子有5个单词,那么,横向展开网络后将有五层神经网络,一层对应一个单词。

在这里插入图片描述

总之,只要你的数据是有顺序的,就可以使用RNN,比如人类说话的顺序,电话号码的顺序,图像像素排列的顺序,ABC字母的顺序等。在前面讲解CNN原理时,它可以看做是一个滤波器滑动扫描整幅图像,通过卷积加深神经网络对图像的理解。

在这里插入图片描述

而RNN也有同样的扫描效果,只不过是增加了时间顺序和记忆功能。RNN通过隐藏层周期性的连接,从而捕获序列化数据中的动态信息,提升预测结果。

在这里插入图片描述



2.RNN应用

RNN常用于自然语言处理、机器翻译、语音识别、图像识别等领域,下面简单分享RNN相关应用所对应的结构。

RNN情感分析: 当分析一个人说话情感是积极的还是消极的,就用如下图所示的RNN结构,它有N个输入,1个输出,最后时间点的Y值代表最终的输出结果。

在这里插入图片描述

RNN图像识别: 此时有一张图片输入X,N张对应的输出。

在这里插入图片描述

RNN语言建模和文本生成: 通过训练RNN模型,我们可以基于给出的一个单词序列,预测下一个单词。这对于语言建模和文本生成而言是非常有价值的。同时,语言模型可以用于评估一个句子出现的可能性,这对机器翻译而言也是非常重要的(因为高概率的句子通常是正确的)。


RNN机器翻译: 输入和输出分别两个,对应的是中文和英文,如下图所示。

在这里插入图片描述

机器翻译类似于语言建模,我们首先输入源语(例如德语),需要输出是目标语(例如英语)。关键区别是,在机器翻译中,翻译的第一个字可能会需要所有已输入句子的信息,所以只有看到全部输入之后才能输出。

在这里插入图片描述

最后再补充一些有趣的RNN应用。
RNN描述照片: RNN被应用于生成描述未被标签的图片模型。并且,两者结合的模型组合甚至可以依据图片特征排列生成文字,以及图片中特征的对应位置。如下图:

在这里插入图片描述

RNN写学术论文或脚本:

在这里插入图片描述

RNN作曲、作画: 后续作者尝试完成该示例。

在这里插入图片描述

相关论文:

  • Recurrent neural network based language model 《基于循环神经网络的语言模型》
  • Extensions of Recurrent neural network based language model 《基于循环神经网络拓展的语言模型》
  • Generating Text with Recurrent Neural Networks 《利用循环神经网络生成文本》
  • A Recursive Recurrent Neural Network for Statistical Machine Translation 《用于统计类机器翻译的递归型循环神经网络》
  • Sequence to Sequence Learning with Neural Networks 《利用神经网络进行序列至序列的学习》
  • Joint Language and Translation Modeling with Recurrent Neural Networks 《利用循环神经网络进行语言和翻译的建模》
  • Towards End-to-End Speech Recognition with Recurrent Neural Networks 《利用循环神经网络进行端对端的语音识别》


二.Keras编写RNN

接着我们讲解如何在Keras代码中编写RNN。

1.代码实现

第一步,打开Anaconda,然后选择已经搭建好的“tensorflow”环境,运行Spyder。

在这里插入图片描述


第二步,导入扩展包。

import numpy as np
from keras.datasets import mnist
from keras.utils import np_utils
from keras.models import Sequential
from keras.layers import SimpleRNN, Activation, Dense
from keras.optimizers import Adam

第三步,定义参数。

TIME_STEPS = 28     # 时间点数据 每次读取1行共28次 same as the height of the image 
INPUT_SIZE = 28     # 每行读取28个像素点 same as the width of the image
BATCH_SIZE = 50     # 每个批次训练50张图片
BATCH_INDEX = 0     
OUTPUT_SIZE = 10    # 每张图片输出分类矩阵
CELL_SIZE = 50      # RNN中隐藏单元
LR = 0.001          # 学习率

第四步,载入MNIST数据及预处理。

  • X_train.reshape(-1, 1, 28, 28) / 255
    将每个像素点进行标准化处理,从0-255转换成0-1的范围。
  • np_utils.to_categorical(y_train, nb_classes=10)
    调用up_utils将类标转换成10个长度的值,如果数字是3,则会在对应的地方标记为1,其他地方标记为0,即{0,0,0,1,0,0,0,0,0,0}。

由于MNIST数据集是Keras或TensorFlow的示例数据,所以我们只需要下面一行代码,即可实现数据集的读取工作。如果数据集不存在它会在线下载,如果数据集已经被下载,它会被直接调用。

# 下载MNIST数据 
# training X shape (60000, 28x28), Y shape (60000, )
# test X shape (10000, 28x28), Y shape (10000, )
(X_train, y_train), (X_test, y_test) = mnist.load_data()

# 数据预处理
# 参数-1表示样例的个数 28*28表示像素长度和宽度
X_train = X_train.reshape(-1, 28, 28) / 255   # normalize
X_test = X_test.reshape(-1, 28, 28) / 255     # normalize

# 将类向量转化为类矩阵  数字 5 转换为 0 0 0 0 0 1 0 0 0 0 矩阵
y_train = np_utils.to_categorical(y_train, num_classes=10)
y_test = np_utils.to_categorical(y_test, num_classes=10)

第五步,创建RNN神经网络。

# 创建RNN模型
model = Sequential()

# RNN cell
model.add(SimpleRNN(
    # 设置输入batch形状 批次数量50 时间点28 每行读取像素28个
    # for batch_input_shape, if using tensorflow as the backend, we have to put None for the batch_size.
    # Otherwise, model.evaluate() will get error.
    batch_input_shape = (None, TIME_STEPS, INPUT_SIZE),
    # RNN输出给后一层的结果为50
    output_dim = CELL_SIZE,
    unroll=True,
))

# output layer
model.add(Dense(OUTPUT_SIZE))        # 全连接层 输出对应10分类
model.add(Activation('softmax'))     # 激励函数 tanh

第六步,定义神经网络优化器并激活神经网络。

# optimizer
adam = Adam(LR)

# We add metrics to get more results you want to see
# 激活神经网络
model.compile(optimizer=adam,                      # 加速神经网络
              loss='categorical_crossentropy',     # 损失函数
              metrics=['accuracy'])                # 计算误差或准确率

第七步,训练及预测。

for step in range(4001):
    # 分批截取数据 BATCH_INDEX初始值为0 BATCH_SIZE为50 取28个步长和28个INPUT_SIZE
    # data shape = (batch_num, steps, inputs/outputs)
    X_batch = X_train[BATCH_INDEX: BATCH_INDEX+BATCH_SIZE, :, :]
    Y_batch = y_train[BATCH_INDEX: BATCH_INDEX+BATCH_SIZE, : ]
    
    # 计算误差
    cost = model.train_on_batch(X_batch, Y_batch)
    
    # 累加参数 
    BATCH_INDEX += BATCH_SIZE
    # 如果BATCH_INDEX累加大于总体的个数 则重新赋值0开始分批计算
    BATCH_INDEX = 0 if BATCH_INDEX >= X_train.shape[0] else BATCH_INDEX
    
    # 每隔500步输出
    if step % 500 == 0:
        # 评价算法
        cost, accuracy = model.evaluate(
                X_test, y_test, 
                batch_size=y_test.shape[0], 
                verbose=False)
        print('test cost: ', cost, 'test accuracy: ', accuracy)


2.完整代码

# -*- coding: utf-8 -*-
"""
Created on Fri Feb 23 18:43:06 2020
@author: xiuzhang Eastmount CSDN
Wuhan fighting!
"""
import numpy as np
from keras.datasets import mnist
from keras.utils import np_utils
from keras.models import Sequential
from keras.layers import SimpleRNN, Activation, Dense
from keras.optimizers import Adam

#------------------------------定义参数------------------------------
TIME_STEPS = 28     # 时间点数据 每次读取1行共28次 same as the height of the image 
INPUT_SIZE = 28     # 每行读取28个像素点 same as the width of the image
BATCH_SIZE = 50     # 每个批次训练50张图片
BATCH_INDEX = 0     
OUTPUT_SIZE = 10    # 每张图片输出分类矩阵
CELL_SIZE = 50      # RNN中隐藏单元
LR = 0.001          # 学习率

#---------------------------载入数据及预处理---------------------------
# 下载MNIST数据 
# training X shape (60000, 28x28), Y shape (60000, )
# test X shape (10000, 28x28), Y shape (10000, )
(X_train, y_train), (X_test, y_test) = mnist.load_data()

# 数据预处理
# 参数-1表示样例的个数 28*28表示像素长度和宽度
X_train = X_train.reshape(-1, 28, 28) / 255   # normalize
X_test = X_test.reshape(-1, 28, 28) / 255     # normalize

# 将类向量转化为类矩阵  数字 5 转换为 0 0 0 0 0 1 0 0 0 0 矩阵
y_train = np_utils.to_categorical(y_train, num_classes=10)
y_test = np_utils.to_categorical(y_test, num_classes=10)

#---------------------------创建RNN神经网络---------------------------
# 创建RNN模型
model = Sequential()

# RNN cell
model.add(SimpleRNN(
    # 设置输入batch形状 批次数量50 时间点28 每行读取像素28个
    # for batch_input_shape, if using tensorflow as the backend, we have to put None for the batch_size.
    # Otherwise, model.evaluate() will get error.
    batch_input_shape = (None, TIME_STEPS, INPUT_SIZE),
    # RNN输出给后一层的结果为50
    output_dim = CELL_SIZE,
    unroll=True,
))

# output layer
model.add(Dense(OUTPUT_SIZE))        # 全连接层 输出对应10分类
model.add(Activation('softmax'))     # 激励函数 tanh

#---------------------------神经网络优化器---------------------------
# optimizer
adam = Adam(LR)

# We add metrics to get more results you want to see
# 激活神经网络
model.compile(optimizer=adam,                      # 加速神经网络
              loss='categorical_crossentropy',     # 损失函数
              metrics=['accuracy'])                # 计算误差或准确率

#--------------------------------训练和预测------------------------------
for step in range(4001):
    # 分批截取数据 BATCH_INDEX初始值为0 BATCH_SIZE为50 取28个步长和28个INPUT_SIZE
    # data shape = (batch_num, steps, inputs/outputs)
    X_batch = X_train[BATCH_INDEX: BATCH_INDEX+BATCH_SIZE, :, :]
    Y_batch = y_train[BATCH_INDEX: BATCH_INDEX+BATCH_SIZE, : ]
    
    # 计算误差
    cost = model.train_on_batch(X_batch, Y_batch)
    
    # 累加参数 
    BATCH_INDEX += BATCH_SIZE
    # 如果BATCH_INDEX累加大于总体的个数 则重新赋值0开始分批计算
    BATCH_INDEX = 0 if BATCH_INDEX >= X_train.shape[0] else BATCH_INDEX
    
    # 每隔500步输出
    if step % 500 == 0:
        # 评价算法
        cost, accuracy = model.evaluate(
                X_test, y_test, 
                batch_size=y_test.shape[0], 
                verbose=False)
        print('test cost: ', cost, 'test accuracy: ', accuracy)


3.运行结果

输出结果如下图所示,训练4000次,每隔500次输出一次误差cost和正确率。从下面的结果可以发现,误差不断减小,正确率不断提高,说明RNN在不断学习。真正做神经网络实验时,我们会针对不同的参数和样本、算法进行比较,也希望这篇文章对您有帮助。

test cost:  2.3657307624816895 test accuracy:  0.07580000162124634
test cost:  0.5747528076171875 test accuracy:  0.840399980545044
test cost:  0.4435984492301941 test accuracy:  0.863099992275238
test cost:  0.3612927794456482 test accuracy:  0.897599995136261
test cost:  0.30560624599456787 test accuracy:  0.9138000011444092
test cost:  0.3092554211616516 test accuracy:  0.9089999794960022
test cost:  0.2737627327442169 test accuracy:  0.9168999791145325
test cost:  0.22912506759166718 test accuracy:  0.9351000189781189
test cost:  0.23802728950977325 test accuracy:  0.9323999881744385


三.绘制图形

为了更好地比较训练次数和误差、Accuracy,我们可以增加可视化分析。其运行结果如下图所示:

在这里插入图片描述

在这里插入图片描述

此时的完整代码如下:

# -*- coding: utf-8 -*-
# -*- coding: utf-8 -*-
"""
Created on Fri Feb 23 18:43:06 2020
@author: xiuzhang Eastmount CSDN
Wuhan fighting!
"""
import numpy as np
from keras.datasets import mnist
from keras.utils import np_utils
from keras.models import Sequential
from keras.layers import SimpleRNN, Activation, Dense
from keras.optimizers import Adam

#------------------------------定义参数------------------------------
TIME_STEPS = 28     # 时间点数据 每次读取1行共28次 same as the height of the image 
INPUT_SIZE = 28     # 每行读取28个像素点 same as the width of the image
BATCH_SIZE = 50     # 每个批次训练50张图片
BATCH_INDEX = 0     
OUTPUT_SIZE = 10    # 每张图片输出分类矩阵
CELL_SIZE = 50      # RNN中隐藏单元
LR = 0.001          # 学习率

#---------------------------载入数据及预处理---------------------------
# 下载MNIST数据 
# training X shape (60000, 28x28), Y shape (60000, )
# test X shape (10000, 28x28), Y shape (10000, )
(X_train, y_train), (X_test, y_test) = mnist.load_data()

# 数据预处理
# 参数-1表示样例的个数 28*28表示像素长度和宽度
X_train = X_train.reshape(-1, 28, 28) / 255   # normalize
X_test = X_test.reshape(-1, 28, 28) / 255     # normalize

# 将类向量转化为类矩阵  数字 5 转换为 0 0 0 0 0 1 0 0 0 0 矩阵
y_train = np_utils.to_categorical(y_train, num_classes=10)
y_test = np_utils.to_categorical(y_test, num_classes=10)

#---------------------------创建RNN神经网络---------------------------
# 创建RNN模型
model = Sequential()

# RNN cell
model.add(SimpleRNN(
    # 设置输入batch形状 批次数量50 时间点28 每行读取像素28个
    # for batch_input_shape, if using tensorflow as the backend, we have to put None for the batch_size.
    # Otherwise, model.evaluate() will get error.
    batch_input_shape = (None, TIME_STEPS, INPUT_SIZE),
    # RNN输出给后一层的结果为50
    output_dim = CELL_SIZE,
    unroll=True,
))

# output layer
model.add(Dense(OUTPUT_SIZE))        # 全连接层 输出对应10分类
model.add(Activation('softmax'))     # 激励函数 tanh

#---------------------------神经网络优化器---------------------------
# optimizer
adam = Adam(LR)

# We add metrics to get more results you want to see
# 激活神经网络
model.compile(optimizer=adam,                      # 加速神经网络
              loss='categorical_crossentropy',     # 损失函数
              metrics=['accuracy'])                # 计算误差或准确率

#--------------------------------训练和预测------------------------------
cost_list = []
acc_list = []
step_list = []
for step in range(4001):
    # 分批截取数据 BATCH_INDEX初始值为0 BATCH_SIZE为50 取28个步长和28个INPUT_SIZE
    # data shape = (batch_num, steps, inputs/outputs)
    X_batch = X_train[BATCH_INDEX: BATCH_INDEX+BATCH_SIZE, :, :]
    Y_batch = y_train[BATCH_INDEX: BATCH_INDEX+BATCH_SIZE, : ]
    
    # 计算误差
    cost = model.train_on_batch(X_batch, Y_batch)
    
    # 累加参数 
    BATCH_INDEX += BATCH_SIZE
    # 如果BATCH_INDEX累加大于总体的个数 则重新赋值0开始分批计算
    BATCH_INDEX = 0 if BATCH_INDEX >= X_train.shape[0] else BATCH_INDEX
    
    # 每隔200步输出
    if step % 200 == 0:
        # 评价算法
        cost, accuracy = model.evaluate(
                X_test, y_test, 
                batch_size=y_test.shape[0], 
                verbose=False)
        # 写入列表
        cost_list.append(cost)
        acc_list.append(accuracy)
        step_list.append(step)
        print('test cost: ', cost, 'test accuracy: ', accuracy)

#--------------------------------绘制相关曲线------------------------------
import matplotlib.pyplot as plt
from mpl_toolkits.axes_grid1 import host_subplot

# 绘制曲线图
host = host_subplot(111)
plt.subplots_adjust(right=0.8) # ajust the right boundary of the plot window
par1 = host.twinx()

# 设置类标
host.set_xlabel("Iterations")
host.set_ylabel("Loss")
par1.set_ylabel("Accuracy")

# 绘制曲线
p1, = host.plot(step_list, cost_list, "bo-", linewidth=2, markersize=12, label="cost")
p2, = par1.plot(step_list, acc_list, "gs-", linewidth=2, markersize=12, label="accuracy")

# 设置颜色
host.axis["left"].label.set_color(p1.get_color())
par1.axis["right"].label.set_color(p2.get_color())

# 绘图
plt.legend(loc="upper left")
plt.title("Keras for RNN - Eastmount CSDN")
plt.draw()
plt.show()


四.总结

写到这里,这篇文章就结束了。接着请同学们思考几个问题:

  • 为什么cost和accuracy会波动呢?预测结果不是一直上升或下降。
  • 学习率之前设置为LR=0.001,它对我们的实验是否有影响呢?
  • 上图仅比较了迭代次数和cost、accuracy,是否可以比较学习率呢?
  • 其他的评价指标是否可以评价RNN的分类实验呢?比如Precision、Recall、F1等。
  • 神经网络的激励函数、神经层数是否有可以比较呢?
  • 如何比较数字0-9不同类标的性能呢?如何验证RNN比普通的神经网络效果更好?

这些实验都是我们在做论文研究或项目评价常见的一些问题,希望读者带着这些问题,结合自己的需求进行深入的思考,更希望大家能学以致用。最后如果文章对您有帮助,请点赞、评论、收藏,这将是我分享最大的动力。

总之,本文通过Keras实现了一个RNN分类学习的案例,并详细介绍了循环神经网络原理知识。最后,希望这篇基础性文章对您有所帮助,如果文章中存在错误或不足之处,还请海涵~作为人工智能的菜鸟,我希望自己能不断进步并深入,后续将它应用于图像识别、网络安全、对抗样本等领域,指导大家撰写简单的学术论文,一起加油!

感恩能与大家在华为云遇见!
希望能与大家一起在华为云社区共同成长。原文地址:https://blog.csdn.net/Eastmount/article/details/104458677

(By:娜璋之家 Eastmount 2021-11-09 夜于武汉)



参考文献:

(完)