お題「入社試験問題」
基礎からのベイズ統計学を読んで考えたことです。
59ページより引用
入社試験問題:ある企業の入社試験では、毎年、同じ難しさの問題を7問出題します。
X大学のxさんは3問正解、4問不正解でした。正解率を$\theta_x$とします。
Y大学のyさんは4問正解、3問不正解でした。正解率を$\theta_y$とします。
X大学とY大学からは、毎年たくさんの受験者がいます。調べてみると、
X大学の受験者の正解率は平均0.8、分散0.04のベータ分布で近似され、
Y大学の受験者の正解率は平均0.4、分散0.04のベータ分布で近似されることがわかりました。
$\theta_x$と$\theta_y$を推定し、母数の値の大きな受験者を1人だけ入社させるとしたら、
xさんとyさんのどちらでしょう。
**「試験の結果だけでなく、その人の所属するグループの能力も併せて評価すると、より精度のよい推定ができる」**ことの是非に関する考察になります。
ここでは、この問題をそのまま解くのではなく、以下のように条件を変えて検討します。
- 学生の能力は1人1つの値とし、その能力値の高い方を選択できるか どうかを目的とします。
- X大学出身の受験者の能力は、平均105、標準偏差10の正規分布に従うとします。
- Y大学出身の受験者の能力は、平均100、標準偏差10の正規分布に従うとします。
- 入社試験の成績である観測値は、平均が自分の能力値、標準偏差10の正規分布に従うとします。
- 試験成績方式では、観測値のみで評価します。
- グループ併用方式では、$補正値 \equiv \frac{グループの平均値+観測値}{2}$ で評価することにします。
Pythonで確認してみる
各値を計算します。能力値でXが高いのはは63.8%、観測値でXが高いのは59.9%です。
import numpy as np, pandas as pd
np.random.seed(1)
n = 1000000 # 評価回数
xave, yave, std = 105, 100, 10 # Xの平均、Yの平均、標準偏差
gx = np.random.normal(xave, std, (n)) # グループXの能力値
gy = np.random.normal(yave, std, (n)) # グループYの能力値
ox = np.random.normal(gx, std) # グループXの観測値
oy = np.random.normal(gy, std) # グループYの観測値
ax = (xave + ox) / 2 # グループXの補正値
ay = (yave + oy) / 2 # グループXの補正値
gf = gx > gy # 能力値でXが高い
of = ox > oy # 観測値でXが高い
af = ax > ay # 補正値でXが高い
print(gf.sum() / n, of.sum() / n)
>>>
0.638318 0.598666
試験成績方式
観測値による評価では、75.9%の正解率です。
print(pd.DataFrame([[(gf&of).sum(), ((~gf)&of).sum()],
[(gf&(~of)).sum(), ((~gf)&(~of)).sum()]],
columns=['能力X', '能力Y'], index=['観測X', '観測Y']) / n)
print('正解率 = ', (gf==of).sum() / n)
>>>
能力X 能力Y
観測X 0.498147 0.100519
観測Y 0.140171 0.261163
正解率 = 0.75931
グループ併用方式
補正値による評価では、76.8%の正解率です。
print(pd.DataFrame([[(gf&af).sum(), ((~gf)&af).sum()],
[(gf&(~af)).sum(), ((~gf)&(~af)).sum()]],
columns=['能力X', '能力Y'], index=['補正X', '補正Y']) / n)
print('正解率 = ', (gf==af).sum() / n)
>>>
能力X 能力Y
補正X 0.549088 0.142374
補正Y 0.089230 0.219308
正解率 = 0.768396
考察
確かに、自分の能力だけでなく所属グループの能力を使うと、精度良く評価できました。
しかし、このような方法が本当に良いのでしょうか?
例えば、大学ではなく性別を使うのは、どうでしょうか?
おそらく男女差で合格可否を変えるのは、問題があるでしょう。
提案方式1
そこで、新たな方法を提案します。その方法は、以下のようなものです。
- 事前に別途試験を受けます。その結果を事前観測値とします。
- その試験の成績が気に入らない場合(事前観測値が自分の能力値以下の場合)は、何もしないとします。
- 気に入った場合は、自ら登録処理をします。登録されている場合、入社試験では、観測値と事前観測値の両方を使わなければいけないこととします。(登録されていなければ、観測値のみで構いません。)
- 全員が別途試験を受けられない可能性も考慮して、登録制を取り入れています。
bx = np.random.normal(gx, std) # グループXの事前観測値
by = np.random.normal(gy, std) # グループYの事前観測値
# 事前観測値が自分の能力値以下であれば、元々の観測値とする
px = ox*(bx < gx) + (ox+bx)/2*(bx >= gx) # グループXの提案値1
py = oy*(by < gy) + (oy+by)/2*(by >= gy) # グループYの提案値1
pf = px > py # 提案値1でXが高い
print(pd.DataFrame([[(gf&pf).sum(), ((~gf)&pf).sum()],
[(gf&(~pf)).sum(), ((~gf)&(~pf)).sum()]],
columns=['提案1X', '提案1Y'], index=['観測X', '観測Y']) / n)
print('正解率 = ', (gf==pf).sum() / n)
>>>
提案1X 提案1Y
観測X 0.518089 0.088829
観測Y 0.120229 0.272853
正解率 = 0.790942
79.1%の正解率となり、精度がよくなりました。
ただし、別途試験を受けるためコストがかかります。
提案方式2
別途試験しない方法も考えてみます。
- 試験の成績が所属グループの平均以上の場合: 観測値を使ってもらう。
- 試験の成績が所属グループの平均未満の場合: 補正値を使ってもらう。
qx = ox*(ox >= xave) + ax*(ox < xave) # グループXの提案値2
qy = oy*(oy >= yave) + ay*(oy < yave) # グループYの提案値2
qf = qx > qy # 提案値2でXが高い
print(pd.DataFrame([[(gf&qf).sum(), ((~gf)&qf).sum()],
[(gf&(~qf)).sum(), ((~gf)&(~qf)).sum()]],
columns=['提案2X', '提案2Y'], index=['観測X', '観測Y']) / n)
print('正解率 = ', (gf==qf).sum() / n)
>>>
提案2X 提案2Y
観測X 0.521504 0.119074
観測Y 0.116814 0.242608
正解率 = 0.764112
観測値だけの推定より、多少よくなりました。
提案方式3
提案方式2でも、所属グループの上位能力者は不満かもしれません。次の方法は、どうでしょうか。
- ユーザごとに自ら登録するかどうかを試験時に決めてもらう。
- 能力値が所属グループの平均以上の場合: 登録しない。→ 観測値を使ってもらう。
- 能力値が所属グループの平均未満の場合: 登録する。→ 補正値を使ってもらう。
rx = ox*(gx >= xave) + ax*(gx < xave) # グループXの提案値3
ry = oy*(gy >= yave) + ay*(gy < yave) # グループYの提案値3
rf = rx > ry # 提案値3でXが高い
print(pd.DataFrame([[(gf&rf).sum(), ((~gf)&rf).sum()],
[(gf&(~rf)).sum(), ((~gf)&(~rf)).sum()]],
columns=['提案3X', '提案3Y'], index=['観測X', '観測Y']) / n)
print('正解率 = ', (gf==rf).sum() / n)
>>>
提案3X 提案3Y
観測X 0.518967 0.119357
観測Y 0.119351 0.242325
正解率 = 0.761292
- 観測値だけの推定より、多少よくなる。
- 受験者が主体的に関われるので、差別されているわけではない。
- コストもほとんどかからない。
提案3(あるいは、提案1と提案3のハイブリッド)が、よいように思われます。
以上