6.5 文本分类、聚类分析实例

本部分以 NLTK 提供的文本数据为例,构建了电影评论文本情感分类的分类器,对 20 newsgroups 的新闻文本进行了聚类,并对比了不同算法的效果。

6.5.1 电影评论文本情感分类

NLTK 提供的 movie_reviews 电影评价文本数据被分为两类:pos 和 neg,即正向评价和负向评价,属于文本情感分类。在训练过程中我们以每一条电影评论为一个样本,构建一个二元分类器即可。

在调用文本分类算法之前,需要先加载数据,根据不同的工具库分类器要求将文本数据转换为指定形式,并合理划分训练集和测试集。

首先导入文本数据并查看数据基本信息


In [1]:from nltk.corpus import movie_reviews

# 可以通过调用以下方法查看相关文本信息

In [2]:movie_reviews.fileids() # 查看语料文件名称
Out[2]:[u'neg/cv000_29416.txt',
        u'neg/cv001_19502.txt',
        ……]
In [3]:movie_reviews.raw() # 查看具体文本内容
Out[3]:u'plot : two teen couples go to a church party , drink and then drive . …… \nnevertheless , i look forward to niccol\'s next film , whatever it may be . \n'
In [4]:movie_reviews.sents() # 分句查看文本内容
Out[4]:[[u'plot', u':', u'two', u'teen', u'couples', u'go', u'to', u'a', u'church', u'party', u',', u'drink', u'and', u'then', u'drive', u'.'], [u'they', u'get', u'into', u'an', u'accident', u'.'], ...]
In [5]:movie_reviews.categories() # 查看文本具体分类
Out[5]:[u'neg', u'pos']

下面选取不同的工具库、不同的分类算法对以上数据训练分类器,对比分类效果:

6.5.1.1 NLTK 多项朴素贝叶斯分类器 MultinomialNB

利用 NLTK 进行分类首先要将文本数据结构化为 [(featureset, label)] 的形式,可以使用 featx.py 中的 bag_of_words 函数直接标注词汇是否出现,该函数定义如下:


def bag_of_words(words):
    '''
    >>> bag_of_words(['the', 'quick', 'brown', 'fox'])
    {'quick': True, 'brown': True, 'the': True, 'fox': True}
    '''
    return dict([(word, True) for word in words])

利用上述函数对文本数据进行处理,并打乱原有文本顺序,划分训练集和测试集:


In [6]:from featx import bag_of_words
       import random
       labeled_fsets = [(bag_of_words(movie_reviews.words(fileid)), category)
                        for category in movie_reviews.categories()
                        for fileid in movie_reviews.fileids(category)]
       random.shuffle(labeled_fsets)
       train_feats =labeled_fsets[:1500]
       test_feats=labeled_fsets[1500:]
       len(train_feats)
Out[6]:1500
In [7]:len(test_feats)
Out[7]:500

电影评论文本由 1000 个正向评论和 1000 个负向评论组成,我们最终使用其中的 1500 条评论作为训练集,其余的 500 条作为测试集。

下面直接利用 NLTK 工具库 NaiveBayesClassifier 类的 train 方法训练朴素贝叶斯分类器。


In [8]:from nltk.classify import NaiveBayesClassifier
       nb_classifier = NaiveBayesClassifier.train(train_feats)

# 使用 classify 进行分类预测:

In [9]:from featx import bag_of_words
       negfeat = bag_of_words(['the', 'story', 'was', 'boring'])
       nb_classifier.classify(negfeat)
Out[9]:'neg'

# 利用测试集测试分类器精度

In [10]:from nltk.classify.util import accuracy
        accuracy(nb_classifier, test_feats)
Out[10]:0.714

下面采用词频作为特征值进行分类,首先使用 word_tokenize 分词、Counter 统计词频,并将数据转换为 labeled feature sets 形式:


In [11]:from nltk.tokenize import word_tokenize
        from collections import Counter

        word_counter = Counter()

        labeled_fsets = [(dict(Counter(word_tokenize(movie_reviews.raw(fileid)))), category)
                          for category in movie_reviews.categories()
                          for fileid in movie_reviews.fileids(category)]

将转换结果划分为训练集和测试集,并构建分类器,查看分类效果:


In [12]:random.shuffle(labeled_fsets)
        train_feats =labeled_fsets[:1500]
        test_feats=labeled_fsets[1500:]
        nb_classifier = NaiveBayesClassifier.train(train_feats)
        accuracy(nb_classifier, test_feats)
Out[12]:0.774

6.5.1.2 TextBlob 朴素贝叶斯分类器 NaiveBayesClassifier

使用 TextBlob 进行文本分类不需要进行上述复杂的文本分词、计数等预处理,直接传入 [(text,label)] 型数据即可:


In [13]:import textblob
        from textblob.classifiers import NaiveBayesClassifier

        # 构建符合训练格式的文本数据集合 labeled_fsets

        labeled_fsets = [(movie_reviews.raw(fileid), category)
                          for category in movie_reviews.categories()
                          for fileid in movie_reviews.fileids(category)]

        # 划分训练集和测试集
        random.shuffle(labeled_fsets)
        train_feats =labeled_fsets[:1500]
        test_feats=labeled_fsets[1500:]

        # 训练分类模型并利用测试集计算分类准确率

        cl = NaiveBayesClassifier(train_feats)
        cl.accuracy(test_feats)
Out[13]:

6.5.1.3 Pattern KNN

分别以欧式距离和夹角余弦为距离衡量方式,构建 KNN 分类器:


