基于LLM的开源时序预测模型Lag-Llama
Lag-Llama 是时间序列预测领域的第一个开源基础模型:它用 LLaMA 式的解码器 Transformer,把一段历史序列翻译成对未来概率分布的预测,无需针对每个数据集重新训练。2023 年 10 月由 Kashif Rasul 等 18 位作者发布(arXiv:2310.08278,代码 Apache-2.0 开源),2024 年 2 月官方权重在 Hugging Face 开放。它不解决多变量相关性这类大问题,只做一件事——单变量序列的通用概率预测,却因此成为时序基础模型赛道的起点:此后 TimesFM、Chronos、Moirai 相继登场,几乎都以它为对比基准。

| 时间 | 事件 | 意义 |
| 2017 | DeepAR 发布 | 概率预测的经典深度模型,但每个数据集都要单独训练 |
| 2022–2023 | PatchTST、OneFitsAll 等出现 | 探索通用架构:有人直接把 GPT-2 搬到时序上(OneFitsAll),效果平平 |
| 2023-10 | Lag-Llama 论文发布 | 首个时序预测基础模型,架构、数据、权重全部开源 |
| 2024-02 | 官方权重 + Colab Demo 开放 | 零样本预测开箱即用,任何频率、任意预测长度 |
| 2024 全年 | TimesFM、Chronos、Moirai 相继发布 | 时序基础模型赛道爆发,Lag-Llama 成为公认的早期基准 |
为什么需要时序预测基础模型
时序预测长期处于碎片化状态:换一个领域、换一种采样频率,就要重新训练一个模型。DeepAR、N-BEATS、TFT 等模型在各自的数据集上表现很好,但它们是数据集专用模型——训练时见过什么模式的序列,就只会预测什么模式的序列。真实世界的预测场景常常很窘迫:新城市的客流数据只有三个月、新上线的设备只有几十条记录、某个行业的数据分布和公开基准完全不一样。没有足够历史数据,专用模型根本训练不起来。
NLP 和 CV 领域早已给出答案:先在海量数据上做通识教育(预训练),再在具体任务上微调,甚至完全不训练直接使用(零样本)。GPT、CLIP 都是这个范式。Lag-Llama 要做的,就是把这条路搬到时间序列上,具体带来三个价值:
- 零样本泛化——面对一个全新的领域/频率,加载预训练权重即可直接预测,不需要任何该领域的训练数据;
- 少样本快速适应——只给 20% 的历史数据做微调,效果就能追上用 100% 数据训练的专用模型(论文实验证实,详见下文);
- 概率输出开箱即用——预测的不是一个点,而是一整个分布,预测区间直接服务于库存、容量、风险这类需要边界值的决策。

基于 lag 的 token 化:把周期性直接写进输入
lag 特征就是回头看的间隔:预测今天 10 点的客流量,最有用的输入不是全部历史,而是昨天 10 点(间隔 24 小时)、上周一 10 点(间隔 168 小时)和上一小时(间隔 1)这几个特定时刻的值。对每个时间步 $t$,模型取过去若干个固定间隔的值组成向量:
$$k_t[j] = x_{t – \mathcal{L}[j]}$$
其中 $\mathcal{L} = \{\ell_1, \ell_2, \dots\}$ 是一个排好序的 lag 索引集合。举个手算的小例子:小时级序列在 $t$ 时刻,取 $x_{t-1}=102$、$x_{t-2}=98$、$x_{t-24}=110$、$x_{t-168}=95$,那么这一时刻的 lag 特征就是向量 $[102, 98, 110, 95]$。预训练使用的 lag 集合覆盖季度、月、周、日、小时、分钟、秒 7 档频率(由 GluonTS 的 get_lags_for_frequency 按频率自动生成后合并去重),所以同一天/周/年的周期在不同采样频率下都能被表达。

