本記事にはプロモーション(広告・アフィリエイトリンク)が含まれます。

この記事でわかること

💡 ポイント

  • 見かけのAUCは外部データで落ち、モデルの順位まで逆転する:候補20変数を全部入れたモデルは開発データ上で 0.775 なのに、外部検証データでは 0.721 まで落ちます。一方で変数を4つに絞ったモデルは 0.738 → 0.756。見かけでは勝っていたモデルが、外部では負けます。
  • 外部データを1件も使わずに落ち幅を見積もれる:ブートストラップによる最適性補正を使うと、0.775 から楽観バイアス 0.068 を引いて 0.707。外部での実際の 0.721 をほぼ言い当てます。
  • 識別が良くても較正は壊れている:20変数モデルの較正傾きは 0.745 で、理想の 1 を下回ります。予測が極端に振れている状態です。
  • AUCは順序しか見ていない:線形予測子を2倍にして予測確率をわざと歪めても、AUCは 0.721 のまま動きません。較正傾きだけが半分になります。
  • 実務で何を報告すべきか:EPV、内部検証の手順、較正の確認、外部集団との違いという観点で、実務で報告すべきことを整理します。

はじめに

「この予測モデルはAUC 0.85でした」と報告されたとき、あなたはその数字をどこまで信用するでしょうか。同じデータでモデルを作り、同じデータで性能を測った数字なのか。変数選択をやり直しながら検証したのか。そして、その0.85は「誰が高リスクかを当てられる」という意味なのか、それとも「20%と出したら本当に20%発症する」という意味なのか。同じ一つの数字でも、背後の手続き次第で意味はまったく変わります。

臨床予測モデルは、いまや研究室の中だけの話ではありません。術後合併症のリスクスコア、治験における患者層別化、リアルワールドデータを使った予後予測、バイオマーカーによる対象集団の選択と、製薬・臨床の実務のあちこちに顔を出します。だからこそ、開発したモデルの性能をどう検証し、どう報告するかが問われます。

本記事では、過学習と楽観バイアス、ブートストラップによる内部検証、識別と較正の違い、そして実務での落とし穴という順に、すべてRの実測値で確認していきます。AUCの計算そのものやカットオフの選び方は ROC曲線とAUCをRで実装する ― 感度・特異度・最適カットオフ で扱いました。本記事はその続きにあたる「そのAUCは信用できるのか」という話です。モデルの土台となるロジスティック回帰そのものは 2値変数とロジスティック回帰:理論・実装・解釈 をあわせてご覧ください。

臨床予測モデルとは ― 「説明するモデル」と「当てるモデル」の違い

同じロジスティック回帰でも、「この薬剤の効果はどれくらいか」を知りたい説明のモデルと、「この患者は発症するか」を当てたい予測のモデルでは、作り方も評価の仕方も別物です。説明のモデルでは交絡因子を理論から選び、着目する係数の推定精度を守ります。予測のモデルで欲しいのは個々の患者に対する当たり具合であり、係数一つひとつの解釈は二の次です。

とくに注意したいのが変数の選び方です。「p値が0.05未満だった変数を残す」という手続きは、説明の文脈でも問題含みですが、予測の文脈では特に相性が悪いものです。p値は目の前のデータでの偶然の揺らぎを強く反映するため、それを基準に選び抜いた変数の組み合わせは、そのデータにだけ都合よく当てはまります。説明のモデルの入口としては 【やさしく解説】交絡(交絡因子)とは傾向スコア分析(Propensity Score Analysis)とは が参考になります。

観点説明するモデル当てるモデル(予測)
目的ある要因の効果の大きさを知る個々の患者の将来を当てる
典型的な問いこの因子はアウトカムを変えるかこの人の発症確率は何%か
変数の選び方理論・先行知見から交絡を選ぶ事前に規定し、数を絞る
主な評価指標係数・信頼区間・p値C統計量、較正、Brier score
気にすることバイアスのない推定未知のデータでの当たり具合

検証には段階があるという点も押さえておきましょう。よく混同されますが、「見かけの性能」「内部検証」「外部検証」は測っているものが違います。

