文章目录
安装
torch安装
- 安装命令以及对应版本参考torch official
tensorboard安装
安装相关
pip install tensorboard==1.14.0
debug:如果出现
ValueError: Not a TBLoader or TBPlugin subclass: ......
When using tensorboard to view the neural network.
pip list
,看相关的版本是否匹配
卸载:pip uninstall tensorboard-plugin-wit
如何使用
在train的程序中加入想要查看的参数
from torch.utils.tensorboard import SummaryWriter
writer = SummaryWriter(args.tb_dir) # tb_dir是保存的文件路径
writer.add_scalar('train_loss', loss, iteration)
writer.add_scalar('lr', param_group['lr'], iteration)
- 终端运行
tensorboard --logdir= tb_dir --port=8890
,然后点tensorborad即可出现绘制的图形,可以把不同模型的tb放在一个终端目录下,即可以选择对比 - 如果报错端口已占用,
ps -ef | grep tensorboard
,kill掉原来的进程重新启动即可
模型搭建基本模块
import torch
import torch.nn as nn
import torch.nn.functional as F
import matplotlib.pyplot as plt
from torch.autograd import Variable
## 数据准备
x = torch.unsqueeze(torch.linspace(-1,1,100),dim=1)
y = x.pow(3)+0.1*torch.randn(x.size())
x , y =(Variable(x),Variable(y))
## 模型定义
class Net(nn.Module):
def __init__(self,n_input,n_hidden,n_output):
super(Net,self).__init__()
self.hidden1 = nn.Linear(n_input,n_hidden)
self.hidden2 = nn.Linear(n_hidden,n_hidden)
self.predict = nn.Linear(n_hidden,n_output)
def forward(self,input):
out = self.hidden1(input)
out = F.relu(out)
out = self.hidden2(out)
out = F.sigmoid(out)
out =self.predict(out)
return out
## 模型实例化 and 前向
net = Net(1,20,1)
print(net)
# 优化器 & 损失函数
optimizer = torch.optim.SGD(net.parameters(),lr = 0.1)
loss_func = torch.nn.MSELoss()
for t in range(5000):
prediction = net(x)
loss = loss_func(prediction,y)
optimizer.zero_grad() # 梯度归零
loss.backward() # 计算梯度
optimizer.step() # 梯度反传&参数更新
维度变化
PyTorch 常用方法总结4:张量维度操作(拼接、维度扩展、压缩、转置、重复……) - 知乎
1.拼接 torch.cat()
>>> a=torch.randn(2,3)
>>> b=torch.cat((a,a),0)
>>> a
tensor([[-0.3728, -1.0802, 0.8086],
[ 0.1498, -1.1031, -0.5884]])
>>> b
tensor([[-0.3728, -1.0802, 0.8086],
[ 0.1498, -1.1031, -0.5884],
[-0.3728, -1.0802, 0.8086],
[ 0.1498, -1.1031, -0.5884]])
2.squeeze & unsqueeze
- unsqueeze解压缩,维度增加
- squeeze压缩,维度减少
>>> a.size()
torch.Size([3, 4])
>>> b=a.unsqueeze(0)
>>> b.size()
torch.Size([1, 3, 4])# 0维度增加一维
>>> c=a.squeeze(0)
>>> c.size()
torch.Size([3, 4])
>>> c=b.squeeze(0) # squeeze只能对于维度数目=1的维度压缩,否则无用
>>> c.size()
torch.Size([3, 4])
>>> c=c.squeeze(0)
>>> c.size()
torch.Size([3, 4])
3.torch.chunk(tensor, chunks, dim=0) → List of Tensors
-
chunk:切分成的chunk数目
-
dim:切分的维度
-
如果不够整个切分,例如4列切成3部分,upper(4/3)=2,会被切成2个2
torch.Tensor.expand()
torch.squeeze() torch.Tensor.repeat()
torch.Tensor.narrow() torch.Tensor.view()
torch.Tensor.resize_() torch.Tensor.permute()
torch.rand() 【0,1)抽取的随机数;
torch.randn() 标准正态分布,均值为0 ,方差为1的随机数
torch.arange(0,5)输出【0,1,2,3,4】
torch.range(0,5)输出【0,1,2,3,4,5】
标准 toch.arange(start, end, step)
model.chilren()和model.modules()输出不一样
modules不仅会输出model还会输出submodule
unfold()
- contiguous :contiguous一般与transpose,permute,view搭配使用
-
即使用transpose或permute进行维度变换后,调用contiguous,然后方可使用view对维度进行变形。
-
有两种说法,一种是维度变换后tensor在内存中不再是连续存储的,而view操作要求连续存储,所以需要contiguous;
-
另一种是说维度变换后的变量是之前变量的浅复制,指向同一区域,即view操作会连带原来的变量一同变形,这是不合法的
tensor打乱
- pytorch的tensor不能直接打乱,先打乱index,再索引
idx = torch.randperm(src_spk_id.nelement())
bt_spk_id = src_spk_id.view(-1)[idx].view(src_spk_id.size())
dataset, dataloader
-
dataset是准备paired input & output,长度不需要准备到一致
-
dataset 封装到dataloader中,准备的是mini-batch,因此维度变成【batch_size, time-length, feature_dim】,需要扩维度&长度通知到max_length
-
enumerate dataloader的时候报错
TypeError: Caught TypeError in DataLoader worker process 0.
(1) print dataset中准备的数据,看是否有问题
(2)检查collate的def __init__(self)
是否正确,是必须有的定义
Pytorch的数据读取主要包含三个类:
Dataset
DataLoader
DataLoaderIter
这三者大致是一个依次封装的关系: 1.被装进2., 2.被装进3.
2.DataLoader()
DataLoader(dataset, batch_size=1, shuffle=False, sampler=None,
num_workers=0, collate_fn=default_collate, pin_memory=False,
drop_last=False)
dataset:加载的数据集(Dataset对象)
batch_size:batch size
shuffle::是否将数据打乱
sampler: 样本抽样,后续会详细介绍
num_workers:使用多进程加载的进程数,0代表不使用多进程
collate_fn: 如何将多个样本数据拼接成一个batch,一般使用默认的拼接方式即可
pin_memory:是否将数据保存在pin memory区,pin memory中的数据转到GPU会快一些
drop_last:dataset中的数据个数可能不是batch_size的整数倍,drop_last为True会将多出来不足一个batch的数据丢弃
- Pytorch中nn.ModuleList 和 nn.Sequential的不同
- Sequential的功能属于ModuleList 的子集,modulelist中网络的顺序在forward中也以打乱调用,只是不利于他人阅读;Sequential在定义时已经构建好了网络,不能打乱。
- 多层一样的网络结构,使用ModuleList 定义更方便;不一样的结构,使用Sequential定义更方便。
self.linears = nn.ModuleList([nn.Linear(5, 10), nn.Linear(10, 10)])
self.block = nn.Sequential(nn.Conv2d(1,20,5),
nn.ReLU(),
nn.Conv2d(20,64,5),
nn.ReLU())
参考:
https://zhuanlan.zhihu.com/p/30934236(详细讲解了三个过程)
https://blog.csdn.net/g11d111/article/details/81504637(更详细的讲解每一个参数作用)
https://zhuanlan.zhihu.com/p/39752167(pytorch提速指南 )
https://zhuanlan.zhihu.com/p/34298983(autograd.variable和tensor)
2. 网络层调用
- 线性层报错
AttributeError: module 'torch.nn' has no attribute 'linear'
from torch import nn
linear1 = nn.Linear(in_dim, out_dim) # 注意这里是大写
- lstm调用,为了加速计算(padding部分不进入lstm),对送入数据进行预处理
pack_length/pad_length的讲解
self.lstm = nn.LSTM(config['Model']['encoder_embedding_dim'],
int(config['Model']['encoder_lstm_dim']), 1,
batch_first=True, bidirectional=True)
## 填充过的变长序列去除冗余(压紧)
x = nn.utils.rnn.pack_padded_sequence(x, input_lengths, batch_first=True)
self.lstm.flatten_parameters()
outputs, _ = self.lstm(x)
## 和pack_padded_sequence()是相反,把压紧的序列再填充回来
outputs, _ = nn.utils.rnn.pad_packed_sequence(outputs, batch_first=True)
更多解释可以参见pytorch对可变长度序列的处理
3. model.eval
使用PyTorch进行训练和测试时一定注意要把实例化的model指定train/eval,eval()时,框架会自动把BN和DropOut固定住,不会取平均,而是用训练好的值,不然的话,一旦test的batch_size过小,很容易就会被BN层导致生成图片颜色失真极大
torch.no_grad()
使用no_grad则设置让梯度Autograd设置为False(因为在训练中我们默认是True),这样保证了反向过程为纯粹的测试,而不变参数。更加节省GPU内存,进而加速计算
with torch.no_grad():
pred_lpc = model.inference((inputs, input_lengths, phn_state))
4. 参数量计算
- 参数个数
model = resnet50()
print("resnet50 have {} paramerters in total".format(sum(x.numel() for x in model.parameters())))
# x是W/b这样的参数
# x.numel()是计算参数矩阵里边真正的参数量
- 模型内存
1 byte = 8 bit
1 kb = 1024 byte
1 MB = 1024 kb
因此得到的参数个数如果是floatn32的话,parameter * 4/1024/1024
5. gpu/cpu load
- 报错模型参数/输入不在同一个device上边
RuntimeError: Input and parameter tensors are not at the same device, found input tensor at cpu and parameter tensor at cuda:0
- 如果是模型中间的输入输出,
input = input.cuda()
载入GPU,input = input.cpu()
载入CPU - 如果是模型的参数,
model=model.cuda()
载入GPU,model = model.cpu()
载入CPU
import torch
import torch.nn as nn
# ----------- 判断模型是在CPU还是GPU上 ----------------------
model = nn.LSTM(input_size=10, hidden_size=4, num_layers=1, batch_first=True)
print(next(model.parameters()).device) # 输出:cpu
model = model.cuda()
print(next(model.parameters()).device) # 输出:cuda:0
model = model.cpu()
print(next(model.parameters()).device) # 输出:cpu
# ----------- 判断数据是在CPU还是GPU上 ----------------------
data = torch.ones([2, 3])
print(data.device) # 输出:cpu
data = data.cuda()
print(data.device) # 输出:cuda:0
data = data.cpu()
print(data.device) # 输出:cpu
6. 模型的保存和载入
模型保存
- 保存的时候可以把权重/优化器以及其他的保存在一个文件中(也可以单独保存),保存的是字典的格式
torch.save({'iteration': iteration,
'state_dict': model.state_dict(),
'optimizer': optimizer.state_dict(),
'learning_rate': learning_rate}, checkpoint_path)
模型载入
- 如果想要查看模型中网络参数量的大小,可以读取模型中权重的部分,另外存储,即可查看
>>> torch.load(checkpoint_path)
>>> torch.save(model['state_dict'], 'model.weights')
>>> du -h model.weights
- 载入模型的时候,model.py中各层在init中定义的顺序也不能改变,除非重新训练模型
训练时打印参数
for i, (key, val) in enumerate(self.spk_confusion.named_parameters()):
print(key) --------获取网络权重名字
print('pitch linear_weight', self.pitch_confusion.linear.weight) ---获取权重数值
- load_state_dict
保存
torch.save(self.opt.state_dict(), save_path)
加载
self.opt.load_state_dict(torch.load('%s.opt'%self.args.load_model_path))
for state_dict in self.opt.param_groups:
print(state_dict['lr'])
load-module
- 保存的时候是多卡,但是load的时候报错“Unexpected key(s) in state_dict: “module.features"
- 方法1:修改dict中参数的名字
model.load_state_dict({k.replace('module.',''):v for k,v in torch.load('checkpoint.pt').items()})
或者
# original saved file with DataParallel
state_dict = torch.load('checkpoint.pt') # 模型可以保存为pth文件,也可以为pt文件。
# create new OrderedDict that does not contain `module.`
from collections import OrderedDict
new_state_dict = OrderedDict()
for k, v in state_dict.items():
name = k[7:] # remove `module.`,表面从第7个key值字符取到最后一个字符,正好去掉了module.
new_state_dict[name] = v #新字典的key值对应的value为一一对应的值。
# load params
model.load_state_dict(new_state_dict) # 从新加载这个模型。
- 方法2:load模型之后仍然修改成多卡并行
```bash
model = nn.DataParallel(model)
model.load_state_dict(checkpoint)
# 训练/测试要改成这样,否则报错
model.module.train()
or
model.module.inference()
7. 模型参数更新指定
方法1: 手动设定某些参数requires_grad = False, 然后opt中删除不需要更新的
- 查看网络现有的参数名及参数值
# 制定参数freeze
for data in self.model.named_parameters():
para_name, para_val = data
if para_name.startswith('linear'):
para_val.requires_grad = False
# 然后需要在优化器中指定更新的参数
optimizer = optim.Adam(filter(lambda p: p.requires_grad, model.parameters()), lr=0.0001, betas=(0.9, 0.999), eps=1e-08, weight_decay=1e-5)
方法2: 在模型中freeze整块
def __init__(self):
self.spk_look_up_table = nn.Embedding(300, dim_emb)
self.encoder = Encoder(self.dim_neck, dim_emb, freq)
for p in self.parameters():
p.requires_grad = False
self.decoder2 = Decoder2(self.dim_neck, dim_emb, dim_pre)
self.postnet = Postnet()
检查设定是否有效果
for name,v in model.named_parameters(): //name, value 参数名,参数值
if name!='xxx.weight' and name!='xxx.bias' :
print(v.requires_grad)#理想状态下,所有值都是False
或者
print(v) ----对比freeze部分参数是否有变化
reload的设置
- ValueError: loaded state dict contains a parameter group that doesn’t match the size of optimizer’s group
- optimizer设置的是state 和param_group,优化器的状态
loaded_model = torch.load(args.checkpoint, map_location = torch.device('cpu'))
para_dict = loaded_model["state_dict"]
for k, v in para_dict.items():
print(k)
del para_dict['spk_table.weight'] ## 删除愿模型部分参数
model.load_state_dict(para_dict) ### optimizer可以使用initial的,否则会报错
9. 单机多卡模型训练
DataParallel
- DataParallel:直接对模型封装一层,然后
CUDA_VISIBLE_DEVICES=0,1 python train.py xxx
model = AutoVC(config).cuda()
print(model)
if torch.cuda.device_count() > 1:
print("Let's use", torch.cuda.device_count(), "GPUs!")
model = DataParallel(model)
DDP
https://fyubang.com/2019/07/23/distributed-training3/
https://blog.csdn.net/comway_Li/article/details/107531165
模型reload
- 不能直接
load(checkpoint['state_dict'])
model_dict = checkpoint_dict['state_dict']
model.load_state_dict(model_dict)
会报错RuntimeError: Error(s) in loading state_dict for DataParallel:
,需要把模型的参数写到一个新的字典中
checkpoint_dict = torch.load(checkpoint_path, map_location='cpu')
state_dict =checkpoint_dict['state_dict']
new_state_dict = OrderedDict()
for k, v in state_dict.items():
if 'module' not in k:
k = 'module.'+k
else:
k = k.replace('features.module.', 'module.features.')
new_state_dict[k]=v
model.load_state_dict(new_state_dict)
单卡cpu测试
- torch内部默认会设定多卡加速,所以为了保证只在单cpu上测试,需要命令行额外制定
OMP_NUM_THREADS=1 python infer.py xxxx
优化器optimizer
梯度累加
for i,(images,target) in enumerate(train_loader):
# 1. input output
images = images.cuda(non_blocking=True)
target = torch.from_numpy(np.array(target)).float().cuda(non_blocking=True)
outputs = model(images)
loss = criterion(outputs,target)
# 2.1 loss regularization
loss = loss/accumulation_steps
# 2.2 back propagation
loss.backward()
# 3. update parameters of net
if((i+1)%accumulation_steps)==0:
# optimizer the net
optimizer.step() # update parameters of net
optimizer.zero_grad() # reset gradient
- loss.backward() 反向传播,计算当前梯度;
- 多次循环步骤1-2,不清空梯度,使梯度累加在已有梯度上;
- 梯度累加了一定次数后,先 optimizer.step() 根据累计的梯度更新网络参数,然后 optimizer.zero_grad() 清空过往梯度,为下一波梯度累加做准备;如果 accumulation_steps 为8,则batchsize ‘变相’ 扩大了8倍,是我们这种乞丐实验室解决显存受限的一个不错的trick,使用时需要注意,学习率也要适当放大。