MLE для цензурированных распределений экспоненциального семейства

С пакетом scipy.stats легко подогнать распределение к данным, например scipy.stats.expon.fit () можно использовать для подгонки данных к экспоненциальному распределению.

Однако я пытаюсь подогнать данные к цензурированному / условному распределению в экспоненциальном семействе. Другими словами, используя MLE, я пытаюсь найти максимум

,

где представляет собой PDF-файл распределения в экспоненциальном семействе, а - соответствующий ему CDF.

Математически я обнаружил, что функция логарифмического правдоподобия является выпуклой в пространстве параметров , поэтому мой Предполагалось, что применить функцию scipy.optimize.minimize должно быть относительно просто. Обратите внимание на вероятность того, что, взяв мы получаем традиционную проблему MLE без цензуры.

Однако я считаю, что даже для простых дистрибутивов, например симплексный алгоритм Нелдера-Мида не всегда сходится, или что он сходится, но оценочные параметры далеки от истинных. Я прикрепил свой код ниже. Обратите внимание, что можно выбрать распределение, и что код достаточно общий, чтобы соответствовать параметрам loc и scale, а также необязательной shape параметры (например, бета- или гамма-распределение).

У меня вопрос: что я делаю не так, чтобы получить эти плохие оценки, или иногда возникают проблемы с конвергенцией? Я пробовал несколько алгоритмов, но, к моему удивлению, нет ни одного, который бы легко работал, поскольку проблема выпуклая. Есть ли проблемы с гладкостью, и что мне нужно найти способ использовать якобиан и гессиан в общем виде для этой проблемы?

Есть ли другие методы решения этой проблемы? Первоначально я думал переопределить функцию fit () в классе scipy.stats.rv, чтобы позаботиться о цензуре с помощью CDF, но это казалось довольно громоздким. Но поскольку проблема выпуклая, я бы предположил, что с помощью функции минимизации scipy я смогу легко получить результаты ...

Комментарии и помощь приветствуются!

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from scipy.stats import expon, gamma, beta, norm
from scipy.optimize import minimize
from scipy.stats import rv_continuous as rv


def log_likelihood(func: rv, delays, max_delays=10**8, **func_pars)->float:
    return np.sum(np.log(func.pdf(delays, **func_pars)+1) - np.log(func.cdf(max_delays, **func_pars)))


def minimize_log_likelihood(func: rv, delays, max_delays):

    # Determine number of parameters to estimate (always 'loc', 'scale', sometimes shape parameters)
    n_pars = 2 + func.numargs

    # Initialize guess (loc, scale, [shapes])
    x0 = np.ones(n_pars)

    def wrapper(params, *args):
        func = args[0]
        delays = args[1]
        max_delays = args[2]
        loc, scale = params[0], params[1]

        # Set 'loc' and 'scale' parameters
        kwargs = {'loc': loc, 'scale': scale}

        # Add shape parameters if existing to kwargs
        if func.shapes is not None:
            for i, s in enumerate(func.shapes.split(', ')):
                kwargs[s] = params[2+i]
        
        return -log_likelihood(func=func, delays=delays, max_delays=max_delays, **kwargs)

    # Find maximum of log-likelihood (thus minimum of minus log-likelihood; see wrapper function)
    return minimize(wrapper, x0, args=(func, delays, max_delays), options={'disp': True}, 
                    method='nelder-mead', tol=1e-8)




# Test code with by sampling from known distribution, and retrieve parameters
distribution = expon
dist_pars = {'loc': 0, 'scale': 4}
x = np.linspace(distribution.ppf(0.0001, **dist_pars), distribution.ppf(0.9999, **dist_pars), 1000)

res = minimize_log_likelihood(distribution, x, 10**8)
print(res)


person flow_me_over    schedule 06.10.2020    source источник
comment
В качестве дополнительного комментария: вообще очень сложно подобрать параметры местоположения с помощью MLE. Сохранение фиксированного параметра 'loc' решило все мои проблемы, и, поскольку указанная выше проблема является выпуклой, теперь я действительно получаю правильные решения для практически произвольных начальных значений x0.   -  person flow_me_over    schedule 22.10.2020


Ответы (1)


Я обнаружил, что сходимость плохая из-за неточностей в числах. Лучше всего заменить

np.log(func.pdf(x, **func_kwargs))

с участием

func.logpdf(x, **func_kwargs)

Это приводит к правильной оценке параметров. То же самое и с CDF. Документация scipy также указывает, что численная точность последнего работает лучше.

Все это прекрасно работает с экспоненциальным, нормальным, гамма-распределением и распределениями chi2. Бета-распределение по-прежнему вызывает у меня проблемы, но я думаю, что это снова некоторые (другие) числовые неточности, которые я проанализирую отдельно.

person flow_me_over    schedule 07.10.2020