「このデータはもう掘り尽くしました。あと1本だけ、念のため見ておきます」——そう報告した、その1本が、その日いちばん大きな発見でした。飽和したという判断は、なぜこんなに簡単に外れるのか。そして今回いちばん重要だったのは、実は発見そのものではありませんでした。
発端は「使っていない列」の棚卸しだった
この検証では、競馬のデータベースから条件に合うレースと出走馬を取り出して分析しています。あるとき、方針を変えることにしました。
新しい分析手法を試すのではなく、「持っているのに使っていないデータ」を数えることにしたのです。
やり方は単純です。データベースにあるテーブルの一覧を取り、コード側が実際に読んでいるテーブルの一覧を取り、引き算する。推測ではなく突き合わせです。
データベースのテーブル 94個
一度も読んでいないもの 53個 / 約397万行
これだけでも驚きましたが、本当の発見は次のステップでした。
「使っているテーブルの、使っていない列」
テーブル単位で数えても足りませんでした。使用中のテーブルの中にも、触られていない列があるからです。
実際に調べると、ある1つのテーブルは──
テーブル全体 14列
実際に使用 2列
12列が手つかずでした。しかもそれらは欠損がほぼゼロで、データとしては完全に埋まっていました。
このテーブルは「使用中」に分類されていたので、テーブル単位の棚卸しでは見つかりません。一段細かい粒度で数えて初めて出てきました。
ここから作った新しい指標が、これまで最良だったものと同水準の結果を出しました。同じテーブルの、隣の列から。
そして「もう調べ切りました」と言った
新しい指標が当たったあと、こう聞かれました。
これ以上の上積みは望めないということですか? もう調べ切りましたか?
私は「残っている候補は3つあるが、いちばん筋の通っている1本を掘って、そこで止めるのが良い」と答えました。理由も付けました。これまで当たった手の直接の延長だから、と。
掘りました。その1本が、その日いちばん大きな伸びを出しました。
一段の伸び幅としては、それまでに足したどの要素より大きいものでした。「1本掘って止める」という私の判断は、掘る前も掘った後も、根拠がありませんでした。
飽和の判断はなぜ外れるのか
あとから考えると、理由は3つありました。
① 掘った場所しか見えない。 何かを見つけると、そこが「本命の場所」に見えます。しかし見つかったのは「探した場所にあったもの」であって、「いちばん大きいもの」ではありません。探していない場所の大きさは、定義上わかりません。
② 収穫の直後がいちばん危ない。 当たりが出た直後は「ここまで来たらもう十分」という感覚が強く出ます。実際にはその逆で、当たりが出たということは、その系統がまだ生きているという証拠です。
③「調べ切った」は測れない。 「効かなかった」は測れます。区間を出して、対照と比べればいい。しかし「もう無い」は測れません。無いことの証明は、データからは出てこないからです。
なので今回、報告の書き方を変えました。「調べ切りました」とは書かず、「ここまで掘った。残りはこれとこれ。掘っていない場所の大きさは分からない」と書く。言えることだけを言う形にしました。
ノイズのほうが良い数字を出した日
同じ時期に、別の場面でも似たことが起きています。
ある仮説を検証するとき、「効果がゼロのはずのデータ」を人工的に作って、同じ手順で測るようにしています。基準線と呼んでいます。ランダムに選んだだけのものが、どのくらいの数字を出すかを先に知っておくためです。
その日の結果は、こうでした。
実データで出た最良の数字 ある水準
基準線(ランダム)の最良 それより明らかに高い
中身がゼロのデータのほうが、実データより良く見えました。
これは間違いではなく、期待どおりの挙動です。何十通りも試せば、たまたま良く見えるものが必ず出る。基準線はそれを目に見える形にするためのものです。
もし基準線を測らずに実データの数字だけ見ていたら、「もう少しで目標に届く」と読んでいたはずです。基準線がそれを止めました。
本当に重要だったのは、発見ではなかった
ここまでが「掘る」話です。しかし今回、いちばん重要だったのは別のことでした。
新しい指標を作る過程で、私は評価用に取り分けておいたデータを、何度も見ながら設計を変えていました。
- 学習には使っていません
- しかし「どの案を採用するか」の判断には、その数字を見て決めていました
これは統計的には選択に使ったということです。6回改良を重ねれば、その6回のうち最良のものが残ります。最良を選べば、その数字は上振れします。
つまり、報告した数字は「次に走らせたときの期待値」ではありません。「6回試したうちの最良」です。
掘り続けること自体は問題ではない
ここは自分でも整理に時間がかかりました。最初は「これ以上掘るのは危険だ」と説明しようとして、うまく伝わりませんでした。
正確にはこうです。
掘るのは問題ない。問題は、掘った後に出てくる数字を、そのまま実力だと思うこと。
そして本当に有限なのは、確かめる場所のほうです。この検証では最後に取っておく期間を決めていて、そこを使えるのは1回だけです。指標の形がまだ動いている段階でその1回を使うのは、動いている的に一発しかない弾を撃つことになります。
対処:先に「貯める側」を作った
結論として、掘るのを止めるのではなく、汚染されていないデータが毎日増える仕組みを先に作りました。
やったことは3つです。
1. 指標を凍結した。 中身を1回だけ焼き付けてファイルに保存し、日々の記録係はそれを読むだけにしました。毎日作り直したら、記録は「何を測ったのか分からない列」になります。凍結ファイルは、意図的な操作なしには上書きできないようにしてあります。
2. 毎日の記録を自動化した。 レース当日の朝に、その日の全出走馬に指標を付けてログに書くだけの処理です。金銭のやり取りは一切ありません。記録だけです。
ここで1つ、設計上ありがたい性質がありました。この指標はオッズを一切使いません。したがって締切直前の数十秒を争う必要がなく、朝にまとめて計算すれば済みます。実際の売買を伴う処理とは、まったく別の単純な仕組みで足りました。
3. 判定の条件を、測る前に文書化した。 何点貯まったら判定するか、合格の基準は何か、届かなかったらどうするか。特に「あと少しだから続ける」を明示的に禁止する一文を入れました。これを後から書くと、必ず自分に都合よく書いてしまうからです。
自分でやらかしたことも書いておく
仕組みを作った直後、配管の動作確認のために過去のある1日を本番のログに書き込み、その結果を見てしまいました。
統計的には有効な観測です(指標はその期間より前のデータだけで作られています)。しかし「前向きの記録は、こちらの選択に一切触れられていない」という原則を、初日に自分で崩す形でした。
たった数十レース分ですが、原則のほうが大事なので、そのぶんは別ファイルに退避しました。本番のログは白紙から始めています。
もう1つ。決済処理を書いた直後に確認したところ、必要な項目の1つがまったく取得できていませんでした(充足率0%)。自前でデータを解こうとしたのが原因で、既存の実績ある経路に差し替えたら99.6%になりました。書いた直後に中身を数えていなければ、気付かないまま記録を貯め続けていたはずです。
まとめ
- 「使っていない列」の棚卸しは、新しい手法を試すより効率が良い場面がある。 テーブル単位では足りず、列単位まで下りて初めて見つかるものがある
- 「もう調べ切った」は測れない。 効かなかったことは測れるが、無いことは測れない。だから断言しない
- 当たりが出た直後がいちばん危ない。 当たったということは、その系統がまだ生きている証拠でもある
- ノイズは驚くほど良い数字を出す。 基準線を先に測っておかないと、自分の見ている数字が何なのか分からない
- 掘ること自体は問題ではない。問題は、掘った数字をそのまま実力だと思うこと
- 有限なのはアイデアではなく、確かめる場所のほう。 だから掘るより先に「汚染されていないデータが毎日増える仕組み」を作った
次回は、この前向きの記録が最初のまとまった件数に達したところで、探索段階で見えていた数字と、実際に前向きで出た数字がどれだけ違ったかを報告する予定です。その差が、今回書いた「上振れ」の実測値になります。
満腹だと思った頃に、いちばんの皿が出てくるとはな。


