摘要
我在《爬取简书百万页面 分析简书用户画像》 中分析了简书用户的书籍喜好,这里继续尝试通过简书影评文章的抓取、内容清洗、影评句子提取,建立机器学习情绪预测模型,挖掘分析简书用户对流行电影的喜好。
涉及的技术点/知识点
涉及的NLP(自然语言处理)知识点:
- 中文分词
- 关键词提取
- 文本相似度
- 中心句提取
- 情绪识别
涉及的ML(机器学习)知识点:
- 有监督分类
- 数据不平衡问题
大纲
- 序
- 数据源
- 难点问题
- 难点1:电影名称识别
- 难点2:评论句提取
- 难点3:文本的情绪识别
- 难点4:如何评估预测结果的准确性
- 总结
序
意欲何为 - 没有评分机制时,如何判定用户对电影的喜恶?
电影的拍摄成本高、票价也不低,消费者投入的关注度和观看成本高(最终的投入成本是包括注意力投入、金钱投入、时间投入和感情投入)。
简单算下观看一部电影的成本(步骤):先看下预告内容,做下同期上映电影的比较,觉得导演、演员阵容都不错,下定决心要看了,跟男/女友/老婆 /老公/基友/闺密做推荐,推荐成功后去选座买票,观影当天说不定还得先吃个饭,饭后乖乖的在影院呆上90分钟(关键时刻还得憋尿),观影结束后指不定还得来个夜宵、开个房间探讨下人生什么的。。。
读一本书的成本(步骤):1.打开书/电子阅读器/手机;2. 读!
这样对比看一部电影比读一本书的投入成本高多了!
因此观众给出的反馈自然也比书籍会强烈一些,特别是满怀期待看完却发现是一部烂片,有种上当受骗的感觉。
因此对电影光是通过词频作为喜好度是不够的,用户可能反复的在骂呢。
所以需要引入多一个分析的因子:情绪。用户提及某个电影时的情绪是怎么样的,是积极愉悦、还是负面愤怒,加上这个才能有效判定用户对电影或者某个事物的喜好。
如何得之 - 如何得到用户的情绪?
在豆瓣上,用户的评分就是最直接的情绪数值:5力荐,4推荐,3还行,2较差,1很差。
但在简书这类写作平台上,发布文章时是没有一个选项来填写分数,也没提供选择心情的选项。
“所言即其所思”,这是普遍人性。每个用户写下的文字表达想法,在字里行间都会不经意的使用一些情绪用词来表达,这些便是我可以获取的“原始数据”。
得之何用 - 情绪分析用途?
精准推荐!用户谈论得多的东西未必是TA所喜爱的,也许是TA痛恨的(比如说前任)。如果用户在自己大力差评某部电影的文章底部看到另一部相似烂片的推荐,推荐文案还写着“亲,我猜你还喜欢看这些”,估计这用户会有种吃了苍蝇刚吐出来发现迎面又飞来几只的感觉。
通过用户在讨论某事物时上下文的情绪分析,来判断对该事物的喜恶,将预测结果加入对事物的打分计算中,最后得出一个可量化、可计算的分数值,便是一个精准推荐系统的需要解决的问题。
如何为之 - 怎么做?
理论上只需要一句话:要识别出文章中提及的电影,以及作者对该电影的评论,是好评还是差评,作为文章作者对该电影的喜恶,并根据评论的情绪强烈程度转化为喜恶程度即可。
那实际上,就要解决以下问题:
- 难点1:电影名称识别
- 难点2:评论句提取
- 难点3:文本的情绪识别
- 难点4:如何评估预测结果的准确率
下面逐一细说。
数据来源
我采集了简书上几个比较受欢迎的电影专题以及一个大V帐号文章(数据截止至采集数据时):
-
收录了 38549 篇文章,601170 人关注
-
收录了 37193 篇文章,50321 人关注
-
41783 粉丝; 1424 文章
但由于简书对专题页面加了访问限制,每个专题只能访问到最新的200个列表页面,因而每个专题可获取2~3000篇文章,以此作为抽样数据样本。
难点问题
难点1:电影名称识别
要分析对电影的评价,首先就要获取到文章中提到电影名称有哪些,最简单的匹配规则便是提取书名号《》
内的内容。
而实际获取到的电影名称真可谓“脏乱差”,有电影名、书籍名、文章名等,有些电影名称会用缩写引用(如《变1》、《复联》、《生化危机4/5/6》),或者中英文混合在一起(如《机器人总动员(wall-e)》),甚至还有错漏别字(如《那些年,我们一起追过的女孩》,多了个“过”字),以及包含中英文特殊标点空白符号等,例如:
因此需要进行一轮“数据大清洗”,我使用如下清洗策略:
- 提取文章中所有书名号
《》
中包含的文本 - 去掉其中为链接、文章标题(句子中包含“文章|写过”等字眼的)、书名(句子中包含“本书|书中|那本...”等字眼的)
- 构建
电影专用词典
,并使用文本相似性算法查找出与电影词典最相似的一个名称 - 构建
电影停用词典
,去掉停用词典里包含的内容 - 使用匹配规则,统一标点符号、去掉空白字符、中英文名称分离、系列名分离
- 去重
构建电影专用词典
其中“电影专用词典”,是通过采集了「猫眼 影片总票房排行榜」2000~2017年间上榜的所有电影,约有3000+部,如图:
再根据文章中所收集到的电影名称的候选文本,抓取「豆瓣网」的官方电影名字(遭遇了豆瓣业内闻名的反爬策略就不多说了),合并去重后约有8400多部。看一眼才发现好多电影连听都没听过,有些电影名称还挺有趣,以后有时间还可以做下电影名称的分析。
电影名称纠错
为了解决错别字,我通过文本相似性算法,在电影专用词典里找出与之最相似名称。
知名的相似算法有不少,如:
- jaro_distance
- ngram_distance
- levenshtein_distance
- damerau_levenshtein_distance
经过简单比较,我选择了其中表现最好的jaro_winkler_distance
# e.g.: str_edit_distance("加勒比海盗五:死无对证") # => ["加勒比海盗5:死无对证", 0.9754]
jaro_winkler_distance(str1, str2).round(4) # 0.9754, best
jaro_distance(str1, str2).round(4) # 0.9589
ngram_distance(str1, str2).round(4) # => 0.9091
normalized_levenshtein_distance(str1, str2).round(4) # => 0.7273
normalized_damerau_levenshtein_distance(str1, str2).round(4) # => 0.7273
运用到文章内容上,可得到相似电影名称,感觉效果还可以:
构建Pipeline
构建一个Pipeline用于提取页面中电影名称,对单个页面测试效果如下:
运用到所有页面上:
至此完成了电影名称的提取、清洗,得出每篇文章提及的电影名称,如图:
难点2:评论句提取
判断一篇文章的情绪,最简单的做法是通过提取所有情绪词来统计下词频及其权重可得到一个粗糙的结果,但这样的结果对“影评”类文章不是很准确。
根据观察,一篇电影评论文章中,往往会有大段的电影情节描述,然后夹杂一两句评论。而有些电影本身剧情就是比较负面、阴暗的(如灾难片类的),但影片本身质量很高(如《釜山行》,豆瓣评分8.2),如果文章内容包含大量的剧情描述,那么得到关键词会偏向于负面情绪,并不能用于表达出作者对电影的喜好情绪。
因此我使用了这样的提取策略:
- 跳过电影名称多于一个的文章(不确定因素太多)
- 直接相关语句子:包含电影名的句子
- 间接相关句子:包含点评词语的句子,如:“这部电影|豆瓣评分|总的来说|看完之后|电影结束|个人感受...”等等
- 总结性段落:文章标题和文章最后段落
评论句子提取的结果示例如下:
这样的策略对有些内容不是很适用,例如这样的标题:《同样是豆瓣9.2分,它或许比《摔跤吧!爸爸》更深》,这是典型的“借势营销”型文案,文章内容中推介的是另外一部韩国电影《熔炉》,而时下《摔跤吧!爸爸》正热,标题中带上这个会更吸引用户点击。
另外一个思路是对文章进行摘要提取,然后对摘要进行情绪识别。而对于文章的主题提取,可以使用在ML算法中的用于解决主题模型
问题的算法(如LDA),但不确定是否适合影评这种混合了大量剧情描述的文章内容。
难点3:文本的情绪识别
得到相关评论文本后,便可对文本进行分词及情绪识别,这里有两种做法,一是使用带有情绪情感度的专业词典,配合专有算法;另外一种是使用机器学习算法。
使用专业词典
一开始我使用了大连理工大学的《大连理工情感词汇本体库》,是目前最权威的中文情绪词典,共含有情感词共计27466个,包含了词语的词性种类、情感类别、情感强度及极性等信息,例如:
词语 | 词性种类 | 词义数 | 词义序号 | 情感分类 | 强度 | 极性 | 辅助情感分类 | 强度 | 极性 |
---|---|---|---|---|---|---|---|---|---|
无所畏惧 | idiom | 1 | 1 | PH | 7 | 1 | |||
手头紧 | idiom | 1 | 1 | NE | 7 | 0 | |||
周到 | adj | 1 | 1 | PH | 5 | 1 | |||
言过其实 | idiom | 1 | 1 | NN | 5 | 2 |
中文情感词汇本体的情感分类体系是在国外比较有影响的Ekman的6大类情感分类体系的基础上构建的。在Ekman的基础上,词汇本体加入情感类别“好”对褒义情感进行了更细致的划分。最终词汇本体中的情感共分为7大类21小类,如图:
把情感词典加入分词词典,对候选每个句子进行分词,得出情感词组,并累加每个情感词的强度值作为句子的情感值。
但经过测试后,发现预测效果并不理想,究其原因发现是情感词典中命中的词语太少,在句子中得不到相关词语,也就无法判断情感极性和强度。
使用情感词典的测试结果:
而专业词典的扩展需要专业领域知识,扩展起来很困难,我目前没找到更新的专业词典,国内这块的资料相当匮乏,而国外有个知名的SentiWordNet库,中文NLP研究的难度系数可想而知。
使用机器学习
鉴于情感词典的资源限制,决定尝试使用机器学习来对文本做正、负面情绪预测。
1). 构建分词词典:
分词需要构建专用领域的词典,这样得到的分词结果才会更精确。
因为只需要有正负情感类别、不需要有情感强度及极性等信息,我使用了网络上公开的一些情感词典,:
- 《大连理工情感词汇本体库》:这里只利用了情感类别
- 《台湾大学简体中文情感极性词典NTUSD》:2810正极性词语 + 8276负极性词语
- 《知网情感分析用词语集》:由知网发布的词典,包括中文情感词典和英文情感词典
- 《BosonNLP情感词典》(加上后发现预测效果并不好,原因是这个词典其实是预测文本的分词结果,不是整理过的专业情感词汇,并没使用上)
- 国外的 《SentiWordNet》 (只有英文单词,这次没有使用)
“正面情感”词语,如:爱,赞赏,快乐,感同身受,好奇,喝彩,魂牵梦萦,嘉许 ...
“负面情感”词语,如:哀伤,半信半疑,鄙视,不满意,不是滋味儿,后悔,大失所望 ...
“正面评价”词语,如:不可或缺,部优,才高八斗,沉鱼落雁,催人奋进,动听,对劲儿 ...
“负面评价”词语,如:丑,苦,超标,华而不实,荒凉,混浊,畸轻畸重,价高,空洞无物 ...
合并、清洗和去重后,最终构建出了14000+
个词的积极情绪词典,18000+
个词的消极情绪词典。
积极词典:
消极词典:
2). 准备训练语料
从网上搜集到几个带有积极、消极的标注的评论语料数据集,分别包含了书籍、DVD、音乐、购物领域的各有4000条评论,如下图:
可惜没找到中文影评相关的语料,但找到国外英文的影评语料 ,对,英文资料很丰富。。。
3). 训练模型
由于是文本型特征,分类算法上选择了朴素贝叶斯
,把数据分为2份,一半训练,另一半做测试。
使用jieba
分词工具,对文本进行分词得到候选词组,把得到的词组作为特征输入来训练情绪预测模型。
训练后的模型测试结果如下:
Classifier stats:
{:categories=>["positive", "negative"],
:category_stats=>
"For category positive, 20731 examples (47.94% of the total) and 144306 total_tokens\n" +
"For category negative, 22512 examples (52.06% of the total) and 91796 total_tokens",
:total_examples=>43243,
:train_times=>43243}
Run measure task for data_type=book
Loading test data for book
Test with 2000(2000~4000) comments for book
Progress(Time: 00:00:14)[2000/2000](100%):|========================================================================================================================|Time: 00:00:14
Measure scores:
{:tp=>796,
:tn=>832,
:fp=>181,
:fn=>191,
:precision=>0.815,
:recall=>0.806,
:f1_score=>0.81}
Accuracy:
Predict 1628 success of 2000, accuracy is 81.4% (😃 Good)
Show details?(y/n):n
save correct_words?(y/n): n
Run measure task for data_type=music
Loading test data for music
Test with 2000(2000~4000) comments for music
Progress(Time: 00:00:12)[2000/2000](100%):|========================================================================================================================|Time: 00:00:12
Measure scores:
{:tp=>716,
:tn=>910,
:fp=>237,
:fn=>137,
:precision=>0.751,
:recall=>0.839,
:f1_score=>0.793}
Accuracy:
Predict 1626 success of 2000, accuracy is 81.3% (😃 Good)
Show details?(y/n):n
save correct_words?(y/n): n
Run measure task for data_type=dvd
Loading test data for dvd
Test with 2000(2000~4000) comments for dvd
Progress(Time: 00:00:12)[2000/2000](100%):|========================================================================================================================|Time: 00:00:12
Measure scores:
{:tp=>791,
:tn=>851,
:fp=>229,
:fn=>129,
:precision=>0.775,
:recall=>0.86,
:f1_score=>0.815}
Accuracy:
Predict 1642 success of 2000, accuracy is 82.1% (😃 Good)
Show details?(y/n):n
save correct_words?(y/n): n
Run measure task for data_type=shopping
Loading test data for shopping
Test with 2000(2000~4000) comments for shopping
Progress(Time: 00:00:11)[2000/2000](100%):|========================================================================================================================|Time: 00:00:11
Measure scores:
{:tp=>970,
:tn=>751,
:fp=>30,
:fn=>249,
:precision=>0.97,
:recall=>0.796,
:f1_score=>0.874}
Accuracy:
Predict 1721 success of 2000, accuracy is 86.1% (😃 Good)
Show details?(y/n):n
save correct_words?(y/n): n
训练后的模型在书籍、音乐、影碟及购物评论的语料上都有不错的效果。
在提取出的影评短句上测试:
又是“无聊”、“又俗又傻”而一个“但”字就反转了情绪,真是难为分类器了:
经过反复对比实验,发现语料的质量决定了模型的精度,而语料的扩展对比扩展专业词典却容易多了。例如几年前都没有“因吹斯汀”这些网络流行词,更不知道它代表是正面情绪,以后只要不断更新相关领域的语料库来重新训练则可得到更精确的模型。
难点4:如何评估预测结果的准确性
俗话说“是骡子是马拉出来遛遛”。
得到一个预测情绪的模型后,便可对影评文章进行预测,但预测的结果是否准确?准确率又有多高?这又是个问题。
每当事情陷入停滞,我就会想起日本漫画大师安达充说过“当故事情节发展不下去的时候,加入一个新角色,是最简单的做法”。
这时需要祭出一个“旁证”了。这所谓的“旁证”,也就是目前大家比较认可的电影评分网站——豆瓣网。
引入“旁证”
大家都知道豆瓣网上的电影用户通过打星(5力荐,4推荐,3还行,2较差,1很差),转换得出的10分制的评分,我们可简单把14.9分视作负面评价、510分视作正面评价,没有分数的视作中性评价。再把豆瓣的评价结果视作大部分人认同的结果,当然这样肯定是有偏差的。
大胆假设,小心求证,先试试看吧。
根据电影名词典再次去抓取豆瓣网上的电影信息,这次包含了电影分数::
使用获取到的电影专题『简书电影院』文章内容测试,结果如下:
1048篇中,豆瓣评分结果与模型预测符合的有974篇,准确率是92.9%,WOW!真的这么准吗?
看下详细结果,豆瓣评分是正面的有1022条,预测对了973篇,例如:
而豆瓣评分是负面的有28条记录,而模型只预测出1篇是负面的:
真的是模型预测错了吗?
从负面例子中抽样看看模型分解的结果:
评论《麻烦家族》(豆瓣评分4.8)的:
从自动摘要出来的句子来看,实际上文章作者表现出正面情绪,模型预测为正面,预测正确 😊。
再看评论同一部电影的另外一个文章的评论:
作者显然是给了个差评,但模型给了出的预测是正面,预测错误 😞。
校验“旁证”
负例的预测准确率低,有两个原因,一个是豆瓣评分正负标准的阀值设定不够精准,二是训练数据不平衡。
之前使用的阀值策略是:“简单把14分视作负面评价、510分视作正面评价,没有分数的视作中性评价”。
我猜测在4~5分这个区间应该是灰度地带,正负面评论都会有。为了验证这个猜想,使用获取到的所有豆瓣电影分数,导入数据分析工具中,排除掉没有分数的条目后得到6500+条电影数据,其中最低是2.1,最高是9.7。
查看评分分布的直方图与比例饼图:
调整区间阀值,把2.1-4.9
划分为一个区间,系统自动分成了3组分值:
发现5分以下电影仅占12%。
从评分分布结果看,在简书上大部分人写文章是针对高分(好评)电影为主,低分(差评)电影的文章数量很少,从提取出来的数据量看,两者比率约为100:3。
解决数据不平衡问题
训练数据不平衡导致正例过拟合,而负例欠拟合。
要解决数据不平衡问题,可以重新采样训练集。有两种方法使不平衡的数据集来建立一个平衡的数据集——欠采样和过采样,解释如下:
欠采样:
欠采样是通过减少丰富类的大小来平衡数据集,当数据量足够时就该使用此方法。通过保存所有稀有类样本,并在丰富类别中随机选择与稀有类别样本相等数量的样本,可以检索平衡的新数据集以进一步建模。
过采样:
相反,当数据量不足时就应该使用过采样,它尝试通过增加稀有样本的数量来平衡数据集,而不是去除丰富类别的样本的数量。通过使用重复、自举或合成少数类过采样等方法(SMOTE)来生成新的稀有样品。
由于训练数据不多,我使用了过采样
中的自举方案。我将另外一个简书的电影专题『影视天堂』中模型预测结果与豆瓣评分结果一致的内容作为训练数据集来强化训练模型:
然后再对电影专题『简书电影院』测试一次:
负例的准确率改善了一些些,这说明效果是有的,以后只需继续获取到新的数据再“喂”给模型,模型预测结果会越来越精准。
总结
至此,得到一个不算成熟的电影情绪预测模型,可以用来做一些有趣的统计分析了。
专题分析
将『影视天堂』专题的分析结果在数据分析工具上进行数据可视化展示,得到一个「长尾图」:
从报表中可得到这些信息:
- 最多人关注的影视作品前5是《欢乐颂》、《欢乐颂2》、《神奇女侠》、《白鹿原》和《深夜食堂》
- 最多人喜欢的是《欢乐颂》(豆瓣评分7.3)
- 最多差评的却是《欢乐颂2》(豆瓣评分5.3),看来观众的期望值被第一部放大了很多
注1:严格来说《欢乐颂》不是电影而是电视剧作品,在用豆瓣网的数据构建电影词典时我没有做严格区分
注2:专题的数据获取是在5月份完成的,以及简书专题的最新收录文章有访问限制,无法获取到旧的文章,因此得到的当时热门影视作品的影评,从而呈现出信息跟当时热播作品比较相关
整体用户分析
再对先前获取到的1万个简书用户的近60万篇文章内容,运行相应的Pipeline进行电影情绪数据的提取。
可视化数据后得到图表:
现在可以看看,简书上用户给予好评的电影有哪些,使用「词云图」展示如下:
简书用户给予差评的电影有哪些:
个体用户分析
现在也可以精准了解每一个简书用户的电影口味了,以『毒舌电影 - 简书』 为例子:
注3:本文发表时简书上“毒舌电影”经历过被封事件后已改名为“Sir电影”;简书专题“影视天堂”已改名为“简书电影”
以后做电影个性化推荐可就不要推荐错了 😉。