「精度を上げたい」の前に決めること:精度改善・閾値・運用を金額で比べる
はじめに
「モデルの精度を上げたい」という相談を受けたとき、私はまず精度の話をしません。先に訊くのは、見逃し1件と誤検知1件でいくら失うのか、判定は月に何件あるのか、いまの閾値は誰がどうやって決めたのか、の3つです。この3つが埋まると、AUCやF1といった統計の指標を「円」に置き換えられます。金額に直せれば、モデル改善・閾値の引き直し・運用の変更を横並びで比較できます。
今回のシミュレーションでは、与信を模した10万件規模のデータで、モデルには一切手を入れず、閾値を単価ベースに引き直すだけで期待損失が約24%下がりました。同じモデルのAUCを0.02上げても、閾値を現行のまま放置すると効果は2%ほどにとどまります。改善の努力をモデルに集中させる前に、閾値がいくらを取りこぼしているかを見たほうがよい、という話です。混同行列を金額に変換する式を置き、閾値・モデル・運用の3つを円で比べるまでを、実装で追います。
対象読者:
- 与信・不正検知・異常検知のモデルを持つ、あるいは導入を検討している事業側の責任者
- 「精度を上げれば効果が出る」と言われて、その効果額を見積もりたい方
- 精度指標を事業の損益にどう結びつけるかを整理したいデータサイエンティスト
記事のポイント:
- 混同行列を期待損失(円)に変換し、閾値・モデル改善・運用変更を同じ金額軸で比べます
- 同じAUCでも閾値しだいで損益が変わること、最適な閾値は統計指標ではなく単価で決まることを実験で示します
- スコアが確率として校正されていないと理論閾値が外れること、単価を±50%動かして結論が覆らないかを確認します
モデルより先に、単価と件数を訊く
精度を金額に置き換えるのに要る材料は、多くありません。
要になるのは、誤りの方向によって損失の大きさが変わることです。見逃し(本来は止めるべき案件を通してしまう、FN)と誤検知(問題ないのに止めてしまう、FP)では、1件あたりの損失がまるで違います。与信なら、貸倒れの数十万円と、優良客を1件逃す期待収益の差は、10倍以上開くのが普通です。この比が閾値をどこに置くべきかを決めます。
もう一段そこに、母集団と件数が要ります。月に何件を判定し、そのうち何%が本当に陽性(この記事ではデフォルト)なのか。同じ誤り率でも、件数が2倍なら損失も2倍です。
残るひとつが、現行の運用です。いまの閾値はいくつで、それはどうやって決まったのか。判定のあとに人の再確認が入るのか。実務でここを掘ると、閾値が「導入時のライブラリのデフォルトのまま」「前任者が置いた値」で、以来誰も触っていない、という答えがよく返ってきます。単価に基づいて決めた形跡がないわけです。閾値をモデルより先に見直す価値がいちばん大きいのは、この状態の現場です。
| 確認すること | 無いと決められないこと |
|---|---|
| 見逃し・誤検知の1件あたり単価 | 損失を最小にする閾値。単価が無ければ「最適な閾値」は定義できない |
| 判定件数と陽性率 | 改善の効果額。率だけでは金額に直せない |
| 現行の閾値と決まった経緯 | いまいくら取りこぼしているか。改善の出発点 |
以前、不正検知のモデルを新しく作りたいという相談を受けたことがあります。話を聞くと、精度そのものより、正常な取引を止めてしまう連絡対応に現場が疲弊していました。単価を分解すると、止めるコストが見逃しに対してかなり高く、しかも当時の閾値はかなり攻めた値に置かれていました。結局そのときはモデルを作らず、閾値を単価に合わせて引き直し、止めた案件を全部拒否せず追加確認に回す運用に変えるところで、目的の大半が片づきました。モデル開発は受注しませんでしたが、この判断は今でも正しかったと思っています。金額で並べる見積もりは、作らないほうが得だという結論も出せます。
混同行列を期待損失(円)に置き換える
精度指標を金額にする式は、素朴です。予測を4つのマスに分け、それぞれの件数に単価を掛けて足します。
利益側で書くなら、正しく止めて防いだ損失から、誤って止めた逸失を引く形になりますが、比較には損失側で十分です。混同するとすべての符号が逆になるので、正例の向きを先に決めます。以下、正例()はデフォルト、つまり「貸してはいけない相手」です。 すると見逃し(FN)は、デフォルトなのに承認してしまい貸倒れになる高コストの誤り、誤検知(FP)は、優良客なのに謝絶して将来の収益を逃す相対的に軽い誤りになります。
与信を例にコストを分解すると、見逃しは与信額に損失率を掛けたもの、誤検知はその顧客から得られたはずの期待収益、追加審査に回すならその件数に単価を掛けた固定費が乗ります。ここでは円建ての架空値として、見逃し50万円、誤検知5万円(比で10:1)を基本ケースに置きます。単価は本来ぶれるものなので、あとで幅を持たせて感度を見ます。
期待損失の計算そのものは数行です。閾値でクラスを分け、FNとFPを数えて単価を掛けます。
def expected_loss(scores, y, thr, c_fn, c_fp, audit=0):
"""閾値 thr で陽性判定したときの総期待損失(円)。"""
pred_pos = scores >= thr
fn = int(np.sum((~pred_pos) & (y == 1))) # 見逃し:デフォルトを承認
fp = int(np.sum(pred_pos & (y == 0))) # 誤検知:優良客を謝絶
n_audit = int(np.sum(pred_pos)) # 止めた件数(追加審査に回す)
return fn * c_fn + fp * c_fp + n_audit * audit
実験の設定
再現できるよう、データは生成します。20万件、陽性率3%、潜在的な信用力からデフォルト確率を引き、観測できる特徴量には測定ノイズを乗せます。このノイズの大きさでモデルの識別力を操作でき、あとでAUCを段階的に変えた比較に使います。スコアはロジスティック回帰で、陽性クラスの確率を出します。基本モデルのAUCは0.854でした。
単価は2ケース用意します。基本のケースは見逃し50万円・誤検知5万円(10:1)、もう1つは見逃し15万円・誤検知5万円(3:1)です。閾値を変えたときの損益がこの比でどう動くかを見ます。
地味ですが効く手当てをひとつ入れてあります。閾値を選ぶデータと、その閾値の損失を評価するデータは分けます。 同じデータで損失最小の閾値を選び、そのまま損失を報告すると、そのデータにたまたま合った値を選んでいるぶんだけ、損失を楽観的に見積もってしまいます。ここでは学習・閾値選択・評価の3つに分け、閾値は選択用で選び、金額は評価用で確定します。この手続きを踏んでいるかどうかで、報告される効果額は数%単位で変わります。前提はすべて架空値で、実在の案件の数字は使っていません。
同じAUCでも、閾値で損益が変わる
まず、モデルを固定したまま閾値だけを動かして、期待損失がどう変わるかを見ます。

