工場長のブログ

日々思ったことを書いてます。

6/3 200m坂ダッシュ*10

曇り。30℃

最近、辛くて避けてた坂ダッシュ。ひさしぶり。追い込みすぎるとまたやらなくなるので、ちょっとだけ手ぬるくやった。ペース的には以前よりも余裕を持って43''くらいで走れるようになったかな。次の日起きたら腰まわりががっつり筋肉痛。

  1. 45
  2. 41
  3. 41
  4. 41
  5. 44
  6. 42
  7. 41
  8. 44
  9. 48
  10. 44

5/30 - 1000m/3'30 * 5, 5/31 - 5km 3'50/km

5/30 - 1000mインターバル * 5

晴れ。26℃。

3'30設定。暑いから300m rest + 給水したのと、今週は平日に負荷かけてないからか、結構楽だった。

  1. 3’35
  2. 3’31
  3. 3’33
  4. 3’31
  5. 3’25

心拍数はもうちょっと上がっても良さそうなものだけど・・・

f:id:imai-factory:20200604090243p:plain

5/31 - 5kmペース走

晴れ。24℃

3'50設定。昨日の疲れが残っていて筋肉痛があったので、30分やるには最初から心が折れてたので、20分走(5km)に。30分でもギリギリいけたかもしれない、くらいの余力を残して終了。昼寝して起きたら身体中痛すぎて笑える。

 

f:id:imai-factory:20200604090422p:plain

f:id:imai-factory:20200604090422p:plain

 

5/4 4'/km * 8kmペース走

今日から練習日誌つけてみることにする。

所感

暑かったせいか、とてもしんどかった。前回同じ練習をやったときは12kmくらいいけそうだったけど、今日の状態だと10kmもいけなそう。これからもっと暑くなるので練習時間を夕方に移さなきゃかな・・

コンディション

  • 気温 23℃
  • 湿度67%

ペース

  1. 4'02
  2. 3'44?
  3. 4'14(イヤフォン閉まったりしていた)
  4. 4'02
  5. 4'02
  6. 3'51?
  7. 4'04
  8. 4'03

心拍数

閾値に届いていない。もっとペース上げたほうがいいのかな・・?一方、感覚的にはとてもキツいんだけどね。筋力が足りていない・・?

f:id:imai-factory:20200524225840p:plain

 

WebRTCでロボット操縦しようぜ!

SORACOM Advent Calendar 2019の12/19の記事です。一年に1回、この時期にだけブログを書いている気がします。

qiita.com

 

もともとは「ぼくのかんがえたさいきょうの再送ロジック」というタイトルで、IoTデバイスクラウドの間でやりとりされるメッセージの再送ストラテジの話を書こうと思っていました。

例えばデバイス -> クラウド方向に関して言えば、Raspberry Piのような比較的リッチなデバイスであれば、データ送信失敗時や通信がオフラインの時にはプログラム内のバッファやファイルシステム上にデータを保存しておいて、Availableになったらリトライすることが容易ですが、(基板として)ArduinoやWioLTE、M5Stackなどのマイクロコントローラでは、取りうる選択肢が非常に狭くなります。また、そもそもどうやってデータ送信失敗を検知するんだっけ?HTTPリクエストでやればいい?でもデータ通信量を最適化するためにはもっとプリミティブなプロトコルを使いたくなったりしますよね。じゃあどうするのがいい?みたいな話を書こうと思っていました。

 

が、今年のAWS Re:InventでKinesis Video StreamsがWebRTCをサポートしたので、これの「触ってみた」記事を書くことに心変わりしました。さーせん。(もし期待してくれていた方がいらっしゃったら)

Amazon Kinesis Video Streams で WebRTC による双方向リアルタイムメディアストリーミングのサポートを開始

あらためて

ということで今回は、ロボットにカメラを付けて、その映像をWebRTCでブラウザまで送信して、それをみながら遠隔操作する、というのをやってみたいと思います。もちろん、SORACOM使って。

WebRTCとは

