PyTorch教程-2.3. 线性代数

电子说

1.2w人已加入

描述

到目前为止,我们可以将数据集加载到张量中,并使用基本的数学运算来操纵这些张量。要开始构建复杂的模型,我们还需要一些线性代数工具。本节简要介绍了最基本的概念,从标量算术开始,一直到矩阵乘法。

 

import torch

 

 

from mxnet import np, npx

npx.set_np()

 

 

from jax import numpy as jnp

 

 

import tensorflow as tf

 

2.3.1. 标量

大多数日常数学都是一次处理一个数字。正式地,我们称这些值为标量。例如,帕洛阿尔托的气温适中72华氏度。如果您想将温度转换为摄氏度,您可以计算表达式c=59(f−32), 环境f到 72. 在这个等式中,值5,9, 和 32是标量。变量c和f代表未知标量。

我们用普通的小写字母表示标量(例如,x, y, 和z) 和所有(连续)实值标量的空间 R. 为了方便起见,我们将跳过严格的空间定义。请记住这个表达式x∈R是一种正式的说法 x是一个实值标量。符号∈(发音为“in”)表示集合中的成员。例如, x,y∈{0,1}表示x和y是只能取值的变量0或者1.

标量被实现为仅包含一个元素的张量。下面,我们分配两个标量并执行熟悉的加法、乘法、除法和求幂运算。

 

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.))

 

 

x = np.array(3.0)
y = np.array(2.0)

x + y, x * y, x / y, x ** y

 

 

(array(5.), array(6.), array(1.5), array(9.))

 

 

x = jnp.array(3.0)
y = jnp.array(2.0)

x + y, x * y, x / y, x**y

 

 

No GPU/TPU found, falling back to CPU. (Set TF_CPP_MIN_LOG_LEVEL=0 and rerun for more info.)

 

 

(Array(5., dtype=float32, weak_type=True),
 Array(6., dtype=float32, weak_type=True),
 Array(1.5, dtype=float32, weak_type=True),
 Array(9., dtype=float32, weak_type=True))

 

 

x = tf.constant(3.0)
y = tf.constant(2.0)

x + y, x * y, x / y, x**y

 

 

(,
 ,
 ,
 )

 

2.3.2. 载体

出于我们的目的,您可以将向量视为固定长度的标量数组。与它们的代码对应物一样,我们将这些值称为 向量的元素(同义词包括条目和组件). 当向量表示现实世界数据集中的示例时,它们的值具有一定的现实意义。例如,如果我们正在训练一个模型来预测贷款违约的风险,我们可能会将每个申请人与一个向量相关联,该向量的分量对应于他们的收入、工作年限或以前的违约次数等数量。如果我们正在研究心脏病发作风险,每个向量可能代表一个患者,其组成部分可能对应于他们最近的生命体征、胆固醇水平、每天的运动分钟数等。我们用粗体小写字母表示向量,(例如,x, y, 和z).

向量实现为1st-阶张量。通常,此类张量可以具有任意长度,受内存限制。注意:在 Python 中,与大多数编程语言一样,向量索引从0,也称为从零开始的索引,而在线性代数中下标开始于1(基于一个的索引)。

 

x = torch.arange(3)
x

 

 

tensor([0, 1, 2])

 

 

x = np.arange(3)
x

 

 

array([0., 1., 2.])

 

 

x = jnp.arange(3)
x

 

 

Array([0, 1, 2], dtype=int32)

 

 

x = tf.range(3)
x

 

 


 

我们可以使用下标来引用向量的元素。例如,x2表示的第二个元素x. 自从x2是标量,我们不加粗。默认情况下,我们通过垂直堆叠元素来可视化向量。

(2.3.1)x=[x1⋮xn],

这里x1,…,xn是向量的元素。稍后,我们将区分这种列向量和元素水平堆叠的行向量。回想一下,我们通过索引访问张量的元素。

 

x[2]

 

 

tensor(2)

 

 

x[2]

 

 

array(2.)

 

 

x[2]

 

 

Array(2, dtype=int32)

 

 

x[2]

 

 


 

表示一个向量包含n元素,我们写 x∈Rn. 正式地,我们称n向量的维 数。在代码中,这对应于张量的长度,可通过 Python 的内置len函数访问。

 

