Python 多项式拟合实战:从Numpy到Torch的完整指南

Python 多项式拟合 文章聚焦于 Python 中的多项式拟合,通过代码示例与理论阐述,全面介绍了相关内容。开篇引入多项式拟合主题,并给出 numpy 库实现的代码,展示如何利用该库生成原始数据、拟合曲线并绘制图像,直观呈现拟合效果。随后介绍 Torch 实现,涵盖数据准备、模型定义、损失函数与优化器设置以及模型训练的全过程,最后输出训练后的权重和偏置。接着深入探讨拟合程度,通过代码展示不同拟合情况的图像,包括过拟合与欠拟合,同时详细阐述了过拟合和欠拟合的概念、产生原因及解决方法. 阅读详情

1. 从零开始:为什么你需要掌握多项式拟合?

如果你刚开始接触机器学习或者数据分析,可能会被那些听起来高大上的算法名字吓到。别担心,今天咱们聊的“多项式拟合”,其实是你入门路上最好的一块敲门砖。说白了,它就是一种用曲线去“描”数据点的方法。想象一下,你有一堆散落在纸上的点,你想画一条光滑的曲线,让它尽可能地从这些点中间穿过去,这条曲线就是“拟合”出来的。这条曲线如果是一个多项式(比如 y = a + bx + cx²),那这个过程就叫多项式拟合。

我刚开始学的时候,也觉得这玩意儿有啥用?后来在真实项目里踩过坑才发现,它的应用场景多到离谱。比如,你想预测一个产品未来几个月的销量趋势,手头只有过去一年的月度数据;或者,你在分析实验数据,想找到一个公式来描述温度和时间的关系。这些时候,一个简单的线性公式(一条直线)可能根本描述不了数据的弯曲变化,而多项式拟合就能派上大用场。它比那些复杂的深度学习模型要轻量、好理解得多,而且计算速度快,结果也直观,特别适合做快速原型验证和趋势分析。

那么,为什么我们要同时学 Numpy 和 Torch 两种实现方式呢?这就像你工具箱里得有螺丝刀和扳手一样,各有各的适用场景。Numpy 是 Python 科学计算的基石,它的实现方式非常直接,一两行代码就能出结果,适合快速验证想法、进行数据分析的前期探索。而 PyTorch 是当下深度学习领域的主流框架,用它来实现多项式拟合,看似“杀鸡用牛刀”,但实际上,这是你理解机器学习训练流程(准备数据、定义模型、计算损失、反向传播、更新参数)的绝佳起点。通过这个简单的例子,你能把深度学习的核心套路摸得一清二楚,以后再学复杂的神经网络,心里就有底了。

所以,这篇指南的目的,就是手把手带你用这两种工具,走完从生成数据、构建模型、训练优化到评估可视化的完整闭环。我会分享我实际编码时觉得好用的技巧,也会指出一些新手容易掉进去的坑。咱们不搞纯理论,就讲怎么用代码把它实实在在地做出来。

2. 用 Numpy 实现:五分钟搞定你的第一条拟合曲线

好了,理论不多说,咱们直接上手。用 Numpy 做多项式拟合,可以说是简单到令人发指。它的核心函数就是 np.polyfit,你几乎不用关心背后的数学(最小二乘法)是怎么算的,它帮你全包了。

2.1 核心三件套:生成、拟合、画图

我们先来复现并深入理解一下原始文章里的那个例子。这个过程我称之为“核心三件套”。

import numpy as np
import matplotlib.pyplot as plt

# 1. 生成模拟数据
np.random.seed(42)  # 设置随机种子,确保每次运行结果一致,这是非常重要的调试习惯
x = np.sort(np.random.rand(100))  # 生成100个0到1之间的随机数,并排序
# 我们的目标是一条有起伏的曲线,这里用正弦函数加噪声来模拟真实世界的数据
y = np.sin(2 * np.pi * x) + 0.3 * np.random.randn(100)

# 2. 执行拟合 - 最关键的一行代码
# np.polyfit(x, y, 3) 的意思是:用3次多项式来拟合x和y的数据
# 它返回的是多项式各项的系数,从高次项到低次项
coefficients = np.polyfit(x, y, 3)
print(f"拟合得到的三次多项式系数为: {coefficients}")
# 输出可能类似:[ 10.2 -15.8   6.1  -0.2],对应 a*x^3 + b*x^2 + c*x + d

# 用np.poly1d将系数数组变成一个可调用的“函数”,方便后续计算y值
p = np.poly1d(coefficients)

# 3. 可视化对比
x_plot = np.linspace(0, 1, 200)  # 为了画出光滑曲线,生成更密集的x值
plt.figure(figsize=(10, 6))
plt.scatter(x, y, alpha=0.6, label='原始数据 (含噪声)', color='blue')
plt.plot(x_plot, p(x_plot), 'r-', linewidth=2, label='三次多项式拟合曲线')
plt.xlabel('x')
plt.ylabel('y')
plt.title('Numpy 多项式拟合演示')
plt.legend()
plt.grid(True, linestyle='--', alpha=0.5)
plt.show()

跑完这段代码,你就能看到一张图,蓝色的散点是我们的原始数据,那条红色的光滑曲线就是拟合出来的三次多项式。怎么样,是不是感觉数据背后的“趋势”一下子就被抓住了?这里我想强调一个细节:np.sort 对x排序。这不是必须的,但排序后的x在画图时能保证曲线是连贯的,而不是来回穿梭的折线。这个小技巧能让你的可视化结果更专业。

