续着上一篇说到的Iris数据集多分类问题,基于softmax的逻辑回归分类需要通过数据训练一轮轮地降低损失函数,以获得最佳的参数w和b。
而对于朴素贝叶斯算法来说,其核心源于贝叶斯公式,这个伟大的业余(?)数学家贝叶斯的著名公式:
P
(
a
∣
b
)
=
P
(
b
∣
a
)
P
(
a
)
P
(
b
)
\begin{aligned} P(a|b) = \frac{P(b|a)P(a)}{P(b)} \end{aligned}
P(a∣b)=P(b)P(b∣a)P(a)
其表现的是先验概率P(a)和后验概率P(a|b)之间的概率。
我们可以用一个形象的例子来说,比如相亲:在不知道其他情况下,我们知道一个普通小伙子被姑娘看上的概率是0.4。根据后续的交流,我们发现这个小伙子为人诚实,和蔼可亲,那么根据常识,被看上的概率应该提高了;如果这个小伙子收入年入百万,英俊潇洒,身高185…那么原来这个0.4的概率就会被不断地修正,不断地升高接近1.0。
我们刚刚这个例子中,一开始那个0.4的概率就是先验概率,后面不断地补充和小伙子相关的其他特征,不断修正得到的概率就是后验概率。
利用贝叶斯公式,我们可以定性的发现先验概率、后验概率与这些其他相关特征发生概率之间的关系。
回到朴素贝叶斯算法中,我们做多分类,也可以采用这种思想。一开始我们可以以这个类别在这个数据集中出现的比例为先验概率,在此之后,我们根据其他特征发生的概率、在这个实体是这个类别条件下发生此特征的概率来计算后验概率,之后比较后验概率(修正后的概率)的大小,选择后验概率最大的作为一个实例的预测值。
对于离散型特征,我们可以用每个特征出现的次数来作为概率:
P
(
x
i
=
v
∣
y
=
c
)
=
N
x
i
=
v
,
y
=
c
N
y
=
c
\begin{aligned} P(x_i=v|y=c) = \frac{N_{x_i=v,y=c}}{N_{y=c}} \end{aligned}
P(xi=v∣y=c)=Ny=cNxi=v,y=c
由于
P
(
x
)
P(x)
P(x)可以认为是一个常数,所以最终我们需要比较
a
r
g
m
a
x
y
P
(
y
=
c
)
∏
i
n
P
(
x
i
=
v
∣
y
=
c
)
\begin{aligned} argmax_y P(y=c)\prod_{i}^{n}P(x_i=v|y=c) \end{aligned}
argmaxyP(y=c)i∏nP(xi=v∣y=c)
这里 x i = v x_i=v xi=v代表第i个特征值为v,y=c代表为第c类
对于连续型特征,我们可以任务特征服从一维正态分布,用其概率密度函数来表示概率:
f
(
x
i
=
x
∣
y
=
c
)
=
1
2
π
σ
e
x
p
(
−
(
x
−
μ
)
2
2
σ
2
)
\begin{aligned} f(x_i=x|y=c) = \frac{1}{\sqrt{2π}\sigma}exp(-\frac{(x-\mu)^2}{2\sigma^2}) \end{aligned}
f(xi=x∣y=c)=2πσ1exp(−2σ2(x−μ)2)
同理最终我们要求:
a
r
g
m
a
x
y
P
(
y
=
c
)
∏
i
n
f
(
x
i
∣
y
=
c
)
\begin{aligned} argmax_y P(y=c)\prod_{i}^{n}f(x_i|y=c) \end{aligned}
argmaxyP(y=c)i∏nf(xi∣y=c)
我们可以发现,朴素贝叶斯不需要数据一轮轮的训练来下降损失函数,只需要利用数据计算公式进行计算和比较即可。所以朴素贝叶斯比起softmax逻辑分类来说,“训练”是轻松很多了。(我要是softmax算法我可能要骂娘了hhh)
下面上代码:
import pandas as pd
from sklearn import datasets
import numpy as np
# 利用sklearn的datasets下载iris数据集
x_data = datasets.load_iris().data
y_data = datasets.load_iris().target
#打乱顺序,分为训练集测试集
np.random.seed(100)
np.random.shuffle(x_data)
np.random.seed(100)
np.random.shuffle(y_data)
dataset_size = len(x_data)
trainData_size = int(dataset_size*0.7)
testData_size = dataset_size - trainData_size
x_train = x_data[:trainData_size]
y_train = y_data[:trainData_size]
x_test = x_data[trainData_size:]
y_test = y_data[trainData_size:]
# 转为dataframe
train = pd.DataFrame(x_train,columns=['SpealLength', 'SpealWidth', 'PetalLength', 'PetalWidth'])
train.insert(0,'target',y_train)
# 训练过程(算公式过程)
# 开辟一个3*8的ndarray存3类4个特征的均值和标准差
w = np.zeros((3,8))
# 存储每类的数量
type_size = np.zeros(3)
# 分别对三类数据对应特征求均值和标准差
for type in range(3):
temp = train[train['target']==type]
type_size[type] = len(temp)
temp = temp[['SpealLength', 'SpealWidth', 'PetalLength', 'PetalWidth']]
mean = temp.mean()
std = temp.std()
for feature in range(4):
w[type][feature * 2] = mean.iloc[feature]
w[type][feature * 2 + 1] = std.iloc[feature]
# 求正态概率密度
def normal_f(mean,std,x):
x = np.array(x)
return 1/(np.sqrt(2*np.pi)*std)*np.exp(-np.square(x-mean)/(2*np.square(std)))
acc_num = 0
for i in range(len(x_test)):
# 后验概率
post_prob = np.zeros(3)
for type in range(3):
#先验概率赋值给后验概率
post_prob[type] = type_size[type] / len(x_train)
for feature in range(4):
post_prob[type] *= normal_f(w[type][feature * 2],w[type][feature * 2 + 1],x_test[i][feature])
ans = np.argmax(post_prob)
print("guess:",ans,'\n',"right_ans:",y_test[i])
if ans == y_test[i]:
acc_num += 1
print("acc:",acc_num/len(x_test))
最终准确率100%