エムスリーテックブログ

エムスリー(m3)のエンジニア・開発メンバーによる技術ブログです

機械学習コンペを開催しました!

先日エムスリーのAIチームメンバーが主催で機械学習のミニハッカソンを開催しましたので、その様子を紹介します。

前回の様子はこちら!

www.m3tech.blog

今回は弊社オフィスで開催しました。

今回のお題は映画を推薦しよう!

まずはAIチームの西場(@m_nishiba)より推薦アルゴリズムについて解説。

f:id:suzan2go:20180306101242j:plain f:id:suzan2go:20180227200320p:plain

今回は協調フィルタリングをみんなで実装していきました。 具体的には、この辺りをPythonで実装することになります。

f:id:suzan2go:20180306101447p:plain

f:id:suzan2go:20180306101613p:plain

f:id:suzan2go:20180306101638p:plain

全然分からん…と思ったあなた。大丈夫です!

私も@m_nishibaの解説と、途中でヒントとして出してくれたサンプルコードを追っかけていって、徐々に意味がわかってきました。 (運営スタッフとして参加していたのに、普通にもくもくしてしまってスミマセン…)

最初は、私含めて若干ポカーンって状態でしたが、サンプルコードのヒントが出たあたりから、徐々にスコア争いはヒートアップ・・・

f:id:suzan2go:20180306102900j:plain

最終的なランキングはこうなりました!

f:id:suzan2go:20180306102939p:plain

今回も1位はぶっちぎりで弊社の @m_nishiba となりました。悔しい。
ちなみに私は違うアルゴリズムで頑張ろうと思ったらドツボにはまってしまい、計算回数を積み上げてきた他の参加者の方にあっさり抜かれてしまいましたw

最後は皆さんと記念撮影! f:id:suzan2go:20180306123117j:plain

機械学習コンペに参加してみて

私自身、機械学習を実装するのもアルゴリズムに触れるのも初めてだったので、最初はかなり戸惑いました。しかし、コンペをやるなかで、以下のようなことが実体験として学べたのが本当によかったなと思います。

  • なぜアルゴリズムが重要なのか?
  • なぜ機械学習には沢山のマシンリソースが必要なのか?
  • 数学勉強しなおさないとヤバイ

なお、参加された方からは以下のような感想をいただきました!

  • 軽い雰囲気で、コンペができる。 日頃は実装に頼りがちなので、内部実装やアルゴリズムを学習できる。

  • 数学の大事さを改めて感じました

エムスリーではエンジニアを募集しています!

エムスリーでは機械学習などのテクノロジーを活用して医療に貢献するエンジニアの仲間を募集中です!勉強会の見学やカジュアル面談も随時受け付けてますのでご興味があれば是非ご応募ください!

jobs.m3.com

CNNと協調フィルタリングを使った日本語文書のリコメンド

機械学習エンジニアの西場(@m_nishba)です。主に自然言語処理を使ったリコメンドや文書分類、ユーザー分析を行っています。

最近、開発中のリコメンデーションのアルゴリズムについて紹介します。

コンテンツ

モチベーション

弊社では様々な形態の医療系コンテンツ(テキスト、動画、画像)を扱っています。

様々なコンテンツを同時にリコメンデーションをしたいと考えています。そこで協調フィルタリングを使用しようと思っているのですが、アイテム・コールド・スタート問題を解決する必要があります。 (※ ユーザーからの評価が少なく、リコメンドするためのデータが十分に蓄積されていないことをコールド・スタートと呼びます)

弊社ではニュース記事等の配信も行っており、全く評価されていない(クリックされていない)アイテムをリコメンドする必要があります。

このようなアイテム・コールド・スタート問題に対応するために、内容ベース・フィルタリングとのハイブリッドにしたいのですが、短い文書に対応する必要があります。というのもニュース記事等は十分な文章量がありますが、動画コンテンツの場合、テキストはタイトルのみ場合もあるからです。

そこでConvolutional Matrix Factorization for Document Context-Aware Recommendationを実装して実データで試してみようと思います。(論文の内容は後述します。)

問題の概要

詳細に入る前にリコメンドアルゴリズム作成時の問題点とアプローチをまとめます。

  • アイテム・コールド・スタート問題
  • 内容ベースで使用できるテキストが短い。
  • 未知の単語のへ対応
  • 不均衡データ・Implicit feedbackデータ
    • 訓練データのサンプリングを工夫

問題の検証結果

リコメンドの前段階として、短いテキスト情報だけを使って似ているテキストを見つける問題でアルゴリズムを検証したところ doc2vec, word2vecを使った方法やword2vec+TFIDFを使った方法より非常に良さそうな結果になりました。詳細は後述します。

文書分類

文書分類の論文の紹介

本題に入る前にテキストに対してCNNを使う方法を紹介したいと思います。

ここで紹介するアルゴリズムは"Convolutional Neural Networks for Sentence Classification"(Yoon Kim, 2014)に従っています。(弊社の工夫は後述する日本語のTokenizeのみです。)

この論文はタイトルの通りCNNを使って文書分類を行っています。 モデルはシンプルで下記の図のようになります。 f:id:nsb248:20180306112507p:plain

