システムでは、前処理や学習、評価等のプロセスをすべて、同じマシーンまたはコンテナで行う。Pipelineシステムでは、各プロセスを分け独立したリソースで実行する。各プロセスは一般的にコンポーネントと呼ばれている。システムでは、コンポーネント間のデータは外部のStorageやDBを経由して行われる。Learningを御覧ください。Engineering)では、MLの学習はPipelineの利用が前提に書かれています。コンポーネントに分け、それぞれを異なるマシーンで実行するようなシステムをゼロから構築することは、非常に複雑で困難であることが想像できると思います。Pipelineを容易に構築することができます。Pipelineのシステム例。Registryで管理し、各コンポーネントはGCE等のリソースを用いて処理する。Storageに保存ができる。Pipeline構築を容易に行うことができる。codeが公開されているので、是非ご覧になってください。Pipelineの構築を楽に行えることが体験できると思います。PipelineHydraはハイパーパラメーター管理のライブラリとして、非常に素晴らしく、この例のように様々な学習用のコードが取り組まれています。Pipelineのコンポーネントとして利用しようとした場合、問題が発生します。AIでは、各コンポーネントにわたす引数を、yamlファイルのargsで定義します。]のように記述する必要があります。argparse形式のコマンドが渡されます。project>の形式でコマンドを引き渡す必要があり、工夫なしで実行するとエラーになります。Hydraで書かれたで公式のコーディングスタイルを使用するとエラーが発生します。エラーを回避するためには、コードを書き換える必要があります。AIの一般的に利用される引数と、それに対応する変換方法は表1に掲載しています。PIpelineの構築を紹介します。codeはこちらで公開しております。READMEにコードをベースとした具体的な動作方法を記載したので、ぜひ皆様体験してみてください。学習を行うの2つのコンポーネントから構成されます。どちらのコンポーネントもHydraで書かれています。prepareのコンポーネントはこちらで記載されています。このコンポーネントも管理を容易になるようHydraで記載しています。フォルダに書き込みます。その後、config.yamlで処理したい関数とその引数を記述することで、処理する関数をパラメーターとして決定することができます。Lightningを用いたコードを用いて行いました。フォルダで行われています。configの書き方は公式のドキュメント、または、Kubeflowのサイト参照ください。ここの注意点として、argsは公式の書き方をしてしまうと、エラーになるので、表1のように書き直しが必要です。定義したコンポーネントの接続はpipeline.pyで定義され、コンパイルが行われます。Pipelineに提出すると、Pipelineが実行されます。Pipelineで動作できるようにするときの問題点と工夫について記載させていただきました。このブログが皆様にとって参考になれば幸いです。今回はSentryというエラー集約管理システムをGo言語で扱う場合の知見を共有したいと思います。Sentryはエラーの集約管理を行うためのシステムで、作成したアプリケーション内で発生したエラーを一括で収集して見やすく管理することができます。sentry.io類似のエラーをグルーピングして発生頻度を確認したり、エラーの発生状況をSlackのようなチャットツールに通知してくれたりします。予期しないエラーが発生したとき、生のログを見なくてもSlackやWebのUIで確認出来るのはとても便利です。バックエンドからフロントエンドまで幅広い言語に対応しているためシステムのエラーを一括で集約が可能です。JX通信社ではエラー管理ツールとしてSentryを広く利用しており100を超えるシステムが登録されています。Sentryはセルフホスト版のSentryも存在していて、当初はサーバーを構築して利用していたのですが運用の煩雑さからマネージドなサービス版を利用するようになりました。sentry-go、触ったことない方もいると思いますので、まずは使い方を軽く紹介します。}}このような形でSDKをセットアップすると、エラーやPanicが発生した場合にSentryへとデータが送られます。呼んでpanicから脱しSentryへの送信を行います。呼ばないと捕捉できないケースがあります。APIサーバーの場合はSentry側でWebFrameworkのインテグレーションを用意してくれています。ユーザーのリクエスト情報(パスやメソッド、リモートアドレスなど)をエラーと合わせて記録してくれるので原因の調査がしやすくなるので利用可能であれば使ってみましょう。おすすめのClientOptionsSentryの初期化時に渡せるClientOptionsについて参考程度におすすめの設定を紹介します。詳細については公式ドキュメントを参照してください。DSNを入れるオプションです。プロジェクトごとに専用のDSNが発行されるので、これをソースコードにセットすることで正しくセットアップができます。いう環境変数をSDKが参照してくれるのでセットせず利用することが多いです。DSNが未設定の場合でもエラーにはなりません。未設定時の場合、sentry.CaptureException()などを呼んでも何も起こりません。有効にするとEventを破棄したログが出ます。ローカルの開発環境ではSentryを使わず開発し、検証用のサーバーにあげるときにSentryのログを有効にする使い方が簡単にできます。Environment:環境を入れるオプションです。入れておくと本番で起きたエラーなのかがすぐ分かります。チャンネルを変えるといった使い方も可能です。AttachStacktrace:CaptureMessageを呼んだ時にスタックトレースを付与するかのオプションですソースコードの位置がわかるので基本付けておいて損はないかと。サーバーにエラーを飛ばした数で料金プランが変化するので、対処する必要は特にないけれど大量に出てしまうエラーなどはここでセットしておくと安心です。他にもパフォーマンス計測用のオプションなどがありますが今回は割愛します。Goでの困りごとエラーの収集は上記で出来るのですが、Go言語の場合すこし困ったことが起こります。機能が提供されるようになり、捕捉したエラーをfmt.Errorfでラップして上位に返すパターンをよく書くようになりました。...ラップをすることでエラーの抽象度があがり、呼び出す側が意図を把握しやすくなります。ところがこうしてラップされたエラーをSentryに渡すとこのような形で処理されてしまいます。呼び出した箇所になってしまっています。エラーの原因を調査をするとき、CaptureExceptionを呼び出した箇所というのはあまり重要ではなく、実際にWrap前のエラーが発生した箇所や、fmt.Errorfでラップを行った箇所を知らせてくれるほうが有用です。Python版のSentryの場合、SDKをセットアップすればよしなにスタックトレースを出してくれたのですが、Goの場合、言語の標準エラーにスタックトレースの機能が存在しないため、少し不便な出力になります。ラップしたエラーがすべて同列のエラーとして集約されてしまう問題もあります。Sentryの場合、エラーのグループごとに通知を設定することが多いので、異なるエラーが同一にまとめられてしまうと重要なエラーを見逃してしまうリスクがあります。このあたりもPython版だとSDK入れるだけだったのですが静的言語なのでいろいろ制約がありそうです。2つが実現できればSentryでの取り扱いがよくなりそうです。外部エラーパッケージを利用すると、Sentryがそこからスタックトレースを取り出して表示してくれることが分かりました。pkg/errorsxerrorsgo-errors/errorspingcap/errorsドキュメントとして明文化されていないものの、現在この4つのパッケージに対応してそうです。有名なエラーパッケージでしたが、現在レポジトリがArchiveされてしまっているため新規では利用しづらいです。Go公式がメンテナンスしているエラーパッケージです。本体に取り込まれたものの、スタックトレースの機能については取り込まれなかったためパッケージが残っています。スタックトレースのみを表示する用途であればこのパッケージで十分そうです。sentryでエラーを投げるところだけではなくてちゃんとエラーが発生したところをトレースできてますね。一括で変換してしまえばいいかというとそうでもなくて、今度は過剰にスタックトレースが付与されてしまいます。それらを全部列挙しようとします。本当に見たい部分以外のスタックトレースが入ってしまうので調査のための情報が逆にノイズになってしまいます。以下の方針でラップすると無駄のないエラーハンドリングが出来るかと思います。した部分もトレースが残っていくので上記の方針で進めると無駄なくエラーにスタックトレースを付与できると思います。ラップしたものがSentry上でまとめられてしまう問題についても見ていきます。https://github.com/getsentry/sentry/issues/17837GitHubのIssueにヒントがないかみてみたのですが明確な解決法が見当たらず、SDKでタイトルを上書きするのがいいのではないかというコメントで終わっています。いうフックが用意されており、こちらを利用することでタイトルの書き換えが可能になるみたいです。keyのように書き換えてあげれば、最後にWrapしたメッセージがグループ名として利用できそうです。*sentry.Eventのようになっており、渡されたeventを書き換えてreturnしてあげれば上書きが可能です。利用されるみたいたいです。場合だけ上書きしてみることにします。無事エラーを書き換えることが出来ました。剥がしていますが、処理を変えればより柔軟にカスタマイズできそうです。機械学習こんにちは!JX通信社シニアMLエンジニアのファンヨンテです。私は普段から、顧客にとって価値の高い(言い変えれば、顧客を幸せにできる)AIを開発することをモットーに仕事に励んでます。そのためには、AI開発の速度を高め、様々なAIやその見せ方を顧客に試していただくことで、顧客の幸福度を上げる方向性を見出していく必要があります。効率性を高め、AIの開発速度を上げるために、JX通信社では様々な工夫を行っており、これまでもブログで紹介してきました。分類をおこなうAIの開発において、とあるClassだけ学習データの数が少なかったり、新たに分類しなければならないClassが追加されることはよくあります。このような場合の対策として、「とあるClassの学習用データだけを追加で作成し、AIの精度を上げたい」と考えることがあると思います。『味に関する言及なし』の3つのClassに分類するAIがありました。これまで辛さに言及しているテキストにラベルを付けてなかったので、辛さのデータが少ないです。4の学習用データに追加していく状況を考えます(図1)。テキストから『甘さに関する言及をしている』、『しょっぱさに関する言及をしている』、『味に関する言及なし』のテキスト分類していたが、『辛さに関する言及をしている』が追加されたり、データが少ない場合がある。含まれるという条件でフィルターして集めるとします。このような条件で集めたデータにはここ最近、本当に辛いから運動して気分転換しよのように「味」としての意味ではなく、「つらさ」を意図したものが含まれてしまう可能性があります。学習データとして追加するにはふさわしくありません。このようにキーワードマッチング程度のフィルターでは誤ったラベルが混入する可能性が高いため、そのまま学習データに加えるのはリスクがあります。その際、人間の力に頼らざるを得ないですが、自動翻訳機やAIを利用することで自動化出来る部分もあります。(本ブログの最後に補足として後述しています。キーワードマッチング程度の軽いフィルタだけでは、学習データとしてふさわしくないノイズが多く紛れている。(翻訳機の利用、学習データ作成に特化したAI等)により、対象Classにふさわしいデータを選別する必要がある。言及しているデータだけを対象Classに追加し、対象Classにふさわしくないデータは廃棄して学習に進んでしまいがちです。味としての辛いと言及したと間違った予想をしてしまいます(図3)。対象Classにふさわしいデータだけを追加してしまうと偽陽性の予想が増えてしまう。この問題はAIに対象Classに入るべきデータだけを教えて、入るべきではないデータを教えなかったことが原因です。データを加えるときの注意点。『辛い』を追加する場合、『味としての辛い』と言及しているものを対象Classに追加し、『つらいの意味で使われている辛い』は不適切であるため廃棄したいと考えがちです。そのように排除し学習してしまうと、『つらいの意味で使われている辛い』は不適切とAIが理解できないため、つらいの意味で使われていても『辛い(からい)』のClassに予想されてしまいます。したがって、『つらいの意味の辛い』等を他のClassに学習データとして含ませることで、それらが『辛さ(からさ)に関する言及をしている』Classからそれらが排除されることを教えて上げる必要があります。そのためには、対象Classにふさわしくないデータ全てに他のラベルを付与し、学習データに追加する必要があります。このラベル付けの作業には多くの時間的、金銭的コストが掛かかるため、できれば避けたいです。そこで本ブログでは自動的にラベル付けをする方法を最後に紹介します。対象Classにふさわしくないデータに再度ラベルを付ける対象Classにふさわしくないデータ全てに、手動でラベルを付与することは非常に労力がかかります。対象Class以外のClassで学習させたAIを用いて、仮をラベルを付ける方法このように、対象Classにふさわしいデータだけではなく、ふさわしくないデータの両方をうまく利用することでAIの精度向上を達成することができますまとめAIをビジネス運用していると、とあるClassの分類精度が他のClassより大事な状況や、環境の変化でAIに求められる性能が変化することは多々あります。追加する方法を思いつきがちですが、この方法では精度が上昇しないどころか、下がる場合もあります。実際、筆者も色んな人の協力や、システムを利用して作成したデータを追加したのに、AIの精度が上昇しなくて苦労した経験があります。その大きな原因は、対象Classにふさわしくないとされたデータを破棄したことにありました。したがって、AIの精度向上のためには、本ブログで紹介したように、対象Classにふさわしくないが対象クラスに近いデータを他のClassに追加して、そのクラスに入るべきではないデータも同時に教える必要があります。筆者がこのブログでもう一つ伝えたかったこととして、アノテーションを手動ですべて行うことは無理があるため、なるべく簡素化するための方法を考える必要があるということです。(本ブログでは以下の2つ)学習データを作成するためだけのAI対象Classに入らないデータの再ラベリングを紹介しました。Model)を用いた同音異義語の分類等、様々な工夫も考えられます。読者の皆さまが同様な状況になった時、この情報が役立つことを願っています。本ブログで紹介したもの以外の原因や工夫も考えられますので、なにか思いつくことがあれば私のtwitterにお知らせください。このドキュメントは文書分類の話ですが、画像分類を含めたその他の種類についての同様に適応できる考えです。本ブログの補足として、データのラベル付けをある程度自動化するためのテクニックを紹介します。翻訳機を利用して、同音異義語を区別する機械翻訳では文脈まで考慮されるので、日本語の同音異義語も翻訳すると、他の単語に変換されることを利用出来ることもあります。変換されますこのように、機械翻訳をし、日本語の同音異義語を別の単語にすることで意味によるフィルタリングができることがあります。一方で、この手法では一定確率でノイズ(ラベルの誤り)が発生してしまうリスクもあります。ここで発生するノイズの割合が低ければ、AIを用いたデータのクレンジングの利用も筋が良いと思います。診断するだけのAIを新たに開発することも考えられます。高速に作成することも出来ます。診断できるAIを作成できるなら、これをサービスで利用すればいいのでは?と思うかもですが、このAIは簡単なフィルタリングを通した後のデータに対しての分類AIなので、汎化性能は少なくフリーテキストのデータの推論には向いていません。*1:色々とフィルター条件を調整することで適切なフィルタを作成可能かもしれませんが、ルールでの対策はいたちごっこになりがちです。その条件を見つけるには多くの時間と労力がかかります。いうより、そもそもそのフィルタが完成するのなら、それをサービスで利用すればよく、AIを構築する必要はありません*2:ここでのラベルはAIがつけた仮のラベルであるため学習は完全な教師あり学習ではなく半教師あり学習になります。Corp.引用をストックしましたストック一覧を見る閉じる引用するにはまずログインしてくださいログイン閉じる引用をストックできませんでした。再度お試しください閉じる限定公開記事のため引用できません。