循环神经网络中的激活函数多采用双曲正切函数tanh,而不是relu.循环神经网络中的第一步不像后面那样,有上一个输出返送回来的数据一起作为输入,但为了保证每一步操作的统一性,我们一般也会手动的添加一个共同的输入(a0)。比如一个全0的向量
我们这句话有4个词,第一个词向量x1和a0一起输入,得到一个输入a1,当然对于一个分类问题,我们不需要最后的预测输出。所以我们把输出部分删除,只保留激活值a1。然后第二个词向量x2和a1一起输入,得到第二个输出a2,同样不需要预测输出,只保留激活值a2。
这样画出的两个图是在时间上的行为并不是空间上的行为。
同样第三个词向量x3和a2一起输入得到第三个输出a3,x4和a3得到a4。
红色就是前向传播的过程,蓝色是反向传播的过程。
循环神经网络可以在一定程度上,应对这种在时间上有依赖的序列问题。
第一步,拿出句子的第一个词向量x1,和aL1_0合并输入第一层得到输出aL1_1,aL1_1作为第二层的输入,和第二层的aL_0合并输入第二层,得到第二层的输出aL2_1,第二步,拿出句子的第二个词向量x2,和上一层保存的aL1_1合并输入第一层。以此类推。
举个例子,上海电视台记者在北京街头采访了一名来自四川的年轻人,他性格开朗,热爱生活。他的家乡有一种著名的动物叫做——( 熊猫 )
但是四川这一词,距离后面非常远,换句话说依赖的路径十分的长,标准的RNN结构在这种“长依赖”问题上表现并不是非常好。为解决此问题。最著名的解决方法——LSTM,长短时记忆法发,并不复杂,可以把这个过程看作是在玩一个小孩搭积木的游戏。
遗忘门——利用sigmoid函数来实现,众所周知,sigmoid函数的输出值在0—1之间,如果我们让sigmoid层的输出和上个细胞状态值相乘,如果sigmoid函数值为1,则为上个细胞值全部保留,如果sigmoid值为0,则对应上个细胞值全部抛弃,如果sigmoid的值介于0-1之间,则对应部分记忆或部分遗忘。
更新们——网络就会选择重要的词汇更新细胞状态。
输出门——经过tanh函数后输出,我们可以在遇到重要的词汇时产生强输出,不重要的词汇时产生弱输出。
LSTM之所以能够应对更长的序列依赖,正是因为,除了输入和输出以外,他还添加了细胞状态的概念。
http://colah.github.io/posts/2015-08-Understanding-LSTMs/
import shopping_data
from keras.preprocessing import sequence
from keras.models import Sequential
from keras.layers import Dense,Embedding
from keras.layers import Flatten
from keras.layers import LSTM
x_train,y_train,x_test,y_test=shopping_data.load_data()
print('x_train.shape:',x_train.shape)
print('y_train.shape:',y_train.shape)
print('x_test.shape:',x_test.shape)
print('y_test.shape:',y_test.shape)
print(x_train[0])
print(y_train[0])
vocalen,word_index=shopping_data.createWordIndex(x_train,x_test)
print(word_index)
print('词典总词数:',vocalen)
x_train_index=shopping_data.word2Index(x_train,word_index)
x_test_index=shopping_data.word2Index(x_test,word_index)
maxlen=25
x_train_index=sequence.pad_sequences(x_train_index,maxlen=maxlen)
x_test_index=sequence.pad_sequences(x_test_index,maxlen=maxlen)
model = Sequential()
model.add(Embedding(trainable=False,input_dim=vocalen,output_dim=300,input_length=maxlen))
model.add(LSTM(128,return_sequences=True)) #LSTM输出的维度128维
model.add(LSTM(128))
model.add(Dense(1,activation='sigmoid'))
model.compile(loss='binary_crossentropy',optimizer='adam',metrics=['accuracy'])
#adam是一种使用动量的自适应优化器,比sgd要快
model.fit(x_train_index,y_train,epochs=200,batch_size=512)
score,acc=model.evaluate(x_test_index,y_test)
print('Test score:',score)
print('Test accuracy:',acc)
经过训练比上节课的全连接层神经网络的准确率要好。
再试试打开Embedding的trainable让词向量同时训练,看看有什么效果
上次实验的全连接层准确率也是如此
因为当我们把嵌入层的训练打开后,嵌入层的参数矩阵会同步参与网络的训练,而我们这嵌入层参数数量有300*9512.换句话说训练的中心跑到这个词嵌入矩阵参数上,所以导致LSTM和普通的全连接层在打开这个嵌入曾层的训练后差别不是很大。而我们这里的语料数据集又不是很丰富,所以最后的词向量训练的效果必然不是太友好。所以最常用的方法时使用别人在海量数据集上训练出来的词向量,