摘要:
本文为了通过增加停用词和用户自定义词库,优化snownlp分词效果,从而提升snownlp情感判断准确率。
增加停用词较简单:对snownlp中-normal文件夹中-stopwords.txt进行补充
增加用户自定义词库(主要为了识别短语名词和否定短语,避免过度分词):
结合jieba中的 jieba.load_userdict(‘words.txt’) 导入自己准备的常用词词典;再用jieba.lcut()替换snownlp原有的seg.seg()分词方法
snwoNLP是python中专门针对中文的情感分析包,使用时也较为简单。
import snownlp
dir(snownlp) #查看包中的方法
‘’‘会得到:['SnowNLP', '__builtins__','__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__path__', '__spec__',
'bm25', 'classification', 'normal', 'seg', 'sentiment', 'sim', 'summary', 'tag', 'textrank', 'unicode_literals', 'utils', 'words_merge']
’‘’
其中seg用来分词,sentiment用来进行情感分析
sentiment情感分析的使用:
from snownlp import SnowNLP
from snownlp import sentiment
sentiment.train('neg.txt','pos.txt')
#snownlp/sentiment文件夹内有自带的购物评价训练集文本,包括neg.txt和pos.txt,分别是负面和正面评价。
#如果需要对其他情境下的文本进行分析,可自行替换,再执行sentiment.train('neg.txt','pos.txt') 命令。
SnowNLP(‘此处为需要判断的中文文本(字符串形式)’).sentiments
#会返回一个[0:1]之间的值,越接近0,代表为负面评价的可能性更大,越接近1,越偏向正面。
通过阅读其他博主对snowNLP源代码的解析(https://www.e-learn.cn/index.php/content/python/976795 这篇写的较为清楚和详细),大致了解了snowNLP的情感判断过程:
1、读取已经分好类的文本neg.txt和pos.txt
2、对所有文本进行分词、去停用词
3、计算每个词出现的频数
4、通过贝叶斯定理计算正面负面先验概率P(pos)和P(neg)
5、对要进行判断的文本分词
6、计算每个词的后验概率p(词|neg)和p(词|pos)
7、选择计算出的概率较大的类别(正或负)
整个原理就是计算每个词分别在neg.txt和pos.txt出现的频数,由此算出待判断的语句中的词出现在neg.txt,pos.txt中的概率,选其中概率大的一个类别。可以看出,要实现这个算法,最重要的部分就是恰当的语料库和分词从而计算词频。
但是使用下来,发现其分词效果和情感分析的准确率有待提升,snownlp在分词时,无法识别出否定短语,例如:不喜欢,会分成:不+喜欢。情感分析时,会因为计算出’不‘在neg.txt中出现概率较大,整体偏负面情绪。但是如果语料库不够充足时,过度拆词会影响情感分析的准确率。此外,能够过滤掉的stopwords停用词(例如:今天、的,以及一些标点符号,单位名称等没有情感含义的词语)太少了,可能会影响最终的情感判断。
为了改进,我们找到handle函数,它是snownlp中sentiment下的用来处理句子的方法,分为两步:seg分词和filter_stop去停用词。
def handle(self, doc): #在sentiment中
words = seg.seg(doc) #seg是snownlp的分词方法
words = normal.filter_stop(words) #过滤停用词
return words
###尝试使用handle看一下分词效果
string='尝试进行分词的几句话,今天是晴天,希望明天不会是阴天。我不喜欢这里。中小板股票会跌。'
sent = sentiment.Sentiment()
words_list=sentiment.Sentiment.handle(sent,string)
##会返回一个list
##['尝试','分词', '的', '几句话', '今天', '是', '晴天', '希望', '明天', '不会', '是', '阴天', '不', '喜欢', '中小板', '股票', '跌']
##没有过滤‘的’‘是’等词语,比较常见的否定短语‘不喜欢’被拆开
由于snownlp通过计算先验概率和后验概率来进行判断的,以上问题会导致分词不准确从而情感判断不准确。
停用词不足的解决很简单,只需要在snownlp中normal文件夹中的stopwords.txt中添加更多的停用词,或者找一些停用词库进行替换(https://download.csdn.net/download/qq_22473297/10296929)。
下面主要是如何结合jieba添加自定义词库。
为了解决以上问题,尝试结合jieba分词包对snownlp的分词进行优化。(一点题外话,jieba是“结巴”的音译,这个分词包的起名很形象)
jieba的优点:可以设置自己的常用词词库。
例如:如果不设定以上词库,
“贵州茅台的业绩大幅增长”,分词结果会是: 贵州、茅台、的、业绩、大幅、增长
“贵州茅台股票没有增长”分词结果会是: 贵州、茅台、股票、没有、增长
通过将“贵州茅台”、“大幅增长”、“没有增长” 加入自己的词库,将”的“加入停用词,可以得到我们想要的效果,两句话的分词结果分别为:
贵州茅台、业绩、大幅增长
贵州茅台、股票、没有增长
#设置jieba自定义词库
import jieba
jieba.load_userdict('words.txt') #自己准备的常用词词典
#优化后的lcut
jieba.lcut('需要分词的文本')
于是我们用jieba中的lcut替换snownlp中的seg.seg,可以达到更好的分词效果
jieba.lcut 和sentiment.Sentiment.handle都会返回分词后的list,形式上也很统一,所以对snownlp中sentiment文件夹下__init__.py 中的handle中的seg进行替换:
#原来的handle,在Sentiment class下:
# -*- coding: utf-8 -*-
import os
import codecs
import jieba
jieba.load_userdict('words.txt') #加载自定义词库
from .. import normal
from ..classification.bayes import Bayes
data_path = os.path.join(os.path.dirname(os.path.abspath(__file__)),
'sentiment.marshal')
class Sentiment(object):
def handle(self, doc):
words = jieba.lcut(doc) ##原本使用的是snownlp自带的seg分词功能,words = seg.seg(doc) 替换为jieba.lcut
words = normal.filter_stop(words) ##补充停用词,mormal文件夹中的stopword.txt
return words
此外再将snownlp文件夹内__init__.py中的seg.seg都替换为jieba.lcut:
import snownlp时,显示以上即是导入正常。
这时我们再执行sentiment分词,得到了较好的改善,另外情感判断的准确率也有所提升,可以识别出否定短语:
from snownlp import SnowNLP
from snownlp import sentiment
string='尝试进行分词的几句话,今天是晴天,希望明天不会是阴天。我不喜欢这里。中小板股票会跌。'
sent = sentiment.Sentiment()
words_list=sentiment.Sentiment.handle(sent,string)
#得到结果:['尝试', '分词', '几句话', '晴天', '希望', '不会', '阴天', '不喜欢', '中小板', '股票', '跌']
可能出现的问题:
注意txt文件放置的路径,word.txt放在python程序执行路径上
记得提前pip install jieba