梯度下降(gradient descent)是机器学习中的一种最优化算法,广泛应用于线性回归和逻辑回归中。它的核心思想是:要获得函数的最小值,最好的方法是沿着该函数的梯度的反方向探寻。
假设这样一个场景:你当前在半山腰上,想要走到山脚下。但这时雾特别大,你只能看到2米远的距离,不能看到山脚的方向。这种情况下,如何继续往山脚的方向走呢?你只要看自己的脚下,往地面坡度下降最大的方向走,如果在地面放一个球,即是球滚动的方向,走完一步后,重新找下山方向,如此继续走下去,就走到山脚下了。
梯度下降的过程类似于下山的场景,对于一个可微分的函数,要找到函数的极小值,就是要沿着的梯度的反方向走,通过不断迭代,最终到达最低点。
梯度下降
梯度的本意是一个向量,表示某一函数在该点处的方向导数沿着该方向取得最大值,即函数在该点处沿着该方向(此梯度的方向)变化最快,变化率最大(此梯度的模)。梯度的表示如下:
<
∂
f
∂
x
,
∂
f
∂
y
,
∂
f
∂
z
>
< \frac{\partial f}{\partial x},\frac{\partial f}{\partial y},\frac{\partial f}{\partial z}>
<∂x∂f,∂y∂f,∂z∂f>
在单变量函数中,梯度其实就是函数的导数,其几何意义是该函数在某个给定点的切线的斜率。比如函数
y
=
x
2
y=x^2
y=x2,它的导数为
y
′
=
2
x
y'=2x
y′=2x,对于函数上的任意一点
x
0
x_0
x0,该点的梯度为
2
x
0
2x_0
2x0。
在多变量函数中,梯度是一个向量,向量有方向,指向的是函数在给定点上升最快的方向。向量的每一个分量是该函数对每一个变量的导数。比如函数
f
(
x
,
y
)
=
x
2
+
y
2
f(x,y)=x^2+y^2
f(x,y)=x2+y2,该函数在点
(
x
0
,
y
0
)
(x_0,y_0)
(x0,y0)处的梯度为
(
2
x
0
,
2
y
0
)
(2x_0,2y_0)
(2x0,2y0)。
对于函数
J
(
θ
)
J(\theta)
J(θ),假设上一个给定点为
θ
t
−
1
\theta^{t-1}
θt−1,则沿梯度负方向的下一个点:
θ
t
=
θ
t
−
1
+
Δ
θ
\theta^t = \theta^{t-1} + \Delta \theta
θt=θt−1+Δθ
将
J
(
θ
t
)
J( \theta^t )
J(θt)在
θ
t
−
1
\theta^{t-1}
θt−1处进行一阶泰勒展开,泰勒公式的内容可学习前文《如何理解泰勒公式?》:
J
(
θ
t
)
=
J
(
θ
t
−
1
+
Δ
θ
)
≈
J
(
θ
t
−
1
)
+
J
′
(
θ
t
−
1
)
Δ
θ
\begin{aligned} J( \theta^t ) &= J(\theta^{t-1} + \Delta \theta) \\ &\approx J(\theta^{t-1}) + J'( \theta^{t-1})\Delta \theta \end{aligned}
J(θt)=J(θt−1+Δθ)≈J(θt−1)+J′(θt−1)Δθ
要使
J
(
θ
t
)
<
J
(
θ
t
−
1
)
J( \theta^t ) < J(\theta^{t-1})
J(θt)<J(θt−1),可取:
Δ
θ
=
−
α
J
′
(
θ
t
−
1
)
\Delta \theta = -\alpha J'(\theta^{t-1})
Δθ=−αJ′(θt−1),则
θ
t
=
θ
t
−
1
−
α
J
′
(
θ
t
−
1
)
\theta^t = \theta^{t-1} -\alpha J'(\theta^{t-1})
θt=θt−1−αJ′(θt−1)
其中
α
\alpha
α为步长,又称学习率,是一个需要设置的超参数。步长决定了在梯度下降迭代的过程中,每一步沿梯度负方向前进的长度,步长设置过小,需要花费的学习时间就很长,设置过大,就可能在最小值附近来回震荡。
举例来讲,给定一个多元目标函数:
J
(
Θ
)
=
θ
1
2
+
θ
2
2
J(\Theta)=\theta^2_1+\theta^2_2
J(Θ)=θ12+θ22
现在要通过梯度下降法计算这个函数的最小值。通过观察就能知道最小值所在的点是(0,0),使用梯度下降法也可以一步步的得到这个最小值点。
首先设定一个初始的起点为:
Θ
0
=
(
1
,
3
)
\Theta^0=(1,3)
Θ0=(1,3)
初始的学习率设为:
α
=
0.1
\alpha=0.1
α=0.1
函数的梯度为:
Δ
J
(
Θ
)
=
(
2
θ
1
,
2
θ
2
)
\Delta J(\Theta)=(2\theta_1,2\theta_2)
ΔJ(Θ)=(2θ1,2θ2)
进行如下多轮迭代:
Θ
(
0
)
=
(
1
,
3
)
Θ
(
1
)
=
Θ
(
0
)
−
α
Δ
J
(
Θ
(
0
)
)
=
(
1
,
3
)
−
0.1
(
2
,
6
)
=
(
0.8
,
2.4
)
Θ
(
2
)
=
Θ
(
1
)
−
α
Δ
J
(
Θ
(
1
)
)
=
(
0.8
,
2.4
)
−
0.1
(
1.6
,
4.8
)
=
(
0.64
,
1.92
)
Θ
(
3
)
=
(
0.512
,
1.536
)
Θ
(
4
)
=
(
0.4096
,
1.2288
)
.
.
.
.
.
.
.
.
.
.
\begin{aligned} \Theta^{(0)}&=(1,3) \\ \Theta^{(1)}&=\Theta^{(0)}-\alpha \Delta J(\Theta^{(0)}) \\ &=(1,3)-0.1(2,6) \\ &=(0.8,2.4) \\ \Theta^{(2)}&=\Theta^{(1)}-\alpha \Delta J(\Theta^{(1)}) \\ &=(0.8,2.4)-0.1(1.6,4.8) \\ &=(0.64,1.92) \\ \Theta^{(3)}&= (0.512,1.536)\\ \Theta^{(4)}&= (0.4096,1.2288)\\ &..........\\ \end{aligned}
Θ(0)Θ(1)Θ(2)Θ(3)Θ(4)=(1,3)=Θ(0)−αΔJ(Θ(0))=(1,3)−0.1(2,6)=(0.8,2.4)=Θ(1)−αΔJ(Θ(1))=(0.8,2.4)−0.1(1.6,4.8)=(0.64,1.92)=(0.512,1.536)=(0.4096,1.2288)..........
最终,迭代结果不断逼近最优参数,即 Θ = ( 0 , 0 ) \Theta= (0,0) Θ=(0,0)
编程实战
给定20个身高-体重样本,如下图所示。显然,可以用一条直线拟合图上的点。
假设我们要拟合的这条直线为:
y
^
=
θ
0
+
θ
1
x
\hat{y}=\theta_0+\theta_1x
y^=θ0+θ1x
为了评估模型拟合的好坏,需要定义一个损失函数(loss function),损失函数越小,意味着拟合程度最好,对应的模型参数即为最优参数。一般定义该模型的预测值与真实值的误差为损失函数: L = ∣ y ^ − y ∣ L = |\hat{y}-y| L=∣y^−y∣
其中
y
^
\hat{y}
y^为预测值,
y
y
y为真实值
这里,我们只使用风险损失(均方误差)来评估模型拟合程度,省略结构损失,即目标函数(成本函数)为:
J
(
Θ
)
=
1
N
∑
i
=
1
N
(
y
i
^
−
y
i
)
2
J(\Theta)=\frac{1}{N}\sum^N_{i=1}(\hat{y_i}-y_i)^2
J(Θ)=N1i=1∑N(yi^−yi)2
梯度为:
Δ
J
(
Θ
)
=
<
∂
J
∂
θ
0
,
∂
J
∂
θ
1
>
\Delta J(\Theta)=<\frac{\partial J}{\partial \theta_0},\frac{\partial J}{\partial \theta_1}>
ΔJ(Θ)=<∂θ0∂J,∂θ1∂J>
梯度的偏导数为
∂
J
∂
θ
0
=
∂
(
y
^
−
y
)
2
∂
θ
0
=
2
(
y
^
−
y
)
∂
(
y
^
−
y
)
∂
θ
0
=
2
(
θ
0
+
θ
1
x
−
y
)
∂
(
θ
0
+
θ
1
x
−
y
)
∂
θ
0
=
2
(
θ
0
+
θ
1
x
−
y
)
∂
J
∂
θ
1
=
2
(
θ
0
+
θ
1
x
−
y
)
x
\begin{aligned} \frac{\partial J}{\partial \theta_0}&=\frac{\partial (\hat{y}-y)^2}{\partial \theta_0} \\ &=2(\hat{y}-y)\frac{\partial (\hat{y}-y)}{\partial \theta_0} \\ &=2(\theta_0+\theta_1x-y)\frac{\partial (\theta_0+\theta_1x-y)}{\partial \theta_0} \\ &=2(\theta_0+\theta_1x-y) \\ \frac{\partial J}{\partial \theta_1}&=2(\theta_0+\theta_1x-y)x \end{aligned}
∂θ0∂J∂θ1∂J=∂θ0∂(y^−y)2=2(y^−y)∂θ0∂(y^−y)=2(θ0+θ1x−y)∂θ0∂(θ0+θ1x−y)=2(θ0+θ1x−y)=2(θ0+θ1x−y)x
我们可以基于经验画一条拟合直线,通过梯度下降法,不断优化参数,达到最好的拟合效果,如下图所示:
import numpy as np
#创建包含20个样本的数据集
def createDataSet():
#特征:身高、体重
dataSet = \
[\
[208.,110.],\
[203.,107.],\
[196.,94.],\
[191.,92.],\
[188.,87.],\
[183.,85.],\
[178.,81.],\
[174.,77.],\
[171.,78.],\
[167.,73.],\
[163.,74.],\
[159.,68.],\
[157.,64.],\
[153.,59.],\
[148.,56.],\
[141.,54.],\
[139.,55.],\
[135.,45.],\
[131.,47.],\
[128.,40.]\
]
labels = ['height','weight']
return dataSet, labels
# 生成数据集
dataSet,labels = createDataSet()
heightList =[item[0] for item in dataSet]
weightList =[item[1] for item in dataSet]
m = np.shape(dataSet)[0] #数据集大小
X0 = np.mat(np.ones((m,1))) # m行1列的矩阵
X1 = np.mat(dataSet)[:,0]
X = np.hstack((X0,X1)) # 按照列堆叠形成新矩阵
Y = np.mat(dataSet)[:,1]
alpha = 0.00005 # 学习率(步长)
# 定义代价函数
def cost_function(theta, X, Y):
error = X * theta - Y # 损失函数
return (1/(2*m)) * (error.T * error) #成本函数
# 定义梯度函数
def gradient_function(theta, X, Y):
diff = X * theta - Y
return (1./m) * (X.T * diff)
# 梯度下降迭代:
def gradient_descent(X, Y ,alpha):
theta = np.mat([[-90],[0.9]])
gradient = gradient_function(theta, X ,Y)
while not all(abs(gradient) <= 1e-3):
theta = theta - alpha * gradient
gradient = gradient_function(theta, X ,Y)
return theta
optimal = gradient_descent(X, Y ,alpha)
print('optimal:', optimal)
print('cost function:', cost_function(optimal, X, Y)[0,0])
# 根据数据画出对应的图像
def plot(X, Y, theta):
%matplotlib inline
import matplotlib.pyplot as plt
p1 = plt.scatter(X,Y,c='blue', marker='o')
x = np.arange(120, 215, 0.1)
y = x*theta[1,0]+theta[0,0]
p2 = plt.plot(x,y,c='red')
plt.xlabel("height(cm)")
plt.ylabel("weight(kg)")
plt.show()
plot(X1.T.A[0], Y.T.A[0], optimal)
最终得到的拟合函数为
y
^
=
0.81343906
x
−
62.44718038
\hat{y}=0.81343906x-62.44718038
y^=0.81343906x−62.44718038,拟合直线如下图所示:
小结
梯度下降法是求解无约束最优化问题的一种常用方法,具有实现简单的优点。它是一种迭代算法,每一步需要求解目标函数的梯度向量。
当目标函数是凸函数时,梯度下降法的解是全局最优解。一般情况下,其解不保证是全局最优解。在线性回归和逻辑回归算法中,他们的损失函数是严格意义上的凸函数,存在全局唯一的极小值,只要有足够的迭代次数,一定可以到达极小值点。