halo 的技术博客

返回

行业分类表(GICS / 申万一级)是量化研究里最常用的”常识”之一:白酒和银行不是一个行业,所以它们的联动是噪声不是信号。但这个常识有两个裂缝:第一,它写死了——新能源车和半导体在 2019 年前分属不同申万行业,但行情上它们早就绑在一起;第二,它不随时间漂移——同一对行业的相关强度在牛市和危机里可以差一个数量级,而分类表不会变。

结论先放这:图对比学习(Graph Contrastive Learning, GCL)能直接从”股票-相关”图里学出行业嵌入向量,且这个表征是数据驱动的、随结构自适应的。 把”股票=节点、收益相关=边”建成图,用子图扰动(删边 / 特征加噪 / 掩码)造正负样本对,让同一个行业在嵌入空间里自然聚拢、不同行业被推开。受控实验里,学到的 2D 嵌入与”真实行业相关性”的 Spearman ρ 从原始表征的 0.30 提升到 0.85。但必须讲清楚:它学的是”相关性结构”,不是”收益方向”——板块 embedding 是聚类工具,不是选股 alpha。

板块相关图:节点=行业,边粗=收益相关强度。同色(同行业)明显聚拢,这是图对比学习要还原的真实结构

一、为什么”行业关系”需要重新学#

传统行业表征有三条路,每条都有硬伤:

  1. 静态分类 one-hot:申万一级做 embedding,等于把”相关性”写死成 0/1。最大问题是跨行业联动丢失——消费电子和半导体的真实相关经常高于白酒内部的两个子行业,但 one-hot 给它们”完全无关”。
  2. 相关矩阵直接降维(如 MDS / t-SNE):能反映相关结构,但非平稳——用哪个窗口?窗口一变,降维结果就变。而且没有”编码器”,来了新股票无法 OOV 推断,只能重新全量算。
  3. 手动构造行业相似度:研究员拍脑袋定权重,不可扩展、不可复现。

GCL 的卖点是:它同时有”编码器(可 OOV 推断)“和”数据驱动的结构学习(随相关自适应)“。学到的不是某个固定矩阵,而是一个把”图结构”压缩进向量、且对局部扰动鲁棒的函数

二、图对比学习的核心:用”子图扰动”造正负样本#

对比学习需要”相似对(正)“和”不相似对(负)“。在图像里这靠数据增强(裁剪/变色)自然得到;在图上,靠子图扰动(graph augmentation)

对原始图 GG,做两种独立扰动得到两个视图 G1,G2G_1, G_2

  • 边扰动(edge perturbation):随机删/加少量边(如 ±10%)。相关性估计本身有噪声,删几条弱边不影响真实的行业结构——同一行业的节点在扰动后大概率还在同一连通块里。
  • 特征掩码(feature masking):随机把部分节点的原始特征置零(如 20%)。逼编码器不要依赖单一节点的特征,而是靠”邻居聚合”推断它。

正样本对:同一节点在 G1G_1G2G_2 的两个视图下的嵌入 (hi(1),hi(2))(h_i^{(1)}, h_i^{(2)})——它们来自同一只股票,应该接近。 负样本对:不同节点在任意视图下的嵌入——它们大概率属于不同行业,应该远离。

损失函数用 InfoNCE

L=1Ni=1Nlogexp(sim(hi(1),hi(2))/τ)j=1Nexp(sim(hi(1),hj(2))/τ)\mathcal{L} = -\frac{1}{N}\sum_{i=1}^N \log \frac{\exp(\mathrm{sim}(h_i^{(1)}, h_i^{(2)}) / \tau)}{\sum_{j=1}^N \exp(\mathrm{sim}(h_i^{(1)}, h_j^{(2)}) / \tau)}

τ\tau 是温度系数,sim\mathrm{sim} 用余弦相似度。分子是”自己和自己(的另一次扰动)“,分母把所有其他节点当负样本——组里同行业的节点越多,互相作为负样本的”误伤”越大,这恰恰逼着模型把判别信号放到”真正的结构差异”上

对比学习前后表征对比:左为原始表征(噪声大、聚类模糊),右为图对比学习后(同行业聚拢、跨行业分离)

三、numpy 从零实现:GraphSAGE 编码器 + InfoNCE#

为了看清信息怎么流动,下面用线性 GraphSAGE 聚合(每层 = 邻居均值 + 自身,过一个线性映射)在一个受控的 6 行业 × 4 子行业 = 24 节点图上完整跑通 GCL。数据生成保证”同行业高相关、跨行业低相关”。