段階何を測っているか使うデータわかること限界
見かけの性能開発データへの当てはまり開発データそのもの計算はできる(上限の目安)楽観的に出る。単独報告は不可
内部検証同じ集団での再現性開発データを使い回す楽観バイアスの大きさ集団が変わったときの挙動は不明
外部検証別の集団への移植可能性別施設・別時期の独立データ実運用に近い性能と較正データの入手そのものが難しい

EPVという物差しも外せません。EPV(events per variable、1変数あたりのイベント数)は、イベント数をモデルに投入した変数の数で割った値です。古くから「10以上」という経験則が使われてきました。本記事で使う仮想データは開発データ n=300、イベント数 102 に対して候補変数が20個なので、EPV は 5.1。経験則の半分ほどしかありません。この「足りなさ」が、後の章で見る性能の落ち込みを生みます。

⚠️ 注意
EPVで効くのは症例数ではなくイベント数です。症例数がどれだけ多くても、イベントが数十件しかなければ、投入できる変数は数個が限度になります。なお「10以上」はあくまで古典的な経験則で、絶対的な基準ではありません。近年はアウトカムの発生割合や期待される説明力を織り込んだ、より精緻な症例数設計の考え方も提案されています。イベントが極端に少ない場面での対処は Firthのロジスティック回帰とは ― 完全分離・希少イベント もあわせてご覧ください。

「開発と検証を分けて報告する」という発想は、国際的にも共有されています。予測モデル研究の報告ガイドラインであるTRIPOD声明(Transparent Reporting of a multivariable prediction model for Individual Prognosis Or Diagnosis、2015年公表)は、開発と検証それぞれについて何を書くべきかを項目として示しています。2024年にはTRIPOD+AIが公表され、回帰モデルと機械学習モデルの双方を対象に、2015年版のチェックリストを置き換えました。また、予測モデル研究のバイアスリスクを評価するツールとしてPROBASTがあり、解析の項では変数選択の手続きや過学習への対処が問われます。機械学習モデルを扱う場合の解釈可能性の観点は 説明可能AI(XAI)とは ― 製薬業界での活用・メリット で整理しています。

見かけの性能はなぜ当てにならないのか

過学習が実際にどれくらいの大きさで起きるのか、手元のRで確かめてみましょう。言葉で「過学習に注意」と言われるより、数字が動くところを見たほうが腑に落ちるはずです。

使うのは次の設定の仮想データです。開発データは n=300、候補となる予測因子は x1〜x20 の20個。ただし真にアウトカムと関係があるのは x1〜x4 の4つだけで、残りの x5〜x20 は結果とまったく無関係なノイズです。開発データのイベント数は102(イベント割合34.0%)、EPV(1変数あたりのイベント数)は5.1で、経験則としてよく引かれる10を下回っています。そして「真の性能」を知るための物差しとして、同じ真のモデルから独立に生成した n=5,000 の外部検証データ(イベント1,529件・イベント割合30.6%)を用意します。

比べるのは2つのモデルです。ひとつは候補20変数をすべて投入したモデル、もうひとつは事前に「x1〜x4 の4つだけを使う」と決めておいたモデルです。どちらも二値アウトカムに対するロジスティック回帰で、当てはめ方そのものは2値変数とロジスティック回帰:理論・実装・解釈で扱った内容と同じです。違うのは「何本の変数を入れたか」だけ、という点に注目してください。

library(pROC)

# 20個の候補変数のうち、真に効くのは x1〜x4 の4つだけ
sim_data <- function(n, seed) {
  set.seed(seed)
  X <- matrix(rnorm(n * 20), n, 20)
  colnames(X) <- paste0("x", 1:20)
  lp <- -1.0 + 0.8*X[,1] + 0.6*X[,2] - 0.5*X[,3] + 0.4*X[,4]
  data.frame(y = rbinom(n, 1, plogis(lp)), X)
}

dev <- sim_data(300, 20260828)   # 開発データ
val <- sim_data(5000, 99)        # 外部検証データ(真の性能の代わり)

vars20 <- paste0("x", 1:20)
f20 <- as.formula(paste("y ~", paste(vars20, collapse = " + ")))
f4  <- y ~ x1 + x2 + x3 + x4

auc_of <- function(model, data) {
  p <- predict(model, newdata = data, type = "response")
  as.numeric(pROC::auc(pROC::roc(data$y, p, quiet = TRUE)))
}

