一、樸素貝葉斯(Naive Bayes)簡介
1.原理
相信大家都學過貝葉斯公式,見下:
而我們正是用這條公式來實現樸素貝葉斯。現要實現這樣一個分類任務:對於一條數據,我們希望預測它屬於m個類別中的哪個類別 。 所以我們就是要求這些條件概率 , 並比較哪一個最大,那麼x就屬於概率最大那一類。根據貝葉斯公式,有:(式中用c代表某一類別)
該算法之所以被稱爲樸素貝葉斯,是因爲它採用了屬性條件獨立性假設,也就是假設x中的所有屬性x1,x2,x3...xn它們之間是獨立的,不相互影響的,都獨立的對分類結果產生影響,基於這樣的假設,上式變爲:
在這裏有一個值得思考的問題。即在上式當中,我們可不可以根據x的各個屬性的取值,直接統計x出現的個數,進而得到P(x|c), 而不需要將所有的屬性分開考慮呢?實際上這往往是不行的。由於訓練集是有限的,而且往往不大,很多樣本取值在訓練集中根本沒有出現,但這不代表它的出現概率爲零,所以直接使用頻率來估計P(x|c)是不可行的。
接下來,由於對於所有類別來說,P(x)相同,根據上面的式子,我們只需要計算、比較分子即可:
令Dc表示訓練集D中第c類樣本組成的集合,若有充足的獨立同分布樣本,則可容易地估算出該類的先驗概率:
對於離散屬性而言,令表示Dc中在第i個屬性上取值爲的樣本組成的集合,則:
對於連續屬性可以考慮概率密度函數,假定, 即服從正態分佈,那麼有:
有人可能發現如果用於連乘的條件概率爲零的話, 整個結果就會變爲零。所以在估計概率值的時候通常要進行smoothing,常用拉普拉斯修正。具體來說,令N表示訓練D中可能的類別數,Ni表示第i個屬性可能的取值數,則上面的式子可以修正爲:
另外,當我們進行連乘操作,往往由於概率的值小而且連乘的概率值很多,或造成數值下溢,所以通常通過取對數的方式,將連乘轉化成連加,下面自己實現的版本就採用的就是連加操作。
2.優點
1. 儘管假設簡單,naive Bayes分類器在很多真實的分類問題,例如文檔分類、垃圾郵件過濾,效果很理想。
2.在數據較少的情況下仍然有效,它只需要少量的訓練數據去估計必要的參數,可以處理多類別問題。
3.相比其它複雜的方法, naive Bayes分類器執行非常快,類條件特徵分佈的可分解性意味着,每一個分佈都可以作爲一維分佈獨立地估計。
3.缺點
儘管naive Bayes是一個適宜的分類器,但是它的估計量並不好,因此,來自predict_proba的概率輸出結果建議只作爲參考。
在現實中,屬性條件獨立性假設往往不成立,屬性和屬性之間往往有一定的關係,所以樸素貝葉斯過於理想,在某些屬性獨立性很弱的數據集上效果往往不夠好。因此也催生了半樸素貝葉斯和貝葉斯網絡算法來解決這個問題。
二、代碼實現
下面是一個用於文檔分類的貝葉斯分類器的實現。
import numpy as np
def load_dataset():
#返回一些自己構造的已經進行了分詞的句子(這裏稱它們爲document)
documents = [['my','dog','has','flea',\
'problems', 'help','please'],
['maybe','not','take','him',\
'to','dog','park','stupid'],
['my','dalmation','is','so','cute',\
'I','love','him'],
['stop','posting','stupid','worthless','garbage'],
['mr','licks','ate','my','steak','how',\
'to','stop','him'],
['quit','buying','worthless','dog','food','stupid']]
labels = [0,1,0,1,0,1]
return documents, labels
def create_vocabulary(dataset):
#創建詞彙表
vocab_set = set([])
for document in dataset:
vocab_set = vocab_set |set(document)
return list(vocab_set)
def words2vect(vocabulary, input_list):
#這裏使用的是獨熱編碼的方式
return_vector = [0]*len(vocabulary)
for word in input_list:
if word in vocabulary:
return_vector[vocabulary.index(word)] = 1
else:
print("the word %s is not in my vocabulary"%word)
return return_vector
def trainNB0(train_matrix, train_category):
#訓練分類器
num_traindocs = len(train_matrix)
num_words = len(train_matrix[0])
# num_abusive代表帶有侮辱性的文檔的數目
num_abusive = np.sum(train_category)
# 這裏使用了拉普拉斯平滑
prob_abusive = (num_abusive+1)/(float(num_traindocs)+2)
p0_num = np.ones(num_words)
p1_num = np.ones(num_words)
for i in range(num_traindocs):
if train_category[i] == 1:
p1_num += train_matrix[i]
else:
p0_num += train_matrix[i]
p1_vect = p1_num/(num_abusive+2)
p0_vect = p0_num/(num_traindocs-num_abusive+2)
return p0_vect , p1_vect, prob_abusive
def classifyNB(test_vector, p0_vect, p1_vect, p_class1):
#進行分類
p1 = np.log(p_class1)
p0 = np.log(1.0-p_class1)
for i in range(len(test_vector)):
if test_vector[i] == 1:
p1 += np.log(p1_vect[i])
p0 += np.log(p0_vect[i])
else:
p1 += np.log(1-p1_vect[i])
p0 += np.log(1-p0_vect[i])
if p1 > p0:
return 1
else:
return 0
def testingNB(data_test):
#進行測試
documents, labels = load_dataset()
vocabulary = create_vocabulary(documents)
train_matrix = []
for doc in documents:
train_matrix.append(words2vect(vocabulary,doc))
p0_vect, p1_vect, prob_abusive = trainNB0(np.array(train_matrix),\
np.array(labels))
for entry in data_test:
entry_vect = np.array(words2vect(vocabulary, entry))
entry_class = classifyNB(entry_vect, p0_vect, p1_vect, prob_abusive)
print("{} classify as {}".format(entry, entry_class))
data_test = [['please,my'],['garbage, trash']]
testingNB(data_test)
======OUTPUT========
[0,1]
三、使用sklearn中的貝葉斯分類器
當我們需要使用貝葉斯分類器時,我們可以直接調用sklearn中的分類器,而不需要自己實現。在sklearn中總共有三種貝葉斯分類器, 分別是BernoulliNB,MultinomialNB和GaussianNB。
1. 三種貝葉斯分類器的區別:
BernoulliNB假設數據的各個屬性都是服從伯努利分佈的,也就是二項分佈,上面的數據就是這樣的分佈。
MultinomialNB假設數據的各個屬性是離散的,就是取有限多個的值。
GaussianNB假設數據各個屬性是連續的。
2.三種貝葉斯分類器的應用場景:
如果樣本特徵是二元離散值,應該使用BernoulliNB。
如果如果樣本特徵的分大部分是多元離散值,使用MultinomialNB比較合適。
一般來說,如果樣本特徵的分佈大部分是連續值,使用GaussianNB會比較好。
貝葉斯分類器一般會對不滿足假設條件的數據進行預處理,使其滿足假設條件。但處理之後效果不一定很好,多元離散值類型的iris數據用BernoulliNB進行分類,準確率極差。
3.代碼
from sklearn.naive_bayes import BernoulliNB, MultinomialNB, GaussianNB
from sklearn import datasets
from sklearn.model_selection import train_test_split
from sklearn.metrics import classification_report
iris = datasets.load_iris()
trainX, testX, trainY, testY = train_test_split(iris.data, iris.target,test_size=0.2)
model1 = BernoulliNB().fit(trainX, trainY)
y_pred = model1.predict(testX)
print(classification_report(testY, y_pred))
model2 = MultinomialNB().fit(trainX, trainY)
y_pred = model2.predict(testX)
print(classification_report(testY, y_pred))
model3 = GaussianNB().fit(trainX, trainY)
y_pred = model3.predict(testX)
print(classification_report(testY, y_pred))
下面是三個模型的分類報告:
四、參考資料
【1】《機器學習實戰》Peter+Harrington
【2】《機器學習》周志華
【3】https://blog.csdn.net/Trance95/article/details/104877399/