手动学习深度学习

1.环境准备

1.1 安装CUDA

选择自己适合的CUDA版本进行下载

1
https://d2l.ai/chapter_appendix-tools-for-deep-learning/aws.html#installing-cuda

1.2 安装miniconda

选择自己合适的miniconda版本进行安装,安装时注意勾选添加环境变量,要不然后面还得手动添加环境变量。

1
https://mirrors.tuna.tsinghua.edu.cn/anaconda/miniconda/

1.3 创建环境

若输出正常则证明安装成功

1
conda 

创建一个conda环境指定其版本为3.8的版本

1
conda creat -n test python=3.8

激活环境

1
conda activate test

安装jupyter,torch,torchvision,由于作者用的是cuda的版本是12.7,所以用最新的torch版本就可以,由于默认源是在国外,要通过换源或者魔法,作者采用的是清华源,换源的过程请自行研究。

1
pip install jupyter d2l torch torchvision

使用命令,若有torch版本则安装正常。

1
conda list

使用jupyter notebook,运行命令后等待一会,他会自动在浏览器中打开。

1
jupyter notebook

预备知识

2.1 数据操作

2.1.1 入门

为了能够完成各种数据操作,我们需要某种方法来存储和操作数据。 通常,我们需要做两件重要的事:(1)获取数据;(2)将数据读入计算机后对其进行处理。 如果没有某种方法来存储数据,那么获取数据是没有意义的。在深度学习中张量(tensor)(多维数组)是一个重要的概念,张量与Numpy的ndarray类似,但深度学习框架又比Numpy的ndarray多一些重要功能: 首先,GPU很好地支持加速计算,而NumPy仅支持CPU计算; 其次,张量类支持自动微分。 这些功能使得张量类更适合深度学习。

首先可以导入torch

1
import torch

张量有很多维表示,一维张量对应数学上的向量(vector),具有两个轴的张量对应的是数学上的矩阵(matrix); 具有两个轴以上的张量没有特殊的数学名称。

张量的每个值称为张量的元素(element)。他默认是整数型的,例如张量X中有12个元素,除非额外指定,他们将储存在内存之中,并且以CPU进行计算。

1
2
x = torch.arange(12)
x
1
tensor([ 0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11])

可以通过张量的shape属性来访问张量(沿每个轴的长度)的形状

1
x.shape
1
torch.Size([12])

如果只想知道张量中元素的总数,即形状的所有元素乘积,可以检查它的大小(size)。 因为这里在处理的是一个向量,所以它的shape与它的size相同。

1
x.numel()
1
12

要想改变一个张量的形状而不改变元素数量和元素值,可以调用reshape函数。 例如,可以把张量x从形状为(12,)的行向量转换为形状为(3,4)的矩阵。 这个新的张量包含与转换前相同的值,但是它被看成一个3行4列的矩阵。 要重点说明一下,虽然张量的形状发生了改变,但其元素值并没有变。 注意,通过改变张量的形状,张量的大小不会改变。

1
2
X = x.reshape(3,4)
X
1
2
3
tensor([[ 0,  1,  2,  3],
[ 4, 5, 6, 7],
[ 8, 9, 10, 11]])

其实不需要我们手动做除法,如果我们知道目标的长度是(高度,宽度),在知道其中的一个值之后,高度可以自动的计算出来,在以上的例子中我们手动指定了宽度和高度,但是我们可以调用-1来自动计算出维度。

1
2
X = x.reshape(-1,4)
X
1
2
3
tensor([[ 0,  1,  2,  3],
[ 4, 5, 6, 7],
[ 8, 9, 10, 11]])

有时,我们希望使用全0、全1、其他常量,或者从特定分布中随机采样的数字来初始化矩阵。 我们可以创建一个形状为(2,3,4)的张量,其中所有元素都设置为0。

1
torch.zeros((2,3,4))
1
2
3
4
5
6
7
tensor([[[0., 0., 0., 0.],
[0., 0., 0., 0.],
[0., 0., 0., 0.]],

[[0., 0., 0., 0.],
[0., 0., 0., 0.],
[0., 0., 0., 0.]]])
1
torch.ones((2, 3, 4))
1
2
3
4
5
6
7
tensor([[[1., 1., 1., 1.],
[1., 1., 1., 1.],
[1., 1., 1., 1.]],

[[1., 1., 1., 1.],
[1., 1., 1., 1.],
[1., 1., 1., 1.]]])

