爆速開発を目指して NewsDigest を Flutter にリプレイスします

f:id:jazzsasori:20211128104040p:plain
爆速開発を目指して NewsDigest を Flutter にリプレイスします

JX通信社 Engineering Manager の @jazzsasori です。
最近アークナイツというソシャゲに課金してしまいましたが妻には内緒にしています。

弊社は NewsDigest という無料ニュースアプリを運営しています。
NewsDigest は記者が業務で愛用するほど、その圧倒的スピードに強みがある速報アプリです。また、一般的なニュース分野での速報に加えて、報道はされにくいが個人にとって価値の高い情報も to B 向けのリスク情報SaaS である FASTALERT と連携して即時に伝える、社会派ニュースアプリです。
現在 (2021/11) 500万ダウンロードを突破しており、今後もさらにユーザーを伸ばそうとしています。

なぜリプレイスを行うのか

サービスとしては 2015年 にストアで公開されたので今日現在 (2021年12月) アプリとしてはもうすぐ8年目となります (すごい)。
iOS でいくと Swift 1 → 2 → 3 → 4 という移行も乗り越えてきました。

技術的負債という課題

7年以上運用していると、どれだけ気をつけていてもいわゆる「技術的負債」が溜まってしまいました。
時が経つにつれて技術的負債の課題は深刻なものになっていきました。
例えばある機能を改修する際、

  • 技術的負債となっている部分が解決すれば1日で終わるタスクに2日かかる
  • 簡単に終わると思っていた改修箇所が実は同じ変更内容を2箇所に適用しなければならなかった

など、よくある技術的負債による工数の肥大化が発生していました。

※ NewsDigest はさまざまな立場で多くのエンジニアの方に改修していただいた歴史があります。
技術的負債に関して否定的なことも記述はしておりますが、関わっていただいたメンバーの方に最大限の尊敬と感謝の念を込めて書いています。

爆速開発できるコードベースをつくりたい

技術的負債の側面に加え、我々が描く未来をより速く実現したいと考えています。
例えば弊社の提供する最新感染状況マップ・感染者数情報 は多くの方に価値を感じていただき、テレビ番組などでは多く特集いただきました。
このようなユーザーにとって価値の高い情報をお届けしつつ、多くの方にご利用いただくことにより、たくさんのフィードバックをいただき、さらにサービスを改善していきたいと考えています。

我々は技術的負債の解消・今後の開発速度の向上という二つの観点から Flutter によるフルリプレイスを行う という選択をしました。

Flutter を選択した背景

NewsDigest という大規模でかつユーザーの多いニュースアプリをリプレイスする技術選定としてはさまざまな可能性を模索しました。

  • 継続してSwift / Kotlin で開発
  • React Native
  • Kotlin Multi Platform
  • ...

特に React Native に関しては弊社の Frontend では React を多く使うこともあり、相性がよいのではないか、という議論もありました。

会社としても大きな投資となるため、経営層・マネージャー層で議論を進めていたところ、弊社のエンジニアから「NewsDigest をもし Flutter で書き直すならこんな感じかなと思って書いてみました」という連絡がありました。リポジトリを覗いてみるとけっこう書き進めてくれていて...というストーリーがあり、 チームメンバーからのボトムアップで Flutter という技術選択をしました。

Flutter でどうリプレイスを進めているのか

今回のテーマとしては「技術的負債の解消」という大きなテーマがあります。
今後の開発速度を爆速にするため、なるべく技術的負債が溜まりにくくするため、アーキテクチャにはこだわっています。

具体的には、まず大きな考え方として Clean Architecture の考え方を参考にしつつ Onion Architecture をベースとしたアーキテクチャを選択しています。例えば社内のドキュメントには SOLID 原則 にのっとってコードを書くように案内するようなものもあります。

また、FAT な Widget を作らないようにすることも重要です。
(私も iOS で 2000行を超える FatViewController を書いたこともあります...)
こちらは Atomic Design の考え方にのっとって Widget を設計しています。
何をもって molecules か、何をもって organisms か、というのは難しい問題ですが、考え方をすり合わせながら・ドキュメント化しながらコーディングルールを明確化していっているのが現状です。

ステート管理については Riverpod を採用しています。

今後どう進めていくのか

NewsDigest の規模のアプリをフルリプレイスするのには工数がかかります。
数ヶ月の工数をかけて出来るだけ正しい形、今後の開発速度が爆速になるよう + 負債が溜まりにくくなるような形でリプレイスを完遂しようとしています。
エンジニアだけではなく、セールスメンバーにも理解いただきつつチーム一丸となってリリース目標を立てて進めています。

