Keras RNN 与 情感分类(代码)

xiaoxiao2021-02-28  113

原文:http://blog.csdn.net/zzulp/article/details/76589044

 

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模块导出类

名称作用原型参数TimeDistributed TimeDistributed(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

 

语料下载,和采集到的评论数据,密码:dva4

 

搭建LSTM做文本情感分类的代码2:

 

import pandas as pd #导入Pandas import numpy as np #导入Numpy import jieba #导入结巴分词 from keras.preprocessing import sequence from keras.optimizers import SGD, RMSprop, Adagrad from keras.utils import np_utils from keras.models import Sequential from keras.layers.core import Dense, Dropout, Activation from keras.layers.embeddings import Embedding from keras.layers.recurrent import LSTM, GRU from __future__ import absolute_import #导入3.x的特征函数 from __future__ import print_function neg=pd.read_excel('neg.xls',header=None,index=None) pos=pd.read_excel('pos.xls',header=None,index=None) #读取训练语料完毕 pos['mark']=1 neg['mark']=0 #给训练语料贴上标签 pn=pd.concat([pos,neg],ignore_index=True) #合并语料 neglen=len(neg) poslen=len(pos) #计算语料数目 cw = lambda x: list(jieba.cut(x)) #定义分词函数 pn['words'] = pn[0].apply(cw) comment = pd.read_excel('sum.xls') #读入评论内容 #comment = pd.read_csv('a.csv', encoding='utf-8') comment = comment[comment['rateContent'].notnull()] #仅读取非空评论 comment['words'] = comment['rateContent'].apply(cw) #评论分词 d2v_train = pd.concat([pn['words'], comment['words']], ignore_index = True) w = [] #将所有词语整合在一起 for i in d2v_train: w.extend(i) dict = pd.DataFrame(pd.Series(w).value_counts()) #统计词的出现次数 del w,d2v_train dict['id']=list(range(1,len(dict)+1)) get_sent = lambda x: list(dict['id'][x]) pn['sent'] = pn['words'].apply(get_sent) #速度太慢 maxlen = 50 print("Pad sequences (samples x time)") pn['sent'] = list(sequence.pad_sequences(pn['sent'], maxlen=maxlen)) x = np.array(list(pn['sent']))[::2] #训练集 y = np.array(list(pn['mark']))[::2] xt = np.array(list(pn['sent']))[1::2] #测试集 yt = np.array(list(pn['mark']))[1::2] xa = np.array(list(pn['sent'])) #全集 ya = np.array(list(pn['mark'])) print('Build model...') model = Sequential() model.add(Embedding(len(dict)+1, 256)) model.add(LSTM(256, 128)) # try using a GRU instead, for fun model.add(Dropout(0.5)) model.add(Dense(128, 1)) model.add(Activation('sigmoid')) model.compile(loss='binary_crossentropy', optimizer='adam', class_mode="binary") model.fit(xa, ya, batch_size=16, nb_epoch=10) #训练时间为若干个小时 classes = model.predict_classes(xa) acc = np_utils.accuracy(classes, ya) print('Test accuracy:', acc)

 

 

 

 

转载请注明原文地址: https://www.6miu.com/read-48935.html

最新回复(0)