ウェブブラウザやモバイルアプリケーション間でリアルタイムな通信(テキストなどのデータやり取りも可能ですし、音声や映像のやり取りも可能です。後者のイメージが強いかなとは思いますが)を実現するための仕組みです。モダンなウェブブラウザはWebRTCをサポートしていますので、追加のソフトウェアをインストールすることなくビデオ会議などが実装できたりします。

ざっくりとしたアーキテクチャは下記のようになっていて、Voice Engine、Video Engineというデータそのものの取り扱いの定義と、Transportというデータ転送についての定義 を持っています。更にSession Managementというピア同士のシグナリングを司るレイヤを持っていて、この部分はアプリケーションデベロッパに実装を任せる形になっています。(例えばWebSocketを使うなど)

(Architecture | WebRTCより抜粋)

Amazon Kinesis Video StreamがWebRTCをサポート?

実態は以下のようなコンポーネントです。

1. Session Managementサービス

  • WebSocketによって実装された、マネージドSession Managementサービスです。

2.  STUN/TURN

  • 実際のビデオや音声などのストリームのNAT越えを助けるためのサービス群です。
  • WebRTCのトラフィックUDP(上の図でいうSRTP部分で定義されています。RTP on DTLS on UDP)を利用していますので、インターネット越しに別々のNATルーター配下にいるクライアント同士が通信をするためにこういったヘルパサービスが必要になってきます。

つまり、ビデオや音声の転送そのものについてはクライアント同士で直接行われ、それを司る部分がサービスとして提供されています。

SFU(一度に多くの視聴者へ映像や音声を配信するための仕組み)も入っているのかなと思ったんですが、そうではないんですね。大規模配信はKinesis Video Streams(本体)でやろうぜって感じなのかな。

なぜWebRTCでロボットの遠隔操作?

で、やっと本題に近づいてくるのですが、なぜWebRTCでロボットの遠隔操作なんでしょうか。これまでのKinesis Video Streamsでもよくない?と思われる方もいらっしゃると思いますし、実際それでもOKです。私もやってみました。

例えばKinesis Video StreamsはHLSでの映像配信をサポートしていますので、RPiなどから送信された映像を簡単にブラウザにスケール感をもって配信することができます。

一方、パイプラインの詳細を見てみると、下記のようなマイクロバッチアーキテクチャになっていて、データソースからブラウザに届くまでの間に5〜10秒といった単位での遅延が挟まります。

もちろんこれでも、非常にゆっくり動くロボットや車であれば遠隔操作は可能ですが、ちょっとずつ、恐る恐る動かす必要があって、なかなかアレです。

f:id:imai-factory:20191219135226p:plain

WebRTCの場合、データソースのクライアントと閲覧者のクライアントが直接接続されてRTPベースのContinuousなデータ転送が実現できるので、より低遅延な配信ができるのではないか、というところを期待して試しています。(まあそういう意味ではRTSPでもRTPベースの通信はできるんですが。実はこれもSORACOM Napterで試しました。確かにレイテンシ小さかった。) 

やってみる

Turtlebot3(ロボット界のRaspberry Piのようなもの。たぶん。)を以下のような構成で動かしてみました。SORACOM Advent CalendarなのでもちろんSORACOM Air経由ですよ。

f:id:imai-factory:20191219171922p:plain

構成の詳細の前にまずはデモ。絵が小さくてすごいわかりづらいかんじになっちゃいましたが・・・汗、画面右側にいるのがTurtlebotです。ラップトップのブラウザに表示されている映像が、Turtlebotに搭載したカメラをWebRTC経由で受け取って表示しているものです。

どうですか?Turtlebotが回転するのに合わせてブラウザ上に表示される映像も回転していっているのが見て取れると思います。遅延は約1〜2秒というところでしょうか。(わかりづらいか。。)

前述のKinesis Video Streamsだと5〜10秒のレイテンシがあったのでだいぶ改善していると言えるでしょう。時速数km/hでうごくロボットであればまあ行けるんじゃないですかね。

ラズパイ上でH264へのエンコードも行っているので、そのあたりの遅延(おそらく1秒前後)も入っているので、この辺、まだ改善の余地もあると思います。

 

