Gunosy Tech Blog

Gunosyの開発メンバーが知見を共有するブログです。

仮想通貨マイニングに関するまとめ

こんにちは。 新規事業開発室の @mosa_siru です。

ブロックチェーンAdvent Calendar 14日目の記事です。

社内では新テクノロジーに対しての研究を一部で進めており、スマートスピーカーやVR/ARなどに加え、ブロックチェーンを現在調査しています。この研究・調査の一貫でマイニングについて調べてみました。

この記事では、マイニングの基礎知識と概観について、かなり平易に説明します。基本から順に説明していくので、知ってる人はどんどん飛ばして読んでってください。

※ビットコイン(BTC)のようなProof of Workを採用しているパブリックブロックチェーンを前提に話します。

マイニングって?

なんとなくかじったことがある人なら、ビットコインでは「マイニング」という大変な演算作業をするとお金(BTC)がもらえるということはぼんやり知っているかと思います。

マイニングはお金儲けの手段という側面もありますが、ブロックチェーンにおいては不正な送金や改竄がないか皆で検証するための重要な作業です。マイニング報酬は、その検証作業に対するインセンティブとなっています。

マイニング(検証作業)も、不正の抑制のために簡単にはできないようになっています。承認したいトランザクション(送金)たちを選び、まずは不正なものがないか確認します。そして、例えるなら皆でPC上でひたすらサイコロ25個を投げまくり、一番速く全て1のゾロ目を出した人がマイニング成功!!という仕組みになっています。とにかく沢山沢山演算できた人が当たりやすいわけですね。

ちゃんと言うと、ビットコインでは、 SHA256(トランザクション全体とか、前のブロックハッシュとか、nonce値) で計算されるハッシュ値が、運良くある値以下になった場合のみ検証成功となります。nonce値(なんでもいい)をドンドン変えて、その条件を満たすまで死ぬほど計算するのです。その検証の難しさを difficulty と呼び、期待される必要計算回数と比例します。

執筆時点で、ハッシュレートは1471万TH/s です。これは、マイナー全体で1秒あたり 1471万 * 10^12 回のハッシュ計算をしていると推定されることを意味します。もはやよくわからないですね。 このハッシュレートは、最強マシンのAntminer S9(14TH/s)でいうと100万台です。やばいですね。。

f:id:mosa_siru:20171214160455p:plain https://blockchain.info/ja/charts/hash-rate?timespan=1year ハッシュレートはどんどんあがってます。

条件を満たすnonceの発見に成功すると、そのブロックを他のマイナーに送信します。皆が検証してそのブロックを認めてくれると、各自のメインチェーンに追加されることになります。検証自体は、含まれるトランザクション等を検証した上で、ハッシュ値をちょろっと検算するだけなので楽ですね。 大抵は競争に負けるので、送られてきたブロックを検証した上で、次のブロックの作成作業に移ることになります。

二重送金などしたい悪い人は、適切なnonce値を自力で見つけて、他の全マイナーより速くブロックを積み上げて最長チェーンにしないといけないので大変です。 「中国の大型マイナーがけしからん!!」みたいな議論はよくありますが、彼らがいるからこそネットワーク全体のハッシュレートが増えて、ポッと出のマイナーが事実上改竄できなくなっているのは間違いないです。 ※もちろん、中国のマイニングプールが結託したら51%を超えるのも事実ですが、それをやってバレたところで保有している大量のBTCが暴落するのでやらないのでは?という見方もあります。

最長チェーンやオーファンブロック、分散型コンセンサスなどの説明は、今回は省略しますmm 個人的には、それぞれが本当に自分のためだけに利益追求してるだけなのに、良いかんじに全体で最適化され合意が形成される(そうでない行動は損するようになっている)ってのは割と鳥肌モノでした!

マイニング報酬

約10分ごとに1ブロックがマイニングされます。マイニングに成功した人には基本報酬 + 皆が払っている送金手数料の合計がもらえます。

現時点では 基本報酬12.5BTC + 手数料 4BTC くらいですね。つまり運良く検証に成功すると、3000万円分のBTCがもらえます。ヤバイですね。10分に1ブロックなので、1日約45億円がうまれているわけです。宇宙ヤバイですね。

