机器学习2021-HW1

HomeWork1: COVID-19 Cases Prediction (Regression)

kaggle:https://www.kaggle.com/competitions/ml2021spring-hw1/submissions#

初始code:https://colab.research.google.com/github/ga642381/ML2021-Spring/blob/main/HW01/HW01.ipynb

参考:

https://github.com/1am9trash/Hung_Yi_Lee_ML_2021/blob/main/hw/hw1/hw1_code.ipynb

https://github.com/pai4451/ML2021

问题与思路(9.27):

  • 首先是python语法不熟悉,看得比较慢
    • 一点点看下来,一点点gpt其实还好,慢慢地就学进去了
    • 整理一下,多多复习
  • 关键函数看不懂,涉及到复杂的python语法以及原理
    • 一点点梳理,先理解原理,再去尝试看懂代码,可以手打一下或者抄一下
    • 多看几遍,慢慢来
  • 整个的流程还是不清晰的,有点困于细节
    • 大的流程就是1.写带未知参的function。2.定义loss。3.优化
    • 先把原始作业版本的流程搞清楚,再去看优化
    • 先搞懂每段代码是做什么,把所有的代码块串接起来,关注重点内容,再去看细节

过程梳理:

  • 目标:
    • 用DNN解决一个Regression problem
    • 了解DNN training tips
    • 熟悉PyTorch
  • 准备:
    • Download Data, Import some packages and utilities
  • Preprocess:(包含train,dev,test)
    • DataSet
      • read csv file
      • extract features
      • split train into train and dev sets
      • normalize features
    • DataLoader
      • loads data from DataSet into batches
  • Deep Neural Network:
    • nn.Module designed for regression
    • consists of 2 fully-connected layer with ReLU activation
    • cal_loss for calculating loss
  • Train/Dev/Test:
    • Training
      • 设置参数
      • 训练循环(epoch)
        • 训练模式
        • 数据迭代(update)
          • 梯度清零
          • 数据传输
          • 前向传播
          • 计算损失(cal_loss)
          • 反向传播
          • 更新参数
          • 记录损失
        • 验证模型(dev)
        • 更新最小损失
        • 早停技术
        • 增加epoch
        • 记录验证损失
        • 早停检查
      • 结束打印,返回最小损失和损失记录
    • Validation
      • 评估模式
      • 初始化损失为0
      • 数据迭代
        • 数据传输
        • 禁用梯度计算
        • 前向传播
        • 计算损失(cal_loss)
        • 累积损失
      • 计算返回平均损失
    • Testing
      • 评估模式
      • 初始化预测列表
      • 数据迭代
        • 数据传输
        • 禁用梯度计算
        • 前向传播
        • 收集预测
      • 合并结果为张量
      • 返回预测结果
  • Setup Hyper-parameters:
    • 设置**config**里的超参数
  • 实操调用
    • Load data and model
    • Start training(train()): 传入:tr_set,dev_set,model,config,device
    • Testing(test()):传入:tt_set,model,device, 保存结果到一个csv文件中

重点代码理解:

1.extract features:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 查看数据量
print("Train:{:4d}".format(len(train_data)))
print(" Test:{:4d}".format(len(test_data)))
# 查看表信息
print(train_data.info())
# 观察数值型数据分布情况
train_data.iloc[:,41:].describe()
test_data.iloc[:,41:].describe()
# 计算features与target之间的相关性并extract对应的features以及它们的index
corr = train_data.iloc[:,41:].corr().iloc[-1]
features = corr[abs(corr)>0.4]
features_col = features.index.to_list()[:-1]
features_id = np.array([train_data.columns.to_list().index(i) for i in features_col]) - 1
print(features)
print("\\nfeatures' id:", ', '.join(map(str, features_id)))

2.normalize features:减去均值,除以标准差,得到均值为0,标准差为1的数据集

1
2
3
self.data[:, 40:] = \\
(self.data[:, 40:] - self.data[:, 40:].mean(dim=0, keepdim=True)) \\
/ self.data[:, 40:].std(dim=0, keepdim=True)

3.批量数据加载器:调用之前写好的COVID19Dataset,对训练数据进行shuffle并返回批量数据

1
2
3
4
5
6
7
8
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
dataloader = DataLoader(
dataset, batch_size,
shuffle=(mode == 'train'), drop_last=False,
num_workers=n_jobs, pin_memory=True) # Construct dataloader
return dataloader

4.DNN:NeuralNet,设置神经网络层数和激活函数,以及在cal_loss里添加正则化