ということでWebRTC試してみました!絵が出た!ロボット動いた!!!めでたい!

How it works

では中身の話をしましょう。ロボットの操作は、上記の絵でいうUbuntuにログインしたターミナル上で(みんな大好き?)WASDキーを押すと、AWS IoT Core経由でROSのcmd_velメッセージ(Command Velocityの略称)がTurtlebotに対して発行されます。より厳密には、WASDキーを押すとごとに、前後方向への速度、回転方向の速度のあるべき状態(下記のようなイメージ)が更新され、それが約10Hzで定常的にメッセージ発行され続けます。(ジョイコンとかもきっとこんな実装ですよね)

 

currently: linear vel 0.0 angular vel -1.7
currently: linear vel 0.0 angular vel -1.8
currently: linear vel 0.0 angular vel -1.9
currently: linear vel 0.0 angular vel -2.0
currently: linear vel 0.0 angular vel -2.1
currently: linear vel 0.0 angular vel -2.2

ROSメッセージをMQTTにトンネルしてAWS IoT Coreを経由させるための仕組みとして、GROOVE Xのエンジニアさんが作っているmqtt_bridgeを使っています。

github.com

このbridgeを使うとROS->MQTT、MQTT->ROSの変換を行ってくれるので、ロボットとEC2、それぞれの出入り口にデプロイして上げるイメージです。それぞれ、ロボットもEC2も内部的には普通にROSプログラミングすればいい、というわけです。

で、これをAWS IoT Coreに接続するためのサンプルが下記に紹介されてるので、それを参考に行いました。ここでは詳細な設定等の説明は(長くなって力尽きそうなので省きます。それなりにハマりました。そのうち書くかもしれません。)

AWSOfficialなサンプルから参照されてるってすごくかっこいいすね、GROOVE Xさん!

github.com

 

あとはロボット側ですが、下記が素のTurtlebot3です。これに・・・・

f:id:imai-factory:20191219173906p:plain

 

こんなかんじにゴテゴテにいろいろ取り付けます。なんかかっこいい!!!!

f:id:imai-factory:20191219145101p:plain

 

Turtlebot3は制御用にRaspberry Pi3B+が載っているのですが、ここに直接手を加えるのは若干はばかられた(依存関係壊したりしてロボット動かなくなったらつらいし、リストアするためにイメージ焼き直して設定作り直してとかめんどうなので)ので、上記の構成図にあったように、もう一台別のラズパイを用意して、そこにカメラ、LTEモデム、前述のmqtt_bridgeとROS Master、Kinesis Video StreamsのWebRTCクライアントをインストールして、制御用ラズパイとEthernetで接続しました。このラズパイはクラウドゲートウェイみたいな位置づけですね。

Turtlebot3の制御用ラズパイは、もともと外部にROS Masterを持つ構成になっているので、この辺は簡単でした。クラウドゲートウェイのラズパイにDHCPサービスを動かしているので、接続して双方電源入れたらすぐにロボットがクラウドにつながるよ!(一点だけ、制御用ラズパイが参照するROS MasterのIPアドレスだけ設定の必要がありました。)

Kinesis Video StreamsのWebRTCクライアントについては下記のC SDKに同梱されているサンプルプログラムをそのまま利用しました。GStreamerからそのままWebRTCに流してくれるので、no configで動きました。サンプルコードの中を覗いてみると、以下のような感じでパイプラインが構築されていることがわかります。

gst-launch-1.0 autovideosrc num-buffers=1000 ! queue ! videoconvert ! video/x-raw,width=1280,height=720,framerate=30/1 ! x264enc bframes=0 speed-preset=veryfast key-int-max=30 bitrate=512 ! video/x-h264,stream-format=byte-stream,alignment=au,profile=baseline ! appsink sync=TRUE emit-signals=TRUE name=appsink-video

github.com

通信量/通信料はどうなの?

残念ながら今回はデモのために数分しか動かしていないので正確な通信量は測れていません。少なくともs1.fastで問題なく動いてくれることは確認しました。