(余談ですが、収益最大化のためにマイナーはなるべく手数料の高い送金を優先してブロックに入れることになります。これが、手数料を高くすると送金が早くなる理由ですね。)

送金手数料

ただし基本報酬は4年ごとに半減期をむかえ、2140年にはなくなってしまいます。(これにより、インフレしなくなることが保証されています。)

なので、どこかしらで手数料が報酬のほとんどを占めることになります。このとき、マイナー収益の維持のために単純に手数料がより多く必要になるのか、BTC価格が上がっているから特に必要な手数料は変わらないのかは、よくわかりません。

傾向的には、送金手数料はどんどん上がっており(2017/12/13で平均¥2680)、送金が早い安い美味いとかって何だったの?という話になっていくでしょう。(このへんも、ブロックサイズ上げて延命しようとか、Lightning Networkなどの新技術でなんとかしようとか、送金は他のコインでやればいいじゃんとか、いろんな議論があります。)

f:id:mosa_siru:20171214162028p:plain https://bitinfocharts.com/comparison/bitcoin-transactionfees.html 平均手数料は1年間で100倍に

difficultyの調整

「マイニング儲かるぞ!」とマイナーがたくさん参入すると、ハッシュレートがあがって早くブロックが採掘されることになり、供給スピードが上がってしまいます。 そのため、ハッシュレートが上昇してもだいたい10分に1回のペースで検証成功になるような難易度に補正される仕組みが入っています。

ただし、BTCは2016ブロックに1回のタイミングで調整されるので、普通は調整まで10分 * 2016 = 14日くらいかかることになります。 そのためやや弾力性に欠け、「Bitcoin Cashの方がマイニング儲かるぞ!!!」などで急激にマイナーが離れてしまうと、1ブロック10分でマイニングがおわらずに2016ブロック後の難易度調整に行き着くまでに途方もない時間がかかり、送金が大幅遅延するという事態が起きるかもしれません。

一方で、一時期のBitcoin Cashのように調整のペースが早く急激だと、収益性の高いコインを求めてBitcoinとBitcoin Cashの間でマイナーが行ったり来たりすることにもなります。この前BCHに修正が入るまでは以下の図のように実際に起きていました。 f:id:mosa_siru:20171214162217p:plain:w500 https://bitinfocharts.com/comparison/hashrate-btc-bch.html#6m

ハッシュレートと価格の関係

マイナーは、掘るために用意したマシン代や、電気代くらいは最低限回収できる金額でBTCを売りたいですよね。 そのため、ネットワーク全体のハッシュパワーは、BTC価格と関係してくることになります。

価格 => ハッシュレート 

例えば、BTC価格があがればマイニング報酬(円建て)もあがるため、より多くのマイナーやマシンが参入し、ハッシュレートが上がることになります。 逆に価格が下がった場合、別のコインのマイニングに移行したり、電気代で割に合わなくなったマイナーが撤退するなどで、ハッシュパワーは下がるでしょう。

ハッシュレート => 価格 

一方で、全体のハッシュパワーが上がると、競争率があがって1ハッシュパワーあたりの収益性が落ちるため、マイナーは収益性を維持するためにこれまで通りの価格ではBTCを売らなくなるかもしれません。

このへんは鶏と卵ですが、たしかに過去の遷移をみると関係してるかも〜〜というのが見て取れます。

f:id:mosa_siru:20171214161825p:plain:w500 https://bitinfocharts.com/comparison/hashrate-price-btc.html#1y

ただし、価格上昇からのハッシュパワー上昇に関しては、すぐにマシンが手に入るわけではないため、やや弾力性に違いがあるかもしれません。

ハッシュアルゴリズムとマシン

ビットコインのハッシュ関数はSHA256ですが、それぞれのコインは異なるハッシュアルゴリズムを採用している場合があり、それによって最適なマシンが変わってきます。

ビットコイン

ビットコインのマイニングにはASICというSHA256に完全特化した集積回路が使うことが一般的で、この有無によってマイニング収益の差が桁違いであるという状態です。 歴史的には、CPU => GPU => FPGA => ASIC とブレイクスルーがあるたびにハッシュレートは急激に増加してきました。

