如何让SciPy minimize优化器的首个参数仅为奇数整数?
我尝试使用SciPy optimize模块的minimize函数对自定义代价函数进行优化:
result = minimize(cost_function, initial_params, args = (TEXT_IMG, BINARY_MASK), method = 'BFGS')
初始参数设置如下:
initial_params = np.array([31, 2, -7])
但minimize会在31附近迭代出浮点数,导致我的代价函数崩溃。我需要首个参数始终为奇数整数(例如31、29、33这类),想知道是否可以通过设置边界与约束实现这一点?另外我了解到可能需要改用differential_evolution替代minimize,希望确认该方案是否必要,或是否有其他解决办法。
代价函数代码:
def abs_diff(mask1, mask2): diff = np.abs(mask1 - mask2) mean_diff = np.mean(diff) return mean_diff def cost_function(params, text_img, binary_mask): blockSize, C, h = params hColor = -3 templateWSize = 5 searchWSize = 5 denoised = cv2.fastNlMeansDenoisingColored(text_img, h, hColor, templateWSize, searchWSize) gray_denoised = cv2.cvtColor(denoised, cv2.COLOR_BGR2GRAY) imgf = cv2.adaptiveThreshold(gray_denoised, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY_INV, blockSize, C) error = abs_diff(binary_mask * 255, imgf) return error
1. 临时修复:在代价函数中强制转换参数为奇数整数
这是最快的解决方式,无需更换优化器。在代价函数开头对第一个参数做处理,确保它是奇数整数:
def cost_function(params, text_img, binary_mask): # 将blockSize转为最近的奇数整数 blockSize = int(np.round(params[0])) if blockSize % 2 == 0: blockSize += 1 # 若为偶数则加1变奇数,也可根据需求改为减1 C, h = params[1], params[2] # 后续原有代码保持不变...
这种方式允许minimize正常迭代浮点数,但实际传入OpenCV函数的是处理后的合法奇数参数。缺点是代价函数会变成非光滑函数,可能影响BFGS这类依赖梯度的优化器的收敛效率,但如果参数范围不大,实际使用中往往能满足需求。
2. 变量映射配合整数约束(效果有限)
可以将第一个参数映射为整数变量:令blockSize = 2*k + 1,其中k为整数。此时优化变量变为[k, C, h],初始参数对应k=(31-1)/2=15,即initial_params = np.array([15, 2, -7])。之后在代价函数中计算blockSize = 2*k +1。
但BFGS仍会将k迭代为浮点数,因此最终还是需要在代价函数中将k转为整数,本质上和第一种方案类似,只是变量形式不同,没有解决根本问题。
3. 改用支持离散/整数变量的优化器(推荐长期方案)
BFGS这类基于梯度的优化器是为连续变量设计的,天生不适合处理离散约束。如果临时修复的收敛效果不理想,更换优化器是更合理的选择:
differential_evolution:支持通过integer参数指定哪些变量为整数,之后只需在代价函数中确保第一个整数参数为奇数即可。示例代码:
同时修改代价函数确保from scipy.optimize import differential_evolution # 定义参数边界,第一个参数范围设为合理的整数区间 bounds = [(1, 51), (-10, 10), (-10, 10)] # 指定第一个变量为整数 result = differential_evolution(cost_function, bounds, args=(TEXT_IMG, BINARY_MASK), integer=[0])blockSize为奇数:def cost_function(params, text_img, binary_mask): blockSize = params[0] if blockSize % 2 == 0: blockSize += 1 # 强制转为奇数 # 后续原有代码保持不变...- 此外,
shgo、dual_annealing等全局优化器也能更好地适配离散变量场景,可根据需求选择。
总结
- 若仅需快速解决问题,优先选择代价函数内强制转换参数的方案,无需更换优化器,但可能影响收敛效率。
- 若追求更稳定的优化效果,改用
differential_evolution这类支持整数变量的优化器是必要的,BFGS本质上无法妥善处理离散约束。
内容的提问来源于stack exchange,提问作者Javier Blanco

