type
status
date
slug
summary
tags
category
icon
password
随着LLM的开枝散叶, 时间序列模型也逐渐走出了“一事一议“的模式,开始往”通用推理能力“迈进。当前,该通用能力的建设主要依赖于两种框架,一种是利用原本已经预训练好的LLM,经过一些对input结构或output结构进行时间序列数据上的fine-tune或者融合为prompt Engineering的方式进行预测(大部分)。或者是利用LLM的训练机制start training from scratch (chronos)。当前业界公认比较好用的,结构相对比较简单,训练思路直接,我个人也比较推崇的也就是Amazon的工作 chronos
该工作个人主观打分:9.5
打分理由:作为follow LLM在time-series上实现范式的一股清流,其简单有效得令人震惊,而且代码完成度非常高,很容易上手去做预测和微调。整体文章结构清晰,对数据构造、模型选择等做了充分的ablation study,实验量非常充足。没有无脑的卷所谓的创新。在Transformer架构下终于摆脱了PatchTST中Patchify的魔爪。这篇文章让我想起了DLinear,真实的简单有效,”小“而美。
可惜的是,该工作的整体实现强依赖于Amazon自有的时序预测库gluonts,在样本抽样和构造这一块的代码很不好读,如果想自己改造其中的样本抽样逻辑,如果不熟悉的话需要花费很大一番功夫。
Chronos: Learning the Language of Time Series
项目地址:
chronos-forecasting
amazon-science • Updated Feb 6, 2025
作者名单:

摘要
我们发明了Chronos这个简单又有效的预训练的时序预测框架,该框架能够进行概率型的时序预测。Chronos先将时序的值进行归一化,然后放到预先定义的分桶(fixed vocabulary)中来变成一个个的
token_id 。利用现有的Transformer-based框架来进行cross-entropy的训练。Chronos模型选择了T5-famliy作为基模(从20M参数到710M参数),然后在大量的公开数据集上进行训练。从评测上来看,包含了42个评测数据集。benchmark包括经典时序模型(classical local models)和其他基于深度学习的时序模型。我们发现Chronos具有很好的监督学习表现和zero-shot能力。打败了许多模型,与最好的supervised 模型也能掰掰手腕。简介
当前时序模型主要有两个分支,一个是传统的ARIMA、ETS那一类的,另一个是神经网络那一类的。在LLM崛起后,具有很好的zero-shot能力的LLM点燃了其在时序领域内的研究热情。大家在想是不是LLM的zero-shot能力能够迁移到时序预测的工作中,训练一个很好的base-model后就能够一劳永逸了。
当前在LLM&时序工作这一块主要有两个方向,一个是直接对预训练好的LLM模型进行prompt engineering,或者是针对时序任务进行相应的fine-tune。然而这些路径还是有挺多限制,一个是对每一个新的任务都可能需要重新设计有效的prompt,另一个是对每个新时序任务都要进行微调(表现为zero-shot能力不足)。
同期的其他工作也基本上都是朝着复杂了走,没有一个比较简单有效的。
在这个领域中,我们抽离出来想了想,LLM的基模中的预测下一个token 和 时间序列基模中的预测下一个value 到底有什么本质区别?
尽管有一个很明显的区别是时序预测的值是一个无边界的,且常常是连续值,而语言中的token是有边界的,离散的值。但两个模型有的共性是都在努力地基于捕捉到的序列关系去预测下一个值。那既然这样,一个好的LLM模型就应该是一个好的时序模型!Chronos将时间序列中连续的数值进行归一化后再分桶,变成了fixed数量的token_id。既然有了数量固定的token_id,那么就可以用任何LLM的结构和训练代码来直接训练时序,简直是方便到爆炸。而且这么做以后,在实验效果上来看也非常好。

