三分钟教你如何PyTorch自定义反向传播

简介: 三分钟教你如何PyTorch自定义反向传播

在前面两篇教程中,我们详细讲解了如何编写cuda算子,并用PyTorch进行调用,并且详细讲述了三种编译cuda算子的方式,具体可以看前面两篇:

PyTorch自定义CUDA算子教程与运行时间分析

详解PyTorch编译并调用自定义CUDA算子的三种方式

本文我们将讲解如何用自定义cuda算子搭建一个简单的神经网络,并实现反向传播,进行模型训练。

完整的代码还是放在了github仓库,欢迎大家star并fork:

https://github.com/godweiyang/torch-cuda-example

本文主要涉及到的是train.py这个代码,功能是搭建了一个PyTorch模型,并且调用了自定义的cuda算子,实现了自定义的反向传播函数,最终完成训练。

模型描述

之前我们实现了一个的tensor求和cuda算子,于是我们可以利用它来实现。

最终训练收敛后和都会趋近于0,模型没有输入,只有两个可训练的参数和。

搭建模型

首先我们还是像正常写PyTorch模型那样搭建一个模型,代码如下:

class AddModel(nn.Module):
    def __init__(self, n):
        super(AddModel, self).__init__()
        # tensor长度
        self.n = n
        # 定义可训练参数a和b
        self.a = nn.Parameter(torch.Tensor(self.n))
        self.b = nn.Parameter(torch.Tensor(self.n))
        # 正态分布初始化参数a和b
        self.a.data.normal_(mean=0.0, std=1.0)
        self.b.data.normal_(mean=0.0, std=1.0)
    def forward(self):
        # 求a^2与b^2
        a2 = torch.square(self.a)
        b2 = torch.square(self.b)
        # 调用自定义cuda算子对两个平方数求和
        c = AddModelFunction.apply(a2, b2, self.n)
        return c

重点就在调用自定义cuda算子那一行AddModelFunction.apply(),你也可以写成c = a2 + b2。不过这里我们为了演示如何使用自定义cuda算子,所以不这么干了。

实现自定义cuda算子前向和反向传播

下面就是如何实现AddModelFunction.apply()函数了,我们先来看一下具体代码:

class AddModelFunction(Function):
    @staticmethod
    def forward(ctx, a, b, n):
        c = torch.empty(n).to(device="cuda:0")
        if args.compiler == 'jit':
            cuda_module.torch_launch_add2(c, a, b, n)
        elif args.compiler == 'setup':
            add2.torch_launch_add2(c, a, b, n)
        elif args.compiler == 'cmake':
            torch.ops.add2.torch_launch_add2(c, a, b, n)
        else:
            raise Exception("Type of cuda compiler must be one of jit/setup/cmake.")
        return c
    @staticmethod
    def backward(ctx, grad_output):
        return (grad_output, grad_output, None)

这个类继承的是torch.autograd.Function类,我们可以用它来实现一下无法自动求导的操作,比如arxmax这种不可导的函数。

我们需要实现两个函数,forwardbackward,分别用来前向和反向传播,注意都得声明成静态函数。

前向传播接收多个参数,第一个固定为ctx,用来存储反向传播中可能会用到的一些上下文,比如input和一些前向过程中的中间变量等等,其他参数随你定。然后我们根据上一教程中调用cuda算子的方法计算得到求和结果,进行返回。

反向传播接收两个参数,第一个同样是ctx,里面存着前向过程中保存的一些上下文变量信息。第二个是grad_output,也就是最终的损失函数对前向传播的返回值求导的结果。在我们这里的模型中,令

那么自定义cuda算子实现的就是这一步,而grad_output就是。我们自定义的cuda算子反向传播的导数就是和,然后根据链式求导法则就可以得到损失函数对每个参数的导数了。

反向传播返回值表示损失函数对前向传播每一个参数的梯度,所以个数必须等于前向传播除了ctx以外的其他参数个数,并且顺序也要一一对应。因为,所以返回值就是grad_outputgrad_outputNone,因为对常数不需要求导,所以直接返回空即可。

训练流程

最终训练流程和平常一样:

# 定义模型
model = AddModel(n)
# 将模型中所有参数拷贝到GPU端
model.to(device="cuda:0")
# 定义优化器
opt = torch.optim.SGD(model.parameters(), lr=0.01)
for epoch in range(500):
    # 清空优化器缓存
    opt.zero_grad()
    # 前向传播
    output = model()
    # 求loss
    loss = output.sum()
    # 反向传播
    loss.backward()
    # 更新参数
    opt.step()
    if epoch % 25 == 0:
        print("epoch {:>3d}: loss = {:>8.3f}".format(epoch, loss))

最终损失函数降到了0,log信息如下:

Loading extension module add2...
Initializing model...
Initializing optimizer...
Begin training...
epoch   0: loss = 1996.658
epoch  25: loss =  727.122
epoch  50: loss =  264.796
epoch  75: loss =   96.431
epoch 100: loss =   35.117
epoch 125: loss =   12.789
epoch 150: loss =    4.657
epoch 175: loss =    1.696
epoch 200: loss =    0.618
epoch 225: loss =    0.225
epoch 250: loss =    0.082
epoch 275: loss =    0.030
epoch 300: loss =    0.011
epoch 325: loss =    0.004
epoch 350: loss =    0.001
epoch 375: loss =    0.001
epoch 400: loss =    0.000
epoch 425: loss =    0.000
epoch 450: loss =    0.000
epoch 475: loss =    0.000

