← 返回教程库

模型会过期,可为什么不能靠人记得去重训

最后更新 2026-08-19
📚 量化与 AI 投研 📖 仓库导览 · qlib ⏱ 约 14 分钟 R2 · 注意风险
你将学到
  • 能说出「靠人记得去重训」会具体出哪三种事故,尤其是「业绩难看时临时重训」为什么本身就是一种过拟合
  • 能读懂一个真实投研框架把重训拆成了哪几个可以分开出错的环节,以及每个环节各自在防什么
  • 能说清实验记录和可复现在投研里到底值什么钱,并把其中几条纪律搬到自己的手工流程上

三个月后的某个下午,有人指着你发出去的一张收益曲线问你:四月中旬那一段特别难看,那时候在跑的是哪一版?

你张嘴要答,卡住了。你记得三月底改过一次东西,也记得四月里某天觉得不对劲重新训了一遍,但改的是用了多长的历史,还是看的那几个指标?重训是四月几号?那张曲线上四月中旬那几十个点,到底是旧的那版留下来的,还是新版接手之后的?

你翻聊天记录、翻文件夹,看到一堆叫 model_finalmodel_final2model_new_ok 的文件,修改时间倒是清清楚楚,可修改时间只告诉你它什么时候被存下来,不告诉你它当时是拿哪一段数据、按什么设置训出来的。

这一刻你会意识到一件事:你不是缺一个更好的模型,你缺的是一本账。

而这本账,恰恰是绝大多数人自己搭一套东西时最不会去建的部分。这里先说清两篇的分工:模型会过期 那篇讲模型为什么会变钝,以及"谁在线"这本账长什么样、里面记些什么;本篇讲更上游的一层——把"重训"这件事拆成几个可以分开出错的环节,以及每一次尝试本身该怎么被留档,还有为什么这两件事一旦交给人的记性,就基本注定要出事。

先说人话:面包房换配方

想象一家开了几年的面包房。

刚开张时只有老板一个人,配方在他脑子里。哪天他觉得口感差了点,就凭手感调一调面粉配比。生意小的时候,这样完全没问题。

现在这家店开到了五家分店。某天客服收到一堆投诉:上周三买的那批吐司发酸。老板要查,就得回答一串问题——上周三那批是哪家店做的?用的是第几版配方?那版配方是什么时候定的、谁定的、当时为什么要改?和它同期还有没有别的版本在跑?

如果店里什么都没记,这些问题一个都答不了。老板能做的只有一件事:凭印象猜,然后再凭印象改一次配方。这一改,可能把好的部分也改掉了,而且下次再出问题,他还是只能猜。

于是正经的食品作坊会做三件很枯燥的事:给每一批产品打批号把每次配方变更连同变更原因一起写进本子每批留样存档。这三件事一点都不"提高产品品质",它们提高的是另一样东西——出了问题能查得清,改了东西能说得明

投研里那套"定期重训"的机制,本质上就是这三件事。它不让你的模型变得更聪明,它让你在三个月后能回答"四月中旬是谁在当值"。

靠人记得,会出哪三种事故

把这件事交给人的记性,故障模式相当固定,而且三种都很贵。

一、人会忘,而且忘得毫无规律

这是最好理解的一种,也是最不致命的一种。你说好每个月重训一次,结果三月出差,四月忙别的,等你想起来已经是五月底。中间那两个月,你用的是一个自己以为已经更新过的东西。

比"忘了做"更麻烦的是"忘了做过什么"。你可能确实重训了,但那次顺手把训练区间往前挪了半年,因为当时觉得"数据多一点应该更稳"。三个月后你完全不记得有这回事,只记得"我一直是按老规矩训的"。于是你后面所有的分析,都建立在一个错误的前提上。

人的记性不是随机丢失信息的,它是有偏地丢失信息的——你会记住那些当时觉得重要的、有戏剧性的改动,忘掉那些当时觉得无所谓的顺手一改。而事后回头看,翻车的往往正是那些顺手一改。

二、人会在业绩难看的时候临时重训,而这件事本身就是过拟合

这一条是本篇最想让你记住的。

