BP仿真实验设计

https://www.iiisle.com/archives/tBcmbcSj

为什么最终的误差和绝对误差值不一样?

误差计算方法的根本区别

训练过程中的误差(最终误差:0.0000000000)

计算方式:均方误差(MSE)

 % 代码中的计算方式
 E = E + 0.5 * sum(el.^2);  % 累加每个样本的误差

数学定义

  • 对每个样本:0.5 * (期望输出 - 实际输出)²

  • 对所有样本求和

  • 这是平方误差,放大了大误差的影响

特点

  • 训练时使用3个标准样本

  • 样本简单,容易达到完美逼近

  • 误差被平方,数值很小

函数逼近的绝对误差(平均绝对误差:0.648143)

计算方式:平均绝对误差(MAE)

 % 代码中的计算方式
 mean_error = mean(abs(Z_target(:) - Z_predicted(:)));

数学定义

  • 对测试网格中每个点:|目标值 - 预测值|

  • 对所有点求平均

  • 这是绝对值误差,反映平均偏差

特点

  • 测试时使用400个网格点(20×20)

  • 函数复杂:Z = sin(x)·sin(y)

  • 反映真实逼近能力

具体数值差异的原因分析

原因1:测试对象不同

  • 训练误差:针对3个简单标准样本

  • 绝对误差:针对复杂函数Z = sin(x)·sin(y) 的400个测试点

原因2:误差度量标准不同

  • 训练误差:平方误差 → 惩罚大误差更严厉

  • 绝对误差:绝对值误差 → 线性反映偏差

原因3:样本复杂度差异

  • 训练样本:线性映射关系,容易学习

  • 测试函数:非线性振荡函数,逼近困难

数学公式对比

误差类型

数学公式

特点

本次实验值

训练误差

E = 0.5 × Σ(期望-实际)²

平方惩罚,数值小

0.0000000000

平均绝对误差

MAE = mean(|目标-预测|)

线性反映,数值大

0.648143

均方误差

MSE = mean((目标-预测)²)

平方平均,数值中等

0.611564

mean函数具体计算方法MAE = (1/n) × ∑|y_i - ŷ_i|

实验结论

训练成功但泛化有限

  • 训练完美:对简单样本实现零误差映射

  • 泛化一般:对复杂函数逼近存在平均0.648的偏差

  • 误差度量差异:不同计算方法导致数值差异巨大

这体现了BP神经网络的一个典型特点:对训练样本可能达到很高精度,但对未见过复杂数据的泛化能力有限。误差值的差异正是这种训练-测试性能差异的量化体现。

BP算法理解

BP神经网络算法的工作逻辑

基本思想:"学习-纠错-改进"的循环过程

想象一下教一个小孩认字:

  • 第一次:你给他看"猫"字,他说"狗" → 错了

  • 你纠正:告诉他正确答案是"猫" → 计算误差

  • 他记住:下次看到"猫"字,更可能说"猫" → 调整记忆

BP神经网络就是这样的学习过程,只不过是用数学公式来实现的。

网络结构:三层"大脑"

 输入层 → 隐含层 → 输出层
  • 输入层:接收问题(比如看到"猫"字的笔画)

  • 隐含层:思考过程(大脑分析笔画特征)

  • 输出层:给出答案(说出"猫"字)

BP算法如何实现逼近

核心机制:误差反向传播

打个比方:你在射击训练

  1. 前向射击:瞄准目标开枪 → 子弹落在某个位置

  2. 观察误差:看子弹离靶心多远 → 计算偏差

  3. 反向调整:根据偏差调整瞄准角度 → 下次更准

具体实现步骤:

步骤1:前向传播(预测)
 输入信号 → 加权求和 → 激活函数 → 输出结果
  • 加权求和:每个连接都有"重要性权重"

  • 激活函数:决定神经元是否"兴奋"

  • 输出结果:网络的预测值

步骤2:误差计算
 误差 = 期望输出 - 实际输出
  • 计算预测值与真实值的差距

  • 差距越大,说明网络越"笨"

步骤3:反向传播(学习)
 从输出层 → 隐含层 → 输入层,逐层调整权重
  • 关键原理:链式法则(微积分)

  • 调整规则:误差大的连接,权重调整幅度大

