ML.DL

来源:动手深度学习PyTorch版-李沐

1 课程安排

  • 深度学习基础:线性神经网络, 多层感知机
  • 卷积神经网络:LeNet, AlexNet, VGG, Inception, ResNet
  • 循环神经网络:RNN, GRU, LSTM, seq2seq
  • 注意力机制:Attention, Transformer
  • 优化算法:SGD, Momentum, Adam
  • 高性能计算:并行,多GPU,分布式
  • 计算机视觉:目标检测,语义分割
  • 自然语言处理:词嵌入,BERT

2 深度学习介绍

AI地图、图片分类、物体检测和分割、样式迁移、人脸合成、文字生成图片、文字生成、无人驾驶
案例研究——广告点击:预测与训练,完整的故事

3 本地安装(略)

4 数据操作

4.1 N维数组

N维数组是机器学习和神经网络的主要数据结构,样例:

  • 0-d(标量):一个类别
  • 1-d(向量):一个特征向量
  • 2-d(矩阵):一个样本——特征矩阵
  • 3-d:RGB图片(宽×高×通道)
  • 4-d:一个RGB图片批量batch(批量大小×宽×高×通道)
  • 5-d:一个视频批量(批量大小×时间×宽×高×通道)

4.2 创建数组

创建数组需要

  • 形状:例如3×4矩阵
  • 类型:每个元素的数据类型,如32位浮点数
  • 数值:每个元素的值,如全是0或随机数

4.3 访问元素

对m×n矩阵,有第0,1,2…i,…(m-1)行、第0,1,2…j,…(n-1)列

  • 一个元素:[i,j]
  • 一行元素:[i,:]
  • 一列元素:[:,j]
  • 一子区域:[$i_1$:$i_2$,j:]
  • 一跳行子区域:[::i,::j](从第0行0列元素开始,每i个数据取行、每j个数据取列访问元素)

4.4 数据操作实现

张量表示一个数值组成的数组,这个数组可能有多个维度

1
2
x = torch.arrange(12)
x

可以通过张量的shape属性来访问张量的形状和张量中元素的总数

1
2
x.shape
x.numel()

可以调用reshape函数来改变一个张量的形状而不改变元素数量和元素值

1
2
x = x.reshape(3,4)
x

使用全0、全1、其他常量或者从特定分布中随机采样的数字

1
2
torch.zeros((2,3,4))
torch.ones((2,3,4))

通过提供包含数值的Python列表(或嵌套列表)来为所需张量中的每个元素赋予确定值

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

常见的标准算术运算符都可以升级为按元素运算

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
torch.exp(x)

把多个张量连结在一起(在第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]])
tensor.cat((X,Y),dim=0), torch.cat((X,Y),dim=1)

通过逻辑运算符构建二元张量

1
X == Y

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

1
X.sum()

即使形状不同,仍然可以通过调用广播机制(broadcasting mechanism)来执行按元素操作

1
2
3
a = torch.arange(3).reshape((3,1))
b = torch.arange(2).reshape((1,2))
a, b, a+b

可以用[-1]选择最后一个元素,用[1:3]选择第二个和第三个元素(第1个、第2个元素)

1
X[-1], Y[1:3]

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

1
X[1,2] = 9

为多个元素赋相同的值,只需索引所有元素然后为其赋值

1
2
X[0:2,:] = 12
X

运行一些操作可能导致新结果分配内存

1
2
3
before = id(Y)
Y = Y+X
id(Y) == before

执行原地操作

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

若后续计算中美哟重复使用X,可以使用X[:] = X+YX += Y来减少操作的内存开销

1
2
3
before = id(X)
X += Y
id(X) == before

转换为NumPy张量

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

将大小为1的张量转换成Python标量

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

5 数据预处理

创建一个人工数据集,并存储在csv文件

1
2
3
4
5
6
7
8
9
10
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')

从创建的csv文件中加载原始数据集

1
2
3
4
5
6
# 如果没有安装pandas,只需取消对以下行的注释:
# !pip install pandas
import pandas as pd

data = pd.read_csv(data_file)
print(data)

为处理缺失的数据,典型方法包括插值和删除,这里考虑差值方法

1
2
3
inputs, outputs = data.iloc[:, 0:2], data.iloc[:, 2]
inputs = inputs.fillna(inputs.mean())
print(inputs)

对于 inputs 中的类别值或离散值,将 “NaN” 视为一个类别

1
2
inputs = pd.get_dummies(inputs, dummy_na=True)
print(inputs)

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

1
2
3
4
import torch

X, y = np.array(inputs.values), torch.tensor(outputs.values)
X, y

其他:reshape???

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

其他:tensor(数学概念,张量,深度学习框架作者数学不太行混用的)和array(计算机概念,数组,ndarray是多维数组)

5 线性代数

5.1 向量

  • 点乘:$a^{\top} b=\sum_{i} a_{i} b_{i}$
  • 正交:$a^{\top} b=\sum_{i} a_{i} b_{i}=0$

