機器學習實戰(三)樸素貝葉斯NB(Naive Bayes)
阿新 • • 發佈:2018-11-02
目錄
學習完機器學習實戰的樸素貝葉斯,簡單的做個筆記。文中部分描述屬於個人消化後的理解,僅供參考。
所有程式碼和資料可以訪問 我的 github
如果這篇文章對你有一點小小的幫助,請給個關注喔~我會非常開心的~
0. 前言
貝葉斯演算法,是一類基於概率論的分類方法。樸素貝葉斯(Naive Bayes),是採取了貝葉斯最原始、最簡單的假設的演算法。
樸素貝葉斯演算法給出例項屬於各個類別的概率,然後選擇概率最大的一類。貝葉斯決策理論的核心思想是選擇具有最高概率的決策。
- 優點:在資料較少的情況下,仍然十分有效果
- 缺點:對於輸入資料的準備方式比較敏感
- 適用資料型別:標稱型資料
樸素貝葉斯假設:
- 所有特徵都是獨立的(即每個特徵出現的可能性與其他特徵無關)
- 每個特徵都是同等重要的
1. 條件概率
稱為事件 的概率。 稱為在事件 發生的情況下,事件 的概率,這就是條件概率。根據概率論,給出以下的定義:
為了計算 ,我們可以設法通過計算 來得到。
2. 樸素貝葉斯(Naive Bayes)
假設類別為 ,測試樣本為 ,要計算 ,可以通過計算 ,因為假設各個特徵獨立,所以有:
其概率,等於各個特徵在此類別前提下的概率之積。
- :屬於不同類別的計算公式中, 為分母相同,可以不計算
- :為訓練集中,第 個類別的概率,即第 個類別的頻率(出現的樣本數/總樣本數)
- :為訓練集屬於第 個類別的樣本中,第 個特徵的概率,即第 個特徵的頻率(特徵出現的次數/總特徵數)
通過以上可得出樸素貝葉斯分類器,對於測試樣本 ,只需將每個特徵的特徵值與對應特徵的概率相乘,就可得到 ,特徵值相當於一個權重。
3. 樸素貝葉斯應用於文字分類
在文字分類中,特徵值的選取可有兩種:
- 詞集模型(set-of-words model):只判斷這個詞彙是否出現,即只有 和
- 詞袋模型(bag-of-words model):判斷這個詞彙出現的次數
演算法可根據以下步驟進行,其他應用場合也可參考:
- 將訓練集中所有文字的單詞拆分出來,去重,獲得詞彙表
- 根據詞彙表(特徵),對每個文字提取特徵值,將文字轉換為向量
- 計算 和
- 將測試文字轉換為向量
- 將向量的每一個元素與對應的概率相乘,即第 個元素的特徵值乘以
- 計算出每一個類別的 ,選擇最大概率的類別,作為分類結果
4. 實戰案例
以下將展示書中案例的程式碼段,所有程式碼和資料可以在github中下載:
4.1. 垃圾郵件分類案例
# coding:utf-8
from numpy import *
import re
"""
垃圾郵件分類案例
"""
# 根據所有樣本集合建立詞彙表
def createVocabList(dataSet):
vocabSet = set()
for document in dataSet:
# 集合求並集
vocabSet = vocabSet | set(document)
return list(vocabSet)
# 通過詞彙表,文字轉換為向量
# 詞集模型(每個詞只記錄是否有出現)
def setOfWords2Vec(vocabList, inputSet):
returnVec = [0] * len(vocabList)
for word in inputSet:
if word in vocabList:
returnVec[vocabList.index(word)] = 1
return returnVec
# 通過詞彙表,文字轉換為向量
# 詞袋模型(每個詞記錄出現的次數)
def bagOfWords2VecMN(vocabList, inputSet):
returnVec = [0] * len(vocabList)
for word in inputSet:
if word in vocabList:
returnVec[vocabList.index(word)] += 1
return returnVec
# 訓練NB分類器
def trainNB0(trainMatrix, trainCategory):
# 文件數量
numTrainDocs = len(trainMatrix)
# 向量中特徵(單詞)的數量
numWords = len(trainMatrix[0])
# 類別1佔總文件的比例
pAbusive = sum(trainCategory) / float(numTrainDocs)
# 為防止再之後計算過程中某一個特徵的概率為0,導致總的概率為0,不採用以下
# p0Num = zeros(numWords)
# p1Num = zeros(numWords)
# p0Denom = 0.0
# p1Denom = 0.0
p0Num = ones(numWords)
p1Num = ones(numWords)
p0Denom = 2.0
p1Denom = 2.0
# 遍歷每一個文字向量
for i in range(numTrainDocs):
# 如果向量屬於類別1
if trainCategory[i] == 1:
# 通過向量,計算文件每個詞彙的出現次數
p1Num += trainMatrix[i]
# 計算類別1中,單詞的總數量
p1Denom += sum(trainMatrix[i])
else:
p0Num += trainMatrix[i]
p0Denom += sum(trainMatrix[i])
# 類別1中,每個單詞出現的次數/總單詞數=每個單詞的出現比例
# 為防止數值太小,對其取Log
p1Vect = log(p1Num / p1Denom)
p0Vect = log(p0Num / p0Denom)
return p0Vect, p1Vect, pAbusive
# 分類演算法
def classifyNB(vec2Classify, p0Vec, p1Vec, pClass1):
# 因為 p1Vec 取過對數,log(x1)+...+log(xn)=log(x1*...*xn) 等於乘積
p1 = sum(vec2Classify * p1Vec) + log(pClass1)
p0 = sum(vec2Classify * p0Vec) + log(1 - pClass1)
if p1 > p0:
return 1
else:
return 0
# 檔案解析
def textParse(bigString):
listOfTokens = re.split('\W+', bigString)
return [tok.lower() for tok in listOfTokens if len(tok) > 2]
# 獲取高頻的前30個詞彙
def calcMostFreq(vocabList, fullText):
freqDict = {}
# 遍歷詞彙表中的每一個詞
for token in vocabList:
freqDict[token] = fullText.count(token)
# 排序
sortedFreq = sorted(freqDict.items(), key=lambda item: item[1], reverse=True)
return sortedFreq[:30]
# 垃圾郵件測試
def spamTest():
docList = []
classList = []
fullText = []
# 遍歷正的資料來源和反的資料來源
for i in range(1, 26):
wordList = textParse(open('email/spam/%d.txt' % i).read())
docList.append(wordList)
fullText.extend(wordList)
classList.append(1)
wordList = textParse(open('email/ham/%d.txt' % i).read())
docList.append(wordList)
fullText.extend(wordList)
classList.append(0)
# 建立詞彙表
vocabList = createVocabList(docList)
# 獲取高頻詞彙
top30Words = calcMostFreq(vocabList, fullText)
# 去除高頻詞彙,因高頻詞彙很可能是冗餘詞彙
for pairW in top30Words:
if pairW[0] in vocabList:
vocabList.remove(pairW[0])
trainingSet = list(range(50))
testSet = []
# 選擇測試向量
for i in range(10):
randIndex = int(random.uniform(0, len(trainingSet)))
testSet.append(trainingSet[randIndex])
del (trainingSet[randIndex])
trainMat = []
trainClasses = []
# 建立訓練集合
for docIndex in trainingSet:
# 將文字集合轉換為文字向量矩陣
trainMat.append(bagOfWords2VecMN(vocabList, docList[docIndex]))
trainClasses.append(classList[docIndex])
# p0V: 類別0中,每個詞彙出現的比例
# p1V: 類別1中,每個詞彙出現的比例
# pSpam: 類別1的文字數量佔總文字數量的比例
p0V, p1V, pSpam = trainNB0(array(trainMat), array(trainClasses))
correctCount = 0
for docIndex in testSet:
wordVector = bagOfWords2VecMN(vocabList, docList[docIndex])
if classifyNB(array(wordVector), p0V, p1V, pSpam) == classList[docIndex]:
correctCount += 1
print('the correct rate is: ', float(correctCount) / len(testSet))
return vocabList, p0V, p1V
# 獲取最具表徵性的詞彙
def getTopWords(vocabList, p0V, p1V):
top0 = []
top1 = []
for i in range(len(p0V)):
if p0V[i] > -6.0:
top0.append((vocabList[i], p0V[i]))
if p1V[i] > -6.0:
top1.append((vocabList[i], p1V[i]))
sorted0 = sorted(top0, key=lambda pair: pair[1], reverse=True)
print("***** 0 *****")
for item in sorted0:
print(item[0])
sorted1 = sorted(top1, key=lambda pair: pair[1], reverse=True)
print('***** 1 *****')
for item in sorted1:
print(item[0])
if __name__ == '__main__':
vocabList, p0V, p1V = spamTest()
# getTopWords(vocabList, p0V, p1V)
如果這篇文章對你有一點小小的幫助,請給個關注喔~我會非常開心的~