m20 <- glm(f20, data = dev, family = binomial)   # 候補20変数を全部入れたモデル
m4  <- glm(f4,  data = dev, family = binomial)   # 事前に4変数だけと決めたモデル

cat("開発データ: n =", nrow(dev), " イベント数 =", sum(dev$y),
    " EPV =", round(sum(dev$y) / 20, 1), "\n\n")
cat("候補20変数モデル  見かけのAUC:", round(auc_of(m20, dev), 3),
    "  外部検証AUC:", round(auc_of(m20, val), 3), "\n")
cat("事前規定4変数モデル 見かけのAUC:", round(auc_of(m4, dev), 3),
    "  外部検証AUC:", round(auc_of(m4, val), 3), "\n")

出力は次のとおりです。

開発データ: n = 300  イベント数 = 102  EPV = 5.1 

候補20変数モデル  見かけのAUC: 0.775   外部検証AUC: 0.721 
事前規定4変数モデル 見かけのAUC: 0.738   外部検証AUC: 0.756 
📝 解釈・補足
20変数モデルは開発データ上では AUC 0.775 と良い成績に見えますが、外部データでは 0.721 まで落ちました。この 0.054 の落ち幅こそ、「自分を作ったデータで自分を採点したことによる下駄」、すなわち楽観バイアス(optimism)が実際に現れた姿です。一方、4変数モデルは見かけ 0.738 に対して外部 0.756 とほとんど落ちず、むしろ上がっています。

そして本記事で最も重要な事実がここにあります。 開発データの見かけの性能だけで2つを比べると、0.775 対 0.738 で20変数モデルの勝ちです。ところが外部データでは 0.721 対 0.756 と順位が逆転し、20変数モデルが負けます。もし「見かけのAUCが高いほうを採用する」という判断をしていたら、現場で使ったときに弱いほうのモデルを選んでしまっていたことになります。落ち幅の大小ではなく、モデル同士の優劣そのものがひっくり返るというのが、見かけの数字の怖さです。

モデル見かけのAUC外部検証AUC落ち幅
候補20変数モデル0.7750.721-0.054
事前規定4変数モデル0.7380.756+0.018

なぜこうなるのかは、数式を使わなくても説明できます。x5〜x20 は真には何の関係もない変数ですが、n=300 という限られたデータの中では、偶然イベントが起きた人にたまたま値が大きい変数が必ず何本か紛れ込みます。ロジスティック回帰はその偶然のパターンにも律儀に係数を割り当て、開発データの当てはまりを少しずつ良くしていきます。つまり、増えた分の「性能」はそのデータにしか存在しない模様を暗記した分であって、次の患者には持ち越せません。EPV が 5.1 と小さいほど、1つの係数を支える情報が乏しくなり、この暗記の割合が大きくなります。逆に、変数を4つに絞ったモデルは暗記する余地が最初からないため、見かけと外部がほぼ一致したわけです。

なお「当てはまりの良さと予測の良さは違う」というこの構図は、情報量規準の考え方そのものでもあります。モデルの複雑さにペナルティを課す発想についてはモデル選択の基礎:AIC・BICを情報量として理解するを、多重共線性や変数選択の一般論については統計検定準1級「回帰分析」攻略 ― 重回帰・多重共線性をあわせてご覧ください。今回の指標であるAUCそのものの意味はROC曲線とAUCをRで実装するで解説しています。

情報量規準がなぜ「当てはまりの良さから複雑さを引く」という形になるのか、その数理的な背景を知りたい方はAIC・BICの本質を数理統計から理解する:KL情報量もどうぞ。今回の楽観バイアスは、まさにその「差し引かれるべき分」を情報量規準の代わりにリサンプリングで直接測ったものだと考えると見通しが良くなります。

内部検証 ― ブートストラップによる最適性補正

さて、実務では都合よく n=5,000 の外部データが手元にあることのほうがまれです。手持ちのデータだけで「外部に出したらどのくらい落ちるのか」を見積もれないでしょうか。

素直に思いつくのは、データを学習用と検証用に分ける方法です。しかしこのやり方には二重の損があります。ひとつはモデルづくりに使えるデータが減ること。もうひとつは、検証に回した分も少数になるため、出てくるAUCの推定自体が不安定になることです。n=300・イベント102という規模では、この損が無視できません。

