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)
类似于上面,只是一种更简洁的写法,需要按照执行顺序排列,保证输入输出大小匹配,forward已经实现。
net = nn.Sequential(
nn.Linear(784, 256),
nn.ReLU(),
nn.Linear(256, 10),
)
print(net)
存储各个网络模块的列表,没有联系也没顺序,需要自己实现forward功能。
net = nn.ModuleList([nn.Linear(784, 256), nn.ReLU()])
net.append(nn.Linear(256, 10)) # # 类似List的append操作
print(net[-1]) # 类似List的索引访问
print(net)
接受字典作为输入,只是存放,需要自己实现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)
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)
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()