用于情感分析的 NLTK 和机器学习

本文是使用 Python 和开源自然语言工具包的情感分析系列的第五篇。在本文中,我们正在构建一个优化的机器学习模型。

我们将介绍一些自然语言工具包 (NLTK) 机器学习分类方案。具体来说,我们将使用朴素贝叶斯分类器来探索应用电影评论的特征分析,并学习如何评估准确性。

本系列情感分析的目标是使用 Python 和开源自然语言工具包 (NLTK) 构建一个库,用于扫描对 Reddit 帖子的回复并检测发帖者是否使用负面、敌对或其他不友好的语言。

在统计推理、人工智能和计算机科学的交汇处,机器学习使我们能够查看数据集并获得洞察力。监督学习是我们从一个数据集开始的过程,该数据集将输入与已标记的预期输出进行映射。

相比之下,无监督学习不使用标签,因此通过这种方法,我们需要在评估算法中发挥积极作用,并迭代评估过程以识别对产生标签结果有意义的特征或定义模式。

这个迭代的监督学习过程是什么样的?我们将研究的几个问题包括:

  • 我们试图回答什么问题?对于这个项目,我们想知道用户对我们的品牌或我们在 Reddit 上分享的内容的反应是正面还是负面。
  • 我们有足够的数据吗?在前面的示例中,我们只查看了单个帖子及其评论,因此我们需要组装一个测试工具来检查更多数据,以用于我们的评估和用于训练的数据。
  • 我们可以识别数据集的哪些特征?特征是我们标记以描述数据特征的值。感叹号的使用可能是一个值得观察的特征,因为它可以表示有意义的强烈反应。
  • 我们将如何衡量成功?

这些似乎是个大问题。但是与许多编程问题一样,我们可以将解决方案分解为可实现的步骤,并且可以使用工具来帮助我们一路走来。

我们将首先将我们的语料库分成多个部分或集合:

  • 训练集
  • 测试集
  • 评估集

创建自定义语料库可能是一项艰巨的工作。它需要一个人首先将所有文本组合在一起,然后逐项检查以对其进行注释或标记。例如,人工审阅者会将“这很酷”标记为正面,将“我不推荐”标记为负面。

在进行这项工作之前,值得四处看看是否有可以使用的现有数据集。一旦我们找到一个,我们就会将该数据集拆分为一个训练集和一个测试集。我们将使用训练集来调整算法并构建分类模型。然后我们将使用测试集来判断分类模型的准确性。

我们可以查看如何拆分数据集的不同比率,但 75/25、80/20 或 90/10 拆分很常见。也就是说,对于 1000 条记录,我们可以使用其中的 800 条进行训练,使用 200 条进行测试。

评估集将是我们想要与我们在使用预训练的 VADER 模型进行 NLTK 情绪分析中执行的 VADER 分析进行比较的任何给定的 Reddit 帖子。我们将在这里讨论的内容不需要理解该分析,但它可能很有用。

准备数据以使用朴素贝叶斯分类器进行分析

在为自然语言处理寻找数据的文章中,我们下载并查看了可从 NLTK 获得的电影评论语料库。我们了解到它是一组简单的文本文件,这些文件被分为正值和负值。

我们将再次使用该语料库作为构建朴素贝叶斯分类器的训练集。我们使用它是因为它在 NLTK 中很容易访问,并且当我们将注意力集中在理解监督学习过程上时,很容易理解影评人对电影有正面或负面评论的概念。

我们首先将数据集构造成元组对,其中包含句子列表作为第一个元素,一个简单的字符串标签作为第二个元素来指示正面或负面情绪。Python复制代码

import random
from nltk.corpus import movie_reviews

# return a list of tuple pairs
#   a string of the raw review before tokenization
#   a string label of a pre-classified sentiment ('pos' or 'neg')
def get_labeled_dataset():
    dataset = []
    for label in movie_reviews.categories():
        for review in movie_reviews.fileids(label):
            dataset.append((movie_reviews.raw(review), label))

    random.shuffle(dataset)
    return dataset

