深度学习神经网络基础知识(二)权重衰减、暂退法(Dropout)

news2024/12/26 0:10:45

专栏:神经网络复现目录

深度学习神经网络基础知识(二)

本文讲述神经网络基础知识,具体细节讲述前向传播,反向传播和计算图,同时讲解神经网络优化方法:权重衰减,Dropout等方法,最后进行Kaggle实战,具体用一个预测房价的例子使用上述方法。

文章部分文字和代码来自《动手学深度学习》


文章目录

  • 深度学习神经网络基础知识(二)
    • 范数
    • 权重衰减
      • 定义
      • 权重衰减的从零实现
      • 运行结果
      • 权重衰减的简洁实现
    • 暂退法(Dropout)
      • 定义
      • 暂退法的从零实现
      • 运行结果
      • 暂退法的简洁实现


范数

L p L_p Lp范数是一种向量范数,定义如下:

∣ x ∣ p = ( ∣ x 1 ∣ p + ∣ x 2 ∣ p + ⋯ + ∣ x n ∣ p ) 1 p \left|\boldsymbol{x}\right|{p}=\left(\left|x{1}\right|^{p}+\left|x_{2}\right|^{p}+\cdots+\left|x_{n}\right|^{p}\right)^{\frac{1}{p}} xp=(x1p+x2p++xnp)p1

其中, p ≥ 1 p \geq 1 p1 x = ( x 1 , x 2 , ⋯   , x n ) \boldsymbol{x}=(x_1, x_2, \cdots, x_n) x=(x1,x2,,xn) 是一个 n n n 维向量。当 p = 2 p=2 p=2 时, L p L_p Lp范数也称为欧几里得范数(Euclidean norm),常用于表达向量的长度或者大小。当 p = 1 p=1 p=1 时, L p L_p Lp范数也称为曼哈顿范数(Manhattan norm)或者 ℓ 1 \ell_1 1范数,常用于表达向量中各个元素的绝对值之和。当 p → ∞ p \rightarrow \infty p 时, L p L_p Lp范数也称为切比雪夫范数(Chebyshev norm)或者 ℓ ∞ \ell_\infty 范数,常用于表达向量中绝对值最大的元素。

L 0 L_0 L0范数不是向量范数,因为它并不满足向量范数的三个条件之一,即正定性。通常把向量 x \boldsymbol{x} x 中非零元素的个数称为 x \boldsymbol{x} x L 0 L_0 L0 范数,但这并不是一个数学上合理的定义。

常见的范数有以下几种:

L 1 L^1 L1 范数: ∣ ∣ x ∣ ∣ 1 = ∑ i = 1 n ∣ x i ∣ ||x||1 = \sum{i=1}^n |x_i| ∣∣x∣∣1=i=1nxi

L 2 L^2 L2 范数: ∣ ∣ x ∣ ∣ 2 = ∑ i = 1 n x i 2 ||x||2 = \sqrt{\sum{i=1}^n x_i^2} ∣∣x∣∣2=i=1nxi2

权重衰减

定义

权重衰减是一种用于降低过拟合的正则化技术。其原理是通过在模型训练过程中增加一个惩罚项(也称作正则化项),来抑制模型的复杂度,从而达到减小过拟合的效果。

具体来说,在损失函数中添加一个正则化项,一般会对模型的参数进行 L 2 L_2 L2范数的约束,也就是让模型的参数尽量小。这样,在模型训练过程中,不仅会尽量减小训练数据的损失,还会尽量让模型参数的平方和小,从而达到抑制模型过拟合的效果。

权重衰减的损失函数为:
在这里插入图片描述
其中 L ( w , b ) \mathcal{L}(\boldsymbol{w}, b) L(w,b) 是原始的无正则化项的损失函数, ∣ w ∣ 2 |\boldsymbol{w}|^2 w2 表示模型参数的 L 2 L_2 L2范数, λ \lambda λ 是正则化强度, n n n 是训练样本数。

在优化算法中,我们需要对这个损失函数进行梯度下降。由于正则化项的梯度为 λ n w \frac{\lambda}{n}\boldsymbol{w} nλw,因此我们需要对原始的梯度加上这个正则化项的梯度:

w ← ( 1 − η λ ∣ B ∣ ) w − η ∣ B ∣ ∑ i ∈ B ∂ ∂ w l ( i ) ( w , b ) w \leftarrow (1 - \frac{\eta \lambda}{|B|})w - \frac{\eta}{|B|} \sum_{i \in B} \frac{\partial}{\partial w} l^{(i)}(w, b) w(1Bηλ)wBηiBwl(i)(w,b)

