**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-1、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的激活函数,A2、A2’分别表示隐藏层正向、反向时输出,V、V’表示隐藏层到输出层的权重参数,f()表示隐藏层激活函数,x2表示输入,W、W’表示上一时刻隐藏层输出到当前时刻隐藏层的权重,U、U’表示输入值到隐藏层的权重。从公式中可以看出,正向和反向隐藏层不共享参数的。
深层循环神经网络
有时候当信息量太大时,简单RNN隐藏层是不能保证一次性记忆所有信息的,那么为了增加模型的表达能力,在有些循环神经网络隐藏层中会堆叠多个隐藏层,这样的神经网络我们成为深层循环神经网络。
深层循环神经网络按时间展开如上图所示。在深层循环神经网络中,隐藏层每一个循环体中参数是共享的,不同的循环体之间参数不共享。
RNN案例实战
利用深度学习工具keras建立一个RNN模型,处理IMDb在线电影数据库中影评数据,IMDb中包含50000项影评数据,分为训练集和测试集各25000项,每一项影评都带有情感分类标签(1:正面 ;0:负面)。
#数据预处理
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 = ([] * + [] * )
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=)
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=)
x_test = sequence.pad_sequences(x_test_seq, maxlen=)
#建立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=,
input_dim=,
input_length=))
model.add(Dropout())
model.add(SimpleRNN(units=))
model.add(Dense(units=,activation='relu' ))
model.add(Dropout())
model.add(Dense(units=,activation='sigmoid' ))
model.summary()
#模型训练
model.compile(loss='binary_crossentropy',
optimizer='adam',
metrics=['accuracy'])
train_history =model.fit(x_train, y_train,batch_size=,
epochs=,verbose=,
validation_split=)
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=)
scores[]
#预测结果
predict=model.predict_classes(x_test)
predict[:]
predict_classes=predict.reshape(-)
predict_classes[:]
# 查看预测结果
SentimentDict={:'正面的',:'负面的'}
def display_test_Sentiment(i):
print(test_text[i])
print('label真实值:',SentimentDict[y_test[i]],
'预测结果:',SentimentDict[predict_classes[i]])
display_test_Sentiment()
display_test_Sentiment()