如何使用Eigen实现带步长(stride)的卷积操作?
使用Eigen实现带步长卷积的可行方案
一、通过extract_image_patches实现带步长卷积
这是当前Eigen框架下最直接高效的方案,完全可以替代原生convolve函数完成需求,核心逻辑和主流深度学习框架的卷积实现一致:先提取输入中所有符合步长要求的卷积窗口(patch),再通过矩阵乘法完成卷积计算。具体步骤如下:
提取指定步长的图像patch
调用extract_image_patches时,通过patch_size指定卷积核尺寸,strides参数直接设置你需要的卷积步长。注意参数维度要和输入张量的维度对应,比如输入是[batch, height, width, channels]格式:// 定义卷积核尺寸和步长 Eigen::array<int, 4> patch_size = {1, kernel_h, kernel_w, 1}; // 仅在高、宽维度取patch Eigen::array<int, 4> strides = {1, stride_h, stride_w, 1}; // 高、宽维度设置目标步长 // 提取所有待卷积的patch,输出形状为[batch, num_patches, kernel_h*kernel_w*channels] auto input_patches = input.extract_image_patches(patch_size, strides);展平卷积核
将形状为[kernel_h, kernel_w, channels, num_filters]的卷积核,展平为二维矩阵,方便后续矩阵乘法:// 展平为[卷积核元素总数, 滤波器数量]的矩阵 auto kernel_flattened = kernel.reshape(Eigen::array<int, 2>{kernel_h*kernel_w*channels, num_filters});矩阵乘法完成卷积并恢复形状
把提取的patch张量转成二维矩阵,和展平的卷积核做矩阵乘法,最后将结果调整为目标卷积输出的形状:int batch = input.dimension(0); int num_patches = input_patches.dimension(1); // 将patch张量转为[batch*num_patches, 卷积核元素总数]的二维矩阵 auto patches_matrix = input_patches.reshape(Eigen::array<int, 2>{batch*num_patches, kernel_h*kernel_w*channels}); // 矩阵乘法计算卷积 auto conv_matrix = patches_matrix * kernel_flattened; // 计算输出的高宽维度 int output_h = (input.dimension(1) - kernel_h) / stride_h + 1; int output_w = (input.dimension(2) - kernel_w) / stride_w + 1; // 恢复为[batch, output_h, output_w, num_filters]的输出形状 auto conv_output = conv_matrix.reshape(Eigen::array<int, 4>{batch, output_h, output_w, num_filters});注:如果需要padding,需先对输入做填充操作,再调整输出高宽的计算公式。
二、基于原生convolve的间接实现(不推荐)
若一定要用Eigen的convolve函数,可以通过输入插值填充+结果采样的方式间接模拟步长卷积,但这种方法会额外增加计算量,操作繁琐:
- 假设步长为s,先在输入张量的高、宽维度按s间隔插入s-1个0(或其他填充值),然后调用步长为1的
convolve,最后对卷积结果按s步长采样,得到带步长的卷积输出。这种方式效率远低于extract_image_patches方案,不建议使用。
总结
extract_image_patches是Eigen中实现带步长卷积的最优选择,逻辑清晰且计算高效,完全能满足你的需求,无需手动编写完整的卷积循环。
内容的提问来源于stack exchange,提问作者user1311627
相关产品推荐
相关产品推荐

