Cookpadのつくれぽ数を予測する

「食と機械学習」のコラボレーションをできないかと最近常々考えています。例えば、「地球上に存在する全ての料理本を何らかのアルゴリズムに入力し、人間にとって『美味しい』レシピとはどういう特徴を持つかを学習し、今まで人類が食べたことのないような斬新なレシピを自動生成する」ことが出来たとしたら素晴らしいとは思いませんか?

レシピの自動生成はさすがに難しそうなので、今回は人気になるレシピに共通する特徴は何かという問いに答えることを目標とします。具体的には、Cookpadのレシピが与えられたとして、そのつくれぽ数を予測する問題に挑戦してみます。レシピが人気になるか否かの要因としては、どういう材料を使用するか、レシピがどれだけ健康的か、またどれだけ手軽に調理できるかなど様々な要因が考えられますが、今回特に興味があるのはレシピの名前や紹介文などの文章の言葉遣いがそのレシピの人気度にどのような影響を及ぼすかです。Cookpadのまとめサイトらを眺めてみると、食べ過ぎ注意☆鶏肉のねぎマヨポン炒め極ウマ♡海老のトマトクリームライスなど、レシピ名を一瞥しただけで腹が減ってきそうなものがありますが、これらのレシピに共通する特徴は何でしょうか。

データ

2013年8月1日から3日にかけてCookpadから72,330件のレシピをクロールしました。これらのレシピは、Cookpadのレシピカテゴリの「きょうの料理」以下に当たる全15個のサブカテゴリ、また「お菓子」以下に当たる18個のサブカテゴリに含まれるものです1

これらのレシピのつくれぽ数の分布を図で見ると以下のような典型的なべき乗分布になります:

およそ2万件のレシピは1つもつくれぽが投稿されていません。ちなみに、このデータに含まれるレシピのうち最も多くのつくれぽがついているものはこちらです。このレシピを投稿したユーザの凄いところは12,000件以上のつくれぽに対し、紋切り型のお礼ではなく一つ一つ丁寧に返事を書いているところです。例えば2番目と3番目に多くのつくれぽがついているこちらこちらのレシピでは毎回同じ返信をしています。

問題の定式化

つくれぽ数に閾値を定め、ある一定数以上のつくれぽがついたレシピを正クラス、それ未満のものを負クラスとすれば、二値分類問題に帰着できます。

手法

一般化線形モデル(ロジスティック回帰)を使用し、モデルをコンパクトにするためにL1ノルム正則化を適用します。

素性

分類するための特徴・素性として以下のものを使用しました。 大きく分けて「メタデータに関する素性」と「テキストに関する素性」の2つに分けられます。

  • メタデータに関する素性:
    • 完成写真の有無
    • 手順写真の有無
    • 投稿したユーザ
    • 材料
    • レシピが含まれるカテゴリ2
  • テキストに関する素性:
    • レシピのタイトル
    • レシピの紹介文
    • レシピのコツ・ポイント3
    • レシピの生い立ち

これらの素性をCookpadのレシピページ上で示したものが次の図です:

材料は、「醤油」一つを例にとっても

  • ○砂糖、醤油、みりん
  • ◎旨味調味料、醤油又は麺つゆ
  • 醤油(薄口がベスト)
  • 醤油orだし醤油orめんつゆ
  • ①あれば醤油
  • 市販の醤油
  • 醤油

など表記に揺らぎがあるため、特殊記号を削除、括弧やその中に含まれる文字列を削除、任意性を表す「あれば」や「お好みで」を削除、読点や「又は」で文字列を分割、仮名を平仮名に統一など必要最低限の前処理をしています。

テキストに関しては分かち書きをした後、unigram・bigram・trigramを素性として使います。

素性値はメタデータ・テキストのどちらにも関わらず、二値ベクトルとして扱っています。訓練データの0.2%未満、もしくは99.8%以上のレシピに出現する素性はpruneしてあります。

評価実験

レシピを投稿してからつくれぽが掲載されるまである程度の時間がかかると思われるため、投稿してから3ヶ月未満のレシピはデータから除外します。上記の72,330件のレシピのうち、投稿されてから3ヶ月以上経過していた70,724件の70%を訓練データ、20%を開発データ、10%を評価データに分けました。開発データで正則化パラメータを選びます。

