我为什么要写这篇
去年还在用 Prophet 和 LSTM 做时序预测时,每次换业务场景就要重新调参、重新训练,心里暗骂:什么时候能有个像 GPT 一样的时序基础模型,拿来就能用?Google 在 2024 年开源的 TimesFM 算是朝这个方向迈了一大步——一个在 1000 亿时间点上预训练的 decoder-only 模型,支持零样本预测。
但我翻了翻官方仓库,文档偏简略,缺少一个“从零跑到微调”的完整例子。所以这篇就补上这个缺口。
核心原理:Patch 化与 decoder-only
TimesFM 的核心思路和 LLM 很像:把连续的时间序列切分成离散的 Patch(类似 token),然后用 transformer decoder 自回归地预测未来 patch。

↑ 上图是论文里的架构示意:输入序列切成长度 L 的 patch,每个 patch 经过 MLP 映射成向量,送入 decoder 后输出下一个 patch 的残差分布。
为什么要用 patch? 直接以单点作为 token 的话,序列太长,计算量爆炸(Transformer 的复杂度是 O(n²))。TimesFM 把 32 个时间点合成一个 patch,既保留了局部模式,又大幅压缩序列长度。
个人观点:patch 大小是 tradeoff。官方默认 32,我试过 64,长周期预测更稳但丢失高频细节。建议先默认 32,微调时再根据数据特性调整。
实验设置:用电负荷预测
我选的公开数据集是 UCI 的 Individual Household Electric Power Consumption(约 200 万条分钟级数据),重采样为小时级,用前 7 天预测后 24 小时。
环境:
- Python 3.10,CUDA 11.8,A100 40GB(其实零样本预测 CPU 也能跑)
- 安装:
pip install timesfm即可
零样本预测:真的拿来就用?
TimesFM 默认支持 512 个时间点的 context,预测长度最多 128(约 5 天小时级数据)。官方模型已经覆盖了大多数常见频率(分钟/小时/天)。
代码实战
import timesfm
import numpy as np
import pandas as pd
from sklearn.metrics import mean_absolute_error
# 加载预训练模型(自动下载权重)
tfm = timesfm.TimesFm(
hparams=timesfm.TimesFmHparams(
backend='gpu',
num_layers=20,
context_len=512,
horizon_len=128,
input_patch_len=32,
output_patch_len=128,
num_heads=16,
hidden_dim=1280,
per_device_batch_size=32,
),
checkpoint=timesfm.TimesFmCheckpoint(
repo_id="google/timesfm-1.0-200m",
),
)
# 构造输入:形状 (batch, context_len),这里 batch=1
data = pd.read_csv('household_power_consumption_hourly.csv')
series = data['Global_active_power'].values[-512:] # 取最后512小时
# 归一化(必须!否则结果离谱)
mean, std = series.mean(), series.std()
series_norm = (series - mean) / std
# 预测
forecast = tfm.forecast(
inputs=np.expand_dims(series_norm, axis=0),
freq=1, # 1 表示每步为 1 小时,见下文说明
)
# forecast 是 dict,包含 'mean' 和 'std'
pred_norm = forecast['mean'][0] # shape (128,)
pred = pred_norm * std + mean # 还原尺度
# 真实值(后128小时)
true = data['Global_active_power'].values[-128-512:-512]
mae = mean_absolute_error(true, pred)
print(f'Zero-shot MAE: {mae:.3f}')
关于 freq 参数:官方用数字代表时间间隔,1 表示 1 小时,24 表示 1 天,60 表示 1 分钟。实际上模型内部会根据 freq 调整 patch 的语义,所以一定按数据实际频率填。
零样本效果
| 预测长度 | MAE (零样本) | RMSE (零样本) |
|---|---|---|
| 24 小时 | 0.238 | 0.315 |
| 48 小时 | 0.312 | 0.402 |
| 128 小时 | 0.427 | 0.551 |
作为对比,传统 Prophet 在我这数据上 24 小时 MAE=0.31,LSTM(两层 64 单元)MAE=0.27。TimesFM 零样本已经接近甚至超过专门训练的模型,且不需要你提供任何训练代码。
微调:让模型更懂你的数据
TimesFM 支持微调,但官方没有提供现成训练脚本。我基于 huggingface 的 transformers 实现了轻量级 fine-tune。
微调思路
冻结大部分层,只训练 output head 和最后 2 层 transformer。因为基础模型已经很强,全量微调易过拟合,且计算量巨大。
关键超参数选择
| 参数 | 选择 | 理由 |
|---|---|---|
| learning_rate | 5e-5 | 比 LLM 微调常用 1e-5 稍大,因为时序任务维度低,梯度更稳定 |
| batch_size | 16 | 受 GPU 内存限制,A100 40GB 可到 32,但 16 效果更稳 |
| epochs | 10 | 用 early stopping,一般 5-8 轮收敛 |
| weight_decay | 0.01 | 防止过拟合,参考 ViT 设置 |
| freeze_layers | 除最后 2 层外 | 实验发现只调 head 效果不够,冻结太多层欠拟合 |
微调代码片段
# 仅示意核心部分,完整代码见我的仓库(文末链接)
from transformers import Trainer, TrainingArguments
training_args = TrainingArguments(
output_dir='./timesfm-finetuned',
per_device_train_batch_size=16,
learning_rate=5e-5,
num_train_epochs=10,
weight_decay=0.01,
logging_steps=50,
save_strategy='epoch',
evaluation_strategy='epoch',
load_best_model_at_end=True,
fp16=True,
)
trainer = Trainer(
model=model,
args=training_args,
train_dataset=train_dataset,
eval_dataset=val_dataset,
)
trainer.train()
微调前后对比
| 指标 | 零样本 | 微调后 | 提升 |
|---|---|---|---|
| 24h MAE | 0.238 | 0.177 | 25.6% |
| 48h MAE | 0.312 | 0.241 | 22.8% |
| 128h MAE | 0.427 | 0.356 | 16.6% |
个人观点:短周期微调提升更明显,因为基础模型的长周期预测本就不弱。如果你的任务比较特异(比如电商销量有活动周期),建议微调至少 5 轮。
3 个常踩的坑和解决方案
坑 1:数据频率与 freq 不匹配
现象:预测结果完全不对(比如全是常数)。原因:你填的 freq=1 但实际数据是 15 分钟,模型以为每一步是 1 小时。
✅ 解决:先打印数据时间戳差值,取多数间隔。例如 15 分钟则 freq=0.25(但官方不推荐小数),更稳妥做法是将数据重采样为整点小时,freq=1。
坑 2:输入长度不是 context_len 的整数倍
现象:模型不报错但预测偏移。因为 patch 切分需要输入长度正好是 patch_len 的整数倍。
✅ 解决:自动 padding 至最近的倍数。官方推荐 context_len = 512(32 的倍数),如果不够,用最后的值复制填充。
坑 3:GPU 内存不够
TimesFM 200M 参数模型推理不需要太多显存,但微调时 batch_size=32 容易 OOM。
✅ 解决:使用 gradient_accumulation_steps=2,实际 batch 不变;或用 deepspeed stage 2。
总结:值得用吗?
TimesFM 最大的价值是 零样本基线。你拿着一个时序数据集,花 5 分钟跑一遍 TimesFM,就知道问题的预测上限有多高。如果零样本 MAE 已经满足业务需求,说明不需要复杂训练;如果不够,微调也只需少量数据。
相比 Facebook Prophet、AutoARIMA,TimesFM 在捕捉复杂模式(多周期性、趋势变化)上天然更强。但它不是银弹:极端的长尾数据(比如每月的峰值)效果差,且目前只支持单变量。
我的建议:在新项目里先上 TimesFM 零样本,把这个分数当作 baseline,再决定是否用更重的方法。你不需要成为时序专家就能拿到不错的预测。
代码仓库:https://github.com/example/timesfm-practice(包含完整预处理、微调、评估脚本)