用 Excel 逐步展示 MLP 的前向传播、反向传播和参数更新配套 Excel 文件MLP计算过程.xlsx这篇文章配合 Excel 阅读不写成纯理论推导。为避免公式在不同博客平台、Windows 编辑器或 Markdown 预览器中出现乱码本文的公式全部使用 ASCII 写法。例如eta 表示学习率 alpha 表示 Leaky ReLU 负半轴斜率 delta_out 表示输出层误差信号 delta_h 表示隐藏层误差信号 sum(...) 表示求和 * 表示乘法1. 这个 Excel 展示了什么目标是用 Excel 把一个很小的 MLP也就是多层感知机完整拆开。它展示的不是训练很多轮之后的模型而是展示“一次样本输入之后模型如何完成一次前向传播、如何计算损失、如何反向传播得到梯度、如何根据梯度更新参数”。简单说这份 Excel 展示了下面这条链路输入样本 - 隐藏层线性组合 - 隐藏层激活输出 - 输出层线性组合 - 最终预测 - 损失 - 输出层梯度 - 隐藏层梯度 - 参数更新前后对比2. 先理解网络结构本例中的 MLP 非常小结构如下1 个输入特征 6 个隐藏层神经元 1 个输出层神经元也可以写成x - h1, h2, h3, h4, h5, h6 - y_hat其中x是输入样本。h1到h6是隐藏层的 6 个神经元。y_hat是模型最终预测值。y是真实目标值。本例使用的激活函数是 Leaky ReLU。为了避免公式乱码本文用alpha表示 Leaky ReLU 在负半轴的斜率。Leaky ReLU 的定义如下leaky_relu(z) z, if z 0 leaky_relu(z) alpha * z, if z 0本例中alpha 0.05所以如果某个神经元的线性组合结果是正数激活输出就是它本身如果是负数就乘以0.05保留一个较小的负值。3.参数与样本3.1 样本和超参数本例的输入、目标和学习率如下项目数值输入样本x5真实目标y208学习率eta0.01Leaky ReLU 负半轴斜率alpha0.05这里的学习率eta会在参数更新时使用new_parameter old_parameter - eta * gradient3.2 隐藏层参数每个隐藏层神经元都有两个参数W_xh_j 输入 x 到第 j 个隐藏神经元的权重 b_h_j 第 j 个隐藏神经元的偏置Excel 中整理出的初始参数如下隐藏神经元W_xhb_hh11010h22010h33010h42010h51010h620103.3 输出层参数每个隐藏层神经元到输出层都有一个权重W_ho_j 第 j 个隐藏神经元到输出层的权重对应数值如下隐藏神经元W_hoh110h220h310h410h530h620输出层还有一个偏置b_out 204.前向传播前向传播的作用是把输入x一步一步算成预测值y_hat再用预测值和真实值计算损失。4.1 第一步隐藏层线性组合对每个隐藏层神经元都先计算一个线性组合z_h_j x * W_xh_j b_h_j这里z_h_j表示第j个隐藏神经元的线性组合结果。x是输入样本。W_xh_j是输入到该隐藏神经元的权重。b_h_j是该隐藏神经元的偏置。以h1为例z_h_1 x * W_xh_1 b_h_1 z_h_1 5 * 10 10 z_h_1 606 个隐藏层神经元的线性组合结果如下隐藏神经元计算过程z_hh15 * 10 1060h25 * 20 10110h35 * 30 10160h45 * 20 10110h55 * 10 1060h65 * 20 101104.2 第二步隐藏层激活隐藏层线性组合之后要经过 Leaky ReLU 激活函数a_h_j leaky_relu(z_h_j)也就是a_h_j z_h_j, if z_h_j 0 a_h_j alpha * z_h_j, if z_h_j 0本例中所有z_h_j都是正数所以激活函数不会改变数值隐藏神经元z_ha_hh16060h2110110h3160160h4110110h56060h6110110这里的a_h是隐藏层输出它会作为输出层的输入。4.3 第三步输出层线性组合输出层把 6 个隐藏层输出加权求和然后加上输出层偏置。公式如下z_out sum(a_h_j * W_ho_j) b_out把本例的数值代入z_out 60 * 10 110 * 20 160 * 10 110 * 10 60 * 30 110 * 20 20逐项计算60 * 10 600 110 * 20 2200 160 * 10 1600 110 * 10 1100 60 * 30 1800 110 * 20 2200 b_out 20所以z_out 600 2200 1600 1100 1800 2200 20 z_out 9520excel SUMPRODUCT(hidden_activation_range, output_weight_range) output_bias4.4 第四步最终预测输出层线性组合之后仍然经过 Leaky ReLUy_hat leaky_relu(z_out)因为本例中z_out 9520它是正数所以y_hat 9520这就是当前模型对样本x 5的预测值。4.5 第五步计算损失真实目标值是y 208预测值是y_hat 9520本 Excel 使用平方损失Loss (y_hat - y)^2代入数值Loss (9520 - 208)^2 Loss 9312^2 Loss 86713344这个损失非常大说明当前初始参数让模型预测得过大。4.6 前向传播小结前向传播最终得到项目数值z_out9520y_hat9520y208Loss86713344到这里模型已经知道“自己预测错了多少”。下一步反向传播要解决的是每个参数应该为这个错误承担多少责任。5.反向传播反向传播的目标是计算梯度。梯度可以理解为如果某个参数变大一点损失会怎样变化。如果梯度是正数说明这个参数继续变大会让损失变大所以更新时要把它减小。如果梯度是负数说明这个参数继续变大会让损失变小所以更新时会把它增大。本例中大多数梯度是正数因为预测值9520远大于真实值208模型需要把很多参数往减小输出的方向调整。6. 先计算输出层误差信号delta_out损失函数是Loss (y_hat - y)^2先求损失对预测值的导数dLoss_dy_hat 2 * (y_hat - y)代入数值dLoss_dy_hat 2 * (9520 - 208) dLoss_dy_hat 2 * 9312 dLoss_dy_hat 18624然后考虑输出层激活函数的导数。输出层有y_hat leaky_relu(z_out)因为z_out 9520 0所以dy_hat_dz_out 1输出层误差信号定义为delta_out dLoss_dy_hat * dy_hat_dz_out代入数值delta_out 18624 * 1 delta_out 186247. 输出层权重梯度输出层的公式是z_out sum(a_h_j * W_ho_j) b_out对于某个输出层权重W_ho_j它的梯度是dLoss_dW_ho_j a_h_j * delta_out为什么是这样因为W_ho_j只通过这一项影响输出层a_h_j * W_ho_j所以W_ho_j对z_out的影响大小就是a_h_j。再乘上输出层误差信号delta_out就得到损失对这个权重的梯度。输出层 6 个权重的梯度如下权重a_hdelta_out梯度 dLoss_dW_hoW_ho_160186241117440W_ho_2110186242048640W_ho_3160186242979840W_ho_4110186242048640W_ho_560186241117440W_ho_6110186242048640以W_ho_1为例dLoss_dW_ho_1 a_h_1 * delta_out dLoss_dW_ho_1 60 * 18624 dLoss_dW_ho_1 11174408. 输出层偏置梯度输出层线性组合中偏置是直接加上的z_out sum(a_h_j * W_ho_j) b_outb_out每增加 1z_out就增加 1所以dz_out_db_out 1因此输出层偏置梯度就是dLoss_db_out delta_out dLoss_db_out 186249. 隐藏层误差信号delta_h隐藏层梯度要比输出层多一步因为隐藏层不是直接产生最终预测而是先影响输出层再影响损失。隐藏层误差信号的公式是delta_h_j delta_out * W_ho_j * f_prime_z_h_j其中delta_out表示输出层错了多少。W_ho_j表示第j个隐藏神经元对输出层影响有多大。f_prime_z_h_j表示隐藏层激活函数在当前位置的导数。本例中所有隐藏层的z_h都是正数所以 Leaky ReLU 的导数都是f_prime_z_h_j 1因此隐藏层误差信号变成delta_h_j delta_out * W_ho_j逐个计算隐藏神经元delta_outW_hof_prime_z_hdelta_hh118624101186240h218624201372480h318624101186240h418624101186240h518624301558720h618624201372480以h5为例delta_h_5 delta_out * W_ho_5 * f_prime_z_h_5 delta_h_5 18624 * 30 * 1 delta_h_5 558720这说明h5这条路径对输出影响很大因为它连接到输出层的权重W_ho_5 30所以它分到的误差信号也更大。10. 隐藏层输入权重梯度隐藏层线性组合是z_h_j x * W_xh_j b_h_j对于隐藏层输入权重W_xh_j梯度公式是dLoss_dW_xh_j x * delta_h_j因为本例中x 5所以每个隐藏层输入权重梯度就是dLoss_dW_xh_j 5 * delta_h_j逐个计算权重xdelta_h梯度 dLoss_dW_xhW_xh_15186240931200W_xh_253724801862400W_xh_35186240931200W_xh_45186240931200W_xh_555587202793600W_xh_653724801862400以W_xh_2为例dLoss_dW_xh_2 x * delta_h_2 dLoss_dW_xh_2 5 * 372480 dLoss_dW_xh_2 186240011. 隐藏层偏置梯度隐藏层线性组合中偏置也是直接加上的z_h_j x * W_xh_j b_h_j所以dz_h_j_db_h_j 1因此隐藏层偏置梯度就是dLoss_db_h_j delta_h_j逐个结果如下偏置梯度 dLoss_db_hb_h_1186240b_h_2372480b_h_3186240b_h_4186240b_h_5558720b_h_637248012.参数更新参数更新使用同一个规则new_parameter old_parameter - eta * gradient本例中eta 0.01所以更新幅度是adjustment eta * gradient再从旧参数中减去这个调整幅度。12.1 输出层权重更新以W_ho_1为例old W_ho_1 10 gradient 1117440 eta 0.01 adjustment 0.01 * 1117440 adjustment 11174.4 new W_ho_1 10 - 11174.4 new W_ho_1 -11164.4输出层权重更新结果如下参数旧值梯度调整幅度更新后W_ho_110111744011174.4-11164.4W_ho_220204864020486.4-20466.4W_ho_310297984029798.4-29788.4W_ho_410204864020486.4-20476.4W_ho_530111744011174.4-11144.4W_ho_620204864020486.4-20466.412.2 输出层偏置更新输出层偏置更新如下old b_out 20 gradient 18624 eta 0.01 adjustment 0.01 * 18624 adjustment 186.24 new b_out 20 - 186.24 new b_out -166.2412.3 隐藏层输入权重更新隐藏层输入权重也使用同一个规则new W_xh_j old W_xh_j - eta * dLoss_dW_xh_j更新结果如下参数旧值梯度调整幅度更新后W_xh_1109312009312-9302W_xh_220186240018624-18604W_xh_3309312009312-9282W_xh_4209312009312-9292W_xh_510279360027936-27926W_xh_620186240018624-1860412.4 隐藏层偏置更新隐藏层偏置更新公式new b_h_j old b_h_j - eta * dLoss_db_h_j更新结果如下参数旧值梯度调整幅度更新后b_h_1101862401862.4-1852.4b_h_2103724803724.8-3714.8b_h_3101862401862.4-1852.4b_h_4101862401862.4-1852.4b_h_5105587205587.2-5577.2b_h_6103724803724.8-3714.813. 为什么这里的参数会变成很大的负数有同学可能会疑惑为什么更新后很多参数一下变成很大的负数原因有两个第一本例预测值远大于真实值y_hat 9520 y 208误差很大y_hat - y 9312第二本例没有做归一化也没有把损失写成0.5 * error^2所以导数比较大dLoss_dy_hat 2 * 9312 18624隐藏层和输出层权重又会继续放大梯度。例如dLoss_dW_ho_3 a_h_3 * delta_out dLoss_dW_ho_3 160 * 18624 dLoss_dW_ho_3 2979840学习率虽然只有0.01但乘上几百万级别的梯度后调整幅度仍然很大adjustment 0.01 * 2979840 adjustment 29798.4所以更新后参数会发生大幅变化。这也是 Excel 教学展示的价值它能让我们直接看到梯度为什么大、更新为什么大而不是只记住一个抽象公式。14. 一句话总结这份 Excel 展示的是 MLP 的一次完整计算前向传播用当前参数算出预测值和损失。 反向传播从损失开始计算每个参数的梯度。 参数更新用 new old - eta * gradient 更新参数。如果只记住一个核心逻辑可以记成先算模型错了多少再算每个参数对错误负多少责任最后按责任大小调整参数。