跳至主要内容

Python 情感分析教程

本教程将帮助您用 Python 化繁为简地进行情感分析。您将学习如何用 Python 构建自己的情感分析分类器,并理解 NLP(自然语言处理)的基础。
· 15分钟 阅读

使用 AI 探索

机器学习的承诺已在众多领域展现出令人惊叹的成果。自然语言处理也不例外,这是机器学习在一些真正复杂的任务上能够部分展现出通用人工智能能力并取得出色结果的领域之一。

当然,NLP(自然语言处理)并不是新领域,机器学习也不是。但这两个领域的融合却相当当下,并且必将持续推进。这属于一种混合应用,几乎每个人(只要有一部预算级智能手机)每天都会遇到。比如“键盘词语联想”或智能自动补全;这些都是 NLP 与机器学习融合的产物,自然而然地,已成为我们生活中不可分割的一部分。

情感分析是 NLP 领域中的一个关键主题。由于其高度相关性以及它正在解决的大量业务问题,它很快就成为了该领域最热门的话题之一。在本教程中,您将以不那么简单的主题,用简单的方式来讲解。我们会拆解背后的数学直觉,并深入理解;在教程结尾,您还将构建一个简单的情感分类器。具体而言,您将学习:

  • 从实践者视角理解情感分析
  • 情感分析的问题表述
  • 用于情感分析的朴素贝叶斯分类
  • 一个 Python 案例研究
  • 情感分析如何影响多个业务场景
  • 更多进阶阅读

我们开始吧。

Sentiment Analysis Diagram 来源:Medium

什么是情感分析——实践者视角:

本质上,情感分析或情感分类属于文本分类任务的大类:您会得到一个短语或一组短语,分类器需要判断其背后的情感是正面、负面还是中性。有时为了保持二分类,第三种属性(中性)不会被纳入。最近的任务中,也会考虑“有点正面”和“有点负面”等细分情绪。我们用一个例子来理解。

请看以下短语:

  1. "Titanic is a great movie."
  2. "Titanic is not a great movie."
  3. "Titanic is a movie."

这些短语对应简短的电影评论,每一个都传达了不同的情绪。例如,第一个短语表达了对电影《泰坦尼克号》的正面情绪,而第二个则认为这部电影并不那么好(负面情绪)。仔细看第三个短语。里面没有任何一个词能告诉您它传达了怎样的情绪。因此,这是一个中性情绪的例子。

从严格的机器学习角度看,这个任务不过是一个监督学习任务。您将一组短语(带有各自情绪标签)提供给机器学习模型,然后在未标注的短语上测试模型。

对于初步介绍情感分析,上述内容已经足够,但要真正构建一个情感分类模型,您还需要更多。继续往下。

Movie Reviews Negative and Positive 来源:SlideShare

情感分析的问题表述:

在理解情感分类任务的问题表述之前,您需要先对通用的文本分类问题有清晰认识。我们先形式化定义一下通用文本分类任务的问题。

  • 输入:- 一个文档 d - 一个固定的类别集合 C = {c1,c2,..,cn}

  • 输出:一个预测类别 c $\in$ C

这里的文档一词具有主观性,在文本分类领域中,文档可以指推文、短语、新闻片段、整篇新闻、完整文章、产品手册、故事等。之所以使用这个术语,是因为相对于原子单位的词而言,文档是较长的词序列。为指代更长的词序列,通常使用文档一词。推文可视为短文档,而文章则是长文档。

因此,一个包含 n 个标注文档的训练集可以表示为:(d1,c1), (d2,c2),...,(dn,cn),最终输出是一个学得的分类器。

您做得很好!但此时您一定会问,文档的特征在哪里?这个问题很关键!我们稍后会讲到。

现在继续问题表述,逐步建立对情感分类的直觉。

进行情感分析时,一个关键点是:并非短语中的所有词都能传达该短语的情感。像“I”“Are”“Am”之类的词并不传递任何情绪,因此在情感分类语境中并无相关性。这里可以借用特征选择的思路。在特征选择中,您试图找出与类别标签最相关的特征。同样的理念在这里也适用。因此,只有少数词参与其中,识别并从短语中提取它们是一项具有挑战性的任务。但别担心,我们会讲到。

请看以下电影评论来更好理解:

"I love this movie! It's sweet, but with satirical humor. The dialogs are great and the adventure scenes are fun. It manages to be romantic and whimsical while laughing at the conventions of the fairy tale genre. I would recommend it to just about anyone. I have seen it several times and I'm always happy to see it again......."

