Re:ゼロから始めるML生活

Standing on the shoulders of Giants

機械学習アプリケーションにおけるテストについて

機械学習系の話題が多い昨今ですが、実際触ってみると期待した精度・結果が出ないなんてことはよくあることではないでしょうか。

機械学習特有の性質として、データ自体がモデルを変化させ、結果として業務に影響を与えたりします。 仮に、機械学習屋さんが精度が出るモデルを構築したと言っても、それを導入するときに、システム全体での品質の維持に苦労したりします。

ということで、不確実性の大きい機械学習系開発についての、設計・テスト戦略でどうやってリスクを低減していけるかが一つカギになってくると思い、方法論について勉強しましたので、そのメモです。

非常に参考にしたのはこちら。

arxiv.org

テストそのもののテクニックなどは、一般的なテスト駆動開発に関する書籍を合わせてをご参考ください。

テスト駆動開発

テスト駆動開発

  • 作者:Kent Beck
  • 発売日: 2017/10/14
  • メディア: 単行本(ソフトカバー)

テスト駆動Python

テスト駆動Python

  • 作者:Brian Okken
  • 発売日: 2018/08/29
  • メディア: 単行本(ソフトカバー)

上がテスト駆動開発といえばの定番、下が機械学習で使われるであろうPythonでのテストに関する書籍になっています。

それでは張り切ってやっていきます。

tl;dr;

  • 機械学習システムのテストは信頼性の向上のためにも求められている
  • 一方で、機械学習のシステムに対するテストは非常に難しい
    • 学習時のデータによってシステムの振る舞いが変更できてしまうなど、従来のソフトウェアテストだけでは信頼性をカバーしきれない
  • 何をテストするかに関しては、精度などの機能要件に加えて公平性などの非機能要件についても考える必要がある
  • どこをテストするかに関しては、データ・プログラム・フレームワークの3種類に大別される
  • どうやってテストするかに関しては、テストデータの生成からテスト結果の分析まで含めて考える必要がある
  • テストの結果だけでなく、テスト自体の確認や問題発生時のトレースを含めて設計する必要がある

背景

昨今、機械学習のアプリケーションは信頼性の問題に悩まされることも多いのでは無いでしょうか。 安全性が非常に強く求められる自動運転や医療関係アプリケーションを始めとして、機械学習モデルの正しさ、ロバスト性、プライバシー、効率性、そして平等性が求められるようになってきています。

システムの信頼性を向上されるために従来行われてきたのがソフトウェアに関するテストですが、機械学習システムにおけるテストは、従来のソフトウェアテストとは大きく異なっています。 従来のソフトウェアテストでは、正しいと想定する結果と実際のソフトウェアとの挙動の差分を検知することで、テストを実施してきました。 一方で、機械学習システムは元来、データドリブンなプログラミングパラダイムを持っており、システムの挙動は学習処理を通じて決定され、その学習は学習アルゴリズムに入力されるデータによって決定されます。 また、モデルの振る舞いは時間とともに変化し、その応答も時間とともに変化していきます。 そのため、従来のソフトウェアではデータを変えても振る舞いを変化させることまではできませんでしたが、機械学習システムではシステムの振る舞いを学習データを通じて変更することが可能であるという特徴があります

機械学習システムのテストを難しくしている大きな問題として、テストオラクル問題(Oracle Problem)があります。 機械学習のシステムは、明確な応答をするようにデザインされているわけではありません (実際、そのように応答するシステムは機械学習は不要となる場合が多いです) 。 そのため、テストを実施する前に正解の挙動を定義できないという問題があります。

また、挙動がおかしな部分が観測されたとしてもシステム全体の出力として観測されているため、テストで発生した問題の切り分けが難しいという問題もあります。 実際、機械学習において出力に問題が確認されても、それがデータ起因なのかプログラム起因なのか、さらにはフレームワークやライブラリ起因なのかを切り分けることは非常に難しくなってきます。