正直なところ忙しいプロジェクトではあります。
一方で多くのユーザーの方にご利用いただいているニュースアプリを根本から改善する、という挑戦的な面白いプロジェクトでもあり、メンバーにはモチベーション高く挑んでいただいています。  

読んでいただいている Flutter エンジニアの方、ぜひ一度弊社のお話を聞いていただけないでしょうか?
フリーランス、正社員問わず募集しています。
こちらからご応募可能なのでぜひお気軽にご連絡ください 🙇

open.talentio.com


NewsDigest を使ってみたいという方は下記よりダウンロードください 🙇‍♂️
iPhone 版 :
app.adjust.com

Android 版: app.adjust.com

ヘビーユーザーが解説するPyTorch Lightning

こんにちは!私はファンヨンテと申します!JX通信社で機械学習エンジニアを行っております! 私はPyTorch Lightningを初めて使ったときの便利さに感動した以来、PyTorch Lightningのヘビーユーザーです! この解説記事ベビーユーザーの私が皆様にPyTorch Lightningを知っていただき、利用のきっかけになってほしいと思って公開しています!

今回の解説記事のサンプルコードはこちらにあります。ぜひ、実際のコードを手にとって体験しPyTorch Lightningの素晴らしさに触れてみてください!

この記事内容は13回のMLOps勉強会で発表しました! speakerdeck.com

読者の対象

  • PyTorch Lightningを使ってみたいが、最初の初め方がわからない人
  • PyTorch をある程度知っている方

PyTorch Vs PyTorch Lightning

PyTorch について

PyTorchはDeep Learningを実行する時に用いられる代表的なフレームワークですが、自由度が高く、個々人で自由な記述が可能です。これはメリットでもありますが、可読性が非常に低くなるリスクがあります。また、学習時のモデルや結果の保存など、サイエンスの側面とは異なるコード書くタイミングが多く、コーディングに多くの時間を取られます。したがって、PyTorchで機械学習を行うなら、データサイエンティストにはサイエンスの能力とコーディングの2つの能力が求められます。

f:id:yoooongtae:20211112121029p:plain
図1 PyTorchを用いた時に、MLエンジニアが行う業務

PyTorch Lightningについて

一方、データサイエンティストが力を存分に発揮し、これまで想像されなかった革新的なことをしたいなら、コーディングの部分よりサイエンスの領域に力を注ぐべきだと思います。

PyTorch Lightningを用いると、コーディングにかける時間を最小限にし、データサイエンティストがサイエンスの領域に全力を捧げられるようになります。

f:id:yoooongtae:20211112121449p:plain
図2 PyTorch Lightningを用いるとサイエンスの部分に注力できる

JX通信社でPyTorch Lightningを採用した理由

JX通信社のMLチームでは"力を使うべき場所に注力しよう"ということを理念を大事にしており、その工夫の一つとして学習・デプロイのテンプレートコードを作成し、運用しています。そのテンプレートはPyTorch Lightningをベースに記述されています。詳しくはこちらのブログをご参照ください。

PyTorch Lightningとは

PyTorch Lightningは"データサイエンティストはサイエンスに力を捧ぐべきで、コーディングは最小限の労力で"をコンセプトに作成された、PyTorch のラッパーです。PyTorch Lightningは後述するように、コードの書き方に指定があり、サイエンスに関わる部分(モデルのアーキテクチャ、学習方法、前処理の方法など)をメインに書きます。このコードの書き方の適度な矯正はチーム間での可読性を上げる効果もあります!また、GPU、TPUでの学習や、モデルと結果の保存など複雑なコーディンに関わる部分は数行書き足すだけで、実行することができるようになっています。

f:id:yoooongtae:20211112121356p:plain
図3PyTorch LightningのHPより引用

PyTorch Lightningの柔軟性について

この様に聞くと、柔軟性が欠けており、できない実験があるのでは?と感じる方もいると思います。しかし、柔軟性を最大化することがPyTorch Lightningの哲学として紹介されており、ほとんどすべての実験を再現することが可能です。

f:id:yoooongtae:20211112121738p:plain
図4Lightning Design Philosophy Githubより引用

またPyTorch Lightningをずっと使ってきたの私個人的な意見としては、こちらのブログにも記述されている通り、業務においてPyTorch Lightningの記述で困ったことはほとんどなく、メリットのみを享受しています。(しかし、半教師あり学習をPyTorch Lightningで記述しようとした際、Optimizerの記述に沼ってしまい、余計な時間と労力を費やしたことが一度あります。)

