DIDL笔记(pytorch版)(五)

文章目录

    • 模型构造
      • 方法一:继承nn.Module类(常用)
      • Module类的子类
        • Sequentail类
        • ModuleList类
        • ModuleDict类
    • 模型参数的访问、初始化和共享
      • 访问
      • 初始化
        • 自定义初始化
      • 共享
    • 自定义层
    • 存储和读取
      • 读写tensor
      • 读写模型
    • pytorch使用GPU

模型构造

方法一:继承nn.Module类(常用)

nn.Module类是所有神经网络模块的基类。用这么类构造模型需要重载__init__函数和forward函数(正向传播)。文中说这个类无须定义反向传播函数,因为系统会自动生成,其解释看下面参考。

参考:https://zhuanlan.zhihu.com/p/37213786
https://www.cnblogs.com/luckyplj/p/13378293.html

虽然最后的结果会grad_fn会显示AddmmBackward,但是这个并没有实现我们想要的backward效果。而且backward()也不在nn.Module类里面了。

import torch
from torch import nn

class MLP(nn.Module):
    def __init__(self, **kwargs):
        super(MLP, self).__init__(**kwargs)
        self.hidden = nn.Linear(784, 256) # 隐藏层
        self.act = nn.ReLU()
        self.output = nn.Linear(256, 10)  # 输出层

    # 定义模型的前向计算,即如何根据输入x计算返回所需要的模型输出
    def forward(self, x):
        a = self.act(self.hidden(x))
        return self.output(a)
net = MLP()
print(net)

Module类的子类

Sequentail类

类似于上面,只是一种更简洁的写法,需要按照执行顺序排列,保证输入输出大小匹配,forward已经实现

net = nn.Sequential(
        nn.Linear(784, 256),
        nn.ReLU(),
        nn.Linear(256, 10), 
        )
print(net)

ModuleList类

存储各个网络模块的列表,没有联系也没顺序,需要自己实现forward功能

net = nn.ModuleList([nn.Linear(784, 256), nn.ReLU()])
net.append(nn.Linear(256, 10)) # # 类似List的append操作
print(net[-1])  # 类似List的索引访问
print(net)

ModuleDict类

接受字典作为输入,只是存放,需要自己实现forward功能

net = nn.ModuleDict({
    'linear': nn.Linear(784, 256),
    'act': nn.ReLU(),
})
net['output'] = nn.Linear(256, 10) # 添加
print(net['linear']) # 访问
print(net.output)
print(net)

模型参数的访问、初始化和共享

访问

对于Sequential实例中含模型参数的层,我们可以通过Module类的parameters()或者named_parameters()方法来访问所有参数(以迭代器的形式返回),后者除了返回参数Tensor外还会返回其名字。
torch.nn.parameter.Parameter,其实这是Tensor的子类,和Tensor不同的是如果一个Tensor是Parameter,那么它会自动被添加到模型的参数列表里。

class MyModel(nn.Module):
    def __init__(self, **kwargs):
        super(MyModel, self).__init__(**kwargs)
        self.weight1 = nn.Parameter(torch.rand(20, 20))	# 加入网络
        self.weight2 = torch.rand(20, 20)	# 并没有
    def forward(self, x):
        pass

n = MyModel()
for name, param in n.named_parameters():
    print(name)

初始化

nn中导入init模块,想不教育之间的初始化,init模块包含的初始化方法多种多样。

for name, param in net.named_parameters():
    if 'bias' in name:
        init.constant_(param, val=0)	# 常数
        print(name, param.data)
	if 'weight' in name:	
        init.normal_(param, mean=0, std=0.01)	# 正态分布
        print(name, param.data)

自定义初始化

我们可以参考torch.nn.init.normal_来发现初始化的结构,以实现自定义初始化。

def normal_(tensor, mean=0, std=1):
    with torch.no_grad():
        return tensor.normal_(mean, std)

需要no_grad()并且原地操作。

def init_weight_(tensor):
    with torch.no_grad():
        tensor.uniform_(-10, 10)
        tensor *= (tensor.abs() >= 5).float()

for name, param in net.named_parameters():
    if 'weight' in name:
        init_weight_(param)
        print(name, param.data)
        
for name, param in net.named_parameters():
    if 'bias' in name:
        param.data += 1		# param也是一个leaf,并且我们原地操作了
        print(name, param.data)

共享

在建立模型的时候,重复使用同一层就建立模型共享了。内存里面其实是同一个对象,并且反向传播的时候,共享的参数也是累积的。

linear = nn.Linear(1, 1, bias=False)
net = nn.Sequential(linear, linear) 
print(net)
for name, param in net.named_parameters():
    init.constant_(param, val=3)
    print(name, param.data)

自定义层

Module带有各种层,但是我们可以自定义层,但是还是要继承Module类型才行。该层是否有参数由自己决定,可以通过nn.Parameter()nn.ParamenterList()nn.ParameterDict()来定义。
List的新增仍然是append(),Dict新增用updata()

import torch
from torch import nn


class Mylayer(nn.Module):
    def __init__(self, **kwargs):
        super(Mylayer, self).__init__(**kwargs)
        # 决定该层是否有参数,可以用nn.parameter类给该层添加参数
        # self.params = nn.ParameterList([nn.Parameter(torch.randn(4, 4)) for i in range(3)])
        # self.params2 = nn.Parameter(torch.randn(4, 1))
        # self.params3 = nn.ParameterDict({
        #    'w': nn.Parameter(torch.randn(4, 4)),
        #    'b': nn.Parameter(torch.randn(4, 1))
        #     })
		# self.params2.append(nn.Parameter(torch.randn(4, 1)))
		# self.params3.update({'linear3': nn.Parameter(torch.randn(4, 2))}) # 新增
    def forward(self, x):
        # for i in range(len(self.params)):
        #    x = torch.mm(x, self.params[i])
        return x
        
net = nn.Sequential(nn.Linear(4, 32), Mylayer())
print(net)

存储和读取

读写tensor

import torch
from torch import nn

x = torch.ones(3)
torch.save(x, 'x.pt')	# 保存
torch.save([x, y], 'xy.pt')
torch.save({'x': x, 'y': y}, 'xy_dict.pt')
x2 = torch.load('x.pt')	# 读取

读写模型

如果训练好的模型,不可能每一次用测试都要我们再训练一次,这时要把参数or模型保存。state_dict()函数是一个从参数名称隐射到参数Tesnor的字典对象,用它可以保存模型参数。除了层,优化器也有state_dict条目。

# 保存参数
torch.save(model.state_dict(), "./net.pt") # 推荐的文件后缀名是pt或pth
# 加载参数
model = TheModelClass(*args, **kwargs)
model.load_state_dict(torch.load(PATH))
# 保存模型
torch.save(model, PATH)
# 加载模型
model = torch.load(PATH)

pytorch使用GPU

torch.cuda.is_available() # 查看是否有可用的GPU
torch.cuda.device_count() # 查看GPU数量
torch.cuda.current_device() # 查看当前GPU索引号,从0开始
torch.cuda.get_device_name(0) # 根据索引号查看GPU名字
x = x.cuda(0)	# tensor放到GPU上
x.device		# 查看该Tensor所在的设备
# 创建时就制定设备
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
x = torch.tensor([1, 2, 3], device=device)
# or
x = torch.tensor([1, 2, 3]).to(device)
# 模型也可以放到GPU上,数据和模型要在同一个设备上才行,否则会报错
net = nn.Linear(3, 1)
net.cuda()

你可能感兴趣的:(深度学习,深度学习,模型,pytorch)