映像を転送してるわけですし、MQTTも10mesasge/sec(操作だけで)飛ばしてるのでそれなりのデータ転送量は発生すると思います。ただ、遠隔操作っていつでも必要なものではなく、どちらかというと緊急時やイレギュラーなこと(ナビゲーションスタックがスタックしちゃったときとか、SLAMするときとか)をやるときに必要なものなのでぜんぜんありなのではないかなと思ってます。

あと、MQTTの部分はSORACOM Beamを使ってというのもありかなと思うんですが、若干メッセージの頻度が高いきがするので(cmd_velだけならまだしも、他のROSメッセージも転送しようと思うと50msg/secとか100msg/sec行っちゃう)、SORACOM Beamを入れるのがベストかどうか迷ったので今回は使ってません(いちおう、SORACOM Kryptonで証明書を払い出しはした)。ただ、やっぱり証明書を払い出したりインストールするのめんどくさいので、SORACOM Beamつかいたいすね。

まとめ

Amazon Kinesis Video StreamのWebRTCの機能を使うと、こんな感じのリアルタイムな遠隔操作なども現実的に、比較的簡単に実装できるようになってきますね!めでたい!マネージドTURN爆誕

 

(操縦しようぜ!って言ったわりには、ロボットを首振りさせただけですね・・・さーせんw

 

ソリューションアーキテクトという仕事について

ex-mixi Advent Calander 2017の12/12分のポストです。みんなエモいことを書いているのでわたしもエモいことを書くことにします。

 

ちなみに今日はわたしの結婚記念日で、嫁とはmixiで出会いました(物理/社内)。

 

で、テーマはソリューションアーキテクト(以下、SAと略します)という仕事について。もっとみんなにSAって何なのかというのを知ってもらいたいなと思ってます。SAってめっちゃ面白いよ!ってのを叫びたいのです。なお、本ポストではわたしがAWSでSAをやっていたときの話をしますが、あくまで経験や「わたしはこうやっていた」という話であって、AWSを代表する見解ではないことをご理解ください。

 

mixiには2010/2から2012/7までの約2.5年在籍していて、ずっと広告関連のアプリケーション開発をやっていました。自分のなかではものすごく体感時間長かったんですが、振り返ってみるととても短いですね。で、わたしはその後AWS、Hortonworks、SORACOMと会社は変わりながらですが、ずっとSAをやってます。

 

一緒に仕事したことあるひとから見ると、恐らくセールスエンジニアとか、場合によってはエバンジェリストのような仕事に見えてると思っていて、それはそれで合っています。が、実はそれだけではないので、そのあたりをみなさんにぜひ知ってもらいたいな、と。とてもおもしろい仕事なので、エンジニアのキャリアパスのひとつとして広まったら嬉しいなと思っています。

 

何をやる仕事なの?

 

AWSは馴染みが深い人が多いと思うので、そのときの話を例に話をすすめていきます。AWSのSAが何をする人かというと、お客さんのビジネスを成功させるため/困り事を解決するために、AWSを「うまく」つかってもらう、そのためにあらゆる技術サポートを提供するというのがお仕事です。

 

AWSを使うのがはじめてのひとが相手であれば「AWSで構築する○○(例えばWeb)アプリケーションのよくある構成」みたいなのを説明します。やりたいことはあるんだけどアプリケーションの設計がぼんやりしているお客さんに対しては、ホワイトボードを使ってアーキテクティングをしたりします。設計まで固まってきているお客さんであれば、その設計をもとにレビューをしたりベターなアーキテクチャを目指したディスカッションをしたりします。

 

MySQLの性能が出なくて困っているのであれば、問題がどこにあるのかを突き止めるためのサポートをしつつ、見えてきた問題に対してAWS的にそれをうまく解決するための方法を提案します。もちろん、アプリケーションやクエリの問題だったということがわかるということもよくあります。

 

ここらへんがSAの面白いところなんですが「あー、それはアプリケーションの話なんでそちらで解決してくださいね」みたいな話にはしないんです。アプリケーションの作りやクエリの中身、クエリの投げ方みたいなところまで突っ込んで議論をして、どうすれば問題が解決するのかというのがわかるまでしっかりDeep Diveします。問題の根本原因を見つけて、お客さんと共通の認識を持ち、それに対するアクションを作っていきます。もちろん、いきなり根本解決できればベストなんですが、動いているアプリケーションをいきなりドラスティックに変えることができないということもよくある話です。まずは暫定的にインスタンスサイズのアップだったりリードレプリカの追加、ストレージのIOをより速いものにしたりという対処をしつつ、アプリケーションの改修のディスカッションなんかをしたりします。

 

他にも、必要な性能や機能を満たせるかどうかの検証のためにアプリケーションを書いたり、関連するオープンソースソフトウェアのソースコードや、プロトコルRFCをがっつり読んだりなんてこともします。場合によっては、お客さんの利用料や利用傾向を見て「それ、アプリケーションをこういうふうに直したらもっと安いインスタンスタイプでも全然まわりますよ」なんて提案することもあります。

 

Do the right things的といいますか、お客さんのアプリケーションがより安定的に運用できるようになることであれば、正しいと信じられること/共感を得られることであればJust do itな感じであります。

 

なんでこんなことをするんでしょうか?それは「サステイナブルにプラットフォーム(例えばAWS)を使ってもらう」ということがとても大事だからです。AWSのようなサービスは売り切り型ではなくストック型のビジネスなので、一瞬の売上の高さをあげることよりも、長く使ってもらうことにより、高さだけでなく長さを掛けた面積を最大化することが重要です。

 

ニワトリタマゴな話なんですが、こういうビジネスであるからこそ、SAは技術者として全力でお客さんのヘルプができるというところがあるのです。理論的には、助ければ助けるだけお客さんのアプリケーションやビジネスは安定して、結果としてより長く、スケーラブルに使ってもらえるようになるので。

 

(いちお、誤解のないように補足をすると、AWSは有償のコンサルティングサービスも提供していて、様々なお客さんが、それぞれ必要とする形に合わせた最適なサポートを提供できるようになっています。)

 

SAの仕事をするなかでよく、お客さんからのTechnical Credibility(技術的信頼度)を得るのがとても大事だよねという話が出てきます。みなさんも、あまりよくしらない人にデータベースの選択やプログラミング言語の選択について相談しませんよね?SAは、お客さんからこういった相談をしてもらえるように、(言葉選びが難しいですが)尊敬されるエンジニアであることが必要です。さらにこの信頼度パラメータが高くなってくると「新しい○○という感じの事業を考えてるんだけど、どんな感じに作ったらいいすかね?」なんて相談を受けて「いいっすね!ちょっとホワイトボード使ってディスカッションしますかー」なんていう隠しイベントが発生する確率が上がってきます。

 

この信頼度パラメータをあげるためには、常に最新の技術事情にキャッチアップをしているのはもちろん大事なんですが、それとともに得意な専門分野を持っていることがとても大事です。わたしの場合mixiで広告配信をやっていたので「ハイトラフィックウェブアプリケーション、アドテク、Hadoop」みたいなキーワードが経験の主戦場で、AWSではゲームやアドテク関連の事業をしているお客さんたちを担当していました。

 

こういうのをアメリカの会社っぽくいうとArea Of Depth(AOD)と呼んだりします。AODにおいてはお客さんに対してリアクティブにアドバイスをするだけじゃなくって「あー、これってきっとこの辺りで困ってますよね?ですよね?そこはこういうパターンの実装をするのがベストプラクティスなんですよー(ドヤ」みたいな感じやっていくことが期待されます。

 

このAODを獲得できたのはまさにmixiのときの経験のおかげで、とても感謝してます。2010〜2012当時のmixiはとてもウェブのトラフィックが多くて、そこに対して安定的に広告を配信するアプリケーションを書いていたというのがすごくいい経験になりました。特に在籍期間の後半では「このゲームはあなたのマイミクの○○さんもプレイしてるよ!」みたいな、いまは当たり前になったソーシャル広告の開発をやってました。これって、広告(1ページにいくつも表示される)ごとにソーシャルグラフを始めとする、mixiの様々な箇所からデータを取ってくる必要がありました。コード的な依存関係を最小化し、かつ負荷的にもできるだけ影響を切り離しつつ、いかに高頻度でデータを取得できるか、みたいなことに毎日頭を悩ませてました。あとは、配信した広告をどれだけ早くレポート出せるかみたいなことにも毎日頭を抱えてました。コンソールに流れるmap reduceの進捗状況を「成功してくれ〜。落ちないでくれ〜。これ落ちたらレポート間に合わないんだよ〜。営業に殺されるよ〜。」と祈りながらよく眺めてました。こんな経験があったからこそ、「あるある」なハマりどころやツボをしっかり自分のこととして語り、負荷の高いウェブアプリケーションにおけるアーキテクティングのアドバイスみたいなことができるようになったのかなと。

 

と、なんか昔語りになっちゃいましたが、こういった経験や知識をもとにお客さんとガチでアプリケーションアーキテクチャのディスカッションをするというのがSAのしごとです。ゲームやアドテクの業界のお客さんは普段から○○千QPS、○○万QPSをさばいているひとたちなので、ガチです。

 

最後になんでSAになったのか、ソフトウェアエンジニアからジョブチェンジしてどうだったのかという点にも触れておきたいなと。やっぱり、はじめてSAになる瞬間はとても悩みました。「コード書けなくなってもいいのか?」みたいなことをすごく悩んで、ウジウジ悩んで、嫁に怒られて泣いたりしてました。最終的にはあんまりロジカルな理由はなくて、転職に対してサポーティブだった嫁のPushと「Amazonで働ける」というミーハーな動機で転職しました。

 

で、コードを書かなくなったかというとぜんぜんそんなことはありません。ガチなエンジニアや、ガチなビジネスのプレイヤーを相手にするということは、提案しているプロダクトやサービスについてお客さんと同じ目線で証明を提供することが必要です。それが場合によっては性能を実証のためのコードであったり、アプリケーションのプロトタイプだったりします。

 

サービスやプロダクトを開発するソフトウェアエンジニアとの違いとしては、お客さんの必要なものを汲み取りながら少ない時間で検証コードを書いて動かして見せて理解をしてもらうというサイクルを回すことが多いので、スプリンターのようにコードを書くことが求められることが多いです。(まあこれは好き嫌いあると思いますが)

 

と、つらつらと書いてしまいましたが、これがSAのしごとです。一文にまとめるなら、お客さんのビジネスややりたいことをよしなに(いい意味でいってる)、クイックに汲み取りつつ、それを成功させるために必要な「あらゆる」技術的サポートをするよ、という感じでしょうか。

 

いまはSORACOMというIoTのための通信プラットフォームを提供している会社でSAをやっています。これまではクラウドとかビッグデータ関連の技術をメインに触っていましたが、IoTでは「物理」とか「無線」とか、わたしにとって未知の世界が多くて四苦八苦してますw

 

SAワークとしては、プロトタイピングする対象がソフトウェアだけじゃなくてデバイスなんかも入ってきて、総合格闘技感あって最高です。楽しくやってます。(35才過ぎて、初めて真面目にシリアルとかUARTとか勉強したw)

広告に関するなにか: RedshiftでAttribution分析の実装

職場の同僚のドッグさん(語尾は上げる)「パイセン、Redshiftとか使ってイイカンジにAttribution分析するならどんなかんじなんすかね〜」って言われたので考えた。

Attribution分析とは

「あるコンバージョンに対して貢献のあった広告の表示やクリックなどの、要因分析」という感じだと思っている。例えば下記のような導線をたどった場合を考えてみる。

banner impression(1)
banner impression(2)
banner click
paid_search impression
paid_search click

このときにconversionに対して貢献度を下記のように割り振るイメージ。割り振りは適当。適当なんだけど、この数値の精度をいかに上げていくのかというのが恐らく一番大事な話なんだと思う。

banner impression(1): 5%
banner impression(2): 10%
banner click: 20%
paid_search impression: 15%
paid_search click: 50%

こうすると、ひとつのコンバージョンに対して複数の広告やクリック以外のアクションの貢献度を数値化することができ、より精度の高い広告の予算配分が可能になりますよという考え方。

「より」というのはどういうことかというと、よくAttribution分析と比べられる手法として、より昔からあったLast Click分析というのがある。この手法で先ほどの導線を分析すると以下のようになる。

banner impression(1): 0%
banner impression(2): 0%
banner click: 0%
paid_search impression: 0%
paid_search click: 100%

コンバージョン前の最後のクリックに対しての貢献度を100%としておくということになるが、この分析手法だと「search adの効果がよかったので、banner adの出稿を減らしてsearch adに予算を集中させましょう」ということになる。実際にはbanner adもこのコンバージョンに対しての貢献をしているにもかかわらず。

ではなぜAttribution分析が新しいかという話だが、理由はいろいろあると思うのだけれど、システム屋てきな発想としては、impressionまで分析対象にすることのシステムコストが大きな原因のひとつであると思う。

たとえばクリックだけを分析対象にするのと比べて、インプレッションまで分析の対象にすると処理するデータの量(ログの行数)だけで1,000倍くらいにになる。(広告のクリックレートを0.1%で計算した場合)更にラストクリックと違い、ひとつのコンバージョンに対しての貢献があるイベントを時系列に並べて、更に貢献度の時間的減衰を計算して・・・みたいなことをやるととても計算コストが高くなる。ログの行数だけで1,000倍なので、総コストで見ると1,000倍を軽く超えるはず。

実装を考える

前置きが長くなってしまったけれど、これの実装を考えてみる。

スキーマ

ログのスキーマはこんな感じとする。ものすごく単純化してるので注意。もっとたくさんアトリビュートはあるはずだし、一般的にはアクションごとにログを分けちゃうはず。

少しでも計算を楽にするためにログにsession_idという概念を取り入れてみる。広告への初回接触時にsession_idを払い出す感じ。で、conversionしたら破棄、もしくは時間が経ったらexpire、みたいな感じ。

{
  action:    '{impression|click|conversion}' // ユーザーのアクションのタイプ
  campaign_id:     'string',       // 広告のキャンペーンを一意に識別するID
  ad_id:  'string'      // 広告を一意に識別するID
  ad_type:   '{banner|listing}',     // 広告の種類
  uid:   'string',     // ユーザーのID
  session_id: 'string' // ひとつのコンバージョンに至るまでの一連のセッションにふられたID
  timestamp: 'timestamp' // アクションのタイムスタンプ
}

さっきの導線をログにしてみるとこんなかんじ。

{"action":"impression", "campaign_id":"123", "ad_type":"banner","uid":"abc","timestamp":"2015-01-01T12:34:56Z"}
{"action":"impression", "campaign_id":"123", "ad_type":"banner","uid":"abc","timestamp":"2015-01-01T12:45:21Z"}
{"action":"click", "campaign_id":"123", "ad_type":"banner","uid":"abc","timestamp":"2015-01-01T12:45:36Z"}
{"action":"impression", "campaign_id":"123", "ad_type":"listing","uid":"abc","timestamp":"2015-01-01T13:01:01Z"}
{"action":"click", "campaign_id":"123", "ad_type":"listing","uid":"abc","timestamp":"2015-01-01T13:01:21Z"}
{"action":"conversion", "campaign_id":"123","uid":"abc","timestamp":"2015-01-01T13:10:12Z"}

出力

とりあえずこんな感じのテーブルにデータを出力してあげたらあとはいろいろ計算できるはず

CREATE TABLE attributions (
  `campaign_id` CHAR(20),
  `ad_id`       CHAR(20),
  `ad_type`     CHAR(20),
  `action`      CHAR(20),
  `score`       REAL,
  `timestamp`   TIMESTAMP
);

計算の大まかな流れを確認する

では、上記のような出力をつくるためにはどんな計算をしたらいいんだろうということでRubyっぽい擬似コードを書いてみる。なんかMapReduceっぽくなった。これそのまま実装すりゃいいじゃんw

actions = [...] #すべてのログへの参照を持つとする
actions
  .group_by{|action| action[:session_id]} #session_idでgrouping。Map処理。
  .each_value{|grouped_actions| #Shuffle & Sort
    grouped_actions.each{|action| #Reduce
      puts    1/actions.size
              * action_factor(action[:action])
              * type_factor(action[:ad_type])
              * recency_factor(actions[-1][:timestamp] - action[:timestamp])
    }
  }

def action_factor
  #クリックやインプレッションなど、ユーザーのアクションによる係数を計算する
end

def type_factor
  #bannerやListingなど、広告の種類による係数を計算する
end

def recency_factor
  #コンバージョンを起点として、アクションのRecencyによる係数を計算する
end

あくまでここで書いているのは計算の流れであってアルゴリズムではない。上記のサンプル内の*_factorの精度をどうやってあげていくのか、というのが非常に重要なポイントであり、アルゴリズムの話。

SQLで実装しなおす

で、計算の流れに戻ると・・・actions.size....?

!!!

これはアレだ。ひとつの計算をするために複数行のデータを参照しなきゃいけないアレだ。Window関数が必要になってくる感じ。あんまり効率のいいSQLにならなそう。

ひとまず書いてみるとこんな感じだろうか。現状、RedshiftにはUDFを取り扱う機能はないが、めんどくさいのでとりあえず擬似コード的にUDFを使って書く。

SELECT
  campaign_id,
  ad_id,
  ad_type,
  action,
  (1/ COUNT(*) OVER (PARTITION BY sessiond_id)) * action_factor(action) * type_factor(ad_type) * recency_factory(LAST_VALUE(timestamp) OVER (PARTITION BY sessiond_id) - timestamp) AS score,
  timestamp
FROM actions;

UDFほしい

・・・はい。UDFほしいですね。いまのところだと、それぞれの*_factor関数をSQLにベタ書きで展開してcase文とか使ったら書けると思う。

しかしそれではメンテナンス性が低い。アルゴリズムの調整をするたびにSQLを修正しなきゃいけないというのはちょっとアレな感じ。去年のre:InventでUDF対応するよって言ってたので、期待して待つのがよさげ。

アルゴリズム

もう長くなってきたので、というのと集中力が切れてきたのでアルゴリズムについてはまた今度考えてみることにする。いちおう、今回の記事を書くのにあたって、いろいろ記事やらなにやらを掘ってみたんだけど、Quoraのこのスレッドがわかりやすくまとまっていてよかった。

What is a conversion attribution model in online advertising?

主に議論の対象になっているのは - バナーなのかリスティングなのか - クリックなのかインプレッションなのか - 3rd Partyのデータもいかすべき

みたいな話なんだけど、とりわけいい話だなと思ったのは下記のコメント。

It's important to distinguish between data-driven and user-driven models. By this I mean that some attribution solutions simply consist of user-assigned weights for each type of touch point. If you think that Search is 2X as good as Display, you can set that weight, and the results you get will be tilted towards search. This type of "model" is easy to implement, but not very objective since it relies on the marketer's existing intuition. Data-driven models, on the other hand, rely on statistical algorithms and extract the value of each channel from the data.

たとえばクリックだったらインプレッションの2倍みたいなアナログな手法よりも、もっとデータドリブンでやるべきということを主張している。

具体的には先程の*_factorな関数たちの係数を決めるためのパラメータを実際のデータから計算しようという話になるんだと思う。ではそれを実現するためにはどうしたらいいのか。例えば以下のようなデータを集めることが想像できる。 - 係数を分散させた複数の広告を出稿して、実際に効果のよさそうな係数設定を探っていく - 流れている広告主の出稿データと結果から係数の精度を上げていく

このあたりはまた今度ふかく考えてみることにする。

まとめ

ありません。次回(いつになるかは・・・)に続く!