之前我们学习了数据的预处理方法——删除和插值。而向量矩阵这些知识我相信朋友们在线性代数的学习中都学习过了,如果你有些遗忘了,那么让我们简单的回顾一下线性代数部分的一些知识。
这一节内容较多,希望大家耐心过一遍,为后面的学习做准备。
线性代数可以说是机器学习中最重要的数学基础之一了,我认为学好这部分知识非常重要,如果有的朋友对这部分还不是很熟悉,可以去回顾一下教材或是听听网课。下面我们将介绍线性代数中的基本数学对象、算术和运算,并用数学符号和相应的代码实现来表示它们。
严格来说,我们称仅包含一个数值的叫 标量 (scalar)。如果要将此华氏度值转换为更常用的摄氏度,则可以计算表达式 c = 5 9 ( f − 32 ) { c=\frac{5}{9}(f−32)} c=95(f−32) ,并将 f f f 赋为 52 。在此等式中,每一项( 5 、 9 和 32 5 、 9 和 32 5、9和32 )都是标量值。符号 c 和 f c 和 f c和f 称为 变量(variables),它们表示未知的标量值。
我们采用小写字母表示标量变量,用 R \mathbb{R} R 表示所有(连续)实数标量的空间。标量由只有一个元素的张量表示,接下来我们实例化两个标量,并使用它们执行一些熟悉的算术运算,即加法,乘法,除法和指数:
import torch
x = torch.tensor([3.0])
y = torch.tensor([2.0])
x + y, x * y, x / y, x**y
(tensor([5.]), tensor([6.]), tensor([1.5000]), tensor([9.]))
其实最直观的理解,可以把向量看作由标量值组成的列表,我们将这些标量成为 元素(elements)或是分量(components)。向量时常作为机器学习中的输入和输出。在数学表示中,我们经常将向量记为粗体、小写的符号。
x = torch.arange(4)
x
tensor([0, 1, 2, 3])
x[3]
tensor(3)
len(x)
4
x.shape
torch.Size([4])
向量是将标量从零阶推广到一阶,矩阵是将向量从一阶推广到二阶。而矩阵我们通常用粗体、大写字母表示,代码中是两个轴的张量。
在数学表示法中,我们使用 A ∈ R m × n \mathbf{A}\in{\mathbb{R^{m×n}}} A∈Rm×n 来表示矩阵 A \mathbf{A} A,其由 m m m 行和 n n n 列的实值标量组成。直观地,我们可以将任意矩阵 A ∈ R m × n \mathbf{A}\in{\mathbb{R^{m×n}}} A∈Rm×n视为一个表格,其中每个元素 a i j a_{ij} aij 属于第 i i i 行第 j j j 列,它的形状是 ( m , n ) (m,n) (m,n)或 m × n m \times n m×n:
A = [ a 11 a 12 ⋯ a 1 n a 21 a 22 ⋯ a 2 n ⋮ ⋮ ⋱ ⋮ a m 1 a m 2 ⋯ a m n ] \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} A=⎣⎢⎢⎢⎡a11a21⋮am1a12a22⋮am2⋯⋯⋱⋯a1na2n⋮amn⎦⎥⎥⎥⎤
A = torch.arange(20).reshape(5, 4)
A
tensor([[ 0, 1, 2, 3],
[ 4, 5, 6, 7],
[ 8, 9, 10, 11],
[12, 13, 14, 15],
[16, 17, 18, 19]])
A.T
tensor([[ 0, 4, 8, 12, 16],
[ 1, 5, 9, 13, 17],
[ 2, 6, 10, 14, 18],
[ 3, 7, 11, 15, 19]])
B = torch.tensor([[1, 2, 3], [2, 0, 4], [3, 4, 5]])
B, B == B.T
(tensor([[1, 2, 3],
[2, 0, 4],
[3, 4, 5]]),
tensor([[True, True, True],
[True, True, True],
[True, True, True]]))
我们可以构建更多轴的数据结构,张量为我们提供了描述具有任意数量轴的 n n n维数组的通用方法。张量通常用特殊的大写字母表示,索引机制与矩阵类似。
张量对于我们处理图像非常重要,图像以 n 维数组形式出现,其中3个轴对应于高度、宽度,以及一个通道(channel)轴,用于堆叠颜色通道(红色、绿色和蓝色),也即RGB三通道。我们随意构建一个张量:
X = torch.arange(24).reshape(2, 3, 4)
X
array([[[ 0., 1., 2., 3.],
[ 4., 5., 6., 7.],
[ 8., 9., 10., 11.]],
[[12., 13., 14., 15.],
[16., 17., 18., 19.],
[20., 21., 22., 23.]]])
A = torch.arange(20, dtype=torch.float32).reshape(5, 4)
B = A.clone() # 通过分配新内存,将A的一个副本分配给B
A, A + B
(array([[ 0., 1., 2., 3.],
[ 4., 5., 6., 7.],
[ 8., 9., 10., 11.],
[12., 13., 14., 15.],
[16., 17., 18., 19.]]),
array([[ 0., 2., 4., 6.],
[ 8., 10., 12., 14.],
[16., 18., 20., 22.],
[24., 26., 28., 30.],
[32., 34., 36., 38.]]))
A * B
tensor([[ 0., 1., 4., 9.],
[ 16., 25., 36., 49.],
[ 64., 81., 100., 121.],
[144., 169., 196., 225.],
[256., 289., 324., 361.]])
a = 2
X = torch.arange(24).reshape(2, 3, 4)
a + X, (a * X).shape
(tensor([[[ 2, 3, 4, 5],
[ 6, 7, 8, 9],
[10, 11, 12, 13]],
[[14, 15, 16, 17],
[18, 19, 20, 21],
[22, 23, 24, 25]]]),
torch.Size([2, 3, 4]))
x = torch.arange(4, dtype=torch.float32)
x, x.sum()
(tensor([0., 1., 2., 3.]), tensor(6.))
A.shape, A.sum()
(torch.Size([5, 4]), tensor(190.))
A_sum_axis0 = A.sum(axis=0)
A_sum_axis0, A_sum_axis0.shape
(tensor([40., 45., 50., 55.]), torch.Size([4]))
A_sum_axis1 = A.sum(axis=1)
A_sum_axis1, A_sum_axis1.shape
(tensor([ 6., 22., 38., 54., 70.]), torch.Size([5]))
A.sum(axis=[0, 1])
tensor(190.)
A.mean(), A.sum() / A.numel()
(tensor(9.5000), tensor(9.5000))
A.mean(axis=0), A.sum(axis=0) / A.shape[0]
(tensor([ 8., 9., 10., 11.]), tensor([ 8., 9., 10., 11.]))
keepdims
参数保持维度:sum_A = A.sum(axis=1, keepdims=True)
sum_A
tensor([[ 6.],
[22.],
[38.],
[54.],
[70.]])
A / sum_A
tensor([[0.0000, 0.1667, 0.3333, 0.5000],
[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, A.cumsum(axis=0)
(tensor([[ 0., 1., 2., 3.],
[ 4., 5., 6., 7.],
[ 8., 9., 10., 11.],
[12., 13., 14., 15.],
[16., 17., 18., 19.]]),
tensor([[ 0., 1., 2., 3.],
[ 4., 6., 8., 10.],
[12., 15., 18., 21.],
[24., 28., 32., 36.],
[40., 45., 50., 55.]]))
现在我们来介绍最基本的操作之一—— 点积(Dot Product)。给定两个向量 x , y ∈ R d \mathbf{x},\mathbf{y}\in{\mathbb{R^{d}}} x,y∈Rd,他们的点积 x ⊤ y \mathbf{x}^\top\mathbf{y} x⊤y 是相同位置的按元素乘积的和:
x ⊤ y = ∑ i = 1 d x i y i \mathbf{x}^\top\mathbf{y}=\sum_{i=1}^{d}x_{i}y_{i} x⊤y=i=1∑dxiyi
y = torch.ones(4, dtype=torch.float32)
x, y, torch.dot(x, y)
(tensor([0., 1., 2., 3.]), tensor([1., 1., 1., 1.]), tensor(6.))
或者:
torch.sum(x * y)
tensor(6.)
了解了点积,我们再来看看矩阵-向量积。我们定义 A ∈ R m × n \mathbf{A}\in\mathbb{R}^{m\times n} A∈Rm×n 和向量 x ∈ R n \mathbf{x}\in\mathbb{R}^{n} x∈Rn。矩阵 A \mathbf{A} A 采用行向量表示:
A = [ a 1 ⊤ a 2 ⊤ ⋮ a m ⊤ ] \mathbf{A}= \begin{bmatrix} {\mathbf{a}_{1}^\top}\\ {\mathbf{a}_{2}^\top}\\ {\vdots}\\ {\mathbf{a}_{m}^\top}\\ \end{bmatrix} A=⎣⎢⎢⎢⎡a1⊤a2⊤⋮am⊤⎦⎥⎥⎥⎤
其中每个 a i ⊤ ∈ R n \mathbf{a}_{i}^\top\in\mathbb{R}^{n} ai⊤∈Rn 都是行向量,表示矩阵的第 i i i 行。而对于矩阵向量积 A x \mathbf{Ax} Ax 的表示为:
A x = [ a 1 ⊤ a 2 ⊤ ⋮ a m ⊤ ] x = [ a 1 ⊤ x a 2 ⊤ x ⋮ a m ⊤ x ] \mathbf{Ax}= \begin{bmatrix} {\mathbf{a}_{1}^\top}\\ {\mathbf{a}_{2}^\top}\\ {\vdots}\\ {\mathbf{a}_{m}^\top}\\ \end{bmatrix}\mathbf{x}= \begin{bmatrix} {\mathbf{a}_{1}^\top\mathbf{x}}\\ {\mathbf{a}_{2}^\top\mathbf{x}}\\ {\vdots}\\ {\mathbf{a}_{m}^\top\mathbf{x}}\\ \end{bmatrix} Ax=⎣⎢⎢⎢⎡a1⊤a2⊤⋮am⊤⎦⎥⎥⎥⎤x=⎣⎢⎢⎢⎡a1⊤xa2⊤x⋮am⊤x⎦⎥⎥⎥⎤
在神经网络的求解中,我们经常会使用到矩阵向量积进行每一层的复杂计算。代码中我们使用张量表示,使用 .mv 函数。值得注意的是,此时矩阵 A \mathbf{A} A 的列数必须与 x 的长度相同。
A.shape, x.shape, torch.mv(A, x)
(torch.Size([5, 4]), torch.Size([4]), tensor([ 14., 38., 62., 86., 110.]))
有了前面的了解,对于矩阵-矩乘法的理解就更加不成问题。我们先定义两个矩阵 A ∈ R m × k \mathbf{A}\in\mathbb{R}^{m\times k} A∈Rm×k 和 B ∈ R k × n \mathbf{B}\in\mathbb{R}^{k\times n} B∈Rk×n:
A = [ a 11 a 12 ⋯ a 1 k a 21 a 22 ⋯ a 2 k ⋮ ⋮ ⋱ ⋮ a m 1 a m 2 ⋯ a m k ] , B = [ b 11 b 12 ⋯ b 1 n b 21 b 22 ⋯ b 2 n ⋮ ⋮ ⋱ ⋮ b k 1 b k 2 ⋯ b k n ] \mathbf{A}= \begin{bmatrix} {a_{11}}&{a_{12}}&{\cdots}&{a_{1k}}\\ {a_{21}}&{a_{22}}&{\cdots}&{a_{2k}}\\ {\vdots}&{\vdots}&{\ddots}&{\vdots}\\ {a_{m1}}&{a_{m2}}&{\cdots}&{a_{mk}}\\ \end{bmatrix}, \mathbf{B}= \begin{bmatrix} {b_{11}}&{b_{12}}&{\cdots}&{b_{1n}}\\ {b_{21}}&{b_{22}}&{\cdots}&{b_{2n}}\\ {\vdots}&{\vdots}&{\ddots}&{\vdots}\\ {b_{k1}}&{b_{k2}}&{\cdots}&{b_{kn}}\\ \end{bmatrix} A=⎣⎢⎢⎢⎡a11a21⋮am1a12a22⋮am2⋯⋯⋱⋯a1ka2k⋮amk⎦⎥⎥⎥⎤,B=⎣⎢⎢⎢⎡b11b21⋮bk1b12b22⋮bk2⋯⋯⋱⋯b1nb2n⋮bkn⎦⎥⎥⎥⎤
然后二者的点积为矩阵 C \mathbf{C} C:
C = A B = [ a 1 ⊤ a 2 ⊤ ⋮ a m ⊤ ] [ b 1 b 2 ⋯ b n ] = [ a 1 ⊤ b 1 a 1 ⊤ b 2 ⋯ a 1 ⊤ b n a 2 ⊤ b 1 a 2 ⊤ b 2 ⋯ a 2 ⊤ b n ⋮ ⋮ ⋱ ⋮ a m ⊤ b 1 a m ⊤ b 2 ⋯ a m ⊤ b n ] , \mathbf{C}= \mathbf{AB}= \begin{bmatrix} {\mathbf{a}_{1}^\top}\\ {\mathbf{a}_{2}^\top}\\ {\vdots}\\ {\mathbf{a}_{m}^\top}\\ \end{bmatrix} \begin{bmatrix} {\mathbf{b}_{1}}&{\mathbf{b}_{2}}&{\cdots}&{\mathbf{b}_{n}}\\ \end{bmatrix} =\begin{bmatrix} {\mathbf{a}_{1}^\top\mathbf{b}_{1}}&{\mathbf{a}_{1}^\top\mathbf{b}_{2}}&{\cdots}&{\mathbf{a}_{1}^\top\mathbf{b}_{n}}\\ {\mathbf{a}_{2}^\top\mathbf{b}_{1}}&{\mathbf{a}_{2}^\top\mathbf{b}_{2}}&{\cdots}&{\mathbf{a}_{2}^\top\mathbf{b}_{n}}\\ {\vdots}&{\vdots}&{\ddots}&{\vdots}\\ {\mathbf{a}_{m}^\top\mathbf{b}_{1}}&{\mathbf{a}_{m}^\top\mathbf{b}_{2}}&{\cdots}&{\mathbf{a}_{m}^\top\mathbf{b}_{n}}\\ \end{bmatrix}, C=AB=⎣⎢⎢⎢⎡a1⊤a2⊤⋮am⊤⎦⎥⎥⎥⎤[b1b2⋯bn]=⎣⎢⎢⎢⎡a1⊤b1a2⊤b1⋮am⊤b1a1⊤b2a2⊤b2⋮am⊤b2⋯⋯⋱⋯a1⊤bna2⊤bn⋮am⊤bn⎦⎥⎥⎥⎤,
由代码表示,两矩阵相乘我们使用 .mm 函数:
B = torch.ones(4, 3)
torch.mm(A, B)
tensor([[ 6., 6., 6.],
[22., 22., 22.],
[38., 38., 38.],
[54., 54., 54.],
[70., 70., 70.]])
注意:
- 矩阵-矩阵乘法不应与前面的“哈达玛积”混淆;
- 两矩阵相乘 A B \mathbf{AB} AB ,必须保证矩阵 A \mathbf{A} A 的列数和矩阵 B \mathbf{B} B 的行数相等。
关于范数的介绍,大家可以看看作者的讲解——范数,这里就总结常见的几种。
u = torch.tensor([3.0, -4.0])
torch.norm(u)
tensor(5.)
torch.abs(u).sum()
tensor(7.)
torch.norm(torch.ones((4, 9)))
tensor(6.)
- 证明一个矩阵 A 的转置的转置是 A : ( A ⊤ ) ⊤ = A (A^⊤)^⊤=A (A⊤)⊤=A
- 给出两个矩阵 A 和 B , 显示转置的和等于和的转置: A ⊤ + B ⊤ = ( A + B ) ⊤ A^⊤+B^⊤=(A+B)^⊤ A⊤+B⊤=(A+B)⊤ 。
- 几个问题:
- 给定任意方矩阵 A , A + A ⊤ A , A+A^⊤ A,A+A⊤ 总是对称的吗?为什么?
- 我们在本节中定义了形状(2,3,4)的张量X。len(X)的输出结果是什么?
- 对于任意形状的张量X,len(X)是否总是对应于X特定轴的长度?这个轴是什么?
- 运行A/A.sum(axis=1),看看会发生什么。你能分析原因吗?
- 当你在曼哈顿的两点之间旅行时,你需要在坐标上走多远,也就是说,就大街和街道而言?你能斜着走吗?
- 考虑一个具有形状(2,3,4)的张量,在轴0,1,2上的求和输出是什么形状?
- 向linalg.norm函数提供3个或更多轴的张量,并观察其输出。对于任意形状的张量这个函数计算得到什么?
这一节就到这了,我们下一节再见~~