目录

cuDNN 深度神经网络的gpu加速库

背景介绍

使用

torch.distributed 分布式通讯包

timm 预训练库

用法示例

基于Apex的混合精度加速

什么是混合精度计算

from apex import amp

两种用于管理配置的模板库

1、argparse 解析命令行参数和选项

使用步骤

创建解析对象

为对象添加参数

parse_args()和parse_known_args()的区别:

2、yacs 定义和管理系统配置

创建CN()作为容器装载参数

API reference

参考 


cuDNN 深度神经网络的gpu加速库

背景介绍

英伟达cuDNN(CUDA Deep Neural Network library)是一个用于深度神经网络的gpu加速库。其可大幅优化标准例程,例如卷积层、池化层、归一化层和激活层的实现。主要特征是: 
1. 为所有常用卷积实现了 Tensor Core 加速,包括 2D 卷积、3D 卷积、分组卷积、深度可分离卷积以及包含 NHWC 和 NCHW 输入及输出的扩张卷积 
2. 为诸多计算机视觉和语音模型优化了内核,包括 ResNet、ResNext、SSD、MaskRCNN、Unet、VNet、BERT、GPT-2、Tacotron2 和 WaveGlow
3. 支持 FP32、FP16 和 TF32 浮点格式以及 INT8 和 UINT8 整数格式 
4. 4D张量的任意维排序、跨步和子区域意味着可轻松集成到任何神经网络实现中 
5. 能为任意 CNN 架构上融合的运算提速

使用

import torch.backends.cudnn as cudnn
cudnn.benchmark = True

在利用pytorch进行网络训练时总会见到这行代码,它的作用是?主要为了优化运行效率,实现网络加速,让内置的cuDNN 的auto-tuner自动寻找最适合当前配置的高效算法。意思就是在模型启动的时候,只要额外多花一点预处理时间,就可以较大幅度减少训练时间。 
代码加在哪里? 一般加在开头就好,设置GPU时,补一句benchmark为True。
但要注意: 
1、如果网络的输入数据维度或类型上变化不大,设置 torch.backends.cudnn.benchmark = true 可以增加运行效率,如batch size、图片大小、输入通道等不变 
2、网络的输入数据在每次 iteration 都变化的话,会导致 cnDNN 每次都会去寻找一遍最优配置,这样反而会降低运行效率。 
3、Benchmark模式会提升计算速度,但是由于计算中有随机性,每次网络前馈结果略有差异。如果想要避免这种结果波动,设置:
torch.backends.cudnn.deterministic = True #设置为True,说明使用非确定性算法

指定使用哪台机器:

torch.cuda.set_device(2)

不鼓励使用此功能函数。多数情况下,使用CUDA_VISIBLE_DEVICES环境变量:

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

torch.distributed 分布式通讯包

所述torch.distributed包提供跨在一个或多个计算机上运行的几个计算节点对多进程并行PyTorch支持与通信原语。 torch.distributed.init_process_group() 在调用任何其他方法之前,需要使用该函数初始化该包。这将阻止所有进程加入。 
torch.distributed.init_process_group(backend, init_method='env://', kwargs) 
参数:
backend (str) - 要使用的后端的名称。根据构建时配置有效值包括:tcp,mpi和gloo。
init_method(str,_可选) - 指定如何初始化包的URL。
world_size(int
,_可选) - 参与作业的进程数。
rank(int,_可选) - 当前进程的等级。
group_name(str
,_可选) - 组名称。请参阅init方法的说明。

timm 预训练库

PyTorch Image Models,是一个巨大的pytorch代码集合,包括:
image models、layers、utilities、optimizers、schedulers、data-loaders / augmentations、training / validation scripts
旨在将各种SOTA模型整合在一起,是一个好用的预训练库,以CV分类任务为主。
加载的模型储存在本地的:

>>>import timm
>>>timm.create_model('resnet34', pretrained=True)
>>>Downloading: "https://github.com/rwightman/pytorch-image-models/releases/download/v0.1-weights/resnet34-43635321.pth" to C:\Users\xxx/.cache\torch\hub\checkpoints\resnet34-43635321.pth

用法示例

import timm  
import torch  
all_pretrained_models_available = timm.list_models(pretrained=True)  
print(all_pretrained_models_available)  
print(len(all_pretrained_models_available))  
model = timm.create_model('resnet34')  
x = torch.randn(1, 3, 224, 224)  
preds = model(x)  
print('preds shape: {}'.format(preds.shape))  
all_feature_extractor = timm.create_model('resnet34', features_only=True)  
all_features = all_feature_extractor(x)  
print('All {} Features: '.format(len(all_features)))  
for i in range(len(all_features)):  
    print('feature {} shape: {}'.format(i, all_features[i].shape))  

feature_extractor = timm.create_model('resnet34', features_only=True, out_indices=[3])  
print('type:', type(feature_extractor))  
print('len: ', len(feature_extractor))  
for item in feature_extractor:
    print(item)

基于Apex的混合精度加速

什么是混合精度计算

半精度-16bit 单精度-32bit 双精度-64bit 
低精度(FP16)的优点:
1)计算速度提升 
2)降低计算中对设备带宽的需求,提升数据通讯和存储效率
3)Tensor Core的普及
低精度的缺点:
1)计算误差
2)溢出错误 

通过研究,学者们发现计算过程中通过合理分配不同环节的数值精度,可以在保证最终计算结果准确的条件下,实现整体计算加速。混合精度(超精度)是指训练时在模型中同时使用16位和32位浮点类型。用FP16做储存和乘法以加速运算,用FP32做累加避免舍入误差 

from apex import amp