そこで使われるのが、データを1件も捨てずに全部でモデルを作り、落ち幅(楽観バイアス)のほうを別途推定して差し引くという発想です。これが Harrell の最適性補正法と呼ばれるアプローチで、落ち幅の推定にブートストラップを使います。

set.seed(1234)
B <- 200
opt <- numeric(B)
for (b in 1:B) {
  idx <- sample(nrow(dev), replace = TRUE)          # 元データと同じサイズで復元抽出
  mb  <- glm(f20, data = dev[idx, ], family = binomial)  # ブートストラップ標本でモデルを作り直す
  opt[b] <- auc_of(mb, dev[idx, ]) - auc_of(mb, dev)     # 楽観の大きさ=標本内AUC−元データAUC
}
apparent <- auc_of(m20, dev)
optimism <- mean(opt)

cat("見かけのAUC(開発データ)    :", round(apparent, 3), "\n")
cat("楽観バイアス(B=200の平均)  :", round(optimism, 3), "\n")
cat("最適性補正後のAUC            :", round(apparent - optimism, 3), "\n")
cat("外部検証データでの実際のAUC  :", round(auc_of(m20, val), 3), "\n")

# 参考:データを2:1に分けるやり方は、分け方しだいで結果が大きく動く
set.seed(56)
sp <- replicate(200, {
  tr <- sample(nrow(dev), round(nrow(dev) * 2/3))
  ms <- glm(f20, data = dev[tr, ], family = binomial)
  auc_of(ms, dev[-tr, ])
})
cat("\n[参考] 2:1分割検証を200回繰り返したときの検証AUC\n")
cat("  平均:", round(mean(sp), 3), " 標準偏差:", round(sd(sp), 3),
    " 範囲:", round(min(sp), 3), "-", round(max(sp), 3), "\n")

出力は次のとおりです。

見かけのAUC(開発データ)    : 0.775 
楽観バイアス(B=200の平均)  : 0.068 
最適性補正後のAUC            : 0.707 
外部検証データでの実際のAUC  : 0.721 

[参考] 2:1分割検証を200回繰り返したときの検証AUC
  平均: 0.678  標準偏差: 0.048  範囲: 0.551 - 0.793 
📝 解釈・補足
推定された楽観バイアスは 0.068 でした。見かけの 0.775 からこれを差し引くと 0.707 になり、外部データでの実際の値 0.721 を誤差 0.014 で言い当てています。強調したいのは、外部データを1件も使わずに、開発データだけからこの見積もりが得られたという点です。手持ちのデータの中に、落ち幅を測るための情報がすでに入っているわけです。

手順を整理すると、次の4つのステップになります。

  • 復元抽出でブートストラップ標本を作る ― 元データと同じ n=300 になるまで、同じ人を何度でも選んでよいという方式で抽出します。
  • その標本の中で、モデルづくりを最初からやり直す ― ここが最大の要点です。変数選択やカットオフの決定など、開発時に行った作業はすべてブートストラップ標本の中で再現します。
  • できたモデルを2つのデータで採点する ― 「自分を作った標本」での AUC と、「元データ全体」での AUC を計算し、その差を取ります。この差が、その1回分の楽観の大きさです。
  • 差の平均を楽観バイアスとし、見かけの値から引く ― 今回は B=200 回繰り返し、平均 0.068 を得ました。B=200 は最低限の目安で、計算資源が許すならさらに増やせます。
⚠️ 注意
よくある誤りが、変数選択をブートストラップの外で1回だけ済ませ、選ばれた変数の当てはめだけを繰り返すやり方です。これでは「変数を選ぶ過程で生じた楽観」がまるごと補正の対象から外れてしまい、楽観バイアスを過小評価します。ステップワイズ法や有意な変数の絞り込みを行ったなら、その操作ごとループの中で再現してください。

