动手学深度学习5.3 PyTorch教程 自定义层

参与11月更文挑战的第20天,活动详情查看:2021最后一次更文挑战


上一小节讲的是怎么自定义初始化参数。动手学深度学习5.2 PyTorch教程 参数初始化 - 掘金 (juejin.cn)

这一节是看怎么自定义层。

这样可以想一下之前接触的楼层是什么。比如nn.Linear,nn.ReLU等。他们的作用就是作为某一层的处理。他们两个的区别在于前者有参数,后者是没有参数列表的。那现在我们也来实现一些有参数和没有参数列表的层操作。

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()
X = torch.arange(5)*0.1
print(layer(X))
复制代码
>>
tensor([-0.2000, -0.1000,  0.0000,  0.1000,  0.2000])
复制代码

经过测试我们可以看到这个层是完全有效的。

那如果将其放到复杂的模型之中呢。

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

Y = torch.rand(10, 8)
print(net(Y).mean().data)
复制代码
>>
tensor(7.8231e-09)
复制代码

好吧,这个模型其实并不复杂,它只有两层。第一个是一个线性层。第二个就是我们的自定义层。

生成一组随机的测试数据Y。然后使用我们构建的网络对外进行计算,然后输出其结果的平均值。

不出意外结果应该是0。虽然这里显示的不是0。这是因为浮点数的存储精度问题,你当然可以把这个极小的数近似看作它是0。

至于结果为什么失灵,这是一个数学问题,会去列几个数字自己算一下就明白了。

带参数的层

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

这个租赁一层是自定义实现了一个全链接层。这个层里的参数需要用到权重和偏置,在计算之后最后返回再使用ReLU激活函数。

linear = MyLinear(5, 3)
print(linear.weight.data)
复制代码
>>
tensor([[ 1.0599,  0.3885,  1.2025],
        [-1.8313,  0.2097, -1.6529],
        [ 1.4119,  0.2675, -0.4148],
        [ 0.2596, -0.0319,  1.9548],
        [-1.2874,  1.0776,  0.5804]])
复制代码

输出它的权重看一下,确实是能生成5×3的权重矩阵。

X = torch.rand(2, 5)
linear(X)
复制代码
>>
tensor([[2.3819, 2.3819, 2.3819],
        [1.8295, 1.8295, 1.8295]])
复制代码

单层测试结果也没有问题。

net = nn.Sequential(MyLinear(64, 8), MyLinear(8, 1))
net(torch.rand(2, 64))
复制代码
>>
tensor([[0.4589],
        [0.0000]])
复制代码

将其放在网络中结果也没有问题。

现在我来放一段对比代码,就是我们自己写的这个层和pytorch人家写的层该怎么实现同样的功能。

net1 = nn.Sequential(MyLinear(64, 8), MyLinear(8, 1))
net2 = nn.Sequential(nn.Linear(64,8),
                     nn.ReLU(),
                     nn.Linear(8,1),
                     nn.ReLU())

def init(m):
    if type(m)==nn.Linear:
        nn.init.ones_(m.weight)
        nn.init.zeros_(m.bias)
net2.apply(init)

Y = torch.rand(4, 64)


print(net1(Y).data)
print(net2(Y).data)

复制代码
>>
tensor([[270.5055],
        [253.7892],
        [238.7834],
        [258.4998]])
tensor([[270.5055],
        [253.7892],
        [238.7834],
        [258.4998]])
复制代码

这样乍一看是不是两个结果完全一样。

相对于pytorch自带的实现来说,这个不需要你写一个加权重的过程,也不需要你再加一个ReLU层。

这样看起来很省事,但是实际中不建议你自己实现pytorch之中已经有的功能。因为使用人家的方法计算效率更高。


  1. 《动手学深度学习》系列更多可以看这里:《动手学深度学习》专栏(juejin.cn)

  2. 笔记Github地址:DeepLearningNotes/d2l(github.com)

还在更新中…………

Guess you like

Origin juejin.im/post/7032647621210537992