In [14]:import pattern
        from pattern.vector import KNN,Document,EUCLIDEAN,COSINE

        # 构建符合训练格式的文本数据集合 labeled_fsets

        labeled_fsets = [Document(movie_reviews.raw(fileid), type=category)
                         for category in movie_reviews.categories()
                         for fileid in movie_reviews.fileids(category)]

        labeled_fsets=[]

        for f in movie_reviews.fileids():
            fset= movie_reviews.raw(fileids=[f])
            label=movie_reviews.categories(fileids=[f])[0]
            labeled_fsets.append(Document(fset, type=label))

        # 划分训练集和测试集

        train_feats =labeled_fsets[:1500]
        test_feats=labeled_fsets[1500:]

        # 使用欧氏距离训练分类模型并利用测试集计算分类准确率

        cl=KNN(train=train_feats,k=2,distance=EUCLIDEAN)
        cl.test(test_feats)
Out[14]:(0.352, 1.0, 0.352, 0.5207100591715976)

        # 使用夹角余弦训练分类模型并利用测试集计算分类准确率

In [15]:cl=KNN(train=train_feats,k=2,distance=COSINE)
        cl.test(test_feats)
Out[15]:(0.44, 1.0, 0.44, 0.6111111111111112)

虽然两种情况下分类器的准确率均较低,但是夹角余弦的分类效果还是要优于欧式距离。

以上示例在聚类之前都未做严格的停用词过滤、词干化等预处理,读者可以尝试进行适当地预处理,对比分类效果是否有改进。

6.5.2 新闻文本聚类

本部分使用 sklearn 内建函数 fetch_20newsgroups 加载 20 newsgroups 新闻数据集中三类新闻文本,计算 TF-IDF 矩阵作为特征值,使用不同的聚类算法进行聚类。关于 fetch_20newsgroups 的调用方法 6.1 节有详细介绍,此处不做多余说明。

同样,在调用文本聚类算法之前,需要先导入文本数据并查看数据基本信息:


In [1]:import sklearn
       from sklearn.datasets import fetch_20newsgroups
       dataset = fetch_20newsgroups(subset="train",shuffle=True, random_state=1,remove=('headers', 'footers', 'quotes'),categories=['alt.atheism','talk.politics.mideast','talk.religion.misc'])
       dataset
Out[1]:{'DESCR': None,
        'data': [u'\n\nAnas, of course ! …… trying to discredit Israel).",……]
        'description': 'the 20 newsgroups by date dataset',
        'filenames': ……
        'target': array([1, 2, 0, ..., 0, 2, 1]),
        'target_names': ['alt.atheism','talk.politics.mideast','talk.religion.misc']}
In [2]:len(dataset["data"])
Out[2]:1421
In [3]:text=dataset["data"]
       labels_true = dataset["target"]

'alt.atheism'、'talk.politics.mideast'、'talk.religion.misc'三类新闻共 1421 条文本。接下来利用 NLTK cluster 包中的 KMeansClusterer 类对以上文本进行简单的聚类处理:

6.5.2.1 文档词频矩阵聚类

使用 sklearn 库 CountVectorizer 类构建文档词频矩阵 dtm :


In [4]:from sklearn.feature_extraction.text import CountVectorizer
       dtm_vectorizer = CountVectorizer()
       dtm = dtm_vectorizer.fit_transform(text).toarray()

利用上步得到的 dtm 矩阵直接将样本聚合成三类


In [5]:from nltk.cluster import KMeansClusterer
       from sklearn import metrics

# 使用欧式距离进行聚类

In [6]:km=KMeansClusterer(num_means=3,distance=nltk.cluster.util.euclidean_distance)
       km.cluster(dtm)  
       labels_pred = [km.classify(i) for i in dtm]
       metrics.homogeneity_score(labels_true, labels_pred)
Out[6]:0.012559370220130626
In [7]:metrics.completeness_score(labels_true, labels_pred)
Out[7]:0.057515794427500502
In [8]:metrics.adjusted_rand_score(labels_true, labels_pred)
Out[8]:-0.003911994181202576

# 使用夹角余弦距离进行聚类

In [9]:km=KMeansClusterer(num_means=3,distance=nltk.cluster.util.cosine_distance)
       km.cluster(dtm)  
       labels_pred = [km.classify(i) for i in dtm]
       metrics.homogeneity_score(labels_true, labels_pred)
Out[9]:0.02218039873071102
In [10]:metrics.completeness_score(labels_true, labels_pred)
Out[10]:0.026715706061670171
In [11]:metrics.adjusted_rand_score(labels_true, labels_pred)
Out[11]:0.02496855873580896

6.5.2.2 TF-IDF 矩阵聚类

使用 sklearn 库 TfidfVectorizer 类构建 TF-IDF 矩阵:


In [12]:from sklearn.feature_extraction.text import TfidfVectorizer
        vectorizer = TfidfVectorizer(stop_words="english")
        tfidf=vectorizer.fit_transform(text).toarray()

利用上步得到的 tfidf 矩阵直接将样本聚合成三类


# 使用夹角余弦距离进行聚类

In [13]:km=KMeansClusterer(num_means=3,distance=nltk.cluster.util.cosine_distance)
        km.cluster(tfidf)
        labels_pred = [km.classify(i) for i in tfidf]
        metrics.homogeneity_score(labels_true, labels_pred)
Out[13]:0.23932874020793218
In [14]:metrics.completeness_score(labels_true, labels_pred)
Out[14]:0.25465755776723092
In [15]:metrics.adjusted_rand_score(labels_true, labels_pred)
Out[15]:0.30077069169403853

results matching ""

    No results matching ""