Appearance
自定义层
一、什么是自定义层
深度学习框架里给我们提供了很多现成的层,比如全连接层、卷积层这些,但有时候我们需要一个特殊功能的层,比如一个能把输入减去它的均值的层,或者一个自己设计的全连接层,这时候就需要自己写一个自定义层。自定义层可以让我们的模型更灵活,满足我们的特殊需求,就像我们自己做了一个专门的工具,用来处理特定的任务。
二、不带参数的自定义层
首先我们先写一个最简单的自定义层,这个层不需要训练参数,它的功能就是把输入减去它的均值。
代码示例(以 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 的张量,说明这个模型可以正常工作。
四、小结
自定义层可以让我们实现框架里没有的特殊功能,让模型更灵活。
不带参数的自定义层只需要继承框架的层类,实现前向传播函数就行,不需要定义可训练的参数。
带参数的自定义层需要用
nn.Parameter来定义可训练的参数,这些参数会在训练的时候被更新。自定义层可以和框架里的内置层一起用,比如放到 Sequential 里组成模型。
五、练习
设计一个层,这个层的作用是计算输入张量的降维,输出
y_k = ∑_{i,j} W_{ijk} x_i x_j,也就是把输入的两个元素相乘,再乘以权重,然后加起来。设计一个层,这个层的作用是返回输入数据的傅里叶系数的前半部分,也就是对输入做傅里叶变换后,只返回前一半的结果。
(注:文档部分内容可能由 AI 生成) 源地址