機器學習文檔Scikit-learn翻譯(第三章)

3.1 加載20新聞組數(shù)據集

這些文件保存在對象的屬性data中,執(zhí)行tweety.data命令這些數(shù)據就會被加載進來,同樣引用文件名稱filenames也具有同樣的效果,如

>>> len(twenty_train.data)
2257
>>> len(twenty_train.filenames)
2257

現(xiàn)在,讓我們看看第一個被加載進的文件的第一行內容:

>>> print("\n".join(twenty_train.data[0].split("\n")[:3]))
From: sd345@city.ac.uk (Michael Collier)
Subject: Converting images to HP LaserJet III?
Nntp-Posting-Host: hampton
>>> print(twenty_train.target_names[twenty_train.target[0]])
comp.graphics

監(jiān)督學習算法要求在訓練集中的每篇文檔都有對應的類別號。在20newsgroups案例中,類別標號是新聞組 newsgroup的名稱,同時,它還與存放該文件的文件名稱一樣。
出于計算運算速率和空間開銷的考慮,scikit-learn以整數(shù)數(shù)組的形式加載目標屬性,該數(shù)組中元素值與target_names列表中類標名稱的索引是一一對應的關系。每一個樣本的類標都儲存在tweety_train對象的target屬性中。

>>> twenty_train.target[:10]
array([1, 1, 3, 3, 3, 3, 3, 2, 2, 2])

我們通過類標號也可以反向獲取類別名稱:

>>> for t in twenty_train.target[:10]:
... print(twenty_train.target_names[t])
...
comp.graphics
comp.graphics
soc.religion.christian
soc.religion.christian
soc.religion.christian
soc.religion.christian
soc.religion.christian
sci.med
sci.med
sci.med

你可能注意到了,樣本已經被隨機混洗(shuffle)了(偽隨機種子方式),這極大地方便了你選擇第一個樣本集進行快速地訓練數(shù)據模型,同時也方便了在未使用所有數(shù)據集訓練模型情況下對數(shù)據模型總體的認識。

3.2 從文本文件中析取特征變量

為了使機器學習能在文本文檔上工作,我們必須把文本內容轉換成數(shù)值特征向量。

3.2.1 詞袋模型

詞袋模型最直觀表現(xiàn):

  1. 只要訓練數(shù)據集的任意文檔中任意單詞出現(xiàn),就為其分配一個固定值。
  2. 對于每一篇文檔 #i,統(tǒng)計每個單詞W出現(xiàn)的次數(shù),并保存在X[i, j],作為特征 #j的值,j為單詞W在字典中索引。

詞袋中的n_features是語料庫中不同單詞的個數(shù),該數(shù)值常常大于100,000。
如果n_samples=10000,那么以float32類型儲存X數(shù)組需要4GB的內存空間,這在現(xiàn)代計算機上是不可忍受的。
幸運的是,在X中大部分值是0,因為對于給定的任意一篇文檔所使用的不同詞都不會高于上千個。鑒于這個原因,我們有把握說,詞袋是高維稀疏數(shù)據集,我們可以僅僅儲存向量中非0特征值。
scipy.sparse 就是處理高維稀疏矩陣的非常好的數(shù)據結構,并且scikit-learn也有對此結構的內建支持。

3.2.2使用scikit-learn標記文本

高水平組件的操作包含了文本處理、標記和過濾停用詞等操作,這使得很容易構建一個特征字典及把文本轉成特征向量。

>>> from sklearn.feature_extraction.text import CountVectorizer
>>> count_vect = CountVectorizer()
>>> X_train_counts = count_vect.fit_transform(twenty_train.data)
>>> X_train_counts.shape
(2257, 35788)

CountVectorizer支持對使用N元分詞語法分成詞數(shù)的統(tǒng)計(N-grams)或對序列字符數(shù)計數(shù)。一旦對模型擬合后,我們便成功地便構建了特征字典的索引切片。

>>> count_vect.vocabulary_.get(u'algorithm')
4690

詞匯表單詞的索引值與該詞在整個訓練語料庫中出現(xiàn)頻數(shù)相關。

3.2.3 從出現(xiàn)與否到出現(xiàn)頻數(shù)

