CLOSE

Solve "unsolved" image recognition problems in service applications(全1記事)

機械学習を用いた画像分類における「未解決問題」を解くためにやったこと

2018年2月10日、恵比寿ガーデンプレイスザ・ガーデンホールにて、「Cookpad TechConf 2018」が開催されました。Cookpadのエンジニアやデザイナーがどのようにサービス開発に取り組んでいるのか、またその過程で得た技術的知見について公開します。続いて登場したのは菊田遥平氏。「Solve "unsolved" image recognition problems in service applications」と題して、人間以上の精度を出すとされていた機械学習による画像認識の理想と現実、そしてそれにまつわる「未解決」な問題の解き方を語ります。

画像分析にはどんな困難があるか

菊田遥平氏(以下、菊田):Thanks Polly. みなさんだいぶお疲れですか? 発表はあと2人なのでもうちょっとがんばってください。私が一番おもしろい話をする。

(会場笑)

……ようにがんばるのでお願いします。今日は、実業務における機械学習を用いた画像分析というお話をしたいと思います。

今、研究開発部で画像分析をしている、菊田遥平といいます。レジュメをGitHubに公開しているので、詳しいものが見たい方はぜひこちらをご覧ください。

あとは@yohei_kikutaというアカウント名でTwitterもやっています。これは今、硬派マシンラーニングツイッタラーとして名を馳せているので、興味がある人はフォローしていただければと思います。

今日は、まず研究開発部の紹介を少しさせていただいて、その後、実業務で画像分析を入れるときにどんな困難があるのかという説明をします。その困難に対して我々は具体的にどういう取り組みをしているのかという例として、「料理きろく」と「レシピ分類」という2つのプロダクトの話をします。

さらに、今取り組んでいる新しいチャレンジとして、モバイル実装の話をして、まとめさせていただきたいと思います。

「画像の分類問題はもう解けている」のか?

研究開発部はどんなチームかというと、まず、メンバーは学生のアルバイトの方々も含めて19人です。1年半ぐらい前にできた時は4〜5名の小さいチームだったんですが、今は大所帯になって、いろんな取り組みをしています。

例えば、料理の専門知識を持っていてデータにラベルをつけてくれるアノテーターの方や、そのデータを使って機械学習をしたり、新しいデバイスを用いて新しい料理体験を提供しようという人や、食文化を研究する人など、いろんなメンバーがいるチームになっています。

具体的な取り組みとしては、画像や自然言語処理をやったり、Alexaでスキルを作ったり、あとはインフラ基盤周りやサービスにおけるアーキテクチャを設計したり、食文化の研究をしたりなど、いろんなことをやっています。

いろんな話をしたいんですが、ここで話しすぎると時間がなくなってしまうことを賢い私は知っているので、あとで詳しい資料を公開します。

今日はこの中でも特に画像分析について話したいと思います。実際に画像分析はすごくよく使われていると聞きますが、それを実サービスに持っていくときにどんな難しさがあるか。その話を最初にしたいと思います。

ただ、こういう話をすると、ちょっと画像分析やったことある人なら「画像の分類問題はもう解けたと聞いたんですが」という疑問も出るかもしれない。

実際、解けました。解けたと言いきるのは、機械学習がすでに人間よりも高い性能を発揮するようになっているからです。

機械学習は「理想的な状況」でしか問題を解けない

これはImageNetのclassificationのタスクですが、「画像を与えられて正しいカテゴリのラベルを選ぶ」という問題に対して、人間が間違える確率はだいたい5パーセントぐらいです。

しかし、機械学習のモデルはその半分以下の誤り率になっている。

この意味で、もう解けています。我々人間が機械に教えることはもうない。ありがとうございました。終了。……となれば楽ですが、もちろん話はそんなに簡単ではなくて、この「解けた」という言葉には強い条件がつきます。「理想的な状況」だったら解けている、ということです。

理想的な状況とはなにかというと、例えば1個の画像に対して適切なラベルが付与されている。あとは適切なカテゴリが設計されている。これは見た目で判断するので、当然、見た目で分類できるカテゴリでないといけません。

例えばケーキの画像があって、「クリスマス用」と「誕生日パーティ用」というカテゴリがあったら、画像だけからは判断できませんよね。そういう問題は当然ですが解けません。