左图:均值归一化、离散化分桶
中图:基于cross-entropy loss来训练模型
右图:基于模型输出的序列,将token_id恢复为对应的数值。
由于LLM的参数量通常比较大,对于NLP任务来说,现有的语料资源很充足,但对于Time Series来说,相比之下,现有的公开的数据集还是太少了。所以不得不用一些数据增强的手段。这里提出了两种数据增强的手段:TSMixup 和 KernelSynth
TSMixup 随机地将不同训练数据集中的时序进行抽样和混合。
KernelSynth 通过高斯过程(Gaussian Processes)来生成合成数据(随机挑选的组合kernel函数)
我们综合的benchmark评价方式包括了42个数据集,囊括in-domain和zero-shot的能力测试。选用的baselines包括传统模型和深度学习模型,以及其他的LLM-based时序模型。【注意看】Chronos有优秀的zero-shot能力,一点儿也不需要调!
背景知识和相关工作
Time series forecasting 任务定义
Formally, given a uniformly-spaced time series , we are
interested in predicting the joint distribution of the next steps, .
In this work, we focus on univariate forecasting, where the observations are scalars, i.e., for all .
经典时序模型(只能一事一议) | 深度学习时序模型(有一定通用性) |
ETS | DeepState (RNN-based)(Rangapuram et al., 2018), |
ARIMA | DeepFactor(Wang et al., 2019), |
Theta | DeepAR(Salinas et al., 2020), |
ㅤ | TimeGrad(Rasul et al., 2021) |
ㅤ | TFT(Lim et al., 2021) |
ㅤ | PatchTST(Nie et al., 2023). |
ㅤ | Informer - point forecast |
ㅤ | DLinear - point forecast |
LLMs 能力定义
Given a sequence of input tokens, , language models aim to predict the next token, , by modeling the conditional distribution,. The tokens belong to a vocabulary, , and maybe characters, subwords (Sennrich et al., 2015), or words, depending on the tokenization scheme used. 可以看见和时序的任务定义其实非常的像
现代的许多LLMs都是基于Transformer的结构,原本最初的Transformer是encoder-decoder的结构,encoder将输入变成一个连续的表征,decoder基于这个表征和之前的输入,一个一个的产出token。许多popular的模型,例如BART和T5就属于这种(Encoder-Decoder架构)。另外一种流行的结构是Decoder-only的,例如GPT-3, LLama 2。LLM一般都会在大量的百亿千亿级别的语料库中进行训练。
LLM-based forecasters
LLM-Based 时序预测模型有许多流派:
- 其中一个流派将数值型的时序数据直接当成文字,然后用LLM进行预测(几乎不经过微调,或者经过很少量的微调).PromptCast 就是这样,将预测问题改造为一个QA结构,利用LLM进行预测。
- LLM-Time提出了一个新的tokenization框架,将实数数值编码成一个固定精度的字符串(有合适的scaling策略),一旦encode 了,就能利用LLM进行预测了。但是这种利用如此大规模的LLM,预测起来会很慢
- Zhou et al 提出GPT4TS,通过将一个pretrained-GPT2,对每一个时序任务只微调其positional emebdding和LayerNorm中的参数。他们没用tokenization,而是直接把相应通过patchify得到的time embedding进行输入,类似PatchTST。
- 另外一个同期工作 Time-LLM,将time-series-patches和一个叫text prototypes的emebdding 进行对齐。然后将这些东西作为prompt输入,再加上一个任务描述的前缀输入合起来输送给冻住的LLM进行预测。Time-LLM和GPT4TS都需要针对每个任务进行fine-tune和test改造。特别是他们都基于预训练的LLM,然而Chronos不基于预训练的LLM,而且不用每个任务都fine-tune
Zero-shot forecasting
Chronos uses a categorical distribution to model the observations, performing regression via classification.
模型结构 Chronos: A Language Modeling Framework for Time Series
While both language and time series are sequential in nature, they differ in terms of their representation — natural language consists of words from a finite vocabulary, while time series are real-valued
our design philosophy involves making minimal changes to the model architectures and training procedure. 从原理上尽可能少的改变LLM的结构和训练方式
Time-Series Tokenization
Scaling 归一化
这里很简单,用的就是把每个输入的时序先经过一个归一化操作。归一化有很多方法,这里用的是 mean scaling. Mean scaling normalizes individual entries of the time series by the mean of the absolute values in the historical context. Specifically, this involves setting
Quantization 离散化分桶
得到归一化后的值仍然是个实数值,需要转换成固定的词表来获得token_id,一个简单的策略就是离散化分桶。分桶的操作有很多,可以进行值域等分,也可以进行分布均分。

