Machine Learning HW1: COVlD-19 Cases Prediction
作业概述
目标
- 用 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


1万+

被折叠的 条评论
为什么被折叠?