(Yoon Kim, 2014)より引用

Word embedding ⇒ Convolution ⇒ Max pooling ⇒ Fully connected layer の流れで処理しています。最後のFully connected layerでdropoutを使っています。

またフィルターを作るときにwindow sizeを複数使用し、n-gramのような処理を実現しています。 さらにchannelとして、ランダムに初期化されたembeddingとwikipedia等を使って学習したword2vec等のembeddingを同時に使用することも提案されています。

精度検証

論文では様々なデータ・セットに対して他のアルゴリズムと比較しています。 f:id:nsb248:20180306112608p:plain

(Yoon Kim, 2014)より引用

ここではMovie reviewsのデータ・セットを使って、そのreviewがpositive/negativeかどうか分類する問題で、論文の結果を再現します。

ソースコードはここにあります(が、検証用でぐちゃぐちゃです)。 https://github.com/nishiba/tf_cnn_mf

事前に学習したword embeddingを使用せず、word-embeddingをrandomに初期化し、分類問題と同時に学習するCNN-randモデルを実装し、検証しました。

結果は下記のようになり、論文の76.1を概ね再現できています。

test: step 4200, loss 0.654164, acc 0.758201

社内データを用いた検証

弊社では医療関連のニュースの配信を行っており、配信するニュースに強く関連するタグを付けて、リコメンドに活かしています。現状、それらのタグは人手で付けており、それを自動化できるか検証します。今回はニュースのタイトルから「地域」タグがつくかどうかの2値問題を考えます。一般にニュース記事に関連する地域の特定は、ニュース配信サービス等では重要な課題となっています。(実際は都道府県等のタグを付けたいのですが、これは他の業務への布石なので簡単なケースにしています。)

Tokenizeの方法

上で紹介したCNNでの手法を使用するために日本語をトークンに分割する必要があります。ここに英語と違って日本語の自然言語処理の難しさがあります。

方法① 普通にMeCabを使う。

今回は地域ニュースかどうかなので、医療専門用語の重要性も低いので医療系のユーザー辞書を使わずにmecab-ipadic-NEologdをそのまま使います。

方法② 文字ベースとMeCabの組合せを使う。

ちょっと工夫した方法でトークン化したいと思います。今後は文字ベースでの自然言語処理を進めていたいと考えています。というのは「肺癌」「胃癌」という単語は状況によっては非常に似ている分散表現を得たいです。

しかし、これらを学習させるには医療系のテキストが大量に必要になります。癌なら多く手に入りますが、希少疾患だと大量のテキストを得るのは難しいです。

そもそも癌という文字が含まれているので、文字ベースのほうが効率的に分散表現が得られるのではないかと考えられます。

そこで下記のようなルールでトークン化を行います。

  1. 連続するカタカナ・アルファベット・数字に対してのみMeCabで単語に分割する
  2. 漢字・ひらがなは一文字ごとに分割する

例えば下記のように分割することになります。

in: かゆみ減らす抗体に有効性 アトピー皮膚炎
out: か, ゆ, み, 減, ら, す, 抗, 体, に, 有, 効, 性, アトピー, 皮, 膚, 炎,

検証結果

(詳細なデータは出すことができません。)

0:地域タグがついていない
1:地域タグが付いている

として分類問題を解いています。

方法①の結果
precision recall f1-score support
0 0.88 0.93 0.90 2039
1 0.76 0.65 0.70 716
avg / total 0.85 0.85 0.85 2755

1のrecallがかなり悪いですね。

方法②の結果
precision recall f1-score support
0 0.97 0.82 0.89 2039
1 0.65 0.93 0.76 716
avg / total 0.89 0.85 0.86 2755

1のrecallは良くなりました! 逆にprecisionが下がっています。

実際は地域タグが付いていないのに、地域タグ付きだと分類してしまった例は下記のようなものがあります。

1  督促状の金額は「0円」 滋賀・彦根市、国保料などミス
2  中村哲さんに旭日双光章 アフガンで人道支援
3  病院内で相次ぎトラブル 飲料に異物、エプロン切断
4  性能不足、新たに90棟 関与4人、組織ぐるみ 東洋ゴムの免震不正拡大
5  在宅生活、支援の動き 治療薬開発、急ピッチ
6  アスベスト被害で無料相談 公営住宅の状況把握へ
7  母子4000組情報流出の可能性…富山大
8  患者死亡事故、再び不起訴 介助の54歳准看護師、札幌
9 【愛知】1位名古屋第ニ赤十字病院、2位愛知医科大学病院、2017マッチング中間
10 東京都千代田区、インフル予防接種、高校生まで無料化

この中で明らかにおかしいの4番、5番、6番ですね。 他は地名や施設名が含まれていますし、3番のタイトルも"病院"となっており、特定の病院を指しているので、地域性が高い可能性があります。

教師ラベルは擬似的に作ったということもありノイズが多そうです。(このモデルを使って教師データの作り直しをそのうちしようかと思います)

コンテンツのリコメンド

本題のコンテンツのリコメンドを考えます。 動画やニュース記事等を一緒にリコメンドしたいのですが、問題が2つあります。

  • 公開期限付きのものや情報鮮度が大事なのもが多く基本的にはアイテム・コールド・スタート問題になります。
  • 動画の原稿はなく、使えるテキストはタイトル(短い文章)しかありません。

