Skip to content

自定义层

一、什么是自定义层

深度学习框架里给我们提供了很多现成的层,比如全连接层、卷积层这些,但有时候我们需要一个特殊功能的层,比如一个能把输入减去它的均值的层,或者一个自己设计的全连接层,这时候就需要自己写一个自定义层。自定义层可以让我们的模型更灵活,满足我们的特殊需求,就像我们自己做了一个专门的工具,用来处理特定的任务。

二、不带参数的自定义层

首先我们先写一个最简单的自定义层,这个层不需要训练参数,它的功能就是把输入减去它的均值。

代码示例(以 PyTorch 为例)

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

# 定义一个自定义层,继承nn.Module
class CenteredLayer(nn.Module):
    def __init__(self):
        super().__init__()  # 调用父类的初始化函数

    def forward(self, X):
        # 前向传播:把输入X减去它的均值
        return X - X.mean()

我们可以测试一下这个层的效果:

python
# 创建这个层的实例
layer = CenteredLayer()
# 给它输入一个数组[1,2,3,4,5]
print(layer(torch.tensor([1, 2, 3, 4, 5])))

运行结果是tensor([-2., -1., 0., 1., 2.]),这个结果是对的,因为 [1,2,3,4,5] 的均值是 3,每个数减去 3 就得到了这个结果。

我们还可以把这个层和其他层一起用,比如放到 Sequential 里:

python
# 定义一个模型,先放一个全连接层,再放我们的自定义层
net = nn.Sequential(nn.Linear(8, 128), CenteredLayer())
# 给模型输入一个随机数据
X = torch.rand(4, 8)
Y = net(X)
# 看看输出的均值是不是接近0
print(Y.mean())

运行结果大概是tensor(-1.1921e-07, grad_fn=<MeanBackward0>),非常接近 0,说明我们的自定义层起作用了,把全连接层的输出减去了它的均值。

三、带参数的自定义层

有时候我们需要一个有可训练参数的层,比如自己实现一个全连接层,这个层有权重和偏置这两个可训练的参数,就像框架里的全连接层一样。

代码示例(以 PyTorch 为例)

python
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):
        # 前向传播:先做矩阵乘法,加上偏置,再用ReLU激活
        linear = torch.matmul(X, self.weight) + self.bias
        return F.relu(linear)

我们可以测试一下这个层的效果:

python
# 创建这个层的实例,输入维度是5,输出维度是3
linear = MyLinear(5, 3)
# 看看这个层的权重参数
print(linear.weight)
# 给它输入一个2×5的随机数据
print(linear(torch.rand(2, 5)))

运行结果里,权重参数是一个 5×3 的张量,输出是一个 2×3 的张量,说明这个层可以正常工作。

我们还可以把这个层放到 Sequential 里组成一个模型:

python
# 定义一个模型,先放我们的自定义全连接层(输入64,输出8),再放一个自定义全连接层(输入8,输出1)
net = nn.Sequential(MyLinear(64, 8), MyLinear(8, 1))
# 给模型输入一个2×64的随机数据
print(net(torch.rand(2, 64)))

运行结果是一个 2×1 的张量,说明这个模型可以正常工作。

四、小结

  1. 自定义层可以让我们实现框架里没有的特殊功能,让模型更灵活。

  2. 不带参数的自定义层只需要继承框架的层类,实现前向传播函数就行,不需要定义可训练的参数。

  3. 带参数的自定义层需要用nn.Parameter来定义可训练的参数,这些参数会在训练的时候被更新。

  4. 自定义层可以和框架里的内置层一起用,比如放到 Sequential 里组成模型。

五、练习

  1. 设计一个层,这个层的作用是计算输入张量的降维,输出y_k = ∑_{i,j} W_{ijk} x_i x_j,也就是把输入的两个元素相乘,再乘以权重,然后加起来。

  2. 设计一个层,这个层的作用是返回输入数据的傅里叶系数的前半部分,也就是对输入做傅里叶变换后,只返回前一半的结果。

(注:文档部分内容可能由 AI 生成) 源地址

京ICP备2024093538号-1