首先我们用一个小的具体的例子来具象化
假设输入是 (N,3,5,5);
第一层卷积是(2,3,1,1),我们设pad=0,stride=1,那么得到的输出结果应该就是(N,2,5,5)
第二层卷积是(2,2,3,3),pad=0,stride=1,那么得到的输出结果应该是(N,2,3,3)
REP(b)的意思是什么呢
如果按照一般的处理方法比如 DFQ——前后两层卷积核数量不一样是否可以进行均衡.md
def conv_forward_naive(x, w, b, conv_param):
"""
A naive implementation of the forward pass for a convolutional layer.
The input consists of N data points, each with C channels, height H and
width W. We convolve each input with F different filters, where each filter
spans all C channels and has height HH and width WW.
Input:
- x: Input data of shape (N, C, H, W)
- w: Filter weights of shape (F, C, HH, WW)
- b: Biases, of shape (F,)
- conv_param: A dictionary with the following keys:
- 'stride': The number of pixels between adjacent receptive fields in the
horizontal and vertical directions.
- 'pad': The number of pixels that will be used to zero-pad the input.
During padding, 'pad' zeros should be placed symmetrically (i.e equally on both sides)
along the height and width axes of the input. Be careful not to modfiy the original
input x directly.
Returns a tuple of:
- out: Output data, of shape (N, F, H', W') where H' and W' are given by
H' = 1 + (H + 2 * pad - HH) / stride
W' = 1 + (W + 2 * pad - WW) / stride
- cache: (x, w, b, conv_param)
"""
out = None
###########################################################################
# TODO: Implement the convolutional forward pass. #
# Hint: you can use the function np.pad for padding. #
###########################################################################
pad = conv_param['pad']
stride = conv_param['stride']
N, C, H, W = x.shape
F, C, FH, FW = w.shape
assert (H - FH + 2 * pad) % stride == 0
assert (W - FW + 2 * pad) % stride == 0
#outH = 1 + (H - FH + 2 * pad) / stride
#outW = 1 + (W - FW + 2 * pad) / stride
outH = int(1 + (H - FH + 2 * pad) / stride)
outW = int(1 + (W - FW + 2 * pad) / stride)
# create output tensor after convolution layer
out = np.zeros((N, F, outH, outW))
# padding all input data
x_pad = np.pad(x, ((0,0), (0,0),(pad,pad),(pad,pad)), 'constant')
H_pad, W_pad = x_pad.shape[2], x_pad.shape[3]
# create w_row matrix
w_row = w.reshape(F, C*FH*FW) #[F x C*FH*FW]
# create x_col matrix with values that each neuron is connected to
x_col = np.zeros((C*FH*FW, outH*outW)) #[C*FH*FW x H'*W']
for index in range(N):
neuron = 0
for i in range(0, H_pad-FH+1, stride):
for j in range(0, W_pad-FW+1,stride):
x_col[:,neuron] = x_pad[index,:,i:i+FH,j:j+FW].reshape(C*FH*FW)
neuron += 1
out[index] = (w_row.dot(x_col) + b.reshape(F,1)).reshape(F, outH, outW)
###########################################################################
# END OF YOUR CODE #
###########################################################################
cache = (x_pad, w, b, conv_param)
return out, cache
从这个过程来看,卷积核在生成feature map的每一个点的时候,都是有偏置参与运算的。也就是点积之后再加上偏置。
但是我们可以做这样一个等效的变换,在生成feature map的每个点的时候先不用bias参与运算。仅仅计算点积,最后feature map上所有的点一口气都加上偏置,也就是这种感觉
那么偏置如何和卷积核进行卷积
通过REP操作我们把b扩展成一个和输出的h和w的一样矩阵,之后这个矩阵再和卷积核进行卷积,还拿上面例子来演示
第一层卷积核中的bias经过rep变成了(1,2,5,5)这样的数据体和第二层卷积核中的w(2,2,3,3)进行卷积得到的(2,2,3,3)这样的数据体也就是
R
E
P
(
b
^
)
REP(\hat b)
REP(b^)
这个REP(\hat b)正好和第二层卷积核中的偏置做REP的结果尺寸(2,3,3)正好一样,就可以加在一起。
这就解释了原文中的公式14和公式15