[李宏毅深度学习作业] 作业1:ML2021Spring-hw1 COVID-19 Cases Prediction【以时间线为记录】

写在开头:
此篇博客主要是记录李宏毅老师2021年春季的深度学习作业1的笔记过程,以时间的角度进行记录。

COVID-19 Cases Prediction

  • 8月27号[初步操作]
    • 初步查看数据集+用基础的RF进行测试结果
  • 8月28号[运行样例代码]
    • 李宏毅助教代码运行+理解
        • 1、Setup Hyper-parameters [设置超参数]
        • 2、Load data [下载数据]
        • 3、Load model [下载模型]
        • 4、Start Training[开始训练]
        • 5、Testing[测试]
        • 6、Submit[提交]
        • 7、后续改进步骤
  • 8月29+30号[代码改进]

8月27号[初步操作]

初步查看数据集+用基础的RF进行测试结果

作业网址:https://www.kaggle.com/competitions/ml2021spring-hw1
用rf进行初步测试数据集:结果不太行,大约在1350/2032
[李宏毅深度学习作业] 作业1:ML2021Spring-hw1 COVID-19 Cases Prediction【以时间线为记录】_第1张图片
提交主要的代码:

from sklearn.ensemble import RandomForestRegressor
from sklearn.metrics import mean_squared_error
clf = RandomForestRegressor()
clf.fit(train,target)
test_pred = clf.predict(test)
test_pred
#Kaggle需要提交最终的csv文件,所以输出一个csv文件:
sample['tested_positive']=test_pred
sample.to_csv('submission.csv', index=False)

8月28号[运行样例代码]

李宏毅助教代码运行+理解

下面的代码是李宏毅老师助教的代码,目前只是跑通了并且弄懂了一部分,由于用的是俩层的全连接层,而且梯度下降用的也是SGD,最后的效果还不如没有调参的rf的效果。

1、Github:代码链接

无脑不用看,直接跑代码:
提交的可运行代码:https://www.kaggle.com/code/bessielee1/ml2021spring-homework1-gpu

下面进行代码解释:

首先,助教的代码风格是将很多的东西放在函数里面,这样对代码进行修改就会极其的方便,只需要改模型部分就行。
【建议】
建议是先看代码的中间部分,而不是去看函数体,函数什么时候调用,就什么时候开始仔细看函数部分。

1、Setup Hyper-parameters [设置超参数]

# 获取当前设备的cpu或gpu
device = get_device()                 # get the current available device ('cpu' or 'cuda')
# 创建多层目录
os.makedirs('models', exist_ok=True)  # The trained model will be saved to ./models/
target_only = False                   # TODO: Using 40 states & 2 tested_positive features

# TODO: How to tune these hyper-parameters to improve your model's performance?
config = {
    'n_epochs': 3000,                # maximum number of epochs
    'batch_size': 270,               # mini-batch size for dataloader
    'optimizer': 'SGD',              # optimization algorithm (optimizer in torch.optim) - 梯度下降优化算法
    'optim_hparas': {                # hyper-parameters for the optimizer (depends on which optimizer you are using)
        'lr': 0.001,                 # learning rate of SGD
        'momentum': 0.9              # momentum for SGD 加动量
    },
    'early_stop': 200,               # early stopping epochs (the number epochs since your model's last improvement)早停法
    'save_path': './.pth'  # your model will be saved here
}

1.1、涉及到get_device()函数

def get_device():
    ''' Get device (if GPU is available, use GPU) '''
    return 'cuda' if torch.cuda.is_available() else 'cpu'

没有用GPU的测试结果:
[李宏毅深度学习作业] 作业1:ML2021Spring-hw1 COVID-19 Cases Prediction【以时间线为记录】_第2张图片

1.2、创建一个多层的目录(如果不想创建可以不写)

运行此行代码,会在右边的output中输出一个目录
[李宏毅深度学习作业] 作业1:ML2021Spring-hw1 COVID-19 Cases Prediction【以时间线为记录】_第3张图片

1.3、设置超参数在config里面