请注意,由于这个语料库的结构是正面评论,然后负面评论按顺序聚集在一起,我们将不得不对其进行随机化。如果我们不对其进行随机化,那么所有正面评价都将在训练集中,而所有负面评价都将在测试集中。这种分布会倾斜而不是均匀分布,给我们带来不好的结果。

我们还需要创建一个可以帮助描述数据集的特征字典。我们将花费大部分时间调整这些功能,但我们将从简单的事情开始:使用的字符数。Python复制代码

def get_features(text):
    features = {}

    # Feature #1 - verbosity
    features['verbosity'] = len(text)

    return features

这些特征应该是具有简单类型值的字符串标签键,这很适合 Python 字典。

用朴素贝叶斯分类器分析情绪

有了数据集和一些特征观察,我们现在可以运行分析。我们将从 NLTK 中的朴素贝叶斯分类器开始,它更容易理解,因为它只是假设训练集中具有最高概率的标签的频率可能是最佳匹配。

让我们评估一下结果:Python复制代码

import nltk.classify
from nltk import NaiveBayesClassifier

def evaluate_model(dataset, train_percentage=0.9):
    feature_set = [(get_features(i), label) for (i, label) in dataset]
    count = int(len(feature_set) * train_percentage)
    train_set, test_set = feature_set[:count], feature_set[count:]
    classifier = NaiveBayesClassifier.train(train_set)
    return nltk.classify.accuracy(classifier, test_set)

dataset = get_labeled_dataset()
print(evaluate_model(dataset))
# Output: 0.53

我们获取数据集中的所有评论,并使用get_labeled_dataset前面显示的函数将它们标记为正面或负面。get_features然后,我们使用我们上面定义的函数,将评论替换为识别我们认为可能重要的特征(例如冗长)的字典。

接下来,我们拆分数据集,使用 90% 来训练分类器。我们将剩余的 10% 保存到测试集。复制代码

count = int(len(feature_set) * train_percentage)
train_set, test_set = feature_set[:count], feature_set[count:]

我们在训练数据集上运行朴素贝叶斯分类器。该模型可以查看每个项目中存在的特征并进行猜测。猜测与实际标签匹配的频率越高,我们的模型就越准确。Python复制代码

classifier = NaiveBayesClassifier.train(train_set)
return nltk.classify.accuracy(classifier, test_set)

在这种情况下,准确率为 53%,这不是很令人印象深刻,但评论中使用的字符数似乎也不是一个有用的参考功能。详细程度可能是一个有用的参与功能,但不是情感。

我们在Using Pre-trained VADER Models for NLTK Sentiment Analysis中使用VADER 分析来识别情绪,但现在使用这种方法,我们可以判断这些极性分数在预测情绪时的准确度。

如果评论的 VADER 分数具有积极的强度,我们希望它与人类确定的积极评论的值相匹配。我们可以将此分数作为特征添加到我们的模型中,然后再次运行训练。Python复制代码

analyzer = SentimentIntensityAnalyzer()

def get_features(text):
    features = {}

    # Feature #1 - verbosity
    features['verbosity'] = len(text)

    # Feature #2 and #3 - lexical word choice
    scores = analyzer.polarity_scores(text)
    features['vader(pos)'] = scores['pos']
    features['vader(neg)'] = scores['neg']

    return features

如果我们evaluate_model(dataset)这次使用三个特征运行,我们会看到 62% 的准确度评估。这比仅使用冗长作为一个特征有所改进,但不足以说我们有一个成功的模型来预测电影评论是正面的还是负面的。

接下来我们可以做些什么来提高准确性?

一个有用的方法是查看预测误差。任何被错误识别为正面或负面的电影评论都可以在识别我们应该考虑添加到模型中的其他特征方面提供信息。

分类模型误差分析

此时我们模型的准确率是 62%。我们错了 38% 是怎么回事?我们应该添加一个新功能吗?我们应该尝试不同的分类算法吗?数据有问题吗?

要了解实现更高准确性的后续步骤是一个反复试验的过程。查看我们的分类模型未能正确识别情绪的实例可能会提供有用的信息。如果我们能够识别有助于区分错误分类的测试数据的新特征,则可以分析这些“预测错误”,从而获得关于如何训练模型的新见解。