其中, w w w是待更新的权重参数, η \eta η是学习率, λ \lambda λ是正则化超参数(即权重衰减超参数), ∣ B ∣ |B| B是当前小批量中的样本数, l ( i ) ( w , b ) l^{(i)}(w, b) l(i)(w,b)是第 i i i个样本的损失函数, ∂ ∂ w l ( i ) ( w , b ) \frac{\partial}{\partial w} l^{(i)}(w, b) wl(i)(w,b)是对权重参数的损失函数梯度。

权重衰减的从零实现

构造生成数据集的函数

%matplotlib inline
import torch
from torch import nn
from d2l import torch as d2l
#生成数据集
def synthetic_data(w,b,num):
    #x通过正态分布生成
    x=torch.normal(0,1,(num,len(w)))
    y=torch.matmul(x,w)+b
    #数据集中加入噪声
    y+=torch.normal(0,0.01,y.shape)
    return x,y.reshape(-1,1)

构造一个数据迭代器

def load_array(data_arrays, batch_size, is_train=True):  #@save
    """构造一个PyTorch数据迭代器"""
    dataset = data.TensorDataset(*data_arrays)
    return data.DataLoader(dataset, batch_size, shuffle=is_train)

生成数据集

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 = synthetic_data(true_w, true_b, n_train)
train_iter = load_array(train_data, batch_size)
test_data = synthetic_data(true_w, true_b, n_test)
test_iter = load_array(test_data, batch_size, is_train=False)

初始化模型参数

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范数惩罚

def l2_penalty(w):
    return torch.sum(w.pow(2)) / 2

训练

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())

运行结果

未使用权重衰减
在这里插入图片描述
使用权重衰减
在这里插入图片描述

权重衰减的简洁实现

def train_concise(weight_decay):
    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  = optim.SGD(model.parameters(), lr=lr, weight_decay=weight_decay)
    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())

关注这行代码

trainer  = optim.SGD(model.parameters(), lr=lr, weight_decay=weight_decay)

其中weight_decay参数即为lambda

暂退法(Dropout)

定义

Dropout是一种用于神经网络的正则化技术,旨在减少模型的过拟合。该算法的核心思想是在网络的训练过程中随机“丢弃”一部分神经元,从而强制模型学习更加鲁棒和通用的特征。在测试时,所有神经元都保留,但是输出值需要乘以一个固定比例以保持期望输出不变。

具体来说,假设我们有一个包含 L L L个层的神经网络。对于第 i i i层,它的输出为 h ( i ) h^{(i)} h(i)。在训练时,我们按照一定的概率 p p p来随机选择一部分神经元,将它们的输出值设置为0。因此,第 i i i层的输出为:

h ~ ( i ) = r ( i ) ⊙ h ( i ) \tilde{h}^{(i)}=r^{(i)}\odot h^{(i)} h~(i)=r(i)h(i)

其中 r ( i ) r^{(i)} r(i)是一个与 h ( i ) h^{(i)} h(i)具有相同形状的二进制向量,其中元素值为1的概率为 p p p,值为0的概率为 1 − p 1-p 1p ⊙ \odot 表示按元素相乘。在前向传播过程中,我们使用 h ~ ( i ) \tilde{h}^{(i)} h~(i)代替 h ( i ) h^{(i)} h(i)进行计算。在反向传播过程中,由于某些神经元的输出被设置为0,我们只需要将其对应的梯度清零即可。

在测试时,我们需要保留所有神经元的输出,但是为了保持期望输出不变,我们需要将所有神经元的输出值乘以 p p p,即:

h t e s t ( i ) = p ⋅ h ( i ) h^{(i)}_{test}=p\cdot h^{(i)} htest(i)=ph(i)

下图形象的展示了暂退法的效果:
在这里插入图片描述

暂退法的从零实现

这是一个实现dropout算法的函数,它接受一个输入张量X和一个dropout概率dropout,然后返回一个应用了dropout的输出张量。

具体来说,该函数会生成一个与X形状相同的掩码张量,其中每个元素都是随机生成的0或1,生成方式是根据概率dropout与0比较,如果大于dropout则为1,否则为0。然后将掩码张量与X相乘并除以(1 - dropout),这个操作相当于将保留下来的元素值除以它们的概率。最后返回应用了dropout的输出张量。

