前言

这篇文章主要是记录我对题述知识点的理解。但是我写博客习惯于讲清来龙去脉,就算是知识点,我也想顺便记录下产生知识点的背景。

科研实习的第一个任务就是在RecBole当中复现论文《Directed Acyclic Graph Factorization Machines for CTR Prediction via Knowledge Distillation》当中提出的KD_DAGFM模型。大抵在1.20日我完成了模型的复现,提交给学长review。其实当时有预训练过程的模型进行超参数搜索这一块是有隐患的(正文详细描述),但是也只是隐患。我参考了相似模型的实现,发现似乎他们也都没有解决这个隐患。所以我想先给学长review,如果会有问题再说。所以我实际上1.20做完了这项工作,但是我一直非常担心自己的复现有很大的问题,所以一直没敢有任务完成松一口气的动作。在2.4学长帮我review code之后,在review的过程中,我自己再次思考了题述的问题,应该是达到了正确的理解,所以解决了这一隐患。

这篇博客主要就是阐述这个理解。

正文

预训练:

我们都知道,深度学习的许多模型都有预训练这一过程以达到更好的实验效果。在预训练之后,我们通常会在预训练的基础上训练模型,使模型表现得更好。因此有预训练的模型在训练阶段,通常需要设置参数pretrain_path这一参数,代表存储预训练得到的模型参数的文件路径。

KD_DAGFM模型中同样有类似预训练的过程。该模型的训练分为三个阶段:teacher_training、distillation、finetuning。首先teacher_training阶段训练得到教师模型的参数设置;distillation阶段,加载训练好的教师模型参数,通过教师模型进行知识蒸馏,训练学生模型DAGFM;finetuning阶段,加载训练好的学生模型参数,进行微调,达到DAGFM模型最好的效果。

因此,distillation阶段,我们就需要设置warm_up参数为teacher_training阶段存储模型参数的文件路径;在finetuning阶段,我们就要设置warm_up参数为distillation阶段存储模型参数的文件路径。

这看起来似乎没什么问题。

超参数搜索:

超参数搜索同样是AI领域非常常见的概念。batch_size、embedding_size、learning_rate等模型超参数会非常大程度的影响模型的性能。因此,为了充分发挥模型的潜力,我们需要寻找最佳的超参数设置。如果人为的根据模型训练效果逐个修改超参数进行比较选取,比较浪费时间和精力,也就是所谓的炼丹。

超参数搜索,就是我们提前设定好一系列的超参数及其选择范围,程序会自己去尝试这些超参数的匹配并报告效果。

这样看似乎也没什么问题。

有预训练过程模型的超参数搜索:

当这两个概念放到一起,似乎就会引发问题了。我们针对KD_DAGFM模型进行讨论。

首先在teacher_training阶段,进行embedding_size、learning_rate的超参数搜索不会产生问题。比如按照(embedding_size, learning_rate)=(10, 0.001)、(10, 0.005)、(10, 0.0001)、(16, 0.001)…的搭配进行模型训练效果的对比,这样没什么问题。

再看distillation阶段。前文已述,distillation阶段需要设置warm_up为teacher_training所得模型的存储位置,比如我们设置为./saved/xxx.pth,假设该模型的embedding_size=16。

接下来我们就要对distillation阶段进行超参数搜索了,要对embedding_size、learning_rate、α、β等进行搜索。这时候程序就会报错了。因为超参数搜索程序每一次跑模型使用的warm_up参数都是同一个路径,也就是说在distillation阶段进行embedding_size=8、10、16等尝试时,我们导入的模型却始终都是embedding_size=16,那么显然就会冲突了。

当时想的解决方案是:

既然不同的embedding_size要对应不同的warm_up,那么超参数搜索程序就要能根据不同的embedding_size来设置warm_up。这想想都让我望而却步。首先伯乐是一个有80+个模型的项目,我不认为我应该改动它统一的超参数搜索程序。其次,就算能让我自己写,我又要怎么写呢。伯乐存储模型也是统一的程序,根据模型、日期、时间进行存储文件命名。我又如何根据这些信息搜索指定embedding_size的模型文件呢?那我还要改模型存储的部分。牵一发而动全身,当时距离我自己给自己定的ddl也不远了,我实在没有精力和勇气去做这样一件事。

所幸,teacher_training阶段是模型训练的第一步,也是参数文件默认的训练阶段,所以项目进行CI测试就算会测试超参数搜索,也是进行teacher_training的超参数搜索,所以并不会出现问题。

我当时也参考了RecBole当中其他有预训练过程的模型复现,他们似乎都没有解决这一问题。

当时的理解是,可能只需要把预训练/teacher_training阶段的超参数搜索好就行(实际当然不是这样了)。当时只好硬着头皮先交了,想着也许确实不需要解决这个问题。所以虽然算是做完了,当时内心还是十分忐忑的。

最终的解决方案:

在昨天(也就是2/4),学长忙完之后帮我review code。我当时想着描述清楚这个问题,然后问学长,在思考怎么把问题描述清楚的时候,我突然想到了这个问题的解决方案(我个人的拙见)。

首先,teacher_training阶段没有问题,正常超参数搜索就好了。

主要就是要解决distillation和finetuning的超参数搜索。实际上会起冲突的就是embedding_size。实际上embedding_size不是一个需要重复进行超参数搜索的参数。不管是teacher_training还是pretraining,它们都会预先找到性能最好的embedding_size,然后distillation或者training就会在此基础上进行调整。所以teacher_training找好了最佳embedding_size之后,后续阶段不必再搜索。如果实在要搜索也可以,但是应该不用自动超参数搜索了。

而对于真正影响知识蒸馏或者训练效果的learning_rate、α、β,我们是单独进行搜索的。也就是确定了embedding_size之后,那么我们学习的最佳教师模型的路径也是确定的(也就是warm_up)不用变。根据固定的教师模型,修改超参数,提高知识蒸馏的效果。finetuning过程同样也是这样。

所以根据这个思路,并不需要改动项目的超参数搜索程序,也不需要改变模型的存储过程。只需要使用模型的人稍加注意就行。

结果

最后当然也没有问学长这个问题,而review code虽然出现了另一个问题(很顺利解决),也没有再出现别的问题。

这个知识点还是非常值得记录的,所以有了这篇博客。

我也深感自己的幸运和误打误撞,我自己都不觉得review会这么顺利。学长还夸我写的非常好。真的是非常开心的。

接下来应该还会写一篇博客回忆、记录我这段过程的经历和心路历程。