自然语言工具包 - 文本分类



什么是文本分类?

文本分类,顾名思义,就是对文本或文档进行分类的方法。但这里出现了一个问题,为什么我们需要使用文本分类器?一旦检查了文档或文本中单词的使用情况,分类器就能确定应该为其分配什么类别标签。

二元分类器

顾名思义,二元分类器将在两个标签之间做出决策。例如,正面或负面。在这种情况下,文本或文档要么是一个标签,要么是另一个标签,但不能同时是两个标签。

多标签分类器

与二元分类器相反,多标签分类器可以为文本或文档分配一个或多个标签。

带标签与无标签特征集

特征名称到特征值的键值映射称为特征集。带标签的特征集或训练数据对于分类训练非常重要,以便它以后可以对无标签的特征集进行分类。

带标签的特征集 无标签的特征集
它是一个看起来像 (feat, label) 的元组。 它本身就是一个特征。
它是一个具有已知类别标签的实例。 如果没有关联的标签,我们可以称之为实例。
用于训练分类算法。 训练完成后,分类算法可以对无标签的特征集进行分类。

文本特征提取

文本特征提取,顾名思义,就是将单词列表转换为分类器可用的特征集的过程。我们必须将我们的文本转换为“dict”风格的特征集,因为自然语言工具包 (NLTK) 期望“dict”风格的特征集。

词袋 (BoW) 模型

BoW,是NLP中最简单的模型之一,用于从文本或文档中提取特征,以便在建模中使用,例如在机器学习算法中。它基本上从实例的所有单词构建一个单词存在特征集。这种方法背后的概念是,它不关心一个单词出现的次数或单词的顺序,它只关心该单词是否存在于单词列表中。

示例

对于此示例,我们将定义一个名为 bow() 的函数 -

def bow(words):
   return dict([(word, True) for word in words])

现在,让我们在单词上调用bow()函数。我们将这些函数保存在名为 bagwords.py 的文件中。

from bagwords import bow
bow(['we', 'are', 'using', 'tutorialspoint'])

输出

{'we': True, 'are': True, 'using': True, 'tutorialspoint': True}

训练分类器

在前面的部分中,我们学习了如何从文本中提取特征。因此,现在我们可以训练分类器了。第一个也是最简单的分类器是NaiveBayesClassifier类。

朴素贝叶斯分类器

为了预测给定特征集属于特定标签的概率,它使用贝叶斯定理。贝叶斯定理的公式如下。

$$P(A|B)=\frac{P(B|A)P(A)}{P(B)}$$

这里,

P(A|B) - 它也称为后验概率,即在第二个事件(即B)发生的情况下,第一个事件(即A)发生的概率。

P(B|A) - 它是第一个事件(即A)发生后第二个事件(即B)发生的概率。

P(A), P(B) - 它也称为先验概率,即第一个事件(即A)或第二个事件(即B)发生的概率。

为了训练朴素贝叶斯分类器,我们将使用NLTK中的movie_reviews语料库。此语料库有两个类别的文本,即:posneg。这些类别使在它们上面训练的分类器成为二元分类器。语料库中的每个文件都由两个组成,一个是正面影评,另一个是负面影评。在我们的示例中,我们将使用每个文件作为训练和测试分类器的单个实例。

示例

为了训练分类器,我们需要一个带标签的特征集列表,它将采用[(featureset, label)]的形式。这里的featureset变量是一个dict,标签是featureset的已知类别标签。我们将创建一个名为label_corpus()的函数,它将获取一个名为movie_reviews的语料库,以及一个名为feature_detector的函数,默认为词袋。它将构建并返回一个{label: [featureset]}形式的映射。之后,我们将使用此映射来创建带标签的训练实例和测试实例列表。

import collections

def label_corpus(corp, feature_detector=bow):
   label_feats = collections.defaultdict(list)
   for label in corp.categories():
      for fileid in corp.fileids(categories=[label]):
         feats = feature_detector(corp.words(fileids=[fileid]))
         label_feats[label].append(feats)
   return label_feats

借助上述函数,我们将获得一个{label:fetaureset}映射。现在,我们将定义另一个名为split的函数,它将获取label_corpus()函数返回的映射,并将每个特征集列表拆分为带标签的训练实例和测试实例。

def split(lfeats, split=0.75):
   train_feats = []
   test_feats = []
   for label, feats in lfeats.items():
      cutoff = int(len(feats) * split)
      train_feats.extend([(feat, label) for feat in feats[:cutoff]])
      test_feats.extend([(feat, label) for feat in feats[cutoff:]])
   return train_feats, test_feats

现在,让我们在我们的语料库(即movie_reviews)上使用这些函数 -

from nltk.corpus import movie_reviews
from featx import label_feats_from_corpus, split_label_feats
movie_reviews.categories()

输出

['neg', 'pos']

示例

lfeats = label_feats_from_corpus(movie_reviews)
lfeats.keys()

输出

dict_keys(['neg', 'pos'])

示例

train_feats, test_feats = split_label_feats(lfeats, split = 0.75)
len(train_feats)

输出

1500

示例

len(test_feats)

输出

500

我们已经看到,在movie_reviews语料库中,有1000个pos文件和1000个neg文件。我们最终也得到了1500个带标签的训练实例和500个带标签的测试实例。

现在让我们使用其train()类方法训练NaïveBayesClassifier -

from nltk.classify import NaiveBayesClassifier
NBC = NaiveBayesClassifier.train(train_feats)
NBC.labels()

输出

['neg', 'pos']

决策树分类器

