import numpy as np
import h5py
import matplotlib.pyplot as plt
%matplotlib inline
plt.rcParams['figure.figsize'] = (5.0, 4.0)
plt.rcParams['image.interpolation'] = 'nearest'
plt.rcParams['image.cmap'] = 'gray'
%load_ext autoreload
%autoreload 2
np.random.seed(1)
这段代码看起来是用于设置环境和导入一些常用的库。
-
import numpy as np
:这行代码导入了名为NumPy的Python库,并为它创建了一个别名np
,这是一种常见的惯例,使得在代码中更容易引用NumPy的函数和类。 -
import h5py
:这行代码导入了名为h5py的Python库,它是用于处理HDF5格式数据的库。HDF5是一种用于存储和组织大型数据集的文件格式。 -
import matplotlib.pyplot as plt
:这行代码导入了Matplotlib库,并为它创建了一个别名plt
,Matplotlib是一个用于绘制图形和图表的库。 -
%matplotlib inline
:这是一个IPython魔术命令,它用于在IPython交互式环境中内联显示图形,而不是弹出新的窗口。这可以让您在Notebook中直接看到图形输出。 -
plt.rcParams
:这里设置了Matplotlib的一些全局参数,包括图形的大小和插值方式以及颜色映射。这些参数将影响之后绘制的图形样式。 -
%load_ext autoreload
:这是另一个IPython魔术命令,用于启用自动重新加载模块的功能。当代码中的模块发生更改时,它会自动重新加载,以确保最新的代码正在运行。 -
%autoreload 2
:这个命令设置自动重新加载模式的详细级别为2,意味着模块的代码将在任何函数调用之前自动重新加载。 -
np.random.seed(1)
:这行代码设置了随机数生成器的种子,这样可以使随机数生成过程具有可重复性。使用相同的种子将产生相同的随机数序列,这对于调试和复现实验结果非常有用。
这些是代码的初始化部分,用于设置环境和随机种子等。
实现CNN相关的一些基本函数:
- 卷积相关的函数:
- 零填补(Zero Padding)
- 卷积窗口(Convolve window )
- 前向卷积(Convolution forward)
- 反向卷积(Convolution backward)
- 池化相关的函数:
- 前向池化(Pooling forward)
- 创建掩码(Create mask )
- Distribute value
- 反向池化(Pooling backward)
def zero_pad(X, pad):
"""
给样本集X的所有样本进行零填补。
参数:
X -- 样本集,维度是(m, n_H, n_W, n_C) ,
m表示样本的数量,这里的样本是图片数据,n_H, n_W, n_C表示图片的高,宽,深度。
pad -- 表示padding的个数,就是我们教程里说的p的数量。
返回值:
X_pad -- 返回填补后的样本集。维度是(m, n_H + 2*pad, n_W + 2*pad, n_C),每张图片的四周都填补了pad个0
"""
# np.pad是numpy提供的一个零填补函数,下面代码给X的n_H和n_W这两个维度填补pad个零。对m和n_C的维度不进行填补
# 例如第一组(pad, pad)表示给图像的上面和下面都填补pad个零。当然,上面和下面也可以填充不同数量的零。
X_pad = np.pad(X, ((0, 0), (pad, pad), (pad, pad), (0, 0)), 'constant', constant_values=0)
return X_pad
这个函数是一个用于对输入样本集进行零填充(zero padding)的函数。
-
def zero_pad(X, pad):
:这是函数的定义,它有两个参数,X
是输入的样本集,pad
是填充的数量。 -
函数的文档字符串(位于三重引号之间的注释)提供了函数的描述、参数说明和返回值说明。
-
np.pad(X, ((0, 0), (pad, pad), (pad, pad), (0, 0)), 'constant', constant_values=0)
:这是实际的填充操作。它使用了NumPy的np.pad
函数来对输入的样本集X
进行填充。具体细节如下:-
X_pad
是填充后的结果。 -
((0, 0), (pad, pad), (pad, pad), (0, 0))
是用于定义填充方式的参数。这个参数是一个元组,包含四个元组,分别对应着样本集X
的不同维度。在这里,它表示对样本数量维度不进行填充(0个填充),对高度维度和宽度维度都分别进行了pad
个填充,对通道数维度也不进行填充(0个填充)。 -
'constant'
是填充的模式,表示用常数值填充,常数值为0。 -
constant_values=0
指定了用于填充的常数值,这里是0。
-
-
最后,函数返回填充后的样本集
X_pad
,其维度是(m, n_H + 2*pad, n_W + 2*pad, n_C)
,其中m
是样本数量,n_H
和n_W
是输入样本的高度和宽度,n_C
是输入样本的通道数。填充后,每张图片的四周都被填充了pad
个0。
这个函数通常用于在卷积神经网络(CNN)中处理图像数据,以确保卷积操作能够正确地应用在图像的边缘上,而不会导致边缘信息丢失。
# 单元测试
np.random.seed(1)
x = np.random.randn(4, 3, 3, 2)
x_pad = zero_pad(x, 2)
print ("x.shape =", x.shape)
print ("x_pad.shape =", x_pad.shape)
print ("x[1, 1] =", x[1, 1])
print ("x_pad[1, 1] =", x_pad[1, 1])
fig, axarr = plt.subplots(1, 2)
axarr[0].set_title('x')
axarr[0].imshow(x[0,:,:,0])
axarr[1].set_title('x_pad')
axarr[1].imshow(x_pad[0,:,:,0])
这段代码是对上述zero_pad
函数进行单元测试的代码块。
-
np.random.seed(1)
:这里设置了随机数生成器的种子,以确保在每次运行代码时生成相同的随机数序列。这可以帮助确保测试结果的可重复性。 -
x
是一个随机生成的四维数组,维度为(4, 3, 3, 2)
,表示了一个样本集,其中包含了4张3x3大小的彩色图像(每张图像有2个通道)。这个数组被用作测试输入。 -
x_pad
是通过调用zero_pad(x, 2)
函数来生成的,这将对输入样本集x
进行2个单位的零填充。 -
接下来的四行代码用于打印一些信息,以展示输入数据和填充后的数据的形状以及一些具体数值。
-
fig, axarr = plt.subplots(1, 2)
:这里创建了一个包含两个子图的图形对象,用于在一行中显示两个图像。 -
axarr[0].set_title('x')
和axarr[1].set_title('x_pad')
:这两行代码设置了子图的标题。 -
axarr[0].imshow(x[0,:,:,0])
和axarr[1].imshow(x_pad[0,:,:,0])
:这两行代码分别在两个子图中显示了x
和x_pad
的第一个样本的第一个通道的内容。imshow
函数用于显示图像数据。
这段代码的目的是测试 zero_pad
函数是否能够正确地对输入数据进行零填充,并且在输出结果上显示填充前后的图像。如果一切正常,您应该能够看到两幅图像,左侧是原始输入 x
,右侧是填充后的 x_pad
。
def conv_single_step(a_slice_prev, W, b):
"""
这个函数只执行一步卷积
参数:
a_slice_prev -- 输入矩阵中的一小块数据,如上面的动图所示,过滤器每次只与矩阵中的一小块数据进行卷积。
-- 这里的输入矩阵也就是上一层的输出矩阵。维度是(f, f, n_C_prev)
W -- 权重参数w。其实这里就是指过滤器。过滤器就是权重参数w。
-- 维度是(f, f, n_C_prev),与a_slice_prev是一样的。因为是它俩进行卷积,所以维度肯定是一样的。
b -- 阈值b,教程中我们说过每一个过滤器会有一个对应的阈值。 维度是(1, 1, 1)
返回值:
Z -- 卷积一步后得到的一个数值。这个数值将是输出矩阵中的一个元素。
"""
# 将a_slice_prev与W的每一个元素进行相乘
s = np.multiply(a_slice_prev, W) + b
# 将上面相乘的结果累加起来
Z = np.sum(s)
return Z
这个函数实现了卷积神经网络 (CNN) 中的一步卷积操作。
-
def conv_single_step(a_slice_prev, W, b):
:这是函数的定义,它接受三个参数,a_slice_prev
是输入矩阵中的一小块数据,W
是权重参数(也称为过滤器),b
是阈值。 -
函数的文档字符串提供了函数的描述、参数说明和返回值说明。
-
s = np.multiply(a_slice_prev, W) + b
:这行代码执行一步卷积操作。具体细节如下:-
np.multiply(a_slice_prev, W)
对输入矩阵a_slice_prev
和权重参数W
中的对应元素进行逐元素相乘。这是卷积操作的一部分,用于计算卷积的局部加权和。 -
+ b
:将阈值b
加到上述相乘结果上。每个卷积滤波器都有一个对应的阈值。
-
-
Z = np.sum(s)
:这行代码计算了所有相乘和加权的结果的总和,得到卷积一步后的一个数值Z
。这个数值将成为输出矩阵中的一个元素。
最后,函数返回了计算得到的卷积结果 Z
。
这个函数是卷积神经网络中的关键部分,它用于在输入数据和卷积滤波器之间执行卷积操作。通常,这个操作会在输入数据的不同位置进行多次,以生成输出特征图。
np.random.seed(1)
a_slice_prev = np.random.randn(4, 4, 3)
W = np.random.randn(4, 4, 3)
b = np.random.randn(1, 1, 1)
Z = conv_single_step(a_slice_prev, W, b)
print("Z =", Z)
这段代码用于测试 conv_single_step
函数,对一个输入切片 a_slice_prev
和一个权重参数 W
进行一步卷积操作,并打印卷积的结果 Z
。
-
np.random.seed(1)
:这里设置了随机数生成器的种子,以确保在每次运行代码时生成相同的随机数序列,以保持结果的可重复性。 -
a_slice_prev
是一个随机生成的三维数组,维度为(4, 4, 3)
,表示一个输入切片,其中包含4x4大小的数据块,每个数据块有3个通道。这个数组是卷积操作的输入。 -
W
是一个随机生成的三维数组,维度也是(4, 4, 3)
,表示卷积操作中的权重参数(过滤器),它与输入切片a_slice_prev
具有相同的维度。 -
b
是一个随机生成的三维数组,维度为(1, 1, 1)
,表示卷积操作中的阈值(偏置)。 -
Z = conv_single_step(a_slice_prev, W, b)
:这一行调用了conv_single_step
函数,对输入切片a_slice_prev
和权重参数W
进行一步卷积操作,并将结果存储在变量Z
中。 -
print("Z =", Z)
:最后,打印卷积操作的结果Z
。
这个代码块测试了 conv_single_step
函数是否能够正确地执行一步卷积操作。如果一切正常,您应该能够看到卷积操作的结果 Z
的值被打印出来。
def conv_forward(A_prev, W, b, hparameters):
"""
实现卷积网络的前向传播
参数:
A_prev -- 本层的输入矩阵,也就是上一层的输出矩阵。维度是(m, n_H_prev, n_W_prev, n_C_prev)
W -- 权重,也就是过滤器。维度是 (f, f, n_C_prev, n_C)。后面的n_C表示过滤器的个数
b -- 阈值。维度是 (1, 1, 1, n_C)。一个过滤器配一个阈值。所以最后一维也是n_C
hparameters -- 超参数步长s和padding数p
返回值:
Z -- 输出矩阵,也就是卷积结果。维度是(m, n_H, n_W, n_C)
cache -- 缓存一些数值,以供反向传播时用。
"""
(m, n_H_prev, n_W_prev, n_C_prev) = A_prev.shape
(f, f, n_C_prev, n_C) = W.shape
stride = hparameters['stride'] # 步长s
pad = hparameters['pad'] # 填补数量p
# 计算输出矩阵的维度。参考上面提供的公式
n_H = int((n_H_prev - f + 2 * pad) / stride) + 1 # 使用int()来实现向下取整
n_W = int((n_W_prev - f + 2 * pad) / stride) + 1
# 初始化输出矩阵
Z = np.zeros((m, n_H, n_W, n_C))
# 给输入矩阵进行padding填补0
A_prev_pad = zero_pad(A_prev, pad)
for i in range(m): # 遍历每一个样本
a_prev_pad = A_prev_pad[i] # 取出一个样本对应的输入矩阵
for h in range(n_H): # 遍历输出矩阵的高
for w in range(n_W): # 遍历输出矩阵的宽
for c in range(n_C): # 遍历每一个过滤器
# 计算出输入矩阵中本次应该卷积的区域的索引,然后通过这些索引取出将被卷积的小块数据。
vert_start = h * stride
vert_end = vert_start + f
horiz_start = w * stride
horiz_end = horiz_start + f
a_slice_prev = a_prev_pad[vert_start:vert_end, horiz_start:horiz_end, :]
# 利用之前我们实现的conv_single_step函数来对这块数据进行卷积。
Z[i, h, w, c] = conv_single_step(a_slice_prev, W[...,c], b[...,c])
assert(Z.shape == (m, n_H, n_W, n_C))
cache = (A_prev, W, b, hparameters)
return Z, cache
这个函数实现了卷积神经网络 (CNN) 的前向传播操作,用于计算卷积层的输出。
-
def conv_forward(A_prev, W, b, hparameters):
:这是函数的定义,它接受四个参数,分别是上一层的输出A_prev
、权重参数W
、阈值b
以及包含超参数的字典hparameters
。 -
函数的文档字符串提供了函数的描述、参数说明和返回值说明。
-
(m, n_H_prev, n_W_prev, n_C_prev) = A_prev.shape
:这行代码从输入矩阵A_prev
中获取了各个维度的大小,其中m
是样本数量,n_H_prev
和n_W_prev
是上一层的输出的高度和宽度,n_C_prev
是上一层的输出的通道数。 -
(f, f, n_C_prev, n_C) = W.shape
:这行代码从权重参数W
中获取了各个维度的大小,其中f
是过滤器的高度和宽度(它是正方形的),n_C_prev
是输入通道数,n_C
是卷积层的过滤器个数。 -
stride = hparameters['stride']
和pad = hparameters['pad']
:这两行代码从超参数字典hparameters
中获取了步长 (stride
) 和填充数量 (pad
)。 -
计算输出矩阵的高度
n_H
和宽度n_W
,这是根据输入矩阵大小、过滤器大小、步长和填充数量计算得出的。 -
初始化输出矩阵
Z
,它的维度是(m, n_H, n_W, n_C)
,即样本数量m
、输出高度n_H
、输出宽度n_W
和卷积层的过滤器个数n_C
。 -
A_prev_pad = zero_pad(A_prev, pad)
:这行代码使用之前定义的zero_pad
函数对输入矩阵A_prev
进行填充。 -
接下来的嵌套循环用于遍历每个样本、输出矩阵的高度和宽度、以及卷积层的过滤器。在每个循环迭代中,执行以下操作:
-
计算出输入矩阵中本次应该卷积的区域的索引,然后通过这些索引取出将被卷积的小块数据。
-
利用之前实现的
conv_single_step
函数来对这块数据进行卷积操作,并将结果存储在输出矩阵Z
的相应位置。
-
-
assert(Z.shape == (m, n_H, n_W, n_C))
:这行代码用于确保输出矩阵Z
的形状与预期的形状相匹配。 -
最后,将一些数值缓存到
cache
中,以便在反向传播时使用。 -
函数返回输出矩阵
Z
和缓存cache
。
这个函数实现了卷积层的前向传播操作,它将输入矩阵 A_prev
与权重参数 W
进行卷积,并添加阈值 b
,最终生成输出矩阵 Z
。
np.random.seed(1)
A_prev = np.random.randn(10, 4, 4, 3)
W = np.random.randn(2, 2, 3, 8)
b = np.random.randn(1, 1, 1, 8)
hparameters = {"pad" : 2,
"stride": 1}
Z, cache_conv = conv_forward(A_prev, W, b, hparameters)
print("Z's mean =", np.mean(Z))
print("cache_conv[0][1][2][3] =", cache_conv[0][1][2][3])
这段代码用于测试 conv_forward
函数,执行卷积层的前向传播操作,并打印输出结果以及缓存值。
-
np.random.seed(1)
:这里设置了随机数生成器的种子,以确保在每次运行代码时生成相同的随机数序列,以保持结果的可重复性。 -
A_prev
是一个随机生成的四维数组,维度为(10, 4, 4, 3)
,表示一个包含10个样本的输入矩阵,每个样本是一个4x4大小的数据块,具有3个通道。 -
W
是一个随机生成的四维数组,维度为(2, 2, 3, 8)
,表示卷积层的权重参数,其中2x2
是过滤器的大小,3
是输入通道数,8
是卷积层的过滤器个数。 -
b
是一个随机生成的四维数组,维度为(1, 1, 1, 8)
,表示卷积层的阈值(偏置),每个过滤器都有一个对应的阈值。 -
hparameters
是包含超参数的字典,其中包括填充数量pad
和步长stride
。 -
Z, cache_conv = conv_forward(A_prev, W, b, hparameters)
:这一行调用了conv_forward
函数,执行卷积层的前向传播操作,并将输出矩阵Z
和缓存值cache_conv
存储在变量中。 -
print("Z's mean =", np.mean(Z))
:打印输出矩阵Z
的平均值。这是为了检查输出的大致范围。 -
print("cache_conv[0][1][2][3] =", cache_conv[0][1][2][3])
:打印缓存cache_conv
中的一个特定值,以检查是否正确存储了缓存。
这段代码块测试了 conv_forward
函数是否能够正确地执行卷积操作,并将结果存储在 Z
中,同时也存储了缓存值以备后用。