'n_epochs': 3000,              
    'batch_size': 270,      
    'optimizer': 'SGD',             
    'optim_hparas': {               
        'lr': 0.001,                
        'momentum': 0.9              
    },
    'early_stop': 200,              
    'save_path': './.pth' 
  • n_epochs:最大的epoch数

  • batch_size:

    • batch_size = N/batch
    • 一个epoch=270batch,我们本题的N=2700,则10个数据为一个batch
      [李宏毅深度学习作业] 作业1:ML2021Spring-hw1 COVID-19 Cases Prediction【以时间线为记录】_第4张图片
  • optimizer:SGD是一种梯度下降优化算法

  • optim_hparas

    • lr:0.001,这个是学习率
    • momentum:这个是加的动量,就是在一定情况下减缓梯度下降的幅度,这个之前上课讲过
  • early_stop:早停法(当epochs的值加到200的时候,就停止了,这个epochs和刚刚的n_epochs不是一个东西)

  • save_path:保存最后存储文件的地址

2、Load data [下载数据]

tr_set = prep_dataloader(tr_path, 'train', config['batch_size'], target_only=target_only)
dv_set = prep_dataloader(tr_path, 'dev', config['batch_size'], target_only=target_only)
tt_set = prep_dataloader(tt_path, 'test', config['batch_size'], target_only=target_only)

用的都是prep_dataloader()这个方法,那接下来对这个方法进行详细的解释:

def prep_dataloader(path, mode, batch_size, n_jobs=0, target_only=False):
    ''' Generates a dataset, then is put into a dataloader. '''
    dataset = COVID19Dataset(path, mode=mode, target_only=target_only)  # Construct dataset
    # 可以通过继承和重写这个抽象类实现自己的数据类,只需要定义__len__和__getitem__这个两个函数
    dataloader = DataLoader(
        dataset, batch_size,
        shuffle=(mode == 'train'), drop_last=False,
        num_workers=n_jobs, pin_memory=True)                            # Construct dataloader
    return dataloader

在函数prep_dataloader()里面,又调用了COVID19Dataset()这个函数,所以说先看COVID19Dataset()这个函数

class COVID19Dataset(Dataset):
    ''' Dataset for loading and preprocessing the COVID19 dataset '''
    # 定义构造函数:后续可以用COVID19Dataset()调用,需要输入path,mode,target_only三个参数
    def __init__(self,
                 path,
                 mode='train',
                 target_only=False):
        self.mode = mode
        # 读取path(有可能是train或者test),将数据变成二维列表,此时原来一行的信息被放入一个二维列表中的其中一个里面。
        # Read data into numpy arrays
        with open(path, 'r') as fp:
            data = list(csv.reader(fp))
            data = np.array(data[1:])[:, 1:].astype(float)
        # 如果target_only为False,则feats为[0,1,2,3,....,92]
        if not target_only:
            feats = list(range(93))
        # 否则:不做处理
        else:
            # TODO: Using 40 states & 2 tested_positive features (indices = 57 & 75)
            pass

        # 如果mode是test(测试集)
        if mode == 'test':
            # Testing data
            # data: 893 x 93 (40 states + day 1 (18) + day 2 (18) + day 3 (17))
            data = data[:, feats] # 其实没有变化,因为feats本身的值就是test的,就是93
            self.data = torch.FloatTensor(data) # 把numpy转换为tensor的类型
        else:
            # Training data (train/dev sets)
            # data: 2700 x 94 (40 states + day 1 (18) + day 2 (18) + day 3 (18))
            target = data[:, -1] # target位于最后一列
            data = data[:, feats] # 除去最后一列的数据
            
            # Splitting training data into train & dev sets
            if mode == 'train':
                # 在2700列中除去可以被十整除的部分
                indices = [i for i in range(len(data)) if i % 10 != 0]
            elif mode == 'dev':
                indices = [i for i in range(len(data)) if i % 10 == 0]
            
            # Convert data into PyTorch tensors
            self.data = torch.FloatTensor(data[indices]) # 不能被10整除的数据转换为tensor作为测试集
            self.target = torch.FloatTensor(target[indices]) # 可以被10整除的数据转换为tensor作为交叉验证集

        # 归一化
        # Normalize features (you may remove this part to see what will happen)
        self.data[:, 40:] = \
            (self.data[:, 40:] - self.data[:, 40:].mean(dim=0, keepdim=True)) \
            / self.data[:, 40:].std(dim=0, keepdim=True)

        self.dim = self.data.shape[1]

        print('Finished reading the {} set of COVID19 Dataset ({} samples found, each dim = {})'
              .format(mode, len(self.data), self.dim))

    def __getitem__(self, index):
        # Returns one sample at a time
        if self.mode in ['train', 'dev']:
            # For training
            return self.data[index], self.target[index]
        else:
            # For testing (no target)
            return self.data[index]

    def __len__(self):
        # Returns the size of the dataset
        return len(self.data)

