多平台统一管理软件接口,如何实现多平台统一管理软件接口
517
2022-06-26
关于卷积操作是如何进行的就不必多说了,结合代码一步一步来看卷积层是怎么实现的。
代码来源:https://github.com/eriklindernoren/ML-From-Scratch
先看一下其基本的组件函数,首先是determine_padding(filter_shape, output_shape="same"):
说明:根据卷积核的形状以及padding的方式来计算出padding的值,包括上、下、左、右,其中out_shape=valid表示不填充。
补充:
math.floor(x)表示返回小于或等于x的最大整数。
math.ceil(x)表示返回大于或等于x的最大整数。
带入实际的参数来看下输出:
pad_h,pad_w=determine_padding((3,3), output_shape="same")
输出:(1,1),(1,1)
然后是image_to_column(images, filter_shape, stride, output_shape='same')函数
说明:输入的images的形状是[batchsize,channel,height,width],类似于pytorch的图像格式的输入。也就是说images_padded是在height和width上进行padding的。在其中调用了get_im2col_indices()函数,那我们接下来看看它是个什么样子的:
说明:单独看很难理解,我们还是带着带着实际的参数一步步来看。
get_im2col_indices((1,3,32,32), (3,3), ((1,1),(1,1)), stride=1)
说明:看一下每一个变量的变化情况,out_width和out_height就不多说,是卷积之后的输出的特征图的宽和高维度。
i0:np.repeat(np.arange(3),3):[0 ,0,0,1,1,1,2,2,2]
i0:np.tile([0,0,0,1,1,1,2,2,2],3):[0,0,0,1,1,1,2,2,2,0,0,0,1,1,1,2,2,2,0,0,0,1,1,1,2,2,2],大小为:(27,)
i1:1*np.repeat(np.arange(32),32):[0,0,0......,31,31,31],大小为:(1024,)
j0:np.tile(np.arange(3),3*3):[0,1,2,0,1,2,......],大小为:(27,)
j1:1*np.tile(np.arange(32),32):[0,1,2,3,......,0,1,2,......,29,30,31],大小为(1024,)
i:i0.reshape(-1,1)+i1.reshape(1,-1):大小(27,1024)
j:j0.reshape(-1,1)+j1.reshape(1,-1):大小(27,1024)
k:np.repeat(np.arange(3),3*3).reshape(-1,1):大小(27,1)
补充:
numpy.pad(array, pad_width, mode, **kwargs):array是要要被填充的数据,第二个参数指定填充的长度,mod用于指定填充的数据,默认是0,如果是constant,则需要指定填充的值。
numpy.arange(start, stop, step, dtype = None):举例numpy.arange(3),输出[0,1,2]
numpy.repeat(array,repeats,axis=None):举例numpy.repeat([0,1,2],3),输出:[0,0,0,1,1,1,2,2,2]
numpy.tile(array,reps):举例numpy.tile([0,1,2],3),输出:[0,1,2,0,1,2,0,1,2]
具体的更复杂的用法还是得去查相关资料。这里只列举出与本代码相关的。
有了这些大小还是挺难理解的呀。那么我们继续,需要明确的是k是对通道进行操作,i是对特征图的高,j是对特征图的宽。使用3×3的卷积核在一个通道上进行卷积,每次执行3×3=9个像素操作,共3个通道,所以共对9×3=27个像素点进行操作。而图像大小是32×32,共1024个像素。再回去看这三行代码:
cols = images_padded[:, k, i, j]
channels = images.shape[1]
# Reshape content into column shape
cols = cols.transpose(1, 2, 0).reshape(filter_height * filter_width * channels, -1)
images_padded的大小是(1,3,34,34),则cols=images_padded的大小是(1,27,1024)
channels的大小是3
最终cols=cols.transpose(1,2,0).reshape(3*3*3,-1)的大小是(27,1024)。
当batchsize的大小不是1,假设是64时,那么最终输出的cols的大小就是:(27,1024×64)=(27,65536)。
最后就是卷积层的实现了:
首先有一个Layer通用基类,通过继承该基类可以实现不同的层,例如卷积层、池化层、批量归一化层等等:
对于子类继承该基类必须要实现的方法,如果没有实现使用raise NotImplementedError()抛出异常。
接着就可以基于该基类实现Conv2D了:
假设输入还是(1,3,32,32)的维度,使用16个3×3的卷积核进行卷积,那么self.W的大小就是(16,3,3,3),self.w0的大小就是(16,1)。
self.X_col的大小就是(27,1024),self.W_col的大小是(16,27),那么output = self.W_col.dot(self.X_col) + self.w0的大小就是(16,1024)
最后是这么使用的:
image = np.random.randint(0,255,size=(1,3,32,32)).astype(np.uint8)
input_shape=image.squeeze().shape
conv2d = Conv2D(16, (3,3), input_shape=input_shape, padding='same', stride=1)
conv2d.initialize(None)
output=conv2d.forward_pass(image,training=True)
print(output.shape)
输出结果:(1,16,32,32)
计算下参数:
print(conv2d.parameters())
输出结果:448
也就是448=3×3×3×16+16
再是一个padding=valid的:
需要注意的是cols的大小变化了,因为我们卷积之后的输出是(1,16,30,30)
输出:
cols的大小:(27,900)
(1,16,30,30)
448
最后是带步长的:
cols的大小:(27,225)
(1,16,15,15)
448
最后补充下:
卷积层参数计算公式 :params=卷积核高×卷积核宽×通道数目×卷积核数目+偏置项(卷积核数目)
卷积之后图像大小计算公式:
输出图像的高=(输入图像的高+padding(高)×2-卷积核高)/步长+1
输出图像的宽=(输入图像的宽+padding(宽)×2-卷积核宽)/步长+1
get_im2col_indices()函数中的变换操作是清楚了,至于为什么这么变换的原因还需要好好去琢磨。至于反向传播和优化optimizer等研究好了之后再更新了。
版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。
发表评论
暂时没有评论,来抢沙发吧~