Pytorch框架学习---(7)常见的4大归一化、模型保存与加载、模型微调、多GPU的使用

本节简单总结Pytorch中常见的4大归一化、模型如何保存并加载、以及模型如何实现微调,pytorch中多GPU的使用。【文中思维导图采用MindMaster软件,Latex公式采用在线编码器

1.Pytorch中封装的4大归一化(BN、LN、IN、GN)

(1)为什么要采用Normalization?

  都是为了解决Internal Covariate Shift(ICS)问题,即数据分布出现异常而导致的网络训练困难。ICS问题在《Batch Normalization: Accelerating Deep Network Training by Reducing Internal Covariate Shift》中提到。

(2)这4大归一化之间的异同点

① 相同点

  都是类似 \(\gamma \times \frac{x- mean }{\sqrt{var^{2}+ \varepsilon } } + \beta\) 这样的形式,即“加减乘除”。

② 不同点

  均值mean和方差variance的求取方式不同,具体以下图为例:

  • (a)BN:对一个batch下的样本数的同一channel下的特征做归一化;
  • (b)LN:由于BN不适合变长的网络,例如RNN,所以LN对单独一个样本计算均值和方差,注意:gamma和beta是逐元素计算
  • (c)IN:由于BN不适合图像生成领域,即每一个图像都有自身的风格,不能够将一个batch中的样本混为一谈,,故对每一个样本逐通道归一化;
  • (d)GN:对于某些情况,batch_size较小,此时易知归一化值不准(类似无法代替全局),故GN对通道数(很多的通道)进行分组,用通道数去弥补小数据量问题。注意:gamma和beta是逐通道计算

(3)具体使用

① BatchNorm

'''BatchNorm基类'''
class _BatchNorm(Module):
    def __init__(self, num_features, eps=1e-5, momentum=0.1, affine=True,
                 track_running_stats=True):

# 参数:
      # num_features:一个样本的特征数量(或者是通道数量)
      # eps:分母修正项
      # momentum:指数加权平均 估计当前的mean/var!!!!!!!
      # affine:是否进行affine transform,即gamma、beta的scale and shift
      # track_running_stats:是训练状态 or 测试状态

注意:测试状态,mean和var只计算当前batch,而训练状态,当前时刻的mean和var会受之前的batch影响,即下面的公式(momentum):

\[running\_mean= \left ( 1- momentum \right )\ast pre\_running\_mean+ momentum\ast mean\_t \]

\[running\_var= \left ( 1- momentum \right )\ast pre\_running\_var+ momentum\ast var\_t \]

  上面的momentum在之前的博客中有提及。

② LayerNorm

class LayerNorm(Module):
    def __init__(self, normalized_shape, eps=1e-5, elementwise_affine=True):

# 参数:
      # normalized_shape:该样本的特征形状
      # eps:分母修正项
      # elementwise_affine:是否要对每个元素进行affine transform操作

注意:实例化LN时,要注意normalized_shape这一项,必须从size末尾写,例如:

        >>> input = torch.randn(20, 5, 4, 10)
        >>> m = nn.LayerNorm(input.size()[1:])
        >>> m = nn.LayerNorm([4, 10])
        >>> m = nn.LayerNorm([5,4, 10])
        >>> m = nn.LayerNorm([10])
        # 但不可以是:
        >>> m = nn.LayerNorm([5, 4])

③ InstanceNorm

'''InstanceNorm基类'''
class _InstanceNorm(_BatchNorm):
    def __init__(self, num_features, eps=1e-5, momentum=0.1, affine=False,
                 track_running_stats=False):

# 参数:
      # num_features:一个样本的特征数量(或者是通道数量)
      # eps:分母修正项
      # momentum:指数加权平均 估计当前的mean/var!!!!!!!
      # affine:是否进行affine transform,即gamma、beta的scale and shift
      # track_running_stats:是训练状态 or 测试状态

④ GroupNorm

class GroupNorm(Module):
    def __init__(self, num_groups, num_channels, eps=1e-5, affine=True):

