RoomClip 開発者ブログ

あるエンジニアと勉強会、そして DroidKaigi

鷲田と言います。業務では RoomClip の Android 版の開発をするほか、時々 iOS 版を開発したりバックエンド側の開発に関する支援を行なっていたりします。

先日、DroidKaigi 2017 というエンジニア向けのカンファレンスがあり、そこでスタッフをやってきました。今年は公式サイトのメンテや司会をしたりしました。

ちなみに、DroidKaigi と前日準備と合わせて三日間業務扱いでした。 \(^o^)/ 重要なことなので言っておきたかった。

過去に参加した勉強会を振り返ってみる

本当は DroidKaigi の感想などを書こうかと思っていたのですが、すでにスタッフを含め色々な人によって良い記事が書かれています。なので今回の DroidKaigi の話は他の方に譲り、この記事ではエンジニアとしての今の自分に大きく影響を与えた勉強会について振り返ろうかと思います。

JavaOne Tokyo 2005

エンジニア向けの勉強会に初めて参加したのは、JavaOne Tokyo 2005 というサン・マイクロシステムズ主催のカンファレンスでした。その時は大学でバイトをしていて、その繋がりで JavaOne Tokyo に行きたい人の募集があったので参加しました。

実は当時は Cocoa 大好き Objective-C 大好きな Mac ユーザーで、Java というとネガティブなイメージを持っていました。それなのになぜ行ったかというと、「タダ飯が食べられる」「そもそも当時勉強会イベントがほとんどなかった」という事情がありました。

そんな軽い理由で参加したのですが、思いの外面白かったです。

当時印象的だったのは Sun のエンジニアによる Beans Binding という技術の発表でした。今の Android でいうと DataBinding のような、要するに「View と Model を綺麗に分離するためのオブザーバー機能」なのですが、これが Cocoa Binding という Cocoa の機能に近く、当時この種の機能に熱狂していた僕は同等の機能を見て「Java 思ったより頑張ってるな!」と思いました1

その他、レセプションでタダで夕飯を食べられたり本社の方と話ができたり iPod Shuffle が抽選で当たったりと…。色々と楽しくて、結果として Java に対する抵抗感が金による買収により消えていました。

JavaOne Tokyo 2005a JavaOne Tokyo 2005b
当時の JavaOne Tokyo の画像を探してきました。ちょっと画質が悪い

WWDC 2006, 2007

学生の頃は WWDC にも行きました。

WWDC 2006 WWDC 2006
昔の画像を探してきました。

WWDC は参加費が 10 万円程度かかる上にサンフランシスコまで行かないといけないのですが、学生向けに Scholarship というのがあり無料で参加する事ができました(旅費は有料)。当時は iOS アプリの開発ができるようになる前で、Apple の開発環境に興味がある開発者は今と比べると少ししかいませんでした。そういった事情で倍率も低かったためか、2 年連続で参加できました。

WWDC にも非常に刺激を受けました。参加した時は「WWDC なんて基調講演で新しい事がほとんど語られてしまっているんじゃないか」と思っていたのですが全くそんな事はなくて、技術的に新しい事や面白い事はむしろセッションの中で発表されていました。

また Apple のエンジニアと直接話せたり実際に開発をしているエンジニアと話ができたりと、学生の身分にとっては勉強になる時間でした。同じく学生で WWDC に参加した ninjinkun と知り合ったりもしました。

あと、他の参加者とカニを食べたりステーキを食べたりしてとても楽しかったです。

WWDC 2006 WWDC 2006
WWDC では毎年アーティストを呼んでライブをやっています。カニはWWDCでは提供されません。

読書会

WWDC に行った何年か後に社会人になったのですが、社会人なりたての頃は日本にはスポンサーが付くような大きなイベントは少なく、個人で開催する小規模な勉強会が多かったように思います。もう少し深く話ができる勉強会に参加したいと思い、カンファレンスよりも読書会に参加していました。

参加した読書会はProgramming with Quartz や Growing Object-Oriented Software Guided by Tests (実戦テスト駆動開発)といった比較的難しい本を題材にしていたものが多かった中、読書のモチベーションを維持する助けになったように思います(とはいえ終盤は脱落してました)。