2.2 深入参数:不只是换个次数那么简单

原始文章里只用了3次拟合,但 np.polyfit 的魅力在于你可以轻松尝试不同的多项式次数。这里就涉及到一个核心问题:到底选几次多项式合适?

# 尝试不同的多项式次数
degrees = [1, 3, 5, 10]  # 分别尝试1次(线性),3次,5次,10次
plt.figure(figsize=(12, 10))

for i, deg in enumerate(degrees):
    coeffs = np.polyfit(x, y, deg)
    p_func = np.poly1d(coeffs)

    plt.subplot(2, 2, i+1)
    plt.scatter(x, y, alpha=0.3, s=20)
    plt.plot(x_plot, p_func(x_plot), 'r-', linewidth=2)
    plt.title(f'多项式次数 = {deg}')
    plt.grid(True, linestyle='--', alpha=0.3)

    # 计算并显示R-squared,这是一个衡量拟合好坏的常用指标
    # 残差平方和
    residuals = y - p_func(x)
    ss_res = np.sum(residuals**2)
    # 总平方和
    ss_tot = np.sum((y - np.mean(y))**2)
    r_squared = 1 - (ss_res / ss_tot)
    plt.text(0.05, 0.9, f'$R^2$ = {r_squared:.4f}', transform=plt.gca().transAxes, fontsize=10)

plt.tight_layout()
plt.show()

运行这段代码,你会看到四张子图。可以明显观察到:

  • 1次(线性):曲线太简单,很多数据点的波动都没捕捉到,这就是典型的“欠拟合”。
  • 3次和5次:曲线弯曲程度适中,既跟随了数据的整体趋势,又没有过分扭曲,看起来比较“舒服”。
  • 10次:曲线变得非常“崎岖”,几乎要穿过每一个数据点。它在训练数据上可能误差极小,但如果我在0到1之间新生成一些数据点,这条“崎岖”的曲线预测效果很可能很差。这就是“过拟合”。

所以,选择多项式次数是一个权衡。次数太低,模型太“笨”,学不到规律(欠拟合);次数太高,模型太“敏感”,连数据里的噪声都学会了(过拟合)。在实际项目中,我通常会画一个类似上面的对比图,并结合R平方等指标,选择一个在验证集上表现最好的次数。

2.3 实战技巧与避坑指南

用 Numpy 做拟合虽然简单,但也有一些小坑。这里分享几个我总结的实用技巧:

  1. 数据标准化:如果你的x值范围很大(比如从0到1000),直接拟合可能会导致数值计算不稳定(系数非常大或非常小)。一个很好的习惯是先对x进行标准化处理:

    x_mean, x_std = np.mean(x), np.std(x)
    x_normalized = (x - x_mean) / x_std
    # 用标准化后的x进行拟合
    coeffs_normalized = np.polyfit(x_normalized, y, 3)
    # 预测时,也需要先将新x值标准化,再用得到的多项式计算
    

    这样做能让拟合过程更稳定,系数也更容易解释。

  2. 评估拟合质量:不要只看图,要量化。除了上面代码中提到的R平方,还可以计算均方误差(MSE)或平均绝对误差(MAE)。

    from sklearn.metrics import mean_squared_error, mean_absolute_error
    y_pred = p(x)
    mse = mean_squared_error(y, y_pred)
    mae = mean_absolute_error(y, y_pred)
    print(f"MSE: {mse:.4f}, MAE: {mae:.4f}")
    

    这些指标能给你一个更精确的判断。

  3. 外推的风险:这是多项式拟合的一个大忌!你拟合的曲线只在你的数据范围(比如x在0到1之间)内是可靠的。千万不要用它去预测远超出这个范围的值。例如,用0到1的数据拟合出的曲线,去预测x=10时的y值,结果往往会非常荒谬,因为多项式在边界外会急剧上升或下降。

Numpy 的方式快、准、狠,适合数据分析的初期。但如果你想真正理解机器学习模型是如何“学习”的,那就必须进入 PyTorch 的世界。

3. 用 PyTorch 实现:亲手搭建一个“学习机器”

用 PyTorch 做多项式拟合,我们就不再是简单地调用一个函数了。我们需要自己搭建一个微型的“机器学习系统”,明确地告诉计算机:这是数据,这是模型结构,这是衡量好坏的标准(损失函数),这是学习的方法(优化器)。这个过程能让你透彻理解深度学习训练的每一个环节。

3.1 重新定义问题与准备数据

在 Numpy 的例子中,我们的模型是 y = a + b*x + c*x² + d*x³。在 PyTorch 里,我们要自己构建这个计算图。一个巧妙的方法是:把多项式拟合看作一个特殊的线性回归。

对于三次多项式拟合,我们真正的模型是: y = w0 * 1 + w1 * x + w2 * x² + w3 * x³ 你看,如果我们把 1, x, x², x³ 看成是四个不同的“特征”,那么这个问题就变成了:寻找一组权重 [w0, w1, w2, w3],使得这组特征和y的线性组合最接近真实值。这样,我们就可以用 PyTorch 的 nn.Linear 层来解决它了!

import torch
import torch.nn as nn
import torch.optim as optim
import numpy as np
import matplotlib.pyplot as plt