len(x)

 

 

3

 

 

len(x)

 

 

3

 

 

len(x)

 

 

3

 

 

len(x)

 

 

3

 

我们还可以通过属性访问长度shape。形状是一个元组,指示张量沿每个轴的长度。只有一个轴的张量具有只有一个元素的形状。

 

x.shape

 

 

torch.Size([3])

 

 

x.shape

 

 

(3,)

 

 

x.shape

 

 

(3,)

 

 

x.shape

 

 

TensorShape([3])

 

通常,“维度”这个词会被过度表示为轴的数量和沿特定轴的长度。为了避免这种混淆,我们使用顺序来指代轴的数量,使用维度 专门指代组件的数量。

2.3.3. 矩阵

就像标量一样0th-阶张量和向量是1st-阶张量,矩阵是 2nd-阶张量。我们用粗体大写字母表示矩阵(例如,X,Y, 和 Z), 并用两个轴的张量在代码中表示它们。表达方式A∈Rm×n 表示一个矩阵A包含m×n 实值标量,排列为m行和n列。什么时候m=n,我们说矩阵是方阵。在视觉上,我们可以将任何矩阵表示为表格。为了引用单个元素,我们对行和列索引都下标,例如,aij是属于的值A的ith行和jth柱子:

(2.3.2)A=[a11a12⋯a1na21a22⋯a2n⋮⋮⋱⋮am1am2⋯amn].

在代码中,我们表示一个矩阵 A∈Rm×n通过一个 2nd-具有形状的阶张量 (m,n). 我们可以转换任何适当大小的m×n张量变成 m×n通过将所需的形状传递给矩阵reshape:

 

A = torch.arange(6).reshape(3, 2)
A

 

 

tensor([[0, 1],
    [2, 3],
    [4, 5]])

 

 

A = np.arange(6).reshape(3, 2)
A

 

 

array([[0., 1.],
    [2., 3.],
    [4., 5.]])

 

 

A = jnp.arange(6).reshape(3, 2)
A

 

 

Array([[0, 1],
    [2, 3],
    [4, 5]], dtype=int32)

 

 

A = tf.reshape(tf.range(6), (3, 2))
A

 

 


 

有时,我们想翻转坐标轴。当我们交换矩阵的行和列时,结果称为转置。形式上,我们表示一个矩阵A的转置A⊤而如果B=A⊤, 然后bij=aji 对全部i和j. 因此,转置一个 m×n矩阵是一个n×m矩阵:

(2.3.3)A⊤=[a11a21…am1a12a22…am2⋮⋮⋱⋮a1na2n…amn].

在代码中,我们可以访问任何矩阵的转置,如下所示:

 

A.T

 

 

tensor([[0, 2, 4],
    [1, 3, 5]])

 

 

A.T

 

 

array([[0., 2., 4.],
    [1., 3., 5.]])

 

 

A.T

 

 

Array([[0, 2, 4],
    [1, 3, 5]], dtype=int32)

 

 

tf.transpose(A)

 

 


 

对称矩阵是方阵的子集,它们等于它们自己的转置:A=A⊤. 以下矩阵是对称的:

 

A = torch.tensor([[1, 2, 3], [2, 0, 4], [3, 4, 5]])
A == A.T

 

 

tensor([[True, True, True],
    [True, True, True],
    [True, True, True]])

 

 

A = np.array([[1, 2, 3], [2, 0, 4], [3, 4, 5]])
A == A.T

 

 

array([[ True, True, True],
    [ True, True, True],
    [ True, True, True]])

 

 

A = jnp.array([[1, 2, 3], [2, 0, 4], [3, 4, 5]])
A == A.T

 

 

Array([[ True, True, True],
    [ True, True, True],
    [ True, True, True]], dtype=bool)

 

 

A = tf.constant([[1, 2, 3], [2, 0, 4], [3, 4, 5]])
A == tf.transpose(A)

 

 


 

矩阵对于表示数据集很有用。通常,行对应于单个记录,列对应于不同的属性。

2.3.4. 张量

