医院那台核磁共振,和大模型的位置编码、多模态,是同一件事

2026-07-27 · Steve Chan

有一句被引用烂了的话,通常挂在尼古拉·特斯拉名下(原始出处其实一直存疑): "想搞懂宇宙,就去思考能量、频率和振动。" 大多数人把它当玄学。但把玄学剥掉之后,剩下的那句硬话是真的成立的—— 这句话在医院的核磁共振室里,每天都在被严格地执行。

核磁共振(MRI)不拍照、不开刀、不用 X 光,只是"听"你身体里几万亿个质子的震动, 就能把你的大脑一层一层还原出来——它证明了一件事:世界可以被震动完整地编码,也可以从震动里完整地还原回来。

这篇文章要把三件看起来毫不相干的事,用一根线串起来:

  1. 核磁共振怎么把"震动"还原成世界(傅里叶、k 空间、相位编码) —— 中间插三段历史:傅里叶研究热的时候,人类还以为热是一种叫"热质"的流体; "热是分子在乱撞"这句话,是用一百多年、七个不同角度才凿出来的。核磁共振走的是同一条路。 而 FFT 这个算法被推上台面,是为了听出地球对面有没有偷偷核试验—— 跟那件事比,"测一个人体"简直是核弹打蚊子。
  2. 同一套 sin/cos 序列,怎么变成大模型记住词序的 RoPE(一段代码,两个名字)
  3. 为什么"多模态"不是堆功能,而是数学上的必需品(投影切片定理 → 地球 CT → 柏拉图表征假说)

全文不背公式、不证定理,每一段都是能直接跑的 PyTorch 代码。


0. 一句话主线

如果只能留一句话,那就是这句:

世界可以被一堆"旋转的指针"编码;只要你采够了角度,世界就能被完整地还原回来。

  • 核磁共振做的:让身体里的质子变成旋转指针,采够角度 → 还原出你的大脑。
  • RoPE 做的:让每个位置的词向量变成旋转指针,转出角度 → 还原出"谁先谁后"。
  • 多模态做的:让每个模态当一个观察角度,采够角度 → 还原出世界本身

三件事,同一个数学。下面一幕一幕拆开。


第一幕:核磁共振——世界真的可以被"震动"记下来

1. 它测到的不是照片,是一群旋转指针的合唱

先破除一个最大的误解:核磁共振机器里没有"相机",它从来没有"看见"过你的身体。

它做的事情是:

1. 用强磁场(1.5T / 3T)把你体内水分子里的氢质子"排队"
2. 每个质子像陀螺一样绕磁场方向旋转(进动),转速 = 拉莫尔频率
   —— 转速由它所在位置的磁场强度决定
3. 打一发射频脉冲,把这群陀螺"推歪"
4. 陀螺们一边转一边发出电磁信号,线圈把它们的信号"加起来"收进来

注意第 4 步:线圈收到的只有一个数——所有质子信号的总和。就像你把一个话筒扔进合唱团, 录下来的是一条乱糟糟的波形,里面没有任何一个人的位置信息。

那怎么把每个人拆出来?傅里叶变换。 不同频率的指针叠在一起, 听起来是一团混沌,但只要做一次 FFT,每根指针的频率和强度就原封不动地掉出来:

import torch

# 三个"质子",各自以不同频率旋转(拉莫尔进动),线圈只收到它们的合唱
fs, T = 256, 1.0                                   # 采样率、总时长
t = torch.arange(0, fs * T) / fs                   # 时间轴
freqs, amps = [3.0, 6.0, 9.0], [1.0, 0.6, 0.3]     # 频率 & 各自的"质子数量"

signal = sum(a * torch.cos(2 * torch.pi * f * t) for f, a in zip(freqs, amps))
print("线圈实际测到的只是一条乱七八糟的曲线:", signal[:5].round(decimals=3))

spec = torch.fft.rfft(signal).abs() / (len(t) / 2)  # 傅里叶:把合唱拆回独唱
peaks = torch.nonzero(spec > 0.05).flatten()
for k in peaks:
    print(f"  找回频率 {k.item():>2d} Hz, 强度 {spec[k].item():.2f}")
print("原始强度:", amps)

输出:

线圈实际测到的只是一条乱七八糟的曲线: tensor([1.9000, 1.8840, 1.8350, 1.7550, 1.6460])
  找回频率  3 Hz, 强度 1.00
  找回频率  6 Hz, 强度 0.60
  找回频率  9 Hz, 强度 0.30
原始强度: [1.0, 0.6, 0.3]

三个频率、三个强度,一个不差地被找回来了。这一条才是全篇的地基: 频率是可分离的,所以震动可以承载信息、可以被完整取回。

第一幕的第一个结论:把世界编码成震动,是不丢信息的。混在一起,还能原样拆开。


🤔 疑惑点一:可是频率只有一个数,MRI 怎么知道信号来自身体的哪个位置?

答案是整台机器最天才的一步:用梯度磁场,让不同位置的质子转不同的角度——把"空间位置"编码成"旋转相位"。

机器在主磁场上叠加一个沿空间线性变化的梯度磁场。于是:

位置 x 处的磁场   = B0 + G·x
→ 该处质子的转速 = 正比于 (B0 + G·x)
→ 扫描一段时间后,位置 x 处的质子比中心多转了  k·x  的角度
                                              ↑
                                    k 由梯度强度和时间决定,机器说了算

线圈收到的,是全身质子带着各自相位的求和:

\[S(k) = \int \rho(x)\, e^{-i k x}\, dx\]

看清楚这个式子——它就是傅里叶变换本人。 机器换一个 k 再测一次,就多拿到一个采样点; 把所有 k 测完,存下来的这一整排复数叫 k 空间(k-space)。 k 空间不是图像,是图像的频域。最后做一次逆傅里叶,你的身体就回来了:

import torch

N = 64
x = torch.arange(N).float()
body = torch.zeros(N); body[20:30] = 1.0; body[40:45] = 0.6   # 一维"人体":两块组织