對單詞作出現(xiàn)與否的統(tǒng)計是一件很好的開始,但是這存在一個問題:對應一篇更長的文檔詞出現(xiàn)次數(shù)的均值總比短文檔要高,即便是在討論同一個話題。
為了避免潛在的差異性,我們統(tǒng)計每個單詞出現(xiàn)次數(shù)與語料庫文檔中單詞總個數(shù)之比,便會生成一個新的特征變量,即頻繁項集:tf。
還需要考慮的問題是,對于一個很高的tf值我們需要降低它在語料庫文檔中出現(xiàn)的權重,tf大代表的信息可能不如僅在語料庫部分文檔中出現(xiàn)次數(shù)較少的單詞代表的信息多,如,“是”、“的”、“我”等詞。
這種操作稱為逆文檔率tf-idf
tftf-idf計算如下:

>>> from sklearn.feature_extraction.text import TfidfTransformer
>>> tf_transformer = TfidfTransformer(use_idf=False).fit(X_train_counts)
>>> X_train_tf = tf_transformer.transform(X_train_counts)
>>> X_train_tf.shape
(2257, 35788)

在上面的實例代碼中,我們首先使用fit函數(shù)來擬合數(shù)據得到估計器,第二,使用transform函數(shù)把統(tǒng)計矩陣轉換成tf-idf表達式。這兩步結合在一起使用也能獲取最終相同的結果,而且速度還要快些,即使用fit_transform函數(shù),下面這個例子與上面等價:

>>> tfidf_transformer = TfidfTransformer()
>>> X_train_tfidf = tfidf_transformer.fit_transform(X_train_counts)
>>> X_train_tfidf.shape
(2257, 35788)

3.3 訓練分類器

現(xiàn)在,我們已經獲取到了文檔詞的特征向量,之后便能訓練分類器來預測一個新的文檔所屬類別。以貝葉斯分類器開始,它提供了很多能夠完成下面任務的基本曲線函數(shù),同時scikit-learn還容納了貝葉斯分類的許多變量參數(shù),其中對文檔單詞處理最適合是multinomial變量。

>>> from sklearn.naive_bayes import MultinomialNB
>>> clf = MultinomialNB().fit(X_train_tfidf, twenty_train.target)

在嘗試預測新文檔輸出結果前,我們需要把前面從訓練樣本中析取特征的操作應用在新文檔上進行析取特征。
transformers變換對象上調用transform方法與調用fit_transform的區(qū)別在于:在前面,我們已經對訓練集樣本進行了擬合操作,現(xiàn)在也就不再需要fit方法擬合了,即只需調用transform方法。

>>> docs_new = ['God is love', 'OpenGL on the GPU is fast']
>>> X_new_counts = count_vect.transform(docs_new)
>>> X_new_tfidf = tfidf_transformer.transform(X_new_counts)
>>> predicted = clf.predict(X_new_tfidf)
>>> for doc, category in zip(docs_new, predicted):
... print('%r => %s' % (doc, twenty_train.target_names[category]))
...
'God is love' => soc.religion.christian
'OpenGL on the GPU is fast' => comp.graphics

3.4 構建pipline管道

為了能使向量對象、變換對象、分類對象在一起工作,scikit-learn提供了Pipeline管道,它工作起來如多個復合分類器對象一樣。

>>> from sklearn.pipeline import Pipeline
>>> text_clf = Pipeline([('vect', CountVectorizer()),
... ('tfidf', TfidfTransformer()),
... ('clf', MultinomialNB()),
... ])

vect、tfidfclf(classifer)名稱是任意取的,我們還能在下面的網格搜索部分再看到它們的身影?,F(xiàn)在,我們使用簡單的命令訓練模型:

>>> text_clf = text_clf.fit(twenty_train.data, twenty_train.target)

3.5 在測試集上對模型評估

評估模型的預測準確度是相當容易的。

>>> import numpy as np
>>> twenty_test = fetch_20newsgroups(subset='test',
... categories=categories, shuffle=True, random_state=42)
>>> docs_test = twenty_test.data
>>> predicted = text_clf.predict(docs_test)
>>> np.mean(predicted == twenty_test.target)
0.834...

還不錯嘛,我們取得了83.4%的準確度。嘗試使用線性支持向量機(SVM)模型是否有更好的結果,該模型廣受好評,是最好的文本分類算法之一,盡管與貝葉斯分類器相比有些不高效。我們可以通過把一個新的不同的分類器對象追加到Pipeline管道對象中達到修改模型學習對象的目的。

>>> from sklearn.linear_model import SGDClassifier
>>> text_clf = Pipeline([('vect', CountVectorizer()),
... ('tfidf', TfidfTransformer()),
... ('clf', SGDClassifier(loss='hinge', penalty='l2',
... alpha=1e-3, n_iter=5, random_state=42)),
... ])
>>> _ = text_clf.fit(twenty_train.data, twenty_train.target)
>>> predicted = text_clf.predict(docs_test)
>>> np.mean(predicted == twenty_test.target)
0.912...

