前几天 arXiv 上挂出一篇 MultiPathFormer(2608.05076,Guda、Sze、Joe-Wong 三个人写的),干的事一句话能说清:把无线信道里每条传播路径当成一个 token,把发射器到接收器之间所有路径的序列当成一句话,然后拿 GPT 预测下一个词的那套办法,去预测下一条路径。预训练任务就是 next path prediction。
我第一反应是:这不就是把 next-token prediction 搬到无线领域嘛,换皮。第二反应是:等等,这换皮换得比我想的讲究——它的 token 不是离散的,是连续值。延迟、功率这些路径统计量是实数,硬塞进“词表”那一套是行不通的。
所以这篇我不复述论文(论文自己写得挺清楚,去看原文),我干造轮匠的老本行:把“预测下一条路径”这个预训练任务,搓一个能跑的最小版本出来。我觉得这篇论文真正值得普通搞 ML 的人带走的,不是那 59% 的性能提升,而是一个直觉——只要你能把你的领域对象排成一个有序序列,next-token prediction 那台机器就能直接开动。这个直觉光读论文读不踏实,搓一遍才踏实。
先给最糙的直觉
一条发射器-接收器链路,物理上是一堆路径:直射的那条、墙弹一下的那条、弹三下的那条……每条路径带着自己的延迟和功率。工程师管这叫多径效应,我们今天管它叫“一句话”——路径按延迟排好序,每条路径是一个“词”。
next-token prediction 我们太熟了:给前面 n 个词,预测第 n+1 个。那 next-path prediction 就是:给前面 n 条路径的(延迟,功率),预测第 n+1 条路径的(延迟,功率)。
糙到这个程度,这就是一个回归问题。我们先来搓个最简版的。
搓一个最糙的 next-path 预测器
不调什么大件,numpy 就够。造假数据的方式也糙:模拟一个“房间”,信号从发射器出发,路径一条比一条延迟大、功率弱——真实信道大体就是这个形状,能量随弹射次数衰减。
import numpy as np
np.random.seed(0)
def make_channel():
# 搓一个假信道:6 条路径,延迟递增,功率指数衰减 + 噪声
n = 6
delay = np.sort(np.random.rand(n)) * 3.0 # 延迟 0~3 微秒,排好序
power = 10 ** (-1.5 * np.arange(n) - np.random.rand(n)) # 逐条衰减
return np.stack([delay, power], axis=1) # (6, 2) 每行一条路径
channels = np.array([make_channel() for _ in range(2000)])
print(channels[0])
跑一下,看看出来啥:
[[0.163 0.177 ]
[0.866 0.0566]
[1.518 0.0215]
[1.958 0.0073]
[2.249 0.0029]
[2.837 0.0011]]
形状对了:6 行,每行一个(延迟, 功率),延迟递增,功率一路掉。这就是我们的“一句话”。
接下来搓模型。最简版到什么程度?一个没有 embedding、没有 attention 的裸 MLP:吃前 5 条路径拍平成 10 维输入,吐 2 维,就是第 6 条路径的预测。loss 用 MSE。
W1 = np.random.randn(10, 32) * 0.1
b1 = np.zeros(32)
W2 = np.random.randn(32, 2) * 0.1
b2 = np.zeros(2)
lr = 1e-3
X = channels[:, :5, :].reshape(2000, 10) # 前 5 条路径
Y = channels[:, 5, :] # 第 6 条
def forward(X):
h = np.tanh(X @ W1 + b1)
return h @ W2 + b2, h
for step in range(3000):
pred, h = forward(X)
err = pred - Y
loss = (err ** 2).mean()
# 手摊梯度,两节链子
grad = 2 * err / len(X)
dW2 = h.T @ grad; db2 = grad.sum(0)
dh = grad @ W2.T
dW1 = X.T @ ((1 - h ** 2) * dh); db1 = ((1 - h ** 2) * dh).sum(0)
W1 -= lr * dW1; b1 -= lr * db1; W2 -= lr * dW2; b2 -= lr * db2
if step % 1000 == 0:
print(f"step {step} loss={loss:.6f}")
step 0 loss=0.614872
step 1000 loss=0.027811
step 2000 loss=0.027591
loss 降了,但降到 0.027 就趴住不动。这个数一开始我没料到,盯着看了一会儿——哦,是噪声。我造数据的时候每条路径的功率上叠了随机抖动,这部分天生不可预测,loss 的地板就是噪声方差本身。所有“预测下一个”的任务都有这个共性:loss 收敛到哪儿,取决于信号里到底有多少可预测结构,模型只是在往那个地板凑。论文里那些下游指标,本质上都是在量这个地板有多低。
这版糙在哪
回头数糙版本缺什么。缺的东西恰好就是 MultiPathFormer 的全部卖点,一样一样来。
第一,我们的 token 是裸的连续值。论文里连续值路径要过一套编码——他们专门搞了个“首路径码本”,把第一条路径(最强、信息量最大那条)离散化成码本里的条目。为什么首路径要特殊对待?因为后续路径的分布严重依赖首路径:首路径延迟大,说明环境绕,后面那串路径整个形状都不一样。我们那个裸 MLP 对此一无所知。
第二,我们没有“环境”。真实信道不是凭空生成的,是环境(建筑布局、材质)决定的。论文加了环境检索增强——预测路径之前先按环境信息检索相似的历史信道。说白了就是 RAG,只不过检索的不是文档是信道。你可以不认同这个设计,但“生成方不是白噪声、背后有一个可检索的环境库”这个假设,我觉得是整篇论文最物理的地方。
第三,也是最要命的:我们只预测了一条路径,而且是拿定死的窗口。真实的 next-path prediction 要自回归地一条一条往下生成,直到模型说“没了”。什么时候停、停早了停晚了怎么算 loss,这些论文里都绕不开,我们今天这版连碰都没碰。
为什么我说这论文值得 ML 的人看一眼
数字不全抄了,捡一个说:27 个无线环境上预训练,迁到没见过的用户,微调一把之后比在新环境从零训的模型还好。这个结果形态眼熟吧?——大语料预训练、小数据微调、打赢从头训的专用模型。NLP 里这套剧本演了快十年了,现在它在无线信道上原样重演了一遍。
我偏心的地方在这儿:这篇论文等于又给“next-token prediction 是个万能预训练任务”这个判断加了一块砝码。文本、代码、蛋白质、现在到无线路径——只要你的领域对象能排成有序序列、且“预测下一个”这个任务逼着模型去学序列背后的生成规律,这台机器就转得起来。反过来说,如果你手头有个领域想上基础模型这套,第一个该问自己的问题不是“用什么架构”,而是“我的数据能不能排成序列、预测下一个有没有意义”。答不上来,再大的模型也白搭。
泼自己一盆冷水:无线信道和文本有个根本差别——文本的词表天然离散,信道的路径统计量是连续的,这个 gap 不是搓个码本就能完全糊弄过去的。论文那 59% 的提升里有多少是环境检索的功劳、多少是路径级表示本身的功劳,光看总体数字拆不出来。这块我每次看都要重新顺一遍,不敢说真拆明白了。
收个尾
所以你看,MultiPathFormer 拆开也没那么玄:把路径排成序列,预测下一条,环境信息检索进来当条件——预训练任务本身,就是我们今天那三十行代码的豪华版。今天这版没有码本、没有环境、没有自回归生成,丑是丑了点,但它真的能跑,而且你现在应该能感觉到“下一句话”这个任务有多能装。
这版只是用来懂的,生产里请老老实实用 PyTorch——真要做无线基础模型,那是一整个团队的活。再往下其实还有一层可搓的:把裸 MLP 换成一个真的 causal attention,让模型自己决定该看前面哪几条路径,那就是 mini 版的路径 Transformer 了。「手搓系列」哪天闲了搓它。我们下次见。