机器学习——自定义层
深度学习成功背后的一个因素是神经网络的灵活性: 我们可以用创造性的方式组合不同的层,从而设计出适用于各种任务的架构。 例如,研究人员发明了专门用于处理图像、文本、序列数据和执行动态规划的层。 有时我们会遇到或要自己发明一个现在在深度学习框架中还不存在的层。 在这些情况下,必须构建自定义层。本节将展示如何构建自定义层。
不带参数的层
首先,我们构造一个没有任何参数的自定义层。下面的CenteredLayer
类要从其输入中减去均值。 要构建它,我们只需继承基础层类并实现前向传播功能。
1 2 3 4 5 6 7 8 9 10 11 | 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() |
1 2 | layer = CenteredLayer() layer(torch.FloatTensor([ 1 , 2 , 3 , 4 , 5 ])) |
tensor([-2., -1., 0., 1., 2.])
现在,我们可以将层作为组件合并到更复杂的模型中。
1 | net = nn.Sequential(nn.Linear( 8 , 128 ), CenteredLayer()) |
作为额外的健全性检查,我们可以在向该网络发送随机数据后,检查均值是否为0。 由于我们处理的是浮点数,因为存储精度的原因,我们仍然可能会看到一个非常小的非零数。
1 2 | Y = net(torch.rand( 4 , 8 )) Y.mean() |
tensor(7.4506e-09, grad_fn=<MeanBackward0>)
注意,torch.rand(4, 8)
是一个PyTorch函数,作用是生成一个形状为(4, 8)的随机张量,元素服从[0,1)上的均匀分布。
带参数的层
以上我们知道了如何定义简单的层,下面我们继续定义具有参数的层, 这些参数可以通过训练进行调整。 我们可以使用内置函数来创建参数,这些函数提供一些基本的管理功能。 比如管理访问、初始化、共享、保存和加载模型参数。 这样做的好处之一是:我们不需要为每个自定义层编写自定义的序列化程序。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | 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,可以自动管理参数初始化、保存、加载等功能。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | 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。(如下)
1 2 3 4 5 6 7 8 | 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为例)
1 2 3 4 5 6 7 8 | 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) |
1 2 | 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)
我们可以使用自定义层直接执行前向传播计算。
1 | linear(torch.rand( 2 , 5 )) |
tensor([[0., 0., 0.],
[0., 0., 0.]])
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)
2022-11-03 1049 数列的片段和
2022-11-03 iomanip库中的常用函数