データ化学工学研究室物語
第I部 研究室配属前夜のデータ化学工学
研究室配属前の学部三年生が、AI・機械学習を化学・化学工学の研究に使う考え方を学ぶ。
第8章 高い精度は本当に良いのか
新人トレーニングの七週目に与えられた課題は、前の週に得た一つの精度を、いったん疑うことだった。
同じデータを用いて、random_state と number_of_neighbors を変えた結果を比較してください。
トレーニングデータの中でクロスバリデーションを行い、number_of_neighbors を決めてください。
テストデータをどの段階で使ってよいか、また異なるデータ分割の r2 を直接比較してよいかを考えてください。
第7章の報告会が終わった日の夕方、先輩から slack に届いた課題を、遥は研究室の席でもう一度読んだ。
random_state。
number_of_neighbors。
クロスバリデーション。
テストデータを使う段階。
前の週には、研究室のサンプルプログラムを動かし、k = 5 のk近傍回帰モデルを作った。
トレーニングデータの MAE は 0.16。
テストデータの MAE は 0.26。
数字は一度得られた。けれど、その数字が、データの分け方を変えても同じなのかは確かめていなかった。
遥は、前回使った Notebook をコピーした。
最初に変えたのは、データを分割する部分だった。
for random_state in range(10):
x_train, x_test, y_train, y_test = train_test_split(
x, y, test_size=0.25, random_state=random_state
)
random_state を 0 から 9 まで変え、同じ k = 5 のモデルで、トレーニングデータとテストデータの MAE と RMSE を保存する。
実行すると、十通りの結果が縦に並んだ。そのうち五つを抜き出すと、次のようになった。
random_state = 0:train MAE 0.16、test MAE 0.26、test RMSE 0.34。
random_state = 1:train MAE 0.17、test MAE 0.20、test RMSE 0.27。
random_state = 2:train MAE 0.16、test MAE 0.35、test RMSE 0.46。
random_state = 3:train MAE 0.15、test MAE 0.24、test RMSE 0.32。
random_state = 4:train MAE 0.16、test MAE 0.30、test RMSE 0.39。
同じデータセット。
同じ説明変数と目的変数。
同じ k近傍回帰。
違うのは、どのサンプルをトレーニングデータにし、どのサンプルをテストデータにしたかだけだった。
それでも、テストデータの MAE は 0.20 から 0.35 まで変わった。
遥は、random_state = 1 の行に目を止めた。
この分け方なら、テストデータの MAE は最も小さい。
発表には、この結果を使えばよいのだろうか。
遥は対話型AIに尋ねた。
train_test_split の random_state を複数試し、テストデータの MAE が最も小さい random_state を採用してもよいですか。問題点と、より適切な評価方法を説明してください。
回答には、テストデータに都合のよい分割を選ぶことになり、性能を過大評価する危険があると書かれていた。
遥は研究室の入門資料でも、データ分割の考え方を確認した。
random_state は、モデルの性能を高めるためのハイパーパラメータではない。
どのサンプルを未知データとして評価するかを決めるための設定。
テスト結果を見て都合のよい分割を選べば、テストデータをモデル選択に使ったことになる。
遥は、結果の表の上に書いた。
random_state を選んで精度を上げない。
しかし、分割によって MAE が大きく変わった理由は、まだ分からない。
遥は、random_state = 1 と random_state = 2 のテストデータを見比べた。
random_state = 1 のテストデータには、透過係数が極端に高いサンプルがほとんど入っていなかった。
一方、random_state = 2 には、polymer_047 と、透過係数が特に低いサンプルが二つ入っていた。
目的変数の範囲が広く、周囲に似たトレーニングサンプルの少ない点も多い。
同じモデルでも、解かせる問題の難しさが違っていた。
遥は、テストデータごとに目的変数のヒストグラムを作った。
random_state = 1 では中央付近の値が多く、random_state = 2 では両端の値が多かった。
MAE や RMSE は同じ対数値の尺度で計算されるため、数値の大きさを見比べることはできる。
ただし、その違いをモデルだけの違いとは考えられない。評価したサンプルの難しさも一緒に変わっている。
r2 の値は、さらに大きく上下していた。
第7章で確認したように、r2 は各評価データにおける y のばらつきを基準に計算される。
サンプルも y の分布も違うテストデータの r2 を並べて、どの分割が優れているかを決めてはいけない。
遥はノートに書いた。
分割を変えると、モデルだけでなく、評価問題の難しさも変わる。
MAE と RMSE に加え、テストデータの y の分布と、含まれるサンプルも確認する。
異なるサンプル集合の r2 は、単純に大小を比較しない。
翌日の午後、遥は紺色のパーカーの先輩に結果を見せた。
「random_state = 1 なら、テストデータの MAE が 0.20 になりました」
先輩は表を見た。
「では、その分け方を採用しますか」
「採用しません。テスト結果を見て分割を選ぶと、評価が良く見える分け方を選んだことになるからです」
「random_state = 2 の MAE が大きいのは、モデルが悪くなったからですか」
「モデルの方法は同じです。難しいサンプルがテスト側に多く入ったことも影響しています」
先輩はうなずいた。
「一回の分割だけで得た精度は、分割の偶然にも左右されます。そこで、トレーニングデータの中で、分け方を入れ替えながら評価します」
クロスバリデーション。
前の章で、名前だけ聞いた方法だった。
遥は、5-fold クロスバリデーションの説明を読んだ。
トレーニングデータを五つの部分に分ける。
四つをモデル構築に使い、残りの一つを検証に使う。
検証に使う部分を入れ替え、五回の予測を行う。
すべてのサンプルは一度ずつ、自分を学習に使っていないモデルによって推定される。
遥は、number_of_neighbors の候補を用意した。
candidate_k = [1, 3, 5, 7, 10, 15]
最初は、第7章で作成した autoscaled_x_train に対して、そのままクロスバリデーションを実行した。
結果では、k = 5 の平均 MAE が最も小さくなった。
けれど、コードを見直していると、標準化の順番が気になった。
autoscaled_x_train は、外側のトレーニングデータ90サンプルすべての平均値と標準偏差で計算されている。
5-fold クロスバリデーションの中では、ある部分を検証データとして残す。
それなのに、その検証部分の値も、標準化の平均値と標準偏差の計算に使われていた。
目的変数は使っていない。
それでも、検証データの分布を、モデル構築前の処理に使っている。
遥は対話型AIに、クロスバリデーションと標準化の順番を尋ねた。
回答には、各foldで、モデル構築側のサンプルだけを使って標準化の平均値と標準偏差を求める必要があると示された。
前処理とモデルを一つの流れにまとめる Pipeline も紹介されていた。
遥は研究室のサンプルプログラムを確認し、次の形に直した。
pipeline = Pipeline([
(“scaler”, StandardScaler()),
(“model”, KNeighborsRegressor())
])
候補とする k は、モデル部分の設定として与える。
parameters = {“model__n_neighbors”: [1, 3, 5, 7, 10, 15]}
これなら、各foldで標準化を学習する範囲と、回帰モデルを学習する範囲が一致する。
修正前の方が、わずかに良い MAE だった。
けれど、それは検証データの情報が前処理へ入り込んだ結果かもしれない。
遥はスライドに書いた。
検証データの情報が、モデル構築や前処理に入り込むことをデータリークと呼ぶ。
数値が良くなることより、未知データを再現した手順で評価することを優先する。
Pipeline を用いた5-fold クロスバリデーションの結果は、次のようになった。
k = 1:平均 MAE 0.32。
k = 3:平均 MAE 0.27。
k = 5:平均 MAE 0.25。
k = 7:平均 MAE 0.24。
k = 10:平均 MAE 0.25。
k = 15:平均 MAE 0.28。
この結果では、k = 7 の平均 MAE が最も小さい。
k = 1 は、第7章のトレーニングデータでは MAE が 0.00 だった。
しかしクロスバリデーションでは、自分自身を近傍に使えないため、MAE は最も大きかった。
トレーニングデータを記憶するようなモデルが、未知サンプルにも強いとは限らない。
一方、k を大きくしすぎると、離れた材料まで平均に加わり、個々の違いが薄くなった。
k = 7 は、今回の候補と今回のクロスバリデーションの中で、最も小さい平均 MAE を示した。
遥は「最適な k は 7」と書きかけた。
しかし、前の週に「k = 5 が一番良い」と思ったことを思い出した。
分割や候補の範囲が変われば、選ばれる値も変わるかもしれない。
遥は表現を直した。
今回のトレーニングデータと5-fold クロスバリデーションでは、候補の中から k = 7 が選択された。
ここで、もう一つ問題が残っていた。
前の章で遥は、同じテストデータを見ながら k を 1、3、5、10 と変えていた。
つまり、そのテストデータはすでに、モデルの選択を考えるために使われている。
完全に触れていない最終評価用データとは言えない。
「このテストデータで、k = 7 の最終性能を報告してよいのでしょうか」
遥が尋ねると、先輩は少し考えた。
「演習として傾向を見ることはできます。ただし、厳密な最終評価としては扱いにくいですね。テスト結果が、すでに瀬戸さんの判断に影響しています」
「新しいテストデータが必要ですか」
「外部から独立したデータを得られるなら、それがよいです。今あるデータだけで、モデル選択まで含めた性能を評価する方法もあります」
先輩は、研究室のサンプルプログラムを開いた。
ダブルクロスバリデーション。
外側でもデータを分け、外側のトレーニングデータの中だけで、さらにクロスバリデーションをして k を選ぶ。
選ばれた k で外側のトレーニングデータ全部を学習し、外側のテスト部分を一度だけ推定する。
外側のテスト部分を入れ替えながら繰り返すことで、モデルを選ぶ手順そのものを評価する。
遥は、箱の中にもう一つ箱があるような図をノートに描いた。
内側:k を決めるためのクロスバリデーション。
外側:k を決める操作まで含めた予測性能の評価。
サンプルプログラムを実行すると、外側の五つの分割ごとに選ばれた k は同じではなかった。
外側fold 1:k = 5。
外側fold 2:k = 7。
外側fold 3:k = 7。
外側fold 4:k = 10。
外側fold 5:k = 5。
データの一部が変わると、クロスバリデーションで選ばれる k も変わった。
外側で得られたすべての推定値を集めて計算した MAE は 0.30、RMSE は 0.39 だった。
random_state = 1 の一回の分割で得られた test MAE 0.20 より大きい。
けれど、0.30 の方が悪いモデルだと単純には言えない。
0.20 は、結果を見た十通りの中で、最も易しい分割に近かった。
0.30 は、すべてのサンプルを一度ずつ外側の未知データとして推定し、モデル選択の手順も含めて評価した結果だった。
精度を低くしたのではない。
評価を、実際の未知サンプルに近い形へ厳しくした。
遥はノートに書いた。
高く見える精度より、再現できる評価手順。
モデル選択に使うデータと、最後の評価に使うデータを分ける。
外部テストデータがない場合は、ダブルクロスバリデーションで選択手順を含めて評価できる。
課題はここまででもよかった。
しかし、遥にはもう一つ気になることがあった。
polymer_membrane_modeling.csv には、同じ繰り返し単位をもち、製膜温度だけが異なる記録がいくつかあった。
120サンプルに対して、異なる繰り返し単位は84種類だった。
ランダムに行を分けると、同じ高分子構造の別条件が、トレーニングデータとテストデータの両方に入ることがある。
k近傍回帰にとっては、ほとんど同じ構造のサンプルがトレーニングデータにあるため、推定しやすい。
それは不正なのだろうか。
遥は、研究の目的へ戻った。
既に知られている高分子について、別の製膜温度で性能を予測したいなら、同じ構造の別条件を学習に使うことには意味がある。
一方、まだ合成していない新しい高分子構造の性能を予測したいなら、同じ構造がトレーニング側にある評価では、実際の目的より易しい。
遥は repeat_unit_smiles をグループとして、同じ繰り返し単位が異なるfoldへ分かれないようにした。
GroupKFold。
構造ごとにまとめて、トレーニング側か検証側のどちらか一方に入れる。
同じ Pipeline と k の候補を用いて、構造グループを分けたクロスバリデーションを実行した。
外側の推定値を集めた MAE は 0.41、RMSE は 0.52 になった。
ランダムに行を分けたときより、誤差は大きい。
特に、独特な構造をもつ polymer_047 の誤差は大きかった。
周囲に同じ構造の別条件がなく、似た既知材料も少ないためだった。
遥は一瞬、グループ分割ではモデルの性能が悪くなったと思った。
しかし、モデルが変わったわけではない。
「既知構造の別条件を予測する」という問いから、「未知構造を予測する」という、より難しい問いへ変わった。
同じ高分子膜のデータでも、何を未知と想定するかによって、適切な分割方法は違う。
遥は追加スライドの題名を付けた。
予測したい未来に合わせて、データを分ける。
新人トレーニング七回目の進捗報告会で、遥は最初に、random_state を変えた十通りの結果を示した。
先生は尋ねた。
「最も MAE が小さかった random_state = 1 を採用しなかったのは、なぜですか」
「テストデータの結果を見て分割を選ぶと、評価しやすいサンプルを選んだことになるからです。random_state はモデルを良くするための設定ではありません」
「random_state = 2 の MAE が大きかった理由は、モデルにありますか」
「モデルは同じです。polymer_047 など、周囲に似たトレーニングサンプルが少ない点と、目的変数の両端のサンプルがテストデータに多く含まれました。分割によって評価問題の難しさが変わっています」
遥は、テストデータごとの y の分布も示した。
「r2 も分割ごとに示してありますが、異なるサンプル集合では y のばらつきが違うため、値の大小を直接比較していません。MAE、RMSE と、サンプルの分布を一緒に確認しました」
先生は次に、クロスバリデーションの結果を見た。
「なぜ、標準化を Pipeline の中に入れたのですか」
「各foldの検証データを、平均値や標準偏差の計算にも使わないためです。前処理も、モデル構築側のデータだけで決める必要があります」
「修正前の MAE の方が小さかったですね」
「はい。でも、未知データを使うときにはできない処理で良くなった精度は、採用しません」
先生はホワイトボードに書いた。
良い数字と、良い評価は違う。
遥は、5-fold クロスバリデーションで k = 7 が選ばれたことを説明した。
先生が尋ねた。
「k = 7 が、真の最適値ですか」
「今回のトレーニングデータ、候補の範囲、分割方法、評価指標の中で選ばれた値です。外側のfoldによって、5、7、10と変わりました」
「では、ダブルクロスバリデーションは何を評価しましたか」
「k を選ぶ内側の手順を含めて、外側の未知サンプルをどの程度予測できるかを評価しました。外側のサンプルは、内側のモデル選択には使っていません」
先生は、ランダム分割と構造グループ分割の結果を指した。
「MAE が 0.30 から 0.41 に大きくなりました。どちらが正しい精度ですか」
遥は少し考えた。
「どちらか一つが常に正しいわけではありません。既知の高分子構造について新しい作製条件を予測したいなら、ランダム分割に近い評価にも意味があります。未知の高分子構造を設計したいなら、同じ構造をトレーニング側に入れないグループ分割の方が目的に近いです」
「高い精度の方を選ぶのではないのですね」
「はい。使いたい場面を再現する分割を選びます」
先生はホワイトボードの「良い数字と、良い評価は違う」の下に、言葉を追加した。
データの集め方。
前処理。
分割方法。
モデル選択。
評価指標。
予測したい未来。
「予測精度は、モデルだけの性質ではありません。これらを含む評価手順から得られる結果です」
遥は、最後のスライドを示した。
トレーニングデータをよく再現することと、未知データを予測できることは同じではない。
テストデータを見ながらモデルや分割を選ぶと、テストデータは最終評価に使えなくなる。
前処理とハイパーパラメータ選択は、評価データを見ない範囲で行う。
データ分割は、実際に予測したい未知サンプルを再現するように決める。
高い精度という言葉には、どのデータを、どの手順で評価したかを添える。
先生は尋ねた。
「では、高い精度は、本当に良いのでしょうか」
遥は、題名と同じ問いを見た。
「正しく評価された高い精度なら、役に立つ情報だと思います。でも、簡単な分割を選んだり、評価データを前処理やモデル選択に使ったりして高くなった精度は、未知の材料に使える根拠にはなりません」
「モデルの精度を高くすることが、研究の目的ですか」
「いいえ。予測したい材料に対して、どの程度の誤差で使えるかを知ることが目的です」
言い終えると、遥は第1章で書いた三つの言葉を思い出した。
自分で調べる。
試す。
考える。
今回は、モデルを試しただけではなかった。
精度が変わる理由を調べ、評価の仕方そのものを考えた。
報告会のあと、遥は結果を保存した。
random_split_results.csv。
cross_validation_results.csv。
double_cross_validation_predictions.csv。
group_cross_validation_predictions.csv。
それぞれのファイルには、分割方法、random_state、fold、選ばれた k、前処理の方法を記録した。
数字だけを残すのではなく、どの手順から得られた数字かを残すためだった。
作業を終えると、先輩から次の課題が slack に届いた。
構造グループを分けた評価で、各テストサンプルと最も近いトレーニングサンプルとの距離を計算してください。
距離と予測誤差との関係を確認してください。
モデルが予測しやすいサンプルと、予測しにくいサンプルの違いを考えてください。
遥は、polymer_047 の行を見た。
新しい構造を未知として評価すると、このサンプルは大きく外れた。
近い既知材料が少ないことは分かる。
では、どの程度遠ければ、モデルの予測を信じない方がよいのだろう。
モデルは、入力があれば数字を返す。
しかし、数字を返せる場所と、信頼してよい場所は同じではない。
遥はノートの新しいページに、次の章の題名を書いた。
AIにも得意な場所と苦手な場所がある。
===
続きはこちら
