《机器学习实战》之朴素贝叶斯 个人注解(上)

《机器学习实战》备忘录,给自己做的笔记,温故而知新。

注:代码的实现依赖numpy,需要在文件中引入from numpy import *
# 创建一些样本raw_data_list,以及对应的样本类别label,这些样本(raw_data_list)是训练样本,
# 之后的贝叶斯分类器,就依据这些数据来分类你输入的数据的。
def load_data_set():
    raw_data_list = [['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']]
    '''
        label中的0代表着褒义, 1代表着贬义。label数组中一共有6个元素,分别代表着
        raw_data_list"每一行"的数组所对应的语义是褒是贬。
        比如"第一行":
        ['my', 'dog', 'has', 'flea', 'problems', 'help', 'please'] 
        对应label的第一个元素:0,也就是说第一行是褒义的,
        而"第二行":
        ['maybe', 'not', 'take', 'him', 'to', 'dog', 'park', 'stupid']
        则对应label的第二个元素:1,也就是说第二行是贬义的。
    '''
    label = [0, 1, 0, 1, 0, 1]
    return raw_data_list, label
'''
    将方法load_data_set执行后返回的raw_data_list,处理成没有重复元素的列表
    至于为何要将raw_data_list转换成列表,大概是因为raw_data_list是一个二维数组,
    每个数组元素又是一个一维数组,而且这个一维数组并不知道具体长度,结构不一致,
    不方便之后的计算,而转换成列表后,计算就方便多了。
'''
def create_no_repeat_data_list(data):
    # 创建一个空的set集合,python里的set是不会出现重复的元素的,
    # 使用set经常用于数据去重。
    data_set = set([])

    for item in data:
        # 遍历raw_data_list这个二维数组,将数组元素依次去重,并入之前创建的空的set集合中。
        data_set = data_set | set(item)

    # 转换成list,并返回。
    return list(data_set)

注:方法create_no_repeat_data_list的参数来源于方法load_data_set的执行结果,即:
data_set, label = load_data_set()
data_list = create_no_repeat_data_list(data_set)
'''
    参数data_list可以的是方法create_no_repeat_data_list执行后,所返回的list,
    参数input_word,则就是你所输入的话,
    这个方法的作用就是标记data_set中的每一条训练样本所出现的单词,在训练样本data_list里的次数,
    若是出现了则标记为1,没有则是0
    PS:data_list是一个列表,将二位数组data_set去重后,将其所有的元素都放进了data_list中。
'''
def detect_word(data_list, input_word):
    # 创建和训练样本一样大的数组,该数组每个元素都是0,因为还没开始标记嘛。
    return_vec = [0]*len(data_list)

    for word in input_word:
        if word in data_list:
            # 将出现在样本里的单词,在对应的位置上的数量加1。
            return_vec[data_list.index(word)] += 1
        else:
            print "The word :%s is not in the vocabulary!" % word

    # 将统计好的结果返回,这个结果之后会用到,用于概率计算。
    return return_vec

注:方法detect_word的使用方式:
data_list = create_no_repeat_data_list(data_set)
result = detect_word(data_list, ['quit', 'buying', 'worthless', 'dog', 'food', 'stupid'])

'''
    参数train_martix:可以是 方法detect_word执行后,所产生的数组,例如:
    trainMatrix = []
    for data in data_set:
        trainMatrix.append(detect_word(data_list, data))

    这个数组记录了每条训练样本里所出现的单词,在整个训练样本中,所出现的次数。
    参数train_category:指的是每个训练样本所对应的类别。

    这个方法的作用是根据train_martix这个记录次数的数组来计算,对应训练样本的类别的情况下,
    是这个单词的概率。比如当该条训练样本是贬义的情况下,当前单词是'dog'这个单词的概率。
'''
def train_nb0(train_martix, train_category):
    # 训练样本的总量。
    total_num = len(train_martix)
    # 获取整个样本里有多少个单词。
    words_vec_length = len(train_martix[0])
    # train_category,比如[0, 1, 1, 0], 0代表褒义, 1代表贬义,将其求和,就是贬义的样本的总数:0+1+1+0=2。
    # 贬义的样本的总数/样本总数=在整个样本中,随机一个样本是贬义样本的概率。
    p_bad_word = sum(train_category) / float(total_num)

    # 初始化一个数组,这个数组用来记录,当该训练样本是贬义时,整个样本中每个单词所出现的次数
    # zeros这个方法是创一个每个元素全为0的,长度为words_vec_length的数组
    # bad_vec = zeros(words_vec_length)
    # 但我们没用zeros来初始化数组,而改用了ones,即每个元素都为1,原因是因为,有些数据确实没有出现过的次数,
    # 这时候他就是0,概率在之后的计算中是需要相乘的,0乘谁都是0,概率会过于的小,反而不准确了。
    bad_vec = ones(words_vec_length)
    # 初始化一个数组,这个数组用来记录,当该训练样本是褒义时,整个样本中每个单词所出现的次数
    # good_vec = zeros(words_vec_length)
    good_vec = ones(words_vec_length)
    # 用于记录,当该训练样本是贬义时,整个样本中所出现的单词的总数
    # bad_word_total_num = 0.0
    # 弃用初始值是0的原因是因为bad_vec的初始值都是1,之后的计算需要bad_vec/bad_word_total_num,
    # 分子初始值变大了,分母的初始值也需要变大
    bad_word_total_num = 2.0
    # 用于记录,当该训练样本是褒义时,整个样本中所出现的单词的总数
    # good_word_total_num = 0.0
    good_word_total_num = 2.0

    # 遍历训练样本
    for index in range(total_num):
        # 如果这个样本是贬义的
        if train_category[index] == 1:
            # train_martix是记录这条训练样本里所出现的单词次数的数组,比如
            # bad_vec是[0,0,0,0,0], train_martix[index]是[0,0,1,0,1], 
            # train_martix可以是[[0,0,1,0,1],
            #                   [0,0,0,0,1]
            #                   [0,0,1,0,0]
            #                   [1,0,0,0,0]
            #                   [1,1,1,0,1]
            #                   [1,0,0,0,1]
            #                   ]
            bad_vec += train_martix[index]
            # sum(train_martix[index])就是这个训练样本里一共出现了几个单词,
            # 比如[0,0,1,0,1],求和后就是2,一共出现了2个单词
            bad_word_total_num += sum(train_martix[index])
        else:
            # 同理如上
            good_vec += train_martix[index]
            good_word_total_num += sum(train_martix[index])

    # bad_vec用来记录,当该训练样本是贬义时,整个样本中每个单词所出现的次数,
    # bad_word_total_num用来记录,训练样本是贬义时,整个样本中所出现的单词的总数
    # 两个相除,所得出的数组p_bad_vec,记录着当样本为贬义时,每个单词所出现的概率。
    p_bad_vec = bad_vec / bad_word_total_num
    # 同上,结果是当样本为褒义时,每个单词所出现的概率。
    p_good_vex = good_vec / good_word_total_num

    # 之所以返回log数,是因为计算出来的概率都特别的小,采用log后,数值会增大,
    # 而且log函数的变化趋势并不影响原先的概率
    return log(p_bad_vec), log(p_good_vex), p_bad_word
