Как scipy.optimize.minimize принимает производную от функции потерь? - PullRequest
1 голос
/ 18 июня 2020

Я пытаюсь понять, как scipy.optimize.minimize работает в машинном обучении. Пока я понимаю, что вы передаете ему функцию потерь, чтобы он мог найти значения параметров, которые дают вам наименьшие потери. Но, насколько я понимаю, он сначала должен найти градиент / гессиан функции потерь, прежде чем он сможет найти минимумы. Как оно это делает? Как он узнает, как принимать производные от функции 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 в конце, поэтому он работает очень хорошо, я просто не понимаю как именно.

1 Ответ

0 голосов
/ 20 июня 2020

Но, насколько я понимаю, сначала нужно найти градиент / гессиан функции потерь, прежде чем он сможет найти минимумы. Как он это делает?

Это зависит от того, что вы даете ему в качестве аргументов, и от того, какая версия SciPy у вас установлена ​​в вашей python среде. Последнее, что я проверил, есть несколько поддерживаемых методов для градиента. Документация для версии 1.4.1 здесь: https://docs.scipy.org/doc/scipy/reference/generated/scipy.optimize.minimize.html

показать

jac{callable, ‘2-point’, ‘3-point’, ‘cs’, bool}, optional

И описывает, какие минимизаторы поддерживают какие. Вы можете использовать двухточечную схему конечных разностей, которая используется по умолчанию для многих.

A callable означает, что вы предоставляете функцию или объект с реализованным атрибутом __call__ (в автономных функциях он есть по умолчанию, у объектов нет). Обычно вы используете это, если знаете якобиан или градиент b / c, который вы рассчитали аналитически и реализовали в python (или 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' et c.), Где фактически выполняется оптимизация. характерно для гессенцев.

Надеюсь, что это поможет вам понять, что происходит «под капотом» процедуры minimize.

Как он узнает, как принимать производные от python функции, которая содержит внутри себя другие вызовы функций и алгоритмы?

В случае, который вы предоставили, я думаю, что это просто вызов функции и ничего особенного.

Кроме того, в качестве примечания (не одного из ваших двух вопросов), если вы хотите отслеживать прогресс оптимизатора, вы можете передать disp=True или disp=1, и это должно показать вам некоторые результаты отслеживания высокого уровня как оптимизатор работает.

...