是的,这无疑是一条对某部电影持正面情绪的评论。但到底哪些具体词语体现了这种积极性?

再看一遍这条评论。

"I love this movie! It's sweet, but with satirical humor. The dialogs are great and the adventure scenes are fun. It manages to be romantic and whimsical while laughing at the conventions of the fairy tale genre. I would recommend it to just about anyone. I have seen it several times and I'm always happy to see it again......."

现在您应该一目了然了。上述文本中加粗的词,是构成文本所传达正面情绪的关键词。

那这些词要如何处理?下一步自然而然是创建如下所示的表示:

positive words table

上述表示在做什么?您已经猜到了。每一行包含一个词及其在文档中的出现频次(从现在起我们就称其为文档)。您或许会疑惑,love 只出现了一次,为什么频次是 2?这是整段评论的一部分。请将该表示理解为对应整条评论。

在表述情感分类问题时,您已经了解了“词袋”表示,而上面的表示正是一个Bag-of-words表示。这大概是 NLP 中最基础的概念之一,也是进行任何文本分类问题的第一步。请务必理解透彻。

一个文档的bag-of-words表示不仅包含特定词,而是文档中所有唯一词及其出现频次。这里的“袋”是数学上的集合,按照集合定义,不包含重复词。

但在这个应用中,您只对前面提到的加粗词感兴趣,因此该文档的词袋只包含这些词。

文档并不是乱序写就的,对吗?文档中的词序很重要。但在情感分类的语境中,词序并不是特别重要。更重要,或者说最重要的是这些词是否出现。

词袋中找出的这些词将构成文档的特征集。设想您有大量电影评论(文档),并为每个文档构建了词袋表示,同时保留其标签(即本例中的情绪:正或负)。那么您的训练集应如下所示:

Bag of words representations

这种表示也称为语料库(Corpus)。

这个训练集应该易于理解——

每一行都是独立的特征向量,包含某个特定文档(电影评论)、相应词语及其情绪标签的信息。注意,标签sentiment常记作(+,-)或(+ve,-ve)。同时,特征 w1、w2、w3、34、...、wn 来自词袋,并不要求所有文档都包含这些特征/词。

接下来,您会将这些特征向量传给分类器。我们来学习下一部分——用于情感分类的朴素贝叶斯分类模型。

用于情感分析的朴素贝叶斯分类:

朴素贝叶斯分类就是应用贝叶斯法则来形成分类概率。本节将从情感分类的语境来学习朴素贝叶斯分类器。强烈建议先了解一下朴素贝叶斯分类和贝叶斯法则。相关资源如下:

可是在有 k-NN、决策树等众多方法的世界里,为什么用朴素贝叶斯?我们稍后再谈。

先在情感分类的语境下,建立起朴素贝叶斯分类器中的一般术语概念。先看贝叶斯法则:

  • 对于一个文档 d 和类别 c:

Bayes rule

来源:Sentiment Analysis

在这里,类别包含两种情绪:正面和负面。

我们来逐一理解上图中的各个术语在本语境中的含义。

  • 右侧项 P(c|d) 读作“在给定文档 d 的条件下类别 c 的概率”。该项也称为后验。
  • P(d|c) 的读法类似。

那么,什么是先验和似然?还有 P(d)(文档的概率)这个项;听起来是否有些奇怪?好问题!我们来找答案!

  • 图中标为Prior的项是您的原始信念,即文档情绪为正或负(就情感而言)的原始标签。
  • Likelihood项是给定类别 c 的条件下,出现文档 d 的概率。
  • 可以将后验理解为通过先验与似然相乘后得到的更新规则或更新信念。
  • 那么归一化常数 P(d) 是什么?将结果除以该项,是为了确保输出可以表示为一个概率分布。

目前为止还不是最细节的解释!但请继续,您会发现更多信息。记住,我们仍在建立将贝叶斯法则与情感分类关联起来的直觉。

我们更深入一点,看看贝叶斯法则在这里到底在做什么。下图展示了更详细的步骤:

Detailed Bayes Rule

来源:Sentiment Analysis

这里有不少陌生术语。慢慢来。

先从右侧项 cMAP 开始。它表示贝叶斯法则在此的主要目标,即找出某个文档属于特定类别的最大后验概率/估计。MAP 是 Max A Posteriori 的缩写。

argmax 是什么?为什么不用 max?

  • argmax 返回的是索引(对应的类别)。假设 P(+|d) > P(-|d),其中 + 和 - 分别表示正面和负面情绪。这些 P(+|d)、P(-|d) 是数值化的概率。但您并不关心具体概率值,而是关心哪个类别对应的概率更大,argmax 就返回这个类别。对于 P(+|d) > P(-|d),argmax 将返回 +。

