機械学習システムの運用課題とコンテナオーケストレーションがもたらすもの

最近仕事では機械学習を使ったアプリケーションをKubernetes上で運用することが多くなっています。 MLOpsのような言葉も次第に浸透してきたりと、システムとしての機械学習をどう運用していくかが活発に議論されるようになってきました。 運用に頭を悩まされてきた身としては心強い限りです。

この記事ではKubernetes(以下k8s)のようなコンテナオーケストレーション技術が、機械学習システム(以下MLシステム)の実行基盤としてなぜ適しているのかについて考えてみました。 注意点として、私はMLアルゴリズムの専門家ではなく、またLinuxやコンテナ技術の専門家でもありません。 あくまで仕事としてMLシステムを運用するためにk8sを利用している立場からの考察です。

TL; DR:

  • MLシステムの運用には次のような課題が存在する
    • 目的によって求められるワークロードの形態が異なる
    • 一時的・断続的に大量の計算リソースを必要とする
    • 実環境での試行錯誤に伴ってシステムが変遷する
  • コンテナオーケストレーションは次のことを可能にする
    • プロセスの実行環境とそのライフサイクルを独立して管理する
    • 異なるワークロードを同一のインフラ上でスケジューリングする

→ MLシステムの運用課題はコンテナオーケストレーションによって解決しやすい

機械学習システムの運用課題

1 - アーキテクチャの多様性

f:id:yubessy:20180415231814p:plain

MLシステムのアーキテクチャは非常に多様です。 計算としての機械学習は「学習」と「予測」という2つのステップから構成されますが、この2つをどのような形態のワークロードとしてシステムを構成すべきかは目的によって異なります。

最もシンプルなアーキテクチャは、状態を持たない単一のバッチ処理として学習・予測を一気に行うものです。 例えばECサイトで1日1度アイテムをレコメンドするメールマガジンを送信したいといったケースはこの形態で十分と思われます。

ここから一歩進んで、新規データに対してオンラインで予測を行うこともよくあります。 ECサイトの例であれば、新規ユーザに対して登録後すぐにいくつかアイテムを推薦したい、といったケースが考えられます。 オンラインでの予測処理は、リクエストを受けてレスポンスを返すまでの短い時間で計算を完了できることもあれば、リクエストをキューイングしてある程度時間をかけて処理してから改めて結果をプッシュすることもあります。

予測だけでなく学習の一部または全部をオンラインで行う場合も考えられます。 Matrix Factorizationのようなアルゴリズムを用いたレコメンドで、ユーザが新しいアイテムを評価した場合にモデルを再学習するようなケースがこれに当てはまります。

これらに加えて転移学習のような既存のモデルを部分的に更新するような場合まで考えれば、状況はさらに複雑化します。 これ以上は詳しく述べませんが、要するに一つのパターンでは解決できないほどMLシステムのアーキテクチャは多岐にわたるということです。

2 - リソース消費の非連続性

システム形態が多様化しても消費する計算リソースが多くなければ、運用上の問題はそれほど大きくならないかもしれません。 しかしMLシステムは概して通常のシステムより大量の計算リソースを消費します。

機械学習の学習処理は、簡単に言えば大量のデータに複雑なアルゴリズムを適用して有用な関数(モデル)を獲得するという計算です。 処理に必要な計算リソースを肥大化させる要因は、たいていデータとアルゴリズムの両方にあります。 このため計算量を削減することがそもそも困難であり、資金を投入して計算リソースを確保することでしか問題が解決できないこともよくあります。

しかし多くの場合、計算リソースを大量に消費するのは主に学習ステップであり、予測ステップに必要な計算リソースはそれに比べるとずっと少ないのが普通です。 さらに学習は1日や数日に1度、時には数ヶ月に1度行うだけで済むこともあります。 画像認識や自然言語処理など、問題が汎用的であるほどモデル全体の学習の頻度は少なくて済みますが、これは学習によって獲得したモデルが長期に渡って価値を発揮できるためです。

