一覧CS-12-2 · 確率の基礎

条件付き確率とベイズ更新

検査結果を条件に対象を絞り、判定後の不良率が元の不良率に左右される仕組みを学びます。

説明

条件付き確率は、ある事象が起きた対象だけに絞ったときの確率です。「不良品が陽性になる割合」と「陽性品が不良である割合」は、分母が異なります。ベイズの定理は、元の割合と検査の性質から観測後の割合を求める関係です。

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

1
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}}
2
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))
3
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)
4
prior = Fraction(1,100); update = lambda p: p*sensitivity/(p*sensitivity+(1-p)*false_positive); update(prior), update(prior) == posterior
(Fraction(10, 21), True)
5
[(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)]
6
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)
7
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)
8
same_record = posterior; float(same_record), float(twice)
(0.47619047619047616, 0.9879253567508233)

演習完了