文章目录
- 一、前文问题
- 1. 先看下改进前我们的代码计算部分
- 2. `问题分析`:
- 二、针对问题进行解决
- 1. 什么是`拉普拉斯平滑`技术
- 2. 拉普拉斯优化-下溢上溢问题
- 3. 改进地方分析:
- 4.改进优化
- 1.优化一,对条件概率计算进行优化
- 2.优化二,对后延概率计算进行优化
- 3. 优化结果分析
- 三、源码
- 四、下一篇 学习如何做舆情分析
一、前文问题
改进前情感分析实践
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. 优化结果分析
可以看到优化后的结果,效果直线上升。
后续问题
::::
但是我在进行优化后的代码执行的的时候
,依然会遇到,不能识别的类别的情况
,甚至,分类的记过都是一种情况,经过试验我发现是因为,样本数据不均衡导致
的,我适当优化下数据集
,就好了。
由此可以看到,机器学习,对数据特征工程依赖还是很大的,到没有深度端到端来的爽,但是机器学习有着深度不可替代的能力。
三、源码
改进后的源码
致谢