另一个重要的分类器是决策树分类器。在这里,为了训练它,DecisionTreeClassifier类将创建一个树结构。在此树结构中,每个节点对应一个特征名称,分支对应特征值。向下沿着分支,我们将到达树的叶子,即分类标签。

为了训练决策树分类器,我们将使用相同的训练和测试特征,即train_featstest_feats变量,这些变量是我们从movie_reviews语料库中创建的。

示例

为了训练此分类器,我们将调用DecisionTreeClassifier.train()类方法,如下所示 -

from nltk.classify import DecisionTreeClassifier
decisiont_classifier = DecisionTreeClassifier.train(
   train_feats, binary = True, entropy_cutoff = 0.8, 
   depth_cutoff = 5, support_cutoff = 30
)
accuracy(decisiont_classifier, test_feats)

输出

0.725

最大熵分类器

另一个重要的分类器是MaxentClassifier,它也称为条件指数分类器逻辑回归分类器。在这里,为了训练它,MaxentClassifier类将使用编码将带标签的特征集转换为向量。

为了训练决策树分类器,我们将使用相同的训练和测试特征,即train_featstest_feats变量,这些变量是我们从movie_reviews语料库中创建的。

示例

为了训练此分类器,我们将调用MaxentClassifier.train()类方法,如下所示 -

from nltk.classify import MaxentClassifier
maxent_classifier = MaxentClassifier
.train(train_feats,algorithm = 'gis', trace = 0, max_iter = 10, min_lldelta = 0.5)
accuracy(maxent_classifier, test_feats)

输出

0.786

Scikit-learn分类器

Scikit-learn是最好的机器学习 (ML) 库之一。它实际上包含各种用途的各种ML算法,但它们都具有以下相同的拟合设计模式 -

  • 将模型拟合到数据
  • 并使用该模型进行预测

这里,我们不会直接访问scikit-learn模型,而是将使用NLTK的SklearnClassifier类。此类是scikit-learn模型的包装类,使其符合NLTK的Classifier接口。

我们将按照以下步骤训练SklearnClassifier类 -

步骤1 - 首先,我们将像在以前的配方中一样创建训练特征。

步骤2 - 现在,选择并导入Scikit-learn算法。

步骤3 - 接下来,我们需要使用所选算法构建SklearnClassifier类。

步骤4 - 最后,我们将使用我们的训练特征训练SklearnClassifier类。

让我们在下面的Python配方中实现这些步骤 -

from nltk.classify.scikitlearn import SklearnClassifier
from sklearn.naive_bayes import MultinomialNB
sklearn_classifier = SklearnClassifier(MultinomialNB())
sklearn_classifier.train(train_feats)
<SklearnClassifier(MultinomialNB(alpha = 1.0,class_prior = None,fit_prior = True))>
accuracy(sk_classifier, test_feats)

输出

0.885

测量精确率和召回率

在训练各种分类器时,我们也测量了它们的准确率。但是除了准确率之外,还有许多其他指标用于评估分类器。这两个指标是精确率召回率

示例

在此示例中,我们将计算之前训练的NaiveBayesClassifier类的精确率和召回率。为了实现这一点,我们将创建一个名为metrics_PR()的函数,它将有两个参数,一个是训练好的分类器,另一个是带标签的测试特征。这两个参数与我们在计算分类器准确率时传递的参数相同 -

import collections
from nltk import metrics
def metrics_PR(classifier, testfeats):
   refsets = collections.defaultdict(set)
   testsets = collections.defaultdict(set)
   for i, (feats, label) in enumerate(testfeats):
      refsets[label].add(i)
      observed = classifier.classify(feats)
         testsets[observed].add(i)
   precisions = {}
   recalls = {}
   for label in classifier.labels():
   precisions[label] = metrics.precision(refsets[label],testsets[label])
   recalls[label] = metrics.recall(refsets[label], testsets[label])
   return precisions, recalls

让我们调用此函数以查找精确率和召回率 -

from metrics_classification import metrics_PR
nb_precisions, nb_recalls = metrics_PR(nb_classifier,test_feats)
nb_precisions['pos']

输出

0.6713532466435213

示例

nb_precisions['neg']

输出

0.9676271186440678

示例

nb_recalls['pos']

输出

0.96

示例

nb_recalls['neg']

输出

0.478

分类器组合和投票

组合分类器是提高分类性能的最佳方法之一。投票是组合多个分类器的最佳方法之一。对于投票,我们需要有奇数个分类器。在下面的Python配方中,我们将组合三个分类器,即NaiveBayesClassifier类、DecisionTreeClassifier类和MaxentClassifier类。

为了实现这一点,我们将定义一个名为voting_classifiers()的函数,如下所示。

import itertools
from nltk.classify import ClassifierI
from nltk.probability import FreqDist
class Voting_classifiers(ClassifierI):
   def __init__(self, *classifiers):
      self._classifiers = classifiers
      self._labels = sorted(set(itertools.chain(*[c.labels() for c in classifiers])))
   def labels(self):
      return self._labels
   def classify(self, feats):
      counts = FreqDist()
      for classifier in self._classifiers:
         counts[classifier.classify(feats)] += 1
      return counts.max()

让我们调用此函数以组合三个分类器并找到准确率 -

from vote_classification import Voting_classifiers
combined_classifier = Voting_classifiers(NBC, decisiont_classifier, maxent_classifier)
combined_classifier.labels()

输出

['neg', 'pos']

示例

accuracy(combined_classifier, test_feats)

输出

0.948

从上面的输出中,我们可以看到组合分类器的准确率高于单个分类器。

广告