nn.Sequential,torch.nn.Module和torch.autograd.Function

参考自:https://blog.csdn.net/qq_27825451/category_8866856.html

 

对于简单模型,我们可以使用torch.nn.Sequential类来实现简单的顺序连接模型,这个模型也是继承自Module类的。

https://blog.csdn.net/qq_27825451/article/details/90551513

对于稍复杂的模型,需自己来定义一个模型或者自定义层来实现了

pytorch中其实一般没有特别明显的Layer和Module的区别,不管是自定义层、自定义块、自定义模型,都是通过继承Module类完成的,这一点很重要。其实Sequential类也是继承自Module类的。

torch里面实现神经网络有两种方式

(1)高层API方法:使用torch.nn.****来实现;

(2)低层API方法:使用低层函数方法,torch.nn.functional.****来实现;

推荐使用高层API的方法,原因如下:

高层API是使用类的形式来包装的,既然是类就可以存储参数,比如全连接层的权值矩阵、偏置矩阵等都可以作为类的属性存储着,但是低层API仅仅是实现函数的运算功能,没办法保存这些信息,会丢失参数信息,但是高层API是依赖于低层API的计算函数的,比如上面的两个层:

Linear高级层——>低层F.linear() 函数

Conv2d高级层——>低层F.conv2d()函数

 

自定义层的步骤

要实现一个自定义层大致分以下几个主要的步骤:

(1)自定义一个类,继承自Module类:构造函数__init__;  逻辑运算函数,即所谓的前向计算函数forward函数

(2)在构造函数_init__中实现层的参数定义,如Conv2d层的in_channels, out_channels等

(3)在前向传播forward函数里面实现前向运算,只要在nn.Module的子类中定义了forward()函数,backward()函数就会被自动实现。

          

注意:1.构造函数主要用于定义参数,forward函数表示层的前向传播逻辑

           2.注意forward()函数定义的是批数据的前向传播逻辑。

           3.一般情况下,我们定义的参数是可以求导的,但是自定义操作如不可导要实现backward函数。

总结:这里其实和定义一个自定义模型是一样的,核心都是实现最基本的构造函数__init__和前向运算函数forward函数

二、自定义层的简单例子

一个简单的层实现功能y=w*sqrt(x^2+bias),