注:方法 train_nb0的使用方式:
trainMatrix = []
for data in data_set:
    trainMatrix.append(detect_word(data_list, data))

p_bad_vec, p_good_vex, p_bad_word = train_nb0(trainMatrix, label)

'''
    这个方法的作用:
    你所输入的话,对比其是褒义的概率,以及是贬义的概率,
    哪个概率大,就是哪一种话。

    参数:vec_array,他是一个数组,记录你所说的话,在样本中所出现的次数,比如:
    test_array = ['I', 'love', 'python'],test_array就是你所说的话。
    vec_array = array(detect_word(data_list, []))

    参数p_bad_vec就是train_nb0执行后所得出的:当样本为贬义时,每个单词所出现的概率。
    参数p_good_vex同上,是当样本为褒义时,每个单词所出现的概率。
    参数p_bad_word同上,是随机一个样本是贬义的概率。
'''
def classify_nb(vec_array, p_bad_vec, p_good_vex, p_bad_word):
    # vec_array*p_bad_vec:vec_array比如[1,0,0],乘p_bad_vec后,得到的就是,话是贬义时,
    # 话里的每个单词,那些在样本中存在的单词,他们出现的概率。
    # 将这个单词出现的概率相乘就是,当话是贬义时,正好是你这句话的概率,将这个概率记为P(A|B),
    # P(B)代表着话是贬义的概率。P(AB) = P(A|B) * P(B),P(AB)就是你这句话是贬义的概率。

    # 因为p_bad_vec已经经过log处理过了,所以p_bad_vec里面所有元素的相乘等于相加,
    # log(a*b*c) = log(a) + log(b) +log(c)
    # sum(vec_array*p_bad_vec)得到的就是P(A|B),再加上log(p_bad_word),相当于乘P(B)
    # sum(vec_array*p_bad_vec) + log(p_bad_word)便得到了这句话是贬义的概率
    p1 = sum(vec_array*p_bad_vec) + log(p_bad_word)
    # 同上,这句话是褒义的概率。
    p2 = sum(vec_array*p_good_vex) + log(1 - p_bad_word)

    if p1 > p2:
        return 'bad words'
    else:
        return 'good words'

注:

利用函数单调性解方程
函数单调性是函数一个非常重要的性质,由于单调函数

       中x与y是一对应的,这样我们就可把复杂的方程通过适当变形转化为型如
       方程,从而利用函数单调性解方程x=a,使问题化繁为简,而构造单调函数是解决问题的关键。
上文中将概率用自然对数处理的原因,一是为了能够让概率的绝对值变大,二是因为log函数是单调函数,随概率的增大而增大,减小而减小。

经过对数处理过的概率,函数变化的趋势并没有被影响,只是取值不同了,而我们最后只需要比出哪个概率更大,所以这样做并没有对最后的结果产生影响。


def classifiy(words, p_bad_vec, p_good_vex, p_bad_word):
    test_array = words.split(' ')
    vec_array = array(detect_word(data_list, test_array))
    print test_array, 'the classified as: ', classify_nb(vec_array, p_bad_vec, p_good_vex, p_bad_word)

data_set, label = load_data_set()
data_list = create_no_repeat_data_list(data_set)

trainMatrix = []
for data in data_set:
    trainMatrix.append(detect_word(data_list, data))

p_bad_vec, p_good_vex, p_bad_word = train_nb0(trainMatrix, label)

words = 'I love python'
classifiy(words, p_bad_vec, p_good_vex, p_bad_word)


 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值