我的模型在偷看明天的报纸

有那么一阵子,我的某个模型看起来像个天才。它的回测干净、笃定,甚至流畅得有些过头。我多么想相信,自己终于做出了一个真正管用的东西。

它并没有学到任何值得留下的东西。它学会的是作弊——悄无声息地,通过一个我亲手搭出来的泄露,让它在决定过去该做什么之前,先读到了未来的信息。

这个好得可疑的学生

一个表现比你预期稍好一点的模型,会让人兴奋。一个表现远远超出它应有水平的模型,则应该让你感到害怕。我的就是后者:在真实市场参差不齐的地方它却平滑,在本该忐忑不安的地方它却笃定。

我在更早的一篇文章里写过,最危险的结果,正是那个讨好你的结果。这又是同一个教训,只是换了一身更隐晦的行头。这一次数字并不离谱——它们只是太好了,而”太好”本身就是一种危险信号,因为市场里真正的优势是微小的、嘈杂的、来之不易的。一个看起来像是已经解开了市场的模型,几乎总是反过来在你的模拟里找到了一道裂缝。

未来是从哪里溜进来的

那道裂缝出在归一化上——就是那个毫不起眼的步骤:你把原始输入重新缩放,好让模型能从中学习。要做归一化,你需要统计量:一个中心,一个离散程度。而我是在整个数据集上一次性算出这些统计量的。

正是这一个选择,让未来泄露了进来。当你跨越全部历史去计算统计量,再把它们应用到过去的某个点上时,那个过去时刻的每一个”归一化”后的值,都悄悄编码了尚未发生的数据的信息。模型在看任意一个瞬间时,其实都被暗中告知了关于整条时间线形状的某些东西——包括它的未来。它从未直接看到明天的价格。它根本不需要。它呼吸的空气里,早已掺进了明天。

为什么前视偏差如此容易造出来

前视偏差之所以危险,并不在于它难以制造。而在于它会从那些再寻常不过的预处理步骤里悄悄混进来。写这段代码最自然、最易读的方式——在数据上拟合你的缩放器,再对数据做变换——对时间序列而言本身就已经是错的,而且没有任何东西会抱怨。不会抛出异常。不会有测试变红。数字看上去完全合理。唯一的症状是,你的模型比它应有的水平更好——而这恰恰是你最没有动力去深究的那一个症状。

这就是泄露的陷阱:它看起来不像一个 bug。它看起来像成功。

那个破绽:在这里光彩夺目,到那里一命呜呼

不过,确实有一个能说明问题的特征,尽管它本身并不构成证据。一个被前视偏差污染的模型,往往会在它被调校过的数据上光彩夺目,却在一碰到它无法偷看的数据时立刻崩溃。样本内的辉煌与样本外的崩塌之间出现巨大落差,是泄露的一个警示信号——尽管这也可能来自过拟合、市场状态的转变,或是你低估了的成本。在我的案例里,泄露才是真凶。

所以真正重要的检验,从来都不是”回测有多好?“,而是”当我把模拟做诚实之后,它会变差多少?“当我堵上那个泄露,表现下滑了——而那次下滑,尽管令人痛苦,却是这套系统第一次给我看到的、可以信任的数字。

修复之道:绝不使用任何你当时还不可能知道的东西

这个修复是一条原则,而不是一行代码:在模拟中的任意时刻,模型只能使用在那一刻或之前就已存在的信息。对归一化而言,这意味着要从过去的一个滚动窗口中推导统计量——在截至某个时刻为止可用的数据上拟合,绝不在整条时间线上拟合,更绝不在未来的任何一个点上拟合。

它更慢。它更繁琐。它产出的回测更难看。这三点全都是优点,而非代价,因为每一点都意味着模拟在拒绝让模型穿越时间。一条诚实的流水线,要在表面表现上交一笔税,而这笔税,正是换取你真正能够信任的数字所付出的代价。

泄露是一个家族,而不是单个 bug

修复这件事最难的部分,并不是那个归一化器。而是意识到归一化器只是其中一道门,未来还能从许许多多扇门走进来。同样的错误,也藏在标签是怎么构造出来的、藏在那些暗中概括了整条序列的特征里、藏在任何同时触及过去与未来的预处理步骤里。堵上一个泄露,你并没有堵住泄露;你只是认清了它长什么样,好让自己去找出其余的那些。

我不再把前视偏差当成一个要修的 bug,而是开始把它当成一种要审计的属性——一种我如今会刻意去搜寻的东西,前提假设就是:如果一个结果看起来太干净,那么未来一定正从某个我还没检查过的地方溜进来。

为什么回测会奖励作弊

在这一切之下,藏着一个关于回测的、让人不舒服的真相:它分不清本事和事后诸葛。对于一个真正能预判市场的模型,和一个悄悄在抄答案的模型,回测给出的回报一模一样。两者都能画出一条漂亮的曲线。严格向前、从未见过的数据,是更有力的检验,它才开始把两者区分开来——尽管即便是它,单凭自身也无法证明一条流水线没有泄露。

这就是为什么”它在回测里表现不错”是一场调查的开始,而不是结束。一条好看的资金曲线是一个问题:*这个模型是凭本事挣到的,还是我不小心让它作了弊?*整套诚实研究的修行,无非就是学会在市场替你提出这个问题之前,先自己问出它。

一个偷看明天报纸的模型,永远会显得像个天才。那份天才属于你,而不属于它——它完完全全藏在那个你忘记堵上的泄露里。

— 没有信号,没有收益,不构成投资建议。