PyTorch Lightning の書き方

サイエンスに関わる部分

PyTorch Lightningに書き方のフレームワークがあり、そこを埋めていくように書くと記述しました。このページでは、PyTorch Lightningの書き方を、PyTorch との差分という意識で記述させていただきます。このムービーは非常にわかりやすいムービーですので、こちらのムービーをご覧になっていただいてから、下の章を読んでみてください。 Google Colabで動作するサンプルコードはこちらにあります。ブログの内容と全てが一対一対応してませんが、実際ご自身の手で動かしてみてください!

PyTorch LightningはDataに関わる部分(DatasetやDataloader等)と学習に関わる部分(モデルの構造、学習ループの書き方)に大きく区分されます。

Dataに関する部分(LightningDataModule)

PyTorch では学習に用いるデータはtorch.utils.dataDataLoaderDatasetを用いて記述されます。PyTorch Ligthningでは、LightningDataModuleを継承したクラスを作成し、その中のtrain_dataloader()と名のついた関数にtrainに用いるDataloaderをreturnする必要があります。validation, testも同様です(図5)。ここで注意すべきなのは関数名は変更してはなりません。この様に関数名が完全に決まっているので、他のチームメンバーがコードを読むときにどのデータがどこに利用されているのかわかりやすくなります。 LightningDataModuleには、このページで紹介した関数以外にも多くの関数が定義されています。LightningDataModuleについての詳細は公式のドキュメントをご覧ください.

f:id:yoooongtae:20211115112938p:plain
図5LightningDataModuleについて

学習に関する部分(LightningModule)

PyTorch では、torch.nnのmoduleを継承したクラスでモデルの構造を決定し、その後、学習ループをtrain.pyなどにベタがきすることが多いと思います。PyTorch Lightningでは、モデルの構造と学習ループでの計算挙動をLightningModuleで定義します。

図6はLightningModuleに必要な最低限のコードを示しています。まず、initでモデルのパーツを定義し、forwordでモデルの構造(計算の流れ)を定義します(ここまではPyTorchと同じ)。

次にPyTorch Lightningでは、学習ループの各ステップでどの様に計算してほしいかを記述します。図6では、training_step()という関数が定義されていますが、この関数は学習ループのミニバッチが与えられたときに、どの様に計算するのか記述してます。ここで、return のdict中の"loss"を鍵とする値を基にLightningの方で逆伝播や最適化が行われます。このときに用いられるOptimizerやschedulerはconfigure_optimizers()に定義する必要があります。

図6では、training_step(学習のミニバッチに対する処理)だけが書かれてますが、training_epoch_endvalidation_step, validation_epoch_end, on_fit_startなど、学習ループの各ステップで行ってほしいことの記述が可能になっています。

また、PyTorchでは学習ループの記述の際、データやモデルをCPUやGPUに移動する必要があり、.to(device)が至る所に記述する必要があると思います。PyTorch Lightningを用いているとto(device)の記述しなくても、Lightningが自動で学習に用いるデバイスにデータを移動してくれます。LightningModuleについての他の関数を含む詳細はこちらを御覧ください。

f:id:yoooongtae:20211115113357p:plain
図6 LightningModuleについて

学習の初め方

上で定義した、LightningDataModuleLightningModuleを用いて学習するには、Trainerを定義し、trainer.fitで学習されます(図7)。

f:id:yoooongtae:20211115113500p:plain
図7Trainerで学習が開始される。右の図は実際のコード下の図は学習が開始されたときのコンソールの表示

サイエンスに関わるコードの書き方まとめ

このページではサイエンスに関わるコードの記述法について記述してきました。PyTorch Lightningを利用していると、上記で解説したサイエンス部分の実装に費やす時間が多くなります! 一方、ここまで読まれた方は以下の疑問を思ったと思います。

  • 結果の表示や保存はどうするの?
  • モデルの保存方法は?
  • 学習のデバイスをどの様に指定するのか? こちらは、Lightningを用いていると、数行の追加で終了します。これがLighthningの強みだと思います。詳細は次章以降のページにて解説します!

サイエンス以外の部分のコードの書き方(結果の表示と保存について)

前章ではPytorch Lightningのサイエンスに関わる部分のコードの記述方について解説しました。

ここでは、結果の表示法と保存法について記述します。

