この記事は、連載「追わない技術」の第 2 回です。うまくいかない症状と、その対処を一つずつ並べています。各回はファイルやスクリプトを一つ置けば完結します。その仕組みがなぜ要るのかは、置いたあとに解説を読めば分かります。連載の全体像と各回の一覧は序論にあります。
今回は、どのモデルに渡すかを決める時点の話です。前回は「いつ止めるか」でしたが、今回は「誰に渡すか」になります。先に、題を否定しておきます。安いモデルは、使うべきです。私も使っています。やめたのは、一番安いのを「既定」にすることのほうです。値段は単価の比 × 量の比で決まり、量の比のほうは、測らないと分かりません。
もう 1 つ断っておきます。この回にもモデルの名前も、価格も、性能の数字も出てきません。半年で古くなるものは、この連載では書かないと決めています。比べるものは「高い側」と「安い側」とだけ呼びます。そのかわり、倍率は全部出します。手元の単価を掛ければ、そのまま判断に使えるはずです。
そして白状します。私はこの検証で 2 度、「差が無い」と読み間違えました。どちらも数字は正しく、読み方が間違っていました。2 度とも、指摘を受けて初めて気づきました。そのままなら「杞憂でした」と書いていたはずです。今回はその 2 回も含めて書きます。
一つ思い出してみてください。
安いモデルに任せた仕事は、本当に安く終わりましたか。
やり直しの回数まで、数えたうえで言えますか。
CC BY 4.0
予測から始めました —— 「安いほうは、見逃しが増えるはず」
最初に立てた予測はこうです。
安いモデルにテストを書かせると、テストは書けるが、見逃しが増える。
題材にテストを選んだのには理由があります。テストは、失敗が合格に隠れる唯一の成果物だからです。実装のバグはいつかどこかで落ちますが、テストの見逃しは何も言いません。「テストを書かせた」は合格で完了に見え、請求は後から来ます。
物差しは、過去に手元のコードへ入れて、手元の自動テストが実際に落とした変異 8 種を当て直したものにしました。この実験のために変異を書いてはいけません。書いた瞬間、見逃し率は書き方ひとつで 0% にも 100% にもなります。
比べる相手も置きました。同じ対象に、人(私)が書いた既存のテストです。そちらは 8 種のうち 6 種しか落としません。天井は満点ではありません。
条件は 2 つだけです。モデル(高い側 / 安い側)と、範囲(call 1 メソッドに絞る / 「必要だと思うテストを書いて」だけ)。観点は 1 つも渡していません。
1 回目 —— 安い側は、半分が走りませんでした
会話を引き継がない作業者を 12 体そろえて書かせ、変異を入れる前に、まず素のまま走らせました。
| 走った体 | 走った体だけの検出(8 種のうち何種を落としたか。⚠️ 多いほうが良い) | |
|---|---|---|
| 高い側 | 6 / 6 | 3.33 / 8 |
| ⚠️ 安い側 | 2 / 6 | 3.00 / 8 |
走らなかった 4 体の中身は、存在しないアサーションを呼ぶ・辞書の行を重複させるといった壊れ方でした。どれも、走らせた瞬間に分かります。
そこで私は、走らなかった 4 体を「見逃しとは別枠」として、検出の集計から外しました。理屈も一応ありました —— 走らないテストは、その場で気づけて、高いほうへ切り替えられます。
これが 1 つ目の間違いです。
間違い 1 —— 走らなかったテストを、母数から落としていました
外した結果、残った安い側は 2 体だけです。その 2 体の平均を「安い側の実力」として、高い側 6 体と並べていました。
⚠️⚠️⚠️ 走らなかったものを母数から落とすと、いちばん危ない側が消えます。
直し方は簡単でした。現実と同じように差し戻したのです。エラーの出力だけを返して「直してください」と送る。何が悪いかは言いません(言えば、それは物差しを渡すことになります)。
差し戻しを重ねて、12 体とも合格にしました。そのうえで数え直すと、こうなりました。
| 範囲を絞る | 絞らない | |
|---|---|---|
| 高い側 | 3.33 / 8 | 3.33 / 8 |
| 安い側 | 3.33 / 8 | 2.33 / 8 |
範囲を絞れば、安い側は高い側と同点です。私はここで、こう書こうとしました。「安いモデルで増えるのは、その場で気づける失敗のほうです。見逃しは増えません。」
これも間違いでした。
間違い 2 —— 物差しに、天井がありました
8 項目のうち 3 項目は、12 体とも 0 でした。人が書いた既存のテストは、その 3 つを落とします。
3 つを並べてみると、共通点がありました。どれも「材料か測り方を、先に自分で用意しないと書けない」項目です。
- 表の中でいちばん長い見出し語を、自分で辞書に入れる
- 同じ表記に読みを 2 通り持たせる
- 戻り値ではなく、問い合わせの本数を数える
課題は、そんなことを一言も言っていませんでした。「単体テストを書いてください」としか書いていない。
つまり 12 体とも 0 だったのは、書けなかったからではなく、課題がそこを指していなかったからかもしれない。
そこで、課題に 1 段落だけ足しました。
setupで作る辞書に現象が入っていなければ、そのテストは何も見ていません。 見たい振る舞いが出せるように、辞書のほうを組み立ててください。 戻り値を見るだけでは分からない振る舞いもあります。そういうものは、測り方のほうを用意してください。
8 項目はどれも名指ししていません。言っているのはやり方だけです。
そして両方の条件に、1 バイトも同じだけ渡しました(条件の差を増やしてしまうと、測っているものが変わります)。
天井が外れたら、結論がひっくり返りました
もう一度 12 体に書かせました。3 項目とも動きました。
| 1 回目(天井あり) | ⭐ 2 回目(天井なし) | |
|---|---|---|
| 高い側 | 3.33 / 8 | 5.00 / 8 |
| 安い側 | 2.83 / 8 | 2.83 / 8 |
| 高い側・範囲を絞る | 3.33 / 8 | 5.00 / 8 |
| ⚠️⚠️ 安い側・範囲を絞る | 3.33 / 8(同点) | 3.00 / 8(同点ではない) |
天井を外すと、高い側だけが伸びました。安い側は 2.83 のまま動きません。
⚠️⚠️ 「絞れば同点」は、両方が簡単な項目しか取れず、そこで並んでいただけでした。
ついでに、範囲の絞り方は見逃しには効きませんでした(高い側は 5.00 と 5.00 で差 0、安い側は 3.00 と 2.67)。1 回目で範囲が効いていたのは「そもそも走るか」のほうで、見逃しの側ではありません。
最初の予測は当たっていました。ただし、当たっていると分かるまでに、母数の落とし方と物差しの天井を 2 つとも直す必要がありました。
差し戻しは、テストを消させます
差し戻しの回数は、高い側が 2 回の実験とも 0 回、安い側は 1 回目が 6 回・2 回目が 10 回でした。ここまでは想像どおりです。
想像していなかったのは、直し方のほうです。「実装は正しい。テストを直してください」と送ると、返ってきた直しのうち 3 件は、失敗しているテストを消したものでした。
- 「上限ちょうどは通す」という 1 件を、削除
- 全角の正規化を見る 1 件を、削除
- 重複した辞書の行を消さず、例外だけを飲む書き方に変更
どれも合格になります。そして、消えた 1 件はそのまま見逃しになります。
⚠️⚠️⚠️ 「実装は正しい」と言って差し戻すと、直すより消すほうへ倒れます。
これはモデルの話ではなく、差し戻し方の話です。
ただし、差し戻しが要ったのは安い側だけでした。だから結果としては、モデルの側に効いています。
原理: 安いかどうかは「単価の比 × 量の比」で決まります
ここまでの数を、判断に使える形へ畳みます。1 回の応答の値段ではなく、1 つの仕事が終わるまでの総額で見ます。
同じ課題で、安い側が高い側の何倍を使ったかです。
| 数えたもの | 範囲を絞る | 絞らない | 全体 |
|---|---|---|---|
| 初めて読ませた量 | 1.64 倍 | 1.24 倍 | 1.48 倍 |
| ⭐ 使い回せた量 | 9.71 倍 | 2.62 倍 | 5.29 倍 |
| 書いた量 | 0.26 倍 | 0.38 倍 | 0.31 倍 |
| 手番(差し戻し込み) | 3.33 倍 | 2.00 倍 | 2.67 倍 |
安い側は、単価が安いだけではありません。量そのものが多いのです。やり直すたびに会話が伸び、伸びたぶんが次の手番でもう一度流れます。
使い回した部分は安く課金されますが、ゼロではありません。ここを 0 と見なすと、リトライは「ほぼタダ」に見えます。5.29 倍のものを 0 と数えれば、どんな結論でも作れます。
書いた量だけは安い側のほうが少ない(0.31 倍)ので、出力の重い仕事なら向きが変わります。
⭐⭐⭐ 値段は「単価の比 × 量の比」。単価の比はカタログに書いてありますが、量の比は測らないと分かりません。
だから「一番安いから既定」は、片方だけを見た判断です。同じ理由で「一番高いから既定」も片方だけです。既定を置かず、用途ごとに 2 つとも数えます。
前作で、私は翻訳の用途では安い側を使うと書きました。その判断はいまも変えていません。あの用途では、安い側のほうが手数まで少なかったからです。今回の結果は、それを打ち消しません。打ち消さないことこそが要点で、用途が変われば向きも変わるというだけです。
検証手順: 単価の比と、量の比を、別々に数える
前提
- 同じ課題を、2 つのモデルへ同じ材料で渡せること。材料が 1 バイトでも違うと、モデルの差ではなく材料の差を測ります
- 合否がプログラムで出る物差しがあること。人が読んで採点すると、測る回数が費用になって続きません
- 使ったトークンが、新しく読ませた量・使い回した量・書いた量に分けて残ること。分かれていないなら、この手順の後半は使えません
所要時間: 半日(物差しが既にある場合)
手順
- 物差しにする変異を、過去に手元のコードへ入れて、手元の自動テストが落としたものから採ります。この実験のために書いてはいけません
- 人が書いた既存のテストを、同じ物差しに掛けます。これが天井です。満点なら物差しが緩いので、変異を足してください
- 同じ課題を 2 つのモデル × 2 つの範囲 × 3 回渡し、書かせます。走らせるのはこちらで、書き手には走らせません
- 変異を入れない状態で全部を走らせます。失敗した体を「集計から外す」のは禁止です。エラーの出力だけを返して差し戻し、全部が合格になるまで繰り返します
- 合格になったものへ、変異を 1 種ずつ当てて走らせます。変異 1 種につき全員ぶんを 1 回で回せます
- 差し戻しの回数と、新しく読ませた量・使い回した量・書いた量を、モデルごとに合計します
- 手元の単価表を掛けて、完了までの総額を出します
合格条件(すべて満たすこと)
- 手順 2 で、人の書いたテストが落とせない変異が 1 つ以上ある(満点なら天井です)
- 手順 4 のあと、全部の体が合格である(1 体でも失敗したまま数えると、いちばん危ない側が母数から消えます)
- 手順 5 で、全員が 0 の項目が、全体の半分未満である。半分以上あるなら、課題がそこを指していません(材料と測り方を自分で用意する、と課題に書き足してください)
- 手順 6 で、使い回した量が 0 でない(0 なら分けて取れていません)
合格しなかったとき
- 人のテストが満点 —— 物差しが緩いか、手元の自動テストが落とした変異を採っていません
- 失敗した体が合格にならない —— そこが「高いほうへ切り替える」判断点です。回数を費用として記録してから切り替えます
- 全員が 0 の項目が半分以上 —— 天井です。課題の骨に 1 段落足して、両方の条件へ同じだけ渡し、測り直します
- 使い回した量が 0 —— 記録が分かれていません。分けて取れないなら、量の比は出さずに手番だけで判断します
後始末
- 一時的に置いたテストを消します。記録(トークンの内訳)は残します
- 結果は日付つきで残します。記録は増え続けるので、同じスクリプトを明日走らせると数が動きます
注意: この数字が言えないこと
1 つの枡が 3 体しかありません。
向きだけを読んでください。
5.00 と 2.83 の差は、順位としては読めますが、倍率として持ち出せる精度ではありません。
比べたのは 2 点だけです。高い側と安い側を 1 つずつ選んだので、「安いほど落ちる」という直線は、ここからは引けません。
8 項目のうち 1 つは、天井を外したあとに 12 体とも 0 になりました(⭐ 天井があったときは、12 体のうち 1 体が落としていた項目です)。人が書いたテストも、それは落とせません。
つまりまだ天井が残っています。「どこまで差があるか」は、今回の物差しでは測り切れていません。
差し戻しは「実装が正しい」という前提で送っています。その前提そのものが、テストを消させました。
実務では実装が間違っていることもあるので、この差し戻し方をそのまま真似しないでください。
書き手にテストを走らせていません。走らせられれば、安い側の失敗は自分で気づけたはずです。
「走らせない丸投げ」の数字だと思ってください。
最後に、題材のコードは、コメントがかなり厚いものでした。壊したときに何が起きるかまで、実装自身が書いています。コメントの薄いコードでは、両方とももっと落ちるはずです。そのまま持ち出せる数ではありません。
CC BY 4.0 はここまで
既定にしなくなったもの
- 「安いほうで足りるだろう」で渡すのをやめました。それは予測です。単価の比はカタログにありますが、量の比は測らないと出ません
- 走らなかった結果を、集計から外すのをやめました。外した瞬間、いちばん危ない側が母数から消えます。差し戻して、全部を合格にしてから数えます
- 「差が無い」と書く前に、物差しが動くかを見るようにしました。全員が 0 の項目が半分あるなら、それは差が無いのではなく、測れていないだけです
- 「実装は正しい。テストを直せ」とだけ言って差し戻すのをやめました。消して合格にする道が、そこに開いています
- やめていないこともあります —— 用途ごとに安い側を選ぶことです。前作で選んだ用途は、いまも安い側のままです
安いモデルは安い、という感じは、たいてい正しく見えます。請求のうち、1 回ぶんの単価しか見ていないからです。見えないのは、やり直した回数と、そのたびに読み直された会話のほうでした。
次回は「最新モデルを使わない技術」。私は、新しいモデルが出た日に乗り換えるのをやめました。強いモデルでないと通らなかった仕事が、モデルを変えずに通るようになったからです。