文章目录

  • 书山有路勤为径,学海无涯苦作舟
    • 凡我不能创造的,我就不能理解
  • 一、RNN神经网络
  • 二、LSTM神经网络
  • 三、使用LSTM进行情感分析
    • 3.1深度学习在自然语言处理中的应用
    • 3.2词向量模型
      • 3.2.1 Word2Vec
    • 3.3 Recurrent Neural Networks (RNNs)
    • 3.4 Long Short Term Memory Units (LSTMs)
    • 3.5 案例流程
      • 3.5.1 导入数据
      • 3.5.2 基于word2vec的LSTM模型
        • 3.5.2.1超参数调整
        • 3.5.2.2 训练

书山有路勤为径,学海无涯苦作舟

凡我不能创造的,我就不能理解

一、RNN神经网络

传统神经网络,不同数据输入,各自进入自己的神经网络感知机进行计算,数据之间并没有深入的联系。如果不同数据之间具有联系,比如时序性,前一个时间会影响后一个时间的数据,传统的神经网络没办法实现数据之间的时序性。

递归神经网络是在传统的神经网络基础上的改进,普通的神经网络,先进入输入,隐藏层,在输出结果。而RNN网络会考虑数据之间的时间序列关系。

加入数据集中有一个时间序列,普通的神经网络并不能考虑这么一个序列,不认为t1和t2和t3之间的关系,每一个操作都是独立来进行的 。但是如果是一个时序的数据,数据之间就有相关性,那么网络能不能学习到由于时间的关系,而对最后的结果造成影响呢?这就是RNN的特点。

RNN的隐藏层,数据经过隐藏层后,得到的特征,再输出。RNN会把前一层的输出结果参与到下一层的输入计算,即X2数据进入计算的时候,此时的输入,不止X2还有X1的中间特征结果一起进入输入,同时传入到隐藏层之中。

  • 会把前一层得到的中间结果保留下来,参与下一层一起的运算。在计算Hn+1的时候会考虑到之前的H0-Hn。
    -

ht表示之前的所有数据的特征结果,在RNN一般只考虑最后的输出结果,前面H0-Ht-1都只是一个中间结果。

需要将每个单词通过word2vec转为向量,参与计算。

  • RNN记忆能力太强了,最后一个结果会考虑之前的所有的结果。但是有的时候其实前面的结果不都重要,可能也只有近期的数据才重要。记得太多也会造成误差和错误。
  • LSTM可以选择性的去忘记一些特征,过滤一些不必要的特征。

二、LSTM神经网络


RNN的核心:

门单元:

LSTM的基本的架构:

sigmoid函数

遗忘门:

更新门:

三、使用LSTM进行情感分析

3.1深度学习在自然语言处理中的应用

自然语言处理是教会机器如何去处理或者读懂人类语言的系统,主要应用领域:

  • 对话系统–聊天机器人(小冰)
  • 情感分析-对一段文本进行情感识别(我们一会要做的)
  • 图文映射- CNN和RNN的融合
  • 机器翻译-将一种语言翻译成另一种语言,现在谷歌做的太牛了
  • 语音识别–能不能应用到游戏上,王者荣耀摁的手疼

3.2词向量模型

计算机可只认识数字

将词进行编码转化为计算机可以认识的数值特征。word2vec,可以通过训练一个模型,将每一个词都转化为一个数值向量。每一个词的向量的长度都是一致的。

你可以将输入数据看成是一个16*D的一个矩阵。

词向量是具有空间意义的并不是简单的映射!例如,我们希望单词“love"和"adore’这两个词在向量空间中是有一定的相关性的,因为他们有类似的定义,他们都在类似的上下文中使用。单词的向量表示也被称之为词嵌入

对于不同的词,建立一个词袋模型或者说是TF-IDF模型,对于不同的词会区别对待,比如,喜欢和喜爱。这两者之间是完全不同的。
但是在词向量模型中,在一个高纬度中间中,意思相同的词,他们之间的距离是非常相近的。词向量并不是简单的数值编码,而是其中的每一个值都有一个实际的意义。

3.2.1 Word2Vec

为了去得到这些词嵌入,我们使用一个非常厉害的模型Word2Vec。简单的说,这个模型根据上下文的语境来推断出每个词的词向量。如果两个个词在上下文的语境中,可以被互相替换,那么这两个词的距离就非常近。在自然语言中,上下文的语境对分析词语的意义是非常重要的。比如,之前我们提到的"adore"和"love”这两个词,我们观察如下上下文的语境。

从句子中我们可以看到,这两个词通常在句子中是表现积极的,而且一般比名词或者名词组合要好。这也说明了,这两个词可以被互相替换,他们的意思是非常接近的。
对于句子的语法结构分析,上下文语境也是非常重要的。所有,这个模型的作用就是从一大堆句子(以Wikipedia为例)中为每个独一无二的单词进行建模,并且输出一个唯一的向量。Word2Vec模型的输出被称为一个嵌入矩阵。

