pytorch使用记录

安装

torch安装

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()01)抽取的随机数;

torch.randn() 标准正态分布,均值为0 ,方差为1的随机数

torch.arange(0,5)输出【01234】

torch.range(0,5)输出【012345】

标准 toch.arange(start, end, step)

model.chilren()和model.modules()输出不一样

modules不仅会输出model还会输出submodule

unfold()
  1. 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. 网络层调用

  1. 线性层报错AttributeError: module 'torch.nn' has no attribute 'linear'
from torch import nn
linear1 = nn.Linear(in_dim, out_dim) # 注意这里是大写
  1. 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

  1. 报错模型参数/输入不在同一个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中删除不需要更新的

  1. 查看网络现有的参数名及参数值
# 制定参数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

梯度累加

torch梯度累加

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,使用时需要注意,学习率也要适当放大。
  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值