PatchTST 时序分块预测:把价格切成一块块「补丁」,Transformer 才真正用得上
Transformer 横扫 NLP 后,直接搬到时间序列预测上却常被简单线性模型吊打——问题出在「逐点建模」:把每个时间步当一个 token,注意力算力爆炸、还学不到局部形态。PatchTST(ICLR 2023)用两招破局:把序列切成一段段 patch(分块)当 token、多变量之间通道独立。本文用纯 numpy 复现「分块 vs 逐点」的核心对比,在趋势+双周期+噪声的自洽合成序列上,两者都把朴素基线的 MSE 从 51.6 压到 2.7,并诚实说明分块在这份干净数据上只领先 2.3%、它真正的价值在哪、以及六类真实陷阱(中阶)。
Transformer 在 NLP 里封神之后,所有人都想把它搬到时间序列预测上。逻辑听着无懈可击:注意力机制能捕捉任意距离的依赖,价格序列不正是充满长程依赖吗?
结果尴尬了。2022 年一篇名字很欠揍的论文《Are Transformers Effective for Time Series Forecasting?》直接证明:一个只有一层的线性模型(DLinear),在多个主流预测基准上把当时最花哨的 Transformer 全都吊打了。 这盆冷水浇得整个领域很难看——难道注意力对时序根本没用?
问题不在 Transformer,而在怎么用。结论先放这:PatchTST(Nie et al., ICLR 2023)用两个极简的改动,让 Transformer 重新在时序预测上站稳脚跟——第一招把序列切成一段段「patch(补丁/分块)」当作 token,第二招是多变量之间「通道独立」。 本文用纯 numpy 复现”分块 vs 逐点”的核心机制,在趋势+双周期+噪声的自洽合成序列上,两种方法都把朴素基线的 MSE 从 51.6 压到 2.7,而分块相对逐点还有 2.3% 的额外增益。数字不大,但背后的道理很重要——下面讲清楚为什么。