通过word2Vec先将所有出现的词都训练成一个个向量,读取语料库,对比word2vec,将文章中的每一个词按照顺序,去取得每个词的向量。就完成了将每个词转为向量,

这个**嵌入矩阵(embeding Matrix)**包含训练集中每个词的一个向量。传统来讲,这个嵌入矩阵中的词向量数据会很大。

Word2NVec模型根据数据集中的每个句子进行训练,并且以一个固定窗口在句子上进行滑动,根据句子的上下文来预测固定窗口中间那个词的向量。然后根据一个损失函数和优化方法,来对这个模型进行训练。

3.3 Recurrent Neural Networks (RNNs)

现在,我们已经得到了神经网络的输入数据——词向量,接下来让我们看看需要构建的神经网络。NLP数据的一个独特之处是它是时间序列数据。每个单词的出现都依赖于它的前一个单词和后一个单词。由于这种依赖的存在,我们使用循环神经网络来处理这种时间序列数据。

循环神经网络的结构和你之前看到的那些前馈神经网络的结构可能有一些不一样。前馈神经网络由三部分组成,输入层,隐藏层和输出层。
![在这里插入图片描述](https://img-blog.csdnimg.cn/e12500ff5b754e58a0c351ab82f3e374.png?x-oss-process=image/watermark,type_ZHJvaWRzYW5zZmFsbGJhY2s,shadow_50,text_Q1NETiBA6amt6aOO5bCR5bm05ZCb,size_20,color_FFFFFF,t_70,g_se,x_16

前馈神经网络和RNN之前的主要区别就是RN考虑了时间的信息。在RNN中,句子中的每个单词都被考虑上了时间步骤。实际上,时间步长的数量将等于最大序列长度。

与每个时间步骤相关联的中间状态也被作为一个新的组件,称为隐藏状态向量th()。从抽象的角度来看,这个向量是用来封装和汇总前面时间步骤中所看到的所有信息。就像x(t)表示一个向量,它封装了一个特定单词的所有信息。

隐藏状态是当前单词向量和前一步的隐藏状态向量的函数。并且这两项之和需要通过激活函数来进行激活。


Max Sequence Length
不同的数据长度可能是不一样的,需要进行数据的统一,因为神经网络要求每天数据输入的格式是一样的,这样才能进行权重的计算与方向传播数据的修正。需要把选取一个适合的固定长度大小。我们一般取文本中出现次数最多的次数的文本长度,长的就切去,短的就设定一个填充值。

3.4 Long Short Term Memory Units (LSTMs)

长短期记忆网络单元,是另一个RN中的模块。从抽象的角度看,LSTM保存了文本中长期的依赖信息。正如我们前面所看到的,H在传统的RNN网络中是非常简单的,这种简单结构不能有效的将历史信息链接在一起”举个例子,在问答领域中,假设我们得到如下一段文本,那么LSTM就可以很好的将历史信息进行记录学习。

在这里,我们看到中间的句子对被问的问题没有影响。然而,第一句和第三句之间有很强的联系。对于一个典型的RNN网络,隐藏状态向量对于第二句的存储信息量可能比第一句的信息量会大很多。但是LSTM,基本上就会判断哪些信息是有用的,哪些是没用的,并且把有用的信息在LSTM中进行保存。

我们从更加技术的角度来谈谈 LSTM 单元,该单元根据输入数据 x(t) ,隐藏层输出 h(t) 。在这些单元中,h(t) 的表达形式比经典的 RNN 网络会复杂很多。这些复杂组件分为四个部分:输入门,输出门,遗忘门和一个记忆控制器。

每个门都将 x(t) 和 h(t-1) 作为输入(没有在图中显示出来),并且利用这些输入来计算一些中间状态。每个中间状态都会被送入不同的管道,并且这些信息最终会汇集到 h(t) 。为简单起见,我们不会去关心每一个门的具体推导。这些门可以被认为是不同的模块,各有不同的功能。输入门决定在每个输入上施加多少强调,遗忘门决定我们将丢弃什么信息,输出门根据中间状态来决定最终的 h(t)

3.5 案例流程

1)制作词向量,可以使用gensim这个库,也可以直接用现成的(可以自己训练,也可以用别人训练的,因为同一个语种互通),但是在专门的领域,有专业词汇的时候,还是要领域的词汇训练的效果好点,比如医学。
2)词和ID的映射,常规套路了 (tensorflow要求,方便取得向量)
3)构建RNN网络架构
4))训练我们的模型
5)试试咋样

3.5.1 导入数据

首先,我们需要去创建词向量。为了简单起见,我们使用训练好的模型来创建。

