理論可參考 :樸素貝葉斯(一)
公式:(P(x)爲常數,可忽略不考慮)
平滑:
Nyk是類別爲yk的樣本個數,n是特徵的維數,Nyk,xi是類別爲yk的樣本中,第i維特徵的值是xi的樣本個數,α是平滑值。
在對NBCorpus詞分類時,帶入上面的公式可得:
某詞屬於某類別的概率 = (該類別該詞的個數 + 1/ 該類別詞的總數 + 所有類別所有不重複單詞總數) ×(該類別樣本個數 / 所有類別總樣本個數) / (所有類別該詞個數 / 所有類別所有詞個數)
所有類別該詞個數 / 所有類別所有詞個數 就是上面公式的P(x) ,它的值與類別無關,值相同,所有把它去掉,公式變成:
(該類別該詞的個數 + 1/ 該類別詞的總數 + 所有類別所有不重複單詞總數) ×(該類別樣本個數 / 所有類別總樣本個數)
(該類別樣本個數 / 所有類別總樣本個數) 就是上面公式的P(yk),即先驗概率。沒有使用平滑。
(該類別該詞的個數 + 1/ 該類別詞的總數 + 所有類別所有不重複單詞總數) 就是上面公式的P(x|yk),就是可能性。使用了平滑,分子加的1,分母加的 所有類別所有不重複單詞總數 都是平滑值。
如果按照上面的公式,就和上面的例子一樣,對於測試的樣本里的每個屬性計算出屬於每個類別的概率,對於每個類別每個屬性的概率相乘。但是,小數越乘越小,最終可能會導致數據會丟失成0,無法比較。解決辦法是對公式加ln操作,把乘法變成加法,且不破壞單調性。
分詞簡單例子:
上面的詞量比較少,沒有進行ln操作。但是如果詞量太大,舉例最後的計算變成 ln(3/4 * (3/7)^3 * 1/14 * 1/14 ) = ln(3/4) + 3*ln(3/7) + ln(1/14) + ln(1/14) 。
數據下載地址:http://download.csdn.net/download/u010668907/10263175
。。。。無語了,想上傳全部的NBCorpus庫,csdn一直說已經存在,整體的庫後續再說吧,上面的鏈接裏的是做下面算法計算的那部分,夠用了。
下面的代碼里加了ln操作,否則計算之後每個類別都是0,無法比較
# -*- coding:utf-8 -*-
# __author__='chenliclchen'
from __future__ import division
import os
import pickle
# 統計每個類別每個單詞出現的次數
# (該類別文檔數) / (所有類別文檔數)
# (該類別該單詞個數 + 1)/(該類別單詞個數 + 所有類別無重複單詞個數)
# step1,統計每個類別每個單詞出現的次數
def count_every_sort_every_word(sort_path):
count = {}
for item in os.listdir(sort_path):
file_path = os.path.join(sort_path, item)
with open(file_path) as file:
words = file.readlines()
for word in words:
word = word.replace('\r\n', '')
if word in count.keys():
count[word] += 1
else:
count[word] = 1
return count
# 保存step1的結果
def save_every_sort_every_word(count_dict, sort_path):
file = open(sort_path, 'wb')
pickle.dump(count_dict, file)
file.close()
# 計算所有類別不重複單詞總數, 總詞數包括重複
def count_all_words(count_path):
all_words_no_repeat = set()
all_words_count = 0
for item in os.listdir(count_path):
file = open(os.path.join(count_path, item), 'rb')
one_sort = pickle.load(file)
all_words_no_repeat.update(one_sort.keys())
for key in one_sort.keys():
all_words_count += one_sort[key]
return len(all_words_no_repeat), all_words_count
# 每個類別樣本數量
def count_every_sort_docu(sort_path):
count_docu = {}
all_docu_num = 0
for item in os.listdir(sort_path):
one_sort_path = os.path.join(sort_path, item)
count_docu[item] = len(os.listdir(one_sort_path))
all_docu_num += count_docu[item]
count_docu['all'] = all_docu_num
return count_docu
# 返回某個類別所有詞的數據量
def count_one_sort(sort_path):
all_words_count = 0
file = open(sort_path, 'rb')
one_sort = pickle.load(file)
file.close()
for key in one_sort.keys():
all_words_count += one_sort[key]
return all_words_count
def get_test_words(test_path):
file = open(test_path)
words = file.readlines()
file.close()
for ind, word in enumerate(words):
words[ind] = word.replace('\r\n', '')
return words
if __name__ == '__main__':
base_path = './NBCorpus'
sort_path = os.path.join(base_path, 'train')
count_path = os.path.join(base_path, 'count')
# 計算並保存step1的數據
for one_sort in os.listdir(sort_path):
one_sort_path = os.path.join(sort_path, one_sort)
one_sort_count = count_every_sort_every_word(one_sort_path)
sort_name = os.path.split(one_sort_path)
one_count_path = os.path.join(count_path, sort_name[-1])
save_every_sort_every_word(one_sort_count, one_count_path)
sort_prob = {}
test_path = os.path.join(base_path, 'test', '487141newsML.txt')
test_words = get_test_words(test_path)
all_words_num, all_words_num_repeat = count_all_words(count_path)
every_docu = count_every_sort_docu(sort_path)
for item in os.listdir(count_path):
one_sort_count_path = os.path.join(count_path, item)
one_sort_num = count_one_sort(one_sort_count_path)
file = open(one_sort_count_path, 'rb')
one_sort_count = pickle.load(file)
prior = every_docu[item] / every_docu['all']
import math
# 加入ln操作
all_words_prob = math.log(prior)
for word in test_words:
word_num = one_sort_count.get(word, 0)
word_prob = (word_num + 1) / (one_sort_num + all_words_num)
# all_words_prob *= word_prob
# 加入ln操作
all_words_prob += math.log(word_prob)
sort_prob[item] = all_words_prob
file.close()
print filter(lambda x:max(sort_prob.values()) == sort_prob[x], sort_prob)[0]
最終的輸出結果就是 :AFRICA