卷积层前向传播函数
def conv_forward(X, W, b, stride=1, padding=0):
"""
实现卷积层的前向传播。
参数:
- X: 输入数据的numpy数组,形状为(num_examples, height, width, channels),
其中num_examples是样本数量,height和width是每个样本的高度和宽度,
channels是输入数据的通道数。
- W: 卷积核权重的numpy数组,形状为(filter_height, filter_width, input_channels, output_channels),
其中filter_height和filter_width是卷积核的高度和宽度,
input_channels是输入数据的通道数(应与X的channels相匹配),
output_channels是输出数据的通道数。
- b: 偏置项的numpy数组,形状为(1, 1, 1, output_channels),为每个输出通道提供一个偏置值。
- stride: 卷积的步长,指卷积核滑动的距离。
- padding: 输入数据的零填充数量,指在输入数据的边缘添加的零的层数。
返回:
- out: 卷积操作的输出,形状为(num_examples, out_height, out_width, output_channels),
其中out_height和out_width是输出数据的高度和宽度。
"""
# 从输入数据X中提取维度信息。
(num_examples, height, width, channels) = X.shape
# 从卷积核权重W中提取维度信息。
(filter_height, filter_width, _, output_channels) = W.shape
# 根据卷积操作的公式计算输出的高度和宽度。
out_height = int((height - filter_height + 2 * padding) / stride + 1)
out_width = int((width - filter_width + 2 * padding) / stride + 1)
# 对输入数据X应用零填充。
X_padded = np.pad(X, ((0,0), (padding, padding), (padding, padding), (0,0)), mode='constant')
# 初始化卷积操作的输出数组。
out = np.zeros((num_examples, out_height, out_width, output_channels))
# 进行卷积操作。
for n in range(num_examples): # 遍历每个样本
for h in range(out_height): # 遍历输出的高度
for w in range(out_width): # 遍历输出的宽度
for c in range(output_channels): # 遍历每个输出通道
# 计算当前卷积核覆盖的输入数据的起始和结束位置。
h_start = h * stride
h_end = h_start + filter_height
w_start = w * stride
w_end = w_start + filter_width
# 提取当前卷积核覆盖的输入数据片段。
X_slice = X_padded[n, h_start:h_end, w_start:w_end, :]
# 计算卷积操作,并加上偏置项。
out[n, h, w, c] = np.sum(X_slice * W[...,c]) + b[...,c]
return out
池化层前向传播函数
def pool_forward(X, size=2, stride=2, mode='max'):
"""
X: 输入数据,形状为(num_examples, height, width, channels)。
同卷积层输入,包含多个样本和多个通道。
size: 池化窗口的大小,表示在输入数据上滑动并进行池化操作的区域。
stride: 池化操作的步长,决定池化窗口滑动时每次移动的像素数。
mode: 池化模式,'max' 代表最大池化,'average' 代表平均池化。
计算输出特征映射的尺寸,遍历每个样本、每个输出位置和每个通道。
对于每个池化窗口,根据池化模式选择窗口内的最大值或平均值作为输出。
"""
(num_examples, height, width, channels) = X.shape
out_height = int((height - size) / stride + 1)
out_width = int((width - size) / stride + 1)
# 初始化输出特征映射
out = np.zeros((num_examples, out_height, out_width, channels))
for n in range(num_examples): # 对每个样本
for h in range(out_height): # 对每个输出高度
for w in range(out_width): # 对每个输出宽度
for c in range(channels): # 对每个通道
# 定位当前池化操作的输入数据片段
h_start = h * stride
h_end = h_start + size
w_start = w * stride
w_end = w_start + size
# 提取输入数据片段
X_slice = X[n, h_start:h_end, w_start:w_end, c]
# 根据池化模式计算输出值
if mode == 'max':
out[n, h, w, c] = np.max(X_slice)
elif mode == 'average':
out[n, h, w, c] = np.mean(X_slice)
return out
全连接层
def fully_connected_forward(X, W, b, activation_function=None):
"""
X: 输入数据,形状为(num_examples, num_features)。
其中,num_examples 是样本数量,num_features 是每个样本的特征数量。
W: 权重矩阵,形状为(num_features, num_outputs)。
其中,num_features 是输入特征的数量,num_outputs 是该层输出节点的数量。
b: 偏置向量,形状为(num_outputs,)。
为每个输出节点添加的固定偏置。
activation_function: 应用于层输出的激活函数。如果为None,则不应用激活函数。
该函数执行以下操作:
1. 计算输入X和权重W的矩阵乘法,然后对每个样本的结果添加偏置b。
2. 如果指定了激活函数,将其应用于上一步的结果。
3. 返回最终的输出。
"""
# 矩阵乘法加偏置
Z = np.dot(X, W) + b
# 应用激活函数(如果有)
if activation_function is not None:
A = activation_function(Z)
else:
A = Z # 没有激活函数,输出就是Z
return A
# 示例激活函数:ReLU
def relu