Tech Blog

ベクトル検索は「意味が近い」を勝手に混ぜてくる — 種類の違うカタログが推薦に紛れ込む罠

RAG Vector Search Embeddings Recommendation Search

はじめに

個人で、映像や音楽を一つに束ねる総合プラットフォーム(“EVERY DAYS”)を作っている。動画のストリーミング(VOD)、DVD や CD の宅配レンタルといった複数のサービスを、同じ会員・同じ画面の下にまとめる構成だ。一つのレンタル事業を DMM のような総合プラットフォームへ広げていく構想は DVDレンタルからDMM型プラットフォームへ に書いた。

この作りのため、映像作品(VOD・DVD)と音楽(CD)が、同じシステムの中に同居する。そして検索・関連レコメンド・パーソナライズは、一つの埋め込み(embedding)索引でまとめて賄っている。アイテムをベクトルにして持っておき、コサイン類似で「近いもの」を返すだけの、軽い作りだ。埋め込みや意味検索そのものの話は 測定駆動でRAGの検索精度を上げる に書いた。この記事は、その構成に静かに潜む一つの罠の話だ。

罠は、種類の違うカタログが同じ索引に同居しているときに出る。映像作品(VOD・DVD)と音楽CDが同じ索引に入っていると、「この映像作品に近いもの」を出したつもりが、音楽CDが混じって返ってくる。埋め込みにとって二つは”意味が近い”からだ。だが利用者からすれば、映像を探しているのに別物を差し込まれることになる。


何が起きるか

この構成では、同じ一つの索引が三つの出口を持つ。

                          ┌─▶ ① 意味検索:クエリ文 ───────▶ 近いアイテム
   一つの埋め込み索引 ─────┼─▶ ② 関連レコメンド:あるアイテム ─▶ 近いアイテム
   (全アイテムが同居)    └─▶ ③ パーソナライズ:履歴の重心 ──▶ 近いアイテム

            └─ 異種カタログ(映像/音楽 …)が同じ索引に同居

                        └─▶「意味が近い」だけで別カテゴリが上位に混入

                          (①②③の三つの出口すべてで起きる)

三つの機能は同じ索引を共有しているので、混線も三つの出口すべてで同じように起きる。一つの画面で気づいても、直すべき場所は一つではない。


なぜ起きるのか

埋め込みの類似度が測るのは「意味の近さ」だけだ。そのアイテムがどのカテゴリに属すか利用者に出してよいものかもう見たものか——といった業務上の制約は、ベクトルの中に入っていない。だから類似度の高い順に上から取ると、意味は近いが出してはいけないものが平気で混ざる。

言い換えると、「意味が近い=出してよい」ではない。 埋め込みは業務の境界を知らない。境界は、埋め込みの外側にある知識だ。


どう塞ぐか

対策は二つで一組になる。片方だけでは効かない。

1. 業務フィルタは、各出口に掛ける

出してよいカテゴリだけを残すフィルタを、検索・関連・パーソナライズのそれぞれの出口に置く。結果を絞るか、問い合わせの側で除外するかはどちらでもいい。大事なのは、共通の索引だからと一箇所で済ませないことだ。罠は三つの出口にあるので、フィルタも三箇所に要る。

2. フィルタするなら、取得ウィンドウを広げる

フィルタは件数を減らす。目標が10件でも、先に上位10件だけ取ってからフィルタすると、除外後に足りなくなる。だから消える分を見越して、目標の数倍を取ってからフィルタする。パーソナライズでは「もう見たもの」も外すので、目標に加えて履歴の件数ぶんも余分に取っておく。

【素朴】上位N件だけ取る ─▶ フィルタ ─▶ 残りが目標に届かない/別カテゴリが残る

                          (消える分を見込んでいない)┘

【対策】目標の数倍+履歴件数を取る ─▶ 業務フィルタ ─▶ 既視聴も除外 ─▶ 上位から目標件数

                                     上位が消えても目標件数が残るよう、広めに取る

そして、埋め込みで十分な件数を出せないときは、人気順のような別の手に静かに切り替える(graceful degrade)。品質は落ちても、結果がゼロになって画面が空になるよりはいい。


「近い」と「関係ある」の間に置くもの

「意味が近い」は「出してよい」ではない。 埋め込みの類似度は意味の近さしか測らない。カテゴリの境界も、出してよいかどうかも、埋め込みの外側にある業務知識だ。似ているからと素通しにしない。

業務フィルタは、共通の索引でも出口ごとに掛ける。 入口(索引)は一つでも、出口は複数ある。一箇所直して直った気にならない。同じ索引を共有する機能は、同じ罠を共有している。一つで踏んだら、残りも点検する。

フィルタするなら、取得を広げる。 除外で件数が減ることを前提に、目標の数倍+除外予定の件数を取ってからフィルタする。「上位が消えても目標件数が残る」ように取得ウィンドウを設計する。

埋め込みは強力だが、素直すぎる。似ているものを全部連れてくる。業務の都合を教えるのは、埋め込みの外に置いた自分のフィルタの仕事だ。


関連する記事

気軽にメッセージください

仕事の依頼、案件紹介、ご感想・ご質問なんでもお待ちしております。 高い志をもった同士の皆様と繋がることを切に願っております。 これからも人生を掛けたチャレンジを続けていきます。 何卒よろしくお願い致します。