虽然您可以仅使用标量、向量和矩阵在机器学习之旅中走得更远,但最终您可能需要使用高阶张量。张量为我们提供了一种通用的方式来描述对 nth-顺序数组。我们将张量类的软件对象称为 “张量”,正是因为它们也可以有任意数量的轴。虽然将张量这个词用于数学对象及其在代码中的实现可能会造成混淆,但我们的意思通常应该从上下文中清楚。我们用特殊字体的大写字母表示一般张量(例如,X, Y, 和Z) 及其索引机制(例如,xijk和[X]1,2i−1,3) 从矩阵中自然得出。

当我们开始处理图像时,张量将变得更加重要。每个图像作为3rd-阶张量,其轴对应于高度、宽度和通道。在每个空间位置,每种颜色(红色、绿色和蓝色)的强度都沿着通道堆叠。此外,一组图像在代码中表示为4th-阶张量,其中不同的图像沿第一轴索引。通过增加形状分量的数量,高阶张量的构造类似于向量和矩阵。

 

torch.arange(24).reshape(2, 3, 4)

 

 

tensor([[[ 0, 1, 2, 3],
     [ 4, 5, 6, 7],
     [ 8, 9, 10, 11]],

    [[12, 13, 14, 15],
     [16, 17, 18, 19],
     [20, 21, 22, 23]]])

 

 

np.arange(24).reshape(2, 3, 4)

 

 

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.]]])

 

 

jnp.arange(24).reshape(2, 3, 4)

 

 

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]]], dtype=int32)

 

 

tf.reshape(tf.range(24), (2, 3, 4))

 

 


 

2.3.5. 张量运算的基本性质

标量、向量、矩阵和高阶张量都有一些方便的属性。例如,逐元素运算产生与其操作数具有相同形状的输出。

 

A = torch.arange(6, dtype=torch.float32).reshape(2, 3)
B = A.clone() # Assign a copy of A to B by allocating new memory
A, A + B

 

 

(tensor([[0., 1., 2.],
     [3., 4., 5.]]),
 tensor([[ 0., 2., 4.],
     [ 6., 8., 10.]]))

 

 

A = np.arange(6).reshape(2, 3)
B = A.copy() # Assign a copy of A to B by allocating new memory
A, A + B

 

 

(array([[0., 1., 2.],
    [3., 4., 5.]]),
 array([[ 0., 2., 4.],
    [ 6., 8., 10.]]))

 

 

A = jnp.arange(6, dtype=jnp.float32).reshape(2, 3)
B = A
A, A + B

 

 

(Array([[0., 1., 2.],
    [3., 4., 5.]], dtype=float32),
 Array([[ 0., 2., 4.],
    [ 6., 8., 10.]], dtype=float32))

 

 

A = tf.reshape(tf.range(6, dtype=tf.float32), (2, 3))
B = A # No cloning of A to B by allocating new memory
A, A + B

 

 

(,
 )

 

两个矩阵的元素积称为它们的Hadamard 积(表示为⊙). 下面,我们拼出两个矩阵的 Hadamard 乘积的条目 A,B∈Rm×n:

(2.3.4)A⊙B=[a11b11a12b12…a1nb1na21b21a22b22…a2nb2n⋮⋮⋱⋮am1bm1am2bm2…amnbmn].

 

A * B

 

 

tensor([[ 0., 1., 4.],
    [ 9., 16., 25.]])

 

 

A * B

 

 

array([[ 0., 1., 4.],
    [ 9., 16., 25.]])

 

 

A * B

 

 

Array([[ 0., 1., 4.],
    [ 9., 16., 25.]], dtype=float32)

 

 

A * B

 

 


 

将标量和张量相加或相乘会产生与原始张量形状相同的结果。在这里,张量的每个元素都被添加到(或乘以)标量。

 

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]))

 

 

a = 2
X = np.arange(24).reshape(2, 3, 4)
a + X, (a * X).shape

 

 

(array([[[ 2., 3., 4., 5.],
     [ 6., 7., 8., 9.],
     [10., 11., 12., 13.]],

    [[14., 15., 16., 17.],
     [18., 19., 20., 21.],
     [22., 23., 24., 25.]]]),
 (2, 3, 4))

 

 

a = 2
X = jnp.arange(24).reshape(2, 3, 4)
a + X, (a * X).shape

 

 