ワークフローの途中で異変が生じたことが確認できたとしても、システムの処理過程で発生した誤差はワークフローのなかで拡大・縮小されてしまうため、実際の問題の箇所がどこにあるのかを特定することは非常に困難となります。 これは従来のソフトウェアテストでいう副作用(side effect)と似てはいるものの、機械学習のそれはすべての機能が絡み合って発生するため、従来よりも厄介な問題になることが多いです。

このように、機械学習システムには信頼性が求められている一方で、従来のソフトウェアと大きく異なる性質を持っているため、非常にテストが難しくなっています。 このような問題意識が広まってきたこともあってか、機械学習におけるテストに関する論文の投稿はここ数年で急増しており、非常に注目されている領域であるということがわかります。

f:id:nogawanogawa:20200227223204j:plain:w600
Machine Learning Testing: Survey, Landscapes and Horizons より引用

機械学習におけるテスト

機械学習アプリケーションの開発フローと構成要素

テストに関して考える前に、テスト対象である機械学習アプリケーションについて簡単に示します。 機械学習アプリケーションの大まかな流れは下記の様になるかと思います。

f:id:nogawanogawa:20190917201235p:plain
https://martinfowler.com/articles/cd4ml.html より

このようなフローを考えた際の、機械学習のソフトウェアとしての構成要素は大きく

  • データセット
    • 学習セット
    • バリデーションセット
    • テストセット
  • 学習プログラム
    • アルゴリズム
  • フレームワーク
    • TensorFlow
    • Pytorch
    • Scikit-learn ...

に分けられるかと思います。 機械学習アプリケーションとしては、これらの構成要素のすべてにシステムの要件を充足させる上でのバグが発生する恐れがあります。

データに対しては、データセットが極端に偏ったラベル分布になっていないか、将来のデータの代表として取り扱って良いかなど、モデルの学習やテストに適しているかを確認するデータの完全性を確認する必要があります。

学習プログラムについては、設計する段階と実装する段階の二段階に問題を切り分けることができます。 テストでは、設計として不適切な構成になっていないか、実装時に過ってバグを入れ込んでいないかを確認する必要があります。

フレームワークは学習や検証の機能を有していますが、これらがアプリケーション全体に対して問題を引き起こしうるかを確認することが求められます。

テストの区分

機械学習のモデルについて、学習からデプロイ、運用までのサイクルはこの様になるかと思います。

f:id:nogawanogawa:20200227223141j:plain
Machine Learning Testing: Survey, Landscapes and Horizons より引用

一般的な機械学習システムはまずプロトタイプが作られ、クロスバリデーションなどでオフラインテストされた後、オンラインにデプロイされてからオンラインテストによって検証されます。

上記の様にオフラインテストとオンラインテストの両方が必要になる理由としては、

  • オフラインテストではテストデータに依存した結果しか得られず、未来のデータについては観点から外れている
  • オフラインテストでは、オンラインで実際に発生するデータの欠損などの状況がテストされない
  • オフラインテストでは、開封率、閲覧時間、CTRなどのビジネス上のメトリクスについて測定することができない

などが挙げられます。 そのため、機械学習のテストについてはオンラインとオフラインのテストを組み合わせて行われることが一般的になります

オフラインテスト

機械学習のモデルを作成する際に、アプリケーションとしての要件を設定しますが、実際に構築したモデルがその要件を満たすかどうかをデプロイする前に確認する必要があります。 これはオフラインテストやバリデーションと呼ばれ、これによって機械学習モデルの品質を確認します。

オンラインテスト

オフラインテストでは、過去のデータを使用してテストを行っていたのに対し、オンラインテストでは現在のデータを使用してテストを実施します。 これによって、オフラインテストでは不明だったユーザーの行動履歴などとも紐付けることで、機械学習モデルが要件を満たしているかをより正確に判断することが可能になります。

