20. 正则化技术:L1/L2正则化防止过拟合,确保曲面平滑

做波动率曲面平滑,最怕什么?

怕过拟合。

你辛辛苦苦拟合出一个曲面,样本点上误差几乎为零,可一到样本外,预测值就飞得离谱。这种「训练集上猛如虎,测试集上二百五」的情况,我见过太多次了。

正则化,就是专门治这个毛病的。

20.1 为什么需要正则化?

先想一个问题:我们为什么要平滑波动率曲面?

因为市场数据本身是有噪声的。同一个到期日、同一个执行价,不同做市商报的隐含波动率可能差0.5个vol。如果你用高阶多项式硬拟合,模型会拼命去「记住」这些噪声点。

结果呢?

曲面变得坑坑洼洼,delta对冲算出来的希腊字母忽大忽小。我有个朋友(真的不是我)曾经用了一个过拟合的曲面做期权定价,结果vega算出来是负的——你说这合理吗?

正则化的核心思想很简单:

  • 拟合误差要小——这是基本要求
  • 模型参数不能太大——这是约束条件

说白了,就是在「拟合得好」和「参数别太离谱」之间找个平衡。

20.2 L2正则化:岭回归

L2正则化,也叫岭回归(Ridge Regression)。

它的损失函数长这样:

Loss = MSE + λ * Σ(θᵢ²)

其中MSE是均方误差,θᵢ是模型参数,λ是正则化强度。

λ越大,惩罚越重,参数就越往0缩。但注意,L2不会把参数精确缩到0,只会让它变得很小。

我在实际项目中,通常把λ设成0.01到0.1之间。太小了没效果,太大了曲面会变成一条直线——那就矫枉过正了。

关键点:L2正则化会让所有参数同时缩小,但不会让任何一个参数变成0。这意味着模型复杂度降低了,但所有特征都保留着。

举个栗子。假设我们用三次样条拟合波动率曲面:

import numpy as np
from scipy.interpolate import UnivariateSpline

# 生成带噪声的波动率数据
x = np.linspace(0.1, 2.0, 20)
y_true = 0.2 + 0.1 * np.exp(-x)  # 真实的波动率曲面
y_noise = y_true + np.random.normal(0, 0.02, len(x))

# 无正则化:s=0表示完全拟合数据点
spline_no_reg = UnivariateSpline(x, y_noise, s=0)

# 有正则化:s=0.1表示允许一定平滑
spline_reg = UnivariateSpline(x, y_noise, s=0.1)

# 对比一下在x=1.5处的预测值
print(f"无正则化预测: {spline_no_reg(1.5):.4f}")
print(f"有正则化预测: {spline_reg(1.5):.4f}")
print(f"真实值: {np.interp(1.5, x, y_true):.4f}")

跑一下你会发现,加了正则化的预测值更接近真实值。为什么?因为它没去拟合那些随机噪声。

20.3 L1正则化:Lasso回归

L1正则化,也叫Lasso回归。

损失函数:

Loss = MSE + λ * Σ|θᵢ|

看起来和L2就差一个平方,但效果天差地别。

L1会把不重要的参数直接变成0。这在特征选择上特别好用——你有一堆基函数,L1帮你挑出哪些是真正有用的。

我的经验:在做波动率曲面插值时,如果基函数数量很多(比如用了20个径向基函数),我会先用L1跑一遍,看看哪些基函数被保留了。那些被淘汰的,说明对曲面形状贡献不大,可以放心去掉。

但L1有个问题:如果特征之间高度相关,它会随机选一个保留,其他的干掉。这会导致结果不稳定。你换一组数据,保留的特征可能就变了。

20.4 L1 vs L2:怎么选?

特性 L2(岭回归) L1(Lasso)
参数收缩 均匀缩小 部分归零
特征选择 不选,全保留 自动选择
稳定性 低(相关特征时)
适用场景 所有特征都有用 特征冗余,需要降维
计算复杂度 低(有闭式解) 较高(需迭代求解)

我个人习惯:做波动率曲面平滑,优先用L2。因为波动率曲面的基函数(比如样条基、径向基)之间往往有相关性,L1会随机丢弃,导致曲面不连续。L2虽然不删特征,但能把参数压得很小,效果更稳定。

