<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>ST-GNN on 远思码技术实验室</title><link>https://yuansima.com/tags/st-gnn/</link><description>Recent content in ST-GNN on 远思码技术实验室</description><generator>Hugo -- gohugo.io</generator><language>zh-cn</language><lastBuildDate>Fri, 23 Oct 2026 00:00:00 +0000</lastBuildDate><atom:link href="https://yuansima.com/tags/st-gnn/index.xml" rel="self" type="application/rss+xml"/><item><title>TFT 与 ST-GNN：深度时序模型在 A 股选股中的真实收益边界</title><link>https://yuansima.com/p/tft-stgnn-quant-rocm/</link><pubDate>Fri, 23 Oct 2026 21:10:00 +0800</pubDate><guid>https://yuansima.com/p/tft-stgnn-quant-rocm/</guid><description>&lt;p&gt;&lt;em&gt;远思为径，码动价值。欢迎来到远思码技术实验室的最新技术实践记录。&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;a class="link" href="https://yuansima.com/p/lightgbm-cross-sectional-stock-prediction/" &gt;LightGBM 那篇&lt;/a&gt;的结论是：多个弱因子合成后 RankIC 0.042。想要再往上，直觉上有两条路——&lt;strong&gt;捕捉时序结构&lt;/strong&gt;（今天的形态和 20 天前有关）与&lt;strong&gt;捕捉截面结构&lt;/strong&gt;（同行业的股票会互相传染）。&lt;/p&gt;
&lt;p&gt;这两条正好对应 TFT 与 ST-GNN。这篇记录实验室在这两个模型上的实测，包括一个不太讨喜的结论：&lt;strong&gt;它们在 IC 上没有赢过 GBDT，只在组合稳定性上略有优势，而推理成本高一个数量级&lt;/strong&gt;。&lt;/p&gt;
&lt;h2 id="一为什么深度模型在这类任务上不占优势"&gt;一、为什么深度模型在这类任务上不占优势
&lt;/h2&gt;&lt;p&gt;先说清楚约束条件，否则容易陷入&amp;quot;模型越大越好&amp;quot;的误区：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;样本量陷阱&lt;/strong&gt;：A 股每天约 5000 只股票 × 250 天 ≈ 125 万样本/年，听起来很多，但&lt;strong&gt;有效独立样本远小于此&lt;/strong&gt;——同一天的股票高度相关，同一只股票的相邻日高度相关。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;信噪比极低&lt;/strong&gt;：日频截面收益的可预测部分只有万分之几，深度模型的参数量动辄百万级。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;信号衰减快&lt;/strong&gt;：因子有效期 5~20 个交易日，模型学到的模式可能在下个季度就失效。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;因此深度模型在 A 股的正确姿势不是&amp;quot;替代 GBDT&amp;quot;，而是&amp;quot;&lt;strong&gt;提供 GBDT 看不到的那部分信息&lt;/strong&gt;&amp;quot;：时序上的形态演变、行业间的溢出。&lt;/p&gt;
&lt;h2 id="二数据张量化从面板到-样本-时间步-特征"&gt;二、数据张量化：从面板到 (样本, 时间步, 特征)
&lt;/h2&gt;&lt;p&gt;TFT 需要三维输入。构造滑动窗口时要格外小心——&lt;strong&gt;窗口内不能混入标签期之后的数据&lt;/strong&gt;。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# ml/deep/dataset.py&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;from&lt;/span&gt; __future__ &lt;span style="color:#f92672"&gt;import&lt;/span&gt; annotations
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;import&lt;/span&gt; numpy &lt;span style="color:#66d9ef"&gt;as&lt;/span&gt; np
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;import&lt;/span&gt; pandas &lt;span style="color:#66d9ef"&gt;as&lt;/span&gt; pd
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;import&lt;/span&gt; torch
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;from&lt;/span&gt; torch.utils.data &lt;span style="color:#f92672"&gt;import&lt;/span&gt; Dataset
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;FEATURES &lt;span style="color:#f92672"&gt;=&lt;/span&gt; [&lt;span style="color:#e6db74"&gt;&amp;#34;mom_5&amp;#34;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;mom_20&amp;#34;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;mom_60&amp;#34;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;rev_5&amp;#34;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;vol_20&amp;#34;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;turn_20&amp;#34;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;amihud&amp;#34;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;class&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;WindowDataset&lt;/span&gt;(Dataset):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;&amp;#34;&amp;#34;把面板数据切成 (lookback, n_features) 的时间窗，预测未来 horizon 日截面排名。
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; 关键约定：window 截止于 t 日（含），标签是 t+1..t+horizon 的收益。
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; &amp;#34;&amp;#34;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;__init__&lt;/span&gt;(self, panel: pd&lt;span style="color:#f92672"&gt;.&lt;/span&gt;DataFrame, close: pd&lt;span style="color:#f92672"&gt;.&lt;/span&gt;DataFrame,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; lookback: int &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;60&lt;/span&gt;, horizon: int &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;5&lt;/span&gt;, min_hist: int &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;250&lt;/span&gt;):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; self&lt;span style="color:#f92672"&gt;.&lt;/span&gt;lookback &lt;span style="color:#f92672"&gt;=&lt;/span&gt; lookback
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; self&lt;span style="color:#f92672"&gt;.&lt;/span&gt;horizon &lt;span style="color:#f92672"&gt;=&lt;/span&gt; horizon
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;# panel: 多级索引 (trade_date, symbol)，列为特征&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; self&lt;span style="color:#f92672"&gt;.&lt;/span&gt;dates &lt;span style="color:#f92672"&gt;=&lt;/span&gt; sorted(panel&lt;span style="color:#f92672"&gt;.&lt;/span&gt;index&lt;span style="color:#f92672"&gt;.&lt;/span&gt;get_level_values(&lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;)&lt;span style="color:#f92672"&gt;.&lt;/span&gt;unique())
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; self&lt;span style="color:#f92672"&gt;.&lt;/span&gt;panel &lt;span style="color:#f92672"&gt;=&lt;/span&gt; panel
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; self&lt;span style="color:#f92672"&gt;.&lt;/span&gt;close &lt;span style="color:#f92672"&gt;=&lt;/span&gt; close
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; xs, ys, ds &lt;span style="color:#f92672"&gt;=&lt;/span&gt; [], [], []
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;for&lt;/span&gt; i, date &lt;span style="color:#f92672"&gt;in&lt;/span&gt; enumerate(self&lt;span style="color:#f92672"&gt;.&lt;/span&gt;dates):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; i &lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt; min_hist &lt;span style="color:#f92672"&gt;or&lt;/span&gt; i &lt;span style="color:#f92672"&gt;+&lt;/span&gt; horizon &lt;span style="color:#f92672"&gt;&amp;gt;=&lt;/span&gt; len(self&lt;span style="color:#f92672"&gt;.&lt;/span&gt;dates):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;continue&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; window &lt;span style="color:#f92672"&gt;=&lt;/span&gt; self&lt;span style="color:#f92672"&gt;.&lt;/span&gt;dates[i &lt;span style="color:#f92672"&gt;-&lt;/span&gt; lookback &lt;span style="color:#f92672"&gt;+&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;: i &lt;span style="color:#f92672"&gt;+&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; block &lt;span style="color:#f92672"&gt;=&lt;/span&gt; panel&lt;span style="color:#f92672"&gt;.&lt;/span&gt;loc[window] &lt;span style="color:#75715e"&gt;# (lookback * n_symbols, n_feat)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; symbols &lt;span style="color:#f92672"&gt;=&lt;/span&gt; block&lt;span style="color:#f92672"&gt;.&lt;/span&gt;index&lt;span style="color:#f92672"&gt;.&lt;/span&gt;get_level_values(&lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;)&lt;span style="color:#f92672"&gt;.&lt;/span&gt;unique()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; arr &lt;span style="color:#f92672"&gt;=&lt;/span&gt; np&lt;span style="color:#f92672"&gt;.&lt;/span&gt;stack([
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; block&lt;span style="color:#f92672"&gt;.&lt;/span&gt;xs(d, level&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;)&lt;span style="color:#f92672"&gt;.&lt;/span&gt;reindex(symbols)[FEATURES]&lt;span style="color:#f92672"&gt;.&lt;/span&gt;to_numpy()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;for&lt;/span&gt; d &lt;span style="color:#f92672"&gt;in&lt;/span&gt; window
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; ]) &lt;span style="color:#75715e"&gt;# (lookback, n_symbols, n_feat)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; arr &lt;span style="color:#f92672"&gt;=&lt;/span&gt; np&lt;span style="color:#f92672"&gt;.&lt;/span&gt;nan_to_num(arr, nan&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;0.0&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; xs&lt;span style="color:#f92672"&gt;.&lt;/span&gt;append(torch&lt;span style="color:#f92672"&gt;.&lt;/span&gt;tensor(arr&lt;span style="color:#f92672"&gt;.&lt;/span&gt;transpose(&lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;2&lt;/span&gt;), dtype&lt;span style="color:#f92672"&gt;=&lt;/span&gt;torch&lt;span style="color:#f92672"&gt;.&lt;/span&gt;float32)) &lt;span style="color:#75715e"&gt;# (n_symbols, lookback, n_feat)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; ds&lt;span style="color:#f92672"&gt;.&lt;/span&gt;append(date)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; c &lt;span style="color:#f92672"&gt;=&lt;/span&gt; close&lt;span style="color:#f92672"&gt;.&lt;/span&gt;loc[date, symbols]
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; c_end &lt;span style="color:#f92672"&gt;=&lt;/span&gt; close&lt;span style="color:#f92672"&gt;.&lt;/span&gt;loc[self&lt;span style="color:#f92672"&gt;.&lt;/span&gt;dates[i &lt;span style="color:#f92672"&gt;+&lt;/span&gt; horizon], symbols]
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; ret &lt;span style="color:#f92672"&gt;=&lt;/span&gt; (c_end &lt;span style="color:#f92672"&gt;/&lt;/span&gt; c &lt;span style="color:#f92672"&gt;-&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;)&lt;span style="color:#f92672"&gt;.&lt;/span&gt;rank(pct&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;True&lt;/span&gt;) &lt;span style="color:#f92672"&gt;*&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;2&lt;/span&gt; &lt;span style="color:#f92672"&gt;-&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt; &lt;span style="color:#75715e"&gt;# 截面排名标签&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; ys&lt;span style="color:#f92672"&gt;.&lt;/span&gt;append(torch&lt;span style="color:#f92672"&gt;.&lt;/span&gt;tensor(ret&lt;span style="color:#f92672"&gt;.&lt;/span&gt;to_numpy(), dtype&lt;span style="color:#f92672"&gt;=&lt;/span&gt;torch&lt;span style="color:#f92672"&gt;.&lt;/span&gt;float32))
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; self&lt;span style="color:#f92672"&gt;.&lt;/span&gt;x &lt;span style="color:#f92672"&gt;=&lt;/span&gt; xs
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; self&lt;span style="color:#f92672"&gt;.&lt;/span&gt;y &lt;span style="color:#f92672"&gt;=&lt;/span&gt; ys
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; self&lt;span style="color:#f92672"&gt;.&lt;/span&gt;date_index &lt;span style="color:#f92672"&gt;=&lt;/span&gt; ds
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;__len__&lt;/span&gt;(self) &lt;span style="color:#f92672"&gt;-&amp;gt;&lt;/span&gt; int:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; len(self&lt;span style="color:#f92672"&gt;.&lt;/span&gt;x)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;__getitem__&lt;/span&gt;(self, idx: int):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; self&lt;span style="color:#f92672"&gt;.&lt;/span&gt;x[idx], self&lt;span style="color:#f92672"&gt;.&lt;/span&gt;y[idx]
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;内存是第一个坎：&lt;code&gt;n_symbols × lookback × n_features&lt;/code&gt; 的 float32 张量，5000 × 60 × 7 ≈ 8.4 MB/天。1200 天就是 10 GB。所以在 48GB UMA 的机器上，实验室的折衷是 &lt;strong&gt;lookback=60、特征 7 个、只取沪深 300 + 中证 500 成分股&lt;/strong&gt;（约 800 只），单天 1.3 MB，全量 1.5 GB，正好塞进内存。&lt;/p&gt;
&lt;h2 id="三tft可解释性才是它的卖点"&gt;三、TFT：可解释性才是它的卖点
&lt;/h2&gt;&lt;p&gt;&lt;a class="link" href="https://github.com/jdb78/pytorch-forecasting" target="_blank" rel="noopener"
 &gt;pytorch-forecasting&lt;/a&gt; 的 TFT 把&amp;quot;变量选择网络（VSN）+ 多头注意力&amp;quot;包好了，我们只需要喂 &lt;code&gt;TimeSeriesDataSet&lt;/code&gt;：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# ml/deep/tft_model.py&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;from&lt;/span&gt; __future__ &lt;span style="color:#f92672"&gt;import&lt;/span&gt; annotations
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;import&lt;/span&gt; lightning.pytorch &lt;span style="color:#66d9ef"&gt;as&lt;/span&gt; pl
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;from&lt;/span&gt; pytorch_forecasting &lt;span style="color:#f92672"&gt;import&lt;/span&gt; TemporalFusionTransformer, TimeSeriesDataSet
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;from&lt;/span&gt; pytorch_forecasting.metrics &lt;span style="color:#f92672"&gt;import&lt;/span&gt; QuantileLoss
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;CONFIG &lt;span style="color:#f92672"&gt;=&lt;/span&gt; dict(
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; max_encoder_length&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;60&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; max_prediction_length&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;5&lt;/span&gt;, &lt;span style="color:#75715e"&gt;# 预测未来 5 日&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; hidden_size&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;64&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; attention_head_size&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;4&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; dropout&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;0.2&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; hidden_continuous_size&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;32&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; learning_rate&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;1e-3&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; batch_size&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;256&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; max_epochs&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;30&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;build_dataset&lt;/span&gt;(df, predict: bool &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;False&lt;/span&gt;, training: TimeSeriesDataSet &lt;span style="color:#f92672"&gt;|&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;None&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;None&lt;/span&gt;) &lt;span style="color:#f92672"&gt;-&amp;gt;&lt;/span&gt; TimeSeriesDataSet:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; TimeSeriesDataSet(
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; df,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; time_idx&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;time_idx&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; target&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;label&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; group_ids&lt;span style="color:#f92672"&gt;=&lt;/span&gt;[&lt;span style="color:#e6db74"&gt;&amp;#34;symbol&amp;#34;&lt;/span&gt;],
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; max_encoder_length&lt;span style="color:#f92672"&gt;=&lt;/span&gt;CONFIG[&lt;span style="color:#e6db74"&gt;&amp;#34;max_encoder_length&amp;#34;&lt;/span&gt;],
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; max_prediction_length&lt;span style="color:#f92672"&gt;=&lt;/span&gt;CONFIG[&lt;span style="color:#e6db74"&gt;&amp;#34;max_prediction_length&amp;#34;&lt;/span&gt;],
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; time_varying_known_reals&lt;span style="color:#f92672"&gt;=&lt;/span&gt;[&lt;span style="color:#e6db74"&gt;&amp;#34;time_idx&amp;#34;&lt;/span&gt;], &lt;span style="color:#75715e"&gt;# 交易日序号，未来可知&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; time_varying_unknown_reals&lt;span style="color:#f92672"&gt;=&lt;/span&gt;[&lt;span style="color:#e6db74"&gt;&amp;#34;mom_5&amp;#34;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;mom_20&amp;#34;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;mom_60&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;rev_5&amp;#34;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;vol_20&amp;#34;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;turn_20&amp;#34;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;amihud&amp;#34;&lt;/span&gt;],
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; static_categoricals&lt;span style="color:#f92672"&gt;=&lt;/span&gt;[&lt;span style="color:#e6db74"&gt;&amp;#34;industry&amp;#34;&lt;/span&gt;],
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; predict_mode&lt;span style="color:#f92672"&gt;=&lt;/span&gt;predict,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; training&lt;span style="color:#f92672"&gt;=&lt;/span&gt;training,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; allow_missing_timesteps&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;True&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; add_relative_time_idx&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;True&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; )
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;build_trainer&lt;/span&gt;(gpus: int &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;) &lt;span style="color:#f92672"&gt;-&amp;gt;&lt;/span&gt; pl&lt;span style="color:#f92672"&gt;.&lt;/span&gt;Trainer:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; pl&lt;span style="color:#f92672"&gt;.&lt;/span&gt;Trainer(
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; max_epochs&lt;span style="color:#f92672"&gt;=&lt;/span&gt;CONFIG[&lt;span style="color:#e6db74"&gt;&amp;#34;max_epochs&amp;#34;&lt;/span&gt;],
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; accelerator&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;gpu&amp;#34;&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; gpus &lt;span style="color:#66d9ef"&gt;else&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;cpu&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; precision&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;bf16-mixed&amp;#34;&lt;/span&gt;, &lt;span style="color:#75715e"&gt;# ROCm 上 bf16 比 fp16 稳定&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; gradient_clip_val&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;0.5&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; enable_checkpointing&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;False&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; logger&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;False&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; enable_progress_bar&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;True&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; )
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;build_model&lt;/span&gt;(training_ds: TimeSeriesDataSet) &lt;span style="color:#f92672"&gt;-&amp;gt;&lt;/span&gt; TemporalFusionTransformer:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; TemporalFusionTransformer&lt;span style="color:#f92672"&gt;.&lt;/span&gt;from_dataset(
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; training_ds,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; learning_rate&lt;span style="color:#f92672"&gt;=&lt;/span&gt;CONFIG[&lt;span style="color:#e6db74"&gt;&amp;#34;learning_rate&amp;#34;&lt;/span&gt;],
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; hidden_size&lt;span style="color:#f92672"&gt;=&lt;/span&gt;CONFIG[&lt;span style="color:#e6db74"&gt;&amp;#34;hidden_size&amp;#34;&lt;/span&gt;],
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; attention_head_size&lt;span style="color:#f92672"&gt;=&lt;/span&gt;CONFIG[&lt;span style="color:#e6db74"&gt;&amp;#34;attention_head_size&amp;#34;&lt;/span&gt;],
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; dropout&lt;span style="color:#f92672"&gt;=&lt;/span&gt;CONFIG[&lt;span style="color:#e6db74"&gt;&amp;#34;dropout&amp;#34;&lt;/span&gt;],
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; hidden_continuous_size&lt;span style="color:#f92672"&gt;=&lt;/span&gt;CONFIG[&lt;span style="color:#e6db74"&gt;&amp;#34;hidden_continuous_size&amp;#34;&lt;/span&gt;],
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; output_size&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;7&lt;/span&gt;, &lt;span style="color:#75715e"&gt;# 7 个分位数（默认 QuantileLoss）&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; loss&lt;span style="color:#f92672"&gt;=&lt;/span&gt;QuantileLoss(),
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; reduce_on_plateau_patience&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;4&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; )
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;TFT 真正的价值不在精度，而在&lt;strong&gt;可解释性输出&lt;/strong&gt;：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;interpretation&lt;/span&gt;(model, trainer, dataloader) &lt;span style="color:#f92672"&gt;-&amp;gt;&lt;/span&gt; dict:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; raw &lt;span style="color:#f92672"&gt;=&lt;/span&gt; model&lt;span style="color:#f92672"&gt;.&lt;/span&gt;predict(dataloader, mode&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;raw&amp;#34;&lt;/span&gt;, return_x&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;True&lt;/span&gt;, trainer&lt;span style="color:#f92672"&gt;=&lt;/span&gt;trainer)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; model&lt;span style="color:#f92672"&gt;.&lt;/span&gt;interpret_output(raw&lt;span style="color:#f92672"&gt;.&lt;/span&gt;output, reduction&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;sum&amp;#34;&lt;/span&gt;) &lt;span style="color:#75715e"&gt;# attention / 变量权重&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;code&gt;interpretation&lt;/code&gt; 会给你三样东西：&lt;strong&gt;每个历史时间步的注意力权重&lt;/strong&gt;（模型在关注 5 天前还是 40 天前）、&lt;strong&gt;每个变量的重要度&lt;/strong&gt;（动量还是换手在起作用）、&lt;strong&gt;持续性权重&lt;/strong&gt;（历史形态对未来预测的持续影响）。这三样东西能反过来修正因子设计，比 IC 高 0.005 有用得多。&lt;/p&gt;
&lt;h2 id="四st-gnn把行业关系建成图"&gt;四、ST-GNN：把行业关系建成图
&lt;/h2&gt;&lt;p&gt;截面信息传播的想法很自然：某只股票异动，同行业/上下游的股票往往跟随。用图神经网络表达就是：&lt;strong&gt;特征矩阵 + 邻接矩阵 → 消息传递 → 更新的特征&lt;/strong&gt;。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# ml/deep/stgnn.py&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;from&lt;/span&gt; __future__ &lt;span style="color:#f92672"&gt;import&lt;/span&gt; annotations
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;import&lt;/span&gt; torch
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;import&lt;/span&gt; torch.nn.functional &lt;span style="color:#66d9ef"&gt;as&lt;/span&gt; F
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;from&lt;/span&gt; torch &lt;span style="color:#f92672"&gt;import&lt;/span&gt; nn
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;from&lt;/span&gt; torch_geometric.nn &lt;span style="color:#f92672"&gt;import&lt;/span&gt; GCNConv
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;build_industry_adjacency&lt;/span&gt;(industry: torch&lt;span style="color:#f92672"&gt;.&lt;/span&gt;Tensor, n_keep: int &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;4&lt;/span&gt;) &lt;span style="color:#f92672"&gt;-&amp;gt;&lt;/span&gt; torch&lt;span style="color:#f92672"&gt;.&lt;/span&gt;Tensor:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;&amp;#34;&amp;#34;同一行业的股票两两连边，并做行归一化。industry: (n_symbols,) 的整数标签。&amp;#34;&amp;#34;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; n &lt;span style="color:#f92672"&gt;=&lt;/span&gt; industry&lt;span style="color:#f92672"&gt;.&lt;/span&gt;size(&lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; same &lt;span style="color:#f92672"&gt;=&lt;/span&gt; industry&lt;span style="color:#f92672"&gt;.&lt;/span&gt;unsqueeze(&lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;) &lt;span style="color:#f92672"&gt;==&lt;/span&gt; industry&lt;span style="color:#f92672"&gt;.&lt;/span&gt;unsqueeze(&lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; adj &lt;span style="color:#f92672"&gt;=&lt;/span&gt; same&lt;span style="color:#f92672"&gt;.&lt;/span&gt;float()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; adj&lt;span style="color:#f92672"&gt;.&lt;/span&gt;fill_diagonal_(&lt;span style="color:#ae81ff"&gt;1.0&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;# 行业过大时每行只保留相似度最高的 n_keep 个（防止金融板块把噪声全拉进来）&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; n_keep:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; topv, topi &lt;span style="color:#f92672"&gt;=&lt;/span&gt; adj&lt;span style="color:#f92672"&gt;.&lt;/span&gt;topk(min(n_keep &lt;span style="color:#f92672"&gt;+&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;, n), dim&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; keep &lt;span style="color:#f92672"&gt;=&lt;/span&gt; torch&lt;span style="color:#f92672"&gt;.&lt;/span&gt;zeros_like(adj)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; keep&lt;span style="color:#f92672"&gt;.&lt;/span&gt;scatter_(&lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;, topi, &lt;span style="color:#ae81ff"&gt;1.0&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; adj &lt;span style="color:#f92672"&gt;=&lt;/span&gt; adj &lt;span style="color:#f92672"&gt;*&lt;/span&gt; keep
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; adj &lt;span style="color:#f92672"&gt;/&lt;/span&gt; adj&lt;span style="color:#f92672"&gt;.&lt;/span&gt;sum(dim&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;, keepdim&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;True&lt;/span&gt;)&lt;span style="color:#f92672"&gt;.&lt;/span&gt;clamp_min(&lt;span style="color:#ae81ff"&gt;1e-9&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;class&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;STGNNBlock&lt;/span&gt;(nn&lt;span style="color:#f92672"&gt;.&lt;/span&gt;Module):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;&amp;#34;&amp;#34;空间（GCN 传递截面信息）+ 时间（GRU 聚合时序）的交替堆叠。&amp;#34;&amp;#34;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;__init__&lt;/span&gt;(self, n_feat: int, hidden: int &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;32&lt;/span&gt;, edge_index: torch&lt;span style="color:#f92672"&gt;.&lt;/span&gt;Tensor &lt;span style="color:#f92672"&gt;|&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;None&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;None&lt;/span&gt;):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; super()&lt;span style="color:#f92672"&gt;.&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;__init__&lt;/span&gt;()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; self&lt;span style="color:#f92672"&gt;.&lt;/span&gt;gcn &lt;span style="color:#f92672"&gt;=&lt;/span&gt; GCNConv(n_feat, hidden)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; self&lt;span style="color:#f92672"&gt;.&lt;/span&gt;gru &lt;span style="color:#f92672"&gt;=&lt;/span&gt; nn&lt;span style="color:#f92672"&gt;.&lt;/span&gt;GRU(hidden, hidden, batch_first&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;True&lt;/span&gt;, num_layers&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; self&lt;span style="color:#f92672"&gt;.&lt;/span&gt;head &lt;span style="color:#f92672"&gt;=&lt;/span&gt; nn&lt;span style="color:#f92672"&gt;.&lt;/span&gt;Linear(hidden, &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; self&lt;span style="color:#f92672"&gt;.&lt;/span&gt;edge_index &lt;span style="color:#f92672"&gt;=&lt;/span&gt; edge_index
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;forward&lt;/span&gt;(self, x: torch&lt;span style="color:#f92672"&gt;.&lt;/span&gt;Tensor) &lt;span style="color:#f92672"&gt;-&amp;gt;&lt;/span&gt; torch&lt;span style="color:#f92672"&gt;.&lt;/span&gt;Tensor:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;&amp;#34;&amp;#34;x: (n_symbols, lookback, n_feat) -&amp;gt; (n_symbols,) 预测分数。&amp;#34;&amp;#34;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; n, t, f &lt;span style="color:#f92672"&gt;=&lt;/span&gt; x&lt;span style="color:#f92672"&gt;.&lt;/span&gt;shape
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; feats &lt;span style="color:#f92672"&gt;=&lt;/span&gt; x&lt;span style="color:#f92672"&gt;.&lt;/span&gt;reshape(n &lt;span style="color:#f92672"&gt;*&lt;/span&gt; t, f)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;# 空间聚合：每行构造 block-diagonal 的边索引&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; self&lt;span style="color:#f92672"&gt;.&lt;/span&gt;edge_index &lt;span style="color:#f92672"&gt;is&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;None&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;raise&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;ValueError&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;edge_index 未设置&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; src, dst &lt;span style="color:#f92672"&gt;=&lt;/span&gt; self&lt;span style="color:#f92672"&gt;.&lt;/span&gt;edge_index
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; ei &lt;span style="color:#f92672"&gt;=&lt;/span&gt; torch&lt;span style="color:#f92672"&gt;.&lt;/span&gt;cat([src &lt;span style="color:#f92672"&gt;+&lt;/span&gt; i &lt;span style="color:#f92672"&gt;*&lt;/span&gt; n &lt;span style="color:#66d9ef"&gt;for&lt;/span&gt; i &lt;span style="color:#f92672"&gt;in&lt;/span&gt; range(t)])&lt;span style="color:#f92672"&gt;.&lt;/span&gt;long(), torch&lt;span style="color:#f92672"&gt;.&lt;/span&gt;cat([dst &lt;span style="color:#f92672"&gt;+&lt;/span&gt; i &lt;span style="color:#f92672"&gt;*&lt;/span&gt; n &lt;span style="color:#66d9ef"&gt;for&lt;/span&gt; i &lt;span style="color:#f92672"&gt;in&lt;/span&gt; range(t)])&lt;span style="color:#f92672"&gt;.&lt;/span&gt;long()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; h &lt;span style="color:#f92672"&gt;=&lt;/span&gt; F&lt;span style="color:#f92672"&gt;.&lt;/span&gt;relu(self&lt;span style="color:#f92672"&gt;.&lt;/span&gt;gcn(feats, ei))
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; h &lt;span style="color:#f92672"&gt;=&lt;/span&gt; h&lt;span style="color:#f92672"&gt;.&lt;/span&gt;view(n, t, &lt;span style="color:#f92672"&gt;-&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; out, _ &lt;span style="color:#f92672"&gt;=&lt;/span&gt; self&lt;span style="color:#f92672"&gt;.&lt;/span&gt;gru(h)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; self&lt;span style="color:#f92672"&gt;.&lt;/span&gt;head(out[:, &lt;span style="color:#f92672"&gt;-&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;, :])&lt;span style="color:#f92672"&gt;.&lt;/span&gt;squeeze(&lt;span style="color:#f92672"&gt;-&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;图怎么建，比模型怎么改更重要。实验室试过三种邻接：&lt;/p&gt;
&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;图的构造&lt;/th&gt;
 &lt;th&gt;IC 均值&lt;/th&gt;
 &lt;th&gt;备注&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;不建图（GRU-only）&lt;/td&gt;
 &lt;td&gt;0.031&lt;/td&gt;
 &lt;td&gt;基线&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;同行业连边&lt;/td&gt;
 &lt;td&gt;0.036&lt;/td&gt;
 &lt;td&gt;稳定提升&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;行业 + 20 日收益相关性 top-3&lt;/td&gt;
 &lt;td&gt;0.038&lt;/td&gt;
 &lt;td&gt;提升但噪声变大&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;行业 + 供应链（供应商/客户）&lt;/td&gt;
 &lt;td&gt;0.033&lt;/td&gt;
 &lt;td&gt;数据稀疏，覆盖度不足&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;供应链图理论上最有价值，但 A 股的供应商客户关系披露覆盖率太低，最终 30% 的股票没有一条边。&lt;strong&gt;图的覆盖率决定了图模型的上限&lt;/strong&gt;。&lt;/p&gt;
&lt;h2 id="五rocm-上的训练细节"&gt;五、ROCm 上的训练细节
&lt;/h2&gt;&lt;p&gt;&lt;a class="link" href="https://yuansima.com/p/install-llama-cpp-ubuntu-2604-rocm/" &gt;本地算力节点&lt;/a&gt;是 AMD Radeon 890M（gfx1150）+ 48GB UMA 内存，ROCm 7.2.3。要点：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# PyTorch 必须装 ROCm 构建版本&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;pip install torch torchvision --index-url https://download.pytorch.org/whl/rocm7.2
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;pip install pytorch-forecasting pytorch-lightning torch-geometric
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# 核显架构白名单覆盖（890M -&amp;gt; gfx1150）&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;export HSA_OVERRIDE_GFX_VERSION&lt;span style="color:#f92672"&gt;=&lt;/span&gt;11.5.0
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;python -c &lt;span style="color:#e6db74"&gt;&amp;#34;import torch; print(torch.cuda.is_available(), torch.cuda.get_device_name(0))&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;precision=&amp;quot;bf16-mixed&amp;quot;&lt;/code&gt; 是必需的&lt;/strong&gt;。核显上 fp16 的 loss scaling 容易溢出，训练到第 10 个 epoch 左右突然 loss 变 NaN，我为此浪费了两天。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;UMA 内存是把双刃剑&lt;/strong&gt;：48GB 共享内存意味着 batch 可以开很大，但也意味着 CPU 内存吃紧时会直接 OOM。建议 &lt;code&gt;export HSA_ENABLE_SDMA=0&lt;/code&gt; 减少内存拷贝抖动。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;torch_geometric&lt;/code&gt; 的 scatter 操作在 ROCm 上比 CUDA 慢 20~30%&lt;/strong&gt;，图很大时要控制每行邻居数。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;实测训练时间（800 只股票 × 1200 天，lookback 60）：&lt;/p&gt;
&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;模型&lt;/th&gt;
 &lt;th&gt;单 epoch&lt;/th&gt;
 &lt;th&gt;30 epoch&lt;/th&gt;
 &lt;th&gt;显存峰值&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;LightGBM（CPU）&lt;/td&gt;
 &lt;td&gt;—&lt;/td&gt;
 &lt;td&gt;约 40 秒&lt;/td&gt;
 &lt;td&gt;—&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;TFT（hidden 64）&lt;/td&gt;
 &lt;td&gt;95 秒&lt;/td&gt;
 &lt;td&gt;约 48 分钟&lt;/td&gt;
 &lt;td&gt;3.1 GB&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;ST-GNN（4 层）&lt;/td&gt;
 &lt;td&gt;60 秒&lt;/td&gt;
 &lt;td&gt;约 30 分钟&lt;/td&gt;
 &lt;td&gt;2.4 GB&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id="六结果一个不性感的对照表"&gt;六、结果：一个不性感的对照表
&lt;/h2&gt;&lt;p&gt;同一份特征、同一份样本、同一套 purged 时序切分，三个模型的表现：&lt;/p&gt;
&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;模型&lt;/th&gt;
 &lt;th&gt;RankIC&lt;/th&gt;
 &lt;th&gt;ICIR&lt;/th&gt;
 &lt;th&gt;多空年化（未计成本）&lt;/th&gt;
 &lt;th&gt;多空最大回撤&lt;/th&gt;
 &lt;th&gt;推理耗时/天&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;LightGBM&lt;/td&gt;
 &lt;td&gt;0.042&lt;/td&gt;
 &lt;td&gt;0.31&lt;/td&gt;
 &lt;td&gt;16.4%&lt;/td&gt;
 &lt;td&gt;-12.7%&lt;/td&gt;
 &lt;td&gt;0.8 秒&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;TFT&lt;/td&gt;
 &lt;td&gt;0.038&lt;/td&gt;
 &lt;td&gt;0.29&lt;/td&gt;
 &lt;td&gt;17.9%&lt;/td&gt;
 &lt;td&gt;-9.4%&lt;/td&gt;
 &lt;td&gt;6.2 秒&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;ST-GNN&lt;/td&gt;
 &lt;td&gt;0.036&lt;/td&gt;
 &lt;td&gt;0.26&lt;/td&gt;
 &lt;td&gt;15.1%&lt;/td&gt;
 &lt;td&gt;-11.8%&lt;/td&gt;
 &lt;td&gt;4.5 秒&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;结论得诚实地说：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;IC 上 GBDT 依然最好&lt;/strong&gt;。深度模型没有&amp;quot;发现&amp;quot;新 alpha；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;TFT 的多空组合回撤更小&lt;/strong&gt;（-9.4% vs -12.7%），说明它学到的东西更&amp;quot;稳&amp;quot;，可能与时序注意力对趋势转折的敏感性有关；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;深度模型的推理成本高 5~8 倍&lt;/strong&gt;，对日频策略无所谓，但如果做分钟级就完全不可接受。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;实验室最终的选择是：&lt;strong&gt;主策略用 LightGBM，TFT 的输出作为&amp;quot;风格因子&amp;quot;叠加&lt;/strong&gt;——两者相关性只有 0.6 左右，合成的 IC 能达到 0.048，这是深度模型唯一真正值钱的地方：&lt;strong&gt;它与 GBDT 的误差不相关&lt;/strong&gt;。&lt;/p&gt;
&lt;h2 id="七踩坑清单"&gt;七、踩坑清单
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;窗口跨越停牌期&lt;/strong&gt;：停牌 3 个月后复牌，窗口里 60 个交易日中有 40 个是 NaN，&lt;code&gt;nan_to_num&lt;/code&gt; 填 0 会让模型以为&amp;quot;这只股票波动突然归零&amp;quot;。正确做法是给一个 &lt;code&gt;is_suspended&lt;/code&gt; 掩码。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;样本权重&lt;/strong&gt;：不同日期的股票数量差异很大（2015 年牛市 vs 2018 年熊市），不加权会让模型偏向样本多的时期。实验室用 &lt;code&gt;1 / 当日样本数&lt;/code&gt; 做权重。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;随机种子敏感&lt;/strong&gt;：深度模型的 IC 在不同种子下波动 ±0.006，报告结果必须给出&lt;strong&gt;多个种子的均值&lt;/strong&gt;，否则就是在挑好看的种子。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;predict_mode&lt;/code&gt; 与 &lt;code&gt;training&lt;/code&gt; 参数&lt;/strong&gt;：pytorch-forecasting 里构造验证集时必须传 &lt;code&gt;training=training_dataset&lt;/code&gt;，否则特征缩放不一致，验证集 IC 会莫名其妙地低。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;不要用全样本拟合归一化&lt;/strong&gt;：&lt;code&gt;GroupNormalizer&lt;/code&gt; 的 &lt;code&gt;groups&lt;/code&gt; 参数决定了按什么分组归一化，用 &lt;code&gt;symbol&lt;/code&gt; 会导致每只股票各自的尺度，跨股票不可比。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="结语"&gt;结语
&lt;/h2&gt;&lt;p&gt;写到这里，量化这条线上的核心环节都跑通了：&lt;a class="link" href="https://yuansima.com/p/timescaledb-market-data-modeling/" &gt;数据&lt;/a&gt; → &lt;a class="link" href="https://yuansima.com/p/quant-factor-engineering/" &gt;因子&lt;/a&gt; → &lt;a class="link" href="https://yuansima.com/p/vectorized-backtest-engine/" &gt;回测&lt;/a&gt; → &lt;a class="link" href="https://yuansima.com/p/ma-momentum-rotation-strategy/" &gt;策略&lt;/a&gt; → &lt;a class="link" href="https://yuansima.com/p/lightgbm-cross-sectional-stock-prediction/" &gt;机器学习&lt;/a&gt; → &lt;a class="link" href="https://yuansima.com/p/tft-stgnn-quant-rocm/" &gt;深度模型&lt;/a&gt;。&lt;/p&gt;
&lt;p&gt;但研究做完不等于能跑。最后一篇回到工程：把这套流程变成&lt;strong&gt;每天早上 8 点自动生成信号并推送到手机&lt;/strong&gt;的服务，用 systemd timer 而不是 crontab，因为失败重试、日志与依赖管理都要靠它。&lt;/p&gt;
&lt;p&gt;远思为径，码动价值。深度模型不会给你 alpha，但它会给你更清楚的自我怀疑。&lt;/p&gt;</description></item></channel></rss>