import torch
from torch import nn
from d2l import torch as d2l


def dropout_layer(X, dropout):
    assert 0 <= dropout <= 1
    # 在本情况中,所有元素都被丢弃
    if dropout == 1:
        return torch.zeros_like(X)
    # 在本情况中,所有元素都被保留
    if dropout == 0:
        return X
    mask = (torch.rand(X.shape) > dropout).float()
    return mask * X / (1.0 - dropout)

具体关注一下:

mask = (torch.rand(X.shape) > dropout).float()

这一行代码的作用是生成一个与X形状相同的张量mask,并且其中的每个元素都是0或1。这里的0和1表示相应的X元素是否被保留,而生成这些0和1的方式是随机的,因为我们用torch.rand()函数生成一个形状与X相同的随机张量,并将其中的每个元素与dropout做比较。

比较的结果是一个布尔类型的张量,即对于X中的每个元素,如果随机生成的相应元素的值大于dropout,那么在mask中相应位置的值为1,表示保留;反之,如果随机生成的值小于等于dropout,那么在mask中相应位置的值为0,表示丢弃。

最后,为了保持期望的值不变,我们将所有保留的元素的值除以 1- dropout,这是因为被保留的概率是1- dropout。所以,最终得到的输出是一个X的掩码版本,其中的一些元素被随机置为零。

测试一下我们写的dropout层

X= torch.arange(16, dtype = torch.float32).reshape((2, 8))
print(X)
print(dropout_layer(X, 0.))
print(dropout_layer(X, 0.5))
print(dropout_layer(X, 1.))

定义模型参数

num_inputs, num_outputs, num_hiddens1, num_hiddens2 = 784, 10, 256, 256

定义模型
这次我们使用了和以往不同、面向对象的模型定义方式,需要重写__init__和forward函数

init 方法用于定义网络结构,包括网络层、激活函数、损失函数等,并初始化权重、偏差等参数。这些网络参数在训练过程中会不断地更新。

forward 方法用于定义数据在网络中的正向传播(也就是模型从输入到输出的计算过程),即输入数据经过网络的各层计算,最终得到输出。在该方法中,我们可以任意组合各种网络层及其参数,实现自己所需要的网络结构和计算过程。

在下面的代码中,Net 类继承自 nn.Module,其中 init 方法用于定义网络的结构,包括三个全连接层和一个 ReLU 激活函数。forward 方法用于实现数据在网络中的正向传播计算,包括将输入 X 经过全连接层和激活函数得到输出 out。在训练模式中,还会在第一个全连接层和第二个全连接层后面添加 dropout 层。

dropout1, dropout2 = 0.2, 0.5

class Net(nn.Module):
    def __init__(self, num_inputs, num_outputs, num_hiddens1, num_hiddens2,
                 is_training = True):
        super(Net, self).__init__()
        self.num_inputs = num_inputs
        self.training = is_training
        self.lin1 = nn.Linear(num_inputs, num_hiddens1)
        self.lin2 = nn.Linear(num_hiddens1, num_hiddens2)
        self.lin3 = nn.Linear(num_hiddens2, num_outputs)
        self.relu = nn.ReLU()

    def forward(self, X):
        H1 = self.relu(self.lin1(X.reshape((-1, self.num_inputs))))
        # 只有在训练模型时才使用dropout
        if self.training == True:
            # 在第一个全连接层之后添加一个dropout层
            H1 = dropout_layer(H1, dropout1)
        H2 = self.relu(self.lin2(H1))
        if self.training == True:
            # 在第二个全连接层之后添加一个dropout层
            H2 = dropout_layer(H2, dropout2)
        out = self.lin3(H2)
        return out


net = Net(num_inputs, num_outputs, num_hiddens1, num_hiddens2)

训练和测试

num_epochs, lr, batch_size = 10, 0.5, 256
loss = nn.CrossEntropyLoss(reduction='none')
train_iter, test_iter = d2l.load_data_fashion_mnist(batch_size)
trainer = torch.optim.SGD(net.parameters(), lr=lr)
d2l.train_ch3(net, train_iter, test_iter, loss, num_epochs, trainer)

运行结果

在这里插入图片描述

暂退法的简洁实现

