Zero-Padding
在卷积神经网络(CNN)中,Zero-Padding(零填充)是一种在输入数据(如图像)的周围添加零值像素的技术。它的主要目的是控制卷积操作后输出的空间尺寸(宽度和高度),同时尽可能多地保留边缘信息。
Zero-Padding的作用:
(1)保持输出尺寸
- 普通卷积操作会导致特征图(Feature Map)的尺寸缩小,例如:输入尺寸为nn,卷积核为kk,输出尺寸为。
(2)保留边缘信息
- 角落边的像素只被一个卷积计算所碰触,对输出的影响较小,图像中的大部分信息都丢失了,而中间的区域的像素,会多次参与卷积计算,输出的影响较大。Zero-Padding可以让边缘像素也能被更多的卷积核完整覆盖,避免信息损失。
(3)支持更深的网络
- 在深层网络中,多次卷积可能导致特征图尺寸过小(甚至小于卷积核尺寸),无法继续运算。通过Zero-Padding,可以维持特征图的尺寸,从而支持更深的网络设计。
Zero-Padding实现
- 通常用𝑝表示,表示在输入的每一边添加的零值行/列数。如果输入尺寸为,卷积核为,步长为,填充量为,则输出尺寸为:
def zero_pad(X, pad): """ 对数据集X的所有样本进行0填充 Parameters: X -- numpy数组,未填充的数据集, shape=[num_samples, num_Height, num_Width, num_Chanel] pad -- 是一个整数,表示对样本高宽填充的大小 Returns: numpy数组,填充后的数据集 """ X_pad = np.pad(X, ((0,0), (pad,pad), (pad,pad), (0,0)), mode='constant') return X_pad import numpy as npX = np.random.randn(5,3,3,3)pad = 1X_pad = zero_pad(X, pad)print(f"X.shape={X.shape}")print(f"X_pad.shape={X_pad.shape}")print("填充前red:")print(X[0,:,:,0])print("填充后red通道:")print(X_pad[0,:,:,0])
X.shape=(5, 3, 3, 3)X_pad.shape=(5, 5, 5, 3)填充前red:[[-1.15376398 -0.75258694 0.24848238] [ 2.77979566 -0.32868589 -1.54132054] [-0.21763341 -2.30183587 -0.40836615]]填充后red通道:[[ 0. 0. 0. 0. 0. ] [ 0. -1.15376398 -0.75258694 0.24848238 0. ] [ 0. 2.77979566 -0.32868589 -1.54132054 0. ] [ 0. -0.21763341 -2.30183587 -0.40836615 0. ] [ 0. 0. 0. 0. 0. ]]
实现一次卷积计算函数
这一部分中,实现一步卷积,也就是将滤波器应用于输入的单个位置。
np.multiply是NumPy 库中用于执行逐元素乘法(Element-wise Multiplication)的函数。它与 * 运算符在数组之间的运算效果相同,但与矩阵乘法(np.dot 或 @)完全不同
def conv_single_step(a_slice, w, b): """ Arguments: a_slice -- numpy数组,输入数据切片, (n_h_filter, n_w_filter, n_c_filter) w -- numpy数组,过滤器权值参数, (n_h_filter, n_w_filter, n_c_filter) b -- 标量,过滤器偏置参数,(1,1,1) Returns: Z -- 一个数值, sum(a_slice*w) + b。 """ s = np.multiply(a_slice, w) Z = np.sum(s) Z = Z + float(b) # Z = Z + b return Z #1、随机生成一个大小为[3,3,3]的过滤器权值矩阵w#2、随机生成一个大小为[1,1,1]的过滤器偏置矩阵b#3、随机生成一个供卷积运算的的输入切片x;#4、调用conv_single_step函数并输出结果z。w = np.random.rand(3,3,3)b = 10x = np.random.rand(3,3,3)z = conv_single_step(x, w, b)print(z)#18.384722200888632
卷积层-实现卷积前向传播
在卷积层的前向传播过程中,需要使用多个过滤器在输入数据上对其进行卷积运算。每个过滤器都会输出一个2D矩阵。然后,堆叠这些2D输出矩阵以获得3D矩阵作为卷积层的输出。
关键点:
- 选择矩阵左上角的切片:a_slice_prev = a_prev[0:2, 0:2, :]
卷积的输出矩阵大小为
计算公式为:
使用for循环完成样本和卷积的移动
注意:
A_prev -- 卷积层的输入: shape=[num_samples, num_H_sample, num_W_sample, num_C_sample]
W -- 卷积层的所有卷积核组成的权值张量:shape=[num_H_filter, num_W_filter, num_C_filter, num_filters],
这个 num_C_sample==num_C_filter
import numpy as npdef zero_pad(X, pad): """ 对数据集X的所有样本进行0填充 Parameters: X -- numpy数组,未填充的数据集, shape=[num_samples, num_Height, num_Width, num_Chanel] pad -- 是一个整数,表示对样本高宽填充的大小 Returns: numpy数组,填充后的数据集 """ X_pad = np.pad(X, ((0,0), (pad,pad), (pad,pad), (0,0)), mode='constant') return X_paddef conv_single_step(a_slice, w, b): """ Arguments: a_slice -- numpy数组,输入数据切片, (n_h_filter, n_w_filter, n_c_filter) w -- numpy数组,过滤器权值参数, (n_h_filter, n_w_filter, n_c_filter) b -- 标量,过滤器偏置参数,(1,1,1) Returns: Z -- 一个数值, sum(a_slice*w) + b。 """ s = np.multiply(a_slice, w) Z = np.sum(s) Z = Z + float(b) # Z = Z + b return Zdef conv_3d(inp, W, B, stride, pad): """ Functions: 对3d数据进行卷积计算 Parameters: inp -- numpy数组,卷积层的输入 shape=[num_samples, num_H_sample, num_W_sample, num_C_sample] W -- numpy数组,卷积层的所有卷积核(过滤器)组成的权值张量 shape=[num_H_filter, num_W_filter, num_C_filter, num_filters] B -- 卷积层所有卷积核的偏置组成的向量: shape=[1,1,1,num_filters] stride -- 卷积移动的步长 pad -- 填充大小 Returns: Z -- 线性输出,卷积运算后的结果 """ n_samples, n_H_sample, n_W_sample, n_C_sample = inp.shape # 样本 n_h_filter, n_w_filter, n_c_filter, n_filters = W.shape #卷积核 n_H = int((n_H_sample - n_h_filter + 2*pad)/stride) + 1 n_W = int((n_W_sample - n_w_filter + 2*pad)/stride) + 1 n_C = n_filters Z = np.zeros((n_samples, n_H, n_W, n_C)) # 构造输出矩阵 inp_pad = zero_pad(inp, pad) #pading for i in range(n_samples): # 遍历训练集的每个样本 sample = inp_pad[i] # 选择第i个样本 for h in range(n_H): # 遍历输出矩阵体的高 # 垂直方向,初始 h=0 0:stride vert_start = stride * h # 固定样本垂直方向上的起始位置 vert_end = vert_start + n_h_filter # 固定样本垂直方向上的结束位置 for w in range(0, n_W): # 遍历输出矩阵的宽 # 水平方向 初始 w=0 0:stride horiz_start = stride * w # 固定样本在水平方向上的起始位置 horiz_end = horiz_start + n_w_filter # 固定样本在水平方向上的结束位置 for c in range(0, n_C): # 从样本上切片 (n_h_filter, n_w_filter, n_c_filter) a_slice = sample[vert_start:vert_end,horiz_start:horiz_end,:] Z[i,h,w,c] = conv_single_step(a_slice,W[:,:,:,c],B[:,:,:,c]) return Zsample=np.random.randn(10,5,5,3)# sample =sample[0] # 得到第i个样本print("样本的shape:",sample.shape)W=np.random.randn(3,3,3,8)B=np.random.randn(1,1,1,8)Z = conv_3d(sample, W, B, stride=1, pad=0)print("卷积后的shape:",Z.shape) #样本的shape: (10, 5, 5, 3)#卷积后的shape: (10, 3, 3, 8)