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

自定义算子

对于输入 x ,其输出为

利用C++实现以上算子,总共只要实现两个文件:

setup.py

利用python中提供的setuptools模块完成事先编译流程,将写有算子的C++文件,编译成为一个动态链接库(在Linux平台是一个.so后缀文件),可以让python调用其中实现的函数功能。需要setup.py编写如下

from setuptools import setup
from torch.utils import cpp_extension

setup(
    name='ncrelu_cpp',						# 编译后的链接库名称
    ext_modules=[
        cpp_extension.CppExtension(
            'ncrelu_cpp', ['ncrelu.cpp']		       # 待编译文件,及编译函数
        )
    ],
    cmdclass={						       # 执行编译命令设置
        'build_ext': cpp_extension.BuildExtension
    }
)

这里Pytorch提供了一个封装cpp_extension,方便编译过程中所需要的设置选项,以及所需包含的头文件位置路径设置等等。

ncrelu.cpp

接下来便是C++实现的函数ncrelu.cpp。首先上代码

#include <torch/extension.h>					// 头文件引用部分 

torch::Tensor ncrelu_forward(torch::Tensor input) {
    auto pos = input.clamp_min(0);				       // 具体实现部分
    auto neg = input.clamp_max(0);
    return torch::cat({pos, neg}, 1);
}

PYBIND11_MODULE(TORCH_EXTENSION_NAME, m) {	// 绑定部分
    m.def("forward", &ncrelu_forward, "NCReLU forward");
}

以上代码包含了三个部分。分别是

头文件引用部分:这里包含了torch/extension.h头文件,是编写Pytorch的C++扩展时必须包含的一个文件。它基本上囊括了实现中所需要的所有依赖,包含了ATen库pybind11和二者之间的交互。其中ATen是Pytorch底层张量运算库,负责实现具体张量操作运算;pybind11是实现C++代码到python的绑定(binding),可以在python里调用C++函数。

具体实现部分:函数返回类型和传递参数类型均是torch::Tensor类,这种对象不仅包含了数据,还附属了诸多运算操作。因此我们可以看到在下面实现方式类似于python中使用Pytorch张量运算操作一样,可以直接调用如截取操作clamp和拼接操作cat等,非常简洁已读且方便。

绑定部分:只需要在m.def中传入参数,分别是绑定到python的函数名称,需绑定的C++函数引用,以及一个简短的函数说明字符串,用来添加到python函数中的__doc__成员名称中。

将以上两个文件放在同一文件夹下,然后进行编译。使用python setup.py build_ext --inplace命令,如果一切正常将会在文件夹下产生类似ncrelu_cpp.cpython-35m-x86_64-linux-gnu.so动态链接文件。然后我们可以启动python检测是否可以导入其中的函数

>> import torch
>> import ncrelu_cpp
>> a = torch.randn(4, 3)
>> a
tensor([[ 0.5921,  0.3207,  0.7690],
        [ 1.4514, -0.8942,  0.9039],
        [-0.3262, -0.1610,  0.6137],
        [ 0.7824, -1.8527,  0.2844]])
>> b = ncrelu_cpp.forward(a)
>> b
tensor([[ 0.5921,  0.3207,  0.7690,  0.0000,  0.0000,  0.0000],
        [ 1.4514,  0.0000,  0.9039,  0.0000, -0.8942,  0.0000],
        [ 0.0000,  0.0000,  0.6137, -0.3262, -0.1610,  0.0000],
        [ 0.7824,  0.0000,  0.2844,  0.0000, -1.8527,  0.0000]])

 

  • 3
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
发布自定义C++算子,需要完成以下步骤: 1. 编写C++代码:编写自定义算子C++代码,包括前向计算和反向传播函数等。 2. 编写Python绑定代码:编写Python绑定代码,将C++算子封装为PyTorch模块,方便在Python中使用。 3. 编译C++代码:使用CMake或者Makefile等工具,将C++代码编译成共享库(.so或.dll文件)。 4. 使用PyTorch C++扩展API:使用PyTorch C++扩展API,将共享库加载到PyTorch中,并注册自定义算子。 5. 测试自定义算子:在Python中测试自定义算子是否能够正常工作。 这里提供一个简单的示例: 1. 编写C++代码 ```cpp #include <torch/extension.h> torch::Tensor my_add_forward(const torch::Tensor& input1, const torch::Tensor& input2) { return input1 + input2; } std::vector<torch::Tensor> my_add_backward(const torch::Tensor& grad_output) { return {grad_output, grad_output}; } PYBIND11_MODULE(TORCH_EXTENSION_NAME, m) { m.def("forward", &my_add_forward, "MyAdd forward"); m.def("backward", &my_add_backward, "MyAdd backward"); } ``` 2. 编写Python绑定代码 ```python import torch my_add = torch.utils.cpp_extension.load(name='my_add', sources=['my_add.cpp'], verbose=True) def my_add_op(input1, input2): return my_add.forward(input1, input2) class MyAddFunction(torch.autograd.Function): @staticmethod def forward(ctx, input1, input2): output = my_add_op(input1, input2) ctx.save_for_backward(input1, input2) return output @staticmethod def backward(ctx, grad_output): input1, input2 = ctx.saved_tensors grad_input = my_add.backward(grad_output) return grad_input[0], grad_input[1] my_add_function = MyAddFunction.apply ``` 3. 编译C++代码 使用以下命令编译C++代码: ```sh g++ -o my_add.so -shared -fPIC my_add.cpp $(python3 -m pybind11 --includes) -I/path/to/torch/include -I/path/to/torch/include/torch/csrc/api/include -L/path/to/torch/lib -ltorch -lc10 ``` 4. 使用PyTorch C++扩展API ```cpp #include <torch/script.h> #include <iostream> int main() { torch::jit::script::Module module = torch::jit::load("model.pt"); module.to(torch::kCPU); std::string code = R"( def forward(x, y): return my_add_function(x, y) )"; torch::jit::script::Module new_module = module.clone(); new_module.define(code); // Test the new module torch::Tensor x = torch::ones({2, 3}); torch::Tensor y = torch::ones({2, 3}); torch::Tensor output = new_module.forward({x, y}).toTensor(); std::cout << output << std::endl; return 0; } ``` 5. 测试自定义算子Python中测试自定义算子: ```python import torch my_add = torch.utils.cpp_extension.load(name='my_add', sources=['my_add.cpp'], verbose=True) def my_add_op(input1, input2): return my_add.forward(input1, input2) class MyAddFunction(torch.autograd.Function): @staticmethod def forward(ctx, input1, input2): output = my_add_op(input1, input2) ctx.save_for_backward(input1, input2) return output @staticmethod def backward(ctx, grad_output): input1, input2 = ctx.saved_tensors grad_input = my_add.backward(grad_output) return grad_input[0], grad_input[1] my_add_function = MyAddFunction.apply # Test the custom operator x = torch.ones(2, 3, requires_grad=True) y = torch.ones(2, 3, requires_grad=True) z = my_add_function(x, y) z.sum().backward() print(x.grad) # tensor([[1., 1., 1.],[1., 1., 1.]]) print(y.grad) # tensor([[1., 1., 1.],[1., 1., 1.]]) ```

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值