步骤4:权重更新
 新权重 = 旧权重 + 学习率 × 误差梯度 + 动量项
  • 学习率:控制学习速度(太小学得慢,太大容易"学过头")

  • 动量项:防止"震荡",让学习更平稳

具体工作流程(基于MATLAB代码)

阶段1:准备阶段

 % 1. 初始化网络
 w1 = rands(3, 6);  % 输入层到隐含层权重(随机初始化)
 w2 = rands(6, 2);  % 隐含层到输出层权重
 ​
 % 2. 设置学习参数
 xite = 0.50;       % 学习率 - 控制学习速度
 alfa = 0.05;       % 动量因子 - 防止震荡

阶段2:训练循环(核心)

对每个样本执行:

前向传播:

% 隐含层计算
for j = 1:6
    I(j) = x * w1(:, j);           % 加权求和
    Iout(j) = 1/(1 + exp(-I(j)));  % Sigmoid激活函数
end

% 输出层计算
yl = w2' * Iout;  % 得到预测输出

误差计算:

el = y - yl;              % 计算误差向量
E = E + 0.5 * sum(el.^2); % 累计均方误差

反向传播:

% 输出层到隐含层权重调整
w2 = w2_1 + xite * Iout * el + alfa * (w2_1 - w2_2);

% 隐含层到输入层权重调整(链式法则)
for j = 1:6
    S = 1/(1 + exp(-I(j)));
    FI(j) = S * (1 - S);  % 计算Sigmoid函数的导数
end

for i = 1:3
    for j = 1:6
        % 计算权重梯度
        dw1(i,j) = xite * FI(j) * x(i) * (el(1)*w2(j,1) + el(2)*w2(j,2));
    end
end
w1 = w1_1 + dw1 + alfa * (w1_1 - w1_2);

阶段3:收敛判断

while E >= 1e-20 && k < 10000  % 直到误差足够小或达到最大迭代次数
    k = k + 1;  % 继续训练
end

逼近机制的本质

万能逼近定理

理论上,只要有足够多的隐含层神经元,3层BP网络可以逼近任何连续函数

权重调整的数学意义

  • 每个权重调整都是在修正函数曲面的形状

  • 通过大量微小调整,逐渐"雕刻"出目标函数的形状

学习过程可视化

  • 开始:网络输出是随机曲面

  • 训练中:曲面逐渐向目标函数靠拢

  • 收敛后:网络输出与目标函数高度相似

通俗理解的关键点

  1. "权重"就是"经验":权重越大,说明该连接越重要

  2. "激活函数"就是"思考方式":决定如何响应输入信号

  3. "反向传播"就是"反思改进":根据错误调整经验

  4. "学习率"就是"学习速度":太快容易错过最优解,太慢学习效率低

  5. "动量项"就是"惯性":让学习过程更平稳,避免来回震荡

这个算法之所以强大,是因为它通过大量简单的计算单元(神经元)和重复的调整过程,最终能够学习复杂的非线性关系,就像我们的大脑通过大量神经元的协同工作来学习复杂技能一样。

meshgrid函数介绍

meshgrid函数的作用

通俗理解:创建坐标网格

想象你要画一张地图:

  • x轴:经度(从西到东)

  • y轴:纬度(从南到北)

  • meshgrid就是帮你生成地图上所有经纬度交叉点的坐标

具体作用:

把两个一维数组(x坐标和y坐标)转换成两个二维矩阵,表示平面上所有点的坐标。

代码实现详解

原始数据:

x_test = linspace(-pi, pi, 20);    % 生成20个x坐标:[-π, -2.8, -2.6, ..., π]
y_test = linspace(-pi, pi, 20);    % 生成20个y坐标:[-π, -2.8, -2.6, ..., π]

meshgrid转换后:

[X_test, Y_test] = meshgrid(x_test, y_test);

转换结果

  • X_test:20×20矩阵,每行都是相同的x_test

  • Y_test:20×20矩阵,每列都是相同的y_test

实际生成的矩阵结构:

X_test = 
[-π, -2.8, -2.6, ..., π]    ← 第1行
[-π, -2.8, -2.6, ..., π]    ← 第2行
...
[-π, -2.8, -2.6, ..., π]    ← 第20行