# 设置随机种子,保证可复现性
torch.manual_seed(42)
np.random.seed(42)

# 1. 准备数据 - 我们使用和Numpy例子相同的数据源
x_np = np.sort(np.random.rand(100))
y_np = np.sin(2 * np.pi * x_np) + 0.3 * np.random.randn(100)

# 关键步骤:构建多项式特征
# 对于每个x,我们生成它的0次幂到3次幂,作为4个特征
def make_poly_features(x, degree=3):
    """将一维x转换为多项式特征矩阵"""
    # x的形状假设为 (n_samples,)
    # 返回形状为 (n_samples, degree+1)
    features = [x ** i for i in range(degree + 1)]
    return np.column_stack(features)  # 按列堆叠

X_poly_np = make_poly_features(x_np, degree=3)  # 形状 (100, 4)

# 将NumPy数组转换为PyTorch张量
X_tensor = torch.from_numpy(X_poly_np).float()  # 特征张量
y_tensor = torch.from_numpy(y_np).float().view(-1, 1)  # 标签张量,并调整形状为(100, 1)

print(f"特征张量形状: {X_tensor.shape}")  # 应为 torch.Size([100, 4])
print(f"标签张量形状: {y_tensor.shape}")  # 应为 torch.Size([100, 1])

注意:这里的数据转换是关键。view(-1, 1) 是为了确保y的形状是 (样本数, 1),这与模型输出的形状匹配,方便后续计算损失。

3.2 构建模型、定义损失与优化器

现在,我们来搭建这个“学习机器”。

# 2. 定义模型
class PolynomialRegression(nn.Module):
    def __init__(self, input_dim):
        super(PolynomialRegression, self).__init__()
        # 输入维度是多项式的最高次数+1(例如3次多项式有4个特征:1, x, x^2, x^3)
        # 输出维度是1(预测一个y值)
        self.linear = nn.Linear(input_dim, 1)

    def forward(self, x):
        # 前向传播:就是一次线性变换
        return self.linear(x)

# 我们的特征维度是4
model = PolynomialRegression(input_dim=4)
print(model)

# 3. 定义损失函数和优化器
criterion = nn.MSELoss()  # 均方误差损失,这是回归问题的标配
optimizer = optim.SGD(model.parameters(), lr=0.01)  # 随机梯度下降优化器,学习率设为0.01

# 我们可以看一下模型的初始参数(权重和偏置)
print("初始权重:", model.linear.weight)
print("初始偏置:", model.linear.bias)

这里我选择了 SGD(随机梯度下降)优化器,学习率设为 0.01。对于这个小模型,SGD 完全够用。你也可以尝试 Adam 优化器(optim.Adam(model.parameters(), lr=0.01)),它通常收敛得更快,但 SGD 更能让你理解梯度下降的基本原理。

3.3 训练循环:见证模型“学习”的过程

最激动人心的部分来了——训练。我们将亲眼看着模型的预测从一团糟慢慢变得准确。

# 4. 训练模型
num_epochs = 2000  # 训练轮数,可以设置大一些
loss_history = []   # 记录每轮的损失,方便后续画图看学习过程

for epoch in range(num_epochs):
    # 前向传播
    outputs = model(X_tensor)  # 用当前模型参数计算预测值
    loss = criterion(outputs, y_tensor)  # 计算预测值和真实值的差距

    # 反向传播与优化
    optimizer.zero_grad()  # 清空上一轮累积的梯度,非常重要!
    loss.backward()        # 计算损失关于模型参数的梯度
    optimizer.step()       # 根据梯度更新模型参数

    # 记录损失
    loss_history.append(loss.item())

    # 每500轮打印一次损失
    if (epoch + 1) % 500 == 0:
        print(f'Epoch [{epoch+1:04d}/{num_epochs}], Loss: {loss.item():.6f}')

# 训练完成后,查看学到的参数
print("\n训练完成!")
print(f"学到的权重 (对应1, x, x^2, x^3的系数): {model.linear.weight.data.squeeze().tolist()}")
print(f"学到的偏置: {model.linear.bias.item():.6f}")

# 绘制损失下降曲线
plt.figure(figsize=(10, 4))
plt.subplot(1, 2, 1)
plt.plot(loss_history)
plt.xlabel('Epoch')
plt.ylabel('Loss (MSE)')
plt.title('训练损失下降曲线')
plt.grid(True, linestyle='--', alpha=0.5)

运行这段代码,你会看到控制台不断打印出越来越小的损失值。这意味着模型正在一步步调整它的权重,让它的预测越来越接近真实数据。画出损失曲线,你会看到一条典型的下降曲线,前期下降快,后期趋于平缓。

3.4 验证与对比:我们学得怎么样?

模型训练好了,是骡子是马拉出来遛遛。我们用它来预测一下,并和 Numpy 的结果做个对比。

# 5. 模型预测与可视化
with torch.no_grad():  # 关闭梯度计算,节省内存和计算资源
    y_pred_tensor = model(X_tensor)  # 用训练好的模型对训练数据做预测

# 转换为NumPy数组方便画图
y_pred_np = y_pred_tensor.squeeze().numpy()

# 为了画光滑曲线,生成密集的测试点
x_plot_np = np.linspace(0, 1, 200)
X_plot_poly = make_poly_features(x_plot_np, degree=3)
X_plot_tensor = torch.from_numpy(X_plot_poly).float()
with torch.no_grad():
    y_plot_pred_tensor = model(X_plot_tensor)