現時点では、ASICを手に入れるには中国のBitmain社から専用機材(Antminer S9など)を買うのが一次ルートですが、一般向けではほとんど売切状態です。Antminer S9は定価15.5万円ですが、現在転売では70万円程度まで価格が上昇しています。

f:id:mosa_siru:20171214162552p:plain:h300

https://shop.bitmain.com/main.htm?lang=en

いかにこのマシンを手に入れるかが勝負を分かつため、Bitmain系列の中国のマイニングプールが強い理由もそこにあります。余談ですが、Bitmainから買うにはBitcoin Cashで払う必要があるのが面白いですね。 また、HalongMining社のDragonMintやGMOさんなどの、Antminerよりも性能の良い回路を作る動きもあります。

ライトコイン

ライトコインでは「Scrypt」というASIC耐性のためにメモリを必要するアルゴリズム(雑な理解)を採用したようですが、現在では攻略されてBitmain社で専用のASICマシンが売られています。そのためハッシュレートが急増しています。

GPU系

その他、イーサリウムはEthash, MoneroのCryptonight, DASHのX11 など、色々なアルゴリズムが採用されています。 このへんはGPUを複数挿ししたマシンで掘るのが一般的なようです。

また、GPUの中でも、AMD Radeonはイーサリウムに、NVIDIA GeForceは他に適しているなどの特徴もあると聞きます。 Radeon 470などは普通のGPUだったのですが、電源効率が良いためマイナーのせいで本当に市場から姿を消してしまったようです。。 そういう意味ではASICと違って、GPUは使い終わったら中古でも売れるかもというところが嬉しいですね。

どのアルゴリズムではどんなマシンが向いているかは、こちらで確認できます。 f:id:mosa_siru:20171214163819p:plain https://www.cryptocompare.com/mining/#/equipment?f1=SHA256

マイニングコインの選択

ビットコインとビットコインキャッシュなど、ハッシュアルゴリズムが同じコインについては、基本的にマイナーは儲かるコインをその都度選択して掘ることになります。 そのためBTC価格があがればBTCを掘るし、その逆も然りです。

この前ハッキングされたという噂の「Nicehash」というマイニングプールは、儲かるコインを自動判断して掘る機能がついていました。まるで広告のSSPみたいですね。

同一マシンで掘れるコイン同士は、マイニング収益性の変化による乗り換えで、ハッシュパワーや価格が大きく変動する可能性があります。例えば、ビットコインは2016年の半減期(基本報酬25.0 => 12.5BTC)では特にハッシュレートは減りませんでしたが、2020年の半減期では、収益性が減った結果ビットコインキャッシュにマイナーが流れるなどのリスクがあるかもしれません。

収益性

マシン代をどれくらいの期間で回収できるか(payback)は1つの判断材料になります。

f:id:mosa_siru:20171214163507p:plain:w500 https://www.cryptocompare.com/mining/#/equipment?f1=Equihash

こちらで、中国の一般電気代におけるpaybackの目安がかいてあります。 ただし、マシンを定価で手に入れられた場合であることに注意しましょう。

電気代は、日本では 約 ¥22/kWh に対し、中国では¥10以下という話を聞きます。最近進出する企業が多い北欧ではもっと安いという話も聞きます。地熱でエコだし、寒くてマシン冷却に抜群ですからね! その他にも、土地代/人件費/故障率などの各種変数があるでしょう。また、より収益性の高いマシンが出てきたときに、古いマシンの収益性がガクっと落ちるリスクがあるため、償却期間を適切に見積もる必要があるかもしれません。

マイニングへの参加

ソロ

家のPCで早速始めることができますが、なかなか大変です。 うまくいけば1発で3000万円もらえますが、あまりにも確率が低い宝くじです。

マイニングプール

そのため、ボラティリティを抑えるために、マイニングプールに参加するという方法があります。

これは、みんなで頑張って計算して、誰かがうまく掘れたら頑張った分に応じて皆で報酬を分かち合うよというものです。手数料もあるため報酬の期待値は少しだけ下がりますが、分散をぐっと抑えることができます。

「頑張った分に応じて」を適正に評価するために、difficultyを緩和して、やや楽になった問題を何回解けたか(ハッシュ値が緩和された値以下になったか)を見ているようです。例えるなら、皆でサイコロを10個振って誰かが合計10を出すまで大量に振り続けるとして、その各自の試行回数のカウントを自己申告ではなく「合計20以下を出した回数」で評価するようなものです。 とはいえマイニングプールも、ビットコインの場合は専用のPCを用意しない限り、日本の電気代で行うのはそもそも期待値がマイナスであるかもしれません。

