-柚子皮-

模型训练和预测

模型训练

单机训练

传统的batch训练函数

简单的说就是进来一个batch的数据,计算一次梯度,更新一次网络。

for i,(images,target) in enumerate(train_loader):# 1. input outputimages = 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. backwardoptimizer.zero_grad()   # reset gradientloss.backward()optimizer.step()            

1 获取loss:输入图像和标签,通过infer计算得到预测值,计算损失函数;
2 optimizer.zero_grad() 清空过往梯度;  lz:这里需要手动进行optimizer.zero_grad(),不然pytorch中默认是累加的。
3 loss.backward() 反向传播,计算当前梯度;
4 optimizer.step() 根据梯度更新网络参数

使用梯度累加的batch训练函数

梯度累加就是,每次获取1个batch的数据,计算1次梯度,梯度不清空,不断累加,累加一定次数后,根据累加的梯度更新网络参数,然后清空梯度,进行下一次循环。

for i,(images,target) in enumerate(train_loader):# 1. input outputimages = 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 regularizationloss = loss/accumulation_steps   # 2.2 back propagationloss.backward()# 3. update parameters of netif((i+1)%accumulation_steps)==0:# optimizer the netoptimizer.step()        # update parameters of netoptimizer.zero_grad()   # reset gradient

1 获取loss:输入图像和标签,通过infer计算得到预测值,计算损失函数;
2 loss.backward() 反向传播,计算当前梯度;   lz:2和3之间应该有一个pytorch自动累加梯度的过程(应该就是backward里面的过程吧)。
3 多次循环步骤1-2,不清空梯度,使梯度累加在已有梯度上;
4 梯度累加了一定次数后,先optimizer.step() 根据累计的梯度更新网络参数,然后optimizer.zero_grad() 清空过往梯度,为下一波梯度累加做准备;

与非梯度累加区别

一定条件下,batchsize越大训练效果越好,梯度累加则实现了batchsize的变相扩大,如果accumulation_steps为8,则batchsize '变相' 扩大了8倍,是解决显存受限的一个不错的trick。

使用时需要注意,学习率也要适当放大。经验法则是,如果batch size加倍,那么学习率就加倍。

PyTorch中在反向传播前为什么要手动将梯度清零?

有时候会不会觉得optimizer.zero_grad()这句很多余,怎么不自动清零,需要手动清零呢?原因在于在PyTorch中,计算得到的梯度值会自动进行累加(而不是覆盖)。pytorch选择自动累加而不是覆盖的原因:

1 从内存消耗的角度来看。这种模式可以让梯度玩出更多花样,比如说上面讲到的梯度累加(gradient accumulation)实现的“显存受限”解决:在内存大小不够的情况下叠加多个batch的grad作为一个大batch进行迭代,因为二者得到的梯度是等价的综上可知,这种梯度累加的思路是对内存的极大友好,是由FAIR的设计理念出发的。即当你GPU显存较少时,你又想要调大batch-size,此时你就可以利用PyTorch的这个性质进行梯度的累加来进行backward。

示例:

在你已经达到计算资源上限的情况下,你的batch size仍然太小(比如8),然后我们需要模拟一个更大的batch size来进行梯度下降,以提供一个良好的估计。

假设我们想要达到128的batch size大小。我们需要以batch size为8执行16个前向传播和向后传播,然后再执行一次优化步骤。

# clear last step
optimizer.zero_grad()

# 16 accumulated gradient steps
scaled_loss = 0
for accumulated_step_i in range(16):
     out = model.forward()
     loss = some_loss(out,y)    
     loss.backward()
      scaled_loss += loss.item()
      
# update weights after 8 steps. effective batch = 8*16
optimizer.step()

# loss is now scaled up by the number of accumulated batches
actual_loss = scaled_loss / 16

2 利用梯度累加,可以在最多保存一张计算图的情况下进行multi-task任务的训练:

从PyTorch的设计原理上来说,在每次进行前向计算得到pred时,会产生一个用于梯度回传的计算图,这张图储存了进行back propagation需要的中间结果,当调用了.backward()后,会从内存中将这张图进行释放。

for idx, data in enumerate(train_loader):
    xs, ys = data
    optmizer.zero_grad()
    # 计算d(l1)/d(x)
    pred1 = model1(xs) #生成graph1
    loss1 = loss_fn1(pred1, ys)
    loss1.backward()  #释放graph1

