Featured image of post 機械学習におけるデータセットの分割の再考

機械学習におけるデータセットの分割の再考

目次

背景

  • 機械学習では、データをTrain・Validation・Testの3つに分けるのが定石とされている
  • Trainでモデルを学習し、ValidationでハイパーパラメータやEarly Stoppingを決め、最後にTestで未知データに対する性能を測るという構造
  • しかし実際にシステムを開発していると、この3分割だけでは説明できない問題にぶつかることがある
  • 特に「Testで失敗している理由を知りたいが、Testを見て改善した瞬間、そのTestはもはやTestではなくなる」という問題がある
  • ここでは、Train・Val・Testという古典的な3分割を、DiagとChallengeを加えた5つの役割に拡張して整理する

Train・Val・Testの役割

まず、通常の3分割それぞれの役割を整理する。

Train(学習データセット)

モデルのパラメータを学習するためのデータ。以下の式で表される、学習データ上での損失を最小化するパラメータ$\theta^*$を求めるために使う。

$$ \theta^* = \arg\min_\theta \hat{R}_{\text{train}}(\theta) $$

Validation(検証データセット)

モデルそのものではなく、学習方法を選択するためのデータ。

  • Learning Rate
  • モデルサイズ
  • 正則化係数
  • Data Augmentation
  • Early Stopping
  • モデルアーキテクチャ

これらを選ぶために使う。つまりValidationも、広い意味では学習プロセスの一部といえる。

Test(評価データセット)

最後までモデル開発から隔離し、最終的な汎化性能を測定するためのデータ。

  • Testを見て意思決定してはいけないという原則が重要
  • Testを見ながらモデルを改善すると、モデルの重みを直接学習していなくても、開発者自身がTestに適応してしまう
  • 結果として、Testへの「開発プロセス全体の過学習」が起きる

train, val, testの違い

  • train data:重み更新に使う
  • validation data:学習中のモデル選択、early stopping、ハイパーパラメータ調整に使う
  • test data:最後の最終評価に使う

Train分布と本番分布が違うという問題

古典的なTrain・Val・Testの説明には、以下のような暗黙の仮定がある。

$$ P_{\text{train}} \approx P_{\text{val}} \approx P_{\text{test}} \approx P_{\text{deployment}} $$

しかし、実運用ではこの仮定は頻繁に崩れる。

  • TrainはWeb画像だが、本番はスマートフォン画像
  • Trainは過去のユーザーだが、本番は将来のユーザー
  • Trainはある病院のデータだが、本番は別の病院
  • Trainはある国のデータだが、本番は別地域
  • Train時とDeployment時で商品の構成が変わる

WILDSベンチマークは、病院・撮影場所・時間・地域など、実世界で自然に発生するDistribution Shiftによって、In-distribution性能とOut-of-distribution性能の間に大きな差が生じることを示している。つまり、以下の不等式で表される状況は例外ではなく、実世界ではかなり普通に起きる。

$$ P_{\text{source}} \neq P_{\text{target}} $$

Testの注意点

Testのパラドックス

例えば、最終Testで性能が大きく低下したとする。Val Accuracyが94%なのに、Test Accuracyが72%だったとすると、開発者は当然「なぜ22ポイントも落ちたのか」を知りたくなる。そこでTestデータを見ると、以下のようなことが分かる。

  • 暗い画像で失敗している
  • 特定の端末だけ性能が低い
  • 特定カテゴリのRecallが悪い
  • Trainに存在しない背景が多い

これがError Analysis。この分析結果を元に、暗所画像をTrainに追加する、Augmentationを変更する、ラベル設計を変更する、モデル構造を変更する、といった改善を行う。

しかし、この瞬間に問題が起きる。

  • Testから得られた情報でモデルを改善した以上、そのTestはもう完全なBlind Testではない
  • 形式的にモデルパラメータへTestを入力していなくても、Test→人間→モデル設計という情報経路が成立している
  • つまりTestは、実質的に開発用データセットになってしまう

なぜ問題なのか: 条件付き確率になってしまう

この問題を、もう少し形式的に表すと分かりやすい。本来知りたいのは、モデル$f$のデプロイ分布上での汎化性能である。