作为该领域的一个最大玩家,Google已经帮助我们在大规模数据集上训练出来了Word2Nec模型,包括1000亿个不同的词!在这个模型中,谷歌能创建300万个词向量,每个向量维度为300。

在理想情况下,我们将使用这些向量来构建模型,但是因为这个单词向量矩阵相当大(3.6G),我们用另外一个现成的小一些的该矩阵由GloVe进行训练得到。矩阵将包含400000个词向量,每个向量的维数为50。

我们将导入两个不同的数据结构,一个是包含400000个单词的Python列表,一个是包含所有单词向量值得400000*50维的嵌入矩阵

wordList 是词的ID映射,wordVectors是词向量。

import numpy as np
wordsList = np.load('./training_data/wordsList.npy')
print('Loaded the word list!')
wordsList = wordsList.tolist() #Originally loaded as numpy array
wordsList = [word.decode('UTF-8') for word in wordsList] #Encode words as UTF-8
wordVectors = np.load('./training_data/wordVectors.npy')
print ('Loaded the word vectors!')

Loaded the word list!
Loaded the word vectors!

print(len(wordsList))
print(wordVectors.shape)

400000
(400000, 50)

我们也可以在词库中搜索单词,比如"baseball",然后可以通过访问嵌入矩阵来得到相应的向量,如下:

先输入词,找到词的索引,根据词的索引找到其输出向量

baseballIndex = wordsList.index('baseball')
wordVectors[baseballIndex]

array([-1.93270004, 1.04209995, -0.78514999, 0.91033 , 0.22711 ,
-0.62158 , -1.64929998, 0.07686 , -0.58679998, 0.058831 ,
0.35628 , 0.68915999, -0.50598001, 0.70472997, 1.26639998,
-0.40031001, -0.020687 , 0.80862999, -0.90565997, -0.074054 ,
-0.87674999, -0.62910002, -0.12684999, 0.11524 , -0.55685002,
-1.68260002, -0.26291001, 0.22632 , 0.713 , -1.08280003,
2.12310004, 0.49869001, 0.066711 , -0.48225999, -0.17896999,
0.47699001, 0.16384 , 0.16537 , -0.11506 , -0.15962 ,
-0.94926 , -0.42833 , -0.59456998, 1.35660005, -0.27506 ,
0.19918001, -0.36008 , 0.55667001, -0.70314997, 0.17157 ], dtype=float32)

现在我们有了向量,我们的第一步就是输入一个句子,然后构造它的向量表示。假设我们现在的输入句子是“I thought the movie was incredible and inspiring”。为了得到词向量,我们可以使用TensorFlow的嵌入函数。这个函数有两个参数,一个是嵌入矩阵(在我们的情况下是词向量矩阵),另一个是每个词对应的索引。

import tensorflow as tf
maxSeqLength = 10 #Maximum length of sentence
numDimensions = 300 #Dimensions for each word vector
firstSentence = np.zeros((maxSeqLength), dtype='int32')
firstSentence[0] = wordsList.index("i")
firstSentence[1] = wordsList.index("thought")
firstSentence[2] = wordsList.index("the")
firstSentence[3] = wordsList.index("movie")
firstSentence[4] = wordsList.index("was")
firstSentence[5] = wordsList.index("incredible")
firstSentence[6] = wordsList.index("and")
firstSentence[7] = wordsList.index("inspiring")
#firstSentence[8] and firstSentence[9] are going to be 0
print(firstSentence.shape)
print(firstSentence) #Shows the row index for each word

(10,)
[ 41 804 201534 1005 15 7446 5 13767 0 0]

有0 是因为设定最大长度后,不够长度的就填充0,超过长度就要填充

tf.nn.embedding_lookup函数

数据管道如下所示:

将每一句话的词转为ID,通过tf.nn.embedding_lookup函数转为向量。这个函数需要输入ID映射列表和词向量表,这个函数会完成查找词向量表,再输出。

输出数据是一个10*50 的词矩阵,其中包括10个词,每个词的向量维度是50。就是去找到这些词对应的向量

with tf.Session() as sess:print(tf.nn.embedding_lookup(wordVectors,firstSentence).eval().shape)

(10, 50)

在整个训练集上面构造索引之前,我们先花一些时间来可视化我们所拥有的数据类型。这将帮助我们去决定如何设置最大序列长度的最佳值。在前面的例子中,我们设置了最大长度为10,但这个值在很大程度上取决于你输入的数据。

训练集我们使用的是IMDB数据集。这个数据集包含2500 条电影数据,其中1250O 条正向数据,12500条负向数据。这些数据都是存储在一个文本文件中.首先我们需要做的就是去解析这个文件。正向数据包含在一个文件中,负向数据包含在另一个文件中。

