机器学习——自定义层

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

 

不带参数的层

首先,我们构造一个没有任何参数的自定义层。下面的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(7.4506e-09, grad_fn=<MeanBackward0>)

注意,torch.rand(4, 8)是一个PyTorch函数,作用是生成一个形状为(4, 8)的随机张量,元素服从[0,1)上的均匀分布

 

带参数的层

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

python
import torch
from torch import nn

class MyLinear(nn.Module):
    def __init__(self, in_features, out_features):
        super(MyLinear, self).__init__()
        # 使用内置函数创建参数
        self.weight = nn.Parameter(torch.randn(in_features, out_features)) 
        self.bias = nn.Parameter(torch.zeros(out_features))

    def forward(self, x):
        return x @ self.weight + self.bias

layer = MyLinear(3, 4)

# 保存和加载可以自动处理
torch.save(layer.state_dict(), 'path/to/params.pth')  
layer.load_state_dict(torch.load('path/to/params.pth'))

 这个自定义线性层使用了PyTorch内置的Parameter和nn.Module,可以自动管理参数初始化、保存、加载等功能。

 

import torch
from torch import nn

class MyLinear(nn.Module):
    def __init__(self, in_features, out_features):
        super(MyLinear, self).__init__()
        
        # 不使用Parameter,自行创建参数       
        self.weight = torch.randn(in_features, out_features)
        self.bias = torch.zeros(out_features)

    def forward(self, x):

        # 需要自行处理参数保存、加载等管理
        return x @ self.weight + self.bias 

layer = MyLinear(3, 4) 

# 需要自定义保存逻辑
torch.save({'weight': layer.weight, 'bias': layer.bias}, 'params.pth')

# 需要自定义加载逻辑
params = torch.load('params.pth')
layer.weight = params['weight']
layer.bias = params['bias']

 主要区别:

1. 不使用Parameter,自己定义参数tensor

2. forward函数中需要自行使用这些参数

3. 保存和加载时需要自定义处理逻辑

4. 初始化、访问等管理都需要自行实现

 

注意,nn.Linear内部会创建weight和bias的参数,被注册为Parameter。(如下)

class MLP(nn.Module):
    def __init__(self):
        super().__init__()
        self.hidden = nn.Linear(20, 256)
        self.output = nn.Linear(256, 10)

    def forward(self, x):
        return self.output(F.relu(self.hidden(x)))

 

下面来看具体实现的例子(以PyTorch为例)

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)
linear = MyLinear(5, 3)
linear.weight
Parameter containing:
tensor([[ 0.1775, -1.4539,  0.3972],
        [-0.1339,  0.5273,  1.3041],
        [-0.3327, -0.2337, -0.6334],
        [ 1.2076, -0.3937,  0.6851],
        [-0.4716,  0.0894, -0.9195]], requires_grad=True)

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

linear(torch.rand(2, 5))
tensor([[0., 0., 0.],
        [0., 0., 0.]])

 

posted @ 2023-11-03 11:13  Yohoc  阅读(9)  评论(0编辑  收藏  举报