そこで、コンテンツ・ベース・フィルタリングと協調フィルタリングのハイブリッドを考えます。 Convolutional Matrix Factorization for Document Context-Aware Recommendation を参考にしました。

ざっくり論文を説明すると、Matrix Factorizationにより、ユーザーとアイテムの潜在ファクターを求め、CNNを使ってアイテムの潜在ファクターをテキストから再現するように学習します。 そうすることで、テキストの情報も組み込んだMatrix Factorizationの潜在ファクターを求めることができます。

詳細は論文を見てほしいのですが、最小化する数式を紹介します。 
\mathcal{L}(U, V, W) = \sum_i \sum_j \frac{I_{ij}}{2}(r_{ij} - u_i^T v_j)^2
+ \frac{\lambda_U}{2} \sum_i ||u_i||^2
+ \frac{\lambda_V}{2} \sum_j ||v_j - \text{cnn}(W, X_j)||^2
+ \frac{\lambda_W}{2} \sum_k || w_k||^2

ここで、 u_i がユーザーの潜在ファクター、 v_j がアイテムの潜在ファクター、cnn(W, X_j) がアイテムの潜在ファクターをテキストから再現するCNN、w_k はCNNのパラメータです。

これは同時に解くことが困難で、ユーザーの潜在ファクター、アイテムの潜在ファクター、CNNと順に学習させることを収束するまで繰り返します。

弊社のデータに関する課題

上記のアルゴリズムを適用したいデータは、動画や記事のクリックログといったimplicit feedbackのデータです。 またユーザー数、アイテム数も論文内で実験に使われていたデータよりも多いです。

そこでアルゴリズムを少し変更します。

SGDを利用する。

論文では潜在ファクターの最適化の際、近似解を逆行列を通して計算していました。これは状況によっては非常に時間がかかります。

ImplicitFeedbackなので、MovieLensのようなレーティングされたデータのように"評価されたペアのみを考える"ことができません。

そこで、確率的勾配降下法(SGD)を使用します。

② Positive/Negative数が等しくなるようにサンプリングを行う。

学習時にクリックされたデータ(positive)と同じデータ数のクリックされていないデータ(negative)を使用します。 negativeデータはepochごとにサンプリングしなおします。

Collaborative Filtering for Implicit Feedback Datasetsのように確信度を導入してもいいのですが、今回SGDを使っているのデータ数調整で同様な効果を得ることにしました。

③ Matrix Factorizationにbiasを導入する。

よく使用される方法ですが、Matrix Factorization時にbiasを導入します。 つまり、 
\sum_i \sum_j \frac{I_{ij}}{2}(r_{ij} - u_i^{T} v_j)^{2}
ではなく、 
\sum_i \sum_j \frac{I_{ij}}{2}(r_{ij} - u_i^{T} v_j - p_i - q_j)^{2}
とします。ここでp_iq_j は それぞれユーザーとアイテムのバイアスを表すスカラー値です。

この導入はパフォーマンスに大きな影響がありました。 というのもニュース記事等は時期や公開時間等のタイミングによってアクセス数が大きく異なります。 それは同じようなタイトルの記事でも全然違うアクセス数になるということです。

最初の式の場合、アクセス数が多いと ||v_j|| が大きくなり、逆に少ないと小さくなります。

同じようなタイトルでこのようなことが生じるとCNNの学習が進まなくなります。それは同じような短いテキストから ||v_j|| の大小まで判断つかないからです。

そこで、このようなタイミング等によるアクセスの大小を表現するためにバイアスを導入し、CNNは同じように \sum_j ||v_j -
\text{cnn}(W, X_j)||^2 を最小にするように学習させます。

④ 文字ベースと単語ベースの組み合わせ

最初の文書分類のときに紹介した方法を使います。 この方法により未知の単語へ少しは対応できるようになります。短い文章の中に未知の単語が含まれると致命的になります。

例えば"子宮動脈塞栓術"という単語が始めて出てきたとしても、文字ベースだとそれなりの分散表現を得ることができます。

リコメンド方法

いったんCNNを学習させるとクリックが全く無いようなアイテムに対しても潜在ファクター v_j を計算することができます。

ここで得られる v_j は同じような傾向の人がクリックするかどうか(協調フィルタリング)と同じような文字がふくまれているかどうか(CNN)といった情報を持ったベクトルになります。

また  u_i^{T} v_j により、ユーザーがアイテムを好むかどうかのスコアが得られます。

何らかの方法で q_j を予測することができれば、新しいアイテムをリコメンドすることができます。 その方法はまた別の機会に紹介したいと思います。

実験

上記の方法で学習されたCNNを使い、短い文章をベクトル化し、文章の似ている度合いを計算してみます。類似度はコサイン類似度を使いました。

ターゲットとする文章は訓練時に使用していない文章を使用しています。

(ちなみに、doc2vec, word2vecを使った方法、word2vec+TFIDFを使った方法より非常に良さそうな結果になりました。)

========================================================================
EGFR変異陽性肺がん薬剤耐性研究の最新情報/実際の症例から学ぶT790M検査のための再生検
--- 似ている記事 ---------------------------------------------------------
  score                              title