(Array([[[ 2, 3, 4, 5],
     [ 6, 7, 8, 9],
     [10, 11, 12, 13]],

    [[14, 15, 16, 17],
     [18, 19, 20, 21],
     [22, 23, 24, 25]]], dtype=int32),
 (2, 3, 4))

 

 

a = 2
X = tf.reshape(tf.range(24), (2, 3, 4))
a + X, (a * X).shape

 

 

(,
 TensorShape([2, 3, 4]))

 

2.3.6. 减少

通常,我们希望计算张量元素的总和。表达向量中元素的总和x长度 n, 我们写∑i=1nxi. 它有一个简单的功能:

 

x = torch.arange(3, dtype=torch.float32)
x, x.sum()

 

 

(tensor([0., 1., 2.]), tensor(3.))

 

 

x = np.arange(3)
x, x.sum()

 

 

(array([0., 1., 2.]), array(3.))

 

 

x = jnp.arange(3, dtype=jnp.float32)
x, x.sum()

 

 

(Array([0., 1., 2.], dtype=float32), Array(3., dtype=float32))

 

 

x = tf.range(3, dtype=tf.float32)
x, tf.reduce_sum(x)

 

 

(,
 )

 

为了表达任意形状张量元素的总和,我们简单地对其所有轴求和。例如,一个元素的总和m×n矩阵A可以写成 ∑i=1m∑j=1naij.

 

A.shape, A.sum()

 

 

(torch.Size([2, 3]), tensor(15.))

 

 

A.shape, A.sum()

 

 

((2, 3), array(15.))

 

 

A.shape, A.sum()

 

 

((2, 3), Array(15., dtype=float32))

 

 

A.shape, tf.reduce_sum(A)

 

 

(TensorShape([2, 3]), )

 

默认情况下,调用 sum 函数会减少沿其所有轴的张量,最终生成标量。我们的库还允许我们指定应减少张量的轴。为了对沿行(轴 0)的所有元素求和,我们指定axis=0in sum。由于输入矩阵沿轴 0 减少以生成输出向量,因此输出的形状中缺少该轴。

 

A.shape, A.sum(axis=0).shape

 

 

(torch.Size([2, 3]), torch.Size([3]))

 

 

A.shape, A.sum(axis=0).shape

 

 

((2, 3), (3,))

 

 

A.shape, A.sum(axis=0).shape

 

 

((2, 3), (3,))

 

 

A.shape, tf.reduce_sum(A, axis=0).shape

 

 

(TensorShape([2, 3]), TensorShape([3]))

 

指定axis=1将通过汇总所有列的元素来减少列维度(轴 1)。

 

A.shape, A.sum(axis=1).shape

 

 

(torch.Size([2, 3]), torch.Size([2]))

 

 

A.shape, A.sum(axis=1).shape

 

 

((2, 3), (2,))

 

 

A.shape, A.sum(axis=1).shape

 

 

((2, 3), (2,))

 

 

A.shape, tf.reduce_sum(A, axis=1).shape

 

 

(TensorShape([2, 3]), TensorShape([2]))

 

通过求和沿行和列减少矩阵等同于对矩阵的所有元素求和。

 

A.sum(axis=[0, 1]) == A.sum() # Same as A.sum()

 

 

tensor(True)

 

 

A.sum(axis=[0, 1]) == A.sum() # Same as A.sum()

 

 

array(True)

 

 

A.sum(axis=[0, 1]) == A.sum() # Same as A.sum()

 

 

Array(True, dtype=bool)

 

 

tf.reduce_sum(A, axis=[0, 1]), tf.reduce_sum(A) # Same as tf.reduce_sum(A)

 

 

(,
 )

 

一个相关的量是均值,也叫平均值。我们通过将总和除以元素总数来计算平均值。因为计算平均值非常普遍,所以它有一个专用的库函数,其工作方式类似于sum.

 

A.mean(), A.sum() / A.numel()

 

 

(tensor(2.5000), tensor(2.5000))

 

 

A.mean(), A.sum() / A.size

 

 

(array(2.5), array(2.5))

 

 

A.mean(), A.sum() / A.size

 

 

(Array(2.5, dtype=float32), Array(2.5, dtype=float32))

 

 

tf.reduce_mean(A), tf.reduce_sum(A) / tf.size(A).numpy()

 

 

(,
 )

 

