sklearn做自然語言處理-1

sklearn做自然語言處理-1

來自專欄愛上數據科學4 人贊了文章

歡迎關注

公眾號:大鄧和他的Python

今天我們以20newsgroups數據集為例,藉助sklearn庫來進行話題分類(文本分類)。在本篇教程中涉及到:

  • 讀取數據
  • 數據預處理
  • 特徵抽取
  • 模型評估

1. 讀取數據

為了便於分析,已將數據集保存到csv文件中,這樣我們可以使用強大的pandas庫操作數據。

import pandas as pddataset = pd.read_csv(20news-18828.csv, header=None, delimiter=,, names=[label, text])dataset.head()

檢查數據集的類別數和數據量(記錄數)

#注意: dataset.label等同於 dataset[label]print("數據集有20類: %s" % (len(dataset.label.unique()) == 20))print("數據集一共有18828條記錄: %s" % (len(dataset) == 18828))

運行結果

數據集有20類: True數據集一共有18828條記錄: True

做機器學習,我們一般需要將數據集分為訓練集和測試集。過去和現在的數據可以作為訓練集去訓練模型,而未來的數據按道理是不可能出現在訓練集中的,所以一般測試集我們可以理解成未知的或者未來的數據,是用來檢驗我們模型的數據。

這裡使用sklearn.modelselection中的traintest_split函數。

train_test_split(X, y, test_size)

X: 一般為矩陣,矩陣的行數與y的長度一致

y: 一般為一維數組。

test_size: 將數據集的多少比例劃分為測試集

from sklearn.model_selection import train_test_split#依次導入X, y, test_sizeX_train, X_test, y_train, y_test = train_test_split(dataset.text, dataset.label, test_size=0.3)

2. CountVectorizer和TfidfVectorizer

CountVectorizer和TfidfVectorizer是文本特徵提取的兩種方法。兩者的主要區別在於,CountVectorizer僅僅通過計算詞語詞頻,沒有考慮該詞語是否有代表性。而TfidfVectorizer可以更加精準的表徵一個詞語對某個話題的代表性。

對CountVectorizer和TfidfVectorizer還是不熟悉的童鞋,可先戳進去閱讀這篇文章。

from sklearn.feature_extraction.text import CountVectorizercv = CountVectorizer()#fit_transform是fit和transform兩種方法的簡寫#fit方法用於構建特徵空間(也就是構建詞典)#transform方法使用該空間將文本數據轉化為特徵矩陣X_train_counts = cv.fit_transform(X_train)#該矩陣共有13179行,對應著13179條數據。 #175237表示特徵空間共有175237個詞語。X_train_counts.shape

運行結果

(13179, 175237)

訓練集其實主要是學會數據中的特徵空間(構建詞典),之後我們需要用學到的特徵空間去處理測試集。所以我們只需要用到transform方法,從而將測試集數據從文本數據轉化為特徵矩陣。

X_test_counts = cv.transform(X_test)#這裡你會發現,測試集特徵矩陣的列數與訓練集特徵矩陣的列數是一致的,都是175237X_test_counts.shape

運行結果

(5649, 175237)

之後,我們使用樸素貝葉斯分類器去學習訓練集數據,從而訓練好模型。之後我們使用訓練好的模型去對測試集進行預測。

from sklearn.naive_bayes import MultinomialNBestimator = MultinomialNB()#這裡是有監督的訓練模型#所以要同時輸入X_train_counts和y_trainestimator.fit(X_train_counts, y_train)#訓練好的模型,對測試集數據進行預測predicted = estimator.predict(X_test_counts)# 這裡我們僅僅列印前10個測試數據的預測 及其真實類別for prediction, truth in zip(predicted[:10], y_test[:10]): print(prediction, truth)

運行結果

soc.religion.christian soc.religion.christianalt.atheism talk.religion.misctalk.politics.mideast talk.politics.mideastrec.sport.baseball misc.forsaletalk.politics.mideast talk.politics.mideastcomp.sys.ibm.pc.hardware comp.os.ms-windows.misctalk.politics.misc talk.politics.miscsci.crypt sci.crypttalk.politics.mideast talk.politics.mideastsci.med sci.med

但是上面用肉眼看這10條數據還可以,但是測試集有5649條,我們不可能用肉眼去逐個對比的方法去檢驗訓練的模型表現情況。好在sklearn為我們提供了檢驗方法,sklearn.metrics中含有分類報告函數classification_report()

from sklearn.metrics import classification_reportfrom sklearn.metrics import precision_recall_fscore_support#按照分類labels分類輸出準確率召回率F1值print(classification_report(y_test, predicted, labels=dataset.label.unique()))

運行結果