3つ目はclosed setということで、ひと言でいうと、学習データの分布とテストデータの分布が違う場合です。例えばラーメンの分類をするために、学習データをいっぱい集めて、塩ラーメンとか味噌ラーメンとか分類できるモデルを作ったとします。

そこで、実際にデプロイしてみたら、ケーキの画像が来ちゃったとします。このときモデルはどういう振る舞いをするかというと、自信満々に例えば「塩ラーメンです」とか、一番似てると判断したラーメンを答える。これは学習とテストのときでぜんぜん違うので、やっぱりうまくできない。つまり、理想と現実は違います。

まず「本当に解きたい問題はなにか」を分析すべき

適切なラベルの付与は、十分データがあればモデルを学習できるぐらいのデータセットを構築できるので、数でカバーできるという意味で三角ぐらい。

カテゴリの設計はもうバツです。そもそもサービスは必ずしも機械学習用に作るものではないので、解きたいカテゴリーが機械学習に適していない場合は往々にしてある。

当然closed setもバツで、基本的にはopen setです。学習時とテスト時にぜんぜん違うデータが来る。

これだけでもう「画像分析って難しそう」と思うかもしれません。ですが、そもそも論として、実はサービスで解くべき問題というものは、多くの場合、間違っています。

我々はこういう問題を解きたいと思ってプロジェクトを進めますが、進めていった結果、実は我々が解きたい問題はもっと違うことだったとか、違う切り口から攻めなければいけなかったということが明らかになってきます。これが実プロジェクトだと思います。

そこで我々がすべきなのは、試行錯誤を繰り返して、本当に我々が解きたい問題は何かを具体化して、かつそれを機械学習で解ける問題に落としこんでいくことです。それができて、ようやく機械学習のプロダクトがサービスに乗るようになっています。

これは画像分析に限らず、機械学習全般、もっといえばサービス開発全般に成り立つ話だと思います。

今日は具体的になにをするかというと、まず「料理きろく」という、画像に対して料理か非料理かを分類する機能について。それからレシピ分類という、料理の画像に対して適切なレシピのカテゴリをアサインする際の分類問題についてお話します。

最後はモバイル実装についてです。プライバシーの問題の解決や、computational resourceの意味でのスケーラビリティの確保という話をしたいと思います。

実際に運用してみないとわからないことが多い

というわけで「料理きろく」の話ですが、進化と現在ということで、実は去年のTechConfでも研究開発部の染谷さんがこの発表をしています。

まずサービスの説明をしますと、ユーザーの携帯端末に入っている画像の中からCNNを使って料理の画像だけを引っ張ってきて、カレンダー形式で表示します。それを見れば、自分がどんな食事をしてきたかがひと目でわかるわけです。「つくれぽ」を送りたいときは、その画像をタップすれば、よく見るレシピとセットで出てくるというサービスです。

非常に多くの方に使っていただいていて、現時点のユーザー数はほぼ20万人です。累積の料理枚数もほぼ2,000万枚というぐらい、継続的にうまく運用できているサービスです。

ここで我々が解きたい問題はなにかといったら、すごく簡単ですよね。与えられた画像を、料理か非料理かに分類すればいい。でも、実はそうではなかった。機械学習の観点から重要な点はなんだったかということです。

まず、解きたい問題が実は違ったということを知るには、運用して結果を見なければいけない。なので、クイックスタートというのは非常に重要な点でした。

まだ研究開発部にそんなに画像分析に強い人がいない段階から、ユーザーに届けられるサービスを、まずCaffeNetでいいから実装してサービスにインしようということで、これを実際にプロダクトに出した。

出した結果、すごくいろんなことがわかってきました。「画像分析にもっといいモデルがあるんだからいいモデル使おう」という基本的なことから、例えば植物はサラダに似てるから間違えやすい、赤ちゃんは形・フォルムから間違えやすいといった発見もありました。

運用モデルは試行錯誤を重ねてできている

単純な2値分類だと思っていたけど実は間違えやすいやつを、特別にハンドルしてマルチクラスにして、そのあと2値分類に落としこむほうが精度が上がるというようなことがわかってきた。

そういうことを知るために、テストデータはめちゃくちゃ重要です。我々はユーザーのデータを見られないので、実際にデプロイしたモデルがうまくいっているかどうかはわかりません。それを知るためには、できるだけ実際の環境に近いものということで、従業員からスマホのデータを集めて、限られた人だけが触れるようにしてテストするということをしていました。

