目录
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详解。也可能因为过于复杂而不写。挖个坑吧。如果大家需要叫多,可留言。
(刚入坑多智能体的菜鸡博士一枚,请多多指教。