【TensorFlow深度学习】十、权重衰退(L2范数正则化)

本文介绍了在深度学习中,特别是使用TensorFlow时,如何通过权重衰减(如L2范数)来限制模型容量,防止过拟合。文章通过实例展示了不使用框架和使用框架(如Keras)实现权重衰减的方法,并观察其对模型性能的影响。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

本专栏是记录作者学习TensorFlow深度学习的相关内容

通过上一节我们可以通过调整拟合多项式的阶数来限制模型的容量。 实际上,限制特征的数量是缓解过拟合的一种常用技术。 然而,简单地丢弃特征对这项工作来说可能过于生硬

如何控制模型的容量?方法有二:1.减少模型参数 2.参数取值范围减小

本节介绍的权重衰退通过控制取值范围缓解过拟合

本节的 Jupyter 笔记本文件已上传至gitee以供大家学习交流:我的gitee仓库

1 均方范数权重衰减

这里只简单介绍一下结论,具体理论基础可以了解L1和L2范数

我们将原损失函数 L ( w , b ) L(\mathbf{w}, b) L(w,b)加上“小尾巴”
L ( w , b ) + λ 2 ∥ w ∥ 2 L(\mathbf{w}, b) + \frac{\lambda}{2} \|\mathbf{w}\|^2 L(w,b)+2λw2
加入了“尾巴”后,每一次更新权重都会对权重进行减小再更新,其中通常 η λ < = 1 \eta\lambda<=1 ηλ<=1 λ \lambda λ越大,对 w w w的约束越大
w = ( 1 − η λ ) w t − η ∂ L ( w t , b t ) ∂ w t \begin{aligned} \mathbf{w} = \left(1- \eta\lambda \right) \mathbf{w_t} - \eta\frac{\partial L(w_t,b_t)}{\partial w_t} \end{aligned} w=(1ηλ)wtηwtL(wt,bt)

2 权重衰减的实现

%matplotlib inline
import tensorflow as tf
from d2l import tensorflow as d2l

2.1 数据集

标签值是关于输入的线性函数。标签同样加入均值为0,标准差为0.01的高斯噪音。

训练集数量:20
测试集数量:100
输入特征维度:200
y = 0.05 + ∑ i = 1 d 0.01 x i + ϵ  where  ϵ ∼ N ( 0 , 0.0 1 2 ) y = 0.05 + \sum_{i = 1}^d 0.01 x_i + \epsilon \text{ where }\epsilon \sim \mathcal{N}(0, 0.01^2) y=0.05+i=1d0.01xi+ϵ where ϵN(0,0.012)
由于训练集非常少,容易出现过拟合的情况

n_train, n_test, num_inputs, batch_size = 20, 100, 200, 5
# 训练集样本数量,测试集样本数量,输入特征数量,批次大小

true_w, true_b = tf.ones((num_inputs, 1)) * 0.01, 0.05
# 真实的模型参数w和b,w是一个(num_inputs, 1)的矩阵,b为标量

train_data = d2l.synthetic_data(true_w, true_b, n_train)
# 生成模拟的训练数据集

train_iter = d2l.load_array(train_data, batch_size)
# 将训练数据集加载到迭代器中,每次返回一个批次大小的样本

test_data = d2l.synthetic_data(true_w, true_b, n_test)
# 生成模拟的测试数据集

test_iter = d2l.load_array(test_data, batch_size, is_train=False)
# 将测试数据集加载到迭代器中,每次返回一个批次大小的样本

2.2 不使用框架实现

#初始化模型的参数
def init_params():
    # 初始化模型参数,生成一个形状为(num_inputs, 1)的随机正态分布权重矩阵w
    w = tf.Variable(tf.random.normal(mean=1, shape=(num_inputs, 1)))
    # 生成一个形状为(1,)的全零向量b
    b = tf.Variable(tf.zeros(shape=(1, )))
    return [w, b]

def l2_penalty(w):
    """
    计算L2正则化惩罚项
    :param w: 权重参数
    :return: L2正则化惩罚项
    """
    return tf.reduce_sum(tf.pow(w, 2)) / 2

