RNN循环神经网络

**RNN(Recurrent Neural Network)
  在传统的全链接神经网络中,从输入层到隐藏层再到输出层,每一层之间的节点是无链接的,由于输入和输出是独立的,因此这种普通的神经网络对于序列数据的处理是无能为力的。而现实中,绝大多数的数据都是序列数据,好比音频、视频、文本等,都存在时间线,想要挖掘数据中的序列信息和语义信息,就须要神经网有更加特殊的结构,好比对于序列信息每一时刻的信息记忆能力。所以,RNN(Recurrent Neural Network)循环神经网络就应运而生,RNN循环神经网络相对于普通的全链接神经网络,其隐藏层多了一个信息记忆功能,即每一时刻隐藏层的输入不只是输入层的输出,还包含上一时刻隐藏层的输出。因此,输出层每一时刻的输出都会考虑序列数据以前的信息。RNN在天然语言处理、图片描述、语音识别等领域有着普遍的应用。
RNN的网络结构及前向传播
这里写图片描述
  RNN的网络结构按时间展开如上图所示,其中x表示输入层、o表示输出层、s表示隐藏层,U、V、W表示权重参数。
  以t时刻为例,隐藏层st的输入除了当前时刻输入层的输出xt,还包含上一时刻隐藏层的输出状态st-1。RNN中隐藏层能够完成对信息的记忆,理论上RNN每一时刻的隐藏层均可以完成对上一时刻信息的记忆,也就是说在理论上RNN的隐藏层能够对信息无限记忆,处理任意长度的序列数据,可是在实际中会存在梯度消失或者梯度爆炸的问题,所以,在RNN中隐藏层st完成的只是信息的短时记忆。
RNN中前向传播过程能够用以下公式表示:
这里写图片描述
  式子1中,ot表示t时刻输出层的输出,g()表示输出层中神经元的激活函数,V表示t时刻隐藏层输出到输出层对应的权重参数;
  式子2中,st表示隐藏层t时刻的输出,f()表示隐藏层中神经元的激活函数,xt表示t时刻输入层的输出,st-1表示上一时刻隐藏层的输出,U表示输入层到隐藏层对应的权重参数,W表示上一时刻隐藏层输出到t时刻隐藏层的权重参数;
  从上面的公式能够看出,RNN循环神经网络与普通的全链接神经网络相比,隐藏层多了一个权重矩阵W。
  若是把式子2反复带入式子1中,能够获得:
这里写图片描述
  从上面的结果能够看到,RNN中每一时刻的输出ot都是受前面历次输入xt、xt-一、xt-2……影响的,这也就是为何说RNN能够处理序列数据的缘由。
RNN参数计算
  RNN中隐藏层参数(包括权重参数、偏置项)
     隐藏层参数 =(h + x)* h + h
  其中,h是隐藏层输出的状态向量的维度(该维度和隐藏层神经元个数一致),x是输入层的输出向量维度,(h + x)* h 是权重参数,h是隐藏层中偏置项个数。
RNN长期依赖
  RNN的训练过程和全链接神经网络同样,都是采用反向传播算法经过计算梯度来更新参数。可是在RNN训练过程当中会存在长期依赖问题,这是因为RNN在训练时会遇到梯度消失或者梯度爆炸,所谓梯度消失和梯度爆炸是指在训练时计算和反向传播时,梯度在每一时刻都是倾向于递增或者递减的,通过一段时间后,当梯度收敛到零(梯度消失)或发散到无穷大(梯度爆炸),此时,参数就不会再更新,也就是说后期的训练不会再对参数的更新产生影响。形象的说,长期依赖问题就是当时间间隔增大时,当前时刻隐藏层会丧失链接到远处信息的能力。
这里写图片描述
  好比说,RNN在处理语言模型时,xo时刻输入的文本信息是“我家住在昆明市”,当时间间隔无限扩大,中间处理了不少其余的文本信息后,在t时刻忽然输入的文本信息是“我在市政府上班”,那么此时神经网络模型就会没法理解这个市政府究竟指的是哪一个城市,也就是说此时的隐藏层已经不具有记忆远处信息的能力了,这就是RNN中的长期依赖。
双向循环神经网络
  有时候利用RNN在处理语言模型时,只是基于前面的文本信息是不够的,同时还要考虑后面的信息,综合判断才可以作出预测。
  好比下面这一句话:
  个人手机坏了,我打算()一部新手机。
  要预测()中出现的词,若是只是基于前面的文本信息,这里多是修、买、或者心情很差,大哭一场等,可是若是考虑了后面的信息“一部新手机”,那么这里()出现“买”这个词的几率就最大。