クラウドマイニング

クラウドマイニングに投資するという方法もあります。こちらは、自分では一切マイニングせず、マイニングしてる企業に投資して擬似的なハッシュパワーを買い、定期的にマイニング収益を分配してもらうという方法です。企業側は、集まった投資額をマシンの購入や増設に充てます。 こちらはお金さえあれば気軽に始められますが、その企業の判断で収益に合わないマイニングを停止されてしまったり、最悪の場合もち逃げされたらどうしようもない等のリスクがあります。そして、人気のあるクラウドマイニングはすぐに売切れになってしまいます。

マイニングプール側の工夫

先程説明したとおり、「Nicehash」というマイニングプールには、GPU系で儲かるコインを自動選択して掘る機能がついていました。 他にも、大手プールはちょっとでも有利に採掘できるように、いろいろと工夫しているらしいです。

運良く早く採掘できたら、ブロックのブロードキャストをあえて少し遅らせて、自分のマイニングプールだけで次のブロックを先に掘りはじめてしまう戦略があるようです。(他のマイナーに比べて次のブロックで優位にたてます。もちろん、その間に他のマイナーがマイニング成功してしまうリスクがあります。)

このへんは、マイニングプールのクライアント上で、ブロックチェーン外のネットワークを築いて色々やっているのかもしれませんね。

マイニングの税金

国税庁のFAQによると、なかなか厳しい課税なように見えます。(とはいえ僕は専門ではないため、以下はあくまで僕の解釈です。)

マイニングしてBTCを得ることに成功したとすると、

 (1)取得時点のBTC時価 - マイニング初期投資 が課税対象

さらに、BTCが価格上昇したとすると、マイニングで得たBTCで円にしたりモノを買ったりすると、

 (2)利確時点の BTC時価 - 取得時点のBTC時価 もさらに課税対象

と、2段階で取られます。大変ですね。

マイニングプールで細かく収益を得るパターンだとか、減価償却って何年なのとか、クラウドマイニングは?実際に税務署は計算できるの?など疑問はつきませんが、とりあえず大変なようです。運用可能かは抜きにして、とりあえず取れるだけ取る方向に倒してると解釈すればいいのかなと。

余談ですがこのFAQに従うと、BTC値上がりした状態でETHとかにかえて(課税対象)、ETHが大幅値下がりして年をまたぐと、税金で破産してしまうことになる気がします。大変だ!!

おわりに

というわけで、長々と説明してきました。

色々とドメイン知識が必要そうで、個人での参加は容易ですが儲けるにはなかなか大変そうですね・・・!

一方で、すでにいくつかの日本企業も参入を表明しておりますし、日本のハッシュパワーが高くなって中国一強の業界全体が健全化しないかな〜と勝手に思っております!

宣伝

Gunosyでは、blockchain.tokyo を主催するなど、ブロックチェーンや仮想通貨に関する調査をしています。もし興味がある方いましたら、お気軽にお声かけくださいmm

blockchain-tokyo.connpass.com

re:invent必須アイテム gunosy.fm #7 #gunosyfm

かとうです。しばらくぶりのgunosy.fmです。大変お待たせしました。

第7回目はあいぼうさん(@aibou)とAWS re:Invent 2017について話しました。

ポッドキャストでは会場の様子に加え、今後日本から参加される方にも参考になるre:Invent期間中の過ごし方についても話題にしているので、ぜひ聴いてくださいね。 (なお、途中機材トラブルでかとうの声が消えております。ご了承ください。)

また、参加した詳細なレポートは前回の記事にもありますのであわせてご覧ください。 tech.gunosy.io

トーク中に話題になったリンクはこちらです。

Podcastの購読はこちらからお願いします。 また、感想や話題にしてほしいことなどはTwitterなどで #gunosyfm のハッシュタグでご意見お寄せください。 次回の更新もお楽しみに!

re:invent 2017に参加してきました!

