精度调优checklist

Posted archimekai

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了精度调优checklist相关的知识,希望对你有一定的参考价值。

常见数据集问题

ds.01 数据集中缺失值过多

检查方法:

缺失值常常以NaN,+/-Inf等形式存在,不同的数据集中使用的缺失值符号也不尽相同。检查缺失值时,首先明确每个字段使用何种方式表示缺失值,然后使用计数器对每个字段中的缺失值个数进行计数,以此来掌握数据集中缺失值的情况。

若数据中存在未处理的缺失值,则此项的检查结果为“存在问题”,需要采取合适的手段处理(处理手段请参考 “精度问题详细定位指南”)

检查结论:

请填写

ds.02 数据的标签错误

检查方法:

若标签不多,建议基于所有的标签对训练数据进行无放回的层次抽样,确保每个标签至少有一个样本入选,然后对入选的样本进行检查。合理选择抽取概率,使得样本量在50个左右。

若标签较多,建议先对标签进行抽样,随机选择20个标签。然后基于所选择的标签对训练数据进行无放回的层次抽样,确保每个标签至少有一个样本入选。合理选择抽取概率,使得样本量在50个左右。

获取完样本后,应采用合适可视化方式进行检查。例如,图像数据可以使用matplotlib绘制出图片后检查,文本数据则可以直接打印到屏幕上进行检查。

检查结论:

请填写

ds.03 数据集每个类别的样本数目不均衡或部分类别训练样本不足

检查方法:

使用计数器对每个类别的样本数目进行计数,然后通过标准差和柱状图等方式判断样本数目是否均衡。一般来说,有监督深度学习算法在每类5000个标注样本的情况下将达到可以接受的精度,当数据集中有1000万个以上的已标注样本时,模型的表现将会超过人类。

检查结论:

请填写

ds.04 训练环境上的数据集同标准数据集不同

检查方法:

当使用标准数据集时,应确认训练环境中的数据集同标准数据集一致。

数据集往往较大,应进行校验以确定数据集完整性和正确性。

  1. 获取参考训练与实际环境训练的数据集文件列表,确保两个文件列表一致。

  2. 获取参考数据集文件与实际环境数据集文件的MD5校验码,确保两组校验码一致。

检查结论:

请填写

常见数据处理算法问题

dp.01 未对数据进行归一化或标准化

检查方法:

检查数据处理代码,确认数据处理代码中进行了必要的归一化或标准化调用。例如Resize、Rescale、Normalize等类似的操作。

例子:

以ModelZoo的resnet50模型为例,可见其在数据处理代码中进行了归一化,因此检查结果为“无问题”。

    trans += [
        C.Resize((224, 224)),
        C.Rescale(1.0 / 255.0, 0.0),
        C.Normalize([0.4914, 0.4822, 0.4465], [0.2023, 0.1994, 0.2010]),
        C.HWC2CHW()
    ]

检查结论:

请填写

dp.02 推理时数据处理方式和训练集不一致

检查方法:

检查训练脚本的数据处理代码和推理脚本的数据处理代码,查看处理逻辑是否一致。需要说明的是,一些用于数据增强的随机操作(如随机旋转,随机裁切等)一般只应用在训练集,推理时无需进行随机操作。

例子:

以ModelZoo的resnet50模型为例(cifar10数据集),其训练脚本和推理脚本复用一个数据处理函数,通过do_train参数区分训练模式和推理模式。检查代码可以发现,do_train仅影响两个随机数据处理算子,在训练模式时使用,在推理模式下不使用,其余处理逻辑相同。因此检查结果为“无问题”。

    if do_train:
        trans += [
            C.RandomCrop((32, 32), (4, 4, 4, 4)),
            C.RandomHorizontalFlip(prob=0.5)
        ]

检查结论:

请填写

dp.03 训练时没有对数据集进行shuffle

检查方法:

