文章目录
作业介绍
- 课堂笔记:[Lecture 9 ] CNN Architectures(CNN架构)
- 作业主页:Assignment 2
- 作业目的:为了使深度神经网络更好的得到训练,一个方案是使用更复杂的优化方法:SGD+Momentum,Adam,RMSProp等。另一个方案就是改变网络结构,比如我们这节要完成的批量归一化。
- 官方示例代码: Assignment 2 code
- 作业源文件
ConvolutionalNetworks.ipynb
1. 卷积操作
1. 1 Convolution: Naive forward pass
在cs231n/layers.py
中实实现conv_forward_naive
。这是一个简单的实现,你不必考虑太多的效率问题。
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
# Hint: you can use the function np.pad for padding. #
stride,pad = conv_param["stride"],conv_param["pad"]
N, C, H, W = x.shape
F, C, HH, WW = w.shape
x_pad = np.pad(x,((0,0),(0,0),(pad,pad),(pad,pad)),
mode = 'constant',constant_values = 0)
# out of shape (N,F,out_H,out_W)
out_H = 1 + (H + 2 * pad - HH) // stride
out_W = 1 + (W + 2 * pad - WW) // stride
out = np.zeros((N,F,out_H,out_W))
for i in range(out_H):
for j in range(out_W):
# 参与计算的数值
x_pad_masked = x_pad[:,:, i*stride:i*stride+HH, j*stride:j*stride+WW]
# 每个输出通道分别计算
for f in range(F):
# 借助广播机制,会执行 N 次
# https://cs231n.github.io/python-numpy-tutorial/
out[:,f,i,j] = np.sum(x_pad_masked * w[f,:,:,:],axis=(1,2,3))
# 给每一个输出通道加上 bias
# b of shape (1,F,1,1)
# out = out + (b)[None,:,None,None]
# 能用广播机制进行计算, 如果 rank不相等,则先在前面 prepend 1s 直到 shape 有相同的长度
out = out + b.reshape((1,F,1,1))
cache = (x, w, b, conv_param)
return out, cache
1.2 Aside: Image processing via convolutions
为了检查实现并更好地理解卷积层可以执行的操作类型,我们将设置一个包含两幅图像的输入,并手动设置执行常见图像处理操作(灰度转换和边缘检测)的过滤器。卷积前向传递将对每个输入图像应用这些操作。然后我们可以将结果可视化为完整性检查。
from scipy.misc import imread, imresize
kitten, puppy = imread('kitten.jpg'), imread('puppy.jpg')
# kitten is wide, and puppy is already square
d = kitten.shape[1] - kitten.shape[0]
kitten_cropped = kitten[:, d//2:-d//2, :]
img_size = 200 # Make this smaller if it runs too slow
x = np.zeros((2, 3, img_size, img_size))
x[0, :, :, :] = imresize(puppy, (img_size, img_size)).transpose((2, 0, 1))
x[1, :, :, :] = imresize(kitten_cropped, (img_size, img_size)).transpose((2, 0, 1))
# Set up a convolutional weights holding 2 filters, each 3x3
w = np.zeros((2, 3, 3, 3))
# The first filter converts the image to grayscale.
# Set up the red, green, and blue channels of the filter.
w[0, 0, :, :] = [[0, 0, 0], [0, 0.3, 0], [0, 0, 0]]
w[0, 1, :, :] = [[0, 0, 0], [0, 0.6, 0], [0, 0, 0]]
w[0, 2, :, :] = [[0, 0, 0], [0, 0.1, 0], [0, 0, 0]]
# Second filter detects horizontal edges in the blue channel.
w[1, 2, :, :] = [[1, 2, 1], [0, 0, 0], [-1, -2, -1]]
# Vector of biases. We don't need any bias for the grayscale
# filter, but for the edge detection filter we want to add 128
# to each output so that nothing is negative.
b = np.array([0, 128])
# Compute the result of convolving each input in x with each filter in w,
# offsetting by b, and storing the results in out.
out, _ = conv_forward_naive(x, w, b, {
'stride': 1, 'pad': 1})
def imshow_noax(img, normalize=True):
""" Tiny helper to show images as uint8 and remove axis labels """
if normalize:
img_max, img_min = np.max(img), np.min(img)
img = 255.0 * (img - img_min) / (img_max - img_min)
plt.imshow(img.astype('uint8'))
plt.gca().axis('off')
# Show the original images and the results of the conv operation
plt.subplot(2, 3, 1)
imshow_noax(puppy, normalize=False)
plt.title('Original image')
plt.subplot(2, 3, 2)
imshow_noax(out[0, 0])
plt.title('Grayscale')
plt.subplot(2, 3, 3)
imshow_noax(out[0, 1])
plt.title('Edges')
plt.subplot(2, 3, 4)
imshow_noax(kitten_cropped, normalize=False)
plt.subplot(2, 3, 5)
imshow_noax(out[1, 0])
plt.subplot(2, 3, 6)
imshow_noax(out[1, 1])
plt.show()
可以看到,第一个卷积核执行的是类似灰度化的操作,即输出结果等于 0.3R+0.6G+0.1*B。
第二个卷积核检测蓝色通道中的水平边缘。
1.3. Convolution: Naive backward pass
也是只实现功能,先暂不考虑效率
def conv_backward_naive(dout, cache):
"""
A naive implementation of the backward pass for a convolutional layer.
Inputs:
- dout: Upstream derivatives.
- cache: A tuple of (x, w, b, conv_param) as in conv_forward_naive
Returns a tuple of:
- dx: Gradient with respect to x
- dw: Gradient with respect to w
- db: Gradient with respect to b
"""
dx, dw, db = None, None, None
# TODO: Implement the convolutional backward pass.
(x, w, b, conv_param) = cache
stride, pad = conv_param["stride"], conv_param["pad"]
N, C, H, W = x.shape
F, C, HH, WW = w.shape
out_H = 1 + (H + 2 * pad - HH