# 计算d(l2)/d(x)
    pred2 = model2(xs)#生成graph2
    loss2 = loss_fn2(pred2, ys)
    loss2.backward()  #释放graph2

# 使用d(l1)/d(x)+d(l2)/d(x)进行优化
    optmizer.step()

[PyTorch中在反向传播前为什么要手动将梯度清零?]

模型训练加速

16-bit 精度

16bit精度是将内存占用减半的惊人技术。大多数模型使用32bit精度数字进行训练。然而,最近的研究发现,16bit模型也可以工作得很好。混合精度意味着对某些内容使用16bit,但将权重等内容保持在32bit。

要在Pytorch中使用16bit精度,请安装NVIDIA的apex库,并对你的模型进行这些更改。

# enable 16-bit on the model and the optimizer
model, optimizers = amp.initialize(model, optimizers, opt_level='O2')# when doing .backward, let amp do it so it can scale the loss
with amp.scale_loss(loss, optimizer) as scaled_loss:                      scaled_loss.backward()

amp包会处理好大部分事情。如果梯度爆炸或趋向于0,它甚至会缩放loss。

在lightning中,启用16bit并不需要修改模型中的任何内容,也不需要执行我上面所写的操作。设置Trainer(precision=16)就可以了。

trainer = Trainer(amp_level='O2', use_amp=False)
trainer.fit(model)

移动到多个GPUs中

多节点GPU训练

在单个节点上多GPU更快的训练distributedDataParallel

[9个技巧让你的PyTorch模型训练变得飞快!]

Gpu训练

要进行2个张量的运算,就必须都在CPU或者都在GPU上。对于不同存储位置的变量,我们是不可以对他们直接进行计算的。存储在不同位置中的数据是不可以直接进行交互计算的。否则出错:RuntimeError: Expected object of backend CUDA but got backend CPU for ***。[PyTorch 20.GPU训练]

把数据和模型从cpu移到gpu中的两种方法

use_cuda = torch.cuda.is_available()

# 方法一:
if use_cuda:
    data = data.cuda()
    model.cuda()

# 方法二:
device = torch.device("cuda" if use_cuda else "cpu")
data = data.to(device)
model.to(device)
个人比较习惯第二种方法,可以少一个 if 语句。而且该方法还可以通过设备号指定使用哪个GPU设备,比如使用0号设备:

device = torch.device("cuda:0" if use_cuda else "cpu")

[PyTorch:tensor-基本操作---CUDA 的用法]

1 如果模型已经在GPU上了,model.to(device)/model.cuda()不会做任何事情。

2 对于自己创建的模型类,由于继承了 torch.nn.Module ,则可同样使用 model.to(device)/model.cuda() 来将模型中用到的所有参数都存储到显存中去。

model_cuda = model.cuda()后,将 cpu类型数据 投入 model_cuda 中去可以发现,系统会报错,而将 .cuda 之后的gpu数据投入 model_cuda 才能正常工作,并且输出的也是具有cuda的数据类型。

self.dnn_layer = MLP(input_dim, dnn_hidden_units, activation=dnn_activation,
                    use_bn=dnn_use_bn, l2_reg=l2_reg_dnn, dropout_rate=dnn_dropout,
                    init_std=init_std, device=device)
self.to(device)
...
print("self.dnn_layer.device: ", self.dnn_layer.device) #cpu,模型to('gpu')时函数本身没有变化,只是参数put到gpu了。
print("self.dnn_layer.parameters()).device: ", next(self.dnn_layer.parameters()).device) #gpu

3 当我们对模型存储到显存中去之后,那么这个模型中的方法后面所创建出来的Tensor是不是都会默认变成cuda的数据类型?答案是否定的。

[浅谈将Pytorch模型从CPU转换成GPU]

Note:

def compile(self, optimizer_name, loss_func_name=None, metrics_name=None):
    """
    :param optimizer_name: String (name of optimizer) or optimizer instance. See [optimizers](https://pytorch.org/docs/stable/optim.html).
    :param loss_func_name: String (name of objective function) or objective function. See [losses](https://pytorch.org/docs/stable/nn.functional.html#loss-functions).
    :param metrics_name: List of metrics to be evaluated by the model during training and testing. Typically you will use `metrics=['accuracy']`.
    """
    self.optimizer = self._get_optim(optimizer_name)
    self.loss_func = self._get_loss_func(loss_func_name)
    self.metrics = self._get_metrics(metrics_name)

