引言
樸素貝葉斯由貝葉斯定理延伸而來的簡單而強大的概率模型兑燥,它根據(jù)每個特征的概率確定一個對象屬于某一類別的概率帆赢。該方法基于一個假設(shè),所有特征需要相互獨立,即任一特征的值和其他特征的值沒有關(guān)聯(lián)關(guān)系叉抡。
雖然這種條件獨立的假設(shè)在許多應(yīng)用領(lǐng)域未必能很好滿足,甚至是不成立的答毫。但這種簡化的貝葉斯分類器在許多實際應(yīng)用中還是得到了較好的分類精度褥民。訓練模型的過程可以看作是對相關(guān)條件概率的計算,它可以用統(tǒng)計對應(yīng)某一類別的特征的頻率來估計洗搂。
樸素貝葉斯最成功的一個應(yīng)用是自然語言處理領(lǐng)域消返,自然語言處理的的數(shù)據(jù)可以看做是在文本文檔中標注數(shù)據(jù),這些數(shù)據(jù)可以作為訓練數(shù)據(jù)集來使用機器學習算法進行訓練耘拇。
本小節(jié)中撵颊,主要介紹使用樸素貝葉斯方法來進行文本的分類,我們將要使用一組標記類別的文本文檔來訓練樸素貝葉斯分類器惫叛,進而對未知的數(shù)據(jù)實例進行類別的預(yù)測倡勇。這個方法可以用作垃圾郵件的過濾。
數(shù)據(jù)集
該實驗的數(shù)據(jù)可以通過scikit-learn獲取一組新聞信息嘉涌。
數(shù)據(jù)集由19,000個新聞信息組成妻熊,其中包含了20個不同的主題,包含政治仑最、體育扔役、科學等內(nèi)容。
該數(shù)據(jù)集可以分成訓練和測試兩部分警医,訓練和測試數(shù)據(jù)的劃分基于某個特定日期亿胸。
數(shù)據(jù)的加載有兩種方式:
- sklearn.datasets.fetch_20newsgroups,該函數(shù)返回一個原數(shù)據(jù)列表法严,可以將它作為文本特征提取的接口(sklearn.feature_extraction.text.CountVectorizer)的輸入
- sklearn.datasets.fetch_20newsgroups_vectorized损敷,該接口直接返回直接可以使用的特征,可以不再使用特征提取了
from sklearn.datasets import fetch_20newsgroups
news = fetch_20newsgroups(subset='all')
print news.keys()
print type(news.data), type(news.target), type(news.target_names)
print news.target_names
print len(news.data)
print len(news.target)
打印信息:
['DESCR', 'data', 'target', 'target_names', 'filenames']
<type 'list'> <type 'numpy.ndarray'> <type 'list'>
['alt.atheism', 'comp.graphics', 'comp.os.ms-windows.misc', 'comp.sys.ibm.pc.hardware', 'comp.sys.mac.hardware', 'comp.windows.x', 'misc.forsale', 'rec.autos', 'rec.motorcycles', 'rec.sport.baseball', 'rec.sport.hockey', 'sci.crypt', 'sci.electronics', 'sci.med', 'sci.space', 'soc.religion.christian', 'talk.politics.guns', 'talk.politics.mideast', 'talk.politics.misc', 'talk.religion.misc']
18846
18846
我們可以查看一下第一項新聞的內(nèi)容和對應(yīng)的類別:
print news.data[0]
print news.target[0], news.target_names[news.target[0]]
打印的新聞內(nèi)容略去深啤,類別為10,類別名為rec.sport.hockey路星。
數(shù)據(jù)的預(yù)處理
機器學習算法只能作用在數(shù)值數(shù)據(jù)上溯街,算法期望使用定長的數(shù)值特征而不是不定長的原始文本文件,我們下一步的工作是將文本數(shù)據(jù)集轉(zhuǎn)換成數(shù)值數(shù)據(jù)集洋丐。
現(xiàn)在呈昔,我們只有一種特征:新聞消息的文本內(nèi)容,我們需要一個函數(shù)將一段文本轉(zhuǎn)換成一組有意義的數(shù)值特征友绝。
直覺上堤尾,可以嘗試著眼于每種文本類別的獨立字符串(更準確說是標記,token)迁客,然后將每種類別對應(yīng)的標記詞的頻率分布特性描述出來郭宝。sklearn.feature_extraction.text
模塊具有一些用文本文檔來構(gòu)建數(shù)值特征向量的有用的工具辞槐。
劃分訓練與測試數(shù)據(jù)
在進行轉(zhuǎn)換工作之前,我們需要將數(shù)據(jù)劃分為訓練和測試數(shù)據(jù)集粘室。由于載入的數(shù)據(jù)是隨機順序出現(xiàn)的榄檬,我們可以將數(shù)據(jù)劃分為兩部分,75%作為訓練數(shù)據(jù)衔统,25%作為測試數(shù)據(jù):
SPLIT_PERC = 0.75
split_size = int(len(news.data)*SPLIT_PERC)
X_train = news.data[:split_size]
X_test = news.data[split_size:]
Y_train = news.target[:split_size]
Y_test = news.target[split_size:]
因為sklearn.datasets.fetch_20newsgroups
本身可以根據(jù)subset
參數(shù)來選擇訓練數(shù)據(jù)和測試數(shù)據(jù)鹿榜,這里訓練數(shù)據(jù)有11,314條,占總數(shù)據(jù)集的60%锦爵,測試數(shù)據(jù)集占40%舱殿。可以通過如下方式得到:
news_train = fetch_20newsgroups(subset='train')
news_test = fetch_20newsgroups(subset='test')
X_train = news_train.data
X_test = news_test.data
Y_train = news_train.target
Y_test = news_test.target
詞袋(Bag of Words)表征
詞袋模型是在自然語言處理和信息檢索中的一種簡單假設(shè)险掀。在這種模型中沪袭,文本(段落或者文檔)被看作是無序的詞匯集合,忽略語法甚至是單詞的順序迷郑。
詞袋模型被用在文本分類的一些方法當中枝恋。當傳統(tǒng)的貝葉斯分類被應(yīng)用到文本當中時,貝葉斯中的條件獨立性假設(shè)導(dǎo)致詞袋模型嗡害。
scikit-learn提供了一些實用工具可以用最常見的方式從文本內(nèi)容中抽取數(shù)值特征焚碌,比如說:
- 標記(tokenizing)文本以及為每一個可能的標記(token)分配的一個整型ID,例如用空格和標點符號作為標記的分割符(中文的話涉及到分詞的問題)
- 計數(shù)(counting)標記(token)在每個文本中的出現(xiàn)頻率
- 在大多數(shù)樣本/文檔中都出現(xiàn)的標記的重要性遞減過程中霸妹,進行標準化(normalizing)和加權(quán)(weighting)
我們將上面這個從一堆文本文件轉(zhuǎn)化成數(shù)值特征向量的過程的策略稱為詞袋
在這種策略下十电,特征和樣本定義如下:
將每個獨立的標記(token)的出現(xiàn)頻率(不管是否標準化)看做是特征
給定一個文檔的所有標記的頻率構(gòu)成向量看做是一個多變量的樣本
這樣一個文本的語料庫就可以表征為一個矩陣,其中每一行代表了一個文檔叹螟,而每一列代表了在該語料庫中出現(xiàn)的一個標記詞鹃骂。
文本可以用詞語的出現(xiàn)頻率表征,這樣可以完全忽略詞在文本中的相對位置信息罢绽,這一點應(yīng)該就保證了貝葉斯的條件獨立性畏线。
稀疏性
大多數(shù)文檔通常只會使用語料庫中所有詞的一個子集,因而產(chǎn)生的矩陣將有許多特征值是0(通常99%以上都是0)良价。
例如寝殴,一組10,000個短文本(比如email)會使用100,000的詞匯總量,而每個文檔會使用100到1,000個唯一的詞明垢。
為了能夠在內(nèi)存中存儲這個矩陣蚣常,同時也提供矩陣/向量代數(shù)運算的速度,通常會使用稀疏表征例如在scipy.sparse包中提供的表征痊银。
文本特征提取的接口
sklearn.feature_extraction.text提供了以下構(gòu)建特征向量的工具:
- feature_extraction.text.CountVectorizer([...]) Convert a collection of text documents to a matrix of token counts
- feature_extraction.text.HashingVectorizer([...]) Convert a collection of text documents to a matrix of token occurrences
- feature_extraction.text.TfidfTransformer([...]) Transform a count matrix to a normalized tf or tf-idf representation
- feature_extraction.text.TfidfVectorizer([...]) Convert a collection of raw documents to a matrix of TF-IDF features.
解釋:
- CountVectorizer方法構(gòu)建單詞的字典抵蚊,每個單詞實例被轉(zhuǎn)換為特征向量的一個數(shù)值特征,每個元素是特定單詞在文本中出現(xiàn)的次數(shù)
- HashingVectorizer方法實現(xiàn)了一個哈希函數(shù),將標記映射為特征的索引贞绳,其特征的計算同CountVectorizer方法
- TfidfVectorizer使用了一個高級的計算方法谷醉,稱為Term Frequency Inverse Document
Frequency (TF-IDF)。這是一個衡量一個詞在文本或語料中重要性的統(tǒng)計方法熔酷。直覺上講孤紧,該方法通過比較在整個語料庫的詞的頻率,尋求在當前文檔中頻率較高的詞拒秘。這是一種將結(jié)果進行標準化的方法号显,可以避免因為有些詞出現(xiàn)太過頻繁而對一個實例的特征化作用不大的情況(我猜測比如a和and在英語中出現(xiàn)的頻率比較高,但是它們對于表征一個文本的作用沒有什么作用)
構(gòu)建樸素貝葉斯分類器
由于我們使用詞的出現(xiàn)次數(shù)作為特征躺酒,可以用多項分布來描述這一特征押蚤。在sklearn中使用sklearn.naive_bayes
模塊的MultinomialNB
類來構(gòu)建分類器。
我們使用Pipeline
這個類來構(gòu)建包含量化器(vectorizers)和分類器的復(fù)合分類器(compound classifer)羹应。
from sklearn.naive_bayes import MultinomialNB
from sklearn.pipeline import Pipeline
from sklearn.feature_extraction.text import TfidfVectorizer, HashingVectorizer, CountVectorizer
#nbc means naive bayes classifier
nbc_1 = Pipeline([
('vect', CountVectorizer()),
('clf', MultinomialNB()),
])
nbc_2 = Pipeline([
('vect', HashingVectorizer(non_negative=True)),
('clf', MultinomialNB()),
])
nbc_3 = Pipeline([
('vect', TfidfVectorizer()),
('clf', MultinomialNB()),
])
nbcs = [nbc_1, nbc_2, nbc_3]
交叉驗證
我們下面設(shè)計一個對分類器的性能進行測試的交叉驗證的函數(shù):
from sklearn.cross_validation import cross_val_score, KFold
from scipy.stats import sem
import numpy as np
def evaluate_cross_validation(clf, X, y, K):
# create a k-fold croos validation iterator of k=5 folds
cv = KFold(len(y), K, shuffle=True, random_state=0)
# by default the score used is the one returned by score method of the estimator (accuracy)
scores = cross_val_score(clf, X, y, cv=cv)
print scores
print ("Mean score: {0:.3f} (+/-{1:.3f})").format(
np.mean(scores), sem(scores))
將訓練數(shù)據(jù)分成5份揽碘,輸出驗證的分數(shù):
for nbc in nbcs:
evaluate_cross_validation(nbc, X_train, Y_train, 5)
輸出為:
[ 0.82589483 0.83473266 0.8272205 0.84136103 0.83377542]
Mean score: 0.833 (+/-0.003)
[ 0.76358816 0.72337605 0.72293416 0.74370305 0.74977896]
Mean score: 0.741 (+/-0.008)
[ 0.84975696 0.83517455 0.82545294 0.83870968 0.84615385]
Mean score: 0.839 (+/-0.004)
從上面的結(jié)果看出,CountVectorizer和TfidfVectorizer進行特征提取的方法要比HashingVectorizer的效果好园匹。
優(yōu)化特征提取提高分類的效果
接下來雳刺,我們通過正則表達式來解析文本得到標記詞。
優(yōu)化提取單詞規(guī)則參數(shù)
TfidfVectorizer
的一個參數(shù)token_pattern
用于指定提取單詞的規(guī)則裸违。
默認的正則表達式是ur"\b\w\w+\b"
掖桦,這個正則表達式只匹配單詞邊界并考慮到了下劃線,也可能考慮到了橫杠和點供汛。
新的正則表達式是ur"\b[a-z0-9_\-\.]+[a-z][a-z0-9_\-\.]+\b"
枪汪。
nbc_4 = Pipeline([
('vect', TfidfVectorizer(
token_pattern=ur"\b[a-z0-9_\-\.]+[a-z][a-z0-9_\-\.]+\b",
)),
('clf', MultinomialNB()),
])
evaluate_cross_validation(nbc_4, X_train, Y_train, 5)
[ 0.86478126 0.85461776 0.84489616 0.85505966 0.85234306]
Mean score: 0.854 (+/-0.003)
這個分數(shù)已經(jīng)比之前的0.839提高了一些了。
優(yōu)化省略詞參數(shù)
TfidfVectorizer
的一個參數(shù)stop_words
這個參數(shù)指定的詞將被省略不計入到標記詞的列表中怔昨,比如一些出現(xiàn)頻率很高的詞雀久,但是這些詞對于特定的主題不能提供任何的先驗支持。
def get_stop_words():
result = set()
for line in open('stopwords_en.txt', 'r').readlines():
result.add(line.strip())
return result
stop_words = get_stop_words()
nbc_5 = Pipeline([
('vect', TfidfVectorizer(
stop_words=stop_words,
token_pattern=ur"\b[a-z0-9_\-\.]+[a-z][a-z0-9_\-\.]+\b",
)),
('clf', MultinomialNB()),
])
evaluate_cross_validation(nbc_5, X_train, Y_train, 5)
[ 0.88731772 0.88731772 0.878038 0.88466637 0.88107869]
Mean score: 0.884 (+/-0.002)
分數(shù)又提升到了0.884趁舀。
優(yōu)化貝葉斯分類器的alpha參數(shù)
MultinomialNB
有一個alpha
參數(shù)赖捌,該參數(shù)是一個平滑參數(shù),默認是1.0矮烹,我們將其設(shè)為0.01巡蘸。
nbc_6 = Pipeline([
('vect', TfidfVectorizer(
stop_words=stop_words,
token_pattern=ur"\b[a-z0-9_\-\.]+[a-z][a-z0-9_\-\.]+\b",
)),
('clf', MultinomialNB(alpha=0.01)),
])
evaluate_cross_validation(nbc_6, X_train, Y_train, 5)
[ 0.91073796 0.92532037 0.91604065 0.91294741 0.91202476]
Mean score: 0.915 (+/-0.003)
這下分數(shù)已經(jīng)優(yōu)化的很好了。
評估分類器性能
我們通過交叉驗證得到了效果比較好的分類器參數(shù)擂送,下面我們可以用該分類器來測試我們的測試數(shù)據(jù)了。
from sklearn import metrics
nbc_6.fit(X_train, Y_train)
print "Accuracy on training set:"
print nbc_6.score(X_train, Y_train)
print "Accuracy on testing set:"
print nbc_6.score(X_test,Y_test)
y_predict = nbc_6.predict(X_test)
print "Classification Report:"
print metrics.classification_report(Y_test,y_predict)
print "Confusion Matrix:"
print metrics.confusion_matrix(Y_test,y_predict)
這里只輸出準確率:
Accuracy on training set:
0.997701962171
Accuracy on testing set:
0.846919808816
參考資料
轉(zhuǎn)載請注明作者Jason Ding及其出處
GitCafe博客主頁(http://jasonding1354.gitcafe.io/)
Github博客主頁(http://jasonding1354.github.io/)
CSDN博客(http://blog.csdn.net/jasonding1354)
簡書主頁(http://www.reibang.com/users/2bd9b48f6ea8/latest_articles)
百度搜索jasonding1354進入我的博客主頁