Up

ベイズ分析における点推定値例のRMSEbiasによる比較

――実験心理学の観点からの検討――

基礎心理学研究、第41巻、第1号、1-7

https://doi.org/10.14947/psychono.41.1

MAP推定値の算出スクリプト例

 

要約

 ベイズ分析におけるパラメータの点推定値としての事後分布の平均値,中央値およびMAP推定値を,真値との関係を表すbiasRMSE(2乗平均平方根誤差)の2つの指標で比較した。全体としての傾向は,MAP推定値が他の推定値よりも真値に近いという結果であった。比較は,2項分布の比率パラメータ,1変量正規分布の標準偏差パラメータ,2変量正規分布の相関係数パラメータにおいて行われた。これらの比較は単純なモデルについてであるが,MAP推定値は任意の最高密度区間に含まれていること,およびフラットな事前分布に対するMAP推定値は漸近的に真値に収束する最尤推定値に一致することを考えれば,MAP推定値は一般的に真値の推定値として適していることが示唆される。MCMCサンプリングからMAP推定値を算出する簡単なPythonスクリプトも示した。

 

 

本文中の図および表の作成に用いたプログラムを公開用に整理したものを以下に示す。これらのファイルは圧縮ファイルPointEstimates.zipにまとめたので、自由に利用されたい。

 

Figure 1のベータ分布の描画プログラム

リスト1のプログラムを実行すると、Figure 1のグラフを得る。

Figure 1

 

Figures 2, 3 and 4 のグラフ描画プログラム

リスト2のプログラムを実行すると、Figures 2, 3, 4を得る。

Figure 2

 

Figure 3

 

Figure 4

 

Table 1のデータの出力プログラム

リスト3のプログラムを実行すると以下の出力ファイルResultsCheck.txtを得る

*/n_samples

  20

*/Biases_mu_mean

 -0.007041961545885269

*/Biases_mu_median

 -0.006976374519543286

*/Biases_mu_map

 -0.007209074323586536

*/SEBiases_mu_mean

 0.007150514505840424

*/SEBiases_mu_median

 0.0071495556400130765

*/SEBiases_mu_map

 0.0071892461915072006

*/Biases_sgm_mean

 0.05242901233745667

*/Biases_sgm_median

 0.027014065601938864

*/Biases_sgm_map

 -0.014603021898702995

*/SEBiases_sgm_mean

 0.0053799409384119945

*/SEBiases_sgm_median

 0.005250154481094522

*/SEBiases_sgm_map

 0.005062170454121036

*/rms_mu_mean

 0.22611571609012457

*/rms_mu_median

 0.22608339060008384

*/rms_mu_map

 0.22734455413293206

*/se_rms_mu_mean

 0.04788157166709367

*/se_rms_mu_median

 0.04786410713297506

*/se_rms_mu_map

 0.04789616874476579

*/rms_sgm_mean

 1.066077692640595

*/rms_sgm_median

 1.0403338160886175

*/rms_sgm_map

 0.9983021329177043

