使用Cos激活函数训练神经网络拟合Cos函数时误差先降后升的问题排查及正确拟合方法咨询
解决你的Cos函数拟合问题:代码错误分析与修复方案
嘿,我仔细看了你的代码和训练日志,发现问题主要出在激活函数的导数计算错误,还有学习率设置可能导致的震荡问题。咱们来逐一解决:
核心错误:Cos激活函数的导数计算完全错了
你在中间层反向传播里写的是:
delta = grad_y * np.sin(self.y) # Differentiation of the cos function
但这里犯了两个关键错误:
- Cos函数的导数是**
-sin(u)**,其中u是中间层的加权输入(也就是np.dot(x, self.w) + self.b),而不是sin(self.y)——你把激活后的输出y=cos(u)当成了u来用,这完全混淆了计算对象。 - 导数的符号是负的,你漏掉了负号,这会导致梯度更新方向完全错误,后期训练自然会崩盘。
而且你在正向传播时没有保存u的值,所以没法直接计算sin(u),得先修正正向传播的代码。
具体修复步骤
1. 先在中间层正向传播里保存加权输入u
修改MiddleLayer的forward方法,把u存下来供反向传播使用:
def forward(self, x): # Forward propagation self.x = x u = np.dot(x, self.w) + self.b self.u = u # 保存加权输入u,后面反向传播要用 self.y = np.cos(u) # Cos激活函数
2. 修正反向传播的导数计算
把backward里的delta计算改成正确的形式:
delta = grad_y * (-np.sin(self.u)) # 正确的Cos导数:dy/du = -sin(u)
3. 调整学习率,避免训练后期震荡
你用的eta=0.1在训练前期可能没问题,但后期模型接近最优解时,这么大的学习率会导致模型在最优值附近来回跳跃,误差就会出现回升。可以试试两种方案:
- 直接把初始学习率降到
0.01 - 或者加个学习率衰减机制,比如每训练5000轮就把学习率乘以0.9,示例代码如下:
# 在训练循环里添加这段逻辑 if i % 5000 == 0 and i != 0: eta *= 0.9 print(f"更新学习率为: {eta:.4f}")
4. 可选:增加中间层神经元数量
当前中间层只有3个神经元,理论上能拟合cos函数,但多给几个(比如5-10个)能让模型更容易捕捉周期性特征,收敛速度更快。
修复后的关键代码片段
# -- 修正后的中间层 -- class MiddleLayer: def __init__(self, n_upper, n): # 初始化 self.w = wb_width * np.random.randn(n_upper, n) # 权重矩阵 self.b = wb_width * np.random.randn(n) # 偏置向量 def forward(self, x): # 正向传播 self.x = x u = np.dot(x, self.w) + self.b self.u = u # 新增:保存加权输入u self.y = np.cos(u) # Cos激活函数 def backward(self, grad_y): # 反向传播 delta = grad_y * (-np.sin(self.u)) # 修正后的导数计算 self.grad_w = np.dot(self.x.T, delta) self.grad_b = np.sum(delta, axis=0) self.grad_x = np.dot(delta, self.w.T) def update(self, eta): # 更新权重和偏置 self.w -= eta * self.grad_w self.b -= eta * self.grad_b
额外小建议
- 你现在用误差均值(
total_error / n_data)来监控训练状态是对的,比总和更直观。 - 训练时可以把误差曲线画出来,能更清楚看到收敛趋势,方便调整超参数。
内容的提问来源于stack exchange,提问作者jaried




