正则化技术详解:L1、L2正则的数学原理与实现

目录

  1. 过拟合问题与正则化的提出
    • 1.1 过拟合的本质
    • 1.2 正则化的基本思想
  2. L1正则化(Lasso回归)
    • 2.1 数学定义
    • 2.2 L1正则化的数学原理
    • 2.3 L1正则化的特性
  3. L2正则化(Ridge回归)
    • 3.1 数学定义
    • 3.2 L2正则化的数学原理
    • 3.3 L2正则化的特性
  4. L1与L2正则化的对比
    • 4.1 数学特性对比
    • 4.2 应用场景对比
    • 4.3 案例分析
  5. 实现带有不同正则项的逻辑回归模型
    • 5.1 数据准备
    • 5.2 实现不同正则化的逻辑回归
    • 5.3 性能评估
    • 5.4 可视化权重分布
    • 5.5 正则化强度的影响
    • 5.6 从头实现带正则化的逻辑回归
  6. 总结与展望

在这里插入图片描述

在机器学习的旅程中,我们常常追求构建能够洞察数据本质、预测未来的模型。然而,模型并非越复杂越好,过度的复杂性往往会导致一个令人头疼的问题——过拟合。想象一下,一位学霸在考试前夜死记硬背了所有习题答案,结果考试时题目稍作变化就束手无策。机器学习模型也是如此,当它过度专注于训练数据的细节和噪声时,就会失去对新数据的泛化能力。

1. 过拟合问题与正则化的提出

1.1 过拟合的本质

过拟合,简单来说,就是模型在训练集上表现得过于优秀,但在未见过的测试集上却表现糟糕。它就像是模型“记住”了训练集中的每一个细节,包括那些无关紧要的噪声,而不是学习到数据背后真正的模式。

从数学的角度来看,过拟合通常与模型参数的Magnitude过大有关。参数越大,模型曲线就可能越弯曲,越能拟合训练数据中的每一个点,但也更容易受到噪声的影响。

我们可以用一个形象的例子来理解。假设我们想要用多项式拟合一组数据点,这些数据点实际上呈现线性关系。如果使用高阶多项式,模型可能会为了穿过每一个训练点而变得曲折蜿蜒,最终完美拟合训练集,但这条曲线在新数据上的表现很可能惨不忍睹。

1.2 正则化的基本思想

为了对抗过拟合这只“拦路虎”,正则化技术应运而生。它的核心思想是在模型的损失函数中加入一个正则化项(惩罚项),如同给模型的学习过程加上一道“紧箍咒”,限制模型的复杂度,使其参数值保持在一个合理的范围内。

正则化的数学表达简洁而优雅:

J r e g u l a r i z e d ( θ ) = J ( θ ) + λ R ( θ ) J_{regularized}(\theta) = J(\theta) + \lambda R(\theta) Jregularized(θ)=J(θ)+λR(θ)

让我们来解读一下这个公式:

  • J ( θ ) J(\theta) J(θ): 这是原始的损失函数,衡量模型预测与真实值之间的差距。我们的目标是最小化这个差距,让模型尽可能准确地拟合数据。
  • R ( θ ) R(\theta) R(θ): 这就是正则化项,它是模型参数 θ \theta θ 的函数,用来惩罚模型的复杂度。常见的正则化项有L1正则化和L2正则化。
  • λ \lambda λ: 这是正则化强度,也称为正则化系数,是一个超参数,用于控制正则化的程度。 λ \lambda λ 越大,正则化效果越强,模型参数越趋向于变小。

通过在损失函数中加入正则化项,我们希望模型在追求最小化预测误差的同时,也尽量保持参数的“苗条”,避免过度膨胀,从而提高模型的泛化能力。

2. L1正则化(Lasso回归)

L1正则化,又名 Lasso (Least Absolute Shrinkage and Selection Operator) 回归,以其能够产生稀疏解的特性而备受青睐。它就像一位精明的特征选择专家,能够自动剔除模型中不重要的特征,让模型更加简洁高效。

2.1 数学定义

L1正则化的正则化项定义为模型参数绝对值之和,用数学公式表达如下:

R ( θ ) = ∑ i = 1 n ∣ θ i ∣ R(\theta) = \sum_{i=1}^{n} |\theta_i| R(θ)=i=1nθi

将L1正则化项加入到原始损失函数中,我们就得到了带L1正则化的损失函数:

J L 1 ( θ ) = J ( θ ) + λ ∑ i = 1 n ∣ θ i ∣ J_{L1}(\theta) = J(\theta) + \lambda \sum_{i=1}^{n} |\theta_i| JL1(θ)=J(θ)+λi=1nθi