$$ R_{\text{true}}(f) = \mathbb{E}_{(x,y)\sim P_{\text{deployment}}}\left[\ell(f(x), y)\right] $$

Testでの性能$\hat{R}_{\text{test}}(f)$がこの$R_{\text{true}}(f)$の妥当な推定値になるのは、$f$が$D_{\text{test}}$と独立している場合に限る。

  • ところが、Testを覗いて開発プロセス(モデル構造・データ・ハイパーパラメータなど)を調整すると、$f$は$D_{\text{test}}$の関数になってしまう
  • このとき実際に測っているのは、以下のような、その特定の$D_{\text{test}}$に条件付けられた量になる
$$ \mathbb{E}\left[\ell(f(D_{\text{test}})(x), y) \mid (x,y) \in D_{\text{test}}\right] $$
  • これは、知りたかった無条件の汎化性能$R_{\text{true}}(f)$とは別物で、その一回限りのTestサンプルに対して有利になるよう調整された、条件付きの当てはまりの良さに過ぎない
  • カンニングが問題なのは、倫理的な話である以上に、推定量として何を測っているかが変わってしまうという統計的な理由による

Target側にもDiagを作る

ここで自然な解決策として、Source側だけでなくTarget側も分割するという発想が出てくる。

  • Source側: Train、Val
  • Target側: Diag、Test

という構造になる。

diagとtestの違い

Diag(診断データセット)

DiagはTarget distributionを理解するためのデータで、開発者が見てよい。サンプルを直接確認し、以下のような分析をする。

  • Failure Mode
  • Slice別性能
  • 分布差
  • ラベル問題
  • Spurious Correlation

Diagから得られた知見は、Trainへ戻して改善に使う。

Diagで特に重要なのは、意図的にOODなサンプルも含めておくこと。

  • Trainがカバーしようとしているスコープ内のサンプルだけでなく、そのスコープの外(OOD)にあたるサンプルもDiagに混ぜておく
  • こうすることで、単に「今のスコープ内でどれだけ性能が出ているか」だけでなく、「スコープの境界がどこにあり、境界を越えるとどう性能が崩れるか」を観察できる
  • つまりDiagは、Target distributionの理解に加えて、スコープの境界そのものに対する試験という役割も持つ

Test(評価データセット)

  • Target distributionから採取するが、最後までBlindにする
  • 最終的に、「Diagを見ながら作った開発プロセスが、別のTargetサンプルにもGeneralizeしたか」を確認する
  • つまり、Train→Val→Diag→Error Analysis→Trainという改善ループと、Model→Blind Testという最終評価を分離する構造になる

Diagだけでも足りない理由

もう一つ問題がある。平均的なDiag performanceだけでは、モデルが持っている重要な弱点を発見できないことがある。

  • 例えば自動運転モデルで、全体のAccuracyが99.5%だったとする
  • 夜間・豪雨・逆光・工事現場・子どもの飛び出しといった状況だけで極端に性能が低ければ、実運用では重大な問題になる
  • しかし、これらが通常データの0.1%しか存在しなければ、平均Accuracyではほとんど見えない

そこで必要になるのがChallenge Set。

Challenge Setは分布を代表する必要がない

Challenge Setは、通常のTestとは目的が違う。Testは、以下のようにdeployment分布を代表することを目指す。

$$ P_{\text{test}} \approx P_{\text{deployment}} $$

一方Challengeは、必ずしもDeployment distributionを代表しなくてよい。むしろ意図的に難しいケースを集める。目的が違うため、以下のように分布が一致していなくても構わない。

$$ P_{\text{challenge}} \neq P_{\text{deployment}} $$

Challengeの目的は、平均性能を推定することではなく、特定の能力・弱点を診断すること。

  • ImageNet-Aのような研究はこの発想の典型例
  • 通常のImageNetとは異なり、既存モデルが失敗しやすい自然画像を意図的に集めることで、平均的なテストセットでは見えにくいモデルの弱点を明らかにする
  • Challengeは1つである必要もなく、Failure Modeごとに複数持ってもよい
    • Challenge(低照度)
    • Challenge(レアクラス)
    • Challenge(OOD)
    • Challenge(ロングテール)

