一般情况下,模型参数在被创建时就被立即初始化了,但如果使用了延后初始化技术,就能在首次传入数据后,再初始化参数,旨在输入维度未知的情况下,预定义灵活的模型,动态推断各个层的参数大小。
有时我们会遇到或要自己发明一个现在在深度学习框架中还不存在的层。 在这些情况下,必须构建自定义层。本节将展示如何构建自定义层。
1 延后初始化的使用场景
- 动态输入形状:在某些情况下,模型的输入形状可能不是固定的,而是动态变化的。例如,在自然语言处理任务中,输入句子的长度可能会有所不同。
- 简化模型定义:延后初始化可以简化模型的定义,避免在模型构建阶段手动指定每个层的输入和输出形状。
2 pytorch实现延后初始化
在 PyTorch 中,可以通过 torch.nn.Lazy
模块来实现延后初始化。torch.nn.Lazy
模块提供了一些常见的层,如 LazyLinear
、LazyConv2d
等,这些层在第一次前向传播时自动确定参数的形状。
以下是一个简单的示例,展示了如何使用 torch.nn.LazyLinear 进行延后初始化:
import torch
import torch.nn as nnclass LazyNet(nn.Module):def __init__(self):super(LazyNet, self).__init__()self.lazy_linear = nn.LazyLinear(out_features=10)# 没有指定输出维度,只指定了输出维度def forward(self, x):x = self.lazy_linear(x)return x# 创建模型实例
net = LazyNet()# 创建一个输入张量,假设输入形状为 (batch_size, input_features)
input_data = torch.randn(32, 100)# 第一次前向传播,此时会自动确定 `LazyLinear` 层的输入形状
output = net(input_data)print(output.shape) # 输出形状为 (32, 10)
运行结果:
3 自定义层
3.1 不带参数的层
我们构造一个没有任何参数的自定义层,下面的CenteredLayer类要从其输入中减去均值。 要构建它,我们只需继承基础层类并实现前向传播功能。
import torch
import torch.nn.functional as F
from torch import nnclass 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]))
现在,我们可以将层作为组件合并到更复杂的模型中。作为额外的健全性检查,我们可以在向该网络发送随机数据后,检查均值是否为0。 由于我们处理的是浮点数,因为存储精度的原因,我们仍然可能会看到一个非常小的非零数。
net = nn.Sequential(nn.Linear(8, 128), CenteredLayer())
Y = net(torch.rand(4, 8))
Y.mean()
3.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.datareturn F.relu(linear)
接下来实例化模型,并访问其参数:
linear = MyLinear(5, 3)
linear.weight
我们可以使用自定义层直接执行前向传播计算。
linear(torch.rand(2, 5))
我们还可以使用自定义层构建模型,就像使用内置的全连接层一样使用自定义层。
net = nn.Sequential(MyLinear(64, 8), MyLinear(8, 1))
net(torch.rand(2, 64))
我们可以通过基本层类设计自定义层。这允许我们定义灵活的新层