2.1.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.1.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()   # 这就是自己定义的一个层
)
'''

注意:此时,将没有训练参数的层没有放在构造函数里面了,所以这些层就不会出现在model里面,但是运行关系是在forward里面通过functional的方法实现的。

总结:所有放在构造函数__init__里面的层的都是这个模型的“固有属性”.

2.1.3 下面开始训练

# 创建输入、输出数据
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()

 

2.2 再看一个例子。用自定义层实现如下功能:输入为两个N维向量x_{1_{}}x_{2},参数为一个N\times N的矩阵M,输出为label=sigmoid(x_{1}\times M\times x_{2})

#-*-coding:utf-8-*-
"""
@author:taoshouzheng
@time:2019/7/1 10:37
@email:tsz1216@sina.com
"""
 
import torch
import torch.nn as nn
import numpy as np
from torch.autograd import Variable
import math
from torch import optim
import torch.utils.data as Data
 
 
# 定义DisMult层
class DisMult(nn.Module):
	def __init__(self, emb_size):
 
		# nn.Module子类的函数必须在构造函数中执行父类的构造函数
		# 下式等价于nn.Module.__init__(self)
		super(DisMult, self).__init__()
		# 隐特征维度
		self.emb_size = emb_size
		# 关系特定的方阵
		# self.weights = nn.Parameter(torch.Tensor(emb_size, emb_size), requires_grad=requires_grad)
		self.weights = nn.Parameter(torch.Tensor(emb_size, emb_size))
		# 初始化参数
		self.reset_parameters()
 
	# 初始化参数
	def reset_parameters(self):
		stdv = 1. / math.sqrt(self.weights.size(0))
		self.weights.data.uniform_(-stdv, stdv)
 
	# 前向传播函数:注意这里定义的是批的处理逻辑
	def forward(self, input1, input2):
		result = []
		for i in range(input1.size(0)):
			result_i = input1[i] @ self.weights @ (input2[i].t())
			result.append(result_i)
 
		return torch.sigmoid(torch.DoubleTensor(result))
 
 
# 定义DisMult模型
class DisMultModel(nn.Module):
	def __init__(self, hidden_dim):
		super(DisMultModel, self).__init__()
		self.hidden_dim = hidden_dim
		# 模型中加入一层DisMult层
		self.dismult = DisMult(hidden_dim)
 
	def forward(self, x_a, x_b):
		return self.dismult(x_a, x_b)
 
 
# 定义网络
model = DisMultModel(hidden_dim=50)
 
# 打印初始化参数
for item in model.named_parameters():
	print(item)
 
# 定义损失函数和优化器
criterion = nn.BCELoss(reduction='sum')		# 二分类交叉熵损失函数
optimizer = optim.SGD(model.parameters(), lr=0.001, momentum=0.9)		# 优化器
 
# 训练网络:输入数据、前向传播 + 反向传播、更新参数
 
# 超参数
EPOCH = 100		# 迭代轮数
BATCH_SIZE = 100		# 批大小
 
 
input_a = []
input_b = []
label = []
 
with open(r'E:\Experiment\数据预处理\仿真数据\relation_1_train.csv', 'r', encoding='utf8') as f:
	contents = f.readlines()
	for content in contents:
		content = content.strip().split('\t')
		# inputs.append([content[0].strip().split(' '), content[1].strip().split(' ')])
		input_a.append(content[0].strip().split(' '))
		input_b.append(content[1].strip().split(' '))
		label.append(content[2])
 
# numpy数组
inputs = np.array([input_a, input_b], dtype=np.float)		# 实数类型
 
label = np.array(label, dtype=np.long)
 
# Tensor
inputs = torch.FloatTensor(inputs)
 
label = torch.DoubleTensor(label)
 
# 先转化成torch能识别的Dataset
torch_dataset = Data.TensorDataset(inputs[0], inputs[1], label)
 
# dataset放到DataLoader中
loader = Data.DataLoader(dataset=torch_dataset, batch_size=BATCH_SIZE, shuffle=True)
 
# 训练
for epoch in range(EPOCH):
 
	# 每一步loader释放一小批数据用于训练
	for step, (batch_inputs_1, batch_inputs_2, batch_label) in enumerate(loader):
 
		# 输入数据
		input_1 = Variable(batch_inputs_1)
		input_2 = Variable(batch_inputs_2)
		label = Variable(batch_label)
 
		# 梯度清零
		optimizer.zero_grad()
 
		# 前向传播 + 反向传播
		output = model(input_1, input_2)		# 计算输出
 
		loss = criterion(output, label)		# 计算损失函数
		loss.requires_grad = True
 
		print('Epoch', epoch + 1,  'Step', step + 1, ':', loss.item())
 
		loss.backward()		# 反向传播
 
		# 更新参数
		optimizer.step()
 
# 打印训练后的参数
for item in model.named_parameters():
	print(item)

 

总结:

PyTorch中的Layer和Module没有明显的区别,不管是自定义层、自定义块,还是自定义模型,都是通过继承Module类完成的,PyTorch中的一切自定义操作基本上都是通过继承nn.Module类来实现的。

但是要特别注意:Sequential类虽然继承自Module类,二者有相似部分,但是也有很多不同的部分,集中体现在:

Sequenrial类实现了整数索引,故而可以使用model[index] 这样的方式获取一个层,但是Module类并没有实现整数索引,不能够通过整数索引来获得层,那该怎么办呢?它提供了几个主要的方法,如下:
 

def children(self):
 
def named_children(self):
 
def modules(self):
 
def named_modules(self, memo=None, prefix=''):
 
'''
注意:这几个方法返回的都是一个Iterator迭代器,故而通过for循环访问,当然也可以通过next
'''

参考:https://blog.csdn.net/qq_27825451/article/details/90550890
原文链接:https://blog.csdn.net/tszupup/article/details/95452976
原文链接:https://blog.csdn.net/qq_27825451/article/details/90705328

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值