• 21.过拟合和欠拟合示例


    1. 背景介绍

    机器学习和深度学习中,过拟合和欠拟合是两个非常重要的概念。过拟合指的是模型在训练数据上表现很好,但在新的测试数据上效果变差的情况。欠拟合则是指模型无法很好地拟合训练数据的情况。这两种情况都会导致模型无法很好地泛化,影响最终的预测和应用效果。

    为了帮助大家更好地理解过拟合和欠拟合的概念及其应对方法,我将通过一个基于PyTorch的代码示例来演示这两种情况的具体表现。我们将生成一个抛物线数据集,并定义三种不同复杂度的模型,分别对应欠拟合、正常拟合和过拟合的情况。通过可视化训练和测试误差的曲线图,以及预测结果的散点图,我们可以直观地观察到这三种情况下模型的拟合效果。

    2. 核心概念与联系

    过拟合和欠拟合是机器学习和深度学习中两个相互对应的概念:

    1. 过拟合(Overfitting): 模型在训练数据上表现很好,但在新的测试数据上效果变差的情况。这通常是由于模型过于复杂,过度拟合了训练数据中的噪声和细节,导致无法很好地推广到未知数据。

    2. 欠拟合(Underfitting): 模型无法很好地拟合训练数据的情况。这通常是由于模型过于简单,无法捕捉训练数据中的复杂模式和关系。

    这两种情况都会导致模型在实际应用中无法很好地泛化,因此需要采取相应的措施来防止和缓解过拟合和欠拟合。常见的应对方法包括:

    - 增加训练样本数量
    - 减少模型复杂度(比如调整网络层数、神经元个数等)
    - 使用正则化技术(如L1/L2正则化、Dropout等)
    - 调整超参数(如学习率、批量大小等)
    - 特征工程(如特征选择、降维等)

    通过合理的模型设计和超参数调优,我们可以寻找到一个恰当的模型复杂度,使其既能很好地拟合训练数据,又能在新数据上保持良好的泛化性能。这就是机器学习中的**bias-variance tradeoff**,也是我们在实际应用中需要权衡的一个关键点。

     3. 核心算法原理和具体操作步骤

    1. import numpy as np
    2. import torch
    3. import torch.nn as nn
    4. import torch.optim as optim
    5. import matplotlib.pyplot as plt
    6. from sklearn.model_selection import train_test_split
    7. # 生成数据
    8. np.random.seed(42)
    9. X = np.random.uniform(-5, 5, 500)
    10. y = X**2 + 1 + np.random.normal(0, 1, 500)
    11. X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=42)
    12. # 定义三种不同复杂度的模型
    13. class UnderFitModel(nn.Module):
    14. def __init__(self):
    15. super(UnderFitModel, self).__init__()
    16. self.fc = nn.Linear(1, 1)
    17. def forward(self, x):
    18. return self.fc(x)
    19. class NormalFitModel(nn.Module):
    20. def __init__(self):
    21. super(NormalFitModel, self).__init__()
    22. self.fc1 = nn.Linear(1, 8)
    23. self.fc2 = nn.Linear(8, 1)
    24. self.activation = nn.ReLU()
    25. def forward(self, x):
    26. x = self.fc1(x)
    27. x = self.activation(x)
    28. x = self.fc2(x)
    29. return x
    30. class OverFitModel(nn.Module):
    31. def __init__(self):
    32. super(OverFitModel, self).__init__()
    33. self.fc1 = nn.Linear(1, 32)
    34. self.fc2 = nn.Linear(32, 32)
    35. self.fc3 = nn.Linear(32, 1)
    36. self.activation = nn.ReLU()
    37. def forward(self, x):
    38. x = self.fc1(x)
    39. x = self.activation(x)
    40. x = self.fc2(x)
    41. x = self.activation(x)
    42. x = self.fc3(x)
    43. return x
    44. # 训练模型并记录误差
    45. def train_and_evaluate(model, train_loader, test_loader):
    46. optimizer = torch.optim.SGD(model.parameters(), lr=0.005)
    47. criterion = nn.MSELoss()
    48. train_losses = []
    49. test_losses = []
    50. for epoch in range(100):
    51. model.train()
    52. train_loss = 0.0
    53. for inputs, targets in train_loader:
    54. optimizer.zero_grad()
    55. outputs = model(inputs)
    56. loss = criterion(outputs, targets)
    57. loss.backward()
    58. optimizer.step()
    59. train_loss += loss.item()
    60. train_loss /= len(train_loader)
    61. train_losses.append(train_loss)
    62. model.eval()
    63. test_loss = 0.0
    64. with torch.no_grad():
    65. for inputs, targets in test_loader:
    66. outputs = model(inputs)
    67. loss = criterion(outputs, targets)
    68. test_loss += loss.item()
    69. test_loss /= len(test_loader)
    70. test_losses.append(test_loss)
    71. return train_losses, test_losses
    72. # 训练三种模型并可视化
    73. under_fit_model = UnderFitModel()
    74. normal_fit_model = NormalFitModel()
    75. over_fit_model = OverFitModel()
    76. under_fit_train_losses, under_fit_test_losses = train_and_evaluate(under_fit_model, train_loader, test_loader)
    77. normal_fit_train_losses, normal_fit_test_losses = train_and_evaluate(normal_fit_model, train_loader, test_loader)
    78. over_fit_train_losses, over_fit_test_losses = train_and_evaluate(over_fit_model, train_loader, test_loader)
    79. plt.figure(figsize=(12, 6))
    80. plt.subplot(1, 2, 1)
    81. plt.plot(under_fit_train_losses, label='Under-fit Train Loss')
    82. plt.plot(under_fit_test_losses, label='Under-fit Test Loss')
    83. plt.plot(normal_fit_train_losses, label='Normal-fit Train Loss')
    84. plt.plot(normal_fit_test_losses, label='Normal-fit Test Loss')
    85. plt.plot(over_fit_train_losses, label='Over-fit Train Loss')
    86. plt.plot(over_fit_test_losses, label='Over-fit Test Loss')
    87. plt.xlabel('Epoch')
    88. plt.ylabel('MSE Loss')
    89. plt.title('Training and Test Loss Curves')
    90. plt.legend()
    91. plt.subplot(1, 2, 2)
    92. plt.scatter(X_test, y_test, label='True')
    93. plt.scatter(X_test, under_fit_model(X_test).detach().numpy(), label='Under-fit Prediction')
    94. plt.scatter(X_test, normal_fit_model(X_test).detach().numpy(), label='Normal-fit Prediction')
    95. plt.scatter(X_test, over_fit_model(X_test).detach().numpy(), label='Over-fit Prediction')
    96. plt.xlabel('x')
    97. plt.ylabel('y')
    98. plt.title('Test Set Predictions')
    99. plt.legend()
    100. plt.show()

    这个代码示例涵盖了我们之前讨论的各个步骤:

    数据生成: 我们生成了一个抛物线形状的数据集,并使用train_test_split函数将其划分为训练集和测试集。
    模型定义: 我们定义了三种不同复杂度的PyTorch模型,分别对应欠拟合、正常拟合和过拟合的情况。
    训练与评估: 我们实现了一个train_and_evaluate函数,该函数负责训练模型并记录训练集和测试集上的损失。
    可视化: 最后,我们使用matplotlib绘制了训练损失和测试损失的曲线图,以及在测试集上的预测结果。

    欠拟合模型:训练误差和测试误差都较大,说明模型无法很好地拟合数据。在测试集上的预测结果也存在较大偏差。
    正常拟合模型:训练误差和测试误差较为接近,说明模型的拟合效果较好。在测试集上的预测也比较准确。
    过拟合模型:训练误差很小,但测试误差较大,说明模型在训练集上表现很好,但在新数据上泛化能力较差。在测试集上的预测结果存在一定偏差。
    通过这个实例,我们可以直观地观察到不同复杂度模型在训练和泛化性能上的差异。欠拟合模型在训练集和测试集上的损失都较大,说明模型无法很好地拟合数据。正常拟合模型在训练集和测试集上的损失较为接近,说明模型具有较好的泛化能力。而过拟合模型在训练集上的损失很小,但在测试集上的损失较大,说明模型过于复杂,在新数据上泛化性能较差。

    通过这种观察训练误差和测试误差的方法,我们可以及时发现模型存在的问题,并针对性地调整模型结构、添加正则化等手段来优化模型性能。这是机器学习和深度学习中非常基础和重要的实践技能。

  • 相关阅读:
    网络安全-学习手册
    uniapp使用request下载图片,并且显示出来
    2023第十二届中国智能产业高峰论坛
    【面试题】 TypeScript 前端面试题 由浅到深
    汇编反外挂
    Linux使用systemctl实现开机自启动
    若依3.6.0使用Mybatis-plus分页失效以及完美替换Pagehelper
    怎么用JMeter写性能测试脚本
    卸载重装最新版mysql数据库亲测有效
    RSA密码的手动算法+快速幂算法
  • 原文地址:https://blog.csdn.net/qq_41238579/article/details/139440890