fastText in Cookpad

研究開発部の原島です。去年からはレシピサービス開発部も兼務しています。そちらの話(検索の話)はおいおいするとして、今日は研究開発部の話(機械学習の話)をします。

fastText

単語の分散表現、重要ですよね。ニューラル全盛期の現代において、使わないという選択肢はほとんどないように思います。

最初に話題になったのは、2013 年に発表された word2vec でしょう。「king」のベクトルから「man」のベクトルを引き、「woman」のベクトルを足したら「queen」のベクトルになったという話は有名です。一方、最近は、2018 年に発表された BERT(及び、それに類するモデル)の話題で持ちきりですね。

fastText は、ご存知の方も多いと思いますが、分散表現を学習するためのライブラリです。学習のアルゴリズム自体を指すこともあるように思います。fastText の論文は以下です。2017 年に発表されたものなので、発展が速いこの業界においてはもう古い論文なのかもしれません。

  • Enriching Word Vectors with Subword Information. Piotr Bojanowski, Edouard Grave, Armand Joulin, Tomas Mikolov.

なぜ fastText なのか?

クックパッドでは fastText をよく使っています。では、なぜ fastText なのでしょう?上でも触れたように、word2vec や BERT などの選択肢もあります。もちろん、fastText も主要な選択肢の一つではありますが、どうして fastText なのでしょうか?

様々な理由がありますが、まとめると、「性能と運用のバランスがよい」といったところでしょうか。

性能の面では、サブワード(部分文字列)が考慮できる分、word2vec よりは fastText がよいでしょう。一方、文脈を考慮した表現が学習できる分、fastText よりは BERT がよさそうです。もちろん、これらは一般論です。実際にはタスクや学習データによって話が違ってくるでしょう。

一方、運用の面では BERT より fastText や word2vec がよいでしょう。BERT は事前学習が大変です。クックパッドでも何度かトライしていますが、お金も時間もかかります。学習データ、単語分割器、サブワード分割器、whole word masking、マスク確率、...。試行錯誤するだけでもかなりのお金と時間がかかります。

もちろん、ファインチューニングで済ますという手もあります。ありがたいことに、世の中には事前学習済みのモデルが沢山あります。これらを使えば、事前学習する必要はありません。しかし、結局、デプロイするにはモデルが大きかったり、API として使うには推論が遅かったりといった問題が残ります。

このように、性能と運用のバランスを考えると、fastText はいまでも非常に優れた選択肢だと思います。

fastText を使っている取り組み

クックパッドで fastText を使っている取り組みとしては、たとえば、以下があります。

  • 単語埋め込みを利用した商品に対するキーワードの予測(to appear). 山口泰弘, 深澤祐援, 原島純. 言語処理学会第 28 回年次大会発表論文集.

こちらは、クックパッドマートの商品名から、食材を表すキーワードを予測する取り組みです。キーワードや商品名をベクトルに変換するのに fastText を使っています。予測結果はクックパッドマートの管理画面で使われています。

余談ですが、こちらの取り組みは今年の言語処理学会で委員特別賞をいただきました。ありがとうございます。

こちらは、レシピのタイトルから、そのレシピで使われるであろう食材を予測する取り組みです。タイトル中の単語をベクトルに変換するのに fastText を使っています。予測結果はレシピの投稿画面で使われています。

こちらは、レシピのタイトルから、そのレシピのカテゴリ(e.g., 肉料理、魚料理、野菜料理、...)を予測する取り組みです。こちらも、タイトル中の単語をベクトルに変換するのに fastText を使っています。予測結果は、近日中に、レシピのブックマーク画面で使われる予定です。

その他、まだ実験段階の取り組みでも fastText をよく使っています。

fastText の学習・利用フロー

以下は、クックパッドにおける fastText の学習・利用フローです。Redshift から学習データを取得し、fastText を学習した後、モデルを S3に保存するというのがおおまかな流れです。たいしたことはしていません。ちょっと変わったことがあるとすれば、学習データが Redshift にあることくらいでしょうか。

