我是 Python 新手。如果我的代码感觉不舒服,我提前道歉。在过去的几周里,我一直在研究一项称为可扣除建模的困难统计挑战。我会尽量避免不必要的统计术语并保持问题简单,因为据我了解,我的问题是编程/优化问题,而不是统计问题。
如果您认为应该,请将此主题移至更合适的堆栈交换站点
我有 8 个参数,其中 2 个是受约束的,应该是正数(phi_freq
和phi_sev
)。我正在尝试最大化对数似然函数,该函数基本上是所有这些参数的非凸、多模态、连续、实值、不可微 (AFAIK) 函数。哎呀!
我知道这些问题意味着提供给搜索算法的种子值将对我们收敛到的局部/全局最优值产生巨大影响,但据我所知,我的起始值是可靠的,即使是硬编码的(在提供的示例中下面),是辅助优化的结果,而不仅仅是主观预感。
我曾尝试使用库的Nelder-Mead
andSLSQP
方法(SLSQP
已注释掉)scipy.optimize.minimize
,但返回的值很尴尬且毫无意义。
以下是 MWE:
from scipy.stats import gamma
from scipy import special
import pandas as pd
import numpy as np
import scipy as s
def freq_mean(intercept_freq, beta_veh_age_log, beta_dr_section_b):
return np.exp(intercept_freq + beta_veh_age_log * freq_data['veh_age_log'] + beta_dr_section_b * freq_data['dr_section_b'])
def sev_mean(intercept_sev, alpha_veh_age_log, alpha_acv_log, df):
return np.exp(intercept_sev + alpha_veh_age_log * df['veh_age_log'] + alpha_acv_log * df['acv_log'])
def freq_dist(x, mu, phi):
return (sp.special.gamma(phi + x) / sp.special.gamma(phi) / sp.special.factorial(x)) * ((phi / (phi + mu)) ** phi) * ((mu / (phi + mu)) ** x)
def sev_dist(x, mu, phi, ded):
gamma_pdf = (((phi / mu) ** phi) / sp.special.gamma(phi)) * (x ** (phi - 1.0)) * np.exp(-phi * x / mu)
gamma_sdf = 1.0 - sp.stats.gamma.cdf(x = phi * ded / mu, a = phi, scale = 1.0)
if gamma_sdf == 0.0:
return 0.0
else:
return gamma_pdf / gamma_sdf
def sev_loglik(intercept_sev, alpha_veh_age_log, alpha_acv_log, phi_sev):
sev_data['mu_sev'] = sev_mean(intercept_sev, alpha_veh_age_log, alpha_acv_log, sev_data)
return sev_data.apply(lambda x : np.log(sev_dist(x['claim_amount'], x['mu_sev'], phi_sev, x['deductible'])), axis = 1).sum()
def freq_loglik(intercept_freq, beta_veh_age_log, beta_dr_section_b, phi_freq, intercept_sev, alpha_veh_age_log, alpha_acv_log, phi_sev):
freq_data['mu_sev'] = sev_mean(intercept_sev, alpha_veh_age_log, alpha_acv_log, freq_data)
freq_data['claim_prob'] = 1.0 - sp.stats.gamma.cdf(x = phi_sev * freq_data['deductible'] / freq_data['mu_sev'], a = phi_sev, scale = 1.0)
freq_data['mu_freq'] = freq_mean(intercept_sev, beta_veh_age_log, beta_dr_section_b)
return freq_data.apply(lambda x : np.log(freq_dist(x['claim_count'], x['exposure'] * x['claim_prob'] * x['mu_freq'], x['exposure'] * phi_freq)), axis = 1).sum()
def obj_func(param):
intercept_freq, beta_veh_age_log, beta_dr_section_b, phi_freq, intercept_sev, alpha_veh_age_log, alpha_acv_log, phi_sev = param
ll_sev = sev_loglik(intercept_sev, alpha_veh_age_log, alpha_acv_log, phi_sev)
ll_freq = freq_loglik(intercept_freq, beta_veh_age_log, beta_dr_section_b, phi_freq, intercept_sev, alpha_veh_age_log, alpha_acv_log, phi_sev)
return -(ll_freq + ll_sev)
def mle(nfev = 100):
intercept_freq, beta_veh_age_log, beta_dr_section_b, phi_freq, intercept_sev, alpha_veh_age_log, alpha_acv_log, phi_sev = (-1.87515443, -0.5675389200, -0.0216802900, 23.78568667, 10.42040743, 0.00465891, 0.00072216, 0.69497075)
seeds = np.array([intercept_freq, beta_veh_age_log, beta_dr_section_b, phi_freq, intercept_sev, alpha_veh_age_log, alpha_acv_log, phi_sev])
return sp.optimize.minimize(fun = obj_func, x0 = seeds, method = 'Nelder-Mead', options = {'maxfev' : nfev})
#cons = ({'type': 'ineq', 'fun' : lambda x : x[3]}, {'type': 'ineq', 'fun' : lambda x : x[7]})
#return sp.optimize.minimize(fun = obj_func, x0 = seeds, method = 'SLSQP', constraints = cons)
policies = pd.read_csv('C:/policies.txt', sep = '\t')
claims = pd.read_csv('C:/claims.txt', sep = '\t')
freq_data = pd.merge(left = policies, right = claims.groupby('ID').agg(claim_count = ('claim_amount', 'count')), how = 'left', on = 'ID')
freq_data['claim_count'].fillna(0, inplace = True)
sev_data = pd.merge(left = claims[['ID', 'claim_amount']], right = policies.drop(['dr_section_b', 'exposure'], axis = 1), how = 'left', on = 'ID')
opt = mle(4000)
我不确定目前如何以及是否需要提供数据(示例代码中引用的policies
和claims
平面文件)。我准备这样做,但需要先将它们匿名化。
所以,我想此时任何指针都会受到欢迎,因为我快要碰壁了。有一个全局解决方案,必须有,这意味着mle
(最大似然估计)必须存在。我的种子值非常现实,因为它们是通过矩匹配(所谓的method of moments
估计器)获得的。我觉得一定有什么我做错了。此外,尽管听起来很蹩脚,但我什至使用 's 求解器重现了完全相同的问题Excel
,并遇到了类似的数值收敛问题。我很乐意提供有关此问题的补充细节,无论技术与否。