樸素貝葉斯算法
樸素貝葉斯法是基于貝葉斯定理與特征條件獨立假設的分類方法。
最為廣泛的兩種分類模型是決策樹模型(Decision Tree Model)和樸素貝葉斯模型(Naive Bayesian Model,NBM)。和決策樹模型相比,樸素貝葉斯分類器(Naive Bayes Classifier 或 NBC)發源于古典數學理論,有着堅實的數學基礎,以及穩定的分類效率。同時,NBC模型所需估計的參數很少,對缺失資料不太敏感,算法也比較簡單。理論上,NBC模型與其他分類方法相比具有最小的誤差率。但是實際上并非總是如此,這是因為NBC模型假設屬性之間互相獨立,這個假設在實際應用中往往是不成立的,這給NBC模型的正确分類帶來了一定影響。
樸素貝葉斯算法(Naive Bayesian algorithm) 是應用最為廣泛的分類算法之一。
樸素貝葉斯方法是在貝葉斯算法的基礎上進行了相應的簡化,即假定給定目标值時屬性之間互相條件獨立。也就是說沒有哪個屬性變量對于決策結果來說占有着較大的比重,也沒有哪個屬性變量對于決策結果占有着較小的比重。雖然這個簡化方式在一定程度上降低了貝葉斯分類算法的分類效果,但是在實際的應用場景中,極大地簡化了貝葉斯方法的複雜性。
貝葉斯方法
貝葉斯方法是以貝葉斯原理為基礎,使用機率統計的知識對樣本資料集進行分類。由于其有着堅實的數學基礎,貝葉斯分類算法的誤判率是很低的。貝葉斯方法的特點是結合先驗機率和後驗機率,即避免了隻使用先驗機率的主觀偏見,也避免了單獨使用樣本資訊的過拟合現象。貝葉斯分類算法在資料集較大的情況下表現出較高的準确率,同時算法本身也比較簡單。
簡單文檔分類執行個體
import numpy as np
## 簡單文檔分類——樸素貝葉斯算法
'''
建立實驗資料集(假設每個樣本已經被分割完成)
傳回值分别是 X_train, y_train
X_train:表示訓練的樣本
y_train:表示對應訓練樣本的分類結果
'''
def loadDataSet():
X_train = [
['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']
] # 切分好的詞條
y_train = [0, 1, 0, 1, 0, 1] # 類别标簽向量,1代表侮辱性詞彙,0代表非侮辱性詞彙
return X_train, y_train
'''
建立詞彙表
trainData:訓練集trainData(每句話代表一條資料),每句話已被切割成單詞了
vocabList:訓練集中所有的單詞(無重複)
'''
def createVocabList(trainData):
vocabSet = set() # 建立一條空的集合
# 周遊訓練集dataSet(每句話代表一條資料)中的每句話(已被切割成單詞了)
for doc in trainData:
vocabSet = vocabSet | set(doc) # 取并集,重複的隻記一遍
# print(vocabSet)
vocabList = list(vocabSet)
# print(len(vocabList))
return vocabList
'''
vocabList:詞彙表
inputSet:目前樣本(話,包含多個已被分割的單詞)
returnVec:傳回每個樣本(話)所對應的結果向量
'''
def setofwords2Vec(vocabList, inputSet):
returnVec = [0] * len(vocabList) # 建立詞彙表大小的零向量
for word in inputSet: # 周遊目前樣本(話)中的每一個單詞
if word in vocabList:
returnVec[vocabList.index(word)] += 1
else:
print(f'{word} is not in my vocabulary!')
return returnVec
'''
生成訓練向量集
dataSet:訓練集
vocabList:詞彙表
trainMat:傳回整個訓練集的結果向量集
(n*m維,n表示樣本數,m表示詞彙表中詞的個數)
'''
def get_trainMat(dataSet, vocabList):
trainMat = [] # 初始化向量清單
for inputSet in dataSet: # 周遊訓練集中每句話(所有的單詞)
returnVec = setofwords2Vec(vocabList, inputSet) # 将目前詞條向量化
trainMat.append(returnVec)
return trainMat
'''
拉普拉斯平滑樸素貝葉斯分類器訓練函數
要計算多個機率的乘積以擷取文檔屬于哪個類别,如果其中一個機率為0,則最終計算結果為0,顯然是不合理的。
是以使分子初始化為1,分母初始化為2,這種做法就叫做拉普拉斯平滑,又被稱為加一平滑。它就是為了解決0機率問題
trainMat:訓練結果向量集
classVec:每個訓練樣本的結果向量
'''
def trainNB(trainMat, classVec):
trainNum = len(trainMat) # 計算訓練集的數目(總文檔數)
ClassNum = len(trainMat[0]) # 計算訓練集中每一條的大小(總單詞數)
p0Num = np.ones(ClassNum) # 非侮辱類每個單詞詞頻初始化為0,為了防止出現0而初始化為1
p1Num = np.ones(ClassNum) # 侮辱類每個單詞詞頻初始化為0,為了防止出現0而初始化為1
p0Denom = 2 # 非侮辱類所有單詞的詞頻,同理初始化為2
p1Denom = 2 # 侮辱類所有單詞的詞頻
num0 = 0 # 各類訓練集個數
num1 = 0 # 同上
pNumList = [p0Num, p1Num]
pDenomList = [p0Denom, p1Denom]
numList = [num0, num1]
for i in range(trainNum): # 周遊每一個訓練樣本
for j in range(2): # 周遊每個類别
if classVec[i] == j: # 如果第i個訓練樣本為j類樣本
pNumList[j] += trainMat[i] # 在該樣本出現的所有單詞詞頻+1
pDenomList[j] += sum(trainMat[i]) # 同理對分母增加的此樣本所有單詞詞頻
numList[j] += 1 # 該類文檔數目加1
# 取log對數仍然是拉普拉斯平滑原理
pVect, pNum = [], []
for index in range(2):
pVect.append(np.log(pNumList[index] / pDenomList[index]))
pNum.append(numList[index] / float(trainNum))
return pVect, pNum # 傳回屬于非侮辱類、侮辱類和整個訓練樣本屬于侮辱類的機率
'''
測試樸素貝葉斯分類器
vec2Classify:向量化的測試樣本
'''
def classifyNB(vec2Classify, pVect, pNum):
# 計算公式 log(P(F1|C))+log(P(F2|C))+....+log(P(Fn|C))+log(P(C))
vpl = [] # 文檔分類到各類的機率
for x in range(2):
vpl.append(sum(vec2Classify * pVect[x]) + np.log(pNum[x]))
reslist = ['非侮辱類', '侮辱類']
index = [vpl.index(res) for res in vpl if res == max(vpl)]
return reslist[index[0]] # 傳回分類值
'''
樸素貝葉斯測試函數
'''
def testingNB(testVec):
dataSet, classVec = loadDataSet() # 建立實驗樣本
vocabList = createVocabList(dataSet) # 建立詞彙表
trainMat = get_trainMat(dataSet, vocabList) # 将訓練樣本向量化
pVect, vpl = trainNB(trainMat, classVec) # 訓練樸素貝葉斯分類器
thisone = setofwords2Vec(vocabList, testVec) # 測試樣本向量化
return classifyNB(thisone, pVect, vpl)
if __name__ == '__main__':
# 測試樣本1
testVec1 = ['love', 'my', 'dalmation']
print('分類結果是:', testingNB(testVec1))
# 測試樣本2
testVec2 = ['stupid', 'garbage']
print('分類結果是:', testingNB(testVec2))
步驟總結
-
1. 收集資料并預處理
針對本例來說,省略了收集資料和預處理的大多數步驟。若無則應對訓練集檔案夾内的檔案讀取,并去掉多餘的符号(若有停用詞則删去對應的詞),最後采用分詞如jieba進行切分,然後進行下一步驟。
-
2. 向量化處理
将得到的訓練集(和測試集)清單建構不重複的單詞集合,然後向量化每一個樣本清單,建構訓練資料的向量集。
-
3. 訓練模型
利用貝葉斯公式計算對應的詞頻進行相應機率計算,注意可能出現0機率現象,是以需要拉普拉斯平滑處理,即特殊初始化操作。
-
4. 測試驗證模型
将測試資料同樣進行向量化處理,然後計算每類機率情況,取機率最大值分類即為預測的類别。本例樣本少不再進行驗證準确性。
樸素貝葉斯分類調用(sklearn)
'''以上涉及擷取資料、求詞彙表、求訓練資料集等方法省略,即替代方法trainNB、classifyNB'''
from sklearn.naive_bayes import GaussianNB
from sklearn.naive_bayes import MultinomialNB
from sklearn.naive_bayes import BernoulliNB
'''調用sklearn'''
def sklearnTest(testVec, testDoc):
# 重複步驟
dataSet, classVec = loadDataSet()
vocabList = createVocabList(dataSet)
trainMat = get_trainMat(dataSet, vocabList)
testVec = setofwords2Vec(vocabList, testVec)
X = np.array(trainMat)
Y = np.array(classVec)
testVec = np.array(testVec)
if testDoc == 'GaussianNB': # 高斯樸素貝葉斯
clf = GaussianNB()
elif testDoc == 'MultinomialNB': # 多項分布貝葉斯
clf = MultinomialNB()
elif testDoc == 'BernoulliNB': # 伯努利分布貝葉斯
clf = BernoulliNB()
clf.fit(X, Y)
# 測試
input = [testVec]
index = clf.predict(input)
resList = ['非侮辱類', '侮辱類']
return resList[index[0]]
if __name__ == '__main__':
# 測試樣本1
testVec1 = ['love', 'my', 'dalmation']
print('普通樸素貝葉斯分類結果是:', testingNB(testVec1))
print('高斯樸素貝葉斯分類結果是:', sklearnTest(testVec1, 'GaussianNB'))
print('多項樸素貝葉斯分類結果是:', sklearnTest(testVec1, 'MultinomialNB'))
print('伯努利樸素貝葉斯分類結果是:', sklearnTest(testVec1, 'BernoulliNB'))
# 測試樣本2
testVec2 = ['stupid', 'garbage']
print('普通樸素貝葉斯分類結果是:', testingNB(testVec2))
print('高斯樸素貝葉斯分類結果是:', sklearnTest(testVec2, 'GaussianNB'))
print('多項樸素貝葉斯分類結果是:', sklearnTest(testVec2, 'MultinomialNB'))
print('伯努利樸素貝葉斯分類結果是:', sklearnTest(testVec2, 'BernoulliNB'))
運作結果
參考:樸素貝葉斯算法一步步教你輕松學樸素貝葉斯模型算法Sklearn深度篇3