そうやって運用していくと、また新たな面が見つかってきます。例えば、料理が画像の一部にだけ写ってるもの。料理自体はしっかり写っているのに、ほかにいろんなものが写っていたりすると「料理じゃない」と判定されてしまうんですね。

これは我々のサービスとしては望ましくないので、もう少しローカリティを取り入れないといけない。そこで、我々はパッチ化というモデルを考えました。

パッチ化とはなにかというと、部分的な料理画像を判別したい、でもセグメンテーションはtoo muchというときに、折衷案として、画像を例えば14×14のパッチに分けて、それぞれのパッチで「料理らしさ」というスコアを返すようにします。このスコアが十分高い領域が一定以上だったら料理と返しましょうというふうにしました。

ここまでして、今、ようやく我々が運用しているモデルができたというふうになっています。なので、単純な2値分類ではぜんぜんなかった。これが1つ目のお話です。

技術開発は「めちゃくちゃ難しくて、でもめちゃくちゃおもしろい」

2つ目はレシピのカテゴリ分類についてです。料理の写真に対して、適切なレシピのカテゴリをアサインしたい。これも「クックパッドにはレシピのカテゴリの情報があるんだから、それを使って単純な多値分類で解けばいい」と思っていました。しかし、実際にやってみたら、想定の100倍ぐらい難しかった。誇張じゃなくて、本当に100倍ぐらい難しいです。

例えばopen setの予測をどうするかという話だったり、そもそもなにをターゲットのカテゴリにして予測器を作るかという問題だったり。あとは類似カテゴリという、同じ画像だけど違うカテゴリのものとか。いろんな問題が出てきて、相当いろんな実験をしました。

モデルを作っては試し、作っては試して、最終的にサービスに乗せられるぐらいのモデルができました。どんなことがポイントだったかを少し詳しくお話ししたいと思います。具体的には、類似カテゴリをどう扱うかと、precisionが高いモデルをどう作るかという話です。

重要なのは、まず予測の対象のカテゴリをどう作るかです。ぜんぜん違うカテゴリもあれば、似てる画像だけど違うカテゴリというものもあったりする。それをどうハンドルするか。

さらに、デプロイ時には基本的にopen setの分類が問題になるので、precisionがめちゃくちゃ低くなるんですね。precisionが低いとサービスとしては使えないので、それをどうやって高めるか。

このへんの話はめちゃくちゃ難しくて、でもめちゃくちゃおもしろいんです。プロジェクトが終わったあとに、一緒にやってくれた有野さんという方とかなりディスカッションを重ねて理論的な議論をしまして、今arXivにペーパーをサブミットしているので、もし技術的な内容に興味がある人はこのペーパーを読んで感想をいただければと思います。

あとは評価方法とかももちろん大事ですが、今日はスキップさせてもらいます。

人間の判断の「揺れ」にもとづいて類似度を定義している

レシピのカテゴリに対してどういう構造があって、そのカテゴリ間の類似度をどう測るのかを少しだけ説明します。

基本的には、画像は生成モデルから生まれてくるものだと思いましょう。生成モデルというのは(スライドを指して)一番下の「p(x|c)」です。

もう少し具体的な言葉でいうと、例えば「スパゲッティ・ボロネーゼ」というカテゴリを所与の条件としたときに、どんな画像が生まれやすいかという分布を描いているのが、グラフの左の山だと思ってください。山の一番高いところが最もスパゲッティミートソースっぽい画像、というような見方です。

当然ほかにもカテゴリがあって、例えば「ボロネーゼ」というカテゴリを与えたときにどんな画像ができやすいかという分布が、右の山だと思ってください。

このときに生成モデルのlatent labelは、当然、我々はestimateできない。相当強い仮定を置かないとestimateできない。

しかも、人間がラベリングするときはどうなるかというと、まず答えのラベルがついていない画像がボーンと出てくる。それに対して人間が「これはミートソースかな?」とか「これはボロネーゼかな?」という感じでラベルをつけていく。

当然、人によって、つけるラベルが変わる可能性があるんです。同じような画像でも「これはミートソース」という人と「これはボロネーゼ」という人がいる。その現象はすごく自然なことだと思っていて、我々はそれにもとづいてカテゴリ間の類似度を定義しようという議論をしました。

