ghee-modelsの紹介

張洪偉氏:洪偉です。パート2でモデル管理するためのghee-modelsを紹介いたします。アジェンダとしてghee-modelsとghee-modelsの事例、表現学習でgraph convolutional networksの応用について紹介いたします。

まずghee-modelsを紹介いたします。ghee-modelsはgheeに基づくマシンラーニングモデルのコレクションを提供するライブラリです。MLFlowを導入することにより、チーム向けモデルを管理する標準な方法を提供いたします。

ghee-modelsの特徴は3つあります。まずMLFlowでモデルとモデルパラメータを一元管理することで再現性が高いです。次に抽象度の高いAPIを提供することで再利用しやすいです。最後はマシンラーニングタスクとデータフォーマットにより前処理、後処理、トレーニングのプロセスなど、内部でカプセル化することで拡張性が高いです。

ghee-modelsの設計と実装

これから具体的な設計と実装を紹介いたします。この図の青い部分は先に紹介されたgheeの実行プロセスになります。gheeをベースとしてghee-modelsにready for useのPreprosess、Train/Predict、Postprocess関数などを実装しました。データが正しいフォーマットになるかチェックできるデータチェッカーと、YAML Configを読めるloaderも用意しました。またモデルを管理するためMLFlowも導入しました。

もっとわかりやすくするために具体的な例を紹介いたします。このコードは新しいモデルを追加したいときに最低限実装しないといけないものです。train_preprosessはトレーニングデータの前処理関数です。predict_preprosessは予測データの前処理関数です。trainはトレーニングの関数です。predictは予測関数です。postprocessは予測だけで扱われる後処理関数です。

画像分類モデルの例として説明いたします。train_preprosess関数は読み込んだ画像を前処理してbatchを生成します。データを各Podのプロセスまで転送される部分はgheeに任せればいいです。前処理が重い場合、単純にPodやプロセスの数を増やせば改善できます。

train関数はデータを受け取ってモデルをトレーニングします。predict_preprosess関数はtrain-preprosess関数と似ています。item_idと画像のbatchを作ります。予測関数はMLFlowからトレーニングしたモデルを読み込んで予測します。裏でgheeが予測した結果をパーティションにまとめてストレージに保存します。

これはインプットフォーマットの定義です。データチェッカーはこの定義を使ってデータ周りをチェックします。

このコードはConfigの例になります。データセグメントでデータのパスを指定できます。モデルセグメントでモデルのパラメータ設定を指定できます。

gheeセグメントでgheeの環境の設定を指定できます。例えばPodごとのPreprosessの数や、Kubernetesセグメントでクラスタの設定を指定できます。例えばPodの数、メモリのサイズなど。

今いろんなマシンラーニングタスク向けのモデルが入っています。分類のためのDNN、自然言語処理モデル、画像処理モデル、レコメンドモデルなどがあります。

表現学習分野におけるgraph convolutional networksの応用

次に、表現学習におけるgraph convolutional networksの応用について紹介いたします。LINEはいろんなサービスにおいて、膨大なユーザーログを保有しています。それはgraphとして表現することができます。graphからノードラベリングとかに使える汎用性のある密ベクトルを表現学習で獲得したいです。

生成された密ベクトルは、レコメンド、ユーザーセグメント推定、ユーザーのLookalikeエンジンなど、いろんなところで使えます。

データの規模についてです。15以上のサービスに適応します。計算対象ユーザーのIDの数は8億を超えます。graphの構造になるとノードの数は合わせて15億を超えます。この規模のgraphデータが揃うため、分散処理は不可欠なものです。それをgheeで解決できました。

この図はモデルのアーキテクチャになります。モデルの下でGCN layerをかけて、ノードの隣接同士の情報を集めて、中間の密ベクトルが生成されます。モデルの上でwide and deep layerをかけて、最後の密ベクトルが生成されます。

使われるlossはcross entropy with negative samplingです。BPR loss、Focal loss、ArcFace lossなどもいろいろ実装しました。あとデータがでかいので毎日全部のデータで学習するのはコストが高いので、incremental learningでモデルを学習させました。新しいノードが追加される場合、モデルの構造も動的に更新されます。

このコードはgraph convolutional networksの実装の一部です。gheeのおかげでgraphの計算部分を簡単に分散できます。preprosess関数はノードの隣接同士をサンプリングして、小さいgraphをGPU側へ転送します。Train関数はgraphデータが受け取ってモデルをトレーニングします。

27個のモデルを日毎学習させます。作った密ベクトルの精度を検証するため、ユーザーセグメント推定タスクで試してみました。sparse DNNのインプットデータはユーザーの履歴IDです。履歴IDはモデル内部でembeddingへ変換されます。

dense DNNのインプットデータはユーザーの密ベクトルになります。結果として表現学習でユーザーの履歴をdense vectorへ変換してもdense vectorの持っている情報が大きく損失しなかったみたいです。

逆にユーザーセグメント推定の精度は高くなりました。またインプットのdense vectorになりましたので、モデルのサイズもけっこう減りました。モデルのトレーニング時間もけっこう減りました。私の発表は以上です。ご清聴ありがとうございました。

cuminというライブラリについて

齋藤祐樹氏:斉藤です。パート3ではデータ生成の生成方法を管理するcuminというライブラリについて紹介いたします。

