对于广义线性模型(即逻辑回归、岭回归、泊松回归),
您可以有效地调整许多正则化超参数
使用精确导数和近似留一交叉验证。
但不要只停留在梯度上,计算完整的 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()
显示