最後に、分割検証との比較も見ておきましょう。2:1分割を200回繰り返した結果は、平均こそ 0.678 と補正後の値に近いのですが、標準偏差は 0.048、範囲は 0.551 から 0.793 まで広がりました。つまり、まったく同じデータ・同じモデルであっても、分け方を変えるだけで「AUC 0.551 の使えないモデル」にも「AUC 0.793 の優秀なモデル」にもなり得るということです。論文や社内報告で見かける「学習用と検証用に分けた結果、検証AUCは0.793でした」という1回きりの数字は、この幅のどこかから引いた1枚のくじにすぎません。n=300 程度の規模では、分割検証よりブートストラップによる補正のほうがはるかに安定した答えを返します。

ブートストラップという手法そのものの仕組みや、バイアス補正・信頼区間への応用についてはブートストラップ法とは ― 信頼区間・バイアス補正で詳しく扱っています。また、そもそも「この規模のデータでモデルを作ってよいのか」を事前に考える枠組みとしては検出力分析(Power Analysis)入門が参考になります。予測モデルの症例数設計は検定の検出力とは考え方が異なりますが、「必要な情報量を先に見積もる」という姿勢は共通しています。

識別(C統計量)と較正(キャリブレーション)は別物です

予測モデルの性能は、大きく二つの軸で測ります。ひとつが識別(discrimination)、もうひとつが較正(calibration)です。

識別は「リスクの高い人と低い人を、順序として区別できているか」を見る軸です。イベントが起きた人の予測確率が、起きなかった人より高くなっている度合いを表すのが C統計量(concordance statistic)で、二値アウトカムでは AUC と一致します。生存時間解析では打ち切りを考慮した形に拡張され、C-index と呼ばれます(Cox比例ハザードモデル入門〜数式から実務応用まで)。AUCそのものの読み方や感度・特異度との関係はROC曲線とAUCをRで実装する ― 感度・特異度・最適カットオフで扱っています。

較正は「出した確率の数字そのものが当たっているか」を見る軸です。「発症確率20%」と予測した患者を100人集めたとき、実際に20人前後で発症していれば較正が取れています。識別は順序、較正は数値の正しさ。この二つは独立に壊れます。

外部検証データで、両モデルの識別と較正を計算してみましょう。

p20 <- predict(m20, newdata = val, type = "response")
p4  <- predict(m4,  newdata = val, type = "response")

cal_stats <- function(p, y) {
  slope <- coef(glm(y ~ qlogis(p), family = binomial))[2]        # 較正傾き
  cil   <- coef(glm(y ~ offset(qlogis(p)), family = binomial))[1] # 全体としてのずれ
  c(AUC = as.numeric(pROC::auc(pROC::roc(y, p, quiet = TRUE))),
    slope = as.numeric(slope), intercept = as.numeric(cil),
    Brier = mean((p - y)^2))
}

res <- rbind("20変数モデル" = cal_stats(p20, val$y),
             "4変数モデル"  = cal_stats(p4,  val$y))
print(round(res, 3))

# 予測値の大きさ順に10等分し、予測と実測を突き合わせる
g   <- cut(p20, breaks = quantile(p20, seq(0, 1, 0.1)), include.lowest = TRUE)
tab <- data.frame(
  群       = 1:10,
  予測平均 = round(tapply(p20, g, mean), 3),
  実測割合 = round(tapply(val$y, g, mean), 3),
  n        = as.vector(table(g))
)
cat("\nキャリブレーション表(20変数モデル・外部検証データを予測値で10等分)\n")
print(tab, row.names = FALSE)

# 線形予測子を2倍にしても順序は変わらない=AUCは1ミリも動かない
cat("\n[識別と較正は別物]\n")
p_bad <- plogis(2 * qlogis(p20))
cat("元の予測値       AUC:", round(cal_stats(p20, val$y)["AUC"], 3),
    " 較正傾き:", round(cal_stats(p20, val$y)["slope"], 3), "\n")
cat("線形予測子を2倍  AUC:", round(cal_stats(p_bad, val$y)["AUC"], 3),
    " 較正傾き:", round(cal_stats(p_bad, val$y)["slope"], 3), "\n")

出力は次のとおりです。

               AUC slope intercept Brier
20変数モデル 0.721 0.745    -0.201 0.189
4変数モデル  0.756 1.113    -0.188 0.177

