美文网首页
使用python+机器学习方法进行情感分析(下)

使用python+机器学习方法进行情感分析(下)

作者: 赖丽春 | 来源:发表于2017-08-31 18:00 被阅读339次

首先是特征提取和选择

一、特征提取方法

1.把所有词作为特征

defbag_of_words(words):

returndict([(word,True)forwordinwords])

返回的是字典类型,这是nltk处理情感分类的一个标准形式。

2.把双词搭配(bigrams)作为特征

importnltk

fromnltk.collocationsimportBigramCollocationFinder

fromnltk.metricsimportBigramAssocMeasures

defbigram(words,score_fn=BigramAssocMeasures.chi_sq,n=1000):

bigram_finder=BigramCollocationFinder.from_words(words)#把文本变成双词搭配的形式

bigrams=bigram_finder.nbest(score_fn,n)#使用了卡方统计的方法,选择排名前1000的双词

returnbag_of_words(bigrams)

除了可以使用卡方统计来选择信息量丰富的双词搭配,还可以使用其它的方法,比如互信息(PMI)。而排名前1000也只是人工选择的阈值,可以随意选择其它值,可经过测试一步步找到最优值。

3.把所有词和双词搭配一起作为特征

defbigram_words(words,score_fn=BigramAssocMeasures.chi_sq,n=1000):

bigram_finder=BigramCollocationFinder.from_words(words)

bigrams=bigram_finder.nbest(score_fn,n)

returnbag_of_words(words+bigrams)#所有词和(信息量大的)双词搭配一起作为特征

二、特征选择方法

有了提取特征的方法后,我们就可以提取特征来进行分类学习了。但一般来说,太多的特征会降低分类的准确度,所以需要使用一定的方法,来“选择”出信息量最丰富的特征,再使用这些特征来分类。

特征选择遵循如下步骤:

1.计算出整个语料里面每个词的信息量

2.根据信息量进行倒序排序,选择排名靠前的信息量的词

3.把这些词作为特征

1.计算出整个语料里面每个词的信息量

1.1计算整个语料里面每个词的信息量

fromnltk.probabilityimportFreqDist,ConditionalFreqDist

defcreate_word_scores():

posWords=pickle.load(open('D:/code/sentiment_test/pos_review.pkl','r'))

negWords=pickle.load(open('D:/code/sentiment_test/neg_review.pkl','r'))

posWords=list(itertools.chain(*posWords))#把多维数组解链成一维数组

negWords=list(itertools.chain(*negWords))#同理

word_fd=FreqDist()#可统计所有词的词频

cond_word_fd=ConditionalFreqDist()#可统计积极文本中的词频和消极文本中的词频

forwordinposWords:

word_fd.inc(word)

cond_word_fd['pos'].inc(word)

forwordinnegWords:

word_fd.inc(word)

cond_word_fd['neg'].inc(word)

pos_word_count=cond_word_fd['pos'].N()#积极词的数量

neg_word_count=cond_word_fd['neg'].N()#消极词的数量

total_word_count=pos_word_count+neg_word_count

word_scores={}

forword,freqinword_fd.iteritems():

pos_score=BigramAssocMeasures.chi_sq(cond_word_fd['pos'][word],(freq,pos_word_count),total_word_count)#计算积极词的卡方统计量,这里也可以计算互信息等其它统计量

neg_score=BigramAssocMeasures.chi_sq(cond_word_fd['neg'][word],(freq,neg_word_count),total_word_count)#同理

word_scores[word]=pos_score+neg_score#一个词的信息量等于积极卡方统计量加上消极卡方统计量

returnword_scores#包括了每个词和这个词的信息量

1.2计算整个语料里面每个词和双词搭配的信息量

defcreate_word_bigram_scores():

posdata=pickle.load(open('D:/code/sentiment_test/pos_review.pkl','r'))

negdata=pickle.load(open('D:/code/sentiment_test/neg_review.pkl','r'))

posWords=list(itertools.chain(*posdata))

negWords=list(itertools.chain(*negdata))

bigram_finder=BigramCollocationFinder.from_words(posWords)

bigram_finder=BigramCollocationFinder.from_words(negWords)