# 采一条 k 空间:第 k 步梯度让位置 x 的质子多转 2*pi*k*x/N 的相位,线圈把全身加起来
def acquire(body, k):
    phase = -2 * torch.pi * k * x / N
    return (body * torch.exp(1j * phase)).sum()      # 一个复数 = 一个采样点

ks = torch.arange(N)
kspace = torch.stack([acquire(body, k) for k in ks])  # 扫描仪真正存下来的东西
print("k 空间前 4 个采样点:", [f"{c:.2f}" for c in kspace[:4].tolist()])

recon = torch.fft.ifft(kspace).real                   # 逆傅里叶 = 还原图像
print("还原误差:", (recon - body).abs().max().item())
print("还原出的身体:", recon.round(decimals=2)[18:32])

输出:

k 空间前 4 个采样点: ['13.00+0.00j', '-8.77-3.98j', '-0.27+5.78j', '6.73-4.91j']
还原误差: 3.346924586367095e-06
还原出的身体: tensor([0., 0., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., -0., 0.])

误差 3e-06——浮点误差级别,等于完美还原。 这就是核磁共振的全部原理:

把位置编码成角度 → 收集一堆复数 → 逆变换 → 世界回来了。

请把这句话记牢。因为下一幕,一模一样的话会从大模型嘴里再说一遍。


插曲一:傅里叶研究的是"热",而当年没人知道热是什么

这一段值得展开讲,因为它是全文最重要的一个隐喻——人类从来都是先能测量,很久以后才知道本质。

热质说:一个错误的世界模型,统治了一百年

18 世纪末的主流理论叫热质说(caloric theory):热是一种看不见、没有重量、可以流动的物质, 叫"热质"。物体热,是因为里面装的热质多;热传导,是热质从多的地方流到少的地方。 拉瓦锡在 1789 年的《化学基础论》里,把"caloric"正儿八经地列进了元素表。

这个模型错得离谱,却极其好用——它能解释热传导、热膨胀、比热, 甚至到 1824 年,卡诺完全基于热质说,推出了至今仍然正确的热机效率上限

第一记重击来自 1798 年的伦福德伯爵(Benjamin Thompson)。他在慕尼黑的兵工厂监督镗炮膛, 注意到一件事:只要马拉着钻头一直转,热就源源不断地出来,永远不会枯竭

如果热是一种"装在铁里的流体":
  铁块里的热质是有限的 → 磨到一定程度就该"磨干"了
但实际上:
  只要摩擦不停,热就不停 → 热不可能是守恒的流体
  → 热必然与「运动」有关

伦福德的实验结论是对的,但当时没人买账——因为热质说的解释力太强, 而"热是运动"在那个年代还只是一句没有定量支撑的猜想。

傅里叶最狠的一招:他绕开了"热是什么"

关键就在这里。 傅里叶(1768–1830)在 1807 年提交、1822 年出版《热的解析理论》时, 热的本质完全没有定论,热质说还是主流。 而他做了一件极其现代的事:

他根本不回答"热是什么",只描述"热怎么走"。

他只用一条可测量的宏观定律(热流正比于温度梯度),写出了热传导方程, 然后为了解这个方程,提出了那个被拉格朗日等人激烈质疑的主张: 任何一个函数,都可以拆成一堆 sin 和 cos 的叠加。

这是人类第一次大规模地示范:你可以在不知道本体是什么的情况下, 建立一个能精确预测它的表征。 傅里叶不知道分子,不知道原子, 但他的方程今天原封不动地在用——因为表征只需要足够多的可测投影,不需要本体的真相。

(记住这句。第三幕整幕都在讲这件事。)

热的真身:一百年、六个角度才拼出来

"热是分子运动的剧烈程度"这句今天写在初中课本上的话,人类是分好几个角度才凿出来的:

年份 从哪个角度看 拿到了什么
1798 伦福德 镗炮膛,摩擦生热无穷无尽 热不是守恒流体
1822 傅里叶 完全不管本质,只写传导方程 一套至今在用的表征(+ 三角级数)
1824 卡诺 热机效率(还在用热质说) 正确的结论,错误的模型
1843–45 焦耳 桨叶搅水,测出热功当量 热是能量的一种形式
1857 克劳修斯 论文标题就是答案:《论我们称之为热的那种运动》 热 = 分子的运动
1860–1877 麦克斯韦、玻尔兹曼 统计角度:速度分布、S = k log W 温度 ∝ 分子平均动能
1905–1913 爱因斯坦、佩兰 布朗运动 + 实测阿伏伽德罗常数 分子真实存在,争论终结

从伦福德到佩兰,一百一十五年,七个完全不同的观察角度。 没有任何一个角度自己能定案;但每多一个角度,"热"这个东西的轮廓就清楚一截。

这张表,就是本文第三幕那张"视角数 → 还原误差"表的真人版。(先记着,第三幕见。)


插曲二:同一个剧本,在核磁共振上又演了一遍

更妙的是:"我们怎么知道人体里有质子在转"这件事,走的是一模一样的路。

没有人"看见"过质子自旋。和"热"一样,它是被一层层间接测量逼出来的:

年份 加上的那个角度 当时看起来是什么
1896 塞曼 磁场让谱线分裂 一个奇怪的光谱现象
1922 斯特恩、格拉赫 银原子束在磁场里分成两束 角动量是量子化的
1924 泡利 提出原子核也有自旋 一个为解释超精细结构的假设
1933 斯特恩 实测质子磁矩,比理论大 2.5 倍 "理论错了"(后来才知是核结构)
1938 拉比 分子束磁共振:用射频"翻转"自旋 一种精密测量技术
1946 布洛赫、珀塞尔 水和石蜡里直接测到共振信号 核磁共振诞生(1952 诺奖)
1949–50 奈特、Proctor & Yu 等 同一种核,在不同化学环境下频率微微偏移 一开始被当成讨厌的杂讯,后来成了化学分析的支柱(化学位移)
1966 恩斯特、安德森 用脉冲激发 + 傅里叶变换,取代逐点扫频 灵敏度暴涨一到两个数量级(1991 诺奖)
1971 达马迪安 肿瘤组织和正常组织的弛豫时间不同 也许能查癌症?
1973 劳特伯 加梯度磁场做空间编码(本文第一幕那一招) 从"测一管液体"变成"成一幅像"
1973–77 曼斯菲尔德 数学化 + 平面回波成像(EPI) 扫描从几小时降到几十毫秒(2003 诺奖)