前回の解説で、Lightningでは学習ループの段階ごとの計算挙動を記述すると記述しました。このときに見たい/保存したい結果があるとき、self.log()に見たいメトリクスを入れると、結果が記録されます (図8)。それぞれの引数の意味は以下のとおりです。

self.log(
        "test_loss", #metiricsの名前
        loss,#metiricsの値
        prog_bar=True,#プログレスバーに表示するか?
        logger=True,#結果を保存するのか?
        on_epoch=True,#1epoch中の結果を累積した値を利用するのか?
        on_step=True,#1stepの結果を利用するのか?
        )

f:id:yoooongtae:20211115123647p:plain
図8LightningModuleでのログの取り方について

TorchMetricsの利用

TorchMetricsのライブラリを利用すると結果を非常に簡単に扱うことができます。TorchMetricsを用いることで、AccuracyやRecallなど一般的な指標が簡潔に書けるだけでなく、MetiricsCollectionを用いると複数の指標を一度に定義し(図9左)、計算することができます(図9右)。

f:id:yoooongtae:20211115123725p:plain
図9TorchMetricsに利用法

結果の表示

self.log()のprog_bar = Trueに設定したMetricsはプログレスバーに表示されます (図10)。

f:id:yoooongtae:20211115123816p:plain
図10 self.logを入れたMetricsがプログレスバーの下に結果が表示される

結果の保存方法

self.log()のlogger = TrueにしたMetricsの保存はどの様になるのでしょうか?

LightningのTrainerを定義するときにloggerを定義すると、その形式にあったログが自動で保存されます。このloggerにはTensorBoardだけでなく、MLFlow, Cometなど有名な実験管理ライブラリに対応していますので、自身の好きなライブラリを用いることができます。

f:id:yoooongtae:20211115123901p:plain
図11loggerについて。loggerにTensorBoardを定義すると(左上)、自動で結果がTensorBoardの形式で保存される(右)。

JX通信社ではGoogle App EngineにデプロイされたMLFlowに結果が保存される様にしています。詳細はこちらを御覧ください。

サイエンス以外の部分のコードの書き方(callback : モデルの保存とearly stopping)

前章は結果の表示と保存方法について解説しました。 このページではモデルの保存方法とEarly stoppingの方法について説明します。 そのためにはLightningのCallbackと呼ばれる機能について理解する必要があるため、先にcallbackについての解説を行った後にモデルの保存方法とEarly stoppingの方法について説明します。

callbackについて

PyTorch Lightning にはCallbackというmoduleがあります。Callbackを用いると、LightningModuleで定義した学習ループの計算挙動に新たな動作を差し込むことができます。 例として、図12の左の図の様に定義したcallbacksをtrainerに入れると、ミニバッチ学習が始めに"train_batch is starting"、終わりに"train_batch is completed"と表示されるようになります。 ですので、学習で普遍的に用いられる便利系のコードをcallbacksで定義しておくことで、LightningModule内の学習ループの定義は簡潔になります。

f:id:yoooongtae:20211115124839p:plain
図12 callbackとは

JX通信社でのcallbackの利用

JX通信社ではWebHookを通じて学習の進捗状況をSlackに知らせるcallbacksを作成し運用しています。詳しくはこちらを御覧ください!