precision recall f1-score support alt.atheism 0.87 0.89 0.88 262 comp.graphics 0.63 0.89 0.74 297 comp.os.ms-windows.misc 1.00 0.13 0.23 302 comp.sys.ibm.pc.hardware 0.61 0.86 0.72 286 comp.sys.mac.hardware 0.90 0.86 0.88 266 comp.windows.x 0.79 0.85 0.82 286 misc.forsale 0.94 0.63 0.75 295 rec.autos 0.92 0.88 0.90 303 rec.motorcycles 0.98 0.92 0.95 282 rec.sport.baseball 0.98 0.95 0.96 293 rec.sport.hockey 0.97 0.98 0.97 296 sci.crypt 0.71 0.98 0.83 258 sci.electronics 0.90 0.78 0.84 305 sci.med 0.94 0.94 0.94 296 sci.space 0.91 0.91 0.91 314 soc.religion.christian 0.69 0.97 0.81 297 talk.politics.guns 0.83 0.97 0.89 278 talk.politics.mideast 0.81 0.95 0.88 283 talk.politics.misc 0.88 0.85 0.87 256 talk.religion.misc 0.98 0.41 0.57 194 avg / total 0.86 0.83 0.82 5649

3. 預處理 和 特徵抽取

可能大家都知道,在文本數據分詞後,其中混雜著很多無意義的停止詞,這些詞語在構建特徵矩陣過程中我們要剔除掉。那麼在構建特徵矩陣其實還有一些需要注意的,比如有時候數字也是很重要的特徵,這時候我們可能需要使用正則表達式對這些數字進行匹配並用如NUM這種詞語去代替。下面我們看一個例子

test = [p2p,me2,just4you,4 k+,500 pounds,300pound]

test中實際上有兩種數字, 第一種是象徵詞語的如p2p 、me2、just4you

第二種就是我們需要去抽取的數字特徵 如4 k+、 500 pounds、 300pound

那麼我們怎麼提取第二種這種真正的數字特徵呢,防止第一種假的」數字「混入數字特徵中

import retest = [p2p,me2,just4you,4 k+,500 pounds,300pound]print(我們先使用d+去匹配看看結果)print([re.findall(rd+, t) for t in test])#發現該正則表達式能滿足我們需要print(r再用d+試試)print([re.findall(rd+, t) for t in test]) print(將數字特徵統一替換為NUM)print([re.sub(rd+, NUM,t) for t in test])我們先使用d+去匹配看看結果[[2], [2], [4], [4], [500], [300]] 再用d+試試[[], [], [], [4], [500], []] 將數字特徵統一替換為NUM[p2p, me2, just4you, NUM k+, NUM pounds, 300pound]

定義我們數字特徵的預處理函數

import redef normalize_numbers(s): """ 將數字特徵統一替換為NUM """ return re.sub(rd+, NUM, s)#CountVectorizer和TfidfVectorizer都有preprocessor參數,該參數傳入預處理函數。#也含有停用詞處理參數stop_wordscv = CountVectorizer(preprocessor=normalize_numbers, stop_words=english)

我們再重複之前的CountVectorizer,重點知識多重複

#fit_transform是fit和transform兩種方法的簡寫#fit方法用於構建特徵空間(也就是構建詞典)#transform方法使用該空間將文本數據轉化為特徵矩陣X_train_counts = cv.fit_transform(X_train)#用學到的特徵空間去對測試集數據進行轉換為特徵矩陣X_test_counts = cv.transform(X_test)

現在我們再使用貝葉斯分類器訓練數據,看看模型是否得到改善。結果發現發現好像整體未得到改善,

estimator = MultinomialNB()estimator.fit(X_train_counts, y_train)predicted = estimator.predict(X_test_counts)print(classification_report(y_test, predicted, labels=dataset.label.unique()))

運行結果

precision recall f1-score support alt.atheism 0.87 0.89 0.88 262 comp.graphics 0.63 0.89 0.74 297 comp.os.ms-windows.misc 1.00 0.13 0.23 302 comp.sys.ibm.pc.hardware 0.61 0.86 0.72 286 comp.sys.mac.hardware 0.90 0.86 0.88 266 comp.windows.x 0.79 0.85 0.82 286 misc.forsale 0.94 0.63 0.75 295 rec.autos 0.92 0.88 0.90 303 rec.motorcycles 0.98 0.92 0.95 282 rec.sport.baseball 0.98 0.95 0.96 293 rec.sport.hockey 0.97 0.98 0.97 296 sci.crypt 0.71 0.98 0.83 258 sci.electronics 0.90 0.78 0.84 305 sci.med 0.94 0.94 0.94 296 sci.space 0.91 0.91 0.91 314 soc.religion.christian 0.69 0.97 0.81 297 talk.politics.guns 0.83 0.97 0.89 278 talk.politics.mideast 0.81 0.95 0.88 283 talk.politics.misc 0.88 0.85 0.87 256 talk.religion.misc 0.98 0.41 0.57 194