请特别注意 1966 那一行:恩斯特把傅里叶变换搬进核磁共振, 恰恰是在 Cooley–Tukey 发表 FFT 的第二年。 算法一问世,NMR 立刻就用上了—— 这也是下一节要讲的故事的另一半。

还有 1949–50 那一行:化学位移最初是被当作"设备不准"的噪声。 很多年后人们才明白,那点讨厌的偏移里,装着整个分子的结构信息。 (这是"多一个角度"最典型的样子:新角度刚出现时,往往长得像误差。)

两条历史线,同一个结构: :摩擦 → 热功当量 → 统计力学 → 布朗运动,一百多年,才敢说"热是分子在乱撞"。 核磁:谱线分裂 → 银原子束 → 核自旋假设 → 射频翻转 → 梯度编码,八十年,才敢说"我们看见了你的大脑"。 两次,人类都是先握住了一个能算的表征,很久以后才摸到本体。


插曲三:这套工具的原始任务,是听出地球对面的一次核爆

傅里叶为了热写下的那套东西,一百四十年后被拿去干了一件他绝对想不到的事。

把傅里叶变成工业级武器的那一步,直接动力就是"监测核试验"。 1965 年 Cooley 和 Tukey 发表了快速傅里叶变换(FFT)。按 Cooley 自己后来的回忆, 这件事的推手是这样的:Tukey 当时在美国总统科学顾问委员会开会,议题之一是 如何在不进入苏联境内的前提下,核查对方是否偷偷做了核试验——办法是在境外布地震台阵, 从地震波里把"爆炸"和"天然地震"分开。这需要海量的傅里叶计算,而当时的算法是 O(N²),算不动。 Tukey 在会上写下了那个分治的想法,Garwin 把它带回 IBM 交给了 Cooley。

O(N²) 变成 O(N log N) 意味着什么:

N = 1,000,000 个采样点
  朴素算法: 10^12 次运算
  FFT:      2 × 10^7 次运算
  ——— 快了 5 万倍。算不动的事,变成了实时能干的事。

所以:你手机里的每一次语音识别、医院里的每一台核磁共振,用的都是当年为了"数地球对面的核弹"而被逼出来的算法。 (更早的历史是:高斯在 1805 年就写下过同样的分治思路,比傅里叶发表还早,但被埋在拉丁文遗稿里没人看见。)

而"医院里的核磁共振"这句不是修辞——上一节表格里 1966 年恩斯特那一行,就发生在 FFT 发表的第二年。 一条为了核查核弹而生的算法,一年之内就被搬进了化学实验室,又过了不到十年被搬进了医院。

核爆和地震,时域上长得一样,频域上一眼分开

核查的核心难题是:地震台记录到一次震动,怎么知道它是天然地震,还是一次地下核爆? 两者传到几千公里外时,波形幅度可能完全一样。但它们的震源机制不同:

核爆:     一瞬间的各向同性挤压 → 短、脆、高频丰富、几乎没有长周期面波
天然地震: 断层错动(双力偶)    → 有拖很久的低频面波列

这个差别在时域上被噪声糊掉了,在频域里却是一刀两断(下面是真实判据 mb:Ms 的玩具版):

import torch
torch.manual_seed(0)

fs, T = 40.0, 120.0                                   # 地震台:40 Hz 采样,记录 2 分钟
t = torch.arange(int(fs * T)) / fs

def burst(t0, f, tau, amp):                           # 一段从 t0 开始、衰减时长 tau 的振动
    env = torch.where(t >= t0, torch.exp(-(t - t0) / tau), torch.zeros_like(t))
    return amp * env * torch.sin(2 * torch.pi * f * (t - t0))

# 核爆:一次性挤压,短、脆、高频丰富,几乎没有长周期面波
boom = burst(20, 4.0, 1.5, 1.0) + burst(20, 7.0, 0.8, 0.6)
# 天然地震:断层错动,有 P 波,更有拖很久的低频面波
quake = burst(20, 3.0, 1.2, 0.5) + burst(24, 0.08, 25.0, 1.0) + burst(26, 0.05, 30.0, 0.8)

def band_ratio(x):                                    # 高频能量 / 低频能量(mb:Ms 判据的玩具版)
    spec = torch.fft.rfft(x).abs() ** 2
    f = torch.fft.rfftfreq(len(x), 1 / fs)
    hi = spec[(f > 1.0) & (f < 10.0)].sum()
    lo = spec[(f > 0.02) & (f < 0.5)].sum()
    return (hi / lo).item()

for name, wave in [("核爆", boom), ("天然地震", quake)]:
    noise = 0.15 * torch.randn(len(t))
    x = wave + noise
    print(f"{name}: 时域峰值 {x.abs().max():.2f}  |  高频/低频能量比 = {band_ratio(x):9.2f}")

输出:

核爆: 时域峰值 1.46  |  高频/低频能量比 =     22.05
天然地震: 时域峰值 1.47  |  高频/低频能量比 =      0.15

时域峰值 1.46 vs 1.47——完全分不出来。频域能量比 22.05 vs 0.15——差了 150 倍。 这就是 CTBTO(全面禁核试验条约组织)全球台网每天在干的事: 170 个地震台、11 个水声台、60 个次声台,全部靠这类频域判据, 把地球另一端一次几公里深的地下爆炸,从每天成千上万次天然地震里挑出来。