また読書会は一人で読むのに比べ、エンジニアの先輩方による本の内容に対する考えや温度感といった事がわかるのが大きかったように思います。

そのあと社内やプライベートで読書会を開催もしたりしました(認識を合わせて議論したりするのがまた面白いと思うのですが、この話はまたの機会に…)。

DroidKaigi 2015 に参加

社会人になってからはずっと Web エンジニアをやっていたのですが、色々あって 2015 年あたりから Android や iOS のネイティブアプリの開発を始める事になりました。

そんな中、WWDC で知り合った ninjinkun が DroidKaigi で発表するというので、見に行く目的もあって DroidKaigi 2015 に参加しました。

当時は自分の周りにアプリエンジニアがとても少なく、Android の事はとりあえずコードを書くために必要な事しか知らないような状態だったのですが、Android のSDKにある色々な機能や流行りのライブラリなど、色々と新しい知識が得られました。

そして何よりも、「Android エンジニアコミュニティは思ったよりずっと活発だな!」という事がわかりました。若干浦島太郎感…。

ちなみに DroidKaigi はランチやアフターパーティーの料理がいつも豪華です(と思います)。

DroidKaigi 2016、2017 にスタッフとして参加

DroidKaigi 2016 の寿司 DroidKaigi 2017 の弁当
DroidKaigi の写真を探したのですが、食べ物の写真しかありませんでした。なお DroidKaigi 2017 に関しては後日公式から写真が公開されると思います。

DroidKaigi が終わってからしばらく後に、ninjinkun が次の DroidKaigi のスタッフ募集のツイートをしていて、それに応募しました。この頃は現状に退屈さを感じていて、もうちょっと変わった事をしたいと思っていました(ちなみにこの時期に前の会社から Tunnel に転職していたりします)。

イベント運営はもちろんやった事がなかったですし、Android アプリの開発はしていたものの Android コミュニティや他のエンジニアがどういった人達なのかもあまりよく知りませんでした。

運営を通じて Android 界隈のいろんなエンジニアと知り合いになれましたし(ちなみに DroidKaigi のスタッフに関しては全般的に優秀な人ばかりだなあというのが一番の印象です)、代表の日高さんはじめ色んな活動をやっている人がいて刺激を受けました。

来年は発表とかしたいですね。

勉強会が良かった事

これだけだと単なる思い出話なので、勉強会に参加してよかった事を振り返ってみたいと思います。勉強会に参加すると以下のような良い事があったと思います。

刺激を受け、選択肢が広がる

DroidKaigi を「知見の激流」という表現をしていた方がいましたが、これは本当にそうだと思います。勉強会には様々な人の知見が熱量とともに集まります。これは DroidKaigi や WWDC のような大きなイベントはもちろん、もっと小規模な勉強会でも本当に刺激を受けますし、それによって選択肢が広がったりもすると思います。

人との繋がり

WWDC で出会った ninjinkun 経由で DroidKaigi のスタッフになったように、縁で色々と選択肢が広がったりしました。実は Tunnel に入社したのは勉強会がきっかけだったりします。

勉強になる

他の人の知見によって本やインターネットとは違った質の理解が得られると思います。特に経験の浅い頃や慣れないプラットフォームを勉強する場合、チュートリアルやリファレンス的な情報だけでは得られない実践的な情報や、情報の集め方などのとっかかりのための知識を得られる事が多いです。

タダ飯

まとめ

JavaOne Tokyo 2005 で「これからは参加の時代」という話がありました。 その時から 10 年以上経っていますが、IT 業界では当時よりもずっとコミュニティが多様化し、参加が身近になり、また参加する事のメリットも増えてきていると思います。コミュニティには参加が難しいものも簡単なものもありますが、エンジニアは手頃な所から勇気を出して参加をしていくと、数年後はきっとより面白い事になっているんじゃないかと思います。


  1. 残念ながら現在はメンテナンスされていないようです。 ↩︎


この記事を書いた人:鷲田

Tunnel株式会社のアプリケーションエンジニア。よくうどんを食べている。


Amazon RekognitionとMachine Learningで画像判定機を気軽に作る

こんにちは、エンジニアの平山です。
昨今のAIブームのなか、引きの強そうなタイトルで思い切って記事を書いてみます。
ちと長くなるのでざっくりですすめますよ。