def _get_optim(self, optimizer):
    if isinstance(optimizer, str):
        if optimizer == "sgd":
            optim = torch.optim.SGD(self.parameters(), lr=0.01)
        elif optimizer == "adam":
            optim = torch.optim.Adam(self.parameters())
        elif optimizer == "adagrad":
            optim = torch.optim.Adagrad(self.parameters())
        elif optimizer == "rmsprop":
            optim = torch.optim.RMSprop(self.parameters())
        else:
            raise NotImplementedError
    else:
        optim = optimizer
    return optim

def optimize(self, max_norm=None, norm_type=2):
    '''
    进行最优化操作
    '''
    with time_block("backward_in_optimize"):
        self.loss.backward()
    if max_norm:
        torch.nn.utils.clip_grad_norm_(self.parameters(), max_norm, norm_type)
    print("next(self.parameters()).device: ", next(self.parameters()).device)
    self.optimizer.step()

init中初始化时:

self.to(device)

self.compile(optimizer, loss, metrics) #需要在self.to(device)后面,因为optim = torch.optim.Adagrad(self.parameters())中next(self.parameters()).device的类型还未确定,self.parameters()为gpu时,optim才是gpu中,self.optimizer.step()执行时才不会出错:RuntimeError: Expected object of backend CPU but got backend CUDA for argument #4 'tensor1'。

指定GPU编号

设置当前使用的GPU设备仅为0号设备,设备名称为 /gpu:0:

os.environ["CUDA_VISIBLE_DEVICES"] = "0"

设置当前使用的GPU设备为0,1号两个设备,名称依次为 /gpu:0、/gpu:1:

os.environ["CUDA_VISIBLE_DEVICES"] = "0,1"

根据顺序表示优先使用0号设备,然后使用1号设备。

Note: 指定GPU的命令需要放在和神经网络相关的一系列操作的前面。

单机多GPU的实现代码

[Pytorch 高效使用GPU的操作]

分布式训练

[PyTorch:模型训练-分布式训练]

模型预测

model.eval()作用:不启用 BatchNormalization 和 Dropout,保证BN和dropout不发生变化,pytorch框架会自动把BN和Dropout固定住,不会取平均,而是用训练好的值,不然的话,一旦test的batch_size过小,很容易就会被BN层影响结果。

# Set to not-training mode to disable dropout
model.eval()     #实际调用model.train(False)

...

with torch.no_grad():    #禁止梯度的计算

y_pred = model(x)

loss = ...

训练时:

# Set back to training mode
model.train(True)  # todo

训练设置

梯度裁剪

nn.utils.clip_grad_norm_ 的参数
parameters – 一个基于变量的迭代器,会进行梯度归一化
max_norm – 梯度的最大范数
norm_type – 规定范数的类型,默认为L2
Note: 梯度裁剪在某些任务上会额外消耗大量的计算时间。

