Keras学习之四:用RNN进行评论好恶预测

1 RNN简介

RNN即循环神经网络,其主要用途是处理和预测序列数据。在CNN中,神经网络层间采用全连接的方式连接,但层内节点之间却无连接。RNN为了处理序列数据,层内节点的输出还会重新输入本层,以实现学习历史,预测未来。
RNN的两个主要改进是LSTM(长短时记忆网络)GRU(门控循环单元),二者为基本神经单元增加了额外的功能门,从而更好的实现长时记忆的处理。
在此基础上,通过两层或者多个RNN层的堆叠,可以实现双向循环神经网络(bidirectionalRNN)及深层循环神经网络(deepRNN)。

2 Keras对RNN的支持

Keras在layers包的recurrent模块中实现了RNN相关层模型的支持,并在wrapper模块中实现双向RNN的包装器。

2.1 recurrent模块导出类

名称作用原型参数
SimpleRNN全连接RNN网络SimpleRNN(units, activation=’tanh’, use_bias=True, kernel_initializer=’glorot_uniform’, recurrent_initializer=’orthogonal’, bias_initializer=’zeros’,dropout=0.0, recurrent_dropout=0.0))
GRU门限循环单元层GRU(units, activation=’tanh’, recurrent_activation=’hard_sigmoid’, use_bias=True, kernel_initializer=’glorot_uniform’, recurrent_initializer=’orthogonal’, bias_initializer=’zeros’, dropout=0.0, recurrent_dropout=0.0)
LSTM长短期记忆模型层LSTM(units, activation=’tanh’, recurrent_activation=’hard_sigmoid’, use_bias=True, kernel_initializer=’glorot_uniform’, recurrent_initializer=’orthogonal’, bias_initializer=’zeros’, unit_forget_bias=True, dropout=0.0, recurrent_dropout=0.0)

2.2 wrapper模块导出类

名称作用原型参数
TimeDistributedTimeDistributed(layer)
Bidirectional双向RNN包装器Bidirectional(layer, merge_mode=’concat’, weights=None)
model = Sequential()
model.add(Bidirectional(LSTM(10, return_sequences=True), input_shape=(5, 10)))
model.add(Bidirectional(LSTM(10)))
model.add(Dense(5))
model.add(Activation('softmax'))
model.compile(loss='categorical_crossentropy', optimizer='rmsprop')

2.3 参数说明

名称作用
unitsRNN的单元数,也是输出维度
activation激活函数,为预定义的激活函数名
dropout0~1之间的浮点数,控制输入线性变换的神经元断开比例
recurrent_dropout0~1之间的浮点数,控制循环状态的线性变换的神经元断开比例
return_sequencesTrue返回整个序列,用于stack两个层,False返回输出序列的最后一个输出
go_backwardsTrue,逆向处理输入序列并返回逆序后的序列
statefulTrue,则一个batch中下标为i的样本的最终状态将会用作下一个batch同样下标的样本的初始状态
implementation0为大的矩阵乘法,便于CPU加速。1为小矩阵乘法 便于GPU加速。2 LSTM和GRU有效,优化GPU运行
input_dim当使用该层为模型首层时,应指定该值
input_length当输入序列的长度固定时,该参数为输入序列的长度。当需要在该层后连接Flatten层,然后又要连接Dense层时,需要指定该参数
merge_mode前向和后向RNN输出的结合方式,为sum,mul,concat,ave和None之一,若为None,则不结合,以列表形式返回

3 情感分析示例

下面的示例使用了LSTM模型,通过对豆瓣电视剧评论进行训练,最终使得模型可以对评论的好恶进行预测,或者说简单的情感分析。

3.1 语料处理

原始语料来自豆瓣,采集了约100w条豆瓣国产剧评论及对应的评分。在语料处理中,借助jeiba分词工具进行分词,并去除停词。这里下载停词表。最终处理得到的语料类似于下面的格式,即每行一条评论。每行第一个字段为评分,其余字段为分词去停词后的评论。
将语料文件review.csv放在corpus目录下。将stop_word.txt放在dict目录下

