Sklearn使用者手冊(三): 文字資料的處理
本次內容:用一堆的法子去處理二十個不同主題下的文件。(資料集:newsgroup posts)
前文為關於安裝的balabala,反正我直接下了個anaconda套裝。
載入資料集
資料集名稱為“Twenty Newsgroups”,關於該資料集的描述:
20 Newsgroups資料集包含了近20000篇新聞文稿,被分成20個不同的主題,最早由Ken Lang為他自己的論文所收集,如今經常被拿來測試機器學習模型,如文字分類或聚類
該資料集可手動下載,然後用sklearn.datasets.load_files函式匯入,不過這裡我們直接載入它
from sklearn.datasets import fetch_20newsgroups
為了加快訓練速度,第一個例子中,只取其中四個主題的文件
categories = ['alt.atheism', 'soc.religion.christian', 'comp.graphics', 'sci.med']
#取訓練集,並隨機排序,另。。時間稍長
twenty_train = fetch_20newsgroups(subset='train', categories=categories, shuffle=True, random_state=42)
之所以要重新排序,是方便你希望拿一個小樣本出來快速驗證一下自己的模型時,不至於從頭一取大部分都是同一類裡的。
返回值是一個類似於字典的objects
print twenty_train.target_names
print len(twenty_train.data),len(twenty_train.filenames)
#還可以看看第一個,全部列印就算了。。
print "\n".join(twenty_train.data[0].split("\n")[:3])
#看看第一條資料的類別
print twenty_train.target_names[twenty_train.target[0]]
提取特徵
要讓電腦可以去處理文字,那就得把每個文字表示成一個可計算可比較可量化的東西,也就是數值型別的特徵向量。對哪一類的資料都是如此。
詞袋模型
這應該是最基本的一種方式了,
初始化一個m*n維的X矩陣,就是模型輸入值。m為文件的數量,n為下文中詞典的長度。
1. 統計任一文件中出現的詞,組合成一個詞典b,其實就是這所有文件的詞彙量彙總了,並且為每個詞編號。
2. 對每個文件,檢視詞典b中的第j個詞在文件中是否出現,出現了,則把出現的次數記錄在X[i, j]中
由此帶來的一個問題是,這個矩陣往往會非常大,如果是20000篇文件中有10000個不同的詞,則所佔空間為20000*10000*4bytes為8GB RAM。這個一般個人電腦玩不轉的。
但是所幸這個矩陣裡有很多零值,因為單就一篇文件而言,詞彙量還是很有限的。於是大家們就引入了稀疏矩陣的儲存方式來解決這個問題。關於此處可見scipy.sparse.
最簡單的一個例子:
import numpy as np
from scipy.sparse import csr_matrix
# row記錄行號,col記錄列號,data為值。如都看陣列的第一個元素,0行1列的值為1,以此類推
row = np.array([0, 1, 1, 2, 3, 3])
col = np.array([1, 0, 2, 3, 0, 1])
data = np.array([1, 2, 1, 1, 2, 3])
X = csr_matrix((data, (row, col)), shape=(4, 4))
print X
標記文字
首先,sklearn中有CountVectorizer函式可以直接得到上述文件的稀疏模型
from sklearn.feature_extraction.text import CountVectorizer
count_vect = CountVectorizer()
X_train_counts = count_vect.fit_transform(twenty_train.data)
#看algorithm裡詞彙表的長度
print count_vect.vocabulary_.get(u'algorithm')
根據詞典庫以及文件中詞語的分佈我們可以判斷矩陣X的哪個位置是有值的了,不過值取多少卻要視情形而定。
- 二值特徵,即只關心有還是沒有,非0即1。聽著不太合理,但事實說,有時候效果還不錯。
- 出現了幾次值為幾。這就牽涉到了一個問題,如果某一文件本身較長,那它啥詞都會顯的更多,因此改用詞出現的頻率,就是出現的次數除以文件總詞數啦。也就是常稱的tf(term frequencies).
但這也不算完,所謂能體現主題的關鍵詞。那當然是露面次數較少的。這樣的詞資訊量才大。什麼時候都說好其實就相當於什麼都沒說。照如此想,似乎又跟熵的理念比較接近。好吧,回到正題,因此針對這種情況,我們還可以給tf加個權重,即總文件數量與該詞出現的文件數量之比。也就是常說的idf(inverse document frequency),當然這個idf的演算法又有很多改進,比如取對數約束一下,或者分母加1防止為0的情形,或者光滑一下等等。但是本質上意思還是這麼個意思
from sklearn.feature_extraction.text import TfidfTransformer
tf_transformer = TfidfTransformer(use_idf=False)
X_train_tf = tf_transformer.fit_transform(X_train_counts)
#use_idf預設為True
tfidf_transformer = TfidfTransformer()
X_train_tfidf = tfidf_transformer.fit_transform(X_train_counts)
上分類器
有了特徵以後,就可以用模型對資料進行分類了,這裡選用的是簡單高效好用的樸素貝葉斯方法。
from sklearn.naive_bayes import MultinominalNB
clf = MultinomialNB().fit(X_train_tfidf, twenty_train.target)
接下來要對測試文件用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])
#利用資料集中提供的測試集來看看準確率
twenty_test = fetch_20newsgroups(subset='test', categories=categories, shuffle=True, random_state=42)
docs_test = twenty_test.data
X_test = count_vect.transform(docs_test)
X_test_tfidf = tfidf_transformer.transform(X_test)
predicted = clf.predict(X_test_tfidf)
accuracy = np.mean(predicted == twenty_test.target)
print accuracy
當然也是可以用前一節說的Gridsearch來幫助選取引數。不過都是純程式碼的東西,不想翻了,另外,困了zzz。。