机器学习:套索回归
套索回归是一种缩减方法,和岭回归类似。它的不同之处在于选择的惩罚方式:套索在参数 \(\beta\) 上施加了一个 \(\ell_1\) 惩罚。也就是说,套索找到了一个最小化函数
的 \(\beta\) 的赋值方式,其中 \(\lambda\) 是超参数,通常 \(X\) 是训练数据,\(Y\) 是观测数据。\(\ell_1\) 惩罚鼓励学到的参数的稀疏性,并且,如我们所看到的,可以驱使许多系数为零。从这个意义上说,套索是一种连续的特征选择方法。
在这个笔记本中,我们展示了如何使用 CVXPY 拟合套索模型,如何评估模型,以及如何调整超参数 \(\lambda\)。
import cvxpy as cp
import numpy as np
import matplotlib.pyplot as plt
编写目标函数
我们可以将 目标函数 分解为 最小二乘损失函数 和一个 \(\ell_1\) 正则化项 的和。
def loss_fn(X, Y, beta):
return cp.norm2(X @ beta - Y)**2
def regularizer(beta):
return cp.norm1(beta)
def objective_fn(X, Y, beta, lambd):
return loss_fn(X, Y, beta) + lambd * regularizer(beta)
def mse(X, Y, beta):
return (1.0 / X.shape[0]) * loss_fn(X, Y, beta).value
生成数据
我们生成线性相关的训练样本和观测结果;我们让关系*稀疏*,然后看看lasso能够近似恢复它。
def generate_data(m=100, n=20, sigma=5, density=0.2):
"生成数据矩阵 X 和观测结果 Y。"
np.random.seed(1)
beta_star = np.random.randn(n)
idxs = np.random.choice(range(n), int((1-density)*n), replace=False)
for idx in idxs:
beta_star[idx] = 0
X = np.random.randn(m,n)
Y = X.dot(beta_star) + np.random.normal(0, sigma, size=m)
return X, Y, beta_star
m = 100
n = 20
sigma = 5
density = 0.2
X, Y, _ = generate_data(m, n, sigma)
X_train = X[:50, :]
Y_train = Y[:50]
X_test = X[50:, :]
Y_test = Y[50:]
拟合模型
我们只需要创建一个CVXPY问题来拟合模型,其中目标是最小化上述定义的目标函数。我们将 \(\lambda\) 设置为CVXPY参数,这样我们可以使用一个CVXPY问题来获得许多 \(\lambda\) 值的估计。
beta = cp.Variable(n)
lambd = cp.Parameter(nonneg=True)
problem = cp.Problem(cp.Minimize(objective_fn(X_train, Y_train, beta, lambd)))
lambd_values = np.logspace(-2, 3, 50)
train_errors = []
test_errors = []
beta_values = []
for v in lambd_values:
lambd.value = v
problem.solve()
train_errors.append(mse(X_train, Y_train, beta))
test_errors.append(mse(X_test, Y_test, beta))
beta_values.append(beta.value)
评估模型
完成正如我们在岭回归中看到的那样,正则化可以提高泛化能力。
``` python
%matplotlib inline %config InlineBackend.figure_format = ‘svg’
- def plot_train_test_errors(train_errors, test_errors, lambd_values):
plt.plot(lambd_values, train_errors, label=”训练误差”) plt.plot(lambd_values, test_errors, label=”测试误差”) plt.xscale(“log”) plt.legend(loc=”upper left”) plt.xlabel(r”$lambda$”, fontsize=16) plt.title(“均方误差(MSE)”) plt.show()
plot_train_test_errors(train_errors, test_errors, lambd_values)
正则化路径和特征选择
随着 \(\lambda\) 的增加,参数被驱使为 \(0\)。当 \(\lambda \approx 10\) 时,大约80% 的系数*确切地*为零。这与 \(\beta^*\) 的生成方式相符,其80% 的条目为零。与具有最慢衰减系数的特征对应的可以解释为最重要的特征。
从定性上看,lasso 与 ridge 的不同之处在于前者经常将参数驱使为零,而后者只是收缩参数而不通常将其设为零。也就是说,lasso 产生的是稀疏模型;ridge(通常)不是。
def plot_regularization_path(lambd_values, beta_values):
num_coeffs = len(beta_values[0])
for i in range(num_coeffs):
plt.plot(lambd_values, [wi[i] for wi in beta_values])
plt.xlabel(r"$\lambda$", fontsize=16)
plt.xscale("log")
plt.title("正则化路径")
plt.show()
plot_regularization_path(lambd_values, beta_values)