#训练
def train(lambd):
    w, b = init_params()
    #快速定义接受输入 X 的匿名函数,该函数使用 d2l.linreg 函数进行线性回归
    #d2l.squared_loss函数定义了损失函数
    net, loss = lambda X: d2l.linreg(X, w, b), d2l.squared_loss
    # 设置训练超参数
    num_epochs, lr = 100, 0.003
    animator = d2l.Animator(xlabel='epochs', ylabel='loss', yscale='log',
                            xlim=[5, num_epochs], legend=['train', 'test'])
    for epoch in range(num_epochs):
        for X, y in train_iter:
            #计算变量对于损失函数的梯度。
            with tf.GradientTape() as tape:
                # 增加了L2范数惩罚项,
                # 广播机制使l2_penalty(w)成为一个长度为batch_size的向量
                l = loss(net(X), y) + lambd * l2_penalty(w)
            grads = tape.gradient(l, [w, b])#计算l关于[w, b]的梯度
            d2l.sgd([w, b], grads, lr, batch_size)
        # 每5轮训练打印一次训练和测试的损失函数
        if (epoch + 1) % 5 == 0:
            animator.add(epoch + 1, (d2l.evaluate_loss(net, train_iter, loss),
                                     d2l.evaluate_loss(net, test_iter, loss)))
    print('w的L2范数是:', tf.norm(w).numpy())

2.2.1 不使用权重衰减

用lambd = 0不使用权重衰减进行训练,由于训练数据过于简单(20个),测试集上的损失值和训练集的损失值相差很大,发生了严重的过拟合。

train(lambd=0)

结果:

w的L2范数是: 16.87396

svg

2.2.2 使用权重衰减

用lambd = 3,使用权重衰减进行训练,测试集的损失值有效下降

train(lambd=3)

结果:

w的L2范数是: 0.51431155

svg

2.3使用框架实现

深度学习框架为了便于我们使用权重衰减, 将权重衰减集成到优化算法中,以便与任何损失函数结合使用

def train_concise(wd):
    # 创建一个顺序模型
    net = tf.keras.models.Sequential()
    # 添加一个全连接层,其中该层的输出节点数为1。同时,使用L2正则化方法对权重进行约束,其中正则化系数为wd
    net.add(tf.keras.layers.Dense(
        1, kernel_regularizer=tf.keras.regularizers.l2(wd)))
    # 设置模型的输入形状
    net.build(input_shape=(1, num_inputs))
    # 创建一个均方误差损失函数
    loss = tf.keras.losses.MeanSquaredError()
    # 设置训练的轮数和学习率
    num_epochs, lr = 100, 0.003
    # 创建一个随机梯度下降优化器
    trainer = tf.keras.optimizers.SGD(learning_rate=lr)
    # 创建一个动画演示对象
    animator = d2l.Animator(xlabel='epochs', ylabel='loss', yscale='log',
                            xlim=[5, num_epochs], legend=['train', 'test'])
    # 遍历训练轮数
    for epoch in range(num_epochs):
        # 遍历训练数据的批次
        for X, y in train_iter:
            # 使用梯度带自动计算损失
            with tf.GradientTape() as tape:
                # 计算损失
                l = loss(net(X), y) + net.losses
            # 计算梯度
            grads = tape.gradient(l, net.trainable_variables)
            # 使用优化器更新模型参数
            trainer.apply_gradients(zip(grads, net.trainable_variables))
        # 每5轮更新一次动画演示
        if (epoch + 1) % 5 == 0:
            animator.add(epoch + 1, (d2l.evaluate_loss(net, train_iter, loss),
                                     d2l.evaluate_loss(net, test_iter, loss)))
    # 打印模型参数w的L2范数
    print('w的L2范数:', tf.norm(net.get_weights()[0]).numpy())

其中解释一下计算损失时使用的l = loss(net(X), y) + net.losses

loss(net(X), y): 这一项表示模型在当前输入 X 下的预测结果与真实标签 y 之间的损失,这使用了均方误差损失函数。
net.losses: 这一项表示模型中的额外正则化损失,通过 net.losses 获取。在模型中,我们在第5行定义了正则化项,这些正则化项会添加到模型的 losses 列表中,然后通过 net.losses 获取并加到主要损失上。

train_concise(0)

结果:

w的L2范数: 1.2884972

svg

train_concise(3)

结果:

w的L2范数: 0.033107705

svg

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

雯雅千鶴子

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

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

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

打赏作者

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

抵扣说明:

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

余额充值