小结

这三个教程暂时告一段落了,通过这些简单的例子,应该大致能学会如何自己写cuda算子,并且用PyTorch调用,完成模型训练了。

更复杂的模型其实基本的原理都是类似的,我不喜欢上来就讲解很复杂的大项目源码,我喜欢抽象出一个最简的example,这样更容易理解底层的原理,而不会被很多冗余的代码干扰。

相关文章
|
机器学习/深度学习 数据采集 PyTorch
使用自定义 PyTorch 运算符优化深度学习数据输入管道
使用自定义 PyTorch 运算符优化深度学习数据输入管道
71 0
|
2月前
|
机器学习/深度学习 存储 PyTorch
PyTorch自定义学习率调度器实现指南
本文将详细介绍如何通过扩展PyTorch的 ``` LRScheduler ``` 类来实现一个具有预热阶段的余弦衰减调度器。我们将分五个关键步骤来完成这个过程。
92 2
|
2月前
|
并行计算 PyTorch 算法框架/工具
基于CUDA12.1+CUDNN8.9+PYTORCH2.3.1,实现自定义数据集训练
文章介绍了如何在CUDA 12.1、CUDNN 8.9和PyTorch 2.3.1环境下实现自定义数据集的训练,包括环境配置、预览结果和核心步骤,以及遇到问题的解决方法和参考链接。
113 4
基于CUDA12.1+CUDNN8.9+PYTORCH2.3.1,实现自定义数据集训练
|
2月前
|
机器学习/深度学习
小土堆-pytorch-神经网络-损失函数与反向传播_笔记
在使用损失函数时,关键在于匹配输入和输出形状。例如,在L1Loss中,输入形状中的N代表批量大小。以下是具体示例:对于相同形状的输入和目标张量,L1Loss默认计算差值并求平均;此外,均方误差(MSE)也是常用损失函数。实战中,损失函数用于计算模型输出与真实标签间的差距,并通过反向传播更新模型参数。
|
3月前
|
机器学习/深度学习 PyTorch 算法框架/工具
PyTorch中的自动微分机制:深入理解反向传播
【8月更文第27天】PyTorch 是一个强大的机器学习框架,它因其灵活性和易用性而受到广泛欢迎。其中一个关键特性就是它的自动微分机制,这个机制使得 PyTorch 能够自动计算任何张量操作的梯度,这对于训练深度学习模型至关重要。本文将详细介绍 PyTorch 中自动微分机制的工作原理,并通过具体的代码示例来展示如何使用这一机制来实现反向传播。
199 1
|
5月前
|
机器学习/深度学习 PyTorch 算法框架/工具
【从零开始学习深度学习】18. Pytorch中自定义层的几种方法:nn.Module、ParameterList和ParameterDict
【从零开始学习深度学习】18. Pytorch中自定义层的几种方法:nn.Module、ParameterList和ParameterDict
|
数据可视化 PyTorch 算法框架/工具
量化自定义PyTorch模型入门教程
在以前Pytorch只有一种量化的方法,叫做“eager mode qunatization”,在量化我们自定定义模型时经常会产生奇怪的错误,并且很难解决。但是最近,PyTorch发布了一种称为“fx-graph-mode-qunatization”的方方法。在本文中我们将研究这个fx-graph-mode-qunatization”看看它能不能让我们的量化操作更容易,更稳定。
242 0
|
6月前
|
机器学习/深度学习 算法 PyTorch
深入理解PyTorch自动微分:反向传播原理与实现
【4月更文挑战第17天】本文深入解析PyTorch的自动微分机制,重点讨论反向传播的原理和实现。反向传播利用链式法则计算神经网络的梯度,包括前向传播、梯度计算、反向传播及参数更新。PyTorch通过`autograd`模块实现自动微分,使用`Tensor`和计算图记录操作历史以自动计算梯度。通过示例展示了如何在PyTorch中创建张量、定义计算过程及求梯度。掌握这些有助于提升深度学习模型的训练效率。
|
6月前
|
机器学习/深度学习 PyTorch 算法框架/工具
PyTorch中的自定义层与模块开发
【4月更文挑战第18天】PyTorch教程:通过继承`nn.Module`创建自定义层和模块。自定义层需实现`__init__`(初始化参数)和`forward`(前向传播逻辑)方法。示例代码展示了一个简单的`CustomLinear`层和包含该层及ReLU激活的`CustomModule`。自定义模块可组合多个层,如`SimpleNet`模型中使用两个`CustomModule`。这使得构建满足特定需求的复杂神经网络成为可能。
|
6月前
|
机器学习/深度学习 自然语言处理 算法
PyTorch实例:简单线性回归的训练和反向传播解析
PyTorch实例:简单线性回归的训练和反向传播解析
PyTorch实例:简单线性回归的训练和反向传播解析

热门文章

最新文章