1
2
3
4
5
self.net = nn.Sequential(
nn.Linear(input_dim, 64),
nn.ReLU(),
nn.Linear(64, 1)
)
  • nn.Sequential是一个容器,按顺序组合多个神经网络层
  • nn.Linear(input_dim, 64)是一个全连接层,针对输入,输出64个特征
  • nn.ReLU(),使用激活函数ReLU,增加模型的非线性,增加模型的表达能力
  • nn.Linear(64, 1),针对64维的输入,输出最终的结果,一个标量

5.cal_loss以及正则化

1
2
3
4
5
6
7
8
9
10
11
def cal_loss(self, y, y_hat):
""" Calculate loss """
loss = torch.sqrt(self.criterion(y, y_hat))
# l2 = 0
# for i in self.parameters():
# l2 += torch.sum(torch.pow(i, 2))
# return loss + 0.01 * l2, loss
l1 = 0
for i in self.parameters():
l1 += torch.sum(abs(i))
return loss + 0.0001 * l1, loss
  • 这里采取了RMSE作为loss function
  • L1正则化就是计算参数绝对值的和,L2正则化就是计算参数平方的和
  • 返回loss+正则化结果*超参数

6.设置超参数:

1
2
3
4
5
6
7
8
9
10
11
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': 'models/model.pth' # your model will be saved here
}
  • ‘optimizer’: ‘SGD’是随机梯度下降优化算法
  • momentum是考虑之前所有g的加权和,在梯度方向上积累历史信息,加快在平坦区域的收敛速度,减缓在梯度方向变化剧烈的收敛速度2
  • early_stop:表示当dev_mse连续很久没有更新,就提前停止退出

分析和改善:

记录:

idx version valid public private desc
1 什么都没增加,最原始的代码 0.7590 1.35281 1.43395 运行一遍,跑通所有代码
2 最完整的版本 0.9296 0.87971 0.89165 运行了一下完整的参考代码
3 增加强相关度的features筛选,增加了正则化,损失函数用RMSE计算 0.9138 1.02623 1.04953 在1的基础上只修改了features选址部分,增加了loss计算时的l1正则化,损失函数从MSE到RMSE
4 增加强相关度的features筛选,增加了正则化,损失函数用MSE计算 0.8360 1.00645 1.04458 在3的基础上把RMSE改为MSE
5 增加强相关度的features筛选,增加了正则化(参数修改为0.001),损失函数用MSE计算 1.0079 0.96859 1.00816 在4的基础上修改了L1正则化的强度参数,从0.0001到0.001
6 增加了epoch的上限 0.9229 0.95927 1.01704 在5的基础上增大了epoch上限为20000
7 增加了layer层数 0.9538 1.09354 1.17069 在6的基础上增加了一层全连接层。有点过拟合了。
8 ReLU→Sigmoid 0.9914 2.03473 2.05528 在6的基础上修改了激活函数——过拟合很严重
9 重新归一化和划分数据集 0.9354 1.01068 1.06830 在6的基础上进行了归一化和划分数据集的修改

TODO:

  • 歸一化 ✔
    • 原本範例code中是不同的dataset分別使用自己的mean跟std做歸一,這是不合理的,應該使用全部資料的mean跟data做歸一,才不會有太大的偏差
  • Train跟Valid dataset切分✔
    • 改為隨機切分,保證資料分布相同
    • 由於資料集太小,建議多切幾次避免將異常值劃分到valid(k-fold應該是個比較好的做法)
  • 修改了归一化和切分,并没有好的效果
  • DNN architecture (layers? dimension? activation function?)✔
    • 尝试增加了一层layer,发现dev上的loss大幅增加
    • 将ReLU改为Sigmoid发现overfitting很严重
  • Training (mini-batch? optimizer? learning rate?)✔
    • 降低了learning rate,有比较好的效果,也方便观察
  • L1 regularization✔
  • There are some mistakes in the sample code, can you find them?

Hight light(Training tips):

