Python做文本挖掘的情感极性分析

来源:互联网 发布:淘宝店铺如何激活手机 编辑:程序博客网 时间:2024/05/17 00:13

Python做文本挖掘的情感极性分析

数据挖掘入门与实战2017-03-23 21:25:41line阅读(27)评论(0)
声明:本文由入驻搜狐公众平台的作者撰写,除搜狐官方账号外,观点仅代表作者本人,不代表搜狐立场。举报

  数据挖掘入门与实战 公众号: datadw

  「情感极性分析」是对带有感情色彩的主观性文本进行分析、处理、归纳和推理的过程。按照处理文本的类别不同,可分为基于新闻评论的情感分析和基于产品评论的情感分析。其中,前者多用于舆情监控和信息预测,后者可帮助用户了解某一产品在大众心目中的口碑。

  目前常见的情感极性分析方法主要是两种:基于情感词典的方法和基于机器学习的方法。

  1. 基于情感词典的文本情感极性分析

  笔者是通过情感打分的方式进行文本情感极性判断,score > 0判断为正向,score < 0判断为负向。

  1.1 数据准备 1.1.1 情感词典及对应分数

  词典来源于BosonNLP数据下载

  http://bosonnlp.com/dev/resource

  的情感词典,来源于社交媒体文本,所以词典适用于处理社交媒体的情感分析。

  词典把所有常用词都打上了唯一分数有许多不足之处。

  • 之一,不带情感色彩的停用词会影响文本情感打分。在

  • 之二,由于中文的博大精深,词性的多变成为了影响模型准确度的重要原因。

      一种情况是同一个词在不同的语境下可以是代表完全相反的情感意义,用笔者模型预测偏差最大的句子为例(来源于朋友圈文本):

      有车一族都用了这个宝贝,后果很严重哦[偷笑][偷笑][偷笑]1,交警工资估计会打5折,没有超速罚款了[呲牙][呲牙][呲牙]2,移动联通公司大幅度裁员,电话费少了[呲牙][呲牙][呲牙]3,中石化中石油裁员2成,路痴不再迷路,省油[悠闲][悠闲][悠闲]5,保险公司裁员2成,保费折上折2成,全国通用[憨笑][憨笑][憨笑]买不买你自己看着办吧[调皮][调皮][调皮]

      里面严重等词都是表达的相反意思,甚至整句话一起表示相反意思,不知死活的笔者还没能深入研究如何用词典的方法解决这类问题,但也许可以用机器学习的方法让神经网络进行学习能够初步解决这一问题。

      另外,同一个词可作多种词性,那么情感分数也不应相同,例如:

      这部电影真垃圾

      垃圾分类

      很明显在第一句中垃圾表现强烈的贬义,而在第二句中表示中性,单一评分对于这类问题的分类难免有失偏颇。

1.1.2 否定词词典

  否定词的出现将直接将句子情感转向相反的方向,而且通常效用是叠加的。常见的否定词:不、没、无、非、莫、弗、勿、毋、未、否、别、無、休、难道等。

  1.1.3 程度副词词典

  既是通过打分的方式判断文本的情感正负,那么分数绝对值的大小则通常表示情感强弱。既涉及到程度强弱的问题,那么程度副词的引入就是势在必行的。词典可从《知网》情感分析用词语集(beta版)

  http://www.keenage.com/download/sentiment.rar

  下载。词典内数据格式可参考如下格式,即共两列,第一列为程度副词,第二列是程度数值,> 1表示强化情感,< 1表示弱化情感。

  

  程度副词词典

  1.1.4 停用词词典

  中科院计算所中文自然语言处理开放平台发布了有1208个停用词的中文停用词表,

  http://www.datatang.com/data/43894

  也有其他不需要积分的下载途径。

  http://www.hicode.cc/download/view-software-13784.html

  1.2 数据预处理 1.2.1 分词

  即将句子拆分为词语集合,结果如下:

  e.g. 这样/的/酒店/配/这样/的/价格/还算/不错

  Python常用的分词工具:

  • 结巴分词 Jieba

  • Pymmseg-cpp

  • Loso

  • smallseg

