Post

跟李沐动手学深度学习笔记 第一部分 01-15

跟李沐动手学深度学习笔记

跟李沐动手学深度学习笔记 第一部分 01-15

01 - 03 忽略

04 数据操作 + 数据预处理

基本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import torch
x = torch.arange(12)

x.shape # 张量形状
# torch.Size([12])

x.numel() # 有多少元素
# 12

x = x.reshape(3, 4) # 改变形状

torch.zeros((2, 3, 4)) # 全零
torch.ones((2, 3, 4)) # 全一
torch.tensor([[1, 2, 3, 4], [5, 6, 7, 8]]) # 自定义

计算与连接

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
# 算术运算符被升级为按元素计算
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  # **运算符是求幂运算

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

# 更多计算
torch.exp(x)

# 把多个张量连接在一起
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) # 按行连接 按列连接

'''
(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 # 按元素判断

'''
tensor([[False,  True, False,  True],
        [False, False, False, False],
        [False, False, False, False]])
'''

x.sum()

# torch 也有广播机制
# 矩阵访问同 python [-1] [1:3]
# 可以同时为多个元素赋值相同的值

内存操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# torch 也有广播机制
# 矩阵访问同 python [-1] [1:3]
# 可以同时为多个元素赋值相同的值

# 运行一些操作可能会导致为新结果分配内存
x = torch.arange(12).reshape(3, 4)
before = id(x) # 可以看作是 c++ 的指针

y = torch.ones((3, 4))
x = x + y
id(x) == before
# False

# 执行原地操作
z = torch.zeros_like(y) # 创建一个全 0 的形状同 y 的张量
print('id(z):', id(z))
z[:] = x + y
print('id(z):', id(z))
# z 的内存没有变化

before = id(x)
x += y
id(x) == before
# 在后续计算中不重复使用之前的结果,为了减少内存开销和计算开销

转换

1
2
3
4
5
6
7
8
9
10
# 转换为 NumPy 张量
A = x.numpy()
B = torch.tensor(A)
type(A), type(B)
# (numpy.ndarray, torch.Tensor)

# 将大小为 1 的张量转换为 Python 标量
a = torch.tensor([3.5])
a, a.item(), float(a), int(a) # item() 函数将张量转换为 Python 标量
# (tensor([3.5000]), 3.5, 3.5, 3)

数据预处理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
import pandas as pd
data = pd.read_csv(file)

# 处理缺失数据
inputs, outputs = data.iloc[:, 0:1], data.iloc[:, 2]
inputs = inputs.fillna(inputs.mean()) # 填充缺失值
inputs = pd.concat((inputs, data.iloc[:, 1]), axis = 1) # 缺失的数值处理
inputs

# 处理类别值或离散值 可以将 NaN 视为一个类型
inputs = pd.get_dummies(inputs, dummy_na=True)

# 将 inputs 中的 T F 转换为 1 0
inputs = inputs * 1
inputs

'''
	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
'''

# 此时 inputs 与 outputs 中的所有条目都是数值类型,可以转换为张量了
X, y = torch.tensor(inputs.values), torch.tensor(outputs.values)
X, y

# 一般类型都是 torch.float64
# 但是比较慢
# 普遍都用 torch.float32

'''
(tensor([[3., 1., 0.],
         [2., 0., 1.],
         [4., 0., 1.],
         [3., 0., 1.]], dtype=torch.float64),
 tensor([127500, 106000, 178100, 140000]))
'''

特别注意 尽量不要改东西

1
2
3
4
5
6
7
8
9
# 尽量别去改东西
a = torch.arange(12)
b = a.reshape((3, 4))
b[:] = 2
a # 显然这里把 a 也给改了, 但我们明明只改了 b

'''
tensor([2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2])
'''
  • torch 的 tensor 与 numpy 的 array 不相似

05 线性代数

Frobenius 范数

Frobenius 范数

正定矩阵

正定矩阵

正交矩阵与置换矩阵

正交矩阵与置换矩阵

特征向量与特征值

特征向量与特征值

矩阵基本操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
import torch

A = torch.arange(20).reshape(5, 4)
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.T
'''
tensor([[True, True, True],
        [True, True, True],
        [True, True, True]])
'''

A = torch.arange(20, dtype = torch.float32).reshape(5, 4)
B = A.clone() # 通过分配新内存,将 A 的一个副本分配给 B

# 矩阵按元素相乘 哈达玛积
A * B
'''
tensor([[  0.,   1.,   4.,   9.],
        [ 16.,  25.,  36.,  49.],
        [ 64.,  81., 100., 121.],
        [144., 169., 196., 225.],
        [256., 289., 324., 361.]])