检查训练脚本的数据处理代码中是否使能了shuffle功能。未进行shuffle,或者混洗不充分,会导致总是以相同的数据顺序更新模型,严重限制了梯度优化方向的可选择性,导致收敛点的选择空间变少,容易过拟合。shuffle功能的常见使能方式有如下几种,使用任意一种方式来使能shuffle均可:

  1. 创建数据集时,指定shuffle参数为True。例如 mindspore.dataset.Cifar10Dataset中的shuffle参数。

  2. 在数据处理的过程中,使用shuffle方法,例如mindspore.dataset.Cifar10Dataset.shuffle

  3. 如果使用了Sampler,还可以使能Sampler提供的shuffle功能。例如mindspore.dataset.PKSampler中的shuffle参数。

例子: 以ModelZoo中的resnet50为例(cifar10数据集),其在创建数据集时shuffle参数指定为True,因此检查结果为“无问题”。

    if device_num == 1:
        data_set = ds.Cifar10Dataset(dataset_path, num_parallel_workers=8, shuffle=True)
    else:
        data_set = ds.Cifar10Dataset(dataset_path, num_parallel_workers=8, shuffle=True,
                                     num_shards=device_num, shard_id=rank_id)

检查结论:

请填写

dp.04 涉及到数据补齐时,补齐方式错误

检查方法:

检查Padding的位置,方式,Padding的值是否同设计一致。

检查结论:

请填写

dp.05 并行训练时多节点分片方式错误

检查方法:

多节点数据预处理流程可能存在基于文件名、文件个数等进行分片的模式。该模式下由于文件读取接口在不同节点上对文件名排序的不同,会导致分片差异较大甚至文件重复分片到不同节点等不期望的结果。

检查结论:

请填写

常见超参问题

hp.01 学习率过大或过小

检查方法:

通过一个简单的实验可以初步确定学习率的范围。具体来讲,选定一个足够大的学习率范围(例如从0到0.5),设置训练运行几个epoch(例如10个),在训练运行过程中,逐迭代地线性(或指数)增大学习率,就可以得到一个准确率和学习率的关系曲线,如图。关注曲线中准确率开始上升和不再上升的部分所对应的学习率,可以将准确率开始上升时的学习率当做学习率的下限,将准确率不再上升时的学习率当做学习率的上限,在上下限之间的学习率都可以认为是合理的。

图1 学习率与准确率的关系。此曲线通过8个epoch的训练得到。图片引用自(Smith, 2017)

检查结论:

请填写

hp.02 epoch过大或过小

检查方法:

通过训练过程中的训练集loss曲线和测试集loss曲线可以帮助判断合理的epoch数目。一般来说,随着训练进行,训练集上的loss不断减小,而验证集上的loss先下降,后缓慢增加,应选择验证集loss最小时的epoch作为训练的最佳epoch数。

图2 epoch和loss的关系

检查结论:

请填写

hp.03 batch size过大

检查方法:

过大的batch size将会降低准确度。一般来讲,32是一个比较安全的值,64、128、256 也都值得尝试。建议先在较小的batch size和数据集上进行训练,得到准确度的基线。在大batch size的训练中关注准确率的变化情况,如果距离基线较远,说明batch size可能较大(也可能是学习率和batch size不匹配,一般在增大batch size时应同步增大learning rate,使batch size和learning rate的比例保持恒定)。

检查结论:

请填写

常见API使用问题

api.01 使用API时未注意到MindSpore API和其它框架API的差异

检查方法:

MindSpore API同其它框架的API存在一定差异。有标杆脚本的情况下,要特别注意(1)MindSpore脚本的参数初始化方式是否同标杆脚本相同(2)MindSpore中部分API的参数默认值,参数含义同其它框架不同。此处我们列举一些比较重要的差异供大家检查。

  1. MindSpore的Conv2d算子,默认没有bias(has_bias=False),而PyTorch的Conv2d算子,默认有bias。Conv2d算子的weight默认使用 Normal(0.0, 0.01),这一初始化方式和PyTorch(Uniform)、TensorFlow(Uniform)均不同。

  2. MindSpore的DropOut算子,参数含义为保留的概率(keep_prob),而PyTorch的DropOut算子,参数含义为丢弃的概率。

  3. MindSpore的BatchNorm中的动量默认值和PyTorch不同。PyTorch默认是0.1,MindSpore中默认值是0.9。

