前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >【动手学深度学习】多层感知机之权重衰减研究详情

【动手学深度学习】多层感知机之权重衰减研究详情

作者头像
SarPro
发布2024-06-06 08:25:06
900
发布2024-06-06 08:25:06
举报
文章被收录于专栏:【计网】Cisco【计网】Cisco

🌊1. 研究目的

  • 防止过拟合:权重衰减和暂退法都是用来控制模型的复杂度,防止模型在训练集上过拟合;
  • 提高模型泛化能力:通过在训练过程中应用权重衰减或暂退法,可以限制模型对训练数据的过度依赖,从而提高模型在未见过的测试数据上的泛化能力;
  • 研究正则化效果:权重衰减和暂退法都可以看作是对模型的正则化约束,通过实验可以研究不同的正则化方法对于模型训练和性能的影响;
  • 了解特征选择:通过应用权重衰减或暂退法,可以观察到一些权重变得非常小或接近于零。

🌊2. 研究准备

  • 根据GPU安装pytorch版本实现GPU运行研究代码;
  • 配置环境用来运行 Python、Jupyter Notebook和相关库等相关库。

🌊3. 研究内容

启动jupyter notebook,使用新增的pytorch环境新建ipynb文件,为了检查环境配置是否合理,输入import torch以及torch.cuda.is_available() ,若返回TRUE则说明研究​​​​​​​环境配置正确,若返回False但可以正确导入torch则说明pytorch配置成功,但研究运行是在CPU进行的,结果如下:


🌍3.1 多层感知机权重衰减

(1)使用jupyter notebook新增的pytorch环境新建ipynb文件,完成基本数据操作的研究代码与练习结果如下:

导入必要库及模型:

代码语言:javascript
复制
%matplotlib inline
import torch
from torch import nn
from d2l import torch as d2l
代码语言:javascript
复制
n_train, n_test, num_inputs, batch_size = 20, 100, 200, 5
true_w, true_b = torch.ones((num_inputs, 1)) * 0.01, 0.05
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)

初始化模型参数

代码语言:javascript
复制
def init_params():
    w = torch.normal(0, 1, size=(num_inputs, 1), requires_grad=True)
    b = torch.zeros(1, requires_grad=True)
    return [w, b]

定义L2范数惩罚

代码语言:javascript
复制
def l2_penalty(w):
    return torch.sum(w.pow(2)) / 2

定义训练代码实现

代码语言:javascript
复制
def train(lambd):
    w, b = init_params()
    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:
            # 增加了L2范数惩罚项,
            # 广播机制使l2_penalty(w)成为一个长度为batch_size的向量
            l = loss(net(X), y) + lambd * l2_penalty(w)
            l.sum().backward()
            d2l.sgd([w, b], lr, batch_size)
        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范数是:', torch.norm(w).item())

忽略正则化直接训练

代码语言:javascript
复制
train(lambd=0)

使用权重衰减

代码语言:javascript
复制
train(lambd=3)

简洁实现