Y_test = 
[-π, -π, -π, ..., -π]       ← 第1行
[-2.8, -2.8, -2.8, ..., -2.8] ← 第2行
...
[π, π, π, ..., π]           ← 第20行

可视化理解

生成的网格点:

( -π, -π)  ( -2.8, -π)  ( -2.6, -π)  ...  ( π, -π)
( -π, -2.8) ( -2.8, -2.8) ( -2.6, -2.8) ... ( π, -2.8)
( -π, -2.6) ( -2.8, -2.6) ( -2.6, -2.6) ... ( π, -2.6)
...
( -π, π)    ( -2.8, π)    ( -2.6, π)    ... ( π, π)

总共生成:20 × 20 = 400个测试点

手动实现meshgrid功能

如果你想自己实现meshgrid的功能,可以这样写:

% 手动实现meshgrid
X_test_manual = zeros(20, 20);
Y_test_manual = zeros(20, 20);

for i = 1:20          % 遍历y坐标
    for j = 1:20      % 遍历x坐标
        X_test_manual(i, j) = x_test(j);  % 第i行第j列 = x_test的第j个值
        Y_test_manual(i, j) = y_test(i);  % 第i行第j列 = y_test的第i个值
    end
end

在BP神经网络中的应用

目的:评估网络泛化能力

Z_target = sin(X_test) .* sin(Y_test);  % 计算目标函数在所有网格点的值

作用

  1. 创建测试集:400个均匀分布的测试点

  2. 计算真实值:得到目标函数Z = sin(x)·sin(y)的精确值

  3. 对比评估:与神经网络预测结果比较,评估逼近精度

具体应用场景:

% 假设已经训练好的神经网络
Z_predicted = zeros(20, 20);  % 存储神经网络预测结果

% 对每个网格点进行预测
for i = 1:20
    for j = 1:20
        % 将网格点坐标输入神经网络
        input_point = [X_test(i,j), Y_test(i,j), 1]';  % 添加偏置项
        % 前向传播计算预测值
        Z_predicted(i,j) = neural_network_predict(input_point, w1, w2);
    end
end

% 计算逼近误差
error = abs(Z_target - Z_predicted);

为什么需要meshgrid?

传统方法的问题:

% 如果不使用meshgrid,需要双重循环
Z_target_manual = zeros(20, 20);
for i = 1:20
    for j = 1:20
        Z_target_manual(i,j) = sin(x_test(j)) * sin(y_test(i));
    end
end

meshgrid的优势:

  1. 代码简洁:一行代码替代双重循环

  2. 计算高效:MATLAB对矩阵运算优化更好

  3. 便于绘图:直接用于surf, mesh等3D绘图函数

总结

meshgrid(x_test, y_test)的核心作用:

  1. 坐标转换:一维坐标 → 二维坐标网格

  2. 矩阵生成:创建等间距的测试点矩阵

  3. 函数评估:便于计算多元函数在所有网格点的值

  4. 可视化准备:为3D曲面图提供标准格式的坐标数据

在BP神经网络实验中,这个函数帮助我们系统性地评估网络在整个输入空间的表现,而不是仅仅在训练样本点上,从而更全面地分析网络的泛化能力和逼近精度。

归一化映射介绍

归一化映射的数学原理

线性变换公式

X_test_norm = (X_test + pi) / (2*pi);

数学推导:

这是一个线性变换,将原始区间 [-π, π] 映射到目标区间 [0, 1]

变换步骤

  1. 平移X_test + pi → 将区间从 [-π, π] 平移到 [0, 2π]

  2. 缩放/ (2*pi) → 将区间从 [0, 2π] 缩放到 [0, 1]

具体数值示例

原始数据范围

x_test = linspace(-pi, pi, 20);  % 生成:[-3.1416, ..., 3.1416]

映射过程演示:

原始值

计算步骤

归一化结果

-π ≈ -3.1416

(-3.1416 + 3.1416) / (2×3.1416)

0 / 6.2832 = 0

0

(0 + 3.1416) / 6.2832

3.1416 / 6.2832 = 0.5