(*/se_rms_sgm_mean

 0.1074652838233129

*/se_rms_sgm_median

 0.10488630620335115

*/se_rms_sgm_map

 0.10089430461605017

 

これらの値は、乱数を用いたシミュレーションによるものなので、プログラムの実行ごとに異なる値が得られるが、SEの推定値からランダムな変動の大きさが推測できる。

 

 

Tables 2 and 3のデータの出力プログラム

リスト5のプログラムを実行すると出力ファイルResultsN50.txtを得る。

*/bias_mean

 0.009920408994082704 0.03211452354398249 0.028884218740121793 -0.0026328761413996504 -0.026355866314786165 -0.012562849096223934 -0.011020725687830983

*/SE_bias_mean

 0.002166293613514624 0.006352606295812506 0.010099708755636351 0.009203343499895711 0.009020196954548528 0.006314623678249787 0.0022096789585528158

*/bias_median

 0.006090627925731067 0.024341765670074733 0.024130178778043306 -0.0028000087042905856 -0.021459953799896535 -0.004869294532790663 -0.007182361311745917

*/SE_bias_median

 0.0021051499843991453 0.00635456231224322 0.010251353698944778 0.00938546793307021 0.00915324564903438 0.006312780165466433 0.0021495696801992837

*/bias_MAP

 -0.0009005450272513482 0.008310915545777241 0.014341217060853001 -0.0029016450822541505 -0.011427571378569002 0.010712035601780039 -0.0003035151757588828

*/SE_bias_MAP

 0.0020021970253489233 0.0063518630703183025 0.01063789456229552 0.009772134749041317 0.009408831241654925 0.0062622276474516075 0.002017314661103084

*/RMSE_mean

 0.032129227979843716 0.09519510683657403 0.14537234044003708 0.12985583090725422 0.1299463672284583 0.0899602420227911 0.03306221307717857

*/SE_RMSE_mean

 0.011974161331843314 0.030981311084084282 0.044360908885495584 0.04001050562758937 0.04044402305213406 0.028420072573137268 0.012871139613899383

*/RMSE_median

 0.03031493663915568 0.09288828521289981 0.14661250222055833 0.1324279226785622 0.13089357850366642 0.08918574728806637 0.031162407259530567

*/SE_RMSE_median

 0.011252491414637482 0.030074829818920106 0.04466027762934707 0.04078827914170566 0.040889195247272775 0.028106586876200927 0.012099536376119631

*/RMSE_MAP

 0.028258817634165195 0.08998865509362647 0.1507496794350532 0.1378834596219362 0.1332189338985517 0.08898669132699984 0.02845934383291899

*/SE_RMSE_MAP

 0.010333327247190044 0.028340088036870655 0.04603693301893274 0.04251454110788044 0.04156855256467426 0.027453424591980552 0.01051948475436297

 

各行は、相関係数パラメータの値が−0.9から+0.9の範囲に対する結果が並んで出力されている。乱数を用いたシミュレーション結果であるので、プログラムの実行ごとに結果はランダムに変動するが、SEの推定値から変動の大きさが推測できる。

 

 

 

リスト1 Figure 1の作図

import numpy as np

import scipy.stats as ss

import matplotlib.pyplot as plt

import scipy.optimize as so

 

 

class Func_med:

    def __init__(self, f):

        self.f = f

    def v(self, t):

        #return self.f(t[0]) - 0.5

        return self.f(t) - 0.5

 

 

X = 1

N = 100

 

d_func = ss.beta(X+1, N-X+1)

 

t_range = np.linspace(0, 0.05)

t_v = d_func.pdf(t_range)

 

mean_v = (X+1) / (N+2)

mode_v = X / N

print('mean = ', mean_v)

print('mode = ', mode_v)

 

d_func = ss.beta(X+1, N-X+1)

func_med = Func_med(d_func.cdf)

f_med = func_med.v

median_v = so.bisect(f_med, 0.0, 1.0)

 

plt.title('Beta(θ|2,100)', fontsize = 16)

plt.plot([0, 0.05], [0.0, 0.0], color = 'k', ls = '-', lw = 1)

plt.plot(t_range, t_v, color = 'k', lw = 2)

plt.plot([mean_v, mean_v], [0, d_func.pdf(mean_v)], c = 'k', lw = 1.5,

         ls = '-.', label = f'mean:{mean_v:.4f}')

plt.plot([median_v, median_v], [0, d_func.pdf(median_v)], c = 'k', lw = 1.5,

         linestyle = (0, (5,1,2,1,2,1)), label = f'median:{median_v:.4f}')

plt.plot([mode_v, mode_v], [0, d_func.pdf(mode_v)], c = 'k',

         ls = '--', lw = 1.5, label = f'mode:{mode_v:.4f}')

plt.xlabel('θ', fontsize = 16)

plt.xticks(fontsize = 14)

plt.yticks([])

plt.legend(fontsize = 14)

plt.tight_layout()

plt.savefig('Figure1.png')

plt.show()

 

 

リスト2 Figures 2,3,4の作図

import numpy as np

import scipy.stats as ss

import matplotlib.pyplot as plt

import scipy.optimize as so

 

X = 1

N = 100

 

d_func = ss.beta(X+1, N-X+1)

 

t_range = np.linspace(0, 0.05)

t_v = d_func.pdf(t_range)

 

mean_v = (X+1) / (N+2)

mode_v = X / N

print('mean = ', mean_v)

print('mode = ', mode_v)

 

 

class Func_med:

    def __init__(self, f):

        self.f = f

    def v(self, t):

        return self.f(t) - 0.5

 

N = 100

 

p_true_range = [v/100 for v in range(1, 100)]

 

print(p_true_range)

 

bias_mean = []

bias_median = []

bias_mode = []

RMSE_mean = []

RMSE_median = []

RMSE_mode = []

RMSER_mean = []

RMSER_median = []

RMSER_mode = []

 

for p_true in p_true_range:

    print(p_true, end = '\r')

    v_sum_mean = 0.0

    v_sum_mode = 0.0

    v_sum_med = 0.0

    ssum_mean = 0.0

    ssum_mode = 0.0

    ssum_med = 0.0

    for X in range(0, N + 1):

        p = ss.binom.pmf(X, N, p_true)

        #print('p =', p)

        v_sum_mean += p * (X + 1) / (N + 2)

        v_sum_mode += p * X / N

        ssum_mean += p * ((X +1) / (N+2) - p_true) ** 2

        ssum_mode += p * ((X / N) - p_true) ** 2

 

       

        d_func = ss.beta(X+1, N-X+1)

        func_med = Func_med(d_func.cdf)

        f_med = func_med.v

        v_med = so.bisect(f_med, 0.0, 1.0)

        v_sum_med += p * v_med

        ssum_med += p * (v_med - p_true) ** 2

       

    bias_mean.append(v_sum_mean - p_true)

    bias_median.append(v_sum_med - p_true)

    bias_mode.append(v_sum_mode - p_true)

    RMSE_mean.append(ssum_mean ** 0.5)

    RMSE_median.append(ssum_med ** 0.5)

    RMSE_mode.append(ssum_mode ** 0.5)

    RMSER_mean.append((ssum_mean ** 0.5) / p_true)

    RMSER_median.append((ssum_med ** 0.5) / p_true)

    RMSER_mode.append((ssum_mode ** 0.5) / p_true)

 

 

N2 = 200

 

p_true_range = [v/100 for v in range(1, 100)]

 

print(p_true_range)

 

bias_mean2 = []

bias_median2 = []

bias_mode2 = []

RMSE_mean2 = []

RMSE_median2 = []

RMSE_mode2 = []

RMSER_mean2 = []

RMSER_median2 = []

RMSER_mode2 = []

 

for p_true in p_true_range:

    print(f'{p_true} for N2', end = '\r')

    v_sum_mean = 0.0

    v_sum_mode = 0.0

    v_sum_med = 0.0

    ssum_mean = 0.0

    ssum_mode = 0.0

    ssum_med = 0.0

    for X in range(0, N2 + 1):

        p = ss.binom.pmf(X, N2, p_true)

        v_sum_mean += p * (X + 1) / (N2 + 2)

        v_sum_mode += p * X / N2

        ssum_mean += p * ((X +1) / (N2+2) - p_true) ** 2

        ssum_mode += p * ((X / N2) - p_true) ** 2

 

       

        d_func = ss.beta(X+1, N2-X+1)

        func_med = Func_med(d_func.cdf)

        f_med = func_med.v

        v_med = so.bisect(f_med, 0.0, 1.0)

        v_sum_med += p * v_med

        ssum_med += p * (v_med - p_true) ** 2

       

    bias_mean2.append(v_sum_mean - p_true)

    bias_median2.append(v_sum_med - p_true)

    bias_mode2.append(v_sum_mode - p_true)

    RMSE_mean2.append(ssum_mean ** 0.5)

    RMSE_median2.append(ssum_med ** 0.5)

    RMSE_mode2.append(ssum_mode ** 0.5)

    RMSER_mean2.append((ssum_mean ** 0.5) / p_true)

    RMSER_median2.append((ssum_med ** 0.5) / p_true)

    RMSER_mode2.append((ssum_mode ** 0.5) / p_true)

 

plt.title('Bias', fontsize = 16)

plt.plot(p_true_range, bias_mean, c = 'k', ls = (0,(4,1)), lw = 2, label = 'mean(N=100)')

plt.plot(p_true_range, bias_median, c = 'k', ls = (0,(4,1,2,1)), lw = 2, label = 'median(N=100)')

plt.plot(p_true_range, bias_mean2, c = 'k', ls = (0,(4,1)), lw = 1, label = 'mean(N=200)')

plt.plot(p_true_range, bias_median2, c = 'k', ls = (0,(4,1,2,1)), lw = 1, label = 'median(N=200)')

plt.plot(p_true_range, bias_mode, c = 'k', ls = (0,(1,2)), lw = 2, label = 'MAP(N=100)')

plt.plot(p_true_range, bias_mode2, c = 'k', ls = (2,(2,2)), lw = 1, label = 'MAP(N=200)')

plt.xticks(fontsize = 14)

plt.xlabel(r'$\theta_0$', fontsize = 16)

plt.legend()

plt.tight_layout()

plt.savefig('Figure_2.png')

plt.show()

 

plt.title('RMS Error', fontsize = 16)

 

plt.plot(p_true_range, RMSE_mode, c = 'k', ls = (0,(1,2)), lw = 2, label = 'MAP(N=100)')

plt.plot(p_true_range, RMSE_median, c = 'k', ls = (0,(4,1,2,1)), lw = 2, label = 'median(N=100)')

plt.plot(p_true_range, RMSE_mean, c = 'k', ls = (0,(4,1)), lw = 2, label = 'mean(N=100)')

plt.plot(p_true_range, RMSE_mode2, c = 'k', ls = (2,(2,2)), lw = 1, label = 'MAP(N=200)')

plt.plot(p_true_range, RMSE_median2, c = 'k', ls = (0,(4,1,2,1)), lw = 1, label = 'median(N=200)')

plt.plot(p_true_range, RMSE_mean2, c = 'k', ls = (0,(4,1)), lw = 1, label = 'mean(N=200)')

plt.legend(fontsize = 12)

plt.xlabel(r'$θ_0$', fontsize = 13)

plt.tight_layout()

plt.savefig('Figure_3.png')

plt.show()

 

plt.title(r'RMS Error / $θ_0$', fontsize = 16)

 

plt.plot(p_true_range[:20], RMSER_mean[:20], c = 'k', ls = (0,(4,1)), lw = 2, label = 'mean(N=100)')

plt.plot(p_true_range[:20], RMSER_median[:20], c = 'k', ls = (0,(4,1,2,1)), lw = 2, label = 'median(N=100)')

plt.plot(p_true_range[:20], RMSER_mode[:20], c = 'k', ls = (0,(1,2)), lw = 2, label = 'MAP(N=100)')

plt.plot(p_true_range[:20], RMSER_mean2[:20], c = 'k', ls = (0,(4,1)), lw = 1, label = 'mean(N=200)')

plt.plot(p_true_range[:20], RMSER_median2[:20], c = 'k', ls = (0,(4,1,2,1)), lw = 1, label = 'median(N=200)')

plt.plot(p_true_range[:20], RMSER_mode2[:20], c = 'k', ls = (2,(2,2)), lw = 1, label = 'MAP(N=200)')

plt.xlabel(r'$\theta_0$', fontsize = 14)

plt.legend(fontsize = 14)

plt.tight_layout()

plt.savefig('Figure4.png')

plt.show()

 

 

リスト3 Table 11変量正規分布)の計算。リスト4Stanスクリプトを用いている。