2.2 L1正则化的数学原理

L1正则化之所以能够产生稀疏解,与其独特的几何特性密不可分。我们可以从优化的角度来理解这一现象。

当我们最小化带有L1正则项的损失函数时,实际上是在原始损失函数的等高线和以原点为中心的L1球(在二维空间中表现为菱形)的交点处寻找最优解。

原始损失函数等高线
最优解
L1正则化项约束区域

由于L1球的菱角往往与坐标轴相交,因此最优解更容易出现在坐标轴上,这意味着某些参数的值会变为零。参数为零就意味着对应的特征被模型“抛弃”了,从而实现了特征选择的效果。

在梯度下降的优化过程中,L1正则化的梯度也体现了其特性:

∂ ∂ θ j J L 1 ( θ ) = ∂ ∂ θ j J ( θ ) + λ sign ( θ j ) \frac{\partial}{\partial \theta_j} J_{L1}(\theta) = \frac{\partial}{\partial \theta_j} J(\theta) + \lambda \text{sign}(\theta_j) θjJL1(θ)=θjJ(θ)+λsign(θj)

其中, sign ( θ j ) \text{sign}(\theta_j) sign(θj) 是符号函数,它的作用是为每个参数施加一个恒定大小的“力”,方向取决于参数的符号,始终指向零点。当参数 θ j \theta_j θj 为正时,梯度会减去 λ \lambda λ,当参数 θ j \theta_j θj 为负时,梯度会加上 λ \lambda λ。这种“推向零”的力量对于较小的参数来说尤其明显,很容易将它们直接推到零点,从而实现稀疏化。

2.3 L1正则化的特性

总结一下L1正则化的特性:

  1. 特征选择 (Feature Selection):L1正则化最突出的特点就是能够自动进行特征选择,将不重要特征的权重压缩至零,保留重要特征,简化模型。
  2. 稀疏性 (Sparsity):L1正则化倾向于产生稀疏解,即模型的大部分参数为零。稀疏模型不仅更易于理解和解释,而且在存储和计算上也更高效。
  3. 鲁棒性 (Robustness):由于L1范数对异常值不敏感,因此L1正则化模型对数据中的异常值具有一定的鲁棒性。
  4. 非平滑性 (Non-smoothness):L1范数在原点处不可微,这给优化算法带来了一些挑战,例如不能直接使用标准的梯度下降法,需要使用次梯度下降等方法。但现代优化库通常已经很好地解决了这个问题。

3. L2正则化(Ridge回归)

L2正则化,又称为 Ridge回归权重衰减 (Weight Decay),是另一种常用的正则化技术。与L1正则化不同,L2正则化不会产生稀疏解,而是使模型的所有参数都趋向于变小,但通常不会变为零。

3.1 数学定义

L2正则化的正则化项定义为模型参数的平方和,数学公式如下:

R ( θ ) = ∑ i = 1 n θ i 2 R(\theta) = \sum_{i=1}^{n} \theta_i^2 R(θ)=i=1nθi2

将L2正则化项加入到原始损失函数中,我们就得到了带L2正则化的损失函数:

J L 2 ( θ ) = J ( θ ) + λ ∑ i = 1 n θ i 2 J_{L2}(\theta) = J(\theta) + \lambda \sum_{i=1}^{n} \theta_i^2 JL2(θ)=J(θ)+λi=1nθi2

3.2 L2正则化的数学原理

L2正则化的作用是使模型参数均匀地缩小,但通常不会压缩至零。这同样可以从几何角度和优化过程来理解。

当我们最小化带有L2正则项的损失函数时,我们是在原始损失函数的等高线和以原点为中心的L2球(在二维空间中表现为圆形)的交点处寻找最优解。

原始损失函数等高线
最优解 交点
L2正则化项约束区域 圆形

与菱形不同,圆形的边界在各个方向上都是平滑的,与等高线的交点通常不会落在坐标轴上,因此L2正则化很少将参数压缩为零。

在梯度下降的优化过程中,L2正则化的梯度为:

∂ ∂ θ j J L 2 ( θ ) = ∂ ∂ θ j J ( θ ) + 2 λ θ j \frac{\partial}{\partial \theta_j} J_{L2}(\theta) = \frac{\partial}{\partial \theta_j} J(\theta) + 2\lambda \theta_j θjJL2(θ)=θjJ(θ)+2λθj