0.918537  タグリッソがEGFR変異陽性NSCLCの1次治療で優先審査品目指定
0.913470          ベダキリンフマル酸塩、多剤耐性肺結核適応で承認取得
0.901056                  イラリス、バイアル入り液剤追加承認
0.892470            RET融合遺伝子陽性肺癌の新薬剤耐性機構を発見
0.890703           後発薬241品目を承認、降圧剤アイミクスに20社
========================================================================

これらの記事は

  • 肺癌に関連している
  • 同じユーザーに読まれている

といった特徴があります。 協調フィルタリングだけの場合、2番めの”同じユーザーに読まれている”しか考慮されませんが、 CNNを組み合わせることにより、”肺癌に関連している”記事を選ぶことができます。

最後に

弊社では、医療分野における

といったコンテンツが豊富にあり、分析によってビジネスを加速することができます。 (医療分野に特化した翻訳や動画からの特徴量抽出にも取り組もうとしています)

弊社のデータ分析・機械学習業務の特徴として、

  • ビジネスに直結している
  • メインサービスにアルゴリズムの導入ができる。
  • アルゴリズムのABテストをビジネスサイドが受け入れてくれる
  • 動画等に対する長期的なアルゴリズム開発にも挑戦できる。
  • 外部API等では実現できない面白い課題に専念できる。(外部APIでできる問題は外部APIを使います。)
  • 新卒・中途関係なく、自分が担当する案件のメイン開発者になれる。

といったところがあります。

もし興味のある方は応募フォームまたは@m_nishibaまでご連絡ください。

ニュース記事推薦システム「Archimedes」の紹介

こんにちは。エンジニアの池田(@progrhyme)です。
昨年11月に私がエムスリーにjoinして、早2ヶ月が経ちました。現在は、主に電子カルテ事業やAI関連事業のサーバーサイドを担当しています。

本日は、私がAPIシステムの構築に携わった、機械学習を利用したニュース記事の推薦システムについて紹介します。

目次:

「Archimedes」とは

このシステムは、社内で「Archimedes」というコードネームで呼ばれています。
以降、本記事でもこのシステムをArchimedesと記します。

Archimedesはm3.comやメールマガジンを送信するシステムのバックエンドで動作し、ユーザにおすすめのニュース記事を出力します。
APIを提供しており、ユーザ単位でおすすめ記事のリストをレスポンスとして返却することが可能です。

学習・推薦アルゴリズムについて

Archimedesでは以下のような流れで、機械学習を行ってユーザへのおすすめ記事を算出します。

  1. 一定期間(=Tとする)におけるニュース記事データについて、トピックモデルに基づき学習を行い、各記事のトピックを計算
  2. 同T期間におけるユーザのニュース記事アクセスログから、ユーザのトピックに対する嗜好を計算する。
  3. ユーザのトピック嗜好ベクトルと、記事の持つトピックベクトルの相関が強いほど、おすすめスコアがUPする。
  4. 上で算出したスコアと、直近の記事アクセスランキングを掛け合わせて、各ユーザに対するおすすめ記事を算出する。

ここで、トピックモデルとは、テキスト中に出現する単語の頻度などに着目して、文書の話題(=トピック)を推定する自然言語処理の手法のことです。*1

Archimedesでは、上のフローのように機械的に算出した結果だけでなく、編集部が手動で設定したニュース記事を必ずリストに含めるような仕組みも設けています。

システム構成

ArchimedesのAPIは、AWS ECS上でDockerコンテナとして稼働しています。

ちょうど、1/10(水)に開催されたエムスリーTech Talkで、Archimedesのシステム構成について発表しました。
そのときのスライドを公開しておりますので、本記事と併せてご覧いただければ幸いです。

以下、スライドと重複する内容もありますが、システム構成について簡単に紹介します。

システム概要

f:id:progrhyme:20180113180147p:plain

Archimedesシステムの概要は上図のようになります。
※図では、ログ送信などのバックグラウンドのシステムや、VPC・AZ構成など細部は省略しています。

主なコンポーネントAPIを提供するフロントエンドのECSクラスタと、機械学習を行っておすすめ記事のリストを作る学習用ECSクラスタの2つです。

APIの開発言語はScalaで、Play Frameworkを使っています。
一方、機械学習関連のECSタスクはPython 3で開発しています。

また、おすすめ記事データを格納するデータベースとしては、MySQL互換のAmazon Auroraを利用しています。

Infrastructure as Codeの実践

Archimedesのインフラは(ほぼ)全てコードとして管理しています。*2

利用している主なツールは、以下の3つです:

Ansibleは、エムスリーの他の多くのチームでも利用されています。

PackerやTerraformについては、私は初心者だったのですが、公式ドキュメントを始めWeb上の情報リソースが豊富にあったこと、前任者が残していったレシピがあったことから、比較的すぐにキャッチアップできたと思います。

Immutable Infrastructureの実践

上述のようなシステム構成とツールを採用することで、Immutableなインフラを作ることができました。
つまり、アプリケーションやAMIに変更があったとき、容易に既存のサーバリソース(〜ECSクラスタ)を破棄して作り直すことで、更新することが可能です。

