pytorch自定义算子 native_functions.yaml

在pytorch的文件夹中搜索native_functions.yaml,可以看到,所有pytorch原生的函数都是在这里注册的

在同文件夹下,存在一个README.md,这是官方教程,教我们怎么用native_functions.yaml注册自己自定义的算子

下面我仿照pytorch原生的relu算子,写一个自己的myrelu

首先,找到native_functions.yaml,有几个函数是我们可以参考的

- func: relu(Tensor self) -> Tensoruse_c10_dispatcher: fullvariants: function, methoddispatch:CPU, CUDA: reluMkldnnCPU: mkldnn_reluQuantizedCPU: relu_quantized_cpu

这里除了dispatch其他可以照搬。dispatch主要是针对不同的backend调用不同的kernel,比如GPU上的调用CUDA的kernel。这里展示的是CPU的,在native_functions.yaml加上

- func: myrelu(Tensor self) -> Tensoruse_c10_dispatcher: fullvariants: function, methoddispatch:CPU: myrelu

接下来,找到原生relu的实现,在pytorch/aten/src/ATen/native/Activation.cpp里面
relu的实现调用了同文件下的threshold,threshold的功能是实现 x > thresh ? x : result
模仿写这个调用太复杂了,而且如果在c++段调用函数的话,真正执行的时候每次调用函数都会经过dispatch,会浪费挺多时间的。因此,我参考另外一个激活函数写自己的myrelu

Tensor hardsigmoid(const Tensor& self) {Tensor result;auto iter = TensorIterator::unary_op(result, self);hardsigmoid_stub(iter.device_type(), iter);return iter.output();
}

hardsigmoid是用直线去近似sigmoid函数,它在这个文件中的实现和我们对relu的实现需求很像。这里大致做的事情是,创建一个空的返回对象,然后创建一个TensorIterator,对象类型是单操作数,然后调用hardsigmoid_stub进行具体的实现,最后返回。
在刚刚的activation.cpp中加入

Tensor myrelu(const Tensor& self) {Tensor result;auto iter = TensorIterator::unary_op(result, self);myrelu_stub(iter.device_type(), iter);return iter.output();
}

接下来,我们要知道还需要加上什么使得myrelu_stub也能像hardsigmoid_stub一样可以用。搜索hardsigmoid_stub,发现文件中还存在一个地方

DEFINE_DISPATCH(hardsigmoid_stub);

这段根据字面意思是说,这是一个分发的对象。
加上

DEFINE_DISPATCH(myrelu_stub);

理论上,activation.h也要看一下,activation.cpp和.h是一个整体
在h文件中存在

using hardsigmoid_fn = void(*)(TensorIterator&);
DECLARE_DISPATCH(hardsigmoid_fn, hardsigmoid_stub);

仿照,在对应的地方分别加上

using myrelu_fn = void(*)(TensorIterator&);
DECLARE_DISPACTH(myrelu_fn, myrelu_stub);

这样感觉算是告一段落,那么问题来了,myrelu_stub应该在哪里实现呢
在刚才activation.cpp的文件夹下,还有一个叫做cpu的文件夹,里面存在一个README.md,里面的说明是如何注册一个CPU的算子
在native/cpu/activation.cpp中搜索hard_sigmoid_stub

REGISTER_DISPATCH(hardsigmoid_stub, &hardsigmoid_kernel);

仿照

REGISTER_DISPATCH(myrelu_stub, &myrelu_kernel);

发现了一个新的kernel,叫做hardsigmoid_kernel
搜索hardsigmoid_kernel

void hardsigmoid_kernel(TensorIterator& iter) {AT_DISPATCH_FLOATING_TYPES(iter.dtype(), "hardsigmoid_cpu", [&] {const scalar_t zero(0.0f);const scalar_t three(3.0f);const scalar_t six(6.0f);using Vec = vec256::Vec256<scalar_t>;const Vec kZeroVec(zero);const Vec kThreeVec(three);const Vec kSixVec(six);cpu_kernel_vec(iter,[&](scalar_t self_val) {return std::min(std::max(self_val + three, zero), six) / six;},[&](Vec self_val) {return vec256::minimum(vec256::maximum(self_val + kThreeVec, kZeroVec),kSixVec) / kSixVec;});});
}

仿照写自己的kernel,这用到了vec256指令集,不清楚具体的语法,仿照着用就行
,具体的用法可以看一下relu真实的kernel threshold_kernel的写法