可以看到,L2正则化对参数的惩罚力度与参数值的大小成正比。参数值越大,惩罚越大,参数向零收缩的“力量”也越大。但当参数接近零时,这个力量也会减小,因此参数会趋近于零,但通常不会精确地变为零。这就是“权重衰减”名称的由来。

3.3 L2正则化的特性

总结L2正则化的特性:

  1. 权重衰减 (Weight Decay):L2正则化使所有权重都向零收缩,但通常不会变为零。它通过减小参数的Magnitude来降低模型的复杂度。
  2. 平滑性 (Smoothness):L2范数处处可微,这使得基于梯度的优化算法(如梯度下降法)能够更稳定、更高效地工作。
  3. 处理多重共线性 (Multicollinearity):当特征之间存在高度相关性(多重共线性)时,L2正则化表现良好。它倾向于将相关特征的权重均匀地分散,而不是像L1正则化那样只选择一个特征并完全忽略其他相关特征。
  4. 计算效率 (Computational Efficiency):由于L2范数是处处可微的,且计算简单,因此L2正则化在计算上通常比L1正则化更高效。

4. L1与L2正则化的对比

L1和L2正则化都是有效的正则化技术,但它们有着不同的数学特性和应用场景。理解它们的差异,有助于我们在实际问题中做出更明智的选择。

4.1 数学特性对比

我们可以从以下几个方面对比L1和L2正则化的数学特性:

特性L1正则化L2正则化
正则化项$\sum_{i=1}^{n}\theta_i
几何形状菱形 (L1球)圆形 (L2球)
导数 sign ( θ j ) \text{sign}(\theta_j) sign(θj) 2 θ j 2\theta_j 2θj
可微性在原点不可微处处可微
解的特性稀疏解 (参数趋向于零)非稀疏解 (参数趋向于小但不为零)
特征选择自动进行特征选择,产生稀疏模型不进行特征选择,所有特征权重都缩小
优化难度相对较难,原点不可微,可能需要次梯度下降等方法相对容易,处处可微,可以使用标准梯度下降法
对异常值敏感性相对鲁棒相对敏感

4.2 应用场景对比

根据L1和L2正则化的特性,我们可以总结它们各自更适合的应用场景:

L1正则化适用于:

  • 特征数量庞大,需要进行特征选择的场景:例如,文本分类、基因分析等领域,特征维度很高,但只有少数特征真正重要。
  • 希望得到稀疏模型的场景:稀疏模型更简洁、可解释性更强,且计算效率更高。
  • 数据中存在大量无关特征的场景:L1正则化可以自动剔除这些无关特征,提高模型的泛化能力。

L2正则化适用于:

  • 几乎所有特征都可能对结果有贡献的场景:例如,图像识别、语音识别等领域,每个像素或频谱分量都可能包含信息。
  • 数据中存在多重共线性的场景:L2正则化可以有效地缓解多重共线性带来的模型不稳定问题。
  • 希望模型更加稳定、泛化能力更强的场景:L2正则化通过缩小所有参数,降低模型的整体复杂度,提高泛化能力。

4.3 案例分析

让我们通过一个案例来更直观地理解L1和L2正则化的应用场景。

案例:房价预测

假设我们有一个房价预测问题,数据集包含房屋的100个特征,例如面积、卧室数量、地理位置、周边设施、房屋朝向、建筑材料等等。

  • 情景一:特征选择。如果我们认为只有少数几个关键特征(例如面积、位置、房龄)真正决定房价,而其他特征可能是噪声或冗余信息,那么 L1正则化 是一个更好的选择。它可以帮助我们自动筛选出最重要的特征,构建一个简洁而有效的房价预测模型。

  • 情景二:所有特征都相关。如果我们在另一个城市进行房价预测,那里的房价可能受到更多因素的综合影响,例如房屋的每一个细节都可能影响买家的心理价位。这时,L2正则化 可能更合适。它可以缩小所有特征的权重,避免模型过度依赖于某些特征,从而提高模型的稳定性和泛化能力。

当然,在实际应用中,我们通常需要根据具体问题和数据特点来选择合适的正则化方法,甚至可以尝试 弹性网络 (Elastic Net),它结合了L1和L2正则化的优点,通过调节 L1_ratio 参数来平衡两种正则化的效果。

5. 实现带有不同正则项的逻辑回归模型

理论学习固然重要,但实践才是检验真理的唯一标准。接下来,我们将通过Python代码实现带有L1和L2正则化的逻辑回归模型,并比较它们在合成数据集上的表现。

5.1 数据准备

