我有以下代码尝试最小化对数似然函数。
#!/usr/bin/python
import math
import random
import numpy as np
from scipy.optimize import minimize
def loglikelihood(params, data):
(mu, alpha, beta) = params
tlist = np.array(data)
r = np.zeros(len(tlist))
for i in xrange(1,len(tlist)):
r[i] = math.exp(-beta*(tlist[i]-tlist[i-1]))*(1+r[i-1])
loglik = -tlist[-1]*mu
loglik = loglik+alpha/beta*sum(np.exp(-beta*(tlist[-1]-tlist))-1)
loglik = loglik+np.sum(np.log(mu+alpha*r))
return -loglik
atimes = [ 148.98894201, 149.70253172, 151.13717804, 160.35968355,
160.98322609, 161.21331798, 163.60755544, 163.68994973,
164.26131871, 228.79436067]
a= 0.01
alpha = 0.5
beta = 0.6
print loglikelihood((a, alpha, beta), atimes)
res = minimize(loglikelihood, (0.01, 0.1,0.1), method = 'BFGS',args = (atimes,))
print res
它给了我
28.3136498357
./test.py:17: RuntimeWarning: invalid value encountered in log
loglik = loglik+np.sum(np.log(mu+alpha*r))
status: 2
success: False
njev: 14
nfev: 72
hess_inv: array([[1, 0, 0],
[0, 1, 0],
[0, 0, 1]])
fun: 32.131359359964378
x: array([ 0.01, 0.1 , 0.1 ])
message: 'Desired error not necessarily achieved due to precision loss.'
jac: array([ -2.8051672 , 13.06962156, -48.97879982])
请注意,它根本没有设法优化参数,最小化值32大于28,这是你得到的a = 0.01,alpha = 0.5,beta = 0.6。通过选择更好的初始猜测可以避免这个问题,但如果是这样,我该如何自动完成?
答案 0 :(得分:14)
我复制了你的例子并试了一下。看起来如果你坚持使用BFGS求解器,经过几次迭代后,mu+ alpha * r
会有一些负数,这就是你获得RuntimeWarning的方法。
我能想到的最简单的解决方法是切换到Nelder Mead解算器。
res = minimize(loglikelihood, (0.01, 0.1,0.1), method = 'Nelder-Mead',args = (atimes,))
它会给你这个结果:
28.3136498357
status: 0
nfev: 159
success: True
fun: 27.982451280648817
x: array([ 0.01410906, 0.68346023, 0.90837568])
message: 'Optimization terminated successfully.'
nit: 92
答案 1 :(得分:3)
注意log()函数的负值,解决它们并通过添加惩罚来告诉优化器它们是坏的:
#!/usr/bin/python
import math
import random
import numpy as np
from scipy.optimize import minimize
def loglikelihood(params, data):
(mu, alpha, beta) = params
tlist = np.array(data)
r = np.zeros(len(tlist))
for i in xrange(1,len(tlist)):
r[i] = math.exp(-beta*(tlist[i]-tlist[i-1]))*(1+r[i-1])
loglik = -tlist[-1]*mu
loglik += alpha/beta*sum(np.exp(-beta*(tlist[-1]-tlist))-1)
argument = mu + alpha * r
limit = 1e-6
if np.min(argument) < limit:
# add a penalty for too small argument of log
loglik += np.sum(np.minimum(0.0, argument - limit)) / limit
# keep argument of log above the limit
argument = np.maximum(argument, limit)
loglik += np.sum(np.log(argument))
return -loglik
atimes = [ 148.98894201, 149.70253172, 151.13717804, 160.35968355,
160.98322609, 161.21331798, 163.60755544, 163.68994973,
164.26131871, 228.79436067]
a= 0.01
alpha = 0.5
beta = 0.6
print loglikelihood((a, alpha, beta), atimes)
res = minimize(loglikelihood, (0.01, 0.1,0.1), method = 'BFGS',args = (atimes,))
print res
答案 2 :(得分:2)
面对相同的警告,我通过重写对数似然函数来解决此问题,以获取log(params)
和log(data)
作为参数,而不是参数和数据。
因此,如果可能的话,我避免在似然函数或Jacobian中使用np.log()
。
答案 3 :(得分:1)
另一个对我有用的解决方案是将函数(和渐变)的值缩放到接近于0的值。例如,当我必须评估60k点的对数似然时,出现了我的问题。这意味着我的对数可能性很大。从概念上讲,对数似然是一个非常棘手的功能。
梯度从大(开始攀登这座尖峰山脉)开始,然后逐渐变小,但绝不小于BGFS例程中的默认gtol
参数(这是所有梯度必须低于以下阈值的阈值)终止)。另外,这时我实际上已经得出了正确的值(我使用的是生成的数据,所以我知道真实的值)。
正在发生的事情是我的渐变约为60k * average individual gradient value
,即使average individual gradient value
很小,也要说小于1e-8,还是说60k * 1e-8> gtol
。因此,即使我已经找到解决方案,但我从未满足过这个门槛。
从概念上讲,由于山峰非常陡峭,该算法进行了小步移动,但将移至的最小最小值却从未达到average individual gradient << 1e-8
,这意味着我的梯度从未低于{{1 }}。
两种解决方案:
1)按一个因数来缩放对数似然和梯度,例如gtol
,其中1/n
是样本数。
2)缩放n
:例如gtol