オンラインテストでは、A/Bテストの方式を導入あるいは段階的に導入することで、最終的に最適なモデルを選択することに使用されます。

機械学習のテストを考える上でのポイント

このような要素で構成された機械学習アプリケーションについてテストを考える上で重要になるポイントとしては、

  • What: 何をテストするか(testing properties)
  • Where: どこをテストするか(testing components)
  • How: どうやってテストするか(testing workflow)

のように分けられるかと思います。以下ではこの3つの観点について考えます。

※ Why・Who・Whenについては、対象外とします

何をテストするか(testing properties)

機械学習アプリケーションにおける機能・非機能要件には、

  • 機能要件
    • 精度
    • 過学習の無さ
  • 非機能要件
    • ロバスト性
    • 公平性
    • セキュリティ
    • 効率性
    • 解釈可能性
    • 匿名性

などが挙げられます。 そのため、機械学習アプリケーションのテストにおいては、これらの要件を充足していることを確認する必要があるかと思います。

精度

機械学習の精度に関するテストとして広く用いられる手法はクロスバリデーションとブートストラップサンプリングかと思います。 精度の指標としてはAccuracy (正確度)、Precision (精度 ・ 適合率)、Recall(再現率)や Area Under Curve (AUC)などがあり、アプリケーションの性質に応じてこれらを選択してテストを行う必要があります。

指標に関する解説はこちらの記事がわかりやすかったです。

qiita.com

過学習の無さ

測定した精度が良いものであっても、そのモデルは過学習をしている場合があるため、過学習に関してテストが必要になります。 データに対してモデルが複雑過ぎたりデータが少なすぎる場合などで、過学習が発生します。 クロスバリデーションは、過学習の検知に非常に効果的でありますが、それでもテストデータと学習データの傾向が異なる場合には過学習になってしまう場合があります。

学習データにノイズを作為的に注入し精度の変化を観察するPerturbed Model Validation (PMV)や、テストデータから敵対的サンプルを生成する手法などが、研究として行われているようです。

ロバスト性

ロバスト性のテストには、ノイズが存在する中でシステムをテストすることが広く行われています。 テストデータにノイズ要素を意図的に注入することで、ノイズの有無による変化によってロバスト性を測定します。

また、GANのような敵対的サンプル生成器によって偽のテストデータを作成することも行われているようです

公平性

公平性に確固たる定義はまだ定まっていませんが、データの偏りによって、モデルに一定のバイアスが掛かってしまうことを指すことが多いです。 これは、処理自体は問題なく動作しますが、その結果を人間が見たときに不快に感じさせたり害を与え、宗教・政治的な問題に抵触する恐れがあるため注意が必要です。

テスト指標としては、

  • 特定の属性を保護しても出力の変化が小さいこと
  • 特定の属性をもとに選択されたグループに対するモデルの出力の確率が他と等しいこと
  • 特定の属性を反転させたとき、出力が等しいこと
  • その他、タスク固有のメトリクスが充足していること

などを確認することが挙げられます。

解釈可能性

解釈可能性については、結局のところ人間が主体となるためモデルの出力に対して人間が解釈できるかを測定します。 解釈可能性について、自動で行ったりも一部では試されているようです。

どこをテストするか(testing components)

バグが混入しうる対象として、

  • データセット
  • 学習プログラム
  • フレームワーク

としていました。 そのため。これら3つについて、バグが発生していないことをテストすることが求められます。

データセット

機械学習はデータによってアプリケーション全体の振る舞いが変わってしまう性質を持っているため、データについてのバグを早期に発見することは非常に重要です。 にもかかわらず、データ自体に関するテストは非常に困難です。

学習データに関するテストでは、意図した通り範囲にデータが収まっているかをルールベースに確認する方法が考えられます。 また、データが想定しているシナリオを十分にカバーしているかについて確認する必要もあります。 その他、ニューラルネットにおいて分類エラーの原因となる”faulty neurons”を発見し、訓練データを再サンプリングする方法なども提案されています。