f:id:yoooongtae:20211115124921p:plain
図13JX通信社でのcallbacksの利用。(https://tech.jxpress.net/entry/2021/10/27/160154より引用)

モデルの保存とearly stopping

Lightningから提供されるBuilt-in callbackを利用することもできます。

ここにはModelCheckpoint, EarlyStoppingなどの便利なcallbackがあります。

ModelCheckpointは指定した指標が最も高い/低いモデルを保存することができるcallbackです。このcallbackの素晴らしいポイントは監視対象を選択し、その値が最大/ 最小のときのモデルを保存するといったベストモデルを保存することができます(図3の場合、valid_lossが最小になるモデルが保存される)。 EarlyStoppingは文字通りearly stoppingのためのcallbackであり、監視対象の値が改良しない場合、学習がストップしてくれます。この2つのcallbacksをTrainerの引数に入れ込むだけで、モデルの保存とearly stoppingを簡単に実装することが可能になります。

f:id:yoooongtae:20211115125127p:plain
図14Lightningが提供するカスタムBuilt in callback(ModelCheckpoint, EarlyStopping)

その他、Built in callbackについては公式のドキュメントをご覧ください。

サイエンス以外の部分のコードの書き方(GPU /TPU /IPUでの学習)

GPU, TPUで学習速度を上げる

Deep learningのモデルはcpuで学習させるには非常に時間がかかります。一方GPU, TPUを用いると、学習期間が数倍~数十倍程度加速されるため、GPU, TPUで学習することが一般的だと考えられます (図15)。 一方PyTorchを用いてGPU, TPUで学習させることを考えると、至る所に.to(divice)を書く必要があり、可読性が下がるだけでなく、不必要なエラーが発生する原因になります。

f:id:yoooongtae:20211115184604p:plain
図15GPU, TPUの利用は学習速度を加速させる。このブログより引用

LightningでのGPU, TPUで利用法

LightningModuleではto(device)のコードを一切書くことなく学習ループを実装します。では、Lightningではどの様にGPU/TPU/IPUで学習することができるのでしょうか? 答えは非常に簡単で、Trainerのgpus/tpu_cores/ipusで利用するGPU/TPU/IPUを指定すると指定したデバイスで学習が開始されます (図16)。

ここで、例えば、gpusの引数にlist型を入れると、学習に用いるGPUのデバイスを詳細に指定することができます。一つの要素しか無いlistを入れるとGPU1台での学習になりますが、複数の要素のlistを入れる、または2以上のintを入れると複数GPUでの並列学習が可能になります。より詳細は公式のドキュメントを御覧ください。

f:id:yoooongtae:20211115182808p:plain
図16学習するデバイスを変更するためにはTrainerの引数を指定するだけで良い

ColabでのTPU利用について

google colabでTPUの利用したい場合は、以下のコマンドを打ってxlaをインストールする必要があります。

!pip install cloud-tpu-client==0.10 https://storage.googleapis.com/tpu-pytorch/wheels/torch_xla-1.9-cp37-cp37m-linux_x86_64.whl

(ここで、torch_xla-1.9の1.9は利用しているTorch のVersionを指定してください)

その後Tranerのtpu_coresの引数を指定することで、TPUでの学習が開始されます。

PyTorch Lightningより上位のラッパー (PyTorch Lightning Flash)

これまで紹介してきたとおりPyTorch LightningはPyTorchのラッパーでコーディングにかける時間を最小限にし、サイエンスにかける時間を最大化してくれました。

ここではPyTorch Lightningのより高位ラッパーであるPyTorch Lightning Flashについてご紹介させていただきます!

PyTorch Lightning Flashは高レベルのAIフレームワークであり、このライブラリを用いることで、図17のような典型的なタスクであれば、十数行のコードで解決することができます。また、PyTorch Lightningのラッパーであるため、Pytorch Lightningで利用できた機能(callbackや, loggingなど)をそのまま利用することができます。

サンプルコードの前半にPyTorch Lightningで行った画像分類タスクを後半のPyTorch Lightning Flashで実装しています!興味がある方はこちらも触ってみてください。

Lightning Flashができるタスクについてはこちらを御覧ください。

f:id:yoooongtae:20211115184143p:plain
図17Lightning Flashから引用

PyTorch Lightning Flashの個人的な利用方法

PyTorch Lightning Flashは数行のコードで機械学習タスクが完結する反面、柔軟性に乏しく、タスク目的に合わせた工夫が困難です。

したがって、個人的にはPyTorch Lightning Flashでベースラインを構築し、精度が十分ならそのまま利用します。一方、タスクに合わせた工夫が求められたときにはPyTorch Lightningのテンプレートコードを基に実験を行います。PyTorchの利用は最終手段だと思っていて、Lightning単体で実装できない最先端の工夫をしたい時に利用すると思います!

f:id:yoooongtae:20211115190731p:plain
図18 Pytorch のラッパーの個人的な利用方法

我々とともに挑戦する仲間を求めています

我々とともに成長しながら、より良い社会のためのMLを開発したい仲間を社員・インターン問わず積極的に募集しています!また、MLエンジニアはもちろん、あらゆる職種のエンジニアを求めています!

正社員、インターン、おためし入社などなど!ほんの少しでも興味を持たれた方はこちらを覗いてみてください!

日本でPyTorch Lightningを盛り上げていきたい!

個人的に、日本でPyTorch Lightningを盛り上げていきたい!と思っています。

PyTorch Lightningをこれまで使っていた方、これから使いたいと思っている方、質問がある方!積極的に私のtwitter, またはFacebookにご連絡ください!

JX通信社は Go Conference 2021 Autumn に協賛します!【11/13(土)開催】

こんにちは! CDO(開発担当役員)の小笠原(@yamitzky)です。

Python のイメージが強い(?)JX 通信社ですが、実は Go に関してもかなり積極的に利用しています。Go のコミュニティ発展を目的として、Go Conference 2021 Autumn にブロンズプランでの協賛を行っています。

Go Conference 2021 Autumn は、今週土曜日 2021/11/13(土) 10:00 〜 20:00 で開催 されますので、ぜひ奮ってご参加ください(参加費無料)。

gocon.connpass.com

JX 通信社と Go

AI リスク情報SaaS「FASTALERT」や、ニュース速報アプリ「NewsDigest」のバックエンドは Python や Go で開発されています。

機能開発だけでなく、VaultTerraform など、Go で開発された OSS も活用しています。

また、僕自身も、今年の ISUCON は同僚と Go で参戦し、本選に進むことができました 💪

NewsDigest での使い方

NewsDigest の API は gqlgen という Go 製の GraphQL ライブラリを使って開発されています。

NewsDigest は速報の強いニュースアプリです。地震速報などの災害や、重大なニュース速報などは事前に予測することができないため、予期できないトラフィックのバーストを捌く必要があります。gqlgen は他言語を含めた GraphQL ライブラリの中でも高速なので、ベンチマーク検証の上、技術選定しました。

参考) Go の GraphQL API のパフォーマンス改善のために分散トレーシングを導入した話 - JX通信社エンジニアブログ