クラス分けのために使われるつくれぽ数の閾値は1、5、10、20件に設定します。 閾値によってはクラスのサンプル数が不均衡になるため、訓練・開発・評価データをstratifiedにしました。また同様の理由で、評価指標としては分類正解率ではなくF値を用います。

実験結果

評価データのF値は以下のようになりました:

上2段は正クラスと負クラスのサンプル数を示しています。 下2段の上段・下段はそれぞれ、メタデータに関する素性のみを用いたときのF値と、メタデータ+テキストの両方に関する素性を用いた時のF値を示します。

テキストに関する素性を使うことにより、メタデータのみを使った場合よりもF値が10%ほど向上することが確認できます。

素性の重みを見てみる

学習によって得られた素性の重みが大きければ大きいほど、該当する単語が文章に含まれていたり該当する材料が含まれていたりすると、レシピにつくれぽが投稿されやすいということを表します4。逆に、重みが負であるほど、そのレシピにつくれぽが投稿されにくいことを表します。

以下の結果は全て閾値を5、素性にはメタデータ+テキストの両方を使用したものです。 素性のカテゴリごとに見ていきます。

投稿したユーザ

学習によって得られた素性の重みを調べてみます。重みが正のもの(の一部)はこちら:

0.4210  meta:author:212659
0.4051  meta:author:4036
0.3566  meta:author:28982
0.3245  meta:author:2513693
0.2072  meta:author:252604

左の数字は素性の重み、右の数字はCookpadのユーザIDを表します。該当するユーザページはこちら:

確かにどのユーザのレシピも色鮮やかで、すきっ腹のときに眺めるのは苦行です。

レシピのタイトル
0.4372  text:title:1:✿
0.3139  text:title:1:♡
0.2410  text:title:1:キャベツ
0.2305  text:title:1:ツナ
0.1953  text:title:1:マヨ
0.1929  text:title:1:❤
0.1928  text:title:1:簡単
0.1849  text:title:1:だけ
0.1819  text:title:1:絶品
0.1809  text:title:1:♪
0.1649  text:title:1:濃厚
0.1568  text:title:3:【 農家 の
0.1562  text:title:1:∮
0.1553  text:title:1:♥

いかにも女子力が高そうな✿♡❤♪などの記号が目立ちます。

レシピの紹介文
0.6908  text:desc:1:✿
0.6223  text:desc:1:♡
0.4982  text:desc:1:♫
0.4714  text:desc:1:♬
0.4434  text:desc:1:❤
0.2325  text:desc:1:☆
0.2223  text:desc:1:シンプル
0.2216  text:desc:1:甘辛
0.2091  text:desc:1:♫♬
0.1904  text:desc:2:です ✿
0.1839  text:desc:1:濃厚
0.1732  text:desc:1:~♪
0.1725  text:desc:1:可愛い
0.1663  text:desc:1:サクサク

レシピのタイトルと同様、記号が数多く並びます5

材料
0.2793  meta:ingr:まよねーず
0.2176  meta:ingr:砂糖
0.2157  meta:ingr:鶏がらすーぷの素
0.2120  meta:ingr:ういんなー
0.1623  meta:ingr:胡麻油

油、砂糖、塩分など脳内麻薬物質が出る組み合わせを含む材料を使うとつくれぽが掲載されやすくなるのは理にかなっています。なお、前処理で材料に含まれる仮名を平仮名に統一しているため、「マヨネーズ」や「ウインナー」を「まよねーず」や「ういんなー」などと表記しています。

逆に負の重みの材料も見てみましょう:

-0.2925  meta:ingr:しいたけ
-0.2735  meta:ingr:しょうが
-0.2732  meta:ingr:えりんぎ
-0.2186  meta:ingr:ぱぷりか
-0.2123  meta:ingr:せろり
-0.1919  meta:ingr:まっしゅるーむ
-0.1895  meta:ingr:椎茸
-0.1768  meta:ingr:まいたけ
-0.1665  meta:ingr:塩麹
-0.1589  meta:ingr:みょうが
-0.1547  meta:ingr:干ししいたけ
-0.1496  meta:ingr:ごーや

好き嫌いが分かれそうな野菜だらけです。昨年ブレイクした塩麹ですが、意外と苦手な人もいるみたいです。

ケーススタディー