首先,我们生成一个合成数据集,其中包含100个特征,但只有前5个特征与目标变量真正相关。这模拟了真实世界中特征冗余的情况,可以用来检验正则化方法的特征选择能力。

import numpy as np
import matplotlib.pyplot as plt
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
from sklearn.linear_model import LogisticRegression
from sklearn.metrics import accuracy_score, roc_auc_score
import pandas as pd

# 设置随机种子,保证实验可重复性
np.random.seed(42)

# 生成合成数据
n_samples = 1000  # 样本数量
n_features = 100 # 特征数量

# 生成特征矩阵,只有前5个特征是有用的
X = np.random.randn(n_samples, n_features) # 生成服从标准正态分布的随机特征

# 生成目标变量,只依赖于前5个特征
true_weights = np.zeros(n_features) # 初始化权重向量为零
true_weights[:5] = np.random.randn(5) # 前5个特征赋予随机权重
z = np.dot(X, true_weights) # 线性组合特征和权重
prob = 1 / (1 + np.exp(-z)) # Sigmoid函数,将线性输出转换为概率
y = np.random.binomial(1, prob) # 根据概率生成二元标签 (0或1)

# 划分训练集和测试集
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)

# 标准化特征,提高模型训练效率和稳定性
scaler = StandardScaler()
X_train = scaler.fit_transform(X_train) # 训练集上拟合标准化模型并转换
X_test = scaler.transform(X_test)       # 测试集上使用训练集拟合的标准化模型进行转换

这段代码首先生成了一个包含1000个样本和100个特征的合成数据集。关键在于 true_weights 的设置,我们只给前5个特征赋予了随机权重,其余特征的真实权重为零。这意味着理想的模型应该只关注前5个特征,而忽略后面的95个特征。

接着,我们将数据集划分为训练集和测试集,并使用 StandardScaler 对特征进行标准化处理。特征标准化是机器学习预处理的重要步骤,可以加速梯度下降的收敛,并提高模型的稳定性。

5.2 实现不同正则化的逻辑回归

接下来,我们使用 scikit-learn 库中的 LogisticRegression 类,分别实现不带正则化、带L1正则化、带L2正则化以及弹性网络正则化的逻辑回归模型。

# 不带正则化的逻辑回归
lr_no_reg = LogisticRegression(penalty=None, random_state=42, max_iter=1000)
lr_no_reg.fit(X_train, y_train) # 使用训练数据拟合模型
y_pred_no_reg = lr_no_reg.predict(X_test) # 预测测试集标签
y_prob_no_reg = lr_no_reg.predict_proba(X_test)[:, 1] # 预测测试集概率 (正类概率)

# L1正则化的逻辑回归
lr_l1 = LogisticRegression(penalty='l1', C=1.0, solver='liblinear', random_state=42, max_iter=1000)
lr_l1.fit(X_train, y_train)
y_pred_l1 = lr_l1.predict(X_test)
y_prob_l1 = lr_l1.predict_proba(X_test)[:, 1]

# L2正则化的逻辑回归
lr_l2 = LogisticRegression(penalty='l2', C=1.0, random_state=42, max_iter=1000)
lr_l2.fit(X_train, y_train)
y_pred_l2 = lr_l2.predict(X_test)
y_prob_l2 = lr_l2.predict_proba(X_test)[:, 1]

# 弹性网络(Elastic Net,结合L1和L2)
lr_elastic = LogisticRegression(penalty='elasticnet', C=1.0, solver='saga',
                                l1_ratio=0.5, random_state=42, max_iter=1000)
lr_elastic.fit(X_train, y_train)
y_pred_elastic = lr_elastic.predict(X_test)
y_prob_elastic = lr_elastic.predict_proba(X_test)[:, 1]

在上面的代码中,我们通过 penalty 参数来指定正则化类型:

  • penalty=None:不使用正则化。
  • penalty='l1':使用L1正则化 (Lasso回归)。 注意 solver 参数需要设置为 ‘liblinear’ 或 ‘saga’ 等支持L1正则化的求解器。
  • penalty='l2':使用L2正则化 (Ridge回归)。 这是 LogisticRegression 的默认选项。
  • penalty='elasticnet':使用弹性网络正则化。 l1_ratio 参数控制L1正则化和L2正则化的混合比例。 l1_ratio=0.5 表示L1和L2正则化各占一半。 solver 需要设置为 ‘saga’。

C 参数是正则化强度的倒数,C=1.0 表示使用默认的正则化强度。random_state 用于设置随机种子,保证实验的可重复性。max_iter 设置最大迭代次数。