5  经典作品 道听途说 以为 懂 实际 读 觉得 独特 意想不到 新颖                  
5  东京 看过 热海 看过 回家                  
5  这部 电影 里 看出 东西 太多 比方说 注意 尾道 家中 鸡冠花 会 明白 黑白 影像 彩色 影像 不能 取代 魅力 母亲 热海 防波堤 上 说 东京 游览 热海 回家 真是 人生 非常 隽永 总结                  
5  刚 几幕 觉得 极 做作 哪有 众人 说 这般 好 再 耐心 看 下去 方 发觉 表面 客套 微笑 下 内心深处 悲凉 其实 很 幸福 其实 幸福 等到 老时 会 老伴 相视而笑
5  生活 总是 人 失望                  

3.2 评分与情感的关系

为了简化示例,简单的认为1-2分为负面情感,4-5分为正面情感。未评分及3分为中性,不计入训练。这样将问题转化为一个二分类问题。

3.3 文本向量表示

借助Keras提供的文本预处理类Tokenizer,可以很容易的实现文本向量化。处理代码如下:

texts = []
labels = []
for line in lines:
    fields = line.split()
    rate = int(fields[0])
    if rate==0 or rate==3:
        continue
    elif rate < 3:
        rate = 0
    else:
        rate = 1
    cont = fields[1:]
    cont = " ".join(cont)
    texts.append(cont)
    labels.append(rate)
tokenizer.fit_on_texts(texts)
tokenizer.texts_to_sequence(texts)

由于每句长度不同,为便于计算,最终统一用0填充成长度为100的向量.

3.4 模型构建

采用双向LSTM的结构,构建代码如下:

model = Sequential()
model.add(Embedding(vocab_size, 256, input_length=sentence_max_len))
model.add(Bidirectional(LSTM(128,implementation=2)))
model.add(Dropout(0.5))
model.add(Dense(2, activation='relu'))
model.compile('RMSprop', 'categorical_crossentropy', metrics=['accuracy'])

3.5 完整代码

from keras.models import Sequential
from keras.preprocessing.text import Tokenizer
import keras.preprocessing.sequence as S
from keras.utils import to_categorical
from keras.layers import Embedding, Bidirectional, LSTM, Dropout, Dense

import jieba
import json
import numpy as np

vocab_size = 350000
sentence_max_len = 100
model_path = 'keras.h5'

class SentimentLSTM:
    def __init__(self):
        self.tokenizer = Tokenizer(num_words=vocab_size)
        self.stop_words = []
        self.model = None

    def load_stop_word(self,path='dict/stop_word.txt'):
        with open(path, 'r') as f:
            for line in f:
                content = line.strip()
                self.stop_words.append(content.decode('utf-8'))

    def jieba_cut(self,line):
        lcut = jieba.lcut(line)
        cut = [x for x in lcut if x not in self.stop_words]
        cut = " ".join(cut)
        return cut

    def load_cuted_corpus(self, dir, input):
        f = open(dir + '/' + input , 'r')
        lines = f.readlines()
        texts = []
        labels = []
        for line in lines:
            fields = line.split()
            rate = int(fields[0])
            if rate==0 or rate==3:
                continue
            elif rate < 3:
                rate = 0
            else:
                rate = 1
            cont = fields[1:]
            cont = " ".join(cont)
            texts.append(cont)
            labels.append(rate)

        self.tokenizer.fit_on_texts(texts)
        f.close()
        return texts,labels

    def load_data(self):
        x,y = self.load_cuted_corpus('corpus', 'review.csv')
        x = self.tokenizer.texts_to_sequences(x)
        x = S.pad_sequences(x,maxlen=sentence_max_len)
        y = to_categorical(y,num_classes=2)
        return ((x[0:500000],y[0:500000]), (x[500000:], y[500000:]))

    def train(self,epochs=50):
        print 'building model ...'
        self.model = SentimentLSTM.build_model()

        print 'loading data ...'
        (text_train, rate_train), (text_test, rate_text) = self.load_data()

        print 'training model ...'
        self.model.fit(text_train, rate_train,batch_size=1000,epochs=epochs)
        self.model.save('model/keras.model')
        score = self.model.evaluate(text_test,rate_text)
        print score

    def load_trained_model(self,path):
        model = SentimentLSTM.build_model()
        model.load_weights(path)
        return model

    def predict_text(self,text):
        if self.model == None:
            self.model = self.load_trained_model(model_path)
            self.load_stop_word()
            self.load_cuted_corpus('corpus', 'review.csv')

        vect = self.jieba_cut(text)
        vect = vect.encode('utf-8')
        vect = self.tokenizer.texts_to_sequences([vect,])
        print vect
        return self.model.predict_classes(S.pad_sequences(np.array(vect),100))

    @staticmethod
    def build_model():
        model = Sequential()
        model.add(Embedding(vocab_size, 256, input_length=sentence_max_len))
        model.add(Bidirectional(LSTM(128,implementation=2)))
        model.add(Dropout(0.5))
        model.add(Dense(2, activation='relu'))
        model.compile('RMSprop', 'categorical_crossentropy', metrics=['accuracy'])
        return model