既然CountVectorizer即使使用了stop_words和預處理數字特徵仍然改善不大(沒有任何改善),那麼我們再試試TfidfVectorizer這種更能體現詞語代表性的特徵抽取方法。

from sklearn.feature_extraction.text import TfidfVectorizer# preprocessor預處理數據tv = TfidfVectorizer(preprocessor=normalize_numbers, stop_words=english)X_train_tf = tv.fit_transform(X_train)X_test_tf = tv.transform(X_test)estimator2 = MultinomialNB()estimator2.fit(X_train_tf, y_train)predicted = estimator2.predict(X_test_tf)print(classification_report(y_test, predicted, labels=dataset.label.unique()))

運行結果

precision recall f1-score support alt.atheism 0.88 0.83 0.85 262 comp.graphics 0.88 0.83 0.85 297 comp.os.ms-windows.misc 0.84 0.85 0.85 302 comp.sys.ibm.pc.hardware 0.74 0.83 0.78 286 comp.sys.mac.hardware 0.87 0.92 0.90 266 comp.windows.x 0.93 0.87 0.90 286 misc.forsale 0.90 0.74 0.81 295 rec.autos 0.94 0.92 0.93 303 rec.motorcycles 0.94 0.95 0.95 282 rec.sport.baseball 0.95 0.97 0.96 293 rec.sport.hockey 0.95 0.99 0.97 296 sci.crypt 0.76 0.98 0.85 258 sci.electronics 0.94 0.81 0.87 305 sci.med 0.97 0.94 0.95 296 sci.space 0.93 0.96 0.95 314 soc.religion.christian 0.65 0.98 0.79 297 talk.politics.guns 0.79 0.98 0.88 278 talk.politics.mideast 0.94 0.97 0.95 283 talk.politics.misc 0.99 0.68 0.80 256 talk.religion.misc 1.00 0.27 0.42 194 avg / total 0.89 0.87 0.87 5649

wow,之前使用CountVectorizer是0.86,現在我們使用TfidfVectorizer將準確率提高到0.89。

4. 模型選擇

根據數據特點和分析任務需求特點,某些分類器可能會比其他的分類器效果更好。最近最大熵(Maximum Entropy)很火,能應用到很多機器學習任務中。注意,最大熵其實就是LogisticRegression,該演算法比樸素貝葉斯慢很多。

from sklearn.linear_model import LogisticRegressioncv = CountVectorizer()X_train_counts = cv.fit_transform(X_train)X_test_counts = cv.transform(X_test)LR = LogisticRegression()LR.fit(X_train_counts, y_train)predicted = LR.predict(X_test_counts)print(classification_report(y_test, predicted, labels=dataset.label.unique()))

運行結果

precision recall f1-score support alt.atheism 0.90 0.91 0.90 262 comp.graphics 0.82 0.84 0.83 297 comp.os.ms-windows.misc 0.85 0.87 0.86 302 comp.sys.ibm.pc.hardware 0.78 0.79 0.78 286 comp.sys.mac.hardware 0.88 0.89 0.89 266 comp.windows.x 0.89 0.87 0.88 286 misc.forsale 0.83 0.87 0.85 295 rec.autos 0.92 0.91 0.92 303 rec.motorcycles 0.96 0.93 0.94 282 rec.sport.baseball 0.92 0.96 0.94 293 rec.sport.hockey 0.97 0.98 0.97 296 sci.crypt 0.96 0.96 0.96 258 sci.electronics 0.85 0.86 0.86 305 sci.med 0.91 0.94 0.92 296 sci.space 0.94 0.95 0.95 314 soc.religion.christian 0.90 0.95 0.93 297 talk.politics.guns 0.92 0.93 0.93 278 talk.politics.mideast 0.98 0.95 0.96 283 talk.politics.misc 0.95 0.84 0.89 256 talk.religion.misc 0.91 0.78 0.84 194 avg / total 0.90 0.90 0.90 5649

哇,LogisticRegression使用 CountVectorizer準確率就已經達到0.9。

最後,我們使用Kmeans演算法做一個聚類分析。

注意聚類分析得出的結果是需要人去解讀。