具体的には、2つの山が重なっている「intersection area」という部分が大きければ大きいほど、類似度が高いカテゴリの組ということにします。さっきのラベリングの例でいえば、カテゴリAとカテゴリBのどっちのタグをつけるかが人によってすごく分かれるときは間違えやすい。だから「類似度が高い」と言いましょう、と定義しました。

人の力で作業してやっと機械学習が扱える問題になる

ただ、こうしても生成モデルを我々が直接求めることはできないので、もう少し簡単に求めたい。我々は具体的には、その学習した学習器を使ってこのintersection areaをどういうふうに近似できるかという話をしました。

答えはめっちゃ簡単で、学習した予測器と分類器があります。その分類器を使ってクラスAとクラスBを見たときに、その2つが間違えやすかったら類似度高いカテゴリのペアだというふうにうまく対応づけました、という話が前述の論文です。

ではこれを使って我々がどうやってレシピのカテゴリを決めていったかという全体像ですが、前提として、料理のカテゴリが全部で1,000カテゴリぐらいあります。

まずは「目で見て区別できない大皿料理は視覚的じゃない」とか、「数が少なすぎるからこれはいらない」というものを除外する。その次は人力で「これはサービスで重要」というものを抽出する。これは欠かせない、すごく重要な作業です。

そこまでしてもまだ、やっぱり機械学習で解ける問題ではない。ここで先ほど言った誤分類に基づく類似度でカテゴリを統廃合して、解きたい問題と解ける問題をすりあわせていきました。最終的には50カテゴリぐらいになっています。

具体的には、例えば焼きそばとビーフンってすごく間違えやすいんです。でも、この2つを一緒のカテゴリにしても、ユーザーからすればそんなに違和感はないし、かつ、モデルの振る舞いもよくなる。じゃあここは統合してしまいましょう、というように決めていきました。

モデルの精度を上げることは予想より100倍難しかった

これでカテゴリの組ができましたが、マルチクラスのclassificationをしてもやっぱりダメで、precisionがめちゃくちゃ低い。そこでprecisionをどうやって高めるか考えて、one vs restの分類器を2段組み合わせたモデルを作りました。

具体例をいうと、画像があったらまずはfeature extractionする。そのextractしたfeatureに対して、各クラスに関して、まずは「salad or rest」、サラダかサラダじゃないかという判断をする。

サラダじゃなかったらもちろん次のカテゴリにいくんですが、もしサラダだと予測された場合は、そのままサラダだと予測してしまう。50カテゴリもあると、サラダに近いカテゴリがほかにもあって、それらとの間違いが発生するので。

それを回避するために、似ているものとの微妙な違いを見つけるための「f(2)」というものを作りました。これは、サラダとサラダに似ているカテゴリだけで同じone vs restのclassifierを作るものです。こいつも通過したら、めでたく「サラダ」と予測する。

ここまでして、ようやくプロダクションに乗せられる精度のモデルができるようになりました。最初に「マルチクラスの分類すればいいじゃん?」と思っていたことからすると、本当に100倍難しい問題だったという話です。

モバイル連携は両方の技術者が協力する必要がある

最後は、今ちょっと取り組んでいる「モバイルへの移行」についてです。

解きたい問題がまだまだあるんですね。モバイルでできればもっと速くできるし、computational resourceの意味でもスケールするし。あとはアプリ内で判別できればユーザーとのインタラクティブなこともできる。

個人の感想ですが、今まさにモバイル実装の機運が高まっていると思います。これはモデルが各種存在していて、古くはNetwork in Networkみたいなところから、Fire moduleを入れたSqueezeNetとか、最近だったらMobileNetV2みたいな感じでどんどん出てきている。

加えてライブラリが充実しているので、iOSならCore ML Toolsがあったり、AndroidならTensorFlow、TensorFlow Liteがある。これだと私は思っています。

ここでの機械学習の重要な点はなにかというと、まず軽いモデルを作らないといけない。ただ、これはライブラリが充実してきているのでそんなに難しくありません。

難しいのは、モバイル側との連携とかはまだやっている人があまりいないので、そういう中でどうやってプロジェクトを進めていくか。これはやっぱりお互いの領域に詳しい人がいないとなかなか難しい。

モバイルができる人がちょっと機械学習を勉強して、モデルを学習して実装したときに、例えば全部「料理」って予測させちゃったとする。そのときのデバッグはめちゃくちゃ難しいですよね。でも機械学習に詳しい人に見せれば「この偏りはきっとインプットのあれがリスケールされていないんだな」ということがあっさりわかったりする。