1、在训练集上效果变差,在测试集上效果可能还是会变好

  • 对比版本1和版本3发现了

    一个有意思的现象

    • 版本1在训练集和验证集上效果都比较好,但是在测试集上效果差
    • 而增加了强相关度的features筛选,增加了正则化的版本3却在训练集和验证集上效果都相对比较差,但是在测试集上效果相对更好
  • 原因:

    • 过拟合与泛化能力

      :

      • 版本1: 由于其在训练集和验证集上表现较好,可能存在过拟合。模型过于复杂或参数过多,导致它很好地记住了训练数据,但在测试集上泛化能力较差。
      • 版本3: 增加正则化后,模型的复杂度降低,更不容易过拟合。这可能导致训练和验证集的表现下降,但在测试集上提高了泛化能力,表现更好。
    • 强相关度特征筛选

      :

      • 增加强相关特征可以帮助模型捕捉到数据的主要模式。这种特征选择可能提高了模型在未见数据(测试集)上的表现,尽管它在训练集上的拟合不如基础版本。
    • 正则化的作用

      :

      • 正则化可以限制模型的复杂度,减少对训练数据噪声的敏感性。这样,即使在训练集上的损失相对较高,模型在面对新的数据时(如测试集)可能表现得更好。
    • 损失函数的变化

      :

      • 从均方误差(MSE)改为均方根误差(RMSE)并不会改变模型的拟合过程,但 RMSE 在一定程度上会更加关注较大的误差。虽然这可能导致模型在训练和验证集上的表现不如基础版本,但有助于在测试集上提高表现,尤其是当测试集具有不同的分布时
  • 总结:优化手段都有点降低模型的复杂度,防止过拟合,增强的泛化,所以会有在训练集和验证集上效果一般,但是在测试集上效果很好的现象,这是很正常的。

2、RMSE→MSE,在dev上测试提前结束了

  • 在版本3的基础上想着loss在验证集上偏大可能有也因为loss的计算方法不一样,于是把损失函数从RMSE变为MSE,但是发生了一个有意思的现象

    • 在dev上运行时,loss还很大,训练就停止了,触发了早停机制
  • 原因:

    • 优化算法的敏感性

      :

      • MSE 对较大误差更敏感,导致损失更新不如 RMSE 稳定,从而更容易达到早停的条件。
    • 正则化影响

      :

      • 如果你在 MSE 损失中加入了正则化项,模型可能会更快地达到损失的阈值,从而提前结束训练。
    • 数据集问题

      :

      • 数据集中可能存在噪声或不一致性,导致模型在训练时难以收敛,尤其是在使用 MSE 时。
  • 总结:

    • 设置的早停条件基于验证集损失在一定轮次内未改善,MSE 的波动可能导致模型在训练过程中的某个时刻没有明显改善,进而提前结束训练。
  • 解决:

    • 降低学习率:从0.001到0.0001,可以帮助平滑更新过程,减少损失的波动,最终训练出了不错的数据。

3、修改正则化L1的超参数,模型性能真的有提高

  • 发现版本4在dev上loss比较小,而在public的loss变大了不少。猜测模型有一点点过拟合的倾向,于是增大了L1的超参数,从0.0001到0.001,发现loss在dev上增加了一点,但是在public和test上均有下降
  • 原因:
    • 该超参数为正则化强度
    • 较小的值意味着正则化对损失的影响较小,可能导致过拟合,尤其是在训练数据较小或噪声较多的情况下。
    • 较大的值: 增加该值将增强正则化的效果,使模型更简单、更平滑。这可能改善模型的泛化能力,但也可能导致欠拟合,因为模型可能无法捕捉到数据中的重要特征。
    • 补充:如何调整这个正则化强度
      • 可以通过交叉验证等方法尝试不同的正则化强度,观察对训练、验证和测试集性能的影响,找到最适合你模型的超参数。
      • 使用网格搜索或随机搜索: 帮助系统地寻找最佳的正则化强度。

4、增加layer训练时才发现之前的几次训练因为epoch上限的原因都及时停止了

  • 分析:
    • 把epoch上限从3000提升到了20000,之前的模型效果在public上有变好,在private上略变差一点
    • 增加了layer后,public的结果明显比dev上损失要高,猜测可能存在一些过拟合的风险

总结:

  • 通过这次作业,对regression模型训练的实际操作流程有了更加深刻的认识
  • 通过多次的反复调整模型,习得了一些training tip,如何判断是否是overfitting,进行optimize
    • 强相关性features的选取
    • 正则化防止模型过于复杂
    • 损失函数MSE和RMSE
    • learning rate的调整可以让模型走得更加平滑,减少损失波动(optimization)
    • 所有数据统计归一化和train,dev数据集随机切分(效果不明显)、
    • 修改DNN(layer,dimension,activation function)
  • 待进步:
    • 整个调优的过程还是有点混乱,想到什么做什么,逻辑可以更加清晰,逐步地进行一个优化是一个比较好的过程
    • 应该把train_loss也进行输出,比较train和dev来初步判断是否是overfitting,而不是通过dev和public,private进行比较

机器学习2021-HW1
http://example.com/2024/09/29/机器学习入门/机器学习2021-HW1/
作者
jhxxxxx
发布于
2024年9月29日
许可协议