5.3 性能评估

我们使用准确率 (Accuracy) 和 ROC曲线下面积 (AUC) 作为评估指标,来衡量不同正则化模型的性能。同时,我们还统计了各个模型中非零权重的数量,以考察L1正则化的特征选择效果。

# 计算性能指标
results = pd.DataFrame({
    'Model': ['No Regularization', 'L1 Regularization', 'L2 Regularization', 'Elastic Net'],
    'Accuracy': [
        accuracy_score(y_test, y_pred_no_reg), # 计算准确率
        accuracy_score(y_test, y_pred_l1),
        accuracy_score(y_test, y_pred_l2),
        accuracy_score(y_test, y_pred_elastic)
    ],
    'AUC': [
        roc_auc_score(y_test, y_prob_no_reg), # 计算AUC值
        roc_auc_score(y_test, y_prob_l1),
        roc_auc_score(y_test, y_prob_l2),
        roc_auc_score(y_test, y_prob_elastic)
    ]
})

print(results) # 打印性能指标表格

# 计算非零权重的数量
n_nonzero_no_reg = np.sum(lr_no_reg.coef_ != 0) # 统计无正则化模型非零权重数量
n_nonzero_l1 = np.sum(lr_l1.coef_ != 0) # 统计L1正则化模型非零权重数量
n_nonzero_l2 = np.sum(lr_l2.coef_ != 0) # 统计L2正则化模型非零权重数量
n_nonzero_elastic = np.sum(lr_elastic.coef_ != 0) # 统计弹性网络模型非零权重数量

print("\n非零权重数量:")
print(f"No Regularization: {n_nonzero_no_reg}")
print(f"L1 Regularization: {n_nonzero_l1}")
print(f"L2 Regularization: {n_nonzero_l2}")
print(f"Elastic Net: {n_nonzero_elastic}")

运行这段代码,我们可以得到不同模型的性能指标和非零权重数量。在我们的合成数据集上,我们通常会观察到以下现象:

  • L1正则化 模型具有最高的稀疏性,非零权重数量最少,验证了L1正则化的特征选择能力。
  • L1和L2正则化 模型通常比 无正则化 模型具有更高的泛化性能 (Accuracy 和 AUC),表明正则化有助于缓解过拟合。
  • 弹性网络 模型的性能往往介于L1和L2正则化模型之间,可以通过调节 l1_ratio 参数来平衡稀疏性和泛化能力。

5.4 可视化权重分布

为了更直观地比较不同正则化方法对模型权重的影响,我们可以将模型的权重分布可视化出来。

plt.figure(figsize=(12, 10))

# 绘制真实权重
plt.subplot(2, 2, 1)
plt.stem(true_weights) # 绘制茎叶图,用于可视化离散数据
plt.title('True Weights')
plt.xlabel('Feature Index')
plt.ylabel('Weight Value')

# 绘制无正则化的权重
plt.subplot(2, 2, 2)
plt.stem(lr_no_reg.coef_[0]) # 绘制无正则化模型权重
plt.title('No Regularization')
plt.xlabel('Feature Index')
plt.ylabel('Weight Value')

# 绘制L1正则化的权重
plt.subplot(2, 2, 3)
plt.stem(lr_l1.coef_[0]) # 绘制L1正则化模型权重
plt.title('L1 Regularization')
plt.xlabel('Feature Index')
plt.ylabel('Weight Value')

# 绘制L2正则化的权重
plt.subplot(2, 2, 4)
plt.stem(lr_l2.coef_[0]) # 绘制L2正则化模型权重
plt.title('L2 Regularization')
plt.xlabel('Feature Index')
plt.ylabel('Weight Value')

plt.tight_layout() # 自动调整子图参数,避免重叠
plt.show() # 显示图像

这段代码使用 matplotlib 库绘制了四个子图,分别展示了真实权重、无正则化模型权重、L1正则化模型权重和L2正则化模型权重。通过对比这些子图,我们可以清晰地看到:

  • 无正则化模型 的权重分布较为分散,很多与真实标签无关的特征也获得了较大的权重。
  • L1正则化模型 的权重分布非常稀疏,大部分权重被压缩为零,只有少数与真实标签相关的特征保留了较大的权重,体现了特征选择的效果。
  • L2正则化模型 的权重分布相对平滑,所有权重都向零收缩,但没有像L1正则化那样产生大量的零权重。
5.5 正则化强度的影响

