You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

C++实现Perceptron(OR门)时部分权重出现极小值问题求助

感知器实现OR门的权重异常与收敛问题分析

问题原因拆解

1. 权重更新公式错误(核心问题)

感知器的标准权重更新规则是:
Δw = η * (y_desired - y_actual) * x_i
w_new = w_old + Δw

你的代码存在两个致命错误:

  • 错误地将输入项固定为1.0,而非使用当前样本的输入x1Inputs[i]和x2Inputs[i],完全违背了感知器的学习逻辑。
  • 误差计算符号颠倒:你定义error = thresholdResult - DesiredOutputs[i],与标准公式的符号相反,导致权重更新方向完全错误。

错误代码片段:

weightOneFinal = weightOne + (learningRate * error * 1.0);
weightTwoFinal = weightTwo + (learningRate * error * 1.0);

2. 浮点数精度引发的极小值显示

你看到的-3e-17是浮点数运算的精度误差:当错误的更新逻辑将权重计算为接近0的值时,二进制浮点数无法精确表示该结果,最终显示为极小的非零值。这是逻辑错误的衍生表现,而非浮点数本身的问题。

3. 训练不收敛的根源

由于权重更新逻辑错误,所有样本的实际输出始终为0,错误持续存在,权重被不断向负方向调整,导致加权和sum越来越小,永远无法达到阈值,训练陷入死循环。

修正后的代码

#include <iostream>
#include <cmath>
#include <iomanip>

using namespace std;

int main()
{
    const double threshold = 0.2;
    const double learningRate = 0.1;
    int actualOutput = 0;

    const double x1Inputs[4] = {0, 0, 1, 1};
    const double x2Inputs[4] = {0, 1, 0, 1};
    const double desiredOutputs[4] = {0, 1, 1, 1};
    
    double sum = 0;
    double weightOne = 0.30;
    double weightTwo = -0.10;
    double weightOneFinal = 0;
    double weightTwoFinal = 0;
    double error = 0;
    bool hasError = false;

    cout << "               Perceptron acting as an OR Gate               " << endl;
    cout << "-------------------------------------------------------------" << endl;

    for (int epoch = 0; epoch < 5; epoch++) // 迭代轮次
    {
        for (int sampleIdx = 0; sampleIdx < 4; sampleIdx++) // 遍历每个样本
        {
            hasError = false;

            // 计算加权和并得到实际输出
            sum = x1Inputs[sampleIdx] * weightOne + x2Inputs[sampleIdx] * weightTwo;
            actualOutput = (sum >= threshold) ? 1 : 0;

            // 计算误差
            if (actualOutput != desiredOutputs[sampleIdx])
            {
                error = desiredOutputs[sampleIdx] - actualOutput;
                hasError = true;
            }
            else
            {
                error = 0;
            }

            // 更新权重
            if (hasError)
            {
                weightOneFinal = weightOne + learningRate * error * x1Inputs[sampleIdx];
                weightTwoFinal = weightTwo + learningRate * error * x2Inputs[sampleIdx];
            }
            else
            {
                weightOneFinal = weightOne;
                weightTwoFinal = weightTwo;
            }

            // 打印表头(每个epoch的第一个样本打印)
            if (sampleIdx == 0)
            {
                cout << " Epoch   Inputs   Desired   Initial   Actual   Error   Final " << endl;
                cout << "-------------------------------------------------------------" << endl;
            }
            
            // 格式化输出,避免浮点数精度问题导致的奇怪显示
            cout << fixed << setprecision(1) 
                 << setw(4) << epoch + 1 
                 << setw(8) << x1Inputs[sampleIdx] << "," << x2Inputs[sampleIdx] 
                 << setw(8) << desiredOutputs[sampleIdx] 
                 << setw(8) << weightOne << "," << weightTwo 
                 << setw(5) << actualOutput 
                 << setw(10) << error 
                 << setw(8) << weightOneFinal << "," << weightTwoFinal << "\n\n";
            
            // 更新权重为新值
            weightOne = weightOneFinal;
            weightTwo = weightTwoFinal;
        }
    }
    return 0;
}

代码规范改进建议

  • 使用const修饰常量(阈值、学习率、输入样本数组),避免意外修改。
  • 变量命名语义化:将thresholdResult改为actualOutput,isError改为hasError,j改为epoch,i改为sampleIdx,提升代码可读性。
  • 输出时添加fixed修饰符,强制以固定小数格式显示,避免极小值以科学计数法展示。
  • 增加收敛判断:当所有样本都无错误时,提前终止训练,无需完成所有epoch。
  • 避免using namespace std;,大型项目中易引发命名冲突,建议使用std::cout、std::setw等完整命名。

内容的提问来源于stack exchange,提问作者thedrooster

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.24 21:47:08