系列文章目录
前言
其实在之前机器学习的章节中我们已经提到了梯度下降的概念,并且用梯度下降解决了实际的最小二乘法求解问题,来完成线性回归的一个例子,大家知道了梯度下降法能让最小二乘法的结果每次都趋向更小,但大家可能还是不太清楚他的本质原理。这一章中我就会给大家真真正正的讲解梯度下降的算法。
一、什么是梯度下降?
梯度下降其实是梯度问题里的一种,只是我们用的比较多,但根据实际情况,我们将梯度问题分为:梯度下降和梯度上升,分别用来求解最小值和最大值问题。而梯度的概念其实很简单,当一个式子的向量由该式子的全部变量的偏导数组成时,这个向量被称为梯度(gradient)。我们用式子来表达可能会更加形象:
假设我们有对象表达式:
f
(
x
0
,
x
1
)
=
x
0
2
+
x
1
2
f(x_{0},x_{1})=x_{0}^2+x_1^2
f(x0,x1)=x02+x12
则他的梯度为:
v
e
c
t
o
r
→
=
(
∂
f
∂
x
0
,
∂
f
∂
x
1
)
\overrightarrow{vector} = ( \frac{\partial f}{\partial x_{0}}, \frac{\partial f}{\partial x_{1}} )
vector=(∂x0∂f,∂x1∂f)
二、为什么梯度的方向是下降最快的方向
1.偏导数的意义
那我们首先要从梯度的组成入手,可以看到梯度是由两个偏导数 ( ∂ f ∂ x 0 , ∂ f ∂ x 1 ) (\frac{\partial f}{\partial x_0},\frac{\partial f}{\partial x_1}) (∂x0∂f,∂x1∂f)组成
而其中:
f
x
(
x
0
,
y
0
)
f_{x}(x_0,y_0)
fx(x0,y0)代表曲面被平面
y
=
y
0
y = y_0
y=y0所截后的曲面在点M出的切线对
x
x
x轴的斜率,
f
y
(
x
0
,
y
0
)
f_{y}(x_0,y_0)
fy(x0,y0)代表曲面被平面
x
=
x
0
x = x_0
x=x0所截后的曲面在点M出的切线对
y
y
y轴的斜率,
于是我们可以看到由于偏导数的局限性,我们很难进行任意方向的变化率计算,于是我们引入了方向导数的概念。
2.方向导数
因为我们已经有了两个方向的偏导数
f
x
(
x
0
,
y
0
)
,
f
y
(
x
0
,
y
0
)
f_x(x_0,y_0),f_y(x_0,y_0)
fx(x0,y0),fy(x0,y0),那类似与向量平面可以由两个向量表达一样,我们也可以通过这俩个偏导数来表示各个方向。
于是我们提出:
u
⃗
=
cos
θ
i
+
sin
θ
j
\vec u = \cos\theta{i}+\sin\theta{j}
u=cosθi+sinθj
当然由于他是针对二元函数
f
(
x
,
y
)
f(x,y)
f(x,y)的,所以这个方向还需要一个前置条件:
lim
t
→
0
f
(
x
0
+
t
cos
θ
,
y
0
+
t
sin
θ
)
t
\lim_{t\to0} \frac{f(x_0+t\cos\theta,y_0+t\sin\theta)} {t}
t→0limtf(x0+tcosθ,y0+tsinθ)这条表达式的极限值如果存在,则可以说我们的方向向量成立,我们将其记为
D
u
f
(
x
,
y
)
D_uf(x,y)
Duf(x,y)
由于我们已知
D
u
f
(
x
,
y
)
=
f
x
(
x
,
y
)
cos
θ
+
f
y
(
x
,
y
)
sin
θ
D_uf(x,y)=f_x(x,y)\cos\theta+f_y(x,y)\sin\theta
Duf(x,y)=fx(x,y)cosθ+fy(x,y)sinθ成立。
设:
P
=
(
f
x
(
x
,
y
)
,
f
y
(
x
,
y
)
)
P = (f_x(x,y),f_y(x,y))
P=(fx(x,y),fy(x,y))
W
=
(
cos
θ
,
sin
θ
)
W = (\cos\theta,\sin\theta)
W=(cosθ,sinθ)
则:
D
u
f
(
x
,
y
)
=
P
⋅
W
=
∣
P
∣
⋅
∣
W
∣
cos
α
D_uf(x,y)=P·W =\left\vert P\right\vert·\left\vert W\right\vert \cos\alpha
Duf(x,y)=P⋅W=∣P∣⋅∣W∣cosα
这样原理一下子就清晰了,想要
D
u
f
(
x
,
y
)
D_uf(x,y)
Duf(x,y)最大也就是
cos
α
\cos\alpha
cosα取1,也就是
W
W
W和
P
P
P平行,其中P是我们的梯度,而W是我们在尝试的方向,所以当然是让W和我们的P相同时
D
u
f
(
x
,
y
)
D_uf(x,y)
Duf(x,y)最大,也就是这个方向幅度最大。
3.实验证明
首先画出我们的函数图像
X0 = np.arange(-6, 6, 0.25)
X1 = np.arange(-6, 6, 0.25)
X0, X1 = np.meshgrid(X0, X1)
Z = np.power(X0,2)+np.power(X1,2)
可以添加一点投影,更加直观的从每个维度观察变化趋势。
然后求我们的梯度
先做一个求导的函数
def _numerical_gradient_no_batch(f, x):
h = 1e-4 # 0.0001
grad = np.zeros_like(x)
for idx in range(x.size):
tmp_val = x[idx]
x[idx] = float(tmp_val) + h
fxh1 = f(x) # f(x+h)
x[idx] = tmp_val - h
fxh2 = f(x) # f(x-h)
grad[idx] = (fxh1 - fxh2) / (2 * h)
x[idx] = tmp_val # 还原值
return grad
再生成我们的函数 f ( x 0 , x 1 ) = x 0 2 + x 1 2 f(x_0,x_1)=x_0^2+x_1^2 f(x0,x1)=x02+x12
def function_2(x):
if x.ndim == 1:
return np.sum(x ** 2)
else:
return np.sum(x ** 2, axis=1)
最后进行求导
def numerical_gradient(f, X):
if X.ndim == 1:
return _numerical_gradient_no_batch(f, X)
else:
grad = np.zeros_like(X)
for idx, x in enumerate(X):
grad[idx] = _numerical_gradient_no_batch(f, x)
return grad
grad = numerical_gradient(function_2, np.array([X, Y]))
然后向量图
plt.quiver(X, Y, -grad[0], -grad[1], angles="xy", color="#666666")
得出结果
我们可以看到对应上面我们 f ( x 0 , x 1 ) f(x_0,x_1) f(x0,x1)的z方向投影图,每个方向的梯度都在向着最低点汇聚。
总结
所以综上所述,梯度下降法在求解很多函数上都有起效,也就是我们前面在解决最小二乘法的时候选择他的原因,同时,梯度的求解方法在后面神经网络的学习中是非常重要的,可以说是一个核心思想。