情感分析是自然语言处理(NLP)中的常见的方法应用,是文本分析的一种。所谓文本分析指的是从文字来源中推导出知识和洞见的过程。而情感分析则进一步从文字中自动识别出人们对特定主题的主观看法、情绪以及态度等等资讯。尽管情绪在很大程度上是主观的,但是情感量化分析已经有很多有用的实践,比如企业分析消费者对产品的反馈信息,或者检测在线评论中的差评信息。
比较简单的情感分析方法利用词语的正负属性来判断句子的情感倾向。句子中的每一个单次都有一个正负得分,乐观的单词为+1,悲观的单词为-1。然后对句子中所有单词的得分进行加总,最后得出句子的情感分值。然而,这种方法存在许多的局限之处,它忽视了句子中上下文的联系。
另一种方法是将文本视为一个“词袋”。将每一个文本看做是一个1 X N维的向量,其中N表示文本词汇的数量。向量中,每一列代表一个单词,其值为该单词在句子中出现的频数。在获得向量后,可以利用机器学习的分类算法,预测句子的情感倾向。虽然这个方法改进了之前的模型,但仍然忽略了上下文关系以及数据量级情况。
今天主要介绍一种新的情感分析方法,利用Doc2vec进行情感分析。Doc2vec是基于Word2vec的基础上发展而来的方法,它可以将一段句子表征为实数值向量。具体可参考
Tomas的Distributed Representations of Sentences and Documents论文。 说到word2vec,
是 Google 在 2013 年年中开源的一款将词表征为实数值向量的高效工具, 其利用深度学习的思想,可以通过训练,把对文本内容的处理简化为 K 维向量空间中的向量运算,而向量空间上的相似度可以用来表示文本语义上的相似度。在获得词向量后, 对词向量进行平均处理,最终获取到句子向量。然后,利用机器学习的分类算法预测句子的情感倾向。这种方法在微博等短文上的应用效果十分不错,这是因为微博通常只有十几个单词,所以即使经过平均化处理仍能保持相关的特性。一旦我们开始分析段落数据时,如果忽略上下文和单词顺序的信息,那么我们将会丢掉许多重要的信息。对于分析长篇评论,更好的方法是采用Doc2vec来创建输入信息。最近,正好接到一个需要对长文本进行情感分析的工作,便利用Doc2vec进行操作。在此,与大家分享一下我是如何利用Doc2vec对长文本进行情感分析的。
1.数据准备
本次使用的数据集包含12000+悲观的社交文本,6000+乐观的社交文本,还有12000+的无标注社交文本,共40000+条数据。
2.依赖包下载
在python中,可以利用gensim库进行Doc2vec模型构建。在进行情感分析前,需要将gensim的依赖包安装好。
3.模型构建
#improt依赖包
from gensim import utils from gensim.models.doc2vec import LabeledSentence from gensim.models import Doc2Vec
from random import shuffle from sklearn.linear_model import LogisticRegression from sklearn.metrics import confusion_matrix import sklearn.metrics as metrics
#Doc2vec需要以LabeledLineSentece对象作为输入,所以需要构建一个类将文本转化为LabeledLineStentece对象。
class LabeledLineSentence(object): def __init__( self , sources): self .sources = sources flipped = {} for key, value in sources.items(): if value not in flipped: flipped[value] = [key] else : raise Exception( 'Non-unique prefix encountered' ) def __iter__( self ): for source, prefix in self .sources.items(): with utils.smart_open(source) as fin: for item_no, line in enumerate(fin): yield LabeledSentence(utils.to_unicode(line).split(), [prefix + '_%s' % item_no]) def to_array( self ): self .sentences = [] for source, prefix in self .sources.items(): with utils.smart_open(source) as fin: for item_no, line in enumerate(fin): self .sentences.append(LabeledSentence(utils.to_unicode(line).split(), [prefix + '_%s' % item_no])) return self .sentences def sentences_perm( self ): shuffle(self .sentences) return self .sentences
#将文本数据以以下方式导入到Doc2vec中
sources = { '/Volumes/Macintosh HD/Users/RayChou/Downloads/情感分析训练语料/neg_train.txt' : 'TRAIN_NEG' , '/Volumes/Macintosh HD/Users/RayChou/Downloads/情感分析训练语料/pos_train.txt' : 'TRAIN_POS' , '/Volumes/Macintosh HD/Users/RayChou/Downloads/情感分析训练语料/uns_train.txt' : 'TRAIN_UNS' , '/Volumes/Macintosh HD/Users/RayChou/Downloads/情感分析训练语料/uns_test.txt' : 'TEST_UNS' }
sentences = LabeledLineSentence(sources)
#构建Doc2vec模型
model = Doc2Vec(min_count= 1 , window= 15 , size= 100 , sample= 1e - 4 , negative= 5 , workers= 8 ) model.build_vocab(sentences.to_array())
#训练Doc2vec模型(本例迭代次数为10,如果时间允许,可以迭代更多的次数)
for epoch in range( 10 ): model.train(sentences.sentences_perm())
#将训练好的句子向量装进array里面,后文作为分类器的输入
train_arrays = numpy.zeros(( 18293 , 100 )) train_labels = numpy.zeros(18293 ) test_arrays = [] true_labels=[] train_data=[] train_lb=[] for i in range( 18293 ): if (i<= 12988 ): prefix_train_neg = 'TRAIN_NEG_' + str(i) train_arrays[i] = model.docvecs[prefix_train_neg] train_labels[i] = 0 if (i> 12988 and i<= 18292 ): j=i-12989 prefix_train_pos = 'TRAIN_POS_' + str(j) train_arrays[i]=model.docvecs[prefix_train_pos] train_labels[i]=1
#载入测试集数据
a=open( "/Volumes/Macintosh HD/Users/RayChou/Downloads/情感分析训练语料/pos_test.txt" ) b=open("/Volumes/Macintosh HD/Users/RayChou/Downloads/情感分析训练语料/neg_test.txt" ) test_content1=a.readlines() test_content2=b.readlines() for i in test_content1: test_arrays.append(model.infer_vector(i)) true_labels.append(1 ) for i in test_content2: test_arrays.append(model.infer_vector(i)) true_labels.append(0 )
#构建逻辑回归分类器
classifier = LogisticRegression(class_weight={ 0 : 0.38 , 1 : 0.62 }) classifier.fit(train_arrays, train_labels)
#构建随机森林分类器
from sklearn.ensemble import RandomForestClassifier RF = RandomForestClassifier(n_estimators=1200 ,max_depth= 14 ,class_weight={ 0 : 0.3 , 1 : 0.7 }) RF.fit(train_arrays, train_labels)
#构建GBDT分类器
from sklearn.ensemble import GradientBoostingClassifier GBDT = GradientBoostingClassifier(n_estimators=1000 ,max_depth= 14 ) GBDT.fit(train_arrays, train_labels)
#对Test数据进行预测
test_labels_LR=[] test_labels_RF=[] test_labels_GBDT=[] for i in range(len(test_arrays)): test_labels_LR.append(classifier.predict(test_arrays[i])) test_labels_RF.append(RF.predict(test_arrays[i])) test_labels_GBDT.append(GBDT.predict(test_arrays[i]))
#打印各个模型的准确率和召回率
print ( "LR:" ) print (classifier.score(test_labels_LR, true_labels)) print (confusion_matrix(test_labels_LR,true_labels)) print ( "RF:" ) print (metrics.accuracy_score(test_labels_RF,true_labels)) print (confusion_matrix(test_labels_RF,true_labels)) print ( "GBDT:" ) print (metrics.accuracy_score(test_labels_GBDT,true_labels)) print (confusion_matrix(test_labels_GBDT,true_labels))
总结
以上对用Doc2vec进行情感分析做了一次有效的尝试。在工作过程中发现,不同的分类器对模型的精度有不同的表现,事实上现时各个分类器算法都有相应封装包,而且效率也非常高,所以在分类器的选择上并不能体现优势,可能几个不同模型之间的准确率也就相差几个百分点。相反,有效的语料库和干净的文本数据是情感分析模型的保证,所以在进行情感分析之前,必须要预先处理好文本数据,方能得到有效的模型。