株式会社ホクソエムのブログ

R, Python, データ分析, 機械学習

【翻訳】機械学習の技術的負債の重箱の隅をつつく (前編)

ホクソエムサポーターの白井です。 今回は Matthew McAteer氏によるブログ記事Nitpicking Machine Learning Technical Debtの和訳を紹介します。

原著者の許可取得済みです。 Thank you!

アメリカの国内ネタも含んでいて、日本語だと理解しにくい箇所もありますが、機械学習の技術的負債をどう対処していくかについて、とても役に立つ記事だと思います。

Nitpicking Machine Learning Technical Debt (機械学習の技術的負債の重箱の隅をつつく)

再注目されているNeurIPS2015の論文を再読する (そして、2020年のための、より関連した25のベストプラクティス)

最近 Hidden Technical Debt in Machine Learning Systems (Sculley et al. 2015)を読み返しました。 (簡潔に、明確にするため、この投稿では技術的負債論文とします) この論文は NeurIPS 2015 で公開されましたが、 Ian GoodFellowによる 「Generative Adversarial Network」技術に皆が夢中になっていたので、そこまで注目されませんでした。

今日、技術的負債論文がまた盛り返してきています。 これを書いている間にも、直近75日のあいだに25の論文が引用しています。 これは、機械学習について私たちが技術的負債について心配しなければいけなレベルにまで来たという意味でしょう。

ですが、多くの人々がこの論文を引用しようとしているならば (「機械学習の技術的負債」というフレーズを持つ論文全てを引用しているだけでないならば)、少なくともどの箇所が本質的なもので、どこがそうでないのか認識すべきです。 そのことを念頭に置いて、どの部分が時代遅れなのかを紹介し、また取って代わる新しい方法を示すことは、機械学習に関係する人々全員の時間と手間を大幅に省くことができると考えました。 これが私がこのBLOG POSTを書いた理由です。 急成長中のスタートアップからグーグル (技術的負債論文の著者の企業) のような大企業で働いてきて、 同じような機械学習の技術的負債による失敗がどこでも行われているのをみているので、私はこの件について執筆する資格があると考えます。

f:id:sh111h:20200622074908p:plain

MLコードは小さくごくわずかなブラックボックスであることに注目してください。 この”小ささ”がこの論文の良い部分の一つですが、悲しいことにこの画像を参考に作られた画像はボックスの大きさの違いを考慮しておらず、この”小ささ”という要点を逃しています。

この記事は技術的負債論文と関連する点のいくつかを網羅しながら、5年前にはないアドバイスを付け加えています。 そのいくつかのアドバイスはその当時存在しなかったツールの形をとっています。 また、ツールや技術自体は間違いなく存在していたものの、当時の著者らが持ち出さなかったことで見逃してしまったアドバイスもあります。

イントロダクション

技術的負債は、エンジニアが他の何よりもデプロイの速い設計を選んだ時のコスト増加を例えたものです。 技術的負債を修正するのは骨の折れる仕事です。 「早く動いて、破壊せよ (Move fast and break things)」から「ああ大変だ、急ぎすぎたから片付けなきゃ (Oh no, we went too fast and gotta clean some of this up)」に方向転換です。

f:id:sh111h:20200622075510p:plain

キャッチーでなくとも、Mark Zuckerburgが二つ目のスローガンに口を挟まなかった理由が私にはわかります。

(訳注: “Move fast and break things” はFacebookのMotto。その後 “Move fast with stable infrastructure” に変更した。 (wikipediaより) )

ソフトウェアにおける技術的負債は悪いものですが、MLシステムの技術的負債は もっと悪い と論文の著者らは強く主張しています。 技術的負債論文はMLにおける技術的負債の種類と、いくつかの解決策を紹介しています (いろいろな種類のゴミ箱があるのと同じで、いろいろなクソコードに対し、いろいろな手法が必要です🚮) 技術的負債論文は、元は人々の注意を引きつけるための意見記事であったことを考えると、論文中のいくつかのアドバイスはもはや関係なかったり、現在ではよりよい解決策があることを覚えておくのが重要です。

Part1 技術的負債はあなたの予想以上に悪い

みなさんはもう、技術的負債についてご存知でしょう。 技術的負債論文ははじめに、技術的負債の解消というものがコードに新しい機能を追加することを意味しないことを明確にしています。 技術的負債の解消はユニットテストを書いたり、可読性を向上させたり、ドキュメントを追加したり、未使用の部分を取り除いたりする、華やかさにかけるタスクであり、将来の開発が楽になるためのタスクです。 まあ、標準的なソフトウェアエンジニアリングは機械学習エンジニアリングで必要なスキルの一部なので、より一般的なソフトウェアエンジニアリングの技術的負債は、MLの技術的負債の一領域にすぎません。