FASTALERT での使い方

FASTALERT のバックエンドでは、API に限らず、バッチ処理、イベント駆動型の処理など、幅広い箇所で Go が活用されています。

JX 通信社では、機能開発を行う際、マイクロサービス的にシステム分割をしながら開発しています。元々は Python によるシステム(マイクロサービス)が多かったのですが、新規システムの開発時に積極的に Go を選定したり、既存システムの置き換えなども行っています。

参考) Goの並行処理について - JX通信社エンジニアブログ

採用情報

JX 通信社では、FASTALERT や NewsDigest を進化させていくバックエンドエンジニアを募集しています! Python が書けない方でも大丈夫です。

もし少しでも「話を聞いてみたい」という方がいたら、小笠原まで Twitter で DM いただくか、Wantedlyコーポレートサイトなどでお声がけください。

open.talentio.com

それでは、明日は Go Conference 2021 Autumn を楽しみましょうー!

属人化しがちなR&Dをチーム開発するためのJX通信社での工夫

こんにちは!JX通信社でMLエンジニアのファンヨンテです。私は自分だけでなくMLチームの成果を最大化するために日々全力を尽くしています!

JX通信社のMLチームでは人的リソースを最大限活用するため "力を使うべき所にのみ注力しよう!" をスローガンに徹底的に効率化しています。 今回はちゅらでーた様と弊社の共同勉強会で私が発表した内容をより掘り下げてお伝えできればと思います。

本内容については

ちゅらデータさんとの共同勉強会にて発表しております!

speakerdeck.com

動画を見たい方はこちら

を御覧ください〜

R&Dタスクの属人化について

f:id:yoooongtae:20211015004452j:plain
図1 アプリ開発におけるチーム開発(左)とR&Dチームで発生しがちな属人化した、タスクの進め方

弊社はNewsDigestを始めとしたアプリを開発しており、アプリ開発の場ではチームの皆が一丸となり、アプリ開発という一つの目標に向かって協力し合いながら進めていきます。(図1左)

一方、R&Dのタスクでは、1タスクを一人が担当しているといったケースが多いのではないでしょうか?(図1右)

f:id:yoooongtae:20211015004718p:plain
図2 R&Dチームが属人化する理由と問題点

R&Dタスクが属人化してしまう理由は様々あると思いますが、その問題点はメンバー間の協力が非常に難しいことにあります。

それゆえに、だれかと同じ苦労を違う人が繰り返す。。。といったことが頻発してしまいます。これでは、 "力を使うべき所にのみ注力しよう!"という我々の信念からずれてしまいます。

個人の能力をフルに発揮しながらも、チームの協力を最大化し無駄な所に時間を割かないための我々JX通信社での工夫を紹介します。

R&Dをチームで行うためのJX通信社の工夫

コードのテンプレート化

f:id:yoooongtae:20211015004824j:plain

f:id:yoooongtae:20211015004829j:plain
図3 コードのテンプレート化について

R&Dのチーム内で協力が生まれにくい大きな原因は、他メンバーのコードを読み理解することが面倒なことだと思います。個々のメンバーが規則なく、自由にコーディングを行うとこの面倒さが発生します。この状況を回避するためにJX通信社では、機械学習モデルの学習時とデプロイ時にそれぞれ利用できるテンプレートを開発し利用しています。