Quantile bining,基于分布的等分是一种data-dependent的分桶策略,根据实际的分布来划分每个桶范围的宽窄,目的是尽量让每个桶的数量相对平均。然而,平均分桶就是根据值域范围进行均等宽度的分桶。由于data-dependent分桶方式会随着数据分布的变化而显著变化,所以这里用了均分策略。
除了分桶得到的token_id,和大多数LLM一样,这里加入了
PAD 和 EOS 这两个special token(index 分别为0 和1)。这里没有 BOS 。PAD token对不同长度的时序进行补全对齐,来达到批量化的固定长度。同时也能替换时序中的缺失值。EOS token来标志该时序的结束时刻。在许多时序相关的工作中,会把时序的频率(h,min,monthly)、时间戳的特征囊括进去(day of week, day of month, month of year, etc),可能比较反直觉,我们这里忽略这些特征,仅仅将时序值当成一个序列。
我们主要聚焦于T5-family上的时序实验,不过为了证明该框架的有效性,我们同样在GPT-2这种 decoder-only 的架构上也实现了。除了词表需要重新学习之外,LLM的架构完全不用动。
Objective Function 目标函数
和大多数LLM相同,目标函数使用了Cross-entropy。是通过”分类“的方式来做到了回归. 这不像典型的时序预测模型用高斯或者student-t分布或者是用quantile-regression来做回归。
注意,categorical cross-entropy loss 并不是一个 distance-aware 的目标函数,也就是说,这个目标函数并不会认为 bin_i 和 bin_i+1 的距离 比 bin_i 和 bin_i+100 的距离短。
这种通过分类来进行回归任务的形式有两个好处:
- 对训练、模型架构没有任何改变(便捷)
- 对输出的分布没有任何限制(灵活)
尽管把这样的输出构造成ordinary形式的预测任务可能更合适,事实上这个课题已经被充分调研过 (McCullagh, 1980; Winship & Mare, 1984),
Forecasting
很简单,这个就是用LLM的forecast功能(
model.generate()),在”probabilitic forecast“方面体现的是用模型输出多个序列,然后把这些序列的token_id找到对应的分桶代表的值,然后再scale回去。模型输出N个序列,每个序列在每个时刻可以计算quantiles,mean,median等统计值。
数据增强
TSMixup:Time Series Mixup 时间序列混合

Mixup 是一种从图像分类中提出来的数据增强框架,Keras对这一个概念有一个Tutorial:https://keras.io/examples/vision/mixup/

样例代码:
具体这里的TimeMixUp,首先会从训练数据集中随机选择个等长的时间序列,,
长度为, ,将他们标准化后,组成一个凸集:
其中 表示第 个 scaled 后的时间序列。 时间序列先标准化(scaled)后再组合,避免了量纲较大的时间序列占据了组合时间序列的主要部分。组合权重 是从一个对称狄利克雷分布中抽取的(Symmetric Dirichlet distribution),
伪代码:

不熟悉dirichlet distribution的同学,我建议读这一篇讲解文章。至于这里为啥要用Dirichlet distribution,我认为目标首先是限制这个组合权重的和必须为1,然后每个组合lamdba的变动范围尽量自由一些。如果自己写这个权重抽样函数,还得先抽十个整数,再归一化变成权重。这里说白了就是偷个懒,直接用一个uniformly distribution的Dirichlet distribution,抽出来的一行天生就符合这个和是1的要求,代码实现上更优雅一些

KernelSynth: Synthetic Data Generation using Gaussian Processes 用高斯过程合成时序数据
尽管TSMixup增加了时序模式的多样性,但对于想训练一个通用的时序模型来说可能仍然是不够的,尤其是对于时序数据本身较小的情况下。为了进一步补充训练数据集,我们提出了KernelSynth,一种用高斯过程(Gaussian Processes,GPs)来生成人工合成的时间序列的方法。
We use the inverse of this process —
randomly compose GP kernels to generate new time series.
GPs是一个由 mean function 定义的函数的分布。(distributions over functions),和positive definite kernel , where is the domain. 这个kernel定义了任意两个时刻的协方差函数 , 我们构建了一个kernel 库 , 我们的时序的模式对应的kernel就从这个库中抽样组合而来。这个库中包括 线性核(趋势),高斯核(RBF kernel)(平滑的局部变化),还有周期核(Periodic kernels)(为了seasonality)。这些核能够组合成各式各样的典型的时序特征,最终的kernel,, 是由多个从kernel库 中抽样(with replacement)的个kernels组合而成:. 这些核一经抽出,随机将这些核进行加法或乘法的组合。一个合成的时序是通过从GP先验抽样一个长度为 样本而来,

伪代码:

关于Gaussian Processes
可以看看这个可视化可交互的博客:https://distill.pub/2019/visual-exploration-gaussian-processes/#Multivariate [A Visual Exploration of Gaussian Processes]
高斯过程这个概念还是挺绕的,特别是融合到用gaussian process去拟合一些data的时候,我们要分清这个背后的过程和平时学的linear regression是完全不一样的(尽管linear regression背后还是有一些基于高斯分布的假设)。具体可以看Exploring Gaussian Process vs Linear Regression
实验部分
在这部分,主要聚焦于benchmark的实验性分析。将数据集分为三个部分
- Pretraining-Only:只进行预训练,包括13个数据集总共7
- Benchmark I:叫 in-domian evaluation,包含15个数据集,在这些数据集上训练、评测
- Benchmark II:叫 zero-shot evaluation,包含27个数据集,仅针对这些数据集进行零样本测试