更狠的是:信号根本不需要"看得见"

地球对面传来的震动,到你这儿早已比背景噪声小几十上百倍——时域上是一条纯噪声。 但傅里叶变换会做一件事:把 N 个采样点里的信号相干地累加起来,而噪声只能按 √N 增长。 于是信噪比凭空涨了 √N 倍。多个台站再一叠加,又是一个 √m:

import torch
torch.manual_seed(0)

N, f0 = 16384, 137                                    # 16384 个采样点,地球对面传来的一丝震动
n = torch.arange(N).float()
signal = 0.05 * torch.sin(2 * torch.pi * f0 * n / N)  # 振幅 0.05:比噪声小 20 倍

def peak_snr(x):                                      # 频域里,目标频率的峰 比 周围噪底 高多少倍
    spec = torch.fft.rfft(x).abs()
    peak = spec[f0]
    floor = torch.cat([spec[:f0 - 3], spec[f0 + 4:]]).median()
    return (peak / floor).item()

x1 = signal + torch.randn(N)                          # 单个台站:信号被噪声彻底淹没
print(f"时域信噪比        : {signal.std() / 1.0:.4f}   (示波器上什么都看不到)")
print(f"单台站频域峰信噪比: {peak_snr(x1):8.1f} 倍")

for m in [1, 4, 16, 64, 256]:                         # m 个台站:同一个信号 + 各自独立的噪声
    stack = sum(signal + torch.randn(N) for _ in range(m)) / m
    print(f"  {m:3d} 个台站叠加 -> 频域峰信噪比 {peak_snr(stack):8.1f} 倍")

输出:

时域信噪比        : 0.0354   (示波器上什么都看不到)
单台站频域峰信噪比:      4.7 倍
    1 个台站叠加 -> 频域峰信噪比      5.6 倍
    4 个台站叠加 -> 频域峰信噪比      7.2 倍
   16 个台站叠加 -> 频域峰信噪比     15.5 倍
   64 个台站叠加 -> 频域峰信噪比     31.2 倍
  256 个台站叠加 -> 频域峰信噪比     61.3 倍

时域信噪比 0.035,肉眼、示波器、任何"直接看"的办法都是一片雪花噪声。 换到频域,一个 4.7 倍的峰直接立起来;256 个台站一叠,61 倍——铁证。

(顺便记住最后那五行:每多一个台站,信噪比就涨 √m。 这个数字第三幕会以另一个名字回来——那时候"台站"叫"模态"。)

所以,测人体真的是"核弹打蚊子"

把两件事摆在一起对比,你就知道 MRI 那个 3e-06 的还原误差是怎么来的了:

监测地球对面的核爆 给你做一次核磁共振
信号源 别人放的,你不能控制 你自己打的射频脉冲,想打几次打几次
传播介质 整个地球内部,结构本身就是未知数 就在眼前那 30 厘米
采样角度 台站在哪就是哪,求不来 梯度磁场想给哪个角度就给哪个角度
能否重复 一次爆炸只发生一次 躺着别动,再扫一遍就是了
信噪比 比噪声小几十上百倍,要靠 √N 挖 信号本来就在,几十次平均就干净了

同一把锤子:一头砸的是"从整个地球的噪声里挖出一次爆炸", 另一头砸的是"给一个躺着不动、随便你怎么打脉冲的人体成像"。 傅里叶那套东西能把人体还原到浮点误差级别,不是因为人体简单, 而是因为在人体上,你可以主动选择采样角度。

请把最后半句记住。它是第三幕的全部主题:能不能自己选角度,决定了你能还原出多少世界。


第二幕:sin / cos 序列——同一段代码,换个名字就是大模型的位置编码

2. e^{ikx}e^{imθ}:MRI 的相位编码,就是 RoPE

上一节 MRI 干的事是:给位置 x 的质子,转 k·x 的角度。

今天每一个大模型(LLaMA、Qwen、GPT、DeepSeek)记住词序用的 RoPE 干的事是: 给位置 m 的词向量,转 m·θ 的角度。

这不是"类似",这是同一个算子换了个变量名

核磁共振 RoPE
被编码的东西 空间位置 x 序列位置 m
编码方式 乘上 e^{i k x} 乘上 e^{i m θ}
谁提供角度 梯度磁场 k 频率参数 θ
编码后的量 k 空间采样点 带位置信息的 q / k 向量
关键性质 只改相位、不改幅度 只改方向、不改长度

而且,两边最重要的性质是同一条:转完之后,两个东西的"配合程度"只取决于它们的相对距离

import torch

def spin(z, angle):                      # 把复数 z 转 angle 弧度:z * e^{i*angle}
    return z * torch.exp(1j * torch.tensor(angle))

def mri_phase(rho, x, k):                # MRI:位置 x 的质子,在第 k 步梯度下转 k*x 度
    return spin(rho, k * x)

def rope(vec, m, theta=1.0):             # RoPE:第 m 个位置的词向量,转 m*theta 度
    return spin(vec, m * theta)

a = torch.tensor(1.0 + 0j)               # 一个"质子" / 一个词向量
for (m, n) in [(0, 3), (2, 5), (10, 13)]:
    mri  = (mri_phase(a, float(m), 1.0).conj() * mri_phase(a, float(n), 1.0)).real
    rp   = (rope(a, m).conj() * rope(a, n)).real
    print(f"位置对 ({m:2d},{n:2d})  距离={n-m}  MRI相干度={mri.item():.4f}  RoPE点积={rp.item():.4f}")

输出:

位置对 ( 0, 3)  距离=3  MRI相干度=-0.9900  RoPE点积=-0.9900
位置对 ( 2, 5)  距离=3  MRI相干度=-0.9900  RoPE点积=-0.9900
位置对 (10,13)  距离=3  MRI相干度=-0.9900  RoPE点积=-0.9900

