深圳融克迪特科技有限公司 Logo,金融科技,量化交易,软件开发

深圳融克迪特科技有限公司

RONG CREDIT TECHNOLOGY CO., LTD.

工具实战

【PyTorch 系列 第2讲】金融特征管线:样本构建、标准化与批处理策略

本讲承接第1讲张量基础与训练循环实现,系统构建面向量化金融时序建模的PyTorch特征工程管线。聚焦样本切片逻辑、多粒度标准化(跨资产/跨时间/跨因子)、动态批处理策略(变长序列填充、滑动窗口对齐、标签滞后一致性)三大核心任务,详解Dataset/Dataloader定制方法、transform组合设计、数值稳定性边界条件及常见内存泄漏与梯度错位报错的根因排查路径。

2026-04-22 智铨研究 阅读时长 17 分钟

目录

PyTorch · 工具实战

  1. 第 1 讲【PyTorch 系列 第1讲】张量、自动求导与训练循环基础
  2. 第 2 讲【PyTorch 系列 第2讲】金融特征管线:样本构建、标准化与批处理策略

一、为什么做 PyTorch 量化建模时,最先出问题的往往不是模型,而是特征管线

很多人刚开始用 PyTorch 做量化,会把主要精力放在模型结构上。是用 LSTM 还是 Transformer,是不是要加注意力,是不是要做多任务输出,这些问题当然重要,但在真正能跑起来的研究流程里,最先把结果拖垮的往往不是模型,而是特征管线。原因并不复杂。量化数据不是干净整齐的图片或文本,而是带着时间顺序、缺失、停牌、复权差异、不同交易日历和不同量纲的一整套时序数据。只要样本构建、标准化和批处理有一环做得粗糙,后面的模型即便结构再先进,也只是在错误的输入上认真学习。

所以这一讲不急着讲更复杂的网络,而是先把输入层真正讲扎实。你要知道,PyTorch 量化特征管线并不是“把 DataFrame 转成 Tensor”这么简单,而是要明确几个关键问题:一条样本序列到底从哪里开始、到哪里结束;标签和输入之间有没有时间穿越;不同量纲的因子如何统一到适合网络学习的尺度上;同一批次里长短不一的序列如何安全送进模型而不让填充噪声污染损失函数。只要这些问题讲清楚,后面的模型选择才有意义。

二、先立边界:特征管线的任务不是替模型做判断,而是把数据整理成模型可以诚实学习的样子

在量化研究里,特征管线最容易被低估。很多人把它理解成一个纯工程环节,仿佛只要把数据读进来、切个窗口、做个标准化就结束了。其实不是。特征管线虽然不直接给出交易结论,但它决定了模型到底看到了什么、没看到什么,以及看到的东西有没有被处理得失真。

比如你预测未来五日收益,如果样本窗口不小心把未来一天的价格也混进输入里,模型很快就会表现得“非常聪明”;可那不是学到了规律,而是偷看了答案。再比如你把成交额、市值对数、波动率和行业哑变量一起喂给网络,却没有处理量纲差异,结果模型的前几层更新几乎全被大数值特征主导,小幅度但有用的因子长期学不到。这些都不是模型架构问题,而是特征管线在一开始就把学习环境弄歪了。

因此,这一层真正的任务,不是把数据转成张量这么简单,而是把数据整理成模型可以“诚实学习”的样子。所谓诚实,就是既不给它偷看未来的机会,也不因为粗暴处理让某些特征被夸大、某些特征被淹没,更不能让无效填充位和真实样本一起参与损失计算。只要这三件事没守住,后面任何高阶模型都不值得信任。

三、样本构建:最先要守住的是因果顺序,而不是窗口看起来多完整

金融时序样本构建的核心,不是窗口长度设多少,而是时间因果顺序是否被严格守住。假设你要用过去六十个交易日的数据预测未来一天收益,那么每一条样本的输入就必须只来自这六十天的已知信息,标签则只能来自窗口之后的那一天。这个原则听起来简单,实操里却最容易被各种偷懒写法破坏。

最常见的问题有两类。第一类是索引偏移搞错。比如窗口切片写成从 ii+window_len,标签又取成 i+window_len+1,结果实际预测的不是紧接窗口后的收益,而是更远的未来,研究者却以为自己在做短期预测。第二类是预处理阶段提前看到了未来。例如先用全样本做缺失填补、再切时间窗口,看起来数据更完整了,实则把未来信息混进了过去。