这个设计的核心动机是频率无关性:模型不需要知道数据是小时级还是季度级,只看相对间隔。代价是它需要比上下文窗口更长的历史——实际输入长度 = context_length + 最大 lag,历史太短的序列喂不饱它(这一点我们在局限一节再展开)。
日期时间特征:让模型读懂时间刻度
如果说 lag 特征回答的是多久之前,那么日期时间特征回答的就是这一刻是几点几分、周几、几月。每个 token 额外拼接一组从秒内分钟一直覆盖到年内季度的时间特征。这些特征有个巧妙性质:相邻两个时间步之间,只有一个时间特征发生变化——模型因此能隐式推断出序列的采样频率,从而处理预训练时从未见过的频率组合。
汇总统计量:告诉模型这条序列的量级
如果说 lag 特征给出形状,汇总统计量给出的就是量级。预训练语料里有的序列是几十瓦的传感器读数,有的是几百万的订单量,数值范围差几个数量级。Lag-Llama 在每个窗口上计算中位数 Med 和四分位距 IQR 作为两个时间无关的标量协变量拼进每个 token,相当于告诉模型:这条序列大致多大、波动有多宽。这保证了跨数据集归一化后模型不会晕数字。
LLaMA 式解码器:RMSNorm + RoPE 的因果 Transformer
特征准备好后,交给一个和 LLaMA 同构的解码器:M 层带因果掩码的 Transformer 层,每层使用 RMSNorm 预归一化,并在注意力层的 query/key 上施加旋转位置编码(RoPE)。因果掩码保证只用过去预测未来;RoPE 把相对时间距离编码进注意力,让模型知道两个 token 隔了多远。官方发布的权重规模仅约 245 万参数(论文搜索出的最优配置:8 层、9 个注意力头、每头 16 维、上下文 32 步)——在动辄上百亿参数的大模型时代,它小得几乎可以放进任何环境。
分布头:输出 Student-t 分布,而不是一个点
最后一层把隐藏向量映射为 Student-t 分布的三个参数:自由度 $\nu$、均值 $\mu$、尺度 $\sigma$,训练目标是最小化预测分布的负对数似然(NLL)。预测的不是明天 10 点的客流是 1200,而是它服从一个中心约 1200、带厚尾的分布——这是概率预测的核心思想。