注:上面为了”从零看清结构”用了随机梯度近似更新;生产里应换成一个真实的优化器(SGD/Adam)对 W1, W2 做精确梯度。核心流程(双视图 → 编码 → InfoNCE)完全一致。

四、训练动态与表征质量#

训练过程中,对比损失单调下降,而”学到的嵌入余弦相似度 vs 真实相关”的 Spearman ρ 同步上升——这是 GCL 自检的关键指标:损失下降 != 学到了行业结构,必须看嵌入和真实相关的对齐度

训练动态:对比损失(红)下降同时,表征质量(绿, Spearman ρ)上升,二者同频

受控实验的对照:

  • 原始表征(无 GCL):节点用随机投影 + 原始特征,嵌入相似度与真实相关的 Spearman ρ ≈ 0.30,行业边界糊成一团(见对比图左)。
  • 图对比学习后:ρ ≈ 0.85,同行业节点在嵌入空间里聚成紧簇,跨行业明显分离(见对比图右)。
  • 消融:去掉特征掩码:只做边扰动,ρ 掉到约 0.62——说明”逼模型靠邻居聚合推断”的掩码是聚拢的关键,没有它模型会偷懒记单个节点特征。
  • 消融:去掉边扰动:只做特征掩码,ρ 约 0.71——边扰动提供的”结构不变性”目标不可替代。

五、学出来的行业嵌入能怎么用#

  1. 行业中性化约束:把因子收益对”行业嵌入的若干主成分”做回归残差,比传统的”申万一级哑变量中性化”更细——能区分”白酒 vs 银行”(真不相关)和”白酒 vs 啤酒”(高度相关但不同一级行业)。
  2. 板块轮动的温度计:监控嵌入空间里”消费簇”和”科技簇”的质心距离,距离拉大 = 风格分化加剧,可作为风格切换的预警。
  3. 新股票 OOV 推断:来了只不在训练图里的股票,只要给它连上邻居(用最新相关矩阵)跑一次编码器前向,就能得到它的行业嵌入,不用重训全图——这是 MDS 降维做不到的。
  4. 聚类做另类行业分类:对嵌入做无监督 KMeans,得到的聚类往往比官方分类更能反映”行情上的真实联动”,可作为多因子模型的行业暴露底表。

六、诚实边界与真实陷阱#

  1. 它学的是”相关性结构”,不是”收益方向”:GCL 优化的是”谁和谁像”,完全不碰”涨还是跌”。把板块 embedding 直接当 alpha 信号是误用——它最多是中性化、聚类、风险提示的工具。签名反转陷阱:如果某行业在训练期是”避险属性”(跌时涨),而测试期变成”顺周期”,embedding 里的相似关系会错位,因为它记的是”相关模式”而非”经济含义”。
  2. 图的质量决定上限:边来自相关矩阵,而相关矩阵对估计窗口极度敏感。用 60 天窗口和 252 天窗口建出来的图,行业结构能差很多。标准做法是多窗口相关做平均,或对图做边置信度加权。
  3. 负相关行业的负样本”误伤”:对比损失把所有其他节点当负样本,但两只”强负相关”的股票(一个涨另一个必跌)在损失里被推开——这在”对冲配对”语境下反而是对的(它们确实该分开),但在”同属一个宏观因子”语境下又该靠近。这是 GCL 在金融图上的固有张力,需要按下游任务决定要不要加”负相关但同因子”的正样本对。
  4. 小行业被大行业淹没:节点采样若按边数加权,大行业(银行 42 只)会把小行业(燃气 4 只)的梯度淹没。标准解法是”节点度归一化”或”行业均衡采样”。
  5. 非平稳让”冻结编码器”失效:训练好的编码器在 6 个月后相关性结构漂移,嵌入会过期。生产里应滚动重训(或在线 GCL),且重训时保留旧嵌入做”结构漂移监控”。

一句话总结:图对比学习给量化研究的是一把”随数据自适应的行业尺子”——它比分类表细、比一次性降维稳、还能 OOV 推断。但这把尺子量的是”谁和谁像”,不是”该买谁”。用对地方(中性化、聚类、风控),它是免费的结构红利;用错地方(当 alpha),它是漂亮的过拟合。

图对比学习板块表征:用子图扰动学出行业嵌入向量
https://blog.halo26812.eu.org/blog/graph-contrastive-sector-embedding
Author halo
Published at 2026年8月28日
版权声明 CC BY-NC-SA 4.0
Comment seems to stuck. Try to refresh?✨