【从0到1搞懂大模型】神经网络的实现:数据策略、模型调优与评估体系(3)

发布于:2025-03-10 ⋅ 阅读:(22) ⋅ 点赞:(0)

一、数据集的划分

(1)按一定比例划分为训练集和测试集

我们通常取8-2、7-3、6-4、5-5比例切分,直接将数据随机划分为训练集和测试集,然后使用训练集来生成模型,再用测试集来测试模型的正确率和误差,以验证模型的有效性。

这种方法常见于决策树、朴素贝叶斯分类器、线性回归和逻辑回归等任务中。

(2)交叉验证法

交叉验证一般采用k折交叉验证,即k-fold cross validation,往往k取为10。在这种数据集划分法中,我们将数据集划分为k个子集,每个子集均做一次测试集,每次将其余的作为训练集。在交叉验证时,我们重复训练k次,每次选择一个子集作为测试集,并将k次的平均交叉验证的正确率作为最终的结果。

from sklearn.model_selection import KFold
import numpy as np

# 将PyTorch Tensor转为Numpy
X_np = X.numpy()
y_np = y.numpy()

# 5折交叉验证
kf = KFold(n_splits=5, shuffle=True, random_state=42)
for fold, (train_idx, val_idx) in enumerate(kf.split(X_np)):
    # 转换为PyTorch DataLoader
    train_fold = TensorDataset(torch.from_numpy(X_np[train_idx]), 
                              torch.from_numpy(y_np[train_idx]))
    val_fold = TensorDataset(torch.from_numpy(X_np[val_idx]), 
                            torch.from_numpy(y_np[val_idx]))
    
    train_loader = DataLoader(train_fold, batch_size=32, shuffle=True)
    val_loader = DataLoader(val_fold, batch_size=32)
    
    print(f"Fold {fold+1}: Train {len(train_fold)}, Val {len(val_fold)}")

(3)训练集、验证集、测试集法

我们首先将数据集划分为训练集和测试集,由于模型的构建过程中也需要检验模型,检验模型的配置,以及训练程度,过拟合还是欠拟合,所以会将训练数据再划分为两个部分,一部分是用于训练的训练集,另一部分是进行检验的验证集。验证集可以重复使用,主要是用来辅助我们构建模型的。

训练集用于训练得到神经网络模型,然后用验证集验证模型的有效性,挑选获得最佳效果的模型,直到我们得到一个满意的模型为止。最后,当模型“通过”验证集之后,我们再使用测试集测试模型的最终效果,评估模型的准确率,以及误差等。测试集只在模型检验时使用,绝对不能根据测试集上的结果来调整网络参数配置,以及选择训练好的模型,否则会导致模型在测试集上过拟合。

一般来说,最终的正确率,训练集大于验证集,验证集大于测试集。

对于部分机器学习任务,我们划分的测试集必须是模型从未见过的数据,比如语音识别中一个完全不同的人的说话声,图像识别中一个完全不同的识别个体。这时,一般来说,训练集和验证集的数据分布是同分布的,而测试集的数据分布与前两者会略有不同。在这种情况下,通常,测试集的正确率会比验证集的正确率低得多,这样就可以看出模型的泛化能力,可以预测出实际应用中的真实效果。

下面是按照 8-1-1 划分数据集的代码示例:

import torch
from torch.utils.data import DataLoader, TensorDataset, random_split

# 生成示例数据
X = torch.randn(1000, 10)  # 1000个样本,10维特征
y = torch.randint(0, 2, (1000,))  # 二分类标签
dataset = TensorDataset(X, y)

# 划分比例:80%训练,10%验证,10%测试
train_size = int(0.8 * len(dataset))
val_size = int(0.1 * len(dataset))
test_size = len(dataset) - train_size - val_size

train_dataset, val_dataset, test_dataset = random_split(
    dataset, [train_size, val_size, test_size],
    generator=torch.Generator().manual_seed(42)  # 固定随机种子
)

# 创建DataLoader
train_loader = DataLoader(train_dataset, batch_size=32, shuffle=True)
val_loader = DataLoader(val_dataset, batch_size=32)
test_loader = DataLoader(test_dataset, batch_size=32)

print(f"训练集样本数: {len(train_dataset)}")  # 直接查看原始数据集
print(f"DataLoader 批次数量: {len(train_loader)}")  # 总批次 = 样本数 // batch_size

# 获取第一个批次的数据
batch = next(iter(train_loader))  # 返回的是 (features, labels)
features, labels = batch

# 查看特征和标签的形状
print("特征张量形状:", features.shape)  # [batch_size, 10]
print("标签张量形状:", labels.shape)    # [batch_size]
print("标签示例:", labels[:5])         # 查看前5个标签

二、偏差与方差

  • 假设这就是数据集,如果给这个数据集拟合一条直线,可能得到一个逻辑回归拟合,但它并不能很好地拟合该数据,这是高偏差(high bias)的情况,我们称为**“欠拟合”(underfitting)**。

  • 相反的如果我们拟合一个非常复杂的分类器,比如深度神经网络或含有隐藏单元的神经网络,可能就非常适用于这个数据集,但是这看起来也不是一种很好的拟合方式分类器方差较高(high variance),数据过度拟合(overfitting)

衡量方式

  • 一般可通过查看训练集与验证集误差来诊断。
  1. 评估偏差(bias),一般看训练集 训练集误差大——偏差较高,欠拟合

  2. 评估方差(variance),一般看验证集 训练集误差小,验证集误差大——方差较高,过拟合

学习曲线

  • 学习曲线作用: 查看模型的学习效果; 通过学习曲线可以清晰的看出模型对数据的过拟合和欠拟合;

  • 学习曲线:随着训练样本的逐渐增多,算法训练出的模型的表现能力;

  • 表现能力:也就是模型的预测准确情况。

 

总结就是如果训练样本准确率一直上不去就是欠拟合,如果训练集准确率很高,但是验证集很低,就是过拟合。下面是两个案例

案例 1——欠拟合

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

# 生成非线性数据(100个样本)
torch.manual_seed(42)
x = torch.unsqueeze(torch.linspace(-5, 5, 100), 1)
y = torch.sin(x) * 2 + torch.normal(0, 0.3, x.shape)

# 划分训练集(70%)和验证集(30%)
split = int(0.7 * len(x))
x_train, y_train = x[:split], y[:split]
x_val, y_val = x[split:], y[split:]

# 构建欠拟合模型(单层线性回归)
class UnderfitModel(nn.Module):
    def __init__(self):
        super().__init__()
        self.linear = nn.Linear(1, 1)  # 仅一个线性层
    
    def forward(self, x):
        return self.linear(x)

model = UnderfitModel()
optimizer = optim.SGD(model.parameters(), lr=0.01)  # 使用低学习率
criterion = nn.MSELoss()

# 训练过程(仅50次迭代)
train_loss = []
val_loss = []
for epoch in range(50):
    # 训练模式
    model.train()
    output = model(x_train)
    loss = criterion(output, y_train)
    
    optimizer.zero_grad()
    loss.backward()
    optimizer.step()
    
    # 验证模式
    model.eval()
    with torch.no_grad():
        val_pred = model(x_val)
        v_loss = criterion(val_pred, y_val)
    
    # 记录损失
    train_loss.append(loss.item())
    val_loss.append(v_loss.item())

# 可视化损失曲线
plt.figure(figsize=(10,5))
plt.plot(train_loss, label='train_loss')
plt.plot(val_loss, label='valid_loss')
plt.ylim(0, 5)
plt.legend()
plt.title("欠拟合训练过程")
plt.show()

# 最终预测可视化
model.eval()
with torch.no_grad():
    pred = model(x)

plt.figure(figsize=(12,5))
plt.scatter(x_train, y_train, c='r', label='train_data')
plt.scatter(x_val, y_val, c='g', label='valid_data')
plt.plot(x, pred.numpy(), 'b-', lw=3, label='model_predict')
plt.plot(x, torch.sin(x)*2, 'k--', label='true_function')
plt.legend()
plt.show()

# 输出误差指标
print(f'[最终误差] 训练集:{train_loss[-1]:.4f} | 验证集:{val_loss[-1]:.4f}')
print(f'模型参数:w={model.linear.weight.item():.2f}, b={model.linear.bias.item():.2f}')

它最终的学习曲线如下

可以很明显看到模型预测与真实曲线相差很远

案例 2——过拟合 

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

# 生成少量训练数据(20个样本)
torch.manual_seed(1)
x_train = torch.unsqueeze(torch.linspace(-5, 5, 20), dim=1)
y_train = 1.2 * x_train + 0.8 + torch.normal(0, 0.5, size=x_train.size())

# 构建过参数化模型(4层全连接网络)
class OverfitModel(nn.Module):
    def __init__(self):
        super().__init__()
        self.net = nn.Sequential(
            nn.Linear(1, 100),
            nn.ReLU(),
            nn.Linear(100, 100),
            nn.ReLU(),
            nn.Linear(100, 100),
            nn.ReLU(),
            nn.Linear(100, 1)
        )
    
    def forward(self, x):
        return self.net(x)

model = OverfitModel()
optimizer = optim.Adam(model.parameters(), lr=0.01)
criterion = nn.MSELoss()

# 训练循环(3000次迭代)
loss_history = []
for epoch in range(3000):
    output = model(x_train)
    loss = criterion(output, y_train)
    
    optimizer.zero_grad()
    loss.backward()
    optimizer.step()
    
    loss_history.append(loss.item())

# 生成测试数据(同分布但未参与训练)
x_test = torch.unsqueeze(torch.linspace(-6, 6, 100), dim=1)
y_test = 1.2 * x_test + 0.8 + torch.normal(0, 0.5, size=x_test.size())

# 绘制结果对比
plt.figure(figsize=(12,5))
plt.scatter(x_train.numpy(), y_train.numpy(), c='r', label='train_data')
plt.plot(x_test.numpy(), model(x_test).detach().numpy(), 'b-', lw=3, label='predict')
plt.plot(x_test.numpy(), 1.2*x_test+0.8, 'g--', label='true_function')
plt.legend()
plt.show()

# 输出训练误差和测试误差
train_loss = criterion(model(x_train), y_train)
test_loss = criterion(model(x_test), y_test)
print(f'训练误差:{train_loss.item():.4f}')
print(f'测试误差:{test_loss.item():.4f}')

可以看到最后生成的拟合曲线如蓝色所示,很明显过拟合,切测试误差比训练误差大很多 

三、过拟合&欠拟合的处理方式

1、首先根据训练集效果来判断是否是高偏差?也就是是否欠拟合。

如果不是,跳转到下一步(判断是否高方差)。

如果是,有四种可尝试的方法:

A、新网络,比如:更多的隐藏层或隐藏单元。

B、增加新特征,可以考虑加入进特征组合、高次特征或者添加多项式特征(将线性模型通过添加二次项或者三次项使模型泛化能力更强)

C、用更多时间训练算法。

D、尝试更先进的优化算法。

反复调试,直到偏差降到和接受范围内,然后进行下一步。

2、根据验证集效果来判断是否是高方差?也就是是否过拟合。

如果不是,说明得到了很好的结果,训练结束,开始将该模型放入测试集。

如果是,有三种可尝试的方法:

A、更多数据来训练。

B、正则化来减少过拟合。

C、控制模型的复杂度,用dropout、early stopping等方法

D、尝试新网络框架(有时有用有时没用)。

名词解释

- 正则化

L2正则化:目标函数中增加所有权重w参数的平方之和, 逼迫所有w尽可能趋向零但不为零. 因为过拟合的时候, 拟合函数需要顾忌每一个点, 最终形成的拟合函数波动很大, 在某些很小的区间里, 函数值的变化很剧烈, 也就是某些w非常大. 为此, L2正则化的加入就惩罚了权重变大的趋势.

model = nn.Sequential(
    nn.Linear(784, 256),
    nn.ReLU(),
    nn.Linear(256, 10)
)

# 设置优化器时添加weight_decay参数(L2系数)
optimizer = optim.Adam(model.parameters(), lr=0.001, weight_decay=1e-4)

L1正则化:目标函数中增加所有权重w参数的绝对值之和, 逼迫更多w为零(也就是变稀疏. L2因为其导数也趋0, 奔向零的速度不如L1给力了).大家对稀疏规则化趋之若鹜的一个关键原因在于它能实现特征的自动选择。一般来说,xi的大部分元素(也就是特征)都是和最终的输出yi没有关系或者不提供任何信息的,在最小化目标函数的时候考虑xi这些额外的特征,虽然可以获得更小的训练误差,但在预测新的样本时,这些没用的特征权重反而会被考虑,从而干扰了对正确yi的预测。稀疏规则化算子的引入就是为了完成特征自动选择的光荣使命,它会学习地去掉这些无用的特征,也就是把这些特征对应的权重置为0。

L1 正则化 torch 没有直接实现,可以手动实现

def l1_regularization(model, lambda_l1):
    l1_loss = 0
    for param in model.parameters():
        l1_loss += torch.sum(torch.abs(param))
    return lambda_l1 * l1_loss

# 训练循环
for data, target in dataloader:
    optimizer.zero_grad()
    output = model(data)
    loss = F.cross_entropy(output, target)
    
    # 添加L1正则项
    l1_lambda = 0.001
    loss += l1_regularization(model, l1_lambda)
    
    loss.backward()
    optimizer.step()

下面是一个同时引用 L1 正则化和 L2 正则化的案例

import torch
import torch.nn as nn
import torch.optim as optim
import torch.nn.functional as F

class RegularizedModel(nn.Module):
    def __init__(self):
        super().__init__()
        self.fc = nn.Sequential(
            nn.Flatten(), 
            nn.Linear(784, 256),
            nn.BatchNorm1d(256),
            nn.ReLU(),
            nn.Linear(256, 10)
        )
        
    def forward(self, x):
        return self.fc(x)

def train(model, train_loader, lambda_l1=0, lambda_l2=0):
    optimizer = optim.AdamW(model.parameters(), lr=0.001)
    
    for epoch in range(100):
        total_loss = 0
        for x, y in train_loader:
            optimizer.zero_grad()
            pred = model(x)
            loss = F.cross_entropy(pred, y)
            
            # L1正则项
            if lambda_l1 > 0:
                l1 = sum(p.abs().sum() for p in model.parameters())
                loss += lambda_l1 * l1
                
            # L2正则项
            if lambda_l2 > 0:
                l2 = sum(p.pow(2).sum() for p in model.parameters())
                loss += 0.5 * lambda_l2 * l2
                
            loss.backward()
            optimizer.step()
            total_loss += loss.item()
            
        print(f"Epoch {epoch}: Loss={total_loss/len(train_loader):.4f}")

# 使用示例

import torch
from torch.utils.data import DataLoader, TensorDataset

# 参数配置
batch_size = 16
input_dim = 784  # 对应模型输入维度
num_classes = 10
num_samples = 1000  # 总样本量

# 生成正态分布虚拟数据
X = torch.randn(num_samples, input_dim)  # 形状 (1000, 784)
y = torch.randint(0, num_classes, (num_samples,))  # 随机标签

# 创建数据集和数据加载器
dataset = TensorDataset(X, y)
train_loader = DataLoader(dataset, batch_size=batch_size, shuffle=True)

# 验证数据形状
sample_X, sample_y = next(iter(train_loader))
print(f"输入数据形状: {sample_X.shape}")  # 应输出 torch.Size([16, 784])
print(f"标签形状: {sample_y.shape}")      # 应输出 torch.Size([16])

model = RegularizedModel()
train(model, train_loader, lambda_l1=1e-5, lambda_l2=1e-4)

- dropout

在训练的运行的时候,让神经元以超参数p的概率被激活(也就是1-p的概率被设置为0), 每个w因此随机参与, 使得任意w都不是不可或缺的, 效果类似于数量巨大的模型集成。

class RegularizedModel(nn.Module):
    def __init__(self, input_dim):
        super().__init__()
        self.net = nn.Sequential(
            nn.Linear(input_dim, 128),
            nn.ReLU(),
            nn.Dropout(0.5),  # 添加Dropout层
            nn.Linear(128, 64),
            nn.ReLU(),
            nn.Dropout(0.3),
            nn.Linear(64, 1)
        )
        
    def forward(self, x):
        return torch.sigmoid(self.net(x)).squeeze()
- early stop

提前终止训练,即在模型对训练数据集迭代收敛之前停止迭代来防止过拟合,常用的停止条件就是当 N 轮迭代都loss 都没有降低后可以停止迭代

下面案例的停止条件就是当 loss 连续 10 次都没有低于最佳 loss-0.001时就触发,这里设置了一个delta为 0.001,就能保证即使损失有波动,只要未突破阈值就计数

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

# 早停监控器(带模型保存功能)
class EarlyStopper:
    def __init__(self, patience=5, delta=0, path='best_model.pth'):
        self.patience = patience    # 容忍epoch数
        self.delta = delta          # 视为改进的最小变化量
        self.path = path            # 最佳模型保存路径
        self.counter = 0            # 未改进计数器
        self.best_score = None      # 最佳监控指标值
        self.early_stop = False     # 停止标志

    def __call__(self, val_loss, model):
        score = -val_loss  # 默认监控验证损失(越大越好)
        
        if self.best_score is None:
            self.best_score = score
            self.save_checkpoint(model)
        elif score < self.best_score + self.delta:
            self.counter += 1
            print(f'EarlyStopping counter: {self.counter}/{self.patience}')
            if self.counter >= self.patience:
                self.early_stop = True
        else:
            self.best_score = score
            self.save_checkpoint(model)
            self.counter = 0

    def save_checkpoint(self, model):
        torch.save(model.state_dict(), self.path)

# 生成模拟数据(回归任务)
def generate_data(samples=1000):
    X = torch.linspace(-10, 10, samples).unsqueeze(1)
    y = 0.5 * X**3 - 2 * X**2 + 3 * X + torch.randn(X.size()) * 10
    return X, y

# 过参数化的全连接网络
class OverfitModel(nn.Module):
    def __init__(self):
        super().__init__()
        self.net = nn.Sequential(
            nn.Linear(1, 128),
            nn.ReLU(),
            nn.Linear(128, 256),
            nn.ReLU(),
            nn.Linear(256, 128),
            nn.ReLU(),
            nn.Linear(128, 1)
        )

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

# 训练函数(集成早停)
def train_with_earlystop(model, train_loader, val_loader, epochs=1000):
    optimizer = optim.Adam(model.parameters(), lr=0.001)
    criterion = nn.MSELoss()
    early_stopper = EarlyStopper(patience=10, delta=0.001)
    
    train_losses = []
    val_losses = []

    for epoch in range(epochs):
        # 训练阶段
        model.train()
        train_loss = 0
        for X_batch, y_batch in train_loader:
            optimizer.zero_grad()
            pred = model(X_batch)
            loss = criterion(pred, y_batch)
            loss.backward()
            optimizer.step()
            train_loss += loss.item()
        train_loss /= len(train_loader)
        train_losses.append(train_loss)

        # 验证阶段
        model.eval()
        val_loss = 0
        with torch.no_grad():
            for X_val, y_val in val_loader:
                pred_val = model(X_val)
                val_loss += criterion(pred_val, y_val).item()
        val_loss /= len(val_loader)
        val_losses.append(val_loss)

        print(f'Epoch {epoch+1:03d} | '
              f'Train Loss: {train_loss:.4f} | '
              f'Val Loss: {val_loss:.4f}')

        # 早停检查
        early_stopper(val_loss, model)
        if early_stopper.early_stop:
            print("==> Early stopping triggered")
            break

    # 恢复最佳模型
    model.load_state_dict(torch.load('best_model.pth'))
    return train_losses, val_losses

# 可视化训练过程
def plot_learning_curve(train_loss, val_loss):
    plt.figure(figsize=(10, 6))
    plt.plot(train_loss, label='Training Loss')
    plt.plot(val_loss, label='Validation Loss')
    plt.xlabel('Epochs')
    plt.ylabel('Loss')
    plt.title('Learning Curve with Early Stopping')
    plt.legend()
    plt.grid(True)
    plt.show()

# 主程序
if __name__ == "__main__":
    # 数据准备
    X, y = generate_data()
    dataset = TensorDataset(X, y)
    
    # 划分训练集和验证集(8:2)
    train_size = int(0.8 * len(dataset))
    val_size = len(dataset) - train_size
    train_dataset, val_dataset = torch.utils.data.random_split(dataset, [train_size, val_size])
    
    train_loader = DataLoader(train_dataset, batch_size=32, shuffle=True)
    val_loader = DataLoader(val_dataset, batch_size=32)

    # 初始化模型
    model = OverfitModel()

    # 开始训练
    train_loss, val_loss = train_with_earlystop(model, train_loader, val_loader, epochs=1000)

    # 绘制学习曲线
    plot_learning_curve(train_loss, val_loss)

    # 最终模型测试
    model.eval()
    with torch.no_grad():
        test_input = torch.tensor([[5.0], [-3.0], [8.0]])
        predictions = model(test_input)
        print("\nModel Predictions at x=5, -3, 8:")
        print(predictions.numpy())

四、模型的效果评估

具体模型的评估方法也可以看我之前的文章,本文主要补充一些代码案例【Python数据分析】数据挖掘建模——分类与预测算法评价(含ROC曲线、F1等指标的解释)_分类f1指标 python-CSDN博客

(1)分类任务评估(混淆矩阵、AUC)

from sklearn.metrics import confusion_matrix, roc_auc_score

def evaluate_model(model, loader):
    model.eval()
    all_preds = []
    all_labels = []
    
    with torch.no_grad():
        for X_batch, y_batch in loader:
            outputs = model(X_batch)
            preds = (outputs > 0.5).float()
            all_preds.extend(preds.cpu().numpy())
            all_labels.extend(y_batch.cpu().numpy())
    
    # 计算指标
    cm = confusion_matrix(all_labels, all_preds)
    auc = roc_auc_score(all_labels, all_preds)
    
    print("Confusion Matrix:")
    print(cm)
    print(f"AUC Score: {auc:.4f}")

# 在测试集上评估
evaluate_model(model, test_loader)

(2)回归任务评估(MAE、MSE) 

def evaluate_regression(model, loader):
    model.eval()
    total_mae = 0
    total_mse = 0
    
    with torch.no_grad():
        for X_batch, y_batch in loader:
            outputs = model(X_batch)
            mae = torch.abs(outputs - y_batch).mean()
            mse = ((outputs - y_batch)**2).mean()
            total_mae += mae.item() * X_batch.size(0)
            total_mse += mse.item() * X_batch.size(0)
    
    mae = total_mae / len(loader.dataset)
    mse = total_mse / len(loader.dataset)
    
    print(f"MAE: {mae:.4f}, MSE: {mse:.4f}")

当然也可以把二者结合一下

import torch
import numpy as np
import matplotlib.pyplot as plt
from sklearn.metrics import (
    roc_curve, 
    auc, 
    confusion_matrix, 
    precision_recall_curve,
    r2_score,
    mean_squared_error
)
import seaborn as sns

def evaluate_model(model, data_loader, task_type='classification'):
    """综合模型评估函数
    
    参数:
        model : 训练好的PyTorch模型
        data_loader : 数据加载器
        task_type : 任务类型 ['classification', 'regression']
        
    返回:
        包含各项指标的字典
    """
    model.eval()
    device = next(model.parameters()).device
    
    all_targets = []
    all_outputs = []
    
    with torch.no_grad():
        for inputs, targets in data_loader:
            inputs = inputs.to(device)
            outputs = model(inputs)
            
            all_targets.append(targets.cpu().numpy())
            all_outputs.append(outputs.cpu().numpy())
    
    y_true = np.concatenate(all_targets)
    y_pred = np.concatenate(all_outputs)
    
    metrics = {}
    
    if task_type == 'classification':
        # 分类任务指标
        y_prob = torch.softmax(torch.tensor(y_pred), dim=1).numpy()
        y_pred_labels = np.argmax(y_pred, axis=1)
        
        # 多分类AUC计算(OvR策略)
        fpr = dict()
        tpr = dict()
        roc_auc = dict()
        n_classes = y_prob.shape[1]
        
        for i in range(n_classes):
            fpr[i], tpr[i], _ = roc_curve((y_true == i).astype(int), y_prob[:, i])
            roc_auc[i] = auc(fpr[i], tpr[i])
        
        # 计算宏观平均AUC
        fpr["macro"], tpr["macro"], _ = roc_curve(y_true.ravel(), y_prob.ravel())
        roc_auc["macro"] = auc(fpr["macro"], tpr["macro"])
        
        metrics.update({
            'accuracy': np.mean(y_pred_labels == y_true),
            'auc_macro': roc_auc["macro"],
            'confusion_matrix': confusion_matrix(y_true, y_pred_labels),
            'classification_report': classification_report(y_true, y_pred_labels)
        })
        
        # 绘制ROC曲线
        plt.figure(figsize=(10, 6))
        for i in range(n_classes):
            plt.plot(fpr[i], tpr[i], lw=1,
                     label=f'Class {i} (AUC = {roc_auc[i]:.2f})')
        plt.plot([0, 1], [0, 1], 'k--', lw=1)
        plt.xlim([0.0, 1.0])
        plt.ylim([0.0, 1.05])
        plt.xlabel('False Positive Rate')
        plt.ylabel('True Positive Rate')
        plt.title('ROC Curves')
        plt.legend(loc="lower right")
        plt.show()
        
    elif task_type == 'regression':
        # 回归任务指标
        metrics.update({
            'mse': mean_squared_error(y_true, y_pred),
            'mae': np.mean(np.abs(y_true - y_pred)),
            'r2': r2_score(y_true, y_pred)
        })
        
        # 绘制预测值与真实值散点图
        plt.figure(figsize=(8, 6))
        plt.scatter(y_true, y_pred, alpha=0.5)
        plt.plot([y_true.min(), y_true.max()], 
                 [y_true.min(), y_true.max()], 'r--')
        plt.xlabel('True Values')
        plt.ylabel('Predictions')
        plt.title('Regression Evaluation')
        plt.show()
    
    return metrics

# 使用示例(分类任务)
if __name__ == "__main__":
    # 假设已有训练好的分类模型和数据加载器
    from sklearn.metrics import classification_report
    
    # 加载测试数据
    test_loader = DataLoader(test_dataset, batch_size=32, shuffle=False)
    
    # 进行评估
    classification_metrics = evaluate_model(model, test_loader, 'classification')
    
    # 打印关键指标
    print(f"准确率: {classification_metrics['accuracy']:.4f}")
    print(f"宏观平均AUC: {classification_metrics['auc_macro']:.4f}")
    print("\n分类报告:")
    print(classification_metrics['classification_report'])
    
    # 绘制混淆矩阵
    plt.figure(figsize=(10, 8))
    sns.heatmap(classification_metrics['confusion_matrix'], 
                annot=True, fmt='d', cmap='Blues')
    plt.title('Confusion Matrix')
    plt.xlabel('Predicted Label')
    plt.ylabel('True Label')
    plt.show()

# 回归任务使用示例
# regression_metrics = evaluate_model(model, test_loader, 'regression')
# print(f"MSE: {regression_metrics['mse']:.4f}")
# print(f"R²: {regression_metrics['r2']:.4f}")

好啦 神经网络的基础就在这结束啦,之后就开始进一步讲深度学习内容(包括 RNN、LSTM、transformer 等)最后就引出大模型的原理,继续期待叭~