最近在查看订阅号后台和文章评论区时,我发现许多小伙伴已经具备了扎实的机器学习和深度学习基础,但在实际操作中仍然面临不少挑战。很多新手在处理算法问题时,常常陷入盲目试错的困境,调参过程毫无目标,导致效率低下。这种无脑调参的现象不仅浪费时间,还容易让人失去信心,认为模型效果不佳是因为调参不够精细。
新手最大的问题在于缺乏清晰的逻辑思维,往往依赖于“蛮力法”,导致陷入不断寻找开源代码、调参、测试的循环中。如果恰好找到一个代码,让模型性能提升,他们就会过度吹嘘其效果,而对没有显著提升的算法则怀疑其有效性。长此以往,他们可能会产生一种错觉,认为深度学习只是简单的调参工作,没有竞争力。
今天,我将分享一些我认为对初学者有用的炼丹常识,希望能帮助大家提高效率,走出无脑调参的怪圈。具体来说,我希望帮助大家实现以下三个目标:
在讨论“快”之前,我们先来看看“准”。因为“准”是最关键的问题,也是“快”的前提。
接手一个算法问题后,如果时间充足,可以先定位该问题所对应的学术会议或期刊。例如,处理query-doc相关性匹配问题时,应优先考虑SIGIR、CIKM等信息检索领域的会议,而不是NLP会议。处理NLI、问答、对话这类语义匹配问题时,则应优先考虑ACL、EMNLP、NAACL、COLING等NLP会议。
如果定位不到合适的会议,至少可以浏览AAAI和IJCAI。然后,根据文章标题,找到近几年内与你的问题最接近的几篇论文,仔细研究它们的相关工作章节和实验章节,通常可以追溯到更早的研究工作。
这样,你就可以找到当前问题的前沿解决方案和经典方法,从而获得不错的基准模型。通过几次策略迭代,或许就能撰写出一篇新的论文。
但如果时间紧迫,问题又比较简单(如文本分类、序列标注等),可以在知乎上搜索,实在不行还可以咨询他人。
总之,不建议一开始就盲目地在GitHub上一个个地调参。很多有价值的解决方案是难以通过通用搜索引擎找到的。
避免盲目试错的第二步是构建完整的策略迭代闭环。不同的问题有不同的限制,因此不存在一个通用的流程适用于所有问题。一个常用的方法是:
对于新手,往往会在第三步和第四步陷入死循环,忽视前两步和最后一步。对于经验丰富的专家,还会额外考虑预处理策略。
很多新手拿到数据集后便迫不及待地开始调参,但其实应该在开始之前对数据集做一个简单的分析,以便提前排除不靠谱的策略和不敏感的超参数,减少初期错误的发生概率。
例如,简单统计样本长度分布可以帮助确定max sequence length的取值范围,避免将它作为一个重要的超参数反复调整。统计类别分布可以防止因正负样本比例失衡而导致的错误决策。多浏览数据集可以发现潜在的问题,比如文本风格不一致等。
这个环节可以直接借鉴上一节的调研结果,将一些论文中有效的策略进行验证。但需要注意的是,搬运策略时要明确目的,即为什么要采用这个策略,解决什么问题。很多论文中的策略应用场景有限,盲目搬运可能增加无效工作。
在比赛打分时,模型评价通常是明确的,但在成熟的算法任务中,评价标准可能已经被解决了。例如,文本分类可以使用准确率和F1分数,机器翻译可以使用BLEU分数。但对于某些问题,如开放域对话生成,很难找到无偏且自动的评价指标。此时,每迭代一次策略都需要人工标注,效率很低。
标量型评价指标(如准确率、F1、BLEU等)可以指导整体效果,但无法发现更细微的问题。通过案例研究,可以发现很多以前尝试过的策略其实是多余的。例如,看似数据不平衡的问题,实际可能是模型处理得当;看似推理问题复杂,实际可能是模型在特定类型上表现不佳。
算法探索具有很大的不确定性,很多代码写到最后可能都无法运行。因此,编写代码时要避免追求完美,允许一些“垃圾代码”的存在,这可以提高实验迭代的效率。
例如,可以使用Bash脚本将功能零散的代码片段连接起来,通过管道命令和多进程处理大规模数据集。对于更碎片化的代码,可以使用IPython,完成后使用%save命令保存为可复用的代码。
在调试过程中,要分阶段进行:
新手往往倾向于将所有变量都作为超参数进行调优。但有些超参数可以通过计算得出合理范围,有些则取决于其他超参数和环境因素。因此,首先要进行超参数敏感性分析,找到对当前任务影响最大的几个超参数,再进行细致调优。
很多新手会遇到这样的情况:
“上次明明跑出了95%的准确率,这次怎么变成了92%?” “我的模型去哪了?” “这个模型是怎么训练出来的?” “这两个策略有什么不同?”
这些问题主要出现在实验管理和代码版本管理上。需要记录每次实验的策略和结果,并使用支持云端同步的工具,如石墨文档或印象笔记。此外,使用版本管理工具,如Git,可以更好地管理代码和实验迭代。
确保训练日志和评估日志以文件形式保存,而不是打印到屏幕上。每次运行时的设置(如超参数、数据集版本、检查点存储路径等)也要保存到日志文件中。可以封装一个run.sh脚本来维护训练环境。
个人习惯是每成功推进一步策略,就使用git tag打个标签,便于追踪和复现。如果要尝试不靠谱的探索,可以在当前策略基础上拉一个分支,完成后合并到主分支。
最后,要做好备份工作,定期将关键代码推送到GitHub等远程仓库,大型文件则可以忽略,存放在Hadoop集群中。
通过以上方法,希望可以帮助初学者提高炼丹能力和效率,避免无脑调参,从而更加高效地解决问题。