ソフトウェア開発プロジェクトのマネージメントと機械学習
こんにちは、Mackerelチームディレクターのid:daiksy です。本稿は技術書典6にて頒布した"Hatena Tech Book"の原稿に加筆・修正を加えたものです。
「世はまさに空前の人工知能時代!」と言わんばかりに、昨今のIT関連のニューストピックには、AIや人工知能といったワードが溢れています。このような風潮ですから、開発チームを率いるマネージャの皆さんも、自分のチームで機械学習の開発にチャレンジする、といった事例が増えてくるのではないでしょうか。
私は現在、Mackerelチームのディレクターをやっています。Mackerelでは2019年3月に「ロール内異常検知」という機能をベータリリースしました。「ロール内異常検知」は、機械学習の技術を用いたサーバー監視の機能です。
ソフトウェア開発プロジェクトのマネージメントは一般的に、不確実性の高い要素をどこまでコントロール可能な状態に落とし込むか、ということがポイントです。見積もりによってプロジェクトのボリュームを明らかにしたり、バグ収束曲線などのソフトウェア工学的なアプローチによって品質を測るといった作業はすべて、不確実性の高い要素をいかに制御可能にするか、というアプローチです。
機械学習プロジェクトは、数あるソフトウェア開発プロジェクトの中でも、極めて不確実性の高い開発プロジェクトです。
一般的なソフトウェアは、事前の設計の段階である程度、最終的なソフトウェアのアウトプットを定義できます。たとえば「この倉庫の中にある在庫をコンピューターで管理したい」という課題設定に対して、どのような実装をすればその課題が解決できるか、かなり明確に設計することが可能です。
一方で機械学習プロジェクトでは、試行錯誤を重ねないと課題解決に至る方法にたどり着けない場合がほとんどです。「サーバーの異常を検知したい」という課題に対して、どのようなアルゴリズムを選定すればよいのか。そのアルゴリズムで本当に期待どおりの結果が出せるか。実際に作ってみて、動かしてみて、チューニングを繰り返すという試行錯誤が長く続きます。何ヶ月も試行錯誤を繰り返した結果、意味のあるアウトプットに至らないことも起こりえます。
機械学習プロジェクトを仕事として扱うには、このようなプロジェクトの性質やリスクを正しく理解した上で取り組む必要があります。
機械学習プロジェクトのチェックポイント
課題設定
機械学習プロジェクトを進めるにあたっては、まずは適切な課題設定が必要です。そして、その課題を解決する方法として、本当に機械学習が最適であるかを見極める必要があります。なぜなら、機械学習を使わずに課題を解決する方法があるのであれば、そちらを選択したほうが良いからです。
機械学習プロジェクトは不確実性が極めて高い、困難なプロジェクトです。機械学習を使わずに、伝統的によく知られている方法で課題が解決できるのであれば、プロジェクトとしてはそちらのほうがよほど制御しやすいでしょう。機械学習プロジェクトの最初のチェックポイントは、「本当に機械学習が必要なのか」を時間をかけて検討することです。
Mackerelの「ロール内異常検知」を例に見てみましょう。
サーバーの異常を検知したい
これが課題の出発点です。
サーバーの異常を検知するには、さまざまな方法があります。CPU使用率やメモリ使用量といった値に対して閾値を設定して、それを上回ったり下回ったりするものを異常と判断する。こういった伝統的な手法でも課題は解決できそうです。つまり、「サーバーの異常を検知したい」という課題だけを解決したいのであれば、機械学習を用いる必要はありません。
閾値を設定して異常を検知する機能は、これまでのMackerelですでに実装されていました。つまり我々には、そこからさらに発展した課題の解決が必要でした。
サーバーのメトリックに対して、閾値を設定して監視を定義していく従来の手法には、「監視設定を定義するために専門的な経験や知識が必要」という課題があります。
たとえば、新しく発明されたIoT機器を屋外に設置して長期間運用する、といった場合を考えてみましょう。新しく発明された製品なので、そもそも人類に運用の経験がありません。この機器が、数年間屋外で稼働した場合に、どういうタイミングや条件で、どういう異常が起こりえるのか、そもそも誰にもわかりません。こういった特性のものに対して、メトリックの閾値を事前に設定して異常に備えるのは不可能です。
機器の正常な状態に対して「いつもと違う傾向を示している」ことが明らかになってはじめて、異常を検知できるわけです。データベースサーバーの運用経験がないアプリケーションエンジニアが、データベースサーバーをどのように監視すればよいのかを考える場合にも、これと同様のことが言えます。
そこで我々が定義した課題はこちらです。
経験や知識に基づいた監視設定を事前に定義することなく、サーバーメトリックの普段と異なる傾向を検知したい
「サーバーメトリックの普段と異なる傾向」を見極めるのは困難な作業です。アプリケーションサーバーとデータベースサーバーでは、当然傾向は異なります。アプリケーションサーバーを例にとっても、アプリケーションによって特性は変わります。業務アプリケーションのように日中の業務時間帯に負荷が高まるという特性もあれば、ゲームを運用するサーバーのように夜間や土日に負荷が高まるといった特性など、さまざまなものがあります。
つまり、「サーバーの正常な状態」をそれぞれの用途ごとに学習し、そこから外れた値を異常と判断する必要があります。
ここまで課題を定義できれば、これを解決するには機械学習を使うのが最適であろうと判断してもよさそうです。
アルゴリズム選定
機械学習で課題を解決しよう、となれば、次に行うのはアルゴリズムの選定です。「機械学習」と一口に言ってもさまざまな手法があるので、どういうアルゴリズムを使えば今回の課題を解決できるのかを検討する必要があります。
この工程には、課題の対象領域に対する専門的なドメイン知識が必要です。優秀な機械学習エンジニアをアサインすればすぐにアルゴリズムは決まるだろう、ということはありません。プロジェクトにアサインされた機械学習エンジニアは、まずアルゴリズム選定に必要なドメイン知識の学習に時間を使います。
Mackerelの「ロール内異常検知」の開発でも、社内の障害事例の収集やインフラを担当するエンジニアのヒアリングなど、この工程にかなりの時間を使いました。この工程では、プロトタイプ実装なども同時に行っていきます。情報収集で得られた事例に対して、さまざまな手法をしらみつぶしに試していきます。この工程で機械学習エンジニアは、課題に対するそれぞれの手法の得手不得手や、計算量の肌感覚を掴んでいきます。
Mackerelの場合、アルゴリズムの選定に重要な要素として、異常を検知すること以外にも別の観点が必要であると判断されました。それは、人間がその挙動を説明可能である、という観点です。
ディープラーニングなどを用いた手法では、結果に対してなぜそうなるのか人間には説明不能なことがあります*1。Mackerelの場合は、単に「異常を検知しました」という結果を示すだけでなく、ユーザーサポートなどの観点からなぜその結論を算出したのかが説明できなければなりませんでした。
また、Mackerel開発チームは機械学習の専門集団ではありません。Webアプリケーションエンジニアが多く在籍している開発チームという特性上、機械学習を専門としないエンジニアがレビューを行ったり、今後メンテナンスを引き継いでいくことができそうか、ということも大事な観点です。ここが不可能であれば、長期間の運用ができないことを意味するわけですから。
このような観点から長期間の試行錯誤を繰り返した結果、Mackerelでは「混合ガウス分布」を用いた判定を行うことが決まりました。複数のガウス分布を考慮することで、夜間や土日のみ負荷が上がる、などの周期性に対して対応できます。実装されたソースコードの理解も比較的容易で、人間が挙動を説明できます。つまり、必要な要件をすべて満たした手法にたどり着くことができました。
実装と検証
ここにきてようやくプロジェクトとして見通しがつきはじめます。アルゴリズムを選定できれば、実装に必要なアーキテクチャなどを考えられるようになり、ここではじめて実装期間の工数見積が可能になります。
Mackerelは現在AWSで稼働しています。「ロール内異常検知」をサブシステムとして構築していくにあたって、どのようなマネージドサービスを採用するか。開発言語に何を用いるか。ようやく馴染みのあるソフトウェア開発のアプローチが始まります。
プロジェクトを推進するマネージャとして、工数を見積もり、スケジュールを引き、リリース日なども考えられそうな気がしてきます。しかし、機械学習プロジェクトはまだまだこの段階においても単純ではありません。
ここで考えるべき機械学習プロジェクトの特性を一つご紹介します。それは、テスト工程についての特性です。
一般的なソフトウェア開発プロジェクトの場合、ソフトウェアの完成を定義し、それを満たしているかどうかをテスト工程で検証します。機械学習プロジェクトで扱うソフトウェアでは、この「完成の定義」がたいへん難しいのです。
Mackerelを例に考えましょう。我々が解決したい課題のゴールは「サーバーの異常を検知すること」です。そのために開発される新しい機能の「完成の定義」はどうなればよいでしょう?
Mackerelの「ロール内異常検知」は、収集されたサーバーメトリックの過去の傾向から「サーバーの正常な状態」を学習します。そして、新しく投稿されたメトリックが、学習された「正常な状態」から外れた値だった場合、異常と判定します。では、普段の傾向からどれくらい外れたものを異常とすればよいでしょうか?
機械学習プロジェクトは、実装が終わったあとのテスト工程で最も多くの時間を必要とします。出来上がったシステムを実際に稼働させて、どこに判定条件を定めれば、人間の直感と一致する結果となるかをチューニングしていくのです。
Mackerelの場合は、誤検知との戦いが困難を極めました。メトリックがいつもと異なる傾向を示したからといって、即座に異常である、とは言えない場合もあります。サーバーは正常な状態なのに、敏感すぎて異常と判定されてしまう。逆に、サーバーが異常な状態なのに、判定基準が鈍感で検知されない。パラメータの調整によって、この両者のちょうど良い塩梅を探っていくわけです。
我々は、実装工程にめどがついたタイミングで、プロジェクトとしてMackerelの「ロール内異常検知」のリリース日を2018年12月に設定しました。それまでの検証でも、かなり良い精度で動いていたため、この予定どおりリリースできそうな見通しがありました。ところがリリース目前となる12月に入って、ライブラリのバージョンアップによる挙動の変化の影響を受け、誤検知が大量に発生する事態になりました。そのため、リリース日を延長して再度念入りなチューニングを行うという判断をしました。このように、機械学習プロジェクトは期間の見積もりが本当に難しいのです。
運用
さまざまな困難を経て、Mackerelの「ロール内異常検知」は2019年3月にβリリースされました。ここから運用の日々が始まります。
「ロール内異常検知」はアルゴリズムの選定段階から強く運用を意識して設計されています。それは、機械学習エンジニア以外でもソースコードが読める、人間が挙動を説明できる、というアルゴリズムの選定条件にも表れています。
はてなでは、サブ会という制度があり、現場のエンジニアがチーム横断的に情報を共有する場があります。現在Mackerelチームに所属している機械学習エンジニアは1名ですが、このサブ会によって「ロール内異常検知」に関する状況は、社内の他のチームの機械学習エンジニアにも共有されています。実際、開発期間中には他チームのエンジニアにペアプロやレビューなどで協力してもらったりもしました。こうして、長期間システムを運用していくために属人性を薄める手立てが取られています。
仕事で機械学習を扱うということ
ここまでMackerelを例に、機械学習プロジェクトの概観を簡単に見てきました。機械学習プロジェクトがいかに不確実性の高いものであるか、その雰囲気を掴んでもらえたのではないかと思います。
仕事で機械学習を扱うには、このような性質やリスクを、経営陣やマネージャ、発注者が理解することが必要です。機械学習プロジェクトでは、品質の達成・維持に最も多くのリソースが必要です。これは同時に、期間の遵守に対してはとても弱いということを意味します。つまり、受託開発などで納期に強くコミットメントする必要のある開発案件では難しい性質のプロジェクトです。プロジェクトに関わる人々全員がこのような性質を理解することが、機械学習プロジェクトをゴールさせて本番稼働を達成するための大原則であると言えます。
*1:最近ではディープラーニングの理由を説明させるモデルをもうひとつ作る、などの説明可能な手法も存在します。