ここまではマクロな視点で、Cookpadの(ほぼ)全てのカテゴリに含まれるレシピに対してモデルを適用してきました。しかし、これでは「サバの味噌煮と鮭のエスカベッシュのどちらが人気か」という料理同士の比較になりかねません。しかし今回の本来の目的は、人気になるレシピに共通する特徴を把握することです6。 特に、レシピの名前や紹介文などの言葉遣いが人気度にどういう影響を及ぼすかを調べたいのですが、データに雑多な料理が含まれていると「カレー」や「パウンドケーキ」などの料理名が強く出てしまい、語句に関する情報が埋もれてしまう可能性があります。

また、これは上記の実験をした後から分かったことなのですが、Cookpadに投稿されたレシピは必ずしもカテゴリに割り振られるわけではなく、カテゴリ入りするにはユーザによって推薦されるかカテゴリマスターによってキュレートされる必要があります。このように、カテゴリ入りしたレシピにはあらかじめバイアスがかかっているので、 そこから得られた結果を鵜呑みにするのも考えものです。

以上の2点から、複数の料理を同時に考慮するのではなく、ある特定の料理に絞ってからモデルを適用するのも検討すべきでしょう。Cookpadのレシピ検索では、カテゴリ入りしたかしてないかに関わらずクエリに関連する全てのレシピがヒットするようなのでこれを使います。

なお、これ以降の実験ではL1正則化を使用したらモデルがスパースになりすぎたのでL2正則化を使いました。つくれぽの閾値は1に設定してあります。

肉じゃが

肉じゃがで検索するとヒットする5,615件のレシピのうち、投稿されてから3ヶ月以上経過している5,349件のレシピを使います。

学習によって得られた素性の重みを調べてみます。重みが正のもの(の一部)はこちら:

0.1819  text:desc:1:♪
0.1513  text:title:1:リメイク
0.1502  text:title:1:簡単
0.1473  text:desc:1:美味しい
0.1361  text:title:1:★

逆に重みが負になったものを見てみましょう:

-0.0651  text:title:1:レンジ
-0.0649  text:desc:1:残り物
-0.0480  text:desc:1:余っ

「残り物」と書いたらつくれぽが投稿されにくいのに、「リメイク」と言い換えるとつくれぽが投稿されやすくなるのは当然といえばそうですが、なかなか面白いです。在庫処分をクリアランスセールと言い換えると高級感が出るのに似ています。

また、レシピ名に「レンジ」を含めるのも(少なくとも肉じゃがでは)薦められないようです。恐らくこれは「ちゃんとした料理は電子レンジなんかで作るものじゃない」という先入観があるからでしょう。逆にプリンなどのデザートの場合は、電子レンジを使うと簡単に作れるからか、このレシピのようにすごい数のつくれぽが掲載されているものもあります。

味噌汁

味噌汁で検索するとヒットする7,344件のレシピのうち、投稿されてから3ヶ月以上経過している7,008件のレシピを使います。

学習によって得られた素性の重みが正のものはこちら:

0.2441  text:desc:1:♡
0.1654  text:desc:1:♪
0.1553  text:title:1:✿
0.1324  text:title:1:☆
0.1067  text:title:1:♡
0.0955  text:desc:1:❤
0.0828  text:desc:1:~♪
0.0668  text:desc:1:簡単

毎度おなじみの女子力が高そうな記号がズラリ。

逆に重みが負になったものも見てみましょう:

-0.1124  text:desc:1:残り物
-0.1109  text:title:1:離乳食
-0.0734  text:title:1:カレー
-0.0730  meta:ingr:マヨネーズ
-0.0626  text:desc:1:手抜き

この中でもひときわ目を引くのがカレーとマヨネーズ。「味噌汁 カレー」でググってみると、どうやらカレーうどんが作れるらしいです。でも味噌汁にマヨネーズというのはいかにもB級グルメっぽくて試す気が起きません。

「残り物」と「リメイク」同様、「手抜き」と書くとつくれぽがつかないのに対し「簡単」と書くとつくれぽが投稿されやすくなるみたいです。また、離乳食もニッチなニーズと思いきや、10,000件以上の離乳食レシピがCookpadに投稿されています。

グラタン

グラタンで検索するとヒットする16,012件のレシピのうち、投稿されてから3ヶ月以上経過している15,535件のレシピを使います。