net = nn.Sequential(nn.Flatten(),
        nn.Linear(784, 256),
        nn.ReLU(),
        # 在第一个全连接层之后添加一个dropout层
        nn.Dropout(dropout1),
        nn.Linear(256, 256),
        nn.ReLU(),
        # 在第二个全连接层之后添加一个dropout层
        nn.Dropout(dropout2),
        nn.Linear(256, 10))

def init_weights(m):
    if type(m) == nn.Linear:
        nn.init.normal_(m.weight, std=0.01)

net.apply(init_weights);

或者是

class Net(nn.Module):
    def __init__(self, input_size, hidden_size, output_size, dropout_prob):
        super(Net, self).__init__()
        self.fc1 = nn.Linear(input_size, hidden_size)
        self.fc2 = nn.Linear(hidden_size, hidden_size)
        self.fc3 = nn.Linear(hidden_size, output_size)
        self.dropout = nn.Dropout(p=dropout_prob)

    def forward(self, x):
        x = torch.relu(self.fc1(x))
        x = self.dropout(x)
        x = torch.relu(self.fc2(x))
        x = self.dropout(x)
        x = self.fc3(x)
        return x

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/358141.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

一次简陋的页面登录练习

看着有点丑&#xff0c;果然我还是不太适合写前端哈<!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta http-equiv"X-UA-Compatible" content"IEedge"><meta name"viewport&q…

2023年,Java岗大厂的架构师之路该怎样走?互联网寒冬能否有所好转?

大厂还值得去吗&#xff1f;程序员这个行业是不是不行了&#xff1f;我还有必要继续学习吗&#xff1f;答案显然是否定的&#xff0c;其实在行业里面很多年的程序员们都知道&#xff0c;大厂每年都会裁员&#xff0c;只不过是裁的数量多少而已&#xff0c;而大规模的裁员&#…

我的周刊(第079期)

我的信息周刊&#xff0c;记录这周我看到的有价值的信息&#xff0c;主要针对计算机领域&#xff0c;内容主题极大程度被我个人喜好主导。这个项目核心目的在于记录让自己有印象的信息做一个留存以及共享。&#x1f3af; 项目ChatGPT 项目推荐基于 ChatGPT 相关接口衍生的项目&…

计算机网络学习笔记02

学习视频&#xff1a;https://www.bilibili.com/video/BV1c4411d7jb/?p7&spm_id_frompageDriver&vd_source75dce036dc8244310435eaf03de4e330 一、计算机网络体系结构 1 常见的计算机网络体系结构 OSI体系结构和TCP/IP体系结构 TCP/IP体系结构的网络接口层并没有规…

【笔试强训】Day_02

目录 一、选择题 1、 2、 3、 4、 5、 6、 7、 8、 9、 10、 二、编程题 1、排序子序列 2、倒置字符串 一、选择题 1、 使用printf函数打印一个double类型的数据&#xff0c;要求&#xff1a;输出为10进制&#xff0c;输出左对齐30个字符&#xff0c;4位精度。…

@Valid与@Validated的区别

1.介绍 说明&#xff1a; 其实Valid 与 Validated都是做数据校验的&#xff0c;只不过注解位置与用法有点不同。 不同点&#xff1a; &#xff08;1&#xff09; Valid是使用Hibernate validation的时候使用。Validated是只用Spring Validator校验机制使用。 &#xff08;2&…

Mysql里的ibtmp1文件太大,导致磁盘空间被占满

目录 一、查看磁盘的时候发现磁盘空间100% 二、 排查的时候&#xff1a;查看是什么文件占用的时候&#xff0c;发现是数据库临时表空间增长的 三、为了避免以后再次出现ibtmp1文件暴涨&#xff0c;限制其大小&#xff0c;需在配置文件加入 四、重启Mysql实例&#xff08;重启后…

6 大经典机器学习数据集,3w+ 用户票选得出,建议收藏

内容一览&#xff1a;本期汇总了超神经下载排名众多的 6 个数据集&#xff0c;涵盖图像识别、机器翻译、遥感影像等领域。这些数据集质量高、数据量大&#xff0c;经历人气认证值得收藏码住。 关键词&#xff1a;数据集 机器翻译 机器视觉 数据集是机器学习模型训练的基础&…

【电商】OMS--零售电商系统的核心

本文讲述了OMS概念以及相关服务和功能&#xff08;包括&#xff1a;信息下发、信息上传、 订单分发协同单号生成与拉、拆单发票服务、状态更新与模板、流水、库存&#xff09; OMS即&#xff1a;订单管理中心&#xff0c;是零售电商系统的核心。 随着中台概念的火热&#xff0…

