为对抗过拟合并提升神经网络模型在未见数据上的表现,需要应用正则化技术。这包括修改网络训练过程的核心组成部分,具体是损失计算和前向/后向传播,以引入L2正则化和Dropout。我们将假设你已经设置了一个基本的神经网络结构和训练循环,可能类似于前一章中构建的那个。我们这里的侧重点是添加正则化所需的修改。实现L2正则化L2正则化在损失函数中增加一个基于权重平方大小的惩罚项。目标是保持权重较小,防止模型过度依赖任何单个输入特征。1. 修改损失函数原始损失函数(例如,交叉熵或均方误差)衡量预测误差。对于L2正则化,我们增加一个惩罚项。新的L2正则化损失 $J_{reg}$ 为:$$ J_{reg} = J_{原始} + \frac{\lambda}{2m} \sum_{l} ||W^{[l]}||_F^2 $$这里:$J_{原始}$ 是原始损失(例如,交叉熵)。$\lambda$ (lambda) 是正则化超参数。它控制惩罚的强度。较高的 $\lambda$ 意味着更强的正则化(权重更小)。$m$ 是批次中的样本数量。$W^{[l]}$ 代表层 $l$ 的权重矩阵。$||W^{[l]}||_F^2$ 是层 $l$ 的权重矩阵的平方Frobenius范数,它就是该层所有权重平方的和。我们通常不对偏置项进行正则化。在你的代码中,计算批次总损失时,你会计算原始损失,然后添加此惩罚项。你需要访问网络中的所有权重矩阵。# 示例:计算L2正则化成本 # 假设 'parameters' 是一个包含权重矩阵 W1, W2, ... 的字典 # 且 'lambd' 是正则化超参数 # 且 'm' 是批次大小 l2_cost = 0 num_layers = len(parameters) // 2 # 假设参数是 W1, b1, W2, b2, ... for l in range(1, num_layers + 1): W = parameters['W' + str(l)] l2_cost += np.sum(np.square(W)) l2_cost = (lambd / (2 * m)) * l2_cost total_cost = original_cost + l2_cost2. 修改梯度计算(反向传播)由于损失函数发生了变化,相对于权重的梯度也随之变化。L2惩罚项相对于权重矩阵 $W^{[l]}$ 的导数是 $\frac{\lambda}{m} W^{[l]}$。在反向传播期间,此项需要添加到每个权重矩阵的原始梯度计算中。权重的更新规则变为:$$ \frac{\partial J_{reg}}{\partial W^{[l]}} = \frac{\partial J_{原始}}{\partial W^{[l]}} + \frac{\lambda}{m} W^{[l]} $$因此,在你的反向传播实现中,计算原始梯度 $dW^{[l]}$(即 $\frac{\partial J_{原始}}{\partial W^{[l]}}$)之后,你只需添加正则化项:# 示例:反向传播期间修改 W[l] 的梯度计算 # 假设 dW_original 是未正则化时计算的梯度 dW_regularized = dW_original + (lambd / m) * parameters['W' + str(l)] # 在参数更新步骤中使用 dW_regularized # parameters['W' + str(l)] = parameters['W' + str(l)] - learning_rate * dW_regularized请记住,偏置梯度 $db^{[l]}$ 通常不被正则化,因此它们的计算保持不变。实现DropoutDropout的工作方式不同。它不直接改变损失函数,而是在训练期间修改网络结构本身。在训练期间的每次前向传播中,Dropout会随机“丢弃”(设为零)一层中一部分神经元的输出。1. 修改前向传播在特定层的前向传播期间(通常应用于隐藏层),计算激活(例如,应用ReLU或Tanh之后)之后,你执行以下步骤:创建一个Dropout掩码:生成一个与层激活输出 A 形状相同的矩阵 D。此矩阵的每个元素会随机为0(概率为 keep_prob)或1(概率为 1 - keep_prob)。keep_prob 是保持神经元激活的概率。应用掩码:将激活 A 与掩码 D 进行元素级乘法。这有效地将一些激活设为零 (A_dropped = A * D)。缩放剩余激活(反向Dropout):将结果除以 keep_prob (A_scaled = A_dropped / keep_prob)。这种缩放确保了层在训练期间的期望输出与测试期间(当dropout关闭时)的期望输出保持一致。这种被称为反向Dropout的技术是标准做法。# 示例:在层 l 的前向传播期间应用 dropout # A_prev 是前一层的激活 # W, b 是当前层的参数 # activation_func 是激活函数(例如,relu) # keep_prob 是保持神经元激活的概率 Z = np.dot(W, A_prev) + b A = activation_func(Z) # 激活输出 # 应用 Dropout D = np.random.rand(A.shape[0], A.shape[1]) < keep_prob # 创建掩码 A = A * D # 应用掩码 A = A / keep_prob # 缩放(反向Dropout) # 将掩码 D 与 Z 和 A 一起存储在缓存中,用于反向传播 # cache = (..., D, ...)2. 修改反向传播在反向传播期间,应用Dropout的层的梯度计算需要考虑掩码 D。需要对被丢弃的神经元关闭反向传播的梯度。你只需将相同掩码 D(你在前向传播期间存储在缓存中的)重新应用于激活 dA 在计算梯度 dW、db 和 dA_prev 之前。记住也要用 keep_prob 进行缩放。# 示例:在层 l 的反向传播期间应用 dropout 掩码 # dA 是损失相对于当前层激活 A 的梯度 # 缓存包含在该层前向传播中使用的掩码 D # keep_prob 是前向传播中使用的相同概率 # 从缓存中检索掩码 # D = cache[某个索引] # 获取该层使用的掩码 dA = dA * D # 应用前向传播期间使用的掩码 dA = dA / keep_prob # 应用缩放 # 使用修改后的 dA 继续其余的反向传播步骤 # dZ = dA * activation_gradient(Z) # dW = ... # db = ... # dA_prev = ...重要提示: Dropout应仅在训练期间处于活跃状态。在评估模型或对新数据进行预测时,你必须关闭Dropout。这表示在评估/预测模式的前向传播中,你不应用掩码或缩放。使用反向Dropout使这变得简单,因为你无需在训练后修改权重。观察效果你如何判断正则化是否有效?随训练周期监控你的训练和验证损失(以及准确率)。如果没有正则化,你可能会看到训练损失持续降低,而验证损失在某个点后开始增加或停滞。这种差距表明过拟合。理想情况下,应用L2正则化或Dropout应:减缓训练损失的下降(或者甚至比没有正则化时略有增加)。保持验证损失更低或使其下降更长时间,减小训练和验证表现之间的差距。{ "data": [ { "x": [1, 5, 10, 15, 20, 25, 30, 35, 40, 45, 50], "y": [1.5, 0.8, 0.5, 0.3, 0.2, 0.15, 0.12, 0.1, 0.09, 0.08, 0.075], "mode": "lines", "name": "训练损失(无正则化)", "line": {"color": "#339af0"} }, { "x": [1, 5, 10, 15, 20, 25, 30, 35, 40, 45, 50], "y": [1.6, 0.95, 0.65, 0.45, 0.35, 0.3, 0.27, 0.25, 0.24, 0.23, 0.225], "mode": "lines", "name": "训练损失(L2/Dropout)", "line": {"dash": "dash", "color": "#339af0"} }, { "x": [1, 5, 10, 15, 20, 25, 30, 35, 40, 45, 50], "y": [1.7, 1.1, 0.8, 0.7, 0.65, 0.68, 0.72, 0.75, 0.79, 0.83, 0.87], "mode": "lines", "name": "验证损失(无正则化)", "line": {"color": "#fd7e14"} }, { "x": [1, 5, 10, 15, 20, 25, 30, 35, 40, 45, 50], "y": [1.8, 1.2, 0.9, 0.8, 0.75, 0.72, 0.70, 0.69, 0.685, 0.68, 0.68], "mode": "lines", "name": "验证损失(L2/Dropout)", "line": {"dash": "dash", "color": "#fd7e14"} } ], "layout": { "title": "正则化对损失的影响", "xaxis": {"title": "训练周期"}, "yaxis": {"title": "损失"}, "legend": {"title": "图例"}, "colorway": ["#339af0", "#339af0", "#fd7e14", "#fd7e14"] } }训练和验证损失在有无正则化情况下的比较。正则化通常会略微增加训练损失,但会降低验证损失,从而减小差距,并显示出更好的泛化能力。这种实践涉及修改你的网络实现中的特定部分。首先尝试通过调整成本和梯度计算来添加L2正则化。然后,在前向传播期间尝试添加Dropout层,并确保你在反向传播期间正确处理它,并在评估期间将其关闭。观察它对你的训练和验证指标的影响,并尝试不同的正则化强度 $\lambda$ 或 Dropout keep_prob 值。