同样,计算均值的函数也可以减少沿特定轴的张量。

 

A.mean(axis=0), A.sum(axis=0) / A.shape[0]

 

 

(tensor([1.5000, 2.5000, 3.5000]), tensor([1.5000, 2.5000, 3.5000]))

 

 

A.mean(axis=0), A.sum(axis=0) / A.shape[0]

 

 

(array([1.5, 2.5, 3.5]), array([1.5, 2.5, 3.5]))

 

 

A.mean(axis=0), A.sum(axis=0) / A.shape[0]

 

 

(Array([1.5, 2.5, 3.5], dtype=float32), Array([1.5, 2.5, 3.5], dtype=float32))

 

 

tf.reduce_mean(A, axis=0), tf.reduce_sum(A, axis=0) / A.shape[0]

 

 

(,
 )

 

2.3.7. 非减额

有时在调用用于计算和或平均值的函数时保持轴数不变可能很有用。当我们想要使用广播机制时,这很重要。

 

sum_A = A.sum(axis=1, keepdims=True)
sum_A, sum_A.shape

 

 

(tensor([[ 3.],
     [12.]]),
 torch.Size([2, 1]))

 

 

sum_A = A.sum(axis=1, keepdims=True)
sum_A, sum_A.shape

 

 

(array([[ 3.],
    [12.]]),
 (2, 1))

 

 

sum_A = A.sum(axis=1, keepdims=True)
sum_A, sum_A.shape

 

 

(Array([[ 3.],
    [12.]], dtype=float32),
 (2, 1))

 

 

sum_A = tf.reduce_sum(A, axis=1, keepdims=True)
sum_A, sum_A.shape

 

 

(,
 TensorShape([2, 1]))

 

例如,由于在sum_A对每一行求和后保留其两个轴,我们可以通过广播除以A创建sum_A一个矩阵,其中每一行总和为1.

 

A / sum_A

 

 

tensor([[0.0000, 0.3333, 0.6667],
    [0.2500, 0.3333, 0.4167]])

 

 

A / sum_A

 

 

array([[0.    , 0.33333334, 0.6666667 ],
    [0.25   , 0.33333334, 0.41666666]])

 

 

A / sum_A

 

 

Array([[0.    , 0.33333334, 0.6666667 ],
    [0.25   , 0.33333334, 0.41666666]], dtype=float32)

 

 

A / sum_A

 

 


 

如果我们想计算沿某个轴的元素的累积和A,比如axis=0(逐行),我们可以调用该cumsum 函数。按照设计,此函数不会减少沿任何轴的输入张量。

 

A.cumsum(axis=0)

 

 

tensor([[0., 1., 2.],
    [3., 5., 7.]])

 

 

A.cumsum(axis=0)

 

 

array([[0., 1., 2.],
    [3., 5., 7.]])

 

 

A.cumsum(axis=0)

 

 

Array([[0., 1., 2.],
    [3., 5., 7.]], dtype=float32)

 

 

tf.cumsum(A, axis=0)

 

 


 

2.3.8. 点积

到目前为止,我们只执行了元素运算、求和和平均。如果这就是我们所能做的全部,那么线性代数就不值得单独一节了。幸运的是,这是事情变得更有趣的地方。最基本的操作之一是点积。给定两个向量x,y∈Rd, 他们的 点积 x⊤y(或者 〈x,y〉) 是同一位置元素乘积的总和: x⊤y=∑i=1dxiyi.

 

y = torch.ones(3, dtype = torch.float32)
x, y, torch.dot(x, y)

 

 

(tensor([0., 1., 2.]), tensor([1., 1., 1.]), tensor(3.))

 

 

y = np.ones(3)
x, y, np.dot(x, y)

 

 

(array([0., 1., 2.]), array([1., 1., 1.]), array(3.))

 

 

y = jnp.ones(3, dtype = jnp.float32)
x, y, jnp.dot(x, y)

 

 

(Array([0., 1., 2.], dtype=float32),
 Array([1., 1., 1.], dtype=float32),
 Array(3., dtype=float32))

 

 

y = tf.ones(3, dtype=tf.float32)
x, y, tf.tensordot(x, y, axes=1)

 

 

(,
 ,
 )

 

等价地,我们可以通过执行逐元素乘法然后求和来计算两个向量的点积:

 