y_plot_pred_np = y_plot_pred_tensor.squeeze().numpy()

# 用Numpy的polyfit再拟合一次,用于对比
coeffs_numpy = np.polyfit(x_np, y_np, 3)
p_numpy = np.poly1d(coeffs_numpy)
y_plot_numpy = p_numpy(x_plot_np)

plt.subplot(1, 2, 2)
plt.scatter(x_np, y_np, alpha=0.6, label='原始数据', s=20)
plt.plot(x_plot_np, y_plot_pred_np, 'r-', linewidth=3, label='PyTorch 拟合')
plt.plot(x_plot_np, y_plot_numpy, 'g--', linewidth=2, label='Numpy polyfit')
plt.xlabel('x')
plt.ylabel('y')
plt.title('拟合结果对比')
plt.legend()
plt.grid(True, linestyle='--', alpha=0.5)
plt.tight_layout()
plt.show()

# 定量对比两种方法的系数
print("\n=== 系数对比 ===")
print(f"Numpy polyfit 系数 (高次到低次): {coeffs_numpy}")
# PyTorch模型的系数顺序是 [x^3, x^2, x, 常数项]?不,我们需要检查一下。
# 我们的特征矩阵是 [1, x, x^2, x^3],nn.Linear的权重对应这些特征。
torch_coeffs = model.linear.weight.data.squeeze().tolist()
torch_bias = model.linear.bias.item()
print(f"PyTorch 学到的权重 (对应[1, x, x^2, x^3]): {torch_coeffs}")
print(f"PyTorch 学到的偏置: {torch_bias}")
# 注意:Numpy的polyfit返回的系数顺序是 [最高次项, ..., 常数项]
# 而我们的PyTorch模型,linear.weight对应特征权重,bias对应额外的偏置。
# 实际上,对于我们的构造方式,PyTorch模型的最终公式是:
# y = bias + weight[0]*1 + weight[1]*x + weight[2]*x^2 + weight[3]*x^3
# 所以常数项是 (bias + weight[0])。对比时需要小心。

通过对比图,你会发现红色实线(PyTorch)和绿色虚线(Numpy)几乎完全重合!这说明我们亲手用梯度下降训练出来的模型,和 Numpy 用最小二乘法直接计算出来的最优解,结果是一致的。这给了我们巨大的信心:我们搭建的这个“学习机器”工作正常,并且学会了正确的知识。

4. 拟合程度的艺术:欠拟合、过拟合与刚刚好

通过前面的实践,你已经感受到了选择多项式次数的重要性。现在,我们系统地聊聊“拟合程度”这件事。这是机器学习中最核心的概念之一,直接决定了你的模型是“书呆子”还是“真学霸”。

4.1 可视化诊断:一眼看出模型的好坏

让我们用一个更综合的例子,把欠拟合、理想拟合和过拟合放在一起对比。这次我们生成一个更复杂的数据。

# 生成一个更复杂的非线性数据
np.random.seed(123)
x_complex = np.linspace(0, 2, 50)
y_true = np.sin(3 * np.pi * x_complex) * np.exp(-x_complex)  # 真实的潜在规律
y_noisy = y_true + 0.2 * np.random.randn(len(x_complex))  # 加上噪声的观测值

degrees_to_try = [1, 4, 15]  # 分别代表欠拟合、理想、过拟合
plt.figure(figsize=(15, 5))

for idx, deg in enumerate(degrees_to_try):
    # 使用Numpy快速拟合
    coeffs = np.polyfit(x_complex, y_noisy, deg)
    p_func = np.poly1d(coeffs)
    x_smooth = np.linspace(0, 2, 200)
    y_smooth = p_func(x_smooth)

    plt.subplot(1, 3, idx+1)
    plt.scatter(x_complex, y_noisy, alpha=0.7, s=40, label='带噪声数据', color='lightblue', edgecolors='blue')
    plt.plot(x_smooth, y_true, 'k--', linewidth=2, label='真实规律', alpha=0.8)
    plt.plot(x_smooth, y_smooth, 'r-', linewidth=3, label=f'{deg}次拟合')
    plt.fill_between(x_smooth, y_true - 0.2, y_true + 0.2, alpha=0.1, color='gray', label='噪声范围')

    # 计算训练集上的误差
    y_pred = p_func(x_complex)
    train_mse = np.mean((y_pred - y_noisy) ** 2)
    # 模拟一个测试集(从同一规律生成,但噪声不同)
    x_test = np.linspace(0.1, 1.9, 30)
    y_test_true = np.sin(3 * np.pi * x_test) * np.exp(-x_test)
    y_test_noisy = y_test_true + 0.2 * np.random.randn(len(x_test))
    y_test_pred = p_func(x_test)
    test_mse = np.mean((y_test_pred - y_test_noisy) ** 2)

    plt.title(f'多项式次数 = {deg}\n训练MSE: {train_mse:.4f}, 测试MSE: {test_mse:.4f}')
    plt.xlabel('x')
    plt.ylabel('y')
    plt.legend(loc='upper right', fontsize='small')
    plt.grid(True, linestyle='--', alpha=0.3)

plt.tight_layout()
plt.show()

