上篇介紹了樸素貝葉斯的原理尚蝌,本篇來介紹如何用樸素貝葉斯解決實(shí)際問題迎变。
樸素貝葉斯最擅長的領(lǐng)域是文本分析,包括:
- 文本分類
- 情感分析
- 垃圾郵件處理
要對文本進(jìn)行分類飘言,首先要做的是如何提取文本的主要信息衣形,如何衡量哪些信息是文本中的主要信息呢?
1姿鸿,對文檔分詞
我們知道谆吴,一篇文檔是由若干詞匯組成的,也就是文檔的主要信息是詞匯苛预。從這個(gè)角度來看句狼,我們就可以用一些關(guān)鍵詞來描述文檔。
這種處理文本的方法叫做詞袋(bag of words)模型热某,該模型會忽略文本中的詞語出現(xiàn)的順序以及相應(yīng)的語法腻菇,將文檔看做是由若干單詞組成的,且單詞之間互相獨(dú)立昔馋,沒有關(guān)聯(lián)筹吐。
要想提取文檔中的關(guān)鍵詞,就得先對文檔進(jìn)行分詞秘遏。分詞方法一般有兩種:
- 第一種是基于字符串匹配丘薛。就是掃描字符串。如果發(fā)現(xiàn)字符串的子串和詞相同邦危,就算匹配成功洋侨。
- 匹配規(guī)則一般有“正向最大匹配”,“逆向最大匹配”倦蚪,“長詞優(yōu)先”等凰兑。
- 該類算法的優(yōu)點(diǎn)是只需基于字典匹配,算法簡單审丘;缺點(diǎn)是沒有考慮詞義,處理歧義詞效果不佳勾给。
- 第二種是基于統(tǒng)計(jì)和機(jī)器學(xué)習(xí)滩报。需要人工標(biāo)注詞性和統(tǒng)計(jì)特征锅知,對中文進(jìn)行建模。
- 先要訓(xùn)練分詞模型脓钾,然后基于模型進(jìn)行計(jì)算概率售睹,取概率最大的分詞作為匹配結(jié)果。
- 常見的序列標(biāo)注模型有隱馬爾科夫模型和條件隨機(jī)場可训。
停用詞是一些非常普遍使用的詞語昌妹,對文檔分析作用不大,在文檔分析之前需要將這些詞去掉握截。比如:
- 中文停用詞:“你飞崖,我,他谨胞,它固歪,的,了” 等胯努。
- 英文停用詞:“is牢裳,a,the叶沛,this蒲讯,that” 等。
- 停用詞文件:停用詞一般保存在文件中灰署,需要自行讀取判帮。
另外分詞階段,還需要處理同義詞氓侧,很多時(shí)候一件東西有多個(gè)不同的名字脊另。比如“番茄”和“西紅柿”,“鳳梨”和“菠蘿”等约巷。
中文分詞與英文分詞是不同的偎痛,我們分別介紹一個(gè)著名的分詞包:
2氓癌,計(jì)算單詞權(quán)重
哪些關(guān)鍵詞對一個(gè)文檔才是重要的谓谦?比如可以通過單詞出現(xiàn)的次數(shù),次數(shù)越多就表示越重要贪婉。
更合理的方法是計(jì)算單詞的TF-IDF
值反粥。
2.1,單詞的 TF-IDF 值
單詞的TF-IDF
值可以描述一個(gè)單詞對文檔的重要性,TF-IDF
值越大才顿,則越重要莫湘。
-
TF:全稱是
Term Frequency
,即詞頻(單詞出現(xiàn)的頻率)郑气,也就是一個(gè)單詞在文檔中出現(xiàn)的次數(shù)幅垮,次數(shù)越多越重要。- 計(jì)算公式:
一個(gè)單詞的詞頻TF = 單詞出現(xiàn)的次數(shù) / 文檔中的總單詞數(shù)
- 計(jì)算公式:
-
IDF:全稱是
Inverse Document Frequency
尾组,即逆向文檔詞頻忙芒,是指一個(gè)單詞在文檔中的區(qū)分度。它認(rèn)為一個(gè)單詞出現(xiàn)在的文檔數(shù)越少讳侨,這個(gè)單詞對該文檔就越重要呵萨,就越能通過這個(gè)單詞把該文檔和其他文檔區(qū)分開。- 計(jì)算公式:
一個(gè)單詞的逆向文檔頻率 IDF = log(文檔總數(shù) / 該單詞出現(xiàn)的文檔數(shù) + 1)
- 為了避免分母為0(有些單詞可能不在文檔中出現(xiàn))爷耀,所以在分母上加1
- 計(jì)算公式:
IDF 是一個(gè)相對權(quán)重值甘桑,公式中log 的底數(shù)可以自定義,一般可取2歹叮,10跑杭,e 為底數(shù)。
假設(shè)我們現(xiàn)在有一篇文章咆耿,文章中共有2000 個(gè)單詞德谅,“中國”出現(xiàn)100 次。假設(shè)全網(wǎng)共有1 億篇文章萨螺,其中包含“中國”的有200 萬篇≌觯現(xiàn)在我們要求“中國”的TF-IDF值。
計(jì)算過程如下:
TF(中國) = 100 / 2000 = 0.05
IDF(中國) = log(1億/(200萬+1)) = 1.7 # 這里的log 以10 為底
TF-IDF(中國) = 0.05 * 1.7 = 0.085
通過計(jì)算文檔中單詞的TF-IDF
值慰技,我們就可以提取文檔中的特征屬性椭盏,就是把TF-IDF
值較高的單詞,作為文檔的特征屬性吻商。
2.2掏颊,TfidfVectorizer 類
sklearn 庫的 feature_extraction.text
模塊中的 TfidfVectorizer 類,可以計(jì)算 TF-IDF
值艾帐。
TfidfVectorizer
類的原型如下:
TfidfVectorizer(*,
input='content',
encoding='utf-8',
decode_error='strict',
strip_accents=None,
lowercase=True,
preprocessor=None,
tokenizer=None,
analyzer='word',
stop_words=None,
token_pattern='(?u)\b\w\w+\b',
ngram_range=(1, 1),
max_df=1.0,
min_df=1,
max_features=None,
vocabulary=None,
binary=False,
dtype=<class 'numpy.float64'>,
norm='l2',
use_idf=True,
smooth_idf=True,
sublinear_tf=False)
常用的參數(shù)有:
-
input
:有三種取值:- filename
- file
- content:默認(rèn)值為
content
乌叶。
-
analyzer
:有三種取值,分別是:- word:默認(rèn)值為
word
柒爸。 - char
- char_wb
- word:默認(rèn)值為
-
stop_words
:表示停用詞准浴,有三種取值:-
english
:會加載自帶英文停用詞。 -
None
:沒有停用詞捎稚,默認(rèn)為None
乐横。 -
List
類型的對象:需要用戶自行加載停用詞求橄。 - 只有當(dāng)參數(shù)
analyzer == 'word'
時(shí)才起作用。
-
-
token_pattern
:表示過濾規(guī)則晰奖,是一個(gè)正則表達(dá)式谈撒,不符合正則表達(dá)式的單詞將會被過濾掉。- 注意默認(rèn)的
token_pattern
值為r'(?u)\b\w\w+\b'
匾南,匹配兩個(gè)以上的字符,如果是一個(gè)字符則匹配不上蛔外。 - 只有參數(shù)
analyzer == 'word'
時(shí)蛆楞,正則才起作用。
- 注意默認(rèn)的
-
max_df
:用于描述單詞在文檔中的最高出現(xiàn)率夹厌,取值范圍為[0.0~1.0]
豹爹。- 比如
max_df=0.6
,表示一個(gè)單詞在 60% 的文檔中都出現(xiàn)過矛纹,那么認(rèn)為它只攜帶了非常少的信息臂聋,因此就不作為分詞統(tǒng)計(jì)。
- 比如
-
mid_df
:單詞在文檔中的最低出現(xiàn)率或南,一般不用設(shè)置孩等。
常用的方法有:
-
t.fit(raw_docs)
:用raw_docs
擬合模型。 -
t.transform(raw_docs)
:將raw_docs
轉(zhuǎn)成矩陣并返回采够,其中包含了每個(gè)單詞在每個(gè)文檔中的 TF-IDF 值肄方。 -
t.fit_transform(raw_docs)
:可理解為先fit
再transform
。
在上面三個(gè)方法中:
-
t
表示TfidfVectorizer
對象蹬癌。 -
raw_docs
參數(shù)是一個(gè)可遍歷對象权她,其中的每個(gè)元素表示一個(gè)文檔。
fit_transform
與 transform
的用法
- 一般在擬合轉(zhuǎn)換數(shù)據(jù)時(shí)逝薪,先處理訓(xùn)練集數(shù)據(jù)隅要,再處理測試集數(shù)據(jù)。
- 訓(xùn)練集數(shù)據(jù)會用于擬合模型董济,而測試集數(shù)據(jù)不會用于擬合模型步清。所以:
-
fit_transform
用于訓(xùn)練集數(shù)據(jù)。 -
transform
用于測試集數(shù)據(jù)感局,且transform
必須在fit_transform
之后尼啡。 - 如果測試集數(shù)據(jù)也用
fit_transform
方法,則會造成過擬合询微。
-
下圖表達(dá)的很清晰明了:
所以一般的使用步驟是:
# x 為 DictVectorizer崖瞭,DictVectorizer 等類的對象
# 用于特征提取
x = XXX()
train_features = x.fit_transform(train_datas)
test_features = x.transform(test_datas)
2.3,一個(gè)例子
比如我們有如下3 個(gè)文檔(docs
的每個(gè)元素表示一個(gè)文檔):
docs = [
'I am a student.',
'I live in Beijing.',
'I love China.',
]
我們用 TfidfVectorizer
類來計(jì)算TF-IDF 值:
from sklearn.feature_extraction.text import TfidfVectorizer
t = TfidfVectorizer() # 使用默認(rèn)參數(shù)
用 fit_transform()
方法擬合模型撑毛,反回矩陣:
t_matrix = t.fit_transform(docs)
用 get_feature_names()
方法獲取所有不重復(fù)的特征詞:
>>> t.get_feature_names()
['am', 'beijing', 'china', 'in', 'live', 'love', 'student']
不知道你有沒有發(fā)現(xiàn)书聚,這些特征詞中不包含
i
和a
唧领?你能解釋一下是為什么嗎?
用vocabulary_
屬性獲取特征詞與ID
的對應(yīng)關(guān)系:
>>> t.vocabulary_
{'am': 0, 'student': 6, 'live': 4, 'in': 3, 'beijing': 1, 'love': 5, 'china': 2}
用 矩陣對象的toarray()
方法輸出 TF-IDF
值:
>>> t_matrix.toarray()
array([
[0.70710678, 0. , 0. , 0. , 0. , 0. , 0.70710678],
[0. , 0.57735027, 0. , 0.57735027, 0.57735027, 0. , 0. ],
[0. , 0. , 0.70710678, 0. , 0. , 0.70710678, 0. ]
])
3雌续,sklearn 樸素貝葉斯的實(shí)現(xiàn)
sklearn 庫中的 naive_bayes 模塊實(shí)現(xiàn)了 5 種樸素貝葉斯算法:
-
naive_bayes.BernoulliNB
類:伯努利樸素貝葉斯的實(shí)現(xiàn)斩个。- 適用于離散型數(shù)據(jù),適合特征變量是布爾變量驯杜,符合 0/1 分布受啥,在文檔分類中特征是單詞是否出現(xiàn)。
- 該算法以文件為粒度鸽心,如果該單詞在某文件中出現(xiàn)了即為 1滚局,否則為 0。
-
naive_bayes.CategoricalNB
類:分類樸素貝葉斯的實(shí)現(xiàn)顽频。 -
naive_bayes.GaussianNB
類:高斯樸素貝葉斯的實(shí)現(xiàn)藤肢。- 適用于特征變量是連續(xù)型數(shù)據(jù),符合高斯分布糯景。比如說人的身高嘁圈,物體的長度等,這種自然界物體蟀淮。
-
naive_bayes.MultinomialNB
類:多項(xiàng)式樸素貝葉斯的實(shí)現(xiàn)最住。- 適用于特征變量是離散型數(shù)據(jù),符合多項(xiàng)分布灭贷。在文檔分類中特征變量體現(xiàn)在一個(gè)單詞出現(xiàn)的次數(shù)温学,或者是單詞的 TF-IDF 值等。
-
naive_bayes.ComplementNB
類:補(bǔ)充樸素貝葉斯的實(shí)現(xiàn)甚疟。- 是多項(xiàng)式樸素貝葉斯算法的一種改進(jìn)仗岖。
每個(gè)類名中的NB 后綴是 Naive Bayes 的縮寫,即表示樸素貝葉斯览妖。
各個(gè)類的原型如下:
BernoulliNB(*, alpha=1.0, binarize=0.0, fit_prior=True, class_prior=None)
CategoricalNB(*, alpha=1.0, fit_prior=True, class_prior=None)
GaussianNB(*, priors=None, var_smoothing=1e-09)
MultinomialNB(*, alpha=1.0, fit_prior=True, class_prior=None)
ComplementNB(*, alpha=1.0, fit_prior=True, class_prior=None, norm=False)
構(gòu)造方法中的alpha
的含義為平滑參數(shù):
- 如果一個(gè)單詞在訓(xùn)練樣本中沒有出現(xiàn)轧拄,這個(gè)單詞的概率就會是 0。但訓(xùn)練集樣本只是整體的抽樣情況讽膏,不能因?yàn)闆]有觀察到檩电,就認(rèn)為整個(gè)事件的概率為 0。為了解決這個(gè)問題府树,需要做平滑處理俐末。
- 當(dāng) alpha=1 時(shí),使用的是 Laplace 平滑奄侠。Laplace 平滑就是采用加 1 的方式卓箫,來統(tǒng)計(jì)沒有出現(xiàn)過的單詞的概率。這樣當(dāng)訓(xùn)練樣本很大的時(shí)候垄潮,加 1 得到的概率變化可以忽略不計(jì)烹卒。
- 當(dāng) 0<alpha<1 時(shí)闷盔,使用的是 Lidstone 平滑。對于 Lidstone 平滑來說旅急,alpha 越小逢勾,迭代次數(shù)越多,精度越高藐吮。一般可以設(shè)置 alpha 為 0.001溺拱。
4,構(gòu)建模型
我準(zhǔn)備了一個(gè)實(shí)戰(zhàn)案例谣辞,目錄結(jié)構(gòu)如下:
naive_bayes\
├── stop_word\
│ └── stopword.txt
├── test_data\
│ ├── test_economy.txt
│ ├── test_fun.txt
│ ├── test_health.txt
│ └── test_sport.txt
├── text_classification.py
└── train_data\
├── train_economy.txt
├── train_fun.txt
├── train_health.txt
└── train_sport.txt
其中:
-
stop_word
目錄中是中文停用詞盟迟。 -
train_data
目錄中是訓(xùn)練集數(shù)據(jù)。 -
test_data
目錄中是測試集數(shù)據(jù)潦闲。 -
text_classification.py
:是Python 代碼,包括以下步驟:- 中文分詞
- 特征提取
- 模型訓(xùn)練
- 模型測試
這些數(shù)據(jù)是一些新聞數(shù)據(jù)迫皱,每條數(shù)據(jù)包含了新聞?lì)愋?/strong>和新聞標(biāo)題歉闰,類型有以下四種:
- 財(cái)經(jīng)類
- 娛樂類
- 健康類
- 體育類
我們的目的是訓(xùn)練一個(gè)模型,該模型的輸入是新聞標(biāo)題卓起,模型的輸出是新聞?lì)愋秃途矗簿褪窍胪ㄟ^新聞標(biāo)題來判斷新聞?lì)愋汀?/p>
來看下數(shù)據(jù)的樣子,每類數(shù)據(jù)抽取了一條:
財(cái)經(jīng)---11月20日晚間影響市場重要政策消息速遞
娛樂---2020金雞港澳臺影展曝片單 修復(fù)版《蝶變》等將映
健康---全面解析耳聾耳鳴戏阅,讓你不再迷茫它的危害
體育---中國軍團(tuán)1人已進(jìn)32強(qiáng)昼弟!趙心童4-1晉級,丁俊暉顏丙濤將出戰(zhàn)
可以看到奕筐,每條數(shù)據(jù)以---
符號分隔舱痘,前邊是新聞?lì)愋停筮吺切侣剺?biāo)題离赫。
下面來看下代碼:
import os
import sys
import jieba
import warnings
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.naive_bayes import MultinomialNB
from sklearn import metrics
warnings.filterwarnings('ignore')
if sys.version.startswith('2.'):
reload(sys)
sys.setdefaultencoding('utf-8')
def load_file(file_path):
with open(file_path) as f:
lines = f.readlines()
titles = []
labels = []
for line in lines:
line = line.encode('unicode-escape').decode('unicode-escape')
line = line.strip().rstrip('\n')
_lines = line.split('---')
if len(_lines) != 2:
continue
label, title = _lines
words = jieba.cut(title)
s = ''
for w in words:
s += w + ' '
s = s.strip()
titles.append(s)
labels.append(label)
return titles, labels
def load_data(_dir):
file_list = os.listdir(_dir)
titles_list = []
labels_list = []
for file_name in file_list:
file_path = _dir + '/' + file_name
titles, labels = load_file(file_path)
titles_list += titles
labels_list += labels
return titles_list, labels_list
def load_stopwords(file_path):
with open(file_path) as f:
lines = f.readlines()
words = []
for line in lines:
line = line.encode('unicode-escape').decode('unicode-escape')
line = line.strip('\n')
words.append(line)
return words
if __name__ == '__main__':
# 加載停用詞
stop_words = load_stopwords('stop_word/stopword.txt')
# 加載訓(xùn)練數(shù)據(jù)
train_datas, train_labels = load_data('train_data')
# 加載測試數(shù)據(jù)
test_datas, test_labels = load_data('test_data')
# 計(jì)算單詞權(quán)重
tf = TfidfVectorizer(stop_words = stop_words, max_df = 0.5)
train_features = tf.fit_transform(train_datas)
test_features = tf.transform(test_datas)
# 多項(xiàng)式貝葉斯分類器
clf = MultinomialNB(alpha = 0.001).fit(train_features, train_labels)
# 預(yù)測數(shù)據(jù)
predicted_labels = clf.predict(test_features)
# 計(jì)算準(zhǔn)確率
score = metrics.accuracy_score(test_labels, predicted_labels)
print score
說明:
-
load_stopwords
函數(shù)用于加載停用詞芭逝。 -
load_data
函數(shù)用于加載訓(xùn)練集和測試集數(shù)據(jù)。 - 使用
fit_transform
方法提取訓(xùn)練集特征渊胸。 - 使用
transform
方法提取測試集特征旬盯。 - 這里使用的是多項(xiàng)式貝葉斯分類器---
MultinomialNB
,平滑參數(shù)設(shè)置為0.001翎猛。 - 用
fit
方法擬合出了模型胖翰。 - 用
predict
方法對測試數(shù)據(jù)進(jìn)行了預(yù)測。 - 最終用
accuracy_score
方法計(jì)算了模型的準(zhǔn)確度切厘,為 0.959萨咳。
5,如何存儲模型
實(shí)際應(yīng)用中迂卢,訓(xùn)練一個(gè)模型需要大量的數(shù)據(jù)某弦,也就會花費(fèi)很多時(shí)間桐汤。
為了方便使用,可以將訓(xùn)練好的模型存儲到磁盤上靶壮,在使用的時(shí)候怔毛,直接加載出來就可以使用。
可以使用 sklearn 中的 joblib 模塊來存儲和加載模型:
-
joblib.dump(obj, filepath)
方法將obj
存儲到filepath
指定的文件中腾降。-
obj
是要存儲的對象拣度。 -
filepath
是文件路徑。
-
-
joblib.load(filepath)
方法用于加載模型螃壤。-
filepath
是文件路徑抗果。
-
在上邊的例子用,我們需要存儲兩個(gè)對象奸晴,分別是:
-
tf
:TF-IDF 值模型冤馏。 -
cfl
:樸素貝葉斯模型。
存儲代碼如下:
from sklearn.externals import joblib
>>> joblib.dump(clf, 'nb.pkl')
['nb.pkl']
>>> joblib.dump(tf, 'tf.pkl')
['tf.pkl']
使用模型代碼如下:
import jieba
import warnings
from sklearn.externals import joblib
warnings.filterwarnings('ignore')
MODEL = None
TF = None
def load_model(model_path, tf_path):
global MODEL
global TF
MODEL = joblib.load(model_path)
TF = joblib.load(tf_path)
def nb_predict(title):
assert MODEL != None and TF != None
words = jieba.cut(title)
s = ' '.join(words)
test_features = TF.transform([s])
predicted_labels = MODEL.predict(test_features)
return predicted_labels[0]
if __name__ == '__main__':
# 加載模型
load_model('nb.pkl', 'tf.pkl')
# 測試
print nb_predict('東莞市場采購貿(mào)易聯(lián)網(wǎng)信息平臺參加部委首批聯(lián)合驗(yàn)收')
print nb_predict('留在中超了寄啼!踢進(jìn)生死戰(zhàn)決勝一球逮光,武漢卓爾保級成功')
print nb_predict('陳思誠全新系列電影《外太空的莫扎特》首曝海報(bào) 黃渤坟奥、榮梓杉演父子')
print nb_predict('紅薯的好處 常吃這種食物能夠幫你減肥')
其中:
-
load_model()
函數(shù)用于加載模型述吸。 -
nb_predict()
函數(shù)用于對新聞標(biāo)題進(jìn)行預(yù)測岗钩,返回標(biāo)題的類型次洼。
6爷抓,總結(jié)
本篇文章介紹了如何利用樸素貝葉斯處理文本分類問題:
- 首先需要對文本進(jìn)行分詞植阴,常用的分詞包有:
- 使用
TfidfVectorizer
計(jì)算單詞權(quán)重。- 使用
fit_transform
方法提取訓(xùn)練集特征塞绿。 - 使用
transform
方法提取測試集特征沟涨。
- 使用
- 使用
MultinomialNB
類訓(xùn)練模型,這里給出了一個(gè)實(shí)戰(zhàn)項(xiàng)目异吻,供大家參考裹赴。 - 使用 joblib 存儲模型,方便模型的使用诀浪。
(本節(jié)完棋返。)
推薦閱讀:
決策樹算法-理論篇-如何計(jì)算信息純度
決策樹算法-實(shí)戰(zhàn)篇-鳶尾花及波士頓房價(jià)預(yù)測
樸素貝葉斯分類-理論篇-如何通過概率解決分類問題