from os import listdir
from os.path import isfile, join
positiveFiles = ['./training_data/positiveReviews/' + f for f in listdir('./training_data/positiveReviews/') if isfile(join('./training_data/positiveReviews/', f))]
negativeFiles = ['./training_data/negativeReviews/' + f for f in listdir('./training_data/negativeReviews/') if isfile(join('./training_data/negativeReviews/', f))]
numWords = []
for pf in positiveFiles:with open(pf, "r", encoding='utf-8') as f:line=f.readline()counter = len(line.split())numWords.append(counter)
print('Positive files finished')for nf in negativeFiles:with open(nf, "r", encoding='utf-8') as f:line=f.readline()counter = len(line.split())numWords.append(counter)
print('Negative files finished')numFiles = len(numWords)
print('The total number of files is', numFiles)
print('The total number of words in the files is', sum(numWords))
print('The average number of words in the files is', sum(numWords)/len(numWords))

Positive files finished
Negative files finished
The total number of files is 25000
The total number of words in the files is 5844680
The average number of words in the files is 233.7872

用matplotlib画出出现次数最多的句子长度

import matplotlib.pyplot as plt
%matplotlib inline
plt.hist(numWords, 50)
plt.xlabel('Sequence Length')
plt.ylabel('Frequency')
plt.axis([0, 1200, 0, 8000])
plt.show()


从直方图和句子的平均单词数,我们认为将句子最大长度设置为250是可行的。设置阈值

maxSeqLength = 250

接下来,让我们看看如何将单个文件中的文本转换成索引矩阵,比如下面的代码就是文本中的其中一个评论。

fname = positiveFiles[3] #Can use any valid index (not just 3)
with open(fname) as f:for lines in f:print(lines)exit

This is easily the most underrated film inn the Brooks cannon. Sure, its flawed. It does not give a realistic view of homelessness (unlike, say, how Citizen Kane gave a realistic view of lounge singers, or Titanic gave a realistic view of Italians YOU IDIOTS). Many of the jokes fall flat. But still, this film is very lovable in a way many comedies are not, and to pull that off in a story about some of the most traditionally reviled members of society is truly impressive. Its not The Fisher King, but its not crap, either. My only complaint is that Brooks should have cast someone else in the lead (I love Mel as a Director and Writer, not so much as a lead).

接下来,我们将它转换成一个索引矩阵。

数据清洗

# 删除标点符号、括号、问号等,只留下字母数字字符
import re
strip_special_chars = re.compile("[^A-Za-z0-9 ]+")def cleanSentences(string):string = string.lower().replace("<br />", " ")return re.sub(strip_special_chars, "", string.lower())

将一句话转为一个Index值列表,句子长度没有到250就填充,大于250就切去。

try函数为了防止有些生僻词在词映射表中,没有找到的情况。

firstFile = np.zeros((maxSeqLength), dtype='int32')
with open(fname) as f:indexCounter = 0line=f.readline()cleanedLine = cleanSentences(line)split = cleanedLine.split()for word in split:try:firstFile[indexCounter] = wordsList.index(word)except ValueError:firstFile[indexCounter] = 399999 #Vector for unknown wordsindexCounter = indexCounter + 1
firstFile

array([ 37, 14, 2407, 201534, 96, 37314, 319, 7158,
201534, 6469, 8828, 1085, 47, 9703, 20, 260,
36, 455, 7, 7284, 1139, 3, 26494, 2633,
203, 197, 3941, 12739, 646, 7, 7284, 1139,
3, 11990, 7792, 46, 12608, 646, 7, 7284,
1139, 3, 8593, 81, 36381, 109, 3, 201534,
8735, 807, 2983, 34, 149, 37, 319, 14,
191, 31906, 6, 7, 179, 109, 15402, 32,
36, 5, 4, 2933, 12, 138, 6, 7,
523, 59, 77, 3, 201534, 96, 4246, 30006,
235, 3, 908, 14, 4702, 4571, 47, 36,
201534, 6429, 691, 34, 47, 36, 35404, 900,
192, 91, 4499, 14, 12, 6469, 189, 33,
1784, 1318, 1726, 6, 201534, 410, 41, 835,
10464, 19, 7, 369, 5, 1541, 36, 100,
181, 19, 7, 410, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0])

现在,我们用相同的方法来处理全部的25000 条评论。我们将导入电影训练集,并且得到一个2500*250的矩阵。这是一个计算成本非常高的过程,可以直接使用理好的索引矩阵文件。

np.save()可以保存下来词的映射矩阵,本案例是直接使用做好的当前评论的Index