インフラ管理に加えて上記のような変更の反映もTerraformで完結させることができ、運用コストも抑えることができたと思います。

今後について

Archimedes APIを使ったニュース記事推薦の仕組みは、まだ一部ユーザに向けて動き出したばかりですが、今後エムスリーグループで提供する他のサービスへの展開も検討されています。

システム的には概ね安定しているのですが、優先度の高いタスクとしてパフォーマンス改善に取り組んでいく予定です。

終わりに

エムスリーでは、機械学習エンジニアの他、機械学習基盤を支えるインフラエンジニアや機械学習システムのAPI化を行うソフトウェアエンジニアを積極的に募集しています。
ご興味がありましたら、是非エムスリー・エンジニア採用ページからご応募ください!

*1:参考: トピックモデル (機械学習プロフェッショナルシリーズ) | 岩田 具治 |本 | 通販 | Amazon, トピックモデルによる統計的潜在意味解析 (自然言語処理シリーズ) | 佐藤一誠, 奥村学 |本 | 通販 | Amazon

*2:コード化できていないインフラ作業としては、SSL証明書発行や、EC2 Key Pairの作成などが挙げられますが、いずれも環境ごとに1回ぐらいしか作業しないようなものです。

AWS Parameter Storeから取得したパラメータを環境変数に設定するgemを作った

おはこんにちばんわ。エムスリーエンジニアの園田です。

先日 aws-ssm-env というRubygemを公開しました。

github.com

AWSParameter Storeからパラメータを取得して環境変数に設定するgemです。
dotenvParameter Store版と思っていただければ分かりやすいかと。

本稿ではgemを作ったきっかけ、簡単な使い方などを説明したいと思います。

Parameter Store とは

その前にParameter Storeとはなんぞや、ということですが。
Parameter StoreAWSのVaultサービスで、Key-Valueでパラメータを格納してAPIによる参照が可能です。
KMS(Key Management Service)による暗号化に対応しています。
詳しくは先日私が投稿したQiitaの記事を見て下さい。(手抜き)

qiita.com

なぜgemを作ったか

現在自分が担当しているサービスは4つあり、そのうち3つのアプリケーションがRailsAWSElasticBeanstalk上で稼働しています。

ElasticBeanstalkで稼働するアプリケーションに変数を渡すのは、ElasticBeanstalk環境変数にパラメータを設定してあげれば済むのですが、以下の問題がありました。

  • マネジメントコンソール上で平文で表示される。
  • EC2内の/opt/elasticbeanstalk/support/envvarsに平文でパラメータ値が出力される。
  • 上記ファイルのパーミッション644 で、EC2にログインすれば誰でも見れる。

これらの問題を回避するために、現在稼働中のシステムではフック処理でParameter Storeから取得したシークレット変数などを.envなど別のファイルに書き出してchmod 600してアプリケーションから参照しています。

このようにElasticBeanstalkは内部的にシェルで管理されていてエコシステムの恩恵を受けづらく、Parameter Storeから設定値を環境変数に設定する処理はアプリケーションごとに毎回シェルを書いていました。
bash -xで実行されるコマンドによりログファイルやCloudWatch Logsにシークレット変数の値が出力されていたという事故も発生しました。

なので、前回の投稿にもあるように、ElasticBeanstalkをやめてECSに移行したいという思いがありました。

www.m3tech.blog

ただECSとなると、環境変数を設定するためにはTaskDefinitionにパラメータを渡す必要があり、CloudFormationなどを利用して渡す必要が出てきて、現状terraformでインフラ管理している構成を大幅に変更しなければなりません。

他にもやりようはありますが、いずれもインフラ構成の複雑化を招くので外部からインジェクションするのではなく、アプリ側からPullする仕組みにしたかったというのがこのgemを作ったきっかけです。

まあ、実際にはOSSへの貢献経験が少なく、やってみたかったというのが一番の理由なのですが。

gemの使い方

READMEに詳しい使い方を記載してあります。ここではテンプレート的な使い方を説明します。

パラメータの登録

アプリケーションを起動する前に予めParameter Storeにパラメータを設定しておく必要があります。

aws ssm put-parameter --name '/myapp/production/RDS_PASSWORD' \
  --type 'SecureString' --value '<secret value>'
aws ssm put-parameter --name '/myapp/production/SECRET_KEY_BASE' \
  --type 'SecureString' --value '<secret value>'

パラメータ参照可能なIAMロール(ユーザ)作成

以下の権限を付与したIAMロールないしはユーザを作成しておきます。

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "",
      "Effect": "Allow",
      "Action": "ssm:GetParametersByPath",
      "Resource": "arn:aws:ssm:YOUR_REGION:YOUR_ACCOUNT_ID:parameter/YOUR_PATH"
    }
  ]
}

YOUR_REGIONYOUR_ACCOUNT_IDはご自身の環境に合わせて変更してください。
YOUR_PATHは後述のAwsSsmEnv#loadの引数に指定するpathを指定してください。

アプリケーション側の設定

例えばRailsの場合、以下のようにします。

