1. 程式人生 > >樸素貝葉斯文字分類(python程式碼實現)

樸素貝葉斯文字分類(python程式碼實現)

樸素貝葉斯(naive bayes)法是基於貝葉斯定理與特徵條件獨立假設的分類方法。

  • 優點:在資料較少的情況下仍然有效,可以處理多分類問題。
  • 缺點:對入輸入資料的準備方式較為敏感。
  • 使用資料型別:標稱型資料。

下面從一個簡單問題出發,介紹怎麼使用樸素貝葉斯解決分類問題。
一天,老師問了個問題,只根據頭髮和聲音怎麼判斷一位同學的性別。
為了解決這個問題,同學們馬上簡單的統計了7位同學的相關特徵,資料如下:

頭髮 聲音 性別

這個問題之前用決策樹做過了,這裡我們換一種思路。
要是知道男生和女生頭髮長短的概率以及聲音粗細的概率,我們就可以計算出各種情況的概率,然後比較概率大小,來判斷性別。
假設抽樣樣本足夠大,我們可以近似認為可以代表所有資料,假設上位7位同學能代表所有資料,這裡方便計算~
由這7位同學,我們馬上得出下面表格概率分佈。

性別 頭髮長 聲音粗
1/3 1
3/5 3/5

假設頭髮和聲音都是獨立特徵,於是
男生頭髮長聲音粗的概率=3/8*1/3*1=1/8
女生頭髮長聲音粗的概率=5/8*3/5*3/5=9/40
因為1/8<9/40所以如果一個人,頭髮長,聲音粗,那麼這個人更可能是女生,於是出現這些特徵就是女生。其他特徵依次類推。
這就是樸素貝葉斯分類方法。是的,就是這麼簡單。
下面來解釋原理,先看貝葉斯公式:
這裡寫圖片描述

公式中,事件Bi的概率為P(Bi),事件Bi已發生條件下事件A的概率為P(A│Bi),事件A發生條件下事件Bi的概率為P(Bi│A)。
帶入我們的例子中,判斷頭髮長的人性別:
P(男|頭髮長)=P(頭髮長|男)*P(男)/P(頭髮長)
P(女|頭髮長)=P(頭髮長|女)*P(女)/P(頭髮長)
判斷頭髮長、聲音粗的人性別:
P(男|頭髮長聲音粗)=P(頭髮長|男)P(聲音粗|男)*P(男)/P(頭髮長聲音粗)
P(女|頭髮長聲音粗)=P(頭髮長|女)P(聲音粗|女)*P(女)/P(頭髮長聲音粗)
可以看到,比較最後比較概率,只用比較分子即可。也就是前面計算頭髮長聲音粗的人是男生女生的概率。

下面應用於文字分類,文字分類不想上面例子有具體的特徵,需先建立文字特徵。以下為文字分類的一個簡單例子。

# _*_ coding:utf-8 _*_
from numpy import *
import re
import random

def loadDataSet(): #建立樣例資料
    postingList = [['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']]
    classVec = [0, 1, 0, 1, 0, 1]  #1代表髒話
    return postingList, classVec

def createVocabList(dataSet):  #建立詞庫 這裡就是直接把所有詞去重後,當作詞庫
    vocabSet = set([])
    for document in dataSet:
        vocabSet = vocabSet | set(document)
    return list(vocabSet)

def setOfWords2Vec(vocabList, inputSet):  #文字詞向量。詞庫中每個詞當作一個特徵,文字中就該詞,該詞特徵就是1,沒有就是0
    returnVec = [0] * len(vocabList)
    for word in inputSet:
        if word in vocabList:
            returnVec[vocabList.index(word)] = 1
        else:
            print("the word: %s is not in my Vocabulary!" % word)
    return returnVec


def trainNB0(trainMatrix, trainCategory):
    numTrainDocs = len(trainMatrix)
    numWords = len(trainMatrix[0])
    pAbusive = sum(trainCategory) / float(numTrainDocs)
    p0Num = ones(numWords) #防止某個類別計算出的概率為0,導致最後相乘都為0,所以初始詞都賦值1,分母賦值為2.
    p1Num = ones(numWords)
    p0Denom = 2
    p1Denom = 2
    for i in range(numTrainDocs):
        if trainCategory[i] == 1:
            p1Num += trainMatrix[i]
            p1Denom += sum(trainMatrix[i])
        else:
            p0Num += trainMatrix[i]
            p0Denom += sum(trainMatrix[i])
    p1Vect = log(p1Num / p1Denom)  #這裡使用了Log函式,方便計算,因為最後是比較大小,所有對結果沒有影響。
    p0Vect = log(p0Num / p0Denom)
    return p0Vect, p1Vect, pAbusive

def classifyNB(vec2Classify,p0Vec,p1Vec,pClass1): #比較概率大小進行判斷,
    p1 = sum(vec2Classify*p1Vec)+log(pClass1)
    p0 = sum(vec2Classify*p0Vec)+log(1-pClass1)
    if p1>p0:
        return 1
    else:
        return 0

def testingNB():
    listOPosts,listClasses = loadDataSet()
    myVocabList = createVocabList(listOPosts)
    trainMat=[]
    for postinDoc in listOPosts:
        trainMat.append(setOfWords2Vec(myVocabList, postinDoc))
    p0V,p1V,pAb = trainNB0(array(trainMat),array(listClasses))
    testEntry = ['love', 'my', 'dalmation'] # 測試資料
    thisDoc = array(setOfWords2Vec(myVocabList, testEntry))
    print(testEntry,'classified as: ',classifyNB(thisDoc,p0V,p1V,pAb))
    testEntry = ['stupid', 'garbage'] # 測試資料
    thisDoc = array(setOfWords2Vec(myVocabList, testEntry))
    print(testEntry,'classified as: ',classifyNB(thisDoc,p0V,p1V,pAb))

if __name__=='__main__':
    testingNB()
#輸出結果
['love', 'my', 'dalmation'] classified as:  0
['stupid', 'garbage'] classified as:  1

參考:
- Machine Learning in Action
- 統計學習方法