scipy.optimize.minimize 如何取损失函数的导数?
Posted
技术标签:
【中文标题】scipy.optimize.minimize 如何取损失函数的导数?【英文标题】:How does scipy.optimize.minimize take the derivative of the loss function? 【发布时间】:2020-10-08 14:19:42 【问题描述】:我试图弄清楚 scipy.optimize.minimize 在机器学习中的工作原理。到目前为止,我知道您向它传递了一个损失函数,以便它可以找到给您带来最低损失的参数值。但据我了解,它首先必须找到损失函数的梯度/Hessian,然后才能找到最小值。它是如何做到的?它是如何知道如何获取包含其他函数调用和算法的python函数的导数?它如何将其推导出为一个数学函数,它可以对其进行导数? 在我的情况下,我传递给最小化的损失函数执行以下操作: 首先,它解决了一个流行病学模型 (SEIRD),该模型旨在预测 COVID-19 的确诊病例和死亡人数。然后它将模型结果与实际数据进行比较,并找到它返回的 MSLE。代码如下所示:
def model_decay(params, data, population, return_solution=False, full_validation=True, forecast_days=0):
R_0, theta, k, L, R_t, D_rec, D_inc = params # R_0, fatality rate, k form, L shape, R end
N = population # Population of each country
n_infected = data['ConfirmedCases'].iloc[0]
n_dead = data['Fatalities'].iloc[0]
max_days = len(data) + forecast_days
rho = 1.0 / D_rec
alpha = 1.0 / D_inc
beta = R_0 * rho
y0 = N - n_infected, 0, n_infected, 0 ,0
def beta(t):
return ((R_0-R_t) / (1 + (t/L)**k)+R_t) * rho
t= np.arange(max_days)
# Solve the SEIR differential equation.
sol = solve_ivp(seir_d, [0, max_days],y0, args=(N, beta, rho, alpha, theta),t_eval=t)
sus, exp, inf, rec, dead = sol.y
#model_decay.counter +=1
#print(model_decay.counter)
# Predict confirmedcases
y_pred_cases = np.clip((inf + rec+dead),0,np.inf)
y_true_cases = data['ConfirmedCases'].values
# Predict fatalities
y_pred_dead = np.clip((dead),0,np.inf)
y_true_dead = data['Fatalities'].values
#Thanks to anjum-48 for the following lines of code in this function.
optim_days = min(20, len(data)) # Days to optimise for finds the lowest num.
weights = 1 / np.arange(1, optim_days+1)[::-1] # Recent data is more heavily weighted
# using mean squre log error to evaluate
msle_conf = mean_squared_log_error(y_true_cases[-optim_days:], y_pred_cases[-optim_days:],weights)
msle_dead = mean_squared_log_error(y_true_dead[-optim_days:], y_pred_dead[-optim_days:],weights)
if full_validation == True :
msle = np.mean([msle_conf,msle_dead])
else :
msle = msle_conf
if return_solution:
return msle, sol
else:
return msle
最小化调用如下所示:
model_decay_res = minimize(model_decay, [1.8, 0.04, 2, 20, 1 ,15, 4.9],
bounds=((0, 6.49), (0.01, 0.15), (0, 5), (0, 200),(0,6.49),(3.48,18),(4.9,5.9)),
args=(train_data, population, False,full_validation),
method='L-BFGS-B')
它最终给了我一个非常低的 MSLE,所以它工作得很好,我只是不明白到底是怎么回事。
【问题讨论】:
见scipy-doc 和wiki。在您使用'L-BFGS-B'
的情况下,我认为数值微分外包给求解器的基于fortran 的外部代码(而不是使用scipy 的代码)。当然有一些规则需要遵守:scipy.optimize 中的大多数求解器都假设您的目标是二次可微的,像 np.clip((inf + rec+dead),0,np.inf)
这样的东西可能会很吓人,而您的肯定结果是“运气”
【参考方案1】:
但据我了解,它首先必须找到损失函数的梯度/Hessian,然后才能找到最小值。它是怎么做到的?
这取决于您为参数提供的内容以及您在 python 环境中使用的 SciPy 版本。最后我检查了一些支持渐变的方法。 1.4.1 版本的文档在这里: https://docs.scipy.org/doc/scipy/reference/generated/scipy.optimize.minimize.html
显示
jaccallable, ‘2-point’, ‘3-point’, ‘cs’, bool, optional
并描述了哪些最小化器支持哪些。 您可以使用有限差分方案“2 点”,这是许多人的默认设置。
callable
表示您提供了一个实现了__call__
属性的函数或对象(独立函数默认具有此属性,对象没有)。如果你知道你已经分析计算并在 python 中实现的雅可比或梯度 b/c(或通过低级可调用的 C/C++:https://docs.scipy.org/doc/scipy-1.4.0/reference/generated/scipy.LowLevelCallable.html?highlight=lowlevelcallable#scipy.LowLevelCallable),你通常会使用它
cs
选项非常酷,这是我第一次开始使用 SciPy 时了解到的。基本上,您在目标函数中的所有操作都传递了具有非常小的复杂值(例如 0.0001)的复杂参数,然后取结果的复杂部分可以很好地估计梯度(优于有限差分)。
布尔值更多的是关于是否表示为稀疏或密集,并且并非在所有优化程序例程中都使用。
我应该澄清的一件事是minimize
例程只是转发到实际处理优化的相关优化器方法(例如'trust-constr'、'slsqp' 等)。
尤其是黑森州。
希望能帮助您了解 minimize
例程的“幕后”发生的事情。
它如何知道如何对包含其他函数调用和算法的 python 函数进行导数?
在您提供的情况下,我认为它只是调用函数并没有什么特别之处。
另外,作为旁注(不是您的两个问题中的任何一个),如果您想跟踪优化器的进度,您可以传递 disp=True
或 disp=1
,这应该会随着优化器的进展向您显示一些高级跟踪输出.
【讨论】:
以上是关于scipy.optimize.minimize 如何取损失函数的导数?的主要内容,如果未能解决你的问题,请参考以下文章
当 scipy.optimize.minimize 可能用于相同的事情时,为啥 scipy.optimize.least_squares 存在?
`scipy.optimize.minimize` 中的 Jacobian 和 Hessian 输入
scipy.optimize.minimize 选择无视约束的参数
为啥 scipy.optimize.minimize (默认)在不使用 Skyfield 的情况下报告成功?