条件付き確率とベイズ更新
検査結果を条件に対象を絞り、判定後の不良率が元の不良率に左右される仕組みを学びます。
説明
条件付き確率は、ある事象が起きた対象だけに絞ったときの確率です。「不良品が陽性になる割合」と「陽性品が不良である割合」は、分母が異なります。ベイズの定理は、元の割合と検査の性質から観測後の割合を求める関係です。
1万個の製品のうち不良100個、正常9900個を考えます。不良の90%を陽性にし、正常の1%も陽性にしてしまう検査を使います。実数の表から陽性後の不良率を求め、元の不良率や追加観測を変えた場合を計算します。
1実際の状態と検査結果
不良100個のうち90個が陽性、10個が陰性です。正常9900個のうち99個が陽性、9801個が陰性となる例を表にします。
from fractions import Fraction分数計算; table = {'defective': {'positive': 90, 'negative': 10}, 'normal': {'positive': 99, 'negative': 9801}}実状態×判定の件数; table四区分の表検査結果と実際の状態は同じではありません。陽性には正常品99個が含まれ、陰性にも不良品10個が残ります。
2不良品を分母にする
不良の中で陽性になる割合と、正常の中で陽性になる割合を求めます。
sensitivity = Fraction(table['defective']['positive'], sum(table['defective'].values()))不良品中の陽性率; false_positive = Fraction(table['normal']['positive'], sum(table['normal'].values()))正常品中の陽性率; sensitivity, false_positive二つの条件付き確率9/10と1/100です。前者の分母は100、後者は9900です。これらは状態が既知の対象から見た検査の性質です。
3陽性品を分母にする
陽性90+99=189個に対象を絞ります。そのうち実際の不良は90個です。
positive_total = sum(row['positive'] for row in table.values())陽性品の総数; posterior = Fraction(table['defective']['positive'], positive_total)陽性の中の不良率; positive_total, posterior, float(posterior)件数・分数・割合陽性後の不良率は10/21、約47.6%です。不良品を90%検出する検査でも、陽性品の90%が不良とは限りません。正常品の母数が大きいため、1%の誤検出が99個になります。
4事前確率から同じ値を計算
事前不良率をpとすると、不良かつ陽性はp×0.9、正常かつ陽性は(1-p)×0.01です。前者を両者の合計で割ります。
prior = Fraction(1,100)事前の不良率1%; update = lambda p: p*sensitivity/(p*sensitivity+(1-p)*false_positive)ベイズ更新; update(prior), update(prior) == posterior件数からの値と照合件数表と同じ10/21になります。分母は陽性となる全ての場合を合計しており、不良からの陽性だけを使っていません。
5元の不良率が違う集団
検査の性質を保ち、元の不良率だけ0.1%、1%、10%へ変えます。
[(p, round(float(update(p)),4)) for p in [Fraction(1,1000), Fraction(1,100), Fraction(1,10)]]事前確率と陽性後の確率陽性後の不良率は元の不良率によって変わります。検査の感度や誤陽性率だけから、観測後の確率を一意に決めることはできません。適用対象の事前確率が必要です。
6陰性という観測
陰性品9811個に絞ると、不良は10個です。陽性とは別の条件付き確率になります。
negative_total = sum(row['negative'] for row in table.values())陰性品の総数; negative_risk = Fraction(table['defective']['negative'], negative_total)陰性の中の不良率; negative_total, negative_risk, float(negative_risk)陰性後の割合陰性後の不良率は10/9811、約0.102%です。陰性によって不良の可能性は下がりますが、表にある10個を0個として扱うことはできません。
7独立な追加検査
実際の状態が同じなら二回の検査結果が独立で、どちらも同じ感度・誤陽性率と仮定します。二回とも陽性だった場合を計算します。
twice = update(posterior)一回目の事後を再更新; direct = prior*sensitivity**2/(prior*sensitivity**2+(1-prior)*false_positive**2)二回陽性の同時確率から計算; twice, float(twice), twice == direct二方式を照合二回陽性後は約98.8%です。最初の事後確率を次の事前確率にした計算と、二回同時の確率からの計算が一致します。独立は検査対象の状態を条件にした独立です。
8同じ結果の再表示は証拠を増やさない
同じ検査結果を二回表示しただけなら、新しい独立観測ではありません。一回目の確率をそのまま保ちます。
same_record = posterior同じ観測の再利用; float(same_record), float(twice)一回分と独立二回分約47.6%と98.8%の差は、追加検査を独立と扱った条件によります。同じ測定の複製や、共通原因で誤判定する検査を独立と仮定すると、確信を過大にしてしまいます。
まとめ
条件付き確率では、どの対象を分母にしたかを明示します。
事後確率は検査の性質だけでなく事前確率にも依存します。
観測を重ねる計算には独立性などの仮定が必要で、同じ記録を重複利用しても新しい証拠にはなりません。
操作履歴と解説を振り返れます。
Python 3
from fractions import Fraction; table = {'defective': {'positive': 90, 'negative': 10}, 'normal': {'positive': 99, 'negative': 9801}}; table{'defective': {'positive': 90, 'negative': 10}, 'normal': {'positive': 99, 'negative': 9801}}sensitivity = Fraction(table['defective']['positive'], sum(table['defective'].values())); false_positive = Fraction(table['normal']['positive'], sum(table['normal'].values())); sensitivity, false_positive(Fraction(9, 10), Fraction(1, 100))
positive_total = sum(row['positive'] for row in table.values()); posterior = Fraction(table['defective']['positive'], positive_total); positive_total, posterior, float(posterior)(189, Fraction(10, 21), 0.47619047619047616)
prior = Fraction(1,100); update = lambda p: p*sensitivity/(p*sensitivity+(1-p)*false_positive); update(prior), update(prior) == posterior(Fraction(10, 21), True)
[(p, round(float(update(p)),4)) for p in [Fraction(1,1000), Fraction(1,100), Fraction(1,10)]][(Fraction(1, 1000), 0.0826), (Fraction(1, 100), 0.4762), (Fraction(1, 10), 0.9091)]
negative_total = sum(row['negative'] for row in table.values()); negative_risk = Fraction(table['defective']['negative'], negative_total); negative_total, negative_risk, float(negative_risk)(9811, Fraction(10, 9811), 0.0010192640913260626)
twice = update(posterior); direct = prior*sensitivity**2/(prior*sensitivity**2+(1-prior)*false_positive**2); twice, float(twice), twice == direct(Fraction(900, 911), 0.9879253567508233, True)
same_record = posterior; float(same_record), float(twice)(0.47619047619047616, 0.9879253567508233)
演習完了