【機器學習實驗】使用樸素貝葉斯進行文本的分類

引言

樸素貝葉斯由貝葉斯定理延伸而來的簡單而強大的概率模型兑燥,它根據(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ù)的加載有兩種方式:

  1. sklearn.datasets.fetch_20newsgroups,該函數(shù)返回一個原數(shù)據(jù)列表法严,可以將它作為文本特征提取的接口(sklearn.feature_extraction.text.CountVectorizer)的輸入
  2. 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

參考資料

wiki:詞袋模型

轉(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進入我的博客主頁

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末唯欣,一起剝皮案震驚了整個濱河市嘹吨,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌境氢,老刑警劉巖蟀拷,帶你破解...
    沈念sama閱讀 211,042評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件碰纬,死亡現(xiàn)場離奇詭異,居然都是意外死亡问芬,警方通過查閱死者的電腦和手機悦析,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 89,996評論 2 384
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來此衅,“玉大人强戴,你說我怎么就攤上這事〉舶埃” “怎么了骑歹?”我有些...
    開封第一講書人閱讀 156,674評論 0 345
  • 文/不壞的土叔 我叫張陵,是天一觀的道長墨微。 經(jīng)常有香客問我道媚,道長,這世上最難降的妖魔是什么翘县? 我笑而不...
    開封第一講書人閱讀 56,340評論 1 283
  • 正文 為了忘掉前任最域,我火速辦了婚禮,結(jié)果婚禮上锈麸,老公的妹妹穿的比我還像新娘镀脂。我一直安慰自己,他們只是感情好掐隐,可當我...
    茶點故事閱讀 65,404評論 5 384
  • 文/花漫 我一把揭開白布狗热。 她就那樣靜靜地躺著,像睡著了一般虑省。 火紅的嫁衣襯著肌膚如雪匿刮。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,749評論 1 289
  • 那天探颈,我揣著相機與錄音熟丸,去河邊找鬼。 笑死伪节,一個胖子當著我的面吹牛光羞,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播怀大,決...
    沈念sama閱讀 38,902評論 3 405
  • 文/蒼蘭香墨 我猛地睜開眼纱兑,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了化借?” 一聲冷哼從身側(cè)響起潜慎,我...
    開封第一講書人閱讀 37,662評論 0 266
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后铐炫,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體垒手,經(jīng)...
    沈念sama閱讀 44,110評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,451評論 2 325
  • 正文 我和宋清朗相戀三年倒信,在試婚紗的時候發(fā)現(xiàn)自己被綠了科贬。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,577評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡鳖悠,死狀恐怖榜掌,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情竞穷,我是刑警寧澤唐责,帶...
    沈念sama閱讀 34,258評論 4 328
  • 正文 年R本政府宣布,位于F島的核電站瘾带,受9級特大地震影響鼠哥,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜看政,卻給世界環(huán)境...
    茶點故事閱讀 39,848評論 3 312
  • 文/蒙蒙 一朴恳、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧允蚣,春花似錦于颖、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,726評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至冒晰,卻和暖如春同衣,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背壶运。 一陣腳步聲響...
    開封第一講書人閱讀 31,952評論 1 264
  • 我被黑心中介騙來泰國打工耐齐, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人蒋情。 一個月前我還...
    沈念sama閱讀 46,271評論 2 360
  • 正文 我出身青樓埠况,卻偏偏與公主長得像,于是被迫代替她去往敵國和親棵癣。 傳聞我的和親對象是個殘疾皇子辕翰,可洞房花燭夜當晚...
    茶點故事閱讀 43,452評論 2 348

推薦閱讀更多精彩內(nèi)容