第二十三课:参数调优的日志与监控

说实话,做参数调优最怕什么?

不是怕参数调不好,而是怕调了半天,回头一看——

「咦?我上次跑的那个0.75的衰减率,到底是在哪个数据集上测的?」

「这个夏普比率2.1的结果,参数组合是什么来着?」

这种问题,我踩过太多次了。所以今天这节课,咱们就聊聊怎么把调优过程「管起来」。

为什么日志和监控这么重要?

你想想看,基差交易的参数空间有多大?

入场阈值、出场阈值、止损比例、仓位管理系数、滚动窗口长度……随便一组合就是几百上千组。

手动记?不现实。

Excel记?容易出错。

最好的办法,就是用专业的工具把整个过程自动化记录下来。

核心原则:每一次调优,都应该能完整复现。包括参数、数据、环境、结果,一个都不能少。

MLflow:我最常用的实验管理工具

MLflow这东西,我用了快三年了。说实话,一开始觉得麻烦,后来发现真香。

它主要解决三个问题:

  • 记录参数:每次实验的输入参数自动保存
  • 记录指标:夏普、最大回撤、年化收益等自动跟踪
  • 记录模型:最优参数组合可以直接打包保存

来看个实际例子。这是我做基差策略调优时常用的代码框架:

import mlflow
import mlflow.sklearn

def run_optimization(params):
    with mlflow.start_run():
        # 记录参数
        mlflow.log_param("entry_threshold", params["entry"])
        mlflow.log_param("exit_threshold", params["exit"])
        mlflow.log_param("stop_loss", params["stop_loss"])
        mlflow.log_param("window_size", params["window"])
        
        # 运行回测
        result = backtest_strategy(params)
        
        # 记录指标
        mlflow.log_metric("sharpe_ratio", result["sharpe"])
        mlflow.log_metric("max_drawdown", result["max_dd"])
        mlflow.log_metric("annual_return", result["ann_ret"])
        mlflow.log_metric("win_rate", result["win_rate"])
        
        # 保存模型(最优参数组合)
        mlflow.sklearn.log_model(result["model"], "model")
        
        return result

跑完之后,在终端输入 mlflow ui,就能在浏览器里看到所有实验的对比。

我的习惯:每次调优前,先创建一个新的Experiment。比如"2024_Q1_basis_tuning",这样不同批次的实验不会混在一起。

TensorBoard:可视化调优过程

MLflow适合记录「结果」,但如果你想看「过程」,TensorBoard是更好的选择。

我记得有一次调优,参数组合跑了整整一个周末。周一回来一看,MLflow里只有最终结果,中间过程完全不知道发生了什么。

后来改用TensorBoard,把每一轮迭代的损失函数、参数变化都记录下来,问题就清楚了。

怎么用?很简单:

from torch.utils.tensorboard import SummaryWriter

writer = SummaryWriter("runs/basis_tuning_001")

for epoch in range(100):
    # 模拟调优过程
    loss = compute_loss(params)
    sharpe = compute_sharpe(params)
    
    # 记录到TensorBoard
    writer.add_scalar("Loss/train", loss, epoch)
    writer.add_scalar("Metrics/Sharpe", sharpe, epoch)
    writer.add_histogram("Params/entry_threshold", 
                         params["entry"], epoch)
    
    # 更新参数
    params = update_params(params, loss)

writer.close()

然后在命令行运行 tensorboard --logdir runs,就能在浏览器里看到实时更新的图表了。

注意:TensorBoard记录的是标量、直方图、图像等。如果你需要记录完整的参数组合和回测结果,还是得靠MLflow。两者配合使用效果最好。

参数调优过程可视化

光有数字不够,得能「看」出问题来。

我个人习惯做三张图:

  1. 参数热力图:横轴是入场阈值,纵轴是出场阈值,颜色代表夏普比率。一眼就能看出最优区域在哪。
  2. 收敛曲线:每一轮迭代的损失函数变化。如果曲线震荡剧烈,说明学习率可能太大了。
  3. 参数重要性排序:用随机森林或SHAP值,看看哪个参数对结果影响最大。

下面这张图,展示了我常用的调优监控流程:

参数调优日志与监控流程 参数组合生成 网格搜索/贝叶斯 回测执行 逐参数运行策略 日志记录 MLflow + TensorBoard 持久化 数据库 可视化监控面板 热力图 / 收敛曲线 / 重要性排序 实时更新,支持交互 性能指标监控 夏普 / 回撤 / 胜率 / 盈亏比 异常值自动告警 调优结果持久化 SQLite / PostgreSQL 支持历史查询与对比 目标:每一次调优都可复现、可追溯、可对比

性能指标监控

调优过程中,哪些指标最值得关注?

我一般盯这五个:

指标 含义 监控频率 异常信号
夏普比率 风险调整后收益 每轮调优 < 0.5 需警惕
最大回撤 账户最大亏损幅度 每轮调优 > 15% 需停止
胜率 盈利交易占比 每轮调优 < 40% 需检查
盈亏比 平均盈利/平均亏损 每轮调优 < 1.5 需优化
参数稳定性 参数微小变化对结果的影响 调优结束后 波动大说明过拟合

我曾经踩过的坑:有一次调优,夏普比率飙到了3.5,我高兴坏了。结果仔细一看,是因为参数过拟合了历史数据,换到样本外数据直接崩了。所以后来我加了一个「参数稳定性」指标——如果参数稍微变一点,结果就剧烈波动,那这个参数组合基本不能用。

调优结果持久化

调优完了,结果得存下来。不能每次都重新跑一遍吧?

我常用的持久化方案有两种:

  • 轻量级方案:用SQLite,适合个人研究。MLflow默认就支持SQLite存储。
  • 团队级方案:用PostgreSQL,适合多人协作。可以共享实验数据,方便对比。

配置起来很简单:

# SQLite(个人用)
mlflow.set_tracking_uri("sqlite:///basis_tuning.db")

# PostgreSQL(团队用)
mlflow.set_tracking_uri("postgresql://user:pass@host:5432/mlflow_db")

存完之后,怎么查?

我一般用MLflow的API来查询历史结果:

import mlflow
from mlflow.tracking import MlflowClient

client = MlflowClient()

# 查询所有实验
experiments = client.search_experiments()

# 查询某个实验的所有运行记录
runs = client.search_runs(
    experiment_ids=["1"],
    order_by=["metrics.sharpe_ratio DESC"],
    max_results=10
)

# 输出最优的5组参数
for run in runs[:5]:
    print(f"Run ID: {run.info.run_id}")
    print(f"夏普比率: {run.data.metrics['sharpe_ratio']}")
    print(f"参数: {run.data.params}")

我的建议:每次调优结束后,把最优的3-5组参数单独导出一个CSV文件,放在项目目录下。这样即使数据库出了问题,你手里还有一份备份。

小结一下

日志和监控这件事,说白了就是给调优过程「上保险」。

MLflow管记录,TensorBoard管可视化,数据库管持久化。三者配合,你的调优工作就不再是「黑箱操作」了。

嗯,这套流程我用了两年多,基本没出过「找不到上次结果」这种低级错误。

你也试试看?


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