機械学習によるレシピの自動分類、その裏側

こんにちは。検索編成部&研究開発チームの原島です。

クックパッドのレシピには、内部で、様々な情報が付与されています。例えば、こちらの「母直伝♪うちの茹でない塩豚」というレシピには「肉料理」という情報が付与されています。これらの情報は、クックパッドの様々なプロダクトで利用されています。

レシピに情報を付与する方法は沢山ありますが、その一つに機械学習があります。クックパッドでは、レシピが肉料理か否か、魚料理か否か、...という分類を行うことで、「肉料理」や「魚料理」などの情報をレシピに付与しています。

今日は、分類をどのように実現しているか、その裏側を紹介します。

■ 実装フェーズ

まず、分類器を実装する際に気をつけたことを紹介します。

モデルを決定する

分類を行うには、そのための機械学習のモデルを決定する必要があります。クックパッドでは、十分な精度が出るだけでなく、リファレンスが多いという点も考慮して、SVM を採用しています。実装には liblinear を利用しています。

また、冒頭で説明した通り、分類は二値(e.g., 肉料理か否か)にしています。多値(e.g., 肉料理か魚料理か...)にすると、各カテゴリに対する分類が独立でなくなって、結果を改善するのが難しくなるためです。

訓練データを用意する

SVM の学習を行うには、そのための訓練データが必要です。例えば、肉料理の分類器を構築するには、肉料理のレシピ(正例)と肉料理でないレシピ(負例)が必要です。

訓練データを用意する方法はいくつかありますが、コストがかかるのが悩みのタネです。訓練データは正しくないといけません。そのため、これを用意するには、どうしても人手が必要です。

そこで、クックパッドでは、レシピカテゴリを利用して、訓練データを用意しています。レシピカテゴリには、自薦 or 他薦によって、各カテゴリのレシピが蓄積されています(下図)。

f:id:jharashima:20150930162845j:plain

肉料理の訓練データを用意する場合、「お肉のおかず」というカテゴリを正例として、その他のカテゴリを負例として利用しています。人手を介したデータを利用することで、訓練データを用意する手間を大きく削減しています。

テストデータを用意する

分類精度をチェックするには、テストデータが必要です。精度をチェックする仕組みがないと、デグレが起こった時に気付けません。これをサボるとひどい目に遭います(遭いました)。テストデータは必ず用意しておくべきです。

クックパッドでは、分類器を更新する際、テストデータにおける精度をチェックしています。そして、精度が閾値を下回った場合、分類器を更新しないようにしています。こうすることで、デグレが起こっても、本番環境に反映されないようにしています。

開発データを用意する

分類器を構築した後、その精度を改善するには、チューニングが必要です。そして、精度が分かれば、その値に基づいて、分類器をチューニングできそうです。

テストデータがあれば、精度が分かります。しかし、テストデータを用いてチューニングしてはいけません。単にテストデータにおける精度が良くなるだけで、その他のデータにおける精度が良くなっているとは限りません。

そこで、テストデータとは別に、開発データも用意しています。開発データを用いてエラーを分析し、分類器をチューニングすることで、分類器が汎用的になるように気をつけています。

■ 改善フェーズ

さて、モデルを決定して、データも用意しました。とりあえず実装できます。しかし、とりあえずで実装した分類器の精度は、大抵悲惨なものです。分類器を改善する必要があります。

以下では、分類器を改善する際に気をつけたことを紹介します。

素性を修正する

最初に思いつくのが、この方法ではないでしょうか。必要な素性を追加したり、不要な素性を削除すれば、精度が良くなりそうです。

しかし、本タスクでは、素性を修正することはほとんどありません。と言うのは、以下で紹介する方法の方が簡単で、効果が高かったからです。

訓練データを修正する

本タスクで一番効果が高かったのは、訓練データを修正する方法です。正確には、「訓練データに利用するレシピを変更する」です。訓練データにはレシピカテゴリを利用しています。利用するカテゴリを変更すれば、分類器の振る舞いも変更できます。

素性を修正するには、少なからずコードを変更する必要があります。特に、素性を追加する場合(レシピから新しい素性を抽出する場合)は、そのためのコードを追加で実装しなければいけません。

一方、我々の場合、訓練データを修正するだけであれば、ほとんどコードを変更する必要がありません。利用するカテゴリを変更するだけで、実装的には定数を変えるだけです。クックパッドでは、訓練データを修正することで、分類器を改善することが多いです。

ルールで直す

ここから先は力技です。

困ったことに、素性を修正しても、訓練データを修正しても、分類誤りを直せないことがあります。このような場合、汎用的なルールが作れれば、そのルールで対応しています。

例えば、サラダの分類器を構築する時、タイトルが「サラダ」で終わるレシピはサラダとみなしても良さそうということが分かりました。このような場合、機械学習に拘らず、ルールを優先して分類を行うようにしています。

手で直す

素性を修正しても、訓練データを修正しても、直りません。汎用的なルールも作れません。それでいて、ユーザさんからお問い合わせがあるなど、何が何でも直さないといけない時があります。

こんな時は、手で直しています。こういう事態を想定して、スタッフが結果を修正するための社内ツールを用意しておくと良いです。ここでも機械学習には拘りません。堂々と手で直しています。

ちなみに、手で直したデータは、後で訓練データとして利用できます。地道な手作業も、積もり積もれば、分類器を賢くしていくのです。

■ まとめ

本エントリでは、レシピの自動分類の裏側を紹介しました。まとめると、

  • 着実な方法で実装する(実績のあるモデルを使う、地道にデータを用意する)
  • 手軽な方法で改善する(素性でなく、訓練データを修正する)
  • 最後は機械学習に拘らない(ルールで直す、手で直す)

といったところでしょうか。

ネットを漁っても、プロダクトに機械学習を導入した時の知見はそれほど多くないようです。本エントリが、私と同じく、機械学習に悩める方の一助となれば幸いです。

なお、検索編成部では検索エンジニアを、研究開発チームではレシピ翻訳エンジニア画像解析エンジニアを募集しています。ご興味がある方は、是非ご応募ください。

/* */ @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;*/ /*}*/