graphsage解读

传统的图方法都是直推式(transductive)的&#xff0c;学习到的是结构固定的图模型&#xff0c;一旦有新的节点加入&#xff0c;便需要重新训练整个图网络&#xff0c;泛化性不强。GraphSAGE是归纳式(inductive)的&#xff0c;它学习一种映射&#xff1a;通过采样和聚合邻居节点…

ModStartBlog v6.7.0 后台管理优化,页面宽度调整

ModStart 是一个基于 Laravel 模块化极速开发框架。模块市场拥有丰富的功能应用&#xff0c;支持后台一键快速安装&#xff0c;让开发者能快的实现业务功能开发。 系统完全开源&#xff0c;基于 Apache 2.0 开源协议。 功能特性 丰富的模块市场&#xff0c;后台一键快速安装会…

二叉树OJ(二)二叉树中和为某一值的路径 I、II、III

二叉树中和为某一值的路径(一) 描述 给定一个二叉树root和一个值 sum &#xff0c;判断是否有从根节点到叶子节点的节点值之和等于 sum 的路径。 1.该题路径定义为从树的根结点开始往下一直到叶子结点所经过的结点 2.叶子节点是指没有子节点的节点 3.路径只能从父节点到子节点&…

对JS文件中每个函数单独混淆加密

自动化脚本&#xff1a;单独对JS代码中的每个函数进行混淆加密 用自动化脚本工具&#xff0c;对JS代码中的每个函数分别进行单独混淆加密。这样加密得到的JS代码&#xff0c;比直接对整个JS代码进行混淆加密&#xff0c;效果要好很多。所谓“好”体现在&#xff1a;加密结果安…

Interspeech2022 | 一种基于元辅助学习的低资源口语语义理解方法

中国移动研究院首席科学家冯俊兰博士带领人工智能与智慧运营中心语音团队共同撰写的文章《Meta Auxiliary Learning for Low-resource Spoken Language Understanding》被语音国际顶会Interspeech2022接收。 关于Interspeech Interspeech 是国际最大且最全面关于言语科学与技…

QT for Android BLE Bluetooch QT BLE

小白式的介绍&#xff0c;很详细了&#xff0c;很多主要内容写在程序的注释里&#xff0c;慢慢看 下面是我的源码 https://download.csdn.net/download/qq_27620407/87464307 源码打不开的话可以试试下图的操作&#xff0c;之后电机确定&#xff0c;可能是加图标搞的&#xff0…

自监督表征预训练之掩码图像建模

自监督表征预训练之掩码图像建模 前言 目前&#xff0c;在计算机视觉领域&#xff0c;自监督表征预训练有两个主流方向&#xff0c;分别是对比学习&#xff08;contrastive learning&#xff09;和掩码图像建模&#xff08;masked image modeling&#xff09;。两个方向在近几…

CLEVE:事件抽取的对比预训练

CLEVE: Contrastive Pre-training for Event Extraction 论文&#xff1a;CLEVE: Contrastive Pre-training for Event Extraction (arxiv.org) 代码&#xff1a;THU-KEG/CLEVE: Source code for ACL 2021 paper “CLEVE: Contrastive Pre-training for Event Extraction” (g…

初探Upgrade内存马(内存马系列篇六)

写在前面 前面讲解了一个特殊的Tomcat内存马-Executor内存马&#xff0c;这篇同样是一个特殊&#xff0c;可以不被检测到的内存马-Upgrade内存马。 这篇就是内存马系列文章的第六篇了。 前置 在阅读这篇文章之前&#xff0c;同样需要对Tomcat的架构&#xff0c;和初始化流程…

秒懂算法 | 莫队算法

01、基础莫队算法 莫队算法 = 离线 + 暴力 + 分块。 “离线”和“在线”的概念。在线是交互式的,一问一答;如果前面的答案用于后面的提问,称为“强制在线”。离线是非交互的,一次性读取所有问题,然后一起回答,"记录所有步,回头再做”。 基础的莫队算法是一种离线…

[Java安全]—Shiro回显内存马注入

文章目录前言流程分析寻找response流程分析获取Http11Processor获取AbstractProtocol获取Connector获取WebappClassLoaderHeader 长度限制绕过1、反射修改maxHeaderSize2、自定义ClassLoader加载Body数据后记参考前言 接上篇[Java安全]—Tomcat反序列化注入回显内存马_&#x…