こんにちは、エンジニアの平山です。
昨今の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つの画像ごとに下記のようなデータが手に入っているはずです。
- 指定100個のラベルが写っているかどうかのそれぞれの確信度
- 部屋全体の写真なのかどうかの判定結果
これが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してくれるわけです。
さっそく本番環境で使えるような具体的な仕組みにしてみましょう。
- S3に画像がアップされる
- それをトリガーにLambdaが起動
- Rekognitionに画像を放り投げLabelとConfidenceを取得
- それをMachine LeaningのEndpointに投げて「部屋全体判定」
びっくりすることに、EC2いらず、いわゆるサーバレスと言うやつです。
これにて、今回のテーマである、
「ある条件に合致する画像かそうでないか、を判断できる機械を作る」
ということがなんとか達成できました。
なんとなくブラックボックスが多いですが、出来合いのものだけでもソコソコのシステムが作れるよ、というお話でした。
なんですが、、、
やっぱり色々もっと詳しく知りたいですよね、という方向けに少しMachine Learningの知識周りの整理を。
ここからさきは興味ある人だけどうぞ。
まず、作成した予想モデルの精度評価はAUCという数値で表現されます。
これはROC曲線の積分値になるのですが、その辺周りの概説をします。
2値判定の予想の場合、その予想結果のリスクは大きく2つに別れます。
僕は勝手にそれを「誤解リスク」と「見逃しリスク」と呼んでます。
- 本当は0だったものを1といってしまうリスク
=> 無罪の人を有罪と言ってしまう誤解リスク - 本当は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をうまく使いこなせると良いと思っています。
非常に長くなりましたが、今日はこのへんで。

この記事を書いた人:平山知宏
ルームクリップ株式会社のCTO。 インフラからサーバサイド全般担当。 インダストリアル系の家に住みたいと思うけど、子供もできちゃったし危ないから工具とかほっぽっとくのやめようと思っている1985年生まれ。