fromcollections importdefaultdict importos importre importjieba importcodecs """1. 文本切割"""defsent2word(sentence): """ Segment a sentence to words Delete stopwords """segList = jieba.cut(sentence) segResult = [] forinsegList: segResult.append(w) stopwords = readLines('stop_words.txt') newSent = [] forword insegResult: ifword instopwords: # print "stopword: %s" % word continue else: newSent.append(word) returnnewSent

  在此笔者使用Jieba进行分词。

  1.2.2 去除停用词

  遍历所有语料中的所有词语,删除其中的停用词

  e.g. 这样/的/酒店/配/这样/的/价格/还算/不错

  --> 酒店/配/价格/还算/不错

  1.3 构建模型 1.3.1 将词语分类并记录其位置

  将句子中各类词分别存储并标注位置。

  """2. 情感定位"""defclassifyWords(wordDict):# (1) 情感词senList = readLines( 'BosonNLP_sentiment_score.txt') senDict = defaultdict() forinsenList: senDict[s.split( ' ')[ 0]] = s.split( ' ')[ 1# (2) 否定词notList = readLines( 'notDict.txt'# (3) 程度副词degreeList = readLines( 'degreeDict.txt') degreeDict = defaultdict() forindegreeList: degreeDict[d.split( ',')[ 0]] = d.split( ',')[ 1] senWord = defaultdict() notWord = defaultdict() degreeWord = defaultdict() forword inwordDict.keys():

  ifword insenDict.keys() andword notinnotList andword notindegreeDict.keys(): senWord[wordDict[word]] = senDict[word]

  elifword innotList andword notindegreeDict.keys(): notWord[wordDict[word]] = -1elifword indegreeDict.keys(): degreeWord[wordDict[word]] = degreeDict[word]

  returnsenWord, notWord, degreeWord 1.3.2 计算句子得分

  在此,简化的情感分数计算逻辑:所有情感词语组的分数之和

  定义一个情感词语组:两情感词之间的所有否定词和程度副词与这两情感词中的后一情感词构成一个情感词组,即notWords + degreeWords + sentiWords,例如不是很交好,其中不是为否定词,很为程度副词,交好为情感词,那么这个情感词语组的分数为:

  finalSentiScore = (-1) ^ 1 * 1.25 * 0.747127733968

  其中1指的是一个否定词,1.25是程度副词的数值,0.747127733968为交好的情感分数。

  伪代码如下:

  finalSentiScore = (-1) ^ (num of notWords) * degreeNum * sentiScore

  finalScore = sum(finalSentiScore)

  """3. 情感聚合"""defscoreSent(senWord, notWord, degreeWord, segResult):W = 1score = 0# 存所有情感词的位置的列表senLoc = senWord.keys() notLoc = notWord.keys() degreeLoc = degreeWord.keys() senloc = -1# notloc = -1# degreeloc = -1# 遍历句中所有单词segResult,i为单词绝对位置forinrange( 0, len(segResult)): # 如果该词为情感词ifinsenLoc: # loc为情感词位置列表的序号senloc += 1# 直接添加该情感词分数score += W * float(senWord[i])

  # print "score = %f" % scoreifsenloc < len(senLoc) - 1:

  # 判断该情感词与下一情感词之间是否有否定词或程度副词# j为绝对位置forinrange(senLoc[senloc], senLoc[senloc + 1]): # 如果有否定词ifinnotLoc: W *= -1# 如果有程度副词elifindegreeLoc: W *= float(degreeWord[j])

  # i定位至下一个情感词ifsenloc < len(senLoc) - 1: i = senLoc[senloc + 1returnscore 1.4 模型评价

  将600多条朋友圈文本的得分排序后做出散点图:

  

  Score Distribution

  其中大多数文本被判为正向文本符合实际情况,且绝大多数文本的情感得分的绝对值在10以内,这是因为笔者在计算一个文本的情感得分时,以句号作为一句话结束的标志,在一句话内,情感词语组的分数累加,如若一个文本中含有多句话时,则取其所有句子情感得分的平均值

  然而,这个模型的缺点与局限性也非常明显:

  • 首先,段落的得分是其所有句子得分的平均值,这一方法并不符合实际情况。正如文章中先后段落有重要性大小之分,一个段落中前后句子也同样有重要性的差异。

  • 其次,有一类文本使用贬义词来表示正向意义,这类情况常出现与宣传文本中,还是那个例子:

      有车一族都用了这个宝贝,后果很严重哦[偷笑][偷笑][偷笑]1,交警工资估计会打5折,没有超速罚款了[呲牙][呲牙][呲牙]2,移动联通公司大幅度裁员,电话费少了[呲牙][呲牙][呲牙]3,中石化中石油裁员2成,路痴不再迷路,省油[悠闲][悠闲][悠闲]5,保险公司裁员2成,保费折上折2成,全国通用[憨笑][憨笑][憨笑]买不买你自己看着办吧[调皮][调皮][调皮]2980元轩辕魔镜带回家,推广还有返利[得意]

      Score Distribution中得分小于-10的几个文本都是与这类情况相似,这也许需要深度学习的方法才能有效解决这类问题,普通机器学习方法也是很难的。

  • 对于正负向文本的判断,该算法忽略了很多其他的否定词、程度副词和情感词搭配的情况;用于判断情感强弱也过于简单。

  总之,这一模型只能用做BENCHMARK...

