特征与标签:机器学习在投资里到底学什么
你大概听过不少「我们用 AI 选股」的说法。听的时候有个问题很少有人替你问出来:这个 AI,学的是哪道题?
这不是抬杠。同样一堆数据、同样一个模型,你把要它猜的东西换一下——从「明天涨还是跌」换成「明天涨多少」,再换成「这三百只票里哪几只最靠前」——难度、评价方式、能不能赚钱,全都不一样。而外面绝大多数「AI 选股」的说法,恰恰就停在「用了 AI」这一层,从不说清楚它在猜什么。
那道题长什么样,是由两样东西定的:特征和标签。这两个词是整条 AI 投研链条的地基,也是最容易被跳过的一环。
先说人话:教小孩认水果
想象你要教一个小孩认苹果。你不会给他讲定义,你会拿一堆水果摆桌上,一个个指着说:这个是苹果,这个不是。指了几百遍以后,他自己就会了。
在这个过程里有两样东西:
- 你让他看的那些线索——颜色、大小、形状、有没有梗。这叫特征(feature),是输入。
- 你每次给出的那个答案——「是苹果」还是「不是苹果」。这叫标签(label),是要预测的答案。
给一堆「线索 + 标准答案」的成对例子,让机器自己找出线索和答案之间的关系,这种学法叫监督学习(有人在旁边给答案,所以叫「监督」)。今天投资里绝大部分号称 AI 的东西,本质就是这个。
关键来了。同样是这堆水果,你换一个标签,就是完全不同的一件事。
你可以让他学「是不是苹果」(是非题)。也可以让他学「这个苹果几克重」(填空题)。还可以让他学「把这十个苹果按甜度从高到低排」(排序题)。摆在桌上的水果一模一样,线索一模一样,但这三件事的难度天差地别,学出来的本事也完全不通用——一个能把苹果按甜度排好队的小孩,未必答得上来某个具体的苹果多少克。
标签定义了题目。题目一变,整件事的性质就变了。 这是本篇最想让你记住的一句话。
投资层:三种标签,三种完全不同的活
回到投资。桌上的「水果」是某一天的某只股票,「线索」是那天为止能算出来的一切——收盘价、成交量、各种技术指标、财务比率。那「标准答案」写什么?
这里有三条常见的路,它们看上去只是措辞不同,实际上是三种活。
第一种:预测方向。 标签写成「往后一段时间涨了没有」,答案只有涨和跌两个值。这在机器学习里叫分类(把样本分到几个筐里)。它的好处是直观,你一眼看得懂;坏处非常隐蔽——它把「涨 0.1%」和「涨 8%」当成了同一个答案。模型学完之后可能方向准确率不低,但你把它真拿去交易,赚的那几次全是涨 0.1%,亏的那几次是跌 5%,账户照样往下走。方向准确率和赚钱之间没有必然关系,这是新手最常撞的一堵墙。
第二种:预测收益率。 标签写成「往后一段时间的涨跌幅是多少」,答案是一个连续的数。这叫回归(猜一个具体数值)。它保留了幅度信息,比方向题诚实。但它也更难:市场收益里绝大部分是噪音,你让模型去精确逼近一个几乎随机的数,它很容易把噪音当成规律硬记下来——这正是过拟合(在历史上背答案,到新数据上就现原形)最爱发生的地方。
第三种:预测排序。 标签仍然是收益率,但你根本不在乎模型猜的绝对值准不准,只在乎它把今天这一批股票的先后顺序排对了没有。这是眼下机构做多因子选股时最主流的口径,因为它跟你的实际动作最贴合——你要做的事,从来不是「预测明天大盘涨几个点」,而是「在今天能买的这一堆里,挑出相对靠前的那一小撮」。整体涨跌被剔掉了,剩下的是相对强弱。
三种标签,对应三种截然不同的评价方式。方向题看准确率,收益率题看误差大小,排序题看的是 IC 这类相关性指标(IC 与 IR 那篇专门讲这个)。评价方式跟着标签走,用错了指标,你连自己做得好不好都判断不了。
看到「AI 选股准确率 78%」,先问一句:准确率是对哪个标签算的。 如果标签是「明天涨不涨」,那把所有样本一律猜「涨」,在一段单边上行的历史里准确率就能很高,模型什么都没学到。这个数字单独拿出来说,几乎不含信息量。
系统层:真实系统里的标签长什么样
概念说完,看一眼它在代码里的样子——不是要你去配置什么,是让你确认前面那些话不是我编的。
qlib 里有一套叫 Alpha158 的现成数据方案,它的标签就一行:
def get_label_config(self):
return ["Ref($close, -2)/Ref($close, -1) - 1"], ["LABEL0"]
Ref($close, -2) 的意思是「往后数第 2 天的收盘价」。整个式子就是:第 T+2 天的收盘价,除以第 T+1 天的收盘价,减一。翻译成人话:从明天到后天的那一段涨跌幅。
这里有个细节值得你盯住看三秒。为什么不是最自然的那个写法——「明天收盘除以今天收盘」?
qlib 的文档把理由写得很明白:在 A 股,你拿到 T 日收盘价的那一刻,最早只能在 T+1 买入,然后 T+2 才能卖出。所以模型该学的,是那一段你真的吃得到的收益,而不是一段你根本没机会参与的收益。
这一格之差就是整件事的分水岭。写成「明天收盘除以今天收盘」,模型学的是一段你在决策那一刻已经买不进去的行情——这就是前视偏差钻进标签里的经典姿势。它不报错、不崩溃,只会让你的历史成绩变好看。标签是前视偏差最喜欢的藏身处,因为标签天生就是往未来看的,往前多看一格还是少看一格,写代码时几乎察觉不到。
freqtrade 的机器学习模块用了另一种风格,同样值得看。它靠列名前缀把两类东西硬分开:特征列必须以 % 开头,标签列必须以 & 开头。
dataframe["%-rsi-period"] = ta.RSI(dataframe, timeperiod=period) # 特征
dataframe["&-s_close"] = ... # 标签
这种设计的用意,是让「哪些列是输入、哪些列是要预测的答案」在命名上就不可能搞混。它的示例标签算的是往后若干根 K 线的平均收盘价相对当前的偏离——注意是取一段的平均,而不是取某一个时点的价格。这是个很实用的处理:单点价格噪音大,一段的均值稳一些,模型不容易被单根异常 K 线带偏。往后看多少根由一个参数控制(示例配置里给的是当前默认值,可配置)。
它的注释里还顺手给了做分类的写法——把标签换成「up」「下」这样的字符串,模型就从猜数值变成了猜方向。一行代码之差,整道题的性质就换了。 这跟前面说的三种活,是同一件事的代码版。
标签不是写完就完了:它还要被再加工一遍
这一层很多人不知道,但它对结果的影响非常大。
qlib 默认对标签做两件事:
_DEFAULT_LEARN_PROCESSORS = [
{"class": "DropnaLabel"},
{"class": "CSZScoreNorm", "kwargs": {"fields_group": "label"}},
]
第一件是丢掉标签为空的样本。哪些样本标签会空?最典型的就是每段数据的末尾——最后几天没有「往后两天」了,答案还没发生。这些样本不能拿去训练。
第二件叫截面归一化。「截面」是指「同一天、所有股票」这个横切面。归一化就是在每一天内部,把所有股票的标签统一减去当天的平均、再除以当天的离散程度,变成一个相对位置。
这一步做了什么?它把大盘的整体涨跌从标签里剔掉了。 处理之后,「大盘涨 5%、这只涨 5%」的标签接近 0,「大盘跌 3%、这只跌 1%」的标签反而是正的。模型从此不再学「明天大盘涨不涨」,而是学「在同一天的所有票里,谁相对更强」——正好就是前面说的第三种活。qlib 里还有个按名次归一化的变体,做得更彻底,直接把当天所有股票的标签换成排名。
所以你看,「模型在学什么」这个问题,光看标签公式还不够,得连它的后处理一起看。 同一行 Ref($close, -2)/Ref($close, -1) - 1,加不加这一道截面处理,训出来的是两个物种。
一个容易被忽略的坑:训练用的答案和上线用的答案不一样
qlib 的数据层还有个设计,第一次看会觉得奇怪:同一份数据,它存了三个版本——原始的、给训练用的、给上线预测用的。
- DK_R / self._data: the raw data loaded from the loader
- DK_I / self._infer: the data processed for inference
- DK_L / self._learn: the data processed for learning model.
为什么要分家?因为有些加工只在训练时合法。比如「丢掉标签为空的样本」——训练时理所应当,可你上线跑预测的时候,恰恰是那些还没有答案的最新日期才需要预测,全丢了就什么都不剩了。源码里给这个处理器写的注释很直白:样本是按标签丢的,所以它不能用在推理阶段。
这件事的教训是通用的:凡是需要用到标签才能做的处理,一律不能出现在上线路径上。 因为上线的那一刻,标签根本不存在——答案还在未来。训练和上线的处理流水线混成一条,是 AI 投研里另一类高频事故,而且同样不报错。
失效边界与常见误用
以为「特征越多越好」。 特征是模型能看的线索,多加一列的边际成本看起来是零,实际不是。线索越多,模型越容易在噪音里找到一个只在历史上成立的巧合。真正决定上限的是标签的信噪比,不是特征数量。特征怎么造是另一个话题,见因子那篇。
把标签往后拉得越远越好。 预测「一个月后涨跌」听起来比「两天后」高级,实际上样本会大幅重叠——今天和明天的一个月标签有 29 天是同一段行情,这让你手上「看起来有几万个样本」的数据集,独立信息量远没那么多。评估时若不处理这层重叠,会严重高估模型的可靠性,时序交叉验证那篇讲了怎么切才不出事。
忘了标签也要考虑交易成本。 你的标签算的是价格涨跌,可你实际能拿到的是扣掉手续费、印花税、滑点之后的东西。一个在标签口径下预测得很准的模型,落到真实账户上可能颗粒无收——尤其当它挑出来的机会平均幅度很小的时候。
把模型输出当成买卖指令。 模型吐出来的是对标签的预测值,它离「买多少、什么时候买」还隔着一整套东西。这一步怎么接,见模型吐出一列分数之后呢。
在没有标签的地方硬套监督学习。 有些问题压根没有客观答案可标(比如「这条新闻是利好还是利空」),你标出来的答案本身就掺着你的主观判断,模型学到的可能只是你的偏见。这时候先怀疑标签,别急着换模型。
小结
- 监督学习就是「给一堆线索 + 标准答案的例子,让机器自己找规律」。线索叫特征(输入),标准答案叫标签(要预测的东西)。
- 标签定义了题目:预测方向(是非题)、预测收益率(填空题)、预测排序(排队题),是三种难度和评价方式都不同的活。方向准确率高不等于赚钱,因为它把大涨和微涨算成了同一个答案。
- 真实系统里的标签会为「你实际什么时候能成交」做位移——qlib 用 T+1 到 T+2 的涨幅而不是今天到明天,就是这个原因。标签是前视偏差最爱藏的地方。
- 标签写完还要被再加工:丢掉答案为空的样本,以及按每天的横切面做归一化,把大盘整体涨跌剔掉,让模型专心学相对强弱。
- 训练用的数据处理和上线用的必须分家——凡是用到标签才能做的步骤,上线那一刻都做不了,因为答案还在未来。
一句话记住这篇:别问它用没用 AI,问它在猜什么、猜对了怎么算分。
本文所引源码与文档均来自上述快照版本,是阅读代码得出的机制说明,不是运行结果;具体行为请以你手上那个版本为准。回 量化与 AI 投研概念库 看其它概念各自在说什么;本站内容为投资教育,不构成任何投资建议,边界见 免责声明。
本文为投资教育整理,关键数据与结论请结合下列权威来源验证。
- qlib 源码快照 79633dd(2026-07-23):qlib/contrib/data/handler.py、qlib/data/dataset/handler.py、qlib/data/dataset/processor.py、examples/benchmarks/LightGBM/workflow_config_lightgbm_Alpha158.yaml、docs/component/data.rst、docs/advanced/alpha.rst ↗
- freqtrade 源码快照 b3404c9(2026-08-18):freqtrade/templates/FreqaiExampleStrategy.py、docs/freqai-feature-engineering.md ↗