
本文详解如何用单层感知机逼近 cos(x) 在 [0, π/4] 上的二次多项式,指出原代码因缺失学习率导致训练发散,并给出收敛性修复、数学符号规范化、激活函数选择及更优替代方案(如线性回归)的完整实践指南。
在函数逼近任务中,将感知机(单层神经网络)用于多项式拟合是一种常见但易被误解的尝试。核心思想是:将输入 $ x $ 映射为特征向量 $ [x^2,\, x,\, 1] $,通过带 sigmoid 激活的线性组合拟合目标函数(如 $ \cos(x) $)。然而,直接套用梯度更新公式而忽略数值稳定性,极易导致训练失败——正如原始代码所示:权重更新步长过大(隐含学习率为 1),引发震荡或发散。
✅ 关键修正:引入学习率并规范梯度方向
原代码中 w += deltaw 实际等价于学习率 $ \eta = 1 $,远超安全阈值。应显式引入较小学习率(如 $ \eta = 0.01 $),并遵循标准梯度下降数学定义:沿损失函数负梯度方向更新权重。修正后的训练逻辑如下:
import numpy as np
import matplotlib.pyplot as plt
import random
import math
def sigmoid(x):
return 1.0 / (1.0 + np.exp(-x))
def sigmoid_prime(x):
s = sigmoid(x)
return s * (1 - s)
# 生成训练数据:1000 个 [0, π/4] 内均匀随机点
R = np.array([random.uniform(0, math.pi/4) for _ in range(1000)])
# 构造二次多项式特征矩阵:[x², x, 1]
X = np.column_stack([R**2, R, np.ones_like(R)]) # shape: (1000, 3)
# 目标输出:sigmoid(cos(x)) —— 注意:这是对 cos(x) 的归一化压缩
y_true = sigmoid(np.cos(R)).reshape(-1, 1)
# 初始化权重
weights = np.random.normal(0, 0.1, (3, 1)) # 避免全1导致对称性问题
# 训练函数(含学习率 & 规范梯度)
def train_nn(X, y_true, weights, niter=5000, learning_rate=0.01):
errors_history = np.zeros(niter)
for epoch in range(niter):
# 前向传播
z = X @ weights # 线性组合
y_pred = sigmoid(z) # 激活输出
# 计算误差与梯度(均方误差下,此处简化为逐点误差)
error = y_true - y_pred
# 反向传播:∂L/∂w = -Xᵀ × (error ⊙ sigmoid'(z))
grad = X.T @ (error * sigmoid_prime(z))
# 权重更新:w ← w + η × ∂L/∂w(注意符号已内嵌于 grad 计算)
weights += learning_rate * grad
# 记录平均绝对误差用于监控
errors_history[epoch] = np.mean(np.abs(error))
return weights, errors_history
# 执行训练
final_weights, errors = train_nn(X, y_true, weights)
print("拟合得到的权重(对应 [x², x, 1]):")
print(final_weights.flatten())? 为什么学习率至关重要? 梯度下降中,过大的学习率会使参数在最优解附近剧烈震荡甚至发散;过小则收敛缓慢。经验上,$ \eta \in [0.001, 0.01] $ 对此类小规模任务较稳健。本例中 0.01 显著提升收敛性与稳定性。
⚠️ 重要注意事项与深层理解
权重 ≠ 多项式系数:最终得到的 final_weights 是映射到 sigmoid 输出空间 的参数,即模型表达式为
$$ \hat{y}(x) = \sigma(w_1 x^2 + w_2 x + w_3), \quad \sigma(z) = \frac{1}{1+e^{-z}} $$
它不等于 $ a x^2 + b x + c \approx \cos(x) $。若强行将权重当作多项式系数使用,结果将严重偏离真实值。要获得纯多项式逼近,请跳过非线性激活。激活函数需匹配目标范围:
cos(x) 在 $[0, \pi]$ 上取值为 $[-1, 1]$,而 sigmoid 输出恒为 $(0,1)$。若扩展至全周期逼近,推荐改用 tanh(输出 $(-1,1)$),或直接移除激活函数(见下文)。-
更优方案:线性回归(推荐):
若目标仅为“用二次多项式逼近 cos(x)”,无需神经网络。线性回归直接求解最小二乘解,高效、解析、无发散风险:# 纯线性回归拟合 cos(x) ≈ a*x² + b*x + c A = np.column_stack([R**2, R, np.ones_like(R)]) coeffs_ls = np.linalg.lstsq(A, np.cos(R), rcond=None)[0] print("线性回归得到的多项式系数 [a, b, c]:", coeffs_ls)
✅ 总结:何时用感知机?何时选回归?
| 场景 | 推荐方法 | 理由 |
|---|---|---|
| 纯多项式函数逼近(如 cos, exp 局部) | 线性回归 | 解析解、零超参、高精度、可解释性强 |
| 需嵌入非线性变换或后续接入深层网络 | 带合适激活的感知机 | 保持端到端可微,但需谨慎设计输出范围与学习率 |
| 多层复杂模式拟合 | 深度神经网络 | 感知机仅作基线,实际任务中能力有限 |
简言之:感知机在此类任务中更多是教学工具;工程实践中,优先选择数学更直接、控制更精确的方法。理解其局限,才能真正驾驭它。