2. 基于机器学习的文本情感极性分析 2.1 还是数据准备 2.1.1 停用词

  (同1.1.4)

  2.1.2 正负向语料库

  来源于有关中文情感挖掘的酒店评论语料,

  http://www.datatang.com/data/11936

  其中正向7000条,负向3000条(笔者是不是可以认为这个世界还是充满着满满的善意呢…),当然也可以参考情感分析资源使用其他语料作为训练集。

  2.1.3 验证集

  Amazon上对iPhone 6s的评论,来源已不可考……

  2.2 数据预处理 2.2.1 还是要分词

  (同1.2.1)

  importnumpy asnp importsys importre importcodecs importos importjiebafrom gensim.models importword2vecfrom sklearn.cross_validation importtrain_test_splitfrom sklearn.externals importjoblibfrom sklearn.preprocessing importscalefrom sklearn.svm importSVCfrom sklearn.decomposition importPCAfrom scipy importstatsfrom keras.models importSequentialfrom keras.layers importDense, Dropout, Activationfrom keras.optimizers importSGDfrom sklearn.metrics importf1_scorefrom bayes_opt importBayesianOptimization asBOfrom sklearn.metrics importroc_curve, auc importmatplotlib.pyplot aspltdef parseSent(sentence): seg_list = jieba.cut(sentence) output = ''.join(list(seg_list)) # use space to join them return output 2.2.2 也要去除停用词

  (同1.2.2)

  2.2.3 训练词向量

  (重点来了!)模型的输入需是数据元组,那么就需要将每条数据的词语组合转化为一个数值向量

  常见的转化算法有但不仅限于如下几种:

  (请原谅不知死活的胖子直接用展示的ppt截图作说明,没错,我就是懒,你打我呀)

  • Bag of Words

      

      Bag of Words

  • TF-IDF

      

      TF-IDF

  • Word2Vec

      

      Word2Vec

  在此笔者选用Word2Vec将语料转化成向量

  def getWordVecs(wordList): vecs = [] forwordinwordList:

  wordwordreplace'n'''try: vecs.append(model[ word]) except KeyError: continue # vecs = np.concatenate(vecs)returnnp.array(vecs, dtype = 'float')def buildVecs(filename): posInput = [] withopen(filename, "rb"astxtfile:

  # print txtfileforlinesintxtfile: lineslinessplit'n ')

  forlineinlinesline= jieba.cut( line) resultList = getWordVecs( line)

  # for each sentence, the mean vector of all its vectors is used to represent this sentenceiflen(resultList) != 0: resultArray = sum(np.array(resultList))/ len(resultList) posInput.append(resultArray) returnposInput # load word2vec modelmodel = word2vec.Word2Vec.load_word2vec_format( "corpus.model.bin", binary = True) # txtfile = [u'标准间太差房间还不如3星的而且设施非常陈旧.建议酒店把老的标准间从新改善.', u'在这个西部小城市能住上这样的酒店让我很欣喜,提供的免费接机服务方便了我的出行,地处市中心,购物很方便。早餐比较丰富,服务人员很热情。推荐大家也来试试,我想下次来这里我仍然会住这里']posInput = buildVecs( 'pos.txt')negInput = buildVecs( 'pos.txt'# use 1 for positive sentiment, 0 for negativey = np.concatenate((np.ones( len(posInput)), np.zeros( len(negInput))))X = posInput[:] forneg innegInput: X.append(neg)X = np.array(X) 2.2.4 标准化

  虽然笔者觉得在这一问题中,标准化对模型的准确率影响不大,当然也可以尝试其他的标准化的方法。

  # standardizationX= scale(X) 2.2.5 降维

  根据PCA结果,发现前100维能够cover 95%以上的variance。

  

  PCA

  # PCA# Plot the PCA spectrumpca.fit(X)plt.figure( 1, figsize=( 43))plt.clf()plt.axes([ .2.2.7.7])plt.plot(pca.explained_variance_, linewidth= 2)plt.axis('tight')plt.xlabel('n_components')plt.ylabel('explained_variance_')X_reduced = PCA(n_components = 100).fit_transform(X) 2.3 构建模型 2.3.1 SVM (RBF) + PCA

  SVM (RBF)分类表现更为宽松,且使用PCA降维后的模型表现有明显提升,misclassified多为负向文本被分类为正向文本,其中AUC = 0.92,KSValue = 0.7。

  """2.1 SVM (RBF) using training data with 100 dimensions"""clf = SVC(C = 2, probability = True)clf.fit(X_reduced_train, y_reduced_train) print'Test Accuracy: %.2f'% clf.score(X_reduced_test, y_reduced_test)pred_probas = clf.predict_proba(X_reduced_test)[:, 1print"KS value: %f"% KSmetric(y_reduced_test, pred_probas)[ 0# plot ROC curve# AUC = 0.92# KS = 0.7fpr,tpr,_ = roc_curve(y_reduced_test, pred_probas)roc_auc = auc(fpr,tpr)plt.plot(fpr, tpr, label = 'area = %.2f'% roc_auc)plt.plot([ 01], [ 01], 'k--')plt.xlim([ 0.01.0])plt.ylim([ 0.01.05])plt.legend(loc = 'lower right')plt.show()joblib.dump(clf, "SVC.pkl") 2.3.2 MLP

  MLP相比于SVM (RBF),分类更为严格,PCA降维后对模型准确率影响不大,misclassified多为正向文本被分类为负向,其实是更容易overfitting,原因是语料过少,其实用神经网络未免有些小题大做,AUC = 0.91。

  """2.2MLP using original training data with 400dimensions """model = Sequential()model. add(Dense( 512, input_dim = 400, init = 'uniform', activation = 'tanh'))model. add(Dropout( 0.5))model. add(Dense( 256, activation = 'relu'))model. add(Dropout( 0.5))model. add(Dense( 128, activation = 'relu'))model. add(Dropout( 0.5))model. add(Dense( 64, activation = 'relu'))model. add(Dropout( 0.5))model. add(Dense( 32, activation = 'relu'))model. add(Dropout( 0.5))model. add(Dense( 1, activation = 'sigmoid'))model.compile(loss = 'binary_crossentropy', optimizer = 'adam', metrics = [ 'accuracy'])model.fit(X_train, y_train, nb_epoch = 20, batch_size = 16)score = model.evaluate(X_test, y_test, batch_size = 16print'Test accuracy: ',

  score[ 1])pred_probas = model.predict(X_test)# print"KS value: %f"% KSmetric(y_reduced_test, pred_probas)[ 0]# plot ROC curve# AUC = 0.91fpr,tpr,_ = roc_curve(y_test, pred_probas)roc_auc = auc(fpr,tpr)plt.plot(fpr, tpr, label = 'area = %.2f'% roc_auc)plt.plot([ 01], [ 01], 'k--')plt.xlim([ 0.01.0])plt.ylim([ 0.01.05])plt.legend( loc'lower right')plt.show() 2.4 模型评价

  • 实际上,第一种方法中的第二点缺点依然存在,但相比于基于词典的情感分析方法,基于机器学习的方法更为客观

  • 另外由于训练集和测试集分别来自不同领域,所以有理由认为训练集不够充分,未来可以考虑扩充训练集以提升准确率。

  via http://www.jianshu.com/p/4cfcf1610a

0 0
原创粉丝点击