其中
x
x
x 是数据集特征,
y
y
y 是数据集标签,假设,我们使用简单线性回归
y
=
w
x
y = wx
y=wx 对该组数据进行拟合,则此处我们可以构造损失函数如下:
S
S
E
L
o
s
s
(
w
)
=
(
2
−
1
∗
w
)
2
+
(
4
−
2
∗
w
)
2
+
(
6
−
3
∗
w
)
2
=
w
2
−
4
w
+
4
+
4
w
2
−
16
w
+
16
+
9
w
2
−
36
w
+
36
=
14
w
2
−
56
w
+
56
=
14
(
w
2
−
4
w
+
4
)
=
14
(
w
−
2
)
2
\begin{aligned} SSELoss(w) & = (2-1*w)^2 + (4-2*w)^2 + (6-3*w)^2 \\ & = w^2-4w+4+4w^2-16w+16+9w^2-36w+36 \\ & = 14w^2-56w+56 \\ & = 14(w^2-4w+4) \\ & = 14(w-2)^2 \end{aligned}
SSELoss(w)=(2−1∗w)2+(4−2∗w)2+(6−3∗w)2=w2−4w+4+4w2−16w+16+9w2−36w+36=14w2−56w+56=14(w2−4w+4)=14(w−2)2
此时,损失函数
S
S
E
L
o
s
s
(
w
)
SSELoss(w)
SSELoss(w) 就是一个关于
w
w
w 的一元函数。围绕该损失函数进行最小值求解,可考虑使用最小二乘法进行计算,即令
S
S
E
L
o
s
s
(
w
)
SSELoss(w)
SSELoss(w) 导函数取值为 0,此时有:
∂
S
S
E
L
o
s
s
(
w
)
∂
(
w
)
=
28
(
w
−
2
)
=
0
\begin{aligned} \frac{\partial{SSELoss{(w)}}}{\partial{(w)}} & = 28(w-2) \\ & = 0 \end{aligned}
∂(w)∂SSELoss(w)=28(w−2)=0
因此,
w
w
w 的最优解为:
w
=
2
w=2
w=2
当然,我们也可以通过绘制一个图像来观察损失函数的基本情况:
x = np.linspace(-1,5,40)
SSELoss =14* np.power(x -2,2)
plt.plot(x, SSELoss)
Lesson 2 中我们曾提及,设
f
(
x
)
f(x)
f(x) 是一个关于
x
x
x 的函数,其中
x
x
x 是向量变元,并且
x
=
[
x
1
,
x
2
,
.
.
.
,
x
n
]
T
x = [x_1, x_2,...,x_n]^T
x=[x1,x2,...,xn]T
则
∂
f
∂
x
=
[
∂
f
∂
x
1
,
∂
f
∂
x
2
,
.
.
.
,
∂
f
∂
x
n
]
T
\frac{\partial f}{\partial x} = [\frac{\partial f}{\partial x_1}, \frac{\partial f}{\partial x_2}, ..., \frac{\partial f}{\partial x_n}]^T
∂x∂f=[∂x1∂f,∂x2∂f,...,∂xn∂f]T
而该表达式也被称为向量求导的梯度向量形式。
∇
x
f
(
x
)
=
∂
f
∂
x
=
[
∂
f
∂
x
1
,
∂
f
∂
x
2
,
.
.
.
,
∂
f
∂
x
n
]
T
\nabla _xf(x) = \frac{\partial f}{\partial x} = [\frac{\partial f}{\partial x_1}, \frac{\partial f}{\partial x_2}, ..., \frac{\partial f}{\partial x_n}]^T
∇xf(x)=∂x∂f=[∂x1∂f,∂x2∂f,...,∂xn∂f]T
进一步的,如果假设
f
(
x
)
f(x)
f(x) 是关于参数
x
x
x 的损失函数,则
∇
x
f
(
x
)
\nabla _xf(x)
∇xf(x) 就是损失函数的梯度向量,或者梯度表达式。
当损失函数的参数
x
x
x 选定一组取值之后,我们就能计算梯度表达式的取值,该值也被称为损失函数在某组参数取值下的梯度。
例如,在上例中,损失函数
f
(
w
)
=
14
(
w
−
2
)
2
f(w) = 14(w-2)^2
f(w)=14(w−2)2
损失函数的梯度向量表示形式为:
∇
w
f
(
w
)
=
∂
f
∂
w
=
28
(
w
−
2
)
\nabla _wf(w) = \frac{\partial f}{\partial w}=28(w-2)
∇wf(w)=∂w∂f=28(w−2)
具体来看,当
w
0
=
10
w_0=10
w0=10 时,梯度计算结果为
∇
w
f
(
w
0
)
=
28
(
10
−
2
)
=
28
∗
8
=
224
\nabla _wf(w_0)=28(10-2)=28*8=224
∇wf(w0)=28(10−2)=28∗8=224
至此,参数
w
w
w 就完成了第一次移动,
w
0
→
w
1
w_0 \rightarrow w_1
w0→w1。
2.2 梯度下降的多轮迭代
当然,
w
w
w 从
w
0
w_0
w0 移动到
w
1
w_1
w1 其实只移动了一步,要最终抵达损失函数的最小值点
w
=
2
w=2
w=2,还有很长一段距离,因此我们还需要移动多次。
参数多次移动过程中的每一步其实都是在重复上述三个步骤。例如,当我们需要第二次移动
w
w
w 时,过程如下:
w
2
=
w
1
−
l
r
⋅
∇
w
f
(
w
1
)
w_2 = w_1 - lr \cdot \nabla _wf(w_1)
w2=w1−lr⋅∇wf(w1)
即
w
2
w_2
w2 等于
w
1
w_1
w1 沿着
w
1
w_1
w1 的负梯度方向移动了
l
r
⋅
∇
w
f
(
w
1
)
lr\cdot\nabla _wf(w_1)
lr⋅∇wf(w1) 距离之后得到的结果。此时我们是在
w
1
w_1
w1 的基础上,减去学习率和
w
1
w_1
w1 梯度(将
w
1
w_1
w1 的值带入梯度计算公式当中即可)的乘积。
当然,依此类推,当从
w
(
n
−
1
)
w_{(n-1)}
w(n−1) 移动到
w
n
w_n
wn 时
w
n
w_n
wn 的计算公式如下:
w
n
=
w
(
n
−
1
)
−
l
r
⋅
∇
w
f
(
w
(
n
−
1
)
)
w_n = w_{(n-1)} - lr \cdot \nabla _wf(w_{(n-1)})
wn=w(n−1)−lr⋅∇wf(w(n−1))
并且,我们可以称从
w
0
w_0
w0 移动到
w
1
w_1
w1 是第一轮迭代,从
w
1
w_1
w1 移动到
w
2
w_2
w2 是第二轮迭代,从
w
(
n
−
1
)
w_{(n-1)}
w(n−1) 移动到
w
n
w_n
wn 是第 n 轮迭代。
defshow_trace(res):"""
梯度下降轨迹绘制函数
"""
f_line = np.arange(-6,10,0.1)
plt.plot(f_line,[14* np.power(x-2,2)for x in f_line])
plt.plot(res,[14* np.power(x-2,2)for x in res],'-o')
plt.xlabel('x')
plt.ylabel('Loss(x)')
show_trace(res)
接下来,我们在此基础之上,详细讨论梯度下降算法的基本特性与超参数取值一般规范。
从图像上,我们发现,梯度下降的计算过程中,刚开始
w
w
w 变化非常快,而随着逐渐接近最小值点
w
w
w 的变化幅度逐渐减少,也就是说,梯度下降其实并不是一个等步长的移动过程。
根据梯度下降的计算公式,
w
(
n
−
1
)
w_{(n-1)}
w(n−1) 和
w
n
w_{n}
wn 的差值是:
∣
w
n
−
w
(
n
−
1
)
∣
=
∣
l
r
⋅
∇
w
f
(
w
(
n
−
1
)
)
∣
|w_n - w_{(n-1)}| = |lr \cdot \nabla _wf(w_{(n-1)})|
∣wn−w(n−1)∣=∣lr⋅∇wf(w(n−1))∣
而 lr 又是一个恒定的数值,那么也就是说,每个点的梯度值是不一样的(哪怕梯度方向一样),并且越靠近最小值点梯度值越小,当然这点从梯度计算公式也能看出:
∇
w
f
(
w
)
=
∂
f
∂
w
=
28
(
w
−
2
)
\nabla _wf(w) = \frac{\partial f}{\partial w}=28(w-2)
∇wf(w)=∂w∂f=28(w−2)
例如在 Lesson 1 中我们曾利用带截距项的简单线性回归
y
=
w
x
+
b
y=wx+b
y=wx+b 对下述数据进行拟合,并得出带有两个参数的损失函数:
数据特征
参数组
模型输出
数据标签
Whole weight(x)
(
w
,
b
)
(w,b)
(w,b)
y
^
\hat y
y^
Rings(y)
1
(w, b)
w+b
2
3
(w, b)
3w+b
4
S
S
E
L
o
s
s
(
w
,
b
)
=
(
y
1
−
y
^
1
)
2
+
(
y
2
−
y
^
2
)
2
=
(
2
−
w
−
b
)
2
+
(
4
−
3
w
−
b
)
2
SSELoss(w, b) = (y_1 - ŷ_1)^2 + (y_2 - ŷ_2)^2 = (2 - w - b)^2 + (4 - 3w - b)^2
SSELoss(w,b)=(y1−y^1)2+(y2−y^2)2=(2−w−b)2+(4−3w−b)2
而此时损失函数图像就是包含两个变量的三维图像
from mpl_toolkits.mplot3d import Axes3D
x = np.arange(-1,3,0.05)
y = np.arange(-1,3,0.05)
w, b = np.meshgrid(x, y)
SSE =(2- w - b)**2+(4-3* w - b)**2
ax = plt.axes(projection='3d')
ax.plot_surface(w, b, SSE, cmap='rainbow')
ax.contour(w, b, SSE, zdir='z', offset=0, cmap="rainbow")#生成z方向投影,投到x-y平面
plt.xlabel('w')
plt.ylabel('b')
plt.show()
并且,我们通过最小二乘法能够找出全域最小值点为 (1,1),也就是当
w
=
1
,
b
=
1
w=1,b=1
w=1,b=1 时,能够让损失函数取值最小。当然,围绕该问题,我们也可以采用梯度下降进行最优解求解。
2. 梯度下降计算流程
此处给出更加严谨的梯度下降实现步骤:
(1) 确定数据及模型
首先还是要明确数据和模型,然后才能进一步确定参数、梯度计算公式等变量。
此处我们以
y
=
w
x
+
b
y=wx+b
y=wx+b 简单线性回归对上述简单数据集进行拟合,参数向量包含两个分量。
(2) 设置初始参数值
然后我们需要选取一组参数值,一种更加通用的方法,是给初始参数赋予一组随机数作为初始取值。
由于参数包含两个分量,因此参数向量的初始值可以由如下过程确定:
np.random.seed(24)
w = np.random.randn(2,1)# 参数都默认为列向量
w
#array([[ 1.32921217],# [-0.77003345]])
(3) 根据损失函数求出梯度表达式
只有确定了梯度表达式,才能在后续每一轮迭代过程中快速计算梯度。
线性回归的损失函数是围绕 SSE 及其衍生的指标所构建的损失函数,此处以 MSE 作为损失函数的构建指标,此处可借助已经定义好的 SSELoss 函数执行计算。
S
S
E
L
o
s
s
(
w
^
)
=
∣
∣
y
−
X
w
^
∣
∣
2
2
=
(
y
−
X
w
^
)
T
(
y
−
X
w
^
)
SSELoss(\hat w) = ||y - X\hat w||_2^2 = (y - X\hat w)^T(y - X\hat w)
SSELoss(w^)=∣∣y−Xw^∣∣22=(y−Xw^)T(y−Xw^)
M
S
E
L
o
s
s
(
w
^
)
=
∣
∣
y
−
X
w
^
∣
∣
2
2
m
=
(
y
−
X
w
^
)
T
(
y
−
X
w
^
)
m
MSELoss(\hat w) = \frac{||y - X\hat w||_2^2}{m} = \frac{(y - X\hat w)^T(y - X\hat w)}{m}
MSELoss(w^)=m∣∣y−Xw^∣∣22=m(y−Xw^)T(y−Xw^)
features = np.array([1,3]).reshape(-1,1)
features = np.concatenate((features, np.ones_like(features)), axis=1)
features
#array([[1, 1],# [3, 1]])
labels = np.array([2,4]).reshape(-1,1)
labels
#array([[2],# [4]])# 计算w取值时SSE
SSELoss(features, w, labels)#array([[2.68811092]])# 计算w取值时MSE
MSELoss(features, w, labels)#array([[1.34405546]])
而对于线性回归损失函数梯度表达式,则可根据 Lesson 2 中线性回归损失函数梯度求导结果得出:
S
S
E
L
o
s
s
(
w
^
)
∂
w
^
=
∂
∣
∣
y
−
X
w
^
∣
∣
2
2
∂
w
^
=
∂
(
y
−
X
w
^
)
T
(
y
−
X
w
^
)
∂
w
^
=
∂
(
y
T
−
w
^
T
X
T
)
(
y
−
X
w
^
)
∂
w
^
=
∂
(
y
T
y
−
w
^
T
X
T
y
−
y
T
X
w
^
+
w
^
T
X
T
X
w
^
)
∂
w
^
=
0
−
X
T
y
−
X
T
y
+
X
T
X
w
^
+
(
X
T
X
)
T
w
^
=
0
−
X
T
y
−
X
T
y
+
2
X
T
X
w
^
=
2
(
X
T
X
w
^
−
X
T
y
)
=
2
X
T
(
X
w
^
−
y
)
\begin{aligned} \frac{SSELoss(\hat w)}{\partial{\boldsymbol{\hat w}}} &= \frac{\partial{||\boldsymbol{y} - \boldsymbol{X\hat w}||_2}^2}{\partial{\boldsymbol{\hat w}}} \\ &= \frac{\partial(\boldsymbol{y} - \boldsymbol{X\hat w})^T(\boldsymbol{y} - \boldsymbol{X\hat w})}{\partial{\boldsymbol{\hat w}}} \\ & =\frac{\partial(\boldsymbol{y}^T - \boldsymbol{\hat w^T X^T})(\boldsymbol{y} - \boldsymbol{X\hat w})}{\partial{\boldsymbol{\hat w}}}\\ &=\frac{\partial(\boldsymbol{y}^T\boldsymbol{y} - \boldsymbol{\hat w^T X^Ty}-\boldsymbol{y}^T\boldsymbol{X \hat w} +\boldsymbol{\hat w^TX^T}\boldsymbol{X\hat w})}{\partial{\boldsymbol{\hat w}}}\\ & = 0 - \boldsymbol{X^Ty} - \boldsymbol{X^Ty}+X^TX\hat w+(X^TX)^T\hat w \\ &= 0 - \boldsymbol{X^Ty} - \boldsymbol{X^Ty} + 2\boldsymbol{X^TX\hat w}\\ &= 2(\boldsymbol{X^TX\hat w} - \boldsymbol{X^Ty}) \\ &= 2X^T(X\hat w -y) \end{aligned}
∂w^SSELoss(w^)=∂w^∂∣∣y−Xw^∣∣22=∂w^∂(y−Xw^)T(y−Xw^)=∂w^∂(yT−w^TXT)(y−Xw^)=∂w^∂(yTy−w^TXTy−yTXw^+w^TXTXw^)=0−XTy−XTy+XTXw^+(XTX)Tw^=0−XTy−XTy+2XTXw^=2(XTXw^−XTy)=2XT(Xw^−y)
此处我们使用 MSE 作为损失函数,则该损失函数的梯度表达式为:
M
S
E
L
o
s
s
(
w
^
)
∂
w
^
=
2
X
T
(
X
w
^
−
y
)
m
\frac{MSELoss(\hat w)}{\partial{\boldsymbol{\hat w}}} = \frac{2X^T(X\hat w -y)}{m}
∂w^MSELoss(w^)=m2XT(Xw^−y)
这里需要注意的是,矩阵运算也是可以提取公因式的。
deflr_gd(X, w, y):"""
线性回归梯度计算公式
"""
m = X.shape[0]
grad =2* X.T.dot((X.dot(w)- y))/ m
return grad
# 计算w取值时梯度
lr_gd(features, w, labels)#array([[-3.78801208],# [-2.22321821]])
在给定梯度计算公式和参数初始值的情况下,我们就能够开始进行梯度下降迭代计算了。在梯度下降过程中,参数更新参照如下公式:
w
n
=
w
(
n
−
1
)
−
l
r
⋅
∇
w
f
(
w
(
n
−
1
)
)
w_n = w_{(n-1)} - lr \cdot \nabla _wf(w_{(n-1)})
wn=w(n−1)−lr⋅∇wf(w(n−1))
则可定义相应函数:
defw_cal(X, w, y, gd_cal, lr =0.02, itera_times =20):"""
梯度下降中参数更新函数
:param X: 训练数据特征
:param w: 初始参数取值
:param y: 训练数据标签
:param gd_cal:梯度计算公式
:param lr: 学习率
:param itera_times: 迭代次数
:return w:最终参数计算结果
"""for i inrange(itera_times):
w -= lr * gd_cal(X, w, y)return w
接下来执行梯度下降计算,在学习率为 0.1 的情况下迭代 100 轮:
np.random.seed(24)
w = np.random.randn(2,1)# 参数都默认为列向量
w
#array([[ 1.32921217],# [-0.77003345]])
w = w_cal(features, w, labels, gd_cal = lr_gd, lr =0.1, itera_times =100)
w
#array([[1.02052278],# [0.95045363]])# 计算w取值时SSE
SSELoss(features, w, labels)#array([[0.0009869]])# 计算w取值时MSE
MSELoss(features, w, labels)#array([[0.00049345]])# 计算w取值时梯度
lr_gd(features, w, labels)#array([[ 0.0070423 ],# [-0.01700163]])
假设
f
(
x
)
=
x
⋅
c
o
s
(
π
x
)
f(x)=x\cdot cos(\pi x)
f(x)=x⋅cos(πx) 就是此时的损失函数,并且此时存在唯一的参数 x,则梯度下降迭代计算有如下过程。
这里需要注意的是,此处我们在未知数据、模型和评估指标的情况下,直接给出了损失函数。
首先是由损失函数到梯度计算表达式的过程,由于损失函数只有一个参数,所以梯度表达式为
f
′
(
x
)
=
(
x
⋅
c
o
s
(
π
x
)
)
′
=
c
o
s
(
π
x
)
+
x
⋅
(
−
s
i
n
(
π
x
)
)
⋅
π
f'(x) = (x\cdot cos(\pi x))' = cos(\pi x) + x\cdot (-sin(\pi x)) \cdot \pi
f′(x)=(x⋅cos(πx))′=cos(πx)+x⋅(−sin(πx))⋅π
defshow_trace_1(res):"""
梯度下降轨迹绘制函数
"""
f_line = np.arange(-1,2,0.01)
plt.plot(f_line,[f_1(x)for x in f_line])
plt.plot(res,[f_1(x)for x in res],'-o')
plt.xlabel('x')
plt.ylabel('Loss(x)')
res = gd_2(itera_times=5000)
res[-1]#0.0013297766246039373
defshow_trace_2(res):"""
梯度下降轨迹绘制函数
"""
f_line = np.arange(-1,2,0.01)
plt.plot(f_line,[f_2(x)for x in f_line])
plt.plot(res,[f_2(x)for x in res],'-o')
plt.xlabel('x')
plt.ylabel('Loss(x)')