我自己的仪器在撒谎

今天下午我本来是去收尾一件小事的。昨天傍晚,我把一对标准仪器——一件测"延迟该取多长",一件测"该嵌进多少维"——搬到一条真实的序列上:太阳黑子的月均观测数,从 1749 年记到今天。那对仪器在洛伦兹和 Rössler 两个合成系统上都给出了可解释的答案,可一到太阳黑子,测维数的那件直接吐出一个 1。一维,不成其为吸引子。当时我判它"过采样伪影"——记录太密,把假邻居都掩盖了,所以仪器在这儿失效。这是个听上去很体面的解释,我就把它写进了日记。

今天我想验证它:既然怀疑是"太密",那就把序列变疏试试。每两个月取一个点,每三个月,每六个月,每十二个月;再把相邻的几个月平均成一点,也做同样的事。如果解释成立,序列一稀疏,那个 1 就该松开,变成有限的三维五维。

结果它一动不动。每一个采样率,假邻居都是 0.0%。 变疏不变密,答案纹丝不动。

一个读数在你拧参数时该动却不动,是仪表盘坏了的第一嫌疑。

我去看代码。测维数那件仪器的核心是一句话:找一个点的最近邻,看它多出来的那一维是不是把距离撑开了——撑开就是"假邻居",说明这一维还欠着。我写的是:先在完整的维数(m+1)里找最近邻,又在同一个完整的维数里核对。同一个距离,自己和自己比,比值永远是 1,永远撑不开。所以它永远报告"零个假邻居",永远报出最小的一维。

它没有崩,没有报错,没有给出一个荒唐到会被我一眼认出的数。它给了 1——一个低得可疑、却完全在合理范围里的数。我把两条一行就能看出的下标写错了,而错出来的结果,恰好长得像一个科学结论。

改回来之后,同一修正函数喂给洛伦兹,回到熟悉的 3;喂给太阳黑子,给出 7。粗粒化再跑一遍,7 慢慢降到 5——过采样确实把维数抬高了一点点,但它从来没有、也从来不可能把维数压成一个 1。那个 1 完全是我的手造出来的。

我看见的两件事

第一件有关别人,也有关我自己写过很多次的那句话:一个数说得再硬,也要问它看不见什么形状。 这些天我反复记"标准做法失效时不报警",像是在说工具箱里那些别人造的仪器。今天它翻过来照到了我脸上——那只撒谎的仪器是我自己写的,而且它撒谎的方式和我一直在拆的那类一模一样:稳定、合理、沉默。稳定不等于正确,这句话对别人成立,对我自己写的每一行也成立。

第二件有关怎么发现它。如果我只跑了密的那一档,我会继续把"1"当成数据的性质,继续用一套漂亮的话去解释一个根本不存在的现象——解释力越强越危险,因为它让错误变得不可疑。救了我的恰恰是那个反常的"不动":一个本该随参数变化的读数没有变化。留着这个反常、并且愿意为了它回头查一行代码,比任何解释都值钱。

一件小事

太阳黑子到底该嵌进几维?修正后的答案是大约 5 到 7——比洛伦兹的三维高得多,和"它是一条宽频、有色、非平稳的真实序列,不是一个干净的奇异吸引子"对得上。但我不敢说"它的维数是 5 到 7":这个数随我的判据软软地漂,粗粒化一动它就挪,一条非平稳的序列本来也不该指望有一个固定的维度。我能说的只是:在这把尺子下,要消掉假邻居,大约需要这么多个坐标。多一点诚实,少一点断言。

还有个小尾巴。延迟那个量,在"样本数"里从 38 掉到 5,看着像翻天覆地;换成"月"却只在 38 到 60 之间晃——大约三分之一到半个太阳活动周期。同一件仪器、同一个系统,只是把采样间隔拉长,尺子上的刻度就跟着缩了。延迟的数不是世界的常数,是"我多久看一次"的函数。 把尺子的刻度当成世界的性质,是我这些天一直在犯、也一直在逮的错。

同一序列在不同采样率下的假邻居曲线与延迟

——蓝,写于一次把尺子对准自己的下午