import pystan

import numpy as np

import scipy.stats as ss

import matplotlib.pyplot as plt

import seaborn as sb

import pickle

import random

import tkinter as tk

 

sm = pystan.StanModel(file = 'OneVarNormal.stan')

 

with open('sm.pkl', 'wb') as f:

    pickle.dump(sm, f)

 

n_simu =  1000 

 

fout = open(f'ResultsCheck.txt', 'w')

 

Biases_mu_mean = []

Biases_mu_median = []

Biases_mu_map = []

SEBiases_mu_mean = []

SEBiases_mu_median = []

SEBiases_mu_map = []

Biases_sgm_mean = []

Biases_sgm_median = []

Biases_sgm_map = []

SEBiases_sgm_mean = []

SEBiases_sgm_median = []

SEBiases_sgm_map = []

 

rms_mu_mean = []

rms_mu_median = []

rms_mu_map = []

se_rms_mu_mean = []

se_rms_mu_median = []

se_rms_mu_map = []

 

rms_sgm_mean = []

rms_sgm_median = []

rms_sgm_map = []

se_rms_sgm_mean = []

se_rms_sgm_median = []

se_rms_sgm_map = []

 

n_samples = 20

 

mu_means = []

mu_medians = []

mu_maps = []

sgm_means = []

sgm_medians = []

