深度学习②|实现人数回归预测

一、前言

在深度学习的任务中,回归预测是一类常见的场景,主要用于预测连续型的数值结果。本文是我在学习新冠阳性人数回归预测后的总结,用于今后的复盘、回顾。如有问题,还望指教。

本项目实现的核心技术栈:

①框架:PyTorch

②数据处理:NumPy、CSV模块

③特征选择:SelectKBest 、卡方检验

④可视化:Matplotlib


二、实现步骤模块

项目的完整流程可概括为:①数据预处理与特征选择②自定义数据集类 → ③构建神经网络模型 → ④训练与验证 → ⑤测试评估 → ⑥保存结果,下面将分模块详细讲解

2.1导入模块

复制代码
# 数据可视化
import matplotlib.pyplot as plt
# 张量计算、神经网络、自动求导
import torch
# 数值计算与矩阵运算
import numpy as np
# CSV文件操作
import csv
# 数据加载核心类
from torch.utils.data import DataLoader, Dataset
# 神经网络模块
import torch.nn as nn
# 优化器模块
from torch import optim
# 训练耗时统计
import time
# 特征选择工具
from sklearn.feature_selection import SelectKBest, chi2
# 操作系统相关操
import os

# 解决Windows系统下libiomp5md.dll库冲突问题
os.environ["KMP_DUPLICATE_LIB_OK"] = "TRUE"

2.2特征选择

在提供的数据集中,不是每一列都重要,部分特征对预期结果贡献很低。甚至是纯噪音,导致数据出现过拟合。于是相关系数的考虑就非常重要。

本项目采用 SelectKBest 结合卡方检验(chi2),筛选出与标签(新冠阳性人数)相关性最高的 k 个特征,提升模型训练效率和预测精度。

复制代码
def get_feature_importance(feature_data, label_data, k=4, column=None):
    """
    筛选与标签相关性最高的k个特征,返回筛选后的数据和特征下标
    """
    # 初始化特征选择器,采用卡方检验评分
    model = SelectKBest(chi2, k=k)
    # 转换数据类型,满足卡方检验要求
    feature_data = np.array(feature_data, dtype=np.float64)
    # 拟合数据并完成特征选择
    X_new = model.fit_transform(feature_data, label_data)
    
    # 获取特征得分并降序排序,提取最优特征下标
    scores = model.scores_
    indices = np.argsort(scores)[::-1]
    
    # 打印选中的特征名称(若传入列名)
    if column:
        k_best_features = [column[i+1] for i in indices[0:k].tolist()]
        print('最优k个特征:', k_best_features)
    
    return X_new, indices[0:k]

2.3数据集类

PyTorch提供了Dataset类,用于封装自定义的数据集,我们需要进程该类,实现我们最核心的三个方法:initgetitemlen,并实现数据读取、预处理、数据集划分、归一化等操作。

复制代码
class CovidDataset(Dataset):
    def __init__(self, file_path, mode="train", all_feature=False, feature_dim=6):
        # 读取CSV文件
        with open(file_path, "r") as f:
            ori_data = list(csv.reader(f))
            column = ori_data[0]
            csv_data = np.array(ori_data[1:])[:, 1:].astype(float)
        
        # 提取特征与标签数据
        feature = np.array(ori_data[1:])[:, 1:-1]
        label_data = np.array(ori_data[1:])[:, -1]
        
        # 筛选特征(全部特征/最优k个特征)
        if all_feature:
            col = np.array([i for i in range(0, 93)])
        else:
            _, col = get_feature_importance(feature, label_data, feature_dim, column)
        col = col.tolist()
        csv_data = csv_data.astype(np.float32)
        
        # 划分训练/验证/测试集
        if mode == "train":
            indices = [i for i in range(len(csv_data)) if i % 5 != 0]  # 80%训练集
            data = torch.tensor(csv_data[indices, :-1])
            self.Y = torch.tensor(csv_data[indices, -1])
        elif mode == "val":
            indices = [i for i in range(len(csv_data)) if i % 5 == 0]  # 20%验证集
            data = torch.tensor(csv_data[indices, :-1])
            self.Y = torch.tensor(csv_data[indices, -1])
        else:
            indices = [i for i in range(len(csv_data))]
            data = torch.tensor(csv_data[indices])
        
        # 特征筛选与数据归一化
        data = data[:, col]
        self.data = (data - data.mean(dim=0, keepdim=True)) / data.std(dim=0, keepdim=True)
        self.mode = mode
    
    # 获取单条数据(DataLoader调用)
    def __getitem__(self, idx):
        if self.mode != "test":
            return self.data[idx].float(), self.Y[idx]
        else:
            return self.data[idx].float()
    
    # 获取数据集总长度(DataLoader调用)
    def __len__(self):
        return len(self.data)

2.4神经网络模型

本项目构建的是一个非常简单的两层全连接神经网络,激活函数采用ReLu。

神经网络定义已经前项传播步骤如下:

复制代码
class MyModel(nn.Module):
    def __init__(self, inDim):
        super(MyModel, self).__init__()
        # 定义网络层:两层全连接、ReLU激活函数
        self.fc1 = nn.Linear(inDim, 64)  
        self.relu1 = nn.ReLU()
        self.fc2 = nn.Linear(64, 1)       
    
    # 前向传播
    def forward(self, x):
        x = self.fc1(x)
        x = self.relu1(x)
        x = self.fc2(x)
        # 压缩维度,以匹配标签形状
        if len(x.size()) > 1:
            return x.squeeze(1)
        return x

2.5训练与验证

2.5.1定义带L2 正则化的MSE损失函数

正则化的作用是:在模型的原始确实函数中加上一个正则化项,对模型复杂程度进行约束和惩罚,迫使模型放弃过于复杂的拟合方式,缓解过拟合现象,让模拟更加平滑。

复制代码
def mseLoss_with_reg(pred, target, model):
    """
    带L2正则化的均方误差损失函数,防止过拟合
    """
    loss = nn.MSELoss(reduction='mean')
    regularization_loss = 0
    # 计算L2正则项(惩罚过大的模型参数)
    for param in model.parameters():
        regularization_loss += torch.sum(param ** 2)
    # 总损失=基础MSE损失+正则项损失
    return loss(pred, target.float()) + 0.00075 * regularization_loss

2.5.2训练与验证核心函数

主要目的是进行多轮训练,在通过验证,来保存下最优的模型。(注意:在验证时,需要关闭梯度计算)

同时记录下各批次训练的缺失的平均值,并通过可视化展现出来。

复制代码
def train_val(model, train_loader, val_loader, device, epochs, optimizer, loss, save_path):
    # 将模型移到指定的设备(GPU/CPU)上,后续计算都在该设备上进行
    # 如果有 GPU(cuda可用),模型和数据都移到 GPU,训练速度会大幅提升
    model = model.to(device)
    plt_train_loss = []
    plt_val_loss = []
    min_val_loss = 99999  # 初始化最小验证损失
    
    # 创建模型保存文件夹
    os.makedirs(os.path.dirname(save_path), exist_ok=True)
    
    # 轮次训练循环
    for epoch in range(epochs):
        train_loss = 0.0
        val_loss = 0.0
        start_time = time.time()
        
        # 训练模式:开启梯度计算
        model.train()
        for batch_x, batch_y in train_loader:
            x, target = batch_x.to(device), batch_y.to(device)
            # 前向传播
            pred = model(x)
            train_bat_loss = loss(pred, target, model)
            # 反向传播与参数更新
            train_bat_loss.backward()
            optimizer.step()
            optimizer.zero_grad()
            # 累加批次损失
            train_loss += train_bat_loss.cpu().item()
        
        # 计算训练平均损失
        plt_train_loss.append(train_loss / len(train_loader))
        
        # 验证模式:关闭梯度计算
        model.eval()
        with torch.no_grad():
            for batch_x, batch_y in val_loader:
                x, target = batch_x.to(device), batch_y.to(device)
                pred = model(x)
                val_bat_loss = loss(pred, target, model)
                val_loss += val_bat_loss.cpu().item()
        
        # 计算验证平均损失并保存最优模型
        avg_val_loss = val_loss / len(val_loader)
        plt_val_loss.append(avg_val_loss)
        if avg_val_loss < min_val_loss:
            torch.save(model, save_path)
            min_val_loss = avg_val_loss
        
        # 打印训练日志
        print("[%03d/%03d] %2.2f sec(s) Trainloss: %.6f | Valloss: %.6f" % \
              (epoch, epochs, time.time() - start_time, plt_train_loss[-1], plt_val_loss[-1]))
    
    # 绘制损失曲线
    plt.plot(plt_train_loss)
    plt.plot(plt_val_loss)
    plt.title("Training & Validation Loss")
    plt.legend(["train", "val"])
    plt.show()

2.6测试评估

测试函数,将我们保存的最优模型对测试集进行操作,并将结果保存到文件,以便后续对比、使用。

复制代码
def evaluate(save_path, test_loader, device, rel_path):
    # 加载最优模型
    model = torch.load(save_path).to(device)
    rel = []
    
    # 测试集预测(关闭梯度计算)
    with torch.no_grad():
        for x in test_loader:
            pred = model(x.to(device))
            rel.append(pred.cpu().item())
    
    # 保存预测结果到CSV文件
    with open(rel_path, "w", newline='') as f:
        csvWriter = csv.writer(f)
        csvWriter.writerow(["id", "tested_positive"])
        for i, value in enumerate(rel):
            csvWriter.writerow([str(i), str(value)])
    
    print("预测结果已保存至:" + rel_path)

三、串联所有模块

以下代码主要负责定义各种参数、创建数据集和数据加载器、初始化模型、初始化优化器等,最后,通过训练验证和测试评估函数,来完成整个项目流程。

复制代码
if __name__ == "__main__":
    # 1. 特征配置
    all_feature = False
    feature_dim = 93 if all_feature else 6
    
    # 2. 文件路径配置
    train_file = "covid.train.csv"
    test_file = "covid.test.csv"
    
    # 3. 创建数据集
    train_dataset = CovidDataset(train_file, "train", all_feature, feature_dim)
    val_dataset = CovidDataset(train_file, "val", all_feature, feature_dim)
    test_dataset = CovidDataset(test_file, "test", all_feature, feature_dim)
    
    # 4. 创建数据加载器
    batch_size = 16
    train_loader = DataLoader(train_dataset, batch_size=batch_size, shuffle=True)
    val_loader = DataLoader(val_dataset, batch_size=batch_size, shuffle=True)
    test_loader = DataLoader(test_dataset, batch_size=1, shuffle=False)
    
    # 5. 自动选择训练设备(GPU/CPU)
    device = "cuda" if torch.cuda.is_available() else "cpu"
    print(f"当前训练设备:{device}")
    
    # 6. 超参数配置
    config = {
        "lr": 0.001,
        "epochs": 20,
        "momentum": 0.9,
        "save_path": "model_save/best_model.pth",
        "rel_path": "pred.csv"
    }
    
    # 7. 初始化模型、损失函数与优化器
    model = MyModel(inDim=feature_dim).to(device)
    loss = mseLoss_with_reg
    optimizer = optim.SGD(model.parameters(), lr=config["lr"], momentum=config["momentum"])
    
    # 8. 启动训练与验证
    train_val(model, train_loader, val_loader, device, config["epochs"], optimizer, loss, config["save_path"])
    
    # 9. 启动测试与结果保存
    evaluate(config["save_path"], test_loader, device, config["rel_path"])

后续会逐渐完善本篇博客 使过程更加详细完善

四、总结

在本次项目的完成中,我曾遇到过诸多问题,例如:PyTorch版本配置错误、学习率一开始过高、模型参数类型不一致等等,通过查询博客等方式一步一步对该项目进行纠错、复盘与总结。后续会更多的学习相关知识,不断进步。

相关推荐
我的xiaodoujiao1 小时前
使用 Python 语言 从 0 到 1 搭建完整 Web UI自动化测试学习系列 40--完善优化 Allure 测试报告显示内容
python·学习·测试工具·pytest
AI街潜水的八角1 小时前
基于paddle框架的MobileNetV2深度学习神经网络番茄/西红柿叶子病虫害识别系统源码
深度学习·神经网络·paddle
可乐要加冰^-^1 小时前
RL for LLM(large language model)
人工智能·语言模型·自然语言处理
计算机C9硕士_算法工程师1 小时前
基于深度学习风力叶片缺陷检测系统 无人机自动巡检风电场 - 风电运维智能诊断平台 - 缺陷生命周期追踪系统
运维·深度学习·无人机
大模型最新论文速读2 小时前
ProFit: 屏蔽低概率 token,解决 SFT 过拟合问题
人工智能·深度学习·机器学习·语言模型·自然语言处理
cskywit2 小时前
VMamba环境本地适配配置
人工智能·深度学习·mamba
victory04312 小时前
minimind SFT失败原因排查和解决办法
人工智能·python·深度学习
妄汐霜2 小时前
小白学习笔记(MySQL基础中其他知识)
笔记·学习·mysql
逐梦苍穹2 小时前
世界模型通俗讲解:AI大脑里的“物理模拟器“
人工智能·世界模型