逆も同じようなことがある。だから両方の知識が必要で、その連携はすごく重要です。1人で2つともできる人はスペシャル人材なので、絶対この領域をやったほうがいいと思います。

モバイル移植は国内外でおもしろいトピック

あとは今どんどんライブラリのバージョンが変わっているので、そこもけっこう疲弊するポイントです。具体的には、例えばCore ML Toolsは2月に入ってようやくPython 3kをサポートしました。

我々はこれをやりたいと思って、実際にモバイルへの移植をやっています。モデルとしては、MobileNetに料理・非料理判定のパッチ化を合わせたものを作っています。

サーバ上の実験では、サーバで動かしているモデルと比べて1パーセントぐらい劣りますが、逆にいえばその程度の誤差しかないモデルを作ることができています。モバイルは実機で検証しないと意味がないので、iOS、Androidともに実機を使って検証しています。

最後はデモですが(スライドを指して)これはXcodeのiOSシミュレーターでやっていますが、相当速い。左側はAndroid実機での実験結果です。

これはPixel 2ですが、ブリストルのやつなので、合法Pixel 2です。それで検証した結果がこんな感じ。

余談ですが、ブリストルのオフィスに出張に行ったときにこれをやりました。向こうのiOSエンジニアとAndroidエンジニアと一緒にできたので、一気に進んだんです。さっき言ったように、お互いがわからないところをすぐデバックできるので一気に進んで、今、国内外で進められる、すごくおもしろいトピックだと思います。

あまり具体的な話はできませんでしたが、まさに昨日DroidKaigiで、これを一緒にやったrejasupotaroこと滝口さんが、この内容をスライドで出してくれているので、そちらも参考にしていただければと思います。

技術をどうサービスに乗せるか、試行錯誤するフェーズにきている

「Quantized」は、重みを量子化して軽くすると、3.3メガバイトぐらいのサイズでしかない。有効数字も2桁で書けばほぼ変わらないぐらい。処理速度も7.5毎パーセクぐらいで、性能の高いデバイスであればそんなにストレスなく使えるところまできている。

iPhoneだったら、iPhone 8でモデルをメモリ内に複数展開するというようなところまで入れると、22毎とか23毎パーセクぐらいまでの速さで処理できるようになっています。

ということで、今けっこう個人的にも興味を持っているので、こういうことをやりたい人はぜひ一緒にやりましょう。

具体的に画像分析に取り組んでいる例として3つお話ししましたが、1つだけ宣伝があります。今、画像分析のコンペをやっています。人工知能学会の「JSAI Cup 2018」というものです。

画像分析に興味を持った人は、ぜひこれに参加してください。プレトレーニングなしで、かつアンサンブルなしというなかなかおもしろい拘束条件でのコンペなので、ぜひチャレンジしてもらいたいと思います。

まとめです。大事なことは問題を定義することですが、ただその定義した問題は往々にして間違っている。だから、それを随時更新して解いていく。そのプロセスをちゃんと回せるかが、ほかのサービス開発と同様、機械学習のサービスでも重要だということです。

とくに画像分析に関しては、技術的な要素技術は相当成熟してきています。今はそれをどうやってサービスに乗せるかという、まさにこういう試行錯誤が重要なフェーズだと思っています。クックパッドでは、料理きろくやレシピ分類というところに画像分析の技術を入れています。

次に来るのはモバイル移植や動画の分析なので、そういうことを一緒にやりたい人がいればぜひやっていきましょう。これで発表を締めたいと思います。ありがとうございました。

(会場拍手)

続きを読むには会員登録
(無料)が必要です。

会員登録していただくと、すべての記事が制限なく閲覧でき、
著者フォローや記事の保存機能など、便利な機能がご利用いただけます。

無料会員登録

会員の方はこちら

関連タグ:

この記事のスピーカー

同じログの記事

コミュニティ情報

Brand Topics

Brand Topics

  • 孫正義氏が「知のゴールドラッシュ」到来と予測する背景 “24時間自分専用AIエージェント”も2〜3年以内に登場する?

人気の記事

新着イベント

ログミーBusinessに
記事掲載しませんか?

イベント・インタビュー・対談 etc.

“編集しない編集”で、
スピーカーの「意図をそのまま」お届け!