让我们创建一个我们的evaluate_model()方法的变体来获得一些额外的见解。更改以红色突出显示。Python复制代码

def analyze_model(dataset, train_percentage=0.9):
    feature_set = [(get_features(i), label) for (i, label) in dataset]
    count = int(len(feature_set) * train_percentage)
    train_set, test_set = feature_set[:count], feature_set[count:]
    classifier = NaiveBayesClassifier.train(train_set)
    classifier.show_most_informative_features(5)

    accuracy = nltk.classify.accuracy(classifier, test_set)
    errors = []
    for (text, label) in dataset[count:]:
        guess = classifier.classify(get_features(text))
         
        if guess != label:
            tokens = nltk.word_tokenize(text)
            errors.append((label, guess, tokens[:10]))
    return (accuracy, errors)

当我们使用这种变体时,首先要深入研究的是对show_most_informative_features(). 输出如下所示:复制代码

Most Informative Features
    vader(pos) = 0.099             neg : pos    =      8.9 : 1.0
    vader(neg) = 0.055             pos : neg    =      7.1 : 1.0
    vader(pos) = 0.131             neg : pos    =      6.9 : 1.0
    vader(pos) = 0.176             pos : neg    =      6.4 : 1.0
    vader(pos) = 0.097             neg : pos    =      6.1 : 1.0

这为我们提供了一个在预测结果方面最成功的特征的有序列表。

第一项向我们展示了特征vader(pos),当它的值恰好为 0.099 时,正确预测负特征的可能性是 8.9 倍。

我们可以从这个输出中得出几个结论。

首先,我们在特征中使用了从 0 到 1 的连续值。如果我们使用离散值和理想的二进制,这种类型的算法会更好。我们可以将情绪分数分组到桶中,作为将其表达为特征的一种方式。

其次,值得注意的是,冗长并不是信息量最大的特征。当我们测试关于我们应该在模型中包含哪些特征的假设时,有时我们应该删除没有帮助的特征。

我们对该函数进行了第二次修改,analyze_model()以将错误收集到一个数组中。这将显示预测值和实际值不匹配的项目。它还显示电影评论本身的一个子集。这里有一些例子:Python复制代码

[
('pos', 'neg', ['susan', 'granger', "'s", 'review', 'of', '``', 'hearts', 'in', 'atlantis', '``']), 
('neg', 'pos', ['phil', '(', 'radmar', 'jao', ')', 'has', 'a', 'hairy', 'problem', '.']), 
('pos', 'neg', ['an', 'astonishingly', 'difficult', 'movie', 'to', 'watch', ',', 'the', 'last', 'temptation']), 
('neg', 'pos', ['while', 'watching', 'loser', ',', 'it', 'occurred', 'to', 'me', 'that', 'amy']),
...]

在某些情况下,我们预测的正面评价被标记为负面,而在其他情况下则相反。以红色突出显示的示例被标记为阳性,但我们预测为阴性。这句话,“一部非常难看的电影,最后的诱惑”,听起来像是一个负面评论,尽管它被贴上了正面的标签,所以值得仔细检查。

我们可以在nltk_data/corpora/movie_reviews/pos_cv440_15243中找到完整的评论。该评论包括以下短语:

  • “一部非常难看的电影”
  • “它只是拖到中间,没有真正发生”
  • “不过,这部电影有很多问题点”

在我看来,这充其量只是一个混合评论,并且是一个很好的例子,说明用于训练我们的机器学习模型的数据与最初分配给它的标签一样好。识别具有与您的问题匹配的标签的数据集非常重要。

下一步

构建优化的机器学习模型是一个反复试验的过程,但现在我们有了明确的目标并朝着这个目标迈进。

我们将在使用数据注释改进 NLTK 情感分析中构建我们自己的数据集。

要查看我们之前使用 VADER 和 NLTK 执行 NLP 分析的步骤,请参阅使用预训练的 VADER 模型进行 NLTK 情绪分析。

发表评论

邮箱地址不会被公开。 必填项已用*标注