【火炉炼AI】机器学习039-NLP文本分类器

【火炉炼AI】机器学习039-NLP文本分类器

(本文所使用的Python库和版本号: Python 3.6, Numpy 1.14, scikit-learn 0.19, matplotlib 2.2, NLTK 3.3)

前面我们学习了很多用NLP进行文本的分词,文本分块,创建词袋模型等,这些步骤可以认为是NLP文本处理的基础,此处我们来看NLP的一个非常重要的应用,对文本使用监督学习进行自动分类。


1. 20 Newsgroups数据集介绍

本文要使用NLP中非常经典的一个数据集:20 Newsgroups。这个数据集是国际标准数据集之一,专门用于文本分类,文本挖掘,和信息检索等领域,类似于传统机器学习所用的Iris鸢尾花数据集一样,可以通过官方网址来下载和了解具体内容。

20 Newsgroups包含有20个类别的已经标注好的样本,总样本数量大约2万。这20个类别分别为:

image

这个数据集有三个版本,其主要内容和区别为:

  1. 20news-19997.tar.gz: 最原始的没有修改过的一个版本。
  2. 20news-bydate.tar.gz: bydate版本,按照时间分类,分为训练集(60%)和测试集(40%)两部分,不包含重复文档和新闻组名。一共有18846个样本(或称为文档)
  3. 20news-18828.tar.gz: 不包含重复样本,只有来源和主题,一共有18828个样本。

sklearn中有两种加载方式,第一种是sklearn.dataset.fetch_20newsgroups,返回一个可以被文本特征提取器(CountVectorizer)自定义参数提取特征的原始文本序列,第二种是sklearn.datasets.fetch_20newsgroups_vectorized,返回一个已提取特征的文本序列,即不需要使用特征提取器。

注:以上内容主要参考博客

此处我们只下载第二个版本,下载后得到20news-bydate.tar.gz文件,解压后得到两个文件夹,如下:

image

这两个文件夹每一个都有20个子文件夹,对应于20个不同类别。每个类别下面有几百个文档,即样本,每个文档都不长,比如一个文档(样本)的内容为:

image

这个数据集的加载方式已经被sklearn集成到代码中了,主要的接口是sklearn.dataset.fetch_20newsgroups,其默认加载第二个版本。这个函数的参数有:subset有三个选择train、test、all,选择数据的类型。category是选择新闻的类型,remove是可以选择去除(‘headers’, ‘footers’, ‘quotes’)这三个文章的选项。其他不重要。

# 认识20newsgroups数据集
from sklearn.datasets import fetch_20newsgroups
# dataset=fetch_20newsgroups(subset='all') 
# 自动下载第二个版本20news-bydate.tar.gz
# print(len(dataset.data)) # dataset_X 的样本数
# print(dataset.target_names) # dataset_y的名称,标签名称

# train_set=fetch_20newsgroups(subset='train') # 仅仅提取中间的train set
# test_set=fetch_20newsgroups(subset='test')

# 如果仅仅需要其中的某几个类别,可以用
sample_cate = ['alt.atheism', 'soc.religion.christian',
               'comp.graphics', 'sci.med', 'rec.sport.baseball'] # 只取5个类别
train_set = fetch_20newsgroups(subset='train',categories=sample_cate,
                               shuffle=True, random_state=42,
                               remove = ('headers', 'footers', 'quotes'))
test_set = fetch_20newsgroups(subset='test', categories=sample_cate,
                              shuffle=True, random_state=42,
                              remove = ('headers', 'footers', 'quotes'))
print(len(train_set.data), len(test_set.data)) # 2854 1899
print(train_set.target_names) # 只有五个类别

-------------------------------------输---------出--------------------------------

2854 1899
['alt.atheism', 'comp.graphics', 'rec.sport.baseball', 'sci.med', 'soc.religion.christian']

--------------------------------------------完-------------------------------------


2. 构建分类器

2.1 准备数据集

# 1, 准备数据集
category_map = {'misc.forsale': 'Sales', 'rec.motorcycles': 'Motorcycles', 
        'rec.sport.baseball': 'Baseball', 'sci.crypt': 'Cryptography', 
        'sci.space': 'Space'}
from sklearn.datasets import fetch_20newsgroups
train_set=fetch_20newsgroups(subset='train',categories=category_map.keys(),
                             shuffle=True,random_state=42,
                            remove = ('headers', 'footers', 'quotes'))
test_set=fetch_20newsgroups(subset='test',categories=category_map.keys(),
                             shuffle=True,random_state=42,
                           remove = ('headers', 'footers', 'quotes'))