テストデータについても、イレギュラーなデータが混入していないか、カバレッジは十分か、ミューテーションスコアが順文化どうかについて確認する必要があります。

学習データとテストデータは、データの傾向として一貫した分布になっていることが望ましく、それらを確認することも求められます。

学習プログラム

学習プログラムに関するテストとしては、設計に関するものと実装に関するものの二段階に問題を切り分けることができます。 実装する段階については、学習プログラムが期待通りに動作しているかどうかを確認します。 これには、動作確認用のテストデータセットを作成するなどして、プログラムの挙動を確認します。

設計に関するテストとしては、想定されるアルゴリズム間で精度などを比較することで、テスト(評価)が行われます。

フレームワーク

フレームワークに関するテストとしては、脆弱性やフレームワークの非効率性に関するものなどがあります。

フレームワークのバグを検出する場合には、複数のフレームワークで同様のアルゴリズムの実装を行い、そのときの挙動の比較を行うというのが一般的のようです。

※ 個人的には、フレームワーク自体のテストに関しては、必要になったタイミングでその都度、技術検証という形で実施するレベルかと思ってます。継続的にテストを行うというのはあまり考えないものと思っています。

どうやってテストするか(testing workflow)

機械学習のテストの模範的な流れは下記のようになります。

f:id:nogawanogawa:20200227223048j:plain
Machine Learning Testing: Survey, Landscapes and Horizons より引用

大きな流れとして、要求が定められた後、

  1. テストのインプットとその正解を用意(生成)
  2. 推論
  3. 結果の評価
  4. テストの十分性やその優先順位を含めて、評価結果について分析
  5. バグの有無の判定
  6. レポートの結果をもとにプログラムの改修

といった流れとなっています。

実際、オンラインのテストとオフラインのテストが行われており、オンラインテストのモニタリングの重要性も叫ばれていますが、テストの方法という観点ではオフラインテストに関する調査研究が盛んに行われているのが現状のようです。

テストデータの調達

機械学習のテストを行う際に、テストデータをどう用意するかは大きなポイントになります

テストデータの用意の方法については敵対的入力(adversarial inputs)と自然入力(natural inputs)があります。 敵対的入力の場合にはオリジナルデータからテストデータを生成し、データの分布が正規分布にならなくなる恐れがありますが、一方でデータのロバスト性やセキュリティ上の見落としを防ぐことに繋がります。 一方、自然入力は手元のデータからサンプリングし、元データの分布に従いやすく、より実際的なアプリケーションシナリオとしてテストが可能になります。

DeepXploreの論文では、従来のソフトウェアテストのカバレッジになぞらえて、ニューロンカバレッジという指標を提案しており、それを高めることがテストとして有効としています。 テストデータは異なるモデル間の違いを示すためだけでなく、できるだけ現実に近いデータを用意する必要があり、連結最適化アルゴリズム(joint optimisation algorithm)によって、最適なテストデータを探索する手法があります。 これによって、ニューロンカバレッジを高めたテストデータを用意することができるようです。

その他、音声や画像、文章などのインプットデータを機械学習によって生成し、それをテストデータとして使用するという方法が多くの分野で使用されています。

ファズテストは、クラッシュ、メモリリーク、失敗(組み込み)アサーションなどを検出するためのプログラム入力としてランダムデータを生成する従来の自動テスト手法であり、多くのシステムセキュリティおよび脆弱性に関するテストへの応用が成功しています。 このようなテストでは、探索的データ探索が行われプログラムに基づいてテストデータセットの探索が行われます。

プログラムが実行しうるパスを網羅的に実行するシンボリック実行が知られていますが、この分岐に応じたテスト生成という手法も提唱されています。 ニューラルネットワークのような手法では明示的に分岐が示されませんが、DeepCheckのような手法を使うことで、シンボリック実行を可能にするなどが行われます。

テストオラクル(Oracle Problem)