# ids = np.zeros((numFiles, maxSeqLength), dtype='int32')
# fileCounter = 0
# for pf in positiveFiles:
#    with open(pf, "r") as f:
#        indexCounter = 0
#        line=f.readline()
#        cleanedLine = cleanSentences(line)
#        split = cleanedLine.split()
#        for word in split:
#            try:
#                ids[fileCounter][indexCounter] = wordsList.index(word)
#            except ValueError:
#                ids[fileCounter][indexCounter] = 399999 #Vector for unkown words
#            indexCounter = indexCounter + 1
#            if indexCounter >= maxSeqLength:
#                break
#        fileCounter = fileCounter + 1# for nf in negativeFiles:
#    with open(nf, "r") as f:
#        indexCounter = 0
#        line=f.readline()
#        cleanedLine = cleanSentences(line)
#        split = cleanedLine.split()
#        for word in split:
#            try:
#                ids[fileCounter][indexCounter] = wordsList.index(word)
#            except ValueError:
#                ids[fileCounter][indexCounter] = 399999 #Vector for unkown words
#            indexCounter = indexCounter + 1
#            if indexCounter >= maxSeqLength:
#                break
#        fileCounter = fileCounter + 1
# #Pass into embedding function and see if it evaluates.# np.save('idsMatrix', ids)
ids = np.load('./training_data/idsMatrix.npy')

3.5.2 基于word2vec的LSTM模型

辅助函数

用来拿到数据

from random import randintdef getTrainBatch():labels = []arr = np.zeros([batchSize, maxSeqLength])for i in range(batchSize):if (i % 2 == 0): num = randint(1,11499)labels.append([1,0])else:num = randint(13499,24999)labels.append([0,1])arr[i] = ids[num-1:num]return arr, labelsdef getTestBatch():labels = []arr = np.zeros([batchSize, maxSeqLength])for i in range(batchSize):num = randint(11499,13499)if (num <= 12499):labels.append([1,0])else:labels.append([0,1])arr[i] = ids[num-1:num]return arr, labels

RNN model
现在,我们可以开始构建我们的 TensorFlow 图模型。首先,我们需要去定义一些超参数,比如批处理大小,LSTM的单元个数,分类类别和训练次数。

batchSize = 24
lstmUnits = 64
numClasses = 2
iterations = 50000

与大多数 TensorFlow 图一样,现在我们需要指定两个占位符,一个用于数据输入,另一个用于标签数据。对于占位符,最重要的一点就是确定好维度。

标签占位符代表一组值,每一个值都为 [1,0] 或者 [0,1],这个取决于数据是正向的还是负向的。输入占位符,是一个整数化的索引数组。

import tensorflow as tf
tf.reset_default_graph()labels = tf.placeholder(tf.float32, [batchSize, numClasses])
input_data = tf.placeholder(tf.int32, [batchSize, maxSeqLength])

一旦,我们设置了我们的输入数据占位符,我们可以调用 tf.nn.embedding_lookup() 函数来得到我们的词向量。该函数最后将返回一个三维向量,第一个维度是批处理大小,第二个维度是句子长度,第三个维度是词向量长度。更清晰的表达,如下图所示:

data = tf.Variable(tf.zeros([batchSize, maxSeqLength, numDimensions]),dtype=tf.float32)
data = tf.nn.embedding_lookup(wordVectors,input_data)

现在我们已经得到了我们想要的数据形式,那么揭晓了我们看看如何才能将这种数据形式输入到我们的 LSTM 网络中。首先,我们使用 tf.nn.rnn_cell.BasicLSTMCell 函数,这个函数输入的参数是一个整数,表示需要几个 LSTM 单元。这是我们设置的一个超参数,我们需要对这个数值进行调试从而来找到最优的解。然后,我们会设置一个 dropout 参数,以此来避免一些过拟合。

最后,我们将 LSTM cell 和三维的数据输入到 tf.nn.dynamic_rnn ,这个函数的功能是展开整个网络,并且构建一整个 RNN 模型。

lstmCell = tf.contrib.rnn.BasicLSTMCell(lstmUnits)
lstmCell = tf.contrib.rnn.DropoutWrapper(cell=lstmCell, output_keep_prob=0.75)
value, _ = tf.nn.dynamic_rnn(lstmCell, data, dtype=tf.float32)

堆栈 LSTM 网络是一个比较好的网络架构。也就是前一个LSTM 隐藏层的输出是下一个LSTM的输入。堆栈LSTM可以帮助模型记住更多的上下文信息,但是带来的弊端是训练参数会增加很多,模型的训练时间会很长,过拟合的几率也会增加。

dynamic RNN 函数的第一个输出可以被认为是最后的隐藏状态向量。这个向量将被重新确定维度,然后乘以最后的权重矩阵和一个偏置项来获得最终的输出值。

weight = tf.Variable(tf.truncated_normal([lstmUnits, numClasses]))
bias = tf.Variable(tf.constant(0.1, shape=[numClasses]))
value = tf.transpose(value, [1, 0, 2])
#取最终的结果值
last = tf.gather(value, int(value.get_shape()[0]) - 1)
prediction = (tf.matmul(last, weight) + bias)

接下来,我们需要定义正确的预测函数和正确率评估参数。正确的预测形式是查看最后输出的0-1向量是否和标记的0-1向量相同。

