C++实现Perceptron(OR门)时部分权重出现极小值问题求助
感知器实现OR门的权重异常与收敛问题分析
问题原因拆解
1. 权重更新公式错误(核心问题)
感知器的标准权重更新规则是:Δw = η * (y_desired - y_actual) * x_iw_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
相关产品推荐
相关产品推荐