为什么选 Student-t 而不是最简单的正态?因为它有厚尾:真实业务序列里常有促销、天气突变、故障这类尖峰,正态分布会把这些当成不可能事件过度惩罚,厚尾则天然宽容。论文刻意选了最简单的参数化分布头(未来可换 normalizing flows、copula 等更复杂的分布),保持模型尽可能简单是作者反复强调的设计原则。
推理时采用贪心自回归解码:用预测出的分布采样下一步,把采样值当作已知历史继续往后推,直到预测长度 P 为止;多次采样得到多条未来轨迹,取经验分位数即可得到预测区间。
鲁棒标准化与数据增强:训练稳定性的两道保险
标准化公式采用减中位数、除 IQR的鲁棒版本,而不是常见的减均值、除标准差:
$$x’_t = \frac{x_t – \operatorname{Med}(x_{1:C})}{\operatorname{IQR}(x_{1:C})}, \qquad \operatorname{IQR}(x_{1:C}) = \operatorname{Med}(\{x_{\lceil C/2 \rceil : C}\}) – \operatorname{Med}(\{x_{1 : \lfloor C/2 \rfloor}\})$$
直观地说:一个突然放大 100 倍的尖峰足以把均值、标准差拉爆,但对中位数和四分位距的影响很小,所以异常值不会污染整个窗口的归一化。数据增强方面使用频域增强 Freq-Mask / Freq-Mix(随机遮蔽/混合序列的频率分量),预训练最优增强概率为 0.5,配合按数据集序列总数加权的分层采样,有效缓解跨领域语料上的过拟合。
| 设计要素 | 一句话作用 | 代码/参数入口 |
| lag token | 注入周期先验,实现频率无关 | lags_seq(按频率自动生成) |
| 日期时间特征 | 让模型识别采样频率 | time_feat=True |
| 汇总统计量 | 传递序列的量级与波动信息 | scaling(mean/robust) |
| 解码器 | 因果序列建模 | n_layer、n_head、n_embd_per_head |
| 分布头 | 概率输出(Student-t) | distr_output=studentT |
| 数据增强 | 缓解过拟合 | aug_prob、freq_mask_rate、freq_mix_rate |
预训练与效果验证
预训练语料是 27 个公开数据集、约 7,965 条单变量序列、约 3.52 亿个窗口 token,覆盖能源、交通、经济、自然、空气质量、云运维六大领域。作者用 catch22 特征(一套 22 维的时序特征描述符)验证了语料覆盖了广泛的时序表现型,这是模型能泛化到未见数据的前提。
| 预训练配置 | 取值 |
| 数据语料 | 27 个公开数据集,六大领域(能源/交通/经济/自然/空气质量/云运维) |
| 序列规模 | 7,965 条单变量序列 ≈ 3.52 亿窗口 token |
| 采样策略 | 分层采样:按数据集总序列数加权 |
| 数据增强 | Freq-Mask / Freq-Mix,增强概率 0.5 |
| 优化器 / 学习率 | Adam / 1e-4 |
| Batch size | 256 |
| 每 epoch 窗口数 | 100(长度 = 最大 lag + 上下文) |
| 早停 | 50 epochs(按验证损失) |
| 训练硬件 | 单张 Nvidia Tesla P100(12GB) |
论文还给出了超参数搜索的最优配置(随机搜索 100 组配置,按预训练验证损失挑选),其中最值得注意的两点:上下文长度只需要 32 步,权重衰减和 Dropout 都设为 0——模型很小,正则化反而多余;以及一个重要的宏观发现:验证损失随预训练数据量按幂律下降,说明时序基础模型同样遵循神经网络的缩放定律,数据与模型继续做大仍有收益。
| 超参数 | 搜索范围 | 最优值 |
| 层数 M | 1–9 | 8 |
| 注意力头数 | 1–9 | 9 |
| 每头嵌入维度 | 16–512 | 16 |
| 上下文长度 C | 32–1024 | 32 |
| 增强概率 | 0–1.0 | 0.5 |
| Freq-Mask 比率 | 0–1.0 | 0.5 |
| Freq-Mix 比率 | 0–1.0 | 0.25 |
| 权重衰减 / Dropout | 0–1.0 | 0 / 0 |
效果验证采用 CRPS(连续排序概率分数,把预测分布与真实值在所有分位数上的差距积分,越低越好):
$$\operatorname{CRPS}(F, x) = \int_{-\infty}^{\infty} \left(F(y) – \mathbb{1}\{y \geq x\}\right)^2 \, dy$$
下表是 7 个未见过的数据集上、基于 100 个经验采样样本计算的 CRPS 值,以及模型在 15 个基线中的平均排名:
| 数据集(领域) | 零样本 CRPS | 微调后 CRPS |
| weather(天气) | 0.164 ± 0.001 | 0.132 ± 0.001 |
| ped-counts(行人计数) | 0.285 ± 0.033 | 0.227 ± 0.010 |
| ett-m2(电力变压器温度) | 0.063 ± 0.002 | 0.017 ± 0.001 |
| platform-delay(平台延迟) | 0.091 ± 0.002 | 0.096 ± 0.002 |
| requests(服务器请求量) | 0.090 ± 0.015 | 0.012 ± 0.002 |
| beijing-pm2.5(空气质量) | 0.130 ± 0.009 | 0.125 ± 0.021 |
| exchange(汇率) | 0.011 ± 0.001 | 0.009 ± 0.000 |
| 平均排名(15 个基线) | 6.714 | 2.786 |

