目录
ConvolutionalNetworks.ipynb里没有重要的实现,都是 验证;
layers.py里有标准卷积层、最大池化层、空间批量归一化层的 实现;
fast_layers.py里有标准卷积层、最大池化层的 快速版本,代码没有粘出来,只有 原理;
layer_utils.py里有标准卷积层、最大池化层、空间批量归一化层的 组合;
cnn.py里有 三层卷积神经网络(有全连接层)。
ConvolutionalNetworks.ipynb
验证卷积层
导包和导入预处理过(减均值)的数据
from __future__ import print_function
import numpy as np
import matplotlib.pyplot as plt
from cs231n.classifiers.cnn import *
from cs231n.data_utils import get_CIFAR10_data
from cs231n.gradient_check import eval_numerical_gradient_array, eval_numerical_gradient
from cs231n.layers import *
from cs231n.fast_layers import *
from cs231n.solver import Solver
%matplotlib inline
plt.rcParams['figure.figsize'] = (10.0, 8.0) # 画图的默认大小
plt.rcParams['image.interpolation'] = 'nearest' # 若需要插值,使用最临近插值
plt.rcParams['image.cmap'] = 'gray'
# 修改某个文件后,自动重新加载该文件已保存的部分
%load_ext autoreload
%autoreload 2
def rel_error(x, y):
""" 相对误差 """
return np.max(np.abs(x - y) / (np.maximum(1e-8, np.abs(x) + np.abs(y))))
# 加载数据
data = get_CIFAR10_data()
for k, v in data.items():
print('%s: ' % k, v.shape)
卷积层的前向传播
x_shape = (2, 3, 4, 4)
w_shape = (3, 3, 4, 4)
x = np.linspace(-0.1, 0.5, num=np.prod(x_shape)).reshape(x_shape)
w = np.linspace(-0.2, 0.3, num=np.prod(w_shape)).reshape(w_shape)
# 这里的num=3是卷积核的个数,与w_shape[0]一致
b = np.linspace(-0.1, 0.2, num=3)
conv_param = {
'stride': 2, 'pad': 1}
out, _ = conv_forward_naive(x, w, b, conv_param)
correct_out = np.array([[[[-0.08759809, -0.10987781],
[-0.18387192, -0.2109216 ]],
[[ 0.21027089, 0.21661097],
[ 0.22847626, 0.23004637]],
[[ 0.50813986, 0.54309974],
[ 0.64082444, 0.67101435]]],
[[[-0.98053589, -1.03143541],
[-1.19128892, -1.24695841]],
[[ 0.69108355, 0.66880383],
[ 0.59480972, 0.56776003]],
[[ 2.36270298, 2.36904306],
[ 2.38090835, 2.38247847]]]])
# 比较输出,相对误差应该为2e-8左右
print('Testing conv_forward_naive')
print('difference: ', rel_error(out, correct_out))
# difference: 2.2121476417505994e-08
通过卷积实现图像预处理(灰度图、边缘检测)
from imageio import imread
from PIL import Image
kitten, puppy = imread('kitten.jpg'), imread('puppy.jpg')
# kitten是长方形的,要转化为正方形。puppy已经是正方形的了
d = kitten.shape[1] - kitten.shape[0]
kitten_cropped = kitten[:, d//2:-d//2, :]
img_size = 200 # 降低分辨率,减少运行时间
x = np.zeros((2, 3, img_size, img_size))
# transpose((2, 0, 1))把颜色通道提前
x[0, :, :, :] = np.array(Image.fromarray(puppy).resize((img_size, img_size))).transpose((2, 0, 1))
x[1, :, :, :] = np.array(Image.fromarray(kitten_cropped).resize((img_size, img_size))).transpose((2, 0, 1))
# 准备两个3*3的卷积核,每个卷积核有3个通道
w = np.zeros((2, 3, 3, 3))
# 第一个卷积核用于获取灰度图像
# 灰度图像=红色*0.3+绿色*0.6+蓝色*0.1
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]]
# 第二个卷积核在蓝色通道上提取水平边缘
w[1, 2, :, :] = [[1, 2, 1], [0, 0, 0], [-1, -2, -1]]
# 灰度图像不需要偏置,但边缘检测需要,这样就不会有负数出现了(原注释翻译过来)
# 就算加了128还是会有负数的,其实不加也可以,因为展示图片前有处理
b = np.array([0, 128])
# 通过卷积对图像进行处理
out, _ = conv_forward_naive(x, w, b, {
'stride': 1, 'pad': 1})
def imshow_noax(img, normalize=True):
""" 以uint8的格式展示图片,并去掉坐标轴 """
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')
# 展示原始图片和卷积操作的结果
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()
卷积层的反向传播
np.random.seed(231)
x = np.random.randn(4, 3, 5, 5)
w = np.random.randn(2, 3, 3, 3)
b = np.random.randn(2,)
dout = np.random.randn(4, 2, 5, 5)
conv_param = {
'stri