キャリブレーション表(20変数モデル・外部検証データを予測値で10等分)
 群 予測平均 実測割合   n
  1    0.055    0.066 500
  2    0.112    0.118 500
  3    0.163    0.194 500
  4    0.213    0.198 500
  5    0.270    0.244 500
  6    0.334    0.344 500
  7    0.406    0.368 500
  8    0.492    0.448 500
  9    0.598    0.456 500
 10    0.765    0.622 500

[識別と較正は別物]
元の予測値       AUC: 0.721  較正傾き: 0.745 
線形予測子を2倍  AUC: 0.721  較正傾き: 0.372 
📝 解釈・補足
較正傾き(calibration slope)は 1 が理想です。20変数モデルは 0.745 と 1 を下回っており、これは予測が極端すぎることを意味します。高リスクの人をより高く、低リスクの人をより低く出しすぎている——過学習したモデルに典型的な症状です。一方、事前に4変数と決めたモデルは 1.113 とほぼ理想的でした。全体としてのずれ(intercept)は 20変数 -0.201、4変数 -0.188 とどちらもマイナスですが、これは開発データのイベント割合 34.0% に対して検証データが 30.6% と低いためで、両モデルとも全体的にやや高めに予測していることを示しています。Brier score は 20変数 0.189 に対し4変数 0.177(小さいほうが良い)。識別・較正・全体的な精度のどの軸で見ても、変数を絞ったモデルが勝っています。

較正を測る3つの指標を整理しておきます。

指標理想値今回の20変数モデル読み方
較正傾き(slope)10.7451未満=予測が極端すぎる(過学習のサイン)。1超=予測が控えめすぎる
全体としてのずれ(intercept)0-0.201マイナス=全体的に高めに予測している。集団のイベント割合の違いを映す
Brier score0に近いほど良い(0には到達できない)0.189識別と較正を合わせた全体的な精度。イベント割合に依存するため、同じデータ上での比較に限って使う

どのリスク帯でずれているのかは、平均値だけでは見えません。予測値の順に10等分し、群ごとの予測平均と実測割合を並べたのが次の表です。

リスク群予測平均実測割合差(実測−予測)人数
群1(最低リスク)0.0550.066+0.011500
群20.1120.118+0.006500
群30.1630.194+0.031500
群40.2130.198-0.015500
群50.2700.244-0.026500
群60.3340.344+0.010500
群70.4060.368-0.038500
群80.4920.448-0.044500
群90.5980.456-0.142500
群10(最高リスク)0.7650.622-0.143500

低リスク側(群1〜3)では予測と実測がよく合っているのに、上位の群になるほどずれが広がり、群9で -0.142、群10では -0.143 と大きく過大予測になっています。臨床の言葉に直すと、群10の患者に「発症する確率は約76%です」と伝えたのに、同じような患者500人のうち実際に発症したのは62%だった、ということです。治療方針や患者説明にこの確率を使うなら、看過できないずれです。

そして本章の核心が、最後の実験です。 線形予測子(ロジット)をそのまま2倍にすると、予測確率は極端な方向に押し広げられ、較正傾きは 0.745 から 0.372 へと半分になります。ところが AUC は 0.721 のまま、1ミリも動きません。AUC は予測値の順序しか見ていないため、順序を保ったままの変換にはまったく反応しないのです。

💡 ポイント
AUC が同じ 0.721 でも、確率としての当たり方はまったく違いうる——これが「識別と較正は別物」の意味です。モデルを「高リスク群/低リスク群の層別化」にだけ使うなら識別で足りますが、予測確率そのものを意思決定や患者説明に使うなら、較正の確認は必須です。「AUCが高い=良いモデル」という等式は成り立ちません。

実務での落とし穴とFAQ

🔑 まとめ・実務ポイント
見かけの数字を「性能」として報告しない。開発データ上のAUCは、少なくとも最適性補正後の値と併記する。
内部検証は変数選択ごと繰り返す。変数を選んでから検証を始めると、楽観バイアスを過小評価する。
確率を意思決定に使うなら較正を確認する。較正傾き・全体としてのずれ・リスク群別の予測と実測の突き合わせをセットで示す。
AUCだけでモデルを比較しない。較正やBrier scoreまで見ると順位が変わることがある。
開発時の集団と、実際に使う集団の違いに注意する。イベント割合が違うだけで全体としてのずれが生じる。

Q. AUCはいくつあれば「良いモデル」と言えますか?

