ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

动手实现深度学习(12): 卷积层的实现

2022-09-12 19:04:47  阅读:76  来源: 互联网

标签:12 stride 卷积 self filter 实现 data col out


9.1 卷积层的运算

传送门: https://www.cnblogs.com/greentomlee/p/12314064.html

github: Leezhen2014: https://github.com/Leezhen2014/python_deep_learning

卷积的forward

卷积的计算过程网上的资料已经做够好了,没必要自己再写一遍。只把资料搬运到这里:

http://deeplearning.net/software/theano_versions/dev/tutorial/conv_arithmetic.html#transposed-convolution-arithmetic

https://www.zhihu.com/question/43609045

https://blog.csdn.net/weixin_44106928/article/details/103079668

这里总结一下有padding\stride的卷积操作:

image

假设,输入大小为(H,W,C),fileter大小为(FH,FW,C)*N ; padding=P, stride=S,卷积后的形状为(OH,OW,OC)

wps80

  1 def forward(self, x):
  2     '''
  3     使用im2col 将输入的x 转换成2D矩阵
  4     然后 y= w*x+b 以矩阵的形式完成
  5     最后返回y
  6     :param x: x为4D tensor, 输入数据
  7     :return: out=w*x+b
  8     '''
  9     FN, C, FH, FW = self.W.shape
 10     N, C, H, W = x.shape
 11     out_h = 1 + int((H + 2 * self.pad - FH) / self.stride)
 12     out_w = 1 + int((W + 2 * self.pad - FW) / self.stride)
 13 
 14     col = im2col(x, FH, FW, self.stride, self.pad)
 15     col_W = self.W.reshape(FN, -1).T
 16     print("col.shape=%s"%str(col.shape))
 17     print("col_W.shape=%s"%str(col_W.shape))
 18 
 19     out = np.dot(col, col_W)
 20     print("out.shape=%s"%str(out.shape))
 21     out=out+ self.b
 22     out = out.reshape(N, out_h, out_w, -1).transpose(0, 3, 1, 2)
 23 
 24     self.x = x
 25     self.col = col
 26     self.col_W = col_W
 27 
 28     return out
 29 

 

卷积的backward

概念介绍: https://zhuanlan.zhihu.com/p/33802329

卷积的backward是对卷积的求导。

代码实现如下:

  1 def backward(self, dout):
  2     '''
  3     反馈过程中也需要将2D 矩阵转换为4D tensor
  4     :param dout: 梯度差
  5     :return:
  6     '''
  7     FN, C, FH, FW = self.W.shape
  8     dout = dout.transpose(0, 2, 3, 1).reshape(-1, FN) # NCHW
  9 
 10     self.db = np.sum(dout, axis=0)# NHWC , 求和
 11     self.dW = np.dot(self.col.T, dout) # 点乘w
 12     self.dW = self.dW.transpose(1, 0).reshape(FN, C, FH, FW)
 13 
 14     dcol = np.dot(dout, self.col_W.T)
 15     dx = col2im(dcol, self.x.shape, FH, FW, self.stride, self.pad)
 16 
 17     return dx

9.2 引入im2col 概念

再讲卷积的实现之前,首先抛出一个问题:如果按照上述的卷积方式计算,是否会影响性能?

答案是肯定会受影响的。

因此,我们需要向优化一下conv的计算方式.

按照“以空间换时间”的思想,我们可以做一些优化,使得在conv和pool的时候运算速度加快。

首先,我们知道Numpy对大型矩阵的运算是有做优化的,这个特点我们应该好好利用;

其次,我们知道Numpy在做多个嵌套的for循环的时候,O(n)会很大;应该避免做多个for循环;

因此,要是将4D的卷积运算转换成2D的矩阵乘法就会好很多;filter也可以变成2D的数组;

Im2col便是将4D数据转换成2D矩阵的函数。

该函数大致的思路是:filter按照行列展开成一个2D矩阵即可,input_data按照计算的单元重新组合。因此需要写一个函数将图像转换成2D矩阵,该函数可以将图像展开成适合与滤波去做乘法的矩阵。

展开和计算的流程如下:

wps81

 

9.3 单元测试im2col

对filter计算有影响的因素有input_data,filter_h,filter_w,stride, padding;im2col会应该根据以上的因因素展开input_data,展开后的input_data一定是比之前要大的;

我们可以尝试计算一下input_data展开后的数据形状:

假设,输入数据为4*4*3大小的tensor; filter有两个为2*(2*2*3),filter_h=2,filter_w=2,stride=1, padding=0;这里可以计算出展开以后的大小:

Filter为有两个,分别为f1和f2; shape=(2*2*3), 按照行展开成2D的矩阵以后如下图所示:

 

image

 

Input_data为4*4*3的tensor,如下图所示:

image

 

Input_data首先会找出filter对应的计算单元,这些还是需要padding\stride\filter_w\filter_h相关,找出计算的单元以后,按照行展开。最后得到的数据便是im2col的结果:

 

image

Input_data和filter这样展开以后,卷积计算就可以按照矩阵乘法的方式计算,避免了重复的for循环。如下图所示,黑色和灰色区域是计算的结果。不必担心矩阵过大是否会影响计算速度,Numpy对大规模矩阵乘法内部有优化加速,这样展开以后恰恰也能充分的利用numpy的特性。

image

 

Im2col的实现:

  1 def im2col(input_data, filter_h, filter_w, stride=1, pad=0):
  2     '''
  3 
  4     :param input_data: 输入数据由4维数组组成(N,C,H,W)
  5     :param filter_h:   filer的高
  6     :param filter_w:   filter的宽
  7     :param stride:     stride
  8     :param pad:        padding
  9     :return:           2D矩阵
 10     '''
 11     # 计算输出的大小
 12     N, C, H, W = input_data.shape
 13     out_h = (H + 2*pad - filter_h)//stride + 1
 14     out_w = (W + 2*pad - filter_w)//stride + 1
 15     # padding
 16     img = np.pad(input_data, [(0,0), (0,0), (pad, pad), (pad, pad)], 'constant')
 17     col = np.zeros((N, C, filter_h, filter_w, out_h, out_w))
 18     # 计算单元
 19     for y in range(filter_h):
 20         y_max = y + stride*out_h
 21         for x in range(filter_w):
 22             x_max = x + stride*out_w
 23             col[:, :, y, x, :, :] = img[:, :, y:y_max:stride, x:x_max:stride]
 24     # 重新排列
 25     col = col.transpose(0, 4, 5, 1, 2, 3).reshape(N*out_h*out_w, -1)
 26     return col

 

测试代码:

  1 # -*- coding: utf-8 -*-
  2 # @File  : test_im2col.py
  3 # @Author: lizhen
  4 # @Date  : 2020/2/14
  5 # @Desc  : 测试im2col
  6 import numpy as np
  7 
  8 from src.common.util import im2col,col2im
  9 
 10 if __name__ == '__main__':
 11     raw_data = [3, 0, 4, 2,
 12                 6, 5, 4, 3,
 13                 3, 0, 2, 3,
 14                 1, 0, 3, 1,
 15 
 16                 1, 2, 0, 1,
 17                 3, 0, 2, 4,
 18                 1, 0, 3, 2,
 19                 4, 3, 0, 1,
 20 
 21                 4, 2, 0, 1,
 22                 1, 2, 0, 4,
 23                 3, 0, 4, 2,
 24                 6, 2, 4, 5
 25     ]
 26 
 27     input_data = np.array(raw_data)
 28     input_data = input_data.reshape(1,3,4,4)
 29     print(input_data.shape)
 30     col1 = im2col(input_data=input_data,filter_h=2,filter_w=2,stride=1,pad=0)#input_data, filter_h, filter_w, stride=1, pad=0
 31     print(col1)
 32 

 

 

========输出:可以发现和上面的绘图的结果是一致的 =====

(1, 3, 4, 4)

[[3. 0. 6. 5. 1. 2. 3. 0. 4. 2. 1. 2.]

[0. 4. 5. 4. 2. 0. 0. 2. 2. 0. 2. 0.]

[4. 2. 4. 3. 0. 1. 2. 4. 0. 1. 0. 4.]

[6. 5. 3. 0. 3. 0. 1. 0. 1. 2. 3. 0.]

[5. 4. 0. 2. 0. 2. 0. 3. 2. 0. 0. 4.]

[4. 3. 2. 3. 2. 4. 3. 2. 0. 4. 4. 2.]

[3. 0. 1. 0. 1. 0. 4. 3. 3. 0. 6. 2.]

[0. 2. 0. 3. 0. 3. 3. 0. 0. 4. 2. 4.]

[2. 3. 3. 1. 3. 2. 0. 1. 4. 2. 4. 5.]]

 

9.3 卷积操作的实现

卷积操作也需要实现forward和backward函数。

