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

📅  最后修改于: 2020-10-14 09:29:37             🧑  作者: Mango


什么是文字分类?

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

二进制分类器

顾名思义,二进制分类器将在两个标签之间决定。例如,正面或负面。在这种情况下,文本或文档可以是一个标签,也可以是另一个标签,但不能同时是两者。

多标签分类器

与二进制分类器相反,多标签分类器可以将一个或多个标签分配给一段文本或文档。

标记与未标记功能集

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

Labeled Feature Set Unlabeled Feature Set
It is a tuple that look like (feat, label). It is a feat itself.
It is an instance with a known class label. Without associated label, we can call it an instance.
Used for training a classification algorithm. Once trained, classification algorithm can classify an unlabeled feature set.

文字特征提取

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

言语袋(BoW)模型

BoW是NLP中最简单的模型之一,用于从文本或文档中提取特征,以便可以将其用于建模(例如ML算法)中。它基本上从实例的所有单词构造单词存在功能集。该方法背后的概念是,它不关心单词出现的次数或单词的顺序,仅关心天气单词在单词列表中是否存在。

对于此示例,我们将定义一个名为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) -这是第二个事件(即B)在第一个事件(即A)发生之后发生的概率。

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

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

对于训练分类器,我们需要一个带标签的功能集列表,形式为[( featuresset,label )]。这里的功能集的变量是一个字典和标签是用于FEATURESET的已知类别的标签。我们要创建一个名为label_corpus()函数将采取文集命名movie_reviews,也是一个函数叫feature_detector,默认为文字包。它将构造并返回形式为{label:[featureset]}的映射。之后,我们将使用此映射来创建带有标签的训练实例和测试实例的列表。

进口馆藏

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学习分类器

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

  • 使模型适合数据
  • 并使用该模型进行预测

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

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

步骤1-首先,我们将像以前的食谱一样创建训练功能。

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

步骤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)

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

从上面的输出中,我们可以看到组合分类器比单个分类器具有最高的准确性。