π ≈ 3.1416

(3.1416 + 3.1416) / 6.2832

6.2832 / 6.2832 = 1

完整映射关系

原始区间 [-π, π] → 归一化区间 [0, 1]

边界点映射:
-π  →  (-π + π)/(2π) = 0/2π = 0
 0  →  (0 + π)/(2π) = π/2π = 0.5
+π  →  (π + π)/(2π) = 2π/2π = 1

中间点映射:
-π/2 ≈ -1.5708 → (-1.5708+3.1416)/6.2832 = 1.5708/6.2832 = 0.25
+π/2 ≈ +1.5708 → (1.5708+3.1416)/6.2832 = 4.7124/6.2832 = 0.75

通用线性映射公式

这个映射遵循标准的线性变换公式:

% 通用线性映射公式
归一化值 = (原始值 - 原区间最小值) / (原区间最大值 - 原区间最小值)

% 应用到本例:
原区间最小值 = -π
原区间最大值 = +π
归一化值 = (X_test - (-π)) / (π - (-π))
         = (X_test + π) / (2π)  % 与代码完全一致

在代码中的具体实现

原始数据:

X_test = 
[-3.1416, -2.8109, -2.4802, ..., 3.1416]  % 20×20网格
[-3.1416, -2.8109, -2.4802, ..., 3.1416]
...
[-3.1416, -2.8109, -2.4802, ..., 3.1416]

归一化后:

X_test_norm = 
[0.0000, 0.0526, 0.1053, ..., 1.0000]  % 全部映射到[0,1]
[0.0000, 0.0526, 0.1053, ..., 1.0000]
...
[0.0000, 0.0526, 0.1053, ..., 1.0000]

为什么需要归一化?

神经网络训练稳定性

  • Sigmoid激活函数在输入接近0时梯度最大

  • 输入范围[0,1]正好落在Sigmoid函数的敏感区域

  • 避免梯度消失或爆炸问题

权重初始化友好

w1 = rands(3, 6);  % 默认生成[-1,1]范围的随机权重

归一化后的输入与权重范围匹配,训练更稳定

收敛速度提升

  • 所有特征在相同尺度,梯度下降方向更准确

  • 学习率选择更容易

数值计算稳定性

避免大数值计算带来的数值精度问题

反向映射(如果需要)

如果训练后需要将预测结果映射回原始范围:

% 反向映射公式
X_original = X_test_norm * (2*pi) - pi;

% 验证:0 → -π, 0.5 → 0, 1 → +π
X_original = X_test_norm * 6.2832 - 3.1416;

映射的数学性质

线性保持性:

  • 直线映射为直线

  • 相对位置保持不变

  • 比例关系保持不变

可逆性:

映射是一一对应的,可以完全还原

总结

这段归一化代码的核心是:

  1. 数学原理:线性变换 (x + π)/(2π)

  2. 作用范围:将 [-π, π] 完美映射到 [0, 1]

  3. 实现方式:简单的矩阵运算,MATLAB自动广播到所有元素

  4. 实际效果:所有测试点均匀分布在[0,1]区间,便于神经网络处理

这种归一化是神经网络数据预处理的标准做法,能够显著提高训练效率和模型性能。

Sigmoid激活函数详解

数学定义

标准Sigmoid函数

f(x) = 1 / (1 + e^(-x))

函数特性

  • 定义域:(-∞, +∞)

  • 值域:(0, 1)

  • 中心对称点:(0, 0.5)

  • 单调性:严格单调递增

在MATLAB中的实现

基本实现

% Sigmoid函数实现
function output = sigmoid(x)
    output = 1 ./ (1 + exp(-x));
end

在BP神经网络中的具体应用

从搜索结果中可以看到实际代码实现:

% 隐含层神经元输出计算
for j = 1:6
    I(j) = x * w1(:, j);           % 步骤1:加权求和
    Iout(j) = 1/(1 + exp(-I(j)));  % 步骤2:Sigmoid激活
end

代码解析

  • x * w1(:, j):计算第j个隐含层神经元的输入(加权和)

  • 1/(1 + exp(-I(j))):应用Sigmoid函数,将输入映射到(0,1)区间

导数计算(反向传播关键)

