料理きろくにおける料理/非料理判別モデルの詳細

研究開発部の菊田(@yohei_kikuta)です。機械学習を活用した新規サービスの研究開発(主として画像分析系)に取り組んでいます。 最近読んだ論文で面白かったものを3つ挙げろと言われたら以下を挙げます。

クックパッドのアプリには「料理きろく」という機能があります。 携帯端末から料理画像のみを抽出して表示することで自分が食べたものを振り返れるようになっており、ここからレシピ投稿やつくれぽを送ることもできるようになっています。

料理きろくはユーザ数が約12万8千人、累積の写真判別枚数が約7900万枚、そのうち料理と判別された画像が約960万枚(数字は20170912時点)と多くの方々に使っていただいている機能です。 本記事では、その料理きろくのコアの技術部分である、機械学習による料理/非料理判別の詳細に関してお伝えします。 料理きろく全体のアーキテクチャに関してはここでは述べませんが、ご興味のある方は AWS Summit Tokyo での発表資料 をご覧ください。

料理きろくの背景と機械学習の役割

我々は、新しい技術を駆使してユーザ体験をより良いものに改善することで、クックパッドの使用頻度を増やしてもらったり、ユーザからもっとレシピやつくれぽを投稿してもらう、などの目標を持っています。 料理きろくはその目標に資する一つの施策であり、これは携帯端末に大量の食事情報が記録されていることから、それを活用してクックパッドのサービスに連携することを狙いとしています。 過去の自分の食事を振り返ることで食への楽しみや関心を高めてもらい、そこからレシピやつくれぽ投稿につなげたいというのが最初のターゲットとなっています。

ちなみに料理きろくではユーザのプライベートな画像を判別することになるため、全てサーバ上で処理がなされ、我々はユーザの画像を一切閲覧できないようになっています。

料理きろくにおける機械学習の役割は、料理/非料理を判別する高性能のモデルを提供することです。 機械学習によって正確に画像の料理/非料理が判別できるかどうかがサービス全体の質に直結するため、ここに Convolutional Neural Network (CNN) を用いたモデルを適用しました。 一口に CNN と言っても実に多様なので、データも拡充しながら、様々なモデルを試行錯誤をして改善を図りました。

以降では本番環境にデプロイしたモデルを中心に、どのような試行錯誤で改善をしていったのかをご紹介します。

最初にデプロイしたモデル

  • モデル:CaffeNet
  • データ:{料理,非料理}の二値ラベルの画像データ
  • フレームワーク:Chainer

最初のリリースでは CaffeNet というモデルを使っています。 これは ILSVRC2012 で優勝した AlexNet を少し変更したもので、pooling が Local Response Normalization の前に来ています。

最初の段階では、素早くリリースまで持っていきたい、社内に画像分析の知見がまだ蓄積していなかった*1、などの理由で、事例も多いこのモデルを採用しました。 データはシンプルに料理画像と非料理画像をとりあえず手当たり次第に集めたものを使用しました。 テストデータも集めた画像の一部をテスト用に切り出して、料理判別の精度と再現率をチェックするというごくごく基本的な手法でした。

本番運用に際しては、精度が低いと料理ではないものが表出されてユーザ体験が悪くなるという考えのもと、再現率をある程度犠牲にしても精度を高めるという方向で閾値を調整しました。 単純な二値判別では softmax の出力が0.5を超えれば料理と判別されることになりますが、このモデルでは閾値を0.9で設定しました。 これは閾値を変えながらテストデータでの結果を目視でチェックしながら定めたものです。

最初のモデルは手探りの部分も多かったですが、素早くリリースまで到達できたことは非常に良かった点で、継続的改善を遂行していける土台が整えることができたので後の改善につながりました。

一回目のモデルアップデート

  • モデル:Inception-v3
  • データ:{料理,非料理,複数の間違えやすい非料理カテゴリ}の多値ラベルの画像データ
  • フレームワーク:TensorFlow

最初にデプロイしたモデルでも結構精度が高かったのですが、自分たちでも使っていくうちに間違いやすい画像があることが分かってきました。 具体的には植物や赤ちゃんの画像などが間違いやすい傾向があることが判明したため、これらの画像にもロバストなモデルを作りたいという要望が出てきました。 また、CNN のモデルも様々な発展があるため、それらを検証してより基本的な性能が高いモデルを採用したいという考えもありました。