在这张对比图中,你可以清晰地看到三种状态:

  • 图1(1次,欠拟合):红色的拟合直线完全无法描述黑色虚线代表的真实规律。它在训练集和测试集上的误差(MSE)都很大。模型太简单,能力不足。
  • 图2(4次,理想拟合):红色曲线大致跟随了真实规律的整体趋势,平滑地穿过了数据分布的中间区域。它没有刻意去穿过每一个蓝色数据点(因为那些是噪声),而是抓住了背后的主要模式。训练误差和测试误差都比较低,且相差不大。
  • 图3(15次,过拟合):红色曲线变得极度扭曲,为了穿过每一个训练数据点(蓝色散点),产生了剧烈的震荡。注意看,在数据稀疏的区域(比如x接近2的地方),曲线出现了毫无道理的剧烈波动。虽然它在训练集上的误差可能极小(因为它几乎“记住”了每个点),但在测试集上的误差会显著增大。模型太复杂,学习了噪声。

4.2 理解过拟合:当模型成了“记忆大师”

过拟合的本质是模型**方差(Variance)**过高。它过度关注训练数据中的细节和噪声,导致学到的规则泛化能力极差。就像一个只靠死记硬背考高分的学生,题目稍一变化就不会了。

在多项式拟合中,过拟合的直接表现就是多项式系数变得非常大。高次项的系数巨大,使得曲线具备极强的弯曲能力去“贴合”噪声点。

# 查看过拟合模型的系数
coeffs_overfit = np.polyfit(x_complex, y_noisy, 15)
print("15次多项式拟合的部分高阶系数(通常绝对值非常大):")
for i, coeff in enumerate(coeffs_overfit[:5]):  # 展示前5个最高次项系数
    print(f"  x^{15-i} 的系数: {coeff:12.6e}")

你会看到类似 x^15 的系数: 3.452e+08 这样的输出,系数巨大无比。这就是过拟合在数学上的特征。

如何在PyTorch中应对过拟合? 虽然我们的多项式拟合例子简单,但PyTorch提供了强大的正则化工具,这在复杂模型中至关重要。

  1. L2正则化(权重衰减):直接在优化器中设置 weight_decay 参数。
    optimizer = optim.SGD(model.parameters(), lr=0.01, weight_decay=1e-5)
    
    weight_decay 参数会给损失函数增加一个惩罚项,防止权重变得过大。
  2. 早停法(Early Stopping):这是我最常用也最有效的方法之一。在训练时,预留一部分数据作为验证集。每训练一定轮数,就在验证集上评估一次性能。当验证集上的误差不再下降反而开始上升时,就停止训练。
    # 伪代码逻辑
    best_val_loss = float('inf')
    patience = 50  # 容忍验证损失不下降的轮数
    counter = 0
    
    for epoch in range(num_epochs):
        # ... 训练步骤 ...
        current_val_loss = validate(model, val_loader) # 在验证集上计算损失
        if current_val_loss < best_val_loss:
            best_val_loss = current_val_loss
            counter = 0
            # 保存最佳模型
            torch.save(model.state_dict(), 'best_model.pth')
        else:
            counter += 1
            if counter >= patience:
                print(f'早停在第{epoch}轮')
                break
    # 训练结束后,加载效果最好的模型
    model.load_state_dict(torch.load('best_model.pth'))
    

4.3 诊断欠拟合:当模型“还没开窍”

欠拟合则相反,是模型**偏差(Bias)**过高。模型本身太简单,无法捕捉数据中潜在的模式。就像用一个线性公式去拟合正弦波,再怎么优化参数,也画不出那条弯曲的线。

解决方法更直接:

  1. 增加模型复杂度:在多项式拟合中,就是增加次数。在神经网络中,可以增加层数或每层的神经元数量。
  2. 更好的特征工程:有时候不是模型不行,而是给模型的信息不够。在多项式拟合的语境下,如果你只用 x 去拟合一个周期性数据,肯定欠拟合。但如果你聪明地加入了 sin(x), cos(x) 作为特征,可能用一个简单的线性模型就能拟合得很好。这就是特征工程的威力。
  3. 减少正则化:如果你不小心设置了过大的 weight_decay,也可能导致模型无法充分学习,表现为欠拟合。可以尝试减小正则化强度。
  4. 训练更长时间:有时候模型有潜力,只是还没训练到位。特别是使用SGD等优化器时,可以适当增加训练轮数 (num_epochs)。

4.4 找到甜蜜点:偏差-方差权衡

机器学习的所有调参过程,几乎都是在进行偏差-方差权衡。我们的目标是在欠拟合(高偏差)和过拟合(高方差)之间找到一个平衡点,使模型在未知数据上的总误差最小。

一个实用的工作流是:

  1. 从简单模型开始:比如先用1次或2次多项式。
  2. 绘制学习曲线:绘制模型在训练集和验证集上的误差随模型复杂度(多项式次数)或训练轮数变化的曲线。
  3. 寻找拐点:理想情况下,随着复杂度增加,训练误差持续下降,而验证误差先下降后上升。那个验证误差最低点,就是我们要的“甜蜜点”。
  4. 交叉验证:对于小数据集,使用K折交叉验证来更稳健地评估不同复杂度模型的表现,避免因一次数据划分的偶然性而选错模型。