这里写图片描述
  双向循环神经网络的结构按时间展开如上,能够看到,在双向循环神经网络隐藏层中多了一个反向传输的隐藏层,其前向传播过程能够用下面公式表示:
这里写图片描述
这里写图片描述
  其中,g()表示输出层y2的激活函数,A二、A2’分别表示隐藏层正向、反向时输出,V、V’表示隐藏层到输出层的权重参数,f()表示隐藏层激活函数,x2表示输入,W、W’表示上一时刻隐藏层输出到当前时刻隐藏层的权重,U、U’表示输入值到隐藏层的权重。从公式中能够看出,正向和反向隐藏层不共享参数的。
深层循环神经网络
  有时候当信息量太大时,简单RNN隐藏层是不能保证一次性记忆全部信息的,那么为了增长模型的表达能力,在有些循环神经网络隐藏层中会堆叠多个隐藏层,这样的神经网络咱们成为深层循环神经网络。
这里写图片描述
  深层循环神经网络按时间展开如上图所示。在深层循环神经网络中,隐藏层每个循环体中参数是共享的,不一样的循环体之间参数不共享。
RNN案例实战
  利用深度学习工具keras创建一个RNN模型,处理IMDb在线电影数据库中影评数据,IMDb中包含50000项影评数据,分为训练集和测试集各25000项,每一项影评都带有情感分类标签(1:正面 ;0:负面)。python

#数据预处理
from keras.datasets import imdb
from keras.preprocessing import sequence
from keras.preprocessing.text import Tokenizer

import re
re_tag = re.compile(r'<[^>]+>')

def rm_tags(text):
    return re_tag.sub('', text)

import os
def read_files(filetype):
    path = "D:data/aclImdb/"
    file_list=[]

    positive_path=path + filetype+"/pos/"
    for f in os.listdir(positive_path):
        file_list+=[positive_path+f]

    negative_path=path + filetype+"/neg/"
    for f in os.listdir(negative_path):
        file_list+=[negative_path+f]

    print('read',filetype, 'files:',len(file_list))

    all_labels = ([1] * 12500 + [0] * 12500) 

    all_texts  = []

    for fi in file_list:
        with open(fi,encoding='utf8') as file_input:
            all_texts += [rm_tags(" ".join(file_input.readlines()))]

    return all_labels,all_texts

y_train,train_text=read_files("train")

y_test,test_text=read_files("test")

token = Tokenizer(num_words=2000)
token.fit_on_texts(train_text)

x_train_seq = token.texts_to_sequences(train_text)
x_test_seq  = token.texts_to_sequences(test_text)

x_train = sequence.pad_sequences(x_train_seq, maxlen=100)
x_test  = sequence.pad_sequences(x_test_seq,  maxlen=100)

#创建RNN模型
from keras.models import Sequential
from keras.layers.core import Dense, Dropout, Activation
from keras.layers.embeddings import Embedding
from keras.layers.recurrent import SimpleRNN

model = Sequential()

model.add(Embedding(output_dim=32,
                    input_dim=2000, 
                    input_length=100))
model.add(Dropout(0.2))

model.add(SimpleRNN(units=16))

model.add(Dense(units=256,activation='relu' ))
model.add(Dropout(0.2))

model.add(Dense(units=1,activation='sigmoid' ))

model.summary()
#模型训练
model.compile(loss='binary_crossentropy', 
              optimizer='adam', 
              metrics=['accuracy'])

train_history =model.fit(x_train, y_train,batch_size=100, 
                         epochs=10,verbose=2,
                         validation_split=0.2)

import matplotlib.pyplot as plt
def show_train_history(train_history,train,validation):
    plt.plot(train_history.history[train])
    plt.plot(train_history.history[validation])
    plt.title('Train History')
    plt.ylabel(train)
    plt.xlabel('Epoch')
    plt.legend(['train', 'validation'], loc='upper left')
    plt.show()

show_train_history(train_history,'acc','val_acc')

show_train_history(train_history,'loss','val_loss')

# 评估模型的准确率

scores = model.evaluate(x_test, y_test, verbose=1)
scores[1]
#预测结果
predict=model.predict_classes(x_test)
predict[:10]

predict_classes=predict.reshape(-1)
predict_classes[:10]

# 查看预测结果

SentimentDict={1:'正面的',0:'负面的'}
def display_test_Sentiment(i):
    print(test_text[i])
    print('label真实值:',SentimentDict[y_test[i]],
          '预测结果:',SentimentDict[predict_classes[i]])

display_test_Sentiment(2)

display_test_Sentiment(12502)