👁️

ベクトル検索を使って顧客情報の重複登録を防止する

に公開

ベクトル検索を使って顧客情報の重複登録を防止する

1. 導入

顧客情報を人の手で管理していると、必ずといっていいほど発生する課題があります。
それは「同じ顧客が複数回登録されてしまう問題」です。

従来は「部分一致」「正規化した上で LIKE 検索」といった方法で荒く抽出したリストの中から重複を検知していました。しかし、現場ではこれらの方法ではどうしても限界があります。

そもそも荒く抽出するため、確認するデータ量が多いという課題があります。
それだけでなく、扱う顧客情報の性質上「法人名が同じでも住所が違う」だったり、その逆もありえます。
加えて過去経緯として、人の手で登録していたため以下のような表記揺れも存在しました。

法人名が

  • 「㈱ABC」
  • 「ABC株式会社」
  • 「ABC(株)」

住所が

  • 「1-1」
  • 「1丁目1番地」

このように書き方が異なっていると、従来の検索では見逃してしまうケースが多々ありました。

そこで試したのが「ベクトル検索」を使って、似ている顧客情報を登録前に検出するアプローチです。


2. アプローチの概要

今回のアプローチでは、顧客情報を Embedding(ベクトル化)し、既存のデータと照合して重複候補を探しました。

利用した技術は BigQuery ML

  • ML.GENERATE_EMBEDDING
  • VECTOR_SEARCH

です。

入力項目はシンプルに「法人名 + 住所」。
これだけで、従来では負荷が高かった重複を簡単に拾えるようになりました。


3. 実装の流れ

実装は大きく分けて以下の流れです。全てBigQueryで完結します。

  1. Embedding の生成
    法人名と住所を結合した文字列を Embedding に変換し、Table化します。

まずはVertex AIのモデルを使用できる状態にします

CREATE OR REPLACE MODEL `sandbox.issuy.vertex_ai`
REMOTE WITH CONNECTION `sandbox.issuy.vertex_ai`
OPTIONS (ENDPOINT = 'gemini-embedding-001');

※仮にsandboxというGCPプロジェクトの、issuyというdatasetを使用していることにします

顧客情報テーブル(customers)から法人名+住所でembedding化します

SELECT *
FROM ML.GENERATE_EMBEDDING(
  MODEL `sandbox.issuy.vertex_ai`,
  (SELECT id, name, address, 'name:' || name || ' address:' || address AS content FROM `sandbox.issuy.customers`),
  STRUCT(TRUE AS flatten_json_output,
    'SEMANTIC_SIMILARITY' AS task_type)
)

※contentsというフィールドが自動的にembedding化されます

このSelect結果を別テーブルに保存すると、ベクトル検索の準備は完了です。
私の場合はDataformでテーブル化し、1日1回最新化される構成にしました。
今回は sandbox.issuy.embedding_result に保存したとして、以下の説明で使います。

  1. 登録時の類似度チェック
    新しい顧客を登録する際に、その法人名+住所でベクトル検索を行い、類似度の高い候補を取得します。
SELECT
  distance
  base.id,
  base.name,
  base.address,
FROM
  VECTOR_SEARCH(
    TABLE `sandbox.issuy.embedding_result`,
    'ml_generate_embedding_result',
    (SELECT ml_generate_embedding_result
      FROM ML.GENERATE_EMBEDDING(
        MODEL `sandbox.issuy.vertex_ai`,
        (SELECT "name:ABC株式会社 address:東京都新宿区西新宿二丁目8番1号" AS content),
        STRUCT(TRUE AS flatten_json_output)
    )),
    distance_type=>"COSINE",
    top_k=>5
  )
ORDER BY distance

これで「ABC株式会社(東京都新宿区西新宿二丁目8番1号)」に似ている顧客情報の上位5件を出力できます
顧客情報登録時にはこの類似度スコアの高い上位5件をオペレーターに提示し、「既存の顧客と重複していないか」を確認してから登録してもらいます。

これにより、大量のリストから重複判定していたことで見逃していたケースでも「たった5件の候補を確認するだけで気づける」ようになりました。


4. 工夫した点

  • 住所・社名の正規化は不要
    従来は全角/半角や漢数字変換などを行っていましたが、Embedding モデルの性能が高いため正規化なしでも十分な精度が得られました。

  • 対象フィールドは最小限に
    法人名と住所の2つに絞ったことで、余計な情報に寄ってしまうことによる誤マッチを減らせました。

  • 性能要件も現実的に
    1回の検索で結果が返るまでに約10秒。顧客登録の頻度を考えると、これ以上の高速化は不要と判断しました。過度な最適化にリソースを割かずに済んだのも良かった点です。


5. 結果

  • 従来の方式では防げなかった「微妙に表記が違う重複登録」を検出できるようになりました。
  • 以前は大量のデータを人力で目視確認していましたが、その負担を大幅に削減。
  • これまではエンジニアが重複判定の仕組みを都度改修していましたが、今ではオペレーターが候補を確認するだけで済み、エンジニアの負担を減らせました。

結果として、省コスト化しつつ精度の高い重複防止が実現できました。


6. まとめと学び

ベクトル検索は、従来の完全一致や部分一致では対処できない「表記ゆれ・曖昧さ」を含む重複検出にとても有効でした。

もちろん万能ではなく、最終的な判定は人の目に委ねる部分も残りますが、それでも「候補を精度良く5件に絞る」だけで業務効率は大きく改善します。

今回の取り組みで学んだのは、

  • 「表記揺れがあるデータ」に対する重複判定の選択肢としてベクトル検索は有効
  • Embedding モデルの性能が高いため、過剰な正規化は不要
  • システム要件に合わせた現実的なチューニングで十分効果が出る

という点です。

今後はこのようなベクトル検索の特徴を活かして、プロダクトの「オススメ商品」のようなロジックにも適用していこうと思っています。

株式会社ドクターズプライム

Discussion