# 一个简单的模型复杂度选择示例(通过交叉验证)
from sklearn.model_selection import cross_val_score
from sklearn.preprocessing import PolynomialFeatures
from sklearn.linear_model import LinearRegression
from sklearn.pipeline import make_pipeline
from sklearn.metrics import make_scorer, mean_squared_error

degrees = range(1, 11)  # 尝试1到10次
cv_scores = []

for deg in degrees:
    # 构建一个“多项式特征+线性回归”的管道
    model_pipeline = make_pipeline(PolynomialFeatures(degree=deg), LinearRegression())
    # 进行5折交叉验证,负均方误差作为评分(sklearn约定:越大越好)
    scores = cross_val_score(model_pipeline, x_complex.reshape(-1,1), y_noisy,
                             cv=5, scoring='neg_mean_squared_error')
    cv_scores.append(-scores.mean())  # 转换为正的平均MSE

best_degree = degrees[np.argmin(cv_scores)]
print(f"通过5折交叉验证,最佳的多项式次数是: {best_degree}")

plt.figure(figsize=(8,5))
plt.plot(degrees, cv_scores, 'bo-', linewidth=2, markersize=8)
plt.xlabel('多项式次数')
plt.ylabel('交叉验证平均MSE')
plt.title('模型复杂度选择曲线')
plt.axvline(x=best_degree, color='r', linestyle='--', label=f'最佳次数={best_degree}')
plt.grid(True, linestyle='--', alpha=0.5)
plt.legend()
plt.show()

这段代码利用了 Scikit-learn 的交叉验证功能,自动为你找到了在给定数据上最合适的多项式次数。在实际项目中,这套组合拳(可视化诊断、学习曲线、交叉验证)能帮你做出更可靠的决定。

欠拟合与过拟合实战诊断:从偏差-方差到工程化干预 欠拟合和过拟合是机器学习模型泛化失败的两大核心根源,本质源于模型表达能力与任务复杂度之间的错配。其理论基础可追溯至偏差-方差分解——高偏差导致模型简化过度、系统性偏离真实函数;高方差则反映模型对训练数据扰动过于敏感,将噪声误作规律。这一原理在各类算法中普遍存在:线性模型易欠拟合非线性问题,高阶多项式或宽MLP易过拟合小样本数据。技术价值在于提供可量化的诊断路径:通过学习曲线、验证曲线与残差图三图联动,结合训练-验证误差gap、验证损失斜率等指标,实现故障定性与定位。典型应用场景涵盖电商推荐效果衰减、工业设备 阅读详情

相关推荐

欠拟合与过拟合的本质:偏差-方差权衡与泛化能力诊断

在机器学习中,模型泛化能力失效的核心症结往往归于偏差与方差的失衡——偏差反映模型对真实规律的系统性偏离,方差则体现其对训练数据噪声的过度敏感。这一Bias-Variance Tradeoff原理,是理解欠拟合(高偏差、低方差)与过拟合(低偏差、高方差)的数学根基。它不仅决定模型在验证集的表现,更直接影响线上稳定性、跨域迁移能力及业务鲁棒性。从推荐系统点击率骤降,到医疗影像识别因设备差异失效,再到Kaggle提交成绩断崖式下跌,这些典型现象均可回溯至该权衡机制的失控。本文聚焦工程落地视角,结合学习曲线、验证曲

273

多项式拟合

多项式拟合(数学建模)

m0_52124992的博客 7783

多项式函数曲线拟合——最小二乘法

多项式函数拟合的任务是假设给定数据由M次多项式函数生成,选择最有可能产生这些数据的M次多项式函数,即在M次多项式函数中选择一个对已知数据以及未知数据都有很好预测能力的函数。 最小二乘法(又称最小平方法)是一种数学优化技术。它通过最小化误差的平方和寻找数据的最佳函数匹配。利用最小二乘法可以简便地求得未知的数据,并使得这些求得的数据与实际数据之间误差的平方和为最小。最...

满腹的小不甘 4万+

多项式拟合一般方程法详细推导

