1 #!/usr/bin/env python 2 #!-*- coding:utf-8 -*- 3 #!@Author:sjl 4 5 import numpy as np 6 from sklearn.naive_bayes import MultinomialNB, BernoulliNB 7 from sklearn.datasets import fetch_20newsgroups 8 from sklearn.feature_extraction.text import TfidfVectorizer 9 from sklearn.linear_model import RidgeClassifier 10 from sklearn.neighbors import KNeighborsClassifier 11 from sklearn.svm import SVC 12 from sklearn.ensemble import RandomForestClassifier 13 from sklearn.model_selection import GridSearchCV 14 from sklearn import metrics 15 from time import time 16 from pprint import pprint 17 import matplotlib.pyplot as plt 18 import matplotlib as mpl 19 20 21 def test_clf(clf): 22 print(u'分类器:', clf) 23 alpha_can = np.logspace(-3, 2, 10) 24 model = GridSearchCV(clf, param_grid={'alpha': alpha_can}, cv=5) 25 m = alpha_can.size 26 if hasattr(clf, 'alpha'): 27 model.set_params(param_grid={'alpha': alpha_can}) 28 m = alpha_can.size 29 if hasattr(clf, 'n_neighbors'): 30 neighbors_can = np.arange(1, 15) 31 model.set_params(param_grid={'n_neighbors': neighbors_can}) 32 m = neighbors_can.size 33 if hasattr(clf, 'C'): 34 C_can = np.logspace(1, 3, 3) 35 gamma_can = np.logspace(-3, 0, 3) 36 model.set_params(param_grid={'C':C_can, 'gamma':gamma_can}) 37 m = C_can.size * gamma_can.size 38 if hasattr(clf, 'max_depth'): 39 max_depth_can = np.arange(4, 10) 40 model.set_params(param_grid={'max_depth': max_depth_can}) 41 m = max_depth_can.size 42 t_start = time() 43 model.fit(x_train, y_train) 44 t_end = time() 45 t_train = (t_end - t_start) / (5*m) 46 print(u'5折交叉验证的训练时间为:%.3f秒/(5*%d)=%.3f秒' % ((t_end - t_start), m, t_train)) 47 print(u'最优超参数为:', model.best_params_) 48 t_start = time() 49 y_hat = model.predict(x_test) 50 t_end = time() 51 t_test = t_end - t_start 52 print(u'测试时间:%.3f秒' % t_test) 53 acc = metrics.accuracy_score(y_test, y_hat) 54 print(u'测试集准确率:%.2f%%' % (100 * acc)) 55 name = str(clf).split('(')[0] 56 print(name) 57 index = name.find('Classifier') 58 if index != -1: 59 name = name[:index] # 去掉末尾的Classifier 60 if name == 'SVC': 61 name = 'SVM' 62 return t_train, t_test, 1-acc, name 63 64 65 if __name__ == "__main__": 66 print(u'开始下载/加载数据...') 67 t_start = time() 68 # remove = ('headers', 'footers', 'quotes') 69 remove = () 70 categories = ('alt.atheism', 'talk.religion.misc', 'comp.graphics', 'sci.space') 71 # categories = None # 若分类全部类别,请注意内存是否够用 72 data_train = fetch_20newsgroups(subset='train', categories=categories, shuffle=True, random_state=0, remove=remove) 73 data_test = fetch_20newsgroups(subset='test', categories=categories, shuffle=True, random_state=0, remove=remove) 74 t_end = time() 75 print(u'下载/加载数据完成,耗时%.3f秒' % (t_end - t_start)) 76 print(u'数据类型:', type(data_train)) 77 print(u'训练集包含的文本数目:', len(data_train.data)) 78 print(u'测试集包含的文本数目:', len(data_test.data)) 79 print(u'训练集和测试集使用的%d个类别的名称:' % len(categories)) 80 categories = data_train.target_names 81 pprint(categories) 82 y_train = data_train.target 83 y_test = data_test.target 84 print(u' -- 前10个文本 -- ') 85 for i in np.arange(10): 86 print(u'文本%d(属于类别 - %s):' % (i+1, categories[y_train[i]])) 87 print(data_train.data[i]) 88 print('\n\n') 89 vectorizer = TfidfVectorizer(input='content', stop_words='english', max_df=0.5, sublinear_tf=True) 90 x_train = vectorizer.fit_transform(data_train.data) # x_train是稀疏的,scipy.sparse.csr.csr_matrix 91 x_test = vectorizer.transform(data_test.data) 92 print(u'训练集样本个数:%d,特征个数:%d' % x_train.shape) 93 print(u'中止词:\n',) 94 pprint(vectorizer.get_stop_words()) 95 feature_names = np.asarray(vectorizer.get_feature_names()) 96 97 print(u'\n\n===================\n分类器的比较:\n') 98 clfs = (MultinomialNB(), # 0.87(0.017), 0.002, 90.39% 99 BernoulliNB(), # 1.592(0.032), 0.010, 88.54% 100 KNeighborsClassifier(), # 19.737(0.282), 0.208, 86.03% 101 RidgeClassifier(), # 25.6(0.512), 0.003, 89.73% 102 RandomForestClassifier(n_estimators=200), # 59.319(1.977), 0.248, 77.01% 103 SVC() # 236.59(5.258), 1.574, 90.10% 104 ) 105 result = [] 106 for clf in clfs: 107 a = test_clf(clf) 108 result.append(a) 109 print('\n') 110 result = np.array(result) 111 time_train, time_test, err, names = result.T 112 x = np.arange(len(time_train)) 113 mpl.rcParams['font.sans-serif'] = [u'simHei'] 114 mpl.rcParams['axes.unicode_minus'] = False 115 plt.figure(figsize=(10, 7), facecolor='w') 116 ax = plt.axes() 117 b1 = ax.bar(x, err, width=0.25, color='#77E0A0') 118 ax_t = ax.twinx() 119 b2 = ax_t.bar(x+0.25, time_train, width=0.25, color='#FFA0A0') 120 b3 = ax_t.bar(x+0.5, time_test, width=0.25, color='#FF8080') 121 plt.xticks(x+0.5, names, fontsize=10) 122 leg = plt.legend([b1[0], b2[0], b3[0]], (u'错误率', u'训练时间', u'测试时间'), loc='upper left', shadow=True) 123 # for lt in leg.get_texts(): 124 # lt.set_fontsize(14) 125 plt.title(u'新闻组文本数据不一样分类器间的比较', fontsize=18) 126 plt.xlabel(u'分类器名称') 127 plt.grid(True) 128 plt.tight_layout(2) 129 plt.show()
这个案例是一个新闻标题分类的案例,NLPCC 2017 Shared Task也有一个相似的案例。所以咱们先拿这个下手了。整个过程归纳起来分为如下几步:python
接下来咱们对这4个部分的代码进行详细的讲解。正则表达式
从上面的代码中,咱们能够看到获取数据很简单:app
1 print(u'开始下载/加载数据...') 2 t_start = time() 3 # remove = ('headers', 'footers', 'quotes') 4 remove = () 5 categories = ('alt.atheism', 'talk.religion.misc', 'comp.graphics', 'sci.space') 6 # categories = None # 若分类全部类别,请注意内存是否够用 7 data_train = fetch_20newsgroups(subset='train', categories=categories, shuffle=True, random_state=0, remove=remove) 8 data_test = fetch_20newsgroups(subset='test', categories=categories, shuffle=True, random_state=0, remove=remove) 9 t_end = time() 10 print(u'下载/加载数据完成,耗时%.3f秒' % (t_end - t_start)) 11 print(u'数据类型:', type(data_train)) 12 print(u'训练集包含的文本数目:', len(data_train.data)) 13 print(u'测试集包含的文本数目:', len(data_test.data)) 14 print(u'训练集和测试集使用的%d个类别的名称:' % len(categories)) 15 categories = data_train.target_names 16 pprint(categories) 17 y_train = data_train.target 18 y_test = data_test.target 19 print(u' -- 前10个文本 -- ') 20 for i in np.arange(10): 21 print(u'文本%d(属于类别 - %s):' % (i+1, categories[y_train[i]])) 22 print(data_train.data[i]) 23 print('\n\n') 24 vectorizer = TfidfVectorizer(input='content', stop_words='english', max_df=0.5, sublinear_tf=True)
这里最重要的就是这个fetch_20newsgroups
方法了,下面咱们来详细讲解:dom
##函数原型是这样的。 ''' fetch_20newsgroups(data_home=None,subset='train',categories=None,shuffle=True,random_state=42,remove=(),download_if_missing=True) ''' ''' data_home指的是数据集的地址,若是默认的话,全部的数据都会在'~/scikit_learn_data'文件夹下. subset就是train,test,all三种可选,分别对应训练集、测试集和全部样本。 categories:是指类别,若是指定类别,就会只提取出目标类,若是是默认,则是提取全部类别出来。 shuffle:是否打乱样本顺序,若是是相互独立的话。 random_state:打乱顺序的随机种子 remove:是一个元组,用来去除一些停用词的,例如标题引用之类的。 download_if_missing: 若是数据缺失,是否去下载。 '''
另外的一种解释是:函数
fetch_20newsgroups的做用是加载文件名,加载20个新闻群组数据集中的数据
参数:data_home:可选参数,默认值为:None
指定一个电脑中的路径来存储加载的数据。若是选择默认,那全部的scikit-learn数据都存储在'~/scikit_learn_data'这个子文件夹中
subset:'train'或者'test','all',可选参数
选择加载获得的数据集用来作训练仍是作测试,或者是二者都选择,能够随用户须要来选择
categories:空集,或者是字符串集合,或者是unicode码
shuffle:bool布尔类型,可选参数
是否须要打乱数据:这一参数对于一些须要让假设样本数据具备独立同分布的模型来讲相当重要,如随机梯度降低
random_state:numpy随机数产生器,或者是种子整数
主要是用来清洗数据
remove:元组
包含头文件(‘headers’,‘footers’,'‘quotes’)的全部子集。都是重新闻群组帖子中被检测或者是移除的各类各样的文本,防止分类器在利用复杂数据特征属性进行分类过程当中过拟合
'headers'去除新闻的头部数据, 'footers'去除新闻位置最后相似于签名区域的一整块区域,'quotes'移除引用其余新闻帖子的行
'headers'听从一个精确的标准;其余的过滤器不必定一直正确
download_if_missing:可选参数,默认值是:真(True)
若是是Flase, 数据不是本地可获取的就会引发一个IOError,而不是尝试着从资源网站下载。 测试
数据采集完成之后,就要开始提取特征了,咱们这里使用的是TFIDF特征。fetch
1 vectorizer = TfidfVectorizer(input='content', stop_words='english', max_df=0.5, sublinear_tf=True) 2 x_train = vectorizer.fit_transform(data_train.data) # x_train是稀疏的,scipy.sparse.csr.csr_matrix 3 x_test = vectorizer.transform(data_test.data) 4 print(u'训练集样本个数:%d,特征个数:%d' % x_train.shape) 5 print(u'中止词:\n',) 6 pprint(vectorizer.get_stop_words()) 7 feature_names = np.asarray(vectorizer.get_feature_names())
这里最重要的是TfidfVectorizer函数,其解释以下:网站
关于参数:
input:string{'filename', 'file', 'content'}
若是是'filename',序列做为参数传递给拟合器,预计为文件名列表,这须要读取原始内容进行分析
若是是'file',序列项目必须有一个”read“的方法(相似文件的对象),被调用做为获取内存中的字节数
不然,输入预计为序列串,或字节数据项都预计可直接进行分析。
encoding:string, ‘utf-8’by default
若是给出要解析的字节或文件,此编码将用于解码
decode_error: {'strict', 'ignore', 'replace'}
若是一个给出的字节序列包含的字符不是给定的编码,指示应该如何去作。默认状况下,它是'strict',这意味着的UnicodeDecodeError将提升,其余值是'ignore'和'replace'
strip_accents: {'ascii', 'unicode', None}
在预处理步骤中去除编码规则(accents),”ASCII码“是一种快速的方法,仅适用于有一个直接的ASCII字符映射,"unicode"是一个稍慢一些的方法,None(默认)什么都不作
analyzer:string,{'word', 'char'} or callable
定义特征为词(word)或n-gram字符,若是传递给它的调用被用于抽取未处理输入源文件的特征序列
preprocessor:callable or None(default)
当保留令牌和”n-gram“生成步骤时,覆盖预处理(字符串变换)的阶段
tokenizer:callable or None(default)
当保留预处理和n-gram生成步骤时,覆盖字符串令牌步骤
ngram_range: tuple(min_n, max_n)
要提取的n-gram的n-values的下限和上限范围,在min_n <= n <= max_n区间的n的所有值
stop_words:string {'english'}, list, or None(default)
若是未english,用于英语内建的停用词列表
若是未list,该列表被假定为包含停用词,列表中的全部词都将从令牌中删除
若是None,不使用停用词。max_df能够被设置为范围[0.7, 1.0)的值,基于内部预料词频来自动检测和过滤停用词
lowercase:boolean, default True
在令牌标记前转换全部的字符为小写
token_pattern:string
正则表达式显示了”token“的构成,仅当analyzer == ‘word’时才被使用。两个或多个字母数字字符的正则表达式(标点符号彻底被忽略,始终被视为一个标记分隔符)。
max_df: float in range [0.0, 1.0] or int, optional, 1.0 by default
当构建词汇表时,严格忽略高于给出阈值的文档频率的词条,语料指定的停用词。若是是浮点值,该参数表明文档的比例,整型绝对计数值,若是词汇表不为None,此参数被忽略。
min_df:float in range [0.0, 1.0] or int, optional, 1.0 by default
当构建词汇表时,严格忽略低于给出阈值的文档频率的词条,语料指定的停用词。若是是浮点值,该参数表明文档的比例,整型绝对计数值,若是词汇表不为None,此参数被忽略。
max_features: optional, None by default
若是不为None,构建一个词汇表,仅考虑max_features--按语料词频排序,若是词汇表不为None,这个参数被忽略
vocabulary:Mapping or iterable, optional
也是一个映射(Map)(例如,字典),其中键是词条而值是在特征矩阵中索引,或词条中的迭代器。若是没有给出,词汇表被肯定来自输入文件。在映射中索引不能有重复,而且不能在0到最大索引值之间有间断。
binary:boolean, False by default
若是未True,全部非零计数被设置为1,这对于离散几率模型是有用的,创建二元事件模型,而不是整型计数
dtype:type, optional
经过fit_transform()或transform()返回矩阵的类型
norm:'l1', 'l2', or None,optional
范数用于标准化词条向量。None为不归一化
use_idf:boolean, optional
启动inverse-document-frequency从新计算权重
smooth_idf:boolean,optional
经过加1到文档频率平滑idf权重,为防止除零,加入一个额外的文档
sublinear_tf:boolean, optional
应用线性缩放TF,例如,使用1+log(tf)覆盖tf编码
这里选用多种模型进行训练spa
1 clfs = (MultinomialNB(), # 0.87(0.017), 0.002, 90.39% 2 BernoulliNB(), # 1.592(0.032), 0.010, 88.54% 3 KNeighborsClassifier(), # 19.737(0.282), 0.208, 86.03% 4 RidgeClassifier(), # 25.6(0.512), 0.003, 89.73% 5 RandomForestClassifier(n_estimators=200), # 59.319(1.977), 0.248, 77.01% 6 SVC() # 236.59(5.258), 1.574, 90.10% 7 ) 8 result = [] 9 for clf in clfs: 10 a = test_clf(clf) 11 result.append(a) 12 print('\n') 13 result = np.array(result) 14 time_train, time_test, err, names = result.T 15 x = np.arange(len(time_train))
1 def test_clf(clf): 2 print(u'分类器:', clf) 3 alpha_can = np.logspace(-3, 2, 10) 4 model = GridSearchCV(clf, param_grid={'alpha': alpha_can}, cv=5) 5 m = alpha_can.size 6 if hasattr(clf, 'alpha'): 7 model.set_params(param_grid={'alpha': alpha_can}) 8 m = alpha_can.size 9 if hasattr(clf, 'n_neighbors'): 10 neighbors_can = np.arange(1, 15) 11 model.set_params(param_grid={'n_neighbors': neighbors_can}) 12 m = neighbors_can.size 13 if hasattr(clf, 'C'): 14 C_can = np.logspace(1, 3, 3) 15 gamma_can = np.logspace(-3, 0, 3) 16 model.set_params(param_grid={'C':C_can, 'gamma':gamma_can}) 17 m = C_can.size * gamma_can.size 18 if hasattr(clf, 'max_depth'): 19 max_depth_can = np.arange(4, 10) 20 model.set_params(param_grid={'max_depth': max_depth_can}) 21 m = max_depth_can.size 22 t_start = time() 23 model.fit(x_train, y_train) 24 t_end = time() 25 t_train = (t_end - t_start) / (5*m) 26 print(u'5折交叉验证的训练时间为:%.3f秒/(5*%d)=%.3f秒' % ((t_end - t_start), m, t_train)) 27 print(u'最优超参数为:', model.best_params_) 28 t_start = time() 29 y_hat = model.predict(x_test) 30 t_end = time() 31 t_test = t_end - t_start 32 print(u'测试时间:%.3f秒' % t_test) 33 acc = metrics.accuracy_score(y_test, y_hat) 34 print(u'测试集准确率:%.2f%%' % (100 * acc)) 35 name = str(clf).split('(')[0] 36 print(name) 37 index = name.find('Classifier') 38 if index != -1: 39 name = name[:index] # 去掉末尾的Classifier 40 if name == 'SVC': 41 name = 'SVM' 42 return t_train, t_test, 1-acc, name
1 mpl.rcParams['font.sans-serif'] = [u'simHei'] 2 mpl.rcParams['axes.unicode_minus'] = False 3 plt.figure(figsize=(10, 7), facecolor='w') 4 ax = plt.axes() 5 b1 = ax.bar(x, err, width=0.25, color='#77E0A0') 6 ax_t = ax.twinx() 7 b2 = ax_t.bar(x+0.25, time_train, width=0.25, color='#FFA0A0') 8 b3 = ax_t.bar(x+0.5, time_test, width=0.25, color='#FF8080') 9 plt.xticks(x+0.5, names, fontsize=10) 10 leg = plt.legend([b1[0], b2[0], b3[0]], (u'错误率', u'训练时间', u'测试时间'), loc='upper left', shadow=True)
#此处由于b1返回的是BarContainer object of 6 artists,也就是有6个直方图,任选其一便可,这里也能够用b[1]等 11 # for lt in leg.get_texts(): 12 # lt.set_fontsize(14) 13 plt.title(u'新闻组文本数据不一样分类器间的比较', fontsize=18) 14 plt.xlabel(u'分类器名称') 15 plt.grid(True) 16 plt.tight_layout(2) 17 plt.show()