深度学习计算 - 延后初始化&自定义层

延后初始化

到目前为止,我们忽略了建立网络时需要做的以下这些事情:

  • 我们定义了网络架构,但没有指定输入维度
  • 我们添加层时没有指定前一层的输出维度
  • 我们在初始化参数时,甚至没有足够的信息来确定模型应该包含多少参数

你可能会对我们的代码你能允许感到惊讶。毕竟,深度学习框架无法判断网络的输入维度是上面。这里的诀窍是框架的延后初始化(defers initialization),即直到数据第一次通过模型传递时,框架才会动态地推断出每个层的大小

在以后,当使用卷积神经网络时,由于输入维度(即图像的分辨率)将影响每个后续层的维数,有了该技术将更加方便。现在我们在编写代码时无需知道维度是什么就可以设置参数,这种能力可以大大简化定义和修改模型的任务。接下来,我们将更深入地研究初始化机制

1 - 实例化网络

首先,让我们实例化一个多层感知机

此时,因为输入维数是未知的,所以网络不可能知道输入层权重的维数。因此,框架尚未初始化任何参数,我们通过尝试访问以下参数进行确认。

接下来让我们将数据通过网络,最终使框架初始化参数

一旦我们知道输入维数是20,框架可以通过待入值20来是被第一层权重矩阵的形状。识别出第一层的形状后,框架处理第二层,依次类推,直到所有形状都已知为止。注意,在这种情况下,只有第一层需要延迟初始化,但是框架仍是按顺序初始化的,等到知道了所有的参数形状,框架就可以初始化参数

2 - 小结

  • 延后初始化使框架能够自动推断参数形状,使修改模型架构变得容易,避免了一些常见的错误
  • 我们可以通过模型传递数据,使框架最终初始化参数

自定义层

深度学习成功背后的一个因素是神经网络的灵活性:我们可以用创造性的方式组合不同的层,从而设计出适用于各种任务的架构。例如,研究人员发明了专门用于处理图像、文本、序列数据和执行动态规划的层。未来,你会遇到或要自己发明一个现在在深度学习框架中不存在的层。在这些情况下,你必须构建自定义层

在本节中,我们将向你展示如何构建

1 - 不带参数的层

首先,我们构造一个没有任何参数的自定义层。下面的CenteredLayer类要从其输入中减去均值。要构建它,我们只需继承基础层并实现前向传播功能

import torch
import torch.nn.functional as F
from torch import nn

class CenteredLayer(nn.Module):
    def __init__(self):
        super().__init__()
        
    def forward(self, X):
        return X - X.mean()

让我们向该层提供一些数据,验证它是否能按预期工作

layer = CenteredLayer()
layer(torch.FloatTensor([1,2,3,4,5]))
tensor([-2., -1.,  0.,  1.,  2.])

现在,我们可以将层作为组件合并到更复杂的模型中

net = nn.Sequential(nn.Linear(8,128),CenteredLayer())

作为额外的健全性检查,我们可以在向该网络发生随机数据后,检查均值是否为0,由于我们处理的是浮点数,因为存储精度的原因,我们仍然可能会看到一个非常小的非零数

Y = net(torch.rand(4,8))
Y.mean()
tensor(0., grad_fn=<MeanBackward0>)

2 - 带参数的层

我们继续定义具有参数的层,这些参数可以通过训练进行调整。我们可以使用内置函数来创建参数,这些函数提供一些基本的管理功能。比如管理访问、初始化、共享、保存和加载模型参数。这样做的好处之一是:我们不需要为每个自定义层编写自定义序列化程序

现在,让我们实现自定义版本的全连接层。回想一下,该层需要两个参数,一个用于表示权重,另一个用于表示偏置项。在此实现中,我们使用修正线性单元作为激活函数

该层需要输入参数:in_units和units,分别表示输入数和输出数

class MyLinear(nn.Module):
    def __init__(self,in_units,units):
        super().__init__()
        self.weight = nn.Parameter(torch.randn(in_units,units))
        self.bias = nn.Parameter(torch.randn(units,))
    
    def forward(self,X):
        linear = torch.matmul(X,self.weight.data) + self.bias.data
        return F.relu(linear)

接下来,我们实例化MyLinear类并访问其模型参数

linear = MyLinear(5,3)
linear.weight
Parameter containing:
tensor([[-0.6740,  2.6241,  0.1015],
        [ 0.2607,  1.2663,  1.7803],
        [-0.4661,  0.6814, -0.2714],
        [-0.1859,  2.0384, -1.0564],
        [ 1.4791,  0.6188, -0.9626]], requires_grad=True)

我们还可以使用自定义层直接执行前向传播计算

linear(torch.rand(2,5))
tensor([[1.4987, 2.9976, 0.0664],
        [1.2634, 2.8115, 0.0000]])

我们还可以使用自定层构建模型,就像使用内置的全连接层一样使用自定层

net = nn.Sequential(MyLinear(64,8),MyLinear(8,1))
net(torch.rand(2,64))
tensor([[ 9.3809],
        [23.3433]])

3 - 小结

  • 我们可以通过基本层类设计自定义层。这允许我们定义灵活的新层,其行为与深度学习框架中的任何现有层不同
  • 在自定层定义完成后,我们就可以在任意在环境和网络架构中调用该自定层
  • 层可以有局部参数,这些参数可以通过内置函数创建
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值