[https://pytorch.org/docs/master/generated/torch.nn.utils.clip_grad_norm_.html]

示例

def optimize(self, max_norm=None, norm_type=2):
        '''
        进行最优化操作
        '''
        self.loss.backward()
        if max_norm:
            torch.nn.utils.clip_grad_norm_(self.parameters(), max_norm, norm_type)
        self.optimizer.step()

import torch.nn as nn

outputs = model(data)
loss= loss_fn(outputs, target)
optimizer.zero_grad()

self.optimize()

16-bit 精度

16bit精度是将内存占用减半的惊人技术。大多数模型使用32bit精度数字进行训练。然而,最近的研究发现,16bit模型也可以工作得很好。混合精度意味着对某些内容使用16bit,但将权重等内容保持在32bit。

要在Pytorch中使用16bit精度,请安装NVIDIA的apex库,并对你的模型进行这些更改。

# enable 16-bit on the model and the optimizer
model, optimizers = amp.initialize(model, optimizers, opt_level='O2')# when doing .backward, let amp do it so it can scale the loss
with amp.scale_loss(loss, optimizer) as scaled_loss:                      scaled_loss.backward()

amp包会处理好大部分事情。如果梯度爆炸或趋向于0,它甚至会缩放loss。

防止验证模型时爆显存

torch.no_grad()

torch.no_grad()是一个上下文管理器,用来禁止梯度的计算,通常用来网络推断中(因为验证模型时不需要求导,即不需要梯度计算),关闭autograd可以减少计算内存的使用量,提高速度,如果不关闭可能会爆显存。
with torch.no_grad():
    # 使用model进行预测的代码
    pass

示例
>>> a = torch.tensor([1.0, 2.0], requires_grad=True)
>>> with torch.no_grad():
...     b = n.pow(2).sum()
...
>>> b
tensor(5.)
>>> b.requires_grad
False
>>> c = a.pow(2).sum()
>>> c.requires_grad
True
上面的例子中,

当a的requires_grad=True时,不使用torch.no_grad(),c.requires_grad为True;

使用torch.no_grad()时,b.requires_grad为False,当不需要进行反向传播时(推断)或不需要计算梯度(网络输入)时,requires_grad=True会占用更多的计算资源及存储资源。

Note: 需要注意的是,torch.no_grad()只是禁止梯度计算,将计算过程中的tensor的requires_grad设置为False,但不会将模型参数如weight这样的tensor的requires_grad设置为False,因为如果训练和测试同时进行,跳出predict的torch.no_grad()后的训练过程还是需要weight中requires_grad=True的。

释放保留的计算图

一个最简单撑爆你的内存的方法是为了记录日志存储你的loss。

losses = []
...
losses.append(loss)

print(f'current loss: {torch.mean(losses)'})
上面的问题是,loss仍然包含有整个图的副本。在这种情况下,调用.item()来释放它。

# good
losses.append(loss.item())

torch.cuda.empty_cache()

Pytorch 训练时无用的临时变量可能会越来越多,导致 out of memory ,可以使用下面语句来清理这些不需要的变量:torch.cuda.empty_cache() 。
官网 上的解释为:
Releases all unoccupied cached memory currently held by the caching allocator so that those can be used in other GPU application and visible innvidia-smi. 意思就是PyTorch的缓存分配器会事先分配一些固定的显存,即使实际上tensors并没有使用完这些显存,这些显存也不能被其他应用使用。这个分配过程由第一次CUDA内存访问触发的。而 torch.cuda.empty_cache() 的作用就是释放缓存分配器当前持有的且未占用的缓存显存,以便这些显存可以被其他GPU应用程序中使用,并且通过 nvidia-smi命令可见。注意使用此命令不会释放tensors占用的显存。对于不用的数据变量,Pytorch 可以自动进行回收从而释放相应的显存。
更详细的优化可以查查 优化显存使用 和 显存利用问题。

BUGs