这是定义的一个COVID19Dataset类,但是我们调用的是她的构造方法,也就是def __init__这个部分。

对于这个部分,代码中已经进行详细的注释,再此进行简单的阐述即可。

  • 1、先用with open读取数据,并将其保存为array的二维列表(一行数据就是一个二维列表中的一个列表)
  • 2、对feats进行设定:因为我们开始选中的是target_only为false,则if语句可以运行,即:feats = list(range(93))
  • 3、feats = list(range(93))这句话的意思是:feats=[0,1,2,3,4,…,92],但是为什么要如此设计呢?是因为test.csv的数据是893×93,故而以93作为feats的值。
  • 4、当输入的mode=test的时候,也就是我们进行处理的数据是测试集,此时 data = data[:, feats]没有什么操作变化(目的是为了和后面的代码形成一致)
  • 5、随后对其进行数据类型转变,从array类型变为tensor类型
  • 6、对于train和dev的选取(训练集和交叉验证集),训练集是选取2700行数据中不可以被10整除的数据,而交叉验证集选取的是2700行数据中可以被10整除的数据(即0,10,20,30…2700)
  • 7、随后进行数据转化为tensor
  • 8、再对数据进行归一化处理,最后设置数据维度

对于prep_dataloader()函数的后一部分: DataLoader()
这是一个系统类,可以通过继承和重写这个抽象类实现自己的数据类,只需要定义__len__和__getitem__这个两个函数。

最后下载数据这一块的输出结果为:
[李宏毅深度学习作业] 作业1:ML2021Spring-hw1 COVID-19 Cases Prediction【以时间线为记录】_第5张图片

3、Load model [下载模型]

model = NeuralNet(tr_set.dataset.dim).to(device) 

对于这个代码,要涉及NeuralNet()这个函数,同时.to(device)的意思是:放入GPU进行运算

  • NeuralNet()进行详细分析:
class NeuralNet(nn.Module):
    ''' A simple fully-connected deep neural network '''
    # 全连接层
    def __init__(self, input_dim):
        super(NeuralNet, self).__init__()

        # Define your neural network here
        # TODO: How to modify this model to achieve better performance?
        self.net = nn.Sequential(
            nn.Linear(input_dim, 64),
            nn.ReLU(),
            nn.Linear(64, 1)
        )

        # Mean squared error loss
        # 新版本中的MSELoss只有一个参数,意思就是要不要压缩
        # 'mean': the sum of the output will be divided by the number of elements in the output.
        # 如果不设置reduction参数,默认是'mean'
        # https://blog.csdn.net/zfhsfdhdfajhsr/article/details/115637954
        self.criterion = nn.MSELoss(reduction='mean')

    def forward(self, x):
        ''' Given input of size (batch_size x input_dim), compute output of the network '''
        # squeeze(1) 是压缩,dim=1
        return self.net(x).squeeze(1)

    def cal_loss(self, pred, target):
        ''' Calculate loss '''
        # TODO: you may implement L2 regularization here
        # criterion是损失函数
        return self.criterion(pred, target)

这里是模型选取的代码部分

  • 神经网络模型:采用的是一层linear,加一层Relu()激活函数,再加一层linear,可以理解为俩层的全连接层
  • loss:用的是MSE,设置的是reduction='mean',也就是’mean’: 输出的总和将除以输出中的元素数
  • 函数forward:前馈,将x进行dim=1压缩
  • 函数cal_loss:计算预测值与目标值target之间的loss值

4、Start Training[开始训练]

model_loss, model_loss_record = train(tr_set, dv_set, model, config, device)

训练用的train()函数,下方贴出:

def train(tr_set, dv_set, model, config, device):
    ''' DNN training '''
    # 传入:训练集,交叉验证集,模型,config

    n_epochs = config['n_epochs']  # Maximum number of epochs - 3000

    # Setup optimizer
    # getattr(a,'qq') :在对象a中是否存在qq,有则输出这个数,没有则返回异常
    optimizer = getattr(torch.optim, config['optimizer'])(
        model.parameters(), **config['optim_hparas'])

    min_mse = 1000.
    loss_record = {'train': [], 'dev': []}      # for recording training loss
    early_stop_cnt = 0
    epoch = 0
    # 训练一个epoches
    while epoch < n_epochs:
        model.train()                           # set model to training mode
        for x, y in tr_set:                     # iterate through the dataloader
            optimizer.zero_grad()               # set gradient to zero
            x, y = x.to(device), y.to(device)   # move data to device (cpu/cuda)
            pred = model(x)                     # forward pass (compute output)
            mse_loss = model.cal_loss(pred, y)  # compute loss
            mse_loss.backward()                 # compute gradient (backpropagation)
            optimizer.step()                    # update model with optimizer
            loss_record['train'].append(mse_loss.detach().cpu().item())

        # After each epoch, test your model on the validation (development) set.
        dev_mse = dev(dv_set, model, device)
        if dev_mse < min_mse:
            # Save model if your model improved
            min_mse = dev_mse
            print('Saving model (epoch = {:4d}, loss = {:.4f})'
                .format(epoch + 1, min_mse))
            torch.save(model.state_dict(), config['save_path'])  # Save model to specified path
            early_stop_cnt = 0
        else:
            early_stop_cnt += 1

        epoch += 1
        loss_record['dev'].append(dev_mse)
        if early_stop_cnt > config['early_stop']:
            # Stop training if your model stops improving for "config['early_stop']" epochs.
            break

    print('Finished training after {} epochs'.format(epoch))
    return min_mse, loss_record
  • 1、确保设置的梯度下降算法(SGD)在torch的optim库中,不在则报异常错误
  • 2、设置一些参数
  • 3、依据epoch的值进行循环
    • 3.1、训练模型model(双层的全连接层)
    • 3.2、对每一个epoch进行batch训练(因为一个batchsize=一个epoch,一个epoch就是要遍历全部的batch)
    • 3.3、设置梯度初试参数为0
    • 3.4、将x和y放入GPU运算,没有就是cpu
    • 3.5、用x的值放入其中进行预测
    • 3.6、将结果与原始的y进行对比(调用的是cal_loss方法)
    • 3.7、完成一个反向传播机制
    • 3.8、进行更新optimizer
    • 3.9、将结果保存入loss_record的train里面
  • 4、预测交叉验证集的x,与y对比进行上述相同操作,将结果保存入loss_record的dev里面
  • 5、对epoch进行+1

5、Testing[测试]

preds = test(tt_set, model, device)

调用的是test()方法:

def test(tt_set, model, device):
    model.eval()                                # set model to evalutation mode
    preds = []
    for x in tt_set:                            # iterate through the dataloader
        x = x.to(device)                        # move data to device (cpu/cuda)
        with torch.no_grad():                   # disable gradient calculation
            pred = model(x)                     # forward pass (compute output)
            preds.append(pred.detach().cpu())   # collect prediction
    preds = torch.cat(preds, dim=0).numpy()     # concatenate all predictions and convert to a numpy array
    return preds

将测试集的数据进行一一预测的值,转换为numpy格式进行输出

6、Submit[提交]

import pandas as pd
sample = pd.read_csv('../input/ml2021spring-hw1/sampleSubmission.csv')
sample['tested_positive'] = preds
sample.to_csv('submission.csv', index=False)

最后提交结果

[李宏毅深度学习作业] 作业1:ML2021Spring-hw1 COVID-19 Cases Prediction【以时间线为记录】_第6张图片

7、后续改进步骤

这个结果很差,还不如用rf进行预测的结果好,原因有好几个:
1、有可能是因为数据量太少,只有2700行
2、模型选取的是双层的全连接层,维度是64,所以说模型可能选取有问题
3、可能这个问题用机器学习就是比深度学习要好,原因可能还是因为1(数据量不够的问题)

后续会进行俩种测试,一种是继续把深度学习的这个模型进行狂改,另一种就是先对数据进行处理以下,再进行魔改模型,第三种就是试试用其他机器学习模型看看效果怎么样(这个最后采用,因为这个作业的目的就是为了学习深度学习而不是学习机器学习算法)

8月29+30号[代码改进]

29号第一天上学,很多事情都没搞完,晚上又做了2个多小时的核酸是,故而没有跑代码。

30号下午,再次进行代码改进,叠层已经不起效果了,但是从0开始改框架,我还没掌握,故而此篇博客暂时记录到此,先去kaggle看一些pytorch实例的代码,学一下怎么用,不然直接大改代码容易把代码改死机,冲冲冲冲冲冲继续干!!

你可能感兴趣的:(学习ML+DL,天池/kaggle比赛,深度学习,python,人工智能,pytorch)