用途を抜きにした基準値はありません。同じ 0.721 でも、スクリーニング用途では物足りず、既知の因子だけでは予測が難しい領域では十分に有用ということもあります。判断すべきは「既存の指標や臨床判断に上乗せがあるか」「その閾値で運用したとき利益が不利益を上回るか」であって、絶対値ではありません。加えて、較正が壊れていれば、AUCがいくら高くても確率としては使えません。

Q. 外部検証データが手に入らない場合はどうすればよいですか?

まずはブートストラップによる最適性補正を、変数選択も含めて正しく回してください。本記事の例では、外部データを1件も使わずに補正後 0.707 という値が、実際の外部性能 0.721 をよく言い当てました。そのうえで、施設・時期・地域といった単位で分けた検証を検討します。リアルワールドエビデンス(RWE)入門で扱っているようなデータベースが使えるなら、集団が異なる外部検証の機会になります。研究デザインとして外部の集団に当てはめる枠組みを設計したい場合は、Target Trial Emulation(TTE)とはの考え方も参考になります。

Q. 機械学習(ランダムフォレストや勾配ブースティング)でも同じ検証が必要ですか?

必要です。むしろ、より強く必要になります。柔軟なモデルほど開発データの偶然のパターンを拾う余地が大きく、見かけの性能と実性能の差が開きやすいためです。ハイパーパラメータの調整もモデルづくりの一部ですから、それも含めてブートストラップやクロスバリデーションの内側で繰り返さなければ、楽観バイアスを取りこぼします。また、識別が高くても確率としては過度に自信のある予測を出すことがあり、較正の確認が欠かせません。予測の中身をどう説明するかについては説明可能AI(XAI)とはもあわせてご覧ください。

Q. モデルが過大予測していました。作り直すしかないのでしょうか?

いいえ、再較正(recalibration)という選択肢があります。順序(識別)が保たれているなら、予測値そのものは情報を持っています。全体としてのずれだけを直すなら intercept を新しい集団で推定し直す、傾きの極端さも直すなら intercept と slope の両方を推定し直す、という段階的なやり方が使えます。今回の20変数モデルは較正傾き 0.745・全体のずれ -0.201 ですから、この2つを新集団で当て直すだけで、確率としての使い勝手はかなり改善します。ただし再較正は過学習そのものを消すわけではないので、変数の入れすぎが原因ならモデル自体を見直すほうが本筋です。なお、特定の患者集団だけずれて見える場合は、本当の差か偶然かを慎重に扱う必要があります。サブグループ解析と交互作用検定を正しく行うで述べた注意点は、予測モデルの部分集団評価にもそのまま当てはまります。

📚 この記事をより深く理解するための参考書籍

予測モデルの開発と検証を、もう一段深く学びたい方に3冊を紹介します。

『臨床予測モデル ― 開発・妥当性確認・更新の手引き』Ewout W. Steyerberg 著、手良向聡・大門貴志 監訳(朝倉書店、2023年)
臨床予測モデルの開発から妥当性確認、そして更新(再較正)までを一冊で扱った定番書の邦訳です。本記事で紹介した楽観バイアスのブートストラップ補正、較正傾き、Brier score といった指標が、どれも独立した章として詳しく解説されています。「自分のデータでどこまでやるべきか」を判断したい方が、次に手を伸ばす一冊です。
『データ解析のための統計モデリング入門――一般化線形モデル・階層ベイズモデル・MCMC』久保拓弥 著(岩波書店)
通称「緑本」と呼ばれる、一般化線形モデルの定番入門書です。本記事で使ったロジスティック回帰がどういう仕組みで確率を出しているのか、当てはまりの良さをどう測るのかが、Rのコードとともに丁寧に説明されています。本記事の出発点を、数式の側から納得したい方に向いています。
『統計的学習の基礎 ―データマイニング・推論・予測―』T. Hastie・R. Tibshirani・J. Friedman 著/杉山将ほか監訳(共立出版)
予測誤差を「バイアスとバリアンスの分解」として捉える視点を、統計と機械学習の両方から体系的に学べる大著です。本記事の楽観バイアスや交差検証は、この本ではモデル評価の一般論として位置づけられています。ランダムフォレストや勾配ブースティングまで扱うため、機械学習へ進みたい方にも適しています。