UndefinedMetricWarning: F-score is ill-defined and being set to 0.0 in labels with no predicted samples.'precision', 'predicted', average, warn_for)`

metrics.f1_score(y_test, y_pred, average='weighted')

原因是预测的label都预测成了一个,可能是预测批数据量少了,也可能是模型有问题,从而全部预测成了同一个label。

some labels in y_true don't appear in y_pred.

[UndefinedMetricWarning: F-score is ill-defined and being set to 0.0 in labels with no predicted samples]

from: -柚子皮-

ref: https://zhuanlan.zhihu.com/p/76459295

[9个技巧让你的PyTorch模型训练变得飞快!]

[使用pytorch时,训练集数据太多达到上千万张,Dataloader加载很慢怎么办?]

PyTorch:模型训练和预测相关推荐

  1. PyTorch 模型训练实用教程(附代码)

    向AI转型的程序员都关注了这个号???????????? 机器学习AI算法工程   公众号:datayx PyTorch 能在短时间内被众多研究人员和工程师接受并推崇是因为其有着诸多优点,如采用 Py ...

  2. 化学实验室自动化 - 1. 深度学习视觉检测(实例分割) - Mask-RCNN模型训练和预测

    在上一篇文章中,我们完成了化学实验室常见物体的COCO格式的实例分割数据集制作.上一篇文章的数据集中总共只有65张图像,而且被分成了训练集.验证集和测试集,经Mask-RCNN模型训练测试,发现模型的 ...

  3. 手把手教你洞悉 PyTorch 模型训练过程,彻底掌握 PyTorch 项目实战!(文末重金招聘导师)...

    (文末重金招募导师) 在CVPR 2020会议接收中,PyTorch 使用了405次,TensorFlow 使用了102次,PyTorch使用数是TensorFlow的近4倍. 自2019年开始,越来 ...

  4. Transformer课程 第8课 NER案例模型训练及预测

    Transformer课程 第8课 NER案例模型训练及预测 Train Our Classification Model 现在,我们的输入数据已正确格式化,是时候对BERT模型进行微调了. 4.1. ...

  5. python模型训练_python模型训练与预测练习

    python模型训练与预测练习 发布时间:2018-03-09 17:49, 浏览次数:368 , 标签: python 我是照着YouTube上的机器学习视频做的,视频里讲的比较简洁,下来自己做遇到 ...

  6. Pytorch模型训练实用教程学习笔记:四、优化器与学习率调整

    前言 最近在重温Pytorch基础,然而Pytorch官方文档的各种API是根据字母排列的,并不适合学习阅读. 于是在gayhub上找到了这样一份教程<Pytorch模型训练实用教程>,写 ...

  7. Tensorflow 2.x(keras)源码详解之第九章:模型训练和预测的三种方法(fittf.GradientTapetrain_steptf.data)

      大家好,我是爱编程的喵喵.双985硕士毕业,现担任全栈工程师一职,热衷于将数据思维应用到工作与生活中.从事机器学习以及相关的前后端开发工作.曾在阿里云.科大讯飞.CCF等比赛获得多次Top名次.现 ...

  8. 《PyTorch模型训练实用教程》—学习笔记

    文章目录 前言 数据 Dataset类 DataLoader类 transform 裁剪-Crop 翻转和旋转-Flip and Rotation 图像变换 对transforms操作,使数据增强更灵 ...

  9. pytorch(4)Pytorch模型训练时从CPU与GPU之间的转换

    1.如何进行迁移 使用Pytorch写的模型: 对模型和相应的数据使用.cuda()处理.通过这种方式,我们就可以将内存中的数据复制到GPU的显存中去.从而可以通过GPU来进行运算了. 另外一种方式, ...

  10. Pytorch模型训练保存/加载(搭建完整流程)

    文章目录 前言 模型训练完整步骤 模型保存与加载 GPU训练 "借鸡生蛋" 模型使用 本博文优先在掘金社区发布! 前言 我们这边还是以CIARF10这个模型为例子. 现在的话先说明 ...

最新文章

  1. System之Ubuntu:VMware虚拟机 Ubuntu安装详细过程(图文教程,最强攻略,步骤详细,建议收藏)
  2. SSI——服务器端嵌入
  3. C#编写串口通信程序(转)
  4. 让QT对话框显示中文
  5. 星巴克又出事 被强制执行1087万!网友:欠租了吗?
  6. Xml的编码和Bom
  7. Zookeeper、Hadoop、Sqoop、Mahout、HBase整合安装
  8. FPGA入门必备学习网站和工具
  9. NPDP 产品经理国际资格认证
  10. 怎样做好服务器运维工作
  11. WPS—JS宏笔记记录
  12. 3G时会有两次 Activate PDP context request消息
  13. 【面试】网易游戏面试题目整理及答案(3)
  14. 可以在xmind上同时有两个大主题吗_矩阵图有何用处?XMind完美展示多对多对象间的关系...
  15. 多重背包问题的二进制优化(C语言)
  16. matlab2016a运行svmtrain函数出错
  17. 互联网电影院升级带来3D+VR“奇妙”新体验
  18. 如何将footer标签固定在底部_div footer标签css实现位于页面底部固定
  19. UVA 10838 (alpha-bate剪枝搜索)
  20. 吃鸡 python开发_python青少年编程第三季——14、吃鸡联盟之亚瑟开枪(上)

热门文章

  1. PYTHON_错误处理
  2. 《浪潮之巅》读书笔记——第6章 Microsoft
  3. .net dropdownlist 動態顯示,指定字段
  4. 【转】BW的星型数据模型
  5. (Hide my Windows)隐藏指定的应用窗口及托盘图标
  6. 牛客练习赛52 C 烹饪(容斥+扩展欧几里得)
  7. 前后端开源的一款简单的微信个人博客小程序
  8. C++学习(十三)(C语言部分)之 练习
  9. 在django项目中使用django-ckeditor
  10. 图说单播,组播,广播,选播和地域播