そこで、まずは様々な CNN のモデルを比較検証をして良いモデルを探すという実験をしました。 この頃には画像分析ができる人員も増えていたため、手分けをして実験をして GHE の wiki に情報を集約し、最終的に我々のデータセットに対して最も良い結果を出した Inception-v3 を採用することにしました。 モデルとして試したのは、{Inception-v3, GoogLeNet, ResNet, VGG, GAN を使った classification, NIN(軽量なモデルにも興味がある), …}、です。

フレームワークに関しても、TensorFlow を使う人が多くなったため、Chainer から切り替えました。 モデルの学習には Keras with TensorFlow backend を使っていますが、本番にデプロイする時は TensorFlow のみで動かすようにしています。 料理きろくのアーキテクチャはモデル部分を疎結合にしてあるので、この変更はそれほど大きなコストもなく実現できました。

次に単純な二値判別では問題として単純化しすぎているのではという考えのもと、多値判別に切り替えるという実験をしてみました。 思考回路としては、仮に世の中の料理画像の全集合が手に入ればその補集合が非料理画像だがそれは不可能→モデルは我々が集めた(非)料理画像の集合から(非)料理らしさを学習→これらは多様なので一つのカテゴリに集約し切るのは無理がありそう→特に間違いやすいものに関しては陽にカテゴリを作ってそちらに誘導したほうが我々が望むモデルができそう、という感じです。 料理/非料理ともに画像(二値ではなく多値のラベルを付与したもの)を追加収集して、それぞれが単一カテゴリ(この場合は多値の情報を潰して二値として扱う)の場合と複数カテゴリの場合とで性能を比較しました。 モデルの出力は一般に多値になりますが、判別結果としては多値の情報を潰して料理/非料理の二値判別として扱うようにしています。 全体的な性能を上げつつ特に精度を高めるものとして、料理は単一カテゴリとして非料理は多値カテゴリ(具体的には植物や人物を含む5カテゴリ)として扱うことに決定しました。

これらの改善によって、手元のデータで試験したところ、精度も再現率も向上し、特に間違えやすかった植物の画像に対しては間違いが約 1/3 に、赤ちゃんの画像に対しては間違いが約 1/20 ほどになりました。

これの取り組みは以前クックパッドで開催された Cookpad Tech Kitchen でも発表しています(発表資料)。

また、2017年度 人工知能学会全国大会 やIJCAIのワークショップとして開催された 9th Workshop on Multimediafor Cooking and Eating Activities などの学術的な場でも発表をしています。

二回目のモデルアップデート

  • モデル:Inception-v3 + patched classification
  • データ:{料理,非料理}の二値ラベルの画像データ、それらを14×14のパッチにしたもの
  • フレームワーク:TensorFlow

一回目のアップデートで大きく改善はしましたが、画像中に人と料理が同時に写っている場合はモデルが判断に迷う(人と判断すべきか、料理と判断すべきか)という問題が残っていました。 我々は「画像中の一部を切り取ってクックパッドのレシピとして掲載できそうなものは料理と判断する」という基準で料理/非料理を判断しているので、十分に料理が写っているのにモデルが非料理と判断されているものは改善の余地がありました。

この問題に取り組むために、まずはマルチラベルの判別をすることを考え、そのために Keras の ImageDataGenerator 辺りを改修したりもしましたが、データ準備のコストが高いため一旦保留としました。 次に、問題の根本は複数のものが写っているのにそれらをまとめて判別してしまっていることだと考え、画像をパッチに分割してパッチ毎に料理/非料理を判別するというモデルを構築しました。 具体的には、通常の Inception-v3 の出力付近で使う GlobalAveragePooling と Dense を、Conv2D や Dropout などを組み合わせて出力が 14×14×1 にするように置き換えて、sigmoid の出力で binary cross entropy を計算するようにしています。 パッチに分けることで料理と非料理を区別しやすくなることが分かったため、再び二値分類のモデル(ただしパッチ毎)になっています。 パッチサイズの 14×14 に関してはいくつかのパターンを実験した結果最も良い結果を返すものを選択しました。