Overall, we used 28 datasets (13 + 15) for training Chronos models, consisting of about 890K univariate time series with approximately 84B observations (tokens) in total.
数据集构成
搜集了网上很多公开数据集,横跨许多领域,包括能源,交通,健康,零售,网络,天气和金融。样本的粒度从5分钟到年级别,跨度也挺大。总的来说,数据集包括55个各种各样渠道的数据,包括 比较常用的 Monash Time Series Forecasting Repository,the M-competitions,和Kaggle上的公开时序数据集。
对于 In-domain 数据集和 zero-shot 数据集中的每一条time series,我们用最后的 个数据点来作为 held-out test set。所有的模型都是在这些held-out test set上进行评价的,没有任何一个模型用这些set来作为训练。,每一个不同的任务对应了不同的预测长度 。
下面是数据集的明细



Training Corpus and Protocols
我们在实验中选择了 T5 模型作为主架构,T5的参数量从16M(Tiny) 到 11B(XXL),有很多选择。此外,为了证明我们的这个框架有效性,也选择了decoder-only的GPT-2进行实验。
我们最终选择了4个不同参数量的T5,也就是 20M(mini),46M(smal),200M(base),和 710M(large)。然后选了一个GPT-2的base模型(90M)。
用TSMixup创造了10M的训练数据(基于28个数据集),K=3,通过这种设置,原本的真实时序数据得到了充分的采样,因为一条时序数据大概有1/3的概率会被采样。
用KernelSYnth创造了1M的训练用的合成数据。
我们以9:1的概率来组合TSMixup数据和KernelSynth得到的合成数据用来训练。每个模型都用256的batch-size(如果不够的话就用gradient accumulate策略)。
分桶的范围在(均值标准化后)[-15, 15] 的区间,Vocabulary的大小是4096,其中包含2个special token(
PAD和EOS),一条时序的上下文长度设置为512(是T5模型的默认值)。预测长度设为64。长度超过64的任务我们仅在评价阶段考虑模型训练参数:
- 200K steps,
- AdamW优化器
- weight decay 0.01
- lr = 0.001, 逐渐衰减到0
- 其他的超参和trainers中的默认值一样
- 8个A100 训练的
Evaluation Metrics
WQL 加权分位数损失

这里的quantile选择了[0.1, 0.2, … 0.9] 9个。WQL是一种比较好的基于quantile的损失,当一个业务中低估和高估造成的影响不一样时,比较适合用特定的quantile去计算WQL损失来衡量这个时序预测模型在业务侧的预测价值。在公式14中, y_{i, t}是标签值,f^{q}_{i,t} 是quantile是q时,的预测值。
实现代码:

WQL衡量了一个模型预测的波动性(不稳定性),MASE仅仅从点预测来看准确率
MASE

Main Results
结论:
Chronos models comfortably outperform statistical baselines and other pretrained models, while performing on par with the best deep learning models trained on these tasks.


这里的Benchmark II指的是 Chronos 是zero-shot,但别的模型仍然是在这些datasets上训练过的。
还有一个价值点是可以看到 Chronos-T5 是遵从scalling law的。参数量越大的表现越好。

chronos-t5 也有很好的fine-tune的表现
Analysis of Hyperparameters
这里相当于是ablation study,从模型参数量,权重初始化方式,训练步数,合成数据比例,上下文长度,和Vocabulary size这几个角度对chronos-t5的表现进行了研究
模型参数量
Unsurprisingly, the training loss improves with the model capacity

权重初始化方式
Overall, these observations suggest that language model weights are not particularly remarkable in the context of time series forecasting and offer no improvement over random initialization.
These results suggest that LLM initialization offers relatively little
advantage in the context of time series forecasting, and instead random initialization may be the preferable choice.

直接随机初始化 v.s. 用LLM(Trianed on C4 dataset)的权重初始化。可以看到LLM在NLP上的预训练权重在时序上的表现并没有增益,不如直接随机初始化。

训练步数、上下文长度、词库数量

都不是越大越好
合成数据比例
TSMixup

