TECH PLAY

KINTOテクノロジーズ

KINTOテクノロジーズ の技術ブログ

936

こんにちは。分析グループ(分析G)でMLOps/データエンジニアしてます伊ヶ崎( @_ikki02 )です。 こちらは「KINTOテクノロジーズ株式会社にてどのようにMLOpsを適用していくのか」というテーマでの連載3本目です。1本目の記事「 KINTOテクノロジーズのMLOpsを定義してみた 」および2本目の記事「 SageMakerを使った学習&推論のバッチパターン 」はそれぞれのリンクよりご確認ください。次回最終回となる記事では、他部署も巻き込んで開催した勉強会のお話を予定しています。 背景(Situation) 連載1本目にて「メタデータ管理(実験管理)」がなぜ必要なのか一般的な背景を記載しました。この記事ではKINTOテクノロジーズでの状況をより深掘りして見ていきたいと思います。 KINTOという会社およびサービスがトヨタグループで立上がってから4年目というまだまだ若いこともあって、ビジネスサイドとデータサイエンスサイドの関係性構築が求められていました。立上げ期特有のスピード感においては、コードの品質や文書化などの管理コストを多少犠牲にしてでも、迅速なアウトプットが重要であり、価値検証に比重が置かれます。 サービスの立上げ期においてこのアプローチは悪くないですが、順調にサービスも伸びてきて人も増えてくると、今度は管理コストの重要性が相対的に増してきます。特にデータサイエンスという分野は、組織的に管理するための方法論がソフトウェア開発のそれに比べるとまだまだ成熟していないと個人的に感じており、ブラックボックスになりがちです。そのため、データサイエンスプロジェクトの管理について方法論を模索し始めたフェーズでもありました。 業務(Task) データサイエンスプロジェクトの管理について、「メタデータ管理(実験管理)」という概念があります。以降、この記事では簡便のため、実験管理という表現で統一しようと思います。 実験管理の重要なポイントは、「シェアビリティ」と「記録の容易さ」「再現性」だと考えています。分析グループにはデータサイエンティストが4名在籍しており(2022/9時点)、各々のプロジェクトもあれば、複数人で共通の大型プロジェクトもあり、情報の共有に際しては共通のフォーマットがあると便利です。理想形は、データサイエンティストの誰がやってもモデル構築のプロセスと結果を再現できるよう、実行環境の情報や利用した具体的なデータ、コードスクリプト、機械学習のアルゴリズム、ハイパーパラメータなどの情報を記録しておくことです。しかし、「シェアビリティ」と「記録の容易さ」は実際にはトレードオフの関係にあり、他の人が結果を再現できるよう情報を記録すればするほど、記録の負荷はあがると言えます。そのため、記録の負荷をなるべく上げないためにも、繰返し記録することになる情報(例えば、実行環境のコンテナイメージ、処理開始&終了時間、データの置き場所、など)は一度きりの記述で自動的に連携されるような仕組みがあると望ましいです。 SageMaker Experimentsを用いれば、パイプラインの定義スクリプトにコードを一度書けば、その情報が自動的に記録されるので、データサイエンティストは記録したい実験に関する指標のみに集中すればよいことになります。また、任意で記録したい指標のコーディングについては、SageMaker SDKを使えば以下のように数行レベルで済むので、記録について最低限の負荷で済むと言えそうです。 やったこと(Action) 命名規則の整理 コンセプト的にマッチしているマネージドサービスを導入していくのは良いアプローチだと思いますが、それだけでは十分に活用できません。SageMaker ExperimentsはExperimentとTrialという小概念があります。 概念 簡単な説明 Experiment Trialを一定の粒度で集めた概念。分析PJや特定のプロダクトなど、何か特定の事例を表すことが多い。 Trial 機械学習プロセスの個別の処理。 概念が細分化されているため、それぞれの命名規則や使い方をうまく整理しなければ、各データサイエンティストがばらばらの使い方をしてしまい、情報のシェアビリティが落ちてしまいます。そこで、以下のように命名規則を整理してみました。 実験管理にあたって、まず用途を考え、大きく2種類の用途を想定しました。「パイプラインの実験管理」および「EDAの実験管理(※)」です。 ※ EDAは「探索的データ分析」のこと。 「パイプラインの実験管理」では、本番稼働しているパイプラインの情報を記録し、失敗した時など何らかの理由でパイプラインの結果を再現する必要があるときにそのプロセスを再現できるようにします。上図の「trialの命名」欄にある命名規則に従った①のTrialを見れば、実行環境に関する情報について、SageMaker Pipelinesが自動で作ってくれた情報を確認することができ、②の命名規則に沿ったTrialを見れば、データサイエンティストが任意で記録した各種指標を確認することができます。 一方で、本番稼働のパイプラインとは別で、モデルをより良くするために実験を都度実施し、その結果も記録して後で参照できるようにしたいというケースもあります。「EDAの実験管理」ではまさにそういったニーズに対応できるように、命名規則をあえて緩く設定しており、「debug-」とついたExperimentに関しては、Trial名はデータサイエンティストの実験の意図に応じて自由に決められるようにしています(実験パターンがある程度存在するようならそれに応じて設定を改めて検討するかもしれません)。 結果(Result) 上記の命名規則も併せてSageMaker Experimentsを使うことで実験管理を行うだけでなく、実施した実験(および本番稼働のパイプライン)の結果の分析もできます。過去のパイプラインの分析に関する指標をサクッと確認したいときは②の命名規則に沿ったTrialをGUIで確認できますが、本番稼働しているパイプラインの結果の推移を追いたいときなど、複数の実験結果を比較して分析したいときは、SageMaker Experimentsの結果をpandasのDataFrameにして分析したりすることができます。例えば、日次実行しているパイプラインの適合率について、折れ線グラフ上で推移を表示して確認したりできます。結果の可視化を行うことで、本番稼働しているパイプラインがモデルドリフトを起こしていないか確認したりできるでしょう。運用を通じてフィードバックを迅速に得ることで、モデルの再開発や微調整、継続的学習といったプロセスを素早く開始することも期待できます。 いかがでしたでしょうか。最終回となる次回の連載では、社内で実施した勉強会のお話を通じて、どのように知識を共有しているか、ご紹介したいと思います。引続きご覧いただける際は「 SageMaker勉強会と文化醸成(4/4) よりご覧ください。また、Tech Blogのツイッターもやっているので、本ページ最下部からフォローいただけると嬉しいです。
アバター
Introduction Hello. My name is Shiode, and I'm in charge of payment-related back-end development at the Woven Payment Solution Group. Our group uses Kotlin for development, and we use Ktor as the web framework. Ktor requires a separate JSON library to process JSON request bodies and return JSON responses. At the beginning, we used Gson as a JSON library, but we encountered problems using Gson, so we switched to Moshi. Today, I'm going to describe why we chose Moshi, the problems we faced when we used Moshi with Ktor, and how we solved them. The version of Ktor used in this article is the 2.x series. Switching from Gson to Moshi Issues with Gson Initially, we were using Gson as a Ktor JSON library. However, when receiving request bodies, we discovered that problems would occur if a necessary key was missing. For example, think about an endpoint that processes input as follows. data class Input ( val required: String, val optional: String? = null, ) post("/") { val input = call.receive<Input>() call.respondText("required was ${input.required}", status = HttpStatusCode.OK) } Since String type is required for the Input class, null is not permitted. However, null is permitted with optional . This endpoint is a simple one that receives the above Input as a request body and returns the required of the received body. With this endpoint, a NullPointerException (commonly known as Nullpo) will occur if you send an empty JSON {} that does not have a required . It would be preferable for an exception to occur during deserialization so as not to affect later processing, but an exception ends up occurring when accessing that variable, in this case call.respondText() . As a solution, there may be a way to make the Input class required a nullable type and perform a null check. However, since we're using Kotlin, we wanted to express it as a type where nulls are not allowed as far as possible, and write processing that is suitable for Kotlin. Therefore, it became necessary to select a JSON library that would throw an exception during deserialization. Why Did We Choose Moshi? This project is developed with a schema base that uses OpenAPI, and the generation of interacting classes depends on the OpenAPI Generator. Also, since the client also uses the one generated by the OpenAPI Generator, a JSON library supported by OpenAPI was brought up as a replacement candidate. The JSON libraries supported by OpenAPI Generator are shown below. Gson is excluded. Moshi Jackson kotlinx.serialization Jackson can support null safe by enabling Option, but when we looked at the relevant source code , performance was described as being affected. We decided to eliminate it as a candidate without comparing performance. Meanwhile, kotlinx.serialization is a library developed by JetBrains, and the fact that it's native to Kotlin and compatible with Ktor made it is a good candidate. However, Moshi has twice as many stars as kotlinx.serialization (at time of writing, Moshi: 8.5K, kotlinx.serialization: 4.1K), and Moshi is Gson v3 , so we decided to adopt Moshi. Problems Using Moshi with Ktor According to Ktor's PR#2004 , there is no Ktor support for Moshi at the time of writing. Also, according to PR#2005 , there seems to be no policy to provide Moshi as a core feature of Ktor in the future, either. On the other hand, it seems that Ktor is planning to provide a Marketplace , so there is a possibility that in the future a third party will provide a Ktor plugin that will allow Moshi to be used. But there is no support for it right now, so you'll have to make your own Ktor plugin to be able to use Moshi. Note that there is no information yet about the Marketplace at the time of writing. We're looking forward to hearing more about it. Solution Create a Ktor Plug-in to Enable Use of Moshi The following are the classes required for using Moshi with Ktor. This implementation is almost the same as GsonConverter for Ktor. class MoshiConverter( private val moshi: Moshi = Moshi::Builder().build(), ) : ContentConverter { override suspend fun serialize( contentType: ContentType, charset: Charset, typeInfo: TypeInfo, value: Any ): OutgoingContent? { return TextContent( moshi.adapter(value.javaClass).toJson(value), contentType.withCharsetIfNeeded(charset) ) } override suspend fun deserialize(charset: Charset, typeInfo: TypeInfo, content: ByteReadChannel): Any? { return withContext(Dispatchers.IO) { val body = content.toInputStream().reader(charset).buffered().use { it.readText() } moshi.adapter(typeInfo.type.java).fromJson(body) } } } It is used as shown below. install(ContentNegotiation) { register(ContentType.Application.Json, MoshiConverter()) } Compatibility with Moshi ArrayList When we use the above Ktor plugin, it deserializes as a JAVA type instead of a Kotlin type when deserializing. For example, it will deserialize as an ArrayList even though you want to store it in a List . Moshi doesn't support ArrayList deserialization by default, so an exception will be thrown when you try to deserialize a List . To support any type in Moshi, you need to prepare an Adapter for that type. Since we want to support ArrayList this time, we must prepare a Moshi adapter for ArrayList ourselves. The following adapters support ArrayList . This is almost identical to Moshi's CollectionJsonAdapter implementation. abstract class ArrayListJsonAdapter<C : MutableCollection<T?>, T> private constructor( private val elementAdapter: JsonAdapter<T> ) : JsonAdapter<C>() { abstract fun newCollection(): C override fun fromJson(reader: JsonReader): C { val result = newCollection() reader.beginArray() while (reader.hasNext()) { result.add(elementAdapter.fromJson(reader)!!) } reader.endArray() return result } override fun toJson(writer: JsonWriter, value: C?) { writer.beginArray() for (element in value!!) { elementAdapter.toJson(writer, element) } writer.endArray() } override fun toString(): String { return "$elementAdapter.collection()" } companion object Factory : JsonAdapter.Factory { override fun create(type: Type, annotations: Set<Annotation>, moshi: Moshi): JsonAdapter<*>? { if (annotations.isNotEmpty()) return null return when (type.rawType) { ArrayList::class.java -> { newArrayListAdapter<Any>(type, moshi).nullSafe() } Set::class.java -> { newLinkedHashSetAdapter<Any>(type, moshi).nullSafe() } else -> null } } private fun <T> newArrayListAdapter(type: Type, moshi: Moshi): JsonAdapter<MutableCollection<T?>> { val elementType = Types.collectionElementType(type, Collection::class.java) val elementAdapter = moshi.adapter<T>(elementType) return object : ArrayListJsonAdapter<MutableCollection<T?>, T>(elementAdapter) { override fun newCollection(): MutableCollection<T?> = ArrayList() } } private fun <T> newLinkedHashSetAdapter(type: Type, moshi: Moshi): JsonAdapter<MutableSet<T?>> { val elementType = Types.collectionElementType(type, Collection::class.java) val elementAdapter = moshi.adapter<T>(elementType) return object : ArrayListJsonAdapter<MutableSet<T?>, T>(elementAdapter) { override fun newCollection(): MutableSet<T?> = LinkedHashSet() } } } } Verifying Operation After creating a Ktor plug-in for using Moshi with Ktor and a Moshi adapter for deserializing ArrayList with Moshi, the next step is to verify operation. In order to use the one I just described, we need to write the following install(ContentNegotiation) { register(ContentType.Application.Json, MoshiConverter( moshi = Moshi::Builder().add(ArrayListJsonAdapter.Factory) )) } Then, let's try sending an empty JSON {} to the endpoint introduced in the first example in this article. Here's that endpoint again. data class Input ( val required: String, val optional: String? = null, ) post("/") { val input = call.receive<Input>() call.respondText("required was ${input.required}", status = HttpStatusCode.OK) } At first, nullpo was generated when accessing required , but now the following exception occurs for call.receive<Input>() during deserialization, as expected. com.squareup.moshi.JsonDataException: Required value ‘required’ missing at $ Conclusions Nullpo can occur when using Gson with Ktor. If you don't want nullpo to occur, you should choose another JSON library. To use Moshi with Ktor, you need to create your own Ktor plugin by yourself. If you do that, you need to make a Moshi adapter to deserialize ArrayList . References "Gson is deprecated," so try Moshi How to serialize ArrayList<float[]> using Moshi JSON library for Android
アバター
こんにちは、KINTO テクノロジーズ (以下 KTC) で DBRE をやっていますあわっち( @_awache ) です。 今回は僕が KTC で実現したい Database のガードレール構想についてお話ししたいと思います。 ガードレールとは ガードレールとは Cloud Center of Excellence (CCoE) の活動の中でよく表現される言葉です。 一言でガードレールを表現すると「可能な限り利用者の自由を確保しつつ、望ましくない領域のみ制限、または発見するためのソリューション」です。 ガバナンスを重視することを求められる Database 領域を取り扱うという役割の特性上、DB エンジニアは時に「ゲートキーパー」として作用してしまい、企業活動のアジリティを損なってしまうこともあります。 そこで DBRE の活動にこの「ガードレール」の考え方を取り入れてアジリティとガバナンスコントロールを両立させようと考えています。 ガードレールの種類 ガードレールは下記の 3種類に分類できます。 種類 役割 概要 予防的ガードレール 制限 対象の操作を出来ないような制限の適用 発見的ガードレール 検知 望ましくない操作を行った、された場合、それを発見、検知する仕組み 訂正的ガードレール 修正 望ましくない設定がなされた場合に自動で修正する仕組み 予防的ガードレール ![予防的ガードレール](/assets/blog/authors/_awache/20221004/preventive_guardrail.png =720x) 発見的ガードレール ![発見的ガードレール](/assets/blog/authors/_awache/20221004/heuristic_guardrail.png =720x) 訂正的ガードレール ![訂正的ガードレール](/assets/blog/authors/_awache/20221004/revise_guardrail.png =720x) ガードレール構想 導入当初の段階から予防的ガードレールによって強い制限を適用してしまうとこれまでできていたことや、やりたいことができなくなることで現場のエンジニアの反発や疲弊に繋がる可能性があります。 逆に訂正的ガードレールによって自動修復を行ってしまうと何が不適切だったのか、不適切な設定をどのように直していくのかを考えるというエンジニアのスキル向上の機会を失わせてしまうこともあるかと思います。 だからこそ現在は可能な限り利用者の自由を確保し、ガバナンスコントロールを実現するための地盤固めのフェーズだと思っています。その上で「発見的ガードレール」を取り入れることが望ましいと考えています。 現在 KTC では DEV/STG/PROD の 3ステージ制を導入しているため発見的ガードレールによってリスクを検知の周期を Daily で回したとしても、多くの場合本番に適用される前に気付ける状態にあります。発見的ガードレールで不適切な設定を定期的に検知し、それを受け取った現場のエンジニアが修正して適用する、というサイクルを継続的に回すことでサービスレベルの底上げに繋げられればこの仕組みの価値も上がっていきます。 もちろん発見的ガードレールは提供して終わりではなく、そこで検知されるルールを現場の状況に合わせてアップデートし続けることも大切です。現場のエンジニアと伴走し KTC の実態にあったガードレールを提供することで、この仕組み自体も KTC と一緒に成長していく必要があります。 Executive Sponsor の強力な後ろ盾 「ガードレールで検知されたものはエラーレベルに応じて対応する」ということが浸透しなければ狼アラートを増やすだけです。また個別のサービスの事情に応じてこのルールは対応しない、ということを許容してしまうこともアンチパターンだと考えます。 そこで大切にするべきことは「KTC としてサービスを提供する以上最低限守るべきルールだけを盛り込むこと」です。 細か過ぎるルールは定義せず、ガードレールでアラートが上がったらエラーレベルに応じて対応をする、の一点を KTC 内の全てのエンジニアに対して共通認識化、浸透させることができなければこの仕組みの価値がありません。 そこで最後の後押しをしてくれるのは、活動を後押ししてくれる Executive Sponsor です。Executive Sponsor は経営層、CxO など、企業としての方向性を示してくれる役割の方が望ましいです。 最初はどれだけ気を遣ったとしても現場のエンジニアにルールを強制する、という本質的なことは変わらないので、この活動が Executive Sponsor を通じて経営でコミットされているということは彼らが協力的に動く一つの理由やモチベーションとして作用するはずです。 責任分界点 KTC の DBRE は横断組織でありサービスを直接運用しているわけではありません。そのためどこからどこまでが DBRE の責任でどこからどこまでは現場のエンジニアの責任なのか、を明確にする必要があります。 僕はこれをDMAIC というフレームワークを用いて考えました。DMAIC については 「 【DMAICとは:定義、測定、分析、改善、定着】業務フロー改善プロジェクトの必勝パターン(リーンシックスシグマ) 」に非常に分かりやすくまとまっていると思いますのでこちらをご覧ください。 この5段階の手順にざっくりと誰が責任を持つのか、そしてそこでやるべきこと、を当てはめると下記のようになります。 定義 内容 作業 最終責任 Define 計測/評価の範囲や内容の定義 文書化 Script 化 DBRE Measure 計測/評価を実施して結果を収集 Script の実行 DBRE Analyze 原因の分析/レポーティング 組織全体の可視性を高める DBRE Improve 不具合の改善/改善計画の作成 問題に対してスムーズな対応を実施 プロダクト Control 成果を確認し、定着を目指す サービスとして健全な状態が継続されている プロダクト ![責任分界点](/assets/blog/authors/_awache/20221004/DemarcationPointOfResponsibility.png =720x) この図はそれぞれ役割を明確にしつつも、 全てのステップでお互いに相談や改善、など連携しながら最終的な責任をどこが持つのか、ということを表しています。例えば DBRE は現場の改善や定着に向けた動きを一切手助けしないということではない、ということを補足させていただきます。 どのようにガードレールを構築するのか ここまで長々とガードレールを構築するまでの考え方を記載させていただきましたがここから具体的な取り組みについて説明させていただきます。 [Define] エラーレベルの定義 最初にエラーレベルを定義することは最も重要です。 エラーレベルとはこのガードレールが KTC に提供する価値そのものです。DBRE としてどれだけ Must でやるべきことと考えたとしても定義されたエラーレベルに合ってなければ Notice もしくは対象外になります。設定したルールを定義に照らし合わせることを僕自身も徹底することで現場のエンジニアに対する説明責任を果たすことや、「何でも Critical」としたい欲求などをコントロールすることができます。 具体的な定義は下記の通り設定しました。 Level 定義 対応速度 Critical セキュリティ事故に直結する可能性があるもの クリティカルな異常に気付けない状態になっているもの 即時対応を実施 Error サービスの信頼性やセキュリティに関係する事故が発生する可能性があるもの Database 設計上の問題でおよそ 1年以内に悪影響を及ぼす可能性のあるもの 2 ~ 3営業日対応を実施 Warning それだけではサービスの信頼性やセキュリティ事故に直結しないもの セキュリティリスクを含むが影響が限定的なもの Database 設計上の問題でおよそ 2年以内に悪影響を及ぼす可能性があるもの 計画的対応を実施 Notice 正常に動作しているが記録しておきたい重要なもの 必要に応じて対応 [Define] 具体的な内容整理 続いて定義したルールの中でガイドライン作成を検討することになるのですが、最初から Database 全体を見ようと思うと失敗してしまいます。そのため僕は最初のステップではガードレールの範囲を「おおよそ自力でできる範囲」と置いています。おおよそ自力でできる範囲、とは今動いているサービスに対する深いドメイン知識がなくてもできる範囲、例えば Database クラスター(KTC ではメインの DB は Amazon Aurora MySQL を使用しています) の設定や、DB 接続ユーザーの作成、Schema、Table、Column の定義、がそこにあたります。逆に現段階でガードレールで手を出さないところはスキーマ設計やデータ構造、Query などになります。 特にここでのポイントは 「Slow Query が発生した場合の対処」をガードレールに設定していないこと、です。Slow Query は非常に重要な指標になりうるものですが、サービス単位の深いドメイン知識がなければ対応は困難です。もし現段階で大量に出ていたとしたらどこから手をつけたらいいのか、そしてそれをエラーレベルに応じた期限通りに確実に対処し続けることも難しい状態です。 Slow Query に関してはまずはガードレールではなく、Slow Query を可視化し誰でも状況が確認できるようにする、そしてそれを対処することを SLO として定義し、 DBRE から個別に提案してみる、という動きをもって段階を踏んで考えていきたいと思っています。 ガードレールで確認する領域イメージ ![Responsibility](/assets/blog/authors/_awache/20221004/Responsibility.png =720x) [Define] ガイドラインの設定/スクリプト化の実施 定義したエラーレベルや、手を出す範囲を決めた上でガイドラインに落とし込みます。そこで合意を得たものに関して自動的に検出できるようにします。 こうして作成したガイドラインを一部紹介します。 チェック項目 Error Level 理由 Database に対する Backup 設定が有効である Backup が設定されてなかった場合には Error となります Backup は自然災害、システム障害、または外部からの攻撃などによるデータ消失のリスクに対する有効な対策です バックアップ保持期間が十分な期間である Backup 保持期間が 7 未満の場合には Notice となります。 重大な損失から復旧するためには一定の期間を必要とします。 どの程度あれば十分なのか、一概な定義はありません。そのため AWS の自動スナップショット機能のデフォルトを設定しています。 Audit Log の出力が有効である Audit Log の設定がされていなかった場合には Critical となります Database に対して、いつ、誰が、何をおこなったのかをログとして残すことでデータ消失やデータ漏洩のリスクに正しく対応することができます Slow Query Log の出力が有効である Slow Query の設定がされていなかった場合には Critical となります Slow Query の設定が有効でなかった場合、サービス障害の起因となる Query を特定できない可能性があります Schema、Table、Column の文字コードに utf8(utf8mb3) で構成されたオブジェクトが存在しない Schema、Table、Column の文字コードに utf8(utf8mb3) で構成されたオブジェクトが存在した場合、 Warning となります utf8(utf8mb3) では格納できない文字列が存在してしまいます また近いうちに MySQL のサポートからも対象外となることが 明記 されています 全てのテーブルに Primary Key が存在している 主キーのないテーブルを利活用していた場合、 Warning となります そのスキーマの主体は何か、機械的にレコードを一意に特定するためには主キーが必要になります Schema、Table、Column の名前に予約語となる文字列だけで構成されたものがない 予約語だけで構成された名前が存在した場合、 Warning となります 予約語だけで構成された名前は将来的に使えなくなる、もしくは必ずバッククオート(`)で囲う必要があることが予定されています。 予約語一覧は 9.3 キーワードと予約語 を確認しましょう これらは AWS の API や Information Schema (一部 mysql Schema や Performance Schema) だけの情報から取得可能な範囲にしています。 ![Point of Automation](/assets/blog/authors/_awache/20221004/PointOfAutomation.png =720x) これらの情報を取得した上でスクリプト化します。例えば 「Schema、Table、Column の文字コードに utf8(utf8mb3) で構成されたオブジェクトが存在しない」を調べたければ下記のクエリを実行することで取得できます。 SELECT SCHEMA_NAME, CONCAT('schema''s default character set: ', DEFAULT_CHARACTER_SET_NAME) FROM information_schema.SCHEMATA WHERE SCHEMA_NAME NOT IN ('information_schema', 'mysql', 'performance_schema', 'sys', 'tmp') AND DEFAULT_CHARACTER_SET_NAME in ('utf8', 'utf8mb3') UNION SELECT CONCAT(TABLE_SCHEMA, ".", TABLE_NAME, ".", COLUMN_NAME), CONCAT('column''s default character set: ', CHARACTER_SET_NAME) as WARNING FROM information_schema.COLUMNS WHERE TABLE_SCHEMA NOT IN ('information_schema', 'mysql', 'performance_schema', 'sys', 'tmp') AND CHARACTER_SET_NAME in ('utf8', 'utf8mb3') ; その他のステップ(Measure/Analyze/Improve/Control) 上記のようなクエリなどガイドラインを満たすための情報取得をスクリプト化したものを定期的に実行し、その結果が適切でないと判断された場合にはアラートを送る、それをガードレールとして機能させる Platform を構築します。そして取得した結果の可視性を高めるための Dashboard を用意し現場のエンジニアに対応してもらう、このサイクルを回すことが当面の僕の DBRE としての活動軸になると考えています。 このガードレールの仕組みの良い点は、例えば 「1秒以上 Slow Query のうち、フロントエンドからの Query が 1ヶ月の 99パーセンタイルで 0 になること」が KTC 内で必要とされるようになった場合には、そのルールを加えればそれだけで KTC で管理している全てのサービスに適用できることです。逆に不要になったルールを外すことも一括で可能となります。 これが僕の考えるスケールする Database ガードレールの構想です。 まとめ いかがでしたでしょうか?今回は僕が KTC の DBRE としてスケールし、継続的な価値提供を生み出していくための一つの軸として考えている DBRE ガードレールについて紹介させていただきました。 まだまだ構築段階ですがこれまでのように Database 技術 を用いているわけではなく、その技術をどのように効果的に KTC に適用するのか、そしてビジネス価値に繋げるところまで考えた DBRE 組織を作っているところです。そう言った意味でも今はチャレンジの時期でアプリケーションエンジニアリングからクラウドエンジニアリングまで幅広く行なっています。 こういったことを一歩一歩積み重ねて継続して皆さんにアウトプットしていこうと思っていますので引き続きよろしくお願いします! またこの活動に少しでも興味を持ったり話を聞いてみたい、と思った方はお気軽に Twitter DM 等でご連絡いただければと思います。
アバター
By Ikki Ikazaki, MLOps/Data Engineer in Analysis Group This is the second part in a multi-part series on how KINTO Technologies Corporation(KTC) developed a system and culture of Machine Learning Operations(MLOps). The first part was How We Define MLOps in KTC . The two subsequent posts will be about SageMaker Experiments to track the experiments conducted by data scientists, and "Benkyo-kai", a series of internal study sessions, to form the common knowledge about SageMaker and MLOps with other departments. Situation In this post, we are focusing on batch implementation of ML training and serving process using SageMaker Pipelines. We will refer to "Batch training pattern" and "Batch pattern" as defined by Mercari, inc. Before getting into details of the technical explanation, let us introduce an analysis backgroud briefly about demand forecast for our service: KINTO ONE. KINTO ONE is an all-inclusive lease solution that gives you all the benefits of driving a new vehicle with a transparent monthly fee. Our Marketing & Planning Department and Analysis Group tries to understand the customer demand for KINTO ONE in Japan and check the dashboard weekly where they can see all the important figures and KPIs. Those numbers or its analytical data are generated by subsequent data and ML pipelines' regular processing, and thus it is important to organize the whole relevant systems of the dashboard to securely monitor and operate. ↑ TOYOTA car lineup KINTO ONE offers(As of July 2022) Task As described above, we need to implement the system so that we can check the latest information, but the question is how often the data should be updated. Launching a serving endpoint for real time prediction incurs unnecessary cost even when we don't need it while updating the prediction result less frequently causes the stale data and even misunderstanding among the business analysts. In our case, they are supposed to check the dashboard weekly and sometimes look up the specific values or KPIs once they come up with some analytical hypotheses. Therefore, it would be enough to update the dashboard daily and that they can check the values based on the data of the previous day. Then, batch system which is triggered by time-based schedule of cron syntax seems good for our use case. Also, since we have the software program of KINTO ONE on top of AWS, it becomes easier to build the ML pipeline using AWS managed service. Action The whole architecture of ML pipeline is depicted as below. SageMaker Pipelines SageMaker is a managed IDE for ML developers and provides us the capability to design, develop, and operate the whole ML-related processes. To implement batch pattern for our usecase, one of its features, SageMaker Pipelines, is the best fit solution. You can implement the ML pipeline with it by combining each pipeline's step which is in turn built as SageMaker Processing, the managed data processing component of SageMaker. Through configuring the pipeline's definition, you can also visualize it in the format of Directed Acyclic Graph(DAG) like below. The following is the brief description about each step. Step Name Brief Description Extract Data extraction step using AWS Athena to query. PreProc Data preprocess step for time series analysis. Train Model building step using the dedicate SageMaker SDK. TrainEvaluation Evaluation step about whether the model performance and objective metrics meet the criteria. Predict Prediction step using the trained model and future data to be predicted. PostProc Data postprocess step for the integration with data platform. You can flexibly design the pipeline like above with your imagination and the important thing here is that you can execute the pipeline as a batch process. Because SageMaker Pipelines is natively integrated with Amazon EventBridge, you can initiate SageMaker Pipelines when the schedule of cron expression triggers the EventBridge. EventBridge can also be used for monitoring. It is important to track whether the daily pipeline was executed successfully or not to ensure the latest dashboards. The execution status of SageMaker Pipelines including each step's status is transmitted to CloudWatch and EventBus without any settings and EventBridge can target SNS Topic to notify the execution status in real time. And while the pipeline is stable most of the time, we do have a Slack channel to monitor the daily status execution in order to handle the issues as soon as the pipeline execution fails. QuickSight At the last step of the above pipeline, the prediction result is registered into Glue Catalog Table so that it can be queried through Athena. Specifically speaking, it adds the daily partition for the prediction result — which brings you another benefit by making the pipeline idempotent that you can just re-run the failed execution to fix the bug. From the consumer perspective, it becomes easier and cheaper to extract the prediction result as it is partitioned and the consumer just specify the data range they need. QuickSight, a managed BI dashboard AWS offers, also extracts data through Athena and displays the dashboards by aggregating the prediction results. Actually, "Athena type" of QuickSight's Datasets can import the queried data to SPICE, its in-memory calculation engine, based on the schedule you set so that the dashboards reflect the latest information. The only trick in the entire system is just to orchestrate the schedules of ML pipeline and dashboard's refresh timing. Since it takes time to finish the ML pipeline execution, we need a buffer time before the dashboard refreshes. One thing we were not expecting is that SageMaker Pipelines take 5-10 minutes to initiate a computing instance at each step. As illustrated above, there are 5 steps in serial, so all in all the pipeline initialization takes almost an hour to finish. Fortunately, there were no fast refresh requirements this time and the extra an hour doesn't cause serious problem for the entire system. Result The orchestration of SageMaker Pipelines and QuickSight can be seen as one of batch implementation of prediction system. The benefit by applying a batch system rather than having endpoints for real-time prediction is to make the operation easy to manage — please also visit my previous post where I described that one of main goals of MLOps is "PJ Management With Speed". Imagine if you deploy a new version on prediction endpoint, you need another deployment system such as Kubernetes Deployment to not cause or reduce downtime. On the other hand, if you implemented an idempotent batch system, the desired outcome can be obtained just by re-running it even if you failed the deployment or found any bugs on the new version. Also, operating the batch pattern prediction costs relatively less compared to having the endpoints simply because batch pattern costs only pay-as-you-go. The cheaper you develop and operate, the higher the chances to increase your return on investment(ROI). In addition, it enables you to assign a dedicated role to each technical component such that ML engineers look after SageMaker Pipelines while BI engineers take care of QuickSight's dashboards, which leads you to a more smoother operation and collaboration. BI engineers always pay attention to dashboards and their values, and notice some abnormalities once the displayed values show anomalies. It may take some time to fix the bug on the ML pipeline side, but BI engineers can handle the issues by themselves by, for example, using the past queried data to compensate the null data or simply not showing the dashboards. This post introduced about batch pattern prediction using SageMaker Pipelines, but there's still a lot more to talk about. MLOps is a huge topic! Next time, we will explain about how we track the model performance of the pipeline every day as metadata management. Follow KTC Tech Blog for future posts to stay up to date. Part 3 is available from here . Reference Shibui, Y., Byeon, S., Seo, J., & Jin, D. (2020). ml-system-design-pattern[GitHub Pages]. Retrieved from https://mercari.github.io/ml-system-design-pattern/
アバター
はじめに こんにちは。Woven Payment Solution Groupで決済関係のバックエンド開発を担当している塩出です。 本Groupでは開発にKotlinを使用しており、webフレームワークにはKtorを使用しています。KtorではJSONのリクエストボディーを処理したり、JSONのレスポンスを返却するために、JSONのライブラリが別途必要となります。 当初、JSONライブラリとしてGsonを使用していましたが、Gsonを使う上で問題に直面したのでMoshiに切り替えました。 今回はなぜMoshiを選んだのか、KtorでMoshiを使う際の問題点とその解決方法についてまとめました。 なお、この記事で使用しているKtorのバージョンは2.x系です。 GsonからMoshiへの切り替え Gsonの問題点 当初はKtorのJSONライブラリとしてGsonを使っていました。しかしリクエストボディーを受け取る際、必須キーが抜けている場合に問題が発生することが分かりました。 例として、以下のようなinputを処理するエンドポイントを考えます。 data class Input ( val required: String, val optional: String? = null, ) post("/") { val input = call.receive<Input>() call.respondText("required was ${input.required}", status = HttpStatusCode.OK) } Input classの required は String 型なのでnullを許容しません。一方 optional はnull許容型です。 このエンドポイントはリクエストボディーとして上記 Input を受け取り、受け取ったbodyの required を返却する、単純なものです。 このエンドポイントに対して required がない空JSON {} を送信するとNullPointerException(通称ぬるぽ)が発生します。後の処理に影響を与えないように、デシリアライズ時に例外が発生してほしいところですが、その変数へのアクセス時、ここでは call.respondText() に例外が発生してしまいます。 解決策としてInput classの required をnull許容型にしてnullチェックをする方法もあるかもしれません。しかしKotlinを使っている以上nullを許容したくないところはなるべく型で表現して、Kotlinっぽく処理を書きたいところです。 したがって、デシリアライズ時に例外を発生させてくれるJSONライブラリの選定が必要になりました。 なぜMoshiを選んだか このプロジェクトではOpenAPIを使用したschema baseで開発しており、やりとりするclassの生成はOpenAPI Generatorに依存しています。またclientもOpenAPI Generatorで生成されるものを使用しているので、乗り換え候補としてOpenAPIが対応しているJSON ライブラリが挙がりました。 OpenAPI Generatorが対応しているJSONライブラリは以下の通りです。Gsonは除外しています。 Moshi Jackson kotlinx.serialization JacksonはOptionを有効にすることでnull safeに対応できますが、 該当するソースコード を見るとパフォーマンスに影響がある、という記述がありました。パフォーマンスの比較は行っていませんが、候補からは外すことにしました。 kotlinx.serializationはJetBrainsが開発しているライブラリであり、KotlinネイティブかつKtorとの相性も良さそうで候補としては良いと思います。しかし、Moshiの方がstar数がkotlinx.serializationよりも倍くらい多く(執筆時でMoshi: 8.5k, kotlinx.serialization: 4.1k)、また MoshiがGson v3である とのことだったので、Moshiを採用することにしました。 KtorでMoshiを使う際の問題点 Ktorの PR#2004 によれば、執筆時点ではKtorによるMoshiのサポートはありません。また PR#2005 によれば今後ともKtorのcore機能としてMoshiを提供する方針は今の所ないようです。 一方で、Ktorで Marketplace なるものを提供する予定があるようなので、将来的に3rdパーティーからMoshiを使えるようにするKtorのプラグインが供給される可能性はあります。しかし今はサポートがないので、Moshiを使えるようにするためのKtorプラグインを自分で作る必要があります。 なお、執筆時点ではまだMarketplaceの情報はありませんでした。続報に期待したいと思います。 解決方法 Moshiを使用可能にするKtorプラグインの作成 以下はKtorでMoshiを使用するために必要なクラスです。この実装はKtorの GsonConverter とほぼ同じです。 class MoshiConverter( private val moshi: Moshi = Moshi::Builder().build(), ) : ContentConverter { override suspend fun serialize( contentType: ContentType, charset: Charset, typeInfo: TypeInfo, value: Any ): OutgoingContent? { return TextContent( moshi.adapter(value.javaClass).toJson(value), contentType.withCharsetIfNeeded(charset) ) } override suspend fun deserialize(charset: Charset, typeInfo: TypeInfo, content: ByteReadChannel): Any? { return withContext(Dispatchers.IO) { val body = content.toInputStream().reader(charset).buffered().use { it.readText() } moshi.adapter(typeInfo.type.java).fromJson(body) } } } 使い方は以下のようになります。 install(ContentNegotiation) { register(ContentType.Application.Json, MoshiConverter()) } MoshiのArrayList対応 上記のKtorプラグインを使用すると、デシリアライズ時にKotlin typeではなく JAVA typeとしてデシリアライズしてしまいます。例えば、 List に格納したいのに、 ArrayList としてデシリアライズが動いてしまうということになります。 Moshiはデフォルトで ArrayList のデシリアライズに対応していないので、 List をデシリアライズしようとしたときに例外が発生してしまいます。 Moshiで任意の型に対応するためには、その型のAdapterを用意する必要があります。今回は ArrayList に対応したいので、 ArrayList 用のMoshi adapterを自分で用意しないといけません。 以下は ArrayList に対応するadapterです。これはMoshiの CollectionJsonAdapterの実装 とほぼ同じです。 abstract class ArrayListJsonAdapter<C : MutableCollection<T?>, T> private constructor( private val elementAdapter: JsonAdapter<T> ) : JsonAdapter<C>() { abstract fun newCollection(): C override fun fromJson(reader: JsonReader): C { val result = newCollection() reader.beginArray() while (reader.hasNext()) { result.add(elementAdapter.fromJson(reader)!!) } reader.endArray() return result } override fun toJson(writer: JsonWriter, value: C?) { writer.beginArray() for (element in value!!) { elementAdapter.toJson(writer, element) } writer.endArray() } override fun toString(): String { return "$elementAdapter.collection()" } companion object Factory : JsonAdapter.Factory { override fun create(type: Type, annotations: Set<Annotation>, moshi: Moshi): JsonAdapter<*>? { if (annotations.isNotEmpty()) return null return when (type.rawType) { ArrayList::class.java -> { newArrayListAdapter<Any>(type, moshi).nullSafe() } Set::class.java -> { newLinkedHashSetAdapter<Any>(type, moshi).nullSafe() } else -> null } } private fun <T> newArrayListAdapter(type: Type, moshi: Moshi): JsonAdapter<MutableCollection<T?>> { val elementType = Types.collectionElementType(type, Collection::class.java) val elementAdapter = moshi.adapter<T>(elementType) return object : ArrayListJsonAdapter<MutableCollection<T?>, T>(elementAdapter) { override fun newCollection(): MutableCollection<T?> = ArrayList() } } private fun <T> newLinkedHashSetAdapter(type: Type, moshi: Moshi): JsonAdapter<MutableSet<T?>> { val elementType = Types.collectionElementType(type, Collection::class.java) val elementAdapter = moshi.adapter<T>(elementType) return object : ArrayListJsonAdapter<MutableSet<T?>, T>(elementAdapter) { override fun newCollection(): MutableSet<T?> = LinkedHashSet() } } } } 動作確認 KtorでMoshiを使えるようにするためのKtorのプラグインと、Moshiで ArrayList をデシリアライズするためのMoshi adapterが作成できたので、動作確認します。 上記で紹介したものを使うには以下のように記述する必要があります。 install(ContentNegotiation) { register(ContentType.Application.Json, MoshiConverter( moshi = Moshi::Builder().add(ArrayListJsonAdapter.Factory) )) } その上でこの記事の最初の例で紹介したエンドポイントに対して、空JSON {} を送信してみます。 そのエンドポイントを再掲します。 data class Input ( val required: String, val optional: String? = null, ) post("/") { val input = call.receive<Input>() call.respondText("required was ${input.required}", status = HttpStatusCode.OK) } 最初は required にアクセスする際にぬるぽが発生していましたが、今度は期待通りにデシリアライズ時の call.receive<Input>() のところで次の例外が発生するようになりました。 com.squareup.moshi.JsonDataException: Required value ‘required’ missing at $ まとめ KtorでGsonを使用するとぬるぽが発生することがあります ぬるぽの発生を嫌うのであれば別のJSONライブラリを選択する必要があります KtorでMoshiを使用するために、自分でKtorのpluginを作る必要があります その場合、 ArrayList をデシリアライズするためのMoshi adapterを作る必要があります 参考 「Gson is deprecated.」らしいのでMoshiを試してみる How to serialize ArrayList<float[]> using Moshi JSON library for Android
アバター
こんにちは。分析グループ(分析G)でMLOps/データエンジニアしてます伊ヶ崎( @_ikki02 )です。 こちらは「KINTOテクノロジーズ株式会社にてどのようにMLOpsを適用していくのか」というテーマでの連載2本目です。1本目の記事「 KINTOテクノロジーズのMLOpsを定義してみた 」はリンクよりご確認ください。後続の記事では、SageMaker Experimentsを用いた実験管理、そして、他部署も巻き込んで開催した勉強会のお話などをしていければと考えています。 背景(Situation) 2本目のこの記事では、SageMaker Pipelinesを用いたバッチパターンの学習および推論の実装に焦点を当てていきたいと思います。バッチパターンとは、メルカリ社が公開している ml-system-design-pattern にて「Batch training pattern」および「Batch pattern」として紹介されている内容(2022/8時点)を参考にしています。 実装の詳細に入る前に、当社のKINTO ONEにおける需要予測について、簡単に紹介します。KINTO ONEはトヨタの新車を自動車保険や自動車税なども込みで定額で利用できるサービスです。 マーケティング部門と分析グループにおいて、ユーザーの申込状況や各種主要KPIをダッシュボードで定期的に確認しています。その数値はデータ基盤のETL処理や機械学習のMLパイプラインを通して生成されるため、ダッシュボードの運用管理には、それらシステムの全体をうまくオーケストレーションする必要があります。 ↑(2022/7時点のKINTO ONEサイトの車種ラインアップページ) 業務(Task) ダッシュボード上で最新の情報を確認できるように、更新頻度をうまく設定する必要があります。データの更新を常時行えるようにしようとすると、任意のタイミングで更新リクエストを投げられるエンドポイントを起動する必要がありますが、リクエストがないときはインスタンスの起動分無駄なコストがかかり続けます。一方で、更新頻度が少なすぎると、古い情報を参照することになり、意思決定プロセスに影響を与えることが懸念されます。 今回の事例では、ダッシュボードの確認頻度は、基本的に週次の定例か、アナリストの突発的な閃きを随時確認するといったケースがほとんどなため、ダッシュボードの更新頻度は日次で前日分のデータを見ることができれば問題ないという要件でした。そのため、実装すべき更新プロセスはcron形式の日次スケジュールで起動できれば要件を満たせそうです。 なお、このバッチの実装に際しては、KINTO ONEのソフトウェアがAWS上で稼働しているということもあり、データ更新パイプラインもAWSのマネージドサービスを使うことで容易な連携が期待できました。 やったこと(Action) データ更新における、機械学習部分のアーキテクチャは以下の図の通りです。 SageMaker Pipelinesについて SageMakerはAWSが提供する機械学習のマネージドな統合開発環境で、バッチ処理の実装に際しては、SageMaker Pipelinesという組込み機能を使うと良さそうでした。 SageMaker Pipelinesは、SageMaker ProcessingというETL処理に特化した別の組込み機能を機械学習パイプラインのステップとして実装することができ、そのステップをDAG(有向非巡回グラフ)として繋ぎ合わせることで機械学習パイプラインを実装することができます。DAGはもちろん可視化することもでき、以下のように表示できます。 実装は自由に作り込めますが、上図では各ステップを次のような責務で実装しています。 ステップ名 簡単な説明 Extract AWS Athenaを用いてデータを抽出するステップ PreProc 時系列データ用の加工をする前処理ステップ Train SageMaker SDKを用いて、学習し、モデルを構築する学習ステップ TrainEvaluation 学習済みモデルの目的変数が基準を満たしているか評価する評価ステップ Predict 学習済みモデルと推論用データを用いて推論を行う推論ステップ PostProc 推論したデータをデータ基盤に連携できるよう加工する後処理ステップ このパイプラインをバッチとして実行するには、SageMaker Pipelinesとネイティブに組込みされているEventBridgeを活用することができます。EventBridgeはAWSの各リソースが生成するイベントに基づいてターゲットを起動するイベント駆動型のトリガーか、cron形式で定義されたスケジュールに基づいてターゲットを実行する方法から選択することができ、今回は日次のスケジュールを指定しています。以下の図のように起動が確認できます。 また、EventBridgeは監視の目的でも利用しています。「SageMaker Pipelinesの実行ステータス(成功、失敗、など)」および「SageMaker Pipelinesを構成する各ステップの実行ステータス」をイベントとして検知することができ、そのイベントをSNSトピックに飛ばすことで機械学習パイプラインの実行ステータスをリアルタイムで確認しています。ダッシュボードが最新の状態に保たれているかどうかはこの監視機構を通じてslackチャンネルに通知しており、もし何らかの理由で失敗すればすぐにパイプラインの実行ログを確認して対応できるようにしています。 QuickSightについて 上述のパイプラインの最後のステップではデータ基盤への連携を行っています。具体的には、推論結果のデータをGlueのカタログテーブルとして読み込めるようにテーブルスキーマを整え、Athenaで推論結果をクエリできるようにしています。パイプラインは日次で実行されるので、Glueのカタログテーブルのパーティションに日付単位で追加されるようにしています。このようにすることで、パイプラインを実行日指定で冪等に実装でき、パイプラインが失敗してもその日の推論結果について再実行すれば直るようにしています(同じ実行日については繰り返し実行しても結果が上書き更新されるだけで処理自体は変化しません)。 また、クエリを書くアナリスト視点では、必要な実行日分の推論結果にのみクエリすることで、クエリ処理時間および費用の削減に繋げています。 このようにAthena経由で推論結果を取得できるので、ダッシュボードとしてはQuickSightを使って更新しています。QuickSightはAWSが提供するマネージドなBIダッシュボードで、QuickSightが読み込むデータセットを"Athena"に指定することでSPICEというインメモリの集計エンジンに日次でデータを取込むことができます。ただし、このQuickSightの日次取込みは、上述の機械学習パイプラインとは別のスケジュールで起動するため、2つのスケジュールをうまく合わせる必要があります。 SageMaker Pipelinesを使って想定外だったのが、SageMaker Pipelinesの各ステップのインスタンス起動に都度5~10分ほどかかるため、上述のパイプラインのように、ステップが5つ同期的に繋がったパイプラインでは、インスタンスの起動と終了だけで1時間弱の実行時間を予め見積もっておく必要があります(上図のElapsed timeが該当します)。今回の要件としては、日次で実行できればよく、実行スケジュールに余裕があったため、特に問題にはなりませんでしたが、要件次第ではSageMaker Pipelinesのクセとして気を付ける必要がありそうです。 結果(Result) SageMaker PipelinesとQuickSightを組み合わせたダッシュボード更新ロジックは、全体として一つのバッチとして見なすことができ、学習および推論のバッチパターンとしてみなすことができると考えています。バッチパターン採用の大きなメリットの一つは、管理を容易にできることです( 前回の記事 にて、MLOpsの目的のひとつは「PJ管理・高速化」だと紹介しました)。例えば、バッチではなくリアルタイム推論が可能な推論エンドポイントを用意する場合、新しいバージョンをデプロイし直す際には、ダウンタイムが発生しない、またはなるべくダウンタイムを短くするための別の仕組み(kubernetesのDeploymentなど)が必要になってきます。一方で、バッチの場合、冪等でさえあれば、デプロイに失敗したり動作がおかしくても、動作確認済みのバッチを再実行するだけで対処することができます。 バッチパターンはコスト削減も期待できます。常時稼働する推論エンドポイントに比べると、バッチパターンの実装は処理にかかった時間単位の課金で済むからです(サーバーレス)。 また、今回の実装においては、職掌別の責務に応じて運用を切分けできるメリットもあります。具体的には、推論データの更新を担う機械学習パイプラインの運用は機械学習エンジニアが担当し、QuickSightのダッシュボードの運用はBIエンジニアが担当するという棲分けができています。BIエンジニアは常にダッシュボードを管理しているので、推論データに異常があった際はすぐに気づくことができますが、大元の機械学習パイプラインの修正が難しかったりします。しかし、棲分けができているということは、例えば当日の更新処理が失敗していても、BIエンジニア自身が過去の推論結果を参照させることで暫定対応をすることができたり(機械学習エンジニアが機械学習パイプラインの修正をするまでの時間を稼ぐことができる)、ダッシュボード自体の公開を控えたりといった対応が可能になっています。 いかがでしたでしょうか。この記事では、SageMaker Pipelinesを用いた学習&推論システムのバッチパターンを紹介してきました。しかし、上述はあくまで定期実行部分のお話にすぎず、MLOpsとしてはまだまだ説明することがあります。次回の連載では、このパイプラインの定期実行の管理を含め、メタデータ管理(実験管理)についてご紹介していきたいと思います。引続きご覧いただける際は「 SageMakerExperimentsを用いた実験管理 (3/4) 」よりご覧ください。また、最新記事やその他情報発信については、本ページ最下部のTech Blogのツイッターをフォローいただけると嬉しいです。 Reference Shibui, Y., Byeon, S., Seo, J., & Jin, D. (2020). ml-system-design-pattern[GitHub Pages]. Retrieved from https://mercari.github.io/ml-system-design-pattern/
アバター
初めに KINTOテクノロジーズでフロントエンドエンジニアをしているクリスです。普段は多国籍のメンバーが在籍しており、チーム内のコミュニケーションは雑談でも技術的な話でも主に英語で行っているグローバル開発チームに所属して、様々な国に向けて現地サービス及びその裏で利用する管理画面を開発しています。同時に複数のプロジェクトを開発する際は、いかに開発作業を効率化させることが重要になってきます。今回はフロントエンド側の視点から話したいと思います。 解決したい課題 KINTOサービスは世界中20ヵ国以上に既に展開しており、国毎にビジネススキーム・開発体制・商習慣が異なる中、いかにグローバルスケールで一貫性を持ったUI/UXを実現するかは我々が直面している課題の一つとなっています。そこで、この課題を解決できるよう、我々がKINTOサービスに特化したデザインシステムを開発していることが、本記事の背景にあります。 普段デザインを実装する時に、HTML/CSS/JSといったフロントエンド開発が発生するため、デザイナーはもちろん、エンジニアも関わりながらスムーズに作業を行うには、お互いのコミュニケーションが不可欠です。複数人のデザイナーが連携なしでデザインをしていくとデザインのスタイルがバラバラになってしまい、開発側もプロジェクトごとに独自のコンポーネントを開発しなければならないかもしれません。この状態でたくさんのプロジェクトを開発していくと、会社にとって大きく分けて3つのデメリットがあります。 統一されないデザインに応じて都度開発するためコストがプロジェクト数に応じて増加すること 将来デザインや開発担当が変わるとスタイルや作り方が変わってしまうためメンテナンスしにくいこと 内部の開発プロセスが見えないユーザーにとっても、デザインがバラバラになることで一貫した体験ができず、サービスを利用することにストレスを感じてしまう可能性があること 工夫したこと デザイナーもエンジニアもデザインを楽に確認する方法 先述した課題に対して、デザイナーができる一つの対策としては様々な要素の方針を定めるデザインガイドラインを用意し、それを意識してUI/UXを設計してもらうことです。しかし、色、フォント、禁止事項などのガイドラインを見たとしても、開発工数の節約には直結しないため、エンジニアとして何ができるかは、また別途考える必要があります。 開発側で一つの対策としてはデザイナーの協力の元でどこでも再利用できるコンポーネントを開発し、各プロジェクトに入れることです。ひとまずは、そのコンポーネントをまとめて確認できる場所があると良いと思い、Storybookというツールを導入することにしました。 Storybook上でコンポーネントを開発 Storybook とはコンポーネント単位で開発、かつドキュメント化できるオープンソースのツールです。これを利用すれば、どんなコンポーネントが再利用できるか一覧化できます。 公式サイトでは各JSフレームワークに対応したインストールガイドがありますが、グローバル開発チームは基本的にVue.jsを利用しているので こちら の内容に沿って環境を立ち上げてみました。 コンポーネントをStory単位で開発・管理 Storybookの一つの特徴としては、似たようなコンポーネントをStoryという単位でグルーピングできることです。例えば、ボタンというコンポーネントと言っても、サイトの中で使うのは様々なバリエーションがあると考えられます(ユーザーアクションを促すプライマリボタン、それ以外で利用されるセカンダリーボタン、限定的に利用されるちょっと小さめのボタン、長いボタンなど)。こういう複数のボタンは xxxx.stories.ts というファイルを通してまとめることができます。(JavaScriptを利用したい方は xxxx.stories.js にしてください) また、実際コンポーネントを利用する際はPropsを渡す場合もあるので、そこでControlsという機能を利用し様々なPropsを渡すことが可能です。例えば、ボタンと言えばクリックできないようにdisabledという属性を設定しますが、Storyファイルに該当Controlを付与すれば、UIのほうで値を変更して、コンポーネントがどう変化するか確認できます。 コンポーネントのドキュメントについて 公式サイトの内容に沿ってStorybookをインストールすると、自動で @storybook/addon-essentials というライブラリが入ります。これは名前の通り、必要だと思われるアドオンがラップされたライブラリです。その中の一つは @storybook/addon-docs で、各Storyにドキュメントを付与するアドオンです。各Storyをクリックしたあと、UIに Docs というタブが見えると思います。Storybookはあらかじめ書いたStoryファイルから様々な情報を吸い上げ、自動でドキュメントを起こしてくれますが、独自のドキュメントを作成したい場合、Storyファイルから作ったファイルをパラメーターに指定すれば反映されます。(以下の例ではマークダウンのファイルをパラメーターに指定しています) import ButtonDocument from '@/docs/ButtonDoc.mdx' export default { title: 'Components/Buttons', parameters: { docs: { page: ButtonDocument, }, }, } 自社もの感のあるStorybook UIを調整 Storybookを通して、たくさん作成した再利用コンポーネントをドキュメントも含めて描画できたのはよいですが、サイト全体の見た目がStorybookのデフォルトのままだと、自社もの感がなかなか出ませんでした。そこで全体のレイアウトにも手を付けることにしました。 具体的には自社ロゴとフォントを差し替え、その上に色やフォントサイズを調整することでした。 ドキュメントの実装と似たように、この作業を実現するためのアドオンである @storybook/theming をインストールする必要がありますが、 @storybook/addon-essentials に含まれているので、まずは .storybook というディレクトリの中に manager.js というファイルを作り、 具体的なテーマ設定を行えば大丈夫です。ちなみに弊社は以下のように設定しています。 import { addons } from '@storybook/addons' import { create } from '@storybook/theming' addons.setConfig({ theme: create({ // storybookが提供してくれるbase設定:lightとdarkから選択できます base: 'light', brandTitle: 'XXXX XXXX', brandUrl: 'xxxxxxxxxxxx', brandImage: '画像のパス', brandTarget: '_blank', appBorderRadius: 10, textColor: '#2C353B', textInverseColor: '#FFFFFF', barTextColor: '#2C353B', barSelectedColor: '#00708D', }) }) 自社のもの感がだいぶ出てきましたが、さらに調整することは可能です。それが直接にCSSでカスタマイズすることです。先ほどの .storybook ディレクトリの中に manager-head.html というファイルを作成して、中にCSSコードを書けば、StorybookのUIに反映します(開発環境の再起動が必要になります)。 例えば、弊社で調整したあとは、最終的に以下のようなUIになります。 ボタンや各種インプットなどよく利用されるコンポーネントが描画できるようになりました。あとは社内メンバーで閲覧できる環境を用意すると関係者が確認できます。 次の一手 Storybookを利用して、各コンポーネントを開発し、各国のデザイナーと開発者が参考にできるように公開しました。しかし、この対策は、まだまだ始まりに過ぎません。すでに社内で進めているものもありますが、以下をやっていきたいと考えています。 コンポーネント単位だけでなく、複数のコンポーネントを組み合わせた事例を出す 複数のコンポーネントを組み合わせる定番のテンプレートもあります。たとえば、ログインで利用するフォームはメールアドレスやパスワードの入力欄、ログイン状態を覚えさせるためのチェックボックスとログインボタンで組み立てられていますが、これらの組み合わせををStorybookから確認できると、開発がより捗ると思います。将来的にはページ単位も確認できるようにしたいと思います。 コンポーネントをライブラリとして書き出す 例えば、自分のプロジェクトに取り入れたいコンポーネントがあっても、今はStorybookのリポジトリから必要なソースコードをコピーするしかありません。これだとコピーのミスが発生する可能性もあり、また今後コンポーネントの仕様に対して変更がある場合も、すでにでき上がった各プロジェクトのソースコードを修正しないといけません。 プライベートのライブラリとして書き出して、各プロジェクトにインストールすれば、コンポーネントを簡単に再利用できるようになりますし、新しいコンポーネントができ上がった時や既存コンポーネントに仕様変更がある場合はパッケージマネージャーでバージョンを上げるだけで反映できるメリットもあります(もちろんバージョン管理を徹底する必要がありますが)。 Storybookについてまとめ 本ツールの導入は世界共通のデザインシステム開発にとっての一つのステップに過ぎませんが、初めてStorybookの開発に関わってみてたくさんのメリットを感じました。 コンポーネントを単体で開発できるので、動的な部分から切り離して、見た目の調整に専念できること Storiesを通して実際のユースケースが再現できること 各プロジェクトの担当者がこのツールを利用することで画面開発する際の参考になること これからもStorybookを社内に広め、効率的な画面開発を進めていきたいと思います。
アバター
前置きと自己紹介 今回はKINTOテクノロジーズでフロントエンドを担当しているイケダがお送り致します。最近はKINTO ONEの開発運用や新規サービス・プロジェクトの立ち上げなどをしています。 自己紹介もそこそこに前置きへ。 前置き 昨今、React / Vue.js / AngularJS / Preact / Ember など様々なJSフレームワークが台頭しています。ここ最近ですとSvelteとSolidの勢いがありますね。(個人的にMithril.jsにもっと伸びてほしい)もっと知りたい方はこちらへ https://mithril.js.org/ その中でも今回はKINTOのコーポレートサイト、KINTOテクノロジーズのコーポレートサイト、そして現在進行しているプロダクトでも採用しているSvelteを使用してみての所感、そして簡単なSGまでのコードを紹介したいと思います。 本記事で紹介するSG(静的サイトジェネレーター)とは フロントエンド目線になりますが、ブログ記事などを取得するのにAPI GET、詳細画面を取得するのにAPI GETと、それぞれアクセスするたびにリクエストが走ってしまいますね。 そうではなく、ビルド時に関連するAPI GETしてすべて静的コンテンツとして生成しておこう、といったものがSG(静的サイトジェネレーター)になります。 メリットの一例として、上記を例にするとブログ一覧画面から詳細画面への遷移時にAPI通信が走らないため、遷移が非常にスムーズです。 他にもSPAやISR、SSRなど様々なアーキテクチャがあります。   Svelteとは ビルドサイズが非常に小さく、かつ後述でも紹介している通り非常に書きやすい、読みやすいフレームワークです。 またJSフレームワークといえばニアリーイコールで仮想DOMというものがありますが、Svelteはコンパイル時にVanilla JSでDOMへの変更処理なども記述されるため、仮想DOMを含みません。 再レンダリングに必要な仮想DOMなどを構築しません。DOMの状態が変更されるとそのまま実DOMを置き換えるのです。 詳しくはこちらを読みください。 Write less code をコンセプトにしたJSフレームワーク https://svelte.jp/blog/write-less-code プロダクト紹介 KINTO コーポレートサイト https://corp.kinto-jp.com/ KINTOのコーポレートサイトはSvelteKit(SG) on[S3 + CloudFront]という構成で作られており、コーディング後、とあるブランチにマージするとGitHub Actions経由でbuildタスクが実行されS3に反映、CloudFrontで配信といった形をとっています。 ※SvelteKitはSvelteを利用したアプリケーションフレームワークです。Reactを利用したNext.jsのような立ち位置です。 詳しくはこちらへ https://kit.svelte.dev/ KINTOテクノロジーズ コーポレートサイト https://www.kinto-technologies.com/ KINTOテクノロジーズ コーポレートサイトはSvelte(SPA) on [S3 + CloudFront]を使用しています。 KINTOのコーポレートサイトがSGであるのに対して、KINTOテクノロジーズのコーポレートサイトがSPAという手法を取っているのは、KINTOテクノロジーズのコーポレートサイトのリポジトリを立てた段階ではコンテンツ数が少なかった、かつSvelteKit β版がリリースされていなかったため、Svelte(SPA)を採用しました。 が、コンテンツ数が増えてきておりSGでもいいのでは・・・?という思惑が生まれてきているのためSvelteKitへのリプレイスが待ち構えています。 Svelteの他とは違うところ 一番大きいのがライブラリではなく、 コンパイラ であるといったところでしょうか。 VueにしてもReactにしてもライブラリファイルのサイズがそれなりにビルドサイズを占めるためどうしてもビルドサイズが大きくなります 読込速度が速い = 正義 だと思っている自分にはぴったりなフレームワークです。 もちろん他のフレームワークでも読込速度、実行速度など存分に改善できるように工夫やプラグインなどが充実しています。 実導入してみてはまったところ 本当にはまったところが少ないです。 簡単なインクリメントもこれくらいの少ない行数で書けます <script> // coutを定義 let count = 0; // onclickで使用する関数 function handleClick() { count += 1; } </script> <button on:click={handleClick}> Clicked {count} {count === 1 ? 'time' : 'times'} </button> あるとするならばβ版というだけあって、まだまだ破壊的な変更が入ってくる故その都度情報の拾い上げなどが必要な程度です。 Svelte特有のSyntaxもわかりやすくて初見でもハマりにくいのではないでしょうか。 もうひとつ独特なSyntaxとして Await blocks というものがあります。 以下のクリックする度にfetchするだけのComponentをご覧ください、awaitをそのままHTMLとして書けます、リーディングに長けてますね。 <script> let promise = getRandomNumber(); async function getRandomNumber() { const URL = "xxx" const res = await fetch(URL); const text = await res.text(); if (res.ok) { return text; } else { throw new Error(text); } } function handleClick() { promise = getRandomNumber(); } </script> <button on:click={handleClick}> generate random number </button> {#await promise} <p>...waiting</p> {:then data} <p>{data}</p> {:catch error} <p style="color: red">{error.message}</p> {/await} 「本当にそんな簡単にできるのかよ、贔屓目じゃないの。」と思っている閲覧者の方へ 論ずるより手を動かせ、です。 実際にやってみましょう。 実導入してみよう! 実践 https://kit.svelte.jp/ SvelteKit公式サイトを参考に作っていきます。 まずは npm init svelte static-site-sveltekit と適当なディレクトリで実行しSvelteKitプロジェクトを作りましょう。 次いで選択肢が出てきますので Skeleton project を選択肢、他はお好みでどうぞ。 CLIがあるのは便利ですね 一通り選択すると以下のような構成になっているかと思います。 本記事では以下を採用 eslint + JavaScript with JSDoc comments + prettier + Playwright Static Site Generateしよう 今回は所謂Jamstackをやってみようと思うので、なにかしら通信をしたいと思います。 dev.to からSvelteに関する記事を取得してみようと思います。 ※本記事ではスタイリングは一切行いません、ボリュームが増えるため。 まずは記事一覧ページを作ってみます。 <script context="module"> export async function load() { let articles try { articles = await fetch(`https://dev.to/api/articles?tag=svelte&per_page=5&page=1`); articles = await articles.json(); console.log(articles) } catch (e) { console.log(e) } return { props: { articles } } } </script> <script> export let articles const PostArticles = articles </script> <svelte:head> <title>Blog</title> </svelte:head> <div> <h1>Svelte devto Articles</h1> {#each PostArticles as article} <div> <header> <h2>{article.title}</h2> <h4>Tags: {article.tags}</h4> </header> <p> {article.description} </p> <a href={`/blog/${article.id}`}>MORE</a> </div> {/each} {#if PostArticles.length === 0} <div>No Articles</div> {/if} </div> async await でdev.to APIをfetchして記事を取得、articlesに格納後 PostArticlesに代入、そしてSvelteのeach構文で描画。 context="module" で書いたものはエクスポートできます。つまり同じComponent内でも呼び出せます。 そして次のscriptセクションでDOMへ渡して、パース。 明解です。 Svelteの良いところは、セクションがはっきりしているので書き手に優しい、見通しが大変いいところに有ると思います。 Vueはeasy、Reactはsimple と聞きますが、 Svelteはeasyかつsimple なのではないでしょうか。 話が飛んでしまいましたが、次は詳細記事を作りましょう。 <script context="module"> export async function load({ fetch, params }) { let article try { article = await fetch(`https://dev.to/api/articles/${params.slug}`); article = await article.json(); console.log(article) } catch (e) { console.log(e) } return { props: { article } } } </script> <script> export let article const post = article </script> <svelte:head> <title>{article.title}</title> </svelte:head> <div> <div> <h1>{post.title}</h1> <section> {@html post.body_html} </section> </div> </div> 以上です、paramsに様々な情報が入っているのでその情報を受け取り、渡して描画。 たったこれだけです。 buildしてみよう あらかたコードは書けました。 最後です、buildをしましょう。 このままでは svete.config.js の中にstatic generateしたいという命令がありません。 https://kit.svelte.jp/docs/adapters 上記にもある通り @sveltejs/adapter-static を使用しましょう。 インストールします yarn add @sveltejs/adapter-static@next -D 次にsvelte.config.jsを書き換えます import adapter from '@sveltejs/adapter-static'; /** @type {import('@sveltejs/kit').Config} */ const config = { kit: { // prerenderを入れないとエラーになる prerender: { default: true }, adapter: adapter({ pages: 'build', assets: 'build', fallback: null }) } } export default config; いざ yarn build || npm run build buildディレクトリに生成物が格納されました。実際に記事を取得できてるのか見てみましょう。 yarn preview || npm run preview 無事に見れましたね。 あとはS3やホスティングサービス、またはレンタルサーバーなど、プロジェクトに応じて生成物を置くだけです。 所感 実際に手を動かす、目でコードを見てもらうことでSvelteの良さが伝わったと信じております。 Svelteのコンセプトにもある  Write less code コード量が少なくアプリケーションを作れる。が実感出来たのではないでしょうか。 まだまだβ版で発展途上のjsフレームワークではありますが、それでもたくさんの良さが感じられたかと思います。 では、みなさま良いSvelteライフをお過ごしください。
アバター
By Ikki Ikazaki, MLOps/Data Engineer at Analysis Group This is the first part of a multi-part series on how KINTO Technologies Corporation(KTC) developed a system and culture of Machine Learning Operations(MLOps). Subsequent posts will cover batch patterns as the prediction serving pattern using SageMaker Pipelines, SageMaker Experiments to track the experiments conducted by data scientists, and "Benkyo-kai", a series of internal study sessions, to form the common knowledge about SageMaker and MLOps with other departments. Situation We have been working on various projects using ML techniques such as demand forecast, present and residual value prediction of the used car, image classification task, and some ranking algorithms, etc. Also, there are a few data scientists in Analysis Group and it comes to the conclusion that we need to build a common platform on which we can develop, manage, and develop machine learning models using MLOps technique. However, the question is what is MLOps and how we can integrate it with the ongoing relevant projects. Actually, the term MLOps is ambiguous and some people say "it is a kind of a buzzword." — and agreed to some extent. Task Considering the above, this blog post tries to define the scope of KTC MLOps by referring to some papers and documents the predecessor had published before. When talking about MLOps, the below image (Sculley et al., 2015) is often cited in Japan as a good illustration that shows ML code is actually a fraction of the whole system. To deploy and operate at a production environment, you need to be familiar with the skills and culture of software engineering and DevOps practices, which is different from the ones of data scientists. However, there are much efforts done these days by ML experts to define the scope of MLOps and now fortunately we could refer to. In 2020, ml-system-design-pattern is published by Shibui et al at Mercari, inc., at that time, and our basic concept of MLOps is greatly influenced by such design patterns. Kreuzberger et al. (2022) also conducted some interview and literature review to try to summarize the latest basic principles and technologies usually required by MLOps. Now it is time to promote those ideas and develop them in depth within our teams. Action Goal First of all, we set the goal of our MLOps. It is not much different from the one defined by the others: to bring ML proofs of concept into production "efficiently" and "reliably" . Also, the key concepts of MLOps is depicted in the decision-tree-like-format using MindMeister, a useful mind map tool, so that it can be easily referenced by the colleagues. The below is the artifact. MindMeister was really useful when illustrate a vague concept like MLOps because of its feature to display or clear the child topics in the tree diagram. We just display the topic of scalability above not to bother you by showing the whole and mess tree of MLOps. Then, we associate the key concepts with our goal and organized like below. Note that the term "Efficiently" in our goal is rephrased as "PJ Management With Speed" in the above picture and "Reliably" is expressed as "Reliable System Integration". Both strategies are important, of course, but we prioritize "PJ Management With Speed" first. The next coming posts of multi-part series will get into the details about "Pipeline" which I think requires and thus belongs to "Scalability" subtopic and "Metadata Management" often called "Experiment Tracking" by data scientists. In this section, I just introduce those concepts in a general way. Scalability Scalability is the term to refer to the flexibility of computing resources or human workload in the organization. There are some technical words in computer science using this term such as scale out, scale in, scale up, and scale down. For data scientists, scalability is crucial at the step of the model building because it is difficult to estimate the computational capacity required to run data processing and train the machine learning algorithms in advance. Could you guess how much data and what kind of format is available in KTC for demand forecast? If you could, what happens if our business suddenly expands which leads to a rapid increase in data? It may cause system resource error and require either proper scale out in the cluster or scale up per instance. In a sense, data itself stands for uncertainty and thus we need a scalable platform on which we can process data to train and host ML models by mitigating the risk. Metadata Management(Experiment tracking) The aim of metadata management is to manage the experiments for model development by tracking its metadata and reproduce its result without the help of by data scientists or developers who conducted the experiment. It is often said that because data science is a relatively new discipline and its technique is too technical for other roles to understand — sometimes even among data scientists — , it often becomes a black box. In this situation, nobody can reproduce the model building process except for the one who created it, which leads to the risk in management once he or she is on leave. Even if other data scientists are familiar with the techniques used, without the exact information - i.e. data extracted and hyper parameters - the model building process cannot be reproduced. Thus, the ML platform needs to have the ability to easily track the metadata on the processing environment, data, models, hyper parameters, objective metrics, and any comments, etc. The well-structured metadata repository not only brings the team the blueprint of data science but also accelerates the experiment cycle for data scientists. It is well suited as one of MLOps goals. Result By clarifying the entire scope of MLOps, it becomes easier to form a common knowledge among team members by pointing out the term or requirement in the scope. This entire map is just version one in KTC and expects continuous improvement. Like AWS Well-Architected tools, it will be great if it becomes a primary source in the project initiation which needs ML integration. Sounds interesting? Next time we deep dive into how we bring the DataScience project into production with the technique of MLOps: batch pattern as the prediction serving pattern using SageMaker Pipelines. Part 2 is available from here and follow KTC Tech Blog for future posts to stay up to date. Reference Kreuzberger, D., Kühl, N., & Hirschl, S. (2022). Machine Learning Operations (MLOps): Overview, Definition, and Architecture. ArXiv, abs/2205.02302. Sculley, D., Holt, G., Golovin, D., Davydov, E., Phillips, T., Ebner, D., Chaudhary, V., Young, M., Crespo, J.-F., & Dennison, D. (2015). Hidden Technical Debt in Machine Learning Systems.. In C. Cortes, N. D. Lawrence, D. D. Lee, M. Sugiyama & R. Garnett (eds.), NIPS (p./pp. 2503-2511), . Shibui, Y., Byeon, S., Seo, J., & Jin, D. (2020). ml-system-design-pattern[GitHub Pages]. Retrieved from https://mercari.github.io/ml-system-design-pattern/
アバター
はじめに こんにちは、共通サービス開発グループで決済プラットフォームのバックエンドを担当しているGo.Wadaです。 担当しているプロダクトでは、システム立ち上げ当初からドメイン駆動設計を用いた開発をスクラムで実施しています。 この記事では、そこで得られた経験を踏まえ、チームで効率よく導入した実例をご紹介します。 ドメイン駆動設計(DDD)とは ソフトウェア開発手法の一つです。 モデリングによってソフトウェアの価値や問題解決力を高めることを目指します。 例えば、ユースケース図、ドメインモデル図といった手段でモデルを表現していきます。 また、ユビキタス言語、つまり開発者のみならずビジネスメンバー含めた関係者が同じ言葉で会話できることを目指します。 もちろん、テクニカルな面でコード品質を上げるのも目的の1つです。 例えば、疎結合・高凝集な実装を行い、スパゲッティのように絡まないようにすることで変更に強くなるようにします。 集約を守り、「尋ねるな、命じよ」(Tell, Don't Ask!)のチェックをし、SOLIDを意識するといった具合にです。 ※補足:「ドメイン」とは 「ソフトウェアで問題解決しようとする対象領域」のことです。 チームの課題感 ドメイン駆動設計を導入するにあたり、チームには次のような課題感がありました。 ソフトウェア設計手法としてDDDを選択したが思ったより難しい 個人学習するものの、理解に差がある そもそも初めて一緒に働く仲間でチームを組んでいる 一方で、チームの方針や未来像としては、 効率よく開発を進めたい システムのメンテナンス性を上げて、将来の開発でも機能開発のスピードを上げていきたい(あるいは、軌道に乗った後はスピードを落としたくない) 開発は学びの機会でもあるので、チームでうまくで学習したい というイメージをしていました。 課題感に対する解決案の1つとしての輪読会 課題感を解決する方法はいくつもありますが、ドメイン駆動設計関連書籍の輪読会を開催することで、立ち向かうことにしました。 次の点を狙ってのことです。 DDDをチームの力で読み解く 学習を効率化、より深いものにする(チームとしてのスキル向上) 輪読会で発生する雑談を通してお互いを理解し、共通認識を醸成する 前提知識を合わせることで、プルリクエストでの会話を減らす 輪読会での雑談や議論を通して、チームの一体感もより持てるようにする 輪読会の実施方法 次のような方法で輪読会を実施しました。 松岡幸一郎氏の書籍「ドメイン駆動設計 モデリング/実装ガイド」を輪読 https://little-hands.booth.pm/items/1835632 プロダクトメンバー全員の合意で全員参加 週1回30分、章毎に担当者を決めて発表 事前に担当章を読み、要点を資料にまとめる 大体発表に15分、ディスカッションに15分 輪読会でのディスカッション 記憶を頼りに書き起こしてみましたが、次のような旨の会話をしていました。 常に正しいインスタンスしか存在させない方が整合性上よさそう ドメインサービスの在り方はこうだ(迂闊に作るな、責務を限定したらよき) バリデーションのやり方の1つ、仕様オブジェクトを使ったほうが良さそう 値オブジェクトの捉え方(集約ルートを構成する要素をVOとすると安全なのでは?) presentation層とapplication層が混じってない?名づけで混乱したな アーキテクチャをどうするか(レイヤードアーキテクチャ、オニオンアーキテクチャ、クリーンアーキテクチャ、など) モデリングに着手する コードを書きたいという気持ちを抑えられないのも山々ですが、モデリングも重要ですので、毎スプリント内で定期的に時間を取ってチーム全員で臨みました。 ドメインエキスパートは決済関連システムや業務に精通しているメンバーが務めました。(ビジネスという点よりも、決済機能自体がシステムで実現されてしまっているので、システムに詳しい点も重要) 実施した内容は大まかに次の通りです。 miroを使って、決済にまつわる概念に何があるかを付箋を利用しブレストしました。 ブレスト成果物 挙がってきた概念を整理し、次のような点を議論しました。 決済プラットフォームとして必要な決済にまつわる動作は何か 動作を時系列に並べるとどうなるのか 集約ルートはどれか どのように概念整理をすると効果的に収まるのか 他の類似システムではどのようになっているか 概念整理イメージ図 ※ブレスト後だけではなく、違和感がある度に、この図に立ち返って検討しなおしました。 ブレスト進行状況イメージ図 ※デフォルメした作業進行イメージです。 議論の結果を、ドメインモデル図やユースケース図に描きました。 ドメインモデル ドメインモデル抜粋 もちろん、モデルは作っておしまいではなく、幾度となく改善を重ねました。 例えば、決済集約の決済カードエンティティを初めは専用の集約にしようと考えていました。 決済カードには決済会社の情報を持たせるイメージがあり、外部システムなので集約を分けた方が取り扱いやすいと判断したためです。 一方で、ある決済にて、このキー値を決済会社とやり取りしたという情報が発生しますが、「決済」とは切り離せない関係でもありました。 強い整合性があると判断を見直し、同一集約に含めることにしました。 ユースケース図 ※ユースケース図については、作成してみたものの、他のドキュメントと内容が被っており廃止しました。 用語集を作りました。 ブレストやモデルの作成中に、同じことを説明するのに、メンバー毎に別の言葉を使っていることに気付きます。 そこで、複数ある言葉からチームで合意できる適切な言葉を用語集で定義しました。(日本語と英語両方定義しました。) 定義した言葉は必ず使えるように、間違ってしまった場合はメンバー間で優しく(冗談を言いながら)指摘し合いました。 例えば、決済プラットフォームを導入するシステムからみて、決済前に所謂注文単位に契約が必要だとします。その契約のことを「決済指示」と呼ぶようにしました。「契約」は様々な意味にとれてしまうと思慮したためです。 また、この用語集は別のシステムやプロダクト(境界付けられたコンテキストの外側)で利用される用語と混同しないことも目的としています。 用語集イメージ 輪読会とモデリングを実施しての学びや効果 輪読会 業務と並行した輪読会は、次のような学びや効果がありました。 即実践で生かせるので、学ぶ量もプロダクトの精度も上がってよい 活かす機会が近いうちにやってくるアジャイルに合っている 共通認識ができることでプルリクエストのレビュー観点が他に向くので質が向上する 気になって関連書籍にも手が伸びる(勢いで学べる) 業務からの部分の学びだけでなく、体系的な学びもできるので学びの汎用性が高い 関係ないテーマの輪読会よりも、輪読会に能動的になれる 能動的な会話によって、チームの雰囲気がさらに良くなるため、リモートワークによる疎外感の対策にもなる ※盛り上がったため、タイムキーパーが重要でした。 モデリング モデリングを時間を取って定期的に実施することで、次のような学びや効果がありました。 実装に目が向きがちなところ、モデルから考えようという習慣がつく モデルレベルで全体像の把握が出来る 実装で迷ったときにモデルに立ち戻って頭を整理できる 今後の展望 書いてきた通りにプラクティスを実施してきましたが、執筆時点で実運用はこれからになります。 現状の開発中の状態では、成功しているように見えます。 一方で、実際の運用から受けるフィードバックを元に、これらの活動が良かったのか、課題点は何だったのかを評価したいと考えています。 また、ドメイン駆動設計自体決定論理というよりも考えるための道筋のような部分が大きいです。 運用で得られる情報も含めて更なる知見の蓄積をしていきたいと考えています。 本記事記載のプラクティスを通して学んだことまとめ 業務と並行した輪読会では、 学びのインプット/アウトプット、プロダクトへの反映、メンバーの成長をほぼ同時に達成できる 業務からの学びと体系的な学びがリンクする 関係者が能動的に関われるし、会話を通してチームの一体感が生まれる という学びがありました。 また、ドメイン駆動設計のモデリングの習慣は、 実装に目が向きがちなところ、モデルから考えようになる モデルレベルで全体像の把握が出来るので実装が局所的になりにくい 実装で迷ったときにモデルに立ち戻って頭を整理できるので混乱しにくい という学びに繋がりました。
アバター
こんにちは。分析グループ(分析G)でMLOps/データエンジニアしてます伊ヶ崎( @_ikki02 )です。 こちらは「KINTOテクノロジーズ株式会社にてどのようにMLOpsを適用していくのか」というテーマでの連載1本目です。後続の記事では、SageMaker Pipelinesを用いたバッチ推論、SageMaker Experimentsを用いた実験管理、そして、他部署も巻き込んで開催した勉強会のお話などをしていければと考えています。 背景(Situation) KINTOテクノロジーズでは機械学習を用いた様々なプロジェクトに取組んできました。新車の申込台数予測、中古車の残価予測、画像の類似度判定、ランキングアルゴリズムの開発など、様々なドメインや機械学習タスクがあります。また、分析グループにはデータサイエンティストが何名か在籍しており、それぞれの実行環境やお作法が異なったり、実験結果のスムーズな共有が難しかったことがありました。そのため、EDA(探索的データ分析のこと)やモデル開発にあたって、共通のプラットフォームが求められており、MLOpsという言葉も社内で使われるようになってきました。 ところで、MLOpsとは何でしょうか。また、既存のプロジェクトに対して、どのように組み込んでいけばよいのでしょうか。個人的にはMLOpsという言葉は抽象度が高すぎて、できれば避けたい言葉だったりします(正確には、成し遂げたい課題に対して、より適切な要素技術や言葉があるはず、と考えています)。 業務(Task) そこで、この記事では、いくつかの論文やドキュメントを参考にしながら、MLOpsの全体像を明確にしつつ、KINTOテクノロジーズで特に重要視している要素技術についてご紹介していければと思います。MLOpsの話をする際に親の顔より見た絵としてよく以下の図が紹介されているかと思います(Sculley et al., 2015)。 この図では機械学習に関連するコードはアプリケーション全体のほんの一部であることが示唆されています。実際、機械学習を本番環境のアプリケーションにデプロイする際には、ソフトウェアエンジニアリングやDevOps等の技術に習熟する必要があり、データサイエンスとはまた異なるスキルセットや文化が求められます。一方で、MLOpsはバズワード化することもなく多くの研究者等によってその定義やスコープが明確にされつつあるようにも思えます。Kreuzberger et al. (2022)は様々な分野で活躍する機械学習の専門家に対してインタビューや文献調査を実施し、MLOpsの9つの原則と要素技術の解説をまとめています。また、メルカリ社により公開されているml-system-design-patternでは、機械学習システムを本番稼働させるためのデザインパターンがまとめられており(Shibui et al., 2020)、MLOpsの社内定義に際して、大いに参考にさせていただきました。 これらの情報をKINTOテクノロジーズの内部にてよく使われている用語や技術をベースに再整理し、社内展開できるようにまとめていきました。 やったこと(Action) 目的とスコープ まず、MLOpsの目的を明文化します。目的については、KINTOテクノロジーズ独自の要素を織り交ぜるわけではなく、ある程度一般性を持たせた形にしています。すなわち、 機械学習のPoCを「効率よく」本番環境へ 「信頼性高く」デプロイし運用する その上で、MindMeisterというマインドマップツールを用いて、要素技術を樹形図状に可視化してみました。 MindMeisterは樹形図の項目を表示したり隠したりできるので、MLOpsという広範な概念をまとめる際に重宝しました。すべての項目を表示すると、見通しが悪くなるので、上記の図では「スケーラビリティ」の一部の項目を中心に展開しています。 また、この樹形図と上述の目的を照らし合わせ、以下の形式でスコープを整理しました。 目的の「効率よく」という文言に図中の「(A) PJ管理・高速化」、「信頼性高く」という文言に「(B) 信頼性を高めるシステム連携」という戦略を割当て、優先度に応じて各項目を手段(戦術)として検討できるようにしています。 いずれの戦略も重要ですが、KINTOテクノロジーズではまず「(A) PJ管理・高速化」に焦点をあて、要素技術の開発ないし普及に努めていこうと考えています。今後投稿予定の連載では、この中の「パイプライン」および「メタデータ管理(実験管理)」について取組みを詳しく紹介していこうと考えており、この記事ではその概要についてご紹介できればと思います。 スケーラビリティ 樹形図の通り、KINTOテクノロジーズでは「スケーラビリティ」という大項目の中に「パイプライン」を位置付けています。スケーラビリティとは、コンピューティングリソースや組織の人的資本に関する拡張性のことを指し、スケールアウトやスケールインといった使われ方をします。データサイエンスのプロジェクトにおいて、このスケーラビリティは重要です。データには非決定的な性質があり、予め必要なコンピューティングリソースの見積もりが難しいためです。仮に既存の設定値でうまく動作していたとしても、たとえばCMや新商品の投入といった需要喚起策で急激にスパイクした場合はいかがでしょうか。アプリケーション内で使われているMLのモデルにもよりますが、メモリやCPU(GPU)のリソース不足で障害の原因となることがよくあります。そのため、このようなリソース不足のエラーに対して柔軟にスケールアウトやスケールアップする(不要時にはうまくスケールインまたはスケールダウン)仕組みが重要になってきます。データサイエンティストがモデルを開発する「学習基盤」、モデルをデプロイし推論結果を提供する「推論基盤」、それらの橋渡しとなる「パイプライン」において、このスケーラビリティの確保は重要です。 メタデータ管理(実験管理) メタデータ管理では、開発したデータサイエンティストや機械学習エンジニア当人のサポート無しに実験結果やモデル開発を再現できるように、その過程のメタデータを記録し管理することが求められます。データサイエンスは組織においてまだまだ新しい領域だとみなされることも多く、特に他の職種の人々にとってその業務内容や関わり方について十分に認識できていることは少ないと考えます。時にはデータサイエンティスト同士でさえ、専門性(e.g. 時系列、画像、言語、テーブルデータ、地理空間、因果推論、ベイズ統計学など)が異なれば、そのデータサイエンスプロジェクトは容易にブラックボックス化してしまうことでしょう。このような状況では、データサイエンスプロジェクトの管理は属人的になり、作った当人がいない状況では誰もその業務を引継ぐことはできません。また、仮に他のデータサイエンティストがプロジェクトで使われている技術に習熟していたとしても、結果を再現するには具体的にどのようなデータがいつ使われ、ハイパーパラメータの正確な値は何であるのか、なぜそう設定したのかなど、膨大かつ厳密な情報が必要になります。そのため、良き機械学習基盤には、実行環境やデータ、利用するMLアルゴリズム、ハイパーパラメータ、評価指標、解釈などの自由記述文といったメタデータを容易に記録し保存できる機能が求められます。必要な情報がうまく管理されているメタデータレポジトリがあれば、それは実験結果やモデル開発を再現するための青写真となり、また情報を思い出すための試行錯誤も減らせるため、結果として実験サイクルを回すのにかかる時間を短縮し、プロジェクトの高速化が期待できます。そのため、メタデータ管理はMLOpsの目的に沿った戦略と考えます。 結果(Result) MLOpsの全体像を可視化することで、図中の項目や要件を指しながら、必要な範囲の業務スコープを洗い出したり、他の部署のメンバーと共通認識が持ちやすくなったと思います。ただし、この全体像はまだバージョン1なので、業務で活用していく中で継続的な改善をしていきたいです。理想としては、AWSのWell-Architectedのように、機械学習プロジェクト立上げ期に真っ先に参照されるようなフレームワークへ成長させ、定着させていけたらいいなと考えています。 いかがでしたでしょうか。次回の連載では、本記事(のスケーラビリティの項)で軽く触れたパイプラインについて、SageMaker Pipelinesを用いたバッチパターンの推論基盤についてご紹介しています(Part2は こちら よりご確認ください)。引続きご覧いただけると嬉しいです。 Reference Kreuzberger, D., Kühl, N., & Hirschl, S. (2022). Machine Learning Operations (MLOps): Overview, Definition, and Architecture. ArXiv, abs/2205.02302. Sculley, D., Holt, G., Golovin, D., Davydov, E., Phillips, T., Ebner, D., Chaudhary, V., Young, M., Crespo, J.-F., & Dennison, D. (2015). Hidden Technical Debt in Machine Learning Systems.. In C. Cortes, N. D. Lawrence, D. D. Lee, M. Sugiyama & R. Garnett (eds.), NIPS (p./pp. 2503-2511), . Shibui, Y., Byeon, S., Seo, J., & Jin, D. (2020). ml-system-design-pattern[GitHub Pages]. Retrieved from https://mercari.github.io/ml-system-design-pattern/
アバター
はじめに KINTOテクノロジーズで KINTO FACTORY のリードエンジニアをしている 中西 葵 です。現在KINTO FACTORYプロジェクトでは今後の対応車種や商品の拡充、全国展開を見据えてシステムの見直しを行っており、システム開発もモダンな技術や開発フローを取り入れている先進的なプロジェクトです。 本記事ではKINTO FACTORYで取り組んでいるスキーマファースト開発について解説します。 スキーマファースト開発とは? スキーマファイルを定義しコードジェネレーターを使用してコードを生成しAPIを開発する手法で以下のような課題を解決します。 結合してみたら型が違って動かない ドキュメントが古くてコードが正しい クライアントの実装が言語毎に重複 1. 結合してみたら型が違って動かない フロントエンド、バックエンド、各マイクロサービス間、外部サービスなどとのインターフェースとしてスキーマを定義する為、データ構造の齟齬などが発生しにくくなります。 2. ドキュメントが古くてコードが正しい ドキュメントの生成もジェネレーターを用いて出力することで運用が続くと発生しがちなドキュメントとコードの内容が乖離する状況も回避できます。 3. クライアントの実装が言語毎に重複 Webアプリ, モバイルアプリなどクライアントの開発言語が何であっても定義したスキーマファイルからコードを自動生成するため同一機能の別言語実装など開発工数のムダも防ぐことができます。 その他 チームに経験者がいない場合導入の壁が高いと感じる方も多いですが、他にも値のバリデーション、モックサーバー用コードの自動生成、git上でバージョン管理など、開発者にとっては良いこと尽くめの開発手法がスキーマファースト開発です。 KINTO FACTORYのシステム構成 KINTO FACTORYではマイクロサービスアーキテクチャを採用して以下の通り ブラウザからはGraphQL サードパーティシステムからはREST API 各マイクロサービス間はgRPC(Protocol Buffers) のような構成で通信を行う設計になっています 定義言語(IDL) 一般的にそれぞれのAPI設計において以下のようなIDL(Interface Description Language)を用いて定義していきます。 Interface IDL GraphQL GraphQL Schema https://graphql.org/learn/schema/ REST API Swagger Spec https://swagger.io/specification/ gRPC Protocol Buffers https://developers.google.com/protocol-buffers ※複数の定義言語を学ぶことは学習コストも高く効率的ではありません スキーマ変換ツール それぞれのIDLは名称や型などを定義してコードを生成することが出来るのであればSchema間での相互変換も可能ではないか?と考えて調査を進めたのが以下の表になります。 変換前 \ 変換後 GraphQL Schema Swagger Spec Protocol Buffers GraphQL Schema - ? ? Swagger Spec openapi-to-graphql - openapi2proto Protocol Buffers go-proto-gql protoc-gen-openapiv2 - GraphQL Schemaをベースに変換するツールは情報が少ない Swagger Specをベースに変換するツールは長期間メンテされていない Protocol Buffersをベースに変換するツールは上記より選択肢や情報が多い 以上の調査結果より Protocol Buffersで定義して他のSchemaに変換を行う選択をしました。 ソースファイル(.proto) 準備 1 https://github.com/googleapis/googleapis からRest APIを定義する上で必要なファイルを取得 google/api/annotations.proto google/api/http.proto google/api/httpbody.proto 準備 2 https://github.com/danielvladco/go-proto-gql からGraphQL Schemaを定義する上で必要なproto定義ファイルを取得 protobuf/graphql.proto 定義ファイル(example.proto) ※以下の定義ファイルはテックブログの記事を例に本稿用に作成したものです syntax = "proto3"; package com.kinto_technologies.blog; option go_package = "blog.kinto-technologies.com"; import "google/api/annotations.proto"; // 準備1で取得したファイルの読み込み import "protobuf/graphql.proto"; // 準備2で取得したファイルの読み込み // 記事 message Article { // タイトル string title = 1; // 著者 string author = 2; // コンテンツ string content = 3; } // リクエスト message Request { uint64 id = 1; } // 結果 message Result { uint64 id = 1; } // テックブログサービス service TechBlog { // 記事投稿 rpc PostArticle(Article) returns (Result) { option (google.api.http) = { post: "/post" }; option (danielvladco.protobuf.graphql.rpc) = { type: MUTATION }; } // 記事取得 rpc GetArticle(Request) returns (Article) { option (google.api.http) = { get: "/get/{id}" }; option (danielvladco.protobuf.graphql.rpc) = { type: QUERY }; } } .proto -> .graphql への変換 go-proto-gqlのインストール リポジトリをクローン git clone https://github.com/danielvladco/go-proto-gql.git cd go-proto-gql Protoc pluginsをインストール cd ./protoc-gen-gql go install .proto から .graphqlに変換 protoc --gql_out=paths=source_relative:. -I=. example.proto 出力ファイル(.graphql) """ テックブログサービス """ directive @TechBlog on FIELD_DEFINITION """ 記事 """ type Article { """ タイトル """ title: String """ 著者 """ author: String """ コンテンツ """ content: String } """ 記事 """ input ArticleInput { """ タイトル """ title: String """ 著者 """ author: String """ コンテンツ """ content: String } type Mutation { """ 記事投稿 """ techBlogPostArticle(in: ArticleInput): Result } type Query { """ 記事取得 """ techBlogGetArticle(in: RequestInput): Article } """ リクエスト """ input RequestInput { id: Int } """ 結果 """ type Result { id: Int } .proto -> .swagger.json への変換 protobufのインストール brew install protobuf protocol-gen-openapiv2のインストール go install github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-openapiv2@latest .proto から .swagger.json への変換 protoc -I . --openapiv2_out=allow_merge=true,merge_file_name=./example:. example.proto 出力ファイル(.swagger.json) { "swagger": "2.0", "info": { "title": "example.proto", "version": "version not set" }, "tags": [ { "name": "TechBlog" } ], "consumes": [ "application/json" ], "produces": [ "application/json" ], "paths": { "/get/{id}": { "get": { "summary": "記事取得", "operationId": "TechBlog_GetArticle", "responses": { "200": { "description": "A successful response.", "schema": { "$ref": "#/definitions/blogArticle" } }, "default": { "description": "An unexpected error response.", "schema": { "$ref": "#/definitions/rpcStatus" } } }, "parameters": [ { "name": "id", "in": "path", "required": true, "type": "string", "format": "uint64" } ], "tags": [ "TechBlog" ] } }, "/post": { "post": { "summary": "記事投稿", "operationId": "TechBlog_PostArticle", "responses": { "200": { "description": "A successful response.", "schema": { "$ref": "#/definitions/blogResult" } }, "default": { "description": "An unexpected error response.", "schema": { "$ref": "#/definitions/rpcStatus" } } }, "parameters": [ { "name": "title", "description": "タイトル", "in": "query", "required": false, "type": "string" }, { "name": "author", "description": "著者", "in": "query", "required": false, "type": "string" }, { "name": "content", "description": "コンテンツ", "in": "query", "required": false, "type": "string" } ], "tags": [ "TechBlog" ] } } }, "definitions": { "blogArticle": { "type": "object", "properties": { "title": { "type": "string", "title": "タイトル" }, "author": { "type": "string", "title": "著者" }, "content": { "type": "string", "title": "コンテンツ" } }, "title": "記事" }, "blogResult": { "type": "object", "properties": { "id": { "type": "string", "format": "uint64" } }, "title": "結果" }, "protobufAny": { "type": "object", "properties": { "@type": { "type": "string" } }, "additionalProperties": {} }, "rpcStatus": { "type": "object", "properties": { "code": { "type": "integer", "format": "int32" }, "message": { "type": "string" }, "details": { "type": "array", "items": { "$ref": "#/definitions/protobufAny" } } } } } } まとめ 本稿では、スキーマファースト開発の紹介と、複数のスキーマ定義を最小限に抑えて運用する方法としてスキーマ定義を変換するツールについて紹介しました。 複数の定義言語が入り乱れている状態を解消したい方、特にProtocol Buffersの定義からGraphQL Schemaへの変換、Swagger Specへの変換について検討している方の一助になれば幸いです。 ドキュメントの生成やバリデーション処理の自動生成、コードの自動生成などについては別の記事として公開したいと思います。 Follow us! KINTOテクノロジーズのTwitterアカウントも運用開始しました。最新情報を発信していきますのでぜひフォローをお願いします https://twitter.com/KintoTech_Dev We are hiring! KINTOテクノロジーズでは一緒にモビリティの未来を創る仲間を募集しています。カジュアル面談なども行っておりますのでご興味をお持ち頂けましたらぜひお気軽にご連絡ください。 https://www.kinto-technologies.com/#/recruit/job
アバター
はじめに my route開発Gの木下です。 普段はモバイルアプリ、フロントエンド、バックエンドを跨ぎ、PoCで先行開発を行っています。 今回は、認定スクラムマスター研修を受講する機会をもらい、無事に試験に合格し、LSMの資格取得しましたので、その経験をまとめていきます。 LSMとは 今回取得したLSMについてですが。LSMは、Scrum Inc.の認定資格で、Scrum Inc.認定スクラムマスター(Licensed Scrum Master)になります。Scrum Inc.認定スクラムマスター研修を受けた後、試験に合格すると貰える資格です。 スクラムマスターの認定団体は複数あり、それぞれで資格の名前が異なります。 名前 認定団体 URL 費用 ライセンス更新費用 LSM、Licensed Scrum Master、認定スクラムマスター Scrum Inc https://scruminc.jp/ 200,000円 / 税抜 $50 / 年 CSM、Certified Scrum Master、認定スクラムマスター Scrum Alliance https://www.scrumalliance.org/ 300,000円 / 税込 $100 / 年 PSM、Professional Scrum Master、プロフェッショナルスクラムマスター Scrum.org https://www.scrum.org/ $150 なし [参考: https://www.ryuzee.com/faq/0034/ ] LSMは、2日間のコースで講義とワークショップが含まれた内容でした。このコースを修了することで、受験資格をもらうことができます。 資格取得後は毎年の更新が必要となり、$50と更新時の試験の合格が必要になります。そのため、継続するかを検討する機会が毎年あります。 なぜ受けたのか KINTOテクノロジーズは、先端なWeb企業の組織を目指し、日本製造業としての文化・風土の足かせとなる部分を変え、ベンダーロック剥がしやレガシーシステムの改修、業務フローのシステム化を行うなど、現在進行系でDX化を進めています。 進めている中で、手法としてアジャイル開発を選択できる機会も多くなってきました。 自分の所属するグループも、アジャイル開発を選択して進めています。その中で、上長からスクラムマスター研修を薦めてもらいました。 多くの小さい規模の開発チームと同様に私が所属しているmy routeチームでもチームのメンバーの少なさから、プロダクトオーナーがスクラムマスターを兼任している現状でもありました。 薦めを受けた後に、友人や知り合いにスクラムマスターと開発者を兼任しているチームの話を多く聞き、少しずつ興味を持ちました。 再度、上長と会話し以下の考えを持ったため、受講することを決めました。 スクラムマスターを行うに当たり必須のものでは無く、無くても行うことは問題ありませんが、これまで体系的に学ぶことをしていないこともあり、良き学ぶ機会にできる スクラムやアジャイルを進めるに当たり、スクラムの人脈ネットワークの構築を行うことができる可能性 例えば、セミナーの中で弊社と同じような組織課題を抱えた会社の人と、進め方について情報交換できるのかもしれない 開発者の目線でも、スクラムマスターの考えや気持ちを学ぶことは、動き方を知る上でメリットが多い LSMを選んだ理由 資格の取得は目的ではないため、PSMは選択肢にありませんでした。 最初は、社内の人から知名度のあるCSMの方を教えてもらっていましたが。 別の社内の人からスクラムのコミュニティーネットワークを構築できるかもしれないとLSMお勧めしてもらい、またトヨタグループのTRI-AD(現ウーブン・プラネット・ホールディングス株式会社)が導入をしていたことからグループとしての相性が良かったことで、社内における信頼感が増してLSMを選びました。 事前知識 研修受講前の知識量としては、 SCRUM BOOT CAMP THE BOOK と スクラムガイド を読み終えた程度です。 また、経験値としては、かっちりとスクラムを取り入れた組織で働いたことはなく、アジャイルをふわっと取り入れているチームで働いたことがある程度になります。 研修でやったこと 研修はZoomを繋いでのオンラインでした。 参加者をいくつかのチームに分けて進めるワークショップを交えながら、講義を進めていく感じでした。 研修の前日に、以下の内容を含んだメールが届きます。 スクラムガイド 用語集 テキスト Zoom チームごとのワークシート MURAL という、オンラインホワイトボードツールのURLを貰います。 ワークシートは修了後、PDFでダウンロード可能です。 学習内容としては、スクラムマスターというものについて、歴史を含め、何をすべきなのか、そのためにどういう行動をすべきなのかを、学問的に深く学ぶ内容でした。 メンバー編成については、研修開始直後にチームビルドのワークが行われ、初めてそこで一緒にワークを行うメンバーを知ることになります。 何名か会社の同僚と一緒に受講しましたが、なるべく同じ会社、近い業種の人と組合わさらないようにと気配りがあり、自分のチームでは、全く別業種の方と組むことができました。 そのため、日々の業務の中では得られない感覚と知見に触れることができました。 進め方は、学習ステップが1つ終わるたびに、座学からワークに移って問題文が出され回答or実践する形で、説明した内容を理解するように進みます。 学習する内容はボリュームがあり、個人的にはワークよりも講義が長いと感じることが多かったです。 しかし、話を聞いていないとワークで何もできなくなるため、集中力を保つことが大事な2日間でした。 試験について 資格試験は、コース修了後すぐに受験可能でした。 回答時間は無制限ではあるものの、研修の内容、テキストとスクラムガイドを理解していないと回答できない、理解の深さを問う問題が多いと感じました。 アジャイルやスクラム未経験の人には、少し辛いところもあるかもしれません。 ですが、1回までは再受験が無料ですぐ受けられるらしい(無事に1度目で合格したため、再受験については説明で聞いた話をそのまま書いています。)ので、合格のチャンスは多いです。 まとめ・感想 チームのメンターやメンバーは、明るくてノリが良かったこともあり、終始ワークショップは暖かい空気で進みました。 ワークショップを通して普段接しない業界のメンバーと進めていけるのは新鮮で、特にとても良かったです。 しかし、リモートであるためインタラクションが少なく、打ち解ける速さや深さは、実際に会って行うとでは雲泥の差がありました。 また一緒に受講した同僚は、1日目はあまり打ち解けあえず冷めたまま進んでいたようで、自分のチームのやり取りを聞いて、「なんて羨ましい…!」とこぼしていました。 メンバーやトレーナーの質によっては、上手くチームビルドができないというガチャ要素があります。 (同僚の場合は、2日目に「あれからすごく話すようになった!やっぱりアイスブレイク大事!!」と言っていたので、無事に打ち解け合えたようです。) 研修の内容に関しては、スクラムマスターというものに対して、学問的に深く学ぶ内容でした。 業務で困っているスクラムの取り入れられない組織での解決方法などを学ぶものではないため、普段の業務での困難についての銀の弾丸を直接に得るものではありませんでした。 研修を通して学問的に学んだものを、チームや組織、仕事にどのように活かすのは自分たち次第になります。 そのため、学習したもの全てをそのまま組織に取り込めて、上手く活かせる会社は稀なので、個々の会社それぞれが、何が合うのかを試し実践を繰り返していくのが大事になっていきます。 可能ならば、ワークで知り合ったチームメンバーでもいいですし、過去の参加者など、外部の人と共有し合える機会を持てる場や機会とか、運営団体のイベントなどで用意があれば、もっと知見が広がり更に良いと思いました。 いざ自身の会社に活かすことを考えると ワークしたチームメンバーに話を聞いたところ、他社もスクラムを活かしにくい環境で、また近しい課題感を抱えているところも多くありました。 それらを踏まえ、以下3パターンの適正があるのではと考えています。 作るものが決まっている場合は、スクラムよりウォーターフォールが適する 作るものが決まっておらず、やりながら作り込む場合は、スクラムは適する サービスを高めようと業種の垣根を超えて、チームが組める風通しが良い場合、スクラムは適する 自社で学びの内容の効果を最大限に発揮するには、考えた適正のように実践しやすい環境から整える必要がありますが、いきなり整えることは一筋縄ではいきません。 時折り完成の定義が崩れてしまうプロダクトインクリメントを正したり、社内の点々と散らばった小さなスクラムチームが繋がる取っ掛かりを探して Scrum of Scrum を目指したりと、少しづつ取り組むことで現状より改善に向けていければを結びの言葉にします。
アバター
はじめに こんにちは、KINTOテクノロジーズでフロントエンドを担当している渡邊です。普段はKINTO開発グループの一員として、国内向けKINTO ONEサービスをReact.jsなどのフレームワークを用いて開発しています。エンジニア集団であるKINTO開発グループでは、毎月数名の新メンバーを受け入れていますが、規模の大きなシステムで業務領域全体を理解することは複雑であると考えています。そこで、毎月中途入社者向けのオリエンテーションを実施し、新メンバーがいち早く活躍出来るようサポートしています。本記事では、なぜ中途入社者向けのオリエンテーションは重要なのかと、実際に行っているオリエンテーションの中身をご紹介したいと思います。 グループ内オリエンテーションのアナウンス オリエンテーションを実施するべき理由 中途入社者がいち早く社内で活躍するためには、早期に職場環境に慣れ、日常的なコミュニケーションからチームでの人間関係を構築することが不可欠です。新卒社員とは異なり、社会人経験があるため、中には「オリエンテーションは必要ない」と考える方もいるかと思います。しかしながら、中途入社者であっても環境の変化にすぐ適応できるとは限りません。前職との仕事のやり方の違いや業界や社内の専門用語が分からないために、入社間もないにも関わらず、不安やモチベーション低下を感じている方もいるかと思います。私たちのチームでもオリエンテーションを行っていなかった当時は、具体的な業務にアサインされるまで何から手をつけていいのか分からず、自席で困惑しているメンバーも見受けられました。また先輩社員も業務の合間を縫って、手取り足取りレクチャーすることは負荷がかかります。 このような課題に対し、KINTO開発グループでは、独自に設計したオリエンテーションを通じて、中途入社者の不安や戸惑いを解消することで、その後の業務にも良い影響を及ぼすと考えています。中途入社者にいち早く会社に馴染んでもらえるよう、以下の3つを目的にオリエンテーションを設計・実装しました。 KINTOのサービス理解を深めてもらう 自身の役割やバリューを把握してもらう 新しい環境・職場を好きになってもらう オリエンテーションを組み立てる4つのアプローチ それでは、私たちが実際に行っているオリエンテーションを4つご紹介いたします。各回60分ほどの講義形式になっていて、先輩社員が講師役を担って進行します。 プロダクト・チーム紹介(KINTO開発グループへようこそ!) グループ内のどのチームでどのような業務を行っているかを、顔つきの相関図を用いて紹介します。入社したばかりで顔と名前が一致しない中でいきなり業務に入ることに不安を感じるという声が多く、オリエンテーションの一番初めに実施しています。チーム構成やプロダクトメンバーを何となくでも把握しておくことで、困ったときに誰に聞いたらいいかを明確にすることが出来ます。 サービス・業務説明(ハンズオンを通じてサービスの流れを体験しよう!) KINTO ONEのサービスの流れをハンズオン(シナリオ)を通じて体験してもらいます。サービスに登場する様々なアクターになりきって、ウェブ画面を操作することで、今後業務で関わるステークホルダーを把握することが出来ます。また、登場人物や用語などを図を介して説明することで、自動車業界に携わってない方でも、理解を深めることが出来ます。 システム概要説明(使われているシステム・技術について理解しよう!) 実際に稼働しているシステムの裏側について、鳥瞰図を用いて紹介しています。システム全体の構成要素や機能、相互作用について事前に把握することで自身の担当・専門領域が明確になり、スムーズにプロジェクトイン出来ると考えています。技術スタックについての質疑応答もこちらのオリエンテーションを通じて行われます。 ウェルカムランチ(先輩社員と仲良くなって会社を好きになろう!) 実は一番大事なコンテンツだと考えています!複数の先輩社員とざっくばらんと会話することで、社内の雰囲気を感じることが出来ます。堅苦しい内容は抜きにして、新入社員の方に居心地の良さを感じてもらうことを目標に実施しています。オフィス周辺のランチ情報をSlackチャンネルに流したりと社内にはグルメな方が多いです😋(座敷や掘りごたつがあるお店などを選ぶと会話が弾みます笑) オリエンテーションを実施してみて分かったこと オリエンテーションを毎月行っていく中で、多くの知見や課題を発見することが出来ます。 オリエンテーションは自由に作れる 普段の開発業務と同様に、オリエンテーションも設計や実装が必要です。バックグラウンドや年齢が異なる中途入社者だからこそ、メンバーに合わせて、言葉を噛み砕いて説明・議論しています。特にクルマの知識がない方でも理解していただけるように、サービスの関わる登場人物を図を用いて説明したり、逆質問から認識齟齬が無いように努めています。全行程終了時には理解度や有効度を測るためにアンケートを取り、今後のオリエンテーションのためにナレッジを蓄積します。月ごとに柔軟にカスタム出来るのもオリエンテーションの面白さの一つです。 同期の仲が深まる 入社間も無く不安を感じている中途入社者にとって、同期入社のメンバーは心の拠り所になると思います。新メンバーには同タイミングで研修やハンズオンを体験してもらうので、メンバー同士で試行錯誤する風景が見受けられます。そこで毎月入社する新メンバーとオリエンテーション担当者でのSlackチャンネルを作成し、気心が知れた同期とコミュニケーションが生まれるように工夫しています。実際、「大人数がいるチャンネルより小規模のグループの方が発言しやすい」という方が多いです。入社オリエンテーションを通じて、同期の仲が深まるきっかけになることは、オリエンテーションを担当していてやりがいを感じる瞬間です。 先輩社員(担当者)の業務理解が深まる オリエンテーションは先輩社員による講義形式で行われます。オリエンテーション用のドキュメントを作成する中で、他グループからヒアリングをしたり、再度ハンズオンをトライすることで、これまで知らなかった・気づけなかった業務知識が増えたりします。そのためにも、定期的なドキュメントのアップデートも必要です。オリエンテーションは新入社員のためのものと思われがちですが、先輩社員にとっても有意義なものです。先述したアンケートも毎回担当者に共有しています。 まとめ エンジニア組織において、オリエンテーションの最大の効果は、新メンバーの「不安を取り除き」ながら、短期集中して「業務知識を得る」ことで、いち早く「プロジェクトに貢献」出来、ユーザーに「価値を提供」し続けられることだと思います。 中途入社者は、細かな指導をしてもらう機会はなかなかありません。分からないことがあれば自分で調べるという風潮があります。もちろん自力解決が一番ですが、入社して間もない仲間をサポートする体制を作ることで、円滑なコミュニケーションが取れる風通しの良いチームになると思います。 今後は、これまで以上に中途入社者をサポート出来るよう、アンケートやツールを用いてオリエンテーションをアップデートしていきたいです。また、多くのグループメンバーがオリエンテーションに携わることで、多角的にコミュニケーションが生まれ、よりクオリティが高くオリジナルなオリエンテーションが作れると思います。 皆さんの会社で実施している面白いオリエンテーションなどもシェアしていただけると幸いです!
アバター
はじめに はじめまして、KINTOテクノロジーズでモビリティマーケットの開発・運用を担当しているリナです。 普段はフロントエンジニアとして、Next.jsを用いて実装しています。 この度、KINTOテクノロジーズでテックブログを始めます!! KINTOテクノロジーズ設立から約1年、弊社で取り扱うプロダクトや社員数も増え、ようやくテックブログを始めることができました👏 KINTOテクノロジーズは、年齢・性別・国籍問わず多様なメンバーが在籍しており、トヨタグループのモビリティサービスの世界展開を実現する技術集団として、日々さまざまな課題に取り組んでいます。 このブログでは、そうした課題への取り組み内容や日々の業務の様子、また、AWSを始めとしたインフラ技術やフロントエンド・バックエンド開発に役立つ情報などをエンジニアから発信していきます。 初回エントリは、KINTOテクノロジーズで取り扱うサービスと弊社の個性豊かなエンジニアによるAdvent Calender2021の記事をご紹介します! KINTOサービス紹介 KINTOテクノロジーズでは、30ヵ国で展開するグローバルモビリティブランド『KINTO』関連プロダクトや、マルチモーダルモビリティサービス『my route』など、「クルマに乗る人」に焦点を当てた新しいサービスの開発・運用を行っています。 現在(2022年7月11日時点)展開しているサービスは、以下7つです。 KINTO Global KINTO KINTOマガジン ![KINTO](/assets/blog/authors/rina.k/blog-start/kinto_logo.jpg =200x) ![Global KINTO](/assets/blog/authors/rina.k/blog-start/kinto_logo.jpg =200x) ![KINTOマガジン](/assets/blog/authors/rina.k/blog-start/kinto_magazine_logo.png =300x) my route モビリティマーケット KINTO FACTORY Vintage Club ![my route](/assets/blog/authors/rina.k/blog-start/myroute_logo.png =150x150) ![モビリティマーケット](/assets/blog/authors/rina.k/blog-start/mobility_market_logo.png =200x) ![KINTO FACTORY](/assets/blog/authors/rina.k/blog-start/kinto_factory_logo.png =200x) ![Vintage Club](/assets/blog/authors/rina.k/blog-start/vintage_club_logo.png =200x) ここで、各サービスを簡単にご紹介します。 1. KINTO 国内向けのサービスの「KINTO ONE」は、車両代金・自動車税・保険料などマイカーにかかる費用がコミコミ&月々定額のサービスです。WEB申し込みで簡単申し込みが可能、トヨタの人気車種やレクサス車からお好きな車両を選ぶことができます。付随サービスとして、 のりかえGO や わりかんKINTO があります。 2. Global KINTO グローバルに展開しているKINTOサービスです。ハイブリッド車を利用したカーシェアサービスの「KINTO SHARE」やライドシェアサービスである「KINTO JOIN」など、6つのサービスを提供しています。 3. KINTO マガジン KINTOやクルマにまつわる情報をコラムや漫画を通じてご紹介しています。 4. my route 移動手段の検索・予約・決済まで、移動に関する一連の機能をひとつのアプリ内で完結できるサービスです。電車・バス・タクシー・サイクルシェア・カーシェアなど、街の色々な移動手段を組み合わせたルートをご提案しています。 5. モビリティマーケット クルマライフの楽しさを広げるサービスです。ドライブしたいと思い立ったときにぴったりなお出かけ先はもちろん、愛車のお手入れに役立つサービスなど、多彩なプログラムをWEBサイトでご紹介しています。 6. KINTO FACTORY クルマのオーナーに向けた愛車のカスタム・機能向上サービスで、お乗りのトヨタ・レクサスのクルマに最新の安全装備等を後付けするサービスです。 7. Vintage Club みなさんとKINTOが一緒に旧車を楽しむためのコミュニティです。SNSフォロワー限定の試乗会やイベントのほか、YouTubeなどオリジナルコンテンツをお届けしています。 Advent Calender 2021 昨年のAdvent Calenderにて、個性豊かなエンジニアがインフラ・開発・マネジメントなど、多岐にわたるテーマの記事を執筆しました! 今回は、私の独断と偏見で一部のおすすめ記事を紹介します。 AWS SES。もう自前バウンス対応は不要かも。 by @okinocchi Amazon Managed Service for PrometheusにECSからアプリケーションメトリクスを収集する by @sokasanan PRテンプレートを使ってチーム力アップ! by @MrSmart 完全コンテナベースのローカル開発環境を構築する(Docker+Spring Boot+MySQL+Flyway+Spock) by @chiggg 他の記事は こちら から閲覧できます。 おわりに 本エントリでは、KINTOテクノロジーズで取り扱うサービスとAdvent Calender2021を紹介しました。少しでも弊社のサービスに興味を持っていただけると幸いです。 そして、KINTOテクノロジーズでは、一緒に働ける仲間を募集しています!詳しくは こちら から 国内だけでなく、グローバルな取り組みも毎週配信していく予定です。 今後の「KINTO Technologies Tech Blog」にご期待ください!
アバター
Introduction Hello, my name is Rina and I’m involved in Mobility Market development and operation at KINTO Technologies. Usually I work as a front-end engineer implementing websites using Next.js. We are excited to announce that we are starting a KINTO Technologies Tech Blog !! It’s been almost a year since KINTO Technologies was established, during which we saw an increase in number of employees as well as the amount of projects we handle, and we are finally able to start a tech blog! 👏 KINTO Technologies is a diverse team who tackles a multitude of challenges each day as a technical group for the global development of Toyota Group’s mobility services. In this blog, our engineers will share the details of these challenges, the state of our daily operations, and useful information on AWS and other infrastructure technologies as well as front-end and back-end development. Our first entry is about the services we offer at KINTO Technologies as well as the Advent Calendar 2021 articles by our engineers full of personality. KINTO Services Introduction KINTO Technologies develops and operates new services focusing on "people in cars", including products related to the "KINTO" global mobility brand, which is available in 30 countries, and the "my route by KINTO" multimodal mobility service. KINTO Services to be Deployed Globally As of December, 2022, the following seven services are being deployed. Service name Description ![KINTO ONE](/assets/blog/authors/rina.k/blog-start/service_logo/Kinto-One.png =600x) Subscription service that allows the customer to drive one model during the contract period. ![KINTO FLEX](/assets/blog/authors/rina.k/blog-start/service_logo/Kinto-Flex.png =600x) Subscription service that allows customers to drive multiple models during the contract period. ![KINTO JOIN](/assets/blog/authors/rina.k/blog-start/service_logo/Kinto-Join.png =600x) A corporate program that allows employees to share rides when commuting by car, thereby helping to ease traffic congestion in the surrounding area, solve parking shortages and even reduce CO2 emissions. ![KINTO SHARE](/assets/blog/authors/rina.k/blog-start/service_logo/Kinto-Share.png =600x) Car sharing service available by the minute. ![KINTO RIDE](/assets/blog/authors/rina.k/blog-start/service_logo/Kinto-Ride.png =600x) A service that calls up vehicles via an online platform and provides door-to-door transport. ![KINTO GO](/assets/blog/authors/rina.k/blog-start/service_logo/Kinto-Go.png =600x) Multi-modal services that combine several modes of transport according to customers needs and present travel route options. ![KINTO FACTORY](/assets/blog/authors/rina.k/blog-start/service_logo/Kinto-Factory.png =600x) A service that makes you 'evolve' your Toyota/Lexus vehicle by updating software, hardware features and items in a timely manner in accordance with subsequent technological innovations of your already purchased Toyota/Lexus vehicle. For more information, see here ( KINTO's global expansion ). KINTO services and related products in Japan Of the above services, the following three are currently operating in Japan (as of December 2022). ![KINTO ONE](/assets/blog/authors/rina.k/blog-start/service_logo/Kinto-One.png =600x) ![KINTO GO](/assets/blog/authors/rina.k/blog-start/service_logo/Kinto-Go.png =600x) ![KINTO FACTORY](/assets/blog/authors/rina.k/blog-start/service_logo/Kinto-Factory.png =600x) KINTO ONE KINTO ONE, a service for the domestic market, is a monthly subscription service that includes the vehicle price, automobile tax, insurance premiums, and other costs associated with a personal car. You can easily apply online and choose your favorite vehicle from popular Toyota and Lexus models. Additional services include のりかえGO (Norikae GO) and わりかんKINTO (Warikan KINTO) . KINTO also provides services such as 'my route by KINTO' and 'KINTO Magazine' as other KINTO-related products. Related service name Description ![my route](/assets/blog/authors/rina.k/blog-start/myroute_logo.png =600x) This mobility service allows customers to perform different actions, from searching for means of transport to booking and payment, to be completed within a single app. The app will suggest you routes that combine various means of transportation within a city, such as trains, buses, taxis, cycle shares, and car shares. ![Vintage Club by KINTO](/assets/blog/authors/rina.k/blog-start/vintage_club_logo.png =600x) A community for you and KINTO to enjoy vintage cars together. In addition to test driving and exclusive events for social media followers, we also upload original content on YouTube. ![Mobility Market by KINTO](/assets/blog/authors/rina.k/blog-start/mobility_market_logo.png =600x) This service extends the enjoyment of your life with a car. Our website introduces a variety of programs, such as services that help you take care of your beloved car, as well as destinations that are perfect for when you want to go for a drive. ![KINTO Magazine](/assets/blog/authors/rina.k/blog-start/kinto_magazine_logo.png =600x) Information on KINTO and cars is introduced through articles and cartoons. ![Prism Japan](/assets/blog/authors/rina.k/blog-start/prism_logo.svg =600x) A destination-inspired AI app that finds the perfect place for you. ​ ![のるウェイ!(Noruwaaaaaaaay)](/assets/blog/authors/rina.k/blog-start/noruway_logo.svg =600x) A web magazine that provides information on cars for those who feel a bit lost and want to relieve their feeling of "I wish I knew that" about cars. Advent Calendar 2021 At last year's Advent Calendar our engineers wrote about their unique take on a wide variety of themes such as infrastructure, development, and management. This time, I will introduce some recommended articles based on my personal preferences. https://qiita.com/okinocchi/items/dfc1db62d9f3988e213d https://qiita.com/sokasanan/items/738c69c7f4d6fd47378a https://qiita.com/MrSmart/items/89a9dec8e9f2a0288da9 https://qiita.com/chiggg/items/4976615fe98d5a437011 Other articles can be viewed here . Summary In this post, we introduced the services we have in KINTO Technologies as well as the content on the Advent Calendar 2021. We hope that this sparked your interest in our services. We plan to post about not only domestic but also global initiatives every week. Please look forward to what’s next in store for the "KINTO Technologies Tech Blog"! Furthermore, KINTO Technologies is looking for people to work with us! You can find more information here
アバター