posBigrams=bigram_finder.nbest(BigramAssocMeasures.chi_sq,5000)

negBigrams=bigram_finder.nbest(BigramAssocMeasures.chi_sq,5000)

pos=posWords+posBigrams#词和双词搭配

neg=negWords+negBigrams

word_fd=FreqDist()

cond_word_fd=ConditionalFreqDist()

forwordinpos:

word_fd.inc(word)

cond_word_fd['pos'].inc(word)

forwordinneg:

word_fd.inc(word)

cond_word_fd['neg'].inc(word)

pos_word_count=cond_word_fd['pos'].N()

neg_word_count=cond_word_fd['neg'].N()

total_word_count=pos_word_count+neg_word_count

word_scores={}

forword,freqinword_fd.iteritems():

pos_score=BigramAssocMeasures.chi_sq(cond_word_fd['pos'][word],(freq,pos_word_count),total_word_count)

neg_score=BigramAssocMeasures.chi_sq(cond_word_fd['neg'][word],(freq,neg_word_count),total_word_count)

word_scores[word]=pos_score+neg_score

returnword_scores

2.根据信息量进行倒序排序,选择排名靠前的信息量的词

deffind_best_words(word_scores,number):

best_vals=sorted(word_scores.iteritems(),key=lambda(w,s):s,reverse=True)[:number]#把词按信息量倒序排序。number是特征的维度,是可以不断调整直至最优的

best_words=set([wforw,sinbest_vals])

returnbest_words

然后需要对find_best_words赋值,如下:

word_scores_1=create_word_scores()

word_scores_2=create_word_bigram_scores()

3.把选出的这些词作为特征(这就是选择了信息量丰富的特征)

defbest_word_features(words):

returndict([(word,True)forwordinwordsifwordinbest_words])

三、检测哪中特征选择方法更优

构建分类器,检验分类准确度,选择最佳分类算法

第一步,载入数据。

要做情感分析,首要的是要有数据。

数据是人工已经标注好的文本,有一部分积极的文本,一部分是消极的文本。

文本是已经分词去停用词的商品评论,形式大致如下:[[word11, word12, ... word1n], [word21,word22, ... , word2n], ... , [wordn1, wordn2, ... , wordnn]]

这是一个多维数组,每一维是一条评论,每条评论是已经又该评论的分词组成。

#! /usr/bin/env python2.7

#coding=utf-8

pos_review=pickle.load(open('D:/code/sentiment_test/pos_review.pkl','r'))

neg_review=pickle.load(open('D:/code/sentiment_test/neg_review.pkl','r'))

我用pickle存储了相应的数据,这里直接载入即可。

第二步,使积极文本的数量和消极文本的数量一样。

fromrandomimportshuffle

shuffle(pos_review)#把积极文本的排列随机化

size=int(len(pos_review)/2-18)

pos=pos_review[:size]

neg=neg_review

我这里积极文本的数据恰好是消极文本的2倍还多18个,所以为了平衡两者数量才这样做。

第三步,赋予类标签。

defpos_features(feature_extraction_method):

posFeatures=[]

foriinpos:

posWords=[feature_extraction_method(i),'pos']#为积极文本赋予"pos"

posFeatures.append(posWords)

returnposFeatures

defneg_features(feature_extraction_method):

negFeatures=[]

forjinneg:

negWords=[feature_extraction_method(j),'neg']#为消极文本赋予"neg"

negFeatures.append(negWords)

returnnegFeatures

这个需要用特征选择方法把文本特征化之后再赋予类标签。

第四步、把特征化之后的数据数据分割为开发集和测试集

train=posFeatures[174:]+negFeatures[174:]

devtest=posFeatures[124:174]+negFeatures[124:174]

test=posFeatures[:124]+negFeatures[:124]

这里把前124个数据作为测试集,中间50个数据作为开发测试集,最后剩下的大部分数据作为训练集。

在把文本转化为特征表示,并且分割为开发集和测试集之后,我们就需要针对开发集进行情感分类器的开发。测试集就放在一边暂时不管。

开发集分为训练集(Training Set)和开发测试集(Dev-Test Set)。训练集用于训练分类器,而开发测试集用于检验分类器的准确度。

