有没有办法在scikit中通过渐变下降来执行超参数调整?虽然可能难以计算超参数梯度的公式,但通过评估超参数空间中的两个关闭点来对超参数梯度进行数值计算应该相当容易。这种方法是否存在实施?为什么这种方法不是一个好主意?
答案 0 :(得分:17)
梯度的计算是最少的问题。至少在高级automatic differentiation软件的时候。 (当然,对所有sklearn分类器实现这一点并不容易)
虽然有些人使用过这种想法,但他们只是针对某些特定且精心设计的问题(例如SVM调整)而这样做。此外,可能有很多假设,因为:
为什么这不是一个好主意?
我可以补充一点,你的一般问题是可以考虑的最糟糕的优化问题,因为它是:
最后一部分是为什么sklearn中提供的方法如此简单:
np.logspace
来分析更多大数字)虽然有许多贝叶斯方法,包括可用的python软件,如hyperopt和spearmint,但许多人认为,随机搜索一般来说是最好的方法(这可能会令人惊讶但是强调提到的问题)。
答案 1 :(得分:2)
以下是两篇描述基于梯度的超参数优化的论文:
通过在整个训练过程中向后链接导数,我们可以针对所有超参数计算出交叉验证性能的精确梯度。这些梯度使我们能够优化数千个超参数,包括步长和动量时间表,权重初始化分布,参数丰富的正则化方案以及神经网络体系结构。我们通过用动量精确地逆转随机梯度下降的动力学来计算超参数梯度。
我们研究了两种方法(反向模式和正向模式),用于计算相对于任何迭代学习算法(例如随机梯度下降)的超参数的验证误差的梯度。这些过程反映了递归神经网络的两种计算梯度的方法,并且在运行时间和空间要求方面具有不同的权衡。我们对反向模式程序的表述与Maclaurin等人先前的工作有关。 [2015],但不需要可逆动力学。前向模式过程适用于实时超参数更新,可以显着加快大型数据集上超参数的优化。
答案 2 :(得分:1)
对于广义线性模型(即逻辑回归、岭回归、泊松回归), 您可以有效地调整许多正则化超参数 使用精确导数和近似留一交叉验证。
但不要只停留在梯度上,计算完整的 Hessian 并使用二阶优化器——它是 更高效、更稳健。
sklearn 目前没有这个功能,但有其他工具可以做到这一点。
例如,这里是如何使用 python 包 bbai 来适应 岭正则化逻辑回归的超参数,以最大化 威斯康星乳腺癌数据集训练数据集的近似留一法交叉验证。
加载数据集
from sklearn.datasets import load_breast_cancer
from sklearn.preprocessing import StandardScaler
data = load_breast_cancer()
X = data['data']
X = StandardScaler().fit_transform(X)
y = data['target']
拟合模型
import bbai.glm
model = bbai.glm.LogisticRegression()
# Note: it automatically fits the C parameter to minimize the error on
# the approximate leave-one-out cross-validation.
model.fit(X, y)
因为它同时使用梯度和粗麻布以及有效的精确公式 (没有自动微分),它可以快速拨入一个精确的超参数,只需要几个 评价。
YMMV,但是当我将它与带有默认参数的 sklearn 的 LogisticRegressionCV 进行比较时,它运行 在很短的时间内。
t1 = time.time()
model = bbai.glm.LogisticRegression()
model.fit(X, y)
t2 = time.time()
print('***** approximate leave-one-out optimization')
print('C = ', model.C_)
print('time = ', (t2 - t1))
from sklearn.linear_model import LogisticRegressionCV
print('***** sklearn.LogisticRegressionCV')
t1 = time.time()
model = LogisticRegressionCV(scoring='neg_log_loss', random_state=0)
model.fit(X, y)
t2 = time.time()
print('C = ', model.C_[0])
print('time = ', (t2 - t1))
印刷品
***** approximate leave-one-out optimization
C = 0.6655139682151275
time = 0.03996014595031738
***** sklearn.LogisticRegressionCV
C = 0.3593813663804626
time = 0.2602980136871338
近似留一法交叉验证 (ALOOCV) 是留一法的近似 交叉验证可以更有效地评估广义线性模型。
首先拟合正则化模型。然后使用牛顿算法的一步来近似 当我们留下单个数据点时,模型权重将是。如果正则化成本函数为 广义线性模型表示为
那么 ALOOCV 可以计算为
哪里
(注:H代表最优权重下代价函数的hessian)
有关 ALOOCV 的更多背景信息,您可以查看此guide。
还可以计算 ALOOCV 的精确导数,从而提高优化效率。
我不会把派生公式放在这里,因为它们很复杂,但请参阅论文 Optimizing Approximate Leave-one-out Cross-validation。
如果我们绘制出 ALOOCV 并与示例数据集的留一法交叉验证进行比较, 您可以看到它非常密切地跟踪它,并且 ALOOCV 最优值几乎与 LOOCV 最优。
计算留一法交叉验证
import numpy as np
def compute_loocv(X, y, C):
model = bbai.glm.LogisticRegression(C=C)
n = len(y)
loo_likelihoods = []
for i in range(n):
train_indexes = [i_p for i_p in range(n) if i_p != i]
test_indexes = [i]
X_train, X_test = X[train_indexes], X[test_indexes]
y_train, y_test = y[train_indexes], y[test_indexes]
model.fit(X_train, y_train)
pred = model.predict_proba(X_test)[0]
loo_likelihoods.append(pred[y_test[0]])
return sum(np.log(loo_likelihoods))
计算近似的留一法交叉验证
import scipy
def fit_logistic_regression(X, y, C):
model = bbai.glm.LogisticRegression(C=C)
model.fit(X, y)
return np.array(list(model.coef_[0]) + list(model.intercept_))
def compute_hessian(p_vector, X, alpha):
n, k = X.shape
a_vector = np.sqrt((1 - p_vector)*p_vector)
R = scipy.linalg.qr(a_vector.reshape((n, 1))*X, mode='r')[0]
H = np.dot(R.T, R)
for i in range(k-1):
H[i, i] += alpha
return H
def compute_alo(X, y, C):
alpha = 1.0 / C
w = fit_logistic_regression(X, y, C)
X = np.hstack((X, np.ones((X.shape[0], 1))))
n = X.shape[0]
y = 2*y - 1
u_vector = np.dot(X, w)
p_vector = scipy.special.expit(u_vector*y)
H = compute_hessian(p_vector, X, alpha)
L = np.linalg.cholesky(H)
T = scipy.linalg.solve_triangular(L, X.T, lower=True)
h_vector = np.array([np.dot(ti, ti) for pi, ti in zip(p_vector, T.T)])
loo_u_vector = u_vector - \
y * (1 - p_vector)*h_vector / (1 - p_vector*(1 - p_vector)*h_vector)
loo_likelihoods = scipy.special.expit(y*loo_u_vector)
return sum(np.log(loo_likelihoods))
绘制结果(连同 ALOOCV 最优值)
import matplotlib.pyplot as plt
Cs = np.arange(0.1, 2.0, 0.1)
loocvs = [compute_loocv(X, y, C) for C in Cs]
alos = [compute_alo(X, y, C) for C in Cs]
fig, ax = plt.subplots()
ax.plot(Cs, loocvs, label='LOOCV', marker='o')
ax.plot(Cs, alos, label='ALO', marker='x')
ax.axvline(model.C_, color='tab:green', label='C_opt')
ax.set_xlabel('C')
ax.set_ylabel('Log-Likelihood')
ax.set_title("Breast Cancer Dataset")
ax.legend()
显示