correctPred = tf.equal(tf.argmax(prediction,1), tf.argmax(labels,1))
accuracy = tf.reduce_mean(tf.cast(correctPred, tf.float32))

之后,我们使用一个标准的交叉熵损失函数来作为损失值。对于优化器,我们选择 Adam,并且采用默认的学习率。

loss = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(logits=prediction, labels=labels))
optimizer = tf.train.AdamOptimizer().minimize(loss)

3.5.2.1超参数调整

选择合适的超参数来训练你的神经网络是至关重要的。你会发现你的训练损失值与你选择的优化器(Adam,Adadelta,SGD,等等),学习率和网络架构都有很大的关系。特别是在RNN和LSTM中,单元数量和词向量的大小都是重要因素。

  • 学习率:RNN最难的一点就是它的训练非常困难,因为时间步骤很长。那么,学习率就变得非常重要了。如果我们将学习率设置的很大,那么学习曲线就会波动性很大,如果我们将学习率设置的很小,那么训练过程就会非常缓慢。根据经验,将学习率默认设置为 0.001 是一个比较好的开始。如果训练的非常缓慢,那么你可以适当的增大这个值,如果训练过程非常的不稳定,那么你可以适当的减小这个值。
  • 优化器:这个在研究中没有一个一致的选择,但是 Adam 优化器被广泛的使用。
  • LSTM单元的数量:这个值很大程度上取决于输入文本的平均长度。而更多的单元数量可以帮助模型存储更多的文本信息,当然模型的训练时间就会增加很多,并且计算成本会非常昂贵。
  • 词向量维度:词向量的维度一般我们设置为50到300。维度越多意味着可以存储更多的单词信息,但是你需要付出的是更昂贵的计算成本。

3.5.2.2 训练

训练过程的基本思路是,我们首先先定义一个 TensorFlow 会话。然后,我们加载一批评论和对应的标签。接下来,我们调用会话的 run 函数。这个函数有两个参数,第一个参数被称为 fetches 参数,这个参数定义了我们感兴趣的值。我们希望通过我们的优化器来最小化损失函数。第二个参数被称为 feed_dict 参数。这个数据结构就是我们提供给我们的占位符。我们需要将一个批处理的评论和标签输入模型,然后不断对这一组训练数据进行循环训练。

sess = tf.InteractiveSession()
saver = tf.train.Saver()
sess.run(tf.global_variables_initializer())for i in range(iterations):#Next Batch of reviewsnextBatch, nextBatchLabels = getTrainBatch();sess.run(optimizer, {input_data: nextBatch, labels: nextBatchLabels}) if (i % 1000 == 0 and i != 0):loss_ = sess.run(loss, {input_data: nextBatch, labels: nextBatchLabels})accuracy_ = sess.run(accuracy, {input_data: nextBatch, labels: nextBatchLabels})print("iteration {}/{}...".format(i+1, iterations),"loss {}...".format(loss_),"accuracy {}...".format(accuracy_))    #Save the network every 10,000 training iterationsif (i % 10000 == 0 and i != 0):save_path = saver.save(sess, "models/pretrained_lstm.ckpt", global_step=i)print("saved to %s" % save_path)