# Gemfile
gem 'aws-ssm-env'
# config/application.rb
# configのブロック内に以下を追記。
AwsSsmEnv.load(path: "/myapp/#{ENV['RAILS_ENV']}", recursive: true)

これでrails startすればENVにパラメータが設定された状態で起動します。

dotenvと違ってgemを追加するだけで勝手に読み込んでくれたりはしません。config/initializersconfig/environmentsではなくapplication.rbに記載する必要があります。理由は2つあって、

  • Parameter Storeの階層設計はシステムごとに全く異なるためCoCにしづらい。
  • database.ymlを読み込む前にENVが設定済みな必要がある。

ので、今のところ直接application.rbのコードに記述する方式を取っています。
AWS_SSM_ENV_PATHなどの環境変数で渡してもいいかなと思いますが、環境変数のために環境変数を設定するというのも微妙な気がして、気が向いたら対応します。

注意: セキュリティレベルについて

実は前述の課題はあまり解決できていません。むしろ最後のものは悪化しています。

  • マネジメントコンソール上で平文で表示される。 => 解決
  • /opt/elasticbeanstalk/support/envvarsファイルに平文で出力される。 => 解決
  • 上記ファイルのパーミッション644 で、EC2にログインすれば誰でも見れる。 => 悪化

というのも、普通はEC2 Instance ProfileにPolicyを設定するので、EC2にログインさえできればaws ssm get-parameters-by-pathコマンドで誰でもシークレット変数が見えてしまいます。
これを回避するためには、EC2 Instance ProfileのIAMロールとは別にIAMユーザを作成しなければならず、その認証情報(AWS_ACCESS_KEY_IDなど)をどこかに持たなければなりません。
そうなると認証情報のバケツリレーになるだけで、認証情報の いたちごっご(インフラあるある) になってしまうのです。

なので、そもそもEC2にログインできるアカウントが限られている環境での利用を想定しています。
Parameter Storeの参照をCloudTrailで監視していればよりベターです。

ただ、パラメータを一元管理できるメリットは享受できるので、このgemを使わないよりは使った方が多くの場合は運用が楽になると思います。

Pull Request 募集中

Ruby歴がJava歴に比べて圧倒的に短く、なるべくRubyらしいコードに仕上げようとは努力しましたが、そうでない箇所も多数あると思われます。
ですので、Pull Requestは随時募集しております。

また、ドキュメントやソースコメントが日本語しかなく、社内のネイティブメンバーに改善をしてもらおうと思っていますが、翻訳のPRもお待ちしています。

一緒に開発してくれる仲間を募集中です!

エムスリーでは Ruby だけでなく、JavaScala、Elixir、Kotlin などが適材適所で大活躍してます。 一緒に開発する仲間を絶賛募集中です!メンバーとカジュアルに話す場も設けてますので是非気軽にお問い合わせください!

jobs.m3.com

エムスリーでの Elixir 開発環境 ver.2017 #m3dev

エンジニアの松原@ma2geです。 実はエムスリーでは数ヶ月前から Elixir を利用しております。現在私が関わっている商用プロダクトはグループ会社横断でエンジニアの有志が立ち上げたプロジェクトで、そのシステムの一部にて Elixir が稼働しております。Elixir が動いているのは広告配信に近い部分で多数の接続を捌く必要があり、アーキテクチャ検討時に普段チームで利用している Rails 含め検討した結果、適性のある Elixir を採用することに決定しました。

今回の記事ではエムスリーにおける現在の Elixir 開発環境周りについて書きます。書こうと思ったきっかけは melpon さんが Qiita に良い記事を公開されていたので、弊社からも1事例ということで公開できたらと思ったためです。

開発環境構築

少し前までは構築の手順があるだけで、以下のような課題がありました。

  • 環境構築時のエラー対応
  • Elixir 以外のアプリとの連携

環境構築時のエラー対応

Elixir のような新しい言語を採用し社内で扱っていくためには、普段別の言語で開発している人からするとそれなりに敷居があります。特に開発環境の構築では構築手順に従っていても環境差分のためにエラーが出たりということが起こりがちです。慣れていない言語で遭遇するエラーは時間も取られ嬉しくありません。

Elixir 以外のアプリとの連携

これは Elixir に限った話ではないですが、現在開発しているプロジェクトでは Elixir 単体のアプリケーションではなく、Rails で作られたアプリケーションと組み合わせる必要のあるものがあります。またほとんどのアプリケーションと同じでミドルウェア類も必要になっています。このため Elixir 単体の開発環境構築だけが簡単に済んでも、それ以外でつまづく可能性がありました。

Docker, Docker Compose で構築

これらを解決するために Docker と Docker Compose を使い、各種リポジトリを落としてきて docker-compose builddocker-compose up -d するだけで開発環境構築が完了するようにしています。今までの作業と比べてもかなりの手間を削減できていて、新しくプロジェクトへメンバーが加入してもすぐに環境構築し開発を始めることができるようになりました。余談ですがこれによって今週も Elixir を動かす開発者が一人増えました。

開発周りのルール

今のところ少人数ということもあり、そんなにルールはありません。今後開発者が増えるに従って随時変化していくでしょう。

コーディングルール

