Google Cloud ベクトル検索の名前空間を用いたチェーン名による商品名のフィルタリング
はじめに
WED株式会社でMLエンジニアをしている catabon です。
以前ご紹介したように、WEDではユーザーの方々から買い取ったレシートに記載されている商品名にデータをより活用できるように、Google Cloud ベクトル検索を用いて最も適切と思われるJANコードを検索し付与するシステムを運用しています。その記事でも例に挙げましたが、同じ商品名でもそれを販売するお店によってレシート上での省略のされ方は異なり、それらに対してどうJANコードを付与するかを考慮する必要があります。
商店名 | レシートの表記 |
---|---|
スーパーA | WED ONE ゼリー 180G |
スーパーB | ウェッドワンゼリー18 |
量販店C | ウエド ワン ゼリ |
コンビニD | ウェッドワンゼリー 180g |
コンビニE | WO ゼリー |
特にパンやおにぎりなどのプライベートブランドの場合、ほぼ同じ名前であっても、チェーンAにしか売られていない商品のJANコードがチェーンBにしか売られていない商品に紐付けられてしまうのは明らかな間違いです。
一つの商品に対して対応するベクトル表現を複数用意して、その際にチェーン名の情報もベクトル表現の中に組み入れ、チェーンが一致した場合のスコアがチェーンが一致しない場合のスコアを上回るようにすることである程度この問題を解消できましたが、この方法ではレシートの表記やそのOCRの結果によっては、異なるチェーンの商品に紐付けてしまう可能性を完全に排除することはできません。
Google Cloud ベクトル検索の名前空間機能
より明示的にチェーンの間でのJANコードの混同を防ぐための方法はないかと考えていたところ、
Google Cloud ベクトル検索の名前空間機能が使えそうだと分かりました。
これは個々のベクトルにrestrictsとしてこのベクトルを利用するための条件を付与するものです。インデックスを作成するときには、"namespace"の値として、属性を指定し、"allow"の値として対応する値のリストを入れます。今回の利用方法としては、"allow"の値として各チェーンの名前を入れました。チェーン名の情報をインデックスに含めること自体は導入前に始めていましたので、その延長で"allow"の値に情報を追加していくことは割とスムーズにできました。
{"id": ..., "embedding": [..., ...], "restricts":[{"namespace": "chain_name", "allow": ["chain_A"]}]}
{"id": ..., "embedding": [..., ...], "restricts":[{"namespace": "chain_name", "allow": ["chain_B"]}]}
{"id": ..., "embedding": [..., ...], "restricts":[{"namespace": "chain_name", "allow": ["default"]}]}
...
商品名からJANコードを検索するときには、"namespace"の値は同じですが、"allow"ではなく、"allow_list"の値として対応する値のリストを入れます。特定のチェーンに依存しないインデックスはデフォルト(default)として常に用い、チェーン名用のインデックスが存在するときにはそれを検索の対象に追加します。
この設定は排他的ではないので、例えばあるチェーン(chain_A)のプライベートブランド商品が系列の他のチェーン(chain_A1)でも売られているときには、そのチェーン名も"allow_list"に入れることでその商品名も検索対象に含めることができます。
{"namespace": "chain_name", "allow_list": ["default", "chain_A", "chain_A1"]}
おわりに
名前空間機能の利用によって、同じレシート表記であっても、他のチェーンの検索結果に悪影響を及ぼさずに特定のチェーンを対象としたインデックスを追加していけるようになりました。また、複数のチェーンで販売されているようなプライベートブランドの商品も、検索時にそのチェーンの商品群に対応するチェーン名を複数指定することで検索に含めることができ、複数のチェーンに対するJANコードの紐付けの質を同時に向上させることができました。
今後もレシートから利用できる情報を活用して、JANコードの紐付けの精度を更に上げていきたいと考えています。
Discussion