用numpy库手写算子二: Conv2d_backward
前言
我们经常可以调用pytorch,tensorflow库等来实现我们的神经网络,但是有的时候需要开发自己的框架,这个时候就得了解每一个算子的计算规则,了解这些计算规则也有助于我们了解他们的计算特性,然后就可以在底层优化上面有一定的针对性。
Conv2d_backward
一个算子的求导,包括两个方面:对Filter求导,这部分的求导作为之后更新梯度的梯度;对Input求导,也就是对每层的Featurre_map进行求导,作为链式求导中的一环向前面的神经层传导。
输入说明
对于conv2d的反向而言:
默认dout,也就是从后面的层反传回来的,对应正向的本层的feature_map的梯度,这部分梯度是作为链式传导过程中的中间变量.数据布局为(N,C,H,W),N代表batch_size,C代表input_channel,H代表input_height,W代表input_width.
默认x数据布局为(N,C,H,W),N代表batch_size,C代表input_channel,H代表input_height,W代表input_width。
关于x和dout的关系,dout相当于正向过程中,根据x和w算出来的本层的feature_map的导数。
w代表正向的本层的filter,默认数据布局为(N,C,H,W),N代表channel_multiplier,C代表input_channel,H代表kernel_height,W代表kernel_width。
stride代表的是(stride__h,stride_w)。
pad代表的是(pad_h,pad_w)。
对Filter求导
本层的filter的导数的形状和w的形状大小是一致。对于一个(ko,ki,kh,kw)的卷积核,其梯度大小相当于反向传导回来的dout和前向的input做正常的卷积计算,累加的轴是batch_size。
对Input求导
本层对input的求导(x),首先是要根据前向的stride的数值,进行dilate,也就是相当于给dout补0,具体参考是dilate_python。dilate之后的dout,根据前向的pad,给边界补0。然后将所有的filter进行翻转180度。最后将dilate+边界补0后的dout和旋转180度的filter,做正常的卷积得到对本层x的梯度。将求的梯度反向传导,作为链式求导过程的中间变量。
总体代码
import numpy as np
def dilate_python(input_np, strides):
"""Dilate operation.
Parameters
----------
input_np : numpy.ndarray
n-D, can be any layout.
strides : list / tuple of n ints
Dilation stride on each dimension, 1 means no dilation.
Returns
-------
output_np : numpy.ndarray
n-D, the same layout as Input.
if stride = 1,input = output
if stride = 2.input = [[[[1. 1.][1. 1.]][[1. 1.][1. 1.]]]],output =