朴素贝叶斯(二)实现NBCorpus分类(附代码和数据)

理论可参考 :朴素贝叶斯(一)

公式:(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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章