更稳妥的做法,是先按时间顺序完成原始数据清洗,再逐条构建窗口,并且在构建样本时明确记录每条样本对应的结束日期和标签日期。这样做虽然比一把切矩阵慢一些,但好处非常大:你后面不管是排查标签定义、检查回测对齐,还是分析某个异常批次,都能知道问题到底发生在哪个时间点。量化研究里,样本构建最怕“代码看起来很紧凑,错误却埋得很深”。宁可把索引关系写得清楚一点,也不要只图一时简洁。

四、窗口长度、标签滞后和步长,不是三个独立参数,而是一组一起决定样本语义的东西

做时序建模时,很多人会把窗口长度、标签滞后和滑动步长看成三个随手可调的参数,实际上它们共同决定了一条样本的语义。窗口长度决定模型能看到多长的历史;标签滞后决定你到底在预测多远的未来;步长则决定样本之间有多密、彼此有多像。

窗口越长,不代表一定越好。长窗口能让模型看到更完整的趋势和状态切换,但也更容易把已经失效的历史状态拖进来,尤其是在市场结构切换较快的时候。标签滞后也不是越短越真实。预测次日收益看起来更贴近交易,但噪声通常更大;预测五日或十日收益,信号可能更稳,却又要求你在输入构建上更严格地处理时间间隔。步长则决定样本冗余程度。步长为 1 时,样本利用率最高,但相邻样本几乎只差一天,彼此高度相似;步长更大时,样本相关性会下降,但训练样本数也会变少。

所以这三个参数不该孤立看,而应一起理解。你设定的窗口、标签和步长,实际上是在定义模型看到的市场片段到底长什么样。只要先把这层语义讲明白,后面不管你是做日频选股、行业轮动还是事件预测,都更容易知道自己的样本到底对应哪一种研究问题。

五、标准化不是“统一缩放”这么简单,而是要避免模型被量纲差异误导

金融因子标准化之所以重要,不只是让数据看起来整齐,更是为了避免模型把量纲差异误读成重要性差异。比如成交额可能是几亿、几百亿,波动率可能只是 0.01 到 0.05,某些哑变量只有 0 和 1。如果你把这些特征原封不动放在一起,网络前几层的更新往往会被大数值特征主导。不是因为这些特征真的更有预测力,而只是因为它们数值更大。

但标准化也不能做得太机械。很多人习惯直接对全样本做一次 z-score,然后就认为问题解决了。量化场景里,这样做常常不够。因为不同特征的业务含义不同,不同资产的分布也可能差很多。某些因子更适合按单个资产的历史分布标准化,以保留资产内部相对变化;某些因子更适合按截面标准化,以强调同一天不同资产之间的相对强弱;还有些因子需要先做截尾或裁剪,再做标准化,否则极端值会把整体分布拉得很难用。

真正稳的做法,不是迷信某一种标准化公式,而是先问自己:我希望模型学习的是资产自身偏离历史常态的程度,还是它在当前市场横截面里的相对位置?不同答案,对应的标准化方式往往不同。只要这层目的不先想清楚,任何“统一标准化”都可能只是把问题重新包装了一遍。

六、批处理为什么在金融时序里比一般任务更容易出错

在普通深度学习任务里,批处理很多时候只是为了提速。但在金融时序里,批处理不仅关乎速度,更关乎样本是否被正确组织。因为不同股票、不同资产、不同上市时间带来的序列长度差异非常常见。你不可能永远假设每一条输入序列都一样长,也不能指望所有资产都在同一个时间点开始和结束。

这时最常见的处理办法就是填充。把短序列补到和长序列一样长,再一起送进模型。问题在于,填充并不是把 0 补进去就结束了。如果模型后面根本不知道哪些位置是真实数据、哪些位置只是补出来的占位符,那么这些无效位置就会参与前向传播、参与损失计算,最后悄悄污染训练过程。模型表现得越复杂,这种污染越不容易第一时间发现。

因此,填充必须和掩码配合。你不仅要把序列补齐,还要明确告诉模型哪些位置有效、哪些无效。若用 RNN,就要配合长度信息或打包序列;若用 Transformer,就要配合 attention mask。更重要的是,在最终计算损失时,也要确保只在真实位置上计算,而不是把填充位一起平均进去。否则你看到的 loss 很可能比真实情况更好看,但那只是因为模型在大量“本来就不该算”的位置上被动占了便宜。

七、Dataset 和 DataLoader 的价值,不只是把数据喂进去,而是把研究流程固定下来

很多人把 PyTorch 的 DatasetDataLoader 理解成一个机械的数据接口,觉得只要能返回张量就行。其实在量化研究里,它们更重要的价值是把你的样本生成逻辑、随机顺序、过滤规则和批处理规则固定下来。只要这一层固定住,后面不管你换模型、换损失函数还是换训练策略,至少输入流程是一致可复现的。