多项式拟合之一般方程法 文章目录多项式拟合之一般方程法1 什么是多项式拟合?2 怎样拟合?3 求bbb正交矩阵QR分解施密特正交化matlab测试函数 之前经常会用到多项式拟合来拟合函数关系,十分好用,得出的表达式使用起来超级方便。在此对多项式拟合原理进行分析。 1 什么是多项式拟合? y=a0+a1x+a2x2+⋯+anxn=∑i=0naixi \begin{aligned} y&=a_0+a_1x+a_2x^2+\cdots +a_nx^n\\ &=\sum_{i=0}^{n}{a_

qq_39545674的博客 2万+

过拟合欠拟合怎么解决|正则化+Dropout+Early Stopping代码

总误差 = 偏差² + 方差 + 噪声L2正则化损失: Loss_total = Loss + λ·Σw²L1正则化损失: Loss_total = Loss + λ·Σ|w|Dropout: 训练时以概率p随机置0,推理时关闭等效于训练 2^n 个子网络的集成。

白话机器学习 624

拟合多项式函数

levenberg_marquardt算法拟合多项式和对数函数 python写的

python之拟合

一、多项式拟合 多项式拟合的话,用的的是numpy这个库的polyfit这个函数。那么多项式拟合,最简单的当然是,一次多项式拟合了,就是线性回归。直接看代码吧 import numpy as np def linear_regression(x,y): #y=bx+a,线性回归 num=len(x) b=(np.sum(x*y)-num*np.mean(x)*np.m

your_answer的博客 5万+

多项式拟合定义及方法

多项式函数是形式比较简单的函数: f(x)=a0+a1(x-x1)+a2(x-x2)^2+a3(x-x3)^3... 多项式拟合一般指,用多项式函数逼近一个函数, 常用方法为利用泰勒公式,将函数展开为拉格朗日级数麦克劳林级数等等

PRML系列:1.1 多项式函数拟合

PRML系列:1.1 多项式函数拟合前言此系列关于Pattern Recognition and Machine Learning的总结,博文记录一些在阅读过程中遇到的难点和自己的感悟。话不多说,直接进入正题吧。正文第一章第一节的内容关于多项式函数的拟合,假设我们给出了一系列的坐标点(x,y)们,可能是某个函数生成的,比如:y=sin(2πx)y = \sin(2 \pi x),如下图:模式识别的目

Demon-初来驾到 3822

UFS电源管理深度解析:从电气特性到功耗模式优化

本文深度解析UFS电源管理,从VCC、VCCQ、VCCQ2三路供电体系切入,详细阐述了UIC链路休眠(HIBERN8)机制与设备七种功耗模式(Active、Idle、Sleep、Power Down)的切换逻辑。文章重点探讨了如何通过主动功耗管理策略与动态调整激活模式电流等级(bActiveICCLevel),在嵌入式与移动设备中实现性能与功耗的精细平衡与优化。

weixin_29243279的博客 490

SAP资产按每天折旧功能介绍

注意:此处在计算百分比时将30天作为剩余使用寿命,因为SAPS/4HANA中的计算是通过考虑购置第一年的折旧总天数来进行的。这是因为我在计算总使用寿命时只使用了365天,如果剩余使用寿命大于1年,SAPS/4HANA会将闰年规范化为365天。此功能仅对确定使用寿命或剩余寿命百分比的折旧键有用,如果需要,这种折旧到日的解决方案可以应用于需要每天计算折旧的跨行业(任何国家/行业)。注意:这里在计算百分比时考虑了2天,因为364天已经过期,但目前我们到达2024年,使用寿命为366天。它只会在下一年生效。

yanzibing99的博客 1562

Python 多项式拟合实战:从Numpy到Torch的模型优化

本文详细对比了使用Numpy和PyTorch进行多项式拟合的实战方法。通过Numpy的polyfit函数可快速实现一键拟合,而PyTorch方案则提供了更灵活的模型构建与训练流程,有助于深入理解机器学习优化过程。文章重点探讨了如何诊断和解决过拟合与欠拟合问题,并分享了工程化封装与超参数调优的实用技巧。

weixin_29072299的博客 256

STM32实战:增量式PID控制气体流量(附完整代码与调参技巧)

本文详细解析了如何在STM32平台上实现气体流量控制的增量式PID算法。内容涵盖从系统硬件架构选型、增量式PID算法原理与代码实现,到ADC采样滤波、PWM驱动配置等关键外设驱动,并提供了完整的参数整定技巧与工程调优实战指南,帮助开发者构建稳定可靠的气体流量控制系统。

rose2的博客 623

Python 多项式拟合实战:从Numpy到Torch的进阶指南

本文提供了Python多项式拟合的实战指南,对比了Numpy与PyTorch两种实现方式。Numpy的np.polyfit函数适合快速原型验证,而PyTorch通过构建模型、定义损失函数和优化器,揭示了梯度下降的训练原理,帮助读者深入理解模型优化过程。文章还探讨了阶数选择、过拟合与欠拟合的识别及正则化应用,是掌握机器学习基础建模思想的绝佳教程。

yolo5detector的博客 417

探索Vortex开源GPGPU:RISC-V SIMT架构(4-1),Execute SFU(1)

本文详细分析了Vortex RISC-V内核6级流水线中Execute阶段的SFU(特殊功能单元)模块。重点介绍了SFU的数据流结构,包含dispatch_unit、pe_switch、csr_unit、wctl_unit和gather_unit五个子模块。文章深入解析了warp控制指令wspawn和tmc的实现机制:wspawn用于激活指定数量的warp并设置新PC值,tmc则用于控制线程掩码和warp状态。通过C代码和汇编指令的对照分析,阐明了这些指令在并行计算中的关键作用,包括线程激活、状态切换和执行

weixin_55313207的博客 1364

实时多目标跟踪(MOT):SORT与DeepSORT优化

实时多目标跟踪(MOT):SORT与DeepSORT优化​ ,人工智能,计算机视觉,大模型,AI,本文围绕实时多目标跟踪中的 SORT 与 DeepSORT 算法展开,阐述了两者原理、实现及优化。SORT 结合卡尔曼滤波和匈牙利算法,依赖目标检测,实时性好但复杂场景表现欠佳。DeepSORT 在其基础上引入外观特征,采用级联匹配,提升了跟踪精度和鲁棒性。还探讨了两种算法在检测、运动模型、外观特征等方面的优化方向,以及应用场景与选择建议,为相关应用和研究提供参考。

不踩坑,不知道坑有多深;不总结,踩过的坑白踩。 20万+

从零开始:Python多项式拟合的保姆级教程(含Numpy/Torch对比)

本文提供了一份从零开始的Python多项式拟合实战教程,详细对比了Numpy与PyTorch两种实现方法。通过生成带噪声的正弦波数据,逐步演示了数据划分、模型拟合、可视化及评估过程,并深入探讨了如何通过选择合适的多项式阶数来优化拟合程度,避免欠拟合与过拟合。

weixin_29094301的博客 211

11、频率响应与伯德图详解

本文详细介绍了电子电路分析中的频率响应与伯德图,涵盖分贝的定义与计算、带宽与3dB点、放大器反馈机制、倍频程与十倍频程概念,并系统讲解了基于实数和复数零极点的伯德图构建方法。通过具体示例分析了传递函数的渐近近似、幅度与相位响应,并结合MATLAB工具展示了伯德图的绘制过程。文章旨在帮助读者深入理解电路在不同频率下的行为特性,为电路设计与优化提供理论支持。

white的博客 173

Python多项式拟合实战:从Numpy到Torch的完整代码解析(附可视化对比)

本文详细解析了Python中多项式拟合的两种实现方式:基于Numpy的简洁数学方法和基于PyTorch的灵活神经网络框架。通过完整代码示例和可视化对比,帮助读者掌握从基础拟合到复杂模型集成的关键技术,适用于数据分析、机器学习等多种场景。

weixin_30335353的博客 199

【Unity实战篇 】| 游戏中实现镂空遮罩效果【矩形、圆形镂空遮罩】

本文来写一下怎样在Unity中完成一个 镂空遮罩 的效果。 镂空遮罩 比较常用的有两种:矩形镂空遮罩、圆形镂空遮罩。 镂空遮罩 在游戏中常被当做新手引导必备的内容,可以给玩家更多不一样的体验。 下面就一起来看一下怎样做出这种效果吧!

努力前行,总会成为自己心中的那道光 1万+

PMP–一、二、三模–分类–13.干系人管理

A:确认是否已制定沟通管理计划,使业务干系人认识到项目的成功之处。B:让业务干系人参加冲刺回顾会议。C:与发起人和关键干系人一起确定产品待办事项列表中的优先要务,以便交付商业利益。D:为关键干系人开展收尾调研。C。

stqer的博客 1418

昇腾CANN NumPy适配Asnumpy深度解读:PyTorch与NumPy互操作完整实战技术指南

在React Native中开发鸿组件(这里指的是鸿蒙(HarmonyOS)组件),你需要了解鸿蒙开发的基础以及如何在React Native项目中集成鸿蒙应用。鸿蒙OS是由华为开发的一个分布式操作系统,主要用于其智能设备,如手机、平板、智能手表等。首先,你需要熟悉鸿蒙OS的开发环境设置和基本开发流程。React Native本身主要用于Harmony和Harmony平台的开发,但你可以通过以下几种方式将鸿蒙应用集成到React Native项目中:A. 使用WebView。

luozhen110的博客 598

最新的sdio 4.0 spec 协议标准 带书签 高清完整PDF版

Part-E1-SDIO-Specification-Ver4.00-Final-120220.pdf:最新的sdio 4.0 spec 协议标准 带书签 高清完整PDF版,包括SDIO 接口的外设,SDIO 接口官方协议等。

机器学习:模型选择、欠拟合和过拟合

【代码】模型选择、欠拟合和过拟合。

qq_43564475的博客 155

数学建模模型总结

这是本人在两年内准备参加数学建模竞赛搜集到的模型资料,已经经过了本人的筛选和整理,希望能够对准备参加数学建模竞赛的学弟学妹们提供一些帮助!

RU DOS版、EFI版32bit/64bit BIOS常用工具

DOS版、EFI版的RU工具,可用来查看IO、MEM、PCI、SMBIOS等资源。亲测在EFI SHELL下可用。

李沐之模型选择,欠拟合和过拟合笔记

李沐之模型选择,欠拟合和过拟合笔记

guoguozgw的博客 339

锂电池充电器电路设计 里面有电路

锂电池充电器电路设计 里面有电路 3.7V升压到5V电路设计。

Numpy学习笔记——便捷函数

4.1 相关性使用cov函数计算股票收益率的协方差矩阵:covariance = np.cov(bhp_returns, vale_returns) print "Covariance", covariance使用diagonal函数查看对角线上的元素:print "Covariance diagonal", covariance.diagonal()使用trace函数计算矩阵的迹,即对角线上元素之

zhuliangjiang的博客 1868

用多项式表示量化神经网络简单性:有效度数ED的原理与PyTorch实现

在神经网络工程实践中,模型复杂度评估常依赖参数量或FLOPs,但这类静态指标难以反映函数空间的真实行为。一个参数量庞大的网络可能学到平滑低阶函数,而小网络也可能因过拟合产生高频振荡。解决这一问题的关键,是借助泰勒展开将网络映射为多项式表达,通过分析各阶系数的能量分布,定义“有效度数”(ED)来量化函数的简单性。ED越低,说明函数越光滑、抗噪与泛化能力越强,这为理解过拟合、欠拟合和模型剪枝提供了新视角。基于PyTorch,我们可从不同采样点计算高阶导数与ED,并将其作为正则项优化训练过程,从而在工程上实现模型

weixin_30851867的博客 295
上一篇: 无人机多传感器融合避障与精准降落技术深度剖析
下一篇: Rockchip RK3562 64位处理器:九鼎开发板在轻量级AI与IoT设备中的应用探索
java5
博客等级 码龄22年 85粉丝 2103原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符  | 博主筛选后可见
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值