前文摘要:
微博热门话题:#中印双方达成五点共识# 阅读量2.4亿,讨论7430条。
1、数据采集,使用python+selenium,采集该话题下的博文及作者信息,以及每个博文下的评论及作者信息;
2、数据预处理,采用Jieba库,构建用户词典,以达到更好的分词;情感分析,采用snownlp库,寻找政治类积极和负面词向量做一个训练,再进行评论分类;
3、对博文及评论作者信息进行分析,查看调查主体的用户类别概况;
4、lda主题分析,对博文做主题分析,依据top3主题关键字,对博文群主类看法进行分析;对正、负向评论做一次主题分析,并分别分析观点;
第一步已完成,现在到第二步;根据网上评价,snownlp作为国内情感分析做的较好的库,但是他的分词能力比较一般,而jieba库在分词这一块做得又比较好,可以扩展用户自定义词典。因此可以考虑,将snownlp与jieba分词库相结合使用。
大概最终的流程就是:
1、修改snownlp源代码使其可扩展;
2、自定义分词方法,jieba;
3、使用新的分词方法对样本进行训练得到新的情感判断器;
4、使用;
参考链接:
snownlp包结构如图所示:
其中常用库如此:
importjiebafrom snownlp import SnowNLP #使用
from snownlp import seg #分词库
from snownlp import sentiment #情感分词
from snownlp import normal #停用词处理
snownlp常用情感分析方法如下:
s = SnowNLP(u'小明不诚信')print("分词:",s.words)print("情感评分(0.6以上为积极,0.2一下为负面):",s.sentiments)
输出:
分词: ['小明', '不', '诚信']
情感评分(0.6以上为积极,0.2一下为负面): 0.16586151271662786
从结果观察:“不诚信”被拆分成了,“不”,“诚信”,本应该一起的词汇被分开了。评分较为负面,判断结果正确。
继续观察snownlp分词的方法,查看源代码,可看到情感分析调用的是Sentiment类中的classify方法,调用的过程如下:
分词(seg.seg(doc))->去除停用词(normal.filter_stop(words))->贝叶斯分类(classifier.classify(self.handle(sent))。
classSnowNLP(object):
@propertydefsentiments(self):return sentiment.classify(self.doc)
classSentiment(object):defhandle(self, doc):
words=seg.seg(doc)
words=normal.filter_stop(words)returnwords#从这里发现,训练和分词,采用的都是 self.handle 函数,如果只修改这里,确实也是可以,还不影响他自带的分词
#如果修改seg.seg的话,就得从新训练过
deftrain(self, neg_docs, pos_docs):
data=[]for sent inneg_docs:
data.append([self.handle(sent),'neg'])for sent inpos_docs:
data.append([self.handle(sent),'pos'])
self.classifier.train(data)defclassify(self, sent):
ret, prob=self.classifier.classify(self.handle(sent))if ret == 'pos':returnprobreturn 1-prob
综上所述:如果需要改善分词,则需要修正seg包,如果要完善去除停用词,则修正Normal包。其中去除停用词比较方便:
对snownlp中-normal文件夹中-stopwords.txt进行补充即可。
再继续观察分词,可以发现原分词方法的使用了正则,为了能让他扩展使用其他分词方法,增加my_handle全局变量:
classSnowNLP(object):
@propertydefwords(self):return seg.seg(self.doc) #分词调用seg方法
defseg(sent):
words=[]if my_handle==None: #此行为更新的代码
for s inre_zh.split(sent):
s=s.strip()if nots:continue
ifre_zh.match(s):
words+=single_seg(s)else:for word ins.split():
word=word.strip()ifword:
words.append(word)else: #此行为更新的代码
words = my_handle(sent) #此行为更新的代码
return words
在seg文件中,补充设置my_handle分词方法:
my_handle=None#查看当前的分词方法
defget_now_handle():if my_handle ==None:returnsegelse:returnmy_handle#设置自定义分词方法
defset_my_handle(h):globalmy_handle
my_handle= h
最终调用执行时,定义分词方法->补充用户自定义词典->调用判断:
#定义自定义分词函数
defmy_cut_handle(sent):returnjieba.lcut(sent)#修改分词方法
seg.set_my_handle(my_cut_handle)
jieba.add_word("不诚信")s= SnowNLP(u'小明不诚信')print("分词:",s.words)print("情感评分(0.6以上为积极,0.2一下为负面):",s.sentiments)
输出:
分词: ['小明', '不诚信']
情感评分(0.6以上为积极,0.2一下为负面): 0.4737672181921908
由此看到,分词已经是准确的,但是,情感评分提升到了中性,与我们的预期不符合,这是因为snownlp库初始读取的情感分析训练器是基于以前的分词方法,因此,如果我们更新了分词方法以后,要使用新的方法重新训练,训练调用如下:
#输入为正负样本
sentiment.train(r'../data/npl_asan/neg.txt',r'../data/npl_asan/pos.txt')
fname= r'../data/npl_asan/sentiment.marshal'sentiment.save(fname)#保存训练结果#sentiment.load(fname) #读取已经训练好的数据
#重新执行
s = SnowNLP(u'小明不诚信')print("分词:",s.words)print("情感评分(0.6以上为积极,0.2一下为负面):",s.sentiments)
输出如下:
分词: ['小明', '不诚信']
情感评分(0.6以上为积极,0.2一下为负面):0.17156078146853382
到此发现:分词与预期到进行了提升。
不足:情感训练样本较少,需要进行补充才能进行更好的判断,如,修改句子增加形容词,表达意思应该是一样的,但是情感提升到了中性:
jieba.add_word("邻居家")
s= SnowNLP(u'邻居家的小明不诚信')print("分词:",s.words)print("情感评分(0.6以上为积极,0.2一下为负面):",s.sentiments)
输出:
分词: ['邻居家', '的', '小明', '不诚信']
情感评分(0.6以上为积极,0.2一下为负面):0.44767845850986676
nlp处理是一个复杂的基础工程,对需要分析的专业场景需要搜集特定的词典以及较完善的正负样本才能进行更好的判断。
emmmm,还需要解决这个最终判断不合适的问题。
我的博客即将搬运同步至腾讯云+社区,邀请大家一同入驻:https://cloud.tencent.com/developer/support-plan