之前训练模型的时候认为,所code即所得,多香,我用的参数就在我写的代码之中,写好了train就行了,奥利给!后来发现需要经常改变参数,每次更改都要检查编写得对不对,很麻烦,索性把关心的、需要经常更改的参数,如data_generator的batch_size,model.compile()用的loss类型,全部提取成(超)参数,并从命令行输入。
经验来看,一大段train脚本,其实只有某些param是我们关心的,另外一些不是,或者一经设定就几乎保持不变。
我把不变量路径写成config,关心的或者说需要经常改变的变量写成参数。
./config/config.json的典型内容:
{
"ROOT_PATH": "YOUR_ROOT_PATH",
"TRAIN_DATA_DIR": "data\\origin\\train\\",
"TEST_DATA_DIR": "data\\origin\\test\\all_tests",
"SAMPLE_DATA_DIR": "data\\origin\\sample\\"
}
对于其他参数,则使用命令行输入,那么就需要argument parser工具读命令行参数。
Argument Parser工具
首先要有import一个option parser工具包,方便我们的脚本从命令行识别出参数并解析出参数值。
EfficientNet的官方代码用的是absl
我为了方便,就用Python的内置parser好了。Python官方有optparse和argparse两个内置库,其中optparse已经不更新的,故使用argparse,文档见
代码
新建一个train_params.py文件,输入代码:
import argparse
parser = argparse.ArgumentParser(
description='Process some training parameters we care.')
parser.add_argument('--start_epoch', type=int, dest='start_epoch',
action='store', default=0, help='start_epoch, i.e., epoches that have been trained, e.g. 80.') # 已经完成的训练数
parser.add_argument('--batch_size', type=int, dest='batch_size',
action='store', default=16, help='batch_size, e.g. 16.') # 16 for Mac, 64, 128 for server
parser.add_argument('--train_epochs', type=int, dest='train_epochs',
action='store', default=150, help='train_epochs, e.g. 150.') # training 150 epochs to fit enough
parser.add_argument('--if_fast_run', type=bool, dest='if_fast_run',
action='store', default=False, help='If set a fast run, the prog will train for 3 epochs.')
parser.add_argument('--loss', type=str, dest='loss',
action='store', default="bce", help='loss name, e.g., bce or cce.')
parser.add_argument('--exper_name', type=str, dest='exper_name',
action='store', default="ResNet56v2", help='exper_name, user named experiment name, e.g., ResNet56v2_BCE.')
parser.add_argument('--config_file', type=str, dest='config_file',
action='store', default="./config/config.json", help='config_file path, e.g., ./config/config.json.')
parser.add_argument('--ckpt', type=str, dest='ckpt',
action='store', default="", help='ckpt, model ckpt file.')
parser.add_argument('--positive_class', type=str, dest='positive_class',
action='store', default="bad_1", help='ckpt, model ckpt file, e.g. bad_1 or good_0.')
parser.add_argument('--alpha', type=float, dest='alpha',
action='store', default=0.99, help='alpha for focal loss if this loss is used.')
parser.add_argument('--n', type=int, dest='n',
action='store', default=6, help='n, order of ResNet, 2 or 6.')
parser.add_argument('--version', type=int, dest='version',
action='store', default=2, help='version, version of ResNet, 1 or 2.')
args = parser.parse_args()
print(args)
说明
add_argument()方法的几个参数:type:数据类型,与Python内置数据类型相同,直接传类型,如int、float、str,而老的optparse传的是字符串'int'、'float',相比之下,argparse更方便;
dest:喂给args = parser.parse_args()的args的目标即destination,对应参数会存储在args的名为dest参数的变量中;
action:选store就好了,存下来;
help:-h的时候打印的帮助信息。
更多文档,见Python argparse module的官方文档
测试解析出来的参数
输入python train_params.py -h,即使用帮助选项,-h是自带的参数,可以看到控制台整齐打印了参数列表。其中有参数说明,这些说明字符串对应add_argument()方法的 help 参数。
不指定任何参数地使用:
python train_params.py
返回一个Namespace(一个临时类)用于存储参数。可以看到默认的参数构成了args的数据成员,使用args的数据成员,就能在接下来的代码里设置训练脚本的参数了,诸如loss类型、训练回合数、batch_size、等等。
搬砖时的实际使用
使用命令行的另一个重要的原因,就是可以top -c在等training的时候确认自己输入的实验参数是什么,不用去代码里找了。top -c显示整个命令行(包括参数)命令而不只是显示命令名。
例如:
python train_params.py --loss=bce
就能把字符串"bce"选项丢给后续的选择loss的代码,而top -c则会在byobu中显示:
python train_params.py --loss=bce
这样一眼就能知道自己传入的loss参数是bce。对于其他实验同理:
python train_params.py --loss=bce --start_epoch=80 --train_epochs=200 --ckpt=ckpt_file_in_ckpt_folder
表示从已经训练了80个回合后的位置开始resume训练(下一个train epoch是81),总目标epochs数到200,记得找到第80个回合保留的ckpt。
这样实验进程的参数,服务器炼丹炉在做的事就一目了然了~。
参考资料https://docs.python.org/zh-cn/3/library/argparse.html#module-argparsedocs.python.org
参考