4、标签构建:如何定义涨跌方向?
好,咱们进入第四讲。前面几章我们把数据拿回来了,也把特征工程做完了。但有个核心问题一直没解决——机器到底要学什么?
说白了,你得告诉它什么是「涨」,什么是「跌」。这就是标签构建。
我见过不少新手,上来就把「明天收盘价比今天高」定义为涨。听起来没毛病对吧?但实际跑起来,模型准确率死活上不去。为什么?因为市场有噪音,单日的涨跌随机性太强了。
嗯,这里要注意。我们真正要预测的,不是明天那一下跳动,而是一段趋势。
4.1 未来N日收益率:标签的数学基础
我个人习惯用未来N日收益率来定义涨跌。公式很简单:
未来N日收益率 = (未来第N日的收盘价 / 当前收盘价) - 1
举个例子。今天收盘价是100元,5天后收盘价是105元。那未来5日收益率就是5%。
你可能会问:「N取多少合适?」
我在项目中遇到过这个问题。取1天,噪声太大。取20天,又太滞后,等你信号出来,行情都走完了。我个人经验是:
- 短线策略:N=3或5
- 中线策略:N=10或20
- 长线策略:N=60或120
当然,这没有标准答案。我建议你根据自己策略的持仓周期来定。
4.2 分类标签的生成:从连续值到离散类别
收益率是连续值,但我们做的是分类任务。所以需要把连续值映射成离散类别。
最常见的做法是设一个阈值:
def generate_label(future_return, threshold=0.02):
if future_return > threshold:
return 1 # 涨
elif future_return < -threshold:
return -1 # 跌
else:
return 0 # 平
这里threshold取2%。为什么是2%?其实没有魔法数字。我一般会看历史收益率的分布,取一个能平衡样本量的值。
举个例子,如果历史数据显示:
- 收益率 > 2% 的样本占30%
- 收益率 < -2% 的样本占30%
- 中间占40%
那这个阈值就挺合理。三类样本不至于太偏。
4.3 平衡性检查:别让模型变成「偏科生」
标签生成完了,第一件事就是检查类别分布。
为什么?因为不平衡的数据会让模型偷懒。比如90%的样本都是「涨」,模型只要永远预测「涨」,准确率就有90%。但它什么也没学到。
我一般用这个函数来检查:
def check_balance(labels):
unique, counts = np.unique(labels, return_counts=True)
total = len(labels)
for cls, cnt in zip(unique, counts):
print(f"类别 {cls}: {cnt} 样本, 占比 {cnt/total:.2%}")
return counts / total
输出结果大概长这样:
| 类别 | 样本数 | 占比 |
|---|---|---|
| -1(跌) | 1200 | 28.6% |
| 0(平) | 1800 | 42.9% |
| 1(涨) | 1200 | 28.6% |
这个分布就挺健康。三类样本差不多。
但如果出现这种情况:
| 类别 | 样本数 | 占比 |
|---|---|---|
| -1(跌) | 300 | 7.1% |
| 0(平) | 3600 | 85.7% |
| 1(涨) | 300 | 7.1% |
那就要警惕了。模型大概率会学成「永远预测平」的废物。
4.4 核心逻辑流程图
下面这张图,把整个标签构建的流程串起来了。你可以把它当作本章的「知识地图」:
4.5 处理不平衡的几种方法
如果检查后发现不平衡,怎么办?别慌,有办法。我按推荐程度排个序:
- 调整阈值:这是最简单的方法。把threshold调大或调小,让三类样本更均衡。
- 重采样:对少数类做上采样(复制样本),或对多数类做下采样(随机丢弃)。我一般用上采样,因为下采样会丢失信息。
- 改用二分类:如果「平」的样本太多,干脆去掉中间类别,只预测「涨」和「跌」。这样问题简化了,模型也更容易学。
- 加权损失函数:给少数类更高的权重,让模型更关注它们。这个方法在深度学习里很常用。
4.6 一个完整的代码示例
最后,给你一个完整的标签构建函数。这是我项目里常用的模板:
import numpy as np
import pandas as pd
def build_labels(df, n_days=5, threshold=0.02, method='three_class'):
"""
构建涨跌分类标签
参数:
df: DataFrame,必须包含 'close' 列
n_days: 未来N日
threshold: 涨跌阈值
method: 'three_class' 或 'two_class'
返回:
df: 添加了 'label' 列的DataFrame
"""
# 计算未来N日收益率
df['future_return'] = df['close'].shift(-n_days) / df['close'] - 1
# 生成标签
if method == 'three_class':
conditions = [
df['future_return'] > threshold,
df['future_return'] < -threshold
]
choices = [1, -1]
df['label'] = np.select(conditions, choices, default=0)
else: # two_class
df['label'] = (df['future_return'] > threshold).astype(int)
# 删除未来数据不可用的行
df = df.dropna(subset=['label'])
# 打印平衡性检查
print("标签分布:")
print(df['label'].value_counts(normalize=True))
return df
# 使用示例
# df = build_labels(data, n_days=5, threshold=0.02, method='three_class')
这个函数会帮你自动完成:计算收益率 → 生成标签 → 删除无效行 → 打印平衡性检查。一步到位。
好了,标签构建就讲到这里。记住,标签的质量直接决定了模型的上限。花时间把这一步做好,后面会省很多事。
公众号:蓝海资料掘金营,微信deep3321