引 言 训练模型的配置参数在每次实验中都存在部分参数值需要略微调整的情况,如果只在运行主程序文件内调整参数值增加了实验的繁琐程度,严重降低用户体验。良好的配置系统应该具备以下特点:
- 方便修改实验所需的超参数
- 用户可以快捷管理项目和复现实验结果
- 项目结构清晰
实际项目中常见的配置系统:1) argparse.ArgumentParser 2) 使用yaml文件配置参数 3)使用yacs库结合yaml文件配置系统。argparse库提供了用户友好的命令行接口可以不用打开文件直接从命令行传递需要修改的配置参数,完美地解决了模型运行环境参数配置的难题。在本文中,主要介绍argparse模块实际使用场景中涉及的一些操作。若想全面了解该库可查阅argparse官方文档。
文章目录
一、创建ArgumentParser对象
语法:
import argparse #导入模块
parser = argparse.ArgumentParser(description='what the program does') #创建对象
参数description
描述本程序用途以及怎么使用。
parser = argparse.ArgumentParser(description='what the program does')
parser.print_help() #打印帮助信息
在命令行输入执行命令后,发现描述信息出现在界面中。
python ssss.py #主程序执行命令
二、add_argument()定义参数及parse_args()解析参数
创建对象后,调用add_argument()
函数定义模型需要使用的单个参数名称以及如何解析。
2.1 定义位置参数或者可选参数
- 定义位置参数语法
parser.add_argument(‘name’),直接输入参数名称name
,位置参数在参数解析过程中必须赋值否则会报错。
parser.add_argument('batch_size')
- 定义可选参数语法
parser.add_argument(‘-flag’,‘–flag’)
可以在参数名flag前添加前缀--
或-
,表示同一个可选参数的两种形式。例如示例中的-m
和--model
。
parser.add_argument('-m','--model')
parser.add_argument('--lr')
2.2 parse_args()参数解析
2.2.1 parse_args()参数赋值及解析的常用方法
定义的参数在命令行传递相应数值后,需调用parse_args()
函数解析参数并返回一个Namespace对象
,参数变成对象的属性。参数值匹配优先按照-
匹配(可选参数),剩余参数值依次赋给定义的位置参数。
parser = argparse.ArgumentParser(description='what the program does')
parser.add_argument('batch_size')
parser.add_argument('-m','--model')
parser.add_argument('--lr')
args = parser.parse_args()
print(args) #打印返回的命名空间对象
- 参数赋值方法一:参数赋值可以在命令行窗口中输入命令:
python ***.py [参数值列表]
参数值会自动解析成字符串,若想要参数值为int
或者float
可以在定义参数时设置关键字type
( 在下面小节详解[type属性] #2.3.6type属性
)。
- 参数赋值方法二:直接在主程序内部的
parse_args()
中添加参数值列表,
parser.parse_args([参数值列表])
传递的参数名和参数值必须为字符串,否则程序会报错。
args = parser.parse_args(['5','--model','lenet'])
print(args)
####结果
Namespace(batch_size='5', lr=None, model='lenet')
- 参数赋值方法三:在命令行输入与方法一相同的命令后,调用
parse_args(sys.argv[1:])
函数解析参数。
注:sys.argv列表的第一项是当前文件相对路径【相对于命令行窗口根目录】,从列表第二项开始为命令行传递的参数名和参数值。
import sys
print(sys.argv) #在主程序中打印argv列表
args = parser.parse_args(sys.argv[1:])
print(args)
注意:
在命令行输入携带参数值的命令后,主程序argv列表
显示结果为下面内容。由于参数在主程序中会解析成字符串,如果在命令行传递参数值为字符串,则会在主程序的的argv列表
中出现双层包裹的字符串,如红色方框标注的那样。命名空间对象中的属性值也是一个双层包裹的字符串。
注意:位置参数在命令行接口中必须赋值,不赋值会报错。可选参数若没有配置required=True时可以赋值也可以不赋值。
举例:在命令行中输入语句只给可选参数[model]赋值,位置参数[batcdh_size]没有赋值时程序报错。
2.2.2 parse_args()和parse_known_args()解析参数的区别
项目主程序脚本除了使用parser.parse_args()
函数解析参数值外,也可以调用parser.parse_known_args()
解析参数,下面会介绍两种解析方法的区别。
parser.parse_args()
只解析在parser.add_argument()
中定义的参数名。
在示例代码中,对未定义参数'--blocks'
赋值程序报错。
import argparse
parser = argparse.ArgumentParser(formatter_class=argparse.ArgumentDefaultsHelpFormatter)
parser.add_argument('-m','--model')
parser.add_argument('--lr')
args = parser.parse_args(['--blocks','10'])
usage: ssss.py [-h] [-m MODEL] [--lr LR]
ssss.py: error: unrecognized arguments: --blocks 10
parser.parse_known_args()
提高未定义参数的容错能力。
parser.parse_known_args()
函数的参数值列表中可以存在未定义的参数名,函数的返回值分为两部分,一个是解析参数的namespace,另一个是未知参数列表。
namespace, unknow_args = parser.parse_known_args([params])
示例代码:
import argparse
parser = argparse.ArgumentParser(formatter_class=argparse.ArgumentDefaultsHelpFormatter)
parser.add_argument('-m','--model')
parser.add_argument('--lr')
args,unknown_args = parser.parse_known_args(['--blocks','10','--lr','0.01','--model','lenet'])
print('known_args:',args)
print('unknown_args:',unknown_args)
##结果##
known_args: Namespace(lr='0.01', model='lenet')
unknown_args: ['--blocks', '10']
访问解析后的参数值可以直接通过args.参数名
方式。
print('model name:', args.model)
###
model name: lenet
2.3 定义参数的属性设置
2.3.1 default属性
在命令行或者parser.parse_args()函数中没有给可选参数赋值时,参数使用默认值。赋值时使用新值。
parser.add_argument('batch_size',default=32)
parser.add_argument('-m','--model',default='Resnet')
parser.add_argument('--lr')
args = parser.parse_args(['8'])
print(args)
###
Namespace(batch_size='8', lr=None, model='Resnet')
2.3.2 action属性
- action=‘store_true’或’store_false’,在命令行或者parser.parse_args()函数中没有出现参数名,参数使用默认bool值,出现参数名称时,动作为’store_true’时,参数bool值为True,动作为’store_false’参数时,bool值为False。
# 1.解析参数列表中出现参数名,use_gpu为True
parser.add_argument('--use_gpu',action='store_true',default=False)
args = parser.parse_args(['6','--use_gpu'])
print(args)
###
Namespace(batch_size='6', lr=None, model='Resnet', use_gpu=True)
# 2.解析参数列表中没有出现参数名,use_gpu为False
parser.add_argument('--use_gpu',action='store_true',default=False)
args = parser.parse_args(['6'])
print(args)
###
Namespace(batch_size='6', lr=None, model='Resnet', use_gpu=False)
- action='const’时,参数存储const所指定的数值。
parser.add_argument('--lr',action='store_const',const=0.01)
args = parser.parse_args(['--lr'])
print(args)
###
Namespace(lr=0.01)
- action='append’时,可以将多个参数值追加到一个列表中,适用于多次给指定选项赋参数值。
parser.add_argument('--lr',action='append')
args = parser.parse_args('--lr 0.1 --lr 0.01'.split())
print(args)
###
Namespace(lr=['0.1', '0.01'])
如果参数选项默认值为非空列表,默认值将出现在参数已解析值中,所有该选项的命令行参数值追加到默认值之后。
parser.add_argument('--lr',action='append',default=[0.2])
args = parser.parse_args(['--lr','0.1','--lr','0.01'])
print(args)
###
Namespace(lr=[0.2, '0.1', '0.01'])
- action='extend’方法于action='append’类似
2.3.3 choices属性
选项参数赋值必须为choices属性列表中的数值否则报错。
parser.add_argument('--dataset',choices=['imagenet','food101','cifar10'])
args = parser.parse_args(['6','--dataset','svhn'])
程序报错
2.3.4 required属性
为True时可选参数必须赋值,否则报错。
parser.add_argument('--epoch',default=5,required=True)
args = parser.parse_args(['6','--dataset','food101'])
2.3.5 nargs属性
确定选项在命令行中关联的参数数目,通常不设置该属性时关联一个参数。nargs属性常用数值为整数N,‘?’,‘*‘和’+’
- nargs=N, N个参数聚合到一个列表中。
parser.add_argument('--data_count',nargs=3)
args = parser.parse_args(['--epoch','10','--data_count','4','9','3'])
print(args)
###
Namespace(data_count=['4', '9', '3'], dataset='cifar10', epoch='10', lr=None, model='Resnet', use_gpu=False)
- nargs=‘?’,如果在命令行中不出现定义的选型名称时,选项直接使用默认值。如果在命令行中出现定义的名称没有跟随参数值时,使用const的参数值。否则从命令行中消耗选项名后的参数。
parser.add_argument('--data_count',nargs='?',default=3,const=10)
args = parser.parse_args([]) #命令行解析时没有出现选项名,使用默认值。
print(args) #Namespace(data_count=3)
args = parser.parse_args(['--data_count']) #命令行解析时出现选项名,后面没有接参数值,使用const数值。
print(args) #Namespace(data_count=10)
args = parser.parse_args(['--data_count','6']) #命令行解析时出现选项名,后面接参数值,直接给选项赋参数值。
print(args) #Namespace(data_count='6')
- nargs=‘*’,聚合0个或者多个参数到一个列表。
parser.add_argument('--data_count',nargs='*')
parser.add_argument('--model_number',nargs='*')
args = parser.parse_args('--data_count 3 6 9 --model_number net_1 net_2'.split())
print(args)
###
Namespace(data_count=['3', '6', '9'], model_number=['net_1', 'net_2'])
- nargs=‘+’,聚合1个或者多个参数到一个列表,当没有传递具体参数时会报错。
parser.add_argument('--data_count',nargs='+')
args = parser.parse_args(['--data_count'])
print(args)
###报错
ssss.py: error: argument --data_count: expected at least one argument
2.3.6 type属性
默认情况下,解析器会将命令行参数视作简单的字符串读入。在实际使用过程中,某些选项会被要求赋值为int或float等类型,使用type关键属性可以对参数进行类型检查并且将参数值字符串转换为指定类型(类型转换)。
parser.add_argument('--data',type=int) #将字符串参数值转换为整数
args = parser.parse_args(['--data','3'])
print(args)
###
Namespace(data=3)
参数赋予的字符串想要变成bool类型时,不建议直接将bool()作为类型转换器,只会将非空字符串转为True,空字符串转为False**
parser.add_argument('--data',type=bool)
args = parser.parse_args(['--data','3'])
print(args) #Namespace(data=True)
args = parser.parse_args(['--data',''])
print(args) #Namespace(data=False)
此时,可以编写str转换bool函数,对字符串参数值进行校验并转换成相应类型。为展示参数值字符串是否能够成功的解析成bool类型,下面列举了参数不设置type关键字和设置关键字type为str转换bool函数时候的参数值的不同表现,以及在参数解析后对参数值进行类型转换的示例代码。
- 示例代码1:不设置关键字参数type,参数值为字符串。
import argparse
parser = argparse.ArgumentParser()
parser.add_argument('--train',default='true')
args = parser.parse_args()
print(args)
print(type(args.train))
###
Namespace(train='true')
<class 'str'>
- 示例代码2:设置关键字参数
type=str2bool
,参数值从字符串变成bool类型,前提是需要手写str2bool()函数。
import argparse
def str2bool(v):
if v is None:
return v
elif type(v) == bool:
return v
elif type(v) == str:
if v.lower() in ('yes', 'true', 't', 'y', '1'):
return True
if v.lower() in ('no', 'false', 'f', 'n', '0'):
return False
raise argparse.ArgumentTypeError
parser = argparse.ArgumentParser()
parser.add_argument('--train',default='true',type=str2bool) #参数值类型转换
args = parser.parse_args()
print(args)
print(type(args.train))
###
Namespace(train=True)
<class 'bool'>
- 示例代码3:可以在参数解析完毕后调用类型转换函数
str2bool(args)
import argparse
def str2bool(args):
bool_list=['train','valid']
for arg in dir(args):
if arg in bool_list and getattr(args,arg) is not None:
if getattr(args,arg).lower() in ('yes', 'true', 't', 'y', '1'):
setattr(args,arg,True)
elif getattr(args,arg).lower() in ('no', 'false', 'f', 'n', '0'):
setattr(args, arg, False)
parser = argparse.ArgumentParser()
parser.add_argument('--train',default='true')
args = parser.parse_args()
str2bool(args) #调用参数值字符串转换bool函数
print(args)
print(type(args.train))
###
Namespace(train=True)
<class 'bool'>
- 示例代码4:调用类型转换函数
str2bool(args)
的简写版本,使用eval()
函数,但要求参数值必须书写为’True’或’False’。
import argparse
def str2bool(args):
bool_list=['train','valid']
for arg in dir(args):
if arg in bool_list and getattr(args,arg) is not None:
setattr(args,arg,eval(getattr(args,arg))) #调用eval()函数将str转换数据类型
parser = argparse.ArgumentParser()
parser.add_argument('--train',default='True')
args = parser.parse_args()
str2bool(args) #调用参数值字符串转换bool函数
print(args)
print(type(args.train))
###
Namespace(train=True)
<class 'bool'>
注意:eval()
函数可以处理动态代码,但需要注意安全问题。
2.3.7 dest属性
parse_args()函数解析ArgumentParser对象传递的命令行参数,在返回的命名空间对象中,属性的名称由dest属性指定。对于位置参数 ,dest属性名为参数名无须特别指定。对于可选参数,当不指定dest属性时,dest属性名优先使用第一个长选项字符串名字(‘–’开头的名字)作为属性名去掉名称前的--
,如果没有长选项字符串就使用第一个短选项字符串(‘-’开头的名字)。注意:字符串名称内部的-
连接符变成下划线确保成为有效字符串。
示例
parser.add_argument('batch_size',default=20,type=int) #属性名为batch_size
parser.add_argument('-m','--model-name','--model',default='Resnet') #属性名为第一个长选项model_name
parser.add_argument('-e','--epoch','--epoch-num',default=30) #属性名为第一个长选项epoch
parser.add_argument('-x','-y','--foo',default=1) #属性名为第一个长选项foo
parser.add_argument('-s','-t',default=10) #属性名为第一个短选项s
parser.add_argument('-learning-rate',default=0.1,dest='lr') #属性名为dest指定的lr
args = parser.parse_args('16 --model lenet'.split())
print(args)
###
Namespace(batch_size=16, epoch=30, foo=1, lr=0.1, model_name='lenet', s=10)
2.3.8 metavar属性
打印帮助信息时,将属性名替换为metavar指定的名称。
2.4 修改parser定义的默认参数值
除在命令行窗口中调用执行命令时对参数进行赋值操作修改在parser.add_argument()
中定义的参数默认值,也可以主程序中调用set_defaults(key=value)
方法修改默认值。
import argparse
parser = argparse.ArgumentParser()
parser.add_argument('--batch-size',default=4)
parser.add_argument('--norm',default='layernorm')
parser.set_defaults(norm='batchnorm') #修改参数norm的默认值
args = parser.parse_args()
print(args)
###
Namespace(batch_size=4, norm='batchnorm')
三、ArgumentParser()、argparse.Namespace()和parse_args()三者之间关系
- ArgumentParser()创建命令行解析器对象并通过
add_argument()
函数定义命令行参数。 - parse_args()解析命令行参数,将参数字符串转换为argparse.Namespace()中的属性,返回带有成员属性的命名空间对象。
import argparse
parser = argparse.ArgumentParser()
parser.add_argument('batch_size',default=20,type=int) #使用type关键字将参数字符串变成整数
parser.add_argument('-m','--model',default='Resnet')
parser.add_argument('--dataset',choices=['imagenet','food101','cifar10'],default='cifar10')
args = parser.parse_args(['16','--model','lenet','--dataset','cifar10'])
print(args)
###
Namespace(batch_size=16, dataset='cifar10', model='lenet')
- 可以直接使用argparse.Namespace()存储想要的命令行参数属性,省略
ArgumentParser()
对象的创建、add_argument()
函数定义命令行参数以及parse_args()
函数的解析过程。
args = argparse.Namespace(foo=16, model='lenet')
print(args)
###
Namespace(foo=16, model='lenet')
四 、打印解析后的args配置参数
如果想要以字典的形式查看解析后的参数名称和参数值。
- 直接调用
vars(返回的解析对象)
函数
parser = argparse.ArgumentParser()
parser.add_argument('batch_size',default=20,type=int) #使用type关键字将参数字符串变成整数
parser.add_argument('-m','--model',default='Resnet')
parser.add_argument('--dataset',choices=['imagenet','food101','cifar10'],default='cifar10')
args = parser.parse_args(['16','--model','lenet','--dataset','cifar10'])
print(vars(args)) #将Namespace()中的属性变成字典。
###
{'batch_size': 16, 'model': 'lenet', 'dataset': 'cifar10'}
- 使用内置函数
dir()
展示args参数名,调用getattr()
函数获取参数值。
args_dict={}
for arg in dir(args):
if not arg.startswith('_') and getattr(args,arg) is not None:
args_dict[arg] = getattr(args,arg)
print(args_dict)
###
{'batch_size': 16, 'dataset': 'cifar10', 'model': 'lenet'}
五、深度学习项目参数配置完整程序示例
import argparse
import sys
parser = argparse.ArgumentParser(description='what the program does')
# parser.print_help() #打印帮助信息
parser.add_argument('batch_size',default=20,type=int) #定义位置参数,用type将字符串解析成整数值
parser.add_argument('-m','--model',default='Resnet') #定义可选参数,有默认值可以不传参
parser.add_argument('--lr',action='append',default=[0.2]) #设置动作关键字,解析参数lr追加多个参数一个列表中,默认值为列表第一数值。
parser.add_argument('--use_gpu',action='store_false',default=False) #设置动作关键词, 解析参数时出现参数名,参数设置为动作属性否则使用默认值。
parser.add_argument('--dataset',choices=['imagenet','food101','cifar10'],default='cifar10') #参数传递值必须为choices列表中指定的名称
parser.add_argument('--epoch',default=5,type=int,required=True) #将可选参数变成必须传值的参数,用type参数字符串解析成整数。
parser.add_argument('--data_count',nargs='*') #参数解析时追加0个参数或者多个参数到列表
parser.add_argument('--model_number',nargs='+') #参数解析时追加1个参数或者多个参数到列表
args = parser.parse_args(['16','--model','lenet','--lr','0.01','--dataset','cifar10','--epoch','30','--model_number','3','5','7']) #可以直接在主程序中添加参数值
# print(sys.argv) #打印argv列表
# args = parser.parse_args(sys.argv[1:]) #使用命令行解析参数
print(args)
print('-'*50)
print(vars(args)) #以字典形式打印Namespace对象属性
print('-'*50)
args_new = argparse.Namespace(batch_size=15,epoch=30,lr=[0.3,0.1]) #直接在命名空间中设置属性值
print(args_new)
输出结果为
Namespace(batch_size=16, data_count=None, dataset='cifar10', epoch=30, lr=[0.2, '0.01'], model='lenet', model_number=['3', '5', '7'], use_gpu=False)
--------------------------------------------------
{'batch_size': 16, 'model': 'lenet', 'lr': [0.2, '0.01'], 'use_gpu': False, 'dataset': 'cifar10', 'epoch': 30, 'data_count': None, 'model_number': ['3', '5', '7']}
--------------------------------------------------
Namespace(batch_size=15, epoch=30, lr=[0.3, 0.1])
六、深度学习项目配置参数系列文章链接
文章标题————————       &nbs | 链接 ———————— |
---|---|
深度学习项目配置参数(一) argparse.ArgumentParser的使用教程 | https://blog.csdn.net/li1784506/article/details/139803120?spm=1001.2014.3001.5502 |
深度学习项目配置参数(二) yacs库的使用教程 | https://blog.csdn.net/li1784506/article/details/139831426?spm=1001.2014.3001.5502 |
深度学习项目配置参数(三)yaml和json配置文件的使用 | https://blog.csdn.net/li1784506/article/details/139857940?spm=1001.2014.3001.5502 |