Reputation: 748
How can I do a maximum likelihood regression using scipy.optimize.minimize
? I specifically want to use the minimize
function here, because I have a complex model and need to add some constraints. I am currently trying a simple example using the following:
from scipy.optimize import minimize
def lik(parameters):
m = parameters[0]
b = parameters[1]
sigma = parameters[2]
for i in np.arange(0, len(x)):
y_exp = m * x + b
L = sum(np.log(sigma) + 0.5 * np.log(2 * np.pi) + (y - y_exp) ** 2 / (2 * sigma ** 2))
return L
x = [1,2,3,4,5]
y = [2,3,4,5,6]
lik_model = minimize(lik, np.array([1,1,1]), method='L-BFGS-B', options={'disp': True})
When I run this, convergence fails. Does anyone know what is wrong with my code?
The message I get running this is 'ABNORMAL_TERMINATION_IN_LNSRCH'. I am using the same algorithm that I have working using optim
in R.
Upvotes: 11
Views: 23496
Reputation: 589
Better separate your model func. & objective func. to minimize SSE - sum of squared errors (or residuals) - in order to use with any other model. If you would like further to implement your stuff in the class for OOP - see S.O.L.I.D. principles here or here
Also, can use log-likelihood in the form of norm.logpdf(y, mu,sigma).sum()
from scipy.stats
rather than your manually defined Normal Law of residual's distribution.
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
# Define a dictionary containing employee data
d = {'maturity':[0.75, 2.5, 3., 5., 7.5, 7.9, 8., 12., 16.],
'price':[967.26, 834.21, 810.52, 769.30, 656.64, 639.71, 604.61, 502.11, 393.38]}
# Convert the dictionary into DataFrame
df = pd.DataFrame(d)
print(df)
x= df.maturity
y= df.price
from scipy.optimize import minimize
from scipy.stats import norm
x0= [0., 1., 1.]
# model
def mod(x, r, b):
return 1000* np.exp(-1*r*x) + b
# loss-function
def ll(theta):
r, b = theta[:-1]
## sigma = theta[-1]
## mu = np.dot(x,beta)
## ll = -N/2 * np.log(2*np.pi*sigma**2) - (1/(2*sigma**2)) * np.sum((y - mu)**2)
## return -1 * ll
sigma = np.exp(theta[-1])
for i in np.arange(0, len(x)):
mu = mod(x, r, b)
ll = norm.logpdf(y, mu,sigma).sum() # work with this version of log-likelihood rather than the one above
return -1 * ll
fit = minimize(ll, np.array(x0), method ='BFGS' )
print(fit.x)
##Formula: price ~ 1000 * exp(-r * maturity)
plt.scatter(x,y)
plt.plot(x, [mod(i, fit.x[0], fit.x[1]) for i in x])
plt.xlabel('time till maturity (maturity date)')
plt.ylabel('bond_price')
plt.show()
Nevertheless, scipy.optimize.minimize
not always works good for regression -- depending on init_x0, method chosen and data itself, methods with jacobian and hessian can increase the speed of convergence
P.S. for exponential-fit with LS better linearize xy relations - e.g. last code - though there still can exist "log of zero" problem - pay attention to estimator
Upvotes: 0
Reputation: 748
Thank you Aleksander. You were correct that my likelihood function was wrong, not the code. Using a formula I found on wikipedia I adjusted the code to:
import numpy as np
from scipy.optimize import minimize
def lik(parameters):
m = parameters[0]
b = parameters[1]
sigma = parameters[2]
for i in np.arange(0, len(x)):
y_exp = m * x + b
L = (len(x)/2 * np.log(2 * np.pi) + len(x)/2 * np.log(sigma ** 2) + 1 /
(2 * sigma ** 2) * sum((y - y_exp) ** 2))
return L
x = np.array([1,2,3,4,5])
y = np.array([2,5,8,11,14])
lik_model = minimize(lik, np.array([1,1,1]), method='L-BFGS-B')
plt.scatter(x,y)
plt.plot(x, lik_model['x'][0] * x + lik_model['x'][1])
plt.show()
Now it seems to be working.
Thanks for the help!
Upvotes: 11