代码语言:javascript
复制
def train_concise(wd):
    net = nn.Sequential(nn.Linear(num_inputs, 1))
    for param in net.parameters():
        param.data.normal_()
    loss = nn.MSELoss(reduction='none')
    num_epochs, lr = 100, 0.003
    # 偏置参数没有衰减
    trainer = torch.optim.SGD([
        {"params":net[0].weight,'weight_decay': wd},
        {"params":net[0].bias}], lr=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:
            trainer.zero_grad()
            l = loss(net(X), y)
            l.mean().backward()
            trainer.step()
        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范数:', net[0].weight.norm().item())

train_concise(0)
代码语言:javascript
复制
train_concise(3)

🌍3.2 基础练习

1.在本节的估计问题中使用λ的值进行实验。绘制训练和测试精度关于λ的函数。观察到了什么?

根据提供的代码,可以看出train_concise函数用于训练一个具有正则化的线性回归模型,并绘制训练和测试精度关于正则化参数λ的函数。

为了实验不同的λ值,可以调用train_concise函数,并将不同的λ值作为参数传递给它。在这种情况下,已经提供了两个示例:train_concise(0)和train_concise(3)。

调用train_concise(0)表示没有正则化,而调用train_concise(3)表示使用正则化参数λ的值为3。你可以尝试不同的λ值并观察结果。

以下是绘制训练和测试精度关于λ的函数的代码示例:

代码语言:javascript
复制
import torch
import torch.nn as nn
import matplotlib.pyplot as plt

def train_concise(wd):
    net = nn.Sequential(nn.Linear(num_inputs, 1))
    for param in net.parameters():
        param.data.normal_()
    loss = nn.MSELoss(reduction='none')
    num_epochs, lr = 100, 0.003
    # 偏置参数没有衰减
    trainer = torch.optim.SGD([
        {"params":net[0].weight,'weight_decay': wd},
        {"params":net[0].bias}], lr=lr)
    train_accuracy = []
    test_accuracy = []
    for epoch in range(num_epochs):
        for X, y in train_iter:
            trainer.zero_grad()
            l = loss(net(X), y)
            l.mean().backward()
            trainer.step()
        train_acc = evaluate_accuracy(net, train_iter)
        test_acc = evaluate_accuracy(net, test_iter)
        train_accuracy.append(train_acc)
        test_accuracy.append(test_acc)
    print('w的L2范数:', net[0].weight.norm().item())
    return train_accuracy, test_accuracy

def evaluate_accuracy(net, data_iter):
    correct = 0
    total = 0
    for X, y in data_iter:
        output = net(X)
        predicted = (output > 0.5).float()
        correct += (predicted == y).sum().item()
        total += y.size(0)
    return correct / total

def plot_accuracy_lambda(lambda_values):
    for lambda_val in lambda_values:
        train_accuracy, test_accuracy = train_concise(lambda_val)
        plt.plot(range(len(train_accuracy)), train_accuracy, label='Train Accuracy (lambda={})'.format(lambda_val))
        plt.plot(range(len(test_accuracy)), test_accuracy, label='Test Accuracy (lambda={})'.format(lambda_val))
    plt.xlabel('Epochs')
    plt.ylabel('Accuracy')
    plt.legend()
    plt.show()

# 定义 num_inputs, train_iter, test_iter,这些变量应该在你的代码中有定义
lambda_values = [0, 0.5, 1, 2, 3, 4, 5]
plot_accuracy_lambda(lambda_values)

在上述代码中,lambda_values列表包含了不同的λ值。train_concise函数的输出被修改为返回训练和测试的精度。plot_accuracy_lambda函数使用这些值来绘制训练和测试精度关于λ的函数图像。

通过调用plot_accuracy_lambda(lambda_values),可以观察到不同λ值对训练和测试精度的影响。

2.使用验证集来找到最佳值λ。它真的是最优值吗?这有关系吗?

使用验证集来找到最佳正则化参数λ是一种常见的模型选择方法。在训练过程中,可以尝试不同的λ值,并使用验证集来评估模型在不同λ值下的性能。最终,选择在验证集上性能最好的λ值作为模型的最终正则化参数。

然而,需要注意的是,通过验证集来选择最佳λ并不一定意味着这是绝对最优的值。这是因为验证集是从训练数据中独立出来的,使用它来估计模型在未见过的数据上的泛化能力。因此,验证集中的性能评估并不总是能够完全反映模型在真实数据上的表现。

过度依赖验证集来选择最佳参数可能导致过拟合验证集的问题,也称为“验证集泄漏”。简单来说,当我们反复调整模型或超参数,直到在验证集上得到理想结果时,可能会选择那些在验证集上仅仅是运气好的模型,而这些模型未必在真实数据上表现优秀。

为了解决验证集泄漏的问题,通常采用交叉验证的方法。交叉验证将数据集划分为多个不相交的子集,并多次训练模型,每次使用一个不同的子集作为验证集,其他子集作为训练集。这样可以得到更稳健的性能评估,并减少验证集泄漏的影响。

另外,最终模型的性能并不仅仅取决于正则化参数λ。其他因素,如模型的复杂性、训练数据的质量和数量等,也会影响最终的模型性能。因此,在选择λ时,应该将它作为一个超参数,同时考虑其他超参数和模型选择中的不确定性。

3.如果我们使用

$ \sum _ { i } | \omega _ { i } |$
$ \sum _ { i } | \omega _ { i } |$

作为我们选择的惩罚(

$ L _ { 1 }$
$ L _ { 1 }$

正则化),那么更新方程会是什么样子?

如果使用L1正则化(使用L1范数作为惩罚项),则更新方程会发生变化。正则化项被添加到损失函数中,以对权重进行约束。

在标准的梯度下降算法中,我们通过在梯度更新中加入正则化项来实现L1正则化。更新方程如下:

$ \omega i \leftarrow \omega i - \eta ^ { * } ( \partial L / \partial \omega i + \lambda ^ { * } \sin ( \omega i ) )$
$ \omega i \leftarrow \omega i - \eta ^ { * } ( \partial L / \partial \omega i + \lambda ^ { * } \sin ( \omega i ) )$

其中:

ω𝑖 是权重的第i个元素 𝜂 是学习率(控制更新的步长) 𝜆 是L1正则化参数(控制正则化的强度) 𝐿 是损失函数 ∂𝐿/∂ω𝑖 是损失函数关于权重ω𝑖的梯度 sign(ω𝑖) 是ω𝑖的符号函数(正数为1,负数为-1,零为0) 更新方程的第二项 𝜆 * sign(ω𝑖) 是L1正则化的关键部分。它对权重进行约束,使得某些权重变为零,从而实现特征选择和稀疏性。

需要注意的是,L1正则化会导致权重稀疏化,即某些权重变为零。这对于特征选择和模型解释性非常有用,可以通过选择重要的特征来提高模型的泛化能力。

更新方程的具体实现可能因使用的深度学习框架而有所不同,但基本的思想是相同的。

4.我们知道

$ | | w ^ { 2 } | | w ^ { T } w $
$ | | w ^ { 2 } | | w ^ { T } w $

。能找到类似的矩阵方程吗(见 2.3.10节 中的Frobenius范数)?

在2.3.10节中,Frobenius范数被定义为矩阵的元素平方和的平方根,即 ||𝑊||_F = sqrt(∑𝑖∑𝑗𝑤_𝑖𝑗^2) ,其中𝑤_𝑖𝑗是矩阵𝑊的元素。

如果希望使用Frobenius范数作为惩罚项来约束权重𝑊,我们可以修改更新方程来实现。对于𝑊的更新方程,可以使用以下形式:

$ W \leftarrow W - \eta ^ { * } ( d L / d W + \lambda ^ { * } W )$
$ W \leftarrow W - \eta ^ { * } ( d L / d W + \lambda ^ { * } W )$

其中:

𝑊 是权重矩阵 𝜂 是学习率(控制更新的步长) 𝜆 是Frobenius范数的正则化参数(控制正则化的强度) 𝐿 是损失函数 𝑑𝐿/𝑑𝑊 是损失函数关于𝑊的梯度 更新方程的第二项 𝜆 * 𝑊 是Frobenius正则化的关键部分。它对权重矩阵𝑊进行约束,使得权重的大小受到限制。

需要注意的是,Frobenius正则化并不会导致权重的稀疏性,它主要用于控制权重的大小。相比于L1正则化和L2正则化,Frobenius正则化在深度学习中的应用相对较少,通常更常见的是使用L1正则化或L2正则化来约束权重。

5.回顾训练误差和泛化误差之间的关系。除了权重衰减、增加训练数据、使用适当复杂度的模型之外,还能想出其他什么方法来处理过拟合?

除了权重衰减、增加训练数据和使用适当复杂度的模型之外,还有一些其他方法可以处理过拟合问题。以下是一些常见的方法:

  • 早停(Early Stopping):在训练过程中,通过监测验证误差的变化,当验证误差不再下降或开始上升时,及时停止训练,防止过拟合。这可以通过保存在训练过程中得到的最佳模型来实现。
  • 数据增强(Data Augmentation):通过对训练数据进行随机变换或扩增,生成新的训练样本。例如,图像分类任务中的随机裁剪、翻转、旋转、缩放等操作可以增加数据的多样性,减少过拟合的风险。
  • Dropout:在训练过程中,以一定的概率随机将神经元的输出置为零,从而随机地丢弃一些神经元,减少神经网络的复杂性。Dropout可以减少神经网络中神经元之间的依赖关系,有助于防止过拟合。
  • 正则化方法:除了权重衰减,还有其他正则化方法可以应用于神经网络。例如,L1正则化、L2正则化、弹性网络(Elastic Net)等,它们通过在损失函数中添加额外的正则化项,限制模型参数的大小,减少过拟合的风险。
  • 模型集成(Model Ensemble):将多个模型的预测结果进行集成,可以通过投票、平均等方式得到最终的预测结果。模型集成可以减少模型的方差,提高模型的泛化能力。
  • 正交化(Orthogonalization):将模型的学习过程分解为两个独立的步骤,一步用于优化训练数据上的训练误差,另一步用于优化验证数据上的泛化误差。这种方法可以帮助确保模型不仅仅是过拟合训练数据,而是能够在未见过的数据上表现良好。

这些方法可以单独使用,也可以结合使用,具体取决于数据和模型的特点。在实际应用中,需要根据具体问题和数据集的特点来选择适合的方法来处理过拟合问题。

6.在贝叶斯统计中,我们使用先验和似然的乘积,通过公式P(w|x)正比于P(x|w)P(x)得到后验。如何得到带正则化的P(w)

在贝叶斯统计中,通常使用正则化项来引入先验概率P(w)。正则化项对参数w的取值进行限制,有助于防止过拟合,特别是在数据较少或特征较多的情况下。

假设我们的模型是一个参数为w的概率模型,数据为x。在贝叶斯统计中,要求参数w的后验概率P(w|x),即给定数据x条件下参数w的概率分布。根据贝叶斯定理,后验概率可以表示为:

P(w|x) ∝ P(x|w) * P(w)

其中,P(x|w)是似然函数,表示在给定参数w下观测数据x的概率;P(w)是先验概率,表示在未观测数据之前参数w的概率分布。

为了引入正则化项,我们可以假设参数w的先验概率P(w)服从某种特定分布,通常我们会选择一个具有特定性质的分布,比如高斯分布。例如,对于L2正则化,我们可以假设参数w的先验概率P(w)服从一个高斯分布,即:

P(w) = N(0, λ^2 * I)

其中,N(0, λ^2 * I)表示均值为0,方差为λ^2的多元高斯分布,I是单位矩阵。

对于L1正则化,我们可以假设参数w的先验概率P(w)服从一个拉普拉斯分布,即:

P(w) = Laplace(0, λ)

其中,Laplace(0, λ)表示均值为0,尺度参数为λ的拉普拉斯分布。

在引入正则化项后,求解参数w的后验概率P(w|x)时,需要将先验概率P(w)乘以似然函数P(x|w),然后归一化,以得到正确的后验概率分布。具体求解过程可以使用贝叶斯推断方法,如马尔可夫链蒙特卡洛(MCMC)等。

总结:在贝叶斯统计中,通过引入正则化项,可以在参数估计过程中考虑先验信息,对参数进行约束,有助于改善模型的泛化能力和鲁棒性。


🌊4. 研究体会

通过这次研究​​​​​​​,我深入学习了多层感知机,解决了分类和回归等问题。在本次实验中,使用Python编写了多层感知机模型,并分别应用了权重衰减和暂退法来观察它们对模型性能的影响。

首先,实现了一个简单的多层感知机模型,包括输入层、隐藏层和输出层。为了进行实验,选择了一个经典的分类问题数据集,并将其划分为训练集和测试集。接着定义了损失函数和优化器,并使用反向传播算法来更新模型的权重和偏置。

接下来,开始尝试权重衰减技术。在训练过程中,我引入了一个权重衰减项,它会惩罚大的权重值。通过调整权重衰减系数,我观察到模型在训练集和测试集上的表现。实验结果显示,适当的权重衰减可以有效减少过拟合,提高模型的泛化能力。

通过本次实验,我深刻理解了权重衰减对于多层感知机模型的重要性和影响。权重衰减技术可以通过惩罚大的权重值来控制模型的复杂度,防止过拟合;而暂退法技术可以通过逐渐减小学习率来提高模型的稳定性和收敛速度。

通过观察模型的权重变化,我发现权重衰减对特征选择起到了一定的作用。在应用权重衰减后,一些权重值会趋近于零或变得非常小,这意味着这些特征对于模型的决策贡献较小。因此,可以根据权重的大小进行特征选择,从而提高模型的解释性和效果。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2024-06-06,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 🌊1. 研究目的
  • 🌊2. 研究准备
  • 🌊3. 研究内容
    • 🌍3.1 多层感知机权重衰减
      • 🌍3.2 基础练习
      • 🌊4. 研究体会
      相关产品与服务
      腾讯云服务器利旧
      云服务器(Cloud Virtual Machine,CVM)提供安全可靠的弹性计算服务。 您可以实时扩展或缩减计算资源,适应变化的业务需求,并只需按实际使用的资源计费。使用 CVM 可以极大降低您的软硬件采购成本,简化 IT 运维工作。
      领券
      问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档