0.3066  text:desc:1:♡
0.2429  text:title:1:簡単
0.2194  text:desc:1:♪
0.2183  text:desc:1:♬
0.2113  text:title:1:豆腐
0.2014  text:desc:1:❤
0.1654  text:desc:1:簡単
0.0980  text:desc:2:だけ !
0.0960  text:desc:2:簡単 な
0.0934  text:desc:2:の 簡単
0.0908  text:desc:2:♪ 簡単

グラタンって面倒くさそうなイメージがやはりあるのでしょうか、「簡単」が数多く見られます。また、グラタンに豆腐というのも意外ですが、Cookpadに豆腐グラタンというカテゴリが存在するほど浸透しているみたいです。

まとめ

今回得られた知見としては、つくれぽが欲しければ「女子力が高そうな書き方をする」「負のコノテーションを持つ言葉を美化する」という至極もっともな結果になりました7

今後の展開としては、

  • 画像の有無だけでなく、画像の「見た目」を考慮(e.g. 明るさ・コントラスト・色合い)
  • 手順に含まれる調理法を考慮(e.g. 昆布でだしを取るのは面倒臭いので敬遠されがち)
  • テキストの前処理をもっとしっかりする(e.g. MeCabに顔文字辞書や料理用語を追加)

などが考えられます。

ここまでやっておいてなんですが、ちゃんとまじめにレシピの人気度を予測するにはCookpadからクロールできるデータでは限界があるかもしれません。というのも、つくれぽを投稿してもすぐに反映はされず、掲載されるにはレシピの作者が手続きを行う必要があるので、つくれぽ数をレシピの人気度の代替として使うのは厳密には正確でない可能性があります。とはいえ、Cookpadは料理に関するデータの宝庫なので今回のような実験を今後も続けていこうかと思います。Cookpadの中の人が読んでいらっしゃったら開発者向けAPIのご検討を是非 :-)

今回の実験と同様に、文章を元に実世界の現象を予測する問題は数多く研究されています。そのうちのいくつか面白いものを載せておきます:

興味がある人は是非参照してください。また、自然言語処理そのものに興味を持たれた方には、こちらの和書を推薦します:

言語処理のための機械学習入門 (自然言語処理シリーズ) [単行本] 高村 大也 (著), 奥村 学 (監修)

最後に、今回のソースコードは全部 https://github.com/mrorii/tsukurepo-predictor に置いておきました❤。


[Images courtesy of justinwkern, ykjc9, gpeters, and pelican]


  1. クロールしたサブカテゴリの詳細はこちらにあります。 [return]
  2. 後述しますが、Cookpadに投稿されるレシピは必ずしもカテゴリに割り振られるわけではないので、カテゴリを素性として使ってしまうと、「カテゴリ入りしたレシピはつくれぽが多くつく」という当たり前の結果になってしまうので取り除きました。 [return]
  3. レシピのコツと生い立ちを使うと素性の数が爆発的に増えメモリを大量に消費する上、精度が全く変わらなかったので取り除きました。 [return]
  4. L1正則化を使うと重みが0につぶれるので、必ずしもクラスとの相関性が大きい素性が特定されるわけではないことに注意。例えば2つの素性が相関している場合、両方ともクラスとの相関性が大きいとしてもそのうちの1つは0につぶされる可能性があります。 [return]
  5. なお、予備実験において「話題」「感謝」などの単語の重みが上位にきていたので、それらはstopwordにしておきました。これは、つくれぽが数多く掲載されるとそのレシピを投稿したユーザが紹介文を以下のように更新することがあるからです:「つくれぽを2000件以上頂きました。ありがとうございます」、「話題入り1000人!感謝☆」、「祝!100人の方に作ってもらいました」。本来であれば、レシピが投稿された時点の紹介文を使いたいのですが、レシピの履歴は公開されていないのでこれらの単語をstopwordにするのが妥当でしょう。 [return]
  6. ある特定の料理を作るためには複数のレシピが存在します。例えば「カレー」という料理を作るには、「佐藤さんの夏野菜を使ったタイ風カレー」「山田君のオーソドックスなカレー」「村上さんの涙が出るほど超激辛なカレー」など複数のレシピが考えられます。 [return]
  7. 2013/08/03 追記:厳密には、「つくれぽが数多くついているレシピはこういう書き方をしているからといって、こういう書き方をするとつくれぽが多くつくわけ」ではありません。因果関係が逆です。 [return]