NVIDIA发布的Apex开源工具库提供了amp(自动混合精度)和FP16_Optimizer两种不同的库。 
amp:在神经网络推理过程中,针对不同的层,采用不同的数据精度进行计算。pytorch1.6版本开始,支持自动混合精度训练已进入稳定阶段,amp训练性能在Tensor Core GPU上实现更高的性能并节省多达50%内存

from apex import amp  
#像往常一样声明模型和优化器,默认(FP32)精度  
model = torch.nn.Linear(D_in, D_out).cuda()
optimizer = torch.optim.SGD(model.parameters(), lr=1e-3
#允许amp根据opt_level的要求执行类型转换
model, optimizer = amp.initialize(model, optimizer, opt_level = "o1" )
with amp.scale_loss(loss, optimizer) as scaled_loss:
     scaled_loss.backward()

opt_level:o0-FP32训练,可作为准确率的baseline;o1-混合精度训练,根据黑白名单自动决定使用FP16还是FP32;o2-几乎FP16混合精度训练,不存在黑白名单,除BN,几乎都用FP16;o3-FP16训练,很不稳定,可作为速度的baseline。 
针对单一optimizer使能amp:
1)代码中Optimizer之后添加:

optimizer = # ... some optimizer  
amp_handle = amp.init()

2)修改反向迭代部分

# ... do a bunch of stuff to compute a loss
loss.backward()
optimizer.step()
# ...finish the iteration

修改为

# ... same as before
with amp_handle.scale_loss(loss, optimizer) as scaled_loss:
     scaled_loss.backward()
optimizer.step()
# ... same as before

针对多Optimizer使能amp

amp_handle = amp.init()
optimizer = amp_handle.wrap_optimizer(optimizer, num_loss=2)    
# ...
optimizer.zero_grad()
loss1 = ComputeLoss1(model) 
with optimizer.scale_loss(loss1) as scaled_loss:
     scaled_loss.backward()
# ...
loss2 = ComputeLoss2(model)
with optimizer.scale_loss(loss2) as scaled_loss:
     scaled_loss.backward()
# ...
optimizer.step()

两种用于管理配置的模板库

1、argparse 解析命令行参数和选项

使用步骤

import argparse   #导入模块  
parser = argparse.ArgumentParser()     #创建解析对象
parser.add_argument()    #向该对象中添加要关注的命令行参数和选项
parser.parse_args()    #调用parse_args()方法进行解析

创建解析对象

ArgumentParser(prog=None,usage=None,description=None,epilog=None,parents=[],formatter_class=argparse.HelpFormatter, prefix_chars='-',fromfile_prefix_chars=None, argument_default=None,conflict_handler='error', add_help=True))

例如:

parse = argparse.ArgumentParser(prog = 'argparseDemo',prefix_chars= '+',description='the message info before help info',epilog="the message info after help info")

为对象添加参数

parser.add_argument('--batch_size', type=int, default=2048,help='batch size')

parse_args()和parse_known_args()的区别:

parse_args()函数返回的是一个命名空间-NameSpace,其中有一些变量就是add_argument()的参数。parse_known_args()返回的是一个有两个元素的元组,第一个元素是NameSpace,第二个是列表。后者的作用是当仅获取到基本设置时,如果运行命令中传入了之后才会获取到的其他配置,不会报错;而是将多出来的部分保存起来,留到后面使用。

import argparse
parse = argparse.ArgumentParser()
parse.add_argument('--data-path', type=str, help='path to dataset')
opt1 = parse.parse_args()
print(opt1)

opt2 = parse.parse_known_args()
print(opt2)
print(opt2[0])
print(opt2[1])

输出:

Namespace(data_path=None)
(Namespace(data_path=None), [])
Namespace(data_path=None)
[]

2、yacs 定义和管理系统配置

配置通常包括用于训练机器学习模型的超参数或可配置模型超参数(如网络深度)
导入:

from yacs.config import CfgNode as CN

创建CN()作为容器装载参数

容器可以嵌套

from yacs.config import CfgNode as CN
__C = CN()
__C.name = 'test'
__C.model = CN()  # 嵌套使用
__C.model.backbone = 'resnet'
__C.model.depth = 18
print(__C)

#输出
model:
   backbone: resnet
   depth: 18
name: test

一般创建一个config文件,命名为config.py/default.py,在文件中指定所有默认的配置选项。

#config.py
import os
import yaml
from yacs.config import CfgNode as CN

_C = CN()

# Base config files
_C.BASE = ['']

# -----------------------------------------------------------------------------
# Data settings
# -----------------------------------------------------------------------------
_C.DATA = CN()
# Batch size for a single GPU, could be overwritten by command line argument
_C.DATA.BATCH_SIZE = 128
# Path to dataset, could be overwritten by command line argument
_C.DATA.DATA_PATH = ''
# Dataset name
_C.DATA.DATASET = 'imagenet'
# Input image size
_C.DATA.IMG_SIZE = 224
……………………
……………………

对于每一次实验,不同的参数设置都需要创建一个yaml配置文件,该文件里只写出需要改变的参数,其他使用config.py里默认的

#configs\swin_base_patch4_window12_384.yaml 
#only for evaluation
DATA:
  IMG_SIZE: 384
MODEL:
  TYPE: swin
  NAME: swin_base_patch4_window12_384
  SWIN:
EMBED_DIM: 128
DEPTHS: [ 2, 2, 18, 2 ]
NUM_HEADS: [ 4, 8, 16, 32 ]
WINDOW_SIZE: 12
TEST:
  CROP: False

API reference

#clone
def get_cfg_defaults():
    return __C.clone()
  
#clear
print(__C.clear())  # None

#merge_from_file()
__C.merge_from_file("./test_config.yaml")

#freeze(),freeze the configs, and you can not change the value after this operation
__C.freeze()

#defrost(), reverse operation of freeze()
__C.defrost()