Forward函数中用到了9.1\9.2的im2col

 

  1 class Convolution:
  2     def __init__(self, W, b, stride=1, pad=0):
  3         '''
  4         conv的构造函数
  5         :param W: 2D矩阵
  6         :param b:
  7         :param stride:
  8         :param pad:
  9         '''
 10         self.W = W
 11         self.b = b
 12         self.stride = stride
 13         self.pad = pad
 14 
 15         # 中间结果(backward的时候使用)
 16         self.x = None
 17         self.col = None
 18         self.col_W = None
 19 
 20         # 权重的梯度/偏置的梯度
 21         self.dW = None
 22         self.db = None
 23 
 24     def forward(self, x):
 25         '''
 26         使用im2col 将输入的x 转换成2D矩阵
 27         然后 y= w*x+b 以矩阵的形式完成
 28         最后返回y
 29         :param x: x为4D tensor, 输入数据
 30         :return: out=w*x+b
 31         '''
 32         FN, C, FH, FW = self.W.shape
 33         N, C, H, W = x.shape
 34         out_h = 1 + int((H + 2 * self.pad - FH) / self.stride)
 35         out_w = 1 + int((W + 2 * self.pad - FW) / self.stride)
 36 
 37         col = im2col(x, FH, FW, self.stride, self.pad)
 38         col_W = self.W.reshape(FN, -1).T
 39 
 40         out = np.dot(col, col_W) + self.b
 41         out = out.reshape(N, out_h, out_w, -1).transpose(0, 3, 1, 2)
 42 
 43         self.x = x
 44         self.col = col
 45         self.col_W = col_W
 46 
 47         return out
 48 
 49     def backward(self, dout):
 50         '''
 51         反馈过程中也需要将2D 矩阵转换为4D tensor
 52         :param dout: 梯度差
 53         :return:
 54         '''
 55         FN, C, FH, FW = self.W.shape
 56         dout = dout.transpose(0, 2, 3, 1).reshape(-1, FN)
 57 
 58         self.db = np.sum(dout, axis=0)
 59         self.dW = np.dot(self.col.T, dout)
 60         self.dW = self.dW.transpose(1, 0).reshape(FN, C, FH, FW)
 61 
 62         dcol = np.dot(dout, self.col_W.T)
 63         dx = col2im(dcol, self.x.shape, FH, FW, self.stride, self.pad)
 64 
 65         return dx
 66 

9.4单元测试卷积操作

输入:input_data\filters

输出:output

image

测试代码:

 

  1   2 # -*- coding: utf-8 -*-
  3 # @File  : test_im2col.py
  4 # @Author: lizhen
  5 # @Date  : 2020/2/14
  6 # @Desc  : 测试im2col
  7 import numpy as np
  8 
  9 from src.common.util import im2col,col2im
 10 from src.common.layers import Convolution
 11 
 12 
 13 if __name__ == '__main__':
 14     raw_data = [3, 0, 4, 2,
 15                 6, 5, 4, 3,
 16                 3, 0, 2, 3,
 17                 1, 0, 3, 1,
 18 
 19                 1, 2, 0, 1,
 20                 3, 0, 2, 4,
 21                 1, 0, 3, 2,
 22                 4, 3, 0, 1,
 23 
 24                 4, 2, 0, 1,
 25                 1, 2, 0, 4,
 26                 3, 0, 4, 2,
 27                 6, 2, 4, 5
 28     ]
 29 
 30     raw_filter=[
 31         1,    1,    1,    1,    1,    1,
 32         1,    1,    1,    1,    1,    1,
 33         2,    2,    2,    2,    2,   2,
 34         2,    2,    2,    2,    2,   2,
 35 
 36     ]
 37 
 38 
 39 
 40     input_data = np.array(raw_data)
 41     filter_data = np.array(raw_filter)
 42 
 43     x = input_data.reshape(1,3,4,4)# NCHW
 44     W = filter_data.reshape(2,3,2,2) # NHWC
 45     b = np.zeros(2)
 46     # b = b.reshape((2,1))
 47     # col1 = im2col(input_data=x,filter_h=2,filter_w=2,stride=1,pad=0)#input_data, filter_h, filter_w, stride=1, pad=0
 48     # print(col1)
 49 
 50     print("input_data.shape=%s"%str(input_data.shape))
 51     print("W.shape=%s"%str(W.shape))
 52     print("b.shape=%s"%str(b.shape))
 53     conv = Convolution(W,b) # def __init__(self, W, b, stride=1, pad=0)
 54     out = conv.forward(x)
 55     print("bout.shape=%s"%str(out.shape))
 56     print(out)

Conv的输出结果,与上图的结果一致。

 

image

标签:12,stride,卷积,self,filter,实现,data,col,out
来源: https://www.cnblogs.com/greentomlee/p/16686862.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有