# 获取到的train_set包含有2968个样本,
print('train sample num: ', len(train_set.data)) # 2968
print(train_set.target_names) # 确保是我们要提取的这五个类别

print('test sample num: ', len(test_set.data)) # 1975

-------------------------------------输---------出--------------------------------

train sample num: 2968
['misc.forsale', 'rec.motorcycles', 'rec.sport.baseball', 'sci.crypt', 'sci.space']
test sample num: 1975

--------------------------------------------完-------------------------------------

2.2 特征提取

此处我们用TfidfVectorizer来进行特征提取,关于TfidfVectorizer可以参考我的上一篇文章【火炉炼AI】机器学习038-NLP创建词袋模型.

直接上代码:

from sklearn.feature_extraction.text import TfidfVectorizer
vectorizer = TfidfVectorizer(stop_words='english',lowercase=True)
train_vector = vectorizer.fit_transform(train_set.data)
print(train_vector.shape) # (2968, 31206)
# 此处相当于有2968个词袋,对这些词袋进行TfidfVectorizer进行特征提取,
# 得到最具典型的一些单词,这些单词的个数有31206个,故而得到(2968, 30206)矩阵
# 矩阵中的元素表示这个单词在该词袋中出现的tf-idf权重,值越大,表示该单词越重要。

2.3 定义模型,训练模型

# 定义模型,训练特征
from sklearn.naive_bayes import MultinomialNB
classifier=MultinomialNB(alpha=.01, fit_prior = False)
classifier.fit(train_vector,train_set.target)

2.4 查看模型在测试集上的表现

# 查看这个数据集在test_set上的表现
from sklearn import metrics
test_vector=vectorizer.transform(test_set.data)
print(test_vector.shape)
pred=classifier.predict(test_vector)
F1_score=metrics.f1_score(test_set.target, pred, average='micro')
print('test set F1 score: ',F1_score)

-------------------------------------输---------出--------------------------------

(1975, 31206)
test set F1 score: 0.8774683544303797

--------------------------------------------完-------------------------------------


3. 用GridSearch优化参数

# 用GridSearchCV优化参数
from sklearn.model_selection import GridSearchCV
from sklearn.metrics import classification_report

parameters = {'fit_prior':(True, False), 'alpha':(0.01,0.05,0.1,0.2,0.3,0.4,0.5,0.6,0.7,0.8,0.9,1.0)}
clf = GridSearchCV(classifier,parameters,cv=5,scoring='precision_macro',n_jobs=-1)
clf.fit(train_vector, train_set.target)
print("Best param_set found on train set: {}".format(clf.best_params_))

print("Detailed classification report on test set:")
y_true, y_pred = test_set.target, clf.predict(test_vector)
print(classification_report(y_true, y_pred))

-------------------------------------输---------出--------------------------------

Best param_set found on train set: {'alpha': 0.05, 'fit_prior': True}
Detailed classification report on test set:
precision recall f1-score support

      0       0.92      0.89      0.91       390
      1       0.80      0.91      0.85       398
      2       0.93      0.88      0.91       397
      3       0.90      0.88      0.89       396
      4       0.91      0.88      0.89       394

avg / total 0.89 0.89 0.89 1975

--------------------------------------------完-------------------------------------

从分类报告中可以看出,结果最好的是第0类和第2类,F1为0.91,最差的是第1类,F1值只有0.85。

########################小**********结###############################

1,用NLP进行文本分类,和传统机器学习的主要区别在于前面特征的提取,一旦提取特征,后面模型的建立,训练,测试,分类报告等都一样。

2,对文本进行特征的提取有两种:CountVectorizer和TfidfVectorizer,但是TfidfVectorizer使用的最多,对文本量非常大的情况更加准确,故而此处我只用TfidfVectorizer来提取特征。

3,有一个地方很容易忽视:测试集在用predict之前,一定要用vectorizer.transform进行转换,这个过程就像是对数据进行归一化等,需要对train_X和test_X都要进行处理。

#################################################################


注:本部分代码已经全部上传到(我的github)上,欢迎下载。

参考资料:

1, Python机器学习经典实例,Prateek Joshi著,陶俊杰,陈小莉译

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 156,069评论 4 358
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 66,212评论 1 287
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 105,912评论 0 237
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,424评论 0 202
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 51,741评论 3 285
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,194评论 1 206
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,553评论 2 307
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,289评论 0 194
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 33,923评论 1 237
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,251评论 2 240
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,775评论 1 255
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,144评论 2 249
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,698评论 3 228
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 25,936评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,658评论 0 192
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,214评论 2 267
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,159评论 2 258

推荐阅读更多精彩内容