torch.sum(x * y)

 

 

tensor(3.)

 

 

np.sum(x * y)

 

 

array(3.)

 

 

jnp.sum(x * y)

 

 

Array(3., dtype=float32)

 

 

tf.reduce_sum(x * y)

 

 


 

点积在广泛的上下文中很有用。例如,给定一组值,用向量表示 x∈Rn和一组权重表示为 w∈Rn, 中值的加权和 x根据权重w可以表示为点积x⊤w. 当权重为非负且总和为一时,即 (∑i=1nwi=1),点积表示加权平均值。将两个向量归一化为单位长度后,点积表示它们之间夹角的余弦值。在本节的后面,我们将正式介绍长度的概念。

2.3.9. 矩阵向量积

既然我们知道如何计算点积,我们就可以开始了解点积m×n矩阵 A和n维向量 x. 首先,我们根据行向量可视化我们的矩阵

(2.3.5)A=[a1⊤a2⊤⋮am⊤],

每个ai⊤∈Rn是一个行向量,表示ith矩阵的行 A.

矩阵向量积Ax只是长度的列向量m,谁的ith元素是点积ai⊤x:

(2.3.6)Ax=[a1⊤a2⊤⋮am⊤]x=[a1⊤xa2⊤x⋮am⊤x].

我们可以考虑与矩阵相乘 A∈Rm×n作为投影矢量的变换Rn到Rm. 这些转换非常有用。例如,我们可以将旋转表示为某些方矩阵的乘法。矩阵向量乘积还描述了在给定前一层输出的情况下计算神经网络中每一层输出所涉及的关键计算。

为了在代码中表达矩阵向量乘积,我们使用函数mv。请注意,(其沿轴 1 的长度)的列维度必须与(其长度)A的维度相同。xPyTorch 有一个方便的运算符@,可以执行矩阵向量和矩阵矩阵乘积(取决于它的参数)。因此我们可以写 A@x.

 

A.shape, x.shape, torch.mv(A, x), A@x

 

 

(torch.Size([2, 3]), torch.Size([3]), tensor([ 5., 14.]), tensor([ 5., 14.]))

 

To express a matrix-vector product in code, we use the same dot function. The operation is inferred based on the type of the arguments. Note that the column dimension of A (its length along axis 1) must be the same as the dimension of x (its length).

 

A.shape, x.shape, np.dot(A, x)

 

 

((2, 3), (3,), array([ 5., 14.]))

 

 

A.shape, x.shape, jnp.matmul(A, x)

 

 

((2, 3), (3,), Array([ 5., 14.], dtype=float32))

 

To express a matrix-vector product in code, we use the matvec function. Note that the column dimension of A (its length along axis 1) must be the same as the dimension of x (its length).

 

A.shape, x.shape, tf.linalg.matvec(A, x)

 

 

(TensorShape([2, 3]),
 TensorShape([3]),
 )

 

2.3.10。矩阵-矩阵乘法

如果您掌握了点积和矩阵-向量积的窍门,那么矩阵-矩阵乘法应该很简单。

假设我们有两个矩阵 A∈Rn×k和 B∈Rk×m:

(2.3.7)A=[a11a12⋯a1ka21a22⋯a2k⋮⋮⋱⋮an1an2⋯ank],B=[b11b12⋯b1mb21b22⋯b2m⋮⋮⋱⋮bk1bk2⋯bkm].

让ai⊤∈Rk表示行向量表示ith矩阵的行 A然后让bj∈Rk 表示来自的列向量jth矩阵的列B:

(2.3.8)A=[a1⊤a2⊤⋮an⊤],B=[b1b2⋯bm].

形成矩阵乘积 C∈Rn×m,我们简单地计算每个元素cij作为点积之间 ith一排A和 jth列的B, IE, ai⊤bj:

(2.3.9)C=AB=[a1⊤a2⊤⋮an⊤][b1b2⋯bm]=[a1⊤b1a1⊤b2⋯a1⊤bma2⊤b1a2⊤b2⋯a2⊤bm⋮⋮⋱⋮an⊤b1an⊤b2⋯an⊤bm].