sgm_maps = []

 

root = tk.Tk()

root.attributes('-topmost', True)

cnvs = tk.Canvas(root, bg = '#ffffff', width = 400, height = 200)

cnvs.pack()

cnvs.create_text(200, 100, text = 'Simulation started.',font = ('Gothic', 48), tags = 'mytext')

cnvs.update()

 

for trial in range(n_simu):

    cnvs.itemconfig('mytext', text = '{0}/{1}'.format(trial+1, n_simu))

    cnvs.update()

 

    X = ss.norm().rvs(size = n_samples)

    Data = {'N': len(X), 'X': X}

    with open('sm.pkl', 'rb') as f:

        sm = pickle.load(f)

    fit = sm.sampling(data = Data, control=dict(adapt_delta=0.97, max_treedepth = 30), n_jobs = 1)

 

    Smpl_mu = fit['mu']

    Mean_E = Smpl_mu.mean()

    mu_means.append(Mean_E)

    Med_E = np.percentile(Smpl_mu, 50)

    mu_medians.append(Med_E)

    L_mu = np.percentile(Smpl_mu, 2.5)

    U_mu = np.percentile(Smpl_mu, 97.5)

    n_points = 10000

    coord = np.linspace(L_mu, U_mu, n_points)

    est_pdf = ss.gaussian_kde(Smpl_mu).pdf(coord)

    map_idx = np.argmax(est_pdf)

    MAP_E = coord[map_idx]

    mu_maps.append(MAP_E)

 

    Smpl_sgm = fit['sgm']

    Mean_E = Smpl_sgm.mean()

    sgm_means.append(Mean_E)

    Med_E = np.percentile(Smpl_sgm, 50)

    sgm_medians.append(Med_E)

    L_sgm = 0.0 # Smpl_sgm.min()

    U_sgm = np.percentile(Smpl_sgm, 99)

    n_points = 10000

    coord = np.linspace(L_sgm, U_sgm, n_points)

    est_pdf = ss.gaussian_kde(Smpl_sgm).pdf(coord)

    map_idx = np.argmax(est_pdf)

    MAP_E = coord[map_idx]

    sgm_maps.append(MAP_E)

 

root.destroy()

 

mu_means = np.array(mu_means)

mu_medians = np.array(mu_medians)

mu_maps = np.array(mu_maps)

mu_mean_bias = mu_means.mean() - 0.0

mu_median_bias = mu_medians.mean() - 0.0

mu_map_bias = mu_maps.mean() - 0

mu_mean_se = ss.sem(mu_means)

mu_median_se = ss.sem(mu_medians)

mu_map_se = ss.sem(mu_maps)

 

Biases_mu_mean.append(mu_mean_bias)

Biases_mu_median.append(mu_median_bias)

Biases_mu_map.append(mu_map_bias)

SEBiases_mu_mean.append(mu_mean_se)

SEBiases_mu_median.append(mu_median_se)

SEBiases_mu_map.append(mu_map_se)

 

mss_mu_means = ((mu_means - 0.0) ** 2).mean()

rms_mu_mean.append(mss_mu_means ** 0.5)

