### [别买黑盒了,自己造一个知道”自己有多蠢”的量化预测系统](https://dashen.wang/article/22) **Published:** 2026-07-11T14:26:42 **Author:** AI最严厉的父亲 **Excerpt:** 华尔街的量化基金每天都在因为AI的盲目自信爆仓。一个号称胜率80%的黑盒模型,把它的说明书拆开来看,里面十有八九是用文本生成的那套逻辑强行套在金融时序上——等同于让文科状元去拆炸弹,拆成功了是运气,炸了是必然。 与其花大价钱买别人的棺材,不 华尔街的量化基金每天都在因为AI的盲目自信爆仓。一个号称胜率80%的黑盒模型,把它的说明书拆开来看,里面十有八九是用文本生成的那套逻辑强行套在金融时序上——等同于让文科状元去拆炸弹,拆成功了是运气,炸了是必然。 与其花大价钱买别人的棺材,不如花几个小时,在你手头的编辑器里,亲手造一口专门装自己无知的盒子。 这就是本文要做的事。 ## 一、先把规矩刻在石头上:劫持云端代理的第一道指令 工具不对,累死白费。 huggingface/skills 本质上是一套遵循 Model Context Protocol (MCP) 标准的接口包,兼容 Claude Code、Codex、Gemini CLI 和 Cursor,让你的本地 AI 助手直接调动 Hugging Face 云端的计算资源和工具链。在你的项目根目录建一个 .mcp.json,或者通过 Claude Code 挂载: ``` claude mcp add --transport http hf-skills https://huggingface.co/mcp?bouquet=skills --header "Authorization: Bearer $HF_TOKEN" ``` 挂上之后,你就拥有了三把核心武器:hugging-face-datasets(拉数据)、hugging-face-jobs(上云炼丹)、hugging-face-trackio(实时盯大盘)。 但在动手之前,必须先下一道硬性禁令。 向你的 AI 助手发出第一道死命令:**"接下来所有开发,禁止调用 hugging-face-model-trainer 这个针对大语言模型微调的预设技能。我们要从零手写时间序列神经网络,通过 hugging-face-jobs 把定制化的 PyTorch 脚本推到云端 GPU 执行。"** 为什么要禁?因为 hugging-face-model-trainer 走的是大模型训练路线——那是调教会说话的语言模型的逻辑,不是预测金融时序的逻辑。把它们混用,就像拿锤子拧螺丝:形状上有那么点相似,但你会把整个工件拧废。 规矩定好,我们开始搭架子。 ## 二、截断黑天鹅:数据处理的第一课就是认清边界 任何预测模型的生死,在喂入第一口数据的时候就注定了。 加密货币的走势是典型的"胖尾分布"——大多数时候风平浪静,但一根黑天鹅行情可以在几分钟内吞掉你三个月的利润。很多新手喜欢把这些极端数据原封不动塞给网络,指望AI能学会"预测黑天鹅"。 **试图用历史数据拟合偶发的黑天鹅,是量化交易员破产的最快路径。** 你的模型学不会捕捉它,它只会被那根惊天大阳线污染,然后在下一次正常行情里胡乱放炮。 正确的做法是:承认模型的边界,然后在边界处装上物理护栏。 动用 hugging-face-datasets 拉取 BTC、ETH 在内的11种高流动性加密货币的日线数据(2020年至今),然后在数据处理脚本里写死一道过滤网: **第一步计算每天的对数收益率**——简单说就是今天收盘价除以昨天收盘价取对数,公式写出来是 rₜ = log(Pₜ / Pₜ₋₁),比直接算涨跌幅更能压缩极端值、对称处理涨跌。 **第二步截断极端值**——把历史上最疯的1%和最惨的1%直接抹平,不是假装它们不存在,而是承认模型根本消化不了。 **第三步独立标准化**——每个交易标的单独做零均值单位方差处理,防止比特币的绝对价格波动把以太坊的信号淹没。 **第四步构造滑动窗口**——用过去50天的数据预测第51天,50这个数字是 ProbFM 原始论文的设定。 ``` import numpy as np import pandas as pd from sklearn.preprocessing import StandardScaler def process_crypto_data(df, lookback=50): # 对数收益率:rₜ = log(Pₜ / Pₜ₋₁) df['log_return'] = np.log(df['close'] / df['close'].shift(1)) df = df.dropna() # 物理隔离黑天鹅:1%到99%的硬截断 # 注意:分位数用训练集历史分布计算,不能用全集,否则就是数据泄露 lower_bound = df['log_return'].quantile(0.01) upper_bound = df['log_return'].quantile(0.99) df['clipped_return'] = df['log_return'].clip(lower=lower_bound, upper=upper_bound) # 独立标准化,零均值单位方差 scaler = StandardScaler() df['scaled_return'] = scaler.fit_transform(df[['clipped_return']]) # 构造50步长的滑动窗口序列 X, y = [], [] returns = df['scaled_return'].values for i in range(len(returns) - lookback): X.append(returns[i:(i + lookback)]) y.append(returns[i + lookback]) return np.array(X), np.array(y), scaler ``` 这段代码没有花哨的特征工程。**认清模型的边界,是建立不确定性系统的第一课。** 截断的不是黑天鹅本身,而是你对它的幻觉。 > **补充说明**:近期学界对加密货币波动率的研究证实,概率预测方法在捕捉极端行情风险方面显著优于传统的确定性点预测。当没有任何一种模型在所有资产和误差指标上稳定压制其他模型时,对不确定性的精确量化反而成了更有价值的输出。这正是我们下一步要做的事。 ## 三、造一个会承认"我不知道"的神经网络:NIG输出头的正确姿势 数据准备好了,现在来做最关键的架构改造:把传统的线性输出层直接砸掉,换上一个深度证据回归(Deep Evidential Regression,DER)的输出头。 过去的量化网络只会吐出一个数字,告诉你"明天大概率涨2%"。好一点的会输出均值加方差,假装自己懂概率。但这些都不够。我们要的是一个模型,在单次前向传播中同时输出四个参数: - **μ(mu)**:预测的方向和幅度,就是模型认为明天会涨多少 - **λ(lambda)**:模型对这个预测积累了多少"证据",越大越自信 - **α(alpha)**:控制不确定性分布的形状,必须大于1,小于1整个数学结构崩塌 - **β(beta)**:不确定性的尺度,越大表示模型越觉得市场不可预测 这四个数字共同构成一个完整的正态-逆伽马(NIG)先验分布。听起来复杂,实际上就是一句话:**它不只告诉你预测结果,还告诉你这个预测结果有多不可靠。** ProbFM 的核心创新就在于此:让模型同时具备三种能力——不预先设定分布族、将认知不确定性和偶然不确定性显式拆分、单次推理完成完整不确定性量化,不需要跑几十次采样来估计置信区间。 向你的AI助手下发严厉的架构指令:"用 PyTorch 构建一个单层、隐藏维度32的 LSTM。Dropout 设为0.1。输出头必须是四个分支,分别对应 NIG 分布的四个参数,并施加严格的数学约束。" 数学规矩铁面无私:λ > 0,α > 1,β > 0。任何一个参数输出负数,整个概率计算当场崩溃。 ``` import torch import torch.nn as nn import torch.nn.functional as F class ProbFMLSTM(nn.Module): def __init__(self, input_size=1, hidden_size=32, dropout=0.1): super().__init__() self.lstm = nn.LSTM( input_size=input_size, hidden_size=hidden_size, num_layers=1, batch_first=True, dropout=dropout if dropout > 0 else 0 ) # NIG先验的四个参数输出分支 self.fc_mu = nn.Linear(hidden_size, 1) self.fc_lambda = nn.Linear(hidden_size, 1) self.fc_alpha = nn.Linear(hidden_size, 1) self.fc_beta = nn.Linear(hidden_size, 1) def forward(self, x): lstm_out, _ = self.lstm(x) last_hidden = lstm_out[:, -1, :] # μ:将预测收益率死死压在正负3个标准差内 # tanh把任意输出映射到(-1,1),乘以3.0就是±3σ的物理封印 mu = 3.0 * torch.tanh(self.fc_mu(last_hidden)) epsilon = 0.01 # 防止除以零的保险丝 lambd = F.softplus(self.fc_lambda(last_hidden)) + epsilon alpha = F.softplus(self.fc_alpha(last_hidden)) + 1.0 + epsilon beta = F.softplus(self.fc_beta(last_hidden)) + epsilon return mu, lambd, alpha, beta ``` **剥离复杂架构的滤镜,你才能看清一个模型是真聪明还是在拼硬件。** 这里的 3.0 \* torch.tanh(…) 是道保险阀。如果模型头脑发热要预测比特币明天暴涨500%,这行代码会直接一巴掌把它拍回现实,强行限定在训练数据标准化后的±3个标准差范围内。 ## 四、写入带反噬机制的惩罚函数:MSE养出来的都是滑头 有了输出头,接下来用最严苛的损失函数来调教它。 丢掉 MSE。均方误差培养出来的模型是滑头——为了账面好看,它疯狂和稀泥,永远输出平庸的中间值,对市场极端时刻毫无警惕。 我们手搓一个联合损失函数:**证据损失**(Evidence Loss)+ **覆盖损失**(Coverage Loss)。 证据损失的逻辑很毒辣:如果模型对一个完全错误的预测给出极高自信,损失函数会成倍放大惩罚,直接把它的权重砸烂——**预测越错、越自信,死得越惨**。覆盖损失则要求模型给出的95%置信区间,必须真真切切地把实际价格框在里面,不是说在嘴上,而是刻在每一次反向传播的梯度里。 ``` import torch import math def evidential_loss(mu, lambd, alpha, beta, y_true, lambda_reg=0.1): omega = 2.0 * beta * (1.0 + lambd) nll = (0.5 * torch.log(math.pi / lambd) - alpha * torch.log(omega) + (alpha + 0.5) * torch.log(lambd * (y_true - mu)**2 + omega) + torch.lgamma(alpha) - torch.lgamma(alpha + 0.5)) # 关键惩罚项:预测偏差越大、模型越自信,惩罚越重 error = torch.abs(y_true - mu) reg = error * (2.0 * alpha + lambd) return torch.mean(nll + lambda_reg * reg) def coverage_loss(mu, lambd, alpha, beta, y_true, target_picp=0.95): # NIG分布推导出的预测方差 variance = beta / (alpha - 1.0) std_dev = torch.sqrt(variance) t_critical = 1.96 # 95%置信区间的近似临界值 ci_lower = mu - t_critical * std_dev ci_upper = mu + t_critical * std_dev is_covered = (y_true >= ci_lower) & (y_true <= ci_upper) actual_picp = is_covered.float().mean() return torch.abs(target_picp - actual_picp) ``` > **一个值得注意的学术背景**:有研究指出,NIG先验在波动率聚集的时间序列上存在校准偏差风险——NIG的层级结构可能无法完全分离认知不确定性和偶然不确定性。这不是废掉这套方法的理由,而是提醒你在生产环境里加入额外的校准验证,而不是闭眼相信模型的置信区间数字。 **容错率从来不是写在募资PPT上的漂亮话,它必须实打实地刻在每一次反向传播的梯度里。** ## 五、云端退火试炼:把菜鸟的自信心死死按住 准备工作就绪,我们把整个脚本扔到 Hugging Face 的云端 GPU 上去跑。 通过 hugging-face-jobs,你可以访问 A100、L4 等高端 GPU,无需本地任何硬件投入,用多少付多少,任务跑完自动关机。这意味着你可以把炼丹的事交给云端,自己去做别的,甚至睡一觉回来看结果。 但训练复杂概率模型有一个极其隐蔽的致命陷阱:**证据坍塌(Evidence Collapse)**——模型在还没学到任何有意义的规律时,就开始给出极度自信的预测。这就像招了一个名校毕业的菜鸟交易员,还没经历过一次完整的牛熊,却每次都满仓梭哈。 解法是上"证据退火"机制:**在训练前20%的步数里,把惩罚力度从零开始线性爬升,强压模型的自信心**。就像规定新人交易员头三个月只能用10%仓位,亏够了才给你加仓权限。 ``` import torch.optim as optim from torch.nn.utils import clip_grad_norm_ def train_probfm(model, dataloader, epochs=50): optimizer = optim.AdamW(model.parameters(), lr=0.001, weight_decay=0.001) scheduler = optim.lr_scheduler.CosineAnnealingLR(optimizer, T_max=epochs) total_steps = epochs * len(dataloader) anneal_steps = total_steps * 0.20 # 前20%为退火期 global_step = 0 for epoch in range(epochs): model.train() for batch_x, batch_y in dataloader: optimizer.zero_grad() mu, lambd, alpha, beta = model(batch_x) # 退火系数:从0线性爬到1,前20%死死压住过度自信 evidence_scale = min(1.0, global_step / anneal_steps) current_lambda_reg = 0.1 * evidence_scale loss_evd = evidential_loss(mu, lambd, alpha, beta, batch_y, current_lambda_reg) loss_cov = coverage_loss(mu, lambd, alpha, beta, batch_y) total_loss = loss_evd + 0.5 * loss_cov total_loss.backward() # 生死攸关:梯度裁剪,把最大范数死死锁在1.0 # 不做这一步,遇到一次剧烈震荡,NIG参数当场爆炸 clip_grad_norm_(model.parameters(), max_norm=1.0) optimizer.step() global_step += 1 scheduler.step() # 通过 hugging-face-trackio 把 loss 同步到监控看板 print(f"Epoch {epoch}: Loss {total_loss.item():.6f}") ``` **毁掉一个极具潜力的量化策略最快的方式,就是让它过早尝到赌赢的甜头。** 把梯度范数死死锁在1.0,配合20%的预热期强压自信心,这台机器才算真正淬炼完成。 > **关于云端任务提交**:用 hugging-face-jobs 提交训练脚本时,记得把超时时间(timeout)设得比你预估时间多20%到30%,否则任务跑到一半被强杀,你的权重文件一分钱都找不回来。第一次跑之前,强烈建议先用少量数据做一次冒烟测试,确认 pipeline 没有格式错误,再提交正式任务。 ## 六、实盘拔网线:用不确定性来决定要不要下单 云端任务跑完,你会得到一个几十MB的权重文件。回到本地,写最后也是最冷血的一步:**实盘推理与不确定性过滤**。 在这套系统的底层逻辑里,预测明天的涨跌只是附带功能。它真正的价值是——**精准丈量你对明天的无知程度**。 NIG 先验把总不确定性拆成两部分: - **认知不确定性** = β / \[(α-1)·λ\] → 理解成:**模型因为没见过这种行情而产生的纯粹瞎蒙**。训练数据越丰富,这个数越小;遇到市场新形态,这个数暴涨,说明模型已经超出了自己的能力圈。 - **偶然不确定性** = β / (α-1) → 理解成:**市场本身发疯的固有底噪**。这部分不管你喂多少数据都降不下去,因为市场本来就有随机性,这是宇宙的背景辐射,不是模型的问题。 两者加总,就是你今天要不要下单的依据——不是看涨跌方向,而是看**这次预测本身有没有资格被执行**。 ``` import numpy as np import torch def generate_trading_signals(model, historical_X, new_X, percentile_threshold=75): model.eval() # 第一步:跑历史验证集,建立不确定性的基准水位线 with torch.no_grad(): _, h_lambd, h_alpha, h_beta = model(torch.tensor(historical_X).float()) # 总不确定性 = 认知 + 偶然 = β/[(α-1)·λ] + β/(α-1) h_variance = (h_beta / (h_alpha - 1.0)) * (1.0 + 1.0 / h_lambd) cutoff_uncertainty = np.percentile(h_variance.numpy(), percentile_threshold) # 第二步:实时推理 with torch.no_grad(): mu, lambd, alpha, beta = model(torch.tensor(new_X).float()) epistemic_unc = beta / ((alpha - 1.0) * lambd) # 认知不确定性 aleatoric_unc = beta / (alpha - 1.0) # 偶然不确定性 total_unc = epistemic_unc + aleatoric_unc predicted_return = mu.numpy() current_unc = total_unc.numpy() # 第三步:冷血的下单逻辑 if current_unc > cutoff_uncertainty: # 当前不确定性超过历史75%分位 → 模型自己都不信自己,坚决不动 signal = "HOLD — 装死(不确定性爆表)" elif predicted_return > 0: signal = "BUY — 做多" else: signal = "SELL — 做空" return signal, current_unc, epistemic_unc.item(), aleatoric_unc.item() ``` 这道过滤网的数据验证极度残忍:加上过滤之后,模型对 ETH 的胜率依然只有50%——抛硬币的水平。但正因为这段代码精准剔除了本质上是随机噪声的交易,年化索提诺比率(专门衡量下行风险)直接从基准的0.64飙到了1.84,最大回撤被压缩到了-38.83个基点。在 USDC 稳定币数据集上,夏普比率更是被推高到了惊人的9.14。 **投资的本质从来不是在每一个牌局都梭哈,而是在看不清对手底牌时,有把双手锁在桌子底下的纪律。** ## 尾声:huggingface/skills 让小白也能进炼丹房了 如果你读到这里觉得"这套东西门槛有点高",那就错了。 就在一两年前,要跑一个自定义的 PyTorch 时间序列模型到云端 A100,你得自己配 Docker、装 CUDA 驱动、写部署脚本、盯 Job 日志、手动把权重推回本地。每一个环节都能卡住一个没有 MLOps 背景的独立开发者。 现在这件事变了。huggingface/skills 让你不只是写训练脚本,而是能直接向 Claude Code 用自然语言描述你想做什么,然后由 AI 助手完成 GPU 选型、Job 配置、任务提交、进度监控和权重推送的全套工作。整套工具链兼容 Claude Code、Codex、Gemini CLI 和 Cursor,技能文件格式通用,不被任何一家厂商锁死。 这意味着一件很有意思的事情发生了:**算力的入口被彻底打平了。** 会写自然语言指令,你就能指挥云端的 A100 集群。 但这也带来了一个新的问题——工具的民主化,不等于认知的民主化。 拿到这套工具箱,绝大多数人会做什么?他们会调用默认的大模型微调脚本,把金融时序当文本来预测,然后在第一次回测亏损后把锅甩给"AI不靠谱"。那些真正从这套基础设施里榨出利润的人,不是因为他们多会用工具,而是因为他们清楚地知道,**模型在什么时候最无知,以及如何把这份无知定价。** 承认无知,量化无知,然后利用无知——这是 ProbFM 的核心逻辑,也是一切靠谱量化系统的底层哲学。 这套防御型代码今天就能在你的编辑器里组装完毕。那么问题来了:你敢把它拿去跑波动更诡异的国内A股数据吗? **Categories:** 产品实验 ---