曲線は深いU字を描きます。閾値を下げすぎれば誰でも止めてしまい誤検知が積み上がり、上げすぎればデフォルトを次々に見逃します。損失が最小になるのはスコア0.078付近でした。ここで大事なのは、よく使われる統計指標の閾値がこの最小からずれることです。感度と特異度のバランスを取るYouden指数の最大点は0.028で、損失は最小より4割ほど高くなりました。F1最大点はこのケースではたまたま近くに来ましたが、それは単価比が10:1のときの偶然です。
ずれるのは、Youden指数やF1が単価をまったく見ていないからです。見逃しと誤検知を対等に扱うか、あるいは決まった重みで扱うだけで、「見逃しは誤検知の10倍高い」という情報が入っていません。単価が変われば、損失を最小にする閾値も動きます。

見逃しが相対的に安くなる(3:1)と、無理に止める必要が薄れ、最適な閾値は0.078から0.252へ上がりました。同じモデル・同じデータでも、単価が違えば止めるべきラインは別の場所にあります。閾値は統計指標ではなく単価で決まり、単価が無い状態に「最適な閾値」は存在しません。
理論閾値が効くのは、確率が校正されているときだけ
単価から閾値を直接引く近道があります。損失を微分すると、最適な閾値はおおよそ
に対応します。10:1なら です。ただしこの式が成り立つのは、スコアが確率として校正されているとき、つまり「0.1と出た集団の実際のデフォルト率がおよそ10%」であるときに限られます。校正されていないスコアにこの理論値を当てると、狙った場所からずれます。

実際に確かめると、生のロジスティック回帰スコアで損失最小になる閾値は0.078で、理論値の0.091から少し離れていました。信頼度曲線を見ると、高い確率の側でスコアが対角線から外れています。ここをisotonic回帰で校正してから同じ計算をすると、損失最小の閾値は0.088となり、理論値の0.091にほぼ重なりました。単価から閾値を引く前に、スコアが確率になっているかを信頼度曲線1枚で確かめておきます。この手順を省くと、正しい単価を入れても閾値を外します。確率校正そのものの手法は確率校正の入門記事で扱っています。
閾値スイープと校正は、あわせて短く書けます。
from sklearn.isotonic import IsotonicRegression
# 閾値は選択用データで選ぶ(評価用と分ける)
grid = np.linspace(0.001, 0.6, 400)
losses = [expected_loss(s_sel, y_sel, t, c_fn, c_fp) for t in grid]
thr_raw = grid[int(np.argmin(losses))]
# スコアを確率として校正してから、同じ計算をやり直す
iso = IsotonicRegression(out_of_bounds="clip").fit(s_sel, y_sel)
s_cal = iso.transform(s_sel)
losses_cal = [expected_loss(s_cal, y_sel, t, c_fn, c_fp) for t in grid]
thr_cal = grid[int(np.argmin(losses_cal))] # 理論値 c_fp/(c_fn+c_fp) に近づく
審査枠に上限があるときは、閾値でなく上位k件を選ぶ
現実には、単価だけで閾値が決まらない場合もあります。追加審査に回せるのが1日◯件までと先に決まっている現場です。この上限があると、止める件数のほうが先に固定され、運用はスコア上位k件を審査に回す形になります。最適化の変数が閾値ではなく件数に変わるわけです。この記事は単価で閾値が決まるケースを主に扱いますが、自分の現場がキャパシティで縛られているなら、以下の議論は「上位k件をどう選ぶか」に読み替える必要があります。
AUCを0.02上げると、いくら得をするのか
閾値を最適に選んだうえで、モデルのAUCを段階的に変えると、期待損失はどう動くでしょうか。