设想一个很自然的场景:你的东西跑了两个月,收益曲线一路往下。你心里越来越难受,某天终于忍不住了:这肯定是市场变了,我重新训一遍。

于是你重训。新模型上线,接下来几周曲线回正了。你松了口气,并且得出一个结论:看来我的重训判断是对的,以后就该这么干。

这个结论错得很隐蔽。问题不在于你重训了,问题在于你选择重训的那个时刻,是被结果挑出来的

拆开看:你只在难看的时候动手,从不在好看的时候动手。而收益曲线本身有均值回复的成分——一段连续的难看之后,即便你什么都不做,接下来的表现大概率也会比刚才那段好一些。你在最低点附近按下了那个按钮,然后把随后的自然回升,全部记在了按钮的功劳簿上。

这个逻辑你可能在别处见过。它和在历史数据里反复调参数、把回测曲线调到最漂亮,是完全同一种错误——只不过被过拟合的东西从"参数"换成了"动手的时机"。数据窥探 讲的就是这类错误的一般形态,而"什么时候重训"是它最不容易被认出来的一种变体,因为它看上去不像在调参,看上去像在负责任地应对市场。

更糟的是,这种做法的收益记录天生无法被检验。你没法回答"如果我当时没重训会怎样",因为你没留下那条平行线。而一个不能被证伪的做法,用久了只会越来越自信。

治它的办法只有一个,而且相当反直觉:把动手的时机从你的判断里拿走。按固定节奏重训,好看的时候也训,难看的时候也训。这时候重训到底有没有用,才是一个能被检验的问题——因为你的动手时机不再和结果相关了。

这也是"把重训做成系统"最核心的价值。它不是为了省事,是为了切断结果对决策时点的反向影响

三、你分不清哪条曲线是谁跑的

第三种事故最安静:真实运行不是一个模型从头跑到尾,而是一串模型接力,你手上那条连续的收益曲线其实是好几段拼起来的,而你不知道接缝在哪。这一层的账该怎么记、不记会同时坏掉归因和评估、以及新旧交接处那种"旧模型的结果留在了本该由新模型负责的时间段里"的污染是怎么冒出来的,模型会过期 那篇已经讲透,这里不重复。

本篇要接着往下问一句:既然归因和评估会因为记录缺失而一起坏掉,那反过来,一套不想让它们坏掉的系统,必须被设计成什么样? 这个问题的答案,就是下一节那四个环节的由来。

归因要成立,逼出的是"任务本身得有身份"。 归因的前提是每一段成绩都能对上一个明确的当事者。这件事没法事后补——你不能三个月后翻着一堆模型文件去猜哪个管哪段,你只能在造这个任务的那一刻就把它的身份钉死:它负责哪一段时间、拿哪一段历史训、跟前一个任务的边界划在哪天。手工做法最常见的破绽正在这里:模型文件是有的,但"它负责哪一段"从来没被写进任何地方,只存在于你当时的意图里,而意图不会留在硬盘上。

评估要成立,逼出的是"逐次留档,且留全"。 比较两版哪个更好,需要的是可比:除了你想比的那一处,其它条件尽量一致。所以记录不能只记结果,还得记全那些"其它条件"——用了哪段数据、什么设置、同期还有没有别的东西被你顺手改过。少记一样,你就多一个无法排除的解释。而这类记录有个很不友好的性质:它必须在你还不知道自己将来要比什么的时候,就先记下来。 等你想比的时候再回头补,能补出来的只有结果,补不出来的恰恰是条件。

这两条合起来,解释了后面那套结构为什么长成那个样子:为什么任务要先生成、先存起来、还带着状态,而不是想训就训;为什么每次运行的设置和产物要一条条落库,而不是等有了漂亮结果再截张图。它们防的都不是"跑不出来"——跑不出来会报错。它们防的是三个月后你手上只剩一堆无法回答问题的文件。

到了系统里,它长什么样

现在看一眼真实框架怎么处理这件事。qlib 把"定期重训"整个拆成了四个可以分开出错的环节:造任务、存任务、跑任务、收结果。这个拆法本身就是最有信息量的部分。

造任务:把"怎么滚"变成一个必须回答的问题

