🏷️sec_linear-algebra
在你已经可以存储和操作数据后,让我们简要地回顾一下基本线性代数的部分内容。这些内容能够帮助你了解和实现本书中介绍的大多数模型。下面我们将介绍线性代数中的基本数学对象、算术和运算,并用数学符号和相应的代码实现来表示它们。
如果你从来没有学过线性代数或机器学习,那么你过去的数学经历可能是一次只想一个数字。如果你曾经报销过发票,或者在餐厅支付餐费,那么你已经知道如何做一些基本的事情,比如在数字间相加或相乘。例如,北京的温度为
在本书中,我们采用了数学表示法,其中标量变量由普通小写字母表示(例如,$x$、$y$ 和
(标量由只有一个元素的张量表示)。在下面的代码中,我们实例化两个标量,并使用它们执行一些熟悉的算术运算,即加法,乘法,除法和指数。
from mxnet import np, npx
npx.set_np()
x = np.array(3.0)
y = np.array(2.0)
x + y, x * y, x / y, x ** y
#@tab pytorch
import torch
x = torch.tensor([3.0])
y = torch.tensor([2.0])
x + y, x * y, x / y, x**y
#@tab tensorflow
import tensorflow as tf
x = tf.constant([3.0])
y = tf.constant([2.0])
x + y, x * y, x / y, x**y
[你可以将向量视为标量值组成的列表]。我们将这些标量值称为向量的 元素(elements)或分量(components)。当我们的向量表示数据集中的样本时,它们的值具有一定的现实意义。例如,如果我们正在训练一个模型来预测贷款违约风险,我们可能会将每个申请人与一个向量相关联,其分量与其收入、工作年限、过往违约次数和其他因素相对应。如果我们正在研究医院患者可能面临的心脏病发作风险,我们可能会用一个向量来表示每个患者,其分量为最近的生命体征、胆固醇水平、每天运动时间等。在数学表示法中,我们通常将向量记为粗体、小写的符号(例如,$\mathbf{x}$、$\mathbf{y}$和$\mathbf{z})$)。
我们通过一维张量处理向量。一般来说,张量可以具有任意长度,取决于机器的内存限制。
x = np.arange(4)
x
#@tab pytorch
x = torch.arange(4)
x
#@tab tensorflow
x = tf.range(4)
x
我们可以使用下标来引用向量的任一元素。例如,我们可以通过
$$\mathbf{x} =\begin{bmatrix}x_{1} \x_{2} \ \vdots \x_{n}\end{bmatrix},$$
:eqlabel:eq_vec_def
其中
x[3]
#@tab pytorch
x[3]
#@tab tensorflow
x[3]
让我们回顾一下 :numref:sec_ndarray
中的一些概念。向量只是一个数字数组。就像每个数组都有一个长度一样,每个向量也是如此。在数学表示法中,如果我们想说一个向量
与普通的 Python 数组一样,我们可以通过调用 Python 的内置 len()
函数来[访问张量的长度]。
len(x)
#@tab pytorch
len(x)
#@tab tensorflow
len(x)
当用张量表示一个向量(只有一个轴)时,我们也可以通过 .shape
属性访问向量的长度。形状(shape)是一个元组,列出了张量沿每个轴的长度(维数)。对于(只有一个轴的张量,形状只有一个元素。)
x.shape
#@tab pytorch
x.shape
#@tab tensorflow
x.shape
请注意,维度(dimension)这个词在不同上下文时往往会有不同的含义,这经常会使人感到困惑。为了清楚起见,我们在此明确一下。向量或轴的维度被用来表示向量或轴的长度,即向量或轴的元素数量。然而,张量的维度用来表示张量具有的轴数。在这个意义上,张量的某个轴的维数就是这个轴的长度。
正如向量将标量从零阶推广到一阶,矩阵将向量从一阶推广到二阶。矩阵,我们通常用粗体、大写字母来表示(例如,$\mathbf{X}$、$\mathbf{Y}$ 和
在数学表示法中,我们使用
$$\mathbf{A}=\begin{bmatrix} a_{11} & a_{12} & \cdots & a_{1n} \ a_{21} & a_{22} & \cdots & a_{2n} \ \vdots & \vdots & \ddots & \vdots \ a_{m1} & a_{m2} & \cdots & a_{mn} \ \end{bmatrix}.$$
:eqlabel:eq_matrix_def
对于任意$\mathbf{A} \in \mathbb{R}^{m \times n}$,$\mathbf{A}$的形状是(
当调用函数来实例化张量时,我们可以[通过指定两个分量$m$ 和
A = np.arange(20).reshape(5, 4)
A
#@tab pytorch
A = torch.arange(20).reshape(5, 4)
A
#@tab tensorflow
A = tf.reshape(tf.range(20), (5, 4))
A
我们可以通过行索引($i$)和列索引($j$)来访问矩阵中的标量元素 eq_matrix_def
那样,我们可以简单地使用矩阵 $\mathbf{A}$ 的小写字母索引下标 $a{ij}$来引用$[\mathbf{A}]{ij}$。为了表示起来简单,只有在必要时才会将逗号插入到单独的索引中,例如 $a{2, 3j}$ 和
有时候,我们想翻转轴。当我们交换矩阵的行和列时,结果称为矩阵的 转置(transpose)。我们用$\mathbf{a}^\top$来表示矩阵的转置,如果$\mathbf{B} = \mathbf{A}^\top$,则对于任意$i$和$j$,都有$b_{ij} = a_{ji}$。因此,在 :eqref:eq_matrix_def
中的转置是一个形状为$n \times m$的矩阵:
现在我们在代码中访问(矩阵的转置)。
A.T
#@tab pytorch
A.T
#@tab tensorflow
tf.transpose(A)
作为方矩阵的一种特殊类型,[对称矩阵(symmetric matrix) B
:
B = np.array([[1, 2, 3], [2, 0, 4], [3, 4, 5]])
B
#@tab pytorch
B = torch.tensor([[1, 2, 3], [2, 0, 4], [3, 4, 5]])
B
#@tab tensorflow
B = tf.constant([[1, 2, 3], [2, 0, 4], [3, 4, 5]])
B
现在我们将 B
与它的转置进行比较。
B == B.T
#@tab pytorch
B == B.T
#@tab tensorflow
B == tf.transpose(B)
矩阵是有用的数据结构:它们允许我们组织具有不同变化模式的数据。例如,我们矩阵中的行可能对应于不同的房屋(数据样本),而列可能对应于不同的属性。如果你曾经使用过电子表格软件或已阅读过 :numref:sec_pandas
,这应该听起来很熟悉。因此,尽管单个向量的默认方向是列向量,但在表示表格数据集的矩阵中,将每个数据样本作为矩阵中的行向量更为常见。我们将在后面的章节中讲到这点。这种约定将支持常见的深度学习实践。例如,沿着张量的最外轴,我们可以访问或遍历小批量的数据样本。如果不存在小批量,我们也可以只访问数据样本。
[就像向量是标量的推广,矩阵是向量的推广一样,我们可以构建具有更多轴的数据结构]。张量(本小节中的 “张量” 指代数对象)为我们提供了描述具有任意数量轴的$n$维数组的通用方法。例如,向量是一阶张量,矩阵是二阶张量。张量用特殊字体的大写字母(例如,$\mathsf{X}$、$\mathsf{Y}$ 和
当我们开始处理图像时,张量将变得更加重要,图像以$n$维数组形式出现,其中3个轴对应于高度、宽度,以及一个通道(channel)轴,用于堆叠颜色通道(红色、绿色和蓝色)。现在,我们将跳过高阶张量,集中在基础知识上。
X = np.arange(24).reshape(2, 3, 4)
X
#@tab pytorch
X = torch.arange(24).reshape(2, 3, 4)
X
#@tab tensorflow
X = tf.reshape(tf.range(24), (2, 3, 4))
X
标量、向量、矩阵和任意数量轴的张量(本小节中的 “张量” 指代数对象)有一些很好的属性,通常会派上用场。例如,你可能已经从按元素操作的定义中注意到,任何按元素的一元运算都不会改变其操作数的形状。同样,[给定具有相同形状的任意两个张量,任何按元素二元运算的结果都将是相同形状的张量]。例如,将两个相同形状的矩阵相加会在这两个矩阵上执行元素加法。
A = np.arange(20).reshape(5, 4)
B = A.copy() # 通过分配新内存,将A的一个副本分配给B
A, A + B
#@tab pytorch
A = torch.arange(20, dtype=torch.float32).reshape(5, 4)
B = A.clone() # 通过分配新内存,将A的一个副本分配给B
A, A + B
#@tab tensorflow
A = tf.reshape(tf.range(20, dtype=tf.float32), (5, 4))
B = A # 不能通过分配新内存将A克隆到B
A, A + B
具体而言,[两个矩阵的按元素乘法称为 哈达玛积(Hadamard product)(数学符号 eq_matrix_def
中定义)和
A * B
#@tab pytorch
A * B
#@tab tensorflow
A * B
将张量乘以或加上一个标量不会改变张量的形状,其中张量的每个元素都将与标量相加或相乘。
a = 2
X = np.arange(24).reshape(2, 3, 4)
a + X, (a * X).shape
#@tab pytorch
a = 2
X = torch.arange(24).reshape(2, 3, 4)
a + X, (a * X).shape
#@tab tensorflow
a = 2
X = tf.reshape(tf.range(24), (2, 3, 4))
a + X, (a * X).shape
🏷️subseq_lin-alg-reduction
我们可以对任意张量进行的一个有用的操作是[计算其元素的和]。在数学表示法中,我们使用
x = np.arange(4)
x, x.sum()
#@tab pytorch
x = torch.arange(4, dtype=torch.float32)
x, x.sum()
#@tab tensorflow
x = tf.range(4, dtype=tf.float32)
x, tf.reduce_sum(x)
我们可以(表示任意形状张量的元素和)。例如,矩阵
A.shape, A.sum()
#@tab pytorch
A.shape, A.sum()
#@tab tensorflow
A.shape, tf.reduce_sum(A)
默认情况下,调用求和函数会沿所有的轴降低张量的维度,使它变为一个标量。
我们还可以[指定张量沿哪一个轴来通过求和降低维度]。以矩阵为例,为了通过求和所有行的元素来降维(轴0),我们可以在调用函数时指定axis=0
。
由于输入矩阵沿0轴降维以生成输出向量,因此输入的轴0的维数在输出形状中丢失。
A_sum_axis0 = A.sum(axis=0)
A_sum_axis0, A_sum_axis0.shape
#@tab pytorch
A_sum_axis0 = A.sum(axis=0)
A_sum_axis0, A_sum_axis0.shape
#@tab tensorflow
A_sum_axis0 = tf.reduce_sum(A, axis=0)
A_sum_axis0, A_sum_axis0.shape
指定 axis=1
将通过汇总所有列的元素降维(轴1)。因此,输入的轴1的维数在输出形状中消失。
A_sum_axis1 = A.sum(axis=1)
A_sum_axis1, A_sum_axis1.shape
#@tab pytorch
A_sum_axis1 = A.sum(axis=1)
A_sum_axis1, A_sum_axis1.shape
#@tab tensorflow
A_sum_axis1 = tf.reduce_sum(A, axis=1)
A_sum_axis1, A_sum_axis1.shape
沿着行和列对矩阵求和,等价于对矩阵的所有元素进行求和。
A.sum(axis=[0, 1]) # Same as `A.sum()`
#@tab pytorch
A.sum(axis=[0, 1]) # Same as `A.sum()`
#@tab tensorflow
tf.reduce_sum(A, axis=[0, 1]) # Same as `tf.reduce_sum(A)`
[一个与求和相关的量是 平均值(mean或average)]。我们通过将总和除以元素总数来计算平均值。在代码中,我们可以调用函数来计算任意形状张量的平均值。
A.mean(), A.sum() / A.size
#@tab pytorch
A.mean(), A.sum() / A.numel()
#@tab tensorflow
tf.reduce_mean(A), tf.reduce_sum(A) / tf.size(A).numpy()
同样,计算平均值的函数也可以沿指定轴降低张量的维度。
A.mean(axis=0), A.sum(axis=0) / A.shape[0]
#@tab pytorch
A.mean(axis=0), A.sum(axis=0) / A.shape[0]
#@tab tensorflow
tf.reduce_mean(A, axis=0), tf.reduce_sum(A, axis=0) / A.shape[0]
🏷️subseq_lin-alg-non-reduction
但是,有时在调用函数来[计算总和或均值时保持轴数不变]会很有用。
sum_A = A.sum(axis=1, keepdims=True)
sum_A
#@tab pytorch
sum_A = A.sum(axis=1, keepdims=True)
sum_A
#@tab tensorflow
sum_A = tf.reduce_sum(A, axis=1, keepdims=True)
sum_A
例如,由于 sum_A
在对每行进行求和后仍保持两个轴,我们可以(通过广播将 A
除以 sum_A
) 。
A / sum_A
#@tab pytorch
A / sum_A
#@tab tensorflow
A / sum_A
如果我们想沿[某个轴计算 A
元素的累积总和],比如 axis=0
(按行计算),我们可以调用 cumsum
函数。此函数不会沿任何轴降低输入张量的维度。
A.cumsum(axis=0)
#@tab pytorch
A.cumsum(axis=0)
#@tab tensorflow
tf.cumsum(A, axis=0)
到目前为止,我们只执行了按元素操作、求和及平均值。如果这就是我们所能做的,那么线性代数可能就不需要单独一节了。
但是,最基本的操作之一是点积。给定两个向量
[点积是相同位置的按元素乘积的和]
y = np.ones(4)
x, y, np.dot(x, y)
#@tab pytorch
y = torch.ones(4, dtype = torch.float32)
x, y, torch.dot(x, y)
#@tab tensorflow
y = tf.ones(4, dtype=tf.float32)
x, y, tf.tensordot(x, y, axes=1)
注意,(我们可以通过执行按元素乘法,然后进行求和来表示两个向量的点积):
np.sum(x * y)
#@tab pytorch
torch.sum(x * y)
#@tab tensorflow
tf.reduce_sum(x * y)
点积在很多场合都很有用。例如,给定一组由向量$\mathbf{x} \in \mathbb{R}^d$ 表示的值,和一组由
现在我们知道如何计算点积,我们可以开始理解 矩阵-向量积(matrix-vector products)。回顾分别在 :eqref:eq_matrix_def
和 :eqref:eq_vec_def
中定义并画出的矩阵
其中每个$\mathbf{a}^\top_{i} \in \mathbb{R}^n$ 都是行向量,表示矩阵的第
我们可以把一个矩阵
在代码中使用张量表示矩阵-向量积,我们使用与点积相同的 dot
函数。当我们为矩阵 A
和向量 x
调用 np.dot(A, x)
时,会执行矩阵-向量积。注意,A
的列维数(沿轴1的长度)必须与 x
的维数(其长度)相同。
A.shape, x.shape, np.dot(A, x)
#@tab pytorch
A.shape, x.shape, torch.mv(A, x)
#@tab tensorflow
A.shape, x.shape, tf.linalg.matvec(A, x)
如果你已经掌握了点积和矩阵-向量积的知识,那么 矩阵-矩阵乘法(matrix-matrix multiplication) 应该很简单。
假设我们有两个矩阵
用行向量$\mathbf{a}^\top_{i} \in \mathbb{R}^k$ 表示矩阵$\mathbf{A}$的第
$$\mathbf{A}= \begin{bmatrix} \mathbf{a}^\top_{1} \ \mathbf{a}^\top_{2} \ \vdots \ \mathbf{a}^\top_n \ \end{bmatrix}, \quad \mathbf{B}=\begin{bmatrix} \mathbf{b}{1} & \mathbf{b}{2} & \cdots & \mathbf{b}_{m} \ \end{bmatrix}. $$
当我们简单地将每个元素$c_{ij}$计算为点积$\mathbf{a}^\top_i \mathbf{b}_j$:
$$\mathbf{C} = \mathbf{AB} = \begin{bmatrix} \mathbf{a}^\top_{1} \ \mathbf{a}^\top_{2} \ \vdots \ \mathbf{a}^\top_n \ \end{bmatrix} \begin{bmatrix} \mathbf{b}{1} & \mathbf{b}{2} & \cdots & \mathbf{b}{m} \ \end{bmatrix} = \begin{bmatrix} \mathbf{a}^\top{1} \mathbf{b}1 & \mathbf{a}^\top{1}\mathbf{b}2& \cdots & \mathbf{a}^\top{1} \mathbf{b}m \ \mathbf{a}^\top{2}\mathbf{b}1 & \mathbf{a}^\top{2} \mathbf{b}2 & \cdots & \mathbf{a}^\top{2} \mathbf{b}m \ \vdots & \vdots & \ddots &\vdots\ \mathbf{a}^\top{n} \mathbf{b}1 & \mathbf{a}^\top{n}\mathbf{b}2& \cdots& \mathbf{a}^\top{n} \mathbf{b}_m \end{bmatrix}. $$
[我们可以将矩阵-矩阵乘法 A
和 B
上执行矩阵乘法。这里的A
是一个5行4列的矩阵,B
是一个4行3列的矩阵。相乘后,我们得到了一个5行3列的矩阵。
B = np.ones(shape=(4, 3))
np.dot(A, B)
#@tab pytorch
B = torch.ones(4, 3)
torch.mm(A, B)
#@tab tensorflow
B = tf.ones((4, 3), tf.float32)
tf.matmul(A, B)
矩阵-矩阵乘法可以简单地称为 矩阵乘法,不应与 哈达玛积 混淆。
🏷️subsec_lin-algebra-norms
线性代数中最有用的一些运算符是 范数(norms)。非正式地说,一个向量的范数告诉我们一个向量有多大。 这里考虑的 大小(size) 概念不涉及维度,而是分量的大小。
在线性代数中,向量范数是将向量映射到标量的函数
第二个性质是我们熟悉的三角不等式:
第三个性质简单地说范数必须是非负的:
这是有道理的,因为在大多数情况下,任何东西的最小的大小是0。最后一个性质要求范数最小为0,当且仅当向量全由0组成。
你可能会注意到,范数听起来很像距离的度量。如果你还记得小学时的欧几里得距离(想想毕达哥拉斯定理),那么非负性的概念和三角不等式可能会给你一些启发。
事实上,欧几里得距离是一个范数:具体而言,它是
($$|\mathbf{x}|2 = \sqrt{\sum{i=1}^n x_i^2},$$)
其中,在
u = np.array([3, -4])
np.linalg.norm(u)
#@tab pytorch
u = torch.tensor([3.0, -4.0])
torch.norm(u)
#@tab tensorflow
u = tf.constant([3.0, -4.0])
tf.norm(u)
在深度学习中,我们更经常地使用
($$|\mathbf{x}|1 = \sum{i=1}^n \left|x_i \right|.$$)
与
np.abs(u).sum()
#@tab pytorch
torch.abs(u).sum()
#@tab tensorflow
tf.reduce_sum(tf.abs(u))
$$|\mathbf{x}|p = \left(\sum{i=1}^n \left|x_i \right|^p \right)^{1/p}.$$
类似于向量的$L_2$ 范数,[矩阵]
($$|\mathbf{X}|F = \sqrt{\sum{i=1}^m \sum_{j=1}^n x_{ij}^2}.$$)
弗罗贝尼乌斯范数满足向量范数的所有性质。它就像是矩阵形向量的
np.linalg.norm(np.ones((4, 9)))
#@tab pytorch
torch.norm(torch.ones((4, 9)))
#@tab tensorflow
tf.norm(tf.ones((4, 9)))
🏷️subsec_norms_and_objectives
虽然我们不想走得太远,但我们可以对这些概念为什么有用有一些直觉。在深度学习中,我们经常试图解决优化问题: 最大化 分配给观测数据的概率; 最小化 预测和真实观测之间的距离。 用向量表示物品(如单词、产品或新闻文章),以便最小化相似项目之间的距离,最大化不同项目之间的距离。 通常,目标,或许是深度学习算法最重要的组成部分(除了数据),被表达为范数。
仅用一节,我们就教会了你所需的,用以理解大量的现代深度学习的全部线性代数。 线性代数还有很多,其中很多数学对于机器学习非常有用。例如,矩阵可以分解为因子,这些分解可以显示真实世界数据集中的低维结构。机器学习的整个子领域都侧重于使用矩阵分解及其向高阶张量的泛化来发现数据集中的结构并解决预测问题。但这本书的重点是深度学习。我们相信,一旦你开始动手尝试并在真实数据集上应用了有效的机器学习模型,你会更倾向于学习更多数学。因此,虽然我们保留在后面介绍更多数学知识的权利,但我们这一节到此结束。
如果你渴望了解有关线性代数的更多信息,你可以参考 线性代数运算的在线附录 或其他优秀资源 :cite:Strang.1993,Kolter.2008,Petersen.Pedersen.ea.2008
。
- 标量、向量、矩阵和张量是线性代数中的基本数学对象。
- 向量泛化自标量,矩阵泛化自向量。
- 标量、向量、矩阵和张量分别具有零、一、二和任意数量的轴。
- 一个张量可以通过
sum
和mean
沿指定的轴降低维度。 - 两个矩阵的按元素乘法被称为他们的哈达玛积。它与矩阵乘法不同。
- 在深度学习中,我们经常使用范数,如
$L_1$ 范数、$L_2$范数和弗罗贝尼乌斯范数。 - 我们可以对标量、向量、矩阵和张量执行各种操作。
- 证明一个矩阵
$\mathbf{A}$ 的转置的转置是$\mathbf{A}$ :$(\mathbf{A}^\top)^\top = \mathbf{A}$。 - 给出两个矩阵
$\mathbf{A}$ 和$\mathbf{B}$ , 显示转置的和等于和的转置:$\mathbf{A}^\top + \mathbf{B}^\top = (\mathbf{A} + \mathbf{B})^\top$. - 给定任意方矩阵$\mathbf{A}$,
$\mathbf{A} + \mathbf{A}^\top$ 总是对称的吗?为什么? - 我们在本节中定义了形状(2, 3, 4)的张量
X
。len(X)
的输出结果是什么? - 对于任意形状的张量
X
,len(X)
是否总是对应于X
特定轴的长度?这个轴是什么? - 运行
A / A.sum(axis=1)
,看看会发生什么。你能分析原因吗? - 当你在曼哈顿的两点之间旅行时,你需要在坐标上走多远,也就是说,就大街和街道而言?你能斜着走吗?
- 考虑一个具有形状(2, 3, 4)的张量,在轴 0,1,2 上的求和输出是什么形状?
- 向
linalg.norm
函数提供 3 个或更多轴的张量,并观察其输出。对于任意形状的张量这个函数计算得到什么?
:begin_tab:mxnet
Discussions
:end_tab:
:begin_tab:pytorch
Discussions
:end_tab:
:begin_tab:tensorflow
Discussions
:end_tab: