hw1例题-李宏毅老师课后作业

本文介绍了使用PyTorch进行深度学习模型训练的过程,包括数据预处理、特征选择、模型构建、训练与验证、损失函数和优化器的使用,以及模型保存和预测。作者通过Kaggle上的COVID19预测任务,展示了如何建立和优化一个简单的神经网络模型。
摘要由CSDN通过智能技术生成

写在前面

跟着李宏毅老师从头开始学习深度学习,因此这里用CSDN记录深度学习的课后例题,需要注意的是以下代码来源于网上,应该是李老师的代码,版权不属于我,写这篇博客主要为了巩固自己关于机器(深度)学习的知识。

问题描述

hw1是Kaggle原题(https://www.kaggle.com/competitions/ml2021spring-hw1/overview),简单来说是预测Covid19的阳性人数。提供的文件有covid.train.csv,covid.test.csv 和sampleSubmission.csv。显然任务是根据训练集的数据训练一个模型,并实现测试集的预测。

代码部分

导入库

import pandas as pd
from torch import nn
import numpy as np
from torch.utils.data import Dataset, DataLoader, random_split
from torch.utils.tensorboard import SummaryWriter
import math
from tqdm import tqdm #tqdm库用于生成训练时的进度条展示
import os
import csv

这里的库只有tqdm库不太了解,注释说是用于进度条展示,所以应该是类似于用户友好的图形处理界面。

建立数据集函数

class COVID19Dataset(Dataset):
    def __init__(self,x,y=None):
        if y is None:
            self.y=y
        else:
            self.y=torch.FloatTensor(y)
        self.x=torch.FloatTensor(x)
    
    def __getitem__(self,idx):
        if self.y is None:
            return self.x[idx]
        else:
            return self.x[idx],self.y[idx]
    
    def __len__(self):
        return len(self.x)

上述函数建立了covid19dataset的类,继承于torch库里的dataset类并覆写了其中的内嵌函数__getitem,添加了函数__len__。(在此之前我一般做法都是直接读取csv文件获取数据集,这里学习到了)

特征提取

def select_feature(train_data,valid_data,test_data,select_all=True):
    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:
        feature_idx=list(range(raw_x_train.shape[1]))
    else:
        feature_idx=[0,1,2,3,4]
    #print(raw_x_train)
    return raw_x_train[:,feature_idx],raw_x_valid[:,feature_idx],raw_x_test[:,feature_idx],y_train,y_valid

感觉这里写的简单了很多,对于特征提取应该可以更精细一点。虽然这个代码块可以实现特征选择,但是选择哪些特征并没有给出,应该对这些特征做一个比较细致的分析例如相关系数分析。

设置随机种子

def same_seed(seed):
    torch.backends.cudnn.deterministic=True
    torch.backends.cudnn.benchmark=False
    np.random.seed(seed)
    torch.manual_seed(seed)
    if torch.cuda.is_avaliable():
        torch.cuda.manual_seed_all(seed)

随机划分训练集和验证集

def train_valid_split(dataset,ratio,seed):
    valid_set_size=int(ratio*len(dataset))
    train_set_size=len(dataset)-valid_set_size
    train_set,valid_set=random_split(dataset,[train_set_size,valid_set_size],generator=torch.Generator().manual_seed(seed))
    return np.array(train_set.dataset.iloc[train_set.indices,:]), np.array(valid_set.dataset.iloc[valid_set.indices,:])

这里我写的与原始代码不同,主要是最后的返回值,按常理来看这里应该是返回训练集和验证集的矩阵形式,但按照原代码的结果返回的却是两个torch.utils.data.dataset.Subset对象。

定义模型

class My_Model(nn.Module):
    def __init__(self,input_dim):
        super(My_Model,self).__init__()
        self.layers=nn.Sequential(
            nn.Linear(input_dim,16),
            nn.ReLU(),
            nn.Linear(16,8),
            nn.ReLU(),
            nn.Linear(8,1)
        )
    
    def forward(self,x):
        x=self.layers(x)
        x=x.squeeze(1)
        return x 

显然这部分代码是定义了一个神经网络,有2个隐藏层,神经元个数分别为16和8,ReLU为激活函数。

定义训练函数

def trainer(train_loader,valid_loader,model,config,device):
    criterion=nn.MSELoss(reduction='mean')
    optimizer=torch.optim.SGD(model.parameters(),lr=config['learning_rate'],momentum=0.9)
    writer=SummaryWriter()
    
    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) #tensoboard画出loss曲线
        
        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(f'Saving model with loss {best_loss:.3f}...')
            early_stop_count=0
        else:
            early_stop_count+=1
        if early_stop_count>=config['early_stop_count']:
            print('\nModel is mot improving, so we halt the training session.')
            return

该部分十分重要,可以说是核心代码了,所以有必要进行详细的说明。这部分代码即是为了完成模型的训练。函数的参数有训练集、验证集、自定义配置和设备(cpu or gpu)。首先定义了损失函数和优化器,算是常见的操作了,SummaryWriter是我第一次见到,与tensorboard相关,后续会仔细再看看。之后定义了存储位置和epoch,best_loss以及early_stop_count。early_stop_count在我用XGBoost时经常遇到,主要是为了停止损失基本不下降模型的训练。下面正式进入epoch,在每一个epoch中:首先model.train(),表明模型开启训练模式,打开batch normalization和drop out,与之对应的是model.eval(),即将对应的设置关闭,在验证集上使用。train_pbar用到了tqdm类,可视化了遍历训练集的进度,position设置了打印进度条的位置,leave是表示执行完成后是否保留进度条。随后开始遍历训练集,将输入与输出分别转移到相应的device上,之后定义损失函数,同时将得到的损失记录下来,遍历完之后,计算平均损失。之后进入模型验证,这里就要关闭模型的训练模式,因为模型验证是为了观察模型在训练之后在验证集的表现,所以这里torch.no_grad()表明无需对模型计算梯度,只需要计算损失即可。最后比较验证集上的平均损失与最低损失,如果小于则更新最低损失为当前平均损失并将early_stop_count设为0,否则early_stop_count+=1。

开始:

框架已经搭好,后面就是正式开始训练模型,这部分内容比较简单易懂,就不多赘述。

读取并划分数据
device = 'cuda' if torch.cuda.is_available() else 'cpu'
config={'seed':0,'select_all':True,'valid_ratio':0.2,'n_epochs':3000,'batch_size':256,'learning_rate':1e-5,'early_stop_count':400,
       'save_path': './models/model.ckpt' }

train_data=pd.read_csv(r'D:\Work Studio\Python 3.8.6\My secret base\hw\hw1\ml2021spring-hw1\covid.train.csv').iloc[:,1:]
test_data=np.array(pd.read_csv(r'D:\Work Studio\Python 3.8.6\My secret base\hw\hw1\ml2021spring-hw1\covid.test.csv').iloc[:,1:])

train_data, valid_data = train_valid_split(train_data, config['valid_ratio'], config['seed'])
x_train, x_valid, x_test, y_train, y_valid = select_feature(train_data, valid_data, test_data, config['select_all'])
train_dataset, valid_dataset, test_dataset = COVID19Dataset(x_train, y_train), \
                                            COVID19Dataset(x_valid, y_valid), \
                                            COVID19Dataset(x_test)
train_loader = DataLoader(train_dataset, batch_size=config['batch_size'], shuffle=True, pin_memory=True)
#pin_memory默认false,打开后更快
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)
训练模型
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)
保存模型并预测数据
def save_pred(preds, file):
    ''' Save predictions to specified file '''
    with open(file, 'w') as fp:
        writer = csv.writer(fp)
        writer.writerow(['id', 'tested_positive'])
        for i, p in enumerate(preds):
            writer.writerow([i, p])

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) 
save_pred(preds, 'pred.csv')

总结

以上即是hw1例题的基本内容,经过这一次的学习和探讨,我对机器学习框架的建立有了更深刻的了解。后续如果有新的感悟和思考,会继续更新补充,也欢迎大家批评指正。

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值