第一步不是训练,是生成一串训练任务。你给它一个任务模板和一个滚动的节奏,它按时间把这个模板复制成一串,每一份对应一个时间窗口。

这里有个细节值得停一下。滚动的方式在代码里被写成了两个明确的选项:一种是起点固定、终点不断往后延(训练数据越滚越多),另一种是窗口长度固定、整段往后平移(永远只用最近这么长的一段)。

这两个选项对应着一个真实的分歧:老数据到底还作不作数?前一种认为多多益善,后一种认为太老的经验已经不适用了。谁对没有普遍答案,但框架逼你选一个,并且把你选的那个记下来。 对照一下手工做法:大多数人从没明确想过这个问题,每次训练时凭当时的心情决定用多长的历史,事后也不记得用了多长。

同一处还有另一个选项,用途是在训练段和测试段之间截掉一小段数据,注释里写明目的是避免未来信息泄漏。为什么需要截?因为标签本身是往后看的——你要预测未来若干天的表现,那么训练段最后那几天的样本,它的标签落在了测试段里面。不截掉,测试段的信息就顺着标签渗回了训练段。这个坑的完整形态见 前视偏差,这里想说的是另一层:它被做成了一个参数,而不是一句写在文档里的提醒。 做成参数意味着每次滚动都会执行,做成提醒意味着每次都得靠人记得。

顺带说一句,"把历史切成一串窗口逐个训练"这个动作,用在评估阶段就是滚动前进验证,用在运行阶段就是真实的定期重训。同一套任务生成逻辑被两边共用,这个复用本身就说明了一件事——你评估时演练的流程,和你上线后真正跑的流程,应该是同一个流程。

存任务:一个有状态机的待办清单

生成出来的任务不是直接跑掉的,而是先存进一个数据库,每个任务带一个状态标记。

状态一共四种:等着被训、正在训、做完了一部分、全部完成。这四种状态存在的理由,不是为了好看,是为了回答一个非常现实的问题:跑到一半机器挂了怎么办?

如果没有状态,机器一挂你就只知道"有些任务跑完了有些没跑完",但不知道是哪些。有了状态,重启之后从"等着被训"的那些接着做就行。而"做完了一部分"这个状态更实在——训练和回测是两个阶段,第一阶段跑完第二阶段崩了,你不该把第一阶段的成果一起丢掉重来。

还有一处设计我觉得特别值得抄。取任务的时候,框架把"取出来"和"用完了"包在一起:取的那一刻任务被标成"正在训",如果中间抛了异常,或者你直接按下中断键,它会把这个任务的状态原样还回去,让它重新变成待办,而不是永远卡在"正在训"上。

这背后的假设很朴素:一定会有任务失败,也一定会有人半路把程序掐掉。 一个不为中断做准备的流程,第一次中断之后就会留下一堆不知道该不该重跑的僵尸任务,然后你只能全部重来。

这个思路完全可以搬到手工流程上:任何一件你会分几次做完的事,得有个地方明确记着"做到哪了",而且这个记录要在你被打断的时候自动是对的,不能依赖你被打断前记得去更新它。

跑任务:跑完只是一半,另一半是记下来

任务跑起来之后,真正的重头戏是记录

框架里的记录系统是三层的:最外面是"实验"的管理者,中间是一个个"实验",每个实验下面挂着一次次"运行"。每一次运行会记下这几样东西:这次用的参数、这次算出来的指标、这次产出的文件、以及给这次打的标签

三层结构听起来有点绕,但它对应的是投研里真实的层级:你在研究一个课题(实验),这个课题下面你试了很多次(运行),每一次的设置和结果都得分开存着。

有一个细节我觉得最有价值:每一次运行本身也带状态,其中包括失败这个状态。

也就是说,跑砸了的那些次,也是要被留下来的

这一条比它看起来重要得多。人的天然倾向是只保留成功的记录——试了二十次,成了一次,你把那一次存下来,另外十九次的痕迹随手清掉。三个月后你打开文件夹,看到一个漂亮的结果,完全不记得它是从二十次里挑出来的。