色々したいと思いつつ現状はそこまで人が多くないこともあり、コードレビューのみで進めています。Elixir 1.6 の formatter を心待ちにしています。1.6 が来たら CI と連携させるところまでやりたい。

テスト

テストは動的言語ということもあり多少手厚めにしており、カバレッジを 90% とることを目標にしています。カバレッジの取得には ExCoveralls を使っています。 カバレッジの取得方法については以前こちらに書いたので、よければ一例として参考にしてみてください。

デプロイ

弊社の園田@sonodar が書いてくれた "Elixir/Phoenix環境をElasticBeanstalk Custom Platformで構築してみた" の内容で構築されており、ElasticBeanstalk でのローリングアップデートによるデプロイをしています。Elixir のデプロイ周りはコンパイル済みバイナリを使ったデプロイが多いですが、ソースコード配置して起動しているところが珍しいかもしれません。

ただ Custom Platform を作る手間や、ElasticBeanstalk 周りのブラックボックス感が割と大変なこと、あと Distillery が割と標準的なリリース方法として使われているのでそっちに合わせたい、と、課題感も出てきており見直したいという話が上がっています。

ちなみになぜデプロイに Distillery のようなものを使うかについては、Adopting Elixir に載っていたので興味がある方は読んでみると良いと思います。

ライブラリ

ここまでで終わりにしようと思ったのですが、ちょっと分量的に寂しい感じになってしまったので現在使っているライブラリで主要なものを載せます。

今後使いたい

まとめ

ElixirConf 2017
ElixirConf 2017

4/1 の Elixir Conf Japan 2017 で LT した時には、まだ本番で動く Elixir アプリケーションがなかったのでフワっとした内容になってしまったのですが、今現在は Elixir が本番環境で動いていますので具体的な部分を書いてみました。

一緒に開発してくれる仲間を募集中です!

エムスリーでは Elixir だけでなく、JavaScalaRuby、Kotlin などが適材適所で大活躍してます。 一緒に開発する仲間を絶賛募集中です!メンバーとカジュアルに話す場も設けてますので是非気軽にお問い合わせください!

jobs.m3.com

エムスリー Advent Calendar 2017 始めてました

エムスリーエンジニアの松原@ma2geです。 今年も Qiita さんの Advent Calendar に参加していて中間地点に到達したので紹介します。 今年はやろうと決めたのが 12/1 の2日前で、その時点で2枠しか埋まっておらず完走できるか不安な感じでしたが、いつの間にか全て埋まって一安心しています。 例年エンジニアから自発的に Advent Calendar が開始されるのは気持ちがいいですね。

振り返ると 2015 年からエムスリーエンジニアの Advent Calendar をしていて、結果を見る限りでは毎年完走していました。各年の Advent Calendar は以下にあります。

M3 Advent Calendar 2017
今年も全て埋まりました👏

前半の振り返り

13記事現時点で公開されていますが、インフラからフロントエンドまで多様性のある内容が公開されているのはエムスリーの特色かなと感じます。

日付 執筆者 タイトル
12/1 juntaki JNIでCからJavaのライブラリを呼び出す
12/2 ma2ge GitLab CI で Elixir プロジェクトのカバレッジを取得する
12/3 suusan2go Spring BootでgRPCする
12/4 maeharin Rails Vue.js TypeScriptでthisの型推論
12/5 namutaka Reactのためのwebpack環境整備
12/6 naofumi-fujii codemodというリファクタリングに便利なツール
12/7 yuba カラムにnullが含まれるなら結果がnullになるmax()
12/8 yuba ファントムリードは起こらないのにSEIRIALIZABLEでない、それがポスグレのREPEATABLE READ
12/9 ngsw_taro KotlinでWebSocketクライアントを作ってモナーコインの取引を見守る
12/10 jooohn1234 Freeモナドで副作用を切り出す
12/11 yteraoka STNSの独自サーバーを書いてみた
12/12 progrhyme Docker ComposeからMinikube + Komposeに移行してみよう
12/13 christophrowley My Top 3 React Tips From 2017

これまでの記事だけでも言語では Java, C, Elixir, JavaScript, Kotlin, Scala, Ruby とあり、 Vue.js, React のフロントエンド、DB 周り、普段使っているツール、インフラ周り、英語記事などと様々あって飽きないですね。 エムスリー Tech Talk の記事でも紹介しましたが、 様々な技術知識を持った人たちが集まっているのもエムスリーエンジニアの一つの魅力なので、 それが Advent Calendar にも現れていて中の人視点で見ても興味深いものがあります。

後半に向けて

Advent Calendar を見るとわかることとして各社の雰囲気や特徴があると思っていますが、エムスリーの雰囲気も感じてもらえればと思い紹介しました。 後半戦は Kotlin 勢が強い感じですが、深層学習やら新規事業開発!?なんてワードも入っているので楽しみにしていてください。

今後のラインナップ

