天天看點

樸素貝葉斯基于機率論的分類算法

機器學習算法的基礎當屬機率論,是以了解和使用機率論在機器學習中就顯得尤為重要。本文給大家提供一個使用機率分類的方法——樸樹貝葉斯。如果寫出一個最簡單的貝葉斯分類器,當你完成這個分類器後可以對機率分類器就有一個更好的了解。

Source: WikiPedia In probability theory and statistics, Bayes’ theorem (alternatively Bayes’ law or Bayes’ rule) describes the probability of an event, based on prior knowledge of conditions that might be related to the event. For example, if cancer is related to age, then, using Bayes’ theorem, a person’s age can be used to more accurately assess the probability that they have cancer, compared to the assessment of the probability of cancer made without knowledge of the person’s age.

貝葉斯引入先驗知識和邏輯推理來處理不确定命題。

分類問題可以看做構造分類器。

已知集合I={y1,y2,…,yn}I={y1,y2,…,yn}、C={y1,y2,…,yn}C={y1,y2,…,yn},映射規則y=f(x)y=f(x), 使得∀xi∈I∀xi∈I有且僅有一個yj∈Cyj∈C使得yj=f(xi)yj=f(xi)成立。(不考慮模糊數學裡的模糊集情況)

其中,CC為類别集合,每一個元素是一個類别,而II為項集合,每一個元素是一個待分類項,ff為分類器。

簡單來說,使用機率分類就是,計算每一個待分類項屬于某一項的機率,最後使用最大機率作為此項的類别。

貝葉斯分類是一類分類算法的總稱,這類算法均以貝葉斯定理為基礎,故統稱為貝葉斯分類。

如果已知P(B|A)P(B|A), 求P(A|B)P(A|B), 那麼貝葉斯準則告訴我們可以通過以下方式計算:

p(A|B)=p(B|A)p(A)p(B)p(A|B)=p(B|A)p(A)p(B)

貝葉斯定理之是以很有用,就是生活中經常遇到,很容易甚至直接得出P(B|A)P(B|A)的機率,但是P(A|B)P(A|B)的機率就非常困難,并且我們也關心這個機率,那麼就可以友善的使用貝葉斯定理。

樸素貝葉斯分類器通常有兩種實作方式:一是基于貝努力模型,二是基于多項式模型實作。有兩個假設:假設一是特征之間互相獨立,假設二是每個特征權重相等。

理論上,樸素貝葉斯是一個條件機率模型:

設A={a1,a2,…,an}A={a1,a2,…,an}為待分類項, 其中a為A項的獨立特征,有類别集合C={c1,c2,…,cn}C={c1,c2,…,cn}, 分别計算P(c1|A),P(c2|A),…,P(cn|A)P(c1|A),P(c2|A),…,P(cn|A)的機率, 若P(ck|A)=max{P(c1|A),P(c2|A),…,P(cn|A)}P(ck|A)=max{P(c1|A),P(c2|A),…,P(cn|A)}, 則說A∈kA∈k類.

然後,統計各個特征在各個類别下的機率,即:

P(a1|c1),P(a2|c1),…P(an|c1);P(a1|c2),P(a2|c2),…P(an|c2);P(a1|c3),P(a2|c3),…P(an|c3).P(a1|c1),P(a2|c1),…P(an|c1);P(a1|c2),P(a2|c2),…P(an|c2);P(a1|c3),P(a2|c3),…P(an|c3).

根據貝葉斯定理有

P(ci|A)=P(A|ci)p(ci)P(A)P(ci|A)=P(A|ci)p(ci)P(A)

因為分母對于所有類别為常數,又根據假設一,是以有:

P(A|ci)p(ci)=P(a1|ci),P(a2|ci),…P(an|ci)P(ci)P(A|ci)p(ci)=P(a1|ci),P(a2|ci),…P(an|ci)P(ci)

=n∏j=1P(aj|ci)P(ci)=∏j=1nP(aj|ci)P(ci)

這個例子來自于《Mechina Learning in Action》 源碼可以在這裡擷取:https://github.com/pbharrin/machinelearninginaction/blob/master/Ch04/bayes.py.

我們先準備好一些資料,主要這次使用的斑點犬愛好者的留言闆, 并且已經标注出是侮辱性(1)還是非侮辱性的語句(0)。

1

2

3

4

5

6

7

8

9

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 is abusive, 0 not

return postingList,classVec

統計所有出現的詞,建構一個字典Set

def createVocabList(dataSet):

vocabSet = set([]) #create empty set

for document in dataSet:

vocabSet = vocabSet | set(document) #union of the two sets

return list(vocabSet)

根據建構好的字典,生成向量。将生成一個與字典長度一樣的所有元素為0的向量,将句子中出現的單詞的位置标注為1,表示為出現。

def setOfWords2Vec(vocabList, inputSet):

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

根據字典長度生成同樣長度的元素為1的初始化向量,周遊每一條評論的所有詞,如果某詞出現就在向量對應就+1,統計完所有詞後除以詞條評論的總次數,獲得詞頻機率。

訓練算法後獲得三個傳回值,包括非侮辱性詞出現的機率向量、侮辱性詞出現的機率向量。

特别需要注意的:

計算多個機率的乘積時,如果出現某個類别為0,那麼結果也為0。為了避免這種影響,将生成1的初始向量,并将分母改成2(出現和不出現都是0.5)。

由于機率都是0以下數,因子非常小,導緻乘積結果也非常小,導緻程式下溢出或者得不到正确答案,采用對乘積取自然對數的方法避免。(f(x)f(x)與ln(f(x))ln(f(x))圖像相似,xx取值範圍是[0,0.5][0,0.5])

12345678910111213141516

def trainNB0(trainMatrix,trainCategory): numTrainDocs = len(trainMatrix) numWords = len(trainMatrix[0]) pAbusive = sum(trainCategory)/float(numTrainDocs) p0Num = ones(numWords); p1Num = ones(numWords) #change to ones() p0Denom = 2.0; p1Denom = 2.0 #change to 2.0 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) #change to log() p0Vect = log(p0Num/p0Denom) #change to log() return p0Vect,p1Vect,pAbusive

先建構分類器。根據各個特征互相獨立的假設,将待驗證項的各個特征向量化與訓練好的機率向量點乘,在将其結果乘以這個類别的機率獲得最終的機率,比較每個類比機率的大小,機率最大的類别即使待驗證項類别。

1234567

def classifyNB(vec2Classify, p0Vec, p1Vec, pClass1): p1 = sum(vec2Classify * p1Vec) + log(pClass1) #element-wise mult p0 = sum(vec2Classify * p0Vec) + log(1.0 - pClass1) if p1 > p0: return 1 else: return 0

然後測試分類器

12345678910111213

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)

至此,整個簡單的樸素貝葉斯分類器就編寫完成了。

在遇到文檔分類的需要的時候,通常都會使用樸素貝葉斯分類器來處理相關内容。我們須假設詞與詞之間是沒有關系(當然,我們知道這是不準确的),然後根據出現詞頻機率來訓練算法,通常是行之有效的方法。但是,這個方法需要人工提前去标注類别,是以在訓練資料的時候,可以使用28原則,将8份的資料進行訓練,然後測試2份的資料進行準确度判斷。