Keras + LSTM + 词向量 情感分类情感分析实验

    xiaoxiao2021-03-25  104

    背景简介

    本人是深度学习入门的菜菜菜鸟一枚… 利用LSTM + word2vec词向量进行文本情感分类/情感分析实验,吸收了网上的资源和代码并尝试转化为自己的东西~

    实验环境

    win7 64位系统Anaconda 4.3.0 , Python 2.7 versionPycharm开发环境python包:keras,gensim,numpy等

    实验数据

    本文的实验数据是来自网上的中文标注语料,涉及书籍、酒店、计算机、牛奶、手机、热水器六个方面的购物评论数据,具体介绍参见该文:购物评论情感分析。

    数据处理

    上面提到的数据在网上见到的次数比较多,原始格式是两个excel文件,如图:

    对,就是这两个…估计来到本文的小伙伴也见过。一些代码就是直接从这两个excel里读取数据、分词、处理…不过我表示自己习惯从txt文本里获取数据,因此本人将数据合并、去重(原数据里有不少重复的评论)、分词(用的是哈工大LTP分词)之后存为一份txt文本,保留的数据情况如下:

    正面评价个数:8680个 负面评价个数:8000个

    文本如图所示:

    然后人工生成一份【语料类别】文本,用1表示正面评价,用0表示负面评价,与评论数据一一对应。

    生成词语的索引字典、词向量字典

    利用上述文本语料生成词语的索引字典和词向量字典。 注意:当Word2vec词频阈值设置为5时,词频小于5的词语将不会生成索引,也不会生成词向量数据。

    工具:gensim里的Word2vec,Dictionary

    代码

    #!/usr/bin/env python # -*- coding: utf-8 -*- """ 功能:利用大语料生成词语的索引字典、词向量,然后保存为pkl文件 时间:2017年3月8日 13:19:40 """ import pickle import logging import tkFileDialog import numpy as np np.random.seed(1337) # For Reproducibility from Functions.TextSta import TextSta from gensim.models.word2vec import Word2Vec from gensim.corpora.dictionary import Dictionary # 创建词语字典,并返回word2vec模型中词语的索引,词向量 def create_dictionaries(p_model): gensim_dict = Dictionary() gensim_dict.doc2bow(p_model.vocab.keys(), allow_update=True) w2indx = {v: k + 1 for k, v in gensim_dict.items()} # 词语的索引,从1开始编号 w2vec = {word: model[word] for word in w2indx.keys()} # 词语的词向量 return w2indx, w2vec # 主程序 logging.basicConfig(format='%(asctime)s : %(levelname)s : %(message)s', level=logging.INFO) print u"请选择大语料的分词文本..." T = TextSta(tkFileDialog.askopenfilename(title=u"选择文件")) sentences = T.sen() # 获取句子列表,每个句子又是词汇的列表 print u'训练Word2vec模型(可尝试修改参数)...' model = Word2Vec(sentences, size=100, # 词向量维度 min_count=5, # 词频阈值 window=5) # 窗口大小 model_name = raw_input(u"请输入保存的模型文件名...\n").decode("utf-8") model.save(model_name + u'.model') # 保存模型 # 索引字典、词向量字典 index_dict, word_vectors= create_dictionaries(model) # 存储为pkl文件 pkl_name = raw_input(u"请输入保存的pkl文件名...\n").decode("utf-8") output = open(pkl_name + u".pkl", 'wb') pickle.dump(index_dict, output) # 索引字典 pickle.dump(word_vectors, output) # 词向量字典 output.close() if __name__ == "__main__": pass

    其中,

    T = TextSta(tkFileDialog.askopenfilename(title=u"选择文件")) sentences = T.sen() # 获取句子列表,每个句子又是词汇的列表

    TextSta是我自己写的一个类,读取语料文本后,sentences = T.sen()将文本里的每一行生成一个列表,每个列表又是词汇的列表。(这个类原来是用作句子分类的,每行是一个句子;这里每行其实是一个评论若干个句子…我就不改代码变量名了…)

    TextSta类部分代码:

    #!/usr/bin/env python # -*- coding: utf-8 -*- """ 功能:一个类,执行文本转换 输入:分词文本 输出:句子列表,全文的词汇列表,TF,DF 时间:2016年5月17日 19:08:34 """ import codecs import re import tkFileDialog class TextSta: # 定义基本属性,分词文本的全路径 filename = "" # 定义构造方法 def __init__(self, path): # 参数path,赋给filename self.filename = path def sen(self): # 获取句子列表 f1 = codecs.open(self.filename, "r", encoding="utf-8") print u"已经打开文本:", self.filename # 获得句子列表,其中每个句子又是词汇的列表 sentences_list = [] for line in f1: single_sen_list = line.strip().split(" ") while "" in single_sen_list: single_sen_list.remove("") sentences_list.append(single_sen_list) print u"句子总数:", len(sentences_list) f1.close() return sentences_list if __name__ == "__main__": pass

    总之,sentences的格式如下:

    [[我, 是, 2月, …], [#, 蒙牛, 百, …], …]

    所有的评论文本存为一个列表,每个评论文本又是词汇的列表。 sentences列表的长度就是文本的行数:len(sentences) = 16680

    利用Keras + LSTM进行文本分类

    工具:Keras深度学习库

    代码:

    #!/usr/bin/env python # -*- coding: utf-8 -*- """ 功能:利用词向量+LSTM进行文本分类 时间:2017年3月10日 21:18:34 """ import numpy as np np.random.seed(1337) # For Reproducibility import pickle from keras.preprocessing import sequence from keras.models import Sequential from keras.layers.embeddings import Embedding from keras.layers.recurrent import LSTM from keras.layers.core import Dense, Dropout, Activation from sklearn.cross_validation import train_test_split from Functions import GetLineList from Functions.TextSta import TextSta # 参数设置 vocab_dim = 100 # 向量维度 maxlen = 140 # 文本保留的最大长度 batch_size = 32 n_epoch = 5 input_length = 140 def text_to_index_array(p_new_dic, p_sen): # 文本转为索引数字模式 new_sentences = [] for sen in p_sen: new_sen = [] for word in sen: try: new_sen.append(p_new_dic[word]) # 单词转索引数字 except: new_sen.append(0) # 索引字典里没有的词转为数字0 new_sentences.append(new_sen) return np.array(new_sentences) # 定义网络结构 def train_lstm(p_n_symbols, p_embedding_weights, p_X_train, p_y_train, p_X_test, p_y_test): print u'创建模型...' model = Sequential() model.add(Embedding(output_dim=vocab_dim, input_dim=p_n_symbols, mask_zero=True, weights=[p_embedding_weights], input_length=input_length)) model.add(LSTM(output_dim=50, activation='sigmoid', inner_activation='hard_sigmoid')) model.add(Dropout(0.5)) model.add(Dense(1)) model.add(Activation('sigmoid')) print u'编译模型...' model.compile(loss='binary_crossentropy', optimizer='adam', metrics=['accuracy']) print u"训练..." model.fit(p_X_train, p_y_train, batch_size=batch_size, nb_epoch=n_epoch, validation_data=(p_X_test, p_y_test)) print u"评估..." score, acc = model.evaluate(p_X_test, p_y_test, batch_size=batch_size) print 'Test score:', score print 'Test accuracy:', acc # 读取大语料文本 f = open(u"评价语料索引及词向量.pkl", 'rb') # 预先训练好的 index_dict = pickle.load(f) # 索引字典,{单词: 索引数字} word_vectors = pickle.load(f) # 词向量, {单词: 词向量(100维长的数组)} new_dic = index_dict print u"Setting up Arrays for Keras Embedding Layer..." n_symbols = len(index_dict) + 1 # 索引数字的个数,因为有的词语索引为0,所以+1 embedding_weights = np.zeros((n_symbols, 100)) # 创建一个n_symbols * 100的0矩阵 for w, index in index_dict.items(): # 从索引为1的词语开始,用词向量填充矩阵 embedding_weights[index, :] = word_vectors[w] # 词向量矩阵,第一行是0向量(没有索引为0的词语,未被填充) # 读取语料分词文本,转为句子列表(句子为词汇的列表) print u"请选择语料的分词文本..." T1 = TextSta(u"评价语料_分词后.txt") allsentences = T1.sen() # 读取语料类别标签 print u"请选择语料的类别文本...(用0,1分别表示消极、积极情感)" labels = GetLineList.main() # 划分训练集和测试集,此时都是list列表 X_train_l, X_test_l, y_train_l, y_test_l = train_test_split(allsentences, labels, test_size=0.2) # 转为数字索引形式 X_train = text_to_index_array(new_dic, X_train_l) X_test = text_to_index_array(new_dic, X_test_l) print u"训练集shape: ", X_train.shape print u"测试集shape: ", X_test.shape y_train = np.array(y_train_l) # 转numpy数组 y_test = np.array(y_test_l) # 将句子截取相同的长度maxlen,不够的补0 print('Pad sequences (samples x time)') X_train = sequence.pad_sequences(X_train, maxlen=maxlen) X_test = sequence.pad_sequences(X_test, maxlen=maxlen) print('X_train shape:', X_train.shape) print('X_test shape:', X_test.shape) train_lstm(n_symbols, embedding_weights, X_train, y_train, X_test, y_test) if __name__ == "__main__": pass

    其中,

    from Functions import GetLineList

    GetLineList是自定义模块,用于获取文本的类别(存为列表),代码如下:

    #!/usr/bin/env python # -*- coding: utf-8 -*- """ 功能:文本转列表,常用于读取词典(停用词,特征词等) 使用:给定一个文本,将文本按行转换为列表,每行对应列表里的一个元素 时间:2016年5月15日 22:45:23 """ import codecs import tkFileDialog def main(): # 打开文件 file_path = tkFileDialog.askopenfilename(title=u"选择文件") f1 = codecs.open(file_path, "r", encoding="utf-8") print u"已经打开文本:", file_path # 转为列表 line_list = [] for line in f1: line_list.append(line.strip()) print u"列表里的元素个数:", len(line_list) f1.close() return line_list if __name__ == "__main__": pass

    实验结果

    参考文献:

    http://buptldy.github.io/2016/07/20/2016-07-20-sentiment analysis/ https://github.com/BUPTLdy/Sentiment-Analysis

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

    最新回复(0)