问小白 wenxiaobai
资讯
历史
科技
环境与自然
成长
游戏
财经
文学与艺术
美食
健康
家居
文化
情感
汽车
三农
军事
旅行
运动
教育
生活
星座命理

欠拟合、过拟合概念以及示例

创作时间:
作者:
@小白创作中心

欠拟合、过拟合概念以及示例

引用
CSDN
1.
https://blog.csdn.net/weixin_61904259/article/details/139751172

欠拟合和过拟合是机器学习中常见的两个问题,它们分别发生在模型复杂度过低和过高时。为了更好地理解这两个概念,本文将通过一个具体的代码示例来展示欠拟合、正常拟合和过拟合的情况。

欠拟合(Underfitting)

第一个模型是线性回归模型(LinearRegression),它只有一层线性变换,没有非线性激活函数。因此,这个模型只能学习线性关系,而无法捕捉到数据中的非线性模式。

对于一个非线性问题(如本例中的y = x^2 + 1加上噪声),线性模型显然是不够复杂的,它无法很好地拟合这种非线性关系。因此,这个模型会表现出欠拟合,即模型在训练数据上的性能也不佳,因为它没有足够的能力去学习数据的底层结构。

正常拟合(Good Fit)

第二个模型是多层感知机(MLP),具有一个隐藏层,该隐藏层有8个神经元,并使用了ReLU激活函数。这个模型有足够的复杂度去学习数据中的非线性模式,同时又不是过于复杂,因此可以在训练数据上达到良好的性能,并且有较好的泛化能力到未见过的数据。

过拟合(Overfitting)

第三个模型是更复杂的多层感知机(MLPOverfitting),具有两个隐藏层,每层都有256个神经元。这个模型非常复杂,具有大量的参数。虽然它可能在训练数据上表现得非常好,甚至能够几乎完美地拟合训练数据,但这种高度的复杂性可能导致模型在未见过的数据上泛化能力下降。

过拟合发生时,模型会学习到训练数据中的噪声和细节,而不是数据的底层结构或规律。因此,当面对新的、未见过的数据时,模型可能无法做出准确的预测。

总的来说,模型的复杂度需要与问题的复杂度相匹配。如果模型太简单(如线性模型用于非线性问题),则会发生欠拟合;如果模型太复杂(如具有大量参数的深层网络用于相对简单的问题),则可能会发生过拟合。

下面通过代码来展示上述三个模型在训练过程中的表现:

import numpy as np
import torch
import torch.nn as nn
import matplotlib.pyplot as plt
from torch.utils.data import DataLoader, TensorDataset
from sklearn.model_selection import train_test_split

np.random.seed(32)  # 设置随机种子 保证每次结果可以复现
# 生成满足 y = x^2 + 1 的数据
num_samples = 100  # 生成100个样本点
X = np.random.uniform(-5, 5, (num_samples, 1))  # 均匀分布
Y = X ** 2 + 1 + 5 * np.random.normal(0, 1, (num_samples, 1))  # 正态分布噪声
# 将NumPy 变量转化为浮点型 Pytorch 变量
X = torch.from_numpy(X).float()
Y = torch.from_numpy(Y).float()
# 绘制数据散点图
plt.scatter(X, Y)
plt.show()

# 将数据划分为训练集和测试集
train_X, test_X, train_Y, test_Y = train_test_split(X, Y, test_size=0.3, random_state=0)
# 将数据封装成数据加载器
train_dataloader = DataLoader(TensorDataset(train_X, train_Y), batch_size=32, shuffle=True)
test_dataloader = DataLoader(TensorDataset(test_X, test_Y), batch_size=32, shuffle=False)

# 定义线性回归模型(欠拟合)
class LinearRegression(nn.Module):
    def __init__(self):
        super().__init__()
        self.linear = nn.Linear(1, 1)

    def forward(self, x):
        return self.linear(x)

# 定义多层感知机(正常)
class MLP(nn.Module):
    def __init__(self):
        super().__init__()
        self.hidden = nn.Linear(1, 8)
        self.output = nn.Linear(8, 1)

    def forward(self, x):
        x = torch.relu(self.hidden(x))
        return self.output(x)

# 定义更复杂的多层感知机(过拟合)
class MLPOverfitting(nn.Module):
    def __init__(self):
        super().__init__()
        self.hidden1 = nn.Linear(1, 256)
        self.hidden2 = nn.Linear(256, 256)
        self.output = nn.Linear(256, 1)

    def forward(self, x):
        x = torch.relu(self.hidden1(x))
        x = torch.relu(self.hidden2(x))
        return self.output(x)

def plot_errors(models, num_epochs, train_dataloader, test_dataloader):
    # 定义损失函数
    loss_fn = nn.MSELoss()

    # 定义训练和测试误差数组
    train_losses = []
    test_losses = []

    # 遍历每类模型
    for model in models:
        optimizer = torch.optim.SGD(model.parameters(), lr=0.005)

        train_losses_per_model = []
        test_losses_per_model = []

        for epoch in range(num_epochs):
            # 在训练数据上迭代
            model.train()
            train_loss = 0
            for inputs, targets in train_dataloader:
                optimizer.zero_grad()
                outputs = model(inputs)
                loss = loss_fn(outputs, targets)
                loss.backward()
                optimizer.step()
                # 记录loss
                train_loss += loss.item()

            train_loss /= len(train_dataloader)
            train_losses_per_model.append(train_loss)

            model.eval()
            test_loss = 0
            with torch.no_grad():
                for inputs, targets in test_dataloader:
                    outputs = model(inputs)
                    loss = loss_fn(outputs, targets)
                    test_loss += loss.item()
                test_loss /= len(test_dataloader)
                test_losses_per_model.append(test_loss)

        # 记录当前模型每轮的训练测试误差
        train_losses.append(train_losses_per_model)
        test_losses.append(test_losses_per_model)
    return train_losses, test_losses

num_epochs = 200
models = [LinearRegression(), MLP(), MLPOverfitting()]
train_losses, test_losses = plot_errors(models, num_epochs, train_dataloader, test_dataloader)

for i, model in enumerate(models):
    plt.figure(figsize=(8, 4))
    plt.plot(range(num_epochs), train_losses[i], label=f"Train {model.__class__.__name__}")
    plt.plot(range(num_epochs), test_losses[i], label=f"Test {model.__class__.__name__}")
    plt.legend()
    plt.ylim((0, 200))
    plt.show()

通过运行上述代码,我们可以得到三个模型在训练过程中的误差变化曲线。从图中可以看出:

  • 线性回归模型(LinearRegression)在训练集和测试集上的误差都较高,说明模型欠拟合。
  • 简单的MLP模型在训练集和测试集上的误差都较低,说明模型拟合良好。
  • 复杂的MLP模型(MLPOverfitting)在训练集上的误差很低,但在测试集上的误差较高,说明模型过拟合。


© 2023 北京元石科技有限公司 ◎ 京公网安备 11010802042949号