信号検出理論
階層モデル
信号検出理論では、ノイズ刺激とシグナルを含むシグナル刺激の2つの刺激の弁別が扱われる。ノイズ刺激およびシグナル刺激によって惹き起こされる感覚をおよびで表し、それぞれ平均がおよび、分散がおよびの正規分布に従うものとする。すなわち、
である。ノイズ刺激とシグナル刺激の感覚の分散が等しい
等分散モデルについて考える。
感覚の次元の単位と原点を決めるために、
とおく。
等分散モデルのもとでは、平均値の差は、で表される。すなわち、
である。
シグナル刺激を正しく「シグナル刺激である」と答える反応はHitと呼ばれ、ノイズ刺激を誤って「シグナル刺激である」と答える反応はFalse Alarmと呼ばれている。感覚がある基準値を越えたとき、「シグナル刺激である」と判断されるとすれば、HitとFalse Alarmの確率は、次式で与えられる。
いま、信号検出理論の実験を4条件、A,B,C,D,で行い、表1の結果を得たとする。
表1 |
||||||||
|
条件A |
条件B |
条件C |
条件D |
||||
|
No |
Yes |
No |
Yes |
No |
yes |
No |
Yes |
ノイズ刺激 |
77 |
23 |
50 |
50 |
77 |
23 |
60 |
40 |
シグナル刺激 |
23 |
77 |
23 |
77 |
10 |
90 |
11 |
89 |
各条件においてノイズ刺激が100回、シグナル刺激が100回、ランダムに提示されたときのNo反応(「ノイズ刺激である」という判断)とYes反応(「シグナル刺激である」という判断)の頻度がまとめられている。条件Aを基準にして考えると、条件B]ではシグナル刺激に対する反応の比率は条件Aと同じであるが、ノイズ刺激に対するYes反応が23回から50回に増えている。条件Cでは、ノイズ刺激に対する判断の比率は条件Aと同じであるが、シグナル刺激に対するYes反応が77回から90回に増えている。条件Dでは、条件Aと比べて、ノイズ刺激とシグナル刺激いずれに対してもYes反応が増えている。
表1のデータを等分散ガウス型モデルで分析するために、パラメータとが以下の階層モデルに従うとする。
条件A、B、C、Dにおけるをda、db、dc、ddで表すとき、これらは共通の分布に従うものとする。
条件A、B、C、Dにおけるも、共通の分布に従うものとする。
ハイパーパラメータ、、、は、次のhyperprior分布に従うものとする。
上の階層モデルをリスト1のようにStanスクリプトで表した。
Stanについての説明は、岡本(2019)で行っている。
リスト1 信号検出理論の階層モデルのStanスクリプト(sdt_h.stan)
data {
int
NFAa; int NCRa; int NHa; int NMa;
int
NFAb; int NCRb; int NHb; int NMb;
int
NFAc; int NCRc; int NHc; int NMc;
int
NFAd; int NCRd; int NHd; int NMd;
}
parameters {
real dmu_h;
real Cmu_h;
real<lower = 0> dsgm_h;
real<lower = 0> Csgm_h;
real da; real Ca; real db; real Cb;
real dc; real Cc; real dd; real
Cd;
}
model {
dmu_h
~ normal(0, 5);
Cmu_h
~ normal(0, 5);
dsgm_h
~ uniform(0, 5);
Csgm_h
~ uniform(0, 5);
da ~ normal(dmu_h, dsgm_h);
db
~ normal(dmu_h, dsgm_h);
dc ~ normal(dmu_h, dsgm_h);
dd
~ normal(dmu_h, dsgm_h);
Ca ~ normal(Cmu_h, Csgm_h);
Cb
~ normal(Cmu_h, Csgm_h);
Cc ~ normal(Cmu_h, Csgm_h);
Cd ~ normal(Cmu_h, Csgm_h);
NFAa
~ binomial(NFAa+NCRa, 1.0 - normal_cdf(Ca,
0.0, 1.0));
NHa
~ binomial(NHa+NMa, 1.0 - normal_cdf(Ca,
da, 1.0));
NFAb
~ binomial(NFAb+NCRb, 1.0 - normal_cdf(Cb, 0.0, 1.0));
NHb
~ binomial(NHb+NMb, 1.0 - normal_cdf(Cb, db, 1.0));
NFAc
~ binomial(NFAc+NCRc, 1.0 - normal_cdf(Cc,
0.0, 1.0));
NHc
~ binomial(NHc+NMc, 1.0 - normal_cdf(Cc,
dc, 1.0));
NFAd
~ binomial(NFAd+NCRd, 1.0 - normal_cdf(Cd,
0.0, 1.0));
NHd
~ binomial(NHd+NMd, 1.0 - normal_cdf(Cd,
dd, 1.0));
}
リスト1のStanスクリプトを用いて表1のデータを分析するPythonスクリプトを、リスト2のように用意した。これらのスクリプトファイルは圧縮ファイルfilesSDT_h.zipにまとめたので、ダウンロード・展開すれば自由に利用できる。
リスト2 表1のデータを分析するスクリプト(sdt_eqvar_h.py)
import numpy
as np
import scipy.stats
as ss
import matplotlib.pyplot
as plt
import seaborn
as sb
import pystan
sm = pystan.StanModel(file
= 'sdt_h.stan')
NFAa = 23; NCRa
= 77;
NHa = 77; NMa = 23;
NFAb = 50; NCRb
= 50;
NHb = 77; NMb = 23;
NFAc = 23; NCRc
= 77;
NHc = 90; NMc = 10;
NFAd = 40; NCRd
= 60;
NHd = 89; NMd = 11;
sdt_data = {'NFAa' :
NFAa, 'NCRa': NCRa,
'NHa': NHa, 'NMa': NMa,
'NFAb': NFAb, 'NCRb': NCRb,
'NHb': NHb, 'NMb': NMb,
'NFAc': NFAc, 'NCRc':NCRc,
'NHc': NHc, 'NMc': NMc,
'NFAd': NFAd, 'NCRd': NCRd,
'NHd': NHd, 'NMd':NMd}
fit = sm.sampling(data
= sdt_data, n_jobs = 1)
print(fit)
da_med = np.median(fit['da'])
db_med = np.median(fit['db'])
dc_med = np.median(fit['dc'])
dd_med = np.median(fit['dd'])
sb.kdeplot(fit['da'], label = 'da')
sb.kdeplot(fit['db'],
label = 'db')
sb.kdeplot(fit['dc'], label = 'dc')
sb.kdeplot(fit['dd'],
label = 'dd')
plt.legend()
plt.title("Distributions of d'" +
'\nda_med = {0:.2f}, db_med
= {1:.2f}, dc_med = {2:.2f}, dd_med
= {3:.2f}'.format(
da_med, db_med, dc_med, dd_med))
plt.savefig('Figdprime.png')
plt.show()
Ca_med = np.median(fit['Ca'])
Cb_med = np.median(fit['Cb'])
Cc_med = np.median(fit['Cc'])
Cd_med = np.median(fit['Cd'])
sb.kdeplot(fit['Ca'], label = 'Ca')
sb.kdeplot(fit['Cb'],
label = 'Cb')
sb.kdeplot(fit['Cc'], label = 'Cc')
sb.kdeplot(fit['Cd'], label = 'Cd')
plt.legend()
plt.title('Distributions of C' +
'\nCa_med = {0:.2f}, Cb_med
= {1:.2f}, Cc_med = {2:.2f}, Cd_med
= {3:.2f}'.format(
Ca_med, Cb_med, Cc_med, Cd_med))
plt.savefig('FigC.png')
plt.show()
dmu_h_med = np.median(fit['dmu_h'])
Cmu_h_med = np.median(fit['Cmu_h'])
sb.kdeplot(fit['dmu_h'],
label = 'dmu_h')
sb.kdeplot(fit['Cmu_h'],
label = 'Cmu_h')
plt.legend()
plt.title('dmu_h_med
= {0:.2f}, Cmu_h_med = {1:.2f}'.format(dmu_h_med, Cmu_h_med))
plt.savefig('Figdmu_h.png')
plt.show()
dsgm_h_med = np.median(fit['dsgm_h'])
Csgm_h_med = np.median(fit['Csgm_h'])
sb.kdeplot(fit['dsgm_h'],
label = 'dsgm_h')
sb.kdeplot(fit['Csgm_h'],
label = 'Csgm_h')
plt.legend()
plt.title('dsgm_h_med
= {0:.2f}, Csgm_h_med = {1:.2f}'.format(dsgm_h_med, Csgm_h_med))
plt.savefig('Figdsgm_h.png')
plt.show()
plt.hist(fit['dsgm_h'],
label = 'dsgm_h', alpha = 0.3)
plt.hist(fit['Csgm_h'],
label = 'Csgm_h', alpha = 0.3)
plt.legend()
plt.title('dsgm_h_med
= {0:.2f}, Csgm_h_med = {1:.2f}'.format(dsgm_h_med, Csgm_h_med))
plt.savefig('Figdsgm_h_hist.png')
plt.show()
リスト2のスクリプトを実行すると、以下の結果を得る。
まず、、すなわちda、db、dc、ddの事後分布が表示される(図1)。
図1
条件AとDの事後分布が重なっており、条件Bより右側、条件Cより左側に位置している。
図1のフォームを閉じると、図2のグラフが表示される。
図2
判断の基準値の事後分布であるが、条件Bの事後分布が最も左側にあり、その右に条件Dの事後分布が位置している。条件AとCの基準値の事後分布が重なって一番右側に位置している。
図2のフォームを閉じると、図3のグラフが表示される。
図3
ハイパーパラメータdmu_hとCmu_hの事後分布である。
図3のフォームを閉じると、図4のグラフが表示される。
図4
ハイパーパラメータdsgmとCsgmの事後分布である。これらのパラメータは負の値を取らないはずなのに、グラフが0より左側、負の領域にはみ出しているのは、滑らかなグラフを描くためにKDEを用いているからである。。
図4のフォームを閉じると、ヒストグラムのグラフが表示される(図5)。
図5
事後分布が正の領域に収まっていることが判る。