使用Modelarts训练yolov5出现报错TypeError: modelarts_pre_process() missing 1 required positional argument:’args’

1 系统环境

硬件环境(Ascend/GPU/CPU): Ascend910
MindSpore版本: mindspore=2.1.0
执行模式(PyNative/ Graph): 不限
Python版本: Python=3.7
操作系统平台: linux

2 报错信息

2.1 问题描述

在Modelarts里面的训练作业里面训练yolov5,出现报错。使用的代码:https://gitee.com/mindspore/models/blob/master/official/cv/YOLOv5

2.2 报错信息

在default_config.yaml文件里面enable_modelarts参数是False的时候出现如下报错:

FileNotFoundError:[Error 2] No such file or directory:’/cache/data/annotations/instances_train2017.json’

但是在enable_modelarts为False的时候,在notbook环境下,是可以正常跑通训练的。把enable_modelarts改为True的时候,在训练界面,就可以找到路径了,但是会报下面的错误。

TypeError: modelarts_pre_process() missing 1 required positional argument:’args’

并且把enable_modelarts改为True的时候,在notbook环境下,也会报和上述相同的错误。

3 根因分析

1.default_config.yaml文件里面enable_modelarts参数是False 报FileNotFoundError:[Error 2] No such file or directory:’/cache/data/annotations/instances_train2017.json’


这个报错是因为代码走到了训练流程里面,因为环境里面没数据集导致。这个只要在对应位置放置数据集就行。
2.enable_modelarts改为True 报TypeError: modelarts_pre_process() missing 1 required positional argument:’args’

其实这里还没走到训练流程,这个是前置流程。
train.py 128行代码定义了moxing_wrapper这个装饰器

@moxing_wrapper(pre_process=modelarts_pre_process, post_process=modelarts_post_process, pre_args=[config])  
def run_train():

这个装饰器定义在 official/cv/YOLOv5/model_utils/moxing_adapter.py

def moxing_wrapper(pre_process=None, post_process=None, **kwargs):  
    """  
    Moxing wrapper to download dataset and upload outputs.  
    """  
    def wrapper(run_func):  
        @functools.wraps(run_func)  
        def wrapped_func(*args, **kwargs):  
            # Download data from data_url  
            if config.enable_modelarts:  
                if config.data_url:  
                    sync_data(config.data_url, config.data_path)  
                    print("Dataset downloaded: ", os.listdir(config.data_path))  
                if config.checkpoint_url:  
                    sync_data(config.checkpoint_url, config.load_path)  
                    print("Preload downloaded: ", os.listdir(config.load_path))  
                if config.train_url:  
                    sync_data(config.train_url, config.output_path)  
                    print("Workspace downloaded: ", os.listdir(config.output_path))  
    
                mindspore.set_context(save_graphs_path=os.path.join(config.output_path, str(get_rank_id())))  
                config.device_num = get_device_num()  
                config.device_id = get_device_id()  
                if not os.path.exists(config.output_path):  
                    os.makedirs(config.output_path)  
    
                if pre_process:  
                    if "pre_args" in kwargs.keys():  
                        pre_process(*kwargs["pre_args"])  
                    else:  
                        pre_process()  
    
            # Run the main function  
            run_func(*args, **kwargs)  
    
            # Upload data to train_url  
            if config.enable_modelarts:  
                if post_process:  
                    if "post_args" in kwargs.keys():  
                        post_process(*kwargs["post_args"])  
                    else:  
                        post_process()  
    
                if config.train_url:  
                    print("Start to copy output directory")  
                    sync_data(config.output_path, config.train_url)  
        return wrapped_func  
    return wrapper

出问题的是modelarts_pre_process 这个函数,该函数有一个参数,但是调用的时候并没有参数传递过去。
也就是下面的代码走到了else分支。

if "pre_args" in kwargs.keys():  
    pre_process(*kwargs["pre_args"])  
else:  
    pre_process()

为什么传递给装饰器的kwargs一开始是有值的,但是变成了空呢?
问题出在了official/cv/YOLOv5/model_utils/moxing_adapter.py
第136行

def moxing_wrapper(pre_process=None, post_process=None, **kwargs):

第142行

def wrapped_func(*args, **kwargs):

以及162行

if "pre_args" in kwargs.keys():
      
    def moxing_wrapper(pre_process=None, post_process=None, **kwargs):  
        """  
        Moxing wrapper to download dataset and upload outputs.  
        """  
        def wrapper(run_func):  
            @functools.wraps(run_func)  
            def wrapped_func(*args, **kwargs):  
    ...  
                if pre_process:  
                    if "pre_args" in kwargs.keys():  
                        pre_process(*kwargs["pre_args"])  
                    else:  
                        pre_process()

外层的moxing_wrapper和内层的wrapped_func都使用了kwargs这个参数名。
而if “pre_args” in kwargs.keys():这个判断明显的判断外层的装饰器输入的参数,而不是内层的被装饰的函数。

4 解决方案

修改文件 official/cv/YOLOv5/model_utils/moxing_adapter.py
修改重名的 kwargs ,另外一个可以用其他的名称代替。这里内部函数修改为wrap_kwargs

def moxing_wrapper(pre_process=None, post_process=None, **kwargs):  
    """  
    Moxing wrapper to download dataset and upload outputs.  
    """  
    def wrapper(run_func):  
        @functools.wraps(run_func)  
        def wrapped_func(*args, **wrap_kwargs):  
            # Download data from data_url  
            if config.enable_modelarts:  
                if config.data_url:  
                    sync_data(config.data_url, config.data_path)  
                    print("Dataset downloaded: ", os.listdir(config.data_path))  
                if config.checkpoint_url:  
                    sync_data(config.checkpoint_url, config.load_path)  
                    print("Preload downloaded: ", os.listdir(config.load_path))  
                if config.train_url:  
                    sync_data(config.train_url, config.output_path)  
                    print("Workspace downloaded: ", os.listdir(config.output_path))  
    
                mindspore.set_context(save_graphs_path=os.path.join(config.output_path, str(get_rank_id())))  
                config.device_num = get_device_num()  
                config.device_id = get_device_id()  
                if not os.path.exists(config.output_path):  
                    os.makedirs(config.output_path)  
    
                if pre_process:  
                    if "pre_args" in kwargs.keys():  
                        pre_process(*kwargs["pre_args"])  
                    else:  
                        pre_process()  
    
            # Run the main function  
            run_func(*args, **wrap_kwargs)  
    
            # Upload data to train_url  
            if config.enable_modelarts:  
                if post_process:  
                    if "post_args" in kwargs.keys():  
                        post_process(*kwargs["post_args"])  
                    else:  
                        post_process()  
    
                if config.train_url:  
                    print("Start to copy output directory")  
                    sync_data(config.output_path, config.train_url)  
        return wrapped_func  
    return wrapper