static void threshold_kernel(TensorIterator& iter,Scalar threshold_scalar,Scalar value_scalar) {AT_DISPATCH_ALL_TYPES(iter.dtype(), "threshold_cpu", [&] {using Vec = Vec256<scalar_t>;scalar_t threshold = threshold_scalar.to<scalar_t>();Vec threshold_v = Vec(threshold);scalar_t value = value_scalar.to<scalar_t>();Vec value_v = Vec(value);cpu_kernel_vec(iter,[&](scalar_t x, scalar_t other) -> scalar_t {return x <= threshold ? value : other;},[&](Vec x, Vec other) -> Vec {return Vec::blendv(other, value_v, x <= threshold_v);});});
}
void myrelu_kernel(TensorIterator& iter) {AT_DISPATCH_ALL_TYPES(iter.dtype(), "myrelu_cpu", [&] {const scalar_t zero(0);using Vec = vec256::Vec256<scalar_t>;const Vec kZeroVec(zero);cpu_kernel_vec(iter,[&](scalar_t self_val) {return self_val > zero ? self_val : zero;},[&](Vec self_val) {return Vec::blendv(x,kZeroVec,x<=kZeroVec);});});
}

这里第一个lambda比较显然,后面调用了blendv方法,具体的功能按照猜想是,如果满足第三个式子,则取第二个Vec的值,否则就取第一个Vec的值
写到这里自然会疑惑,过程中有许多对于backward的定义,比如hardsigmoid_backward_kernel
这个反向传播函数在哪里注册呢?
实际上最开始的README中有提到,需要向tools/derivatives.yaml注册
仿照

- name: hardsigmoid(Tensor self) -> Tensorself: hardsigmoid_backward(grad, self)
- name: myrelu(Tensor self) -> Tensorself: myrelu_backward(grad, self)

类似得,myrelu_backward方法同样需要再native_functions.yaml和activation.h activation.cpp 以及cpu文件夹下的activation.cpp中补上对应的代码

实现之后就可以在本地编译了,本地编译的准备工作我在其他的博客中有提到。因为我是用macos,而且不希望编译其他的附加的backend,只是测试一下,所以提供如下的变量设置

export CMAKE_PREFIX_PATH=${CONDA_PREFIX:-"$(dirname $(which conda))/../"} DEBUG=1 USE_CUDA=0 USE_CUDNN=0 USE_FBGEMM=0 USE_NUMPY=0 BUILD_TEST=0 USE_MKLDNN=0 USE_NNPACK=0 USE_QNNPACK=0 USE_DISTRIBUTED=0 BUILD_CAFFE2_OPS=0 MACOSX_DEPLOYMENT_TARGET=10.9 CC=clang CXX=clang++python setup.py install

完成之后用下面的python 文件测试一下

import torch
a = torch.randn(5,3,requires_grad=True)
b = torch.randn(5,3,requires_grad=True)
print(a.myrelu())
print(a.relu())
a.relu().backward(b)
print(a.grad)
a.myrelu().backward(b)
print(a.grad/2)

输出:

tensor([[0.0000, 0.0000, 0.0000],[0.0000, 0.9754, 1.0307],[0.0000, 0.0000, 1.4163],[0.0000, 0.0000, 0.0000],[0.3238, 1.4044, 0.0000]], grad_fn=<MyreluBackward>)
tensor([[0.0000, 0.0000, 0.0000],[0.0000, 0.9754, 1.0307],[0.0000, 0.0000, 1.4163],[0.0000, 0.0000, 0.0000],[0.3238, 1.4044, 0.0000]], grad_fn=<ReluBackward0>)tensor([[ 0.0000,  0.0000,  0.0000],[ 0.0000,  0.8448,  0.8919],[ 0.0000,  0.0000, -0.4559],[ 0.0000,  0.0000,  0.0000],[-0.8337, -1.9224,  0.0000]])
tensor([[ 0.0000,  0.0000,  0.0000],[ 0.0000,  0.8448,  0.8919],[ 0.0000,  0.0000, -0.4559],[ 0.0000,  0.0000,  0.0000],[-0.8337, -1.9224,  0.0000]])

可以看出,两者的结果是完全一致的
另外,自定义的反向传播函数也成功地注册了