テンプレートを作成した目的は以下のとおりです。

  1. コードの書き方に適度な矯正をすることで、可読性を増やすこと
  2. Poetryを用いて学習環境を管理することで、学習を引き継ぐ時などに学習環境を気にしなくても良くなること
  3. 後述するMLflow等の便利系のTipsを予め仕込むことで、初学者でも"いい感じに"学習できること

テンプレートには我々の信念である "力を使うべき所にのみ注力しよう!"と同じコンセプトで作られたPytorch Lightningを用いて開発されています。(私がPytorch Lightningヘビーユーザーであり2021年11月17日のMLOps勉強会にてPytorch Lightningについて発表します。もしよろしければ、来てください!)

このテンプレートコードはJXの社員・インターンにより随時アップデートされており、誰かが一度体験した苦労を他メンバーが体験しないようにしています。

MLflow + App Engineを用いた実験の一元管理

f:id:yoooongtae:20211015005604j:plain
図4 JX通信社におけるmlflowの使い方

実験した結果を実験者本人しか知らない状況を作ってしまうと、実験の引き継いだ後に同じ実験をしてしまったり、実験の工夫が結果に及ぼす影響を定量的に共有ができなかったりと、"力を使うべき所にのみ注力しよう!"ができません! そこでJX通信社では皆が行った実験結果を1元管理できる仕組みを作成しました。Pytorch Lightningを用いたテンプレートを用いて学習しているので、実験管理は数行のコードの追加で行うことができます。弊社では実験管理のOSSとして有名なMLflowを用いて行っています。

App EngineにMLflowサーバーを構築しており、学習時に結果とモデルがアップロードされるようテンプレートコードに組み込んであります。したがって、テンプレートコードを利用すると、(実験者は意識しなくても、google colabを含む)どのサーバーで学習しても学習状況とモデルがGCSにアップロードされ、JX通信社のすべてのメンバーがすべての実験内容を把握できるようになっています。

学習状況がSlackに通知されるシステム

f:id:yoooongtae:20211015004216j:plain
図5 Slackに学習状況が通知されるシステムについて

学習の引き継ぎや、"モデルA →モデルB"のようなパイプライン状のモデルの学習を複数メンバーで分担して行う場合、他メンバーの学習の進捗状況を知りたい時が頻繁にあります。直接、進捗状況をメンバーに聞くのもありですが、そのメンバーの集中を妨げることになります。

そこでJX通信社では学習の進捗情報と精度などの結果をSlackに自動で投稿してくれる昨日を開発しテンプレートに仕込んでます。

したがって、JXの皆がテンプレートを利用し学習してくれると、自動でSlackに学習状況が共有されることになります!

まとめ

今回の記事では、MLチームにおいて"力を使うべき所にのみ注力しよう!"を達成するにチームメンバーが互いに協力しあえる環境・システム構築についてお話しました。

"力を使うべき所にのみ注力しよう!"を達成するためにはシステムで解決できる部分と解決できない部分があると思います。今回は、システムで解決できると思った部分にフォーカスしてお話しましたが、システムで解決できない部分(価値のAIをそもそも作っているのか?メンバー間のコミュニケーションはうまく取れているのか?等)についても様々な工夫をとっており、これについてもいつか記事を書ければと思ってます!

JX通信社のMLチームは、ML以外のチームとも連携し合いながら、これからもどんどん成長していきたいと思ってます!

我々とともに挑戦する仲間を求めています

我々とともに成長しながら、より良い社会のためのMLを開発したい仲間を社員・インターン問わず積極的に募集しています!また、MLエンジニアはもちろん、あらゆる職種のエンジニアを求めています!

正社員、インターン、おためし入社などなど!ほんの少しでも興味を持たれた方はこちらを覗いてみてください!

PyCon JP 2021 でのミニゲームをGoogle Cloud RunとFirebase Hostingで作った話

JX通信社はPyCon JP 2021のゴールドスポンサーとしてスポンサーさせていただきました。 今回弊社平瀬さんがLocastの内容で登壇されました!内容はこちらからご覧ください。

speakerdeck.com

その際、会社のブースではPythonを使ったミニゲームを実施していました。

PyCon JP 2021について

f:id:jx_k_watanabe:20211019211553p:plain