まず開発に至った動機から説明します。我々はLINEのサービスのために多くのレコメンデーションを提供しています。これらのレコメンデーションごとに多くのテーブルを用いてデータを生成しています。

データ生成の際には適切な学習データを得るためにテーブルごとにさまざまな条件を設定し、リークやノイズの除去を行う必要があります。MLエンジニアの多くはレコメンデーションを作るにあたって、これらの必要な設定を独自のフォーマットで設定していました。

そのためレコメンデーションごとに設定方法が異なり、フィルター条件がパラメータとなっているものもあれば、クエリにそのまま書かれているものもありました。

これら以外にも学習データの生成にあたり必ず行う処理、例えばデータの件数のチェックや配布テーブルのパーティションの存在確認を行う必要があります。これらの定型処理がレコメンデーションごとにいくつも存在するため、コードの可読性が低下していました。

また実験段階においても問題がありました。それは実験におけるデータの再現性がないことです。よりよいモデルを得るためにモデルのハイパーパラメータだけではなく学習データの生成段階においても条件を変えることはよくあります。

ghee-modelsでは学習に利用したハイパーパラメータはMLFlowに残っていますが、データ生成に使った条件は残っていません。そのためデータを変えて実験した際の条件が保存されておらず、実験の再現性がないことがありました。

cuminについて

次にcuminについて紹介します。まずcuminは利用するテーブルやカラム、フィルター条件や定型処理を記述するフォーマットを提供します。基本的にPythonのデータクラスとして宣言し、その中に設定を記述します。

これによって今までMLエンジニアごとにバラバラだった設定方法の統一化を行うことができます。フィルター条件もここで設定することができるため、実験段階における条件変更なども残すことができ再現性を得ることができます。

次にcuminの利用の仕方について説明します。基本的にcuminを利用するために特別な処理は必要ありません。Sparkセッションを立ち上げるのと同じようにcuminのセッションを立ち上げてもらうだけでよいです。

この記述のみで裏でSparkを起動し、これまでと同じようにSparkを用いて設定したテーブルにアクセスすることができます。また同時に設定にデータ件数などのチェックなどの項目があれば、裏で自動的に検証が走り、もし問題があればワーニングがあがります。

cuminのその他の機能

そのほかの機能の一部についても説明いたします。1つはYAMLファイルでの設定の読み出しと書き出しです。これはその他のコンポーネントからの入力パスや出力パスを受け取るために利用します。

もう1つは出力ファイルサイズの調整です。gheeでPreprosessをCPUで行う際にデータサイズに対して適切なファイル数に分割されていないとデータ転送の効率が下がってしまいます。また極端に小さいファイルを大量に生成するとHDFSに負荷がかかってしまいます。そこでcuminでは1ファイルをブロックサイズ程度になるように自動的に分割して、これらの問題を回避することができるようにしています。

cuminの実装

次に実装について簡単に説明いたします。基本的にSparkの関数をフックするかたちで実装しています。cuminを利用する場合はSparkの起動の前に設定を読み込む必要があります。そのためSpark起動時には利用するテーブルやカラム、パーティションやフィルター条件がわかっています。

それらを利用して本来ならばSparkの起動後にそれぞれバリデーションを行うところを、Sparkの起動と同時に裏で実行します。これによって定型処理をユーザーが明示的に書くことなく検証が走ります。

またHDFSに書き出す関数をフックするかたちで出力するデータの一部を書き出し、全体の出力ファイルサイズの概算値を計算します。そして書き出しの前にリパーティションをかけることによってファイルサイズの最適化を実現しています。

cuminの利用例

最後にcuminの利用方法の例を紹介します。まず学習データの生成に必要なテーブルをcuminで設定します。ここでフィルター条件も記述することができるので、どのような条件のもとでデータが生成されたかを残すことができます。

次にghee-modelsにcuminで生成したデータを与えます。学習に利用したパラメータはghee-models内でMLFlowに保存されます。これによってcuminではデータ生成部分の再現性、ghee-modelsではモデル部分の再現性を担保することができます。

今後の課題

最後に本セッションのまとめです。

gheeは大規模データを簡単に扱うためのライブラリです。KubernetesのPod管理も行い、ユーザーは簡単な設定で大規模データを前処理のためのCPUノードと複数台のGPUノードを利用してモデルを学習することを可能にします。

ghee-modelsはモデルを管理するためのライブラリです。これはgheeをベースに精度のよかったモデルを登録することができます。これにより同じモデルを再実装することなく利用が可能になります。また共通のインターフェースを用いることによって、あるデータセットに対して複数のモデルを簡単に評価することが可能です。その結果より早くプロダクトにレコメンドを提供することが可能になりました。

cuminは学習データ生成のための設定方法を提供します。フィルター条件などを明示的に記述させることでデータの再現性を担保することができます。これらを組み合わせることによって大規模データを効率的に扱いながら再現性のある実験をすることができます。

今後の課題について述べます。ghee-modelsは簡単にモデルを登録することができるため、新しくより精度が高いとされているモデルを組み込んでいきます。またghee-modelsはモデルだけではなく損失関数なども登録し、再利用することができるため、それの拡充も行っていきます。

cuminではより多くのデータソースをサポートする予定です。現在はSparkとHDFSの利用がメインとなっていますが、Kafkaなどもサポートしていきたいと考えています。以上で発表は終わりとなります。ご清聴ありがとうございました。