5.2 矩阵

  • 乘法(矩阵乘以向量):$c=A b$ where $c_{i}=\sum_{j} A_{i j} b_{j}$
  • 乘法(矩阵乘以矩阵):$C=A B$ where $C_{i k}=\sum_{j} A_{i j} B_{j k}$
  • 范数:$c=A \cdot b$ hence $|c| \leq|A| \cdot|b|$
    • 矩阵范数:最小的满足上面公式的值
    • Frobenius范数:$|A|_{\text {Frob }}=\left[\sum_{i j} A_{i j}^{2}\right]^{\frac{1}{2}}$
  • 特殊矩阵
    • 对称和反对称:$A_{i j}=A_{j i}$ and $A_{i j}=-A_{j i}$
    • 正定矩阵:$|x|^{2}=x^{\top} x \geq 0$ generalizes to $x^{\top} A x \geq 0$
    • 正交矩阵:$U U^{\top}=\mathbf{1}$
    • 置换矩阵:$P$ where $P_{i j}=1$ if and only if $j=\pi(i)$
  • 特征向量和特征值:特征向量是不被矩阵改变方向的向量,对称矩阵总能找到特征向量

5.3 线性代数实现

标量由只有一个元素的张量表示

1
2
3
4
5
import torch

x = torch.tensor([3.0])
y = torch.tensor([2.0])
x+y, x*y, x/y, x**y

可将向量视为标量值组成的列表

1
2
x = torch.arange(4)
x

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

1
x[3]

访问张量的长度

1
len(x)

只有一个轴的张量,形状只有一个元素

1
x.shape

通过指定两个分量m和n来创建一个形状为m×n的矩阵

1
2
A = torch.arange(20).reshape(5,4)
A

矩阵的转置

1
A.T

对称矩阵(symmetric matrix)A等于其转置:$\mathbf{A}=\mathbf{A}^{\top}$

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

构建具有更多轴的数据结构

1
2
X = torch.arange(24).reshape(2,3,4)
X

给定具有相同形状的任何两个张量,任何按元素二元计算的结果都将是相同形状的张量

1
2
3
A = torch.arange(20, dtype.float32).reshape(5,4)
B = A.clone()
A, A+B

两个矩阵的按元素乘法称为哈达玛积(Hadamard product)(数学符号

1
2
3
4
A * B
a = 2
X = torch.arange(24).reshape(2,3,4)
a+X, (a*X).shape

计算其元素的和

1
2
x = torch.arange(4, dtype=torch.float32)
x, x.sum()

表示任意形状张量的元素和

1
2
A = torch.arange(20*2).reshape(2,5,4)
A.shape, A.sum()

指定求和汇总张量的轴

1
2
3
4
5
6
7
8
A_sum_axis0 = A.sum(axis=0)
A_sum_axis0, A_sum_axis0.shape

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

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

一个与求和相关的量是平均值(mean/average)

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

计算总和或均值保持轴数不变

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

通过广播将A除以sum_A

1
A/sum_A

点积是相同位置的按元素乘积的和

1
2
y = torch.ones(4, dtype=torch.float32)
x, y, torch.dot(x,y)

也可以通过执行按元素乘法,然后进行求和来表示两个向量的点积

1
torch.sum(x*y)

矩阵向量积Ax是一个长度为m的列向量,其$i^{\text {th }}$元素是点积$\mathbf{a}_{i}^{\top} \mathbf{x}$

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

可以将矩阵乘法AB看作是简单执行m次矩阵-向量积,然后将结果拼接在一起,形成一个n×m矩阵

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

$L_{2}$范数是向量元素平方和的平方根:$|\mathbf{x}|_{2}=\sqrt{\sum_{i=1}^{n} x_{i}^{2}}$

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

矩阵的弗罗贝尼乌斯范数(Frobenius norm)是矩阵元素的平方和的平方根:$|\mathbf{X}|_{F}=\sqrt{\sum_{i=1}^{m} \sum_{j=1}^{n} x_{i j}^{2}}$

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

[补充]按特定轴求和

  • shape: [5,4] i.e., axis: 0,1
    • axis=0, sum: [4]
    • axis=1, sum: [5]
  • shape: [2,5,4] i.e., axis: 0,1,2
    • axis=1, sum: [2,4]
    • axis=2, sum: [2,5]
    • axis=[1,2], sum: [2]

keepdims=True

  • shape: [2,5,4] i.e., axis: 0,1,2
    • axis=1, sum: [2,1,4]
    • axis=[1,2], sum: [2,1,1]
1
2
3
4
5
6
7
8
9
10
11
import torch

a = torch.ones((2,5,4))
a.shape

a.sum(axis=0).shape
a.sum(axis=1).shape
a.sum(axis=[0,2]).shape

a.sum(axis=1,keepdims=True).shape
a.sum(axis=[0,2],keepdims=True).shape

问题:torch不区分行向量和列向量吗?
一个数学概念的向量对计算机来讲就是一个一维数组,可以用一个二维矩阵来区分行向量和列向量

  • 行向量:矩阵的行数为1,列数为n
  • 列向量:矩阵的行数为n,列数为1

6 矩阵计算

6.1 标量导数——导数是切线的斜率

6.2 亚导数——将导数拓展到不可微的函数

6.3 梯度——将导数拓展到向量

6.4 拓展到矩阵