まずはじめに、
機械学習系の話題はテーマを絞り込まないとすぐに深い森に迷い込んでしまうので、
一番最初にそこだけはっきりさせましょう。
本日のテーマです。

「ある条件に合致する画像かそうでないか、を判断できる機械を作る」

例えば、
「部屋全体を写している写真なのか、そうでないのか」という判定はRoomClipでは少し重要な気がします。 もちろん、そうでない写真、例えば何かの接写であっても「問題がある」というわけではないのですが、 ときに「部屋全体が写っている写真に絞り込みたい」と思うこともあります。 そういったことを「画像だけで」判定する方法は、 Deep Learning旺盛の昨今たくさーんありそうですが、本気でこれを組もうとするとなかなか厄介です。 なので、できるだけ簡便に、出来合いのものだけで、ちゃちゃちゃっと作ってみましょう。

さて、まずは改めてテーマに戻ります。
余計な部分を省いて眺めると、

「...合致する画像か、そうでないか、を判断...」

とあるので、これはいわゆる「2値分類」とか言われる領域とわかります。
要は「TRUE」か「FALSE」か、仕分けるだけ。
この学習だけなら、Amazon Machine Learningで十分できそうです。

さて残る問題は、「ある条件に合致する画像」という部分。
画像に対して「条件」を作用させなくてはならない。
つまり、画像側が同じ「条件」で表現されていなくてはならない。
これは少し困りました。画像を入力としてある程度正規化された表現を得なくてはいけない。

過去多くの場合はここで止まっていたと思います。
が、現代は画像解析アプリケーションはゴマンとあるわけです。
その一つがAmazon rekognition。これを使えば非常に簡単にAPIをつかって画像解析ができます。 例えば僕の部屋の画像を使うと、、、

こんな風に「この画像に何が写っているのか」を簡易に解析してくれます。
ただ、もちろん万能ではありません。
限られたLabelと、そのLabelが指し示すものが写っているかどうかの確信度、
この程度しか現時点では得られません。(※随時年齢判定とか追加されたみたいです)
例えば、「Foodが80%の確率で写っていて、さらにDogが90%の確率で写ってる」とかそういうレベルです。

しかしそれでも工夫と根性で予測モデルを作ることはできます。
さっそく設計してみましょう。

Amazon Machine Learningの2値分類は、ロジスティック回帰と呼ばれる手法で学習するのですが、 これは一見難しそうに見えてとてもシンプルな方法です。
要は「回帰式」なのだから、
目的変数 = (説明変数×係数)の和
という式に最終的になるのです。
ロジスティック回帰の場合、この目的変数がざっくり「確率」になります。
今回の具体的な例で言うと、
「画像が部屋全体を写したものである確率」を目的変数として、
「Rekognitionによってつけられたラベルごとの確信度」を説明変数とします。
そうすれば、なんとなくRekognitionの結果をつかって、
画像が「部屋全体であるか、否か」を判定できそうです。

もっと具体的な話をすると、
ある画像をRekognitionにかけたとき、「90の確信度でRoom」「80の確信度でDinning」と仮に判定したとします。
この時、この画像はどれくらいの確率で「部屋全体の写真」と言えるのかを判断する式が、

(この画像が部屋全体である確率) = (Roomの確信度×0.005) + (Dinningの確信度×0.004)

で与えられたとしたら、

(この画像が部屋全体である確率) = 90 × 0.005 + 80 × 0.004 = 0.65

なので、65%で部屋全体の画像であることが言えました!

こういうことができれば、見事に目的を達成できます。
障害になりそうなのは、

  • 「どうやって0.005とか0.004とかの係数を計算すればいいの?」
  • 「RoomとDinningとか、どのラベルで判定すればいいの?」

という2つです。
前者も後者も「事前に人が判定した正解データ(=教師データ)」があれば、
あとはAmazon Machine Learningがなんとかしてくれます。
RekognitionとMachine Learningがうまく連鎖してくれそうですね。

じゃぁやってみましょう。