f:id:saori-oota3:20171204174024j:plain開発・運用推進部のおおたです。
SREチームに所属していて、普段はパフォーマンス改善やAWSとのインテグレーションツールの開発を担当しています。

re:invent 2017(11/26~12/1)に参加してきましたので、主にServerlessセッションに関してこちらにまとめを書きたいと思います。
Serverlessとは言っても、Lambdaに限らず、DynamoやGlueまで幅広くご紹介したいと思います。

Optimizing Serverless Application Data Tiers With Amazon DynamoDB 10:00@Aria

アマゾンPrimeDayの構成モデルの内容です。
ユーザー情報をKinesisで受けて、Lambdaで関数からDynamoDBへ情報をアップデートする構成です。
弊社のアプリのニュースパスでも、Kinesis->Lambdaの構成はユーザログを中心に使っています。

Dynamoに入れた情報をLambda経由で、S3,Lambda,DynamoDBへ置いてそれを分析するイメージです。
TTLを上手く使い、古いデータはS3に置くなどしてDynamoDB上には新しいデータだけを配置しています。
ただし、これをやるには手前にあるKinesisはスケールしないので、ユーザ数の増加に合わせてシャードを増やす必要があるとのことです。
その他のLambdaのThroughputはShardに合わせてスケールし、Dynamoもスループットに合わせてスケールします。
DynamoDBについては、構造とアクセスパターン、書き込みなどを最適化するようにするといいとのことです。

続いてCapital One Bankの例。アメリカでは10個の大きい銀行に入り、45百万の顧客がいるとのことです。
スピード、データの量、データのセキュリティでAWSを選んだとのことです。
バッチの部分を3階層のLambdaに分けて最後にDynamoDBに書き込みしています。

ベストプラクティスとしては、データをよく知ること、スケールのテストをすること、アクセスパターンを知る、VPCエンドポイントを使う、暗号化をする、Lamdaのコンテナ再利用をするとのことでした。

How We built a Mission-Critical Serverless File Processing Pipeline for over 100 Million Photos 14:30@Aria

ExpediaのHomeAwayという避暑地の宿泊サービス(190ヵ国!)をServerlessにした例です。
S3,Lambda,DynamoDB,Kinesisを使用して実現しています。
もともと9年前にJavaで書かれたアプリで、NFSのストレージに保管していたのをAWSに移行。

HomeAwayで使われている写真のサイズを変えて保存するシステムの説明です。
AWSにして、アプリがLambdaのおかげでスケールする、S3によってストレージコストが下がる、DynamoDBのメタデータが扱いやすい、マルチリージョンで高い可用性を保てるとのことです。

筆者も昔高いストレージを運用していたのでS3を利用するようになってコスト(と運用!)がかからなくなった点は非常にわかります。
X-Rayでのユニットテスト、Dead Letter Queueの監視もするといいと書いてありますが、Dead Letter Queueがあると異変に気づきやすいので良いと思います。

Severless Authentication And Authorization 13:30@Aria


APIGatewayとLambdaを使ったServerless API

これに加えて、Serverless AppでID管理をするために、Cognito User Pool、Cognito Fedelated Identieies,IAMを使います。
サインインはソルトを使ったハッシュで管理するのがベスト、またはSRP(セキュアリモートパスワード)を使います。

ここのDemoにおいても、API Gateway=>Lambda=>Dynamoの黄金パターンの登場。それらにCognitoを挟むだけになります。
ただし、API GatewayのPOST/DELETEなどのアクションはAdminRoleだけにすることです。
3つの認証方法のやり方について解説されています。
ニュースパスでは、Federated Identitiesを使ってまさにこのやり方で行っています!

ちなみにこのSessionのDemoアプリに関してはGitHubにあります。 https://github.com/awslabs/aws-serverless-auth-reference-app

Multi Region Serverless 17:30@Aria

まだ筆者が担当のサービスはマルチマスターを使用していないので、興味深いと思い、聞いてみました。

マルチチージョンでのAPI GatewayのRoute53を利用して実現します。
マルチマスターだと大変さもある。マルチリージョンのACIDトランザクションは非現実的とのことです。
書き込みは常にマスターリージョンで、読み込みはそれ以外のリージョンですること。
マルチチージョンでもログは1箇所(片方)にまとめます。

f:id:saori-oota3:20171204165713j:plain