t_sem = ss.sem((mu_means - 0.0) ** 2)

se_rms_mu_mean.append(t_sem ** 0.5)

 

mss_mu_medians = ((mu_medians - 0.0) ** 2).mean()

rms_mu_median.append(mss_mu_medians ** 0.5)

t_sem = ss.sem((mu_medians - 0.0) ** 2)

se_rms_mu_median.append(t_sem ** 0.5)

 

mss_mu_maps = ((mu_maps - 0.0) ** 2).mean()

rms_mu_map.append(mss_mu_maps ** 0.5)

t_sem = ss.sem((mu_maps - 0.0) ** 2)

se_rms_mu_map.append(t_sem ** 0.5)

 

sgm_means = np.array(sgm_means)

sgm_medians = np.array(sgm_medians)

sgm_maps = np.array(sgm_maps)

sgm_mean_bias = sgm_means.mean() - 1.0

sgm_median_bias = sgm_medians.mean() - 1.0

sgm_map_bias = sgm_maps.mean() - 1.0

sgm_mean_se = ss.sem(sgm_means)

sgm_median_se = ss.sem(sgm_medians)

sgm_map_se = ss.sem(sgm_maps)

 

Biases_sgm_mean.append(sgm_mean_bias)

Biases_sgm_median.append(sgm_median_bias)

Biases_sgm_map.append(sgm_map_bias)

SEBiases_sgm_mean.append(sgm_mean_se)

SEBiases_sgm_median.append(sgm_median_se)

SEBiases_sgm_map.append(sgm_map_se)

 

mss_sgm_means = ((sgm_means - 0.0) ** 2).mean()

rms_sgm_mean.append(mss_sgm_means ** 0.5)

t_sem = ss.sem((sgm_means - 0.0) ** 2)

se_rms_sgm_mean.append(t_sem ** 0.5)

 

mss_sgm_medians = ((sgm_medians - 0.0) ** 2).mean()

rms_sgm_median.append(mss_sgm_medians ** 0.5)

t_sem = ss.sem((sgm_medians - 0.0) ** 2)

se_rms_sgm_median.append(t_sem ** 0.5)

 

mss_sgm_maps = ((sgm_maps - 0.0) ** 2).mean()

rms_sgm_map.append(mss_sgm_maps ** 0.5)

t_sem = ss.sem((sgm_maps - 0.0) ** 2)

se_rms_sgm_map.append(t_sem ** 0.5)

 

fout.write('*/n_samples\n')

fout.write(f'  {n_samples}')

fout.write('\n')

fout.write('*/Biases_mu_mean\n')

for v in Biases_mu_mean:

    fout.write(f' {v}')

fout.write('\n')

fout.write('*/Biases_mu_median\n')

for v in Biases_mu_median:

    fout.write(f' {v}')

fout.write('\n')

fout.write('*/Biases_mu_map\n')

for v in Biases_mu_map:

    fout.write(f' {v}')

fout.write('\n')

fout.write('*/SEBiases_mu_mean\n')

for v in SEBiases_mu_mean:

    fout.write(f' {v}')

fout.write('\n')

fout.write('*/SEBiases_mu_median\n')

for v in SEBiases_mu_median:

    fout.write(f' {v}')

fout.write('\n')

fout.write('*/SEBiases_mu_map\n')

for v in SEBiases_mu_map:

    fout.write(f' {v}')

fout.write('\n')

fout.write('*/Biases_sgm_mean\n')

for v in Biases_sgm_mean:

    fout.write(f' {v}')

fout.write('\n')

fout.write('*/Biases_sgm_median\n')

for v in Biases_sgm_median:

    fout.write(f' {v}')

fout.write('\n')

fout.write('*/Biases_sgm_map\n')

for v in Biases_sgm_map:

    fout.write(f' {v}')

fout.write('\n')

fout.write('*/SEBiases_sgm_mean\n')

for v in SEBiases_sgm_mean:

    fout.write(f' {v}')

fout.write('\n')

fout.write('*/SEBiases_sgm_median\n')

for v in SEBiases_sgm_median:

    fout.write(f' {v}')

fout.write('\n')

fout.write('*/SEBiases_sgm_map\n')

for v in SEBiases_sgm_map:

    fout.write(f' {v}')

fout.write('\n')

 

fout.write('*/rms_mu_mean\n')

for v in rms_mu_mean:

    fout.write(f' {v}')

fout.write('\n')

fout.write('*/rms_mu_median\n')

for v in rms_mu_median:

    fout.write(f' {v}')

fout.write('\n')

fout.write('*/rms_mu_map\n')

for v in rms_mu_map:

    fout.write(f' {v}')

fout.write('\n')

fout.write('*/se_rms_mu_mean\n')

for v in se_rms_mu_mean:

    fout.write(f' {v}')

fout.write('\n')

fout.write('*/se_rms_mu_median\n')

for v in se_rms_mu_median:

    fout.write(f' {v}')

fout.write('\n')

fout.write('*/se_rms_mu_map\n')

for v in se_rms_mu_map:

    fout.write(f' {v}')

fout.write('\n')