正则化强度 λ \lambda λ (或 LogisticRegression 中的 C 参数,C = 1/λ) 是一个重要的超参数,它控制着正则化的程度。正则化强度过大或过小都可能影响模型的性能。为了探究正则化强度对模型性能和稀疏性的影响,我们可以进行如下实验:

# 测试不同正则化强度的影响
C_values = [0.001, 0.01, 0.1, 1, 10, 100] # 不同的C值,对应不同的正则化强度 (C越大,正则化越弱)
l1_nonzero = [] # 存储不同C值下L1正则化模型的非零权重数量
l2_nonzero = [] # 存储不同C值下L2正则化模型的非零权重数量
l1_accuracy = [] # 存储不同C值下L1正则化模型的准确率
l2_accuracy = [] # 存储不同C值下L2正则化模型的准确率

for C in C_values:
    # L1正则化
    lr_l1 = LogisticRegression(penalty='l1', C=C, solver='liblinear', random_state=42, max_iter=1000)
    lr_l1.fit(X_train, y_train)
    l1_nonzero.append(np.sum(lr_l1.coef_ != 0)) # 记录非零权重数量
    l1_accuracy.append(accuracy_score(y_test, lr_l1.predict(X_test))) # 记录准确率

    # L2正则化
    lr_l2 = LogisticRegression(penalty='l2', C=C, random_state=42, max_iter=1000)
    lr_l2.fit(X_train, y_train)
    l2_nonzero.append(np.sum(lr_l2.coef_ != 0)) # 记录非零权重数量
    l2_accuracy.append(accuracy_score(y_test, lr_l2.predict(X_test))) # 记录准确率

plt.figure(figsize=(14, 6))

# 绘制非零权重数量与正则化强度的关系
plt.subplot(1, 2, 1)
plt.semilogx(C_values, l1_nonzero, 'o-', label='L1') # 绘制L1正则化非零权重数量曲线,x轴为对数刻度
plt.semilogx(C_values, l2_nonzero, 's-', label='L2') # 绘制L2正则化非零权重数量曲线,x轴为对数刻度
plt.xlabel('C (1/λ)')
plt.ylabel('Number of Non-Zero Weights')
plt.title('Feature Selection vs. Regularization Strength')
plt.legend() # 显示图例
plt.grid(True) # 显示网格

# 绘制准确率与正则化强度的关系
plt.subplot(1, 2, 2)
plt.semilogx(C_values, l1_accuracy, 'o-', label='L1') # 绘制L1正则化准确率曲线,x轴为对数刻度
plt.semilogx(C_values, l2_accuracy, 's-', label='L2') # 绘制L2正则化准确率曲线,x轴为对数刻度
plt.xlabel('C (1/λ)')
plt.ylabel('Accuracy')
plt.title('Accuracy vs. Regularization Strength')
plt.legend() # 显示图例
plt.grid(True) # 显示网格

plt.tight_layout() # 自动调整子图参数,避免重叠
plt.show() # 显示图像

这段代码遍历了一系列 C 值,分别训练了L1和L2正则化的逻辑回归模型,并记录了每个模型在不同正则化强度下的非零权重数量和准确率。然后,我们将这些数据可视化为曲线图,横轴为 C 值 (对数刻度),纵轴分别为非零权重数量和准确率。

通过观察这些曲线图,我们通常会发现:

  • 随着 C 值增大 (正则化强度减弱),L1正则化模型的非零权重数量逐渐增加,L2正则化模型的非零权重数量基本保持不变 (接近于特征总数)。
  • 在一定的 C 值范围内,L1和L2正则化模型的准确率都高于无正则化模型,且随着 C 值的变化,准确率会呈现先升高后降低的趋势,表明存在一个最优的正则化强度。
  • C 值过大 (正则化强度过弱) 时,模型的准确率可能会下降,表明模型可能过拟合。
  • C 值过小 (正则化强度过强) 时,模型的准确率也可能会下降,表明模型可能欠拟合。

因此,在实际应用中,我们需要通过 交叉验证 等方法来选择合适的正则化强度,以达到模型性能和稀疏性之间的最佳平衡。

5.6 从头实现带正则化的逻辑回归

为了更深入地理解正则化的工作原理,我们不妨从头开始实现一个带有L1和L2正则化的逻辑回归模型。这部分代码将展示如何手动计算损失函数、梯度,并使用梯度下降法进行优化。

