HW01: COVlD-19 Cases Prediction

Machine Learning HW1: COVlD-19 Cases Prediction

baseline网址
colab网址

作业概述

目标

  • deep neural networks (DNN)架构解决一个regression的问题
  • 理解基础的 DNN 训练方法:调参数、选择feature 、regularization(正则化)
  • 熟悉 PyTorch

任务

给出过去4天的数据,预测第五天的感染人数
在这里插入图片描述

数据

在这里插入图片描述

评价指标(MSE)

平均预测误差
在这里插入图片描述

原始代码(baseline)

在Golab里面打开
在这里插入图片描述

1. Download data

直接在kaggle链接里面download all,解压缩后上传
在这里插入图片描述
在这里插入图片描述
train.csv训练数据
哪列为1就代表是哪个州
在这里插入图片描述
黄色是第1、2、3、4天的检测情况,红色是我们要预测的第5天的情况
在这里插入图片描述

2. Import packages

# Numerical Operations
import math  # 导入math库,用于执行数学运算,如三角函数、指数等。
import numpy as np  # 导入numpy库,并重命名为np,用于高效的数值计算。

# Reading/Writing Data
import pandas as pd  # 导入pandas库,并重命名为pd,用于数据分析和操作。
import os  # 导入os库,用于操作系统功能,如文件路径操作。
import csv  # 导入csv库,用于读写CSV文件。

# For Progress Bar
from tqdm import tqdm  # 从tqdm库中导入tqdm函数,用于在长循环中添加一个进度条。

# Pytorch
import torch  # 导入PyTorch库,用于深度学习模型的构建和训练。
import torch.nn as nn  # 从PyTorch库中导入nn模块,并重命名为nn,用于构建神经网络。
from torch.utils.data import Dataset, DataLoader, random_split  # 从PyTorch库中导入数据集处理相关的类和函数。

# For plotting learning curve
from torch.utils.tensorboard import SummaryWriter  # 从PyTorch库的tensorboard工具中导入SummaryWriter,用于记录训练过程中的指标,以便于后续使用TensorBoard进行可视化。

3. Some Utility Functions(公用函数)

不用改变

def same_seed(seed): 
    '''设置随机种子以保证结果可复现'''
    torch.backends.cudnn.deterministic = True  # 确保卷积操作可复现
    torch.backends.cudnn.benchmark = False  # 关闭性能优化以保证可复现
    np.random.seed(seed)  # 设置numpy随机种子
    torch.manual_seed(seed)  # 设置PyTorch随机种子
    if torch.cuda.is_available():
        torch.cuda.manual_seed_all(seed)  # 如果使用GPU,设置所有GPU的随机种子

def train_valid_split(data_set, valid_ratio, seed):
    '''将数据集分割为训练集和验证集'''
    valid_set_size = int(valid_ratio * len(data_set))  # 计算验证集大小
    train_set_size = len(data_set) - valid_set_size  # 计算训练集大小
    train_set, valid_set = random_split(data_set, [train_set_size, valid_set_size], 
                                         generator=torch.Generator().manual_seed(seed))  # 随机分割数据集
    return np.array(train_set), np.array(valid_set)  # 返回numpy数组格式的训练集和验证集

def predict(test_loader, model, device):
    '''对测试数据进行预测'''
    model.eval()  # 设置模型为评估模式
    preds = []  # 初始化预测结果列表
    for x in tqdm(test_loader):  # 遍历测试数据
        x = x.to(device)  # 将数据发送到设备(CPU或GPU)
        with torch.no_grad():  # 不计算梯度,节省内存和计算资源
            pred = model(x)  # 模型预测
            preds.append(pred.detach().cpu())  # 将预测结果移动到CPU并添加到列表
    preds = torch.cat(preds, dim=0).numpy()  # 合并预测结果并转换为numpy数组
    return preds  # 返回预测结果

4. Dataset

自定义的PyTorch数据集类,用于处理COVID-19相关的数据。它继承自PyTorch的Dataset类,并实现了__init__、__getitem__和__len__三个方法。

class COVID19Dataset(Dataset):
    '''
    自定义PyTorch数据集类,用于COVID-19数据。
    x: 特征数据。
    y: 目标数据(标签),如果为None,则用于预测(不提供标签)。
    '''
    def __init__(self, x, y=None):
        '''
        初始化函数。
        :param x: 特征数据。
        :param y: 目标数据(标签),可以为None,表示不使用标签。
        '''
        if y is None:
            self.y = y  # 如果没有提供标签,就保持为None
        else:
            self.y = torch.FloatTensor(y)  # 将标签转换为浮点张量
        self.x = torch.FloatTensor(x)  # 将特征数据转换为浮点张量

    def __getitem__(self, idx):
        '''
        获取单个样本。
        :param idx: 样本的索引。
        :return: 如果没有标签,返回特征数据;否则返回特征和标签的元组。
        '''
        if self.y is None:
            return self.x[idx]  # 如果没有标签,只返回特征数据
        else:
            return self.x[idx], self.y[idx]  # 返回特征和标签的元组

    def __len__(self):
        '''
        返回数据集中样本的数量。
        :return: 样本数量。
        '''
        return len(self.x)  # 返回特征数据的长度

5. Neural Network Model

尝试通过修改下面的类来尝试不同的模型架构
My_Model类是一个继承自nn.Module的PyTorch神经网络模型。它定义了一个简单的三层前馈神经网络,其中包括两个隐藏层和一个输出层。每一层后面都跟着一个ReLU激活函数,除了最后的输出层。


class My_Model(nn.Module):
    '''
    一个简单的前馈神经网络模型。
    :param input_dim: 输入特征的维度。
    '''
    def __init__(self, input_dim):
        super(My_Model, self).__init__()
        # TODO: 修改模型的结构,注意维度匹配。
        self.layers = nn.Sequential(
            nn.Linear(input_dim, 16),  # 第一层线性变换,输入维度到16
            nn.ReLU(),  # 激活函数,使用ReLU
            nn.Linear(16, 8),  # 第二层线性变换,16到8
            nn.ReLU(),  # 再次使用ReLU激活函数
            nn.Linear(8, 1)  # 最后一层线性变换,8到1
        )

    def forward(self, x):
        '''
        前向传播函数。
        :param x: 输入数据。
        :return: 模型的输出。
        '''
        x = self.layers(x)  # 通过所有层
        x = x.squeeze(1)  # 去掉单维度,将(B, 1)变为(B),B是批次大小
        return x  # 返回模型输出

6. Feature Selection(重点)

通过修改下面的函数来选择你认为有用的特征进行回归分析

# Feature Selection
def select_feat(train_data, valid_data, test_data, select_all=True):
   '''
   选择用于回归分析的有用特征。
   :param train_data: 训练数据集。
   :param valid_data: 验证数据集。
   :param test_data: 测试数据集。
   :param select_all: 是否选择所有特征,默认为True。
   :return: 选择特征后的训练集、验证集、测试集以及对应的目标变量。
   '''
   y_train, y_valid = train_data[:,-1], valid_data[:,-1]
   raw_x_train, raw_x_valid, raw_x_test = train_data[:,:-1], valid_data[:,:-1], test_data

   if select_all:
       # 如果选择所有特征,则feat_idx为所有特征的索引
       feat_idx = list(range(raw_x_train.shape[1]))
   else:
       # 否则,可以手动选择特定的特征列,例如:
       # feat_idx = [0, 1, 2]  # 假设我们只选择第1、2、3个特征
       # TODO: Select suitable feature columns.
       feat_idx = [0, 1, 2, 3, 4]  # 示例:选择前5个特征列

   # 返回选择特征后的训练集、验证集、测试集以及对应的目标变量
   return raw_x_train[:, feat_idx], raw_x_valid[:, feat_idx], raw_x_test[:, feat_idx], y_train, y_valid

7. Training Loop

trainer函数 训练和验证一个神经网络模型

def trainer(train_loader, valid_loader, model, config, device):
    '''
    训练神经网络模型的函数
    :param train_loader: 训练数据的加载器
    :param valid_loader: 验证数据的加载器
    :param model: 要训练的神经网络模型
    :param config: 包含训练配置的字典 例如学习率 训练周期数等
    :param device: 训练模型的设备 例如CPU或GPU
    '''
    criterion = nn.MSELoss(reduction='mean')  # 定义损失函数 这里使用的是均方误差损失

    # 定义优化算法 这里使用的是随机梯度下降 SGD
    optimizer = torch.optim.SGD(model.parameters(), lr=config['learning_rate'], momentum=0.9)

    writer = SummaryWriter()  # 创建TensorBoard的写入器 用于记录训练过程

    if not os.path.isdir('./models'):
        os.mkdir('./models')  # 如果不存在模型保存目录 则创建

    n_epochs, best_loss, step, early_stop_count = config['n_epochs'], math.inf, 0, 0  # 初始化训练参数

    for epoch in range(n_epochs):  # 进行多个训练周期
        model.train()  # 将模型设置为训练模式
        loss_record = []  # 初始化损失记录列表

        train_pbar = tqdm(train_loader, position=0, leave=True)  # 创建训练进度条

        for x, y in train_pbar:  # 遍历训练数据
            optimizer.zero_grad()  # 清零梯度
            x, y = x.to(device), y.to(device)  # 将数据移动到指定设备
            pred = model(x)  # 进行前向传播
            loss = criterion(pred, y)  # 计算损失
            loss.backward()  # 反向传播 计算梯度
            optimizer.step()  # 更新模型参数
            step += 1
            loss_record.append(loss.detach().item())  # 记录损失

            train_pbar.set_description(f'Epoch [{epoch+1}/{n_epochs}]')
            train_pbar.set_postfix({'loss': loss.detach().item()})

        mean_train_loss = sum(loss_record)/len(loss_record)  # 计算平均训练损失
        writer.add_scalar('Loss/train', mean_train_loss, step)  # 记录训练损失

        model.eval()  # 将模型设置为评估模式
        loss_record = []  # 重新初始化损失记录列表
        for x, y in valid_loader:  # 遍历验证数据
            x, y = x.to(device), y.to(device)
            with torch.no_grad():  # 不计算梯度
                pred = model(x)  # 进行前向传播
                loss = criterion(pred, y)  # 计算损失
            loss_record.append(loss.item())

        mean_valid_loss = sum(loss_record)/len(loss_record)  # 计算平均验证损失
        print(f'Epoch [{epoch+1}/{n_epochs}]: Train loss: {mean_train_loss:.4f}, Valid loss: {mean_valid_loss:.4f}')
        writer.add_scalar('Loss/valid', mean_valid_loss, step)  # 记录验证损失

        if mean_valid_loss < best_loss:  # 如果验证损失更低 则保存模型
            best_loss = mean_valid_loss
            torch.save(model.state_dict(), config['save_path'])  # 保存模型参数
            print('Saving model with loss {:.3f}...'.format(best_loss))
            early_stop_count = 0
        else:
            early_stop_count += 1

        if early_stop_count >= config['early_stop']:  # 如果连续多个周期没有改善 则停止训练
            print('\nModel is not improving, so we halt the training session.')
            return

8. Configurations

# 设置设备为GPU如果可用,否则使用CPU
device = 'cuda' if torch.cuda.is_available() else 'cpu'

# 配置字典,包含训练过程中的所有设置
config = {
    'seed': 5201314,      # 设置随机种子,可以是任何数字
    'select_all': True,   # 是否选择所有特征进行训练
    'valid_ratio': 0.2,   # 验证集占训练集的比例
    'n_epochs': 3000,     # 训练的最大周期数
    'batch_size': 256,    # 每个批次的样本数
    'learning_rate': 1e-5, # 学习率
    'early_stop': 400,    # 如果400个周期内模型没有改进,则停止训练
    'save_path': './models/model.ckpt' # 模型保存路径
}

9. Dataloader


# 设置随机种子以确保结果可复现
same_seed(config['seed'])
# 读取训练数据和测试数据
train_data, test_data = pd.read_csv('./covid.train.csv').values, pd.read_csv('./covid.test.csv').values
# 将训练数据分为训练集和验证集
train_data, valid_data = train_valid_split(train_data, config['valid_ratio'], config['seed'])
# 打印数据集大小
print(f"""train_data size: {train_data.shape}
valid_data size: {valid_data.shape}
test_data size: {test_data.shape}""")
# 选择特征
x_train, x_valid, x_test, y_train, y_valid = select_feat(train_data, valid_data, test_data, config['select_all'])
# 打印特征数量
print(f'number of features: {x_train.shape[1]}')
# 创建PyTorch数据集
train_dataset, valid_dataset, test_dataset = COVID19Dataset(x_train, y_train), \
                                           COVID19Dataset(x_valid, y_valid), \
                                           COVID19Dataset(x_test)

# 创建PyTorch数据加载器
train_loader = DataLoader(train_dataset, batch_size=config['batch_size'], shuffle=True, pin_memory=True)
valid_loader = DataLoader(valid_dataset, batch_size=config['batch_size'], shuffle=True, pin_memory=True)
test_loader = DataLoader(test_dataset, batch_size=config['batch_size'], shuffle=False, pin_memory=True)

10. Start training!

model = My_Model(input_dim=x_train.shape[1]).to(device) # put your model and data on the same computation device.
trainer(train_loader, valid_loader, model, config, device)

11. Plot learning curves with tensorboard (optional)

tensorboard 可以让你可视化你的训练进度。

%reload_ext tensorboard
%tensorboard --logdir=./runs/

在这里插入图片描述

12. Testing

模型在测试集上的预测结果将存储在 pred.csv 文件中

# 定义保存预测结果的函数
def save_pred(preds, file):
    '''将预测结果保存到指定的文件'''
    with open(file, 'w') as fp:  # 打开文件进行写入
        writer = csv.writer(fp)  # 创建CSV写入器
        writer.writerow(['id', 'tested_positive'])  # 写入列名
        for i, p in enumerate(preds):  # 遍历预测结果
            writer.writerow([i, p])  # 写入预测的id和是否阳性的结果

# 创建模型实例并加载训练好的参数
model = My_Model(input_dim=x_train.shape[1]).to(device)
model.load_state_dict(torch.load(config['save_path']))

# 使用测试数据集进行预测
preds = predict(test_loader, model, device)

# 将预测结果保存到CSV文件
save_pred(preds, 'pred.csv')

运行和提交

在这里插入图片描述
simple baseline
在这里插入图片描述

改进

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值