fout.write('*/rms_sgm_mean\n')

for v in rms_sgm_mean:

    fout.write(f' {v}')

fout.write('\n')

fout.write('*/rms_sgm_median\n')

for v in rms_sgm_median:

    fout.write(f' {v}')

fout.write('\n')

fout.write('*/rms_sgm_map\n')

for v in rms_sgm_map:

    fout.write(f' {v}')

fout.write('\n')

fout.write('(*/se_rms_sgm_mean\n')

for v in se_rms_sgm_mean:

    fout.write(f' {v}')

fout.write('\n')

fout.write('*/se_rms_sgm_median\n')

for v in se_rms_sgm_median:

    fout.write(f' {v}')

fout.write('\n')

fout.write('*/se_rms_sgm_map\n')

for v in se_rms_sgm_map:

    fout.write(f' {v}')

fout.write('\n')

 

fout.close()

 

 

リスト4 1変量正規分布モデルのStanスクリプト

data {

    int N;

    real X[N];

}

parameters {

    real mu;

    real<lower = 0.0> sgm;

}

model {

    mu ~ normal(0.0, 1000.0);

    sgm ~ exponential(0.001);

    for (i in 1:N) {

        X[i] ~ normal(mu, sgm);

    }

}

 

 

 

リスト5 Tables 2, 3(相関係数)の計算。リスト6Stanスクリプトを用いている。

import pystan

import numpy as np

import scipy.stats as ss

import matplotlib.pyplot as plt

import seaborn as sb

import pickle

import random

import tkinter as tk

 

sm = pystan.StanModel(file = 'TwoVarNormal.stan')

 

with open('sm.pkl', 'wb') as f:

    pickle.dump(sm, f)

 

n_samples = 50

n_simu =  200

rhos = [-0.9, -0.6, -0.3, 0.0, 0.3, 0.6, 0.9]

 

 

bias_mean = []

bias_median = []

bias_MAP = []

SE_bias_mean = []

SE_bias_median = []

SE_bias_MAP = []

RMSE_mean = []

RMSE_median = []

RMSE_MAP = []

SE_RMSE_mean = []

SE_RMSE_median = []

SE_RMSE_MAP = []

 

 

root = tk.Tk()

root.attributes('-topmost', True)

cnvs = tk.Canvas(root, bg = '#ffffff', width = 400, height = 300)

cnvs.pack()

cnvs.create_text(200, 150, text = 'Simulation started.',font = ('Gothic', 32), tags = 'mytext')

cnvs.update()

 

for rho_true in rhos:

    #print('rho =', rho_true)   

    s_bias_mean = 0.0

    s_bias_median = 0.0

    s_bias_MAP = 0.0

    se_s_bias_mean = []

    se_s_bias_median = []

    se_s_bias_MAP = []

    ss_mean = 0.0

    ss_median = 0.0

    ss_MAP = 0.0

    se_ss_mean = []

    se_ss_median = []

    se_ss_MAP = []

    Means = []

    Medians = []

    MAPs = []

    for trial in range(n_simu):

       

        cnvs.itemconfig('mytext', text = f'rho = {rho_true}\ntrial = {trial+1}/{n_simu}')

        cnvs.update()

       

        X = 0

        u = np.random.normal(size = n_samples)

        v = np.random.normal(size = n_samples)

        random.shuffle(u)

        random.shuffle(v)

        t = u * ((1.0 - (rho_true**2))**0.5) + v * rho_true

        X = np.vstack([t, v]).T

       

        Data = {'N': len(X), 'X': X}

        sm = 0

        with open('sm.pkl', 'rb') as f:

            sm = pickle.load(f)

        fit = 0

        fit = sm.sampling(data = Data,  iter = 4000, n_jobs = 1) 

 

        Smpl_rho = fit['rho']

        Mean_E = Smpl_rho.mean()

        Med_E = np.percentile(Smpl_rho, 50)

        coord = np.linspace(-1.0, 1.0, 20000)

        est_pdf = ss.gaussian_kde(Smpl_rho).pdf(coord)

        map_idx = np.argmax(est_pdf)

        MAP_E = coord[map_idx]

       

        Means.append(Mean_E)

        Medians.append(Med_E)

        MAPs.append(MAP_E)

 

        s_bias_mean += Mean_E - rho_true

        s_bias_median += Med_E - rho_true

        s_bias_MAP += MAP_E - rho_true

        se_s_bias_mean.append(Mean_E - rho_true)

        se_s_bias_median.append(Med_E - rho_true)

        se_s_bias_MAP.append(MAP_E - rho_true)

        ss_mean += (Mean_E - rho_true) ** 2

        ss_median += (Med_E - rho_true) ** 2

        ss_MAP += (MAP_E - rho_true) ** 2

        se_ss_mean.append((Mean_E - rho_true) ** 2)

        se_ss_median.append((Med_E - rho_true) ** 2)

        se_ss_MAP.append((MAP_E - rho_true) ** 2)

       

    bias_mean.append(s_bias_mean / n_simu)

    bias_median.append(s_bias_median / n_simu)

    bias_MAP.append(s_bias_MAP / n_simu)

    SE_bias_mean.append(ss.sem(se_s_bias_mean))

    SE_bias_median.append(ss.sem(se_s_bias_median))

    SE_bias_MAP.append(ss.sem(se_s_bias_MAP))

    RMSE_mean.append((ss_mean / n_simu) ** 0.5)

    RMSE_median.append((ss_median / n_simu) ** 0.5)

    RMSE_MAP.append((ss_MAP / n_simu) ** 0.5)

    SE_RMSE_mean.append(ss.sem(se_ss_mean) ** 0.5)

    SE_RMSE_median.append(ss.sem(se_ss_median) ** 0.5)

    SE_RMSE_MAP.append(ss.sem(se_ss_MAP) ** 0.5)

 