def main():
    lstm = SentimentLSTM()
    lstm.train(10)
    while True:
        input = raw_input('Please input text:')
        if input == 'quit':
            break
        print lstm.predict_text(input)

if __name__=="__main__":
    main()

运行代码,在训练完模型之后,在交互器中输入新的评论,即可以查看训练的模型对评论的预测了.负向输出为0,正向输出为1.

PS:在约60w的数据集上,CPU上跑10轮至少要10个小时.在GeForce GTX 1080上跑需要30分钟. 模型在测试集上的准确度能达到86%,召回率98%,精确度61%,F1评分75%.增大训练的轮数,100轮左右,仍可提升相关得分.

4 学习资料

1 深入浅出Tensorflow(五):循环神经网络简介

2 LSTM与GRU

### 卷积神经网络 (CNN) 与循环神经网络 (RNN) 原理及应用场景比较 #### CNN 的基本原理 卷积神经网络(Convolutional Neural Networks, CNN)是一种专门设计用于处理具有网格状拓扑结构数据的深度学习模型。它的核心在于局部感受野和权值共享机制,这使得它可以高效地提取空间上的特征[^5]。 CNN 主要由卷积层、池化层以及全连接层构成。其中,卷积层负责通过滤波器捕获局部模式;池化层则降低维度并保留重要信息;最后,全连接层完成分类或其他高级任务。 #### RNN 的基本原理 循环神经网络(Recurrent Neural Networks, RNN)专为序列数据分析而生。不同于前馈神经网络仅能一次性处理固定长度的输入,RNN 引入了内部状态来保存历史信息,从而可以动态调整当前输出基于之前的计算结果[^1]。这种特性使其非常适合于时间序列建模或者自然语言处理中的上下文依赖关系捕捉。 #### CNN 和 RNN 的主要差异 尽管两者都属于人工神经网络家族成员之一,并且都可以应用于复杂的实际问题解决当中,但在以下几个方面存在显著的区别: - **数据类型**:CNN 更擅长处理二维或更高维数的空间型数据集比如图片等静态对象;相反的是,RNN更适合用来分析随时间变化的一维线性排列项目如文字串或者是传感器读取记录下来的连续信号流[^4]. - **架构特点**: 如前所述,CNN依靠多级过滤操作逐步提炼高层次抽象概念; 而RNN则是借助反馈回路维持长时间跨度内的关联记忆. - **训练难度**: 因为其特殊的递归性质可能会引发梯度消失/爆炸等问题所以在优化过程中相对更加困难些. #### 应用场景对比 以下是两种网络各自典型的应用实例: - **CNN 应用** - 图像识别: 自动驾驶汽车需要快速准确地标记道路上的各种物体位置尺寸类别等等信息.[^3] - 医疗影像诊断: 辅助医生发现早期癌症病灶区域提高治愈率可能性. - **RNN 应用** - 机器翻译: 将一种语言自动转换成另一种保持原意不变的同时还要注意语法句法正确性.[^2] - 情感分析: 根据评论内容判断消费者对该产品的好恶程度进而帮助企业改进服务质量. ```python import tensorflow as tf from tensorflow.keras.layers import Conv2D, MaxPooling2D, Flatten, Dense, SimpleRNN # Example of a simple CNN model architecture model_cnn = tf.keras.Sequential([ Conv2D(32, kernel_size=(3, 3), activation='relu', input_shape=(28, 28, 1)), MaxPooling2D(pool_size=(2, 2)), Flatten(), Dense(10, activation='softmax') ]) # Example of a basic RNN model structure model_rnn = tf.keras.Sequential([ SimpleRNN(50, input_shape=(None, 1)), Dense(1) ]) ```
评论 9
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值