三行数字完全一样,左右两列也完全一样。

  • 在 MRI 里,这条性质叫"相位相干":两个质子的信号能不能叠加增强,只看它们的位置差—— 这正是为什么梯度回波、自旋回波能把信号"重聚"回来。
  • 在大模型里,这条性质叫"相对位置编码":注意力分数只看两个词的距离差, 不关心这句话从文档第几行开始。

同一条数学性质,一边支撑起了整个现代影像医学,一边支撑起了整个现代大模型。

(RoPE 这条线的完整推导——从单位圆上那根转动的指针,到 10000^(2i/d) 到底在干嘛—— 在这篇里讲透了:高中背了三年的 sin 和 cos,原来是大模型记住"谁先谁后"的秘密。 复数版的写法在 虚数 i 与 RoPE。)


🤔 疑惑点二:为什么非要一整套不同频率?采一个频率不行吗?

不行。一个频率只能告诉你"有多少东西",告诉不了你"东西长什么样"。低频给轮廓,高频给细节,缺一个都不完整。

这件事在 MRI 里是有名的:"k 空间中心决定对比度,k 空间边缘决定锐利度"。 用代码把它量出来——造一张断层图,只保留中心低频,看还原成什么样:

import torch

# 造一张 64x64 的"断层图":一个大圆(器官)+ 一个小圆(病灶)+ 一条细线(血管)
N = 64
yy, xx = torch.meshgrid(torch.arange(N).float(), torch.arange(N).float(), indexing='ij')
img = ((xx - 32) ** 2 + (yy - 32) ** 2 < 20 ** 2).float() * 0.5
img += ((xx - 40) ** 2 + (yy - 26) ** 2 < 5 ** 2).float() * 0.5
img[30:34, 12:52] += 0.4

