im2col解析2
im2col
表示image to column
,将图像转换成列向量
卷积操作步骤:首先将卷积核映射到x_padded
左上角,然后沿着行方向操作,每次滑动stride
距离;到达最右端后,将卷积核往列方向滑动stride
距离,再实现从左到右的滑动
图像转列向量
在以下操作中,假设感受野大小为field_height = field_width = 2
,零填充padding = 0
,步长stride = 2
2维图像
以(3,3)大小矩阵为例
1 | >>> a = np.arange(9).reshape(3,3) |
那么得到的局部连接数为
\[ (3 - 2 + 2*0)/1 + 1 = 2\\ num = 2*2 = 4 \]
所以共有4个局部连接,分别是
\[ \begin{bmatrix} 0 & 1\\ 3 & 4 \end{bmatrix}\ \ \begin{bmatrix} 1 & 2\\ 4 & 5 \end{bmatrix}\ \ \begin{bmatrix} 3 & 4\\ 6 & 7 \end{bmatrix} \begin{bmatrix} 4 & 5\\ 7 & 8 \end{bmatrix} \]
其坐标分别为
\[ \begin{bmatrix} (0,0) & (0,1)\\ (1,0) & (1,1) \end{bmatrix}\ \ \begin{bmatrix} (0,1) & (0,2)\\ (1,1) & (1,2) \end{bmatrix}\ \ \begin{bmatrix} (1,0) & (1,1)\\ (2,0) & (2,1) \end{bmatrix} \begin{bmatrix} (1,1) & (1,2)\\ (2,1) & (2,2) \end{bmatrix} \]
将其列向量化,可得
\[ matrix= \begin{bmatrix} 0 & 1 & 3 & 4\\ 1 & 2 & 4 & 5\\ 3 & 4 & 6 & 7\\ 4 & 5 & 7 & 8 \end{bmatrix} \]
\[ indexs= \begin{bmatrix} (0,0) & (0,1) & (1,0) & (1,1)\\ (0,1) & (0,2) & (1,1) & (1,2)\\ (1,0) & (1,1) & (2,0) & (2,1)\\ (1,1) & (1,2) & (2,1) & (2,2) \end{bmatrix} \]
进行行列坐标分离
\[ rows_{index}= \begin{bmatrix} 0 & 0 & 1 & 1\\ 0 & 0 & 1 & 1\\ 1 & 1 & 2 & 2\\ 1 & 1 & 2 & 2 \end{bmatrix} \]
\[ columns_{index}= \begin{bmatrix} 0 & 1 & 0 & 1\\ 1 & 2 & 1 & 2\\ 0 & 1 & 0 & 1\\ 1 & 2 & 1 & 2 \end{bmatrix} \]
对卷积核而言,每行的行坐标一致,共有field_width
个,每个卷积核有field_height
行
1 | # i0 = np.repeat(np.arange(field_height), field_width) |
每行共有out_width
个局部连接矩阵,每个矩阵相隔stride
,共有out_height
行
1 | # i1 = stride * np.repeat(np.arange(out_height), out_width) |
对于局部连接矩阵的行坐标为
1 | # i = i0.reshape(-1, 1) + i1.reshape(1, -1) |
同样的,对于卷积核的列来说,其相邻列相差1
,长field_width
,共有field_height
行
1 | # j0 = np.tile(np.arange(field_width), field_height * C) |
每行有out_width
个局部连接矩阵,矩阵之间相差stride
步长,同一列矩阵相对于该行最左侧的距离相同
1 | # j1 = stride * np.tile(np.arange(out_width), out_height) |
计算局部连接矩阵的行坐标
1 | # j = j0.reshape(-1, 1) + j1.reshape(1, -1) |
得到列向量矩阵的行坐标和列坐标后,求取局部连接矩阵的列向量矩阵
1 | >>> a[i,j] |
3维图像
如果图像有多通道,每个通道图像的卷积操作一致,局部连接总数不变,仅扩展每个卷积矩阵的大小,所以仅需在行/列坐标矩阵的列方向扩展即可
比如有\(2\times 3\times 3\)大小图像,通道数为2
1 | >>> a = np.arange(18).reshape(2,3,3) |
局部连接矩阵大小为\(2\times 2\times 2\),比如
1 | array([[[ 0, 1], |
对于行坐标矩阵
1 | >>> i0 = np.repeat(np.arange(2), 2) |
对于列坐标矩阵
1 | >>> j0 = np.tile(np.arange(2), 2*2) |
还需要计算通道向量k,用于指定哪个通道图像
1 | >>> k = np.repeat(np.arange(2), 2*2).reshape(-1,1) |
最后求取局部连接矩阵的列向量矩阵
1 | >>> a[k,i,j] |
4维图像
批量处理多通道图像,比如批量图像数据大小为\(2\times 2\times 3\times 3\),共2
张图片,每张图像2
通道,大小为\(3\times 3\)
1 | >>> a = np.arange(36).reshape(2,2,3,3) |
对于行/列坐标矩阵i,j
以及通道向量k
与3
维图像操作一致
1 | >>> a[:,k,i,j] |
得到的是一个3
维数据体,第一维表示图像数,第二维表示单个矩阵向量,第三维表示每个图片的局部矩阵数
先进行维数转换,再变形为2维矩阵
1 | >>> c = np.transpose(b, (1,2,0)) |
最后得到了2
维矩阵,每列表示一个局部连接矩阵向量,其排列方式为依次加入每个图像的相同位置局部连接矩阵,再向左向下滑动(im2col.py
实现方式)
如果想要先完成单个图像所有局部连接矩阵,再进行下一个图像的转换,可以修改维数变换如下
1 | >>> c = np.transpose(b, (1,0,2)) |
列向量转图像
将图像转列向量小节中得到的列向量矩阵重新映射回图像
2维图像
已知图像大小为\(3\times 3\),卷积核大小为\(2\times 2\),步长为2
,零填充为0
1 | >>> a = np.arange(9).reshape(3,3) |
根据图像数据和参数获取行/列坐标矩阵
1 | >>> i |
获取2
维列矩阵
1 | >>> cols = a[i,j] |
将2
维列矩阵映射到图像
1 | >>> b = np.zeros(a.shape) |
反向映射得到的图像数据和原先图像数据不一致,因为卷积操作中许多下标的位置被多次采集
如果想要得到原图,需要除以叠加的倍数
1 | >>> c = np.zeros(a.shape) |
3维图像
1 | >>> a = np.arange(18).reshape(2,3,3) |
反向计算图像
1 | >>> np.add.at(b, (k, i, j), cols) |
除以叠加倍数,转变回原图
1 | >>> c = np.ones(a.shape) |
4维图像
最终要实现的是批量图片列向量矩阵的反卷积操作
从批量图像数据中通过坐标矩阵获取的列向量矩阵是3
维大小,还需要通过维数转换和变形
列向量转图像需要执行反向操作,首先进行数据变形,再进行维数转换,最后通过坐标矩阵叠加
在上一小节中最后得到了两种排列的列向量矩阵,一种是先提取同一位置局部连接矩阵,另一种是先提取同一图片局部连接矩阵
如果前向操作如下(第二种)
1 | >>> cols = np.transpose(b, (1,2,0)) |
那么反向操作为
1 | >>> N=2 |
批量图片大小为\(2\times 2\times 3\times 3\),得到最终的反向结果
1 | b = np.zeros(a.shape) |
除以叠加倍数,得到最初的图像
1 | >>> c = np.ones(a.shape) |
im2col.py
im2col.py
实现代码如下
1 | from builtins import range |
包含两部分功能:图像转列向量以及列向量转图像
函数get_im2col_indices
的功能是计算单个图像行/列坐标矩阵以及通道向量
函数im2col_indices
的功能是实现图像转列向量
函数col2im_indices
的功能是实现列向量转图像
注意,col2im_indices
得到的图像不等于原图,是叠加后的结果