root.destroy()

 

fout = open(f'ResultsN{n_samples}.txt', 'w')

 

print()

print('bias_mean =', bias_mean)

print('bias_edian =', bias_median)

print('bias_MAP =', bias_MAP)

print('RMSE_mean =', RMSE_mean)

print('RMSE_median =', RMSE_median)

print('RMSE_MAP =', RMSE_MAP)

 

fout.write('*/bias_mean\n')

for v in bias_mean:

    fout.write(f' {v}')

fout.write('\n')

fout.write('*/SE_bias_mean\n')

for v in SE_bias_mean:

    fout.write(f' {v}')

fout.write('\n')

 

fout.write('*/bias_median\n')

for v in bias_median:

    fout.write(f' {v}')

fout.write('\n')

fout.write('*/SE_bias_median\n')

for v in SE_bias_median:

    fout.write(f' {v}')

fout.write('\n')

 

fout.write('*/bias_MAP\n')

for v in bias_MAP:

    fout.write(f' {v}')

fout.write('\n')

fout.write('*/SE_bias_MAP\n')

for v in SE_bias_MAP:

    fout.write(f' {v}')

fout.write('\n')

 

fout.write('*/RMSE_mean\n')

for v in RMSE_mean:

    fout.write(f' {v}')

fout.write('\n')

fout.write('*/SE_RMSE_mean\n')

for v in SE_RMSE_mean:

    fout.write(f' {v}')

fout.write('\n')

 

fout.write('*/RMSE_median\n')

for v in RMSE_median:

    fout.write(f' {v}')

fout.write('\n')

fout.write('*/SE_RMSE_median\n')

for v in SE_RMSE_median:

    fout.write(f' {v}')

fout.write('\n')

 

fout.write('*/RMSE_MAP\n')

for v in RMSE_MAP:

    fout.write(f' {v}')

fout.write('\n')

fout.write('*/SE_RMSE_MAP\n')

for v in SE_RMSE_MAP:

    fout.write(f' {v}')

fout.write('\n')

 

fout.close()

 

 

リスト6 2変量正規分布モデルのStanスクリプト

data {

    int N;

    vector[2] X[N];

}

parameters {

    real<lower = -1.0, upper = 1.0> rho;

    real mu1;

    real mu2;

    real<lower = 0.0> sgm1;

    real<lower = 0.0> sgm2;

}

transformed parameters {

    vector[2] mu;

    cov_matrix[2] cov;

    mu[1] = mu1;

    mu[2] = mu2;

    cov[1][1] = sgm1 * sgm1;

    cov[1][2] = rho * sgm1 * sgm2;

    cov[2][1] = rho * sgm1 * sgm2;

    cov[2][2] = sgm2 * sgm2;

}

model {

    rho ~ uniform(-1.0, 1.0);

    mu1 ~ normal(0.0, 1000.0);

    mu2 ~ normal(0.0, 1000.0);

    sgm1 ~ exponential(0.001);

    sgm2 ~ exponential(0.001);

    for (i in 1:N) {

        X[i] ~ multi_normal(mu, cov);

    }

}

 

 

MAP推定値の算出スクリプト例

 

ベイズ分析(岡本、2022pp.160-167)では、分析結果は事後分布として得られる。パラメータの点推定値は、事後分布からMAP推定値(岡本、2019pp.175-176)として求めることができる。例えば、次の関数calcMAPEstのように事後分布のサンプルsamplesから算出できる。

 

def calcMAPEst(samples, a = 0.05, n_points = 1000):

    """ MAP推定値とMAP推定値における推定確率密度関数の値を求める

         samples:    MAP推定値を求めるサンプル

         a:     サンプルの左右a/2を除く。デフォルト値=0.05

         n_points:   KDEによる確率密度関数値を求める点の数。

               デフォルト値=1000

    """

    import numpy as np

    import scipy.stats as ss

    Lp, Up = np.percentile(samples, [100 * a/2, 100 * (1 - a/2)])

    coord = np.linspace(Lp, Up, n_points)

    est_pdf = ss.gaussian_kde(samples).pdf(coord) 

    map_idx = np.argmax(est_pdf)

    MAP_Est = coord[map_idx]      

    return MAP_Est, est_pdf[map_idx]    #   MAP推定値とMAP推定値における確率密度関数値

 