为了检验分类器准确度,必须对比“分类器的分类结果”和“人工标注的正确结果”之间的差异。

所以第一步,是要把开发测试集中,人工标注的标签和数据分割开来。第二步是使用训练集训练分类器;第三步是用分类器对开发测试集里面的数据进行分类,给出分类预测的标签;第四步是对比分类标签和人工标注的差异,计算出准确度。

一、分割人工标注的标签和数据

dev,tag_dev=zip(*devtest)#把开发测试集(已经经过特征化和赋予标签了)分为数据和标签

二到四、可以用一个函数来做

defscore(classifier):

classifier=SklearnClassifier(classifier)#在nltk中使用scikit-learn的接口

classifier.train(train)#训练分类器

pred=classifier.batch_classify(testSet)#对开发测试集的数据进行分类,给出预测的标签

returnaccuracy_score(tag_test,pred)#对比分类预测结果和人工标注的正确结果,给出分类器准确度

之后我们就可以简单的检验不同分类器和不同的特征选择的结果

importsklearn

from nltk.classify.scikitlearnimportSklearnClassifier

fromsklearn.svmimportSVC,LinearSVC,NuSVC

fromsklearn.naive_bayesimportMultinomialNB,BernoulliNB

fromsklearn.linear_modelimportLogisticRegression

fromsklearn.metricsimportaccuracy_score

posFeatures=pos_features(bag_of_words)#使用所有词作为特征

negFeatures=neg_features(bag_of_words)

print'BernoulliNB`s accuracy is %f'%score(BernoulliNB())

print'MultinomiaNB`s

accuracy is %f'%score(MultinomialNB())

print'LogisticRegression`s

accuracy is %f'%score(LogisticRegression())

print'SVC`s

accuracy is %f'%score(SVC())

print'LinearSVC`s

accuracy is %f'%score(LinearSVC())

print'NuSVC`s

accuracy is %f'%score(NuSVC())

1.我选择了六个分类算法,可以先看到它们在使用所有词作特征时的效果:

BernoulliNB`s accuracy is

0.790000

MultinomiaNB`s accuracyis0.810000

LogisticRegression`s accuracy is 0.710000

SVC`s accuracyis0.650000

LinearSVC`s accuracy is 0.680000

NuSVC`s accuracyis0.740000

2.再看使用双词搭配作特征时的效果(代码改动如下地方即可)

posFeatures=pos_features(bigrams)

negFeatures=neg_features(bigrams)

结果如下:

BernoulliNB`s accuracy is

0.710000

MultinomiaNB`s accuracyis0.750000

LogisticRegression`s accuracy is 0.790000

SVC`s accuracyis0.750000

LinearSVC`s accuracy is 0.770000

NuSVC`s accuracyis0.780000

3.再看使用所有词加上双词搭配作特征的效果

posFeatures=pos_features(bigram_words)

negFeatures=neg_features(bigram_words)

结果如下:

BernoulliNB`s accuracy is

0.780000

MultinomiaNB`s accuracyis0.780000

LogisticRegression`s accuracy is 0.780000

SVC`s accuracyis0.600000

LinearSVC`s accuracy is 0.790000

NuSVC`s accuracyis0.790000

可以看到在不选择信息量丰富的特征时,仅仅使用全部的词或双词搭配作为特征,分类器的效果并不理想。

接下来将使用卡方统计量(Chi-square)来选择信息量丰富的特征,再用这些特征来训练分类器。

4.计算信息量丰富的,并以此作为分类特征

word_scores=create_word_scores()

best_words=find_best_words(word_scores,1500)#选择信息量最丰富的1500个的特征

posFeatures=pos_features(best_word_features)

negFeatures=neg_features(best_word_features)

结果如下:

BernoulliNB`s accuracy is

0.870000

MultinomiaNB`s accuracyis0.860000

LogisticRegression`s accuracy is 0.730000

SVC`s accuracyis0.770000

LinearSVC`s accuracy is 0.720000

NuSVC`s accuracyis0.780000

可见贝叶斯分类器的分类效果有了很大提升。