我们可以想到矩阵-矩阵乘法AB作为表演m矩阵向量乘积或m×n点积并将结果拼接在一起形成一个 n×m矩阵。在以下代码片段中,我们对A和执行矩阵乘法B。这里, A是一个2行3列的矩阵,B是一个3行4列的矩阵。相乘后得到一个2行4列的矩阵。

 

B = torch.ones(3, 4)
torch.mm(A, B), A@B

 

 

(tensor([[ 3., 3., 3., 3.],
     [12., 12., 12., 12.]]),
 tensor([[ 3., 3., 3., 3.],
     [12., 12., 12., 12.]]))

 

 

B = np.ones(shape=(3, 4))
np.dot(A, B)

 

 

array([[ 3., 3., 3., 3.],
    [12., 12., 12., 12.]])

 

 

B = jnp.ones((3, 4))
jnp.matmul(A, B)

 

 

Array([[ 3., 3., 3., 3.],
    [12., 12., 12., 12.]], dtype=float32)

 

 

B = tf.ones((3, 4), tf.float32)
tf.matmul(A, B)

 

 


 

术语矩阵-矩阵乘法通常简化为矩阵乘法,不应与 Hadamard 乘积混淆。

2.3.11。规范

线性代数中一些最有用的运算符是范数。通俗地说,向量的范数告诉我们它有多大。例如,ℓ2范数测量向量的(欧几里德)长度。在这里,我们使用了一个大小概念,它涉及向量分量的大小(而不是其维度)。

范数是函数‖⋅‖将向量映射到标量并满足以下三个属性:

给定任何向量x,如果我们用标量缩放向量(的所有元素)α∈R, 它的范数相应地缩放:

(2.3.10)‖αx‖=|α|‖x‖.

对于任何向量x和y:范数满足三角不等式:

(2.3.11)‖x+y‖≤‖x‖+‖y‖.

向量的范数是非负的,只有当向量为零时它才会消失:

(2.3.12)‖x‖>0 for all x≠0.

许多函数都是有效的范数,不同的范数编码不同的大小概念。我们小学几何学计算直角三角形的斜边时都学过的欧几里德范数,就是对向量元素的平方和开平方根。正式地,这被称为ℓ2 规范并表示为

(2.3.13)‖x‖2=∑i=1nxi2.

该方法norm计算ℓ2规范。

 

u = torch.tensor([3.0, -4.0])
torch.norm(u)

 

 

tensor(5.)

 

 

u = np.array([3, -4])
np.linalg.norm(u)

 

 

array(5.)

 

 

u = jnp.array([3.0, -4.0])
jnp.linalg.norm(u)

 

 

Array(5., dtype=float32)

 

 

u = tf.constant([3.0, -4.0])
tf.norm(u)

 

 


 

这ℓ1范数也很流行,相关的度量标准称为曼哈顿距离。根据定义,ℓ1norm 对向量元素的绝对值求和:

(2.3.14)‖x‖1=∑i=1n|xi|.

相比于ℓ2norm,它对异常值不太敏感。计算ℓ1范数,我们用求和运算合成绝对值。

 

torch.abs(u).sum()

 

 

tensor(7.)

 

 

np.abs(u).sum()

 

 

array(7.)

 

 

jnp.linalg.norm(u, ord=1) # same as jnp.abs(u).sum()

 

 

Array(7., dtype=float32)

 

 

tf.reduce_sum(tf.abs(u))

 

 


 

这俩ℓ2和ℓ1规范是更一般的特殊情况ℓp 规范:

(2.3.15)‖x‖p=(∑i=1n|xi|p)1/p.

在矩阵的情况下,事情要复杂得多。毕竟,矩阵既可以看作是单个条目的集合,也 可以看作是对向量进行运算并将它们转换为其他向量的对象。例如,我们可以问矩阵向量乘积多长时间Xv可能是相对于 v. 这种思路导致了一种称为 谱范数的范数。现在,我们介​​绍Frobenius 范数,它更容易计算并定义为矩阵元素平方和的平方根:

(2.3.16)‖X‖F=∑i=1m∑j=1nxij2.

Frobenius 范数的行为就好像它是一个ℓ2矩阵形向量的范数。调用以下函数将计算矩阵的 Frobenius 范数。

 

torch.norm(torch.ones((4, 9)))

 

 

tensor(6.)

 

 