Serverless Application Model(SAM)があり、CloudFormationはServerlessに最適化されています。
http://github.com/aws/labs/serverless-application-modelにも詳細があります。

さらに、ここにAWS CodePipelineを組み合わせるとより効力があります。(codecommit,codebuild,cloudformation,SAM)
マルチリージョンの構成は複雑になるので注意深くやるべきとのことです。
マスター/マスター構成のレプリケーションは、複雑になり、顧客体験もよくないのでシンプルにやるべきとのことです。

f:id:saori-oota3:20171204165823j:plain
Route53を使用すればfailoverチェックが行われるが、メトリクスやアラームは仕掛けるべきとのことです。

Serverless ETL With AWS Glue 14:15@Aria

4 stepでできるETLの紹介です。
s3=>AWS Glue Crawler=> AwS Glue Data Catalog=>Athena/EMR/Redshift Spectrum<= BI Toolで分析。

f:id:saori-oota3:20171204170209j:plain

使っている顧客にはNTT Docomo, Expedia,有名な車の会社などがあります。
スキーマ変更、データソースの変更、データボリュームの増加、フォーマットの変更をコードで対応できます。
パフォーマンスについては、Year, Month, Dayであった時に、ファイルサイズが小さい方がパフォーマンスは出るとのことです。
f:id:saori-oota3:20171204170134j:plain

10DPU,Spark2.1.1でJson=>CSV変換でやった時の結果で差があります。 同じことをSparkとGlueでやると、Sparkは640KファイルでOOMになり、Glueは1.2Millionファイルまでできたとのことです。
(Crawlerの1タスクにつき自動にグループされたファイル)

またDevEndpoint機能でZeppelinが使え、pysparkコードを書いていきます。
Glue transforms:
toDF()
Spigot()
Unbox()
Filter(),Map()
Join()など

近々、Scalaがサポートされ、新たなリージョンとして東京(!!!)とアイルランドで使えるとのことです。

残りの時間で質疑応答がありました。
- Jobが遅いのはDPUを増やしてほしい。
- EMRとの違いは、EMRはmanagedであり、serverlessでない。Glueはそれに比べてオペレーションが少ない。 EMRはHadoopエコシステムの1つの位置づけ。

Deploying Machine Lerarning Models of AWS Services, AWS EMR , Batch, Lambda 8:30@Venetian

EMR(Spark)でのDeploy,Batch Deploy,ECS Deployの方法について、App Developper,App Engineerの登場人物を含めたやり方の解説でした。
Chalk Talkなのでセッションの半分は質疑応答中心でした。

ここでもDepoloyはCodeBuildを使ったやり方が推奨されていました。
この図でもわかるように、使い分けが重要です。
f:id:saori-oota3:20171204170322j:plain

感想

今回、初めての参加でしたが、遊び心があるイベントでまた機会があれば参加してみたいと思いました。
特に日本のイベントにはないような、DJやライブを挟んだりする構成は観客をひきつけてやまなく、非常に興味深く感じました。

re:Playの様子です。 f:id:saori-oota3:20171204181527j:plain
f:id:saori-oota3:20171204181615j:plain

今回、参加して感じたことは、Serverlessの流れは、サーバーに止まらず、機械学習やETLにまで発展してきているということで、それらを上手く組み合わせて利用するのが重要だと感じました。

また、特にこちらでは触れませんでしたが、KeynoteにもあったようにAWSは今後も省力化、セキュリティ、インターフェースには注力していくとのことで、この流れはサービス全体にも広がりそうです。

まだ一部しかアップデートが来ておらず、東京リージョンでは使えない新サービスもありますが、出てきたらまた弊社の事例などをご紹介したいと思います。

最後に、そんなAWSの新しいサービスを使って開発したい、使いこなしたいSRE・サーバサイドエンジニアを絶賛募集中です!ご応募ください!

hrmos.co

hrmos.co

Gunosyのパーソナライズを支える技術 -ワークフロー編-

この記事は Gunosy Advent Calendar 2017 4日目の記事です

qiita.com

はじめに

こんにちは、データ分析部のy-abeです。 パーソナライズシリーズの続きになります。

tech.gunosy.io

tech.gunosy.io

