watch: PyTorch - 刘二 04 | Backpropagation

最基本的数据类型:Tensor,存储所有的数值,标量,向量,矩阵,高阶tensor

tensor有两个成员: data(权重数值本身w), grad(也是tensor,损失值(标量)对权重的导数 ∂loss/∂w)

构建计算图

 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
import torch

# 准备数据集
x_data = [1.0, 2.0, 3.0]
y_data = [2.0, 4.0, 6.0]

w = torch.Tensor([1.0]) # 创建权重初始值,tensor变量中只有一个值
w.requires_grad = True  # 该变量需要计算梯度,默认的tensor不需要计算梯度

# 设计模型
def forward(x):
    return x* w     # w是个tensor,乘法*被重载了: tensor 与tensor之间的数乘,x被自动转换成tensor,所以乘法结果也是tensor,并且也会需要计算梯度。

# 计算损失
def loss(x,y):
    y_pred = forward(x)         # 计算预测值
    return (y_pred - y) ** 2    # 每调用一次loss函数,计算图被动态地构建出来

# 训练过程
print("predict (before training)", 4, forward(4).item())

for epoch in range(100):    #训练100轮
    for x,y in zip(x_data, y_data):     # 对应组合拼起来
        # 前馈:计算每个样本的损失(随机梯度下降),是一个(标量)tensor,含有1个值,
        # 如果是一个向量没法backward
        l = loss(x,y)   

        # 反向传播:调用张量 l 的成员函数,把图上的所有梯度求出来,存到 w 中,然后计算图就被释放了,
        # 下一次loss计算会创建新的计算图(因为每次计算图可能不一样)
        l.backward()

        print('\t grad:', x,y, w.grad.item()) #item把grad变成int/float,直接拿出它的数值(防止产生计算图)

        w.data = w.data - 0.01 * w.grad.data    #更新数值,成员grad也是一个tensor,tensor的乘法会建立计算图,所以要取其data再做乘法,就不会建立计算图,只是修改w的数值(并不是说以后还要对这个运算求梯度)

        w.grad.data.zero_() #权重的梯度(导数)的数值清零,否则各次由.backward()计算出的梯度值会累加(有的时候需要梯度累加)

    print("progress",epoch, 1.item())   #打印每轮训练的loss

print("predict (after training)", 4, forward(4).item())

如果要在程序中对损失 l 求和取平均,注意要取出数值(使用int/float运算): sum + = l.item(),否则因为 l 是tensor,一直加,计算图一直延长,导致内存泄漏。 (这就是为什么要避免 in-place operation? 1)


Ref

Built with Hugo
Theme Stack designed by Jimmy