自定义调试信息¶
Linux
Ascend
GPU
CPU
模型调优
中级
高级
概述¶
本文介绍如何使用MindSpore提供的Callback
、metrics
、Print
算子、日志打印等自定义能力,帮助用户快速调试训练网络。
Callback介绍¶
Callback
是回调函数的意思,但它其实不是一个函数而是一个类,用户可以使用回调函数来观察训练过程中网络内部的状态和相关信息,或在特定时期执行特定动作。
例如监控loss、保存模型参数、动态调整参数、提前终止训练任务等。
MindSpore的Callback能力¶
MindSpore提供Callback
能力,支持用户在训练/推理的特定阶段,插入自定义的操作。包括:
MindSpore框架提供的
ModelCheckpoint
、LossMonitor
、SummaryCollector
等Callback
类。MindSpore支持用户自定义
Callback
。
使用方法:在model.train
方法中传入Callback
对象,它可以是一个Callback
列表,例:
ckpt_cb = ModelCheckpoint()
loss_cb = LossMonitor()
summary_cb = SummaryCollector(summary_dir='./summary_dir')
model.train(epoch, dataset, callbacks=[ckpt_cb, loss_cb, summary_cb])
ModelCheckpoint
可以保存模型参数,以便进行再训练或推理。LossMonitor
可以在日志中输出loss,方便用户查看,同时它还会监控训练过程中的loss值变化情况,当loss值为Nan
或Inf
时终止训练。SummaryCollector
可以把训练过程中的信息存储到文件中,以便后续可视化展示。
在训练过程中,Callback
列表会按照定义的顺序执行Callback
函数。因此在定义过程中,需考虑Callback
之间的依赖关系。
自定义Callback¶
用户可以基于Callback
基类,根据自身的需求,实现自定义Callback
。
Callback
基类定义如下所示:
class Callback():
"""Callback base class"""
def begin(self, run_context):
"""Called once before the network executing."""
pass
def epoch_begin(self, run_context):
"""Called before each epoch beginning."""
pass
def epoch_end(self, run_context):
"""Called after each epoch finished."""
pass
def step_begin(self, run_context):
"""Called before each step beginning."""
pass
def step_end(self, run_context):
"""Called after each step finished."""
pass
def end(self, run_context):
"""Called once after network training."""
pass
Callback
可以把训练过程中的重要信息记录下来,通过一个字典类型变量cb_params
传递给Callback
对象,
用户可以在各个自定义的Callback
中获取到相关属性,执行自定义操作。也可以自定义其他变量传递给cb_params
对象。
cb_params
中的主要属性包括:
loss_fn
:损失函数optimizer
:优化器train_dataset
:训练的数据集cur_epoch_num
:当前的epoch数cur_step_num
:当前的step数batch_num
:一个epoch中step的数量…
用户可以继承Callback
基类自定义Callback
对象。
下面通过两个例子,进一步了解自定义Callback
的用法。
在规定时间内终止训练。
class StopAtTime(Callback): def __init__(self, run_time): super(StopAtTime, self).__init__() self.run_time = run_time*60 def begin(self, run_context): cb_params = run_context.original_args() cb_params.init_time = time.time() def step_end(self, run_context): cb_params = run_context.original_args() epoch_num = cb_params.cur_epoch_num step_num = cb_params.cur_step_num loss = cb_params.net_outputs cur_time = time.time() if (cur_time - cb_params.init_time) > self.run_time: print("epoch: ", epoch_num, " step: ", step_num, " loss: ", loss) run_context.request_stop()
实现逻辑为:通过
run_context.original_args
方法可以获取到cb_params
字典,字典里会包含前文描述的主要属性信息。 同时可以对字典内的值进行修改和添加,上述用例中,在begin
中定义一个init_time
对象传递给cb_params
字典。 在每次step_end
会做出判断,当训练时间大于设置的时间阈值时,会向run_context
传递终止训练的信号,提前终止训练,并打印当前的epoch
、step
、loss
的值。保存训练过程中精度最高的checkpoint文件。
class SaveCallback(Callback): def __init__(self, eval_model, ds_eval): super(SaveCallback, self).__init__() self.model = eval_model self.ds_eval = ds_eval self.acc = 0 def step_end(self, run_context): cb_params = run_context.original_args() result = self.model.eval(self.ds_eval) if result['accuracy'] > self.acc: self.acc = result['accuracy'] file_name = str(self.acc) + ".ckpt" save_checkpoint(save_obj=cb_params.train_network, ckpt_file_name=file_name) print("Save the maximum accuracy checkpoint,the accuracy is", self.acc)
具体实现逻辑为:定义一个
Callback
对象,初始化对象接收model
对象和ds_eval
(验证数据集)。在step_end
阶段验证模型的精度,当精度为当前最高时,自动触发保存checkpoint方法,保存当前的参数。
MindSpore metrics功能介绍¶
当训练结束后,可以使用metrics评估训练结果的好坏。
MindSpore提供了多种metrics评估指标,如:accuracy
、loss
、precision
、recall
、F1
。
用户可以定义一个metrics字典对象,里面包含多种指标,传递给model
对象,通过model.eval
来验证训练的效果。
metrics = {
'accuracy': nn.Accuracy(),
'loss': nn.Loss(),
'precision': nn.Precision(),
'recall': nn.Recall(),
'f1_score': nn.F1()
}
model = Model(network=net, loss_fn=net_loss, optimizer=net_opt, metrics=metrics)
result = model.eval(ds_eval)
model.eval
方法会返回一个字典,里面是传入metrics的指标和结果。
在eval过程中也可以使用Callback
功能,用户可以调用相关API或自定义Callback
方法实现想要的功能。
用户也可以定义自己的metrics
类,通过继承Metric
基类,并重写clear
、update
、eval
三个方法即可实现。
以Accuracy
算子举例说明其内部实现原理:
Accuracy
继承了EvaluationBase
基类,重写了上述三个方法。
clear
方法会把类中相关计算参数初始化。update
方法接受预测值和标签值,更新Accuracy
内部变量。eval
方法会计算相关指标,返回计算结果。
调用Accuracy
的eval
方法,即可得到计算结果。
通过如下代码可以更清楚了解到Accuracy
是如何运行的:
x = Tensor(np.array([[0.2, 0.5], [0.3, 0.1], [0.9, 0.6]]))
y = Tensor(np.array([1, 0, 1]))
metric = Accuracy()
metric.clear()
metric.update(x, y)
accuracy = metric.eval()
print('Accuracy is ', accuracy)
输出:
Accuracy is 0.6667
Print算子功能介绍¶
MindSpore的自研Print
算子可以将用户输入的Tensor或字符串信息打印出来,支持多字符串输入,多Tensor输入和字符串与Tensor的混合输入,输入参数以逗号隔开。目前Print
算子仅支持在Ascend环境下使用。
Print
算子使用方法与其他算子相同,在网络中的__init__
声明算子并在construct
进行调用,具体使用实例及输出结果如下:
import numpy as np
from mindspore import Tensor
import mindspore.ops as ops
import mindspore.nn as nn
import mindspore.context as context
context.set_context(mode=context.GRAPH_MODE)
class PrintDemo(nn.Cell):
def __init__(self):
super(PrintDemo, self).__init__()
self.print = ops.Print()
def construct(self, x, y):
self.print('print Tensor x and Tensor y:', x, y)
return x
x = Tensor(np.ones([2, 1]).astype(np.int32))
y = Tensor(np.ones([2, 2]).astype(np.int32))
net = PrintDemo()
output = net(x, y)
输出:
print Tensor x and Tensor y:
Tensor(shape=[2, 1], dtype=Int32, value=
[[1]
[1]])
Tensor(shape=[2, 2], dtype=Int32, value=
[[1 1]
[1 1]])
数据Dump功能介绍¶
训练网络时,若训练结果和预期有偏差,可以通过数据Dump功能保存算子的输入输出进行调试。
同步Dump功能使用方法¶
同步Dump同时支持GPU和Ascend上的图模式,暂不支持PyNative模式。在Ascend上面开启Dump的时候,待Dump的算子会自动关闭内存复用。在网络占用内存不大的情况下,请优先使用同步Dump。若开启同步Dump后出现设备内存不足的报错,请使用下一节里面的异步Dump。
创建配置文件
data_dump.json
。JSON文件的名称和位置可以自定义设置。
{ "common_dump_settings": { "dump_mode": 0, "path": "/absolute_path", "net_name": "ResNet50", "iteration": 0, "input_output": 0, "kernels": ["Default/Conv-op12"], "support_device": [0,1,2,3,4,5,6,7] }, "e2e_dump_settings": { "enable": true, "trans_flag": false } }
dump_mode
:设置成0,表示Dump出该网络中的所有算子;设置成1,表示Dump"kernels"
里面制定的算子。path
:Dump保存数据的绝对路径。net_name
:自定义的网络名称,例如:”ResNet50”。iteration
:指定需要Dump的迭代,若设置成0,表示Dump所有的迭代。input_output
:设置成0,表示Dump出算子的输入和算子的输出;设置成1,表示Dump出算子的输入;设置成2,表示Dump出算子的输出。该参数仅支持Ascend,GPU只能Dump算子的输出。kernels
:算子的名称列表。开启IR保存开关context.set_context(save_graphs=True)
并执行用例,从生成的IR文件获取算子名称。例如,device_target
为Ascend
时,可以从trace_code_graph_{graph_id}
中获取算子名称,device_target
为GPU
时,可以从hwopt_pm_7_getitem_tuple.ir
中获取算子全称。详细说明可以参照教程:借助IR图进行调试support_device
:支持的设备,默认设置成0到7即可;在分布式训练场景下,需要dump个别设备上的数据,可以只在support_device
中指定需要Dump的设备Id。enable
:开启E2E Dump,如果同时开启同步Dump和异步Dump,那么只有同步Dump会生效。trans_flag
:开启格式转换。将设备上的数据格式转换成NCHW格式。
指定Dump的json配置文件。
export MINDSPORE_DUMP_CONFIG={Absolute path of data_dump.json}
在网络脚本执行前,设置好环境变量;网络脚本执行过程中设置将会不生效。
在分布式场景下,Dump环境变量需要调用
mindspore.communication.management.init
之前配置。
执行用例Dump数据。
可以在训练脚本中设置
context.set_context(reserve_class_name_in_scope=False)
,避免Dump文件名称过长导致Dump数据文件生成失败。解析Dump数据。
同步Dump生成的数据文件是以
.bin
结尾的二进制文件,可以通过numpy.fromfile
读取解析。Dump路径的命名规则为:
{path}/{net_name}/device_{device_id}/iteration_{iteration}/
。Dump文件的命名规则为:
{算子名称}_{input_output_index}_{shape}_{data_type}_{format}.bin
。
下面以一个简单网络的Dump为例,Dump生成的文件:
/absolute_path/ResNet50/device_0/iteration_0/Default--Add-op1_input_0_shape_1_3_3_4_Float32_DefaultFormat.bin
。 其中Default--Add-op1
是算子名称,input_0
是{input_output_index}
,shape_1_3_3_4
是{shape}
,Float32
是{data_type}
,DefaultFormat
是{format}
。
异步Dump功能使用方法¶
异步Dump仅支持Ascend上的图模式,不支持PyNative模式。开启异步Dump的时候不会关闭内存复用。
创建配置文件
data_dump.json
。JSON文件的名称和位置可以自定义设置。
{ "common_dump_settings": { "dump_mode": 0, "path": "/absolute_path", "net_name": "ResNet50", "iteration": 0, "input_output": 0, "kernels": ["Default/Conv-op12"], "support_device": [0,1,2,3,4,5,6,7] }, "async_dump_settings": { "enable": true, "op_debug_mode": 0 } }
dump_mode
:设置成0,表示Dump出改网络中的所有算子;设置成1,表示Dump"kernels"
里面指定的算子。path
:Dump保存数据的绝对路径。net_name
:自定义的网络名称,例如:”ResNet50”。iteration
:指定需要Dump的迭代。非数据下沉模式下,iteration
需要设置成0,并且会Dump出每个迭代的数据。input_output
:设置成0,表示Dump出算子的输入和算子的输出;设置成1,表示Dump出算子的输入;设置成2,表示Dump出算子的输出。kernels
:算子的名称列表。开启IR保存开关context.set_context(save_graphs=True)
并执行用例,从生成的trace_code_graph_{graph_id}
IR文件中获取算子名称。kernels
仅支持TBE算子、AiCPU算子、通信算子,若设置成通信算子的名称,将会Dump出通信算子的输入算子的数据。详细说明可以参照教程:借助IR图进行调试support_device
:支持的设备,默认设置成0到7即可;在分布式训练场景下,需要dump个别设备上的数据,可以只在support_device
中指定需要Dump的设备Id。enable
:开启异步Dump,如果同时开启同步Dump和异步Dump,那么只有同步Dump会生效。op_debug_mode
:该属性用于算子溢出调试,设置成0,表示不开启溢出;设置成1,表示开启AiCore溢出检测;设置成2,表示开启Atomic溢出检测;设置成3,表示开启全部溢出检测功能。在Dump数据的时候请设置成0,若设置成其他值,则只会Dump溢出算子的数据。
设置数据Dump的环境变量。
export MINDSPORE_DUMP_CONFIG={Absolute path of data_dump.json}
在网络脚本执行前,设置好环境变量;网络脚本执行过程中设置将会不生效。
在分布式场景下,Dump环境变量需要调用
mindspore.communication.management.init
之前配置。
执行用例Dump数据。
可以在训练脚本中设置
context.set_context(reserve_class_name_in_scope=False)
,避免Dump文件名称过长导致Dump数据文件生成失败。解析文件。
Dump路径的命名规则为:
{path}/{device_id}/{net_name}_graph_{graph_id}/{graph_id}/{iteration}
。Dump文件的命名规则为:
{op_type}.{op_name}.{task_id}.{timestamp}
。
以一个简单网络的Dump结果为例:
Add.Default_Add-op1.2.161243956333802
,其中Add
是{op_type}
,Default_Add-op1
是{op_name}
,2
是{task_id}
,161243956333802
是{timestamp}
。使用run包中提供的
msaccucmp.pyc
解析Dump出来的文件。不同的环境上msaccucmp.pyc
文件所在的路径可能不同,可以通过find命令进行查找:find ${run包安装路径} -name "msaccucmp.pyc"
找到
msaccucmp.pyc
后,到/absolute_path
目录下,运行如下命令解析Dump数据:python ${msaccucmp.pyc的绝对路径} convert -d {原始的dump文件路径} -out {解析生成的文件路径}
若需要转换数据格式,可参考使用说明链接https://support.huaweicloud.com/tg-Inference-cann/atlasaccuracy_16_0013.html 。
Running Data Recorder¶
Running Data Recorder(RDR)是MindSpore提供训练程序运行时记录数据的功能。要记录的数据将会在MindSpore中进行预设,运行训练脚本时,如果MindSpore出现了运行异常,则会自动地导出MindSpore中预先记录的数据以辅助定位运行异常的原因。不同的运行异常将会导出不同的数据,比如出现Run task error
异常,将会导出计算图、图执行顺序、内存分配等信息以辅助定位异常的原因。
并非所有运行异常都会导出数据,目前仅支持部分异常导出数据。
使用方法¶
创建配置文件
mindspore_config.json
。{ "rdr": { "enable": true, "path": "/home/mindspore/rdr" } }
enable: 控制RDR功能是否开启 path: 设置RDR保存数据的路径。当前必须为绝对路径。
通过
context
配置RDR。context.set_context(env_config_path="./mindspore_config.json")
假如在Ascend 910上使用MindSpore进行训练,训练出现了
Run task error
异常。这时我们到
/home/mindspore/rdr
目录中,可以看到有几个文件出现在该目录中,每一个文件都代表着一种数据。 比如hwopt_d_before_graph_0.ir
该文件为计算图文件。可以使用文本工具打开该文件,用以查看计算图,分析计算图是否符合预期。
日志相关的环境变量和配置¶
MindSpore采用glog来输出日志,常用的几个环境变量如下:
GLOG_v
该环境变量控制日志的级别。
该环境变量默认值为2,即WARNING级别,对应关系如下:0-DEBUG、1-INFO、2-WARNING、3-ERROR。GLOG_logtostderr
该环境变量控制日志的输出方式。
该环境变量的值设置为1时,日志输出到屏幕;值设置为0时,日志输出到文件。默认值为1。GLOG_log_dir
该环境变量指定日志输出的路径。
若GLOG_logtostderr
的值为0,则必须设置此变量。
若指定了GLOG_log_dir
且GLOG_logtostderr
的值为1时,则日志输出到屏幕,不输出到文件。
C++和Python的日志会被输出到不同的文件中,C++日志的文件名遵从GLOG
日志文件的命名规则,这里是mindspore.机器名.用户名.log.日志级别.时间戳
,Python日志的文件名为mindspore.log
。MS_SUBMODULE_LOG_v
该环境变量指定MindSpore C++各子模块的日志级别。
该环境变量赋值方式为:MS_SUBMODULE_LOG_v="{SubModule1:LogLevel1,SubModule2:LogLevel2,...}"
。
其中被指定子模块的日志级别将覆盖GLOG_v
在此模块内的设置,此处子模块的日志级别LogLevel
与GLOG_v
的日志级别含义相同,MindSpore子模块的划分如下表。
例如可以通过GLOG_v=1 MS_SUBMODULE_LOG_v="{PARSER:2,ANALYZER:2}"
把PARSER
和ANALYZER
模块的日志级别设为WARNING,其他模块的日志级别设为INFO。
MindSpore子模块按照目录划分如下:
Source Files | Sub Module Name |
---|---|
mindspore/ccsrc/backend/kernel_compiler | KERNEL |
mindspore/ccsrc/backend/optimizer | PRE_ACT |
mindspore/ccsrc/backend/session | SESSION |
mindspore/ccsrc/common | COMMON |
mindspore/ccsrc/debug | DEBUG |
mindspore/ccsrc/frontend/operator | ANALYZER |
mindspore/ccsrc/frontend/optimizer | OPTIMIZER |
mindspore/ccsrc/frontend/parallel | PARALLEL |
mindspore/ccsrc/minddata/dataset | MD |
mindspore/ccsrc/minddata/mindrecord | MD |
mindspore/ccsrc/pipeline/jit/*.cc | PIPELINE |
mindspore/ccsrc/pipeline/jit/parse | PARSER |
mindspore/ccsrc/pipeline/jit/static_analysis | ANALYZER |
mindspore/ccsrc/pipeline/pynative | PYNATIVE |
mindspore/ccsrc/profiler | PROFILER |
mindspore/ccsrc/pybind_api | COMMON |
mindspore/ccsrc/runtime/device | DEVICE |
mindspore/ccsrc/transform/graph_ir | GE_ADPT |
mindspore/ccsrc/transform/express_ir | EXPRESS |
mindspore/ccsrc/utils | UTILS |
mindspore/ccsrc/vm | VM |
mindspore/ccsrc | ME |
mindspore/core/gvar | COMMON |
mindspore/core/ | CORE |
glog不支持日志文件的绕接,如果需要控制日志文件对磁盘空间的占用,可选用操作系统提供的日志文件管理工具,例如:Linux的logrotate。