機械学習アルゴリズムの多くが確率的なプログラムであることから、機械学習のテストではテストオラクルは非常に難しいものとなります。 これは、テストの前に事前にテストデータと対になる出力データを用意することが困難なことに起因します。

このような問題に対する解決策としてはメタモルフィックテストが研究されているようです。 これは学習データや入力データに対する変換処理を行ったときの、出力の変化を観察して挙動を確認するものです。

メタモルフィックテストに関しては下記の記事が参考になりましたので、合わせてご参照ください。

qiita.com

また、クロスリファレンスと呼ばれる手法が取られる場合もあります。 複数の類似したアプリケーションに対して同一の入力を与えて、その出力を観察する手法です。 この手法を機械学習に適用することで、アルゴリズムのバグを検出します。

また、機械学習の非機能要件に関するテストについては、テストの結果の統計量を測定し、それを期待する統計量と比較することが行われるようです。

妥当性評価

テストでは、テスト自体の妥当性評価をする必要が出てきます。

従来のソフトウェアテストではコードカバレッジなどで妥当性を評価しています。 機械学習においては、コードカバレッジではなく、下記のようなカバレッジが評価される場合があります。

  • ニューロンカバレッジ
    • ニューラルネットにおいて活性化されたニューロンの数とニューロンの総数の比率を考慮
  • MC/DCカバレッジ
    • ニューラルネットにおいてニューロンの符号、値、または距離の変化を観測することでテスト入力の因果的な変化を観察
  • レイヤレベルカバレッジ
    • ニューラルネットの各層の活性化関数の相互作用の割合を観察することで活性化状況を観察
  • 状態レベルカバレッジ
    • RNNなどにおけるステートを考慮

また、意図的にコードレベル・モデルレベルで軽微な変更を施し、その際の変異を測定することも行われているようです

その他、特異点に関する分析やルールベースによって妥当性が評価されます。

優先順位とケースの削減

上記のように、どのようにテストデータを生成し、どのような指標で測定し、どのように妥当性を評価するかについて簡単に確認しました。 十分にテストしようとすると、テストケースは膨大になりコストが増大します。

このような場合には、テストケースの優先順位をつけ、テストケースを削減するという戦略が取られることがあります。 これらのテスト結果を踏まえて、挙動は問題ないように見えて要件として問題があるという機械学習特有のバグを検出・デバッグに活用していきます。

テスト戦略と階層関係

ここまでで、テストを行う上での観点について個別に見てきました。 今度は、それらを統合してどうテスト戦略を作ればよいかについて考えます。

CD4MLの記事において、テストピラミッドの例は下記のように示されています。

f:id:nogawanogawa:20190917201756p:plain

最下層のピラミッドとして大きく、

  • データ
  • パイプライン
  • 周辺システム

に分けられるかと思います。 先に挙げたテスト観点は主にデータとパイプラインに対してであり、それに加えて周辺システムについてのテストが加わる形となります。

これら個別のテストに加えて、精度などの機能要件、公平性などの非機能要件のテスト、それらを統合してIntegration Test、E2E Test、Exploratory Testを行う上記のようなテストピラミッドが基本になりそうです。 これらを各アプリケーションの特性に合わせて組み換え、実際に行うテストに落とし込んでいくことが基本方針としては良さそうです。

参考文献

下記の文献を参考にさせていただきました。ありがとうござます。

arxiv.org

martinfowler.com

qiita.com

qiita.com

感想

機械学習のテストが思っていた以上に厄介な問題だということはよくわかりました。 正直なところ、テストはどれだけやっても終わりが満足しませんし、場合によっては部分的に不要になるケースもあるかと思います。 費用(時間?)対効果も考えて開発のフローを組んでいく必要がある一方で、品質を向上させることが今後どんどん求められていくんだろうなと思います。

世の中の機械学習アプリケーションに携わるエンジニアの方々はすごいなと心の底から思いました…

今回も勉強になりました。