技術部の根岸(@negipo)です。さいきん小説のコンテストで入賞してコミック百合姫に名前が載りました。百合コミック誌の巻頭カラーに載っているnegipoという文字列を眺めているとじわじわ狂気じみた笑いがこみ上げてきます。
さてみなさん、買い物してますか? もちろんしてると思うんですが、スーパーの店先で商品を見たときに「オッ、これは安い。お得〜」ってどれくらいちゃんとわかっているでしょうか。ぼくはお肉が大好きなので肉類についてはそれなりにわかっているつもりなのですが、季節の野菜とかは正直なところあまりよくわかりません。そんなとき、ぱっとお得な商品がわかるとうれしいですよね。
なんと、実はさいきんトクバイアプリにつくられた『トクバイおすすめラベル』で、トクバイに掲載されているお得な商品がよりわかりやすくなりました。
というわけで、この記事では『トクバイおすすめラベル』で用いられている商品の安さを判定する基盤、『安さ基盤』の技術背景について紹介しようと思います。
戦略
目の前で売っている商品がどれくらい安いのか、みなさんはあたまの中でどのように判定しているでしょうか。多くの場合では、自分の経験から導き出した相場と比較して高いか安いかを決めていると思います。
安さ基盤では人間の値段観における戦略と同じように、トクバイに投稿された数千万件の商品が持っているさまざまな情報から『相場価格』を算出するモデルを作成し、算出されたそれよりもどれくらい安いのかを判定することで、個別の商品が安いと言えるかどうかを決定しています。
相場価格の算出
商品の情報
『商品の情報から相場価格を算出する』ためにまず必要なことは、商品を構成している情報を整理し、その内容から相場価格を推定する方法を考えてゆくことです。
では、トクバイの商品を構成している情報にはどんなものがあるでしょうか。簡単に列挙すると、下記のようなものがありそうです。
- どのカテゴリの商品か(商品カテゴリ)
- どれくらいの量の商品か(数量・単位の数値表現)
- 何月に掲載されている商品か(掲載月)
- 価格
- などなど……
トクバイでは、これらの情報をスーパーの店員さんが手入力しています。具体的な商品例としては、下記のようなものが挙げられます。
- 商品名: 『メイクイーン』
- 数量・単位: 『1個』
- 掲載月: 『1月』
- 価格: 『18円』
さて、この商品の相場価格がいくらかということは、メイクイーンがじゃがいもの品種であることをわかっていれば、『一個のじゃがいもの相場価格がいくらなのか』という単純な問題に落とせそうです。
商品カテゴリの推定は難しいタスクですが、トクバイにはすでに商品情報から商品のカテゴリを推定する基盤が存在します。今回の安さ基盤ではその基盤技術を用いているので、この記事では省略します。
また、商品カテゴリは木構造を持っていますが、あまりに意味の広い、あるいは狭いカテゴリ分類に対して相場価格の算出を行うと適当な妥当性が得られないため、人間の手で相場価格の算出対象となる商品カテゴリを決定しています。
回帰分析の概要
相場価格は、数量の情報から数値表現を取り出し、回帰式を介することで推定できそうです。
相場価格 = 回帰係数 * 数値表現の数値 + 切片
このように、数値表現を一つだけもちいた多項式で目的となる値を算出するような分析を単回帰分析と言います。より詳しく知りたい方は良い解説をしているサイトを参照してください。
それでは、手持ちの情報から回帰係数と切片を求めることは今すぐに可能でしょうか。
数値表現の正規化
前節で『数値表現を取り出す』と書きましたが、回帰分析で取り扱える『数値表現の数値』と、スーパーの店員さんが入力してくれた自然文の『数量・単位』にはけっこう距離があります。すなわち、自然文で書かれた数量・単位がどのような数値表現に相当するかを抽出・解釈し、具体的な数値を抽出しなければなりません。
ここでは、数量・単位に対して自然言語処理を行い、分かち書きされた数詞と周辺の品詞から特徴を見ていきます。かんたんに言えば、『1個』であれば『1』と『個』に分割し、投稿された商品に登場する回数が高い文から順に数値と数詞の特徴を確認します。すぐにわかることですが、このあとの段階で回帰分析を行うことを踏まえると、この時点で抽出した数値表現に対してある程度抽象化した分類が必要であることが見えてきます。とうぜん、『1kg』のじゃがいもと、『1個』のじゃがいもは、ぜんぜん価格が違いそうですよね。
結果として、今回の実装では数値表現の系統に3種類の分類を設けました。
- 個数系
- 例: 『1個』のとき、
1.0
の数値を持つ
- 例: 『1個』のとき、
- パッケージ系
- 例: 『1/2パック』のとき、
0.5
の数値を持つ
- 例: 『1/2パック』のとき、
- 尺度系
- 例: 『日本ハム 35g×3パック』のとき、
0.105
な数値を持つ
- 例: 『日本ハム 35g×3パック』のとき、
ここで分割された数値表現の系統を、商品カテゴリの分類と同様にそれぞれ別個の回帰分析の対象として取り扱うことにしました。
またこのとき、『1袋 6個』のように複数の系統が併記されている場合、尺度系 > 個数系 > パッケージ系という詳細度の優先順位を設け、より詳細度の高い系統の数値表現を採用するようにしています。
回帰分析
安さ基盤ではscikit-learn
のLinearRegression
で最小二乗法を用いた単回帰分析を行っています。トクバイのコードベースのほとんどはRuby
ですが、個々のタスクに適切な言語があるときはそれぞれ個別の言語を採用しています。統計等のタスクではライブラリが充実しているのでPython
を使うことが多いです。
さて、単回帰分析の回帰式についてはすでに書きましたが、安さ基盤で相場価格の算出に実際に利用されている式は下記のような多項式です。
相場価格 = 月別補正比 * (回帰係数 * ln(数値表現の数値) + 切片) ※ lnは自然対数
この式の下記の内容は、精度検証において有益と捉えられたため付加された修飾です。付加の際に使われた評価方法については後ほど説明します。
- 自然対数: 価格と数値表現の関係が線形の関係よりも自然対数の変換を介した関係に近く、また外れ値の影響を小さくできるため追加した
- 月別補正比: 季節の情報は商品価格に影響が大きいので、何月に投稿された商品なのかという情報をもとに各月の平均価格の比による補正比を追加した
結果として、自然対数を取った数値表現を用いて回帰分析による回帰係数と切片の決定を行うことができました。また、月別補正比自体は過去の商品価格から比を出すだけなので簡単なタスクです。
さて、この回帰式に立脚すると、最初に例示した下記の商品は
- 商品名: 『メイクイーン』
- 数量・単位: 『1個』
- 掲載月: 『1月』
- 価格: 『18円』
次のような正規化と分類によって回帰式に適用することができます。
- メイクイーンは『じゃがいも』の商品カテゴリに所属する
- 『1個』という数量から抽出される数値表現は『1.0』という数値を持つ『個数系』に所属する
- 掲載月から、月別補正比は『1月』のものを採用することになる
係数は下記のように決まります。
- 掲載月が1月
- 月別補正比: 1.03
- 商品カテゴリが『じゃがいも』で、数値表現が『個数系』な単回帰分析の係数
- 回帰係数: 73.48
- 切片: 30.14
相場価格 = 月別補正比 x (回帰係数 x ln(数値表現の数値) + 切片) = 1.03 * (73.48 * ln(1.0) + 30.14) = 31.04
結果として、相場価格は31円となります。
安さの判定
相場価格の算出によって、18円のメイクイーンは相場価格の31円よりもかなり安いことがわかりました。これをどの程度安いと言っていいのでしょうか。
今回公開された機能では、商品の相場価格に対する分布とユーザーテストから相場価格と価格の比に対して二段階の閾値を設けています。お得な商品には少し目立つ表示、非常にお得な商品にはかなり目立つ表示を施すことで、日常の買い物における使いやすさを志向しています。
たとえば、下記のような基準で判定してみましょう。
- 安さが同じカテゴリの上位30%に含まれる = 相場価格から20%以上安い = 『お得な商品』
- 安さが同じカテゴリの上位10%に含まれる = 相場価格から40%以上安い = 『非常にお得な商品』
この基準に従うと、18円のメイクイーンはちょうど相場価格の6割を切る安さなので『非常にお得な商品である』となります。感覚的には正しそうに見えますね。
※ この節で書かれている閾値は現在公開されている機能で実際に使われているものではありません。
評価
回帰分析を行う際、数値表現が抽出できない、回帰係数が負になるなどの理由で、回帰式の適用が適切ではないケースが出てきます。そういった不適切な適用を除外すると、 今回適用した相場価格算出のカバー率は対象となる生鮮商品の84%になっています。また、商品カテゴリごとの相場価格を平均価格にフォールバックして算出するという手法も取っており、それを加味したカバー率は88%になっています。
また、手法の適用を決定する際、誤差を示す指標であるRMSEやMAEなどを価格に対してもちいて、算出された各手法の相場価格を相対評価し取捨選択するという戦略を取っています。ここでは詳述しませんが、自然対数を取り入れない単回帰分析、数値表現の系統をすべて用いた重回帰分析、一部の非線形回帰分析などの回帰分析手法や、地理情報や総務省統計局の小売物価統計調査を用いた補正など、さまざまな手法を試しましたが、最終的には採用しませんでした。
課題
安さ基盤で取り入れられた数値表現の抽出と分類は一定の正しさを持っていますが、失敗している例がいくつかあります。たとえば『たい』の商品カテゴリでは、『個数系』の数値表現において、『1』では『尾』、『2』では『切』が支配的に採用されていることがわかっています。これを鑑み『カット系』などのあらたな系統の導入を検討する必要があるかもしれません。
商品カテゴリの分類に失敗している場合があります。たとえば『牛乳』では、『900ml』の商品のほうが『1000ml』の商品よりも平均価格が高いです。これは『900ml』のパッケージを採用しているブランドに比較的高価なものが多いためです。また『ふぐ』では夏に投稿されるもののほとんどが干物で構成されるために補正比が極端な値になっているという状況があり、鮮魚と干物が同一のカテゴリに属していることの問題を示しています。いずれも相場価格算出を目的とした商品カテゴリの分類としては不適切な例と言えるでしょう。
また、価格と自然対数を取った数値表現の数値の関係が線形ではないために回帰分析に失敗している例があります。たとえば『みかん』『1個』の相場価格算出にはかなり失敗していることがわかっています。この問題に対しては決定木を用いた非線形回帰分析で相対的にかなり高い評価を作ることができているため、今後検討が進められると良さそうです。
商品価格は気候などの影響によって年ごとに大きく変動することがあります。現状では月別補正比のウィンドウが今年投稿された商品も含んでいるので一定の対応は行なわれていますが、即時性を考慮するためにより感度の高い分析を取り入れることが求められていくかもしれません。
おわりに
商品の安さがわかる、安さ基盤を作りました。 生鮮の買い物にあまり慣れていない人はもちろん、毎週のように買い物をしているような人でも、普段あまり買わない野菜や魚介類の相場はわからないことがあるのではないでしょうか。そういう日常でただしくお得な商品が買えるようになるための、けっこう良い技術基盤が作れたと思っています。
かしこく、たのしい買い物のために、ロコガイドはこれからも生活に寄り添った開発を続けていきます。