推荐算法策略——多目标参数贝叶斯优化
推荐算法策略——多目标参数贝叶斯优化
在推荐系统中,如何有效地进行多目标参数调优是一个常见且重要的问题。本文将介绍如何使用贝叶斯优化方法来解决这一问题,包括其基本原理、具体实现步骤以及实际应用效果。
一、贝叶斯优化简介
贝叶斯优化是一种黑盒优化方法,它在每次迭代中都会平衡探索和利用的权衡,以找到最优解。针对贝叶斯优化原理就不多说了,网上很多优秀的解释。大致过程就是:
首先假设目标函数遵循高斯过程,并通过观察目标函数的值来更新这个假设。然后,它选择一个收益最大化的点作为下一个观察点。
(放一个混元理解的图片,有点抽象。。)
二、多目标超参数调优
在推荐系统中,往往模型是多目标的。以内容流为例,目标可以是:点击、时长、转发、评论、点赞、关注等等。而在实践中,一定会遇到的问题是:多目标融合公式内的超参数拍定。
简单一点,可以使用“拍脑袋(经验决策)+暴力搜参”。这种方式对于超参数较少或者业务迭代的初期很适用。但是如果超参数较多,暴力搜参再进行A/B实验往往会浪费大量的时间、流量。因此可以通过贝叶斯优化来辅助我们调参。
2.1 确定需要调整的超参数
多目标常见的融合方式是幂乘,那么最简单的,超参数可以是各个目标的幂指数。
Score=\prod Predict_{i}^{α_{i}}
其中α_{i}为第i个目标的幂指数,Predict_{i}为第i个目标的模型预测值。那么α_{i}即是我们需要调整的超参数。
2.2 定义reward
贝叶斯优化中,需要确定优化目标,即一个具体的数值。因此需要根据线上A/B实验的效果来决定reward函数,比如:
Reward=20∗Time+10∗Like+35∗CTR+10∗Share
这里有几点经验:
- 每个目标的值最好采用A/B实验中,实验组相比对照组提升的百分点(Percentage),而不是取每个目标提升的绝对值。这样可以尽量保证每个目标的量纲不会有太大差异。当然,如果某个目标的百分点波动较大,可以适当调整该目标的权重。
- 当有目标出现负向显著的时候,需要给更大的惩罚。理想情况下,调权的目标是希望在不伤害任何指标的前提下,尽可能做提升。但是实际中,往往会出现严重的指标置换情况。因此,我们需要对负向的指标给更大的惩罚,来优化Reward函数,比如某个指标负向了,那么就乘2倍或者更多(根据自己业务情况调整)。
- reward权重拍定,在一定程度上可以理解是“可接受的指标置换”,比如上面的公式中,大致可以理解成愿意牺牲1点“Like”来置换2份“Time”。
2.3 使用贝叶斯优化进行多目标超参数调优
现在我们可以使用贝叶斯优化来寻找最优的超参数。具体步骤如下:
- 初始化贝叶斯优化器,设置超参数的搜索范围(边界)。
- 选择一个收益函数,代码中是UCB。
- 进行多次迭代,在每次迭代中:
- 线上A/B实验回收数据指标
- 离线根据2.2的reward的函数计算每组实验得分
- 将新观察到的数据点添加到贝叶斯优化器中
- 在迭代完成后,贝叶斯优化器会返回一组最优的超参数。
这里需要注意的是,线上A/B实验观察的时候,尽可能的保证数据置信,比如实验组给较大流量或实验周期较长。根据自己的业务,一般3-7天可以得到相对置信的结论。
另外,最初的参数往往是人工经验拍定,差距可以大一些,作为第一轮调参进行A/B实验。
三、贝叶斯优化demo
废话不多直接上代码:
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
from bayes_opt import BayesianOptimization, UtilityFunction
def black_box_function(x, y):
return (x - 1) ** 2 + y ** 2 - 1
optimizer = BayesianOptimization(
f=None,
pbounds={'x': (-3, 3), 'y': (-3, 3)},
verbose=2,
random_state=1,
allow_duplicate_points=True
)
utility = UtilityFunction(kind="ucb", kappa=2.5, xi=0.0)
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
ax.set_xlabel('X')
ax.set_ylabel('Y')
ax.set_zlabel('Z')
# Evaluate the black_box_function on a meshgrid
x = np.linspace(-3, 3, 100)
y = np.linspace(-3, 3, 100)
x, y = np.meshgrid(x, y)
z = black_box_function(x, y)
# Plot the surface
ax.plot_surface(x, y, z, cmap='viridis', alpha=0.3)
# Optimization loop
explored_points = []
for _ in range(12):
next_point = optimizer.suggest(utility)
target = black_box_function(**next_point)
optimizer.register(params=next_point, target=target)
explored_points.append((next_point['x'], next_point['y'], target))
print(next_point, target)
# Plot all explored points after the optimization loop
for i in range(1, len(explored_points)):
x_values = [explored_points[i-1][0], explored_points[i][0]]
y_values = [explored_points[i-1][1], explored_points[i][1]]
z_values = [explored_points[i-1][2], explored_points[i][2]]
ax.plot(x_values, y_values, z_values, c='red', marker='o', markersize=5, linewidth=1)
plt.show()
print(optimizer.max)
- 这里我们定义了一个黑盒函数black_box_function来作为例子,在实际的推荐系统中,黑盒函数即是线上A/B实验的反馈结果。
- 在demo的黑盒函数中,拥有x、y两个参数。我们为这两个参数设定了边界:pbounds={'x': (-3, 3), 'y': (-3, 3)}
- 接着我们初始化了贝叶斯优化器以及收益函数UCB
- 对该黑盒函数迭代12轮,每一轮将观测到的值重新register至贝叶斯优化器中,实现迭代。最终画图整个过程图。
- 线上A/B实验和demo类似,只要把观测值离线手动算好来代替black_box_function的输出塞进来就行了。
代码参考链接:https://github.com/bayesian-optimization/BayesianOptimization
四、 线上收益
指标 提升效果
A指标 +% 正向显著
B指标 +% 正向显著
C指标 +% 正向显著
D指标 +% 正向显著
E指标 -**% 负向显著
最后放一下业务真实的线上指标,ABCD四个指标均有效提升,E指标负向显著。中间调过好几轮,一直会存在指标置换的情况无法避免,最后评估完置换是可以接受的。