上のスクリプトでは、サンプルの両端の部分を切り捨てている。これは、サンプルの裾が以上に長い場合に、モード付近の確率密度分布関数の精度が落ちないようにするためである。サンプルから両端を切り取った残りの区間をn_points個の点で離散化して、KDE (Kernel Density Estimation) により確率密度分布関数の推定を行っている。これらn_points個の点における値の最大値を与える点として、MAP推定値を求め、その点における確率密度関数の値とともに関数calcMAPEstの返り値としている。

MAP推定値を求める例として、リストA1に正規分布のパラメータ平均値と標準偏差を求めるスクリプト例を示す。

 

リスト1A 正規分布のパラメータ平均値と標準偏差を求めるスクリプト例

import numpy as np

import matplotlib.pyplot as plt

import seaborn as sb

import pystan

import scipy.stats as ss

 

 

def calcMAPEst(samples, a = 0.05, n_points = 1000):

    """ MAP推定値とMAP推定値における推定確率密度関数の値を求める

         samples:    MAP推定値を求めるサンプル

         a:     サンプルの左右a/2を除く。デフォルト値=0.05

         n_points:   KDEによる確率密度関数値を求める点の数。

               デフォルト値=1000

    """

    import numpy as np

    import scipy.stats as ss

    Lp, Up = np.percentile(samples, [100 * a/2, 100 * (1 - a/2)])

    coord = np.linspace(Lp, Up, n_points)

    est_pdf = ss.gaussian_kde(samples).pdf(coord) 

    map_idx = np.argmax(est_pdf)

    MAP_Est = coord[map_idx]      

    return MAP_Est, est_pdf[map_idx]    #   MAP推定値とMAP推定値における確率密度関数値

 

 

Data = [51, 38, 60, 42, 47]

 

#

#         正規分布モデル

#

StanCode_normal = """

    data {

        int n;

        real d[n];

    }

    parameters {

        real mu;

        real<lower = 0.0> sigma;

    }

    model {

        mu ~ normal(0.0, 1000.0);

        sigma ~ exponential(0.001);

        d ~ normal(mu, sigma);

    }

"""

 

sm = pystan.StanModel(model_code = StanCode_normal)

fit = sm.sampling(data = {'n':len(Data), 'd':Data}, iter = 10000, n_jobs = 1)

print(fit)

 

mu_MAP = calcMAPEst(fit['mu'])[0]

sigma_MAP = calcMAPEst(fit['sigma'])[0]

#

#       グラフ描画

#

plt.figure(figsize = (8,3))

#

#       muの事後分布の描画

#

plt.subplot(121)

sb.kdeplot(fit['mu'])

plt.xlabel('$\mu$', fontsize = 14)

plt.title('$\hat\mu =$ {0:.1f}(MAP)'.format(mu_MAP), fontsize = 14)

#

#        sigmaの事後分布の描画

#

plt.subplot(122)

sb.kdeplot(fit['sigma'])

plt.xlabel('$\sigma$', fontsize = 14)

plt.title('$\hat\sigma =$ {0:.1f}(MAP)'.format(sigma_MAP), fontsize = 14)

plt.tight_layout()

plt.savefig('PostMuSgm.png')      #     グラフの保存

plt.show()

print('PostMuSgm.png was saved.')

 

リストA1のスクリプトを実行すると、Stan岡本、2019pp.179-210)によるMCMCサンプリングが行われる。その結果は、図A1のように表示される。

A1

 

MAP推定値がグラフの上に表示されている。

A1の表示されているウィンドウを閉じると、スクリプトの実行終了となる。端末には、以下のメッセージが表示されている。

 

Inference for Stan model: anon_model_b3c1b2700ba7728d2ab9aa18f53eeba1.

4 chains, each with iter=10000; warmup=5000; thin=1;

post-warmup draws per chain=5000, total post-warmup draws=20000.

 

        mean se_mean     sd   2.5%    25%    50%    75%  97.5%  n_eff   Rhat

mu     47.61    0.15   7.76   33.6  44.33  47.61  50.89  61.07   2565    1.0

sigma  13.34    0.18   9.53   5.51   8.28  10.91  15.25  35.84   2785    1.0

lp__  -11.86    0.02   1.31 -15.38 -12.36 -11.46 -10.93  -10.6   3396    1.0

 

Samples were drawn using NUTS at Thu Dec 15 08:36:13 2022.

For each parameter, n_eff is a crude measure of effective sample size,

and Rhat is the potential scale reduction factor on split chains (at

convergence, Rhat=1).

PostMuSgm.png was saved.

 

平均値のパラメータmuの推定値は、事後分布の平均値、中央値、MAP推定値に大差はないが、これは事後分布がほぼ左右対称形であることによる。事後分布が左右対称形でないsigmaの場合は、平均値13.34、中央値10.91MAP推定値8.8と異なった値になっている。

なお、実行は、PyStan2.17.1.0/Python3.7/Anaconda/Windows 10で行った。

PyStanについては、<岡本安晴いまさら聞けないPythonデータ分析丸善出版pp.178-210で説明している。

 

Up