Sigmoid导数公式

f'(x) = f(x) * (1 - f(x))

MATLAB实现

% 在反向传播中的导数计算
for j = 1:6
    S = 1/(1 + exp(-I(j)));    % Sigmoid输出
    FI(j) = S * (1 - S);       % 导数计算
end

数学推导

f(x) = 1/(1+e^(-x))
f'(x) = e^(-x)/(1+e^(-x))² 
       = [1/(1+e^(-x))] * [1 - 1/(1+e^(-x))]
       = f(x) * (1 - f(x))

主要作用

非线性变换

  • 输入:任意实数 (-∞, +∞)

  • 输出:压缩到 (0, 1) 区间

  • 效果:引入非线性,使神经网络能够学习复杂模式

概率解释

  • 输出值可以解释为概率激活程度

  • 接近0:神经元"不激活"

  • 接近1:神经元"强烈激活"

  • 0.5:决策边界

梯度平滑

  • 导数处处连续且非零

  • 便于梯度下降算法优化

在BP神经网络中的具体作用

前向传播

% 将线性组合转换为非线性激活
Iout(j) = 1/(1 + exp(-I(j)));  % 线性→非线性

反向传播误差计算

% 通过链式法则传播误差
for i = 1:3
    for j = 1:6
        dw1(i,j) = xite * FI(j) * x(i) * (el(1)*w2(j,1) + el(2)*w2(j,2));
        %        ↑学习率  ↑Sigmoid导数 ↑输入    ↑输出误差反向传播
    end
end

优缺点分析

优点

  1. 输出范围固定:(0,1)便于解释

  2. 函数平滑:处处可导,适合梯度下降

  3. 概率解释:输出可视为概率值

缺点

  1. 梯度消失:当|x|较大时,梯度接近0

  2. 输出非零中心:导致梯度下降效率较低

  3. 计算开销:涉及指数运算

与其他激活函数对比

激活函数

输出范围

是否零中心

梯度消失问题

Sigmoid

(0,1)

❌ 否

严重

Tanh

(-1,1)

✅ 是

中等

ReLU

[0,∞)

❌ 否

缓解

实际应用建议

适用场景:

  • 二分类问题的输出层

  • 需要概率解释的场合

  • 浅层神经网络的隐含层

不适用场景:

  • 深层神经网络(梯度消失严重)

  • 需要快速收敛的任务

  • 大规模数据处理

总结

Sigmoid激活函数在BP神经网络中扮演着关键角色:

  1. 定义f(x) = 1/(1+e^(-x)),将输入压缩到(0,1)

  2. 实现:MATLAB中直接使用1/(1+exp(-x))公式

  3. 作用:引入非线性、提供概率解释、支持梯度反向传播

在您提供的BP神经网络代码中,Sigmoid函数是隐含层的核心激活机制,通过将线性加权和转换为非线性输出,使网络能够学习复杂的输入-输出映射关系。

基于搜索到的BP神经网络代码,我来详细分析偏置项1的作用:

偏置项1的介绍

在代码中定位偏置项1

从搜索结果中可以看到关键代码片段:

% 前向传播过程
for j = 1:6 % 遍历隐含层每个神经元
    I(j) = x * w1(:, j); % 计算隐含层第j个神经元的输入(加权和)
    Iout(j) = 1/(1 + exp(-I(j))); % 使用S型激活函数计算输出
end

偏置项1的具体作用

数学表达

在BP神经网络中,偏置项通常作为额外的输入:

I(j) = w1(1,j)*x1 + w1(2,j)*x2 + w1(3,j)*1

其中:

  • w1(1,j):输入x1到第j个隐含层神经元的权重

  • w1(2,j):输入x2到第j个隐含层神经元的权重

  • w1(3,j)偏置项1的权重(对应常数输入1)

作用机制

平移激活函数

I(j) = x * w1(:, j);  % 包含偏置项的加权和
Iout(j) = 1/(1 + exp(-I(j)));  % Sigmoid激活

偏置项的效果

  • 当所有输入为0时:I(j) = w1(3,j)(偏置权重)

  • 偏置项水平移动Sigmoid函数,改变激活阈值

具体功能