5つの役割

ここまで整理すると、評価データは以下のようになる。

Dataset主目的開発者が見るかモデル改善に使用するか
TrainParameter LearningYesYes
ValModel SelectionYesYes
DiagError AnalysisYesYes
ChallengeFailure-mode / Capability EvaluationYesYes
TestFinal Generalization EstimateNoNo
  • つまり、Train・Val・Diag・Challenge・Blind Testという構造
  • ただし、これは「データを機械的に5分割せよ」という意味ではなく、重要なのはそれぞれの役割を分離すること

ValとDiagの違い

一見するとValとDiagは同じものに見え、実際に多くの文献ではValidation Setと開発用データセットがほぼ同義語として使われる。ここではあえて分離して考える。

  • Val: どのモデルを選ぶかを決めるためのデータ
  • Diag: なぜ本番で失敗するのかを理解するためのデータ

例:

  • 例えば、Val AccuracyでModel Aが91.2%、Model Bが92.1%ならModel Bを選ぶ
  • これがModel Selection
  • 一方Diagでは、「Model Bは平均では良いが、夜間画像ではModel Aより悪い」といった分析をする
  • これがError Analysis
  • つまり、ValとDiagは役割が異なる

ChallengeとDiagの違い

Diagは、Target distributionをなるべく代表する。一方Challengeは、意図的に分布を歪める。

  • 例えば本番データで、通常画像98%・夜間1%・豪雨0.5%・逆光0.5%だったとする
  • Diagではこの比率をある程度維持する
  • しかしChallengeでは、夜間33%・豪雨33%・逆光34%のように歪めてもよい

なぜならChallengeはPopulation Performanceではなく、Capabilityを測っているため。

メインsplitとサブsplit

ここまでのTrain・Val・Diag・Testと、Challengeとの違いを一般化すると、splitには2つの種類があると整理できる。

メインsplit

  • Train・Val・Diag・Test
  • いずれも「何らかの分布を代表する」ことが目的で、共通のデータプールからのholdout・stratified split、または独立したソースからのサンプリングという形で作られる
  • スコープや汎化性能そのものを定義・推定するためのsplit

サブsplit

  • Challengeのように、診断・分析という前段のステップを経て、事後的に目的を絞って作るsplit
  • メインsplitと違い、分布を代表する必要がない
  • Challenge以外にも、次の2種類がある

処方箋split

  • Diagやエラーアナリシスで見つかった特定の弱点(hard case・OOD・クラス境界の混同など)を、直接修正するために作るデータ
  • 機械学習におけるクラスとラベルの再考のhard case mining手法(shifted positive・adversarial negatives・Truncated Negative・Hidden Negativeなど)や、境界専用データセットの作り方がそのまま使える
  • 作った処方箋splitはTrainに戻し、次のTrain・Val・Diagサイクルで効果を確認する

err split

  • エラーアナリシスで見つかった実際のエラー事例そのものを、固定化して保存しておくデータ
  • 同じ間違いが再発していないかを継続的に確認する回帰テストとして使う
  • 機械学習におけるデータとデータソースの再考のデータ品質QAで触れた「過去に見つかったデータバグを回帰テストとして固定化する」という考え方と同じ

まとめると

split分布の代表性作るタイミング
メインsplit(Train/Val/Diag/Test)必要データ収集時に一度作る
Challenge不要Capabilityを測りたい時に作る
処方箋split不要Diag・エラーアナリシスで弱点が見つかった後に作る
err split不要エラーアナリシスで見つかったエラーを固定化する時に作る

既存研究との関係

  • ここで整理した5つの役割そのものが、機械学習の標準規格として確立されているわけではなかった
  • むしろ、既存研究で別々に議論されてきた問題を、1つの開発フローとして整理したものと考える方が正確

