文本分類的研究學習

  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

  1. 數據採集
  2. 特徵提取
  3. 模型訓練
  4. 模型評估

接下來咱們對這4個部分的代碼進行詳細的講解。正則表達式

2.1 數據採集

從上面的代碼中,咱們能夠看到獲取數據很簡單: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,而不是嘗試着從資源網站下載。 測試

2.2 特徵提取

數據採集完成之後,就要開始提取特徵了,咱們這裏使用的是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編碼

2.3 模型訓練

這裏選用多種模型進行訓練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

2.4模型的分析評估

 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()
相關文章
相關標籤/搜索