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

过拟合欠拟合的直观展示

欠拟合(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.legend()
    plt.ylim((0,200))
    plt.show()

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值