二、矩阵方程与向量求导方法
在铺垫了基本矩阵和线性代数相关知识后,接下来,我们尝试将 Lesson 1 中的方程组表示形式转化为矩阵表示形式,并借助矩阵方法进行相关方程的求解。
在 Lesson 1 中,我们已经简单讨论了最小二乘法这一优化算法的基本思路,最小二乘法一个最基础的优化算法。
因此,本节开始,我们先从矩阵方程入手,先进行矩阵运算的相关方法的回顾、以及进行矩阵求导方法的讲解,讨论最小二乘法的基本原理。
1. 方程组求解与矩阵方程求解
- 在 Lesson 1 中,我们曾经利用损失函数的偏导函数方程组进行简单线性回归模型参数的求解:
尽管方程组求解有很多种方法,类似 Lesson 1 中所采用的,先通过方程变量相消法反解出一个变量(w=1),再将其带入其中任意一个式子求解出另一个变量(b=1),确实能够顺利求出方程组的解。
但在如果想借助编程工具求解方程组,则需要将原先的方程组求解问题转化为矩阵方程的求解问题。例如:上述求解过程方程组为:
- 至此我们就将方程组转化为了矩阵方程,并且,借助矩阵运算,我们可以直接在矩阵方程中对参数向量 X 进行求解。
- 首先我们利用 NumPy 基础知识,通过创建二维张量去表示上述矩阵方程中的 A 和 B。
A = np.array([[20, 8], [8, 4]]) A #array([[20, 8], # [ 8, 4]]) B = np.array([[28, 12]]).T B #array([[28], # [12]])
- 此时 B 也是二维张量,可以使用矩阵乘法。
B.ndim #2
- 然后通过行列式计算结果,简单验证 A 是否满秩。
np.linalg.matrix_rank(A) #2
- 当然,也可以通过观察 A 的行列式计算结果是否为 0,来判断A是否满秩。
np.linalg.det(A) #15.999999999999991
- 对于满秩矩阵,我们可以求其逆矩阵。
np.linalg.inv(A) #array([[ 0.25, -0.5 ], # [-0.5 , 1.25]])
然后在矩阵方程左右两端同时左乘其逆矩阵,即可解出 X 的取值。
np.matmul(np.linalg.inv(A), B) #array([[1.], # [1.]]) # 也可以使用dot方法,对于二维数组,dot就是执行矩阵乘法 np.linalg.inv(A).dot(B) #array([[1.], # [1.]]
- 当然,此外,NumPy 中还提供了一种求解矩阵方程的函数,类似于上述 A∗XT=B 的矩阵方程,我们还可以通过下式进行求解。
np.linalg.solve(A, B) #array([[1.], # [1.]])
2. 向量求导运算
2.1 向量求导基本方法
其中, x 为向量变元。
至此,我们就完成了向量求导的基本过程。核心点在于,我们是依据向量变元中的变量排列顺序,依次填写了对应变量的偏导函数计算结果。
不过,更进一步的来看,既然方程组需要改写成向量/矩阵形式,那么原始函数方程其实也同样需要改写成向量/矩阵形式。因此,原方程我们可以改写成。
- 其中 x 是向量变元, A 是列向量。
- 很多时候我们并不区分所谓向量方程和矩阵方程,一般所有自变量为向量或矩阵的方程,我们会统一称其为矩阵方程。包含向量或者矩阵的表达式,我们也会统一称其为矩阵表达式。
- 向量求导的定义法。
通过求得函数的梯度向量求解向量导数的方法,也被称为定义法求解。
值得注意的是,多元函数是一定能够求得梯度向量的,但梯度向量或者说向量求导结果,能否由一些已经定义的向量解决表示,如 A AA 就是 f ( x ) f(x)f(x) 的向量求导结果,则不一定。
2.2 常见向量求导公式
在前期学习中,数学理论推导涉及到的求导以向量变元求导居多,因此,除了掌握基本的向量求导方法以外,我们还需要推导几个常用的向量求导公式,在这些公式中,向量求导结果都能通过一些已经定义的向量简洁表示。
至此,我们完成相关向量求导常用公式的证明。不过从上面的证明不难看出,使用定义法进行公式证明往往会非常繁琐(尽管流程相对清晰)。
此外,对于矩阵来说,也有类似的求导方法。即如果变量以矩阵形式出现,则针对该矩阵的方程求导之际上也就是依照矩阵基本结构,在每个位置上对对应的变量分量求偏导函数。只不过相比向量,矩阵多了一个维度、结构更加复杂,因此求解过程也更加复杂。
最后,还需要简单进行一个概念辨析,那就是关于矩阵函数和矩阵方程二者概念的区别。
矩阵方程:指变量为矩阵的方程;
矩阵函数:同函数矩阵,指自变量和因变量都是n阶矩阵的函数,也可以简单理解成由函数构成的矩阵,并且每个函数的变量都是矩阵。
三、最小二乘法的推导过程及使用方法
- 有了上述内容铺垫之后,接下来,我们从数学角度讨论最小二乘法的基本理论,并见尝试简单实现最小二乘法求解损失函数的一般过程。
1. 模型及方程组的矩阵形式改写
首先,我们尝试对模型进行矩阵形式改写。
模型改写成矩阵表达式。
假设多元线性方程有如下形式
- 在机器学习领域,我们将线性回归自变量系数命名为 w,其实是 weight 的简写,意为自变量的权重。
- 将带入数据后的方程组改写为矩阵方程。
2. 构造损失函数
3. 最小二乘法求解损失函数的一般过程
4. 最小二乘法的简单实现
- 接下来,我们回到最初的例子,简单尝试利用上述推导公式求解简单线性回归参数。原始数据如下。
Whole weight | Rings |
1 | 2 |
3 | 4 |
- 代码实现过程:
X = np.array([[1, 1], [3, 1]]) X #array([[1, 1], # [3, 1]]) y = np.array([2, 4]).reshape(2, 1) y #array([[2], # [4]]) np.linalg.inv(X.T.dot(X)).dot(X.T).dot(y) #array([[1.], # [1.]])
即解得 w = 1 , b = 1 w=1,b=1w=1,b=1,即模型方程为 y = x + 1 y=x+1y=x+1。至此,我们即完成了最小二乘法的推导以及简单实现。
最小二乘法计算函数。
当然,除了借助 NumPy 中矩阵运算的函数进行最小二乘法计算以外,NumPy中也有独立的用于计算最小二乘法的函数:np.linalg.lstsq。
通过该方法,我们能够在直接输入特征矩阵和标签数组的情况下进行最小二乘法的计算,并在得出最小二乘法计算结果的同时,也得到一系列相应的统计指标结果。
np.linalg.lstsq(X, y, rcond=-1) #(array([[1.], # [1.]]), # array([], dtype=float64), # 2, # array([3.41421356, 0.58578644]))
- 其中,返回的元组中第一个元素是最小二乘法计算结果,第二个元素是 SSE 的计算结果,第三个元素是矩阵 X 的秩,最后一个结果是矩阵 X 的奇异值。