このことはそれ自体MLシステムの長所でもありますが、運用上は悩みの種でもあります。 一定期間に1度しか行われない計算のために常に大量の計算リソースを確保しておくのは大きな無駄となります。 しかし複数のシステムを同一のインフラに同居させようとすれば、マシンのプロビジョニングは複雑化し、システムが採用できる技術の制約は多くなります。

3 - アーキテクチャ・運用レベルの時間的変化

MLシステムの効果は事前に交差検証やバックテストである程度確認できるものの、本質的には実環境で実データを使って運用してみなければ不確実性が大きいものです。 このためMLシステムの開発プロジェクトでは早い段階でシステムを実環境にデプロイし、そこで得られるフィードバックをもとに試行錯誤を繰り返す必要があります。

試行錯誤の過程ではライブラリや収集・利用するデータはもちろん、クラスタリングか次元削減かといった問題設定までもが変わることもあります。 またシステムがある程度の効果を上げるようになれば、学習や予測の頻度を高くしたり、複数のモデルの並行テストやフェイルオーバーが求められることもあります。

これは運用においてシステムの実行環境やリソース要件が頻繁に変更されることを意味します。 変更のたびにマシンの構築やプロビジョニング、キャパシティプランニングが必要となれば当然運用の負担は大きくなります。

さらにMLシステムが価値を発揮するにつれ、ビジネスの売上・利益がそれに強く依存するという状況が生まれることすらあります。 もちろんこれは悪いことではないのですが、システムの可用性が失われればビジネス上の損失が生じることを意味するため、運用には相応の信頼性が求められることになります。

コンテナオーケストレーションがもたらすもの

1 - プロセスの実行環境とライフサイクルの独立化

Linuxにおける計算処理の単位はプロセスです。 コンテナはプロセスを名前空間ファイルシステム・ネットワークといった実行環境ごとカプセル化したものです。 これを応用すればアプリケーションを依存ライブラリごとイメージとしてアーカイブするといったことも可能になります。

プロセスの実行パターンは成否を表すコードとともに終了するか無限に実行を続けるかの2通りしかありません。 従って高度なシステムでは、複数のプロセスを時にはマシンにまたがって協調動作させることになります。 常に一定数の実行プロセスを維持するサーバや、巨大な計算を並列処理するジョブなどがこれにあたります。

コンテナオーケストレーションの導入により、協調する複数のプロセス(=コンテナ)とそのライフサイクルをひとつのワークロードとして管理できるようになります。 多くのオーケストレーションツールは典型的なワークロードのパターンを予め提供しています。 例えばk8sでは1つ以上のコンテナからなるワークロードの最小の単位をPodとし、常に一定数のPodを実行するようなワークロードにはReplicaSet、加えてPodの世代管理を行うワークロードにはDeploymentといったビルトインのパターンを用意しています。 バッチジョブに適したワークロードパターンとしては、全てのPodを完了まで実行するJob、Jobを定期実行するCronJobなどがあります。

コンテナオーケストレーションの特徴は、プロセスのライフサイクルを実行環境とは独立して管理できる点です。 実行環境が全く異なるプロセスであっても、ライフサイクルが同じであれば同じパターンが適用できます。 これにより従来はミドルウェアフレームワークが担っていたプロセス管理の役割をオーケストレーションツールに移譲でき、アプリケーションは本来の計算処理とインタフェースの提供に集中できます。

2 - 異なるワークロードの同一インフラ上でのスケジューリング

コンテナオーケストレーションのもうひとつの役割は、コンテナに対するコア・メモリ・ボリュームといったリソースの割当です。 ワークロードが追加されると、オーケストレーションツールは指定されたリソース要件に基づいてクラスタにコンテナ群をスケジューリングします。

このリソース要件の指定とスケジューリングの仕組みもまたプロセスの実行環境とは独立しています。 例えば全く異なるライブラリやフレームワークを用いて実装されたWeb APIサーバとバッチジョブを同一のクラスタ上でスケジューリングすることも可能です。

さらにk8sのようなツールはクラスタを構成する各マシンの負荷状況に応じてワークロードを実行するマシンを選択したり、障害に備えてレプリケーションを分散する機能を備えています。 これらの機能を利用すれば、複数のシステムを効率的に運用しつつ信頼性を保つことができます。

