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):
上面的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)