日付 執筆者 タイトル
12/14 etoriet BigQueryを使い込んで便利だと思ったこと
12/15 sonodar AmazonLinux2のVagrant boxを作成してみた
12/16 nishiba 論文読み "Attention Is All You Need"
12/17 reki2000 JEP 286 を調べていたはずなのにいつのまにか Kotlin で Hindley-Milner 型推論を実装していた
12/18 maeharin サーバーサイドKotlin (Spring Boot / Doma 2) 入門
12/19 oboenikui Kotlin/Nativeを使ってXcode + CLionでiOSアプリを書く
12/20 naokia テストしやすいクレジットカード決済をStripe+RSpecで作る
12/21 takayamag QAエンジニアがVue.jsと戯れてみた
12/22 yteraoka Bitwardenで自分専用パスワードマネージャーサーバー構築
12/23 nickhall Make Validation Great Again! Vue form validation with vee validate.
12/24 yamamuteki プロダクトマネジメントと新規サービス開発
12/25 jirokun Reactを使って本気でアンケートシステムをつくった

興味を持たれた方は是非 Qiita さんのこちらから購読してください。

エムスリーTech Talk 82回目 を開催しました! #m3dev

準社内向けの勉強会であるTech Talkの82回目を11/17に開催しました!今回はそのレポート記事です。

f:id:oboenikui:20171122113306j:plain

Tech Talkについては前回の記事をお読みください。

www.m3tech.blog

今回はGMOペパボのほんざわさん (@Joe_noh) も招いての勉強会となりました。

KotlinConf行ってきた + Google Assistant試した

Kotlinの実質的なエバンジェリストであるたろう (id:taro, @taro_ngsw) によるKotlinConf出張報告 & Google Assistant トライアルについて。

f:id:oboenikui:20171122114720p:plain

KotlinConfの発表内容で興味深いのが、何と言ってもAndroid・ブラウザ(JS)・iOSで共通のコード (common module) を使って書けるようになったことでしょう!

また、発表内容の内訳を見るともはやAndroid向けKotlinは当たり前と捉えられているのか、サーバーサイドKotlinに関する発表の数がAndroid Kotlinの発表よりも多かった (たろう調べ) 、というのもこれからのKotlinの発展に期待が持てる要素だと思います。

彼のサンフランシスコ滞在記はこちらの記事をご覧ください。衝撃の出オチです。

taro.hatenablog.jp

また、Dialogflowを使ってGoogle Assistantが某テーマパークのアトラクションの待ち時間を教えてくれるアプリを作った話も。 (Androidアプリの移植版)

残念ながら発表時のデモではGoogle Assistantさんが緊張していたのかちゃんと答えてくれませんでしたが、後日機嫌を取り戻してくれたようです。

個人的にはDialogflowを使うとGoogle Assistantさんは何故声変わりしてしまうのかが気になっています。

gRPC on Spring Boot (Kotlin)

今月からエムスリー社員となったすずけん (@suusan2go) による発表。

過去のブログ記事でも何度か紹介し、先日のJJUG CCC 2017 Fallでも発表したように、弊社の医師キャリア事業では複雑になったアーキテクチャのリニューアルにKotlin + Spring Bootを用いています。

今回の発表はその中で入社後すぐに行ったgRPCとSpring Frameworkを連携する部分の調査結果についてでした。

speakerdeck.com

プロダクトに使えるレベルとのことですので、実際に適用してみて果たして問題なくプロダクトで使えるのか!? Kotlin + Spring Frameworkアーキテクチャで問題は発生しないのか!? 今後に期待しましょう。

Vue.jsのユニットテスト

GMOペパボのほんざわさんによるVue.jsコンポーネント単体テストをする話です。

www.slideshare.net

Vue.jsは以前は公式のテストライブラリがなかったので、様々な非公式テストツールが作られていたようなのですが、最近まだベータ版ではあるもののvue-test-utilsという公式でメンテされているテストライブラリが登場しました。

ほんざわさんは業務でVue.jsを用いているそうで、実際に業務でコンポーネントのテストとしてvue-test-utilsを用いてみて、使い方だけでなくハマりポイントもレクチャーしてくださいました。

Promiseが使われる際問題になったり、他のライブラリが関与するとうまく動かなかったりと、なかなか難しいですね。

Understanding Filesystem using go-fuse, from scratch

前職ではNASの組み込みLinuxを触っていた滝安 (@juntaki) による、Goでファイルシステムを実装した話。

speakerdeck.com

彼はGo言語愛好家であるとともに前職との関連から低レイヤを触るのも好きなようで、以前はGoからJava資産を使うというネタを披露したのですが、今回はもっと実用的な話で、GoでS3をファイルシステムとしてマウントするプログラムを作った話です。

以前のスライド↓

speakerdeck.com

LinuxではFUSEを使えば、簡単にファイルシステムを作成できるそうです。

リポジトリも公開しているので興味のある方は読んでみてはいかがでしょうか。

github.com

懇親会

今回は外部参加者も数名いらっしゃったので、懇親会を開催しました。

f:id:oboenikui:20171122120036p:plain

f:id:oboenikui:20171122121228p:plain

テンションが若干低めに見えるとしたら撮影者(私)の技量の問題です…… 精進します…!

まとめ

今回は外部の方を交えてということで、いつも以上に賑やかな勉強会となりました。

Tech Talkでは、表立って募集はしておりませんが今回のように外部の方でも参加できる勉強会となっています。

もしご登壇・ご聴講を希望される場合は、こちらのフォームからご連絡ください!