5.计算信息量丰富的词和双词搭配,并以此作为特征

word_scores=create_word_bigram_scores()

best_words=find_best_words(word_scores,1500)#选择信息量最丰富的1500个的特征

posFeatures=pos_features(best_word_features)

negFeatures=neg_features(best_word_features)

结果如下:

BernoulliNB`s accuracy is

0.910000

MultinomiaNB`s accuracyis0.860000

LogisticRegression`s accuracy is 0.800000

SVC`s accuracyis0.800000

LinearSVC`s accuracy is 0.750000

NuSVC`s accuracyis0.860000

可以发现贝努利的贝叶斯分类器效果继续提升,同时NuSVC也有很大的提升。

此时,我们选用BernoulliNB、MultinomiaNB、NuSVC作为候选分类器,使用词和双词搭配作为特征提取方式,测试不同的特征维度的效果。

dimension=['500','1000','1500','2000','2500','3000']

fordindimension:

word_scores=create_word_scores_bigram()

best_words=find_best_words(word_scores,int(d))

posFeatures=pos_features(best_word_features)

negFeatures=neg_features(best_word_features)

train=posFeatures[174:]+negFeatures[174:]

devtest=posFeatures[124:174]+negFeatures[124:174]

test=posFeatures[:124]+negFeatures[:124]

dev,tag_dev=zip(*devtest)

print'Feature

number %f'%d

print'BernoulliNB`s

accuracy is %f'%score(BernoulliNB())

print'MultinomiaNB`s

accuracy is %f'%score(MultinomialNB())

print'LogisticRegression`s

accuracy is %f'%score(LogisticRegression())

print'SVC`s

accuracy is %f'%score(SVC())

print'LinearSVC`s

accuracy is %f'%score(LinearSVC())

print'NuSVC`s

accuracy is %f'%score(NuSVC())

print

结果如下(很长。。):

Featurenumber500

BernoulliNB`s accuracy is

0.880000

MultinomiaNB`s accuracyis0.850000

LogisticRegression`s accuracy is 0.740000

SVC`s accuracyis0.840000

LinearSVC`s accuracy is 0.700000

NuSVC`s accuracyis0.810000

Featurenumber1000

BernoulliNB`s accuracy is 0.860000

MultinomiaNB`s accuracyis0.850000

LogisticRegression`s accuracy is 0.750000

SVC`s accuracyis0.800000

LinearSVC`s accuracy is 0.720000

NuSVC`s accuracyis0.760000

Featurenumber1500

BernoulliNB`s accuracy is 0.870000

MultinomiaNB`s accuracyis0.860000

LogisticRegression`s accuracy is 0.770000

SVC`s accuracyis0.770000

LinearSVC`s accuracy is 0.750000

NuSVC`s accuracyis0.790000

Featurenumber2000

BernoulliNB`s accuracy is 0.870000

MultinomiaNB`s accuracyis0.850000

LogisticRegression`s accuracy is 0.770000

SVC`s accuracyis0.690000

LinearSVC`s accuracy is 0.700000

NuSVC`s accuracyis0.800000

Featurenumber2500

BernoulliNB`s accuracy is 0.850000

MultinomiaNB`s accuracyis0.830000

LogisticRegression`s accuracy is 0.780000

SVC`s accuracyis0.700000

LinearSVC`s accuracy is 0.730000

NuSVC`s accuracyis0.800000

Featurenumber3000

BernoulliNB`s accuracy is 0.850000

MultinomiaNB`s accuracyis0.830000

LogisticRegression`s accuracy is 0.780000

SVC`s accuracyis0.690000

LinearSVC`s accuracy is 0.710000

NuSVC`s accuracyis0.800000

把上面的所有测试结果进行综合可汇总如下:

不同分类器的不同特征选择方法效果

bag_of_words

bigrams

bigram_words

best_word_feature

best_word_bigram_feature

BernoulliNB

0.79

0.71

0.78

0.87

0.91

MultinomiaNB

0.81

0.75

0.78

0.86

0.86

LogisticRegression

0.71

0.79

0.78

0.73

0.8

SVC

0.65

0.75

0.6

0.77

0.8

LinearSVC

0.68