iteration 1001/50000… loss 0.6308178901672363… accuracy 0.5…
iteration 2001/50000… loss 0.7168402671813965… accuracy 0.625…
iteration 3001/50000… loss 0.7420873641967773… accuracy 0.5…
iteration 4001/50000… loss 0.650059700012207… accuracy 0.5416666865348816…
iteration 5001/50000… loss 0.6791467070579529… accuracy 0.5…
iteration 6001/50000… loss 0.6914048790931702… accuracy 0.5416666865348816…
iteration 7001/50000… loss 0.36072710156440735… accuracy 0.8333333134651184…
iteration 8001/50000… loss 0.5486791729927063… accuracy 0.75…
iteration 9001/50000… loss 0.41976991295814514… accuracy 0.7916666865348816…
iteration 10001/50000… loss 0.10224487632513046… accuracy 1.0…
saved to models/pretrained_lstm.ckpt-10000
iteration 11001/50000… loss 0.37682783603668213… accuracy 0.8333333134651184…
iteration 12001/50000… loss 0.266050785779953… accuracy 0.9166666865348816…
iteration 13001/50000… loss 0.40790924429893494… accuracy 0.7916666865348816…
iteration 14001/50000… loss 0.22000855207443237… accuracy 0.875…
iteration 15001/50000… loss 0.49727579951286316… accuracy 0.7916666865348816…
iteration 16001/50000… loss 0.21477992832660675… accuracy 0.9166666865348816…
iteration 17001/50000… loss 0.31636106967926025… accuracy 0.875…
iteration 18001/50000… loss 0.17190784215927124… accuracy 0.9166666865348816…
iteration 19001/50000… loss 0.11049345880746841… accuracy 1.0…
iteration 20001/50000… loss 0.06362085044384003… accuracy 1.0…
saved to models/pretrained_lstm.ckpt-20000
iteration 21001/50000… loss 0.19093847274780273… accuracy 0.9583333134651184…
iteration 22001/50000… loss 0.06586482375860214… accuracy 0.9583333134651184…
iteration 23001/50000… loss 0.02577809803187847… accuracy 1.0…
iteration 24001/50000… loss 0.0732395276427269… accuracy 0.9583333134651184…
iteration 25001/50000… loss 0.30879321694374084… accuracy 0.9583333134651184…
iteration 26001/50000… loss 0.2742778956890106… accuracy 0.9583333134651184…
iteration 27001/50000… loss 0.23742587864398956… accuracy 0.875…
iteration 28001/50000… loss 0.04694415628910065… accuracy 1.0…
iteration 29001/50000… loss 0.031666990369558334… accuracy 1.0…
iteration 30001/50000… loss 0.09171193093061447… accuracy 1.0…
saved to models/pretrained_lstm.ckpt-30000
iteration 31001/50000… loss 0.03852967545390129… accuracy 1.0…
iteration 32001/50000… loss 0.06964454054832458… accuracy 1.0…
iteration 33001/50000… loss 0.12447216361761093… accuracy 0.9583333134651184…
iteration 34001/50000… loss 0.008963108994066715… accuracy 1.0…
iteration 35001/50000… loss 0.04129207879304886… accuracy 0.9583333134651184…
iteration 36001/50000… loss 0.0081111378967762… accuracy 1.0…
iteration 37001/50000… loss 0.022405564785003662… accuracy 1.0…
iteration 38001/50000… loss 0.03473325073719025… accuracy 1.0…
iteration 39001/50000… loss 0.09315425157546997… accuracy 0.9583333134651184…
iteration 40001/50000… loss 0.3166258931159973… accuracy 0.9583333134651184…
saved to models/pretrained_lstm.ckpt-40000
iteration 41001/50000… loss 0.03648881986737251… accuracy 1.0…
iteration 42001/50000… loss 0.2616865932941437… accuracy 0.9583333134651184…
iteration 43001/50000… loss 0.013914794661104679… accuracy 1.0…
iteration 44001/50000… loss 0.020460862666368484… accuracy 1.0…
iteration 45001/50000… loss 0.15876878798007965… accuracy 0.9583333134651184…
iteration 46001/50000… loss 0.007766606751829386… accuracy 1.0…
iteration 47001/50000… loss 0.02079685777425766… accuracy 1.0…
iteration 48001/50000… loss 0.017801295965909958… accuracy 1.0…
iteration 49001/50000… loss 0.017789073288440704… accuracy 1.0…


查看上面的训练曲线,我们发现这个模型的训练结果还是不错的。损失值在稳定的下降,正确率也不断的在接近 100% 。然而,当分析训练曲线的时候,我们应该注意到我们的模型可能在训练集上面已经过拟合了。过拟合是机器学习中一个非常常见的问题,表示模型在训练集上面拟合的太好了,但是在测试集上面的泛化能力就会差很多。也就是说,如果你在训练集上面取得了损失值是 0 的模型,但是这个结果也不一定是最好的结果。当我们训练 LSTM 的时候,提前终止是一种常见的防止过拟合的方法。基本思路是,我们在训练集上面进行模型训练,同事不断的在测试集上面测量它的性能。一旦测试误差停止下降了,或者误差开始增大了,那么我们就需要停止训练了。因为这个迹象表明,我们网络的性能开始退化了。

导入一个预训练的模型需要使用 TensorFlow 的另一个会话函数,称为 Server ,然后利用这个会话函数来调用 restore 函数。这个函数包括两个参数,一个表示当前的会话,另一个表示保存的模型。

sess = tf.InteractiveSession()
saver = tf.train.Saver()
saver.restore(sess, tf.train.latest_checkpoint('models'))

INFO:tensorflow:Restoring parameters from models\pretrained_lstm.ckpt-40000

然后,从我们的测试集中导入一些电影评论。请注意,这些评论是模型从来没有看见过的。

iterations = 10
for i in range(iterations):nextBatch, nextBatchLabels = getTestBatch();print("Accuracy for this batch:", (sess.run(accuracy, {input_data: nextBatch, labels: nextBatchLabels})) * 100)

Accuracy for this batch: 91.6666686535
Accuracy for this batch: 79.1666686535
Accuracy for this batch: 87.5
Accuracy for this batch: 87.5
Accuracy for this batch: 91.6666686535
Accuracy for this batch: 75.0
Accuracy for this batch: 91.6666686535
Accuracy for this batch: 70.8333313465
Accuracy for this batch: 83.3333313465
Accuracy for this batch: 95.8333313465