from sklearn.cluster import KMeansimport numpy as npfrom sklearn.model_selection import train_test_splitfrom sklearn.feature_extraction.text import CountVectorizerfrom sklearn import metricsfrom sklearn import preprocessing#random_state是隨機狀態碼,這樣保證下次運行與這次運行結果一致X_train, X_test, y_train, y_test = train_test_split(dataset.text, dataset.label, test_size=0.3, random_state=100)# 這裡我主動將特徵空間壓縮到200維,也就是只要兩百個詞語作為特徵詞cv = CountVectorizer(max_features=200)X_train_counts = cv.fit_transform(X_train)X_test_counts = cv.transform(X_test)#因為知道數據集是20類,我們將聚類數n_clusters設為20#random_state是隨機狀態碼,這樣保證下次運行與這次運行結果一致kmeans = KMeans(n_clusters=20, random_state=100)kmeans.fit(X_train_counts)#查看所有訓練集中所有數據對應的聚類標籤print(kmeans.labels_)print(kmeans.predict(X_test_counts))

運行結果

[19 19 19 ... 19 7 19][ 7 7 19 ... 19 8 19]

5. 交叉檢驗cross-validation

實際上之前我們只是將數據集分為訓練集和測試集,並且測試集佔比30%。這就導致一個問題,我們沒有充分利用測試集的數據,導致可供訓練的數據變少。為了能夠充分利用數據,又能對訓練進行測試。這裡有一種方法叫做交叉檢驗。

交叉檢驗是將dataset分成k等份的subsets後,每次取1份subset作為testset,其餘k-1份作為trainingset。

from sklearn.model_selection import KFoldfrom sklearn.naive_bayes import MultinomialNBfrom sklearn.metrics import precision_recall_fscore_supportdataset = pd.read_csv(20news-18828.csv, header=None, delimiter=,, names=[label, text])X = dataset.texty = dataset.label#cross-validation 打亂順序,5等分。kf = KFold(n_splits=5, shuffle=True)for train_index, test_index in kf.split(X): X_train, X_test, y_train, y_test = X[train_index], X[test_index], y[train_index], y[test_index] cv = CountVectorizer() X_train_counts = cv.fit_transform(X_train) X_test_counts = cv.transform(X_test) clf = MultinomialNB() clf.fit(X_train_counts, y_train) predicted = clf.predict(X_test_counts) p, r, f1, _ = precision_recall_fscore_support(y_test, predicted, average=macro) print("
準確率: {0}, 召回率: {1}, F1值: {2}".format(p, r, f1))

運行結果

準確率: 0.8713883483238088, 召回率: 0.8444331487200941, F1值: 0.8381792939819347 準確率: 0.8531060729419465, 召回率: 0.8312135932152993, F1值: 0.8191609558805071 準確率: 0.8635337763830291, 召回率: 0.8327561380347246, F1值: 0.8179305639933958 準確率: 0.863773840212702, 召回率: 0.8366519217276238, F1值: 0.8242302926326295 準確率: 0.8691118498586106, 召回率: 0.8422442830086692, F1值: 0.8300984911679719

6. 總結

本文好像不難吧,認真看下來相信大家能使用sklearn做一些有趣的數據分析。之前分享過的數據,大家可以用來玩玩。

數據及代碼獲取

關注公眾號:大鄧和他的Python

回復關鍵詞: "nlp1"

即可獲得數據和notebook代碼,方便您事後學習和實驗。

往期文章

100G Python學習資料:從入門到精通! 免費下載

在校大學生如何用知識月入3000

上百G文本數據集等你來認領|免費領取

為什麼你要為2019,而不是2018做計劃?

2017年度15個最好的數據科學領域Python庫

初識K-means演算法

機器學習|八大步驟解決90%的NLP問題

使用sklearn做自然語言處理-1

複雜嵌套字典數據結構處理庫-glom

讀取pdf和docx文件,親測有效

如何從文本中提取特徵信息?

對於中文,nltk能做哪些事情

留在網上的每個字,都在泄露你的身份

優雅簡潔的列表推導式

Get小技巧等分列表

如何對數據進行各種排序?

【視頻講解】Scrapy遞歸抓取簡書用戶信息

美團商家信息採集神器

用chardect庫解決網頁亂碼問題

gevent:非同步理論與實戰

輕盈高效的非同步訪問庫grequests庫

selenium驅動器配置詳解

爬蟲神器PyQuery的使用方法

簡易SQLite3資料庫學習

Python通過字元串調用函數

Python圈中的符號計算庫-Sympy

Python中處理日期時間庫的使用方法

推薦閱讀:

tableau 製作waterfall chart
python分析信用卡反欺詐(下)——兩種採樣方法解決數據不平衡及效果分析、模型調參示例
決策易支招之如何在大數據時代佔領高地
udacity優達學城數據分析學習筆記一
好看的圖表千篇一律,能交互的可視化數據萬里挑一

TAG:自然語言處理 | Python | 數據分析 |