此外,您可以省略分母项 P(d)。是否保留完全取决于实现。

但我该如何求 $P(d|c)$ 和 $P(c)$?这正是词袋大显身手的地方。怎么用?

继续读!

您已经知道如何将文档转换为词袋表示。更重要的是,您可以用它将文档表示为一组特征。因此,本质上 cMAP 可以写成(忽略分母项 P(d)):

cMAP term formula

来源:Sentiment Analysis

但是,概率到底怎么计算?我们先从 $P(c)$ 开始。

P(c) 关注这样一个问题:“该类别出现的频率有多高?”比如,您的文档数据集中 60% 为正面情绪,40% 为负面情绪。则 $P(+) = 0.6$,$P(-) = 0.4$。

现在,如何理解这个项:P(x1, x2,...,xn | c)?

可以这样想——在给定类别 c 的条件下,这些词(特征)共同出现的概率是多少。比如,您有 1000 篇文档,语料中只有两个词——“good”和“awesome”。在这 1000 篇文档中,500 篇标注为正面,剩下 500 篇为负面。同时,您发现 500 篇正面文档中,有 200 篇同时包含“good”和“awesome”(注意 P(x1,x2) 表示 P(x1 且 x2))。因此,P(good,awesome | +) = 200 / 1000 = 1/5。

这里有个重要点:如果您的词汇表大小为 $X$,而文档包含 n 个词,那么在理论上您需要计算Xn种似然(例如 P(good,awesome | +))概率。

记住,您需要对两个类别都计算似然。因此,如果总词汇 2000,每篇文档平均含 20 个词,那么组合总数就是 (2000)20。这个数字大得离谱!如果语料规模以百万计(实践中确实会发生),怎么办?

这就是贝叶斯分类器。但它不可行,因为计算量过于庞大。接下来,我们通过一些假设将贝叶斯分类器化简为朴素贝叶斯分类器。

这些假设称为朴素贝叶斯独立性假设,如下:

P(x1, x2,...,xn | c)

- 词袋假设:假设词的位置不重要。假设某个词出现在第 10 个和第 20 个位置,根据该假设,您只关心它出现的频次为 2。10 和 12 这两个位置数字在这里无关紧要。

- 条件独立假设:这是将贝叶斯分类器变为朴素贝叶斯的关键假设。它表述为“假设特征概率 P(xi|cj) 彼此独立”。仔细体会这句话。它意味着 P(x1|cj)、P(x2|cj) 等彼此独立。(并不意味着 P(x1)、P(x2) 等彼此独立)现在,项 P(x1, x2,...,xn | c) 可以表示为:Conditional independence assumption

来源:Sentiment Analysis

因此,自然地,Xn 个组合被化简为 Xn,数量呈指数级减少(当词汇表大小为 $X$ 且文档包含 n 个词时)。数学地说,贝叶斯分类器化简为朴素贝叶斯分类器后形式如下:

Naive Bayes classifier

来源:Sentiment Analysis

朴素贝叶斯有两个优点:

  • 参数数量减少。
  • 时间复杂度从指数级降为线性级。

当朴素贝叶斯分类机制应用于文本分类问题时,通常称为“多项式朴素贝叶斯”分类。

现在,您已经能够很好地理解朴素贝叶斯分类器的机制,尤其是在情感分类问题上的应用。接下来,是时候实现一个情感分类器了。

我们将在 Python 中完成!现在开始案例研究。

用 Python 构建一个简单的情感分类器:

在本案例中,您将使用 NLTK 书中介绍的离线电影评论语料库,可从此处下载。nltk提供了该数据集的一个版本,数据集中每条评论被标注为正面或负面。您需要先按如下方式下载:

python -m nltk.downloader all

不建议从 Jupyter Notebook 运行。请尝试从命令行(Windows 请用命令提示符)运行。下载需要一些时间,请耐心等待。

有关 NLTK 数据集的更多信息,请访问此链接。

您将使用NLTK(Natural Language Toolkit,自然语言工具包)实现朴素贝叶斯,或更确切地说是多项式朴素贝叶斯分类器。这是一个专注于 NLP 和 NLU 相关任务的库,文档非常完善,覆盖了许多技术并提供免费数据集用于实验。

这里是 NLTK 的官方网站。务必看看,里面有不少写得很好的 NLP 教程,涵盖不同的 NLP 概念。

