r
redannancy/timer-base-84m
模型介绍
文件和版本
Pull Requests
讨论
分析

timer-base-84m:昇腾 NPU 上的时间序列零样本预测

1. 这个模型是做什么的

时间序列预测一直是工业场景里又常见又棘手的问题——电力负荷、设备温度、流量监控,处处需要"看了过去、预判未来"。timer-base-84m 是 thuml 团队开源的时间序列基础模型,走的是"预训练 + 零样本"路线:它在大规模时序语料上练好了"怎么从历史形态推演未来",所以拿来做预测时不需要再针对某个数据集重新训练,直接把历史序列丢给它就能得到未来一段时间的预测值。

84M 参数量在基础模型里属于轻量级,但得益于 decoder-only 的 GPT 式架构,它对"趋势延续、周期往复、噪声扰动"这类序列规律有不错的建模能力。本次我们把它部署到了本地昇腾 Ascend910 上,跑通了从权重下载、模型加载到 HTTP 服务发布的完整链路,并给出了一份可复现的实操文档。

2. 适配过程中踩到的关键点

先说结论:这个模型不是大语言模型。它虽然长着一张 GPT-2 的脸(8 层 decoder、8 头注意力、1024 隐藏维),但喂给它的是连续的时间序列数值,吐出来的是未来的连续预测值,全程没有 token、没有词表、没有采样器。这意味着 vLLM 那种"分词 -> 生成 -> 采样"的文本服务管线对它不适用——实测 vllm serve 会直接拒绝这个架构(TimerForPrediction are not supported)。

因此我们的适配路线是:保留 vLLM-Ascend 的昇腾推理栈(CANN + torch_npu 提供 NPU 算子执行),把模型本体用 transformers 的 remote code 机制加载,再包一层 FastAPI 做成服务。另外,模型自带的远程代码是按 transformers==4.40.1 写的,我们环境里是 4.57.6,两者在缓存类接口上有不兼容,这一步我做了三处最小改动(后面"注意事项"里有清单),没有动任何权重,所以推理结果和官方实现是一致的。

3. 验证环境

本次跑通所用的环境如下,供复现参考:

  • 操作系统:Linux 5.10.0 (aarch64)
  • 硬件:华为昇腾 Ascend910,双卡(单卡 64GB HBM),npu-smi 25.5.5
  • CANN:8.5.1
  • Python:3.11.14
  • torch 2.9.0 + torch_npu 2.9.0.post1
  • transformers 4.57.6、vLLM-Ascend 0.18.0
  • 服务框架:FastAPI + uvicorn

模型权重为 float32 的 safetensors 单文件,约 321MB(84M 参数)。

4. 怎么把它跑起来

目录里直接给出了可运行的 inference.py,建议按下面的步骤操作:

# 安装依赖
pip install -r requirements.txt

# 启动服务(默认 0.0.0.0:8088,NPU 设备 0)
python3 inference.py --port 8088 --device 0

服务起来后(首次加载约 1~2 秒),就可以用下面这些请求来调用:

# 看服务是否就绪
curl http://127.0.0.1:8088/health

# 扔一段真实的序列进去,预测后面 96 个点
curl -X POST http://127.0.0.1:8088/infer \
  -H 'Content-Type: application/json' \
  -d '{"series":[20.0,20.5,21.0,21.5,22.0,22.5,23.0,23.5,24.0,24.5,25.0,24.5,24.0,23.5,23.0,22.5],"prediction_length":96}'

# 不准备数据的话,也可以点一下内置的冒烟接口
curl -X POST http://127.0.0.1:8088/infer_simple

如果只是想在脚本里用,也可以不启服务、直接加载模型:

from transformers import AutoModelForCausalLM
model = AutoModelForCausalLM.from_pretrained("本地模型目录", trust_remote_code=True).to("npu:0")
seqs = torch.randn(1, 2880)                  # 历史序列
pred = model.generate(seqs, max_new_tokens=96)  # 未来 96 点

5. 冒烟验证(Smoke 验证)

部署完先别急着接业务,花十秒钟确认三个接口都通:

  1. GET /health 应返回 {"status":"ok","device":"npu:0","model_loaded":true,...};
  2. POST /infer_simple 会用内置的随机序列跑一次前向,返回 shape [1, 96] 的预测;
  3. POST /infer 传入真实数据应返回 forecast、shape、elapsed_ms 等字段。

三个接口都返回 200,就说明模型加载、前向、序列化三个环节都没问题,可以往下走。

6. 性能数据(性能参考)

在单张 Ascend910 上、float32 精度下实测:

项目结果
模型加载约 1.92 s
稳态单次推理9~10 ms
首请求(算子预热)约 185 ms
持续压测吞吐25 秒约 2770 次,平均约 110 次/秒
推理时 NPU 显存约 6203 MB / 65536 MB
推理时算力占用AICore 18%~20%
服务进程显存约 616 MB

可以看到 84M 的小模型在这张卡上是非常富余的,单次预测 10ms 级别,即便扛不住高并发,横向加进程或者换 batch 也能轻松应对。对大多数业务来说,这台机器的算力瓶颈基本不在推理侧。

7. 精度说明(精度测评)

精度这块我们的处理原则是"不引入额外误差":

  • 全程 float32,不开混合精度,数值路径与官方 PyTorch 完全一致;
  • 适配只改了 API 兼容层,不碰权重、不改算子语义,因此 NPU 上的输出和 GPU 上应保持一致;
  • 模型自带 RevIN(实例归一化),输出会还原到输入的量纲,所以预测值和输入序列的数值区间是对得上的。

我们用一段"趋势 + 正弦周期 + 噪声"合成的 2880 点序列做了验证:输入均值约 21.44、标准差约 1.72,输出 96 点预测均值约 22.64、标准差约 0.33,数值上平滑地承接了输入末端的上行趋势,符合零样本预测的预期。生产环境建议再用自己的历史数据做一轮 MAE/MSE 评估,看模型在你的业务序列上的实际贴合度。

8. 使用注意事项

  • 再次提醒:不要尝试用 vLLM 文本管线去 serve 这个模型,架构不支持;本仓库的 inference.py 就是为这个模型定制的服务。
  • transformers 版本敏感:模型远程代码原本针对 4.40.1,若你用未打补丁的原版文件在 4.57 环境跑,会遇到 DynamicCache 接口报错。本仓库的两个 modeling_timer.py / ts_generation_mixin.py 已修好。
  • 输入长度要求:序列长度需是 96 的整数倍,推荐直接用 2880(模型预训练时的上下文长度);不够的会自动截取。
  • 换卡:默认用 NPU 0,人多共机时记得用 --device 指到空闲卡、--port 换到空闲端口。
  • 预测长度:默认输出 96 点;需要更长就传 prediction_length,服务会按 96 步滚动拼接。

9. 仓库文件说明

timer-base-84m/
├── inference.py         # FastAPI 服务:加载模型 + /health /infer /infer_simple
├── README.md            # 本说明
├── requirements.txt     # 依赖清单
└── assets/
    └── README.md        # 部署与调用细节补充