機械学習を利用するプロダクトのテスト
このエントリについて
ポエムです。
11/8(火) に開催された Cloudera World Tokyo 2016 に参加しました。
- 大規模データに対するデータサイエンスの進め方 #CWT2016 (以下、発表 1)
- データ分析グループの組織編制とその課題 マーケティングにおけるKPI設計の失敗例 ABテストの活用と、機械学習の導入 #CWT2016 (以下、発表 2)
その中で上記の2つの発表がとてもいい話でした。 多少絡みのある内容として機械学習を利用するプロダクトのテストについて述べたいとちょっと前から考えていたので、いい機会なので書いてみます。
おそらくできてる組織は当たり前のようにできているでしょうし、できてない組織は当たり前のようにできていない話です。
2種類の要件
通常テストはプロダクトの仕様や要件にもとづいて用意されますが、システムの要件には機能要件・非機能要件の2種類があります。
ここでは機能要件と、非機能要件の中でも性能要件にフォーカスします。 機械学習を利用するプロダクトにおいては、例えば
- 参考にした論文に記載の式のとおりにパラメータを更新しているか → 機能要件
- 予測・分類等で期待される精度が出ているか → 性能要件
などと考えることができます(もちろんこれだけではありませんが)。 個人的にはどちらの要件のテストも必要だと考えています。
ところがこれまで私が見てきた中ではこれらのどちらか(または両方)ができていないチームもありました。 機械学習のアルゴリズムの実装は組織によって、エンジニア的な人がやる場合と研究職(or アナリスト)的な人がやる場合とがあると思います。 私の経験からすると、機械学習のアルゴリズムの実装を担当するのが前者の場合は性能要件のテストが、後者の場合は機能要件のテストが抜け落ちてしまうことがあるようです。
性能要件のテスト
普段エンジニアが性能要件を考慮していないかというとそんなことはなくて、システムのスループットやレイテンシー等は常に見ていると思われます。 ですが機械学習のプロジェクトの経験があまりないような場合は「予測や分類等のタスクをどの程度の精度でこなせるか」という検証が盲点になることがあります。 最初に計画していた実装でアプリケーションに求められる精度を達成できるかは分からない場合が多いのではないでしょうか。
offline と online
性能のテストには offlineと online があります。 offline とは本番運用に影響を与えないという意味です。 過去のログデータ等を用いて手元で精度を検証し、より良いアルゴリズムの選定やパラメータの調整を行います。
一方で online の性能テストは本番環境に乗った形で行います。 例えば web サービスなら A/B テストで実際のユーザの行動によってアルゴリズムやパラメータの良し悪しを比較します。
二者を比較すると、
- offline 性能テスト
- 本番運用、つまり売上等への直接的な影響がない
- 比較的高速にテストできる(過去ログを使うため実時間でログがたまるのを待つ必要がない)
- 同じログデータを使うことで再現性のあるテストが可能
- online 性能テスト
- 実際の本番運用の条件でテストできる
という特徴があります。
以上から、たくさんのアルゴリズムやパラメータそれぞれについて性能テストを実施し、性能が高い組み合わせにあたりをつけるのは offline の性能テストが向いていると言えます。 なおかつ offline では本番環境と条件が異なる場合もよくあるので最終的に online 性能テストで可否を確認するというのが堅実な流れです。 発表 1 でもこの流れが説明されていますね。
offline 性能テストの自動化
頻繁に offline 性能テストを回したい部分についてはテストの自動化をしたいところです。 xUnit のような各言語のテストフレームワークに offline の性能テストを無理矢理のせることもできますが、おすすめしません。 性能のテストには大量のデータが必要となるため、それなりの時間がかかります。 つまりスローテストになるので CI に組み込むのはつらい感じです。
自動化する場合は CI で実施するテストとは別物と割り切って、前処理・特徴抽出・学習・評価を行うワークフロー的な仕組みを導入するのがよいでしょう。 shell 等で自作してもいいのですが、Spark の ML Pipelines や Spotify が開発した Luigi 等のワークフローをサポートするフレームワークを使うと楽かもしれません。
重要なのは例えば1ヶ月後にチームの別のメンバーが同じ条件のテストを再現できるか(できればなるべく簡単に)です。 必要ならドキュメントも残しておきたいです。
A/B テストはすぐにほしい
機械学習を導入したいのであれば、まずABテストから
上記は発表 2 でからの引用です。 私もこれに激しく同意です。 *1
「まずは機械学習的なものを導入しましょう、A/B テストは必要になってから開発しましょう」みたいなケースを何度か見たことがありますが、これは以下の2点から好ましくありません。
- 既存システムに機械学習を追加する場合、機械学習があったときとなかったときの比較ができない
- ex. EC サイトに商品レコメンデーションを追加したけどその効果が測れない
- A/B テストを後から導入するのは結構たいへん
- 経験上、最初は「性能はとりあえず問わない」みたいな話で開発が始まっても結局後で性能が問題になるのはあるあるで、どうせ A/B テストが必要になる *2
プレスリリース芸や営業資料等のビジネス上の観点から少ない開発リソースで A/B テストなしで機械学習を実装せざるを得ないケースもあるかもしれませんが、設計時になるべく後から A/B テストを入れられるようにしておきたいところです。
機能要件のテスト
さて、次は機能テストの必要性について話していきます。
ここで言う機能テストはいわゆる CI に組み込まれて自動で実行される、機能が要件・仕様どおりの挙動を示すかをチェックするテストです。 性能テストのように数値の尺度があるのではなく、「◯◯ができる/できない」でチェックできる項目がテスト対象です。 機械学習のアルゴリズムにおいては、例えば元となっている論文の式がそのとおりに実装されているか、入力データのある次元の分散が 0 でもおかしな挙動にならないか、などといったことをチェックするテストが挙げられます。
通常こういったテストは各言語のテストフレームワークを使って作られます。 Python なら unittest や nose, C++ なら googletest や CppUnit, Scala なら Specs2 や ScalaTest など各言語でいろいろありますが、それぞれのフレームワークで考え方は似ているので慣れれば難しくありません。
性能テストのみで十分なのでは?
「性能テストでそれっぽい精度が出てれば問題ないのでは」という意見を実際に聞いたことがあります。 そうとも言えそうだし結局性能テストもやらないといけないわけなのですが、これには反論があります。
まず「期待する性能を確認できること」と「アルゴリズムを想定通りに実装できていること」はイコールではありません。 アルゴリズムの実装の一部に問題があってもそこそこの性能が出ることはあります。 しかしその後パラメータチューニング等をするときに理論どおりの挙動にならず「???」となった上で実装の問題を発見して、性能テストをやり直し…ということもあるでしょう。
また、プログラムを修正したけど性能には大きな影響はないはず、という場合もあります。 このような場合に動作確認のためにわざわざ性能テストをしないといけないのもコストが高いです。 もちろん修正に悪影響がないか確認しないというのもよろしくありません。 機能テストを自動化していれば影響をすぐに確認できます。
テストデータ生成
通常の xUnit のテストでは「この関数にこのデータを入れるとこの値が返ってくる」というようなチェックの仕方をすることが多いですが、返ってくる値の正解を用意してやる必要があります。 例えば勾配の更新式等、数式レベルの機能であれば Excel 等(できれば非プログラム的な方法)で簡単に計算した値を正解データとして使うといいかと思います。 これは答えが明確で用意しやすいです。(とはいえ面倒臭い)
難しいのは予測や分類などの大きめのレベルの機能の正解データをどう用意するかですが、私は例えば分類なら少量の入力データかつごく簡単な分類タスクを用意してやり、「これならいくらなんでも分類できるでしょ」というのを正解にします。 本当に最低限の性能は保証されている前提でテストデータを作るわけです。 *3
開発リソースに余裕があるのであれば、実装する人とテストデータを作る人を別にすると安全かつ仕様の共有が捗ります。
機能テストをいつ作るか
自動化された機能テストを用意するタイミングとしては
- offline 性能テストにかける段階
- online 性能テストにかける段階
- baseline として適用される段階(online 性能テストで勝利した後)
のどれかになるかと思いますが、チームとしてのルールの中で決められていればいいと思います。
個人的には 1. の時点ではアルゴリズムの肝となる数式の部分や問題が起こりそうなところ、2. の時点では今のデータで本番サーバでの運用に耐えられるレベル、3. の時点ではテストするのが面倒臭いけど潰しておきたいところ、と段階的にテストを充実させていくのがいいんじゃないかなと思います。
まとめ
このエントリで言いたかったのは性能も機能もどちらもテストしましょうということです。
これを実現するのに難しいところがあるとするなら、それはデータ分析を担当するチームの構成ではないでしょうか。 発表 1 & 2 でも述べられていますが、データ分析のチームにはいろいろなスキルを持った人が必要です。 言い換えると異なった文化的背景を持った人たちが集まった low context なチームというわけです。 そういった状況でゴールに向かうにはチーム内をルールなりでまとめることが必要で、そのルールを適切に運用できるリーダー的な人の存在が重要なのだと思います。
最後になってしまいましたが、何のためにテストをやるかと言うと平たく言ってしまえば安心するため、不安を軽減するためだというのが個人的な認識です。 機能的な不安も性能的な不安も減らしていきたい所存です。