比赛结束半个多月了,终于来做总结了,第一次SOLO参与这样的比赛,最终成绩是TOP15%,离铜牌区差的也不远了,可惜自己就差那一点时间,或者说就是因为自己前期犯了很多错以及毫无比赛经验,比赛里面太多高手了,也有很多乐于分享的人,学到了很多,于是做个总结,记录一下,也给需要的朋友分享。
下面分点给出一些个人认为对于深度学习竞赛有用的点(不以重要性排序)
1.GPU资源,没有这个寸步难行,更别提人家大佬的GPU比你的好,比你的多。但是这也不是我们放弃的理由,从初学者的角度来看,GPU资源足够很重要的一点是可以让你有更多的机会试错。在相同的知识水平下,训练一次模型一个小时跟训练一次十分钟对比,一个人做一次实验,另一个人已经做了六次,那你说,是谁获取的有用信息多呢?况且深度学习领域,即使你查了很多paper,知道了很多技巧,但就我个人经验来说,把他们带入你的代码中,并不能有提升,甚至有反作用,我不认为他们的东西就是假的,只是泛化性不太行吧哈哈,至于最终有没有用,还是得实验说话。(导师老是说深度学习里面做的这些没什么理论支撑,我也想有理论指导,可惜对我来说,大多数都是先做实验,获取到理想的结果之后,再来反推罢了)
2.写一个好的pipeline。pipeline就是指你的用来产生你的最后提交模型的项目代码,可以理解为生产模型的流水线(个人理解)。这点挺重要的,自己动手写一个好用的模板,不仅对本次比赛有效,以后的比赛也可以沿用这个模板,可以节约一些重复性工作的时间。本人就是因为对于创建一个竞赛pipeline不熟悉,所以最后写出来的代码存在很多bug,费尽心思调好了以后,cv和lb(即本地验证集得分与公共排行榜得分)gap特别大,于是调整了很久,浪费了大把时间。所以写一个好的训练代码模板吧,提前就准备好,比赛的时候就从容不迫了。我认为有以下几点在写训练代码时需要特别注意:①训练集验证集划分(通常使用sklearn的Kfold,SKfold,以及GKfold)②数据预处理与读取到输入模型之前不能出错③验证得分计算方式是否与比赛evaluate方式相同④模型复现性(注意固定好各种随机种子)。欢迎补充,另外就是要考虑好训练速度,这是竞赛的窍门之一。例如出现tensor在cpu和gpu来回反复搬这种情况,就会大大降低你的训练速度。具体要自己一步步看,是否有可以优化来提升训练速度的步骤。提出以下几点:
①使用albumentations而不是torchvision进行图像数据增强。
#使用范例
import albumentations as A
from albumentations.pytorch import ToTensorV2
data_transforms = {
"train": A.Compose([
A.Resize(224, 224, interpolation=cv2.INTER_NEAREST),
A.HorizontalFlip(p=0.5),
# A.VerticalFlip(p=0.5),
# A.ShiftScaleRotate(shift_limit=0.0625, scale_limit=0.05, rotate_limit=30, p=0.5),
# A.OneOf([
# A.GridDistortion(num_steps=5, distort_limit=0.05, p=1.0),
# # A.OpticalDistortion(distort_limit=0.05, shift_limit=0.05, p=1.0),
# A.ElasticTransform(alpha=1, sigma=50, alpha_affine=50, p=1.0)
# ], p=0.25),
# A.CoarseDropout(max_holes=8, max_height=256 // 20, max_width=256 // 20,
# min_holes=5, fill_value=0, mask_fill_value=0, p=0.5),
ToTensorV2()], p=1.0),
"valid": A.Compose([
A.Resize(224,224, interpolation=cv2.INTER_NEAREST),
ToTensorV2()], p=1.0)
}
②读取大型的DataFrame时,pandas可能速度就有些慢,我们可以使用cudf,利用gpu加速。具体使用就不在这里介绍了,一般情况下瓶颈不在这里。
③使用混合精度训练。新的pytorch已经整合了混合精度训练,调用也非常方便。简单介绍一下原理,就是在前向传播过程中数据类型使用FP16而不是FP32,即浮点数位数只用16位(模型大多数层中),以微不足道的精度损失换取很快的训练速度,强烈推荐使用。
#伪代码,请不要直接带入,只是说明用法
from torch.cuda.amp import autocast as autocast
from torch.cuda.amp import GradScaler
...
#training for one epoch
scaler = GradScaler()#一定要搭配这个gradscaler使用
for step,data in dataloder:
...
with autocast(enabled=True):#前向传播的每一步请用这个with把它装起来
y_pred = model(images)
loss = criterion(y_pred, masks)
#下面三个缺一不可
scaler.scale(loss).backward()#这一句替代通常的loss.backward()
scaler.step(optimizer)
scaler.update()
...
3.做好训练log。例如每次训练都要记录好你关心的变量状态,否则当你想要查看的时候你发现自己根本没记录。不管是print到屏幕还是写入图表或者文件都可以。另外可以像我这样,每次运行代码自动创建一个文件夹记录本次实验的数据与模型。
4.做好断点继续训练模型的准备。保存模型的时候,不要光是torch.save(model),最好是用字典记录好各种继续训练需要的变量,例如epoch,optimizer等等。关于如何保存最好的文章很多,不再一一介绍了。
5.复现性。众所周知,我们小学就学过,计算机里面的随机不是真随机,我们可以通过设定特殊的随机种子控制,就像你玩MC的地图种子一样。而且不止cuda有随机性,numpy,python都有,所以我们要统统设置好。贴出大佬的代码。(不同的随机种子,确实对训练的模型的最后效果有影响,一个好的种子确实能帮你战胜其他对手,但找个好种子谈何容易)
def set_seed(seed=42):
'''Sets the seed of the entire notebook so results are the same every time we run.
This is for REPRODUCIBILITY.'''
np.random.seed(seed)
random.seed(seed)
torch.manual_seed(seed)
torch.cuda.manual_seed(seed)
# When running on the CuDNN backend, two further options must be set
torch.backends.cudnn.deterministic = True
torch.backends.cudnn.benchmark = False
# Set a fixed value for the hash seed
os.environ['PYTHONHASHSEED'] = str(seed)
print('> SEEDING DONE')
6.做好earlystopping。即在模型发生过拟合时即使停止训练。这里不再介绍过拟合的概念。方法很简单,即设置一个patience变量,当验证得分没有出现上升的情况连续发生patience个epoch的时候,break,停止训练。这个方法可以节省很多时间。
7.loss函数的选取。如果想要真正的理解自己在干什么,那不可避免的一件事就是去读懂你使用的loss函数,他到底是如何计算的,目标是什么,或者更进一步,对比他与其他loss函数的优缺点。否则你所做的事只是依葫芦画瓢。以下简单介绍以下本次比赛,被大量使用的Arcface Loss,也是我自己使用的,在本次比赛效果确实优于其他CEloss,三元组损失等等。
上图中,他用简单的八个类别(个体,更多指人脸识别或者行人重识别ReID),二维的矩阵,做图,直观的看,交叉熵损失函数不能将个体之间的特征向量距离拉开,他们边界模糊,且同类之间的特征向量距离不够近,对于我们最后使用特征向量来识别测试集或者开发集个体是不利的。而ArcFace正好解决了这个问题。本篇论文提出的损失函数我认为很有价值,也很经得起考验。
来自于原论文,L1即交叉熵损失函数公式,L2是ArcFace损失函数,先抛开s&m,很直观地,分子中,他把权重矩阵与i个体向量乘积换成了他们的夹角的余弦值,这意味着衡量个体是否相似的指标从欧式距离变成了角度。而s的作用是扩大这个超球面,m即增大了惩罚,保证个体之间的角度足够。(本人不专业,有错欢迎大佬指出)
感兴趣的同学可以查看下面链接深入学习:
ArcFace: Additive Angular Margin Loss for Deep Face Recognition
8.有好的竞赛学习环境。怎么说呢,国内的这种社区环境对于Kaggle这种社区,真的是难以望其项背。对于我来说,如果遇到了自己查资料也难以短时间解决的问题,我大多数情况下会在Kaggle社区发言提问,大多数情况下也会获得来自竞赛头部大佬们的耐心解答。不要害羞提问,只要你的问题有意义。同时有没有靠谱的,志同道合的队友这也很重要,我本人也是自学罢了,然后孤独跑实验。希望今天也能有个靠谱的竞赛队伍,有这方面兴趣的朋友欢迎私信我~也不介意给大佬打杂哈哈
暂时也就想到这些了,Kaggle比赛很多也很有含金量,想成为这方面的佼佼者不是一天两天就行的,也不能指望有个老师能手把手带你做,老师都忙着做项目挣钱或者push学生发论文搞经费呢,如果你和我一样,想要在人工智能领域潜心学习,并且想要通过竞赛获取经验,建立自己的知识体系,我想遇到的困难不会少,大多数情况下都是铩羽而归,毕竟你挑战的人不是资源足就是肯吃苦。我也一样,只是个初学者,每天都在struggle,苦逼研一学生罢了,不过认定了这条路就要好好做,总比每天都浑浑噩噩摸鱼度过强吧哈哈。