PythonのカンファレンスであるPyCon JP 2021はオンラインとオフラインでのハイブリッド開催となりました。スポンサーブースはオンラインのみで、Discordのチャンネルに参加者の方に入っていただき、そこで会社の紹介や、参加者の方と交流するという流れになっていました。画像はブースで待機している図です。

f:id:jx_k_watanabe:20211019210537p:plain:w300

(JX通信社のブースです)

その中でJX通信社ではミニゲームを開催し、来場者の方にゲームを遊んでいただき、抽選で景品を差し上げることにしました。 オンラインブースに来てもらうためのコンテンツを用意すると、当日来てくれた方と話すきっかけになったり、宣伝もしやすかったので用意して良かったです。

f:id:jx_k_watanabe:20211019210616p:plain:w400

ゲームの内容

ゲームを作るに当たり、UI部分まで作るのは大変そうだったので、小規模に、かつPythonを使ってゲームを楽しめるようにできないか考えた結果、お客さんはエンジニアなのでターミナルからアクセスしてもらう形式にしました。

Responseはすべてテキストで返却して、回答者に答えをPostしていただく形式になっています。

f:id:jx_k_watanabe:20211019224510p:plain

(ちなみにResponseをapplication/jsonではなくtext/plainにすることでターミナル上で改行がきちんと反映されるというハックがあります)

また、回答していただけた方にアンケートを実施していたのですが、回答が全て好評でよかったです。

f:id:jx_k_watanabe:20211019223735p:plain:w500

要件

今回設定したのは以下の要件でした。

  • APIだけで完結させる
  • お客さんのHTTPClientで答えをPostしてもらう

システム構成

f:id:jx_k_watanabe:20211019210706p:plain

ソースコードはこちらで公開しています。

FastAPI(ゲーム部分)

API部分はFastAPIで作成しました。テキスト・問題・解答はyamlファイルに入れておき、ゲームの進行度に応じて適切な内容を返すようにしたので、ロジック部分はほぼありません。

f:id:jx_k_watanabe:20211019210729p:plain:w300

FastAPIは基本JSONレスポンスなので、文章を出す際に改行コードが出てしまい、若干見づらかったためテキスト形式でレスポンスを返すようにしました。

工夫した点1: Cloud Runからのレスポンスタイムが遅い

Cloud Runは、通常コールドスタートといって、リクエストが来ない間はインスタンスを立ち上げない状態のままにしておき料金を抑えてくれるという特徴があります。しかし、リクエストが来る最初のレスポンスは時間がかかってしまう問題があり、コールドスタートのままだとリクエストから5秒以上かかってしまうこともありました。ゲームは(一般的なAPIエンドポイントと比較すると)リクエスト数が少なくなるため、ユーザーがアクセスするときに時間がかかってしまうことは避けたかったので、Cloud Runのオプションで最低1つのインスタンスを立ち上げておくようにしました。

このようにデプロイ時にプロパティを設定することで、最低インスタンス数を指定することができます。

gcloud run deploy game --source . --min-instances 1

工夫した点2: ドメイン名を読みやすいようにしたい

当初はCloud Runが自動生成していたURLを利用していたのですが、実際にゲームを参加していただく方に分かりづらいドメイン名になってしまっていたため(https://game-mbqu6va7zq-an.a.run.app/ のようなドメインが生成されます)、ある程度読みやすいドメインがほしいなと思いました。

そこで、Firebase hostingを使って、ゲームに必要なドメインを作成することにしました。デフォルトだと、プロジェクト名そのままのドメインが生成されてしまうため、カスタムドメインを作成します。 Firebaseの画面から新規でウェブのアプリを作成します。その際に、Firebase Hostingの設定もできるため、ここで追加します。

f:id:jx_k_watanabe:20211019211204p:plain

その後、チュートリアルに従ってFirebaseの設定をします。加えて、Firebase HostingからCloud Runに送信するための設定を記述します。以下すべてのトラフィックをCloud Runに送信するための設定です。また、生成されたindex.htmlは削除しましょう。

"hosting": {
  // ...
  "rewrites": [ {
    "source": "**",
    "run": {
      "serviceId": "game", // ここにはCloud Runのプロジェクト名を指定します
      "region": "asia-northeast1"
    }
  } ]
}

おまけ

今回PyConJPの開催期間2日稼働させた結果、150円前後で抑えることができました。(Cloud Runの最低インスタンスを抑えることでもう少し安くできたかもしれないですが)

f:id:jx_k_watanabe:20211019211252p:plain:w500

まとめ

今回もスポンサーとして参加できてよかったです。

JX通信社はPythonistaを募集しております!

open.talentio.com