有时我们想通过从某个特定的概率分布中随机采样来得到张量中每个元素的值。 例如,当我们构造数组来作为神经网络中的参数时,我们通常会随机初始化参数的值。 以下代码创建一个形状为(3,4)的张量。 其中的每个元素都从均值为0、标准差为1的标准高斯分布(正态分布)中随机采样。

1
torch.randn(3,4)
1
2
3
tensor([[-0.9328, -1.0965, -0.4413,  1.5306],
[-0.3912, -0.6224, 1.5258, 0.6877],
[ 1.8359, 0.1706, -0.5137, 0.2882]])

除了以上方法,我们还可以使用python的列表,进行赋值。最外层的列表对应于轴0,内层的列表对应于轴1。

1
torch.tensor([[2, 1, 4, 3], [1, 2, 3, 4], [4, 3, 2, 1]])
1
2
3
tensor([[2, 1, 4, 3],
[1, 2, 3, 4],
[4, 3, 2, 1]])

2.1.2 运算符

要想在数据的基础上进行运算操作,则需要操作符的参与,其中最简单且最有用的操作是按元素(elementwise)运算。 它们将标准标量运算符应用于数组的每个元素。 对于将两个数组作为输入的函数,按元素运算将二元运算符应用于两个数组中的每对位置对应的元素。 我们可以基于任何从标量到标量的函数来创建按元素函数。