而"从二十次里挑出来的最好一次"和"试了一次就成了",是完全不同性质的两件事。 前者好看是理所当然的,就算这二十次全是纯噪音,最好的那次看起来也会不错。你把十九次失败删掉的那一刻,就把判断这件事的唯一依据也删掉了。样本内与样本外参数高原与参数尖峰 讲的都是这个家族的问题,而实验记录是对付它最笨也最有效的手段——你试了多少次,本身就是一个必须被记录的数字。

还有一点:记录里"参数"和"结果"是分开存的两类东西,这个区分很关键。结果会过时,参数不会。 半年后那条收益曲线的具体数值可能已经没什么意义了,但"当时用的是哪段训练数据、什么设置"这件事,是你唯一能据以复现、据以对比的东西。大多数人的记录习惯恰好反过来——截了一张收益曲线的图,设置一个字没留。

收结果:分析必须依附在具体那次运行上

最后一环是把结果整理成能看的东西。框架为此提供了几种记录模板:一种负责把模型的预测本身存下来,一种负责算预测质量的统计量,一种负责跑组合回测。

这里有个结构上的安排值得说:后面那两种模板在代码里明确声明了自己依赖前一种。它们不能凭空运行,必须建立在"预测已经被存下来"这个基础上。

这个依赖关系落到实处,意味着一件事:任何一份分析报告,都能顺着链条追回到产生它的那次具体运行、那批具体预测。 你看到的那个预测准确度指标(就是 IC 与 IR 那篇讲的东西)不是一个漂在空中的数,它挂在某一次运行下面,而那次运行记着自己用了什么参数。

对照一下常见的手工做法:分析结果躺在一个 Excel 里,模型文件躺在另一个文件夹里,两者之间靠文件名的一点点相似性维系。改天你把某个模型重训了一遍覆盖掉,那个 Excel 就变成了一份永远无法被验证的历史文件

那么,实验记录到底值多少钱

把上面几段串起来,会得到一个和直觉相当不同的排序。

第一,它是让"我的方法有效"这句话变得可以被检验的唯一途径。 没有记录,你的每一个结论都只有一个观察者——你自己,而且是那个已经知道结果、并且希望自己是对的自己。有记录,你才有可能三个月后回头,发现当初那个判断其实站不住。能反驳自己,是投研里最贵的能力,而它需要基础设施。

第二,它把你的经验从"感觉"变成了"数据"。 你做过五十次重训,如果每一次的设置、时点、结果都在,那这五十次本身就是一份可以分析的样本——你可以回头问"训练窗长一点是不是真的更稳",并且用你自己的历史来回答。没有记录,这五十次经验只剩下一句"我觉得差不多每季度一次比较合适"。

第三,它是对付事后叙事的解药。 人在解释已经发生的事情时,编故事的能力强得可怕。四月那段回撤,你事后总能找出三个像模像样的理由。而一份当时写下的记录——当时的设置、当时打的标签、当时正在跑的是哪一版——会毫不留情地戳破那些故事里对不上的部分。

第四,它让"接力"这件事变得可分析。 前面说过,真实运行是一串模型接力。有了逐次记录和在线状态的账本,你才能把某一段成绩归到某一版身上,才能比较,才能知道换新到底有没有带来改善。否则你永远只能评价"整体",而整体是不可优化的。

顺着第四点还能推出一个不舒服的结论,它在 模型会过期 里讲透了,这里只取它对本篇的意义:你该检验的对象,是这条流水线,不是它某一次的产物。 而流水线能不能被检验,取决于前面四个环节有没有各自留下可追的记录——这正是本篇一直在说的那件事。

如果你只是手工做,能抄走什么

这套系统的价值不在于你要不要装它,而在于它把几件事逼到了明处。三条可以直接搬:

把重训的时机写死,并且提前写下来。 不是"看情况",是一个跟结果无关的节奏。写在你能看见的地方,然后到点就做,好看也做难看也做。这一条唯一的作用是切断结果对你动手时机的影响,而这一条能挡掉的错误,比其它两条加起来都多。

每次改动留一行字:改了什么、为什么改、当时的想法是什么。 一行就够,但必须是当时写的,不能是事后补的。事后补的那行,写的已经是你希望自己当初怎么想的了。