20.5 弹性网络:两全其美

如果你既想要L1的特征选择能力,又想要L2的稳定性,那就用弹性网络(Elastic Net)。

Loss = MSE + λ₁ * Σ|θᵢ| + λ₂ * Σ(θᵢ²)

它把L1和L2的惩罚项加在一起。两个λ可以分别调,但实际中我一般固定λ₁:λ₂ = 1:1,然后调总惩罚强度。

注意:弹性网络有两个超参数要调,调参成本翻倍。如果你的数据量不大(比如只有几十个期权报价),建议先用L2试试。我曾经在一个小数据集上硬调弹性网络,结果过拟合更严重了——教训啊。

20.6 正则化强度怎么选?

λ选多大?这是个好问题。

太小了,正则化没效果;太大了,曲面变成一条直线,连基本的市场结构都丢了。

我常用的方法是交叉验证:

  1. 把数据分成K份(通常K=5或10)
  2. 对每个候选λ,用K-1份训练,1份验证
  3. 选验证集误差最小的λ

代码实现也不复杂:

from sklearn.linear_model import RidgeCV

# 候选λ值
alphas = np.logspace(-3, 1, 20)

# 自动交叉验证选λ
model = RidgeCV(alphas=alphas, cv=5)
model.fit(X_train, y_train)

print(f"最优λ: {model.alpha_:.4f}")

嗯,这里要注意:交叉验证选出来的λ,在样本外不一定最优。我一般会在选出来的λ附近再手动试几个值,看看曲面形状是否合理。

20.7 实战:正则化在波动率曲面中的应用

最后,我们看一个完整的例子。假设我们有10个期权报价,要用三次样条拟合波动率曲面:

import numpy as np
from scipy.interpolate import UnivariateSpline
import matplotlib.pyplot as plt

# 模拟数据:10个期权报价
strike = np.array([0.8, 0.9, 0.95, 1.0, 1.05, 1.1, 1.15, 1.2, 1.3, 1.4])
vol = np.array([0.25, 0.22, 0.20, 0.19, 0.18, 0.18, 0.19, 0.20, 0.23, 0.27])
# 加一点噪声
vol_noise = vol + np.random.normal(0, 0.01, len(vol))

# 无正则化
spline_no_reg = UnivariateSpline(strike, vol_noise, s=0)

# 有正则化(s控制平滑程度)
spline_reg = UnivariateSpline(strike, vol_noise, s=0.05)

# 预测
x_new = np.linspace(0.7, 1.5, 100)
y_no_reg = spline_no_reg(x_new)
y_reg = spline_reg(x_new)

# 对比
print("无正则化 - 参数个数:", len(spline_no_reg.get_coeffs()))
print("有正则化 - 参数个数:", len(spline_reg.get_coeffs()))

你会发现,无正则化的样条参数更多,曲线在数据点附近剧烈波动。加了正则化后,参数变少,曲线也更平滑。

核心结论:正则化不是万能的,但没有正则化是万万不能的。在波动率曲面平滑中,L2正则化是最稳妥的选择。它不会改变曲面的整体形状,但能有效抑制噪声带来的局部抖动。

我曾经在一个实盘项目中,因为没加正则化,导致曲面在远月合约上出现了「波浪形」——做市商看了直接说这曲面没法用。后来加了L2正则化,问题立刻解决。从那以后,正则化就成了我代码里的标配。

正则化技术知识结构 正则化技术 为什么需要正则化? L2正则化(岭回归) L1正则化(Lasso) 弹性网络(Elastic Net) 正则化强度调参(λ) 波动率曲面实战 过拟合 → 曲面不平滑 参数均匀缩小,保留所有特征 参数归零,自动特征选择 L1 + L2 结合,两全其美 交叉验证选择最优λ
一句话总结:L2正则化是波动率曲面平滑的「安全牌」,L1是「手术刀」,弹性网络是「瑞士军刀」。根据你的数据量和特征数量,选合适的工具。

公众号:蓝海资料掘金营,微信deep3321