背景
我有一个函数,它接受一些参数并返回一个我想要最小化的错误度量(使用 scipy.optimize.leastsq
,但现在不在这一点上) .
作为一个玩具示例,让我们假设我的优化功能取四个参数a,b,c,d:
def f(a,b,c,d):
err = a*b - c*d
return err
然后,优化器需要一个带有签名 func(x, *args)
的函数,其中 x
是参数向量 .
也就是说,我的函数目前写成:
def f_opt(x, *args):
a,b,c,d = x
err = a*b - c*d
return err
但是,现在我想进行一些实验,其中我修复了一些参数,同时在优化步骤中保留了一些参数 .
我当然可以这样做:
def f_ad_free(x, b, c):
a, d = x
return f(a,b,c,d)
但这将是麻烦的,因为我有超过10个参数,这意味着不同数量的自由与固定参数的组合可能会非常大 .
使用dicts的第一种方法
我的一个解决方案是用关键字args而不是位置args编写我的内部函数 f
,然后像这样包装解决方案:
def generate(func, all_param, fixed_param):
param_dict = {k : None for k in all_param}
free_param = [param for param in all_param if param not in fixed_param]
def wrapped(x, *args):
param_dict.update({k : v for k, v in zip(fixed_param, args)})
param_dict.update({k : v for k, v in zip(free_param, x)})
return func(**param_dict)
return wrapped
创建一个修复'b'和'c'的函数然后变成以下内容:
all_params = ['a','b','c']
f_bc_fixed = generate(f_inner, all_params, ['b', 'c'])
a = 1
b = 2
c = 3
d = 4
f_bc_fixed((a,d), b, c)
提问时间!
我的问题是,是否有人能想出一个更简洁的方法来解决这个问题 . 由于最终函数将在优化步骤中运行,因此我不能接受每个函数调用的过多开销 . 生成优化函数所花费的时间是无关紧要的 .
2 回答
我可以想到几种避免使用闭包的方法,但是在做了一些测试之后,我不确定其中任何一个都会更快 . 一种方法可能是跳过包装器并只编写一个接受的函数
一个向量
免费名称列表
将名称映射到值的字典 .
然后做一些非常像你上面做的事情,但在函数本身:
对于多次使用变量名称的代码,请将vars置于本地,例如
等等 . 这可能看起来很麻烦,但它具有使一切都明确的优点,避免了可能使闭包变慢的命名空间搜索类型 .
然后通过
args
参数将自由名称列表和固定参数字典传递给optimize.leastsq
. (请注意,params
字典是可变的,这意味着理论上可能存在副作用;但在这种情况下它应该无关紧要,因为只有自由参数被update
覆盖,所以我省略了复制步骤速度 . )这种方法的主要缺点是它将一些复杂性转移到对
optimize.leastsq
的调用中,并且它使您的代码不再可重用 . 第二种方法避免了这些问题,尽管它可能不那么快:使用可调用类 .你可以看到我简化了
__init__
的参数结构;固定的参数在此处作为关键字参数传递,用户必须确保free_names
和fixed_params
没有重叠的名称 . 我认为简单性值得权衡,但您可以像在包装器代码中那样轻松地强制实现两者之间的分离 .我最喜欢第二种方法;它具有基于闭包的方法的灵活性,但我发现它更具可读性 . 所有的名字都在本地命名空间中(或者可以通过它访问),我认为这样可以加快速度 - 但经过一些测试后我认为有理由认为闭包方法仍然比这更快;访问
__call__
方法似乎每次调用开销增加约100 ns . 如果性能是一个真正的问题,我强烈建议测试 .你的
generate
函数与functools.partial
基本相同,这就是我在这里使用的 .