我目前正在使用Optuna,并且试图建议一些受多个变量限制的比率。具体来说,我处理的是比率X_1, X_2, ..., X_k
,它们受限于∑X_i = 1
和0 <= X_i <= 1
,其中i
为任意值。
遗憾的是,Optuna并没有提供Dirichlet分布,这本来是解决这个问题的理想方法。
我尝试了以下方法,通过迭代建议每个比率并从剩余总量中减去它。然而,这并没有按预期工作:
def objective(trial): k = 10 ratios = np.zeros(k) residual = 1 for i in range(k - 1): ratios[i] = trial.suggest_float(f'ratio_{i}', 0, residual) residual -= ratios[i] # ratios[k - 1] = trial.suggest_float(f'ratio_{k - 1}', residual, residual) ratios[k - 1] = residual return np.log(ratios).sum()study = optuna.create_study(direction='maximize')study.optimize(objective, n_trials=20)
我还尝试了另一种方法,即独立建议所有比率,然后对它们进行归一化。这种方法虽然不会产生错误,但它建议了k
次,尽管自由度是k - 1
,这不一致:
def objective(trial): k = 10 ratios = np.zeros(k) for i in range(k): ratios[i] = trial.suggest_float(f'ratio_{i}', 0, 1) ratios /= ratios.sum() return np.log(ratios).sum()study = optuna.create_study(direction='maximize')study.optimize(objective, n_trials=20)
我正在寻找一种方法来建议受多个变量限制的比率。虽然上述例子简单且可微分,但我需要处理一个更复杂的目标函数,该函数需要变量的参与。
回答:
这个问题通过创建一个Objective
类得到了解决,该类维护了一个用于比率建议的最大值(self.max
)。这个最大值在每次试验中更新为当前最大值和当前试验中建议的最大比率的平均值。这种方法确保了建议的比率逐渐调整到最优值。
这是修正后的代码:
class Objective: def __init__(self): self.max = 1 def __call__(self, trial): k = 10 ratios = np.zeros(k) for i in range(k): ratios[i] = trial.suggest_float(f'ratio_{i}', 0, self.max) ratios /= ratios.sum() self.max = (self.max + ratios.max()) / 2 return np.log(ratios).sum()study = optuna.create_study(direction='maximize')study.optimize(Objective(), n_trials=100)
在这段代码中,Objective
类被实例化并传递给study.optimize
方法。Objective
类的__call__
方法用作优化过程的目标函数。这个方法建议比率,对它们进行归一化,更新建议的最大值,并返回比率的对数之和。