在数据下载完成后,先通过 from nltk.corpus import movie_reviews 导入电影评论数据集。然后,构建一个文档列表,并标注上相应的类别。

# Load and prepare the dataset
import nltk
from nltk.corpus import movie_reviews
import random

documents = [(list(movie_reviews.words(fileid)), category)
              for category in movie_reviews.categories()
              for fileid in movie_reviews.fileids(category)]

random.shuffle(documents)

接下来,您将为文档定义一个特征提取器,让分类器知道应关注数据的哪些方面。“在本例中,您可以为每个词定义一个特征,用于指示文档是否包含该词。为了限制分类器需要处理的特征数量,您首先构建整个语料库中最常见的 2000 个词的列表”来源。然后定义一个特征提取器,简单检查这些词是否出现在给定文档中。

# Define the feature extractor

all_words = nltk.FreqDist(w.lower() for w in movie_reviews.words())
word_features = list(all_words)[:2000]

def document_features(document):
    document_words = set(document)
    features = {}
    for word in word_features:
        features['contains({})'.format(word)] = (word in document_words)
    return features

“之所以计算文档中所有词的集合 document_words = set(document),而不是直接在列表中检查词是否存在,是因为在集合中检查成员关系比在列表中检查要快得多”——来源。

特征提取器已定义。现在,您可以用它来训练朴素贝叶斯分类器,以预测新的电影评论的情绪。为评估分类器表现,您将计算其在测试集上的准确率。NLTK 提供 show_most_informative_features() 来查看分类器认为最有信息量的特征。

# Train Naive Bayes classifier
featuresets = [(document_features(d), c) for (d,c) in documents]
train_set, test_set = featuresets[100:], featuresets[:100]
classifier = nltk.NaiveBayesClassifier.train(train_set)
# Test the classifier
print(nltk.classify.accuracy(classifier, test_set))
0.71

太棒了!在没有调任何参数或做任何微调的情况下,分类器就取得了 71% 的准确率。对第一次尝试而言,这已经很不错!

# Show the most important features as interpreted by Naive Bayes
classifier.show_most_informative_features(5)
Most Informative Features
       contains(winslet) = True              pos : neg    =      8.4 : 1.0
     contains(illogical) = True              neg : pos    =      7.6 : 1.0
      contains(captures) = True              pos : neg    =      7.0 : 1.0
        contains(turkey) = True              neg : pos    =      6.5 : 1.0
        contains(doubts) = True              pos : neg    =      5.8 : 1.0

“在该数据集中,提及 "Illogical" 的评论几乎比正面更可能为负面近 8 倍,而提及 "Captures" 的评论大约更可能为正面 6 倍”——来源。

现在的问题——为什么选朴素贝叶斯?

  • 我们选择学习朴素贝叶斯,是因为它的设计与实现方式。文本数据有一些实际且精妙的特性,在不考虑神经网络的前提下,与朴素贝叶斯的匹配度很高。此外,它易于解释,不会产生“黑箱模型”的感觉。

朴素贝叶斯也有一定劣势:

朴素贝叶斯的主要限制是对特征独立性的假设。在现实中,几乎不可能获得一组完全相互独立的特征。

为什么情感分析如此重要?

情感分析解决了许多真实的业务问题:

  • 有助于预测某一产品的客户行为。
  • 可用于测试产品的适配性。
  • 自动化客户偏好报告的生成。
  • 可以轻松自动化评估一部电影的票房走向:通过分析来自多个平台的影评情绪加以判断。
  • 等等!

下一步:

恭喜您坚持到最后!NLP 是一个非常广博而有趣的领域,正在解决一些颇具挑战的问题。尤其是 NLP 与深度学习的交叉,催生了一系列令人惊叹的产品,彻底革新了聊天机器人的交互方式。列表多不胜数。

本教程希望能为您在 NLP 的核心子领域之一——情感分析——提供一个良好的起点。您学习了 NLP 中最基础的话题之一——词袋模型,并详细研究了朴素贝叶斯分类器,也审视了它的局限。您使用了 nltk(最受欢迎的 Python NLP 与 NLU 库之一),并基于其提供的电影语料实现了一个简单的朴素贝叶斯分类器。为自己鼓掌,您当之无愧!

如果您想从这个朴素的开始更进一步,以下是一些出色的资源链接:

本教程参考了以下资料:

如果您有兴趣学习 NLP 基础并将其应用于真实世界数据集,欢迎参加 DataCamp 的 “Python 自然语言处理基础”课程。

主题