文章目录

一、前文问题

改进前情感分析实践

1. 先看下改进前我们的代码计算部分

条件概率计算部分

练后的结果展示

2. 问题分析

可以看到此时的训练后的结果是,在开心和伤心的条件概率中存在0,的结果。那么在计算后验概率P(C|X)其中X是特征C是label。发现:计算的结果为0
原因是什么?
来看下我们的条件概率计算过程是什么:

'''
	在计算样本 x 属于某个类别 C 的后验概率时,朴素贝叶斯假设各个特征之间相互独立,即 
	p(x|C) = p(x1|C)p(x2|C)...p(xn|C),其中 x1, x2, ..., xn 分别是特征向量的不同维
	度。这个假设简化了计算过程,但是忽略了特征之间的相关性。	
	根据贝叶斯公式,朴素贝叶斯可表示为: p(C|x) = p(x|C)p(C)/p(x) 其中,p(x|C) 表示在类
	别 C 下特征向量 x 出现的概率,p(C) 表示类别 C 的先验概率,p(x) 表示特征向量出现的概
	率。由于对于所有类别都是相同的,所以可以省略分母 p(x)

	可以看到vec_0 =  array_0/_0计算的就是p(x|C) = p(x1|C)p(x2|C)...p(xn|C)
	也就是当是开心样本的时候,是特征 X 的概率。
	array_0,中的每一个维度就是一个特征,那么array_0将所有开行样本中的数据相加,也就计算出
	了,每个特征发生次数。那么就可以计算,当开心label时候,特征X发生的概率。即 每个特征发生
	次数/总的样本发生次数
	_0 计算的就是,开心样本数据中,总的样本发生次数。
'''

可以看到,array_0计算的是 每个特征发生次数/总的样本发生次数,那么如果我们的样本中某个特征发生次数为0,这个时候这进行p(x|C) = p(x1|C)p(x2|C)...p(xn|C)计算的时候,就会出现为0的情况。

二、针对问题进行解决

我们经过分析知道问题所在那么可以针对问题进行解决了。

1. 什么是拉普拉斯平滑技术

拉普拉斯平滑是一种用于解决朴素贝叶斯算法中零概率问题的技术。在计算条件概率时,有些情况下会出现某个特征在某个类别下没有出现过的情况,导致概率为零,这就无法使用贝叶斯公式进行计算。为了避免这种情况,可以对概率进行平滑处理,使得每个特征在每个类别下至少出现一次,从而避免概率为零的情况。而拉普拉斯平滑就是一种常用的平滑方法,它在计算概率时将每个特征的计数都加上一个常数k,从而保证每个特征至少出现k次。

2. 拉普拉斯优化-下溢上溢问题

在进行拉普拉斯平滑时,条件概率的计算会涉及多个特征的连乘积,这容易导致数值过小而出现下溢(underflow)或者上溢(overflow)的问题。因此,为了避免这种问题,在实际应用中通常会使用对数操作,将连乘积转换成加和运算,从而方便计算。同时,取对数还有一个好处是可以简化计算,因为对数具有以下常用的运算规则:

log(a*b) = log(a) + log(b)
log(a/b) = log(a) - log(b)
通过这些运算规则,可以大大简化计算,并且防止由于浮点数的精度限制而导致的计算误差。因此,在进行拉普拉斯优化时,经常会使用对数操作来计算条件概率。

3. 改进地方分析:

看到我们在进行初始化的时候,使用的是np.zeros()函数,也就造成前面说的问题。

4.改进优化

1.优化一,对条件概率计算进行优化

def train(data,label):
    '''
    :param data: 这里的数据是样本经过向量化后的向量信息
    :param label:  样本所对应的标签信息
    :return:
    1. vec_0 --- 开心的条件概率组
    2. vec_1 --- 伤心的条件概率组
    3. simple_1 --- 样本数据属于伤心的概率
    '''
    num_simple = len(data) #样本的数量
    num_words = len(data[0]) #统计每个样本的词的数据量

    simple_1 = sum(label)/float(num_simple)  #计算的是伤心的
    '''
    这里说明一下,为什么使用的是sum(label) 来进行计算:
    正常这里应该是,属于伤心的样本数据/总的样本数
    那么我们这里用sum的效果就是,伤心样本的数量,因为伤心
    的label为1,开心为0,所有label的和就是,伤心的数量
    这里取巧了。
    '''
    #todo 改进一
    # 进行条件概率数组初始化
    # array_0,array_1 = np.zeros(num_words),np.zeros(num_words)
    array_0,array_1 = np.ones(num_words),np.ones(num_words) #todo 改进一
    _0 = 2.0 #todo 改进
    _1 = 2.0 #这里是多求条件概率的分母进行初始化,至于怎么用后面说
	'''
	这里为甚使用np.ones,和2来进行优化,这个没有定数,你可以自己指定,
	用这个主要还是好计算
	'''


    #todo 对所有伤心的样本进行计算条件概率
    for i in range(num_simple):
        if label[i] == 1: #伤心
            array_1 += data[i]
            _1 += sum(data[i])
        else: #开心
            array_0 += data[i]
            _0 += sum(data[i])

    #todo 改进优化二
    # vec_0 =  array_0/_0
    vec_0 =  np.log(array_0/_0)
    vec_1 =  np.log(array_1/_1)
    return vec_0,vec_1,simple_1

改进后的结果

2.优化二,对后延概率计算进行优化

这里分析一下为什么使用对数优化

pro_0 = sum(vec_test*vec_0) * np.log(1 - simple_1)
print('pro_0:',pro_0)
#pro_1 = reduce(lambda x,y:x*y,vec_test*vec_1) * simple_1
pro_1 = sum(vec_test*vec_1) * np.log(simple_1)
'''
这里pro_0 计算的是后验概率P(C|X),我们的vec_0计算的是条件概率,
我们知道朴素贝叶斯:后验概率=先验概率*条件概率
根据 log(后验概率)=log(先验概率) + log(条件概率)
vec_0在训练的时候已经进行log()过了。
'''


print('pro_1:',pro_1)
if pro_0 > pro_1:
    return 0
else:
    return 1

改进后结果展示

3. 优化结果分析

可以看到优化后的结果,效果直线上升。
后续问题::::

但是我在进行优化后的代码执行的的时候,依然会遇到,不能识别的类别的情况,甚至,分类的记过都是一种情况,经过试验我发现是因为,样本数据不均衡导致的,我适当优化下数据集,就好了。
由此可以看到,机器学习,对数据特征工程依赖还是很大的,到没有深度端到端来的爽,但是机器学习有着深度不可替代的能力。

三、源码

改进后的源码

致谢

四、下一篇 学习如何做舆情分析