SMETimes:用小语言模型做时间序列预测
论文推荐:《Small but Mighty: Enhancing Time Series Forecasting with Lightweight LLMs》
原文代码:https://github.com/xiyan1234567/SMETimes
原作者上传至github的代码只适合在linux系统上跑,小淘将其改成了在windows上也可以跑的格式。并整理代码放在了这里:https://pan.baidu.com/s/1sK49HrME8CJYywF_e4viiQ 提取码: pqfg
写在前面
时间序列预测领域,正在经历一场不亚于当年 "GPT 时刻" 的范式转移 —— 大语言模型正在以前所未有的速度杀入这个传统地盘。将大模型引入时序预测,最经典的三个代表作你不可错过:
-
• 第一篇代表作: NeurIPS 2023 Spotlight 的FPT(One Fits All)—— 直接把 GPT-2 的预训练 block 冻住,加个轻量适配器,一个模型通吃预测、分类、异常检测等多种时序任务,MSE 比 FEDformer 直接降了 18.7%。它第一次石破天惊地证明:语言模型里学到的东西,真的能迁移到时序上。
-
• 第二篇代表作:ICLR 2024 的Time-LLM—— 不重训模型,只 "重编程" 输入。把时间序列补丁翻译成 LLM 看得懂的文本原型,再配上 Prompt-as-Prefix 提示,LLaMA-7B 原封不动就能拿来做预测,零样本迁移能力直接拉满。时序分析,第一次被真正当成了一门 "语言任务"。
-
• 第三篇代表作:NeurIPS 2024 清华 THUML 团队的AutoTimes问了个更根本的问题:LLM 最强大的自回归能力,为什么在时序里没用上?于是他们把时间序列直接投影到语言 token 的嵌入空间,让 LLM 像生成文本一样,一个 token 一个 token 地 "续写" 未来 —— 任意长度的预测、上下文学习、多模态适配,通通原生支持。
然而本期的主角却不是Time-LLM、AutoTimes和FPT ,这三种方法证明的一件事是:虽然大语言模型主要是在文本上预训练,但其内部形成的序列建模能力,也可以迁移到温度、负荷、气象和光伏功率等连续数值序列上,证明了预训练语言模型确实具备一定的序列建模能力。
问题也随之而来。
为了预测一段并不长的时间序列,我们真的需要动用数十亿甚至上百亿参数的大模型吗?如果主干模型过于庞大,显存占用、训练时间和推理成本都会显著增加。对于边缘设备、工业终端和普通实验室而言,这类方法往往“论文效果很好,实际跑起来很重”。
本期要推荐的SMETimes 的出发点正是如此:
与其不断扩大语言模型,不如使用更轻量的小语言模型,再通过针对时间序列设计的结构补偿其能力。
论文提出了一套面向时间序列预测的轻量化框架。它没有简单地把大模型缩小,而是围绕“数值序列如何被语言模型理解”设计了三项关键机制:
-
1. 统计增强提示结构:把时间范围、均值、标准差和变化量写成文本,让连续数值获得可解释的语义描述;
-
2. 自适应融合嵌入:动态融合原始数值表示与文本语义表示;
-
3. 动态专家混合结构:让不同专家分别学习不同类型的时间模式。
SMETimes 没有继续扩大模型,而是选择使用 3B 甚至1B参数以内的小语言模型,并通过专门设计的时间序列表示方法提升预测效果。
论文的核心思想可以概括为一句话:
不让语言模型直接理解原始数字,而是同时给它“数值表示”和“统计语义表示”,再让模型自己学习如何融合。

接下来咱们就详细讲一下,SMETimes是如何用轻量的小语言模型,再通过针对时间序列设计的结构补偿其模型能力,来实现时序预测的。
一、SMETimes 的整体流程
模型输入是一段历史时间序列。论文通常使用长度为 672 的历史窗口,并将其切分成多个长度为 96 的时间片段。
每个片段会经过两条处理路径。
第一条是数值路径。模型直接将原始时间序列片段送入线性层或 MLP,将长度为 96 的数值片段映射为与语言模型隐藏维度一致的向量。
第二条是文本路径。模型会计算该片段的时间范围、均值、标准差和首尾变化量,并将这些信息组织成一段文字,例如:
time range: 2023-01-03 08:00:00 to 2023-01-07 07:00:00
mean: 0.1527
standard deviation: 0.0831
change: -0.0264
这段文字随后被送入冻结的小语言模型,得到一个语义向量。
数值向量保留了原始波形的细节,文本向量则提供了对该时间片段的整体描述。模型通过一个可学习的门控参数,将两种表示进行融合,再送入语言模型主干和专家混合模块,最终得到未来时间序列的预测结果。
整个过程可以简化为:
原始时间序列
↓
切分为多个时间片段
↓
数值片段编码 ──────────────┐
├─ 自适应融合 → 小语言模型 → MoE → 预测结果
统计量与时间信息 → 文本编码 ┘
SMETimes 的重点并不是让语言模型“读懂所有数值”,而是为语言模型提供更容易理解的时间上下文和统计信息。
二、三个核心设计
1. 统计增强提示
时间序列是连续数值,而语言模型主要处理离散文本,两者之间存在明显的模态差异。
SMETimes 的解决方法是先把一个时间片段转化为简洁的文字描述。对于每个片段,模型主要计算三个统计特征:

其中,均值表示该片段的整体水平,标准差表示波动程度,首尾差值表示基本变化方向。除此之外,模型还会加入该片段的开始时间和结束时间。
这一步相当于为每段时间序列添加一张“信息卡片”。原始数值告诉模型具体波形,统计文本则告诉模型这一段数据大致处于什么水平、波动是否明显、整体是上升还是下降。
从代码来看,这部分主要位于:
data_provider/data_loader.py
models/Preprocess_Llama.py
preprocess.py
data_loader.py 负责生成统计描述,Preprocess_Llama.py 负责调用冻结的语言模型编码文本,preprocess.py 则会将全部文本嵌入提前计算并保存为 .pt 文件。
这种离线预计算方式比较实用。统计文本不会随着训练轮次发生变化,因此没有必要在每一轮训练中重复调用语言模型。先生成语义特征,再进行正式训练,可以减少大量重复计算。
论文中给出的统计结构的信息如下:

代码中生成的文本如下:
time range: 2023-01-03 08:00:00 to 2023-01-07 07:00:00
mean: 0.1527
standard deviation: 0.0831
change: -0.0264
2. 自适应融合
得到数值向量和文本向量后,模型需要决定两者各自占多大权重。
SMETimes 使用一个可学习的门控参数:

融合结果为:

其中,(SE_i) 是数值嵌入,(TE_i) 是文本语义嵌入。
当α更接近 1 时,模型更依赖原始时间序列;当 α 更接近 0 时,模型更依赖统计文本。训练开始时,该参数通常初始化为 0,经过 Sigmoid 后等于 0.5,也就是先让两种表示各占一半,再由模型根据数据自动调整。
这种方法比直接相加更合理。因为统计信息只能描述一个片段的整体特征,无法完整表示局部峰值、频率变化和细节波动。如果文本信息权重过高,可能会丢失原始波形;如果权重过低,统计提示又无法发挥作用。
需要注意的是,当前公开的 LLaMA 代码中,这个门控值更接近一个全局可学习参数,而不是针对每个样本分别计算的门控网络。因此,它能够学习整体融合比例,但还不是完全意义上的样本级动态融合。
这里附上自适应融合的代码:
def forecast(self, x_enc, x_mark_enc, x_dec, x_mark_dec):
# 1. 对输入时间序列进行标准化
means = x_enc.mean(1, keepdim=True).detach()
x_enc = x_enc - means
stdev = torch.sqrt(
torch.var(x_enc, dim=1, keepdim=True, unbiased=False) + 1e-5
)
x_enc /= stdev
# 2. 调整维度,将每个变量作为独立时间序列处理
bs, _, n_vars = x_enc.shape
x_enc = x_enc.permute(0, 2, 1)
x_enc = x_enc.reshape(x_enc.shape[0] * x_enc.shape[1], -1)
# 3. 将长时间序列切分为多个固定长度的片段
fold_out = x_enc.unfold(
dimension=-1,
size=self.token_len,
step=self.token_len
)
token_num = fold_out.shape[1]
# 4. 自适应融合数值嵌入与文本语义嵌入
if self.fusion_gate is not None:
# 检查文本嵌入维度是否与 LLM 隐藏维度一致
if x_mark_enc.shape[-1] != self.hidden_dim_of_llama:
raise ValueError(
f"time embedding dim {x_mark_enc.shape[-1]} does not match LLM hidden dim "
f"{self.hidden_dim_of_llama}; rerun preprocess.py with the same --llm_ckp_dir."
)
# 通过可学习门控参数控制两种特征的融合比例
alpha = torch.sigmoid(self.fusion_gate)
times_embeds = (
alpha * self.encoder(fold_out)
+ (1 - alpha) * x_mark_enc
)
else:
# 不使用文本语义时,仅保留数值嵌入
times_embeds = self.encoder(fold_out)
# 5. 将融合后的时间序列嵌入送入 LLaMA 进行特征建模
outputs = self.llama.model(
inputs_embeds=times_embeds
)[0]
# 6. 通过 MoE 模块进一步提取不同时间模式特征
outputs = self._apply_moe(outputs)
# 7. 将隐藏特征解码回时间序列
dec_out = self.decoder(outputs)
# 8. 恢复为 [batch, time, variables] 的数据格式
dec_out = dec_out.reshape(bs, n_vars, -1)
dec_out = dec_out.permute(0, 2, 1)
# 9. 反标准化,恢复到原始数据尺度
dec_out = dec_out * (
stdev[:, 0, :]
.unsqueeze(1)
.repeat(1, token_num * self.token_len, 1)
)
dec_out = dec_out + (
means[:, 0, :]
.unsqueeze(1)
.repeat(1, token_num * self.token_len, 1)
)
return dec_out
3. 动态专家混合
时间序列中通常同时存在趋势、周期、短期波动和异常变化。只使用一个统一映射,未必能够兼顾所有模式。
SMETimes 在语言模型输出后加入了 Mixture-of-Experts,也就是专家混合结构。模型设置多个专家,每个专家学习不同的时间模式,同时使用一个门控网络为各专家分配权重。
其基本过程为:



可以把门控网络理解为一个分配器。对于不同输入,它会选择更合适的专家组合。例如,某些专家可能更擅长处理周期变化,另一些专家则更关注突变或长期趋势。
论文实验显示,加入 MoE 后,LLaMA、OPT 和 GPT-2 等不同骨干模型都能获得一定提升,说明该模块并不是只对某一个语言模型有效。
不过,复现代码时需要特别检查 num_experts 参数。当前 run.py 中默认专家数为 1,如果不进行修改,就无法真正体现多专家选择。想测试 MoE 效果时,应显式设置大于 1 的专家数量。
三、实验结果
论文在 7 个常见数据集上进行了测试。
|
数据集 |
变量数 |
采样频率 |
数据类型 |
|
ETTh1 |
7 |
1 小时 |
变压器温度 |
|
ETTh2 |
7 |
1 小时 |
变压器温度 |
|
ETTm1 |
7 |
15 分钟 |
变压器温度 |
|
ETTm2 |
7 |
15 分钟 |
变压器温度 |
|
Weather |
21 |
10 分钟 |
气象数据 |
|
ECL |
321 |
1 小时 |
电力负荷 |
|
Solar-Energy |
137 |
10 分钟 |
光伏发电 |
对比模型包括 AutoTimes、Time-LLM、UniTime、FPT、DLinear、PatchTST 和 TimesNet。
下面是论文中各预测长度平均 MSE 的部分结果:
|
数据集 |
SMETimes |
AutoTimes |
Time-LLM |
|
ETTh1 | 0.387 |
0.394 |
0.415 |
|
ETTh2 | 0.349 |
0.379 |
0.367 |
|
ETTm1 | 0.347 |
0.358 |
0.361 |
|
ETTm2 | 0.267 |
0.276 |
0.273 |
|
Weather |
0.239 |
0.239 | 0.231 |
|
ECL | 0.164 |
0.168 |
0.186 |
|
Solar |
0.207 | 0.203 |
0.248 |
从结果可以看出,SMETimes 在 ETTh1、ETTh2、ETTm1、ETTm2 和 ECL 上表现较好,但在 Weather 和 Solar 上并不是最优。
这说明 SMETimes 的优势并不是在所有数据集上都绝对领先,而是在使用更小模型的情况下,仍然保持了有竞争力的预测精度。
论文同时强调了资源效率。与更大的语言模型方案相比,SMETimes 具有更快的训练速度和更低的显存消耗。对于普通实验室、工业设备和边缘部署场景,这一点往往比单纯降低少量 MSE 更有实际意义。
论文中的预测案例也比较直观。SMETimes 的预测曲线整体较稳定,能够较好跟随真实变化;AutoTimes 的预测波动更强;Time-LLM 的预测则相对保守,在部分位置存在滞后。
预测曲线(这里附上我用1B模型跑出来的结果):

可以看到这是一个滚动的时许预测,用1B模型跑出来的效果也是非常不错的!

四、总结
SMETimes 的主要价值,是证明了时间序列预测并不一定依赖更大的语言模型。
它通过统计增强提示,为原始数值提供了简单的语义说明;通过自适应融合,平衡数值信息和文本信息;通过专家混合结构,进一步处理不同类型的时间模式。
这种设计的优点是结构清晰,也比较适合继续扩展。例如,可以加入更丰富的统计特征、频域信息、趋势和周期描述,也可以将全局融合参数改为样本级门控,并进一步研究稀疏专家路由。
总体来看,SMETimes 给时间序列预测提供了一条很有启发性的路线:
模型不一定越大越好,合适的数据表示、模态对齐和任务结构同样重要。
代码链接放在文章开头了。注意:原始官方代码只能运行于LINUX系统,这里我整理了一份可以在WINDOWS系统跑的版本。当然,你的电脑配置要足够支撑运行一个1B的模型,我用的电脑配置是一个块4060ti,8G的显卡,运行1B模型基本无压力,但是3B模型跑起来是很费劲的。
文章中用的轻量化语言模型是:llama-3.2-1b-instruct和llama-3.2-3b-instruct模型,这两个模型在魔搭社区都能下载,下载方式我都放在压缩包里了,都是免费获取,帮忙留个赞!
**免责声明:本文内容仅用于论文学习、代码复现与技术交流,不构成任何学术、商业或投资建议。文中对论文方法、代码逻辑及实验结果的解读均基于个人学习理解,难免存在疏漏或理解偏差,请以原论文、官方代码及作者说明为准。相关模型、代码及数据版权归原作者或所属机构所有,如有不当之处,欢迎指正交流。
更多推荐

所有评论(0)