PyTorch卷积の实现(CPU)
参考链接:知乎
搭建项目
项目地址:Github
Windows环境
提前安装:cl.exe(参考:配置cl)
编译
运行python setup.py develop,就能一键编译和安装。如果运行后没有报编译错误,就可以把实现的卷积用起来了
setup.py内容:
cpp_extension.CppExtension:和编译相关的内容- 文件路径
- 头文件搜索路径
name:包的名称cmdclass:指定使用cpp_extension.BuildExtension类来构建C++扩展模块
如下是编译后的目录树
1 | E:. |
测试
运行python test.py
测试函数 test_one 和 test_two,用于测试单通道和多通道的情况
如果没有任何输出(报错信息),就说明卷积实现成功了
1 | def test_one(): |
CPU实现过程
- 头文件:
pytorch_cpp_helper.hpp,pytorch_cuda_helper.hpp,common_cuda_helper.hpp
C++实现
- C++实现加法算子
算子的实现函数和C++接口绑定
1 |
|
卷积的实现
my_conv.cppmy_conv_forward是卷积的主函数
1
2
3
4
5void my_conv_forward(Tensor input, Tensor weight, Tensor bias,
Tensor output, Tensor columns, int kW,
int kH, int dW, int dH, int padW, int padH,
int dilationW, int dilationH, int group,
int im2col_step)先做
im2col操作,再做了矩阵乘法- 使用循环,对每个批次的输入进行 im2col 操作,根据设备调用相应的 im2col 函数。
将处理后的列矩阵进行形状变换,以便后续矩阵乘法操作。
利用矩阵乘法(
addmm_函数)更新输出缓冲区
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32for (int elt = 0; elt < batchSize / im2col_step; elt++)
{
if (isCuda)
{
my_conv_im2col_cuda(input[elt], nInputPlane, inputHeight,
inputWidth, kH, kW, padH, padW, dH, dW, dilationH,
dilationW, im2col_step, columns);
}
else
{
my_conv_im2col_cpu(input[elt], nInputPlane, inputHeight,
inputWidth, kH, kW, padH, padW, dH, dW, dilationH,
dilationW, im2col_step, columns);
}
columns = columns.view({group, columns.size(0) / group, columns.size(1)});
weight = weight.view({group, weight.size(0) / group, weight.size(1),
weight.size(2), weight.size(3)});
for (int g = 0; g < group; g++)
{
output_buffer[elt][g] = output_buffer[elt][g]
.flatten(1)
.addmm_(weight[g].flatten(1), columns[g])
.view_as(output_buffer[elt][g]);
}
columns =
columns.view({columns.size(0) * columns.size(1), columns.size(2)});
weight = weight.view({weight.size(0) * weight.size(1), weight.size(2),
weight.size(3), weight.size(4)});
}- CPU 实现的 im2col (
my_conv_im2col_cpu)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31void my_conv_im2col_cpu(Tensor data_im,
const int channels, const int height,
const int width, const int ksize_h,
const int ksize_w, const int pad_h, const int pad_w,
const int stride_h, const int stride_w,
const int dilation_h, const int dilation_w,
const int parallel_imgs, Tensor data_col)
{
// 计算 im2col 输出的高度和宽度
int height_col =
(height + 2 * pad_h - (dilation_h * (ksize_h - 1) + 1)) / stride_h + 1;
int width_col =
(width + 2 * pad_w - (dilation_w * (ksize_w - 1) + 1)) / stride_w + 1;
// 计算要处理的总 kernel 数量
int num_kernels = channels * height_col * width_col * parallel_imgs;
// 使用 PyTorch 的宏 AT_DISPATCH_FLOATING_TYPES_AND_HALF 遍历浮点数类型和半精度类型
AT_DISPATCH_FLOATING_TYPES_AND_HALF(
data_im.scalar_type(), "", [&]
{
// 调用具体的 im2col 核函数,根据不同的数据类型执行不同的实现
my_conv_im2col_cpu_kernel<scalar_t>(
num_kernels, data_im.data_ptr<scalar_t>(),
height, width, ksize_h, ksize_w,
pad_h, pad_w, stride_h, stride_w, dilation_h, dilation_w,
parallel_imgs, channels,
height_col, width_col, data_col.data_ptr<scalar_t>());
});
}
Python封装
- 调用
1 | import my_ops |
- 使用 Pybind11 封装 C++ 函数
1 | PYBIND11_MODULE(my_ops, m) |
通过 Pybind11,可以在 Python 中直接调用名为 my_conv_forward 的函数,并将参数传递给底层的 C++ 实现,完成卷积操作
MyConvF类和MyConv2d类
1 | import torch |