'''

求值

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
'''
(tensor([[ 0.,  1.,  2.,  3.],
         [ 4.,  5.,  6.,  7.],
         [ 8.,  9., 10., 11.],
         [12., 13., 14., 15.],
         [16., 17., 18., 19.]]))
'''

A.sum(axis=0), A.sum(axis=1) # 按行求和 按列求和
'''
(tensor([40., 45., 50., 55.]), tensor([ 6., 22., 38., 54., 70.]))
'''

# 平均值
A.mean(), A.sum() / A.numel()

# 列均值
A.mean(axis=0), A.sum(axis=0) / A.shape[0]

# 计算总和或均值时保持轴数不变
A.sum(axis=1, keepdims=True), A.sum(axis=1, keepdims=False) 
'''
(tensor([[ 6.],
         [22.],
         [38.],
         [54.],
         [70.]]),
 tensor([ 6., 22., 38., 54., 70.]))
'''

# 某个轴计算 A 元素的累积总和
A.cumsum(axis=0), A.cumsum(axis=1)
'''
tensor([[ 0.,  1.,  2.,  3.],
        [ 4.,  6.,  8., 10.],
        [12., 15., 18., 21.],
        [24., 28., 32., 36.],
        [40., 45., 50., 55.]])

tensor([[ 0.,  1.,  3.,  6.],
        [ 4.,  9., 15., 22.],
        [ 8., 17., 27., 38.],
        [12., 25., 39., 54.],
        [16., 33., 51., 70.]])
'''

矩阵各种乘法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
x = torch.arange(4, dtype=torch.float32)
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.))
'''

# 矩阵向量积
A, x, A.shape, x.shape, torch.mv(A, x) 
'''
(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.]),
 torch.Size([5, 4]),
 torch.Size([4]),
 tensor([ 14.,  38.,  62.,  86., 110.]))
'''

# 矩阵 - 矩阵乘法
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.]])
'''

范数

1
2
3
4
5
6
7
8
9
10
11
12
13
# L2 范数是向量元素平方和的平方根
# L1 范数是向量元素的绝对值之和
u = torch.tensor([3,0, -4.0])
torch.norm(u, p=2), torch.abs(u).sum(), torch.norm(u, p=1)
'''
(tensor(5.), tensor(7.), tensor(7.))
'''

# Frobenius 范数是矩阵元素平方和的平方根
torch.norm(torch.ones((4, 9)))
'''
tensor(6.)
'''

06 矩阵计算

纯数学 无代码

扩展

07 自动求导

  • .requires_grad_(True) 允许存储梯度

  • .backward() 进行反向传播

  • .grad.zero_() 进行梯度清零,Pytorch 会累积梯度,不会自动清除

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
import torch
x = torch.arange(4.0)
x
'''
tensor([0., 1., 2., 3.])
'''

x.requires_grad_(True)
x.grad # 存储了梯度 默认值是 None

# 等价于 x = torch.arange(4.0, requires_grad=True)

y = 2 * torch.dot(x, x)
y
'''
tensor(28., grad_fn=<MulBackward0>)
'''

# 可以反向传播
y.backward()
x.grad
'''
tensor([ 0.,  4.,  8., 12.])
'''

x.grad == 4 * x
'''
tensor([True, True, True, True])
'''

# 现在让我们计算 x 的另一个函数
# 在默认情况下 pytorch 会累积梯度 所以我们要清楚之前的值
x.grad.zero_()
y = x.sum()
y.backward()
x.grad
'''
tensor([1., 1., 1., 1.])
'''

# 深度学习中 我们的目的不是计算微分矩阵 而是批量中每个样本单独计算的偏导数之和
x.grad.zero_()
y = x * x
# 等价于 y.backward(torch.ones(len(x)))
y.sum().backward()
x.grad
'''
tensor([0., 2., 4., 6.])
'''

# 将某些计算移动到记录的计算图之外
x.grad.zero_()
y = x * x
u = y.detach() # 将 y 从计算图中分离出来
u # u 成了一个常量
'''
tensor([0., 1., 4., 9.])
'''

z = u * x
z.sum().backward()
x.grad == u
'''
tensor([True, True, True, True])
'''

x.grad.zero_()
y = x * x
y.sum().backward()
x.grad == 2 * x
'''
tensor([True, True, True, True])
'''

# 即使构建函数的计算图需要通过 Python 控制流
# 我们仍然可以计算得到的变量的梯度
def f(a):
    b = a * 2
    while b.norm() < 1000:
        b = b * 2
    if b.sum() > 0:
        c = b
    else:
        c = 100 * b
    return c

a = torch.randn(size=(), requires_grad=True)
d = f(a)
d.backward()
a.grad == d / a
'''
tensor(True)
'''
This post is licensed under CC BY 4.0 by the author.