Run this notebook online: or Colab:
2.3. 线性代数¶
在你已经可以存储和操作数据后,让我们简要地回顾一下基本线性代数的部分内容。这些内容能够帮助你了解和实现本书中介绍的大多数模型。下面我们将介绍线性代数中的基本数学对象、算术和运算,并用数学符号和相应的代码实现来表示它们。
2.3.1. 标量¶
如果你从来没有学过线性代数或机器学习,那么你过去的数学经历可能是一次只想一个数字。如果你曾经报销过发票,或者在餐厅支付餐费,那么你已经知道如何做一些基本的事情,比如在数字间相加或相乘。例如,北京的温度为 \(52\) 华氏度(除了摄氏度外,另一种温度刻度)。严格来说,我们称仅包含一个数值的叫 标量 (scalar)。如果要将此华氏度值转换为更常用的摄氏度,则可以计算表达式 \(c = \frac{5}{9}(f - 32)\),并将 \(f\) 赋为 \(52\)。在此等式中,每一项(\(5\)、\(9\) 和 \(32\))都是标量值。符号 \(c\) 和 \(f\) 称为 变量(variables),它们表示未知的标量值。
在本书中,我们采用了数学表示法,其中标量变量由普通小写字母表示(例如,\(x\)、\(y\) 和 \(z\))。我们用 \(\mathbb{R}\) 表示所有(连续)实数 标量的空间。为了方便,我们之后将严格定义 空间(space)是什么,但现在只要记住,表达式 \(x \in \mathbb{R}\) 是表示\(x\)是一个实值标量的正式形式。符号 \(\in\) 称为 “属于”,它表示“是集合中的成员”。我们可以用 \(x, y \in \{0, 1\}\) 来表明 \(x\) 和 \(y\) 是值只能为 \(0\) 或 \(1\)的数字。
标量由只有一个元素的 NDArray 表示。在下面的代码中,我们实例化两个标量,并使用它们执行一些熟悉的算术运算,即加法,乘法,除法和指数。
%load ../utils/djl-imports
NDManager manager = NDManager.newBaseManager();
NDArray x = manager.create(3f);
NDArray y = manager.create(2f);
x.add(y)
ND: () gpu(0) float32
5.
x.mul(y)
ND: () gpu(0) float32
6.
x.div(y)
ND: () gpu(0) float32
1.5
x.pow(y)
ND: () gpu(0) float32
9.
2.3.2. 向量¶
你可以将向量视为标量值组成的列表。我们将这些标量值称为向量的 元素(elements)或分量(components)。当我们的向量表示数据集中的样本时,它们的值具有一定的现实意义。例如,如果我们正在训练一个模型来预测贷款违约风险,我们可能会将每个申请人与一个向量相关联,其分量与其收入、工作年限、过往违约次数和其他因素相对应。如果我们正在研究医院患者可能面临的心脏病发作风险,我们可能会用一个向量来表示每个患者,其分量为最近的生命体征、胆固醇水平、每天运动时间等。在数学表示法中,我们通常将向量记为粗体、小写的符号(例如,\(\mathbf{x}\)、\(\mathbf{y}\)和\(\mathbf{z})\))。
我们通过一维 NDArray
处理向量。一般来说, NDArray
可以具有任意长度,取决于机器的内存限制。
NDArray x = manager.arange(4f);
x
ND: (4) gpu(0) float32
[0., 1., 2., 3.]
我们可以使用下标来引用向量的任一元素。例如,我们可以通过 \(x_i\) 来引用第 \(i\) 个元素。注意,元素 \(x_i\) 是一个标量,所以我们在引用它时不会加粗。大量文献认为列向量是向量的默认方向,在本书中也是如此。在数学中,向量 \(\mathbf{x}\) 可以写为:
其中 \(x_1, \ldots, x_n\) 是向量的元素。在代码中,我们通过 NDArray 的索引来访问任一元素。
x.get(3)
ND: () gpu(0) float32
3.
2.3.2.1. 长度、维度和形状¶
让我们回顾一下 sec_ndarray
中的一些概念。向量只是一个数字数组。就像每个数组都有一个长度一样,每个向量也是如此。在数学表示法中,如果我们想说一个向量
\(\mathbf{x}\) 由 \(n\) 个实值标量组成,我们可以将其表示为
\(\mathbf{x} \in \mathbb{R}^n\)。向量的长度通常称为向量的
维度(dimension)。
我们可以调用 sizze()
函数来访问 NDArray 的长度:
x.size(0)
4
当用NDArray表示一个向量(只有一个轴)时,我们也可以通过 getShape()
函数访问向量的长度。形状(shape)是一个元组,列出了 NDArray
沿每个轴的长度(维数)。对于只有一个轴的NDArray,形状只有一个元素。
x.getShape()
(4)
请注意,维度(dimension)这个词在不同上下文时往往会有不同的含义,这经常会使人感到困惑。为了清楚起见,我们在此明确一下。向量或轴的维度被用来表示向量或轴的长度,即向量或轴的元素数量。然而, NDArray 的维度用来表示 NDArray 具有的轴数。在这个意义上, NDArray 的某个轴的维数就是这个轴的长度。
2.3.3. 矩阵¶
正如向量将标量从零阶推广到一阶,矩阵将向量从一阶推广到二阶。矩阵,我们通常用粗体、大写字母来表示(例如,\(\mathbf{X}\)、\(\mathbf{Y}\) 和 \(\mathbf{Z}\)),在代码中表示为具有两个轴的 NDArray 。
在数学表示法中,我们使用 \(\mathbf{A} \in \mathbb{R}^{m \times n}\) 来表示矩阵 \(\mathbf{A}\) ,其由\(m\) 行和 \(n\) 列的实值标量组成。直观地,我们可以将任意矩阵 \(\mathbf{A} \in \mathbb{R}^{m \times n}\) 视为一个表格,其中每个元素 \(a_{ij}\) 属于第 \(i\) 行第\(j\) 列:
对于任意\(\mathbf{A} \in \mathbb{R}^{m \times n}\),\(\mathbf{A}\)的形状是(\(m\), \(n\))或\(m \times n\)。当矩阵具有相同数量的行和列时,其形状将变为正方形;因此,它被称为 方矩阵(square matrix)。
当调用函数来实例化 NDArray 时,我们可以通过指定两个分量\(m\) 和 \(n\)来创建一个形状为\(m \times n\) 的矩阵。
NDArray A = manager.arange(20f).reshape(5,4);
A
ND: (5, 4) gpu(0) float32
[[ 0., 1., 2., 3.],
[ 4., 5., 6., 7.],
[ 8., 9., 10., 11.],
[12., 13., 14., 15.],
[16., 17., 18., 19.],
]
我们可以通过行索引(\(i\))和列索引(\(j\))来访问矩阵中的标量元素 \(a_{ij}\),例如 \([\mathbf{A}]_{ij}\)。如果没有给出矩阵 \(\mathbf{A}\) 的标量元素,如在 (2.3.2)那样,我们可以简单地使用矩阵 \(\mathbf{A}\) 的小写字母索引下标 \(a_{ij}\)来引用\([\mathbf{A}]_{ij}\)。为了表示起来简单,只有在必要时才会将逗号插入到单独的索引中,例如 \(a_{2, 3j}\) 和 \([\mathbf{A}]_{2i-1, 3}\)。
有时候,我们想翻转轴。当我们交换矩阵的行和列时,结果称为矩阵的 转置(transpose)。我们用\(\mathbf{a}^\top\)来表示矩阵的转置,如果\(\mathbf{B} = \mathbf{A}^\top\),则对于任意\(i\)和\(j\),都有\(b_{ij} = a_{ji}\)。因此,在 (2.3.2) 中的转置是一个形状为\(n \times m\)的矩阵:
现在我们在代码中访问矩阵的转置。
A.transpose()
ND: (4, 5) gpu(0) float32
[[ 0., 4., 8., 12., 16.],
[ 1., 5., 9., 13., 17.],
[ 2., 6., 10., 14., 18.],
[ 3., 7., 11., 15., 19.],
]
作为方矩阵的一种特殊类型,对称矩阵(symmetric matrix)
\(\mathbf{A}\)
等于其转置:\(\mathbf{A} = \mathbf{A}^\top\)。这里我们定义一个对称矩阵
B
:
NDArray B = manager.create(new float[][] {{1, 2, 3}, {2, 0, 4}, {3, 4, 5}});
B
ND: (3, 3) gpu(0) float32
[[1., 2., 3.],
[2., 0., 4.],
[3., 4., 5.],
]
现在我们将 B
与它的转置进行比较。
B.eq(B.transpose())
ND: (3, 3) gpu(0) boolean
[[ true, true, true],
[ true, true, true],
[ true, true, true],
]
矩阵是有用的数据结构:它们允许我们组织具有不同变化模式的数据。例如,我们矩阵中的行可能对应于不同的房屋(数据样本),而列可能对应于不同的属性。如果你曾经使用过电子表格软件或已阅读过
sec_pandas
,这应该听起来很熟悉。因此,尽管单个向量的默认方向是列向量,但在表示表格数据集的矩阵中,将每个数据样本作为矩阵中的行向量更为常见。我们将在后面的章节中讲到这点。这种约定将支持常见的深度学习实践。例如,沿着
NDArray
的最外轴,我们可以访问或遍历小批量的数据样本。如果不存在小批量,我们也可以只访问数据样本。
2.3.4. NDArray¶
就像向量是标量的推广,矩阵是向量的推广一样,我们可以构建具有更多轴的数据结构。NDArray(本小节中的 “NDArray” 指代数对象)为我们提供了描述具有任意数量轴的\(n\)维数组的通用方法。例如,向量是一阶 NDArray ,矩阵是二阶 NDArray 。 NDArray 用特殊字体的大写字母(例如,\(\mathsf{X}\)、\(\mathsf{Y}\) 和 \(\mathsf{Z}\))表示,它们的索引机制(例如 \(x_{ijk}\) 和 \([\mathsf{X}]_{1, 2i-1, 3}\))与矩阵类似。
当我们开始处理图像时, NDArray 将变得更加重要,图像以\(n\)维数组形式出现,其中3个轴对应于高度、宽度,以及一个通道(channel)轴,用于堆叠颜色通道(红色、绿色和蓝色)。现在,我们将跳过高阶 NDArray ,集中在基础知识上。
NDArray X = manager.arange(24f).reshape(2, 3, 4);
X
ND: (2, 3, 4) gpu(0) float32
[[[ 0., 1., 2., 3.],
[ 4., 5., 6., 7.],
[ 8., 9., 10., 11.],
],
[[12., 13., 14., 15.],
[16., 17., 18., 19.],
[20., 21., 22., 23.],
],
]
2.3.5. NDArray算法的基本性质¶
标量、向量、矩阵和任意数量轴的NDArray有一些很好的属性,通常会派上用场。例如,你可能已经从按元素操作的定义中注意到,任何按元素的一元运算都不会改变其操作数的形状。同样,给定具有相同形状的任意两个NDArray,任何按元素二元运算的结果都将是相同形状的 NDArray。例如,将两个相同形状的矩阵相加会在这两个矩阵上执行元素加法。
NDArray A = manager.arange(20f).reshape(5,4);
NDArray B = A.duplicate(); // 通过分配新内存,将A的一个副本分配给B
A
ND: (5, 4) gpu(0) float32
[[ 0., 1., 2., 3.],
[ 4., 5., 6., 7.],
[ 8., 9., 10., 11.],
[12., 13., 14., 15.],
[16., 17., 18., 19.],
]
A.add(B)
ND: (5, 4) gpu(0) float32
[[ 0., 2., 4., 6.],
[ 8., 10., 12., 14.],
[16., 18., 20., 22.],
[24., 26., 28., 30.],
[32., 34., 36., 38.],
]
具体而言,两个矩阵的按元素乘法称为 哈达玛积(Hadamard product)(数学符号 \(\odot\))。对于矩阵 \(\mathbf{B} \in \mathbb{R}^{m \times n}\),其中第 \(i\) 行和第 \(j\) 列的元素是 \(b_{ij}\)。矩阵\(\mathbf{A}\)(在 (2.3.2) 中定义)和 \(\mathbf{B}\)的哈达玛积为:
A.mul(B)
ND: (5, 4) gpu(0) float32
[[ 0., 1., 4., 9.],
[ 16., 25., 36., 49.],
[ 64., 81., 100., 121.],
[144., 169., 196., 225.],
[256., 289., 324., 361.],
]
将 NDArray 乘以或加上一个标量不会改变 NDArray
的形状,其中
NDArray
的每个元素都将与标量相加或相乘。
int a = 2;
NDArray X = manager.arange(24f).reshape(2, 3, 4);
X.add(a)
ND: (2, 3, 4) gpu(0) float32
[[[ 2., 3., 4., 5.],
[ 6., 7., 8., 9.],
[10., 11., 12., 13.],
],
[[14., 15., 16., 17.],
[18., 19., 20., 21.],
[22., 23., 24., 25.],
],
]
(X.mul(a)).getShape()
(2, 3, 4)
2.3.6. 降维¶
我们可以对任意
NDArray
进行的一个有用的操作是计算其元素的和。在数学表示法中,我们使用
\(\sum\)
符号表示求和。为了表示长度为\(d\)的向量中元素的总和,可以记为
\(\sum_{i=1}^d x_i\)。在代码中,我们可以调用计算求和的函数:
NDArray x = manager.arange(4f);
x
ND: (4) gpu(0) float32
[0., 1., 2., 3.]
x.sum()
ND: () gpu(0) float32
6.
我们可以表示任意形状 NDArray
的元素和。例如,矩阵 \(\mathbf{A}\)
中元素的和可以记为\(\sum_{i=1}^{m} \sum_{j=1}^{n} a_{ij}\)。
A.getShape()
(5, 4)
A.sum()
ND: () gpu(0) float32
190.
默认情况下,调用求和函数会沿所有的轴降低 NDArray
的维度,使它变为一个标量。 我们还可以指定 NDArray
沿哪一个轴来通过求和降低维度。以矩阵为例,为了通过求和所有行的元素来降维(轴0),我们可以在调用函数时指定axis=0
。
由于输入矩阵沿0轴降维以生成输出向量,因此输入的轴0的维数在输出形状中丢失。
NDArray ASumAxis0 = A.sum(new int[] {0});
ASumAxis0
ND: (4) gpu(0) float32
[40., 45., 50., 55.]
ASumAxis0.getShape()
(4)
指定 axis=1
将通过汇总所有列的元素降维(轴1)。因此,输入的轴1的维数在输出形状中消失。
NDArray ASumAxis1 = A.sum(new int[] {1});
ASumAxis1
ND: (5) gpu(0) float32
[ 6., 22., 38., 54., 70.]
ASumAxis1.getShape()
(5)
沿着行和列对矩阵求和,等价于对矩阵的所有元素进行求和。
A.sum(new int[] {0,1}) // Same as `A.sum()`
ND: () gpu(0) float32
190.
一个与求和相关的量是 平均值(mean或average)。我们通过将总和除以元素总数来计算平均值。在代码中,我们可以调用函数来计算任意形状 NDArray 的平均值。
A.mean()
ND: () gpu(0) float32
9.5
A.sum().div(A.size())
ND: () gpu(0) float32
9.5
同样,计算平均值的函数也可以沿指定轴降低 NDArray
的维度。
A.mean(new int[] {0})
ND: (4) gpu(0) float32
[ 8., 9., 10., 11.]
A.sum(new int[] {0}).div(A.getShape().get(0))
ND: (4) gpu(0) float32
[ 8., 9., 10., 11.]
2.3.6.1. 非降维求和¶
但是,有时在调用函数来计算总和或均值时保持轴数不变会很有用。
NDArray sumA = A.sum(new int[] {1}, true);
sumA
ND: (5, 1) gpu(0) float32
[[ 6.],
[22.],
[38.],
[54.],
[70.],
]
例如,由于 sumA
在对每行进行求和后仍保持两个轴,我们可以通过广播将
A
除以 sumA
。
A.div(sumA)
ND: (5, 4) gpu(0) float32
[[0. , 0.1667, 0.3333, 0.5 ],
[0.1818, 0.2273, 0.2727, 0.3182],
[0.2105, 0.2368, 0.2632, 0.2895],
[0.2222, 0.2407, 0.2593, 0.2778],
[0.2286, 0.2429, 0.2571, 0.2714],
]
如果我们想沿某个轴计算 A
元素的累积总和,比如
轴0
(按行计算),我们可以调用 cumsum()
函数。此函数不会沿任何轴降低输入 NDArray 的维度。
A.cumSum(0)
ND: (5, 4) gpu(0) float32
[[ 0., 1., 2., 3.],
[ 4., 6., 8., 10.],
[12., 15., 18., 21.],
[24., 28., 32., 36.],
[40., 45., 50., 55.],
]
2.3.7. 点积(Dot Product)¶
到目前为止,我们只执行了按元素操作、求和及平均值。如果这就是我们所能做的,那么线性代数可能就不需要单独一节了。 但是,最基本的操作之一是点积。给定两个向量 \(\mathbf{x}, \mathbf{y} \in \mathbb{R}^d\),它们的 点积(dot product) \(\mathbf{x}^\top \mathbf{y}\)(或 \(\langle \mathbf{x}, \mathbf{y} \rangle\))是相同位置的按元素乘积的和:\(\mathbf{x}^\top \mathbf{y} = \sum_{i=1}^{d} x_i y_i\)。
点积是相同位置的按元素乘积的和
NDArray y = manager.ones(new Shape(4));
x
ND: (4) gpu(0) float32
[0., 1., 2., 3.]
y
ND: (4) gpu(0) float32
[1., 1., 1., 1.]
x.dot(y)
ND: () gpu(0) float32
6.
注意,我们可以通过执行按元素乘法,然后进行求和来表示两个向量的点积:
x.mul(y).sum()
ND: () gpu(0) float32
6.
点积在很多场合都很有用。例如,给定一组由向量\(\mathbf{x} \in \mathbb{R}^d\) 表示的值,和一组由 \(\mathbf{w} \in \mathbb{R}^d\) 表示的权重。\(\mathbf{x}\) 中的值根据权重 \(\mathbf{w}\) 的加权和可以表示为点积 \(\mathbf{x}^\top \mathbf{w}\)。当权重为非负数且和为1(即 \(\left(\sum_{i=1}^{d} {w_i} = 1\right)\))时,点积表示 加权平均(weighted average)。将两个向量归一化得到单位长度后,点积表示它们夹角的余弦。我们将在本节的后面正式介绍长度(length)的概念。
2.3.8. 矩阵-向量积¶
现在我们知道如何计算点积,我们可以开始理解 矩阵-向量积(matrix-vector products)。回顾分别在 (2.3.2) 和 (2.3.1) 中定义并画出的矩阵 \(\mathbf{A} \in \mathbb{R}^{m \times n}\) 和向量 \(\mathbf{x} \in \mathbb{R}^n\)。让我们将矩阵\(\mathbf{A}\)用它的行向量表示
其中每个\(\mathbf{a}^\top_{i} \in \mathbb{R}^n\) 都是行向量,表示矩阵的第 \(i\) 行。矩阵向量积 \(\mathbf{A}\mathbf{x}\) 是一个长度为 \(m\) 的列向量,其第 \(i\) 个元素是点积 \(\mathbf{a}^\top_i \mathbf{x}\):
我们可以把一个矩阵 \(\mathbf{A}\in \mathbb{R}^{m \times n}\) 乘法看作是一个从 \(\mathbb{R}^{n}\) 到 \(\mathbb{R}^{m}\) 向量的转换。这些转换证明是非常有用的。例如,我们可以用方阵的乘法来表示旋转。 我们将在后续章节中讲到,我们也可以使用矩阵-向量积来描述在给定前一层的值时,求解神经网络每一层所需的复杂计算。
在代码中使用 NDArray 表示矩阵-向量积,我们使用与点积相同的 dot
函数。当我们为矩阵 A
和向量 x
调用
A.dot(x)
时,会执行矩阵-向量积。注意,A
的列维数(沿轴1的长度)必须与 x
的维数(其长度)相同。
A.getShape()
(5, 4)
x.getShape()
(4)
A.dot(x)
ND: (5) gpu(0) float32
[ 14., 38., 62., 86., 110.]
2.3.9. 矩阵-矩阵乘法¶
如果你已经掌握了点积和矩阵-向量积的知识,那么 矩阵-矩阵乘法(matrix-matrix multiplication) 应该很简单。
假设我们有两个矩阵 \(\mathbf{A} \in \mathbb{R}^{n \times k}\) 和 \(\mathbf{B} \in \mathbb{R}^{k \times m}\):
用行向量\(\mathbf{a}^\top_{i} \in \mathbb{R}^k\) 表示矩阵\(\mathbf{A}\)的第 \(i\) 行,并让列向量\(\mathbf{b}_{j} \in \mathbb{R}^k\) 作为矩阵\(\mathbf{B}\)的第 \(j\) 列。要生成矩阵积 \(\mathbf{C} = \mathbf{A}\mathbf{B}\),最简单的方法是考虑\(\mathbf{A}\)的行向量和\(\mathbf{B}\)的列向量:
当我们简单地将每个元素\(c_{ij}\)计算为点积\(\mathbf{a}^\top_i \mathbf{b}_j\):
我们可以将矩阵-矩阵乘法 \(\mathbf{AB}\) 看作是简单地执行
\(m\)次矩阵-向量积,并将结果拼接在一起,形成一个
\(n \times m\) 矩阵。在下面的代码中,我们在 A
和 B
上执行矩阵乘法。这里的A
是一个5行4列的矩阵,B
是一个4行3列的矩阵。相乘后,我们得到了一个5行3列的矩阵。
NDArray B = manager.ones(new Shape(4,3));
A.dot(B)
ND: (5, 3) gpu(0) float32
[[ 6., 6., 6.],
[22., 22., 22.],
[38., 38., 38.],
[54., 54., 54.],
[70., 70., 70.],
]
矩阵-矩阵乘法可以简单地称为矩阵乘法,不应与 哈达玛积 混淆。
2.3.10. 范数¶
线性代数中最有用的一些运算符是 范数(norms)。非正式地说,一个向量的范数告诉我们一个向量有多大。 这里考虑的 大小(size) 概念不涉及维度,而是分量的大小。
在线性代数中,向量范数是将向量映射到标量的函数 \(f\)。向量范数要满足一些属性。 给定任意向量 \(\mathbf{x}\),第一个性质说,如果我们按常数因子 \(\alpha\) 缩放向量的所有元素,其范数也会按相同常数因子的 绝对值 缩放:
第二个性质是我们熟悉的三角不等式:
第三个性质简单地说范数必须是非负的:
这是有道理的,因为在大多数情况下,任何东西的最小的大小是0。最后一个性质要求范数最小为0,当且仅当向量全由0组成。
你可能会注意到,范数听起来很像距离的度量。如果你还记得小学时的欧几里得距离(想想毕达哥拉斯定理),那么非负性的概念和三角不等式可能会给你一些启发。 事实上,欧几里得距离是一个范数:具体而言,它是 \(L_2\) 范数。假设\(n\)维向量 \(\mathbf{x}\) 中的元素是\(x_1, \ldots, x_n\),其 \(L_2\) 范数 是向量元素平方和的平方根:
(**
**)
其中,在 \(L_2\) 范数中常常省略下标 \(2\),也就是说,\(\|\mathbf{x}\|\) 等同于 \(\|\mathbf{x}\|_2\)。在代码中,我们可以按如下方式计算向量的 \(L_2\) 范数。
public NDArray l2Norm(NDArray w){
return ((w.pow(2)).sum()).sqrt();
}
NDArray u = manager.create(new float[] {3,-4});
l2Norm(u)
ND: () gpu(0) float32
5.
在深度学习中,我们更经常地使用 \(L_2\) 范数的平方。你还会经常遇到 \(L_1\) 范数,它表示为向量元素的绝对值之和:
(**
**)
与 \(L_2\) 范数相比,\(L_1\) 范数受异常值的影响较小。为了计算 \(L_1\) 范数,我们将绝对值函数和按元素求和组合起来。
u.abs().sum()
ND: () gpu(0) float32
7.
\(L_2\) 范数和 \(L_1\) 范数都是更一般的\(L_p\)范数的特例:
类似于向量的\(L_2\) 范数,矩阵 \(\mathbf{X} \in \mathbb{R}^{m \times n}\) 的 弗罗贝尼乌斯范数(Frobenius norm) 是矩阵元素平方和的平方根:
(**
**)
弗罗贝尼乌斯范数满足向量范数的所有性质。它就像是矩阵形向量的 \(L_2\) 范数。调用以下函数将计算矩阵的弗罗贝尼乌斯范数。
l2Norm(manager.ones(new Shape(4,9)))
ND: () gpu(0) float32
6.
2.3.10.1. 范数和目标¶
虽然我们不想走得太远,但我们可以对这些概念为什么有用有一些直觉。在深度学习中,我们经常试图解决优化问题: 最大化 分配给观测数据的概率; 最小化 预测和真实观测之间的距离。 用向量表示物品(如单词、产品或新闻文章),以便最小化相似项目之间的距离,最大化不同项目之间的距离。 通常,目标,或许是深度学习算法最重要的组成部分(除了数据),被表达为范数。
2.3.11. 关于线性代数的更多信息¶
仅用一节,我们就教会了你所需的,用以理解大量的现代深度学习的全部线性代数。 线性代数还有很多,其中很多数学对于机器学习非常有用。例如,矩阵可以分解为因子,这些分解可以显示真实世界数据集中的低维结构。机器学习的整个子领域都侧重于使用矩阵分解及其向高阶 NDArray 的泛化来发现数据集中的结构并解决预测问题。但这本书的重点是深度学习。我们相信,一旦你开始动手尝试并在真实数据集上应用了有效的机器学习模型,你会更倾向于学习更多数学。因此,虽然我们保留在后面介绍更多数学知识的权利,但我们这一节到此结束。
如果你渴望了解有关线性代数的更多信息,你可以参考 线性代数运算的在线附录 或其他优秀资源 [Strang, 1993][Kolter, 2008][Petersen et al., 2008]。
2.3.12. 小结¶
标量、向量、矩阵和 NDArray 是线性代数中的基本数学对象。
向量泛化自标量,矩阵泛化自向量。
标量、向量、矩阵和 NDArray 分别具有零、一、二和任意数量的轴。
一个 NDArray 可以通过
sum
和mean
沿指定的轴降低维度。两个矩阵的按元素乘法被称为他们的哈达玛积。它与矩阵乘法不同。
在深度学习中,我们经常使用范数,如 \(L_1\)范数、\(L_2\)范数和弗罗贝尼乌斯范数。
我们可以对标量、向量、矩阵和 NDArray 执行各种操作。
2.3.13. 练习¶
证明一个矩阵 \(\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)的 NDArray
X
。X.size()
的输出结果是什么?对于任意形状的 NDArray
X
,X.size()
是否总是对应于X
特定轴的长度?这个轴是什么?运行
A / A.sum(new int[] {1})
,看看会发生什么。你能分析原因吗?当你在曼哈顿的两点之间旅行时,你需要在坐标上走多远,也就是说,就大街和街道而言?你能斜着走吗?
考虑一个具有形状(2, 3, 4)的 NDArray ,在轴 0,1,2 上的求和输出是什么形状?
向
l2Norm()
函数提供 3 个或更多轴的 NDArray ,并观察其输出。对于任意形状的 NDArray 这个函数计算得到什么?