对于任意具有相同形状的张量, 常见的标准算术运算符(+-*/**)都可以被升级为按元素运算。数后面带 .浮点数的意思

1
2
3
x = torch.tensor([1.0, 2, 4, 8])
y = torch.tensor([2, 2, 2, 2])
x + y, x - y, x * y, x / y, x ** y # **运算符是求幂运算
1
2
3
4
5
(tensor([ 3.,  4.,  6., 10.]),
tensor([-1., 0., 2., 6.]),
tensor([ 2., 4., 8., 16.]),
tensor([0.5000, 1.0000, 2.0000, 4.0000]),
tensor([ 1., 4., 16., 64.]))

“按元素”方式可以应用更多的计算,包括像求幂这样的一元运算符。

1
torch.exp(x)
1
tensor([2.7183e+00, 7.3891e+00, 5.4598e+01, 2.9810e+03])

我们也可以把多个张量连结(concatenate)在一起, 把它们端对端地叠起来形成一个更大的张量。我们只需要提供张量列表,并给出沿哪个轴连结。 下面的例子分别演示了当我们沿行(轴-0,形状的第一个元素) 和按列(轴-1,形状的第二个元素)连结两个矩阵时,会发生什么情况。

1
2
3
X = torch.arange(12, dtype=torch.float32).reshape((3,4))
Y = torch.tensor([[2.0, 1, 4, 3], [1, 2, 3, 4], [4, 3, 2, 1]])
torch.cat((X, Y), dim=0), torch.cat((X, Y), dim=1)
1
2
3
4
5
6
7
8
9
(tensor([[ 0.,  1.,  2.,  3.],
[ 4., 5., 6., 7.],
[ 8., 9., 10., 11.],
[ 2., 1., 4., 3.],
[ 1., 2., 3., 4.],
[ 4., 3., 2., 1.]]),
tensor([[ 0., 1., 2., 3., 2., 1., 4., 3.],
[ 4., 5., 6., 7., 1., 2., 3., 4.],
[ 8., 9., 10., 11., 4., 3., 2., 1.]]))

有时,我们想通过逻辑运算符构建二元张量。 以X == Y为例: 对于每个位置,如果XY在该位置相等,则新张量中相应项的值为1。 这意味着逻辑语句X == Y在该位置处为真,否则该位置为0。

1
X == Y 
1
2
3
tensor([[False,  True, False,  True],
[False, False, False, False],
[False, False, False, False]])

对张量中的所有元素进行求和,会产生一个单元素张量。

1
X.sum()
1
tensor(66.)

2.1.3 广播机制

在上面的部分中,我们看到了如何在相同形状的两个张量上执行按元素操作。 在某些情况下,即使形状不同,我们仍然可以通过调用 广播机制(broadcasting mechanism)来执行按元素操作。这种机制的工作方式如下:

  1. 通过适当复制元素来扩展一个或两个数组,以便在转换之后,两个张量具有相同的形状;
  2. 对生成的数组执行按元素操作

在大多数情况下,我们将沿着数组中长度为1的轴进行广播,reshape(a, b)的意思是按照a行b列的方式进行广播。

1
2
3
a = torch.arange(3).reshape((3, 1))
b = torch.arange(2).reshape((1, 2))
a, b
1
2
3
4
(tensor([[0],
[1],
[2]]),
tensor([[0, 1]]))

由于ab分别是和矩阵,如果让它们相加,它们的形状不匹配,但是a+b会自动广播,矩阵a将复制列, 矩阵b将复制行,然后再按元素相加

1
a + b
1
2
3
tensor([[0, 1],
[1, 2],
[2, 3]])

2.1.4 索引和切片

就像在任何其他Python数组中一样,张量中的元素可以通过索引访问。 与任何Python数组一样:第一个元素的索引是0,最后一个元素索引是-1;可以用[-1]选择最后一个元素,可以用[1:3]选择第二个和第三个元素:

1
X[-1], X[1:3]
1
2
3
(tensor([ 8.,  9., 10., 11.]),
tensor([[ 4., 5., 6., 7.],
[ 8., 9., 10., 11.]]))

除读取外,我们还可以通过指定索引来将元素写入矩阵。

1
2
X[1, 2] = 9
X
1
2
3
tensor([[ 0.,  1.,  2.,  3.],
[ 4., 5., 9., 7.],
[ 8., 9., 10., 11.]])

如果我们想为多个元素赋值相同的值,我们只需要索引所有元素,然后为它们赋值。 例如,[0:2, :]访问第1行和第2行,其中“:”代表沿轴1(列)的所有元素。

1
2
X[0:2, :] = 12
X
1
2
3
tensor([[12., 12., 12., 12.],
[12., 12., 12., 12.],
[ 8., 9., 10., 11.]])

2.1.5 节约内存

如果使用Y = X + Y,我们将取消引用Y指向的张量,而是指向新分配的内存处的张量。

这可能是不可取的,原因有两个:

  1. 首先,我们不想总是不必要地分配内存。在机器学习中,我们可能有数百兆的参数,并且在一秒内多次更新所有参数。通常情况下,我们希望原地执行这些更新;
  2. 如果我们不原地更新,其他引用仍然会指向旧的内存位置,这样我们的某些代码可能会无意中引用旧的参数。

幸运的是,执行原地操作非常简单。 我们可以使用切片表示法将操作的结果分配给先前分配的数组,例如Y[:] = <expression>。 为了说明这一点,我们首先创建一个新的矩阵Z,其形状与另一个Y相同, 使用zeros_like来分配一个全的块。也可以使用X[:] = X + YX += Y来减少操作的内存开销

1
2
3
4
Z = torch.zeros_like(Y)
print('id(Z):', id(Z))
Z[:] = X + Y
print('id(Z):', id(Z))
1
2
id(Z): 2322001590128
id(Z): 2322001590128

2.1.6 转换为其他Python对象

将深度学习框架定义的张量转换为NumPy张量(ndarray)很容易,反之也同样容易。 torch张量和numpy数组将共享它们的底层内存,就地操作更改一个张量也会同时更改另一个张量。

1
2
3
A = X.numpy()
B = torch.tensor(A)
type(A), type(B)
1
(numpy.ndarray, torch.Tensor)

要将大小为1的张量转换为Python标量,我们可以调用item函数或Python的内置函数。

1
2
a = torch.tensor([3.5])
a, a.item(), float(a), int(a)
1
(tensor([3.5000]), 3.5, 3.5, 3)

2.2 数据预处理

在Python中常用的数据分析工具中,我们通常使用pandas软件包

2.2.1 读取数据集

如果不改变jupyter的默认路径,创建文件会在C盘的用户目录下(Linux不是)建议改一下路径。

我们首先创建一个人工数据集,并存储在CSV(逗号分隔值)文件 ./data/house_tiny.csv

1
2
3
4
5
6
7
8
9
import os
os.makedirs(os.path.join('.','data'),exist_ok=True)
data_file = os.path.join('.','data','house_tiny.csv')
with open (data_file,'w') as f:
f.write('NumRooms,Alley,Price\n') # 列名
f.write('NA,Pave,127500\n') # 每行表示一个数据样本
f.write('2,NA,106000\n')
f.write('4,NA,178100\n')
f.write('NA,NA,140000\n')
1
print(os.path.abspath(os.path.join('..', 'data', 'house_tiny.csv')))  // 输出文件的路径

要从创建的CSV文件中加载原始数据集,我们导入pandas包并调用read_csv函数

1
2
3
import pandas as pd
data = pd.read_csv(data_file)
print(data)
1
2
3
4
5
   NumRooms Alley   Price
0 NaN Pave 127500
1 2.0 NaN 106000
2 4.0 NaN 178100
3 NaN NaN 140000

2.2.2 处理缺失值

“NaN”项代表缺失值。 为了处理缺失的数据,典型的方法包括插值法删除法,插值法是用一个值弥补缺失值,删除法则是直接忽略缺失值。在这里我们使用插值法。

通过位置索引iloc,我们将data分成inputsoutputs,data为NumRooms,Alley对于NaN的数值,取平均值并填充。

1
2
3
inputs, outputs = data.iloc[:, 0:2], data.iloc[:, 2]
inputs = inputs.fillna(inputs.select_dtypes(include='number').mean())
print(inputs)
1
2
3
4
0       3.0  Pave
1 2.0 NaN
2 4.0 NaN
3 3.0 NaN

对于inputs中的类别值或离散值,我们将“NaN”视为一个类别。 由于“巷子类型”(“Alley”)列只接受两种类型的类别值“Pave”和“NaN”, pandas可以自动将此列转换为两列“Alley_Pave”和“Alley_nan”。 巷子类型为“Pave”的行会将“Alley_Pave”的值设置为1,“Alley_nan”的值设置为0。 缺少巷子类型的行会将“Alley_Pave”和“Alley_nan”分别设置为0和1。dtype=int是指定类型为int类型的,若不指定则是bool类型的。

1
2
inputs = pd.get_dummies(inputs, dummy_na=True, dtype=int)
print(inputs)
1
2
3
4
5
   NumRooms  Alley_Pave  Alley_nan
0 3.0 1 0
1 2.0 0 1
2 4.0 0 1
3 3.0 0 1

2.2.3 转换为张量格式

现在inputsoutputs中的所有条目都是数值类型,它们可以转换为张量格式

1
2
3
4
5
import torch

X = torch.tensor(inputs.to_numpy(dtype=float))
y = torch.tensor(outputs.to_numpy(dtype=float))
X, y
1
2
3
4
5
(tensor([[3., 1., 0.],
[2., 0., 1.],
[4., 0., 1.],
[3., 0., 1.]], dtype=torch.float64),
tensor([127500., 106000., 178100., 140000.], dtype=torch.float64))

2.3 线性代数

2.3.1. 标量

作者因为刚经历完考研,线代这块原理就不再过多赘述,感兴趣的可以自己google

简单的加减乘除

1
2
3
4
5
6
import torch

x = torch.tensor(3.0)
y = torch.tensor(2.0)

x + y, x * y, x / y, x**y
1
(tensor(5.), tensor(6.), tensor(1.5000), tensor(9.))

2.3.2 向量

人们通过一维张量表示向量。一般来说,张量可以具有任意长度,取决于机器的内存限制。

1
2
x = torch.arange(4)
x
1
tensor([0, 1, 2, 3])

我们通过张量的索引来访问任一元素

1
x[3]
1
tensor(3)

与普通数组一样可以使用 .len()来访问张量的长度

1
len(X)
1
4

当用张量表示一个向量(只有一个轴)时,我们也可以通过.shape属性访问向量的长度。 形状(shape)是一个元素组,列出了张量沿每个轴的长度(维数)。

1
X.shape
1
torch.Size([4, 3])

当调用函数来实例化张量时, 我们可以通过指定两个分量和来创建一个形状的矩阵。

1
2
A = torch.arange(20).reshape(5, 4)
A
1
2
3
4
5
tensor([[ 0,  1,  2,  3],
[ 4, 5, 6, 7],
[ 8, 9, 10, 11],
[12, 13, 14, 15],
[16, 17, 18, 19]])

现在在代码中访问矩阵的转置。

1
A.T
1
2
3
4
tensor([[ 0,  4,  8, 12, 16],
[ 1, 5, 9, 13, 17],
[ 2, 6, 10, 14, 18],
[ 3, 7, 11, 15, 19]])

2.3.3 矩阵

二阶向量

2.3.4 张量

张量(本小节中的“张量”指代数对象)是描述具有任意数量轴的n维数组的通用方法。例如,向量是一阶张量,矩阵是二阶张量,它们的索引机制与矩阵类似

2.3.5 张量算法的本质

标量、向量、矩阵和任意数量轴的张量(本小节中的“张量”指代数对象)有一些实用的属性。 例如,从按元素操作的定义中可以注意到,任何按元素的一元运算都不会改变其操作数的形状。 同样,给定具有相同形状的任意两个张量,任何按元素二元运算的结果都将是相同形状的张量。 例如,将两个相同形状的矩阵相加,会在这两个矩阵上执行元素加法

1
2
3
A = torch.arange(20, dtype=torch.float32).reshape(5, 4)
B = A.clone() # 通过分配新内存,将A的一个副本分配给B
A, A + B
1
2
3
4
5
6
7
8
9
10
(tensor([[ 0.,  1.,  2.,  3.],
[ 4., 5., 6., 7.],
[ 8., 9., 10., 11.],
[12., 13., 14., 15.],
[16., 17., 18., 19.]]),
tensor([[ 0., 2., 4., 6.],
[ 8., 10., 12., 14.],
[16., 18., 20., 22.],
[24., 26., 28., 30.],
[32., 34., 36., 38.]]))

具体而言,两个矩阵的按元素乘法称为Hadamard积(Hadamard product)

1
A * B
1
2
3
4
5
tensor([[  0.,   1.,   4.,   9.],
[ 16., 25., 36., 49.],
[ 64., 81., 100., 121.],
[144., 169., 196., 225.],
[256., 289., 324., 361.]])

将张量乘以或加上一个标量不会改变张量的形状,其中张量的每个元素都将与标量相加或相乘。(因为扩散机制)

1
2
3
a = 2
X = torch.arange(24).reshape(2, 3, 4)
a + X, (a * X).shape
1
2
3
4
5
6
7
8
(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]))

2.3.6 降维

我们可以对任意张量进行的一个有用的操作是计算其元素的和。数学表示法使用$$ \sum $$进行表示,为了表示长度为的向量中元素的总和,可以记为$$ \sum _{i=1}^x {x_i}$$在代码中可以调用计算求和的函数:

1
2
x = torch.arange(4, dtype=torch.float32)
x, x.sum()
1
(tensor([0., 1., 2., 3.]), tensor(6.))

我们可以表示任意形状张量的元素和

1
A.shape, A.sum()
1
(torch.Size([5, 4]), tensor(190.))

默认情况下,调用求和函数会沿所有的轴降低张量的维度,使它变为一个标量。 我们还可以指定张量沿哪一个轴来通过求和降低维度。 以矩阵为例,为了通过求和所有行的元素来降维(轴0),可以在调用函数时指定axis=0。 由于输入矩阵沿0轴降维以生成输出向量,因此输入轴0的维数在输出形状中消失。

1
2
A_sum_axis0 = A.sum(axis=0)
A_sum_axis0, A_sum_axis0.shape
1
(tensor([40., 45., 50., 55.]), torch.Size([4]))

指定axis=1将通过汇总所有列的元素降维(轴1)。因此,输入轴1的维数在输出形状中消失。

1
2
A_sum_axis1 = A.sum(axis=1)
A_sum_axis1, A_sum_axis1.shape
1
(tensor([ 6., 22., 38., 54., 70.]), torch.Size([5]))

沿着行和列对矩阵求和,等价于对矩阵的所有元素进行求和。

1
A.sum(axis=[0,1]) #结果和A.sum()一样
1
tensor(190.)

求平均值

1
A.mean(),A.sum()/A.numel()
1
(tensor(9.5000), tensor(9.5000))

同样计算平均值的函数可以沿指定轴降低张量的维度。

1
A.mean(axis=0),A.sum(axis=0)/A.shape[0]
1
(tensor([ 8.,  9., 10., 11.]), tensor([ 8.,  9., 10., 11.]))

非降维求和,有时候调用函数来计算总值和或均值时保持轴数不变会很有用。

1
2
sum_A = A.sum(axis=1,keepdims=True)
sum_A
1
2
3
4
5
tensor([[ 6.],
[22.],
[38.],
[54.],
[70.]])

由于sum_A在对每行进行求和后仍保持两个轴,我们可以通过广播将A除以sum_A

1
A/sum_A
1
2
3
4
5
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元素的累积总和,比如axis=0(按行计算),可以调用cumsum函数,这个函数不会沿任何轴降低维度

1
A.cumsum(axis=0)
1
2
3
4
5
tensor([[ 0.,  1.,  2.,  3.],
[ 4., 6., 8., 10.],
[12., 15., 18., 21.],
[24., 28., 32., 36.],
[40., 45., 50., 55.]])

2.3.7 点积

点积同矩阵的运算一样,算出来是一个数。

1
2
y = torch.ones(4,dtype = torch.float32)   // 创建一个长度为4的全1
x,y,torch.dot(x,y)
1
(tensor([0., 1., 2., 3.]), tensor([1., 1., 1., 1.]), tensor(6.))

注意,我们可以通过执行元素乘法,然后进行求和来表示两个向量的点积。

1
torch.sum(x*y)
1
tensor(6.)

2.3.8 矩阵-向量积

矩阵的向量积在几何上来说是对矩阵进行如旋转等操作,这些在矩阵上是非常有用的。

在代码中使用张量表示矩阵-向量积,我们必须用mv函数,当我们为矩阵A和向量x调用torch.mv(A,x)时,会执行矩阵-向量积。

1
A.shape,x.shape,torch.mv(A,x)
1
(torch.Size([5, 4]), torch.Size([4]), tensor([ 14.,  38.,  62.,  86., 110.]))

2.3.9 矩阵-矩阵乘法

可以将矩阵乘法AB看作简单的执行M次矩阵-向量积。

1
2
B = torch.ones(4,3)
torch.mm(A,B)
1
2
3
4
5
tensor([[ 6.,  6.,  6.],
[22., 22., 22.],
[38., 38., 38.],
[54., 54., 54.],
[70., 70., 70.]])

2.3.10 范数

在数学上,范数包括向量范数和矩阵范数,向量范数表征向量空间中向量的大小,矩阵范数表征矩阵引起变化的大小。一种非严密的解释就是,对应向量范数,向量空间中的向量都是有大小的,这个大小如何度量,就是用范数来度量的,不同的范数都可以来度量这个大小,就好比米和尺都可以来度量远近一样;对于矩阵范数,学过线性代数,我们知道,通过运算AX=B,可以将向量X变化为B,矩阵范数就是来度量这个变化大小的。

默认条件下norm计算的是二范数(即欧几里得范数),公式为:
$$
||x||2 = \sqrt{\sum{i=1}^n x_i^2}
$$

计算范数:

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

在深度学习中经常使用$$ L_2 $$范数的平方,也会经常遇到$$ L_1 $$范数,它表示为向量元素的绝对值之和:
$$
||x||1 = \sum{i=1}^n |x_i|
$$
与$$ L_2 $$范数相比$$ L_1 $$范数受异常值的影响较小。为计算$$ L_1 $$范数,我们将绝对值函数和按元素求和组合起来。

1
torch.abs(u).sum()
1
tensor(7.)

Frobenius 范数,是矩阵元素平方和的平方根:

$$
||x||F = \sqrt{\sum{i=1}^m \sum_{j=1}^n x_{ij}^2}
$$
Frobenius范数满足向量范数的所有性质,它就像是矩阵形向量的$$L_2$$范数

1
torch.norm(torch.ones((4, 9)))
1
tensor(6.)

范数和的目标:

在深度学习中,我们经常试图解决优化问题: 最大化分配给观测数据的概率; 最小化预测和真实观测之间的距离。 用向量表示物品(如单词、产品或新闻文章),以便最小化相似项目之间的距离,最大化不同项目之间的距离。 目标,或许是深度学习算法最重要的组成部分(除了数据),通常被表达为范数。

2.4 微积分

在深度学习中,我们“训练”模型,不断更新它们,使它们在看到越来越多的数据时变得越来越好。 通常情况下,变得更好意味着最小化一个损失函数(loss function), 即一个衡量“模型有多糟糕”这个问题的分数。 最终,我们真正关心的是生成一个模型,它能够在从未见过的数据上表现良好。 但“训练”模型只能将模型与我们实际能看到的数据相拟合。 因此,我们可以将拟合模型的任务分解为两个关键问题:

  • 优化(optimization):用模型拟合观测数据的过程;
  • 泛化(generalization):数学原理和实践者的智慧,能够指导我们生成出有效性超出用于训练的数据集本身的模型。

2.4.1 导数和微分

这里不过多赘述,只需要知道人工智能模型很多都是用求导来近似的。感兴趣可以看看拟合过程。

2.4.2 偏导数

到目前为止,我们只讨论了仅含一个变量的函数的微分。 在深度学习中,函数通常依赖于许多变量。 因此,我们需要将微分的思想推广到多元函数(multivariate function)上。

2.4.3 梯度

梯度是多元实值函数对应的一个向量值函数;在场论中也可认为是一个将标量场作用为向量场的算子。它代表多元函数的值改变“最快”的方向。

2.5 自动微分