f:id:jharashima:20220418092004p:plain:w300

1. 学習データの取得

fastText の学習にはテキストが必要です。日本語の場合、さらに、単語分割が必要です。

クックパッドの場合、全レシピのテキスト(e.g., タイトル)が Redshift に保存されています。また、その分割結果も Redshift に保存されています。詳細は以下の記事をご覧ください。fastText の学習にはこれを使っています。

分割結果の取得には Queuery(きゅうり)というシステムを使っています。Queuery は、UNLOAD を使うことで、Redshift やクライアントに負荷をかけずに SELECT を実行できるシステムです。Queuery は去年末に OSS 化されました。詳細は以下の記事をご覧ください。研究開発部の山口による Python クライアントもあります。

2. fastText の学習

Python スクリプトに以下の 2 行を書くだけです。fastText、便利すぎますね...。

import fasttext
model = fasttext.train_unsupervised('data.txt', model='skipgram')  # cbow でも可

全レシピ(2022 年 4 月時点で約 367 万品)のテキストを使っても、学習は約 10 分で終わります。メモリも 2GB 程度で済んでいます。学習には EC2 のスポットインスタンスを使っています。

パラメータは特にいじっておらず、デフォルトのままです。たとえば、ベクトルの次元数は 100 です。パラメータのチューニングは今後の課題(後述)です。

3. モデルの保存

モデルは S3 に保存しています。ロールバックできるように、過去に学習したモデルも残してあります。幸い、実際にロールバックが必要になったことはありません。まだ特に困っていませんが、ライフサイクルくらいは設定してもいいかもしれません。

4. モデルのダウンロード

学習済みのモデルを使いたいアプリケーションに対して S3 の該当フォルダへの Read アクセスを許可します。これで各アプリケーションでモデルをダウンロードできます。

以上が fastText の学習・利用フローです。その他、補足事項として以下があります。

fastText はレシピのフィールド(e.g., タイトル、材料、...)毎に学習しています。これは、fastText を使うタスク毎に着目するフィールドが違うためです。タイトルに着目するタスク(e.g., レシピの分類)ではタイトルで学習したモデルが使えるように、材料に着目するタスク(e.g., 材料の分類)では材料で学習したモデルが使えるようにしています。

ジョブスケジューラーやデプロイツールには Kuroko2hako を使っています。実行は基本的に月次です。学習時間が短いので、日次で実行したところで、特に問題はありません。ただ、分散表現はそんなに変わらないだろうと思うので、月次としています。もしかしたら年次でもいいのかもしれません。

今後の課題

最後に、今後の課題を三つほど挙げておきます。

一つ目は、「fastText の学習」でも触れたように、パラメータのチューニングです。学習アルゴリズムや学習データ、学習率、ベクトルの次元数、サブワードのレンジなど、チューニングの余地はたくさんあります。この辺りは腰を据えて取り組んでいきたいです。

二つ目は分散表現の評価です。一つ目の話とも関連するのですが、どのような分散表現がよいかは自明ではありません。基本的には、後段のタスクにおける評価指標を最適化する分散表現がよい気がします。ただ、後段のタスクにもいろいろあるので、悩ましいところです。

三つ目は代替モデルの調査です。「なぜ fastText なのか?」でも触れたように、本番での運用まで考えると、BERT のようなモデルが fastText より明らかによいとは言えません。一方、この業界の発展は速く、様々な懸念を払拭するモデルが明日にも発表されるかもしれません。業界の動向には常にアンテナを張っていきたいです。

おわりに

そういえば、つい最近、就業形インターンシップに「機械学習コース」を開設しました。上で挙げた課題はもちろん、クックパッドにおける機械学習に興味がある方は是非ご応募ください。

中途採用のご応募もお待ちしております。

/* */ @import "/css/theme/report/report.css"; /* */ /* */ body{ background-image: url('https://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('https://cdn-ak.f.st-hatena.com/images/fotolife/c/cookpadtech/20140527/20140527172848.png');*/ /*background-repeat: no-repeat;*/ /*background-position: left 0px;*/ /*}*/