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 np
X = np.random.randn(5,3,3,3)
pad = 1
X_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 = 10
x = 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 np
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
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
def 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 Z
sample=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)