在机器学习中,逻辑回归是一种广泛使用的分类算法。为了提高模型的泛化能力,通常会在模型中加入正则化项。L1惩罚和L2惩罚是两种常见的正则化方法,它们可以控制模型的复杂度,防止过拟合。此外,还有一种结合了L1和L2惩罚的Elastic-Net方法。本文将探讨在不同正则化参数C值下,这三种方法对模型稀疏性的影响。
稀疏性是指模型中系数为零的比例。在L1惩罚下,随着C值的减小,模型的稀疏性增加,这意味着更多的特征系数会被压缩为零。这有助于特征选择,因为只有少数重要的特征会保留在模型中。相反,L2惩罚对模型的稀疏性影响较小,它倾向于让所有特征的系数都接近于零,但不会完全为零。Elastic-Net惩罚的稀疏性介于L1和L2之间,它结合了两者的特点。
为了验证这些理论,使用了一个8x8的数字图像数据集,将数字0-4和5-9分为两个类别。通过调整C值,观察了不同惩罚方法对模型系数的影响。结果显示,当C值较大时,模型的自由度较高,稀疏性较低;而当C值较小时,模型的稀疏性增加。具体来说,当C=1.00时,三种惩罚方法的稀疏性均为4.69%,模型的准确率均为0.90。当C=0.10时,L1惩罚的稀疏性增加到29.69%,而L2惩罚的稀疏性仍然保持在4.69%。当C=0.01时,L1惩罚的稀疏性进一步增加到84.38%,而L2惩罚的稀疏性仍然很低。
这些结果表明,L1惩罚在控制模型稀疏性方面非常有效,尤其是在C值较小的情况下。这使得L1惩罚成为特征选择的理想选择。然而,L2惩罚在保持模型稳定性方面可能更有优势,因为它不会让太多的特征系数变为零。Elastic-Net惩罚则提供了一个折中的方案,它可以根据l1_ratio参数调整L1和L2惩罚的权重,以适应不同的应用场景。
为了进一步分析这些模型,使用了Python的matplotlib库来可视化模型的系数。通过比较不同C值下的系数图,可以直观地看到模型稀疏性的变化。这些图像显示了每个特征对模型的贡献程度,系数较大的特征在图像中更为明显。通过这种方式,可以更好地理解模型的工作原理,并根据需要调整正则化参数。
import matplotlib.pyplot as plt
import numpy as np
from sklearn import datasets
from sklearn.linear_model import LogisticRegression
from sklearn.preprocessing import StandardScaler
# 加载数据集
X, y = datasets.load_digits(return_X_y=True)
X = StandardScaler().fit_transform(X)
# 二分类问题:小数字(0-4)与大数字(5-9)
y = (y > 4).astype(int)
# 设置Elastic-Net正则化的L1权重
l1_ratio = 0.5
# 创建子图
fig, axes = plt.subplots(3, 3)
# 设置正则化参数
for i, (C, axes_row) in enumerate(zip((1, 0.1, 0.01), axes)):
# 增加容忍度以缩短训练时间
clf_l1_LR = LogisticRegression(C=C, penalty="l1", tol=0.01, solver="saga")
clf_l2_LR = LogisticRegression(C=C, penalty="l2", tol=0.01, solver="saga")
clf_en_LR = LogisticRegression(C=C, penalty="elasticnet", solver="saga", l1_ratio=l1_ratio, tol=0.01)
clf_l1_LR.fit(X, y)
clf_l2_LR.fit(X, y)
clf_en_LR.fit(X, y)
coef_l1_LR = clf_l1_LR.coef_.ravel()
coef_l2_LR = clf_l2_LR.coef_.ravel()
coef_en_LR = clf_en_LR.coef_.ravel()
# 计算稀疏性
sparsity_l1_LR = np.mean(coef_l1_LR == 0) * 100
sparsity_l2_LR = np.mean(coef_l2_LR == 0) * 100
sparsity_en_LR = np.mean(coef_en_LR == 0) * 100
print(f"C={C:.2f}")
print(f"Sparsity with L1 penalty: {sparsity_l1_LR:.2f}%")
print(f"Sparsity with Elastic-Net penalty: {sparsity_en_LR:.2f}%")
print(f"Sparsity with L2 penalty: {sparsity_l2_LR:.2f}%")
print(f"Score with L1 penalty: {clf_l1_LR.score(X, y):.2f}")
print(f"Score with Elastic-Net penalty: {clf_en_LR.score(X, y):.2f}")
print(f"Score with L2 penalty: {clf_l2_LR.score(X, y):.2f}")
if i == 0:
axes_row[0].set_title("L1 penalty")
axes_row[1].set_title(f"Elastic-Net\nl1_ratio={l1_ratio}")
axes_row[2].set_title("L2 penalty")
for ax, coefs in zip(axes_row, [coef_l1_LR, coef_en_LR, coef_l2_LR]):
ax.imshow(np.abs(coefs.reshape(8, 8)), interpolation="nearest", cmap="binary", vmax=1, vmin=0)
ax.set_xticks(())
ax.set_yticks(())
axes_row[0].set_ylabel(f"C={C}")
plt.show()