可复现这件事在量化里尤其重要。因为你的样本是时序的,你的标签带未来偏移,你的资产池可能随时间变化,你的数据清洗还有各种边界条件。若这些规则散落在 notebook 里、函数里、训练脚本里,每次改一点都会导致结果悄悄变样,你很难知道到底是哪一步动了研究结论。相反,如果把样本构建和批处理逻辑收进 Datasetcollate_fn,很多事情会清楚得多。哪怕你后面发现某类停牌样本需要过滤,或者标准化要改成滚动方式,也能知道改动发生在哪一层。

对团队协作来说,这一点价值更大。因为模型层的争论往往很多,但如果连输入层都没有被规范下来,团队里的每个人其实都在用不同版本的数据世界做研究。特征管线的真正作用,就是把这种隐性分歧压到最小。

八、最常见的报错背后,其实都指向同一个问题:输入没有被真正讲清楚

做 PyTorch 金融特征管线时,经常会遇到显存溢出、张量维度不一致、loss 变成 NaN、梯度报错之类的问题。表面上看,错误信息五花八门,实际上很多都指向同一个根因:输入没有被真正讲清楚。

比如显存溢出,常见原因不是 GPU 太差,而是你以为自己在喂一个小批次,实际在 collate_fn 里把窗口展开成了远超预期的高维张量。维度不一致,往往不是 PyTorch 有问题,而是某些样本构建时长度被动态改掉了,自己却没意识到。loss 变 NaN,常常是标准化时某个特征标准差为 0,或者原始数据里已经带了无穷值和缺失值。梯度报错则多半来自不恰当的原地操作,或者把本不该参与梯度的输入处理混进了计算图。

这些问题一旦出现在训练阶段,很多人会本能地继续调模型,结果越调越乱。更有效的做法,往往是退回输入层,检查每一条样本到底长什么样、标准化后还有没有异常值、一个 batch 里各个字段的形状是否真的符合预期。量化建模最怕“模型还没学,数据已经乱了”。因此,特征管线里每一步都最好留出可检查的中间结果,而不是把所有处理写成一团看似优雅、实际难以定位问题的流水线。

九、真正能落地的特征管线,最后沉淀下来的不是技巧,而是一套稳定顺序

做完这一层之后,真正该留下来的往往不是某个特别花哨的技巧,而是一套稳定顺序。先定义研究问题和标签,再按因果顺序切样本;再根据因子性质决定标准化方式,而不是先默认一种统一处理;接着在批处理时把填充和掩码一起设计好;最后再把这整套过程固定进 Dataset、DataLoader 和验证脚本里。

这个顺序之所以重要,是因为它能跨模型复用。无论你后面用 LSTM、GRU、TCN 还是 Transformer,只要输入层的顺序和边界稳,模型之间的比较才有意义。否则你表面上是在比较网络结构,实际上比较的是谁更能容忍脏输入、谁更能掩盖样本定义的问题。这种比较是没有价值的。

从更长的周期看,特征管线也是量化研究里最值得长期维护的资产之一。模型会换,损失函数会换,训练框架也会换,但一套清楚、可复现、能自证没有时间穿越的输入流程,往往能陪你走很久。

十、总结

这一讲真正想讲清楚的,是 PyTorch 金融特征管线为什么不能只被当成一个“把表转成张量”的技术动作。样本构建决定模型有没有偷看未来,标准化决定模型是否会被量纲误导,批处理和掩码决定无效位置会不会混进训练结果。只要这三层没有立住,后面任何看起来高级的模型都不值得太早相信。

十一、系列衔接

本讲是《PyTorch量化建模完整学习计划》的第 2/12 讲,当前主题是《PyTorch金融特征管线:样本构建、标准化与批处理策略》。上一讲已经把张量、自动求导和训练循环的基本框架搭起来,这一讲则把模型真正吃进去的数据流程讲清楚。下一讲将进入《PyTorch时序网络基础:LSTM/GRU在收益预测中的实践》,届时重点会转向:当输入层已经足够稳之后,哪些时序网络结构更适合日频量化数据,哪些看起来很强的模型设计其实并不一定比一个扎实的基线网络更可靠。

十二、风险揭示与免责声明

风险揭示与免责声明

本页面内容仅用于量化研究与技术交流,旨在展示研究方法与流程,不构成对任何金融产品、证券或衍生品的要约、招揽、推荐或保证。

本文所涉历史数据、回测结果与示例参数不代表未来表现,也不应作为投资决策依据。

市场存在波动、流动性与执行偏差等不确定性,任何策略均可能出现收益波动或阶段性失效。

读者应结合自身风险承受能力进行独立判断,并在必要时咨询持牌专业机构意见。