先行研究のTopics:

  • 分布シフト(WILDS)
    • Koh et al.のWILDSは、現実世界ではTraining distributionとDeploymentに近いTest distributionが異なることを明示的に扱っている
    • 病院、地域、時刻、カメラなどによる実世界のDistribution Shiftをベンチマーク化しており、Source/Targetを分けて考える必要性を示している
  • Target performanceの推定(Mandoline)
    • Chen et al.のMandolineは、SourceとTargetの分布差を、実務者が定義したSliceを利用してTarget上の性能推定に利用する
    • 「Target distributionを単一のAccuracyだけではなく、その構造を見ながら評価する」という、Diag的な発想に近い
  • 分布シフトに対するStress Test
    • Subbaswamy et al.は、単一の評価分布で平均性能を見るだけでなく、分布を変化させた際のモデルのRobustness・Stabilityを評価する枠組みを提案している
    • Challenge SetやStress Testingの考え方と接続する
  • Challenge Set(ImageNet-A)
    • Hendrycks et al.のImageNet-Aは、既存モデルが失敗しやすい自然画像を意図的に集めることで、通常のTest Accuracyでは見えにくい弱点を評価した、Challenge evaluationの分かりやすい例
  • Testから学習してしまう問題
    • Hernández-Orallo et al.は、AI evaluationを単一のAggregate Metricに潰すことの問題を指摘している
    • つまり、SystemとProblemの組み合わせに対するより詳細な評価情報を利用する考え方を議論している
    • タイトル自体が「Training on the Test Set」であり、評価データからどこまで情報を抽出してよいかという問題を正面から扱っている

ラベル・属性の管理

サンプルが持つ属性(クラス・Positive/Negativeなど)をどう区分・管理するかについては、機械学習におけるクラスとラベルの再考で詳しく扱っている。

SourceとTargetによる分類

以下のような定義になる。

  • Train・Val:
    • Source distributionからサンプリング
  • Diag・Test:
    • Target distributionからサンプリング(Diagは見てよい、Testは見てはいけない)
  • Challenge:
    • Target(デプロイ環境)に関連するが、意図的に分布を歪めて集める
    • 「独立」というより「Target寄りだが非代表的」という位置づけ

本質はInformation Boundary

ここで一番重要なのは、Train・Val・Diag・Challenge・Testという名前ではない。

  • 本質は、どの評価情報を開発プロセスへフィードバックしてよいのかというInformation Boundary
  • モデル開発は、Data->Metric->Human->Decision->Modelというループで進む
  • そのため、「モデルのWeight Optimizerに渡していないからTest leakageではない」とは限らない
  • 人間がTest結果を見てArchitectureを変更すれば、それも立派な情報伝達になる
  • したがって評価セットは、まず開発に利用してよいデータと、開発から完全に隔離するデータに分ける必要がある
  • その上で開発側を、Parameter Learning・Model Selection・Error Analysis・Capability Analysisに分けると、Train・Val・Diag・Challengeが生まれ、最後にBlind Testを置く、という構造になる

まとめ

  • 機械学習ではTrain・Val・Testの3分割が基本とされているが、実運用では学習分布と本番分布が一致しないことが珍しくない
  • Testで性能が低下した理由を知るにはTarget側のデータを観察する必要があるが、Testを観察して改善に利用すると、そのTestはもはや完全なTestではなくなる
  • そこでTrain・Val・Diag・Challenge・Testという役割分離を考える
    • Train: モデルを学習する
    • Val: モデルを選択する
    • Diag: 本番分布での失敗を理解する診断データセット
    • Challenge: 重要なFailure Modeを集中的に診断する
    • Test: 最後までBlindにして最終性能を測る
  • Source側からTrainとVal、TargetからTest、それと独立したDiagとChallenge
  • スコープという軸で見ると、Trainはスコープ内、Diagはスコープ内外の境界、Testはスコープを問わない未知のサンプルという整理もできる
  • Train・Val・Diag・Testは分布を代表するメインsplitだが、Challenge・処方箋split・err splitは診断や分析を経て事後的に作るサブsplitであり、分布を代表する必要がない
  • Testを覗いて開発プロセスを調整すると、測っている量が無条件の汎化性能から、その特定のTestサンプルに条件付けられた量にすり替わってしまう
  • これはデータ分割のテクニックというより、機械学習開発における情報の流れを設計する問題
  • 本当に守りたいのは、モデル改善に使った情報と最終評価に使う情報を分離すること

参考文献

Built with Hugo
テーマ Stack は Jimmy によって設計されています。