一、逐点建模:Transformer 用错地方了#
早期把 Transformer 用于时序,最自然的做法是把每个时间步当一个 token。回看 336 个交易日,就是 336 个 token,扔进自注意力算两两关系。
这有两个致命问题:
问题一:算力爆炸。 自注意力是 复杂度。 还行,但金融里想看更长的历史(一年 250 天、三年 750 天), 直接把显存吃穿。
问题二,也是更根本的——单个时间步没有语义。 在 NLP 里,一个 token(一个词)本身携带完整语义,“苹果”就是”苹果”。但时序里单独一个价格点几乎不含信息:今天收盘 101 块,这个”101”孤立看毫无意义,有意义的是”过去两周从 95 涨到 101 的这段形态”。逐点建模强行让注意力去关联单个价格点和单个价格点,就像让你不看单词、只看一个个字母去理解句子——注意力被迫在毫无语义的原子上打转,自然学不出东西。
这就是为什么简单线性模型能赢:DLinear 直接把整段历史线性映射到未来,反而”整体地”用到了序列形态,绕过了逐点注意力的这个坑。
二、PatchTST 的两招#
招式一:Patching(分块)——给 token 注入局部语义#
PatchTST 的核心洞察:别把单个时间步当 token,把一段连续的子序列(patch)当 token。
具体做法:用一个长度为 的窗口,以步长 在序列上滑动,切出一串 patch。比如 、、,就切出 个 patch,每个 patch 是一段长 16 的子序列。
这一招同时解决了两个问题:
- 语义有了。 每个 patch 是一段完整的局部形态(一个小趋势、一次回调),注意力现在关联的是”形态 vs 形态”,而不是”点 vs 点”。这正好对应技术分析里”看 K 线形态”而非”看单根收盘价”的直觉。
- 算力降了。 token 数从 降到 ,自注意力的 直接缩了近 倍。想看更长历史,成本也可控了。
招式二:Channel Independence(通道独立)#
金融预测常是多变量的:你想同时预测 100 只股票,或者用”开高低收量”5 个变量。传统做法是把多变量在每个时间步拼成一个大向量一起建模(channel-mixing)。
PatchTST 反其道而行:每个变量(通道)用完全相同的一套模型权重,各自独立预测,互不干扰。 100 只股票,就是同一个模型跑 100 遍。
这听着反直觉——不是应该让模型学变量间的相互作用吗?但实证发现通道独立更强,原因有二:一是共享权重 = 天然的数据增强,100 只股票的样本全用来训练同一套参数,等于样本量翻 100 倍,极大缓解过拟合;二是channel-mixing 容易学到虚假的跨变量相关,这些相关在样本外经常翻车。通道独立牺牲了显式的跨资产建模,换来了强得多的泛化。
三、纯 numpy 复现核心机制#
完整的 PatchTST 需要训练一个多层 Transformer,代码量大且训练慢。我们抓住它的灵魂——分块嵌入——用一个可复现的简化版来对比”分块 vs 逐点”:分块方法把序列切 patch、用共享的线性嵌入(这里用 patch 主成分作为固定嵌入基,保证稳定可复现)把每个 patch 压成低维向量,再拼接起来做预测;逐点方法则直接把整段展平做线性预测。两者都用岭回归收尾,公平对比。
先造自洽合成序列——趋势 + 两个周期(50 和 12)+ 噪声:
import numpy as np
rng = np.random.default_rng(20260722)
T = 2600
t = np.arange(T)
trend = 0.02 * t
season1 = 6.0 * np.sin(2*np.pi*t/50) # 中周期 50
season2 = 3.0 * np.sin(2*np.pi*t/12 + 0.7) # 短周期 12
noise = rng.normal(0, 1.5, T)
series = 50 + trend + season1 + season2 + noise
L, H, P, S = 336, 48, 16, 8 # 回看 / 预测 / patch长 / 步长
n_patch = (L - P) // S + 1 # = 41python滑窗构造样本,并做实例归一化(RevIN 的核心思想:每个窗口减去自己的均值,预测完再加回来,专治分布漂移):
def make_windows(x, L, H):
X, Y = [], []
for i in range(L, len(x) - H + 1):
X.append(x[i-L:i]); Y.append(x[i:i+H])
return np.array(X), np.array(Y)
X, Y = make_windows(series, L, H)
ntr = int(len(X) * 0.7)
Xtr, Ytr, Xte, Yte = X[:ntr], Y[:ntr], X[ntr:], Y[ntr:]
mu_tr = Xtr.mean(1, keepdims=True); mu_te = Xte.mean(1, keepdims=True)
Xtr_n, Ytr_n = Xtr - mu_tr, Ytr - mu_tr # 实例归一化
Xte_n, Yte_n = Xte - mu_te, Yte - mu_tepython方法 A——逐点线性,把整段 展平直接映射到 :
def ridge_fit(A, B, lam):
d = A.shape[1]
return np.linalg.solve(A.T @ A + lam*np.eye(d), A.T @ B)
Wp = ridge_fit(Xtr_n, Ytr_n, lam=5.0)
pred_point = Xte_n @ Wp + mu_tepython方法 B——PatchTST 思想:切 patch → 每个 patch 过共享嵌入(主成分基)→ 拼接 → 预测:
def to_patches(Xn):
m = Xn.shape[0]
out = np.zeros((m, n_patch, P))
for k in range(n_patch):
out[:, k, :] = Xn[:, k*S : k*S+P] # 第 k 个 patch
return out
Ptr, Pte = to_patches(Xtr_n), to_patches(Xte_n)
# 共享线性嵌入: 用训练 patch 的前 D 个主成分作为固定嵌入基(无监督、可复现)
D = 8
flatP = Ptr.reshape(-1, P)
Xc = flatP - flatP.mean(0)
_, _, Vt = np.linalg.svd(Xc, full_matrices=False)
E = Vt[:D].T # (P, D)
emb_tr = (Ptr @ E).reshape(ntr, n_patch*D) # 每个 patch 独立嵌入再拼接
emb_te = (Pte @ E).reshape(len(Xte_n), n_patch*D)
Wb = ridge_fit(emb_tr, Ytr_n, lam=5.0)
pred_patch = emb_te @ Wb + mu_tepython四、结果:都碾压朴素,分块小幅领先#
序列长度 2600 | 回看 L=336 | 预测 H=48 | patch长 P=16 步长 S=8 | patch数 41
[MSE] 朴素持平=51.595 | 逐点线性=2.701 | PatchTST分块=2.639
[MAE] 朴素持平=5.869 | 逐点线性=1.322 | PatchTST分块=1.312
分块相对逐点 MSE 降幅: 2.3%plaintext诚实地读这三行:
两种正经方法都把朴素基线打成了渣。 朴素持平(用最后一个值当未来 48 步的预测)MSE 51.6,逐点线性和分块都在 2.7 左右——降了近 20 倍。这说明这份合成序列的趋势和周期结构非常”可学”,任何用了整段历史的方法都能吃到。
分块相对逐点只领先 2.3%。 这个数字我必须如实说:在这么干净、这么规整的合成数据上,patching 的优势很小。为什么?因为逐点线性在如此低噪、结构如此清晰的序列上已经接近上限,没给分块留多少提升空间。别被”降幅 2.3%“这种个位数吓到就否定 PatchTST——它的真正价值不在这种玩具数据上,见第五节。

