Байесовская оценка среднего и стандартного отклонения (2D) - PullRequest
0 голосов
/ 31 мая 2019

Я в настоящее время следую за Think Bayes, вводным текстом к Байесовской статистике (http://www.greenteapress.com/thinkbayes/thinkbayes.pdf) и пытался повторить главу 10. В этой главе набор данных для высот используется для генерации байесовской модели для населения. среднее значение и стандартное отклонение для конкретного образца.

Идея состояла в том, чтобы сделать чистые вычислительные методы более устойчивыми с помощью различных способов, включая приблизительные байесовские вычисления, вероятности записи в журнал и ограничение диапазона предшествующих значений mu и sigma для результирующего распределения с использованием известных правил выборки (например, S = sigma /sqrt(n)).

Мой код, кажется, должен работать, но он дает мне ответы, которые явно неверны:

import numpy as np
import matplotlib.pyplot as plt
import scipy.stats as stats

def MakeRange(estimate, stderr, num_stderrs,num_points=100):
    spread = stderr * num_stderrs
    array = np.linspace(estimate-spread,estimate+spread,num_points)
    return array

def FindPriorRanges(xs, num_stderrs=3.0):
    # compute m and s
    n = len(xs)
    m = np.mean(xs)
    s = np.std(xs)
    # compute ranges for m and s
    stderr_m = s / np.sqrt(n)
    mus = MakeRange(m, stderr_m, num_stderrs)
    stderr_s = s / np.sqrt(2 * (n-1))
    sigmas = MakeRange(s, stderr_s, num_stderrs)
    return mus, sigmas

def Likelihood(x,mus,sigmas):

    like=np.nan_to_num(np.fromfunction(lambda x,a,b:stats.norm.pdf(x,mus[a],sigmas[b]), (len(x),len(mus),len(sigmas)),dtype=np.int))#stats.norm.pdf(x,mu,sigma)
    return like

def logLikelyhood(x,mu,sigma):
    like=Likelihood(x, mu, sigma)
    largest=np.max(like)
    return np.nan_to_num(np.log(like/largest))
    #his code also has something to remove 0 probability elements to prevent -Inf when you apply log

def expLogLikelyhood(logdist):
    m=np.max(logdist)
    return np.exp(logdist-m)


#note that this is extremely slow, and was made to ensure the algorithm is working exactly as expected
def approxBayesianComputation(data,mus,sigmas):
    n=len(data)
    m=np.mean(data)
    s=np.std(data)
    #he adds together the likelyhoods (log) of the mean and the sigma occuring given the particular dataset (rather than the exact data we got here (in terms of numbers))

    #would be two seperate apply along axes
    #muloglike=np.apply_along_axis(func1d, 0, arr)

    #very slow

    #using log likelyhood (same result as below)
#     arr=np.ones([len(mus),len(sigmas)])
#     for muind,mu in enumerate(mus):
#         for sigmaind,sigma in enumerate(sigmas):
#             stderr_m = sigma / np.sqrt(n)
#             loglike=np.log(stats.norm.pdf(m, mu, stderr_m))
#             stderr_s=sigma/np.sqrt(2 * (n-1))
#             loglike+=np.log(stats.norm.pdf(s, sigma, stderr_s))
#             arr[muind][sigmaind]=loglike
# 
#     return np.exp(arr)    

    #without log likelyhood
    arr=np.ones([len(mus),len(sigmas)])
    for muind,mu in enumerate(mus):
        for sigmaind,sigma in enumerate(sigmas):
            stderr_m = sigma / np.sqrt(n)
            loglike=stats.norm.pdf(m, mu, stderr_m)
            stderr_s=sigma/np.sqrt(2 * (n-1))
            loglike*=stats.norm.pdf(s, sigma, stderr_s)
            arr[muind][sigmaind]=loglike

    return arr      


def Normalize(arr):
    return arr/np.sum(arr)

#data=[1,2,3,4,5,6,7,8,2,3,4,5,6,7,3,3,4,4,1,1,1,1,1,1,1,1,1,1,1,1,12,2]*70
#data=[1,4,2,5,6,7,8,-3,-4,-5,-1]
#data=[1,1,1,1,1,1,2,2,2,0,0,0]
data=[1,2,3,4,6]
#data=[-2,0,2,3,4,5]
#data=[7,7,7,7,7,7,7,7,7,7,7,7,7.1]
#data=[0,-1,1,-2,2,-3,3,-4,4,-5,5,-6,6]
#data=[0,0,0,0]
#data=[5,5,5,5]

#all likely ranges for mu and sigma 
mus, sigmas=FindPriorRanges(data)

#sigmas=np.linspace(.01,10,100)
#mus=np.linspace(0,30,1000)

prior=np.ones([len(mus),len(sigmas)])

#make 2d distribution

multarray=Likelihood(data,mus,sigmas)


posterior=Normalize(np.multiply(prior,np.prod(multarray,axis=0)))

plt.imshow(posterior, cmap='gist_heat', interpolation='nearest',extent=[min(sigmas),max(sigmas),min(mus),max(mus)])#extent=[min(mus),max(mus),min(sigmas),max(sigmas)]
plt.show()


#using log likelyhoods for larger datasets (note that this works on large datasets where the above fails due to discretization error

multarray=logLikelyhood(data,mus,sigmas)
#the product of logs is the log of the sum
posterior=Normalize(np.multiply(prior,expLogLikelyhood(np.sum(multarray,axis=0))))

plt.imshow(posterior, cmap='gist_heat', interpolation='nearest',extent=[min(sigmas),max(sigmas),min(mus),max(mus)])
plt.show()


plt.imshow(approxBayesianComputation(data, mus, sigmas), cmap='gist_heat', interpolation='nearest',extent=[min(sigmas),max(sigmas),min(mus),max(mus)])
plt.show()

То, что делает эта программа, вычисляет диапазон mus, средств и сигм, стандартных отклонений, на основе конкретной выборки данных (названных данных в коде). В настоящее время он генерирует 100 точек по умолчанию для каждого параметра.

np.fromfunction используется для распространения массива, имеющего размеры (len(data),len(mus),len(sigmas)), используя обычную pdf-функцию scipy (в качестве точечной функции). Затем выполняется умножение вдоль оси данных для обновления распределения, которое затем умножается на предыдущее, которое я имею в качестве равномерного распределения. Для logLikelyhood процесс почти идентичен, за исключением того, что он складывается вдоль оси данных (сумма журналов - это журнал продукта), преобразует журнал обратно в стандартные вероятности путем возведения в степень, а затем умножает на предыдущее.

Однако, когда я запускаю код, я замечаю несколько странных вещей:

  • Распределение не центрировано в центре рассчитанных вероятных диапазонов для мю и сигмы, как я считаю, должно быть
  • По-видимому, распределение имеет правильный перекос для му, а также расширяющийся «хвост кометы» для сигмы при увеличении му. Обычно вы ожидаете, что он будет идеально эллиптическим.
  • Максимум mu и sigma в дистрибутиве - это не mu и sigma данных, что наверняка неправильно.
  • Для некоторых закомментированных распределений данных данные, похоже, даже не похожи на упомянутую ранее "комету" или эллипс.

И вероятность, и логоподобие дают одинаковые распределения последовательно. Тем не менее, я получаю некоторое деление на 0 ошибок, а для некоторых дистрибутивов с логарифмической вероятностью я получаю nan значений, которые я сгладил с помощью np.nan_to_num(). Однако даже для дистрибутивов, не имеющих ни одной из этих ошибок (которые, кажется, не оказывают существенного влияния на результат). Кроме того, approxBayesianComputation, предназначенный для прозрачности процесса, который происходил (он медленно умножает все с помощью python для циклов), дает ответы с аналогичными свойствами для более быстрых реализаций numpy.

...