两个结论值得记住。第一,零样本已经很能打:不训练任何参数,平均排名 6.714,已经和一大批认真训练过的专用模型处在同一水平(DeepAR 5.714、Informer 6.429),在 weather、platform-delay 等数据集上尤其接近最优。第二,微调才是它的高光:在 ETT-M2、weather、requests 三个数据集上达到 SOTA,平均排名 2.786,领先最强监督基线 TFT(5.000)两个名次以上;更夸张的是少样本场景——只给 20% / 40% / 60% / 80% 的历史数据做微调,平均排名分别达到 1.857 / 1.500 / 1.571 / 1.429,每一档都是全场第一。注意也有反例:platform-delay 微调后 CRPS 不降反升(0.091 → 0.096),说明微调不是万能药,数据量过小时反而可能扰动预训练学到的知识。
上手实践:零样本 → 微调 → 评估
官方实现基于 GluonTS 构建,安装、下载权重、预测的流程非常短。第一步永远是先跑零样本:一个 29MB 的权重文件 + 十行代码,就能拿到带区间的预测。
git clone https://github.com/time-series-foundation-models/lag-llama.git cd lag-llama pip install -r requirements.txt # 下载官方预训练权重(约 29MB,Apache-2.0 许可) huggingface-cli download time-series-foundation-models/Lag-Llama lag-llama.ckpt --local-dir .
import torch
from lag_llama.gluon.estimator import LagLlamaEstimator
# 1) 加载官方预训练权重(lag-llama.ckpt 与脚本同目录)
ckpt = torch.load(lag-llama.ckpt, map_location=cpu)
# 2) 创建估计器:预测未来 24 步,用历史 64 步,采样 100 条轨迹
estimator = LagLlamaEstimator(
ckpt_path=lag-llama.ckpt,
prediction_length=24,
context_length=64,
nonnegative_pred_samples=True, # 客流/电量等非负场景建议开启
num_parallel_samples=100, # 轨迹数越多,分位数越平滑
)
predictor = estimator.create_predictor(
estimator.create_transformation(),
estimator.create_lightning_module(),
)
# 3) 用 GluonTS 内置数据集做零样本预测(示例:澳大利亚用电需求)
from gluonts.dataset.repository.datasets import get_dataset
dataset = get_dataset(australian_electricity_demand, regenerate=False)
test_data = dataset.test
for forecast in predictor.predict(test_data):
mean = forecast.mean # 预测均值
p50 = forecast.quantile(0.5) # 中位数
lo, hi = forecast.quantile(0.1), forecast.quantile(0.9) # 80% 预测区间
零样本确认可用后,再用自己的数据微调。微调的关键是从预训练权重出发、减小模型规模、放宽正则——官方 notebook 的常用配置如下:
from lag_llama.gluon.estimator import LagLlamaEstimator
from gluonts.dataset.common import ListDataset
# 把自己的数据包装成 GluonTS 格式:每条序列一个 {start, target}
train_data = ListDataset(
[{start: start_ts, target: values}], # start 为 pd.Timestamp,values 为 np.ndarray
freq=H, # 数据频率(D/H/T 等)
)
estimator = LagLlamaEstimator(
ckpt_path=lag-llama.ckpt, # 从预训练权重出发(迁移学习)
prediction_length=48,
context_length=32,
n_layer=3, # 微调时减少层数,防止小数据过拟合
n_embd_per_head=16,
n_head=4,
scaling=mean,
nonnegative_pred_samples=True,
aug_prob=0.5, # 数据增强开关
freq_mask_rate=0.1,
freq_mix_rate=0.05,
lr=1e-3, # 微调用更大学习率
batch_size=32,
num_parallel_samples=50,
)
transformation = estimator.create_transformation()
train_dl = estimator.create_train_dataloader(train_data, transformation)
valid_dl = estimator.create_validation_dataloader(valid_data, transformation)
trainer = estimator.create_trainer()
trainer.fit(estimator.create_lightning_module(), train_dl, valid_dl)
最后用 GluonTS 的评估工具量化效果。注意论文里的 CRPS 与分位数加权损失本质等价(CRPS 是对所有分位数的 pinball loss 求积分),因此用 GluonTS 的 Evaluator 即可:
from gluonts.evaluation import make_evaluation_predictions, Evaluator
forecast_it, ts_it = make_evaluation_predictions(
dataset=test_data, # GluonTS 格式的测试集
predictor=predictor,
num_samples=100,
)
evaluator = Evaluator(quantiles=[0.1, 0.5, 0.9])
agg_metrics, _ = evaluator(ts_it, forecast_it)
print(agg_metrics[mean_wQuantileLoss]) # 分位数加权损失(越低越好)
实践中还有四个容易踩的坑:
- 权重版本——官方在 2024 年中修复了 KV-cache 的因果注意力正确性问题,务必从 Hugging Face 拉取最新lag-llama.ckpt,并在自己的数据上做回归对比;
- 采样数——num_parallel_samples直接决定区间质量:太少(如 10)分位数毛糙,100 是常用值,但推理成本随之线性上升;
- lag 集合——默认 lag 面向常见周期,如果你的序列有不常见周期(如 30 天账期、学期制),需要自定义lags_seq;
- 单变量限制——每条序列独立预测,多变量问题要么逐通道预测,要么换 Moirai / TimesFM 等多变量方案。
它最合适的三个场景:
- 库存/安全库存——决策需要边界值而非均值,概率输出(分布头 + 采样)直接给出缺货概率,适配原因:区间越准,备货成本越低;
- 运维容量与成本——服务器请求量、云资源用量通常只有少量历史且模式多样,零样本即可上线,微调后逼近专用模型,适配原因:少数据场景正是基础模型的主场;
- 出行/零售的客流需求预测——早晚高峰、周末、节假日的周期性极强,lag 特征天然覆盖日/周/年周期,而大促、突发天气带来的尖峰正好落在 Student-t 厚尾的容忍范围内。
与同期模型的横向对比
Lag-Llama 之后,时序基础模型迅速形成四强并立的格局。它们最大的分歧在于如何把连续序列变成模型能吃的输入:Lag-Llama 用 lag 特征,TimesFM 用 patch 化,Chronos 把数值量化成 token(把时序当语言建模),Moirai 则同时支持多变量与混合频率。
| 维度 | Lag-Llama | TimesFM | Chronos | Moirai |
| 提出时间 | 2023-10(论文)/ 2024-02(权重) | 2024 | 2024-03 | 2024-05 |
| 输入表示 | lag 特征 + 日历特征 | patch 化 | 量化 token | patch + 任意变量 |
| 概率输出 | Student-t 分布头 | 分位数头 | token 分类分布采样 | 分布输出 |
| 变量类型 | 单变量 | 单变量(可加协变量) | 单变量 | 任意变量 |
| 参数量 | ≈2.45M | ≈200M | 8M–710M(5 档) | small / base / large |
| 最擅长 | 轻量、易微调、原生概率 | 长上下文、零样本 | 把时序当语言建模 | 多变量、MoE |
| 许可证 | Apache-2.0 | Apache-2.0 | Apache-2.0 | Apache-2.0 |
选型建议可以一句话概括:要原生概率输出、要在小数据上快速微调、预算有限——选 Lag-Llama;要超长上下文零样本——选 TimesFM;要处理多变量联动——选 Moirai;想体验把时序当语言的新范式——看 Chronos。
局限与破局
基础模型不是银弹,Lag-Llama 的边界非常清晰,四条局限都对应着明确的破局路径:
- 只做单变量——不建模多个变量之间的相关性,多变量系统只能逐通道预测、丢失联动信息。破局:Moirai 等任意变量模型直接建模多变量;Lag-Llama 自身也有引入 LSTM 增强长期依赖的变体研究;
- lag 特征要求最小上下文——实际输入必须 ≥ 最大 lag,短序列(如只有一周数据的冷启动场景)喂不饱。破局:Chronos 的量化 token 和 TimesFM 的 patch 化对上下文要求更宽松;极短序列也可回到 ETS / ARIMA 等传统方法;
- 零样本只是能打,不是最强——真正的 SOTA 要靠微调,且个别数据集微调后反而变差(如 platform-delay)。破局:预算内优先微调,用官方 notebook 配置起步;用验证集做早停,防止小数据过拟合破坏预训练知识;
- 生态更新节奏慢——2024 年中才修复 KV-cache 正确性问题,checkpoint 演进不如大厂模型频繁。破局:锁定版本 + 建立回归测试;需要长期维护的生产系统可评估 TimesFM 等更新更活跃的选项。
Lag-Llama 用lag 特征 + LLaMA 解码器 + Student-t 分布头这个朴素组合,证明了时序基础模型这条路走得通,并由此开启了 2024 年时序基础模型的全面爆发——今天的 TimesFM、Chronos、Moirai,都是在回答它提出的同一个问题:如何让一个模型学会预测所有时间序列。