np.linalg.norm(np.ones((4, 9)))

 

 

array(6.)

 

 

jnp.linalg.norm(jnp.ones((4, 9)))

 

 

Array(6., dtype=float32)

 

 

tf.norm(tf.ones((4, 9)))

 

 


 

虽然我们不想过于超前,但我们已经对这些概念为何有用有了一些直觉。在深度学习中,我们经常试图解决优化问题:最大化分配 给观察数据的概率;最大化与推荐模型相关的收入;最小化预测和地面实况观察之间的距离;最小化同一个人的照片表示之间的距离,同时最大化 不同人的照片表示之间的距离。这些构成深度学习算法目标的距离通常表示为规范。

2.3.12。讨论

在本节中,我们回顾了理解大量现代深度学习所需的所有线性代数。线性代数还有很多内容,其中大部分对机器学习很有用。例如,矩阵可以分解为因子,这些分解可以揭示现实世界数据集中的低维结构。机器学习的整个子领域都专注于使用矩阵分解及其对高阶张量的推广来发现数据集中的结构并解决预测问题。但这本书侧重于深度学习。我们相信,一旦您亲手将机器学习应用于真实数据集,您将更愿意学习更多数学。因此,虽然我们保留稍后介绍更多数学的权利,但我们在此结束本节。

如果你渴望学习更多的线性代数,有很多优秀的书籍和在线资源。如需更高级的速成课程,请考虑查看 Kolter ( 2008 )、Petersen等人。(2008 年),斯特朗(1993 年)。

回顾一下:

标量、向量、矩阵和张量是线性代数中使用的基本数学对象,分别具有零、一、二和任意数量的轴。

可以分别通过索引或sum和 等操作沿指定轴对张量进行切片或缩减。mean

Elementwise 产品称为 Hadamard 产品。相比之下,点积、矩阵-向量积和矩阵-矩阵积不是按元素运算,并且通常返回与操作数具有不同形状的对象。

与 Hadamard 积相比,矩阵-矩阵积的计算时间要长得多(三次而不是二次时间)。

范数捕捉了向量大小的各种概念,并且通常应用于两个向量的差异以测量它们的距离。

常见的向量范数包括ℓ1和ℓ2 范数,常见的矩阵范数包括谱范数和Frobenius 范数。

2.3.13. 练习

证明矩阵转置的转置就是矩阵本身:(A⊤)⊤=A.

给定两个矩阵A和B, 表明总和和转置交换: A⊤+B⊤=(A+B)⊤.

给定任何方阵A, 是 A+A⊤总是对称的?你能仅用前两个练习的结果来证明这个结果吗?

我们在本节中定义了形状为 (2, 3, 4) 的张量X。的输出是什么len(X)?在不执行任何代码的情况下写下您的答案,然后使用代码检查您的答案。

对于任意形状的张量X,总是len(X)对应于某个轴的长度X?那个轴是什么?

跑看看会发生什么。能分析一下原因吗?A / A.sum(axis=1)

在曼哈顿市中心的两点之间旅行时,根据坐标,即大道和街道,您需要经过的距离是多少?可以对角线旅行吗?

考虑一个形状为 (2, 3, 4) 的张量。沿轴 0、1 和 2 的求和输出的形状是什么?

将具有 3 个或更多轴的张量输入函数linalg.norm并观察其输出。对于任意形状的张量,此函数计算什么?

定义三个大矩阵,比如说 A∈R210×216, B∈R216×25和 C∈R25×214,例如用高斯随机变量初始化。你想计算产品ABC. 内存占用和速度是否有任何差异,具体取决于您是否计算(AB)C或者 A(BC). 为什么?

定义三个大矩阵,比如说 A∈R210×216, B∈R216×25和 C∈R25×216. 根据您是否计算,速度是否有任何差异 AB或者AC⊤?为什么?如果初始化会发生什么变化 C=B⊤没有克隆内存?为什么?

定义三个矩阵,比如说 A,B,C∈R100×200. 通过堆叠构成具有3个轴的张量 [A,B,C]. 什么是维度?切出第三轴第二坐标恢复B. 检查你的答案是否正确。
 

 

打开APP阅读更多精彩内容
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

全部0条评论

快来发表一下你的评论吧 !

×
20
完善资料,
赚取积分