调整激活阈值
% 没有偏置项:激活阈值固定在0
I(j) = w1(1,j)*x1 + w1(2,j)*x2
Iout(j) = 1/(1 + exp(-I(j)))  % 在I(j)=0时输出0.5

% 有偏置项:激活阈值可调节
I(j) = w1(1,j)*x1 + w1(2,j)*x2 + w1(3,j)*1
Iout(j) = 1/(1 + exp(-I(j)))  % 在I(j)=-w1(3,j)时输出0.5
增加模型灵活性
  • 没有偏置项:决策边界必须通过原点

  • 有偏置项:决策边界可以在任意位置

处理零输入情况

当所有输入特征为0时:

  • 没有偏置项:神经元输出固定为0.5(Sigmoid中心)

  • 有偏置项:神经元输出由偏置权重决定

在反向传播中的更新

从代码片段可以看到偏置项在权重更新中的作用:

for i = 1:3  % 遍历所有输入(包括偏置项)
    for j = 1:6
        dw1(i,j) = xite * FI(j) * x(i) * (el(1)*w2(j,1) + el(2)*w2(j,2));
    end
end

偏置项的特殊性

  • i=3时,x(3) = 1(偏置项输入)

  • 偏置项的梯度:dw1(3,j) = xite * FI(j) * 1 * error_term

实际应用示例

在正弦函数逼近任务中

% 输入向量构造
x = [X_norm, Y_norm, 1]  % 前两个是坐标,第三个是偏置项1

% 隐含层计算
for j = 1:6
    I(j) = x(1)*w1(1,j) + x(2)*w1(2,j) + 1*w1(3,j)
    Iout(j) = sigmoid(I(j))
end

偏置项对逼近效果的影响

  1. 调节激活区域:偏置权重决定神经元在输入空间的哪个区域激活

  2. 补偿非线性:帮助网络更好地逼近复杂的正弦函数曲面

  3. 提高拟合能力:增加了一个可学习的参数,提升模型表达能力

总结

偏置项1在BP神经网络中的作用

  1. 数学功能:作为常数输入1,对应权重w1(3,j),平移激活函数

  2. 网络效果:增加模型灵活性,允许决策边界不通过原点

  3. 训练作用:通过反向传播学习最优的激活阈值

  4. 实际意义:在函数逼近任务中,帮助网络更好地拟合复杂的非线性关系

偏置项1是网络能够成功逼近Z = sin(X)·sin(Y)函数的关键组件之一,它提供了必要的平移自由度,使网络能够更精确地匹配目标函数的形状和位置。

BP神经网络各步骤代码实现

BP神经网络各步骤代码实现详解

初始化阶段

% 网络权重初始化
w1 = rands(3, 6);  % 输入层到隐含层权重矩阵 (3×6)
w2 = rands(6, 2);  % 隐含层到输出层权重矩阵 (6×2)

% 学习参数设置
xite = 0.50;       % 学习率 - 控制权重调整幅度
alfa = 0.05;       % 动量因子 - 防止训练震荡

代码解释

  • rands(3,6):生成3行6列的随机矩阵,对应3个输入节点到6个隐含节点的连接权重

  • rands(6,2):生成6行2列的随机矩阵,对应6个隐含节点到2个输出节点的连接权重

训练循环控制

k = 0;              % 迭代计数器
E = 1;              % 初始误差(设为大于阈值)
while E >= 1e-20 && k < 10000  % 循环条件:误差>阈值且未超最大迭代
    k = k + 1;      % 迭代次数+1
    E = 0;          % 重置本轮误差
    % ... 训练代码 ...
end

前向传播实现

隐含层计算
for j = 1:6
    I(j) = x * w1(:, j);           % 步骤1:加权求和
    Iout(j) = 1/(1 + exp(-I(j)));  % 步骤2:Sigmoid激活函数
end

代码详解

  • x * w1(:, j):输入向量x与权重矩阵第j列的乘积 → 第j个隐含节点的输入

  • 1/(1 + exp(-I(j))):Sigmoid函数,将输入映射到(0,1)区间

输出层计算
yl = w2' * Iout;  % 输出层计算(线性输出)