對于更深次的結果分析,scikit-learn提供了很多實用功能,如:

>>> from sklearn import metrics
>>> print(metrics.classification_report(twenty_test.target, predicted,
... target_names=twenty_test.target_names))
...
precision recall f1-score support
alt.atheism 0.95 0.81 0.87 319
comp.graphics 0.88 0.97 0.92 389
sci.med 0.94 0.90 0.92 396
soc.religion.christian 0.90 0.95 0.93 398
avg / total 0.92 0.91 0.91 1502
>>> metrics.confusion_matrix(twenty_test.target, predicted)
array([[258, 11, 15, 35],
[ 4, 379, 3, 3],
[ 5, 33, 355, 3],
[ 5, 10, 4, 379]])

正如預期的一樣,混淆矩陣顯示來自20newsgroupsathesimchristian是更凌亂的與computer graphics相比。

3.6 使用網格搜索優(yōu)化參數(shù)

TfidfTransformer類中內建了很多參數(shù),如use_idf,而且,分類器對象也傾向于使用更多的參數(shù),如,MultinomialNB類包含了一個平滑參數(shù)alpha參數(shù),SGDClasifier也有懲罰項(penalty)參數(shù)alpha和在對象函數(shù)可配置的損失函數(shù)及懲罰項(penalty items)
代替多個組件鏈對參數(shù)的調整操作,通過對網格可能值窮舉來搜索最佳參數(shù)值是可能的。我們使用單一詞或二元分詞語法,并對每個線性SVM分類器賦予0.01或0.001的懲罰項系數(shù)試驗所有的分類器。

>>> from sklearn.grid_search import GridSearchCV
>>> parameters = {'vect__ngram_range': [(1, 1), (1, 2)],
... 'tfidf__use_idf': (True, False),
... 'clf__alpha': (1e-2, 1e-3),
... }

很顯然,這種搜索的運算開銷是巨大的。如果我們有多CPU內核,我們通過對n_jobs參數(shù)設置來告訴網格搜索對象能夠使用的CPU核數(shù),使它嘗試并行計算由八個參數(shù)組成的聯(lián)合體。如果給n_jobs參數(shù)賦值-1,那么機器上所有可用CPU內核將全部投入使用。

>>> gs_clf = GridSearchCV(text_clf, parameters, n_jobs=-1)

網格搜索對象工作方式與scikit-learn其它模型一樣。讓我們看看該網格搜索對象在較小的訓練數(shù)據集中運算速率大小。

>>> gs_clf = gs_clf.fit(twenty_train.data[:400], twenty_train.target[:400])

GridSearchCV對象上調用fit方法的結果是一個分類器對象,我們能夠使用該對象進行預測操作。

>>> twenty_train.target_names[gs_clf.predict(['God is love'])]
'soc.religion.christian'

它是一個相當大、復雜的對象,不過,我們可以通過檢查對象的grid_scores_屬性來獲取最佳的參數(shù),該屬性返回一個參數(shù)與分數(shù)對(parameters/score)的列表。為得到最佳得分的屬性,進行如下操作:

>>> best_parameters, score, _ = max(gs_clf.grid_scores_, key=lambda x: x[1])
>>> for param_name in sorted(parameters.keys()):
... print("%s: %r" % (param_name, best_parameters[param_name]))
...
clf__alpha: 0.001
tfidf__use_idf: True
vect__ngram_range: (1, 1)
>>> score
0.900...

3.6.1 練習

為了練習方便而不破壞原有文件,我們復制'skeletons'目錄里內容到新目錄'workspace'。

% cp -r skeletons workspace

在ipython中執(zhí)行如下命令:

%run workspace/exercise_XX_script.py arg1 arg2 arg3

如果拋出異常,請使用%debug命令進行調試。

擴展

  1. CountVectorizer類中多次嘗試練習analyzer 和1tokennormalisation`操作。
  2. 如果沒有類標號,嘗試使用Clustering
  3. 如果每篇文檔具有多個類標簽,那么翻閱Multiclass and multilabel 部分
  4. 對于隱藏的語義分析,請使用Truncated SVD。
  5. 好好使用 Out-of-core Classification類從不適合存入計算機內存數(shù)據中學習。
  6. 你應該好好看看 與CountVectorizer類相比,儲存效率高的Hashing Vectorizer類。
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

友情鏈接更多精彩內容