元学习量化策略:用 MAML 让模型在少样本上快速适应新市场
普通模型换个市场、换个 regime 就得从头训,可新环境往往只有几十个样本,根本喂不饱。元学习换个目标:不学『某个市场的最优参数』,而学一个『最容易被少量样本快速微调』的元初始化。本文用纯 numpy 从零实现一阶 MAML(FOMAML),把每个市场建模成因子→收益的线性任务,任务间共享中心 w0、各自偏移 τ=1.1。200 个训练市场上元训练 300 轮,外层 query 损失从 3.65 压到 1.67 逼近 Oracle 下界 1.00。在 100 个全新市场上只用 K=10 个样本适应:MAML 的 MSE 2.85,碾压忽略任务差异的 Pooled 全局模型(7.75,降 63%)和从零学的 Scratch(3.17)。few-shot 曲线显示 MAML 起点就低、一两步就到位;样本量敏感性揭示真相——K 越小 MAML 优势越大(K=3 时 4.73 vs Scratch 5.75),样本足够时三者收敛。拆穿元学习=预训练、任务同分布假设、内外循环学习率耦合、少样本≠零样本四类陷阱(中阶)。
先说结论:量化里最难的从来不是”在一个市场上训到最优”,而是”换个市场、换个 regime,只有几十个样本时还能快速站稳”。普通监督学习的隐含假设是训练集和测试集同分布,但市场天然是分段的——牛熊切换、风格轮动、政策拐点,每一段都是一个新任务,而新任务刚开始时你手里只有寥寥几十根 K 线。这时候从头训必然过拟合,用旧模型硬套又水土不服。
元学习(Meta-Learning)换了个目标函数:它不学”某个市场的最优参数”,而学一个最容易被少量样本快速微调的起点。本文用纯 numpy 从零实现一阶 MAML,在 200 个合成市场上元训练后,面对 100 个从没见过的新市场、每个只给 10 个样本,适应误差(MSE 2.85)比忽略任务差异的全局模型(7.75)低了 63%。
一、为什么”换市场就崩”是个结构性问题#
先把问题说清楚。假设你在市场 A 上训了个因子模型,效果很好。现在市场进入了新 regime B,你面临三个糟糕的选择:
- 直接套用 A 的模型:B 的因子→收益映射变了,旧参数直接失配。
- 在 B 上从头训:B 只有 30 个样本,参数比样本还多,必然过拟合。
- 把 A 和 B 混起来训一个全局模型:等于假设所有 regime 共享同一套参数,把差异当噪声抹平——结果对谁都不最优。
这三条路对应本文后面的三个基线:Scratch(从零学)、Pooled(全局混训),以及理论上界 Oracle(大样本直接拟合)。它们的共同盲点是:都把”参数”当成学习目标。
MAML 的洞见是——把”初始化”当成学习目标。
二、MAML 的核心:学一个”会学习”的起点#
想象你要训练一批新员工。普通做法是把每个人培养成某个岗位的专家(学参数)。MAML 的做法是:招一个”学习能力极强”的人,他到任何新岗位,看几个案例就能上手(学初始化)。
数学上,把每个市场建模成一个任务 ,它有自己的因子→收益映射:
不同任务的 从共同分布采样:。这里 是任务间共享的结构, 控制任务间的差异强度。本文取 ,——差异不小,意味着”每个市场确实很不一样”。
MAML 分内外两层循环:
- 内循环(inner loop / 适应):在新任务的 K 个样本(support set)上,从元初始化 出发做几步梯度下降,得到适应后的参数 。
- 外循环(outer loop / 元更新):在同任务的另一批样本(query set)上,评估 的损失,对 求梯度并更新。
关键在外循环:它优化的不是”在某个任务上表现好”,而是”从 出发、走几步之后表现好”。这就是”学一个会学习的起点”。
三、从零实现 FOMAML(一阶近似)#
完整 MAML 需要对”梯度下降过程”再求梯度(二阶导),计算和内存开销都大。实践里常用一阶近似 FOMAML:直接在适应后的参数上求 query 梯度,忽略二阶项。效果损失很小,实现干净得多。
先定义线性任务的基本组件:
import numpy as np
P = 5 # 因子数
W0 = np.array([0.9, -0.6, 0.4, 0.5, -0.3]) # 任务共享中心
TAU = 1.1 # 任务间差异强度
K = 10 # few-shot 样本数
INNER_LR = 0.12
INNER_STEPS = 5
def sample_task(seed):
r = np.random.default_rng(seed)
w = W0 + TAU * r.standard_normal(P) # 每个任务的真实参数
return w, r
def gen_data(w, n, r):
X = r.standard_normal((n, P))
y = X @ w + r.standard_normal(n) # 观测噪声 σ=1
return X, y
def grad(w, X, y):
e = X @ w - y
return 2.0 * X.T @ e / len(y)
def adapt(w_init, X, y, lr=INNER_LR, steps=INNER_STEPS):
"""内循环:从 w_init 出发做几步梯度下降"""
w = w_init.copy()
for _ in range(steps):
w = w - lr * grad(w, X, y)
return wpython外循环元训练:
N_TASKS_TRAIN = 200
META_LR = 0.02
META_EPOCHS = 300
TASK_BATCH = 8
train_tasks = [sample_task(1000 + i) for i in range(N_TASKS_TRAIN)]
rng = np.random.default_rng(42)
w_meta = W0 + 0.3 * rng.standard_normal(P) # 随机起点
for epoch in range(META_EPOCHS):
idx = rng.choice(N_TASKS_TRAIN, TASK_BATCH, replace=False)
meta_grad = np.zeros(P)
for j in idx:
w_task, r = train_tasks[j]
Xs, ys = gen_data(w_task, K, r) # support:适应用
Xq, yq = gen_data(w_task, 100, r) # query:评估用
w_adapted = adapt(w_meta, Xs, ys) # 内循环
meta_grad += grad(w_adapted, Xq, yq) # FOMAML:适应后参数上的 query 梯度
w_meta -= META_LR * meta_grad / TASK_BATCHpython注意 FOMAML 的精髓在最后两行:w_adapted 是内循环适应的结果,但我们把它的 query 梯度直接回传给 ——这就是一阶近似(把 ∂w_adapted/∂w_meta 当成恒等映射)。
四、元训练:外层损失逼近理论下界#
元训练 300 轮,外层 query 损失(在适应后参数上评估)稳步下降:

损失从起点 3.65 压到 1.67,逼近 Oracle 下界 1.00(用 2000 个样本直接拟合每个任务能达到的误差,本质是观测噪声 的地板)。这条曲线说明: 确实在朝”最容易被 10 个样本快速微调到接近最优”的方向移动。
五、少样本适应:MAML 起点低、一两步就到位#
真正见功力的是”面对新市场时的适应速度”。下图对比 MAML 元初始化 vs 随机初始化(Scratch),横轴是新市场上的适应步数:

两个关键差异:
- 起点低:第 0 步(还没适应),MAML 的初始化就已经比随机初始化的误差低——因为 落在所有任务的”公共中心”附近。
- 收敛快:MAML 一到两步就压到接近下界,随机初始化要爬更多步、且落点更高。
这正是元学习的承诺:不是训得更久,而是学得更快。
六、100 个全新市场:MAML vs 三个基线#
在 100 个从没出现在元训练里的新市场上,每个只给 K=10 个样本,对比四种方法的适应误差分布:

平均 MSE:
| 方法 | 平均 MSE | 相对 MAML |
|---|---|---|
| MAML(元初始化 + 适应) | 2.85 | 基准 |
| Pooled(全局混训) | 7.75 | 高 172% |
| Scratch(从零学) | 3.17 | 高 11% |
| Oracle(大样本下界) | 1.00 | 理论地板 |
两个结论:
- Pooled 最差(7.75):这最反直觉——用了最多数据的全局模型反而崩了。原因是 时任务差异太大,“一套参数打天下”等于对每个市场都用错误的 。数据多 ≠ 适应好,用错地方的数据是负担。
- MAML 比 Scratch 低 11%(2.85 vs 3.17):都用了同样的 10 个样本适应,差别只在起点。MAML 学到的起点让同样的样本发挥出更大价值。
七、样本量敏感性:K 越小,MAML 优势越大#
元学习到底在什么场景下值得用?答案藏在样本量敏感性里:

MAML 随 K 变化的 MSE:
| K(新市场样本数) | MAML | Scratch | Pooled |
|---|---|---|---|
| 3 | 4.73 | 5.75 | 7.02 |
| 5 | 3.71 | 4.50 | 6.99 |
| 10 | 2.65 | 2.98 | 6.99 |
| 20 | 2.12 | 2.45 | 7.04 |
| 50 | 1.72 | 1.93 | 6.93 |
| 100 | 1.50 | 1.68 | 6.96 |
规律非常干净:
- K 极小时(K=3),MAML(4.73)显著优于 Scratch(5.75)——样本越少,一个好起点越重要。
- K 足够大时,MAML 和 Scratch 收敛(都逼近 Oracle),因为样本足够多时,起点不再重要,从哪出发都能走到最优。
- Pooled 始终躺在 7 附近:它根本不做 per-task 适应,加多少样本都没用——这暴露了”全局模型”的结构性缺陷。
一句话:元学习是”少样本快速适应”的工具,不是”数据多多益善”的工具。 如果你的新 regime 有的是数据,直接从头训就好,MAML 的价值恰恰在样本荒的时候。
八、四个必须拆穿的陷阱#
陷阱一:把元学习等同于预训练/迁移学习。 预训练学的是”一套好特征/好参数”,迁移时通常固定大部分、只微调最后一层。MAML 学的是”一个好初始化”,它的目标函数里显式包含了适应过程——优化的是”微调之后”的损失,不是”微调之前”。这是本质区别:预训练不知道你要微调几步,MAML 知道,并为此优化。
陷阱二:忽略任务同分布假设。 MAML 假设训练任务和测试任务来自同一个任务分布(本文里都是 )。如果新市场的结构彻底超出这个分布——比如出现了训练期从没有过的因子关系——元初始化照样失灵。MAML 让你在”同一族市场”内快速适应,但救不了”全新物种”。 上线前务必检查:新 regime 真的和历史 regime 同族吗?
陷阱三:内外循环学习率耦合导致的隐性过拟合。
内循环学习率(INNER_LR)和步数(INNER_STEPS)不是自由参数——它们在元训练时就被”编译”进了 。如果元训练时用 5 步适应,部署时却用 20 步, 可能被”过度适应”到偏离。内循环配置必须训练和部署一致,否则元初始化的优势会被侵蚀。
陷阱四:少样本 ≠ 零样本。 MAML 需要新市场的 K 个样本来适应(哪怕只有 3 个)。如果你面对的是”全新市场、零历史数据”,MAML 退化成只能用元初始化 (图里 few-shot 曲线的第 0 步),此时它只是个”所有市场的平均模型”,未必比精心设计的先验更好。元学习省的是样本量,不是省到零。
九、落地路径#
本文用线性任务和合成数据把机制讲透,真实落地时的关键改动:
- 任务定义:把”市场/regime”换成真实的时间分段——按波动率状态、按行业轮动、按宏观周期切任务,每个任务是一段历史窗口。
- 模型升级:线性映射换成小型神经网络(MLP/浅层),内循环对全部权重做梯度下降,FOMAML 依然适用。
- 验证方式:用时间序列的 walk-forward——训练任务全部在测试任务之前,严防未来信息泄漏(这比同分布假设更致命)。
- 样本预算:明确你的”新 regime 识别延迟”——从 regime 切换到攒够 K 个样本需要多久?这个延迟决定了 MAML 的实际适应窗口。
元学习不是万能钥匙。它的适用边界很清晰:同一族市场、样本稀缺、需要快速适应。踩准这个场景,一个好起点抵得过十倍数据。