# 参数:
      # num_groups:分组数(必须能将通道数整除!!!!!!!!)
      # num_channels:通道数
      # eps:分母修正项
      # affine:是否进行affine transform,这里是对每个通道进行!!!!

2.模型的保存与加载

(1)pytorch提供的方法

  主要参数如下:(torch.save(obj, f))

  • obj:要保存的对象(模型,张量,参数等);

  • f:保存的路径。

(2)模型的加载 torch.load

  主要参数如下:(torch.load(f, map_location=None))

  • f:读取路径;

  • map_location:决定该文件放在GPU上还是cpu位置上,当想要加载的模型是GPU上的,此时机子没有GPU,则可以将map_location='cpu',将该模型放置在cpu上。

(3)两种保存模型的方式

# 保存整个网络
torch.save(net, PATH) 
# 保存网络中的参数
torch.save(net.state_dict(),PATH)

#对应上面的保存方式:
model_dict=torch.load(PATH)

model_dict=model.load_state_dict(torch.load(PATH))

(4)端点续训练(其实就是把优化器、模型的参数,以及epoch保存下来,方便后续继续训练)

'''保存'''
checkpoint = {"model_state_dict": net.state_dict(),
              "optimizer_state_dict": optimizer.state_dict(),
              "epoch": epoch}

torch.save(checkpoint, path_checkpoint)

'''加载'''
checkpoint = torch.load(path_checkpoint)

net.load_state_dict(checkpoint['model_state_dict'])

optimizer.load_state_dict(checkpoint['optimizer_state_dict'])

start_epoch = checkpoint['epoch']

scheduler.last_epoch = start_epoch  # 用于更新优化器的学习率

3.模型微调

  模型finetune感觉这里对python编程较高 ( 咳咳。。。——>_——>。。。只怪本人python编程目前还比较low ),其实主要要对字典、列表等高级操作要熟悉!!

  在这里本人就不献丑了,等后续用到finetune再来补充。大家可以先参考知乎Pytorch自由载入部分模型参数并冻结

4.GPU的使用

(1)to函数 (用于转换类型/设备)

  to函数可以用于tensor和module:

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

'''对于张量tensor'''
# 张量不执行原位inplace操作,即必须赋值
a = torch.ones((5, 5))
a = a.to(torch.float64)
a = a.to(device)

'''对于模型moudle'''
# 模型执行inplace
net.to(device)

(2)torch.cuda常用方法

  上图中设置GPU对应的os.environ.setdefault操作,可以有效防止多人使用GPU冲突问题,或者多程序在不同的GPU上跑的冲突,可用可见的GPU(逻辑GPU)与物理GPU的对应关系可以理解为:(对应图3os.environ粉色方块的操作)

(3)多GPU运算的分发并行机制

① 发并行机制的流程图

  这里一般利用torch.nn.DataParallel操作,例如batch_size=16,我们利用4个GPU并行运算,分配给每个GPU的batch_size=4该机制主要流程为:

② torch.nn.DataParallel

class DataParallel(Module):
    def __init__(self, module, device_ids=None, output_device=None, dim=0):

# 参数:
      # module:需要处理的模型;
      # device_ids:可分法的GPU,默认分发到所有可见可用的GPU;(通过设置上述os.environ.setdefault操作,即可默认)
      # output_device:最后结果输出设备,默认主GPU

(4)在多GPU上运行的模型,其pkl文件加载时的问题

  当我们加载多GPU模型参数,print load后的pkl文件,会发现较原始模型linear.0,多了一个module,即module.linear.0,因而我们需要取出module之后的参数,可以参考github上的代码:

'''state_dict_load为OrderedDict形式,无法直接操作'''

    state_dict_load = torch.load(path, map_location="cpu")
    from collections import OrderedDict
    new_state_dict = OrderedDict()
    for k, v in state_dict_load.items():
        namekey = k[7:] if k.startswith('module.') else k  # 重新取出module后层信息,作为新的key
        new_state_dict[namekey] = v

    net.load_state_dict(new_state_dict)
posted @ 2020-07-20 17:21  steven_zhao1001  阅读(1494)  评论(0编辑  收藏  举报