*注:本博客参考李宏毅老师2020年机器学习课程. 视频链接
目录
1 分类模型
回归模型和分类模型都是一种输入到输出之间的映射关系,不同的是,回归模型是值到值的映射,分类模型是值到类别的映射。
1.1 应用
分类模型应用广泛,比如可以解决如下几种问题:
- 输入患者的年龄、身高、体重、血糖、血压等等一系列指标,输出该患者得了哪一种病;
- 输入一张图片,判断该图片的内容是什么物体;
1.2 分类与回归的区别
如果使用回归的方法来解决分类问题,例如规定回归模型输出1则表示类别1,输出2则表示类别2,输出3则表示类别3,等等。这种方法看上去可行,然而实际上会出现以下问题:
- 从输出的设定来看,越接近于某个值则表示属于该类的可能性越大,然而,如果输出值远大于设定的最后一个类别的值,尽管直观上来看它仍属于最后一个类别,但是在梯度下降时,为了减小损失,最后两个类别的分界线将会偏离“正确”的位置,而向这个较大的输出值偏移。
- 由于相邻两个类别的输出值较接近,从逻辑上来说,它们应该具有较强的关联关系,而不相邻的类别之间具有较弱的关联。如果实际上这种关联不存在,将会导致模型精度变差。这使得训练数据的选取变难,能够解决的问题范围变小。
因此,不能简单地使用回归模型来处理分类问题。
1.3 分类模型
1.3.1 目标函数
在回归模型中,目标函数描述为
y
=
g
(
x
)
y=g\left(x\right)
y=g(x),表示从输入值到输出值之间的映射。在分类模型中,目标函数可能会更复杂一些。例如,假设我们使用分类模型处理一个三分类的问题,则可能需要三个函数,分别对应于三个类别,
c
i
=
g
i
(
x
)
c_i=g_i\left(x\right)
ci=gi(x),若
c
i
>
0
c_i>0
ci>0,则说明x属于类别i。最后比较大于0的
c
i
,
i
=
1
,
2
,
3
c_i,i=1,2,3
ci,i=1,2,3的大小,最大的即为x的类别。因而目标函数可以表示为:
y
=
f
(
x
)
=
m
a
x
_
i
n
d
e
x
(
g
i
(
x
)
)
y=f\left(x\right)=max\_index(g_i\left(x\right))
y=f(x)=max_index(gi(x))
1.3.2 损失函数
除此之外,在回归模型中,我们使用了均方差作为损失函数,在使用上述目标函数的情况下,由于相邻类别之间可能不存在关联关系,因此不能使用均方差。一种可能的替代方案是,使用分类错误的数量作为损失函数,例如:
L
o
s
s
(
f
)
=
∑
f
(
x
)
≠
y
^
Loss(f)=\sum{f\left(x\right)\neq\hat{y}}
Loss(f)=∑f(x)=y^
上述方法最大的问题在于,损失函数无法求导。
1.4 朴素贝叶斯分类模型
让我们反过来思考,假设有两类数据A和B,他们具有相同的特征维度 X X X。
现在,有一个新的个体,其特征值为
X
0
X_0
X0(假设
X
0
X_0
X0中各分量相互独立),我们要根据
X
0
X_0
X0去推断它属于哪一个类别。虽然A类和B类中都可能会出现特征值为
X
0
X_0
X0的个体,但由于两个类别的差异性,产生这一特征值的个体的概率是不一样的。设A类产生
X
0
X_0
X0特征值的概率为:
P
(
X
0
∥
A
)
P\left(X_0\|A\right)
P(X0∥A),B类产生该特征值的概率为
P
(
X
0
∥
B
)
P\left(X_0\|B\right)
P(X0∥B)。设不考虑特征值的情况下,对任意的个体,其属于A类和B类的概率分别为:
P
(
A
)
P\left(A\right)
P(A)和
P
(
B
)
P\left(B\right)
P(B),那么容易得到,
X
=
X
0
X=X_0
X=X0的个体属于A类的概率为:
P
(
A
∥
X
0
)
=
P
(
X
0
∥
A
)
P
(
A
)
P
(
X
0
∥
A
)
P
(
A
)
+
P
(
X
0
∥
B
)
P
(
B
)
(1)
P\left(A\|X_0\right)=\frac{P\left(X_0\|A\right)P\left(A\right)}{P\left(X_0\|A\right)P\left(A\right)+P\left(X_0\|B\right)P\left(B\right)} \tag{1}
P(A∥X0)=P(X0∥A)P(A)+P(X0∥B)P(B)P(X0∥A)P(A)(1)
因此,只要分别计算出个体属于A类和B类的概率,就可以估计该个体的类别。在这个例子中,由于有且仅有两个类别,因此
P
(
A
∥
X
0
)
+
P
(
B
∥
X
0
)
=
1
P\left(A\|X_0\right)+P\left(B\|X_0\right)=1
P(A∥X0)+P(B∥X0)=1
所以只要计算
P
(
A
∥
X
0
)
P\left(A\|X_0\right)
P(A∥X0)即可。
在公式(1)中,
P
(
A
)
P\left(A\right)
P(A)和
P
(
B
)
P\left(B\right)
P(B)的计算相对简单。设在已知的具有n条数据的数据集中,有a条数据为A类,则
P
(
A
)
=
a
n
,
P
(
B
)
=
n
−
a
n
P\left(A\right)=\frac{a}{n},P\left(B\right)=\frac{n-a}{n}
P(A)=na,P(B)=nn−a
而
P
(
X
0
∥
A
)
P\left(X_0\|A\right)
P(X0∥A)也就是类别A中的个体所服从的概率分布在
X
0
X_0
X0处的概率密度,一般假设服从正态分布。即:
P
(
X
0
∥
A
)
=
1
2
π
σ
e
−
(
x
−
μ
)
2
2
σ
2
(2)
P\left(X_0\|A\right)=\frac{1}{\sqrt{2\pi}\sigma}e^{-\frac{(x-\mu)^2}{2\sigma^2}} \tag{2}
P(X0∥A)=2π
σ1e−2σ2(x−μ)2(2)
其中
μ
\mu
μ表示样本均值,
σ
\sigma
σ表示样本标准差。
根据上述论断,如果一个模型能够将A类的个体划分为A类,即
P
(
X
0
∥
A
)
P\left(X_0\|A\right)
P(X0∥A)越大,则其分类效果越好,因此可以将损失函数定义为:
L
o
s
s
(
f
)
=
∑
(
y
c
^
−
P
(
C
∥
X
)
)
2
Loss\left(f\right)=\sum(\hat{y_c}-P\left(C\|X\right))^2
Loss(f)=∑(yc^−P(C∥X))2
其中,
y
c
^
\hat{y_c}
yc^表示个体属于类别C的概率,如果
y
^
=
1
\hat{y}=1
y^=1,则该个体属于C类,如果
y
^
=
0
\hat{y}=0
y^=0,则该个体不属于C类。
2 分类实例
一般来说,男性和女性在身高和体重上有着较明显的差异,我们获取了400个样本数据,用其中的100个作为训练集,300个作为测试集,来通过身高和体重对人的性别进行分类。
使用到的数据如下:
import numpy as np
import matplotlib.pyplot as plt
from datas.hws_data import get_data
train, test = get_data()
s = 20
plt.scatter(train[train[:, 2] == 1][:, 0], train[train[:, 2] == 1]
[:, 1], c="green", label="males_train", s=s)
plt.scatter(train[train[:, 2] == 0][:, 0], train[train[:, 2] == 0]
[:, 1], c="orange", label="females_train", s=s)
plt.legend()
plt.show()
plt.scatter(test[test[:, 2] == 1][:, 0], test[test[:, 2] == 1]
[:, 1], c="green", label="males_test", s=s)
plt.scatter(test[test[:, 2] == 0][:, 0], test[test[:, 2] == 0]
[:, 1], c="orange", label="females_test", s=s)
plt.legend()
plt.show()
假设身高和体重分别服从正态分布,根据100个数据分别计算男性和女性的身高和体重的样本均值和方差,估计其分布。然后根据前述的贝叶斯公式计算个体属于男性的概率,如果大于0.5则认为是男性。
注意:该方法不需要梯度下降。
males = train[train[:, 2] == 1][:,:2]
females = train[train[:, 2] == 0][:,:2]
# 男性的概率
P_male = (males.shape[0])/(males.shape[0]+females.shape[0])
# 女性的概率
P_female = 1-P_male
# 计算均值
E_male, E_female = males.mean(axis=0), females.mean(axis=0)
# 计算方差,共享方差
F_male = F_female = ((males-E_male)**2).sum(axis=0) / \
males.shape[0]
def denstiny(mean, f, x):
# 正态分布概率密度
return np.exp(-(x-mean)**2/(2*f**2))/(np.sqrt(np.pi*2*f))
def check_gendar(x):
p_m = denstiny(E_male, F_male, x)*P_male
p_f = denstiny(E_female, F_female, x)*P_female
p_m /= p_m+p_f
return p_m
在测试集上计算模型的准确性和损失,结果如下:
counter = 0
loss = 0
for m in test:
p_m = check_gendar(m[:2]).mean()
sex = 1 if p_m > 0.5 else 0
if m[2] == sex:
counter += 1
loss += (m[2]-p_m)**2+(m[2]-1+p_m)**2
print("准确率:{:.2f}%,损失:{:.2f}".format(counter/test.shape[0]*100, loss))
准确率:93.00%,损失:150.58
3 朴素贝叶斯分类器进一步讨论
如果将公式1的分子和分母同时除以分子,那么将得到:
P
(
A
∥
X
0
)
=
1
1
+
P
(
X
0
∥
B
)
P
(
B
)
P
(
X
0
∥
A
)
P
(
A
)
(3)
P\left(A\|X_0\right)=\frac{1}{1+\frac{P\left(X_0\|B\right)P\left(B\right)}{P\left(X_0\|A\right)P\left(A\right)}} \tag{3}
P(A∥X0)=1+P(X0∥A)P(A)P(X0∥B)P(B)1(3)
令
z
=
ln
P
(
X
0
∥
A
)
P
(
A
)
P
(
X
0
∥
B
)
P
(
B
)
=
ln
P
(
X
0
∥
A
)
P
(
X
0
∥
B
)
+
ln
P
(
A
)
P
(
B
)
z=\ln{\frac{P\left(X_0\|A\right)P\left(A\right)}{P\left(X_0\|B\right)P\left(B\right)}}=\ln{\frac{P\left(X_0\|A\right)}{P\left(X_0\|B\right)}}+\ln{\frac{P\left(A\right)}{P\left(B\right)}}
z=lnP(X0∥B)P(B)P(X0∥A)P(A)=lnP(X0∥B)P(X0∥A)+lnP(B)P(A),则式3可以简化为:
P
(
A
∥
X
0
)
=
1
1
+
e
−
z
(4)
P\left(A\|X_0\right)=\frac{1}{1+e^{-z}} \tag{4}
P(A∥X0)=1+e−z1(4)
即sigmoid函数,将式2带入z,得:
z
=
ln
P
(
A
)
P
(
B
)
+
ln
σ
B
σ
A
+
(
x
−
μ
B
)
2
2
σ
B
2
−
(
x
−
μ
A
)
2
2
σ
A
2
=
ln
P
(
A
)
P
(
B
)
+
ln
σ
B
σ
A
+
(
1
2
σ
B
2
−
1
2
σ
A
2
)
x
2
+
(
μ
A
σ
A
2
−
μ
B
σ
B
2
)
x
+
(
μ
B
2
2
σ
B
2
−
μ
A
2
2
σ
A
2
)
\begin{aligned} z&=\ln{\frac{P\left(A\right)}{P\left(B\right)}}+\ln{\frac{\sigma_B}{\sigma_A}}+\frac{(x-\mu_B)^2}{2\sigma_B^2}-\frac{(x-\mu_A)^2}{2\sigma_A^2}\\ &=\ln{\frac{P\left(A\right)}{P\left(B\right)}}+\ln{\frac{\sigma_B}{\sigma_A}}+(\frac{1}{2\sigma_B^2}-\frac{1}{2\sigma_A^2})x^2+(\frac{\mu_A}{\sigma_A^2}-\frac{\mu_B}{\sigma_B^2})x+(\frac{\mu_B^2}{2\sigma_B^2}-\frac{\mu_A^2}{2\sigma_A^2}) \end{aligned}
z=lnP(B)P(A)+lnσAσB+2σB2(x−μB)2−2σA2(x−μA)2=lnP(B)P(A)+lnσAσB+(2σB21−2σA21)x2+(σA2μA−σB2μB)x+(2σB2μB2−2σA2μA2)
假设
σ
A
=
σ
B
=
σ
\sigma_A=\sigma_B=\sigma
σA=σB=σ,即假设两个特征值的变化幅度相同,则上式进一步简化为:
z
=
ln
P
(
A
)
P
(
B
)
+
(
μ
A
−
μ
B
σ
2
)
x
+
(
μ
B
2
−
μ
A
2
2
σ
2
)
(5)
z=\ln{\frac{P\left(A\right)}{P\left(B\right)}}+(\frac{\mu_A-\mu_B}{\sigma^2})x+(\frac{\mu_B^2-\mu_A^2}{2\sigma^2})\tag{5}
z=lnP(B)P(A)+(σ2μA−μB)x+(2σ2μB2−μA2)(5)
令
w
=
μ
A
−
μ
B
σ
2
,
b
=
ln
P
(
A
)
P
(
B
)
+
(
μ
B
2
−
μ
A
2
2
σ
2
)
w=\frac{\mu_A-\mu_B}{\sigma^2},b=\ln{\frac{P\left(A\right)}{P\left(B\right)}}+(\frac{\mu_B^2-\mu_A^2}{2\sigma^2})
w=σ2μA−μB,b=lnP(B)P(A)+(2σ2μB2−μA2),则最终得到:
z
=
w
x
+
b
z=wx+b
z=wx+b
即一个一次线性表达式。到这里我们发现,经过一系列复杂的计算之后,我们又回到了最初的起点——线性回归。
因此我们可以实际计算出上述实例的
w
w
w和
b
b
b的值:
w=(E_male-E_female)/(F_male**2)
b=np.log(P_male/P_female)+(E_female**2-E_male**2)/(2*F_male**2)
print("w:{},b:{}".format(w,b))
loss=0
counter=0
for m in test:
z=(w*m[:2]+b).sum()
p_m=1/(1+np.exp(-z))
sex = 1 if p_m > 0.5 else 0
if m[2] == sex:
counter += 1
loss += (m[2]-p_m)**2+(m[2]-1+p_m)**2
print("准确率:{:.2f}%,损失:{:.2f}".format(counter/test.shape[0]*100, loss))
w:[0.02572485 0.0035972 ],b:[-4.3315505 -0.21241086]
准确率:93.00%,损失:152.29
根据
w
w
w和
b
b
b的值,可以求出两个类的分界线,即图像中当输出为0.5时的若干点。计算方法如下:
1
1
+
e
−
w
x
−
b
=
0.5
e
−
w
x
−
b
=
1
w
1
x
1
+
b
1
+
w
2
x
2
+
b
2
=
0
x
2
=
−
b
1
−
b
2
w
2
−
w
1
w
2
x
1
\begin{aligned} \frac{1}{1+e^{-wx-b}}&=0.5\\ e^{-wx-b}&=1\\ w_1x_1+b_1+w_2x_2+b_2&=0\\ x_2&=\frac{-b_1-b_2}{w_2}-\frac{w_1}{w_2}x_1 \end{aligned}
1+e−wx−b1e−wx−bw1x1+b1+w2x2+b2x2=0.5=1=0=w2−b1−b2−w2w1x1
在训练集和测试集上分别作出两条分界线,这是一条直线。
B = -(b.sum()/w[1])
A = -w[0]/w[1]
xx = np.linspace(100, 200)
plt.ylim(train[:,1].min()-5,train[:,1].max()+5)
plt.xlim(train[:,0].min()-5,train[:,0].max()+5)
plt.plot(xx, A*xx+B)
plt.scatter(train[train[:, 2] == 1][:, 0], train[train[:, 2] == 1]
[:, 1], c="green", label="males_train", s=s)
plt.scatter(train[train[:, 2] == 0][:, 0], train[train[:, 2] == 0]
[:, 1], c="orange", label="females_train", s=s)
plt.legend()
plt.show()
plt.ylim(test[:,1].min()-5,test[:,1].max()+5)
plt.xlim(test[:,0].min()-5,test[:,0].max()+5)
plt.plot(xx, A*xx+B)
plt.scatter(test[test[:, 2] == 1][:, 0], test[test[:, 2] == 1]
[:, 1], c="green", label="males_test", s=s)
plt.scatter(test[test[:, 2] == 0][:, 0], test[test[:, 2] == 0]
[:, 1], c="orange", label="females_test", s=s)
plt.legend()
plt.show()