这份笔记总结会围绕“核心概念+关键差异+实操代码”展开,将两个知识点拆分成独立模块,方便你快速查阅和对比。
一、L1、L2正则化
正则化是机器学习中防止模型过拟合的核心手段,通过在损失函数中添加参数惩罚项,约束模型权重规模。
1. 正则化的作用
-
防止过拟合:当模型复杂度过高(如参数过多)时,容易“死记硬背”训练数据的噪声,正则化通过惩罚大权重,降低模型复杂度,提升泛化能力。
-
稳定模型输出:约束参数范围,避免因输入微小变化导致输出剧烈波动,增强模型鲁棒性。
-
辅助特征选择:L1正则化可产生稀疏权重,自动筛选关键特征(不重要特征的权重会被压缩至0)。
2. L1与L2正则化的区别
两者核心差异在于惩罚项的数学形式,进而导致对参数的影响、解的特点完全不同,具体对比如下:
| 对比维度 | L1正则化(Lasso) | L2正则化(Ridge) |
|---|---|---|
| 惩罚项公式 | 损失函数 + λ×Σ | w |
| 对参数的影响 | 压缩部分权重至 0,产生稀疏解 | 仅缩小权重值,不产生稀疏解 |
| 解的唯一性 | 可能存在多个最优解 | 通常只有唯一最优解 |
| 适用场景 | 特征数量多、需筛选关键特征时 | 特征重要性均衡、需稳定权重时 |
3. 代码示例(基于PyTorch)
import torch
import torch.nn as nn
import torch.optim as optim
# 1. 定义简单模型(以线性回归为例)
model = nn.Linear(in_features=10, out_features=1)
# 2. L1正则化实现(两种方式)
# 方式1:使用L1Loss作为正则项,手动加到总损失中
l1_lambda = 0.01 # 正则化强度(超参数,需调优)
criterion = nn.MSELoss() # 基础损失(如均方误差)
optimizer = optim.SGD(model.parameters(), lr=0.001)
# 模拟训练过程
x = torch.randn(32, 10) # 32个样本,每个样本10个特征
y = torch.randn(32, 1) # 对应标签
optimizer.zero_grad()
y_pred = model(x)
base_loss = criterion(y_pred, y)
# 计算L1正则项(所有参数的绝对值之和)
l1_regularization = torch.tensor(0., requires_grad=True)
for param in model.parameters():
l1_regularization += torch.norm(param, 1) # L1范数(绝对值和)
total_loss = base_loss + l1_lambda * l1_regularization
total_loss.backward()
optimizer.step()
# 3. L2正则化实现(PyTorch优化器直接支持,参数weight_decay)
# L2正则化=权重衰减,无需手动计算,直接在optimizer中设置weight_decay
optimizer_l2 = optim.SGD(model.parameters(), lr=0.001, weight_decay=0.01) # weight_decay即λ
二、指数加权平均(Exponential Weighted Moving Average, EWMA)
指数加权平均是一种对序列数据的平滑处理方法,通过给近期数据更高权重、远期数据更低权重,动态计算平均值,常用于优化器(如Adam)、温度预测等场景。
1. 指数加权平均的含义
-
核心公式: vt=β⋅vt−1+(1−β)⋅θtv_t = \beta \cdot v_{t-1} + (1-\beta) \cdot \theta_tvt=β⋅vt−1+(1−β)⋅θt 其中, vtv_tvt 是第t时刻的指数加权平均值, θt\theta_tθt 是第t时刻的原始数据, β\betaβ 是平滑系数(通常取0.9、0.99等)。
-
权重特点:近期数据的权重为 (1−β)(1-\beta)(1−β) ,前一时刻平均值的权重为 β\betaβ ,远期数据的权重会以 βk\beta^kβk 形式指数衰减(k为时间间隔)。
-
近似窗口:可理解为仅关注最近 11−β\frac{1}{1-\beta}1−β1 个数据的平均,例如 β=0.9\beta=0.9β=0.9 时,近似关注前10个数据; β=0.99\beta=0.99β=0.99 时,近似关注前100个数据。
2. 指数加权平均的迭代举例
以“每日温度”数据为例,假设 β=0.9\beta=0.9β=0.9 ,原始温度序列为 [25, 26, 24, 27],逐步计算迭代过程:
-
第1天(t=1):无历史平均值,初始 v0=0v_0=0v0=0 ,则 v1=0.9×0+0.1×25=2.5v_1 = 0.9 \times 0 + 0.1 \times 25 = 2.5v1=0.9×0+0.1×25=2.5 (初始值偏差大,实际中常做“偏差修正”)。
-
第2天(t=2): v2=0.9×v1+0.1×26=0.9×2.5+2.6=4.85v_2 = 0.9 \times v_1 + 0.1 \times 26 = 0.9 \times 2.5 + 2.6 = 4.85v2=0.9×v1+0.1×26=0.9×2.5+2.6=4.85 。
-
第3天(t=3): v3=0.9×v2+0.1×24=0.9×4.85+2.4=6.765v_3 = 0.9 \times v_2 + 0.1 \times 24 = 0.9 \times 4.85 + 2.4 = 6.765v3=0.9×v2+0.1×24=0.9×4.85+2.4=6.765 。
-
第4天(t=4): v4=0.9×v3+0.1×27=0.9×6.765+2.7=8.7885v_4 = 0.9 \times v_3 + 0.1 \times 27 = 0.9 \times 6.765 + 2.7 = 8.7885v4=0.9×v3+0.1×27=0.9×6.765+2.7=8.7885 。
- 偏差修正:初始阶段(t较小时),用 vt/(1−βt)v_t / (1-\beta^t)vt/(1−βt) 修正,例如 t=1 时修正为 2.5/(1−0.91)=252.5/(1-0.9^1)=252.5/(1−0.91)=25 ,避免初始值带来的偏差。
3. 代码示例(手动实现+PyTorch EMA)
import torch
import numpy as np
# 1. 手动实现指数加权平均(带偏差修正)
def exponential_weighted_average(data, beta):
v_list = [] # 存储每个时刻的平均值
v_prev = 0 # 初始平均值v0=0
for t in range(len(data)):
theta_t = data[t]
# 计算当前平均值vt
v_t = beta * v_prev + (1 - beta) * theta_t
# 偏差修正(仅前几轮需要,t从0开始计数)
v_t_corrected = v_t / (1 - beta ** (t + 1))
v_list.append(v_t_corrected)
v_prev = v_t
return v_list
# 测试:用温度数据
temperatures = [25, 26, 24, 27, 28, 25]
beta = 0.9
ewma_result = exponential_weighted_average(temperatures, beta)
print("修正后的指数加权平均值:", np.round(ewma_result, 2)) # 输出:[25. , 25.11, 24.9, 25.41, 26.07, 25.76]
# 2. PyTorch中模型参数的EMA(常用于模型保存与推理)
class EMA:
def __init__(self, model, decay=0.999):
self.model = model
self.decay = decay
# 初始化EMA权重(与原模型参数结构一致)
self.ema_weights = {name: param.clone().detach() for name, param in model.named_parameters()}
def update(self):
# 迭代更新EMA权重
for name, param in self.model.named_parameters():
if param.requires_grad:
self.ema_weights[name] = self.decay * self.ema_weights[name] + (1 - self.decay) * param.data
def apply_shadow(self):
# 将EMA权重赋给原模型(用于推理)
self.origin_weights = {name: param.clone() for name, param in self.model.named_parameters()}
for name, param in self.model.named_parameters():
param.data = self.ema_weights[name]
def restore(self):
# 恢复原模型权重(用于继续训练)
for name, param in self.model.named_parameters():
param.data = self.origin_weights[name]
# 测试EMA类
model = nn.Linear(10, 1)
ema = EMA(model, decay=0.999)
# 模拟训练:每轮训练后更新EMA
for epoch in range(10):
# (省略模型训练步骤)
ema.update()
# 推理时使用EMA权重
ema.apply_shadow()
# 推理结束后恢复原权重
ema.restore()
691

被折叠的 条评论
为什么被折叠?



