1 前言: 如何自定义一个模型-通过继承nn.Module 类来实现,在__init__ 构造函数中申明各个层的定义,在forward 中实现层直接的连接关系,实际上就是前向传播的过程。
实际上,在pytorch 里面自定义层也是通过继承nn.Module 类来实现,pytorch 里面一般没有层的概念,层也是当成一个模型来处理的。
2 torch 里面实现神经网络有两种方法:
(1) 高层API 方式: 高层API 是使用类的形式来包装的,类可以存储参数,例如全连接层的权重值矩阵,偏置矩阵等都可以作为类的属性存储着,但是低层API 仅仅是实现函数的运算功能,没有办法保存这些信息。
3 自定义层的步骤
要实现一个自定义层大概分以下几个主要的步骤:
(1) 自定义一个类:继承自Module类,并且一定要实现两个基本的函数,第一是构造函数__init__,第二个是层的逻辑运算函数,即所谓的前向计算函数forward 函数。
(2)在构造函数__init__ 中实现层的参数定义。比如Linear 层的权重和偏置,Conv2d 层的in_channels, out_channels, kernel_size, stride=1,padding=0, dilation=1, groups=1,bias=True, padding_mode='zeros'这一系列参数。
(3) 在前向传播forward 函数里面实现前向运算。这一般都是通过torch.nn.funtional, ** 函数来实现。
(4)一般情况下,我们定义的参数是可以求导的,但是自定义操作如不可导,需要实现backward 函数。
4 自定义层的简单例子
要实现一个简单的层,这个层的功能是y=w*sqrt(x2+bias)
即输入X 的平方再加上一个偏执项,再开根号,然后再乘以权重矩阵w,。按照上面的定义过程,先定义一个这样的层(即一个类),代码如下:
2.1 定义一个自定义层MyLayer
# 定义一个 my_layer.py
import torch
class MyLayer(torch.nn.Module):
'''
因为这个层实现的功能是:y=weights*sqrt(x2+bias),所以有两个参数:
权值矩阵weights
偏置矩阵bias
输入 x 的维度是(in_features,)
输出 y 的维度是(out_features,) 故而
bias 的维度是(in_fearures,),注意这里为什么是in_features,而不是out_features,注意体会这里和Linear层的区别所在
weights 的维度是(in_features, out_features)注意这里为什么是(in_features, out_features),而不是(out_features, in_features),注意体会这里和Linear层的区别所在
'''
def __init__(self, in_features, out_features, bias=True):
super(MyLayer, self).__init__() # 和自定义模型一样,第一句话就是调用父类的构造函数
self.in_features = in_features
self.out_features = out_features
self.weight = torch.nn.Parameter(torch.Tensor(in_features, out_features)) # 由于weights是可以训练的,所以使用Parameter来定义
if bias:
self.bias = torch.nn.Parameter(torch.Tensor(in_features)) # 由于bias是可以训练的,所以使用Parameter来定义
else:
self.register_parameter('bias', None)
def forward(self, input):
input_=torch.pow(input,2)+self.bias
y=torch.matmul(input_,self.weight)
return y
2.2 自定义模型并且训练
import torch
from my_layer import MyLayer # 自定义层
N, D_in, D_out = 10, 5, 3 # 一共10组样本,输入特征为5,输出特征为3
# 先定义一个模型
class MyNet(torch.nn.Module):
def __init__(self):
super(MyNet, self).__init__() # 第一句话,调用父类的构造函数
self.mylayer1 = MyLayer(D_in,D_out)
def forward(self, x):
x = self.mylayer1(x)
return x
model = MyNet()
print(model)
'''运行结果为:
MyNet(
(mylayer1): MyLayer() # 这就是自己定义的一个层
)
'''
下面开始训练
# 创建输入、输出数据
x = torch.randn(N, D_in) #(10,5)
y = torch.randn(N, D_out) #(10,3)
#定义损失函数
loss_fn = torch.nn.MSELoss(reduction='sum')
learning_rate = 1e-4
#构造一个optimizer对象
optimizer = torch.optim.Adam(model.parameters(), lr=learning_rate)
for t in range(10): #
# 第一步:数据的前向传播,计算预测值p_pred
y_pred = model(x)
# 第二步:计算计算预测值p_pred与真实值的误差
loss = loss_fn(y_pred, y)
print(f"第 {t} 个epoch, 损失是 {loss.item()}")
# 在反向传播之前,将模型的梯度归零,这
optimizer.zero_grad()
# 第三步:反向传播误差
loss.backward()
# 直接通过梯度一步到位,更新完整个网络的训练参数
optimizer.step()
输出:
第 0 个epoch, 损失是 42.33689498901367
第 1 个epoch, 损失是 42.3133430480957
第 2 个epoch, 损失是 42.289825439453125
第 3 个epoch, 损失是 42.26634979248047
第 4 个epoch, 损失是 42.2429084777832
第 5 个epoch, 损失是 42.21950912475586
第 6 个epoch, 损失是 42.19615173339844
第 7 个epoch, 损失是 42.17283248901367
第 8 个epoch, 损失是 42.14955520629883
第 9 个epoch, 损失是 42.12631607055664
本文的实践说明了如何使用Module 父类来拓展实现自定义模型,自定义层,两者有异曲同工之处。后面会继续讲解通过Function 来自定义一个层。需要注意的是:
Function 与Module 都可以对pytorch 进行自定义拓展,使其满足网络的需求,但这两者还是有十分重要的不同,具体的下次再说。
ref:https://blog.csdn.net/qq_27825451/article/details/90705328