class CustomLogisticRegression:
    def __init__(self, penalty='none', C=1.0, l1_ratio=0.5, lr=0.01, max_iter=1000, tol=1e-4, random_state=42):
        self.penalty = penalty # 正则化类型 ('none', 'l1', 'l2', 'elasticnet')
        self.C = C             # 正则化强度的倒数
        self.l1_ratio = l1_ratio # 弹性网络中L1正则化比例
        self.lr = lr           # 学习率
        self.max_iter = max_iter # 最大迭代次数
        self.tol = tol         # 收敛容忍度
        self.random_state = random_state # 随机种子
        self.coef_ = None      # 模型权重系数
        self.intercept_ = None # 模型截距

    def _sigmoid(self, z):
        return 1 / (1 + np.exp(-z)) # Sigmoid函数

    def _loss_grad(self, X, y, coef):
        z = np.dot(X, coef) # 线性组合
        y_prob = self._sigmoid(z) # 预测概率
        loss = -np.mean(y * np.log(y_prob + 1e-8) + (1 - y) * np.log(1 - y_prob + 1e-8)) # 交叉熵损失函数
        grad = np.dot(X.T, (y_prob - y)) / len(y) # 交叉熵损失函数梯度

        # 添加正则化项和梯度
        if self.penalty == 'l1':
            loss += (self.C / 2) * np.sum(np.abs(coef)) # L1正则化项
            grad += (self.C / 2) * np.sign(coef) # L1正则化梯度
        elif self.penalty == 'l2':
            loss += (self.C / 2) * np.sum(coef ** 2) # L2正则化项
            grad += self.C * coef # L2正则化梯度
        elif self.penalty == 'elasticnet':
            loss += (self.C / 2) * (self.l1_ratio * np.sum(np.abs(coef)) + (1 - self.l1_ratio) * np.sum(coef ** 2)) # 弹性网络正则化项
            grad += (self.C / 2) * (self.l1_ratio * np.sign(coef) + 2 * (1 - self.l1_ratio) * coef) # 弹性网络正则化梯度

        return loss, grad

    def fit(self, X, y):
        np.random.seed(self.random_state) # 设置随机种子
        n_features = X.shape[1] # 特征数量
        self.coef_ = np.random.randn(n_features) # 初始化权重系数
        self.intercept_ = 0.0 # 初始化截距

        X_b = np.c_[X, np.ones(len(X))] # 添加偏置项 (全为1的列)
        initial_coef = np.r_[self.coef_, self.intercept_] # 合并权重系数和截距

        coef = initial_coef.copy() # 复制初始系数
        losses = [] # 存储损失值

        for iteration in range(self.max_iter):
            current_coef = coef[:-1].copy() # 当前迭代的权重系数 (不包含截距)
            loss, grad_coef = self._loss_grad(X_b, y, current_coef) # 计算损失和梯度
            grad = np.r_[grad_coef, np.array([np.mean(y_prob - y)])] # 梯度,截距部分的梯度为预测概率与真实标签之差的均值

            coef = coef - self.lr * grad # 梯度下降更新系数
            losses.append(loss) # 记录损失值

            if iteration > 1 and np.abs(losses[-2] - losses[-1]) < self.tol: # 检查是否收敛
                break

        self.coef_ = coef[:-1] # 提取权重系数
        self.intercept_ = coef[-1] # 提取截距
        return self

    def predict_proba(self, X):
        X_b = np.c_[X, np.ones(len(X))] # 添加偏置项
        z = np.dot(X_b, np.r_[self.coef_, self.intercept_]) # 线性组合
        return self._sigmoid(z) # 返回预测概率

    def predict(self, X, threshold=0.5):
        y_prob = self.predict_proba(X) # 预测概率
        return (y_prob >= threshold).astype(int) # 根据阈值将概率转换为标签 (0或1)

这个 CustomLogisticRegression 类实现了以下功能:

  • 支持 L1, L2 和 Elastic Net 正则化:通过 penalty 参数选择正则化类型,通过 Cl1_ratio 参数控制正则化强度。
  • 交叉熵损失函数:使用交叉熵损失函数作为优化目标。
  • 梯度下降法:使用批量梯度下降法进行模型优化。
  • Sigmoid 激活函数:使用 Sigmoid 函数将线性输出转换为概率。
  • 预测概率和预测标签:提供 predict_probapredict 方法进行预测。

我们可以使用这个自定义的 CustomLogisticRegression 类来训练带正则化的逻辑回归模型,并与 scikit-learn 库中的 LogisticRegression 进行比较,验证我们实现的正确性。

