究极小白详解pymarl框架的main.py函数

目录

sys与fd

实例化

params

config_ dict

logger

ex.run_commandline(params)


sys与fd

SETTINGS['CAPTURE_MODE'] = "sys" # set to "no" if you want to see stdout/stderr in console

此处为设置捕获模式,其实就是设定是什么系统,如果是windows系统,就用"sys",其它系统就要用"fd"。源码如下:

SETTINGS = FrozenKeyMunch.fromDict(
    {
        "CONFIG": {
            # make sure all config keys are compatible with MongoDB
            "ENFORCE_KEYS_MONGO_COMPATIBLE": True,
            # make sure all config keys are serializable with jsonpickle
            # THIS IS IMPORTANT. Only deactivate if you know what you're doing.
            "ENFORCE_KEYS_JSONPICKLE_COMPATIBLE": True,
            # make sure all config keys are valid python identifiers
            "ENFORCE_VALID_PYTHON_IDENTIFIER_KEYS": False,
            # make sure all config keys are strings
            "ENFORCE_STRING_KEYS": False,
            # make sure no config key contains an equals sign
            "ENFORCE_KEYS_NO_EQUALS": True,
            # if true, all dicts and lists in the configuration of a captured
            # function are replaced with a read-only container that raises an
            # Exception if it is attempted to write to those containers
            "READ_ONLY_CONFIG": True,
            # regex patterns to filter out certain IDE or linter directives
            # from inline comments in the documentation
            "IGNORED_COMMENTS": ["^pylint:", "^noinspection"],
            # if true uses the numpy legacy API, i.e. _rnd in captured functions is
            # a numpy.random.RandomState rather than numpy.random.Generator.
            # numpy.random.RandomState became legacy with numpy v1.19.
            "NUMPY_RANDOM_LEGACY_API": version.parse(opt.np.__version__)
            < version.parse("1.19")
            if opt.has_numpy
            else False,
        },
        "HOST_INFO": {
            # Collect information about GPUs using the nvidia-smi tool
            "INCLUDE_GPU_INFO": True,
            # Collect information about CPUs using py-cpuinfo
            "INCLUDE_CPU_INFO": True,
            # List of ENVIRONMENT variables to store in host-info
            "CAPTURED_ENV": [],
        },
        "COMMAND_LINE": {
            # disallow string fallback, if parsing a value from command-line
            # failed
            "STRICT_PARSING": False,
            # show command line options that are disabled (e.g. unmet
            # dependencies)
            "SHOW_DISABLED_OPTIONS": True,
        },
        # configure how stdout/stderr are captured. ['no', 'sys', 'fd']
        "CAPTURE_MODE": "sys" if platform.system() == "Windows" else "fd",
        # configure how dependencies are discovered. [none, imported, sys, pkg]
        "DISCOVER_DEPENDENCIES": "imported",
        # configure how source-files are discovered. [none, imported, sys, dir]
        "DISCOVER_SOURCES": "imported",
        # Configure the default beat interval, in seconds
        "DEFAULT_BEAT_INTERVAL": 10.0,
    },
)

其中,源码中的此键值对表明windows与其他系统区别

"CAPTURE_MODE": "sys" if platform.system() == "Windows" else "fd",

实例化

logger = get_logger()
ex = Experiment("yaml")
ex.logger = logger

获得logger方法,将Experiment实例化,并将logger方法赋给ex实例。

ex.captured_out_filter = apply_backspaces_and_linefeeds

设置输出文件的格式,避免有些实时输出(进度条等)不适合文件输出的形式

results_path = os.path.join(dirname(dirname(abspath(__file__))), "results")

dirname(abspath(__file__))是获得当前执行脚本的路径,abspath(__file__)是获得其绝对路径。如main.py在E:\fuxian\CADP-main\CADP-VD\src,则dirname(abspath(__file__))得输出即为:E:\fuxian\CADP-main\CADP-VD\src。dirname(dirname(abspath(__file__)))输出为上一条路径,为:E:\fuxian\CADP-main\CADP-VD,最后将results加入路径得到结果:results_path =E:\fuxian\CADP-main\CADP-VD\results。

params

if __name__ == '__main__':
    params = deepcopy(sys.argv)

 此时的params值为[E:\fuxian\CADP-main\CADP-VD\src\main.py],即只有脚本路径一个值的列表

python src/main.py --config=qmix --env-config=sc2 with env_args.map_name=2s3z

 在终端的命令语句如上,其实意思是将"--config=QMIX_CADP","--env-config=sc2"俩个值加入params这个列表中。最终params值为:[E:\fuxian\CADP-main\CADP-VD\src\main.py,"--config=QMIX_CADP","--env-config=sc2"]。其实也就相当于用append放入main.py函数中,如下:

    params.append("--config=QMIX_CADP")
    params.append("--env-config=sc2")
