4. 贝叶斯优化
在量化策略参数优化中,网格搜索与随机搜索虽然简单直观,但面对高维参数空间或每次评估成本高昂的回测场景时,效率往往不尽如人意。贝叶斯优化(Bayesian Optimization)通过构建参数与目标函数之间的概率代理模型,能够以更少的迭代次数找到更优的参数组合,尤其适合回测一次耗时数分钟甚至更长的复杂策略。
4.1 贝叶斯优化原理
贝叶斯优化的核心思想是:利用已有的评估结果,建立一个概率模型来近似未知的目标函数,然后通过一个采集函数(Acquisition Function)决定下一个最有潜力的采样点。整个过程可以看作“边探索边利用”的智能搜索过程。
核心流程: 初始化样本 → 构建高斯过程代理模型 → 优化采集函数选择下一个评估点 → 执行回测获得新结果 → 更新代理模型 → 重复直至收敛。
与网格搜索的暴力枚举不同,贝叶斯优化会记住每一次回测的结果,并利用这些信息推测参数空间中哪些区域可能藏着更优的解,从而避免在无效区域浪费计算资源。
4.2 高斯过程(Gaussian Process, GP)
高斯过程是贝叶斯优化中最常用的代理模型。它不直接预测某个参数组合的目标值(如夏普比率),而是给出一个均值预测和不确定性估计(方差)。
| 概念 | 数学含义 | 在策略优化中的角色 |
|---|---|---|
| 均值函数 μ(x) | 在参数 x 处的预测期望值 | 表示该参数组合预期能获得的绩效指标 |
| 协方差函数 k(x, x') | 衡量两个参数点之间的相关性 | 决定代理模型的平滑程度与泛化能力 |
| 后验分布 | 融合先验与观测数据后的更新分布 | 提供每个参数点的预测均值与置信区间 |
直观理解: 高斯过程就像一张“弹性橡胶膜”,每次回测结果都会拉扯这张膜,使其在已评估点附近更贴合真实值,而在未评估区域则保持较大的不确定性(方差大)。
4.3 采集函数(Acquisition Function)
采集函数是贝叶斯优化的“决策大脑”,它利用高斯过程输出的均值和方差,为每个候选参数点计算一个“值得评估”的分数。常见的采集函数有三种:
| 采集函数 | 全称 | 核心公式(简化) | 偏好倾向 | 适用场景 |
|---|---|---|---|---|
| EI | Expected Improvement | EI(x) = E[max(f(x) - f*, 0)] | 平衡探索与利用 | 通用场景,最常用 |
| UCB | Upper Confidence Bound | UCB(x) = μ(x) + κ·σ(x) | 偏向探索(κ大时) | 需要快速覆盖未知区域 |
| PI | Probability of Improvement | PI(x) = Φ((μ(x) - f*) / σ(x)) | 偏向利用 | 已接近最优解,精细调优 |
其中 f* 为当前最优观测值,μ(x) 和 σ(x) 分别为高斯过程在 x 处的预测均值和标准差,κ 为探索-利用权衡参数,Φ 为标准正态累积分布函数。
实践建议: 在策略参数优化的早期阶段,推荐使用 UCB(κ=2.0~3.0)快速探索;中期切换为 EI 实现稳定收敛;后期若已接近局部最优,可使用 PI 进行精细搜索。实际应用中,EI 因其鲁棒性成为默认首选。
4.4 Python 实现示例
以下使用 scikit-optimize 库演示贝叶斯优化在双均线策略参数优化中的应用。该库封装了高斯过程与采集函数,使用简洁。
import numpy as np
import pandas as pd
from skopt import gp_minimize
from skopt.space import Integer
from skopt.utils import use_named_args
# 模拟回测函数:双均线策略,参数为短周期和长周期
def backtest_strategy(short_window, long_window):
"""
模拟回测函数,返回夏普比率(实际使用时替换为真实回测引擎)
"""
# 模拟:短周期与长周期差距适中时夏普较高
spread = long_window - short_window
sharpe = 0.5 + 0.3 * np.exp(-((spread - 30) ** 2) / 200)
# 加入随机噪声模拟回测不确定性
noise = np.random.normal(0, 0.02)
return -(sharpe + noise) # gp_minimize 默认最小化,取负值
# 定义参数搜索空间
param_space = [
Integer(5, 50, name='short_window'),
Integer(20, 200, name='long_window')
]
# 使用命名参数包装目标函数
@use_named_args(param_space)
def objective(short_window, long_window):
return backtest_strategy(short_window, long_window)
# 执行贝叶斯优化
result = gp_minimize(
func=objective,
dimensions=param_space,
n_calls=50, # 总评估次数
n_initial_points=10, # 初始随机采样点数
acq_func='EI', # 采集函数:Expected Improvement
random_state=42
)
# 输出最优结果
print(f"最优参数:short_window={result.x[0]}, long_window={result.x[1]}")
print(f"最优夏普比率(取反后):{result.fun:.4f}")
print(f"实际最优夏普比率:{-result.fun:.4f}")
# 查看每次迭代的收敛过程
print(f"迭代次数:{len(result.func_vals)}")
print(f"最终最优值:{-result.fun:.4f}")
关键参数说明:
•
•
•
•
•
n_calls:总评估次数,建议设置为参数维度的 10–20 倍。•
n_initial_points:初始随机采样点数,用于构建初始高斯过程模型,建议为总次数的 20%。•
acq_func:采集函数类型,可选 'EI'、'UCB'、'PI'。•
random_state:随机种子,保证结果可复现。
4.5 贝叶斯优化 vs 传统方法对比
| 对比维度 | 网格搜索 | 随机搜索 | 贝叶斯优化 |
|---|---|---|---|
| 搜索策略 | 穷举所有组合 | 随机采样 | 基于历史结果智能选择 |
| 所需评估次数 | 极高(组合爆炸) | 中等 | 低(通常 30–100 次) |
| 对高维空间适应性 | 差 | 一般 | 较好(≤10 维) |
| 是否利用历史信息 | 否 | 否 | 是(构建代理模型) |
| 不确定性量化 | 无 | 无 | 有(高斯过程方差) |
| 实现复杂度 | 低 | 低 | 中(需调采集函数参数) |
适用场景判断: 如果一次回测耗时超过 10 秒,且参数维度在 3–10 之间,贝叶斯优化是性价比最高的选择。对于超低维度(1–2 维)或超低耗时(毫秒级),网格搜索仍然可用。
4.6 实践注意事项
- 参数空间边界: 确保参数上下界合理,避免高斯过程在边界外进行无意义的外推。
- 目标函数噪声: 回测结果本身存在随机性(如市场数据切片差异),高斯过程可以建模噪声,但建议设置
noise='gaussian'或提供噪声水平估计。 - 采集函数调参: UCB 的 κ 参数控制探索强度,κ 越大越偏向探索;EI 无需额外参数,鲁棒性更好。
- 早停机制: 当连续多次迭代的最优值提升幅度小于阈值(如 0.1%)时,可以提前终止优化,节省计算资源。
- 多峰问题: 策略参数空间通常存在多个局部最优,建议多次运行贝叶斯优化(不同随机种子)并对比结果。