データとモデルのバージョン管理 — 再現できない実験は存在しない
「精度92%出ました」を半年後に再現できないなら、それは実験ではなく逸話です。中身からidを作るコンテンツアドレス、入力から出力への系譜(リネージ)、そして「どこまで固定すると割に合うか」の3段階を、ハッシュの式・マニフェストのコード・現場の落とし穴まで前提知識ゼロから解説します。
半年前の自分を、再現できるか
祖母の煮物のレシピを書き取ろうとして、困ったことはないでしょうか。「醤油は適量」「いつもの鍋で」「色を見て」。書いてある通りにやっても、同じ味にならない。レシピが悪いのではなく、再現に必要な情報の大半が書かれていないのです。
機械学習の実験ノートは、たいていこの祖母のレシピです。「このスクリプトで学習して、検証精度92%でした」。半年後、同じコマンドを回すと89%になる。データが更新されていたのかもしれないし、ライブラリが上がったのかもしれないし、シードを渡し忘れていたのかもしれない。原因を特定できないまま、その92%は誰にも信じられなくなります。
再現できない結果は、比較にも改善にも使えません。「Aより0.4ポイント良い」という主張は、AとBが同じ条件で測られたときにだけ意味を持つからです。再現できない実験は、実験ではなく逸話です。 この記事は、逸話を実験に変える道具の話をします。
出力を決めているのは4つの入力
何を管理すれば足りるのかを、まず整理します。学習の結果は次の4つで決まります。
- コード — モデル定義、学習ループ、前処理
- データ — 学習データ、検証データ、その分割の仕方
- 環境 — PyTorchのバージョン、CUDA、cuDNN、OSのライブラリ、GPUの型番
- 乱数と順序 — 重みの初期化、シャッフルの順、dropoutのマスク、データの供給順
このうち、まともに管理されているのは1つ目だけです。gitはコードのためにできていて、数百GBのデータも、コンテナの中身も、実行時の乱数も見ていません。「commit shaを記録しました」だけでは、4分の1しか記録できていないことになります。
残り3つの漏れ方は、だいたい決まっています。データは共有ドライブに置かれ、誰かが黙って行を足す。環境は「動いたときのマシン」に依存し、そのマシンが更新される。乱数は seed=42 と書いてあるのに、DataLoaderのワーカー数を変えたら供給順が変わる。どれも悪意はなく、ただ名前が中身を保証していないことから来ています。
名前ではなく、中身で呼ぶ
dataset_v2_final.jsonl というファイル名は、中身について何も約束していません。誰かが1行足しても名前は同じままですし、逆に同じ中身が train.jsonl と train_copy.jsonl という別名で2つ存在することもあります。名前と中身が独立しているせいで、名前を記録しても再現できないわけです。
発想を逆にします。中身から名前を作る。 これをコンテンツアドレス(content-addressing、内容によるアドレス付け)と呼びます。
はハッシュ関数(SHA-256など)です。ファイルの中身を最初から最後まで読み、固定長の値(SHA-256なら256ビット=16進数64桁)を吐き出します。この には3つの性質があります。
- 決定的: 同じ中身なら、いつどのマシンで計算しても必ず同じidになる
- 敏感: 1バイトでも違えば、まったく無関係な値になる(似た中身が似たidになったりしない)
- 一方向: idから中身は復元できない
つまりidは、そのファイルの指紋です。dataset_v2_final.jsonl の代わりに sha256:9f2b...c41a と呼べば、名前が中身を保証してくれます。ハッシュ関数そのものの性質はハッシュと近傍探索で扱っています。
「1バイト違えば別のid」は分かるとして、逆はどうでしょうか。違う中身が偶然同じidになる(衝突する)ことはないのか。誕生日問題の近似で見積もれます。
はどこかで衝突が起きる確率、 は登録した中身の個数、 はハッシュのビット数です。指数の中身が小さいときは と読めます。SHA-256()なら が1兆でも分母は ですから、確率は事実上ゼロ。同じハッシュなら同じ中身だと信じてよい、というのが実務上の結論です。
ディレクトリ丸ごとのidも作れます。各ファイルのidを計算し、パスと並べて辞書順にソートして一覧を作り、その一覧をもう一度ハッシュする。これがMerkleツリーで、gitのオブジェクトも同じ発想です。おかげで100万ファイルのデータセットが、64桁の1行で表せます。
指紋のありがたみは、比べるときに効きます。データセット内の重複を全ペア比較で調べると要素数の2乗に比例した回数が要りますが、指紋を取って表に入れれば要素数に比例した回数で済みます。この差は「少し速い」ではなく、桁が変わります。
コメント
コメントにはログインが必要です