我翻转了一个符号,却把它当成洞见的那一天

有一次回测返回的胜率,几乎正好与我所期望的相反。我本想让模型大部分时候是对的,可它却大部分时候是错的——而且不是随机地错,而是干净、对称地错,仿佛它一直在努力亏钱。

我没有去问为什么,而是做了一件在大约一个小时里感觉很高明的事。我把逻辑翻转过来去匹配它。我推理道,如果模型可靠地错,那么反着照它说的做就一定可靠地对。一个小小的改动,一条亏钱的规则在纸面上就变成了一条赚钱的规则。我为自己的发现而沾沾自喜。

那并不是一个发现。那是我开始用一次单字符的编辑对自己撒谎的时刻。

那个恰好错了的数字

可疑之处从来都不是结果很糟。糟糕的结果是常态;这份工作大部分时候就是如此。可疑之处在于,它糟得如此整齐、如此镜像对称——这种对称,事后看来,往往意味着底层有某种更平庸的东西坏掉了,而不是一个真正”反着来”的策略。

但当对称指向一个轻松的胜利时,它很有诱惑力。一个乱糟糟的糟糕结果会让你去调试。而一个干净利落地反转过来的结果,则会向你低语一条捷径:你比你以为的更接近了——只要把它掉个头就行。

那个单字符的修复

于是我把它掉了个头。我反转了那个条件——翻转了一个不等号——同样的逻辑,原本一直在亏,现在在回测里赢了。我提交了这个改动,感觉自己智胜了这个问题。整个修正比这句话还要短。

那种”短小”本应是一个警告。真正的优势难以找到,更难以保持。当单个翻转的字符看似把失败变成了成功,诚实的假设不应是你找到了一个隐藏的真理。而应是你找到了一个隐藏的 bug,只不过换了一边站着而已。

为什么翻转符号感觉像天才之举

这套逻辑几乎无法抗拒:如果一个预测器一贯地错,那么把它反转过来就让它一贯地对。一个反向的天才。一座逆向思维的金矿。谁不愿意拿一个可靠地错误的模型,干脆倒过来读呢?

这套推理的缺陷在于”一贯地”这个词。它假定模型确实在预测某种东西——只是方向反了。而这个假定,恰恰是我从未去核实的那一件事。

真正坏掉的是什么

模型并不是反着的。它几乎根本没在预测。它的输出是退化的——被压缩进一个狭窄、毫无信息量的区间里,无论给它看什么,它说的几乎都一样。它并不是学到了一个信号却指错了方向。它是没能学到信号,产生出某种接近噪声、带着一点微弱而毫无意义的偏向的东西。

短短一串大多是反面的抛硬币结果,并不能证明存在一个稳定、可利用的偏差——它同样很可能只是一枚坏掉的或不可靠的硬币。翻转你读取一个含噪信号的方式,并不会让这个信号变得有信息量。它只是给噪声重新贴了个标签。

这次反转让情况更糟了

这是回测最终逼我承认的部分。翻转逻辑之后,性能并没有镜像地向上变好。它依然很差——而且按某些度量来看变得更糟了。根本没有什么可反转的。我拿了噪声,把它翻过来,又得到了噪声,只不过现在它披上了一件”有意图”的戏服。

那个短暂而美丽、让我自觉聪明的结果,正是我以前写过的那种海市蜃楼:一个奉承你的数字,恰好奉承得够久,让你停止去问那个唯一重要的问题——模型到底有没有学到任何东西?

真正的 bug 在我身上

那个单字符的翻转,从来都不只是一次代码改动。它是一个范畴错误。我把一个症状——糟糕的胜率——当成了一个我可以去调的策略,而它其实是一个我拒绝去做的诊断。我没有去问模型的输出为什么会是那个样子,而是把一个不等号对着回测一路优化,直到那个数字变绿。

那就是过拟合最纯粹、最具人性的形态。你不需要一千个参数才能过拟合。你只用一个符号就能做到,只要你愿意根据你想要的结果、而不是你理解的原因来选择它。

一个出人意料的结果是一个问题,不是一个答案

我从中学到的纪律,说起来简单,做起来是真的难:当一个结果出来得与你预期的相反,那是一个问题,不是一个答案。诱惑在于伸手去抓那个能让数字看起来对的最小改动——翻转一个符号,反转一条规则,调换一个比较。那个最小的改动往往是最危险的,因为它修复了表象,却让原因丝毫未动。

如今,当有什么东西在我面前反转时,我会努力抵抗那个优雅的反转,转而去问那个丑陋的问题:不是”我怎样让这个数字指向正确的方向?“,而是”这里到底有没有一个真实的信号,朝着任何方向?“通常还没有。而一个翻转的符号造不出一个来。

一个倒过来读的亏钱模型,仍然是一个亏钱模型。反转唯一改变的,是你要花多久才能发现这一点。

—— 没有信号,没有回报,并非投资建议。