pytorch自定义算子 native_functions.yaml相关推荐

  1. pytorch之C++实现自定义算子

    自定义算子 对于输入 x ,其输出为 利用C++实现以上算子,总共只要实现两个文件: setup.py 利用python中提供的setuptools模块完成事先编译流程,将写有算子的C++文件,编译成 ...

  2. 这可能是关于Pytorch底层算子扩展最详细的总结了!

    1.前言​ ​一般情况下,pytorch推荐使用python层的前端语言来构建新的算子.因为pytorch在python层的api已经足够丰富,可以构造出很多自定义的算子.但是有时候出于一些其他方面的 ...

  3. pytorch 矩阵相乘_深入浅出PyTorch(算子篇)

    Tensor 自从张量(Tensor)计算这个概念出现后,神经网络的算法就可以看作是一系列的张量计算.所谓的张量,它原本是个数学概念,表示各种向量或者数值之间的关系.PyTorch的张量(torch. ...

  4. 自定义算子高性能开发

    自定义算子高性能开发 在计图中,一共有三种方法来开发自定义的算子: 使用元算子进行组合. 使用Code算子开发自定义算子. 使用计图编译器编译自定义的模块和custom op. 其中,元算子开发是最为 ...

  5. java如何给一个链表定义和传值_如何在CUDA中为Transformer编写一个PyTorch自定义层...

    如今,深度学习模型处于持续的演进中,它们正变得庞大而复杂.研究者们通常通过组合现有的 TensorFlow 或 PyTorch 操作符来发现新的架构.然而,有时候,我们可能需要通过自定义的操作符来实现 ...

  6. Pytorch自定义数据集

    简述 Pytorch自定义数据集方法,应该是用pytorch做算法的最基本的东西. 往往网络上给的demo都是基于torch自带的MNIST的相关类.所以,为了解决使用其他的数据集,在查阅了torch ...

  7. Halcon初学者知识 【11】自定义算子和应用实例

    1 算子的约定 halcon的自定义算子,包含如下格式: 算子名称 (图象变量-输入 : 图象变量-输出: 控制变量-输入 : 控制变量-输出 ) 要点是: 在HALCON的所有算子(系统算子,自定义 ...

  8. Pytorch自定义Dataset和DataLoader去除不存在和空的数据

    Pytorch自定义Dataset和DataLoader去除不存在和空的数据 [源码GitHub地址]:https://github.com/PanJinquan/pytorch-learning-t ...

  9. 使用pytorch自定义DataSet,以加载图像数据集为例,实现一些骚操作

    使用pytorch自定义DataSet,以加载图像数据集为例,实现一些骚操作 总共分为四步 构造一个my_dataset类,继承自torch.utils.data.Dataset 重写__getite ...

最新文章

  1. Android-02:使用SharedPreferences存储简单数据
  2. wxWidgets:wxGenericAboutDialog类用法
  3. [置顶]信息发布系统 Jquery+MVC架构开发(4)Model 层
  4. python学习笔记(15)循环设计
  5. 什么是SFP光模块?光模块知识介绍!
  6. React v16.0正式版发布
  7. YaCy开源搜索引擎的热门技巧
  8. 浅谈_依赖注入 asp.net core
  9. date工具类 DateUtils.java
  10. Ionic2学习笔记
  11. 用angular中的ng-repeat和ng-show来实现tab选项卡
  12. android 动画效果
  13. ORA-01033 ORACLE initialization or shutdow in progress
  14. 项目服务器装系统,项目1服务器系统的安装.ppt
  15. Ubuntu 12.04 设置终端字体为文泉驿
  16. Android PreferenceScreen
  17. ClickHouse 创建数据库建表视图字典 SQL
  18. Mark一下,以提醒自己
  19. 【ASP.NET MVC系列】浅谈Google Chrome浏览器(操作篇)(上)
  20. 样本标准差个除以n-1与总体标准差除以n 数学意义是什么?

热门文章

  1. VS2019MFC进行无预览简单绘图:直线,画笔,画刷,矩形,椭圆
  2. JavaScript+css+html通过点击右边眼睛的开关来控制密码显示还是隐藏
  3. 将word文档导入数据库_如何将用户信息插入Word文档
  4. 明星投资五花八门,未来将呈现何种趋势?
  5. 单片机中断系统(51为例)
  6. Substance Pt材质解析与思路笔记
  7. 边境突围三排套路,你Get到了吗?
  8. 新手编程VS2019出现 error LNK2019 LNK1120 报错
  9. MBSE是什么?有什么用?怎么学习?
  10. pwn_heap(未完待续)