从上图的左图可以看到对于zero-shot的场景,TSMixup还是很有用的(越低越好)。
KernelSynth
右图看到的是KernelSynth中合成数据占据训练集的比例对模型学习的效果的影响。可以看到子啊0-10%的时候是单调下降的,但如果训练比例增大,会有有害效果。这里被认为是脱离了真实的数据分布,所以越训练越坏。
Qualitative Analysis and Limitations

代码部分
项目:
chronos-forecasting
amazon-science • Updated Feb 6, 2025
首先得把对应的模型从huggingface上下载下来

预测
训练
训练需要安装这个项目中的training 部分,详情参考https://github.com/amazon-science/chronos-forecasting/tree/main/scripts
将你的数据组织成 list of numpy univariate array 的形式(list中每个元素可以不等长,试过了), 然后经过这个scripts的脚本中提到的方式转为Arrow的数据。后训练的fine-tune脚本可以直接在配置文件中指向你生成的Arrow数据。
数据集抽样函数
在运行chronos微调的函数时,有一点比较迷惑,我到底是应该把数据组织成啥样才能比较理想地适配他的训练函数。比如我有时序数据长下面这样:
- 6000行4列的多变量时序
- 4500行4列的多变量时序
- …
我到底是应该把上述时序直接拆成单变量的,然后直接放到一个list就完事儿,还是得亲自手动切成论文中提到的512长度的?
为此,我去读了一下它的Dataset类的实现,是真不太好读。
Train.py
在
main函数中, 会读取配置文件中的 training_data_paths 项,经过一系列合法性检查,会落到先看看
FileDataset 是怎么构成的:FileDataset 是gluonts中dataset的一个函数实现,主要是检查各种路径的问题,后返回这里的
file_dataset是一个偏函数,这个函数从loaders中读取数据后返回数据值。如果loaders的长度为1(只有1个数据集,直接返回),否则返回DatasetCollection类。file_dataset中最重要的是这个 _FileDataset函数感觉也没干点啥,主要是工程师的一些优化。
回到train.py中的main函数,在建立了train_dataset后,包装为
ChronoDataset类,由于是用作训练,所以shuffle设置为TrueChronoDataset类继承了IterableDataset 和 ShuffleMixin两个类,对于该类的作用,official implementation有以下描述主要是用来将时序数据转换为 input_ids,attention_mask和labels的,
这个类主要看其重写的
iter 方法我们先只关注train data的部分,首先是数据的预处理
其中再次用到了偏函数来初始化
preprocess_entry ,对每一个datasets生效可以看到这个entry首先是一个字典,包括两个key:
start, targettarget是时间序列,转换为
numpy.array的数据格式, 要求是1维的时序。这里model_type基本上都是seq2seq,所以第一个if可以忽略。drop_prob这里也可以忽略。create_training_data对于一个数据集,包装成Cyclic后,进行一个
_create_instance_splitter 的动作,然后再对全是空值的时序丢掉。这个
instance_splitter调用了ExpectedNumInstanceSampler 类,然后塞入InstanceSplitter 。首先ExpectedNumInstanceSampler 是gluonts中的一个sampler的一个类,功能是给一个序列后进行一系列不等长的indexes的抽样这里是对每个dataset都能够抽出一个indices的数据点,例如:
InstanceSplitter对数据进行抽样,ExpectedNumInstanceSampler负责抽index,InstanceSplitter负责将index之前的变为输入,之后的变为输出部分。如果输入如不够,则left-pad (with np.nan)
itertools.islice的基本用法为:
itertools.islice(iterable, start, stop[, step])
可以返回从迭代器中的start位置到stop位置的元素。如果stop为None,则一直迭代到最后位置。
https://www.jianshu.com/p/4e0344191895

这里worker_info,在单机环境下就是None。有多卡情况下不为None。这里应该就是每个worker_id为起点,分别开始抽间隔为num_workers的样。
一旦预处理完成后,就开始对数据集(datasets)抽样,比如有10个数据集,每个数据集有N条时序,这里是对数据集抽样,概率为probs。抽样后产出 to_hf_format
数据抽样部分的结论 — 该如何准备你的训练数据
将你的训练数据首先切成univariate的,长度只要是大于 input_length+pred_length就好。然后作为一个元素放进list中,在训练时,会从该元素中进行随机抽样。 在自己训练时,训练代码中不包含TSMixup的设置。如需要,那么应该自己写这部分代码,生成相应的数据集(Arrow格式)后,再进行训练
- Author:Yixin Huang
- URL:https://yixinhuang.cn/article/timeseries-forecasting-llm-chronos
- Copyright:All articles in this blog, except for special statements, adopt BY-NC-SA agreement. Please indicate the source!