AUCが上がれば損失は下がります。ただしその効き方は問題の設定しだいで、この設定ではAUCを0.02上げても損失の減りは3千万円台にとどまりました。少なくないとはいえ、閾値の置き方で動く額とは桁が違います。
そこで、出発点を「現行は慣習の閾値0.5のまま」として、3つの施策を同じ金額軸で並べます。0.5という値は、多くのライブラリが既定にしている一方で、陽性率3%のデータでは高すぎて、デフォルトのほとんどを見逃します。
| 施策 | AUC | 閾値 | 期待損失 | 現行比 |
|---|---|---|---|---|
| 現行運用 | 0.854 | 0.5(慣習) | 704百万円 | — |
| a) モデルをAUC+0.02改善(閾値は0.5のまま) | 0.872 | 0.5 | 691百万円 | −2% |
| b) モデルは触らず、閾値を単価ベースに引き直す | 0.854 | 0.078 | 537百万円 | −24% |
| c) 誤検知単価を半減(追加確認に回す)+閾値最適化 | 0.854 | 0.045 | 412百万円 | −41% |
| (参考)a) に加えて閾値も最適化 | 0.872 | 0.091 | 509百万円 | −28% |
モデルを改善しても閾値を放置すれば、効果は2%どまりです。改善した精度が、高すぎる閾値でせき止められて損益に届かないからです。逆に、モデルには一切触れず閾値を単価ベースに引き直すだけで24%減りました。運用側で誤検知の単価を下げ、そのうえで閾値を攻められるようにすると41%です。モデル改善に閾値最適化まで足した参考行(−28%)も、閾値だけを直したb(−24%)をわずかに上回るにすぎません。この現場で支配的なのは、モデルの精度ではなく、閾値と運用の設計です。
ただし、この結論は単価の値に頼っています。単価は1点で当てにくいので、±50%ぶらして施策の大小が入れ替わるかを確かめます。

単価を低め・中央・高めのどこに置いても、閾値を引き直したほうが損失は小さいままでした。結論が単価の細部で覆らないなら、いま持っている単価の当たりで意思決定してよいという判断が立ちます。もし覆るようなら、意思決定の前に単価の精査が先、という順番も同時に見えます。
この見積もりが外れるとき
この枠組みは強力ですが、そのまま鵜呑みにすると本番で数字が合わなくなる箇所がいくつかあります。
最適な閾値は、入力や出力の分布が変われば古くなります。景気や季節で申込者の顔ぶれが動けば、去年引いた閾値は今年の単価最小ではありません。だから閾値は引いて終わりではなく、分布の変化を監視して引き直す運用とセットにします。分布のずれをどう検知して回すかは、小規模チームのMLOps最小構成で扱っています。同じ理由で、スコアの校正も時間とともにずれます。導入時に校正しても、半年後に確率がずれていれば理論閾値は外れます。
単価そのものにも注意が要ります。KPIから逆算すると、営業と審査で違う数字が出てくるのが普通で、どの単価で最適化するかは合意の場を先に作る必要があります。それから、期待値だけを見て裾を見ない危険もあります。期待損失は平均の話で、まれに起きる巨額の貸倒れが分布の裾にどれだけ潜むかは別に見なければなりません。裾の評価そのものはテールリスクの記事で扱っています。加えて、先に触れた審査キャパシティの制約と、閾値を評価データに合わせすぎる過適合もあります。この2つも、本番で数字が合わなくなる典型です。
まとめ
精度改善は打ち手のひとつにすぎません。閾値の設計も運用の変更も、同じ「円」に換算しなければ優劣を比べられません。今回のシミュレーションでは、モデルに手を入れず閾値を引き直すだけで期待損失が約24%下がり、AUCを0.02上げて閾値を放置した場合の2%を大きく上回りました。改善の予算をどこに割くかは、この比較を見てから決めればよいはずです。
比較には単価が要ります。だから相談の入口で訊くのは、モデルの精度ではなく、見逃しと誤検知の単価、判定件数、いまの閾値です。この3つが揃えば、精度の議論をそのまま金額の議論として進められます。
もっとも、この枠組みがそのまま使えない領域もあります。損失を金額に落とすこと自体が難しいドメイン、たとえば安全や法令が判定を縛っていて、見逃し1件を円で言い切れない場合です。単価が原理的に決めにくいなら、期待損失の最小化より先に、満たすべき制約を明文化するほうが実務に合います。逆に、単価の当たりと件数、現行の閾値がおおよそ言えるなら、手元のスコアを一度校正し、単価から閾値を引き直して、いまの運用がいくら取りこぼしているかを金額で出してみてください。改善予算をモデルに向けるべきかどうかは、その数字を見れば決まります。