失败的尝试不要删。 至少留下"我试过这个方向,没成"这一句。它的价值在于半年后你又想起同一个主意时,能知道自己已经走过一遍。

这三条都不需要任何软件,需要的只是承认一件事:你对自己过去做过什么的记忆,比你以为的不可靠得多。

它明确不负责的事

说完好处,边界要交代清楚,否则很容易把这套东西当成万能。

  • 它不判断该不该重训。 节奏是你定的,它只负责按你定的节奏执行、并且忠实记下你定的是什么。你把节奏定错了,它会精确地、可复现地、有完整记录地一直错下去。
  • "重训治什么病"和"多久重训一次"这两个问题不在本篇射程内。 它们属于模型衰减那一层,模型会过期 讲得更完整;本篇只管到点该发生的事怎么被可靠地执行下去、并且留下痕迹。
  • 记录不能替代判断。 一本记得极其工整的账,不会让一套错误的方法变正确。它只保证你能更快地发现它是错的,而这已经是很大的价值了,但也仅止于此。
  • 一切都建立在数据每天准时且正确更新这个假设上。 这条为什么致命、以及该盯什么,模型会过期 已经讲了;落到本篇就是一句:四个环节里没有任何一环能替你发现上游变了,所以这道检查必须单独存在,且排在最前面。
  • 框架自己会写明它做不到什么。 qlib 的在线服务文档里有专门一节讲已知限制,坦白了某些能力因为公开数据的限制并不具备。这一节值得在动手前先读——它划的是你需要自己补哪一段的边界。

顺带一句和风险有关的话:这一整套东西一旦真的连上真实账户自动运行,出错的代价就不再是一张难看的图。任何自动执行的安排都需要有人定期看着、有明确的停手条件,也需要你事先想清楚最坏情况能承受到什么程度。 这篇讲的是机制,不是让你去搭一套跑起来。

小结

  • 重训不是一个动作,是造、存、跑、收四个可以分开出错的环节;把它当一个动作,你连是哪一环坏了都说不出来。
  • 靠人记得会出三种事故:会忘且忘得有偏、会在难看的时候临时动手、会分不清哪段成绩归谁。第三种反过来规定了系统的形状——归因要成立,任务在生成那一刻就得带上不可覆盖的身份;评估要成立,条件必须在你还不知道要比什么的时候就先被记全。
  • 三种里最贵的是第二种:只在业绩难看时重训,等于用结果挑选了你的动手时机,这是彻头彻尾的过拟合,而且它的效果天生无法被检验。解法是把时机从判断里拿走,按固定节奏执行。
  • 真实框架把重训拆成四段:造任务(明确回答滚动方式和防泄漏截断)、存任务(带状态的待办清单,为失败和中断预留了归还机制)、跑任务(参数、指标、产物、标签逐次记录,失败也留档)、收结果(分析依附在具体那次运行上,可追溯)。
  • 实验记录的价值排序:让结论可被检验 > 把经验变成可分析的数据 > 戳破事后叙事 > 让模型接力可归因。
  • 失败的记录和成功的记录同样重要:不知道你试了多少次,就无法判断那一次成功值多少钱。
  • 记录里参数比结果更持久:结果会过时,"当时怎么设的"才是唯一能据以复现的东西。
  • 这套东西不判断该不该重训、不能替代判断,而且全部建立在数据准时正确这个假设上;至于重训治什么病、多久一次,那是 模型会过期 的射程。

一句话说完这篇:你三个月后一定不记得当时改了什么、为什么改,所以要么现在就把它写下来,要么以后所有的复盘都只是编故事。

本文基于开源仓库的源码与文档快照做概念讲解,未实际运行该程序,具体行为请以你手上那个版本为准;本站内容为投资教育,不构成任何投资建议或软件使用建议,边界见 免责声明。想系统看这一卷的其它内容,回到 量化与 AI 投研卷,或从 qlib 是什么:一条完整的 AI 投研流水线 开始读这条导览线。

内容有错、看不懂、或想看下一篇?告诉我们 →

本文为投资教育整理,不构成任何投资建议;不荐个股/基金、不预测点位、不承诺收益。关键数据与结论请结合权威来源自行验证,并见风险免责声明