with env_args.map_name=2s3z

等同于将map_name=2s3z放入env,yaml文件中的env_args参数项下。

config_ dict

with open(os.path.join(os.path.dirname(__file__), "config", "default.yaml"), "r") as f:
        try:
            config_dict = yaml.load(f, Loader=yaml.FullLoader)
        except yaml.YAMLError as exc:
            assert False, "default.yaml error: {}".format(exc)

打开E:\fuxian\CADP-main\CADP-VD\src\config\default.yaml文件,并将里面的参数作为字典传给config_dict。

    # Load algorithm and env base configs
    env_config = _get_config(params, "--env-config", "envs")
    alg_config = _get_config(params, "--config", "algs")
def _get_config(params, arg_name, subfolder):
    config_name = None
    for _i, _v in enumerate(params):
        if _v.split("=")[0] == arg_name:
            config_name = _v.split("=")[1]
            del params[_i]
            break

    if config_name is not None:
        with open(os.path.join(os.path.dirname(__file__), "config", subfolder, "{}.yaml".format(config_name)), "r") as f:
            try:
                config_dict = yaml.load(f, Loader=yaml.FullLoader)
            except yaml.YAMLError as exc:
                assert False, "{}.yaml error: {}".format(config_name, exc)
        return config_dict

 检查params中是否有参数--env-config,如果有,就将等号后面的"sc2"传递给函数内的参数config_name。进入if判断后,打开E:\fuxian\CADP-main\CADP-VD\src\config\envs\sc2.yaml文件,将里面的参数作为字典,传给env_config。alg_config同。

# config_dict = {**config_dict, **env_config, **alg_config}
    config_dict = recursive_dict_update(config_dict, env_config)
    config_dict = recursive_dict_update(config_dict, alg_config)
def recursive_dict_update(d, u):
    for k, v in u.items():
        # 两个参数表示的意思为:
        # object – 实例对象,就相当于刚才例子中的2.
        # classinfo – 可以是直接或间接类名、基本类型或者由它们组成的元组。
        # 返回值:如果对象的类型与参数二的类型(classinfo)相同则返回 True,否则返回 False。
        new_var = collections.abc.Mapping
        if isinstance(v, new_var):
            # 通过key值获取字典内容并返回,如果没有key可以返回指定值
            d[k] = recursive_dict_update(d.get(k, {}), v)
        else:
            d[k] = v
    return d

 更新config_dict,将env_config这个字典作为值,“env_config”作为键,更新并入config_dict字典中。

# now add all the config to sacred
    ex.add_config(config_dict)

 将config_dict字典作为参数配置,传给ex实例中。

logger

# Save to disk by default for sacred
    logger.info("Saving to FileStorageObserver in results/sacred.")
    file_obs_path = os.path.join(results_path, "sacred")
    ex.observers.append(FileStorageObserver.create(file_obs_path))

 在终端输出一句等级为info的话"Saving to FileStorageObserver in results/sacred."

创建file_obs_path=E:\fuxian\CADP-main\CADP-VD\src\sacred

创建一个ex的观察者文件(其实就是写日志的),存储路径为file_obs_path。

ex.run_commandline(params)

 ex.run_commandline(params)
@ex.main
def my_main(_run, _config, _log):
    # Setting the random seed throughout the modules
    config = config_copy(_config)
    np.random.seed(config["seed"])
    th.manual_seed(config["seed"])
    config['env_args']['seed'] = config["seed"]

    # run the framework
    run(_run, config, _log)

ex.run_commandline(params)语句将ex中的参数配置,logger等信息汇总传给my_main函数,最后运行my_main函数,并且自动进行各种关键信息的输出,

@ex.main是一个装饰器,the @ex.main is just a short way of saying:my_main=ex.main(my_main)。相当于把my_main函数放进ex类的main方法中。最后的ex.run_commandline(params)语句也会调用此函数。

在my_main函数中,主要设置配置参数,随机数种子等。最后的run函数为训练等一系列函数。

具体ex.run_commandline(params)这行命令怎么调用的my_main函数,我debug了一下午,倒是有些眉目,但是里面给中嵌套调来调去,各种@装饰来装饰去,及其复杂,研究明白了可能会写ex.run_commandline(params)函数调用my_main详解。也可能因为过于复杂而不写。挖个坑吧。如果大家需要叫多,可留言。

(刚入坑多智能体的菜鸡博士一枚,请多多指教。 

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值