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

如何用感知器构建cos(x)的二次近似多项式(训练不收敛问题)

问题分析与解决方案

核心问题

你的代码不收敛主要有三个关键问题:

  • 不必要的sigmoid激活:你要拟合的是二次多项式逼近cos(x),这本质是线性回归问题(权重和输入是线性组合),不需要用sigmoid激活函数,反而会引入非线性干扰拟合效果。
  • 缺少学习率:权重更新时直接累加梯度,步长过大导致权重震荡,无法收敛。
  • sigmoid导数计算错误:如果一定要保留sigmoid,sigmoid_prime的输入应该是sigmoid的原始输入(即numpy.dot(training_inputs, w)),而非输出值。

修正后的代码

方案1:去掉sigmoid,直接拟合cos(x)(推荐)

这是最直接的线性回归拟合,完全匹配你用二次多项式逼近的需求:

import numpy
import random
import math
import matplotlib.pyplot as plt

# 生成0到π/4的随机点
R = [random.uniform(0, math.pi/4) for _ in range(1000)]

# 构造训练输入:[x², x, 1]
training_inputs = numpy.ones((len(R), 3))
for idx, x in enumerate(R):
    training_inputs[idx, 0] = x**2
    training_inputs[idx, 1] = x

# 训练输出直接用cos(x),不需要额外激活
training_outputs = numpy.array([math.cos(x) for x in R]).reshape(len(R), 1)

# 随机初始化权重(替代全1,避免初始偏置)
weights = numpy.random.randn(3, 1)

def train_nn(training_inputs, training_outputs, initial_weights, niter, lr):
    w = initial_weights.copy()
    error_history = []
    for _ in range(niter):
        # 前向传播:直接计算线性组合输出
        outputs = numpy.dot(training_inputs, w)
        # 计算误差
        errors = training_outputs - outputs
        # 带学习率更新权重
        deltaw = numpy.dot(training_inputs.T, errors) * lr
        w += deltaw
        # 记录平均误差用于观察收敛情况
        error_history.append(numpy.mean(numpy.abs(errors)))
    return outputs, w, error_history

# 训练参数
NITER = 5000
LEARNING_RATE = 1e-3
outputs, weights, error_history = train_nn(training_inputs, training_outputs, weights, NITER, LEARNING_RATE)

# 输出二次多项式系数
print("二次多项式系数:")
print(f"w1(x²): {weights[0][0]:.6f}")
print(f"w2(x): {weights[1][0]:.6f}")
print(f"w3(常数项): {weights[2][0]:.6f}")

# 验证拟合效果
x_test = numpy.linspace(0, math.pi/4, 100)
y_pred = weights[0]*x_test**2 + weights[1]*x_test + weights[2]
y_true = numpy.cos(x_test)
plt.plot(x_test, y_true, label='真实cos(x)')
plt.plot(x_test, y_pred, label='拟合二次多项式', linestyle='--')
plt.legend()
plt.show()

方案2:保留sigmoid的修正版本(不推荐,仅作演示)

如果你坚持要用sigmoid包裹输出,需要修正导数计算逻辑并添加学习率:

import numpy
import random
import math

def sigmoid(x):
    return 1.0/(1.0 + numpy.exp(-x))

def sigmoid_prime(x):
    return sigmoid(x)*(1 - sigmoid(x))

# 生成数据
R = [random.uniform(0, math.pi/4) for _ in range(1000)]
training_inputs = numpy.ones((len(R), 3))
for idx, x in enumerate(R):
    training_inputs[idx, 0] = x**2
    training_inputs[idx, 1] = x
training_outputs = numpy.array([sigmoid(math.cos(x)) for x in R]).reshape(len(R), 1)

weights = numpy.random.randn(3, 1)

def train_nn(training_inputs, training_outputs, initial_weights, niter, lr):
    w = initial_weights.copy()
    for _ in range(niter):
        # 前向传播:记录sigmoid的原始输入z
        z = numpy.dot(training_inputs, w)
        outputs = sigmoid(z)
        # 计算误差
        errors = training_outputs - outputs
        # 修正梯度:用z计算sigmoid导数
        deltaw = numpy.dot(training_inputs.T, errors * sigmoid_prime(z)) * lr
        w += deltaw
    return outputs, w

NITER = 5000
LEARNING_RATE = 1e-2
outputs, weights = train_nn(training_inputs, training_outputs, weights, NITER, LEARNING_RATE)

print(weights)

关键修正说明

  • 移除sigmoid:二次多项式是权重与输入的线性组合,直接拟合cos(x)的连续值更高效,完全匹配你的问题目标。
  • 添加学习率:控制权重更新步长,避免模型震荡,1e-3是经过测试的合适值。
  • 随机初始化权重:替代全1初始化,避免模型陷入局部最优解。
  • 修正导数计算:若保留sigmoid,必须用sigmoid的原始输入计算导数,而非输出值,否则梯度计算完全错误。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.29 22:11:30