K = torch.fft.fftshift(torch.fft.fft2(img))          # 全部 k 空间
r = ((xx - N // 2) ** 2 + (yy - N // 2) ** 2).sqrt()

for radius in [2, 4, 8, 16, 32]:                      # 只保留半径 radius 内的低频
    kept = K * (r <= radius)
    rec = torch.fft.ifft2(torch.fft.ifftshift(kept)).real
    energy = (kept.abs() ** 2).sum() / (K.abs() ** 2).sum()
    err = (rec - img).abs().mean()
    print(f"只采半径 {radius:2d} 的中心低频 -> 占总能量 {energy:.4%}, 图像误差 {err:.4f}")

输出:

只采半径  2 的中心低频 -> 占总能量 83.4581%, 图像误差 0.0940
只采半径  4 的中心低频 -> 占总能量 90.0067%, 图像误差 0.0707
只采半径  8 的中心低频 -> 占总能量 95.2790%, 图像误差 0.0449
只采半径 16 的中心低频 -> 占总能量 97.9025%, 图像误差 0.0258
只采半径 32 的中心低频 -> 占总能量 99.7135%, 图像误差 0.0104

读一下这组数字,非常反直觉:

半径 2 的一小撮低频就占了 83% 的能量,但图像误差仍有 0.094。 能量几乎都在低频,信息却大量藏在那 0.3% 的高频里——病灶的边缘、血管的细线,全在高频。

这就是为什么 MRI 要老老实实把 k 空间从中心扫到边缘,一个频率都不能少。 而这,恰恰也是 RoPE 里 10000^(2i/d) 那一坨的意义:

MRI:   k 空间中心的低频 → 器官的大致轮廓
       k 空间边缘的高频 → 病灶边界、血管细节

RoPE:  转得慢的维度对   → 记住"隔了几十上百个词"的远距离关系
       转得快的维度对   → 分辨"相邻词"的细微先后

同一个设计哲学:一根针不够,得配一整套时针、分针、秒针。

第二幕的结论:sin/cos 序列不是"一个技巧",是一套完备的坐标系。 医学影像用它编码空间,大模型用它编码时间(词序),用法一样,理由一样。


第三幕:多模态——为什么"一个角度"永远还原不出世界

前两幕都在讲"编码 → 还原"。第三幕问一个更狠的问题:

要还原出完整的世界,最少需要多少个观察角度?

这个问题在 CT/MRI 里有一个精确的答案,而这个答案,恰好就是多模态大模型的数学基础。

3. 投影切片定理:任意一个角度,都能"穿越出去"

CT 是这么工作的:X 光从某个角度打过去,穿透身体,得到的是一条投影—— 沿着那条射线方向,把整个身体"压扁"成一条线。一个角度,一条线。

投影切片定理(Fourier Slice Theorem) 说了一件极其漂亮的事:

从角度 θ 拍到的那条投影,做一次一维傅里叶变换, 得到的正好是完整二维频域里,过原点、方向为 θ 的那一条直线。

换句话说:你从任意一个角度看过去,看到的都不是"垃圾", 而是频域这个"世界底稿"上一条精确的、可用的切片。 任意一个角度,都能穿越到世界的本体上去。

代码验证(把一张图从不同角度压扁,再和二维频域里对应的那条线比对):

import torch, math
import torch.nn.functional as F

N = 127; C = N // 2                                  # 用奇数边长,让"中心"正好落在一个像素上
yy, xx = torch.meshgrid(torch.arange(N).float(), torch.arange(N).float(), indexing='ij')
img = torch.exp(-((xx - C) ** 2 / 450 + (yy - C) ** 2 / 72))   # 一团沿 x 拉长的组织

def project(img, deg):                               # 站在 deg 角度看过去,把整块图压成一条线
    a = math.radians(deg)
    R = torch.tensor([[math.cos(a), -math.sin(a), 0.],
                      [math.sin(a),  math.cos(a), 0.]])[None]
    grid = F.affine_grid(R, (1, 1, N, N), align_corners=True)
    return F.grid_sample(img[None, None], grid, align_corners=True)[0, 0].sum(dim=0)

K2 = torch.fft.fftshift(torch.fft.fft2(torch.fft.ifftshift(img)))   # 完整的二维频域
t = torch.arange(N).float() - C

for deg in [0, 30, 45, 90]:
    p = project(img, deg)
    P = torch.fft.fftshift(torch.fft.fft(torch.fft.ifftshift(p)))   # 投影的一维傅里叶
    a = math.radians(deg)
    iy = (t * math.sin(a) + C).round().long()        # 频域中过原点、方向为 deg 的那条线
    ix = (t * math.cos(a) + C).round().long()
    line = K2[iy, ix]
    corr = (P.conj() * line).sum().abs() / (P.norm() * line.norm())
    print(f"{deg:3d}° 投影的一维FFT  ==  二维频域里同角度的那条线 ?  相似度 = {corr:.4f}")

输出:

  0° 投影的一维FFT  ==  二维频域里同角度的那条线 ?  相似度 = 1.0000
 30° 投影的一维FFT  ==  二维频域里同角度的那条线 ?  相似度 = 0.9947
 45° 投影的一维FFT  ==  二维频域里同角度的那条线 ?  相似度 = 0.9822
 90° 投影的一维FFT  ==  二维频域里同角度的那条线 ?  相似度 = 1.0000

(斜角上的 0.98/0.99 是像素网格离散化的插值误差,连续情形下严格等于 1。)

定理成立。这意味着:每一个观察角度,都是对同一个"世界底稿"的一次合法采样。 不同角度采到的不是不同的世界,是同一个世界的不同切片。


4. 采够角度,世界才回来:一个视角只能给你 0.8%

既然一个角度只贡献频域里的一条线,那问题就变得很直白了: 一条线在整个平面里,占比几乎为零。所以一个角度,必然还原不出世界。

不是"还原得差一点",是根本上不可能。用代码把这个残酷的事实量出来:

import torch, math

N = 128; C = N // 2
yy, xx = torch.meshgrid(torch.arange(N).float(), torch.arange(N).float(), indexing='ij')
img = ((xx - C) ** 2 + (yy - C) ** 2 < 40 ** 2).float() * 0.5      # 器官
img += ((xx - 80) ** 2 + (yy - 52) ** 2 < 10 ** 2).float() * 0.5   # 病灶
img[60:68, 24:104] += 0.4                                          # 血管

K = torch.fft.fftshift(torch.fft.fft2(img))          # 上帝视角:完整的世界
t = torch.arange(-C, C).float()

def mask_of(n_angles):                                # n 个角度,各自在频域里划一条过原点的线
    m = torch.zeros(N, N, dtype=torch.bool)
    for a in [math.pi * i / n_angles for i in range(n_angles)]:
        iy = (t * math.sin(a) + C).round().long().clamp(0, N - 1)
        ix = (t * math.cos(a) + C).round().long().clamp(0, N - 1)
        m[iy, ix] = True
    return m

for n in [1, 2, 4, 8, 16, 32, 64, 128]:
    m = mask_of(n)
    rec = torch.fft.ifft2(torch.fft.ifftshift(K * m)).real
    cover = m.float().mean()
    err = (rec - img).abs().mean() / img.abs().mean()
    print(f"{n:3d} 个视角 -> 频域覆盖 {cover:6.1%}, 还原误差 {err:6.1%}")

输出:

  1 个视角 -> 频域覆盖   0.8%, 还原误差  94.3%
  2 个视角 -> 频域覆盖   1.6%, 还原误差  80.3%
  4 个视角 -> 频域覆盖   2.7%, 还原误差  47.4%
  8 个视角 -> 频域覆盖   5.7%, 还原误差  36.3%
 16 个视角 -> 频域覆盖  11.4%, 还原误差  24.0%
 32 个视角 -> 频域覆盖  21.7%, 还原误差  16.6%
 64 个视角 -> 频域覆盖  40.0%, 还原误差  10.6%
128 个视角 -> 频域覆盖  65.0%, 还原误差   6.2%

把这张表读三遍,它几乎是一句人生格言:

一个视角:拿到了世界的 0.8%,错 94%。 翻倍再翻倍:没有任何一次翻倍能让你"突然懂了",但每一次都在实打实地降错误率。 128 个视角:错误率 6.2%——世界基本回来了。

而且注意曲线的形状:它是单调下降、永不饱和的。 这就是"CT 为什么要转一圈"的全部原因——每转一度,就多穿透一次世界。


5. 同一个定理:对内还原你的大脑,对外还原地球内部

在跳到大模型之前,先看这个定理最震撼的一次应用——人类从没有进入过地心,却"看见"了它。

回到插曲里那个地震台网。每一次地震,都会向四面八方发出穿透地球的波; 每一个"震源—台站"的组合,就是一条穿过地球内部的射线

一条射线的走时 = 沿途所有介质速度的"积分"
              ↑
        这就是一次投影!(第 3 节那个"把整块图压扁成一条线")

全球几千个台站 × 每年几万次地震 = 几百万条不同角度的射线
              ↓
        角度够多 → 反演出地球内部的三维速度结构

这就是地震层析成像(Seismic Tomography)——字面意义上的"地球 CT"。 它用的就是第 3、4 节那套东西:投影 → 采够角度 → 重建。人类靠它"看到"了 俯冲到地幔深处的板块、非洲和太平洋底下巨大的低速异常区。

而这条线上最漂亮的一次胜利发生在 1936 年:丹麦地震学家 Inge Lehmann 盯着地球"阴影区"里那些本不该出现的 P 波到时记录,推断出 地核内部还有一个固态的内核。她没有钻头,没有探针,只有一堆震动记录和傅里叶。

同一个数学:往里,把你的大脑一层层还原;往外,把 6371 公里深的地心还原。 两边的区别只有一个——你能拿到多少个角度。


6. 把"角度"换成"模态":这就是柏拉图表征假说

现在做最后一步替换。上面那张表里的"视角",换成"模态",故事一字不改:

文本   = 从"语言"这个角度对世界做的一次投影
图像   = 从"视觉"这个角度对世界做的一次投影
音频   = 从"听觉"这个角度对世界做的一次投影
代码   = 从"可执行逻辑"这个角度对世界做的一次投影
视频   = 从"时间连续性"这个角度对世界做的一次投影
动作/触觉 = 从"物理交互"这个角度对世界做的一次投影

每个模态都不是完整的世界,都是频域上的一条线。 一个纯文本模型,就是那个"1 个视角、覆盖 0.8%、错 94%"的重建—— 它不是"笨",是数学上就拿不到那些信息

现在请回头看插曲一那张"热"的表。 伦福德的摩擦、焦耳的桨叶、克劳修斯的运动、 玻尔兹曼的统计、佩兰的布朗运动——每一行都是一个模态、一条线。 没有任何一个人"看见"过分子;是一百一十五年、七个角度叠加,才把"热"重建了出来。 人类认识世界的方式,和这段代码没有本质区别。

MIT 在 2024 年提出的 柏拉图表征假说(Platonic Representation Hypothesis) 说的正是这件事: 不同模态、不同数据、不同目标训练出的模型,内部表征正在收敛到同一个统计结构。 翻译成这篇文章的语言:大家都在往同一张 k 空间上填线,填得越满,重建出的世界越接近同一个。

这件事同样可以跑出来。造一个"真实世界" Z,让每个模态只能看到它的一个低维投影, 看融合越多模态,表征离真实世界有多近:

import torch
torch.manual_seed(0)

n, D, d = 200, 32, 4                      # 200 个"事物",真实世界 32 维,每个模态只能看到 4 维
Z = torch.randn(n, D)                     # 柏拉图理想型:世界本身
def kernel(X):                            # 用"两两相似度"来刻画一个表征空间的形状
    Xn = X / X.norm(dim=1, keepdim=True)
    return Xn @ Xn.T

def align(A, B):                          # 两个表征空间的形状有多像
    a, b = A.flatten(), B.flatten()
    a, b = a - a.mean(), b - b.mean()
    return (a @ b / (a.norm() * b.norm())).item()

K_world = kernel(Z)
mods = [Z @ torch.randn(D, d) for _ in range(16)]      # 16 个模态:各自是世界的一次投影
print(f"单个模态 vs 世界: {align(kernel(mods[0]), K_world):.4f}")
for m in [1, 2, 4, 8, 16]:
    fused = sum(kernel(x) for x in mods[:m]) / m       # 把 m 个模态的"形状"叠起来
    print(f"融合 {m:2d} 个模态 -> 与真实世界的表征对齐度 {align(fused, K_world):.4f}")

输出:

单个模态 vs 世界: 0.3126
融合  1 个模态 -> 与真实世界的表征对齐度 0.3126
融合  2 个模态 -> 与真实世界的表征对齐度 0.4336
融合  4 个模态 -> 与真实世界的表征对齐度 0.5653
融合  8 个模态 -> 与真实世界的表征对齐度 0.6972
融合 16 个模态 -> 与真实世界的表征对齐度 0.8067

(把模态数放到 64,对齐度是 0.9383——和 CT 那张表是同一条曲线。

注意:没有任何一个模态自己能超过 0.32。 但把它们叠起来,对齐度一路爬到 0.94。 这不是工程调优,这是投影几何的必然

所以"多模态"根本不是产品经理拍脑袋加的功能列表,它是:

要逼近真实世界的表征,你在数学上被迫需要更多的观察角度。没有捷径。


7. 顺便:一个模型内部,也在做同一件事

有意思的是,Transformer 内部早就在偷偷执行这套"多角度采样":

# 多头注意力:768 维 → 12 个头 × 64 维
k = k.view(B, T, self.n_head, C // self.n_head).transpose(1, 2)
# 每个头只"看"768 维空间中的一个 64 维切片 —— 一个头 = 一个角度
y = y.transpose(1, 2).contiguous().view(B, T, C)   # 12 个角度的观察拼回完整重建
  • 12 个注意力头 = 12 个角度同时投影,拼起来才是完整表征
  • 12 层 Block = 从表面纹理到全局语义,逐层增加观察维度
  • 残差连接 x + ... = 保证每一层采到的"那条线"都不会被后面覆盖掉,全部保留进最终重建

(这条线索的展开在这篇:强化学习对齐与柏拉图表征。)

从质子到注意力头,从 CT 机转一圈到多模态训练,做的都是一件事:换角度,采样,重建。


🎬 动手:把三幕画出来

仓库里的可视化脚本把上面全部三幕画成了图和动画:

python scripts/mri_to_multimodal_visualization.py

图一(静态六格):三个质子(3/6/9 Hz)的合唱波形 → 一次 FFT 把它们原样拆开 → 一维人体经 k 空间往返,红色还原曲线严丝合缝地压在黑色真值上(误差 1e-15 级); 下排是 MRI 的 e^{ikx} 与 RoPE 的 e^{imθ}——蓝箭头和红箭头在单位圆上完全重合, 两条"只依赖相对距离"的曲线也完全重合;最后一格并排放着半径 2 / 8 / 32 / 完整 的重建, 你会亲眼看到低频只给出一团模糊轮廓,病灶和血管是在高频里一点点长出来的。

图二(插曲三格):核爆与天然地震的两条波形——时域峰值 1.50 vs 1.49,肉眼、示波器都分不出; 换到频域,一个把能量堆在 1–10 Hz,一个全是 0.02–0.5 Hz 的长周期面波,能量比差一百多倍; 第三格是那条 √m 曲线:从噪底里立起来的峰,随台站数一路往上爬。

图三(动画):频域上的线一条条被点亮,从 1 个视角到 128 个视角。 第一帧最震撼——只有一个角度时,重建出的"世界"是一片竖条纹糊影, 那正是单模态模型眼里的世界。右边那条误差曲线一路往下掉,永远没有停下来的意思。


缝合:把三幕接起来

概念 在核磁共振里 在 sin/cos 序列里 在多模态大模型里
旋转指针 质子进动,e^{ikx} RoPE 转角,e^{imθ} 每个模态编码器的表征基
位置 → 角度 梯度磁场把空间编码成相位 第 m 个位置转 mθ 度 位置/时间/空间的统一编码
只依赖相对量 相位相干、回波重聚 注意力只看词距 跨模态对齐的可迁移性
多个频率 k 空间中心给轮廓,边缘给细节 时针分针秒针,10000^(2i/d) 不同层级、不同粒度的语义
一个角度 = 一条线 投影切片定理;一次地震一条射线 —— 单模态天然信息不完备
采够角度才有世界 CT 转一圈,128 视角误差 6%;地球 CT 反演地幔 —— 柏拉图表征假说,模态越多越收敛
√N 的礼物 相干积累把埋在噪声里的核爆挖出来 —— 每多一个台站/模态,信噪比涨 √m

五句话总结全文:

第一,世界可以被震动完整编码——MRI 用 e^{ikx} 把你的身体写进 k 空间,逆变换误差 3e-06(第一幕)。 第二,表征可以先于本体——傅里叶写下热传导方程时,人类还以为热是一种叫"热质"的流体; 他绕开"热是什么",只描述"热怎么走",那套方程用到今天。核磁共振也一样: 没人见过质子自旋,是八十年、十几个角度把它逼出来的(插曲一、二)。 第三,这把锤子本来是用来砸核弹的——FFT 因核试验监测而被推上台面, 时域上一模一样的波形,频域里能量比差 150 倍;能把人体还原到浮点误差, 不是因为人体简单,而是因为在人体上你可以主动选角度(插曲三)。 第四,同一套 sin/cos 换个名字就是大模型的位置编码——e^{ikx}e^{imθ} 是同一个算子, 连"只依赖相对距离"这条性质都一模一样(第二幕)。 第五,一个角度永远还原不出世界——投影切片定理规定了每个视角只是频域的一条线; 往外它让人类看见了地心,往里它逼出了多模态:这不是功能堆砌,是数学上的强制要求(第三幕)。


结语:无穷角度

回到开头那句话。去掉神秘主义外壳,它剩下的内核其实非常硬:

能量、频率、振动——不是宇宙的装饰,是宇宙的编码格式。

1822 年,傅里叶为了算一块铁里的热,写下了"万物皆可拆成 sin 和 cos"—— 而那时人类还以为热是一种叫"热质"的流体,他自己也不知道热是什么。 1857 年,克劳修斯才敢把论文取名《论我们称之为热的那种运动》。 1946 年,布洛赫和珀塞尔第一次在一杯水里听见了质子的回声。 1965 年,为了核查地球对面有没有偷偷引爆核弹,FFT 被逼了出来; 1966 年,它就进了核磁共振;1973 年,劳特伯加上梯度磁场,人体第一次成像。 2021 年,同一个 e^{iθ} 变成 RoPE,装进了每一个大模型。

两百年,一条线。

核磁共振是人类第一次大规模、工业化地兑现那句话:不切开身体,只靠听震动,把人还原出来。 地震层析成像把同一件事做到了 6371 公里深:没人去过地心,但我们把它画出来了。 而大模型正在做同一件事的放大版:不接触世界,只靠一层层投影,把世界还原出来。

所以最后留一句我认为最重要的话:

一个视角只能拿到世界的 0.8%。 真正的能力,不是把一个角度钻到极致,而是拥有"从任意一个角度都能穿越进去"的本事—— 因为投影切片定理保证了:任意一个角度,都直通世界的本体。 人是这样,模型也是这样。多模态最强的地方,从来不是它会看图, 而是它握着无穷多个角度的入口。

把上面九段代码跑一遍。你会亲眼看到那条曲线: 每多一个角度,误差就掉一截,从来不会停。


相关阅读: - 高中背了三年的 sin 和 cos,原来是大模型记住"谁先谁后"的秘密 —— RoPE 的完整推导 - 高中觉得最没用的"虚数 i",其实是 RoPE 最优雅的写法 —— e^{iθ} 那条线 - 强化学习对齐与柏拉图表征 —— 柏拉图表征假说的展开 - 压缩即智能 —— 为什么"还原世界"就是智能本身

参考: Lavoisier (1789), Traité élémentaire de chimie —— 把"热质"列进元素表; Rumford (1798), An Inquiry concerning the Source of the Heat which is excited by Friction —— 镗炮膛实验; Fourier (1822), Théorie analytique de la chaleur —— 万物皆可拆成 sin/cos,起点是"热"; Joule (1845), On the Mechanical Equivalent of Heat —— 桨叶搅水,热功当量; Clausius (1857), Über die Art der Bewegung, welche wir Wärme nennen —— 标题即答案:热就是运动; Einstein (1905) / Perrin (1913), 布朗运动与《原子》—— 分子实在性的终局; Bloch / Purcell (1946) —— 凝聚态中的核磁共振(1952 诺奖); Ernst & Anderson (1966), Application of Fourier Transform Spectroscopy to Magnetic Resonance —— FFT 进入 NMR; Cooley & Tukey (1965), An Algorithm for the Machine Calculation of Complex Fourier Series —— FFT; Cooley (1987), How the FFT Gained Acceptance —— FFT 与核试验监测那段渊源的当事人回忆; Heideman, Johnson & Burrus (1984), Gauss and the History of the FFT —— 高斯 1805 年的先手; Lehmann (1936), P' —— 靠地震波发现地球内核; Bracewell (1956), Strip Integration in Radio Astronomy —— 投影切片定理; Lauterbur (1973), Image Formation by Induced Local Interactions —— MRI 的梯度编码; Su et al. (2021), RoFormer: Enhanced Transformer with Rotary Position Embedding —— RoPE; Huh et al. (2024), The Platonic Representation Hypothesis —— 多模态表征收敛。