最初は根性フェーズです。
事前に正解のデータを作っておく必要があります。
試しに、3500件ほどの画像について人力で判定してみましょう。(え
ここでは3500件のうち、1800件が「部屋全体の写真」で、1200件が「そうではない写真」だったとします。

続いて、3500件の画像すべてをRekognitionに放り投げ、labelを取得しておきます。
仮にそのlabelの種類が全部で1000個ほどあったとします。
(Dog,Food,Room,Chair...などなど)
これは少し多い気がするので、そのうち「部屋全体」と判定された写真のなかで多く出現するlabelを50個。
逆に「部屋全体ではない」と判定された写真の中で多く出現するlabelを50個もってきましょう。
これで合計100個のラベルが手に入りました。

この時点で、1つの画像ごとに下記のようなデータが手に入っているはずです。

  1. 指定100個のラベルが写っているかどうかのそれぞれの確信度
  2. 部屋全体の写真なのかどうかの判定結果

これが3500件あるわけです。
今、部屋全体の写真なのかどうかの判定結果を、judgeというラベルで管理し、

  • 「judge = 1」なら部屋全体、
  • 「judge = 0」なら部屋全体ではない、

というふうに数値で表現するとしましょう。

すると、画像ごとに合計101個の要素をもったベクトルが手に入ります。
これをカンマ区切りで、3500行のCSVにしてみましょう。
やりました。これこそが「教師データ」となります。

あとはこれをAmazon Machine Learningに放り込みます。
放り込むときは「2値判定をする」「目的変数をjudgeとする」「説明変数を残り100個のラベルの値とする」というような設定をすれば、もう完了です。
本当にこれだけで完了です。
具体的な画面遷移はこんな感じ。

S3にあげた教師データを選択する。1click。


CSVの1行目がラベルであることを教える。2click。


目的変数がjudgeであることを教える。3click。


あとはVerify!を連続して3,4回ほどクリックすれば、
じっと待っているとそのうち計算が終わり、見事に予測できるようになるのです!
こうなれば、全く新しい画像であっても、まずはRekognitionでLabelとその確信度を取得し、 その値をMachine Learningになげつければ、「部屋全体」の写真なのかどうかをjudgeしてくれるわけです。

さっそく本番環境で使えるような具体的な仕組みにしてみましょう。

  1. S3に画像がアップされる
  2. それをトリガーにLambdaが起動
  3. Rekognitionに画像を放り投げLabelとConfidenceを取得
  4. それをMachine LeaningのEndpointに投げて「部屋全体判定」

びっくりすることに、EC2いらず、いわゆるサーバレスと言うやつです。
これにて、今回のテーマである、

「ある条件に合致する画像かそうでないか、を判断できる機械を作る」

ということがなんとか達成できました。
なんとなくブラックボックスが多いですが、出来合いのものだけでもソコソコのシステムが作れるよ、というお話でした。





なんですが、、、
やっぱり色々もっと詳しく知りたいですよね、という方向けに少しMachine Learningの知識周りの整理を。
ここからさきは興味ある人だけどうぞ。






まず、作成した予想モデルの精度評価はAUCという数値で表現されます。
これはROC曲線の積分値になるのですが、その辺周りの概説をします。

2値判定の予想の場合、その予想結果のリスクは大きく2つに別れます。
僕は勝手にそれを「誤解リスク」と「見逃しリスク」と呼んでます。

  1. 本当は0だったものを1といってしまうリスク
    => 無罪の人を有罪と言ってしまう誤解リスク
  2. 本当は1だったものを0といってしまう=リスク
    => 病気の人を健康と言ってしまう見逃しリスク

まず自分がやろうとしている予想が、
どちらのリスクを「最低限にしなければならないか」を決めないといけません。
今回の「部屋全体」判定の場合、
本当は「部屋全体」だったものを見逃すリスクより、
「外の写真」だったものを「部屋全体」としてしまう誤解リスクのほうを最低限にしなければいけません。
とにかく「部屋全体」の写真しか見たくないのですから。

本当は0だったものを1といってしまう誤解リスクは、
本当に0である要素のうち、どれくらいの割合1と予想してしまったのかという誤解割合が重要になります。
つまり、

1と予想したが本当は0だった数 / 実際に0である数

となり、もっと詳しく言うと、

(1と予想したがFalseした) / (1と予想してFalseした + 0と予想してTrueだった)

と表現でき、これを一般に、

FP / (FP + TN)

と書きます。 FPは False Positive といい、Positiveと予想したが間違えた、という表現となります。
TNは True Negative といい、Negativeと予想して、たしかに的中した、という表現です。

このレートのことを、
False Positive Rateといいます。
(MLのAmazon説明はこちら

このレートが小さいほど「1と予想して外すことが少ない = 誤診が少ない」と言えそうです。
実はこれものすごーく簡単に下げることができます。
シンプルに沈黙すればいいのです。
ビビり倒していれば少なくとも誤診することはありません。
しかしそれではなんの意味もありません。

よって今度は、予想結果のリスクではなく効能について考えてみます。
予想結果の効能は、1のものを1といえる力、つまり再現率が高いことです。
つまり1と予想して本当に1だった数が、実際に1だった人の数と近ければよいのです。
これを先程のルールで表現すると、

TP/(TP+FN)

となります。 True Positive Rateといえなくもないですが、一般にこれをRecall(再現率)と呼びます。

しかしよく見るとこれも問題のある指標です。
とにかく皆を1と判定しまくれば、たしかに実際に1だった人を全員1と判定することができます。

かくして、

「沈黙する」 vs 「何でもかんでも1という」

の綱引きが始まるわけですね。

ここで、リスクの話を思い出します。
今回の予想モデルでは誤診が一番の問題なので、これを最小限に抑えて、
再現率は努力目標としたいところです。
なので、とにかくこの誤診率=False Positive Rateを「どこまで妥協できるか」という覚悟を決めなくてはいけません。
仮にこのレートを5%まで、と決めたとしましょう。

すると、Machine Learningはとても便利なのでこのFalse Positive Rateをいじることができます。
Evaluationsの右下にある、このバーです。


まずこのFalse Positive Rateを0まで下げます。
するとRecall(再現率)の値も下がるはずです。これでは効能が得られない。
よって、ゆっくりゆっくりFPRを上昇させていきます。
するとRecallの値も上がっていきます。
そして、FPRが我慢の限界、今回は0.05ですが、この値まで上がってきたところでストップしましょう。
その時のRecallの値がこのモデルの性能と呼べます。

ところで、このFPRを調整しているとき一体何を調整していたのでしょうか。
それはモデルから出力される確率のしきい値=score thresholdです。
このしきい値を超えたscoreが出力されれば1と予想し、そうでなければ0と予想するという風にモデルを調整できるわけです。
極端な話、このしきい値を1としてしまえば、このモデルはすべて0と予想します。
いわゆる「沈黙」です。
逆にしきい値を0としてしまえば、何でもかんでも1と予想します。

さて、少し前に戻って、
FPRをゆっくりあげていったとき、きっとこう思ったはずです。
「FPR(誤診率)の上昇に比して、Recall(再現率)が凄い勢いで上昇すればいいのに」
全くその通りです。
FPRが0.01増えるだけでRecallが0.8くらいまであがってくれれば願ったり叶ったりですね。 乱暴に言うと、この度合いを示しているのが最初のAUCという値になります。
AUCが1に近ければ、この勢いが激しいと言えるケースが多くなります。
(厳密にはFPRとRecallのプロットの積分値。プロットの曲線が上に凸な増加関数であればAUCは高くなる)
よってこのAUCの値がざっくり「このモデルの全体的な性能」を評価していることになるので、まずもってこの値が重要になるんですね。
score thresholdは実際にそのモデルを使うシーンの覚悟によって異なるわけです。

以上で大体のAmazon Machine Learningでの2値問題処理をする時の重要な指標の説明となります。

ちなみに、
弊社で色々試行錯誤を重ねた結果、
300程度の説明変数、6000くらいの教師データ数で、そこそこの誤診率・再現率を叩き出しております。
ロジスティック回帰の係数を計算する方法(SGD:確率的勾配降下法)の調整などもできるようですが、 複雑なことが必要な機械学習であればもう実装したほうが良いと思います。 あくまでざっくり簡単な学習、というときにAmazon MLをうまく使いこなせると良いと思っています。

非常に長くなりましたが、今日はこのへんで。


この記事を書いた人:平山知宏

Tunnel株式会社のエンジニア。 インフラからサーバサイド全般担当。 インダストリアル系の家に住みたいと思うけど、子供もできちゃったし危ないから工具とかほっぽっとくのやめようと思っている1985年生まれ。