贴吧评论敏感词识别及情感分析初级实现之情感分析
分三个模块实现贴吧评论敏感词识别及情感分析研究:“评论爬虫抓取”、“评论敏感词识别”、“评论情感分析(积极或消极)”。数据存储于MongoDB中,现设数据库“spiders”,数据集合users。其余两个模块见本人博文。
在贴吧评论敏感词识别及情感分析初级实现里,只涉及最基础的知识,未进行代码的升级以及相应模块的技术完善。
评论情感分析
现有两种对于短文本情感倾向研究的方法,一种是基于词典匹配,另一种是基于机器学习。
词典匹配法,即直接将待测文本分句,找到其中的情感词、程度词、否定词等,计算出每句情感倾向分值。词典匹配情感分析语料适用范围更广,但受限于语义表达的丰富性。
Python+机器学习情感分析,即选出一部分积极情感的文本与消极情感的文本,之后用机器学习方法进行训练,得出情感分类器,再通过这个情感分类器对所有文本进行积极与消极的二分分类。
该模块旨在对全部的网民言论进行情感分析,判定是积极言论还是消极言论。利用机器学习判断待测文本的情感倾向,即积极或消极。因为需要根据给定的输入预测某个结果,并且应该有输入/输出对的示例,所以属于有监督的二分类机器学习,类标签即为neg(消极)与pos(积极)。已知有监督机器学习的流程,如图所示。
模块实现
(1)处理语料库
语料库中有10000条购酒体验评论,现将所有评论分别归属于“good.txt”与“bad.txt”,即人为的为文本赋予类标签。
(2)特征提取
四种特征提取方式:
- 单字作为特征
- 双字(词语)作为特征
- 单字加双字作为特征
- 结巴分词形成的词语作为特征
(3)特征降维
- 所有字作为特征
- 双词作为特征,并利用卡方统计选取信息量排名前n的双字
- 单字和双字共同作为特征,并利用卡方统计选取信息量排名前n的
单字和双字; - 结巴分词外加卡方统计选取信息量排名前n的词汇作为特征
(4)特征表示
def text():
f1=open('good.txt','r',encoding='utf-8')
f2=open('bad.txt','r',encoding='utf-8')
line1=f1.readline()
line2=f2.readline()
str=''
while line1:
str+=line1
line1=f1.readline()
while line2:
str+=line2
line2=f2.readline()
f1.close()
f2.close()
return str
#单个字作为特征
def bag_of_words(words):
return dict([(word,True) for word in words])
#print(bag_of_words(text())
#把词语(双字)作为搭配,并通过卡方统计,选取排名前1000的词语
from nltk.collocations import BigramCollocationFinder
from nltk.metrics import BigramAssocMeasures
def bigram(words, score_fn=BigramAssocMeasures.chi_sq, n=1000):
bigram_finder = BigramCollocationFinder.from_words(words)
bigrams = bigram_finder.nbest(score_fn, n) # 使用卡方统计的方法,选择排名前1000的词语
newBigrams = [u + v for (u, v) in bigrams]
#return bag_of_words(newBigrams)
#print(bigram(text(),score_fn=BigramAssocMeasures.chi_sq,n=1000))
# 把单个字和词语一起作为特征
def bigram_words(words, score_fn=BigramAssocMeasures.chi_sq, n=1000):
bigram_finder = BigramCollocationFinder.from_words(words)
bigrams = bigram_finder.nbest(score_fn, n)
newBigrams = [u + v for (u, v) in bigrams]
a = bag_of_words(words)
b = bag_of_words(newBigrams)
a.update(b) # 把字典b合并到字典a中
return a # 所有单个字和双个字一起作为特征
#print(bigram_words(text(),score_fn=BigramAssocMeasures.chi_sq,n=1000))
import jieba
# 返回分词列表如:[['我','爱','北京','天安门'],['你','好'],['hello']],一条评论一个
def readfile(filename):
stop=[line.strip() for line in open('stop.txt','r',
encoding='utf-8').readlines()]
f=open(filename,'r',encoding='utf-8')
line=f.readline()
str=[]
while line:
s=line.split('\t')
fenci=jieba.cut(s[0],cut_all=False)
str.append(list(set(fenci)-set(stop)-set(['\ufeff','\n'])))
line=f.readline()
return str
from nltk.probability import FreqDist,ConditionalFreqDist
from nltk.metrics import BigramAssocMeasures
# 获取信息量较高(前number个)的特征(卡方统计)
def jieba_feature(number):
posWords=[]
negWords=[]
for items in readfile('good.txt'):
for item in items:
posWords.append(item)
for items in readfile('bad.txt'):
for item in items:
negWords.append(item)
word_fd=FreqDist() # 可统计所有词的词频
con_word_fd=ConditionalFreqDist() # 可统计积极文本中的词频和消极文本中的词频
for word in posWords:
word_fd[word]+=1
con_word_fd['pos'][word]+=1
for word in negWords:
word_fd[word]+=1
con_word_fd['neg'][word]+=1
pos_word_count=con_word_fd['pos'].N() # 积极词的数量
neg_word_count=con_word_fd['neg'].N() # 消极词的数量
# 一个词的信息量等于积极卡方统计量加上消极卡方统计量
total_word_count=pos_word_count+neg_word_count
word_scores={}
for word,freq in word_fd.items():
pos_score=BigramAssocMeasures.chi_sq(con_word_fd['pos'][word],(freq,
pos_word_count),total_word_count)
neg_score=BigramAssocMeasures.chi_sq(con_word_fd['neg'][word],(freq,
neg_word_count),total_word_count)
word_scores[word]=pos_score+neg_score
best_vals=sorted(word_scores.items(),key=lambda item: item[1],
reverse=True)[:number]
best_words=set([w for w,s in best_vals])
return dict([(word,True) for word in best_words])
# 构建训练需要的数据格式:
# [[{'买': 'True', '京东': 'True', '物流': 'True', '包装': 'True', '\n': 'True', '很快': 'True', '不错': 'True', '酒': 'True', '正品': 'True', '感觉': 'True'}, 'pos'],
# [{'买': 'True', '\n': 'True', '葡萄酒': 'True', '活动': 'True', '澳洲': 'True'}, 'pos'],
# [{'\n': 'True', '价格': 'True'}, 'pos']]
def build_features():
#feature = bag_of_words(text())
#feature = bigram(text(),score_fn=BigramAssocMeasures.chi_sq,n=900)
# feature = bigram_words(text(),score_fn=BigramAssocMeasures.chi_sq,n=900)
feature = jieba_feature(1000) # 结巴分词
posFeatures = []
for items in readfile('good.txt'):
a = {}
for item in items:
if item in feature.keys():
a[item] = 'True'
posWords = [a, 'pos'] # 为积极文本赋予"pos"
posFeatures.append(posWords)
negFeatures = []
for items in readfile('bad.txt'):
a = {}
for item in items:
if item in feature.keys():
a[item] = 'True'
negWords = [a, 'neg'] # 为消极文本赋予"neg"
negFeatures.append(negWords)
return posFeatures, negFeatures
(5)划分训练集和测试集
posFeatures,negFeatures=build_features()
from random import shuffle
shuffle(posFeatures) #把文本的排列随机化
shuffle(negFeatures)
train=posFeatures[200:]+negFeatures[200:] #只在1000条数据时合适,二八原则
test=posFeatures[:200]+negFeatures[:200]
data,tag=zip(*test) #分离测试集合的数据和标签,便于测试
(6)选出最佳分类器
通过比较准确度score,选出最佳特征提取方式、最佳特征维度、最佳分类算法。
首先固定特征维度为1000,进行如下计算(表格数据不准确),可取分别5次实验的平均值
bag_of_words | bigram | bigram_words | jieba_feature | |
伯努利朴素贝叶斯 | ||||
多项式分布朴素贝叶斯 | ||||
逻辑回归 | ||||
SVC | ||||
LinearSVC | ||||
NuSVC |
通过表格得出最佳分类算法与特征提取方式,例如结巴分词加卡方统计的特征提取方式与逻辑回归分类算法,接着通过固定特征提取方式与分类算法,不断调整特征维度,进而比较score得出最佳特征维度。例如(结巴分词+卡方统计+逻辑回归):
特征维度 | 100 | 600 | 1100 | 1600 | 2100 | 2600 | 3100 |
1 | 0.9075 | 0.9325 | 0.9475 | 0.9400 | 0.9350 | 0.9375 | 0.9550 |
2 | 0.9150 | 0.9600 | 0.9375 | 0.9550 | 0.9425 | 0.9600 | 0.9350 |
3 | 0.9250 | 0.9475 | 0.9550 | 0.9625 | 0.9550 | 0.9525 | 0.9525 |
4 | 0.9075 | 0.9300 | 0.9625 | 0.9475 | 0.9450 | 0.9500 | 0.9500 |
5 | 0.9175 | 0.9550 | 0.9375 | 0.9550 | 0.9525 | 0.9375 | 0.9525 |
平均值 | 0.9145 | 0.9450 | 0.9480 | 0.9520 | 0.9460 | 0.9475 | 0.9490 |
from nltk.classify.scikitlearn import SklearnClassifier
def score(classifier):
classifier=SklearnClassifier(classifier)
classifier.train(train)
pred=classifier.classify_many(data)
n=0
s=len(pred)
for i in range(0,s):
if(pred[i]==tag[i]):
n=n+1
return n/s
#通过实验,比较预测准确度score进而得出最佳特征提取方式、最佳特征维度、最佳分类算法
from sklearn.linear_model import LogisticRegression
from sklearn.svm import SVC, LinearSVC, NuSVC
from sklearn.naive_bayes import MultinomialNB, BernoulliNB
#print('LogisticRegression`s accuracy is %f' % score(LogisticRegression()))
(7)处理输入文本
将评论文本处理成可预测的格式,例如“京东的物流是没得说了,很快。这次买酒水类,包装很仔细,没有出现意外。酒到手了,绝对是正品。感觉很不错哟!”
利用结巴分词作为特征提取方式时,表示为
[‘很快’, ‘正品’, ‘不错’, ‘类’, ‘没得说’, ‘感觉’, ‘仔细’, ‘酒’, ‘酒水’, ‘物流’, ‘出现意外’, ‘包装’, ‘京东’, ‘到手’, ‘买’],
进行特征降维(卡方统计)与特征表示后,其可预测格式为
{‘物流’: ‘True’, ‘到手’: ‘True’, ‘仔细’: ‘True’, ‘感觉’: ‘True’, ‘很快’: ‘True’, ‘京东’: ‘True’, ‘正品’: ‘True’, ‘酒水’: ‘True’, ‘不错’: ‘True’, ‘出现意外’: ‘True’, ‘没得说’: ‘True’, ‘类’: ‘True’, ‘酒’: ‘True’, ‘包装’: ‘True’, ‘买’: ‘True’}
#处理输入的评论文本,使其成为可预测格式
def build_page(page):
#四中特征提取方式
# feature1 = bag_of_words(text())
# n为特征维度,可调整
# feature2 = bigram(text(),score_fn=BigramAssocMeasures.chi_sq,n=1000)
# feature 3= bigram_words(text(),score_fn=BigramAssocMeasures.chi_sq,n=1000)
feature4 = jieba_feature(1000) # 结巴分词,选取1000为特征维度,可调整
temp={}
'''
#单个字为特征
for word in page:
if word in feature1:
temp[word]='True'
#双字为特征
bigrams= BigramCollocationFinder.from_words(words)
text=[u + v for (u, v) in bigrams.ngram_fd]
for words in text:
if words in feature2:
temp[words]='True'
#单字和双字为特征
bigrams= BigramCollocationFinder.from_words(words)
text=[u + v for (u, v) in bigrams.ngram_fd]
for word in page:
text.append(word)
for words in text:
if words in feature3:
temp[words]='True'
'''
#现采用结巴分词形式处理待测文本
fenci0=jieba.cut(page,cut_all=False)
stop=[line.strip() for line in open('stop.txt','r',
encoding='utf-8').readlines()] #停用词
for words in list(set(fenci0)-set(stop)):
if words in feature4:
temp[words]='True'
return temp
(8)保存最佳分类器,进行预测
#将实验比较得出的最佳分类算法(classifier_ag)构造的分类器保存
def classfier_model(classifier_ag):
classifier = SklearnClassifier(classifier_ag)
classifier.train(train)
return classifier
#假设逻辑回归为最佳分类算法
#classifier=classfier_model(classifier_ag=LogisticRegression)
#用最佳分类器预测待测文本
def predict_page(page):
pred = classifier.classify_many(data)
return pred
(9)对数据集合中的评论page进行情感定位
注意:在此之前,一定要保存好分类器
#对users中的每条评论进行情感判断
def emotion_decide():
results=collection1.find()
for result in results:
if result['page']: #若评论为空,则不进行情感分析
condition={'page':result['page']}
if collection1.update_many(condition,{'$set':{
'emotion':emotional.predict_page(page=result['page'])}}):
print("successful")
###参考来源
(1)使用python+机器学习方法进行情感分析(详细步骤)
(2)python情感分析代码