感知机前向传播是输入与权重的点积加偏置后经符号函数判定,输出±1;无矩阵乘法、自动微分或第三方库,需用std::vector确保长度一致并显式处理偏置。

前向传播就是点积加偏置再套符号函数
感知机的前向传播没有矩阵乘法、没有自动微分、也不需要任何第三方库。它本质是一次加权求和(std::vector 点积)加一个偏置项,最后用 sign 或 step 判定正负。所谓“深度学习”在这里是误称——感知机是单层线性分类器,连隐藏层都没有。
实操建议:
- 用
std::vector<double></double>存输入x和权重w,长度必须一致;不一致会越界,调试时加assert(x.size() == w.size()) - 偏置
b单独传入,比硬塞进权重数组首项更安全、更易调试 - 输出返回
double(如1.0或-1.0),方便后续更新逻辑复用,不用强转类型 - 不要用
std::signbit或std::copysign——它们处理 0 的行为不统一;直接用sum >= 0 ? 1.0 : -1.0更可控
为什么不能直接用 sigmoid 或 relu 替代 sign
因为那就不是感知机了。原始 Rosenblatt 感知机定义的激活函数是阶跃型:输出严格为 +1 或 -1(或 0/1),训练规则 w = w + η·y·x 依赖这个二值输出。换成 sigmoid 后,梯度变小、收敛慢,且不再保证在有限步内收敛(前提是线性可分)。
常见错误现象:
立即学习“C++免费学习笔记(深入)”;
- 训练轮次跑几百上千次仍不收敛 → 很可能用了平滑激活函数,但数据其实是线性可分的
- 预测结果全是 0.5 附近浮动 →
sigmoid输出域是 (0,1),没做阈值截断 - 更新方向混乱 →
y是连续值,而原始更新公式中y必须是真实标签(+1/-1)
std::vector 实现点积时的性能与边界注意点
对几十维以内的输入,for 循环点积足够快;不需要引入 std::inner_product 或 Eigen。但有三个容易被忽略的细节:
- 初始化
sum = b,不是 0 —— 偏置必须参与首轮累加 - 循环变量用
size_t i,但要小心x.size()为 0 时进入空循环(合法,但需确保w也为空) - 如果输入含 NaN,
sum会污染整个结果;生产环境建议加std::isnan(x[i])检查(训练前清洗更合适)
示例片段:
double predict(const std::vector<double>& x, const std::vector<double>& w, double b) {
assert(x.size() == w.size());
double sum = b;
for (size_t i = 0; i < x.size(); ++i) {
sum += x[i] * w[i];
}
return sum >= 0 ? 1.0 : -1.0;
}前向传播输出不等于预测标签,只是中间决策值
感知机的前向传播输出是模型对当前样本的“判决结果”,但它本身不带概率解释,也不参与 loss 计算(原始版本无 loss 函数)。训练时只关心它是否与真实标签 y_true 一致:
- 若
predict(x, w, b) == y_true→ 跳过更新 - 若不等 → 执行
w[i] += η * y_true * x[i]和b += η * y_true -
η(学习率)通常取 1.0 即可,调大不会加速收敛,反而可能震荡
真正容易被忽略的是:前向传播函数本身不修改任何状态,它只是纯计算。所有权重更新都发生在训练主循环里,别把 predict 写成带副作用的函数。


