0.77

0.79

0.72

0.75

NuSVC

0.74

0.78

0.79

0.78

0.86

候选分类器在不同特征维度下的效果

500

1000

1500

2000

2500

3000

BernoulliNB

0.88

0.86

0.87

0.87

0.85

0.85

MultinomiaNB

0.85

0.85

0.86

0.85

0.83

0.83

NuSVC

0.81

0.76

0.79

0.7

0.8

0.8

综合来看,可以看出特征维数在500或1500的时候,分类器的效果是最优的。

所以在经过上面一系列的分析之后,可以得出如下的结论:

Bernoulli朴素贝叶斯分类器效果最佳

词和双词搭配作为特征时效果最好

特征维数为1500时效果最好

为了不用每次分类之前都要训练一次数据,所以可以在用开发集找出最佳分类器后,把最佳分类器存储下来以便以后使用。然后再使用这个分类器对文本进行分类。

一、使用测试集测试分类器的最终效果

word_scores=create_word_bigram_scores()#使用词和双词搭配作为特征

best_words =

find_best_words(word_scores, 1500) #特征维度1500

posFeatures=pos_features(best_word_features)

negFeatures=neg_features(best_word_features)

trainSet=posFeatures[:500]+negFeatures[:500]#使用了更多数据

testSet=posFeatures[500:]+negFeatures[500:]

test,tag_test=zip(*testSet)

deffinal_score(classifier):

classifier=SklearnClassifier(classifier)

classifier.train(trainSet)

pred=classifier.batch_classify(test)

returnaccuracy_score(tag_test,pred)

printfinal_score(BernoulliNB())#使用开发集中得出的最佳分类器

其结果是很给力的:

0.979166666667

二、把分类器存储下来

(存储分类器和前面没有区别,只是使用了更多的训练数据以便分类器更为准确)

word_scores=create_word_bigram_scores()

best_words=find_best_words(word_scores,1500)

posFeatures=pos_features(best_word_features)

negFeatures=neg_features(best_word_features)

trainSet=posFeatures+negFeatures

BernoulliNB_classifier=SklearnClassifier(BernoulliNB())

BernoulliNB_classifier.train(trainSet)

pickle.dump(BernoulliNB_classifier,open('D:/code/sentiment_test/classifier.pkl','w'))

在存储了分类器之后,就可以使用该分类器来进行分类了。

三、使用分类器进行分类,并给出概率值

给出概率值的意思是用分类器判断一条评论文本的积极概率和消极概率。给出类别也是可以的,也就是可以直接用分类器判断一条评论文本是积极的还是消极的,但概率可以提供更多的参考信息,对以后判断评论的效用也是比单纯给出类别更有帮助。

1.把文本变为特征表示的形式

要对文本进行分类,首先要把文本变成特征表示的形式。而且要选择和分类器一样的特征提取方法。

#! /usr/bin/env python2.7

#coding=utf-8

moto=pickle.load(open('D:/code/review_set/senti_review_pkl/moto_senti_seg.pkl','r'))#载入文本数据

defextract_features(data):

feat=[]

foriindata:

feat.append(best_word_features(i))

returnfeat

moto_features=extract_features(moto)#把文本转化为特征表示的形式

注:载入的文本数据已经经过分词和去停用词处理。

2.对文本进行分类,给出概率值

importpickle

import sklearn

clf=pickle.load(open('D:/code/sentiment_test/classifier.pkl'))#载入分类器

pred=clf.batch_prob_classify(moto_features)#该方法是计算分类概率值的

p_file=open('D:/code/sentiment_test/score/Motorala/moto_ml_socre.txt','w')#把结果写入文档

foriinpred:

p_file.write(str(i.prob('pos'))+' '+str(i.prob('neg'))+'\n')

p_file.close()

最后分类结果如下图:

前面是积极概率,后面是消极概率。

( PS:有部分知识是通过摘引前辈的知识点,同时向前辈分享的知识,表示感谢。如有误之处,有待指正。)

相关文章

网友评论

      本文标题:使用python+机器学习方法进行情感分析(下)

      本文链接:https://www.haomeiwen.com/subject/msfcjxtx.html