# 使用自定义逻辑回归模型进行训练和预测 (L1正则化)
custom_lr_l1 = CustomLogisticRegression(penalty='l1', C=1.0, lr=0.1, max_iter=2000, tol=1e-4, random_state=42)
custom_lr_l1.fit(X_train, y_train)
y_pred_custom_l1 = custom_lr_l1.predict(X_test)
y_prob_custom_l1 = custom_lr_l1.predict_proba(X_test)

# 评估自定义L1正则化逻辑回归模型性能
print("\nCustom L1 Regularization Logistic Regression:")
print(f"Accuracy: {accuracy_score(y_test, y_pred_custom_l1)}")
print(f"AUC: {roc_auc_score(y_test, y_prob_custom_l1)}")
print(f"Non-zero weights: {np.sum(custom_lr_l1.coef_ != 0)}")

# 使用自定义逻辑回归模型进行训练和预测 (L2正则化)
custom_lr_l2 = CustomLogisticRegression(penalty='l2', C=1.0, lr=0.1, max_iter=2000, tol=1e-4, random_state=42)
custom_lr_l2.fit(X_train, y_train)
y_pred_custom_l2 = custom_lr_l2.predict(X_test)
y_prob_custom_l2 = custom_lr_l2.predict_proba(X_test)

# 评估自定义L2正则化逻辑回归模型性能
print("\nCustom L2 Regularization Logistic Regression:")
print(f"Accuracy: {accuracy_score(y_test, y_pred_custom_l2)}")
print(f"AUC: {roc_auc_score(y_test, y_prob_custom_l2)}")
print(f"Non-zero weights: {np.sum(custom_lr_l2.coef_ != 0)}")

通过比较自定义模型和 scikit-learn 库模型的性能指标,我们可以验证自定义实现的正确性,并更深入地理解正则化在模型训练过程中的作用。

6. 总结与展望

正则化技术是机器学习工具箱中不可或缺的利器,它能够有效地对抗过拟合,提高模型的泛化能力和鲁棒性。L1和L2正则化作为两种最常用的正则化方法,各有千秋,适用于不同的应用场景。

  • L1正则化 擅长特征选择,能够产生稀疏模型,适用于高维稀疏数据和需要模型解释性的场景。
  • L2正则化 倾向于产生平滑的模型,对多重共线性问题具有一定的缓解作用,适用于大多数场景,特别是当所有特征都可能相关时。
  • 弹性网络 结合了L1和L2正则化的优点,可以通过调节混合比例来适应不同的数据和任务。

在实际应用中,选择合适的正则化方法和强度通常需要进行实验和调优。交叉验证是一种常用的超参数选择方法。此外,还有诸如 Dropout、Batch Normalization 等更高级的正则化技术,它们在深度学习领域发挥着重要作用。

希望本文能够帮助读者深入理解正则化技术的数学原理和应用方法,在机器学习的实践中更加游刃有余!


在训练神经网络模型时,每个epoch代表一次完整的训练循环,其中包含了对所有训练样本的一次前向传播和反向传播过程。Loss是衡量模型预测真实值之间差距的指标,通常通过计算模型输出真实标签的损失函数来得到。 如果在某一个epoch中,loss突然升高,可能有以下几个原因: 1. 学习率过大:学习率决定了每次参数更新的步长。如果学习率设置过大,可能导致模型在参数空间中跳过最优解,无法收敛到更好的状态,从而导致loss上升。 2. 模型过拟合过拟合是指模型在训练数据上表现良好,但在未见过的数据上表现较差。如果模型复杂度过高,容易出现过拟合现象。当模型开始过拟合时,loss会在一定程度上上升。 3. 数据集问题:如果数据集中存在异常值、噪声或者标签错误等问题,会导致模型在训练过程中难以得到准确的预测结果,从而导致loss上升。 4. 梯度爆炸或梯度消失:梯度爆炸指梯度值过大,导致参数更新过大;梯度消失指梯度值过小,导致参数更新缓慢。这两种情况都可能导致模型训练不稳定,loss上升。 针对这些问题,可以尝试以下解决方法: - 调整学习率:逐渐降低学习率,或使用学习率衰减方法。 - 增加正则化项:通过添加L1或L2正则化等方法,减少模型复杂度,防止过拟合。 - 数据预处理:对数据进行清洗、去噪或标准化等处理,确保数据质量。 - 梯度裁剪:限制梯度的范围,防止梯度爆炸。 - 更换优化算法:尝试不同的优化算法,如Adam、RMSprop等,以获得更好的收敛性能。 需要根据具体情况进行分析和调试,找出问题的根源并采取相应措施。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

海棠AI实验室

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值