
先讲一个最直观的现象刷论文列表的时候如果标题里同时出现 In-Context Learning、Linear Dynamical Systems 和 Transformers 这三个词基本能猜到这篇工作在干什么——它想把“上下文学习”从函数拟合推进到更复杂的系统辨识。我最近在 NIPS 2025 的稿件堆里留意到一篇标题包含 “In-Context Learning of Linear Dynamical Systems with Transformers: Approximation Bounds” 的工作标题后面应该还跟着泛化或误差相关的字眼但光看前半部分已经足够引起兴趣。它想解决的问题其实是一个固定参数的 Transformer能不能像人类看几组数据就总结规律那样在推理时只凭上下文里给出的输入输出样本现场学会一个线性动态系统的行为然后对未来做预测这个问题既有理论难度又有实际价值。这篇博文我想从我的角度把这类工作拆开聊它到底解决了什么、核心细节在哪儿、如果只想搭一个最小实验验证想法应该从哪几个地方下手以及复现时最容易踩的坑。1. 这篇论文到底在解决什么问题1.1 一句话版本让 Transformer 在多步预测现场学会系统规律如果你熟悉上下文学习应该知道一个典型场景给模型一段包含若干“输入-输出”示例的文本然后让它回答新输入对应的输出。模型不做任何梯度更新所有“学习”都发生在 Attention 对上下文的加权组合里。现有研究证明了 Transformer 能在上下文中解决线性回归、逻辑回归、甚至一些简单优化问题。但线性动态系统是另一类更硬的对象。它不只意味着一个输入到输出的静态映射而是存在隐藏状态当前输出受历史状态影响系统行为由状态转移矩阵决定。用生活化的类比来说线性回归像是“看着几个菜谱就学会了一道菜的配方”而线性动态系统更像是“只看几段行情就要现场判断接下来行情怎么走”因为市场状态是隐藏的、连续的、带噪声的。前者是一个点对点的映射问题后者是一个序列滤波与预测问题复杂度不在一个量级。基于常见实践的直观判断Transformer 之所以能处理这种任务是因为因果自注意力天然具有“按时间顺序聚合历史”的结构。它可以在前向计算中隐式维护一个关于系统状态或参数的统计量不断用新观测修正。论文标题里的 “Approximation Bounds” 给我们划定了清晰的研究目标不只是说“能学会”而是从理论上证明在有限层数、有限宽度、有限上下文长度的条件下这个“现场学会”的误差边界到底是多少。1.2 上下文学习要和传统系统辨识对应上传统做法是离线系统辨识。拿一批观测序列用预测误差法、子空间方法或者直接跑卡尔曼滤波来做状态估计估计出状态转移矩阵和观测矩阵再部署预测。整个流程分为“收集数据、拟合模型、上线预测”三步。而上下文学习的做法是不提前为每个系统单独拟合参数而是把所有系统的共性规律压缩进 Transformer 的权重里。推理时模型通过上下文样本识别当前是哪个系统直接输出预测。这两种路径各有利弊。传统系统辨识每一步都可解释有现成的控制理论工具做稳定性分析但更新模型参数需要重新优化或在线递推在数据分布漂移时经常要重新拟合。Transformer 上下文学习则把自适应放到更靠前的位置所有适配都发生在序列内部。它的弱点是理论解释还不像卡尔曼滤波那么完备而且模型要见足够多的系统才能举一反三。明白这个对照之后标题里的 “Approximation Bounds” 就更有意义了。它本质上是在问如果我想让 Transformer 模仿传统系统辨识流程甚至达到接近卡尔曼滤波的效果到底需要多少层、多少头、多少上下文长度这个问题的答案直接指导实验设计和模型选型而不是停留在“效果还行”的工程判断上。1.3 为什么 “Approximation Bounds” 值得单独关注我是做应用出身的人以前看到理论文章里的 bound 常常跳过去。但这个方向我建议你别跳因为它的结论能帮你省大量实验经费。假设一个 Transformer 模型要达到近似卡尔曼滤波的预测效果理论上需要深度 L 和宽度 D 都随状态维度或上下文长度增长。如果事先知道这个依赖关系你就能判断一个 6 层的模型在 128 步上下文里大概率够用还是明显不够用。另外这类论文通常会区分“表达能力”和“可学习性”两个层次。前者研究的是是否存在一组注意力权重让 Transformer 在这个任务上达到很小误差。后者研究的是用梯度下降能不能学到那组权重。很多做实验的人会发现模型效果差第一时间怀疑数据或调参但有时候根本问题是网络容量不够或者上下文长度不够模型没有获得足够信息来辨识系统。Approximation Bound 就是那个“别瞎猜先算容量”的参考。所以我个人读这类稿子的习惯是先把问题定义搞清楚再去看边界定理的形式最后才是实验部分。问题定义决定了你要预测什么、误差怎么衡量、上下文如何组织这些搞清楚之后复现的时候就不会跑偏。2. 核心细节解读Transformer 如何逼近线性动态系统估计2.1 先把线性动态系统写成“人话”很多刚接触这个方向的同学会被状态空间模型吓到其实核心就两条公式。假设系统有一个隐藏状态向量 (\mathbf{x}_t)你能观测到的输出是 (\mathbf{y}_t)系统的演化满足[ \mathbf{x}_{t1} \mathbf{A}\mathbf{x}_t \mathbf{B}\mathbf{u}_t \mathbf{w}_t ][ \mathbf{y}_t \mathbf{C}\mathbf{x}_t \mathbf{v}_t ]其中 (\mathbf{u}_t) 是控制输入(\mathbf{w}_t) 和 (\mathbf{v}_t) 分别是过程噪声和观测噪声。预测任务就是给定历史观测序列和输入序列估计当前隐藏状态并预测未来的 (\mathbf{y})。这类问题之所以难难在隐藏状态不可直接观测。要想从观测中恢复状态系统需要满足可观测性条件。用一个简单类比你看不到房间里的温度计内部结构但每隔一段时间能看到温度读数如果你知道读数和真实温度的映射关系就能反过来估计真实温度。线性动态系统的辨识就是同时估计“映射关系”和“隐状态演化规律”。在上下文学习的设置里模型并不知道 (\mathbf{A}) 和 (\mathbf{C})它只看到一组轨迹。理想行为是先从示例轨迹中推断出系统参数再对新轨迹做滤波和预测。这就和纯记忆不同因为系统参数在推理时是不可见的模型必须通过上下文完成一次隐式的系统辨识。2.2 自注意力机制如何模拟状态估计过程自注意力机制本身做的是加权求和。给定一串 token每个 token 会通过 Query、Key、Value 三个矩阵和其他 token 交互。因果掩码保证了每个 token 只能看到它之前的 token这就构成了一种“按时间顺序递推”的结构。你可以把注意力输出看作历史信息的加权平均而不同的权重分配方式就对应不同的估计算法。线性动态系统估计中最经典的工具是卡尔曼滤波。卡尔曼滤波每一轮做的事情无非是根据当前状态估计和系统模型做一步预测然后根据新观测计算残差和增益再更新状态估计。这个“预测-更新”的过程是可以被 Transformer 中的层堆叠模拟出来的。一层或多层叠加可以维护一个隐式状态向量每读入一个新的观测 token就更新一次这个向量最后用更新后的状态去做未来多步预测。更具体一点注意力头可以扮演不同的角色。某个头负责找与当前观测最相似的过去观测从而计算残差另一个头可能负责把当前隐状态估计与预测值拼接MLP 块则负责执行非线性变换比如把协方差矩阵的更新映射成可用的增益。多头机制相当于并行运行多个不同的估计算法最后把它们的结果加权融合。这也是为什么论文里要讨论“层数”和“宽度”。层数对应递推更新的轮数宽度对应每个轮次能存储多少统计信息。如果状态维度是 d那模型至少要能在隐状态里保存 d 维状态估计和 d×d 量级的协方差信息。仅从信息量角度推理D 应该随 d 增长仅从递推轮数角度推理L 应该随上下文长度或迭代次数增长。这就是 Approximation Bound 给出的基本直觉深度和宽度不能太小否则表达不出卡尔曼滤波需要的中间量。2.3 逼近界给出的理论直觉这类论文里的界通常长成下面这样模型输出与最优估计之间的误差被一个关于上下文长度 T、层数 L、宽度 D、状态维度 d 的表达式控制。直观上它们分别对应了三个资源上下文长度是样本量层数是迭代轮数宽度是存储容量。影响因素直观对应理论表现上下文长度 T样本量越大辨识越准误差随 T 增加而下降层数 L递推更新轮数越多越接近理想算法误差随 L 增加而下降宽度 D可存储的统计量越多至少需要包含状态和协方差量级状态维度 d系统越复杂需要的信息越多下界随 d 增长这个表格看着简单但实际用处很大。比如你跑实验时发现加大上下文长度收益不大可能不是样本不够而是模型深度不够无法完成足够的递推更新。你发现增加宽度明显提高了效果说明模型缺少的是存储容量而不是迭代能力。这些判断在没有理论的时候只能靠反复试错有了边界之后至少能明确排查方向。另外标题如果带了 “Generalization” 或类似词通常还会回答另一个问题在训练时见过的系统分布上学的经验能不能泛化到没见过的系统。这对应的是元学习里的“跨任务泛化”问题。这个层面更实际因为没人会在部署时保证新系统一定跟训练分布完全一致。3. 实操思路搭一个最小可复现实验3.1 任务定义与数据生成如果你想验证 Transformer 能不能上下文学习线性动态系统我建议从最小的可控实验开始。第一步是定义系统分布。不要只固定一个系统那样模型只需记忆曲线形态不叫上下文学习。要随机生成一批系统让模型每次面对一个新系统必须看上下文才能知道它是什么。生成随机线性动态系统时最需要注意的就是稳定性。状态转移矩阵 (\mathbf{A}) 的特征值谱半径要小于 1否则信号会发散数值不稳定。常见做法是先生成一个随机矩阵再做谱半径缩放。例如用numpy.random.randn(d, d)生成矩阵然后计算特征值取最大模把矩阵除以这个模再乘一个 0.9 左右的系数保证谱半径小于 1。噪声尺度也要控制。过程噪声和观测噪声如果太小问题会退化成确定性系统模型只要靠记忆轨迹规律就行如果太大信号被噪声淹没任何方法都难学。经验上噪声标准差设为信号标准差的 0.05 到 0.2 之间是比较合理的起始范围。3.2 构造 Prompt 与训练目标上下文学习的核心是提示格式。一种直接的做法是每个训练样本由同一个系统的多条短轨迹拼接而成再加上最后一条要预测的查询轨迹。模型读取整个序列看到前几条轨迹后对最后一条轨迹逐点预测未来输出。具体格式可以设计成[ [y_{1,1}, y_{1,2}, ..., y_{1,T}, \text{sep}, y_{2,1}, ..., y_{2,T}, \text{sep}, y_{q,1}, ..., y_{q,T}, y_{q,T1}] ]其中 sep 是分隔符 token。训练目标是预测 y_{q,T1} 以及查询轨迹内部每个下一步输出。如果用 teacher forcing就允许模型看到查询轨迹历史的真实值如果要模拟真实部署建议在训练后期加入一部分自由运行free-run模式让模型用自己的预测作为输入。关于批量组织有一个重要经验一个 batch 中每个样本必须来自不同的随机系统。如果多个样本共享同一个系统模型在训练时可能会偷懒直接跨样本共享信息。这就破坏了上下文学习的基本设定。理想情况是每个样本一个系统从分布里独立采样。3.3 一个 PyTorch 风格的最小骨架下面给一个简化但能说明结构的 PyTorch 风格骨架。它不是一个完整可调参的项目但把关键模块列出来了你可以在上面快速迭代。import torch import torch.nn as nn class CausalSelfAttention(nn.Module): def __init__(self, d_model, n_heads): super().__init__() self.qkv nn.Linear(d_model, 3 * d_model) self.proj nn.Linear(d_model, d_model) self.n_heads n_heads self.d_head d_model // n_heads def forward(self, x): B, T, D x.shape qkv self.qkv(x).reshape(B, T, self.n_heads, 3 * self.d_head) q, k, v qkv.chunk(3, dim-1) attn torch.matmul(q, k.transpose(-1, -2)) / (self.d_head ** 0.5) mask torch.triu(torch.ones(T, T, devicex.device), diagonal1).bool() attn attn.masked_fill(mask, float(-inf)) attn attn.softmax(dim-1) out torch.matmul(attn, v) out out.reshape(B, T, D) return self.proj(out) class TransformerBlock(nn.Module): def __init__(self, d_model, n_heads, d_ff): super().__init__() self.attn CausalSelfAttention(d_model, n_heads) self.norm1 nn.LayerNorm(d_model) self.mlp nn.Sequential( nn.Linear(d_model, d_ff), nn.GELU(), nn.Linear(d_ff, d_model) ) self.norm2 nn.LayerNorm(d_model) def forward(self, x): x x self.attn(self.norm1(x)) x x self.mlp(self.norm2(x)) return x class MinimalICLTransformer(nn.Module): def __init__(self, d_input, d_model, n_heads, n_layers, d_ff): super().__init__() self.embed nn.Linear(d_input 1, d_model) self.pos nn.Embedding(512, d_model) self.blocks nn.ModuleList([ TransformerBlock(d_model, n_heads, d_ff) for _ in range(n_layers) ]) self.head nn.Linear(d_model, d_input) def forward(self, y, t): B, T, _ y.shape pos self.pos(t) x self.embed(torch.cat([y, t.unsqueeze(-1).float()], dim-1)) x x pos for block in self.blocks: x block(x) return self.head(x)这个骨架把时间索引直接作为额外输入而不是只依赖位置编码。原因是线性动态系统对时间顺序极其敏感明确的时间信息能帮助模型区分“第几步”和“系统时间”。做实验时可以把 d_model 先设成 64层数设成 3 到 4 层头数设成 4先用一维状态跑通再逐步加复杂度。3.4 评估方法别只看一步预测训练完成后的评估比训练本身更容易出错。最常见的问题是只测一步预测误差也就是给定真实历史预测下一时刻。这个指标太宽松因为模型如果退化成简单的自回归模型也能做到看起来不错。真正有意义的是多步滚动预测只给定上下文前若干个真实观测之后就让模型用自己的预测继续生成未来轨迹不断对比真实值。建议至少计算两个指标。第一个是标准化均方根误差把误差除以真实轨迹的标准差得到相对误差。第二个是“有效预测长度”也就是预测误差超过某个阈值之前还能走多少步。后者更接近实际应用感受。比如一个模型可能一步误差很小但三步之后就开始发散这在控制系统中是完全不可接受的。还可以设置一个 oracle 对照如果系统参数已知卡尔曼滤波可以达到理论最优。Transformer 的预测误差如果显著高于这个 oracle说明还有提升空间如果接近它说明模型在上下文学习中已经学到了接近最优滤波器的行为。这个对照比单纯看绝对误差更有说服力。4. 复现中的踩坑记录与排查技巧4.1 数据生成里的稳定性问题我在刚跑类似实验时踩过一个大坑生成的系统里混入了一些谱半径刚好大于 1 的矩阵结果训练到一半损失突然变成 NaN。排查到最后发现不是模型问题而是某几条轨迹发散数值溢出。建议生成数据时不仅要做谱半径缩放还要在轨迹生成后检查最大幅值把超过设定阈值的样本重新采样。另一个细节是对齐观测尺度。如果不同系统生成的值范围差别很大Attention 里的 softmax 很容易饱和导致几乎所有注意力集中到一两个 token 上。模型退化成只看最近一步学不到长期依赖。解决办法是在每个样本内部用上下文统计量做标准化但不能用全量数据统计替代否则推理时新样本会面临不一致的归一化尺度。这里提醒一句数据标准化最好放在模型输入之前并且在评估时使用完全相同的统计量计算方式。如果训练用了全局均值和方差推理时不保持一致模型输出会明显漂移。这类 bug 很隐蔽但造成的效果差异比模型结构还大。4.2 上下文长度与示例数量的选择很多人会把上下文长度设得很大觉得信息越多越好。但实验上并不是这样。上下文过长时位置编码可能覆盖不到训练长度模型在推理时表现崩塌上下文过短时系统辨识信息不足模型只能做到“抄近路”式的短期预测。我的实验建议是从 T32 或者 T64 开始先把模型跑稳再逐渐增加到 128 甚至 256。如果训练时随机采样不同长度的上下文模型对位置编码的依赖会更鲁棒推理时遇到比训练更长的序列也不至于完全失效。给每条轨迹设置不同长度相当于一种数据增强。示例轨迹数量也很关键。对同一个系统如果只给一条几十步的轨迹模型很难区分观测噪声和系统动态。给两条或三条轨迹辨识效果会有明显提升。从信息论角度看示例越多系统参数的后验越集中。但示例太多又会超过注意力能有效整合的范围所以需要找到一个平衡点这个最好通过小范围网格搜索确定。4.3 评估指标的陷阱与修正如果你发现训练损失降得很低但滚动预测很快发散不用慌这几乎是常态。原因在于训练目标通常是每步用真实历史来预测下一步即 teacher forcing。可是推理时模型看到的是自己的预测误差会不断累积。训练和推理之间的一致性存在明显缺口。解决思路有两个。一是训练时随机用一部分自己的预测替换真实输入模拟推理时的误差传播二是直接训练多步预测目标让模型输出未来连续 k 步然后和真实轨迹计算整体损失。第二种方法收敛更慢但评测表现通常更稳。建议先用 teacher forcing 训练再用少量轮次精调成多步一致模式两者兼顾。在做评估之前一定要把“训练时见过的系统”和“没见过的系统”分开。上下文学习最有价值的地方就是对新系统的快速适应如果只在训练分布内的系统上评测你测的是泛化还是记忆很难说清。最好设置一组保留系统它们的参数分布和训练时有细微偏移用来模拟真实部署中的分布漂移。4.4 与经典算法的对照基准强烈建议在实验报告里放一个对照统一的表格把不同方法在同一组保留系统上的效果列出来。下面是我通常会比较的几个选项方法是否需要更新参数可解释性滚动预测精度适用场景卡尔曼滤波已知系统否高最优理论下界在线递推最小二乘是中较好需要在线自适应离线状态空间辨识是高好数据充足Transformer 上下文学习否低取决于训练分布多系统快速切换这个对照的意义不只是展示 Transformer 好坏而是快速定位问题。如果 Transformer 连已知系统参数的卡尔曼滤波都差很远大概率是容量不够如果差得不远说明架构表达力到位问题出在跨系统泛化。这两类问题需要完全不同的优化方向前者加宽度层数后者加系统多样化。5. 后续影响与我的个人判断5.1 对时间序列基础模型的影响如果这类工作做扎实了最直接的影响是时间序列基础模型的玩法会变。现在很多时间序列预测模型还是先在一个大数据集上预训练然后在目标数据上微调。微调意味着要保存模型副本、准备验证集、跑优化流程成本并不低。上下文学习一旦成熟预测模型可以在推理时直接利用目标序列的最近观测做现场适应连梯度更新都省了。这种模式对“多客户、多场景、模型统一”的部署非常友好。同一个模型服务很多不同的数据流每条数据流有自己独特的动态规律传统做法是为每条数据流维护一个微调副本成本随数据流数量线性增长。上下文学习则把每个数据流的历史直接丢进上下文模型自动适配几乎零边际维护成本。我之前在某个时序项目里做过类似尝试理论上很诱人但当时缺少对动态系统的理论支撑很多效果靠调参堆出来。现在有了近似界的分析至少能指导上下文长度和模型容量怎么选。5.2 对控制与自适应系统的影响控制领域的自适应控制一直想解决一个问题如何让控制器快速适应环境变化。传统自适应控制依赖实时参数辨识对噪声和未建模动态很敏感。如果 Transformer 能够在上下文中完成系统辨识并直接输出预测或控制量那它就可以扮演“在线辨识器预测器”的角色而且不更新权重因此没有灾难性遗忘问题。这里面有个需要注意的地方控制任务比单纯预测多了一个闭环反馈环节模型预测误差会通过控制器影响系统输入再影响未来状态。所以上下文学习中证明的预测误差界不能直接迁移到闭环控制需要有闭环稳定性分析。这一点我自己踩过模型预测控制的坑开环预测看起来很好一旦闭环就出现振荡。如果未来论文能在闭环设置下给出误差界或稳定性条件那才是真正可落地的突破。5.3 个人经验与扩展想法如果让我给后来者划一条上手的路径我会建议先跑一维状态系统不要一上来就挑战高维状态。一维状态的好处是所有统计量退化成标量协方差更新和增益计算都很好理解你可以手动画出模型内部注意力权重来对照卡尔曼滤波。当一维系统跑通再慢慢加维度、加控制输入、加部分可观测性。因为一旦状态维度升高注意力需要维护的交叉协方差矩阵迅速膨胀你会立刻看到“上下文长度够、层数不够”的瓶颈。还有一个我觉得很有潜力的扩展方向把线性动态系统的上下文学习推广到带未知时滞或部分非线性环节的系统。现实系统很少是纯线性但很多非线性系统在工作点附近可以线性化。如果模型先通过上下文估计当前工作点再切换到对应的线性动态预测这可能成为轻量级在线系统辨识的新范式。个人经验是这类方向做理论可能很难但做工程原型非常有意思先用小模型把数据管线跑通再看理论研究给什么启示。最后一个小建议真遇到效果不佳时不要急着换模型结构。先把数据生成分布、上下文长度、评估指标三个变量各自做一次简单敏感性分析。我见过太多人一上来就堆大模型结果训练不稳定最后连问题是出在容量还是数据都理不清。把控制变量先拆干净再谈优化这是这类任务里最实用的心法。