gitbook/Python自动化办公实战课/docs/345158.md
2022-09-03 22:05:03 +08:00

205 lines
12 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 06 | jieba分词如何基于感情色彩进行单词数量统计
你好,我是尹会生。
在涉及运营、市场的工作中,我们经常需要根据产品评论的情感分析,来了解某一产品的口碑。所谓的情感分析,就是指根据用户对产品的评论,分析出用户对产品的喜好程度。
最简单的,我们会**区分产品的评价是正向还是负向**的,然后根据反馈结果改变产品的特性。稍微复杂一点的,我们会**根据情感色彩将产品的评价关键词提取出来,进行统计和分类(用于更深入的分析产品)。**
如果靠人工对产品评价进行辨析,有很大的局限性:一个是不够公平,因为每个人对词语感情色彩的理解并不是完全一致的;另一个是产品评价有很多,而且还会不定期增加,人工分析很难保证及时性。
因此在进行词语的情感分析时我通常都会使用Python的jieba库来自动化实现文本情感分析功能。一般需要经过三个步骤分别是**分词、优化分词结果和情感分析**。
那我就先带你看看**为什么要进行分词,以及如何进行分词操作。**
# 如何分词?
要想判断一段话表达的情感是正向还是负向,就需要根据这句话中的关键词来得到情感的倾向。例如一段话中出现了“开心”“高兴”“物超所值”等正向的词语,我们就可以认定这条产品的评价是偏正向的。相反,出现“不喜欢”“差”等词语,评价就是偏负向的。
但是,要想从一句话中将这些表达情感的词一个一个找出来,就需要依靠专业的工具把一句话根据语义划分成多个词,再把表达情感的词语提取出来,进行情感分析。
为什么要先根据语义来划分词呢?这主要是因为中文句子里的每个词中间没有用空格进行分隔,没有分隔就没法进行之后的情感分析。而对中文句子按照语义进行切割的这种操作,我们就称为“分词”。
Python中有非常成熟的分词库其中最流行的库是jieba库。在计算机中实现语义分割的技术有两种一种是从统计学的角度分词另一种是从词库的角度基于TF-IDF算法实现分词。jieba就是采用第二种基于词库的角度对文章进行自动分词的。
那我就以电商网站上的一段商品评论为例给你演示一下jieba库是如何实现分词的。
```
import jieba
words1="速度快,包装好,看着特别好,喝着肯定不错!价廉物美"
words2 = jieba.cut(words1)
print("/".join(words2))
# 速度/快//包装/好//看着/特别/好//喝/着/肯定/不错//价廉物美
```
在这段代码中我利用jieba库的cut()函数实现了自动分词功能。我刚才讲了jieba分词是依靠词库实现的词库里包含了提前准备好的词和词性。下图就是jieba词库的内容
```
一鼓 ru
一鼓作气 ru
一马当先 ru
... ...
```
这些词库中的词jieba是怎么识别的呢
在你使用pip命令安装了jieba库之后它会附带一个默认的词库。在官方文档中将这个词库称作“字典”文件。这个文件包含了日常汉语的词语、词性。jieba库会先基于“字典”对文章中所有可能出现的词进行匹配。匹配之后会生成句子中的汉字所有可能形成的词。然后再将这些词构成的有向无环图DAG并采用动态规划算法查找最大概率路径尽可能不会将一个词拆分成单个汉字。最后再从“字典”找出基于词频的最大切分组合把这分词的组合从句子中找出来形成一个一个的词。
而且为了提高分词的准确率jieba对没有记录在字典的词称作未登录词也使用了分词的模型它就是大名鼎鼎的基于汉字成词能力的HMM模型隐马尔可夫模型。对词库中的词和未登录词进行处理之后jieba就可以实现自动化分词了。
不过,分词之后,我们还需要对分词结果进行优化。因为在分词结果中存在着大量的标点符号,还有“看着”“喝着”“包装” 等和表达产品评价的情感无关的词语,为了加快计算词语的情感速度、避免无关词语影响情感倾向判断,我们就要优化分词的结果。
# 优化分词结果
优化分词结果主要从两个方面进行优化:一方面是移除标点符号;一方面是删除和情感无关的助词、名词等。
我先来带你学习下怎么从分词结果中移除标点符号。
移除标点符号一般有两种方法:
1. 删除停止词Stop Words
2. 根据词性提取关键词。
先来看看第一种,删除停止词。
所谓的停止词,就是指**为了节省空间和提高匹配词语情感倾向的效率,在进\*\*\*\*行情感分析前自动过滤掉的某些字或词。**
停止词主要是标点符号也可以是“啊呀呢”等语气助词。把标点符号写入停止词列表后再使用for循环功能将jieba分好的词和停止词列表依次匹配。如果jieba分好的词出现在列表中就将这些词删掉。如果没有出现在列表中就把这些词再组合成一个新的列表后续就可以对新的列表进行情感分析。
删除停止词的代码如下。通过删除停止词,我们就可以得到只有汉字的分词结果。
```
words2 = jieba.cut(words1)
words3 = list(words2)
print("/".join(words3))
# 速度/快//包装/好//看着/特别/好//喝/着/肯定/不错//价廉物美
stop_words = ["", ""]
words4 =[x for x in words3 if x not in stop_words]
print(words4)
# ['速度', '快', '包装', '好', '看着', '特别', '好', '喝', '着', '肯定', '不错', '价廉物美']
```
另一种优化分词结果的方式叫做根据词性提取关键词。这种方式的优点在于不用事先准备停用词列表jieba库就能够根据每个词的词性对其进行标注。
我这里为你提供了一张paddlepaddle是百度开源的深度学习平台jieba使用了paddle的模型库模式词性表作为参考你可以根据jieba自动分析得到的词性结果手动将助词、虚词标点符号移除。
![](https://static001.geekbang.org/resource/image/de/5a/dec72024563b5179f08b5c314a59da5a.png)
我把这个基于词性移除标点符号的代码也提供给你:
```
# words5 基于词性移除标点符号
import jieba.posseg as psg
words5 = [ (w.word, w.flag) for w in psg.cut(words1) ]
# 保留形容词
saved = ['a',]
words5 =[x for x in words5 if x[1] in saved]
print(words5)
# [('快', 'a'), ('好', 'a'), ('好', 'a'), ('不错', 'a')]
```
在这段代码中我在使用jieba库的posseg类实现分词的同时也对词性进行了标注。为了让你看到更直接的结果我只保留了形容词因此变量saved的列表参数就只有一个a表示保留的词类型为形容词。
如果你希望保留更多的词性可以将词性表中代表每种词的英文缩写写入saved列表中其中我建议你在处理之后把形容词、副词、动词都保留下来这些都有助于你进行下一步的语义情感分析。
在优化分词结果之后,我们就得到了只有形容词的处理结果。那么,接下来,我们需要基于这些形容词来获取产品评价的正向或负向结果,以及基于词语的情感色彩来统计单词的数量。
# 语义情感分析
对于已经分好词的语句我们需要使用另一个库统计词的正向、负向情感倾向这个库就是snownlp库。
snownlp库既能实现分词也能计算词出现频率以及进行情感分析。那你可能就发出疑问了为什么不直接使用snownlp进行分词而要使用jieba分词呢
原因就在于snownlp的算法问题会让它对否定词划分得不够准确。例如“不喜欢”snownlp会把这个词划分为两个独立的词分别是“不”和“喜欢”。那么在计算语义情感时就会产生较大的误差。所以我们会先采用jieba进行分词分词之后再采用snownlp来实现语义情感分析功能。
接下来我带你看一下如何使用snownlp得到完成分词之后的情感分析结果。代码如下
```
from snownlp import SnowNLP
words6 = [ x[0] for x in words5 ]
s1 = SnowNLP(" ".join(words3))
print(s1.sentiments)
# 0.99583439264303
```
这段代码通过snownlp的Bayes贝叶斯模型训练方法将模块自带的正样本和负样本读入内存之后再使用Bayes模型中的classify()函数进行分类这样就得到了sentiments属性的值sentiments的值表示情感倾向的方向。在snownlp中
* 如果情感倾向是正向的sentiments的结果会接近1。
* 如果情感倾向是负向的结果会接近0。
可以看到我们在刚刚的代码中得到的情感分析的结果是0.9958非常接近1因此这条产品的评价就是正向的。
情感倾向结果趋近于1或者趋近于0都是非常理想的情况可以直接得到感情色彩比较强烈的产品评价。但是有时候感情色彩不太强烈在这种情况下我们就需要根据评价的数值范围对评论进行分组统计每组包含多少个评价。
这个功能也可以通过snownlp实现我把代码写在这里你可以参考
```
positive = 0
negtive = 0
for word in words6:
s2 = SnowNLP(word)
if s2.sentiments > 0.7:
positive+=1
else:
negtive+=1
print(word,str(s2.sentiments))
print(f"正向评价数量:{positive}")
print(f"负向评价数量:{negtive}")
# 快 0.7164835164835165
# 好 0.6558628208940429
# 好 0.6558628208940429
# 不错 0.8612132352941176
# 价廉物美 0.7777777777777779
# 正向评价数量:3
# 负向评价数量:2
```
通过snownlp库配合jieba分词的结果你就可以实现批量产品评论的自动语义情感分析了。同时你还可以根据不断累积产品的评价来持续优化你的产品。
# 小结
最后,我来为你总结一下对文件进行情感倾向分析的关键步骤和注意事项。实现语义情感分析功能,你必须掌握分词、优化分词结果、语义情感分析这三个步骤。
其中分词是实现中文语义分析的第一步也是最基础的部分。分词的好坏决定了对每个词的情感进行标注的准确程度。如果默认的jieba分词没有正确地把词语划分你也可以使用jieba自带的suggest\_freq()函数进行词频调节。
举个小例子,“中”“将”两个字可以组成“中将”的词语,也可以拆开来用“我们中/将有人成功考上北大”。在不同的语言环境中,我们要通过词频调节来让它们以词的形式出现,还是以两个字的方式出现。调整的方法是:
```
jieba.suggest_freq(("中", "将"), tune = True)
```
可以看到利用调节词频使“中”“将”都能被分出来而不会被错误地识别为一个词“中将”通过这种方式就可以提升jieba的识别正确率。
在优化分词结果这一步,你可以通过减少虚词和标点符号,通过停止词、词性的选择,来降低它们对情感分析结果的干扰。
最后你还可以为snownlp增加新的流行词和网络用语帮你更准确地分析用户对产品的喜好程度从而提高产品定位的精确度。
在snownlp中通过train()和 save()两个函数把模型训练和保存之后就能实现扩展默认字典的功能了。此外我在工作中还会利用这种方式增加emoji表情对应的情感倾向分析功能以此来进一步提升snownlp分析情感倾向的准确度。
我将训练模型和保存训练后的模型的函数也写在这里供你参考,希望通过训练自己的模型,能够让你的产品分析更加准确。
```
sentiment.train(neg2.txt,pos2.txt); # 训练用户自定义正负情感数据集
sentiment.save('sentiment2.marshal'); # 保存训练模型
```
今天用到的代码,我都放在了 GitHub 上,你可以点击[这个链接](https://github.com/wilsonyin123/python_productivity/blob/main/%E6%96%87%E7%AB%A06%E4%BB%A3%E7%A0%81.zip)查看。
# 思考题
我给你留一道思考题,我在最后一段代码分别统计了正向和负向评价的数量,你能否根据这段代码统计一段文字中包含了多少个动词、多少个名词和多少个形容词呢?欢迎你在课程评论区留言,和我一起讨论。