今回はワークフロー編です。 パーソナライズにおいてユーザーや記事の素性抽出や、モデル作成をするコンポーネントや記事リストを生成するAPIが必要です。 それらのコンポーネント間でうまくデータを取り回すためにはワークフローが重要です。 ワークフローは、いわばシステム上における兵站といってもいいでしょう。 「戦争のプロは兵站を語り、戦争の素人は戦略を語る」という名言もあるくらいです。

f:id:y-abe-hep:20171204183158j:plain

さて、パーソナライズ記事配信のタスクの流れをざっくりいうと、 ユーザーと記事の素性を集めて整形(ベクトル化) -> 機械学習でモデルを作成 -> 素性とモデルを利用し、ユーザー毎に記事リストを生成・配信 という流れになっています。 詳しい背景やアーキテクチャについては上で紹介した記事をご参照ください。

ワークフローがやることはバッチ系の各タスクを関連付けて実行することで、素性を集めるところとモデル作成を管理しています。 具体的には以下のことを行っています。

  • 配信候補記事リストの作成
  • Hadoopクラスタの起動と機械学習タスクの実行
  • 記事スコアの計算
  • ハイパーパラメータの自動最適化

今回は2番目に挙げたHadoopクラスタの管理について書いていきます。 (他のタスクは言ってみればスケジューリングをしているだけなので・・・)

候補記事生成やハイパーパラメータの自動最適化あたりの詳細については、またいずれ紹介したいと思います。

Hadoopクラスタの起動と機械学習タスクの実行

機械学習タスクは Spark on EMR で行います。 このタスクはバッチ処理なので常時起動させる必要は無く、また、たくさんのインスタンスを起動するため、結構なお金が飛んでいきます。 そのためクラスタはタスクが実行される都度作りタスクが終わった時点で停止させる運用をしています。

DigdagではEMRを直接操作できるオペレータがありますが、細かな操作(インスタンスプロフィール等)ができなかったため、EMRはPythonオペレータを使ってPythonのコードを実行しています。

タスクは以下のように切り出しました。

  1. EMRでHadoopクラスタを作る
  2. 機械学習タスクを行うステップを追加する
  3. モニタリング
  4. タスクが終わったら学習結果を通知して終了

クラスタを作る際、 KeepJobFlowAliveWhenNoSteps というオプションを False にしておき、全タスクが終了するとクラスタが停止するようにします。

docs.aws.amazon.com

モニタリングの段階では describe_cluster というメソッドを使ってクラスタのステータスを監視します。 クラスタのステータスが TERMINATING になることを検知して次のステップに進み、学習の結果を通知して終了します。

おわりに

今回はDigdagを用いたGunosyパーソナライズのワークフローについて紹介しました。 特に、EMRを利用して機械学習モデルを作成するフローについて取り上げました。 DigdagはPython以外にもShellスクリプトRubyなどにも対応しており、柔軟にワークフローを設計することができました。 Digdagは社内随所で利用されてきており、バッチ処理のみならずembulkと組み合わせて分析用のデータフローにも利用されています。 このあたりもまた後日紹介したいと思います。

Gunosyのパーソナライズを支える技術 -1クリックで始まるパーソナライズ-

この記事は Gunosy Advent Calendar 2017 の3日目の記事です。
昨日の記事はaikizokuさんの現場で役立つAutoLayoutのTips集でした。

はじめに

ニュースパス開発部の koid です。
この記事は、先日 @mathetake が投稿した、下記の記事の続編になります。
tech.gunosy.io

プロジェクトの背景的な部分は、上記の記事を読んでいただきたいのですが、

1. ユーザーはクリックするたびに社内ではファインマンベクトル呼ばせている呼ばれているベクトルがリアルタイムに生成かつ更新される

ちょうど、このファインマンベクトルユーザベクトルを、ユーザが記事をクリックする度に、リアルタイムに更新する部分に関わったので、そのときの話を書きたいと思います。

下のアーキテクチャっぽい図でいうと、ちょうど上半分の話になります。

f:id:y-koid:20171201185700p:plain

課題と背景

弊社では以前からも様々なプロダクトで、行動履歴ログからベクトルを生成していました。が、ユーザの興味嗜好の変化をより早く反映、新規ユーザの趣味嗜好をより早く反映し、最適な情報を届けるために、旧来のバッチ型アーキテクチャから脱却し、リアルタイム(イベントドリブン)でのユーザベクトル生成を目指しました。