从单样本预测看,分块和逐点几乎重叠,都很好地还原了周期波动;而朴素持平从第一步就成了一条平线,完全接不住结构。

更有意思的是误差随步长的曲线:分块和逐点在短步长(1-10 步)上几乎没差别,分块的优势主要出现在长步长(30-48 步)的远端——这恰好印证了 PatchTST 的设计初衷:它是为长程预测设计的,短程预测本来就不是它的主战场。
五、这份数据的 2.3% 之外,PatchTST 真正的价值#
玩具数据低估了 PatchTST。它在真实长序列预测里的价值体现在三处,是本文简化版没法完全展现的:
- 算力可扩展。 真实场景想看一年、三年的历史,逐点注意力的 直接崩溃,而 patching 把 token 数砍到 ,才让”超长回看窗口”在计算上可行。序列越长,分块的相对优势越大。
- 通道独立带来的泛化。 本文只做了单变量,没体现第二招。在几百个变量的真实多资产预测里,通道独立的共享权重 = 数百倍数据增强,这才是 PatchTST 拉开身位的地方。
- 对噪声更鲁棒。 patch 内部相当于做了一次局部平滑,比逐点建模更抗单点噪声。合成数据噪声低,这个优势没显出来;真实金融数据噪声大,差距会拉开。
六、六类真实陷阱#
把 PatchTST 用到真实交易上,这六个坑一个都躲不开:
陷阱一:金融时序的信噪比远低于本文合成数据。 这是最根本的落差。合成序列的趋势和周期是”真的”,模型学到就能预测。真实价格接近随机游走,可预测的成分极其微弱——别指望在真实收益率上复现 20 倍于朴素基线的 MSE 降幅,能稳定跑赢朴素持平就已经很了不起。
陷阱二:非平稳与 regime 切换。 合成序列的周期是恒定的 50 和 12,真实市场的”周期”随时会变、会消失。实例归一化(RevIN)能缓解均值漂移,但对结构性 regime 切换(牛熊转换、波动率跳变)无能为力。训练集学到的模式,测试集可能整个失效。
陷阱三:patch 长度和步长是敏感超参。 太短,patch 退化回逐点、失去语义; 太长,token 太少、丢失时间分辨率。 决定重叠程度。这几个参数要在验证集上调,而金融数据上”调参”极易过拟合——你很容易调出一组只在历史上好看的 。
陷阱四:预测价格 ≠ 能赚钱。 本文预测的是价格水平(含强趋势),MSE 好看是因为趋势好预测。但趋势的收益已经被 buy-and-hold 拿走了,真正难的是预测去趋势后的收益率——那才是 alpha 所在,也是信噪比最低的部分。别用”预测价格 MSE 低”来论证策略能赚钱,这是深度学习选股里最常见的自欺。
陷阱五:前视偏差藏在归一化里。 实例归一化只用回看窗口内的均值,这是安全的。但如果你图省事用了全序列的均值/方差做标准化,就偷看了未来——这是时序深度学习里最隐蔽的 look-ahead,回测会莫名其妙地好。
陷阱六:本文是简化版,不是完整 PatchTST。 我用主成分嵌入 + 岭回归替代了 Transformer 编码器 + 多头自注意力,为的是可复现、讲清分块的思想。真实 PatchTST 的自注意力能建模 patch 之间的长程依赖,表达力远强于此。别拿本文的 2.3% 当 PatchTST 的真实上限——那只是”分块这个动作本身”在玩具数据上的边际贡献。
七、总结#
PatchTST 的故事,本质是一个”用对工具”的故事。Transformer 不是对时序无效,而是早期被用错了粒度——让注意力去关联毫无语义的单个价格点。PatchTST 只做了一件真正重要的事:把 token 的粒度从”点”提升到”段”,让注意力终于有了值得关注的东西。
在本文的合成数据上,分块相对逐点只领先 2.3%,因为数据太干净、逐点已接近上限。但那个”误差在长步长上衰减更慢”的曲线,那个”token 数砍到 1/8”的算力账,才是 PatchTST 在真实长程、多变量预测里真正值钱的地方。
给量化实践者的建议:如果你在做多资产、长回看窗口的预测,PatchTST 是当前性价比很高的基线,务必开启通道独立。但请把陷阱四刻在脑子里——预测价格水平的漂亮 MSE,和策略能不能赚钱,是两码事。 先在去趋势收益率上验证它有没有真 alpha,再谈上不上仓位。