《自然语言处理学习之路》 13 RNN简述,LSTM情感分析相关推荐

  1. 视频教程-自然语言处理实战——LSTM情感分析-深度学习

    自然语言处理实战--LSTM情感分析 CSDN讲师名下集合了诸多业界知名讲师的公开课内容,内容涵盖人工智能.大数据.区块链等诸多热门技术领域的最佳技术实践,聚合美团.滴滴.AWS.科大讯飞等知名企业的 ...

  2. ZYNQ学习之路13.创建PetaLinux工程

    在前面的学习中,我们知道如何根据PetaLinux BSP设计去创建一个工程,现在,我们结合Vivado设计我们自己PetaLinux系统. 开发环境:Ubuntu16 64bit, PetaLinu ...

  3. Stanford CS230深度学习(七)RNN和LSTM

    在CS230的lecture 7中主要讲了神经网络的解释性,包括: 显著性图saliency maps(计算pre-softmax分数关于输入层的梯度并可视化) 遮挡敏感性occlusion sens ...

  4. 唐宇迪之tensorflow学习笔记项目实战(LSTM情感分析)

    我们首先来看看RNN的网络结构,如下图所示 xt 表示第t,t=1,2,3-步(step)的输入 st 为隐藏层的第t步的状态,它是网络的记忆单元. st=f(u×xt+w×st−1) ,其中f一般是 ...

  5. 基于深度学习的汽车行业评论文本的情感分析

    使用卷积神经网络对汽车行业评论文本进行情感分析. dateset 爬取汽车之家车主口碑评论文本,抽取口碑中最满意以及最不满意评论文本,分别作为正向情感语料库和负向情感语料库. 爬虫技术视频链接:htt ...

  6. 使用深度学习模型在 Java 中执行文本情感分析

    积极的? 消极的? 中性的? 使用斯坦福 CoreNLP 组件以及几行代码便可对句子进行分析. 本文介绍如何使用集成到斯坦福 CoreNLP(一个用于自然语言处理的开源库)中的情感工具在 Java 中 ...

  7. [深度学习] PyTorch 实现双向LSTM 情感分析

    一  前言 情感分析(Sentiment Analysis),也称为情感分类,属于自然语言处理(Natural Language Processing,NLP)领域的一个分支任务,随着互联网的发展而兴 ...

  8. 数据挖掘作业学习学习笔记-电商产品评论数据情感分析

    使用的教材:<电商产品评论数据情感分析> 作业&学习笔记:数据挖掘第14周 说明:书本内容详实.此篇用于自己期末回顾知识的重点内容,故做出的学习笔记缺省了书本原本的其他精粹. 随着 ...

  9. 《自然语言处理学习之路》15 Seq2Seq、Attention机制

    书山有路勤为径,学海无涯苦作舟 黑发不知勤学早,白首反悔读书迟. 1. Sequence-to-Sequence(N to M) 1.1 简介 先编码,再解码.STAR开始解码,END终止解码. EN ...

最新文章

  1. JAVA/PHP/C#版RSA验签--转
  2. 超赞思想!牛津大学提出 PSViT 让Transformer模型不在冗余!!!
  3. 用算法描述对数几率回归,逻辑回归算法描述,参考答案
  4. Unity3D面试问题
  5. VC编译选项 /EHa 异常处理
  6. php方便,两个方便测试PHP特性的小程序
  7. Chrome控制台console的各种用法(方便调试)
  8. Javascript:Ajax案例实操
  9. 【微软2014实习生及秋令营技术类职位在线測试】题目2 : K-th string
  10. SQL注入攻击和防御
  11. back to wuxi
  12. 时尚pr标题模板,简约故障风格pr文字模板
  13. 你知道吗?申报深圳市专精特新企业必须先申报创新型中小企业!
  14. GB2312汉字编码字符集对照表
  15. USB转串口(rj45)使用secureCRT调试设备
  16. C公司的产品项目:一家组织架构参考
  17. Windows Phone 7游戏高级编程:使用XNA Game Studio 4
  18. Linux 压缩管理、进程管理、网络管理命令总结
  19. 【Paper Note】基于情感分析和关系网络的影视产品评论数据文本挖掘研究
  20. Flume官方文档翻译之(十二)

热门文章

  1. 知行合一(科学实践理论)
  2. java错误 找不到或无法加载主类_JAVA报找不到或无法加载主类的错误
  3. 有趣的bat(批处理)文件~~
  4. sat2 计算机科目,sat2
  5. 带你初识JSP(JAVA服务器页面)
  6. 利用python做微信聊天记录词云分析——记录美好回忆
  7. 360浏览器保存图片是webp格式怎么解决
  8. java 读取串口数据
  9. 基本的排序算法c++实现
  10. SQL Server之查询检索操作