项目

一般

简介

03-全链路企业级算力防线蓝图 » 历史记录 » 版本 4

Huarui Lin, 2026-04-24 09:59

1 1 Huarui Lin
# # 全链路企业级算力防线蓝图 (严格 0-7 阶段版)
2 2 Huarui Lin
3 1 Huarui Lin
## 第 0 阶段:项目脚手架与 CI 基线
4 2 Huarui Lin
5 1 Huarui Lin
本阶段是全链路的“宪法”,不写任何业务逻辑,仅确立物理约束与拦截规则。
6 2 Huarui Lin
7 1 Huarui Lin
### Step 0.1: 企业级目录拓扑与依赖隔离
8 2 Huarui Lin
9 1 Huarui Lin
1.  **空间隔离**:建立 `src/core`(纯算子,零业务逻辑)、`src/pipelines/s2_to_s5`(离线训练流水线编排)、`src/api/s6_inference`(在线微服务)、`src/backtest`(回测对接层)、`configs`、`tests` 的强约束目录。`src/core` 内层严禁使用 `from .xxx import *`,4个核心模块必须建立独立子目录,且在各自的 `__init__.py` 中显式白名单导出,防止跨层逆向导入导致的 DRY 拓扑破坏。
10
2.  **依赖锁死**:在 `pyproject.toml` 中精确锁定 `polars`, `duckdb`, `lightgbm`, `numpy`, `scipy`, `fastapi`, `pydantic` 的大版本号,彻底封杀上游破坏性更新引发的静默精度漂移。【依赖纯净化红线】 严禁引入实验追踪库(如 mlflow、weights&biases)与超参搜索库(如 optuna、ray[tune])。规约 S5.2 已在拓扑层面彻底否决超参搜索,此类依赖仅会污染 Docker 镜像层与增加无用编译时间。
11 2 Huarui Lin
12 1 Huarui Lin
### Step 0.2: 配置中心统一收敛
13 2 Huarui Lin
14 1 Huarui Lin
1.  将 D-1 基金类型白名单、MAD 裁剪系数 `1.4826` 与宽容度倍数 `5.0`、Spearman 阈值 `0.90` 全部沉淀于 `config.yaml`。
15
2.  建立 `core/config_loader.py`,启动时做 Pydantic 强校验,缺失配置直接 `sys.exit(1)` 阻断。
16 2 Huarui Lin
17 1 Huarui Lin
### Step 0.3: CI/CD 红线拦截器部署 (强制门禁)
18 2 Huarui Lin
19 3 Huarui Lin
在 Git 流水线与本地 Pre-commit 中植入四大静态/动态拦截器:
20
1. **Pandas 零容忍**:基于现有 AST 语法树扫描器 `check_forbidden_imports.py`,发现 `import pandas` 直接报错阻断。
21 4 Huarui Lin
2. **Schema 对称差拦截 (已解封内聚)**:通过 Pytest 注册 `schema_drift` 标记,在 S2/S3/S4 物理落盘后立刻触发强校验,比对产出文件的列名集合与硬编码契约的对称差,差集非空直接 `sys.exit(1)`。
22
3. **底层算子金色单测 (已解封内聚)**:通过 Pytest 注册 `golden_precision` 标记,在 Step 2.4 OLS/EWM 算子实现时生成极小确定性 `.npy` 基线,后续每次跑单测强制比对,浮点误差越过 `1e-7` 直接阻断。
23
4. **工程治理正则扫描**:新增纯 Python 脚本 `check_engineering_governance.py`。全局拦截违规 API 与魔法数字硬编码。内置注释过滤逻辑,防止误杀文档说明。
24
25 1 Huarui Lin
26 2 Huarui Lin
---
27 3 Huarui Lin
28 1 Huarui Lin
## 第 1 阶段:S2 - 数据血缘与清洗拓扑
29 2 Huarui Lin
30 1 Huarui Lin
面对 2800 万行原始净值,走“DuckDB 粗筛压榨 -> Polars 流式微操 -> Out-of-core 物理落盘”降维路径。
31 2 Huarui Lin
32 1 Huarui Lin
### Step 1.1: DuckDB 纯粗筛算子防火墙
33 2 Huarui Lin
34 1 Huarui Lin
1.  **底层转码**:Polars 读取原始 CSV,强制指定类型(`fund_id` 为 `Categorical`,日期为 `Date32`),落盘为原生 Parquet 数据湖。严禁 DuckDB 直接对抗 CSV。
35
2.  **D-1 全局物理下推**:在 DuckDB 内基于配置白名单过滤基础信息,提取合法 `fund_id` 集合。
36
3.  **周频无状态聚合**:利用唯一合法的 SQL 实体(基于 `1970-01-05` 基准的纯数学整数除法对齐周一),Inner Join 后按周取组内 `max(date)` 对应净值。粗筛产物导出为极小的 `s2_raw_weekly.parquet`,关停 DuckDB。
37 2 Huarui Lin
38 1 Huarui Lin
### Step 1.2: Polars 外层 Fund 隔离与长缺失切断
39 2 Huarui Lin
40 1 Huarui Lin
1.  **长缺失切断判定**:按 `fund_id` 分组,计算相邻两行真实数据周日期差值。当差值绝对值 > 5 时,使用累加逻辑为每个连续物理块打上递增的 `segment_id`。
41 2 Huarui Lin
42 1 Huarui Lin
### Step 1.3: 内层 Segment 绝对闭包与短缺失 5 步强制顺序
43 2 Huarui Lin
44 1 Huarui Lin
1.  **微型连续日历生成**:闭包内提取起止日期,利用纯整数差值生成无缺失周序列,Left Join 标记 `is_original`。
45
2.  **首行越界防御**:对净值列 `forward_fill()`,提取闭包首行,若为 NULL 立即抛出 `SegmentFirstRowNullError` 阻断流水线。
46
3.  **血缘切断重算收益率**:基于纯净净值序列向量化重算全量 `weekly_return`。
47
4.  **占位行收益率置 NULL**:执行掩码,将 `is_original=False` 的行收益率强制置为 NULL,彻底切断 0.0 幻影收益向下游 `rolling_std` 的传播。
48 2 Huarui Lin
49 1 Huarui Lin
### Step 1.4: 内存尖峰拦截与按年 Out-of-core 落盘
50 2 Huarui Lin
51 1 Huarui Lin
1.  **LazyFrame 谓词下推**:年份循环内,在 Lazy 状态按年过滤,完美命中 Parquet Row Group 统计信息。
52
2.  **物理排序死锁约束落地**:链式调用 `.sort(by=["net_value_date", "fund_id"], maintain_order=False)`。
53
3.  **零内存膨胀落盘**:对接 `sink_parquet()`,强制注入 `row_group_size=100000`, `compression="zstd"`, `compression_level=7`。产出 `s2_YYYY.parquet`,循环内就地 `del` 释放。
54 2 Huarui Lin
55 1 Huarui Lin
---
56
## 第 2 阶段:S3 - 特征工程
57 2 Huarui Lin
58 1 Huarui Lin
全链路算力雪崩最高危区,走“按年安全池 -> 纯 NumPy 24 核微线程 -> 物理哨兵封堵”极限拓扑。
59 2 Huarui Lin
60 1 Huarui Lin
### Step 2.1: S3 入口按年流式与局部安全池桥接
61 2 Huarui Lin
62 1 Huarui Lin
1.  单年数据全量拉入安全沙箱(< 3GB)。
63
2.  执行 `partition_by(["fund_id", "segment_id"], as_dict=True)`,零拷贝打散为字典视图传递给 C 层。
64 2 Huarui Lin
65 1 Huarui Lin
### Step 2.2: 纯 NumPy C 算子微线程拓扑
66 2 Huarui Lin
67 1 Huarui Lin
1.  实例化 24 核 `ThreadPoolExecutor` 分发字典值。
68
2.  GIL 防线:内层函数第一步必须 `to_numpy().flatten()` 抽出纯连续内存数组。
69 2 Huarui Lin
70 1 Huarui Lin
### Step 2.3: A/B/C 类特征与分母隔离防线
71 2 Huarui Lin
72 1 Huarui Lin
1.  **分母隔离**:所有 12/26/52 周窗口计算,入口判断 `len(arr) < window`,不满足直接输出 `None`。
73
2.  **A/B/C 类向量化**:使用 `cumsum` 截取法算均值偏离;`np.minimum(ret, 0)` 屏蔽正收益算下行波动;`cummax` 位移算最大回撤;使用 `is_start` 与 `cum_arr` 的 O(N) 骨架算连阴周数。
74 2 Huarui Lin
75 1 Huarui Lin
### Step 2.4: OLS 矩阵击穿与 EWM 封堵
76 2 Huarui Lin
77 1 Huarui Lin
1.  **OLS 骨架**:提取 `np.log(nav)`,O(1) 公式算分母 `ss_xx`,纯 `np.dot` 算分子,斜率强制乘 `52` 年化。封杀 `lstsq`。
78
2.  **EWM 骨架**:转换为 `scipy.signal.lfilter` 分子分母系数,利用 C 递推短暂释放 GIL。
79 2 Huarui Lin
80 1 Huarui Lin
### Step 2.5: D/E 类特征与 NULL 穿透
81 2 Huarui Lin
82 1 Huarui Lin
1.  **D 类定投**:分母强制修正为带窗口期的 `rolling_mean(nav, X)`,计算成本偏离与胜率。
83
2.  **E 类风险**:显式写出 `(mean_ret - weekly_rf) / std_ret` 结构。
84
3.  **Regime NULL**:上市不足 52 周导致分母为 NULL 时,直接输出 NULL,不做任何 `fill_null(0)` 替换,任由其触发哨兵。
85 2 Huarui Lin
86 1 Huarui Lin
### Step 2.6: 物理哨兵生成与落盘
87 2 Huarui Lin
88 1 Huarui Lin
1.  横向非空判定生成 `is_feature_complete`。
89
2.  复用全局 `sink_parquet` 拓扑产出 `s3_YYYY.parquet`。
90 2 Huarui Lin
91 1 Huarui Lin
---
92
## 第 3 阶段:S4 - 标签生成
93 2 Huarui Lin
94 1 Huarui Lin
业务逻辑核心,防“毒特征污染”与“索引错位”。
95 2 Huarui Lin
96 1 Huarui Lin
### Step 3.1: S4 绝对隔离加载
97 2 Huarui Lin
98 1 Huarui Lin
1.  `scan_parquet` 扫描 S2 产物,立即 `.select(["fund_id", "net_value_date", "segment_id", "cumulative_net_value"])`。严禁拉入 `weekly_return`。
99 2 Huarui Lin
100 1 Huarui Lin
### Step 3.2: DCA 闭式向量化公式
101 2 Huarui Lin
102 1 Huarui Lin
1.  断言 `nv_array` 纯净且大于 0。
103
2.  倒数累加范式:`inv_nv -> cum_inv -> weeks -> returns`,全程无循环。
104 2 Huarui Lin
105 1 Huarui Lin
### Step 3.3: 止盈边界与 0-based 转 1-based 拦截
106 2 Huarui Lin
107 1 Huarui Lin
1.  `np.where(returns >= 0.20)[0]` 命中检索。
108
2.  索引偏移修正:`label = int(hit_indices[0] + 1) if len > 0 else 0`。
109
3.  绝对边界断言:`assert 0 <= label <= 150`。
110 2 Huarui Lin
111 1 Huarui Lin
### Step 3.4: 按年落盘
112 2 Huarui Lin
113 1 Huarui Lin
复用全局工具函数产出 `s4_YYYY.parquet`。
114 2 Huarui Lin
115 1 Huarui Lin
---
116
## 第 4 阶段:S5 - 标准化与 Spearman 降维
117 2 Huarui Lin
118 1 Huarui Lin
融合“截面数学推导”与“内存防爆累加”。
119 2 Huarui Lin
120 1 Huarui Lin
### Step 4.1: 截面聚合与双形态参数物理时序隔离
121 2 Huarui Lin
122 1 Huarui Lin
1.  **纯横截面聚合**:按 `net_value_date` 分组计算 38 维的 `mean, std, median, mad`,推导 MAD 上下界,生成 `is_production_ready` 哨兵。
123
2.  **路径 A (训练宽表)**:宽表上 `.shift(1)` 实现 T-1 对齐,按日期排序落盘。
124
3.  **路径 B (推理长表)**:Unpivot 为长表,动态生成自然日历 `forward_fill` 至每日,按 `["feature_name", "date"]` 绝对物理排序落盘。
125 2 Huarui Lin
126 1 Huarui Lin
### Step 4.2: 标准化 Apply 与 IEEE 754 拦截
127 2 Huarui Lin
128 1 Huarui Lin
1.  实体表 `.collect()` 后与几百行宽表参数 Join(零内存膨胀广播)。
129
2.  遍历 38 列构建 `when(std==0).then(None).otherwise(...)` 表达式,封杀除零产生 `Inf`。
130 2 Huarui Lin
131 1 Huarui Lin
### Step 4.3: 训练 Join 三键死锁与毒样本截断
132 2 Huarui Lin
133 1 Huarui Lin
1.  三键强锁 Join:`["fund_id", "net_value_date", "segment_id"]`。
134
2.  哨兵覆写:`is_feature_complete == False` 的行,标签强制覆写为 `0`。
135 2 Huarui Lin
136 1 Huarui Lin
### Step 4.4: Spearman 秩相关内存防爆防线
137 2 Huarui Lin
138 1 Huarui Lin
1.  按年流式加载标准化特征。
139
2.  截面内 `rankdata(method="average")` 锁定平均秩,算秩后立即减均值。
140
3.  Pearson 展开项 `np.dot` 联合累加 `sum_xy`,利用对称性减半算力。
141
4.  跨年累加后执行 `(matrix + matrix.T) / 2.0` 强制对称化截断。
142
5.  剔除 `>= 0.90` 的冗余特征,输出 `final_feature_list`。
143 2 Huarui Lin
144 1 Huarui Lin
---
145
## 第 5 阶段:S5.3 - 训练流水线
146 2 Huarui Lin
147 1 Huarui Lin
将降维后的纯净特征转化为 LightGBM LambdaRank 可消费的形态并进行闭环重训。
148 2 Huarui Lin
149 1 Huarui Lin
### Step 5.1: LambdaRank 样本平衡重构
150 2 Huarui Lin
151 1 Huarui Lin
1.  **Group 划分**:全量表按 `net_value_date` 排序,每个日期即一个 Query Group。
152
2.  **差异化等距抽样**:正样本池(`label>0`)用 `np.linspace` 抽最多 50 条;负样本池抽最多 15 条。
153
3.  **相关性得分映射**:`label=0` 映射为 `0`,`label=1~150` 映射为 `151 - label`(第 1 周得 150 分)。
154
4.  **Group 边界构建**:记录每个截面抽样数,构建一维数组传给 LightGBM 的 `group` 参数。
155 2 Huarui Lin
156 1 Huarui Lin
### Step 5.2: Time-Series 5 折评估防线
157 2 Huarui Lin
158 1 Huarui Lin
1.  时间切分:按 20% 步长划分 5 个验证区块,使用累积扩展窗口。
159
2.  **NDCG@10 绝对核心**:训练传入 `group_boundaries`,评估强制锁定 `NDCG@10` 指标,直接对齐 Top 10 榜单业务 KPI。
160
3.  **防穿透锁定**:5 折期间严禁再次剔除特征或修改任何超参,仅观察指标分布。
161 2 Huarui Lin
162 1 Huarui Lin
### Step 5.3: 闭环全量重训与产物契约落盘
163 2 Huarui Lin
164 1 Huarui Lin
1.  使用全量抽样数据 + `final_feature_list` 进行最终无截断训练。
165
2.  **产物强制打包**:将 `model.txt` 与两张标准化参数表打入同一个带时间戳的发布目录(如 `artifacts/v_20231024/`)。
166
3.  **MD5 强校验**:生成模型文件的 MD5 摘要,作为后续 S6 加载前的防篡改门禁。
167 2 Huarui Lin
168 1 Huarui Lin
---
169
## 第 6 阶段:S6 - 推理服务
170 2 Huarui Lin
171 1 Huarui Lin
纯粹的“无状态数学计算引擎”,彻底剥离所有 I/O 职责。
172 2 Huarui Lin
173 1 Huarui Lin
### Step 6.1: FastAPI 骨架与进程级全局缓存防抖
174 2 Huarui Lin
175 1 Huarui Lin
1.  **生命周期加载**:在 `lifespan` 启动钩子中,读取发布目录产物,加载 `_PARAMS_LONG_CACHE` (长表)、`_AVAIL_DATES_CACHE` (日期数组)、`_MODEL` (Booster)。
176
2.  **MD5 门禁校验**:加载模型前比对 MD5,不一致直接 `os._exit(1)` 拒绝启动。
177 2 Huarui Lin
178 1 Huarui Lin
### Step 6.2: 两步走时间锚定防线
179 2 Huarui Lin
180 1 Huarui Lin
1.  **第一步锚定**:接收单维 `request_date`,与 `_AVAIL_DATES_CACHE` 执行极速 `asof_join` 寻找 `valid_date`。无效则短路返回空结构体。
181
2.  **第二步拉取**:基于 `final_feature_list` 构建左表,按 `["feature_name", "valid_date"]` 排序,与 `_PARAMS_LONG_CACHE` 执行双键 `asof_join`,后置过滤 `is_production_ready`。
182 2 Huarui Lin
183 1 Huarui Lin
### Step 6.3: 2D 矩阵级向量化批处理内核
184 2 Huarui Lin
185 1 Huarui Lin
1.  **统一抽象**:无论单基金(N=1)还是批量(N=1000),统一接收 `(N, 38)` 原始特征 2D 矩阵。
186
2.  **复用 S5 内核**:调用 `core.standardization_math.apply_zscore`,利用 NumPy 广播一次性完成 N 行的 MAD 裁剪与 Z-Score 转换。
187
3.  **矩阵预测**:`_MODEL.predict(matrix)`,利用 C++ 底层极速输出得分。
188 2 Huarui Lin
189 1 Huarui Lin
### Step 6.4: 接口物理隔离与归因红线
190 2 Huarui Lin
191 1 Huarui Lin
1.  **单基金接口**:允许调用 `predict_contrib` 返回 SHAP 归因明细。
192
2.  **批量接口**:架构级禁止引入 `predict_contrib` 代码,归因字段强制硬编码为空字符串 `""`。
193 2 Huarui Lin
194 1 Huarui Lin
---
195
## 第 7 阶段:端到端集成与打通
196 2 Huarui Lin
197 1 Huarui Lin
这是将离线数学模型转化为线上业务价值的最终闭环,解决“产物交接、预热、回测对接、极限压测”四大工程鸿沟。
198 2 Huarui Lin
199 1 Huarui Lin
### Step 7.1: 产物自动化交接与灰度发布契约
200 2 Huarui Lin
201 1 Huarui Lin
1.  **CI/CD 打包流**:S5 训练流水线成功后,CI 自动将 `artifacts/v_xxx/` 打包为 Docker Image 或推送到 OSS/Minio。
202
2.  **S6 滚动更新**:K8s/ Docker Compose 执行滚动更新。新 Pod 启动时拉取最新产物进行 MD5 校验与预热,校验失败则 Pod 启动失败,旧 Pod 继续服役,实现无损回滚。
203 2 Huarui Lin
204 1 Huarui Lin
### Step 7.2: S6 冷启动掩盖与 Health Check 强断言
205 2 Huarui Lin
206 1 Huarui Lin
1.  **就绪探针**:在 FastAPI 中实现 `/health` 接口。不仅返回 200,内部必须验证 `_MODEL is not None` 且 `_PARAMS_LONG_CACHE.shape[0] > 0`。未加载完毕前,K8s 绝不将流量打入该 Pod。
207
2.  **预热请求**:Pod 启动后,后台线程自动发起一次包含 10 只基金的矩阵预测请求,强制触发 LightGBM 内部树的 JIT 缓存预热,掩盖首次真实请求的微秒级延迟毛刺。
208 2 Huarui Lin
209 1 Huarui Lin
### Step 7.3: 回测引擎标准化数据对接
210 2 Huarui Lin
211 1 Huarui Lin
1.  **定义回测接口契约**:S6 批量接口不直接服务 C 端,而是作为 BFF 层被回测引擎(如 Backtrader/Qlib)调用。
212
2.  **历史截面回放**:回测引擎按历史日期循环,将历史 T 日全市场基金原始特征组装为 `(N, 38)` 矩阵,调用 S6 获取 T 日得分排名。
213
3.  **严格按照 NDCG@10 验证**:回测引擎拿到 Top 10 池后,向后看 150 周计算真实定投收益率。对比回测出的“Top 10 平均达标率”与 S5 验证集的“NDCG@10 曲线”,两者必须呈现强正相关,否则证明存在数据穿越 Bug。
214 2 Huarui Lin
215 1 Huarui Lin
### Step 7.4: 全链路极限压测与 cgroup 防雪崩验证
216 2 Huarui Lin
217 1 Huarui Lin
1.  **流量炮台构建**:使用 Locust 编写压测脚本,模拟 50 并发线程,每线程持续发送 N=1000 的批量推荐请求。
218
2.  **P99 延迟红线**:监控 S6 服务的 P99 响应时间,必须死锁在 `< 50ms` 以内。一旦超限,说明触犯了矩阵广播外的隐性锁或内存交换。
219
3.  **内存防雪崩观察**:在压测期间,通过 `docker stats` 严格监视 S6 容器的内存曲线。因为 S6 是纯矩阵运算无中间态膨胀,其内存曲线必须是一条绝对水平的直线。任何呈阶梯状上升的现象,都证明存在隐藏的内存泄漏或未触发全局缓存的灾难逻辑,必须阻断上线。