如何在多个内核中使用 Eigen::Tensor::convolve?
How to use Eigen::Tensor::convolve with multiple kernels?
将形状为 (3, 20, 30)
的输入张量(通道优先表示法)与形状为 (3, 5, 7)
的 8
滤波器进行卷积应该得到形状为 (8, 24, 16)
的张量。我正在尝试使用 Eigen::Tensor::convolve
来实现它,但结果形状是 (1, 24, 16)
。所以似乎只应用了一个过滤器而不是所有 8
.
这是一个最小的例子:
#include <cassert>
#include <iostream>
#include <eigen3/unsupported/Eigen/CXX11/Tensor>
int main() {
int input_height = 20;
int input_width = 30;
int input_channels = 3;
int kernels_height = 5;
int kernels_width = 7;
int kernels_channels = 3;
int kernel_count = 8;
assert(kernels_channels == input_channels);
int expected_output_height = input_height + 1 - kernels_height;
int expected_output_width = input_width + 1 - kernels_width;
int expected_output_channels = kernel_count;
Eigen::Tensor<float, 3> input(input_channels, input_width, input_height);
Eigen::Tensor<float, 4> filters(kernels_channels, kernels_width, kernels_height, kernel_count);
Eigen::array<ptrdiff_t, 3> dims({0, 1, 2});
Eigen::Tensor<float, 3> output = input.convolve(filters, dims);
const Eigen::Tensor<float, 3>::Dimensions& d = output.dimensions();
std::cout << "Expected output shape: (" << expected_output_channels << ", " << expected_output_width << ", " << expected_output_height << ")" << std::endl;
std::cout << "Actual shape: (" << d[0] << ", " << d[1] << ", " << d[2] << ")" << std::endl;
}
及其输出:
Expected output shape: (8, 24, 16)
Actual shape: (1, 24, 16)
当然可以 iterate over the filters one by one and call .convolve
for each one 但是这个
- 会导致通道不是第一维的张量
- 可能不像一次调用那样优化性能
- 需要更多自定义代码
所以我想我在使用 Eigen 库时做错了什么。如何正确完成?
不支持同时与多个内核进行卷积(docs):
The dimension size for dimensions of the output tensor which were part
of the convolution will be reduced by the formula: output_dim_size =
input_dim_size - kernel_dim_size + 1 (requires: input_dim_size >=
kernel_dim_size). The dimension sizes for dimensions that were not
part of the convolution will remain the same.
根据上面 expected_output_channels
应该等于 1 = 3 - 3 + 1
。
我觉得应该不可能如你所愿,因为卷积运算是一个数学运算,而且定义明确,所以不遵循数学定义才奇怪。
未测试解决方案
我没有检查,但我相信下一段代码会产生您希望的输出:
Eigen::Tensor<float, 3> input(input_channels, input_width, input_height);
Eigen::Tensor<float, 4> filters(kernels_channels, kernels_width, kernels_height, kernel_count);
Eigen::Tensor<float, 3> output(kernel_count, expected_output_width, expected_output_height);
Eigen::array<ptrdiff_t, 3> dims({0, 1, 2});
for (int i = 0; i < kernel_count; ++i){
output.chip(i, 0) = input.convolve(filters.chip(i, 3), dims).chip(0, 0);
}
如你所见,第一个和第三个问题都不是什么大问题。希望你运气好,这部分代码不会成为你的瓶颈:)
将形状为 (3, 20, 30)
的输入张量(通道优先表示法)与形状为 (3, 5, 7)
的 8
滤波器进行卷积应该得到形状为 (8, 24, 16)
的张量。我正在尝试使用 Eigen::Tensor::convolve
来实现它,但结果形状是 (1, 24, 16)
。所以似乎只应用了一个过滤器而不是所有 8
.
这是一个最小的例子:
#include <cassert>
#include <iostream>
#include <eigen3/unsupported/Eigen/CXX11/Tensor>
int main() {
int input_height = 20;
int input_width = 30;
int input_channels = 3;
int kernels_height = 5;
int kernels_width = 7;
int kernels_channels = 3;
int kernel_count = 8;
assert(kernels_channels == input_channels);
int expected_output_height = input_height + 1 - kernels_height;
int expected_output_width = input_width + 1 - kernels_width;
int expected_output_channels = kernel_count;
Eigen::Tensor<float, 3> input(input_channels, input_width, input_height);
Eigen::Tensor<float, 4> filters(kernels_channels, kernels_width, kernels_height, kernel_count);
Eigen::array<ptrdiff_t, 3> dims({0, 1, 2});
Eigen::Tensor<float, 3> output = input.convolve(filters, dims);
const Eigen::Tensor<float, 3>::Dimensions& d = output.dimensions();
std::cout << "Expected output shape: (" << expected_output_channels << ", " << expected_output_width << ", " << expected_output_height << ")" << std::endl;
std::cout << "Actual shape: (" << d[0] << ", " << d[1] << ", " << d[2] << ")" << std::endl;
}
及其输出:
Expected output shape: (8, 24, 16)
Actual shape: (1, 24, 16)
当然可以 iterate over the filters one by one and call .convolve
for each one 但是这个
- 会导致通道不是第一维的张量
- 可能不像一次调用那样优化性能
- 需要更多自定义代码
所以我想我在使用 Eigen 库时做错了什么。如何正确完成?
不支持同时与多个内核进行卷积(docs):
The dimension size for dimensions of the output tensor which were part of the convolution will be reduced by the formula: output_dim_size = input_dim_size - kernel_dim_size + 1 (requires: input_dim_size >= kernel_dim_size). The dimension sizes for dimensions that were not part of the convolution will remain the same.
根据上面 expected_output_channels
应该等于 1 = 3 - 3 + 1
。
我觉得应该不可能如你所愿,因为卷积运算是一个数学运算,而且定义明确,所以不遵循数学定义才奇怪。
未测试解决方案
我没有检查,但我相信下一段代码会产生您希望的输出:
Eigen::Tensor<float, 3> input(input_channels, input_width, input_height);
Eigen::Tensor<float, 4> filters(kernels_channels, kernels_width, kernels_height, kernel_count);
Eigen::Tensor<float, 3> output(kernel_count, expected_output_width, expected_output_height);
Eigen::array<ptrdiff_t, 3> dims({0, 1, 2});
for (int i = 0; i < kernel_count; ++i){
output.chip(i, 0) = input.convolve(filters.chip(i, 3), dims).chip(0, 0);
}
如你所见,第一个和第三个问题都不是什么大问题。希望你运气好,这部分代码不会成为你的瓶颈:)