関連記事・次のステップ

予測モデルの土台をもう一度固めたい方は、2値変数とロジスティック回帰ROC曲線とAUCをRで実装するから読み直すのがおすすめです。モデルの形そのものを整理したい方には一般線形モデルと一般化線形モデルの違い一般化線形モデル(GLM)の裏側、イベントが極端に少ない場面にはFirthのロジスティック回帰とはが対応します。アウトカムの型が違う場合は順序ロジスティック回帰ポアソン回帰とはCox比例ハザードモデル入門をご覧ください。

「当てはまりの良さ」と「汎化性能」の関係を掘り下げたい方には、モデル選択の基礎:AIC・BICを“情報量”として理解するAIC/BICの“本質”を数理統計から理解するを。今回使った再標本化の考え方そのものはブートストラップ法とはで、回帰分析の基礎は線形回帰の“本質”をつかむ統計検定準1級「回帰分析」攻略で扱っています。

「説明するモデル」の側に進みたい方は、交絡(交絡因子)とは傾向スコア分析とは逆確率重み付け(IPTW)によるATE推定へ。予測モデルをリアルワールドデータの文脈で使う話はリアルワールドエビデンス(RWE)入門Target Trial Emulation(TTE)とは、機械学習モデルの説明性は説明可能AI(XAI)とはが入口になります。Rの操作そのものが不安な方はR入門から始められます。

まとめ

本記事では、臨床予測モデルの検証とは何を確かめる作業なのかを、Rの実測値をたどりながら整理しました。

最初に確かめたのは、見かけの性能が当てにならないという事実です。候補20変数をすべて投入したモデルは、開発データ上ではAUC 0.775と良く見えましたが、外部の検証データでは0.721まで落ちました。一方、あらかじめ4変数だけと決めておいたモデルは、見かけ0.738に対して外部0.756とほとんど落ちません。見かけの数字で選べば20変数モデルの勝ちですが、実際に他の患者に使ったときの順位は逆転します。変数を増やすほどモデルが良くなるように見えるのは、開発データ限りの偶然を拾っているからにすぎません。

次に確かめたのは、その落ち幅を外部データなしで見積もる方法です。ブートストラップ標本のなかでモデルづくりを最初からやり直し、標本内での評価と元データでの評価の差を平均すると、楽観バイアスは0.068と求まりました。見かけの0.775からこれを引いた0.707は、外部データでの実際の値0.721を誤差0.014で言い当てています。同じデータを2:1に分ける検証は、分け方しだいで0.551から0.793まで動いてしまい、1回の分割で出た数字を報告することの危うさもあわせて確認しました。

そして最後に、識別と較正は別の性質だという点です。20変数モデルの較正傾きは0.745と1を下回り、予測が極端すぎることを示していました。最上位のリスク群では予測0.765に対して実測0.622と、高リスク者ほど過大に予測しています。さらに、線形予測子を2倍にするという操作を加えると、較正傾きは0.372まで壊れるのに、AUCは0.721のまま1ミリも動きません。AUCは予測値の順序しか見ていないため、確率の数字そのものが正しいかどうかについては何も語っていないのです。

予測モデルを臨床や意思決定の場に持ち込むということは、「この患者は何%です」という数字を誰かに手渡すということです。その数字の順序が正しいだけでは足りず、大きさそのものが当たっている必要があります。ご自身のモデルを評価するときは、AUCを1つ報告して終わりにせず、内部検証で補正した値と較正傾き、そして予測と実測を突き合わせた表までを揃えていただければと思います。手元のデータで実際に手を動かしてみたい方はブートストラップ法とはを、モデルの複雑さと汎化性能の関係をもう一段深く知りたい方はモデル選択の基礎:AIC・BICを“情報量”として理解するをご覧ください。良い予測モデルとは、開発データでよく当たったモデルではなく、まだ見ぬ患者で当たり続けるモデルです。

ABOUT ME
tomokichi
外資系製薬会社で生物統計家として働ている1児のパパ。生物統計家とは何か、どのようなスキルが必要か、何を行っているのかを共有していきたいと思っております!生物統計に関する最新情報を皆様にお届けすべく、日々奮闘中です。趣味は筋トレ、温泉巡り、家族と散歩。