较完整的API差异列表请参考 https://gitee.com/mindspore/docs/blob/master/resource/api_mapping/api_mapping.md 。

例子:

以ModelZoo中的resnet50为例(cifar10数据集),结合API差异列表可知,此脚本中使用了如下可能存在差异的API:mindspore.nn.Conv2d,mindspore.nn.BatchNorm2d。

逐个确认可知,脚本中使用Conv2d时,显式指定了weight_init方式。脚本中使用BatchNorm2d时,显式指定了momentum参数。这些参数的值和设计相符,因此可以判定“无问题”。

检查结论:

请填写

api.02 使用API时未根据训练/推理场景对应设置参数

检查方法:

训练脚本中,未使用model.train API进行训练时需要进行本检查。

评估脚本中,未使用model.eval API进行评估时需要进行本检查。

推理脚本中,未使用model.predict API进行推理时需要进行本检查。

根据是否为训练场景,在调用模型时提前设置cell.set_train()。若为训练场景,应该先调用 cell.set_train(True),其它场景,则应该先调用cell.set_train(False)。

例子:

以ModelZoo中的resnet50为例(cifar10数据集),其在train.py和eval.py中分别使用的model.train和model.eval API进行训练和推理,在infer.py中,其在推理前显式调用net.set_train(False),因此检查结果为“无问题”。

检查结论:

请填写

常见计算图结构问题

为了检查计算图结构问题,请读者首先参考收集Summary数据,将计算图保存到summary文件中,然后使用MindInsight可视化查看计算图

检查结论:

请填写

cg.01 权重共享错误

检查方法:

权重共享错误,是指应该共享的权重未共享,或者不应该共享的权重共享了。在MindInsight计算图页面对感兴趣的权重进行抽检,选中权重节点,根据权重节点的输出节点可以判断权重是否被共享,观察输出节点的名称,可以得知输出节点大致对应的代码。节点名称以Default开头的,一般为正向图中的节点,需要重点关注。节点名称以Gradients开头或者含有optimizer的,一般为反向图或者优化器相关的节点,检查时可以忽略。例如,要检查Conv2d的权重,可以在计算图页面右上角的搜索框中键入conv2d,然后选择任意感兴趣的conv2d节点,接着找到该节点的输入权重节点,即可对该权重节点进行检查。

检查结论:

请填写

cg.02 权重冻结错误

检查方法:

对照代码检查权重的冻结情况是否同设计一致。有两种方法可以冻结权重,这两种方法在代码中都具有明显的特征。

方法一:设置Parameter的requires_grad参数为False。

方法二:使用stop_gradient阻止梯度继续向后传播,阻止所有会影响权重的梯度后,该权重的更新也就事实上被阻止了。

检查结论:

请填写

cg.03 节点连接错误

检查方法:

在MindInsight计算图页面自顶向下地对重要的模型元素进行检查,确保这些元素体现在了计算图中。例如,检查LeNet5是否存在节点连接错误时,可以逐层展开MindInsight计算图页面显示的计算图,确认conv、relu、fc等重要元素存在于计算图中,并且连接正确。Gradients命名空间下的计算图为MindSpore自动微分生成,检查时可以忽略。

检查结论:

请填写

cg.04 loss函数有误

检查方法:

使用MindSpore内置loss函数时,应检查使用的loss函数种类是否同设计相符。使用自定义loss函数时,应检查loss函数的代码实现是否和设计相符,必要时可以手动实现一个numpy版本,使用相同输入对比MindSpore版本的loss函数和numpy版本的loss函数输出是否一致。

检查结论:

请填写

常见权重初始化问题

wi.01 权重初始值全部为0

检查方法:

检查脚本中的权重(不含优化器中的状态)初始化方式(包括显式指定的权重初始值和部分mindspore.nn命名空间下API中默认初始化的权重),是否有将权重初始化为全零的情况。需要注意的是,部分参数,例如bias,初始化为全0是正确的。

检查结论:

请填写

wi.02 加载的预训练模型不正确

检查方法:

加载预训练模型/骨干模型时,要确保加载了正确的权重。当使用resnet等骨干模型时,要注意加载同设计相符,匹配使用场景的预训练模型。有标杆脚本时,要确保加载的预训练模型和标杆脚本能使用的预训练模型有相同的权重值。

检查结论:

请填写

常见混合精度和溢出问题

mp.01 训练中存在溢出问题

检查方法: 当使用混合精度训练,或者是使用Ascend AI处理器训练时,建议检查是否存在溢出问题。

使用GPU时,通过调试器中的“检查张量溢出”监测点可以进行溢出检查。

使用Ascend AI处理器时,使能溢出检查的详细方法请见异步Dump文档。使能溢出检查时,注意设置op_debug_mode为3,开启全部溢出检测功能。若在指定的目录存在算子溢出信息文件,则说明存在溢出问题,反之,则说明不存在溢出问题。

发现溢出问题后,应首先找到并分析第一个出现溢出的节点(对于Ascend的溢出数据,可以按文件名中的时间戳,找时间戳最小的一个;对于GPU上的溢出,只要找执行序中最靠前的一个),结合算子的输入输出数据确定溢出原因。

出现溢出问题后常见的解决措施如下:

  1. 使能动态loss scale功能,或者是合理设置静态loss scale的值,请参考LossScale。需要注意的是,直接将GPU场景中的静态loss scale用于Ascend上的训练时,可能会导致不期望的频繁溢出,影响收敛。loss scale使能后,可能需要多次实验以调整loss scale的初始值init_loss_scale、调整比例scale_factor、调整窗口scale_window等参数,直到训练中浮点溢出非常少,请参考DynamicLossScaleManager以了解这些参数的含义。

  2. 溢出问题对精度有关键影响且无法规避的,将相应的算子调整为FP32算子(调整后可能对性能有较大影响)。

检查结论:

请填写

mp.02 混合精度训练时,未正确设置loss scale

检查方法:

在使用混合精度时,应检查是否正确设置了loss scale,推荐优先使用动态loss scale。对于Ascend AI处理器上的训练,其在大部分情况下为混合精度训练。由于Ascend AI处理器计算特性与GPU混合精度计算特性存在差异,LossScaleManager超参也往往需要进行适当的调整以保证精度。当用户模型基于默认Loss Scale参数训练产生溢出的迭代过多,影响最终精度时,需要对Loss Scale参数进行适当调整,减少发生浮点异常的次数。

检查结论:

请填写

mp.03 loss scale和gradient clip的应用顺序不正确

检查方法:

如果同时使用了loss scale和gradient clip,需要进行此检查。请对照代码检查确认gradient clip的应用对象是除以loss scale后得到的原始梯度值。

检查结论:

请填写

mp.04 计算梯度惩罚时,没有先将梯度恢复为无loss scale的梯度

检查方法:

如果同时使用了loss scale和梯度惩罚(gradient penalty),需要进行此检查。检查确认计算梯度惩罚项时,输入的梯度为无loss scale的梯度。例如,可以先将代用loss scale的梯度除以loss scale,再用来计算梯度惩罚项。

检查结论:

请填写

以上是关于精度调优checklist的主要内容,如果未能解决你的问题,请参考以下文章

精度调优checklist

MindSpore模型精度调优实战:常用的定位精度调试调优思路

MindSpore模型精度调优实战:如何更快定位精度问题

GLSL-片段着色器不同部分的精度不同

超参数调优后精度保持不变

常用精度调优方法