機械学習システムとコンテナオーケストレーションの親和性

ここまでの内容で想像がつくように、先に挙げたMLシステムの運用課題はコンテナオーケストレーションを用いることで解決しやすくなります。

まずワークロード形態の多様性の問題には、オーケストレーションツールが提供する汎用的なパターンを目的に応じて選択することで対処できます。 k8sであればMLシステムに最も適用しやすいのはJobでしょう。 Jobは処理の完了を待つだけでなく、異常終了時のリトライやタイムリミット超過時の強制終了などの機能も持っています。 またReplicaSetやDeploymentはオンラインでの学習・予測を行うサーバを実行するのに役立ちます。 さらにはSparkやTensorFlowのような大規模な分散処理クラスタをk8sのワークロードパターンを用いて構築することも可能です。

次にリソース消費の非連続性の問題には、複数のワークロードを同一インフラ上でスケジューリングすることが有効です。 複数の定期実行されるMLジョブを運用する場合、それらの実行時間をずらしてリソースを共有するといった単純な戦略が考えられます。 あるいはMLシステムと他のアプリケーションを同居させるという選択肢もあります。 ディスクIOやネットワーキングのようなマシン全体のスループットの制限には注意が必要ですが、MLシステムの消費するリソースの大部分はコアやメモリであるため、スケジューリングにおける制約は比較シンプルになります。

最後にアーキテクチャ・運用レベルの時間的変化についても、先の2点と同様のことが言えます。 システムが進化してアーキテクチャが大幅に変わったとしても、新たなインフラを構築せず既存のインフラ上でワークロードパターンを組み換えるという選択肢を取ることができます。 これにより専用のインフラを慎重に設計する時間的猶予が得られたり、成功するかわからないアーキテクチャを一時的に試せるといった利点もあります。 リソース消費の増加に対処したり冗長性を確保するためにはクラスタの拡張が必要ですが、その際のマシンのプロビジョニングが個々のシステムに依存するようなことはなくなります。

これから

MLシステムの重要な運用課題のいくつかはコンテナオーケストレーションにより解決できると思われます。 そしてこの潮流はまさに始まったばかりであり、今後さらなる技術的発展が期待されます。 記事の締めくくりとして、自分がいま気になっている技術をいくつか取り上げたいと思います。

Argo - k8sのCRDとして実装されたワークフローマネージャ

github.com

LuigiやAirflowのようなワークフローマネージャをk8sのCustom Resource Definition(CRD)として実現しようというプロジェクトです。 個々のステップがコンテナ化されたジョブを、直列・並列・繰り返しなどを含む柔軟なパターンで記述できます。 CRDはKubernetesのJobやDeploymentのようなワークロードパターンをユーザが定義できる機能です。 Argo UIと呼ばれるUIが提供されているほか、ロードマップでは様々なトリガーへの対応を予定しています。

Seldon - k8s上での汎用MLプラットフォーム

github.com

k8s上に汎用的なMLプラットフォームを構築しようというプロジェクトです。 特定のMLライブラリに依存しない汎用的な仕組みのもとでTensorFlowやSparkなどのフレームワークを統一的に運用するという、非常に野心的な取り組みを行っています。 成功するかどうかは未知数ですが、開発は活発に続けられているようです。

RiseML - k8s上での深層学習プラットフォーム

github.com

k8s上に深層学習プラットフォームを構築しようというプロジェクトです。 Seldonに似ていますが、こちらは深層学習フレームワークに特化しています。 またコードは公開されているものの、基本的にはPaaSとしてサービスを提供しているようです。

Kubeflow - k8s上でのTensorFlow構築ツール

github.com

TensorFlowクラスタとその付随要素をまとめてk8s上に構築しようというプロジェクトです。 TensorFlowのTrainingワークフローをCRDとして実装しているほか、JupyterHubやTensorFlow Servingのような付随するシステムをまとめて立ち上げることができます。 TensorFlowクラスタを自前で運用したい場合には有効かもしれません。

長くなりましたがこのあたりで終わります。

参考資料

O'Reilly Japan - 仕事ではじめる機械学習

Machine learning on kubernetes