このモデルの学習にはパッチ毎にラベルが付与されたデータが必要ですが、これは単純に元データをパッチに分割して、全パッチに元データと同じラベルを付与するという作り方でデータ準備を簡略化しました。 ただしこの作り方だと特に画像の端の部分が悪さをする可能性があるので、適切なラベルが得られるように一部の画像を crop したりもしています。

また、本番にデプロイした場合の性能を見積もるために、本番でのデータ分布に近くなるように社員から許諾を得て携帯端末のデータを提供してもらいました。 プライベートな画像のため、閲覧権限を絞って、特定の人が正解ラベルを付与してそれを使ってモデルの詳細な性能検証を実施しました。

このような改善を経て本番環境にデプロイされたモデルの結果の一例が以下の図となります。 色がついている領域が料理らしさが高い領域で、閾値以上のパッチを取り出してその領域が一定以上であれば料理と判別するというモデルになっています。

この改善によって、テストデータに対して精度を落とさずに再現率を5%以上改善することができました。 料理きろくは1000万というオーダーで画像を処理しているので、改善のインパクトは大きなものとなります。

モデル構築に使用したデータ

クックパッドには大量の料理画像があるので正例のデータには事欠きませんが、モデルの学習には負例のデータも重要になるため Creative Commons のデータや社員からデータを集めたりして様々な画像を収集しました。 プロジェクトの進行と共に画像を追加して試行錯誤してという作業を繰り返し、結果としてトータルで数十万枚程度の画像を扱いました。

どのような画像が重要になったかは上述のモデルアップデートでもご紹介しましたが、改めてモデルの性能向上に重要であった特徴的なデータをいくつか挙げたいと思います。

  • 料理 
    当然ながら正例としての料理画像は最重要のデータとなります。 クックパッドの豊富な料理画像データを用いて充実したデータセットを構築することができました。
  • 人(特に赤ちゃん) 
    人物画像は判別を間違えた場合のリスクも高い(ユーザ体験の観点から)ため、負例の中でも特に気をつけて画像を収集して学習に用いました。
  • 植物 
    想像に難くないですが、色合いや形状から誤判別される場合が多かったのが植物の画像でした。
  • 植木鉢 
    植物と似ていますが、植木鉢の場合は器もあるためにより一層誤判別されるものが多かったです。 そのため意識して負例としてデータを収集しました。
  • 料理が乗っていない空の皿
    料理には皿がつきものなので、皿があれば正例と勘違いしかねないため何も乗っていない皿も負例としてデータに追加しています。
  • 本番運用時にモデルが扱うデータ分布に近いテストデータ
    本番ではユーザの携帯端末中の画像が対象ですが、学習は収集したラベル付きの限定的なデータを用いているので、モデルの正しい性能が測りづらいという問題があります。 ユーザの画像は我々も閲覧できないため、この問題はオフライン/オンラインに限らず原理的な問題です。 そこで、許諾を得た社員のスマホのデータを使い、人力でラベルを付け、これを使ってモデルの評価を実施しました。 プライベートな画像であるため、閲覧権限を必要最低限の人間に絞って詳細な性能評価をして、他の人には統計情報のみを共有するという手法を採りました。

やはり学習データは最重要ですね。 料理きろくにおいては、多くの人の協力によって様々なデータを収集することができたので、非常に心強かったです。

今後の展望

料理きろくのプロジェクトを通じて、やはり継続的な試行錯誤に基づいた適切な改善策を講じることが重要であることが再認識できました。 それゆえに、これで完成ではなく、日進月歩の Deep Learning 技術を取り込んだり、データを拡充したりして、よりユーザにとって有用なモデルを構築し続けるのが大事だと考えています。

例えば最新の SENet や料理ドメインに特化した 横方向に広くスライスした畳み込み を試してみたり、自分たちで使いながら判別を間違った画像を収集してその傾向を探る、より高速に動作するモデルを構築するなど、改善の方向性は様々です。

「自分ならもっと良いモデルが作れる!」という人がいましたら是非一緒にやりましょう。

おまけ

以前 Twitter で話題になっていた画像として、犬と食べ物の区別が難しい画像というものがありました。 みなさんは下の画像のどれが犬でどれが食べ物かが判別できるでしょうか? 遠目で見ると判別はかなり難しい感じがします。

