在正式开始进行神经网络建模之前,我们还需要掌握pytorch
中最核心的基础数学工具——autograd(自动微分)
模块。虽然对于任何一个通用的深度学习框架都会提供许多自动优化的算法和现成的loss function
,但如果想更深入理解神经网络,对深度学习的建模不仅仅停留在调包和调参的层次,就必须掌握一些数学工具,在日后取得更好的建模效果。当然,要使用好微分工具,就首先要了解广泛应用于机器学习建模的优化思想。
所谓优化思想,指的是利用数学工具求解复杂问题的基本思想。在实际建模过程中,我们往往会给出待解决问题的数值评估指标,并在此基础上构建方程、采用数学工具、不断优化评估指标结果,以期达到可以达到的最优结果。
import numpy as np
import torch
21 简单线性回归的机器学习建模思路
在上一节末尾,我们曾简单提及线性方程建模问题,将方程组转化为矩阵表示形式,最终使用逆矩阵的方法解出线性方程系数。线性回归是较为基础且通用的模型,但使用矩阵方法求解不是通用方法。
接下来将进一步将简单线性回归的求解参数问题转化为最优化问题求解,这也是机器学习建模中最通用的思想。
21.1 回顾简单线性回归建模问题
import matplotlib as mpl
import matplotlib.pyplot as plt
A = torch.arange(1, 5).reshape(2, 2).float()
A
# output :
tensor([[1., 2.],
[3., 4.]])
# 绘制点图查看两个点的位置
plt.plot(A[:, 0], A[:, 1], 'o')
# output :
如果更近一步,我们希望在二维空间中找到一条直线,来拟合这两个点,也就是所谓的构建以恶线性回归模型,我们可以设置线性回归方程如下:
y
=
a
x
+
b
y = ax + b
y=ax+b
21.2 转化为优化问题
上述问题除了可以使用矩阵方法求解以外,还可以将其转化为最优化问题,然后通过求解最优化问题的方法对其进行求解。
最优化问题的转化分为两步,其一是确定优化数值指标,其二则是确定优化目标函数。在大多数问题中,这二者是相辅相成的,确定了优化的数值指标,也就确定了优化的目标函数。
如果我们希望通过一条直线拟合二维平面空间上分布的点,最核心的目标毫无疑问就是希望方程的预测值和真实值相差较小。假设真实的y
值用y
表示,预测值用ŷ
表示,带入a、b
参数,则表示如下:
x(i) | y(i) | ŷ(i) |
---|---|---|
1 | 2 | a+b |
3 | 4 | 3a+b |
y ^ 1 = 1 ∗ a + b = a + b ŷ_1 = 1*a + b = a + b y^1=1∗a+b=a+b
y ^ 2 = 3 ∗ a + b = 3 a + b ŷ_2 = 3*a + b = 3a + b y^2=3∗a+b=3a+b
而这两个预测值和真实值相差:
y
1
=
2
,
y
^
1
=
a
+
b
,
y
1
−
y
^
1
=
2
−
a
−
b
y_1 = 2, ŷ_1 = a + b, y_1 - ŷ_1 = 2 - a - b
y1=2,y^1=a+b,y1−y^1=2−a−b
y 2 = 4 , y ^ 2 = 3 a + b , y 2 − y ^ 1 = 2 = 4 − 3 a − b y_2 = 4, ŷ_2 = 3a + b, y_2 - ŷ_1=2 = 4 - 3a - b y2=4,y^2=3a+b,y2−y^1=2=4−3a−b
我们希望y
和ŷ
尽可能接近,因此可以考虑计算上述误差总和,但为了避免正负相消,我们使用平方和来进行衡量两个点的误差总和,而不是简单的求和:
(
y
1
−
y
^
1
)
2
+
(
y
2
−
y
^
2
)
2
(y_1 - ŷ_1)^2 + (y_2 - ŷ_2)^2
(y1−y^1)2+(y2−y^2)2
= ( 2 − a − b ) 2 + ( 4 − 3 a − b ) 2 = (2 - a - b)^2 + (4 - 3a - b)^2 =(2−a−b)2+(4−3a−b)2
上式也就是两个点的预测值和真实值间差值的平方和,也就是误差平方和(Sum of the Squared Errors)
。
此处只带入了
(1,2)
和(3,4)
两个点来计算SSE,也就是带入了两条数据来训练y = ax + b
这个模型。
至此,我们已经将原问题转化为了一个最优化问题,接下来我们的问题是,当a、b取何值时,SSE取值最小?
值得注意的是,SSE
方程就是我们优化的目标方程(求最小值),因此上述方程也被称为目标函数,同时,SSE
代表着真实和预测之间的差值,因此也被称为损失函数。
换言之,当
SSE
取值最小的时候,a、b
的取值就是最终线性回归方程的系数取值。
注: 目标函数和损失函数并不完全等价,但大多数目标函数都由损失函数构成。
21.3 最优化问题的求解方法
在上述问题中,我们需要围绕SSE
求最小值。SSE
是一个关于a
和b
的二元函数,要求其最小值,需要借助数学工具,也就是所谓的最优化方法。选择优化方法并执行相应计算,可以硕士整个建模过程中最核心也是相对较难的部分,很多时候这个过程会直接决定模型的性能。
图形展示目标函数
为了更好讨论目标函数(SSE)
求最小值的过程,对于上述二元函数,我们可以将其展示在三维空间内。
from matplotlib import pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
x = np.arange(-1, 3, 0.05)
y = np.arange(-1, 3, 0.05)
a, b = np.meshgrid(x, y) # 生成一个二维的网格点坐标矩阵a和b
SSE = (2 - a - b) ** 2 + (4 - 3 * a - b) ** 2
ax = plt.axes(projection = '3d') # 创建了一个三维坐标轴对象ax
ax.plot_surface(a, b, SSE, cmap = 'rainbow') # 在三维坐标轴上绘制曲面,cmap='rainbow'指定了颜色映射,使得曲面根据高度显示不同的颜色。
ax.contour(a, b, SSE, zdir = 'z', offset = 0, cmap = 'rainbow')
plt.show() # 在三维视图中添加了等高线图
# output :
函数的凹凸性
不难看出目标函数是个整体“向下凸”
的函数。
函数的凹凸性是函数的重要性质,首先我们给出凸函数的一般定义,对于任意一个函数,如果函数f(x)
上存在任意两个点x1,x2,且
(
f
(
x
1
)
+
f
(
x
2
)
)
/
2
>
=
f
(
(
x
1
+
x
2
)
/
2
)
(f(x_1) + f(x_2))/2 >= f((x_1 + x_2)/2)
(f(x1)+f(x2))/2>=f((x1+x2)/2)
我们就判定这个函数是凸函数。
典型的,例如y = x2,我们可以绘制函数图像如下:
x = np.arange(-10,10,0.1)
y = x ** 2
plt.plot(x, y, '-')
plt.show()
# output :
不难看出,函数上任意两个点y的取值的均值都不小于这两个点均值的y值。
# x1 = 1, x2 = 3
(1 ** 2 + 3 ** 2)/2
# output :
5.0
# x1 = 1, x2 = 3
((1+3)/2) ** 2
# output :
4.0
而对于一个凸函数来说,全域最小值明显存在,基于凸函数的数学定义,我们可以进一步给出求解上述SSE
凸函数最小值的一般方法,也就是著名的最小二乘法。
凸函数的最小值
通过y = x2函数不难看出,最小值x = 0
唯一存在,并且最小值点对应的函数切线与x轴平行,也就是在最小值点,函数的导数为0。这其实也凸函数求解最小值的一般方法:
-
对于一元函数,如果存在导数为0的点,则该点就是最小值点;
-
对于多元函数,如果存在某一点,使得函数的各个自变量的偏导数都为0,则该点就是最小值点。
因此,对于凸函数的最小值求解,最基本的出发点就是寻找导数为0的点。而最小二乘法也是基于偏导函数取值为0联立的方程组进行的求解。
从更严格的意义上来说,凸函数的最小值点其实是根据边界点和驻点(导数为0的点)决定,如果没有边界点且没有驻点,则函数没有最小值(例如
y = x
),如果存在边界点,但没有驻点,则边界点的一侧就是最小值点。如果存在驻点(且左右两边单调性相反),则驻点就是最小值点,例如,对于y = x2而言,
y′ = 2x
,2x = 0
时x取值为0
,也就是0点就是最小值点
。
值得注意的是,驻点也可以说是临界点,但不是拐点,拐点特指左右两边函数凹凸性发生变化的点,切勿和驻点混淆。
机器学习建模中的目标函数,大多数时候都是可导的函数,而凹凸性则是影响使用哪种最优化方法的最核心因素。正因如此,凸函数的最优化问题是优化方法的一类重要应用,甚至围绕凸函数,还衍生出了凸优化相关的一大类优化问题分支学科。
在机器学习中,我们经常听说的最小二乘法就是求解凸优化问题的重要工具。当然,很多本身不是针对凸函数设计的最优化方法,但鉴于凸函数在求最小值时的优异特性,我们也会在凸优化过程中应用,例如梯度下降、拟牛顿法等等等等,都是本次重点学习的内容。
SSE最小值
对于简单线性回归的损失函数,SSE是凸函数,因此,对于$ SSE(a,b) = (2 - a - b)^2 + (4 - 3a - b)^2 $而言,最小值点就是a、b两个参数求偏导等于0的点
S
S
E
=
(
y
1
−
y
^
1
)
2
+
(
y
2
−
y
^
2
)
2
SSE = (y_1 - ŷ_1)^2 + (y_2 - ŷ_2)^2
SSE=(y1−y^1)2+(y2−y^2)2
= ( 2 − a − b ) 2 + ( 4 − 3 a − b ) 2 = (2 - a - b)^2 + (4 - 3a - b)^2 =(2−a−b)2+(4−3a−b)2
∂ S S E ( a , b ) ∂ ( a ) = 0 \frac{\partial{SSE_(a,b)}}{\partial{(a)}} = 0 ∂(a)∂SSE(a,b)=0
∂ S S E ( a , b ) ∂ ( b ) = 0 \frac{\partial{SSE_(a,b)}}{\partial{(b)}} = 0 ∂(b)∂SSE(a,b)=0
其中:
∂
S
S
E
(
a
,
b
)
∂
(
a
)
=
2
(
2
−
a
−
b
)
∗
(
−
1
)
+
2
(
4
−
3
a
−
b
)
∗
(
−
3
)
=
20
a
+
8
b
−
28
(
1
)
=
0
\begin{align} \frac{\partial{SSE_{(a,b)}}}{\partial{(a)}} & = 2(2-a-b)*(-1) + 2(4-3a-b)*(-3)\\ & = 20a+8b-28(1)\\ & = 0 \end{align}
∂(a)∂SSE(a,b)=2(2−a−b)∗(−1)+2(4−3a−b)∗(−3)=20a+8b−28(1)=0
∂ S S E ( a , b ) ∂ ( b ) = 2 ( 2 − a − b ) ∗ ( − 1 ) + 2 ( 4 − 3 a − b ) ∗ ( − 1 ) = 8 a + 4 b − 12 ( 2 ) = 0 \begin{align} \frac{\partial{SSE_{(a,b)}}}{\partial{(b)}} & = 2(2-a-b)*(-1) + 2(4-3a-b)*(-1)\\ & = 8a+4b-12(2) \\ & = 0 \end{align} ∂(b)∂SSE(a,b)=2(2−a−b)∗(−1)+2(4−3a−b)∗(−1)=8a+4b−12(2)=0
( 1 ) 式 − ( 2 ) 式 ∗ 2 可得: 4 a − 4 = 0 , a = 1 (1)式 - (2)式*2 可得: 4a-4 = 0,a=1 (1)式−(2)式∗2可得:4a−4=0,a=1
将 a = 1 带入 ( 2 ) 式可得: 4 b − 4 = 0 , b = 1 将a=1带入(2)式 可得: 4b-4 = 0,b=1 将a=1带入(2)式可得:4b−4=0,b=1
y = x + 1 y = x + 1 y=x+1
利用偏导等于0得出的方程组求解线性回归方程参数,就是最小二乘法求解过程。
此处我们求得a = 1,b = 1
时,SSE(a,b)
取得最小值,也就是(1,1)
是目标函数的最小值点。
21.4 机器学习建模一般流程
至此,我们就完成了一个基本的简单线性回归建模过程。当然,对于线性回归来说,有很多种建模方式,哪怕是主流的统计学和机器学习,在利用线性回归进行建模时都有不一样的流程。
此处我们是通过一个简单的例子,来介绍机器学习、包括深度学习的利用优化方法建模的一般思想,我们可以将其总结如下:
-
Step 1
:提出基本模型
如本节中,我们试图利用一条直线(y=ax+b)去拟合二维平面空间中的点,这里我们所使用的这条直线,就是我们提出的基本模型。而在后续的深度学习的学习过程中,我们还将看到更为强大、同时也更加通用的神经网络模型。当然,不同的模型能够适用不同的场景,在提出模型时,我们往往会预设一些影响模型结构或者实际判别性能的参数,如简单线性回归中的a和b; -
Step 2
:确定损失函数和目标函数接下来,围绕建模的目标,我们需要合理设置损失函数,并在此基础之上设置目标函数,当然,在很多情况下,这二者是相同的。例如,在上述简单线性回归中,我们的建模目标就是希望y=ax+b这条直线能够尽可能的拟合(1,2)、(3,4)这两个点,或者说尽可能“穿过”这两个点,因此我们设置了SSE作为损失函数,也就是预测值和真实值的差值平方和。当然,在计算过程中不难发现,SSE是一个包含了a和b这两个变量的方程,因此SSE本身也是一个函数(a和b的二元函数),并且在线性回归中,SSE既是损失函数(用于衡量真实值和预测值差值的函数),同时也是我们的目标函数(接下来需要优化、或者说要求最小值的函数)。这里尤其需要注意的是,损失函数不是模型,而是模型参数所组成的一个函数。
-
Step 3
:根据目标函数特性,选择优化方法,求解目标函数之前提到,目标函数既承载了我们优化的目标(让预测值和真实值尽可能接近),同时也是包含了模型参数的函数,因此完成建模需要确定参数、优化结果需要预测值尽可能接近真实值这两方面需求就统一到了求解目标函数最小值的过程中了,也就是说,当我们围绕目标函数求解最小值时,也就完成了模型参数的求解。当然,这个过程本质上就是一个数学的最优化过程,求解目标函数最小值本质上也就是一个最优化问题,而要解决这个问题,我们就需要灵活适用一些最优化方法。当然,在具体的最优化方法的选择上,函数本身的性质是重要影响因素,也就是说,不同类型、不同性质的函数会影响优化方法的选择。在简单线性回归中,由于目标函数是凸函数,我们根据凸函数性质,判断偏导函数取值为0的点就是最小值点,进而完成a、b的计算(也就是最小二乘法),其实就是通过函数本身的性质进行最优化方法的选取。
22 第一个优化算法:最小二乘法
前面提到,利用优化方法求解目标函数其实是机器学习建模过程中最为核心的环节,因此,我们有必要将围绕上述简单线性回归问题,进一步讨论最小二乘法背后的数学逻辑和优化思想,同时简单探讨数据的矩阵表示方法和基本矩阵运算。
虽然最小二乘法并不是主流的深度学习损失函数的优化算法,但从最小二乘法入手了解优化算法背后的数学逻辑,却是非常有必要,同时,线性方程也是构建神经网络模型的基础,因此,我们有必要深入探讨线性模型建模细节以及最基本的优化算法:最小二乘法。
最小二乘法的代数表示方法
从更加严格的数学角度出发,最小二乘法有两种表示形式,分别是代数法表示和矩阵表示。我们先看最小二乘法的代数表示方法。
首先,假设多元线性方程有如下形式:
f
(
x
)
=
w
1
x
1
+
w
2
x
2
+
.
.
.
+
w
d
x
d
+
b
f(x) = w_1x_1+w_2x_2+...+w_dx_d+b
f(x)=w1x1+w2x2+...+wdxd+b
令
w
=
(
w
1
,
w
2
,
.
.
.
w
d
)
w = (w_1,w_2,...w_d)
w=(w1,w2,...wd),
x
=
(
x
1
,
x
2
,
.
.
.
x
d
)
x = (x_1,x_2,...x_d)
x=(x1,x2,...xd),则上式可写为
f
(
x
)
=
w
T
x
+
b
f(x) = w^Tx+b
f(x)=wTx+b
在机器学习领域,我们将线性回归自变量系数命名为
w
,其实是weight
的简写,意为自变量的权重
。
多元线性回归的最小二乘法的代数法表示较为复杂,此处先考虑简单线性回归的最小二乘法表示形式。在简单线性回归中,w
只包含一个分量,x
也只包含一个分量,我们令此时的
x
i
x_i
xi就是对应的自变量的取值,此时求解过程如下
优化目标可写为:
S
S
E
=
∑
i
=
1
m
(
f
(
x
i
)
−
y
i
)
2
=
E
(
w
,
b
)
SSE = \sum^m_{i=1}(f(x_i)-y_i)^2 = E_(w,b)
SSE=i=1∑m(f(xi)−yi)2=E(w,b)
通过偏导为0求得最终结果的最小二乘法求解过程为:
∂
S
S
E
(
w
,
b
)
∂
(
w
)
=
2
(
w
∑
i
=
1
m
x
i
2
−
∑
i
=
1
m
(
y
i
−
b
)
x
i
)
=
0
\begin{align} \frac{\partial{SSE_(w,b)}}{\partial{(w)}} & = 2(w\sum^m_{i=1}x^2_i - \sum^m_{i=1}(y_i-b)x_i) = 0 \end{align}
∂(w)∂SSE(w,b)=2(wi=1∑mxi2−i=1∑m(yi−b)xi)=0
∂ S S E ( w , b ) ∂ ( b ) = 2 ( m b − ∑ i = 1 m ( y i − w x i ) ) = 0 \begin{align} \frac{\partial{SSE_(w,b)}}{\partial{(b)}} & = 2(mb - \sum^m_{i=1}(y_i-wx_i)) = 0 \end{align} ∂(b)∂SSE(w,b)=2(mb−i=1∑m(yi−wxi))=0
进而可得
w
=
∑
i
=
1
m
y
i
(
x
i
−
x
ˉ
)
∑
i
=
1
m
x
i
2
−
1
m
(
∑
i
=
1
m
x
i
)
2
w = \frac{\sum^m_{i=1}y_i(x_i-\bar{x}) }{\sum^m_{i=1}x^2_i-\frac{1}{m}(\sum^m_{i=1}x_i)^2 }
w=∑i=1mxi2−m1(∑i=1mxi)2∑i=1myi(xi−xˉ)
b = 1 m ∑ i = 1 m ( y i − w x i ) b = \frac{1}{m}\sum^m_{i=1}(y_i-wx_i) b=m1i=1∑m(yi−wxi)
其中, x ˉ = 1 m ∑ i = 1 m x i , x i \bar x = \frac{1}{m}\sum^m_{i=1}x_i,x_i xˉ=m1∑i=1mxi,xi为x的均值,并且 ( x i , y i ) (x_i,y_i) (xi,yi)代表二维空间中的点。
最小二乘法的矩阵表示形式
从上节的矩阵部分内容不难理解,对于线性方程组来说,矩阵表示是一种更加简洁的表示方式,并且对于支持数组运算的torch
来说,线性方程组的矩阵表示也更贴近代码的实际书写形式。首先先回顾上节中提到的将上述方程转化为矩阵的过程。线性方程如下
1
∗
a
+
b
=
2
1*a + b = 2
1∗a+b=2
3 ∗ a + b = 4 3*a + b = 4 3∗a+b=4
在转化为矩阵表示的过程中,我们令
A
=
[
1
1
3
1
]
A = \left [\begin{array}{cccc} 1 &1 \\ 3 &1 \\ \end{array}\right]
A=[1311]
B = [ 2 4 ] B = \left [\begin{array}{cccc} 2 \\ 4 \\ \end{array}\right] B=[24]
X T = [ a b ] X^T = \left [\begin{array}{cccc} a \\ b \\ \end{array}\right] XT=[ab]
则原方程组可表示为
A
∗
X
T
=
B
A * X^T = B
A∗XT=B
更为一般的情况下,多元线性回归方程为
f
(
x
)
=
w
1
x
1
+
w
2
x
2
+
.
.
.
+
w
d
x
d
+
b
f(x) = w_1x_1+w_2x_2+...+w_dx_d+b
f(x)=w1x1+w2x2+...+wdxd+b
令
w
^
=
(
w
1
,
w
2
,
.
.
.
,
w
d
,
b
)
\hat w = (w_1,w_2,...,w_d,b)
w^=(w1,w2,...,wd,b)
x ^ = ( x 1 , x 2 , . . . , x d , 1 ) \hat x = (x_1,x_2,...,x_d,1) x^=(x1,x2,...,xd,1)
- w ^ \hat w w^:方程系数所组成的向量,并且我们将自变量系数和截距放到了一个向量中,此处 w ^ \hat w w^就相当于前例中的a、b组成的向量(a,b);
- x ^ \hat x x^:方程自变量和1共同组成的向量;
因此,方程可表示为
f
(
x
)
=
w
^
∗
x
^
T
f(x) = \hat w * \hat x^T
f(x)=w^∗x^T
另外,我们将所有自变量的值放在一个矩阵中,并且和此前A矩阵类似,为了捕捉截距,添加一列全为1的列在矩阵的末尾,设总共有m组取值,则
X
=
[
x
11
x
12
.
.
.
x
1
d
1
x
21
x
22
.
.
.
x
2
d
1
.
.
.
.
.
.
.
.
.
.
.
.
1
x
m
1
x
m
2
.
.
.
x
m
d
1
]
X = \left [\begin{array}{cccc} x_{11} &x_{12} &... &x_{1d} &1 \\ x_{21} &x_{22} &... &x_{2d} &1 \\ ... &... &... &... &1 \\ x_{m1} &x_{m2} &... &x_{md} &1 \\ \end{array}\right]
X=
x11x21...xm1x12x22...xm2............x1dx2d...xmd1111
对应到前例中的A矩阵,A矩阵就是拥有一个自变量、两个取值的X矩阵。令y为因变量的取值,则有
y
=
[
y
1
y
2
.
.
.
y
m
]
y = \left [\begin{array}{cccc} y_1 \\ y_2 \\ . \\ . \\ . \\ y_m \\ \end{array}\right]
y=
y1y2...ym
此时,SSE可表示为:
S
S
E
=
∣
∣
y
−
X
w
^
T
∣
∣
2
2
=
(
y
−
X
w
^
T
)
T
(
y
−
X
w
^
T
)
=
E
(
w
^
)
SSE = ||y - X\hat w^T||_2^2 = (y - X\hat w^T)^T(y - X\hat w^T) = E(\hat w)
SSE=∣∣y−Xw^T∣∣22=(y−Xw^T)T(y−Xw^T)=E(w^)
根据最小二乘法的求解过程,令
E
(
w
^
)
E(\hat w)
E(w^)对
w
^
\hat w
w^求导方程取值为0,有
E
(
w
^
)
∂
w
^
=
∂
∣
∣
y
−
X
w
^
T
∣
∣
2
2
∂
w
^
=
∂
(
y
−
X
w
^
T
)
T
(
y
−
X
w
^
T
)
∂
w
^
∵
(
A
−
B
)
T
=
A
T
−
B
T
并且
(
A
B
)
T
=
B
T
∗
A
T
∴
上式
=
∂
(
y
T
−
w
^
X
T
)
(
y
−
X
w
^
T
)
∂
w
^
=
∂
(
y
T
y
−
w
^
X
T
y
−
y
T
X
w
^
T
+
w
^
X
T
X
w
^
T
)
∂
w
^
=
0
−
X
T
y
−
X
T
y
+
2
X
T
X
w
^
T
=
X
T
X
w
^
T
−
X
T
y
=
0
值得注意的是,在矩阵求导中,
a
为常数,有如下规则:
∂
a
∂
A
=
0
,
∂
A
T
B
T
C
∂
A
=
B
T
C
,
∂
C
T
B
A
∂
A
=
B
T
C
,
∂
A
T
B
A
∂
A
=
(
B
+
B
T
)
A
\begin{aligned} \frac{E(\hat w)}{\partial{\boldsymbol{\hat w}}} &= \frac{\partial{||\boldsymbol{y} - \boldsymbol{X\hat w^T}||_2}^2}{\partial{\boldsymbol{\hat w}}} \\ &= \frac{\partial(\boldsymbol{y} - \boldsymbol{X\hat w^T})^T(\boldsymbol{y} - \boldsymbol{X\hat w^T})}{\partial{\boldsymbol{\hat w}}} \\ \\ \because &\ (A-B)^T = A^T - B^T并且(AB)^T = B^T*A^T \\ \\ \therefore& 上式=\frac{\partial(\boldsymbol{y}^T - \boldsymbol{\hat w X^T})(\boldsymbol{y} - \boldsymbol{X\hat w^T})}{\partial{\boldsymbol{\hat w}}} \\ &=\frac{\partial(\boldsymbol{y}^T\boldsymbol{y} - \boldsymbol{\hat w X^Ty}-\boldsymbol{y}^T\boldsymbol{X \hat w^T} +\boldsymbol{\hat wX^T}\boldsymbol{X\hat w^T})} {\partial{\boldsymbol{\hat w}}}\\ &= 0 - \boldsymbol{X^Ty} - \boldsymbol{X^Ty} + 2\boldsymbol{X^TX\hat w ^T}\\ &= \boldsymbol{X^TX\hat w^T} - \boldsymbol{X^Ty} = 0 \\ &值得注意的是,在矩阵求导中,a为常数,有如下规则: \begin{equation} \\\frac{\partial{a}}{\partial{A}} = 0, \ \ \ \frac{\partial{A^TB^TC}}{\partial{A}} = B^TC, \ \ \ \frac{\partial{C^TBA}}{\partial{A}} = B^TC, \ \ \ \frac{\partial{A^TBA}}{\partial{A}} = (B+B^T)A \end{equation} \end{aligned}
∂w^E(w^)∵∴=∂w^∂∣∣y−Xw^T∣∣22=∂w^∂(y−Xw^T)T(y−Xw^T) (A−B)T=AT−BT并且(AB)T=BT∗AT上式=∂w^∂(yT−w^XT)(y−Xw^T)=∂w^∂(yTy−w^XTy−yTXw^T+w^XTXw^T)=0−XTy−XTy+2XTXw^T=XTXw^T−XTy=0值得注意的是,在矩阵求导中,a为常数,有如下规则:∂A∂a=0, ∂A∂ATBTC=BTC, ∂A∂CTBA=BTC, ∂A∂ATBA=(B+BT)A
进一步可得
X
T
X
w
^
T
=
X
T
y
X^TX\hat w^T = X^Ty
XTXw^T=XTy
要使得此式有解,等价于
X
T
X
X^TX
XTX(也被称为矩阵的交叉乘积crossprod
存在逆矩阵,若存在,则可解出
w
^
T
=
(
X
T
X
)
−
1
X
T
y
\hat w ^T = (X^TX)^{-1}X^Ty
w^T=(XTX)−1XTy
最小二乘法的简单实现
回到最初的例子,不难发现,有如下对应关系:
X
=
A
=
[
1
1
3
1
]
X = A = \left [\begin{array}{cccc} 1 &1 \\ 3 &1 \\ \end{array}\right]
X=A=[1311]
y = B = [ 2 4 ] y = B = \left [\begin{array}{cccc} 2 \\ 4 \\ \end{array}\right] y=B=[24]
w ^ T = X T = [ a b ] \hat w ^T = X^T = \left [\begin{array}{cccc} a \\ b \\ \end{array}\right] w^T=XT=[ab]
手动实现代码验证最小二乘法
X = A
X
# output :
tensor([[1., 1.],
[3., 1.]])
y = B
y
# output :
tensor([[2.],
[4.]])
X.t()
# output :
tensor([[1., 3.],
[1., 1.]])
w = torch.mm(torch.mm(torch.inverse(torch.mm(X.t(),X)),X.t()),y)
w
# output :
tensor([[1.0000],
[1.0000]])
和此前结果保持一致。当然,最小二乘法作为最优化问题的求解方法,我们可以这么理解w最终取值:当w取值为(1,1)时,自变量为w的SSE函数取得全域最小值。
当然,我们也可以直接调用最小二乘法函数torch.lstsq(B, A)
进行求解
torch.lstsq(y, X)
# output :
torch.return_types.lstsq(
solution=tensor([[1.0000],
[1.0000]]),
QR=tensor([[-3.1623, -1.2649],
[ 0.7208, -0.6325]]))
对于lstsq
函数来说,第一个参数是因变量张量,第二个参数是自变量张量,并且同时返回结果还包括QR
矩阵分解的结果,QR
分解也是一种矩阵分解方法,后续在涉及到QR
分解内容时会详细进行讲解。
另外,在最小二乘法数学推导过程,涉及到矩阵范数的运算,在PyTorch
中,我们使用linalg.norm
函数求向量或矩阵的范数
在深度学习中,范数
(Norm)
是一个数学概念,用于衡量向量的大小或长度。它是一个非负值,可以用来表示向量在多维空间中的距离或幅度。范数在深度学习中被广泛应用于各种场景,例如正则化、梯度下降、损失函数等。常见的范数有:
L1范数(曼哈顿距离):向量各元素绝对值之和。
∥ x ∥ 1 = ∑ i = 1 n ∣ x i ∣ \|\mathbf{x}\|_1 = \sum_{i=1}^{n} |x_i| ∥x∥1=i=1∑n∣xi∣
L1范数在深度学习中常用于正则化,可以促进模型的稀疏性。L2范数(欧几里得距离):向量各元素平方和的平方根。
∥ x ∥ 2 = ∑ i = 1 n x i 2 \|\mathbf{x}\|_2 = \sqrt{\sum_{i=1}^{n} x_i^2} \ ∥x∥2=i=1∑nxi2L2范数在深度学习中常用于正则化,可以防止模型过拟合。
注意,老版本教材会推荐使用norm函数进行求解,但实际上新版
pytorch
已经开始推荐使用linalg.norm
替换norm
进行范数运算,因此更推荐使用linalg.norm
函数
t = torch.tensor([-1, 2.])
# 默认情况,求解L2范数,各元素的平方和开平方
torch.linalg.norm(t)
# output :
tensor(2.2361)
torch.sqrt(torch.tensor(5.))
# output :
tensor(2.2361)
# 输入参数,求解L1范数,各元素的绝对值之和
torch.linalg.norm(t, 1)
# output :
tensor(3.)
反向验证导数为零
当然,我们也可以反向验证,看下损失函数SSE
在a=1,b=1
时偏导数是否都为0
。此时就需要借助PyTorch
中的autograd
模块来进行偏导计算。严格意义上来讲,autograd
模块是PyTorch
中的自动微分模块,我们可以通过autograd
模块中的函数进行微分运算,在神经网络模型中,通过自动微分运算求解梯度是模型优化的核心。关于微分计算梯度进而执行优化的相关方法我们会在后续逐步介绍,此处我们仅适用autograd
模块来进行简单的微分计算尝试,也就是对SSE
进行偏导计算,判断a、b
同时取值为1
时偏导是否为0
。
目前市面上流通的PyTorch
教材有些会介绍Variable
类,在进行微分运算时需提前将Tensor
类转化为Variable
类,但其实在PyTorch 0.4
版本以后Tensor
就已经不仅仅是一个纯计算的载体,而是可以支持微分运算,Variable
的概念被逐渐弱化,可微分性也变成了Tensor
的一个基本属性,我们只需要在创建Tensor
时,通过设置requires_grad
属性为True
、规定张量可微分即可。
a = torch.tensor(1.,requires_grad = True)
a
# output :
tensor(1., requires_grad=True)
此时a
就是一个可微分的张量,requires_grad
是a
的一个属性,可以查看可以修改。
# 查看可微分性
a.requires_grad
# output :
True
# 修改可微分性
a.requires_grad = False
a.requires_grad
# output :
False
a.requires_grad = True
b = torch.tensor(1.,requires_grad = True)
b
# output :
tensor(1., requires_grad=True)
然后创建损失函数
(
y
1
−
y
^
1
)
2
+
(
y
2
−
y
^
2
)
2
(y_1 - ŷ_1)^2 + (y_2 - ŷ_2)^2
(y1−y^1)2+(y2−y^2)2
= ( 2 − a − b ) 2 + ( 4 − 3 a − b ) 2 = (2 - a - b)^2 + (4 - 3a - b)^2 =(2−a−b)2+(4−3a−b)2
sse = torch.pow((2 - a - b), 2) + torch.pow((4 - 3 * a - b), 2)
使用torch.autograd.grad
进行偏导运算,并输出a=1、b=1时偏导数的值
torch.autograd.grad(sse,[a, b])
# output :
(tensor(-0.), tensor(-0.))
至此,也可验证(1,1)
是损失函数的最小值点。
torch.autograd.grad函数
torch.autograd.grad
是通用微分函数,当只输入一个自变量时计算结果就是导数,输入多个自变量时则会计算偏导数。
x = torch.tensor(1.,requires_grad = True)
y = x ** 2
# 导数计算结果
torch.autograd.grad(y, x)
# output :
(tensor(2.),)
当然,微分计算作为神经网络优化的核心,autograd
模块的功能远不仅于此,更多的微分运算,我们将在下一节继续讲解。