*注:本博客参考李宏毅老师2020年机器学习课程. 视频链接
目录
1 逻辑回归
1.1 概念
从上一节的讨论中,我们发现对一个具有特征值
X
X
X的个体,如果
X
X
X的各分量相互独立,并且每一个
X
X
X也是独立同分布的话,那么对这个个体进行分类,其属于某一个类别的概率
f
(
x
)
f(x)
f(x)可以表示为:
f
(
x
)
=
g
(
z
)
=
1
1
+
e
−
z
,
z
=
b
+
∑
w
i
x
i
(1)
f(x)=g(z)=\frac{1}{1+e^{-z}},z=b+\sum{w_ix_i}\tag{1}
f(x)=g(z)=1+e−z1,z=b+∑wixi(1)
易得
g
(
z
)
∈
(
0
,
1
)
g(z)\in(0,1)
g(z)∈(0,1)。既然如此,那么也可以通过与线性回归类似的方式来使用梯度下降算法,让计算机自动求解w和b。这类问题就称为逻辑回归问题。
1.2 损失函数
在线性回归中,我们使用了均方差作为损失函数,而逻辑回归需要使用另一种损失函数——交叉熵。
交叉熵是这样产生的:假设有两个类Class 1和Class 2,对于数据集中某一个体的特征值
X
i
X_i
Xi,当个体属于Class 1时,其产生的概率为
1
−
f
(
x
i
)
1-f(x_i)
1−f(xi),个体属于Class 2时,其产生的概率为
f
(
x
i
)
f(x_i)
f(xi)。由于所有个体的产生概率相互独立,因此如果计算每一个个体产生的概率,再将它们相乘,就可以得到整个数据集产生的概率。若分类模型使得整个数据集产生概率最大,那么这个分类模型被认为是准确的。因此可以定义L如下:
L
(
f
)
=
f
(
x
1
)
f
(
x
2
)
(
1
−
f
(
x
3
)
)
.
.
.
f
(
x
n
)
L(f)=f(x_1)f(x_2)(1-f(x_3))...f(x_n)
L(f)=f(x1)f(x2)(1−f(x3))...f(xn)
对上式左右两边取自然对数,不改变其单调性,因此得:
ln
L
(
f
)
=
ln
f
(
x
1
)
+
ln
f
(
x
2
)
+
ln
(
1
−
f
(
x
3
)
)
+
.
.
.
ln
f
(
x
n
)
\ln{L(f)}=\ln{f(x_1)}+\ln{f(x_2)}+\ln{(1-f(x_3))}+...\ln{f(x_n)}
lnL(f)=lnf(x1)+lnf(x2)+ln(1−f(x3))+...lnf(xn)
上式的格式不统一,因为对于类别不同的个体需要分别使用两种不同的计算方式,于是约定当个体属于Class 1时,
y
^
=
1
\hat{y}=1
y^=1,个体属于Class 2时,
y
^
=
0
\hat{y}=0
y^=0。上式可以写成:
ln
L
(
f
)
=
(
y
1
^
ln
f
(
x
1
)
+
(
1
−
y
1
^
)
ln
(
1
−
f
(
x
1
)
)
)
+
(
y
2
^
ln
f
(
x
2
)
+
(
1
−
y
2
^
)
ln
(
1
−
f
(
x
2
)
)
)
+
(
y
3
^
ln
f
(
x
3
)
+
(
1
−
y
3
^
)
ln
(
1
−
f
(
x
3
)
)
)
+
.
.
.
+
(
y
n
^
ln
f
(
x
n
)
+
(
1
−
y
n
^
)
ln
(
1
−
f
(
x
n
)
)
)
\begin{aligned} \ln{L(f)}&=(\hat{y_1}\ln{f(x_1)}+(1-\hat{y_1})\ln{(1-f(x_1))})\\ &+(\hat{y_2}\ln{f(x_2)}+(1-\hat{y_2})\ln{(1-f(x_2))})\\ &+(\hat{y_3}\ln{f(x_3)}+(1-\hat{y_3})\ln{(1-f(x_3))})\\ &+...\\ &+(\hat{y_n}\ln{f(x_n)}+(1-\hat{y_n})\ln{(1-f(x_n))})\\ \end{aligned}
lnL(f)=(y1^lnf(x1)+(1−y1^)ln(1−f(x1)))+(y2^lnf(x2)+(1−y2^)ln(1−f(x2)))+(y3^lnf(x3)+(1−y3^)ln(1−f(x3)))+...+(yn^lnf(xn)+(1−yn^)ln(1−f(xn)))
ln
L
(
f
)
\ln{L(f)}
lnL(f)的值越大,则分类模型越好。但是损失函数要求值越小,代表模型越好,于是取
ln
L
(
f
)
\ln{L(f)}
lnL(f)的相反数,得:
L
o
s
s
(
f
)
=
−
ln
L
(
f
)
=
−
ln
L
(
g
)
=
−
∑
(
y
^
ln
g
(
z
)
+
(
1
−
y
^
)
ln
(
1
−
g
(
z
)
)
)
(2)
Loss(f)=-\ln{L(f)}=-\ln{L(g)}=-\sum{(\hat{y}\ln{g(z)}+(1-\hat{y})\ln{(1-g(z))})} \tag{2}
Loss(f)=−lnL(f)=−lnL(g)=−∑(y^lng(z)+(1−y^)ln(1−g(z)))(2)
这就是交叉熵的定义。在上式中,由于
g
(
z
)
∈
(
0
,
1
)
g(z)\in(0,1)
g(z)∈(0,1),因此
ln
g
(
z
)
∈
(
−
∞
,
0
]
\ln{g(z)}\in(-\infty,0]
lng(z)∈(−∞,0],而
ln
(
1
−
g
(
z
)
)
∈
(
0
,
∞
)
\ln{(1-g(z))}\in(0,\infty)
ln(1−g(z))∈(0,∞)
1.3 梯度下降
对式2求导,则有:
∂
L
o
s
s
∂
w
i
=
∂
L
o
s
s
∂
g
(
z
)
∂
g
(
z
)
∂
z
∂
z
∂
w
i
∂
L
o
s
s
∂
g
(
z
)
=
∑
(
1
−
y
^
1
−
g
(
z
)
−
y
^
g
(
z
)
)
∂
g
(
z
)
∂
z
=
g
(
z
)
(
1
−
g
(
z
)
)
∂
z
∂
w
i
=
x
i
\begin{aligned} \frac{\partial{Loss}}{\partial{w_i}}&=\frac{\partial{Loss}}{\partial{g(z)}}\frac{\partial{g(z)}}{\partial{z}}\frac{\partial{z}}{\partial{w_i}}\\ \frac{\partial{Loss}}{\partial{g(z)}}&=\sum{(\frac{1-\hat{y}}{1-g(z)}-\frac{\hat{y}}{g(z)})}\\ \frac{\partial{g(z)}}{\partial{z}}&=g(z)(1-g(z))\\ \frac{\partial{z}}{\partial{w_i}}&=x_i \end{aligned}
∂wi∂Loss∂g(z)∂Loss∂z∂g(z)∂wi∂z=∂g(z)∂Loss∂z∂g(z)∂wi∂z=∑(1−g(z)1−y^−g(z)y^)=g(z)(1−g(z))=xi
整理得:
∂
L
o
s
s
∂
w
i
=
∑
(
g
(
z
)
−
y
^
)
x
i
(3)
\frac{\partial{Loss}}{\partial{w_i}}=\sum{(g(z)-\hat{y})x_i}\tag{3}
∂wi∂Loss=∑(g(z)−y^)xi(3)
如果对
w
i
w_i
wi进行参数更新,其过程是这样的:
w
i
t
+
1
=
w
i
t
−
η
∑
(
g
(
z
)
−
y
^
)
x
i
(4)
w_i^{t+1}=w_i^t-\eta\sum{(g(z)-\hat{y})x_i}\tag{4}
wit+1=wit−η∑(g(z)−y^)xi(4)
从式4可以看出,参数
w
i
w_i
wi更新得速度取决于三个值:
- 学习率 η \eta η的大小;
- 参数 w i w_i wi本身的大小;
- 模型计算所得属于某个类别的概率与实际情况的差距。
下面使用逻辑回归的方法对2_1节中的问题进行拟合,结果如下:
from models.simple_regression_model import Logistic_Regression as Model
import numpy as np
from datas.hws_data import get_data
train, test = get_data()
m = Model(1)
m.train(train[:, :2], train[:, 2], lr=1, epoch=40000)
m.show()
l = (np.abs(np.around(m.forward(test[:, :2]))-test[:, 2])).sum()
print("准确率:{:.2f}%".format(100-l/test.shape[0]*100))
y=[-12.67 -12.6 ]+[0.07 0.23]x
abs loss: 22.553192817202635
准确率:89.00%
与2_0节一样,可以做出分界线如下:
import matplotlib.pyplot as plt
B = -(m.w[0].sum()/m.w[1][1])
A = -m.w[1][0]/m.w[1][1]
xx = np.linspace(100, 200)
s=20
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()
2 为什么不使用均方差作为损失函数
在逻辑回归中,我们使用了交叉熵作为损失函数。但是既然逻辑回归和线性回归都是回归问题,为什么不使用线性回归中常用的均方差作为损失函数呢?
我们可以从两个函数的图像得到直观的解释:
from mpl_toolkits.mplot3d import axes3d
import matplotlib.pyplot as plt
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
w1 = np.linspace(-5, 5)
w2 = np.linspace(-5, 5)
w1, w2 = np.meshgrid(w1, w2)
z = w1*1+w2*1
y1 = 1/(1+np.exp(-z))
def MSE(y1, y):
return (y1-y)**2
def CE(y1, y):
return -(y*np.log(y1)+(1-y)*np.log(1-y1))
ax.plot_wireframe(w1, w2, MSE(y1, 0), rstride=3, cstride=3, color="red",label="MSE")
ax.plot_wireframe(w1, w2, 0.25*CE(y1, 0), rstride=3, cstride=3,label="CE")
plt.legend()
plt.show()
图中红色线条表示均方差的值随着两个参数 w 1 w_1 w1和 w 2 w_2 w2的变化情况,蓝色表示交叉熵(为了方便比较,将交叉熵的大小按比例缩放为原来的1/4)。
设置y=0,x=[1,1]那么我们可以算得当
w
1
w_1
w1和
w
2
w_2
w2越大,目标函数的值与y的差距越大,此时更新参数的速度应该越快,因为此时函数与目标相差较远。
然而从图中我们可以看到,均方差的表现却恰恰相反,在目标函数的值与y的差距很大时,梯度却为0,更新参数极为缓慢,这显然与我们的设想不一致,而交叉熵(蓝色曲线)则不会出现这种情况。
可以在数学上给出严格证明,这里略过。
3 多类别逻辑回归
3.1 模型调整
当需要对超过两个类别进行逻辑回归时,直接使用某一个类别的概率已经不能满足需求。这时网络的输出应该定义为每一个类别的概率,例如当对类A、B、C进行分类,则模型输出为一个三维向量,每个分量的值分别表示该个体属于类A、B、C的概率。
则可以预见的是,模型需要为每一个类别设置一组参数,每组参数与两个类别时一致。
3.2 Softmax函数
一般情况下,我们可以假设一个个体仅属于一个类别,为了更直观地得到每个类别的概率,应当满足:
1
=
∑
P
(
c
l
a
s
s
)
1=\sum{P\left(class\right)}
1=∑P(class)
其中
P
(
c
l
a
s
s
)
P\left(class\right)
P(class)表示个体属于某一个类别的概率,该表达式表面个体属于每个类别的概率之和为1,即个体属于且仅属于一个类别。
因此可以使用Softmax函数接在sigmoid函数之后,其定义如下:
S
o
f
t
m
a
x
(
Z
1
)
=
e
z
1
∑
i
=
1
n
e
z
i
Softmax(Z_1)=\frac{e^{z_1}}{\sum_{i=1}^{n}{e^{z_i}}}
Softmax(Z1)=∑i=1neziez1
不难得出Softmax函数的输出介于0到1之间,且各个类别的输出之和为1.
修改了目标函数之后相应的损失函数的偏导值也需要修改,但此处不再给出。
4 逻辑回归的局限与解决方案
4.1 无法分类的局限
对于一个二分类的问题,由于逻辑回归的表达式是一条直线,在一些情况下逻辑回归无法的分开两个类别。例如有如下四个个体:
显然,无法找到一条直线来区分这两个类别。
4.2 解决方案——特征提取
一种可行的解决方案是,在进行逻辑回归之前,首先对四个个体的特征值进行加工,例如,计算四个个体与(1,1)和(3,3)的距离,那么属于class 1的个体的特征值分别为:(0, 2 2 2\sqrt{2} 22)( 2 2 2\sqrt{2} 22,0),而class 2的两个类别的特征值分别为:(2,2)(2,2),在图中的位置为:
显然能够找到一条直线来分开这两个类别的点。
但是提取特征也是一个需要技巧的过程,如果不能提取到合适的特征,可能还是无法清楚地分开两个类别的点。那么当我们需要使用梯度下降算法来自动求解时,应该如何提取特征呢?
先前我们已经使用到的逻辑回归模型,如果最后不经过sigmoid函数,那么其输入是一个包含特征值的向量,输出也是一个向量。如果将整个逻辑回归模型视为一个单元,那么理论上来说,一个单元的输出可以作为另一个单元的输入。那么在理想的情况下,两个相互堆叠的逻辑回归模块(第一个不经sigmoid函数)构成的模型,第一个模块将进行特征提取的工作,第二个模块进行分类工作,由此就能够实现自动求解适合用于该分类问题的模型。
这种思想正是深度学习的基本思想,将在下一节中论述。