画像は以下から引用させていただきました。
左の画像:https://twitter.com/teenybiscuit/status/707727863571582978
右の画像:https://twitter.com/ohmycorgi/status/867745923719364609

これらの画像に我々の料理/非料理判別モデルを適用して、料理画像だけを抽出してもらうとしましょう。

見事に料理画像だけを選び出すことができました! Deep Learningのモデルが適切に料理とそれ以外を区別できていることを伺い知ることができます。

これ以外にも、近い内容の話として 弁護士の柿沼太一先生との対談 などもあります。 ご興味があれば是非ご覧ください。

まとめ

クックパッドアプリの料理きろくという機能で用いている料理画像判別技術に関してお伝えしました。 CNN のモデル自体はもの凄く特殊なものを構築しているわけではありませんが、試行錯誤を経たモデルの変遷やその過程で遭遇したタスク特有の問題点などに興味を持っていただけたなら幸いです。 本記事でお伝えしたのは一例であり、クックパッドでは様々なサービスにおいて、発展著しい機械学習の技術をユーザに有益なものへと昇華させる取り組みに日々励んでいます。

いかがでしたでしょうか。 クックパッドでは、機械学習を用いて新たなサービスを創り出していける方を募集しています。 興味のある方はぜひ話を聞きに遊びに来て下さい。 クックパッド株式会社 研究開発部 採用情報

*1:このプロジェクトが始まった段階では私はまだ入社していませんでした

/* */ @import "/css/theme/report/report.css"; /* */ /* */ body{ background-image: url('http://cdn-ak.f.st-hatena.com/images/fotolife/c/cookpadtech/20140527/20140527163350.png'); background-repeat: repeat-x; background-color:transparent; background-attachment: scroll; background-position: left top;} /* */ body{ border-top: 3px solid orange; color: #3c3c3c; font-family: 'Helvetica Neue', Helvetica, 'ヒラギノ角ゴ Pro W3', 'Hiragino Kaku Gothic Pro', Meiryo, Osaka, 'MS Pゴシック', sans-serif; line-height: 1.8; font-size: 16px; } a { text-decoration: underline; color: #693e1c; } a:hover { color: #80400e; text-decoration: underline; } .entry-title a{ color: rgb(176, 108, 28); cursor: auto; display: inline; font-family: 'Helvetica Neue', Helvetica, 'ヒラギノ角ゴ Pro W3', 'Hiragino Kaku Gothic Pro', Meiryo, Osaka, 'MS Pゴシック', sans-serif; font-size: 30px; font-weight: bold; height: auto; line-height: 40.5px; text-decoration: underline solid rgb(176, 108, 28); width: auto; line-height: 1.35; } .date a { color: #9b8b6c; font-size: 14px; text-decoration: none; font-weight: normal; } .urllist-title-link { font-size: 14px; } /* Recent Entries */ .recent-entries a{ color: #693e1c; } .recent-entries a:visited { color: #4d2200; text-decoration: none; } .hatena-module-recent-entries li { padding-bottom: 8px; border-bottom-width: 0px; } /*Widget*/ .hatena-module-body li { list-style-type: circle; } .hatena-module-body a{ text-decoration: none; } .hatena-module-body a:hover{ text-decoration: underline; } /* Widget name */ .hatena-module-title, .hatena-module-title a{ color: #b06c1c; margin-top: 20px; margin-bottom: 7px; } /* work frame*/ #container { width: 970px; text-align: center; margin: 0 auto; background: transparent; padding: 0 30px; } #wrapper { float: left; overflow: hidden; width: 660px; } #box2 { width: 240px; float: right; font-size: 14px; word-wrap: break-word; } /*#blog-title-inner{*/ /*margin-top: 3px;*/ /*height: 125px;*/ /*background-position: left 0px;*/ /*}*/ /*.header-image-only #blog-title-inner {*/ /*background-repeat: no-repeat;*/ /*position: relative;*/ /*height: 200px;*/ /*display: none;*/ /*}*/ /*#blog-title {*/ /*margin-top: 3px;*/ /*height: 125px;*/ /*background-image: url('http://cdn-ak.f.st-hatena.com/images/fotolife/c/cookpadtech/20140527/20140527172848.png');*/ /*background-repeat: no-repeat;*/ /*background-position: left 0px;*/ /*}*/