f:id:sh111h:20200622075741p:plain

言い換えてはいますが、(Sculley et al. 2015) が言っていることとほぼ同じです。

Part2 機械学習の漠然とした性質

技術的負債論文の、イントロダクションの後の章は、機械学習モデルの漠然とした性質が、どのように技術的負債の扱いを難しくしているかに続きます。 技術的負債を防いだり直したりするための大部分は、コードが適切に整理され、モジュールとして分離されているかを確かめることです。 正確なルールや、コーディングでは細かい指定が難しい場合に、私たちは機械学習を使います。 データを出力するためのルールをハードコーディングする代わりに、多くの場合、アルゴリズムにデータと出力を与えてルールを出力しようとしています(時にはそれさえしないことがあります)。 その際、私たちは分離や整理に必要なルールが何なのかすらわからないのです。

Best practice #1 解釈可能・説明可能なツールを使おう

ここで、もつれた (Entanglement) 問題が出てきます。 要するに、もしモデルのどこかを変更すると、全体のパフォーマンスを変えてしまう恐れがあるということです。 例えば、個人の健康記録について100の特徴量を持つモデルに対して、(例えば、大麻を吸ったことがあるかをヒアリングしてデータ化するといった話です)101個目の特徴量を加えることを考えます。 全てがつながっているのです! まるで、カオスシステムを扱っているようなものです。(皮肉なことに、何人かの数学者はニューラルネットワークを二重振り子や気象システムのようにカオスなアトラクターとして表現しようとしています

一本の紐(調整パラメータ)をチューニングしていくと(実際は何百本もの紐がありもつれています)、全体がつながっているのでうっかりブラインドを完全に消すこともできるわけです (the blinds can go OUT the window too)。 (訳注: go out the windowは「完全に消えてなくなる」という口語表現)

アンサンブルモデルや高次元可視化ツールを用いてこの問題を修正できる可能性について著者らは示していますが、もしアンサンブルモデルの出力が相関していたり、データが 高次元である場合は不十分です。 解釈可能なMLとして提案されるものの多くは少し曖昧です。 それを踏まえた上で、私はFacebookの高次元可視化ツールをおすすめします。 また、解釈可能な機械学習についての最高の資料“Interpretable Machine Learning” by Christoph Molnar (オンラインで利用可能)を読むこともおすすめします。

時には、決定木のような、より説明可能なモデルが、もつれた問題の手助けになるでしょうが、ニューラルネットワークで解決するベストプラクティスがあるので、確定したわけではありません。

Best practice #2 可能であれば、説明可能な種類のモデルを使おう

修正の伝播 (Correction Cascades) (訳注: Hidden Technical Debt in Machine Learning Systems参照)は漠然とした機械学習モデルへ入力の一部、それ自体が漠然とした機械学習モデルである場合に起こります。 エラーのドミノラリーを準備しているようなものです。

例えば、事前に存在しているモデルを新しいドメイン (もしくは多くの人が “startup pivot” と呼ぶもの) に適用する時、このようにモデルを繋げるのは非常に魅力的です。 ランダムフォレストの前に、教師なし次元削減ステップはさんだことがあるかもしれませんが、t-SNEのパラメーターを変化させると残りのモデルのパフォーマンスが急落します。 最悪の場合、全体のシステムのパフォーマンスを下げることなく、元のモデルや次元削減の部分などの “サブな部分” の精度を改善するのは不可能です。 機械学習パイプラインは、正の合算価値を生み出すものからゼロサムになります (これは技術的負債論文の用語ではないですが、載せるチャンスを逃しただけだと感じています)。

XKCDのRandal Munroe提供

(訳注:
Cueball「これを見て。必要な情報全てを集めて処理した完全自動化データパイプラインを作りました。」 Ponytail「行き当たりばったりのスクリプトで構成された巨大な (トランプの家のような) 不安定な構造で、変な入力があった瞬間に崩れませんか?」 Cueball「そう…ではないかもしれない」 Ponytail「私が推測するに、なにか」 Cueball「おっと、壊れた。ちょっと待って、修正します。」
Explain xkcdの書きおこしを参考に翻訳)

これを防ぐためには、greedy unsupervised layer-wise pretraining (or GULP) のvariantがよりよい技術の一つです。 なぜうまくいくかという数学的理由については意見がわかれているものの、基本的にはモデル初期段階やアンサンブルの最初部分を学習し、それらを固定してから、残りのシークエンスを順々に作業します (これまた、技術的負債論文で言及されていませんが、この技術自体は少なくとも2007年から存在しているので、機会を逃しています)。

Best practice #3 常に、順番に下流モデルを再学習しよう

他にも機械学習モデルの不都合な特徴があります。 あなたが認識している以上に、ただの機械学習モデルの域を超えて、その機械学習の出力を頼っている消費者がいるかもしれないということです。 著者らはこれを 宣言していない消費者 (Undeclared Consumers) として言及しています。 ここでの問題は、出力データが非構造だったり正しくフォーマットされていないことではなく、出力に依存しているシステムがどの程度あるかを把握できる人が誰もいないことです。

例えば、kaggleのようなサイトでは、たくさんのカスタムデータセットがあり、そのデータセットの多くは機械学習の出力です。 多くのプロジェクトやスタートアップは、初期の機械学習モデルの構築や学習を、内部のデータセットのかわりにこれらのデータを使うことがあります。 ほぼ予告なしでデータセットが変更される可能性があるにもかかわらずに、です!(しかもこれらに依存したスクリプトやタスクがその変更を検知できることは稀です)

また、データにアクセスするためサインインなどが必要ないAPIの場合、問題がより複雑になります。 アクセスキーやサービス水準合意のように、モデルにアクセスする入り口に障壁を作らない限り、扱いが難しくなります。 モデルの出力をファイルとして保存したら、共有ディレクトリにデータがあるからという理由で、チームのメンバーがその出力データを使うかもしれません。 試験的なコードだとしても、まだ確認できていないモデルの出力にアクセスできる人がいるかどうか、気をつけないといけません。 これはJupyterLabのようなtoolkitにとって大きな問題となる傾向があります (もし私が過去に遡って技術的負債論文に警告を付け加えるなら、JupyterLabについて警告するでしょう)。

基本的に、このような技術的負債の解決は、機械学習エンジニアとセキュリティーエンジニアの協力が必要です。

Best practice #4 アクセス鍵、ディレクトリの権限、サービス水準合意をセットアップしよう

f:id:sh111h:20200622075851p:plain

下流の消費者グラフはこのようにシンプルで包括的であればよいと思います。

Part3 (通常の依存関係の頂上にある) データ依存関係

3つめの章はデータ依存関係の問題について、深く掘り下げます。 ソフトウェアエンジニアリングにおける通常のコードに加え、機械学習システムは、開発者が認識する以上に不安定な、膨大なデータソースにも依存しています。 これは悪いニュースです。

例えば、入力データは、裏で変化するルックアップテーブルや、連続的なデータストリームであったり、所有していないAPIからのデータを用いているかもしれません。 MolNetデータセットのホストが、より正確な数値で更新することを決めたとしたらどうなるかを想像してみてください(どうやって更新するかは無視するとして)。 更新したデータはより正確に現実を反映するかもしれませんが、数えきれないモデルが古いデータで構築されており、先週は確実に動いたノートブックを再実行したとき、多くの製作者が、精度が急降下したことに気づくでしょう。

著者らによる提案のひとつはPhotonのようなデータ依存追跡ツールを使うことです。 (訳注: Photon: Fault-tolerant and Scalable Joining of Continuous Data Streams) 2020年においては、新しいツール DVC (文字通り「Data Version Control」の意味) があり、Photonを多くの点において時代遅れにしています。 このツールはgitと同じように振る舞い、データセット・データベースの変更の追跡を保持するDAGを保存します。 他に2つのバージョン管理のために一緒に使われるツールとして挙げられるのは、 Streamlit (実験とプロトタイプの追跡を保持) とNetflix’s Metaflowです。

どの程度バージョン管理をするかは、メモリ使用量の増加と、学習過程の大きなギャップ防止のトレードオフの関係にあります。 それでも、不十分ないし不適切なバージョン管理は、モデル学習のときに、過剰な生存バイアスが起こり(可能性を無駄にし)ます。

f:id:sh111h:20200622080023p:plain

DVCのページです。ダウンロードしましょう。今すぐ!

Best Practice #5 データのバージョン管理ツールを使おう

データ依存性の怖い話はまだ続きます。 不安定なデータ依存性と比較すれば、十分に活用していないデータは悪くないようにみえますが、そうやってハマるんです! つまり、使っていないデータ、一度使ったもののレガシー扱いになったデータ、他と関連しているため冗長になったデータに注意し続ける必要があるのです。 あなたがデータパイプラインを管理していて、全体のギガバイトが過剰だと気づいた時、それだけで開発コストが発生しています。

相互に関連したデータは特に扱いにくいです。 なぜならばどの変数が関連していて、どれが原因となるか、理解する必要があるからです。 これは生物学のデータにとって大きな問題です。 ANCOVA (訳注: 共分散分析)のようなツールはますます旧式となり、残念ながらANCOVAの仮定には間違いなく適用していないいくつかの事態で使われています。 いくつかのグループはONION (訳注: arXivリンク) やDomain Aware Neural Networksのような代替案を提案しようとしていますが、多くは特に印象的でない標準的な手法を改良しています。 MicrosoftやQuantumBlackのような企業は因果関係のもつれのためのパッケージを開発しました(それぞれ、DoWhyCasualNexです)。 私は特にDeepmindのベイジアン因果推論が気に入っています。

これらの多くは技術的負債論文の書かれた時期にはなく、多くのパッケージは独自の操作性という負債を抱えていますが、 ANCOVAはどんな場合でもうまくいく (one-size-fits-all) 解決策では ない ことを知ってもらうことが重要だと思っています。

Best practice #6 使っていないファイル、膨大な関係する特徴量を削除し、因果関係推論ツールを使おう

ところで、著者らはこれらの修正に対してあまり悲観的ではありませんでした。 クリック予測の中でGoogleが使っているものを例として、彼らはデータ依存性の静的な分析を提案しました。

技術的負債論文が出版されて以降、対処するための選択肢はとても発展しました。 例えば、Snorkelのような、データのスライスを、どの実験で使ったか追跡するツールがあります。 AWSやAzureのようなクラウドサービスは、DevOpsのためのデータ依存性追跡サービスを持っていて、Red Gate SQL dependency tracker のようなツールもあります。 著者らが楽観的だったのは正しかったようです。

Best practice #7 データの依存関係を追跡する、無数にあるDevOpsツールのいくつかを使おう

Part4 イライラさせるほど未定義なフィードバックループ

前のセクションで、希望が少し見えましたが、データ依存性に関する悪いニュースはまだ終わりではないです。 論文の4章では未チェックのフィードバックループが機械学習の開発サイクルに対して、どのような影響をもたらすかに踏み込みます。 半教師あり学習や強化学習のような直接のフィードバックループ、他の機械学習の出力からエンジニアが選択する直接でないループについても言及しています。

技術的負債論文において、イシューとして最低限しか定義されていないもののひとつですが、いまや数えきれないぐらいの組織がフィードバックループ問題に取り組んでいます、OpenAI全体がやっているようなものも含めて (少なくとも、彼らの宣言の「長期的安全性」の章ではそうなっています、「上限つき利益 (Capped Profit)」騒動の前から)。 なにが言いたいかというと、直接または間接的なフィードバックループの研究・調査をするのであれば、この論文よりも はるかに 優れた具体的な選択肢があるということです。

この章では、前のセクションよりも少し絶望的に見える解決策を掲げています。 彼らはバンディッドアルゴリズムを直接のフィードバックループに耐性がある例としてあげていますが、このアルゴリズムはスケールしないだけでなく、スケールしたシステムを構築 しようと するとき、技術的負債が最も蓄積されます。 役に立ちません。

間接的なフィードバックの修正はあまり良くないです。 実際、間接的なフィードバックループのシステムは同じ構造の一部ですらありません。 お互いにメタゲームをしようとする、異なる企業の交易アルゴリズムのようで、代わりにフラッシュ・クラッシュを起こすかもしれません。 もしくは、バイオテックでより関連した例として、様々な実験装置の誤り確率を予測するモデルを考えてみてください。 時がたつにつれ、実際の誤り率は、人がより練習することで下がったり、科学者が装置をより頻繁に使うことで下がっていきますが、この部分を補正するためのモデルのカリブレーション頻度は増えません。 (訳注: カリブレーション (較正)は、この文脈では確率の較正ではなくパラメータ再調整だと思えばよい) 究極的には、これを修正するために大事なのは、高いレベルでの設計上の決定と、モデルのデータの背後にある仮説(特に独立性の仮説)を可能な限り多く確認することです。

これはまた、セキュリティエンジニアリングの多くの原則と実践がとても有効になる領域です (例えば、システムを通るデータのフローを追跡し、悪質な関係者が利用する前にシステムが防ぐ方法を探す)。

f:id:sh111h:20200622080115p:plain

直接的なフィードバックループの一例です。 実際には、このダイアグラムのいくつかのブロックがMLモデルです。 これは間接的な相互作用をうわべだけ扱っているわけではありません。

Best practice #8 モデルの背後にある独立性の仮定を確認しよう (セキュリティーエンジニアの近くで働こう)

今や、特にANCOVAについてコメントした後であれば、あなたはおそらく仮定の検証というテーマに気づいているでしょう。 私は、著者らがこの話題に少なくとも1セクションを使えばよかったのにと思っています。

後編に続きます

元のブログ は1つの記事ですが、少々長いので、前後編に分けさせていただきました。

続きでは「コードのアンチパターン」や「構成の負債」、「実世界での活用」について紹介します。