代码详解

  • w2' * Iout:隐含层输出与输出层权重的乘积 → 最终预测输出

误差计算实现

el = y - yl;              % 步骤1:计算输出误差向量
E = E + 0.5 * sum(el.^2); % 步骤2:累计均方误差

代码详解

  • y - yl:期望输出与实际输出的差值

  • 0.5 * sum(el.^2):均方误差(乘以0.5是为了求导时消去系数)

反向传播实现

输出层权重调整
% 保存历史权重用于动量项
w2_2 = w2_1; w2_1 = w2;

% 权重更新公式
dw2 = xite * Iout * el + alfa * (w2_1 - w2_2);
w2 = w2_1 + dw2;

代码详解

  • xite * Iout * el:学习率 × 隐含层输出 × 输出误差 → 权重调整量

  • alfa * (w2_1 - w2_2):动量项,利用历史权重变化平滑训练过程

隐含层权重调整
% 保存历史权重
w1_2 = w1_1; w1_1 = w1;

% 计算Sigmoid函数的导数
for j = 1:6
    S = 1/(1 + exp(-I(j)));
    FI(j) = S * (1 - S);  % f'(x) = f(x) * (1 - f(x))
end

% 计算隐含层权重梯度
for i = 1:3
    for j = 1:6
        dw1(i,j) = xite * FI(j) * x(i) * (el(1)*w2(j,1) + el(2)*w2(j,2));
    end
end

% 更新隐含层权重
w1 = w1_1 + dw1 + alfa * (w1_1 - w1_2);

代码详解

  • FI(j) = S * (1 - S):Sigmoid函数导数,用于链式法则

  • el(1)*w2(j,1) + el(2)*w2(j,2):输出误差通过权重反向传播到隐含层

  • 双重循环:遍历所有输入节点到隐含节点的连接

完整的训练流程代码

% 完整训练循环
for i = 1:1:3  % 遍历3个训练样本
    % 选择训练样本
    switch i
        case 1, x = [0.970, 0.001, 0.001]'; y = [0.9862, 0.0094]';
        case 2, x = [0.000, 0.980, 0.000]'; y = [0.0080, 0.4972]';
        case 3, x = [0.002, 0.000, 1.040]'; y = [-0.0145, 1.0202]';
    end
  
    % 前向传播
    for j = 1:6
        I(j) = x * w1(:, j);
        Iout(j) = 1/(1 + exp(-I(j)));
    end
    yl = w2' * Iout;
  
    % 误差计算
    el = y - yl;
    E = E + 0.5 * sum(el.^2);
  
    % 反向传播
    w2_2 = w2_1; w2_1 = w2;
    dw2 = xite * Iout * el + alfa * (w2_1 - w2_2);
    w2 = w2_1 + dw2;
  
    w1_2 = w1_1; w1_1 = w1;
    for j = 1:6
        S = 1/(1 + exp(-I(j)));
        FI(j) = S * (1 - S);
    end
    for i = 1:3
        for j = 1:6
            dw1(i,j) = xite * FI(j) * x(i) * (el(1)*w2(j,1) + el(2)*w2(j,2));
        end
    end
    w1 = w1_1 + dw1 + alfa * (w1_1 - w1_2);
end

关键算法步骤总结

步骤

代码实现

数学含义

前向传播

yl = w2' * Iout

网络预测输出

误差计算

el = y - yl

预测与真实的差距

输出层调整

dw2 = xite * Iout * el

根据误差调整输出权重

隐含层调整

dw1(i,j) = xite * FI(j) * x(i) * (el(1)*w2(j,1) + ...)

链式法则反向传播误差

权重更新

w1 = w1_1 + dw1 + alfa*(w1_1-w1_2)

应用调整并加入动量

代码中的关键技巧

  1. 动量项alfa * (w_1 - w_2) 防止训练震荡

  2. 链式法则:通过权重矩阵将输出误差反向传播到隐含层

  3. 激活函数导数S*(1-S) 是Sigmoid函数的特殊性质

  4. 批量处理:对3个样本依次训练,累计误差

这个代码完整实现了BP算法的所有关键步骤,通过反复执行"预测-误差计算-权重调整"的循环,让网络逐渐学习到输入输出的映射关系。