使用NLTK和Scikit-Learn做文本分类【翻译】

963 阅读

原文:http://bbengfort.github.io/tutorials/2016/05/19/text-classification-nltk-sckit-learn.html

作者经常被问到在文本处理中,NLTK或者Scikit-Learn哪一个更好用。作者的回答是他经常混合着使用这些工具。在这篇博客中,作者主要讲述如何使用NLTK来做预处理和序列化和词语切分,然后使用Scikit-Learn处理机器学习任务(如建立线性SVM方法,用梯度下降求解)。作者将描述如何使用NLTK处理一个对电影评论做文本分类的任务。这里,电影评论将被分为正向和负向。

首先请确保本机安装了NLTK和Scikit-Learn。

pip install nltk scikit-learn
python -m nltk.downloader all

同时,作者也会使用一些辅助工具,如timeit、identity等。完整的代码请见sentiment.py。注意,代码中去除了import部分。

Pipelines

使用Scikit-Learn构建机器学习工具的最核心部分是Pipelines。Scikit-Learn给出的关于机器学习中标准API主要由两个接口:Transformer和Estimator。这两个类都有一个fit方法,其作用是基于数据优化内部参数。Transformers然后实现了一个transform方法,用来进行特征抽取或者修改数据,estimators实现了预测方法,用来从特征向量中产生新数据。

Pipelines允许开发者构建一个有序的有向无环图的transformers,并带有一个estimator,来确保特征抽取与预测过程紧密相联。这在文本处理中特别重要,通常这些原始文本是在硬盘上的文档。尽管Scikit-Learn提供了文本处理的相关接口,但是NLTK却是一个更好的自然语言处理的工具。作者个人的文本处理过程通常是如下顺序:

CorpusReader从原始语料中读取数据。Tokenizer将原始文本切分成句子,单词和标点符号。然后使用WordNet词典进行词性标注(part of speech, POS)与词干抽取(lemmatization)。Vectorizer则将文本中的单词进行编码(比如使用TF-IDF)成向量。最终就可以使用分类器训练了。

预处理

为了限制特征的长度,并提供一个高质量的文本表示,作者使用NLTK的高级文本处理机制,包括Punkt切词,Brill标注以及基于WordNet词典的词干抽取。这些操作不仅可以减少词汇,也可以将冗余单词变成一个(比如,bunny,bunnies,Bunny,bunny!,和d _bunny_都可以变成bunny)。

为了把这样的预处理过程加入到Scikit-Learn的处理中,我们创建一个Transformer对象:

import string

from nltk.corpus import stopwords as sw
from nltk.corpus import wordnet as wn
from nltk import wordpunct_tokenize
from nltk import WordNetLemmatizer
from nltk import sent_tokenize
from nltk import pos_tag

from sklearn.base import BaseEstimator, TransformerMixin


class NLTKPreprocessor(BaseEstimator, TransformerMixin):

    def __init__(self, stopwords=None, punct=None,
                 lower=True, strip=True):
        self.lower      = lower
        self.strip      = strip
        self.stopwords  = stopwords or set(sw.words('english'))
        self.punct      = punct or set(string.punctuation)
        self.lemmatizer = WordNetLemmatizer()

    def fit(self, X, y=None):
        return self

    def inverse_transform(self, X):
        return [" ".join(doc) for doc in X]

    def transform(self, X):
        return [
            list(self.tokenize(doc)) for doc in X
        ]

    def tokenize(self, document):
        # Break the document into sentences
        for sent in sent_tokenize(document):
            # Break the sentence into part of speech tagged tokens
            for token, tag in pos_tag(wordpunct_tokenize(sent)):
                # Apply preprocessing to the token
                token = token.lower() if self.lower else token
                token = token.strip() if self.strip else token
                token = token.strip('_') if self.strip else token
                token = token.strip('*') if self.strip else token

                # If stopword, ignore token and continue
                if token in self.stopwords:
                    continue

                # If punctuation, ignore token and continue
                if all(char in self.punct for char in token):
                    continue

                # Lemmatize the token and yield
                lemma = self.lemmatize(token, tag)
                yield lemma

    def lemmatize(self, token, tag):
        tag = {
            'N': wn.NOUN,
            'V': wn.VERB,
            'R': wn.ADV,
            'J': wn.ADJ
        }.get(tag[0], wn.NOUN)

        return self.lemmatizer.lemmatize(token, tag)

这是一大块代码,我们将一个方法一个方法的分析。首先初始化这个Transformer,它可以载入各种语料并进行切分。通过使用NLTK内置的停用词,WordNetLemmatizer将从WordNet字典进行查询。这里可能会需要花点时间。

DataLearner 官方微信

欢迎关注 DataLearner 官方微信,获得最新 AI 技术推送

DataLearner 官方微信二维码