[CUDA] 手写一个PyTorch的算子

news/2025/2/9 0:24:56/文章来源:https://www.cnblogs.com/CQzhangyu/p/18705629

[CUDA] 手写一个PyTorch的算子

(其实是本人之前上过的分布式机器学习课程的一个作业,这里简单记录一下)

我们都知道,PyTorch里的算子是跑在GPU上的。虽然最外层的接口是python,最内部的实现其实是CUDA。那么,一个python代码是如何一步步的调用内层的CUDA代码的呢?这里用一个简单的例子来讲解一下:

自定义nn.Module

我们想要实现一个自定义的LayerNorm算子,其前向传播公式如下

\[y=\frac {x-\mathrm{E}[x]} {\sqrt{\mathrm{Var}[x]+\epsilon}} \]

PyTorch的官方LayerNorm算子的接口可以参考https://pytorch.org/docs/1.5.0/nn.html#layernorm。

为了简便,这里只考虑normlized_shape为最后一维 dim size 且elementwize_affine=False的情况。

OK,我们按照官方的接口,自己写一个myLayerNorm类,继承nn.Module

class myLayerNorm(nn.Module):__constants__ = ['normalized_shape', 'eps', 'elementwise_affine']def __init__(self, normalized_shape, eps=1e-5, elementwise_affine=False):super(myLayerNorm, self).__init__()if isinstance(normalized_shape, numbers.Integral):normalized_shape = (normalized_shape,)self.normalized_shape = tuple(normalized_shape)self.eps = epsself.elementwise_affine = elementwise_affineif self.elementwise_affine:print("Do not support elementwise_affine=True")exit(1)else:# 注册需要进行更新的参数self.register_parameter('weight', None)self.register_parameter('bias', None)def forward(self, input):return myLayerNormFunction.apply(input, self.normalized_shape, self.eps)

另外定义myLayerNormFunction函数,执行具体的前向/反向传播

class myLayerNormFunction(torch.autograd.Function):@staticmethoddef forward(ctx, input, normalized_shape, eps):# 将输入存下来供反向传播使用ctx.save_for_backward(input)ctx.normalized_shape = normalized_shapectx.eps = eps# 调用外部的cuda方法output = mylayer_cuda.forward(input, *normalized_shape, eps)return output[0]

Python调用C++&CUDA

下一步,我们要实现从python调用c++&cuda的函数。具体做法是通过setuptools。我们编写一个setup.py脚本,内容如下:

from setuptools import setup
from torch.utils.cpp_extension import BuildExtension, CUDAExtensionsetup(// 要创建的python类name='mylayer_cuda',ext_modules=[CUDAExtension('mylayer_cuda', ['mylayer_cuda.cpp','mylayer_cuda_kernel.cu',])],cmdclass={'build_ext': BuildExtension})

其中,mylayer_cuda是我们要创建的一个python类,它的具体实现在mylayer_cuda.cppmylayer_cuda_kernel.cu中。其中的CUDAExtensionBuildExtension是pytorch为我们提供好的两个拓展,专门用于编译CUDA与PyTorch相关的库。

另一边,我们需要在mylayer_cuda.cpp中,提供一套用于Python访问的接口,代码大致如下:

// 调用pytorch的C++拓展库
#include <torch/extension.h>// 函数的返回值是若干个Tensor的tuple
std::vector<torch::Tensor> mylayer_forward(torch::Tensor input,int normalized_shape,float eps) 
{// 具体的CUDA实现return mylayer_cuda_forward(input, normalized_shape, eps);
}// 对应前面python代码中的mylayer_cuda.forward(...)
PYBIND11_MODULE(TORCH_EXTENSION_NAME, m) {m.def("forward", &mylayer_forward, "myLayerNorm forward (CUDA)");
}

编写CUDA代码

最后,我们只需要用CUDA写出myLayerNorm的具体实现即可,然后在C++中调用它。

编写mylayer_cuda_kernel.cu代码:

#include <torch/extension.h>#include <cuda.h>
#include <cuda_runtime.h>// 具体的kernel函数
template <typename scalar_t>
__global__ void layer_forward_kernel(scalar_t* A,scalar_t eps,const int M, const int N) 
{const int row = blockIdx.x * blockDim.x + threadIdx.x;if (row >= M) {return ;}scalar_t l1_sum = 0;scalar_t l2_sum = 0;for (int i = row * N; i < (row + 1) * N; i ++) {l1_sum += A[i];l2_sum += A[i] * A[i];}scalar_t avg = l1_sum / N;scalar_t var = (l2_sum / N - avg * avg);scalar_t mul = 1.0 / sqrtf(var + eps);scalar_t add = - avg * mul;for (int i = row * N; i < (row + 1) * N; i ++) {A[i] = A[i] * mul + add;}
}std::vector<torch::Tensor> mylayer_cuda_forward(torch::Tensor input,int normalized_shape,float eps)
{const int N = input.size(-1);const int M = input.numel() / N;const int block = 256;const int grid = (M - 1) / 256 + 1;// AT_DISPATCH_FLOATING_TYPES是PyTorch提供的工具// 它可以根据输入的数据类型,调度相应的CUDA内核AT_DISPATCH_FLOATING_TYPES(input.type(), "mylayer_cuda_forward", ([&] {// 调用kernel函数layer_forward_kernel<scalar_t><<<grid, block>>>(input.data<scalar_t>(),(scalar_t)eps,M,N);}));return {input};
}

编译

我们使用setup.py来编译我们刚写的C++和CUDA文件,将其作为一个Python库。

python setup.py install --user

最后,只需要在前面写的myLayerNormimport mylayer_cuda即可。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/881049.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

《Operating System Concepts》阅读笔记:p2-p8

《Operating System Concepts》学习第 2 天,p2-p8 总结,总计 7 页。 一、技术总结 1.operating system An operating system is software that manages a computer’s hardware。 2.system bus data bus, address bus, control bus 统称为 system bus。 二、英语总结(生词:…

踩坑---中断中调用系统定时器延时卡死

踩坑---中断中调用系统定时器延时卡死 背景 ​ 配置外部中断作为按键输入时,调用了系统滴答定时器为基准的延时。然后每次一按按键,单片机就卡死。一开始怀疑时中断没有配置好。反复研究中断配置是否出现错误,最后debug出来,发现卡在了// 3. 等待计数值变为0,判断CTRL标志…

windows 10 安装 wsl

在 windows 上安装 Debian 版本的 wsl以管理员身份运行 cmd,执行 wsl --help 可查看 wsl 的帮助信息。执行 wsl --list --online 查看可供安装的 wsl子系统 版本。执行 wsl --install --distribution Debian 安装 debian 版本的 wsl有了计划记得推动,不要原地踏步。

开学作业13

学习使用了vue中watch知识 可以实时更新数据 也非常好用

开学作业14

前几天也是这样通过打电话合作 前后端分着写 已经完成大部分前后端的交并 预计明天写完

做开学作业10

使用的是element ui vue2 springboot技术

手把手教你如何用飞书实现betterGI消息推送

在平时,我们一般会使用betterGI的一条龙系统来完成体力的刷取,有的人可能需要知道啥时候刷完体力方便远程关闭电脑啥的,这里给大家如何通过飞书实现BetterGI消息推送的方法 1、支持的事件提醒 事件列表 notify.test : 测试通知 domain.reward : 自动秘境奖励 domain.start :…

2025【重庆联通】活动

2025年2月8日更新 扫码显示详情及办理 扫码显示详情及办理 套餐资费和活动内容均来源于重庆联通【10010人工客服可查,中国联通app官方客服可查】 这里是下面的54个活动的办理名称,请确认【点击也可直接跳转到相应位置】1.云创安全组合包10元(CQ)-立即生效 2.云创数字人名片权…

LLVM+CMAKE+VScode

在mac上使用vscode+CMAKE+LLVM 配置C++环境 仅供参考,请多谅解 原先的vscode官方推荐插件intellisense实在是太慢,以至于在很多大型项目(ns3,OS)等进行静态检查的速度奇慢无比,并且大量消耗资源。于是尝试在mac上使用llvm+clangd进行配置。由于采用的是homebrew安装clang…

FastAPI for Machine Learning: Live coding an ML web application

FastAPI for Machine Learning: Live coding an ML web application https://www.bilibili.com/video/BV1kC411b7Se/?spm_id_from=333.788.videopod.sections&vd_source=57e261300f39bf692de396b55bf8c41b翻译:FastAPI用于机器学习:现场编码一个ML Web应用程序。欢迎!加…

08_LaTeX之自定义LaTeX命令和功能

本章的内容将让你能编写可重复利用的模块——宏包和文档类,并在其中自己定义命令和环境。08_\(\LaTeX{}\) 之自定义\(\LaTeX{}\)命令和功能 目录08_\(\LaTeX{}\) 之自定义\(\LaTeX{}\)命令和功能自定义命令和环境定义新命令定义环境xparse 宏包简介编写自己的宏包和文档类编写…

2024FJ省队集训 - 笔记 游记

Day 0 火车上写了两道可爱小清新数学题。题没写多少bug还一堆。 我们住的是福建省团校,据说是福州有演唱会导致各种酒店房间紧张。 和 wzh,zzp 口胡了一些题目就去睡觉了。 团校的住宿条件确实不错,睡得挺香。 Day 1 T1 提交答案题就是依托美味的构式,你T2T3费劲心思骗个五分…