ナード戦隊データマン

データサイエンスを用いて悪と戦うぞ

カンニングした生徒の割合を調べる方法

ベイズ推定を使えば、生徒から集めたアンケートを「正しく」用いてカンニングした生徒の割合を求めることができます。ここでは、そのアルゴリズムと算出方法を書いておきます。

プライバシーを守るアルゴリズム

各学生に面接をする方法を取る場合、生徒に直接「カンニングしましたか?」と聞いてしまっては、プライバシーが守られません。そこで、次のアルゴリズムでアンケートを収集します。

  1. 学生は面接者(先生)に"見られないように"、コイントスする。
  2. 表なら正直に答えてもらう。
  3. 裏が出たら、再度コイントスし、表なら「カンニングした」と言ってもらう。裏なら「カンニングしていない」と言ってもらう。

これにより先生は、その生徒が二回目のコイントスの結果として「カンニングした」といったのか、正直に告白したのかがわからなくなります。

モデル化

それでは、問題をモデル化します。

P(YES) = P(1回目表)P(カンニングした) + P(1回目裏)P(2回目表)
= p/2 + 1/4

pymcを使います。カンニングした割合は一様分布に従うと仮定します。また、アンケートデータは適当に収集済みであると仮定し、生徒100人中35人が「カンニングした」と答えたと仮定します。アンケートは二項分布であるとします。

モデル化したら、MCMCで事後分布からサンプリングし、カンニングした確率をトレースしてプロットします。このプロットされた確率は、事前分布の一様分布に、証拠である「アンケート」を取り入れた事後分布です。

with pm.Model() as model:
    p = pm.Uniform("freq_cheating", 0, 1)
    p_skewed = pm.Deterministic("p_skewed", 0.5*p + 0.25)
    yes_responses = pm.Binomial("number_cheaters", 100, p_skewed, observed=35)
    step = pm.Metropolis()
    trace = pm.sample(25000, step=step)
    burned_trace = trace[2500:]

figsize(12.5, 3)
p_trace = burned_trace["freq_cheating"]
plt.hist(p_trace, histtype="stepfilled", normed=True, alpha=0.85, bins=30, 
         label="posterior distribution", color="#348ABD")
plt.vlines([.05, .35], [0, 0], [5, 5], alpha=0.2)
plt.xlim(0, 1)
plt.legend();

download.png

事後分布からわかること

この事後分布からわかることは、カンニングはあった可能性が高いということです。観測データは、p=0の可能性を排除したので、カンニングが存在したという確信が高いと言えます。

参考

[1] https://github.com/CamDavidsonPilon/Probabilistic-Programming-and-Bayesian-Methods-for-Hackers