【进阶篇】全流程学习《20天掌握Pytorch实战》纪实 | Day07 | 动态计算图

举报
府学路18号车神 发表于 2022/05/11 15:26:31 2022/05/11
【摘要】 开源自由,知识无价~@TOC所用到的源代码及书籍+数据集以帮各位小伙伴下载放在文末,自取即可~ 一、🎉前言本节我们将介绍 Pytorch 的动态计算图。包括:动态计算图简介计算图中的Function计算图和反向传播叶子节点和非叶子节点计算图在TensorBoard中的可视化 二、🎉动态计算图简介Pytorch的计算图由节点和边组成,节点表示张量或者Function,边表示张量和Funct...

开源自由,知识无价~

@TOC

所用到的源代码及书籍+数据集以帮各位小伙伴下载放在文末,自取即可~

一、🎉前言

本节我们将介绍 Pytorch 的动态计算图。

包括:

  • 动态计算图简介

  • 计算图中的Function

  • 计算反向传播

  • 叶子节点非叶子节点

  • 计算图在TensorBoard中的可视化

二、🎉动态计算图简介

Pytorch的计算图由节点组成,节点表示张量或者Function表示张量Function之间的依赖关系

Pytorch中的计算图动态图。这里的动态主要有两重含义

第一层含义是:计算图的正向传播立即执行的。无需等待完整的计算图创建完毕,每条语句都会在计算图中动态添加节点,并==立即执行正向传播得到计算结果==。

第二层含义是:计算图在反向传播后立即销毁。下次调用需要重新构建计算图。如果在程序中使用了backward方法执行了反向传播,或者利用torch.autograd.grad方法计算了梯度,那么创建的计算图会被立即销毁,释放存储空间,下次调用需要重新创建。

1. 计算图的正向传播是立即执行的

import torch 
w = torch.tensor([[3.0,1.0]],requires_grad=True)
b = torch.tensor([[3.0]],requires_grad=True)
X = torch.randn(10,2)
Y = torch.randn(10,1)
Y_hat = X@w.t() + b  # Y_hat定义后其正向传播被立即执行,与其后面的loss创建语句无关
loss = torch.mean(torch.pow(Y_hat-Y,2))

print(loss.data)
print(Y_hat.data)

输出:

tensor(40.9172)
tensor([[ 7.4992],
        [ 5.4003],
        [ 1.7159],
        [-4.4277],
        [ 5.3206],
        [10.4804],
        [-4.4612],
        [ 5.6376],
        [ 7.9420],
        [-3.1899]])

Process finished with exit code 0

2. 计算图在反向传播后立即销毁

import torch 
w = torch.tensor([[3.0,1.0]],requires_grad=True)
b = torch.tensor([[3.0]],requires_grad=True)
X = torch.randn(10,2)
Y = torch.randn(10,1)
Y_hat = X@w.t() + b  # Y_hat定义后其正向传播被立即执行,与其后面的loss创建语句无关
loss = torch.mean(torch.pow(Y_hat-Y,2))

#计算图在反向传播后立即销毁,如果需要保留计算图, 需要设置retain_graph = True
loss.backward()  #loss.backward(retain_graph = True) 

#loss.backward() #如果再次执行反向传播将报错

三、🎉计算图中的Function

计算图中的 张量我们已经比较熟悉了, 计算图中的另外一种节点是Function, 实际上就是 Pytorch中各种对张量操作的函数。

这些Function和我们Python中的函数有一个较大的区别,那就是它同时包括正向计算逻辑和反向传播的逻辑。

我们可以通过继承torch.autograd.Function来创建这种支持反向传播的Function

class MyReLU(torch.autograd.Function):

    # 正向传播逻辑,可以用ctx存储一些值,供反向传播使用。
    @staticmethod
    def forward(ctx, input):
        ctx.save_for_backward(input)
        return input.clamp(min=0)

    # 反向传播逻辑
    @staticmethod
    def backward(ctx, grad_output):
        input, = ctx.saved_tensors
        grad_input = grad_output.clone()
        grad_input[input < 0] = 0
        return grad_input
import torch 
w = torch.tensor([[3.0,1.0]],requires_grad=True)
b = torch.tensor([[3.0]],requires_grad=True)
X = torch.tensor([[-1.0,-1.0],[1.0,1.0]])
Y = torch.tensor([[2.0,3.0]])

relu = MyReLU.apply # relu现在也可以具有正向传播和反向传播功能
Y_hat = relu(X@w.t() + b)
loss = torch.mean(torch.pow(Y_hat-Y,2))

loss.backward()

print(w.grad)
print(b.grad)

输出:

tensor([[4.5000, 4.5000]])
tensor([[4.5000]])

打印梯度函数:

# Y_hat的梯度函数即是我们自己所定义的 MyReLU.backward

print(Y_hat.grad_fn)

输出:

<torch.autograd.function.MyReLUBackward object at 0x0000029C32DF3900>

四、🎉计算图与反向传播

了解了Function的功能,我们可以简单地理解一下反向传播的原理和过程。理解该部分原理需要一些高等数学求导链式法则的基础知识。

import torch 

x = torch.tensor(3.0,requires_grad=True)
y1 = x + 1
y2 = 2*x
loss = (y1-y2)**2

loss.backward()
  • ==注意==:

loss.backward()语句调用后,依次发生以下计算过程。

  1. loss自己的grad梯度赋值为1,即对自身的梯度为1。

  2. loss根据其自身梯度以及关联的backward方法,计算出其对应的自变量即y1y2梯度,将该值赋值到y1.grady2.grad

  3. y2y1根据其自身梯度以及关联的backward方法, 分别计算出其对应的自变量x的梯度,x.grad将其收到的多个梯度值累加。

(注意,1,2,3步骤的求梯度顺序和对多个梯度值的累加规则恰好是求导链式法则的程序表述)

正因为求导链式法则衍生的梯度累加规则,张量的grad梯度不会自动清零,在需要的时候需要手动置零。

对于学过高数的,链式求导法则应该不难理解吧!~

五、🎉叶子节点和非叶子节点

执行下面代码,我们会发现 loss.grad并不是我们期望的1,而是 None

类似地y1.grad以及y2.grad也是 None.

这是为什么呢?这是由于它们不是叶子节点张量。

在反向传播过程中,只有 is_leaf=True的叶子节点,需要求导的张量的导数结果才会被最后保留下来。

那么什么是叶子节点张量呢?叶子节点张量需要满足两个条件。

  1. 叶子节点张量是由用户直接创建的张量,而非由某个Function通过计算得到的张量。

  2. 叶子节点张量的 requires_grad属性必须为True.

Pytorch设计这样的规则主要是为了节约内存或者显存空间,因为几乎所有的时候,用户只会关心他自己直接创建的张量的梯度。

所有依赖于叶子节点张量的张量, 其requires_grad 属性必定是True的,但其梯度值只在计算过程中被用到,不会最终存储到grad属性中。

如果需要保留中间计算结果的梯度到grad属性中,可以使用 retain_grad方法。
如果仅仅是为了调试代码查看梯度值,可以利用register_hook打印日志

import torch 

x = torch.tensor(3.0,requires_grad=True)
y1 = x + 1
y2 = 2*x
loss = (y1-y2)**2

loss.backward()
print("loss.grad:", loss.grad)
print("y1.grad:", y1.grad)
print("y2.grad:", y2.grad)`在这里插入代码片`
print(x.grad)

输出:

loss.grad: None
y1.grad: None
y2.grad: None
tensor(4.)
print(x.is_leaf)
print(y1.is_leaf)
print(y2.is_leaf)
print(loss.is_leaf)
True
False
False
False

利用retain_grad可以保留非叶子节点的梯度值,利用register_hook可以查看非叶子节点的梯度值。

import torch 

#正向传播
x = torch.tensor(3.0,requires_grad=True)
y1 = x + 1
y2 = 2*x
loss = (y1-y2)**2

#非叶子节点梯度显示控制
y1.register_hook(lambda grad: print('y1 grad: ', grad))
y2.register_hook(lambda grad: print('y2 grad: ', grad))
loss.retain_grad()

#反向传播
loss.backward()
print("loss.grad:", loss.grad)
print("x.grad:", x.grad)

输出:

y2 grad:  tensor(4.)
y1 grad:  tensor(-4.)
loss.grad: tensor(1.)
x.grad: tensor(4.)

五、🎉计算图在TensorBoard中的可视化

可以利用 torch.utils.tensorboard 将计算图导出到 TensorBoard进行可视化

from torch import nn 
class Net(nn.Module):
    def __init__(self):
        super(Net, self).__init__()
        self.w = nn.Parameter(torch.randn(2,1))
        self.b = nn.Parameter(torch.zeros(1,1))

    def forward(self, x):
        y = x@self.w + self.b
        return y

net = Net()
from torch.utils.tensorboard import SummaryWriter
writer = SummaryWriter('./data/tensorboard')
writer.add_graph(net,input_to_model = torch.rand(10,2))
writer.close()
%load_ext tensorboard
#%tensorboard --logdir ./data/tensorboard
from tensorboard import notebook
notebook.list() 
#在tensorboard中查看模型
notebook.start("--logdir ./data/tensorboard")

在这里插入图片描述

🤗往期纪实

Date 《20天掌握Pytorch实战》
Day01 【进阶篇】全流程学习《20天掌握Pytorch实战》纪实 | Day01 | 结构化数据建模流程范例
Day02 【进阶篇】全流程学习《20天掌握Pytorch实战》纪实| Day02 | 图片数据建模流程范例
Day03 【进阶篇】全流程学习《20天掌握Pytorch实战》纪实 | Day03 | 文本数据建模流程范例
Day04 【进阶篇】全流程学习《20天掌握Pytorch实战》纪实 | Day04 | 时间序列建模流程范例
Day05 【进阶篇】全流程学习《20天掌握Pytorch实战》纪实 | Day05 | 张量数据结构
Day06 【进阶篇】全流程学习《20天掌握Pytorch实战》纪实 | Day06 | 自动微分机制

🥇总结

本期介绍了动态计算图,Function的使用,反向传播,子节点和非叶子节点以及可视化,还是比较基础的。

本文示例主要解释了张量数据结构的基本操作。对于0基础的同学来说可能还是稍有难度,因此,本文中给出了大部分使用到的库的解释,同时给出了部分代码的注释,以便小伙伴的理解,仅供参考,如有错误,请留言指出,最后一句:开源万岁~

同时为原作者打Call

如果本书对你有所帮助,想鼓励一下作者,记得给本项目加一颗星星star⭐️,并分享给你的朋友们喔😊!

地址在这里哦:https://github.com/lyhue1991/eat_pytorch_in_20_days

😊Reference

书籍源码在此:
链接:https://pan.baidu.com/s/1P3WRVTYMpv1DUiK-y9FG3A
提取码:yyds

【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。