im2col这个名称是“image to column”的缩写,翻译过来就是“从图像到矩阵”的意思。
im2col是将一个[C,H,W]矩阵变成一个[H,W]矩阵的一个方法,其原理是利用了行列式进行等价转换。im2col原本是matlab中的一个操作
在Pytorch中可以用torch.unfold, torch.cat和torch.transpose的组合实现im2col操作.
im2col:将卷积运算转为矩阵相乘
将卷积运算转化为矩阵乘法,从乘法和加法的运算次数上看,两者没什么差别,但是转化成矩阵后,运算时需要的数据被存在连续的内存上,这样访问速度大大提升(cache),同时,矩阵乘法有很多库提供了高效的实现方法,像BLAS、MKL等,转化成矩阵运算后可以通过这些库进行加速。
缺点呢?这是一种空间换时间的方法,消耗了更多的内存——转化的过程中数据被冗余存储
这是不带padding的,即输入数据在输入im2col之前,如果有padding要先做了
def im2col(img, kernel_h, kernel_w, stride=1): """ Parameters ---------- input_data : 由(数据量, 通道, 高, 长)的4维数组构成的输入数据 kernel_h : 滤波器的高 kernel_w : 滤波器的长 stride : 步幅 Returns ------- col : 2维数组 """ N, C, H, W = img.shape out_h = (H - kernel_h)//stride + 1 out_w = (W - kernel_w)//stride + 1 col = torch.zeros((N, C, kernel_h, kernel_w, out_h, out_w)) for y in range(kernel_h): y_max = y + stride*out_h for x in range(kernel_w): x_max = x + stride*out_w col[:, :, y, x, :, :] = img[:, :, y:y_max:stride, x:x_max:stride] col = col.permute(0, 4, 5, 1, 2, 3).contiguous().reshape(N*out_h*out_w, -1) #col = col.permute(0, 4, 5, 1, 2, 3).contiguous().reshape(N, out_h*out_w, -1) return col
这是带padding的
def im2col(input_data, filter_h, filter_w, stride=1, pad=0): """ Parameters ---------- input_data : 由(数据量, 通道, 高, 长)的4维数组构成的输入数据 filter_h : 滤波器的高 filter_w : 滤波器的长 stride : 步幅 pad : 填充 Returns ------- col : 2维数组 """ N, C, H, W = input_data.shape out_h = (H + 2*pad - filter_h)//stride + 1 out_w = (W + 2*pad - filter_w)//stride + 1 img = np.pad(input_data, [(0,0), (0,0), (pad, pad), (pad, pad)], 'constant') col = np.zeros((N, C, filter_h, filter_w, out_h, out_w)) for y in range(filter_h): y_max = y + stride*out_h for x in range(filter_w): x_max = x + stride*out_w col[:, :, y, x, :, :] = img[:, :, y:y_max:stride, x:x_max:stride] col = col.transpose(0, 4, 5, 1, 2, 3).reshape(N*out_h*out_w, -1) #col = col.permute(0, 4, 5, 1, 2, 3).contiguous().reshape(N, out_h*out_w, -1) return col
nn.Unfold
其实Pytorch中有现成的函数实现这个功能,nn.Unfold()
结果的size和我们上面实现的略有不同
import torch from torch import nn import numpy as np def im2col(img, kernel_h, kernel_w, stride=1): """ Parameters ---------- input_data : 由(数据量, 通道, 高, 长)的4维数组构成的输入数据 kernel_h : 滤波器的高 kernel_w : 滤波器的长 stride : 步幅 Returns ------- col : 2维数组 """ N, C, H, W = img.shape out_h = (H - kernel_h)//stride + 1 out_w = (W - kernel_w)//stride + 1 col = torch.zeros((N, C, kernel_h, kernel_w, out_h, out_w)) for y in range(kernel_h): y_max = y + stride*out_h for x in range(kernel_w): x_max = x + stride*out_w col[:, :, y, x, :, :] = img[:, :, y:y_max:stride, x:x_max:stride] # col = col.permute(0, 4, 5, 1, 2, 3).contiguous().reshape(N*out_h*out_w, -1) col = col.permute(0, 4, 5, 1, 2, 3).contiguous().reshape(N, out_h*out_w, -1) return col x = torch.rand(4,2,5,5) unfold = nn.Unfold(kernel_size=3) output = unfold(x) output1 = im2col(x, kernel_h=3, kernel_w=3) assert torch.allclose(output, output1.permute(0,2,1).contiguous())
用nn.Unfold()实现Conv2d