アーキテクチャ概要

ユーザベクトルの生成の流れを簡単にまとめると、下記の流れになります。

  1. 記事収集と同時に、記事の持つ様々な特徴量を駆使しdenseな記事ベクトルを生成
  2. ユーザが過去にクリックした記事(およびその際のcontext)のリストを生成
  3. 上記クリックした記事のベクトルにcontextによって重みを付け、その和を正規化しユーザベクトルを生成

f:id:y-koid:20171201201941p:plain
それぞれ、図中のそれっぽい箇所に対応しています。

ベクトル自体の話については前回と同じく大人の事情により余り多くは語れないので、それ以外の部分で語っていきたいと思います。

ユーザの行動履歴(クリックした記事・context)の保持

冒頭でも触れましたが、いわゆるバッチ的に生ログから更新対象ユーザのログを検索し、時系列順にソートして取得するのは、なかなか時間もマシンパワー(すなわち💰)も必要でした。

リアルタイムにベクトル生成をするために、ユーザID一発でGetすることができて、そして揮発しないログストアが欲しく、今回は、DynamoDBのList型を利用することにしました。(下記は雑なイメージです)
f:id:y-koid:20171201184258p:plain
DynamoDBには1項目あたり400KBという制限がありますが、クリックした記事と少しのcontextに限定すれば、そこそこの件数が詰められます。1logのサイズからListに詰められる件数を逆算し、list_appendで詰められるだけ詰め(push)、溢れそうになったらremoveで古いlogを追い出していくことで(trim)、ユーザID一発で至近N件のlogをGetできるようになりました。

これを、クリックログが入ってくるKinesis StreamsをTriggerとし、Lambda内でUserID毎にPush/Trimすることで、ほぼ遅延なくDynamoDBに反映することができました。

ユーザの行動履歴の更新に合わせ記事ベクトルを取得

ユーザの行動履歴が更新されるたびに、ユーザベクトルの更新が走ります。ユーザベクトルの更新のためには、最大N件の記事ベクトルを取得(BatchGet)する必要があります。
アーキテクチャの図にもありますが、DynamoDBに入っている記事ベクトルを素直に毎度Getしていると、レイテンシの懸念だけでなく、油田(すなわち💰)も必要になります。

こういうケースの場合、当然前段にキャッシュサーバを挟むと思いますが、ただのGetOrSetならいざしらず、BatchGetOrSetを真面目にコード書くのは面倒です。
下記、弊社エンジニアmosa_siruの資料ですが(良い資料です)、直感的にはわかるもののいざ自分でコードを書き始めてみようとするとなかなか大変です。

そこで DynamoDB用のインメモリキャッシュである、DynamoDB Accelerator (DAX) を利用しました。

aws.amazon.com

DAXを利用するとこんなイメージです(すごい雑なイメージです)

f:id:y-koid:20171201220428p:plain

BatchGetOrSetのOrSetの部分は、DAXの中で隠蔽してやってくれるので、そこを意識したコードを書く必要がなく、大変楽です。また、memcachedやredis同様、ローレベルTCPでの通信となるため、非常に低いレイテンシでレスポンスを返してくれます。

これもまた、行動履歴を保存したDynamoDBの更新(DynamoDB Streams)をTriggerにし、Lambda内でユーザベクトル生成と組み合わせることで、最終的にAPIサーバから参照されるDynamoDBにユーザベクトルを反映することができました。

おわりに

現状、新パーソナライズAPIでの計算をどんどん複雑なものにしていきたいという背景もあり、レスポンス速度を維持するために今のところは一旦、ユーザベクトルの生成はあえてリクエストベースにしていません。

しかし、今後新パーソナライズAPIでのロジック(計算量)が固まっていけば、リクエストベースでのユーザベクトル生成にもトライしていけるかなと考えています。

余り多くは語れませんでしたが、詳細が気になるという方、高速なAPIと機械学習アルゴリズムを設計実装する機械学習エンジニア並びにそれを支えるサーバーサイドエンジニアを絶賛募集中です!ご応募ください!

hrmos.co
hrmos.co
hrmos.co