現役のITエンジニアが、 システム開発の現場で求められる知識を発信
記事検索
公開

類似検索を実現するための「エンベディング」のしくみ

生成AI関連

はじめに

データ検索と聞くと、多くの場合は「完全一致検索」や「部分一致検索」を思い浮かべるのではないでしょうか。
データベースに保存されている文字列と、検索クエリが一致すればヒットする、という仕組みです。

一方で、実務では次のような場面も少なくありません。

  • 表現が少し違うだけで検索にヒットしない
  • 意味は近いのに、キーワードが一致しない
  • ユーザーの入力が曖昧で、適切な結果を返せない

こうした課題を解決する手法の一つが類似検索です。
そして、その中核となる技術がエンベディング(埋め込み表現)と呼ばれるものです。

本記事では、「エンベディングとは何か」、「なぜ類似検索ができるようになるのか」を、デモを交えながら説明します。

エンベディング(埋め込み表現)とは

エンベディングとは、

  • 単語
  • 文章
  • 画像

といったデータを、その意味や特徴を保ったままベクトル(数値の配列)に変換する技術です。
この変換を行うことで、

  • 自然言語や画像を数値として扱える
  • 数値計算によって「似ている」「近い」を判断できる

ようになります。
つまり、人間が感覚的に行っている「これとあれは似ている」という判断を、数学的に扱えるようにする仕組みだと考える事ができます。

エンベディングの仕組み

Nano Banana Proで生成

デモ:エンベディングを可視化してみる

では、実際にエンベディングを用いて、どのように類似検索を行うのか見てみましょう。
ここではわかりやすさ重視で、単語の類似検索を行ってみます。

実際のエンベディングは、3072次元、1536次元、768次元といった高次元ベクトルを利用するのが一般的です。
ただし、高次元のままでは図示ができないため、本記事では可視化を目的として、1536次元で生成されるベクトルを主成分分析(PCA)という技術を用いて、2次元まで圧縮します。
実運用では精度の観点から高次元のまま利用するのが一般的です。

今回のデモでは、エンベディングモデルにOpenAIのtext-embedding-3-smallを使用します。

※ OpenAIのライブラリの機能で直接2次元のベクトルを生成する機能はありますが、高次元生成→主成分分析をしたほうが精度が良くなることが多いため、今回は主成分分析を採用しています。

デモ用の単語(5語 × 4クラスタ)

エンベディングの性質が分かりやすくなるよう、ジャンルが異なる2つのカテゴリを用意しました。

プログラミング・開発系

Python
JavaScript
プログラミング
実装
デバッグ
AI・データ系

AI
機械学習
深層学習
データ分析
人工知能
食べ物・料理系

寿司
ラーメン
カレー
ハンバーグ
パスタ
乗り物・移動系

自動車
電車
バス
飛行機
自転車

この構成により、「意味が近い単語はまとまり、関係のない単語は離れる」という挙動を視覚的に確認できます。

2次元ベクトルを生成するコード例

以下は、OpenAI APIで2次元のベクトルを生成するサンプルコードです。

embedding_words_with_pca.py

import numpy as np
from dotenv import load_dotenv
from openai import OpenAI

load_dotenv()

client = OpenAI()

words = [
    "Python", "JavaScript", "プログラミング", "実装", "デバッグ",
    "AI", "機械学習", "深層学習", "データ分析", "人工知能",
    "寿司", "ラーメン", "カレー", "ハンバーグ", "パスタ",
    "自動車", "電車", "バス", "飛行機", "自転車",
]

# デフォルトで1536次元のベクトルが生成される
response = client.embeddings.create(
    model="text-embedding-3-small",
    input=words,
)

vectors = np.array([d.embedding for d in response.data]) 

# 主成分分析という技術で、1536次元を2次元に圧縮(ここの説明は省略)
vectors_2d = pca_to_2d(vectors)

for word, vector in zip(words, vectors_2d):
    print(f"{word}: {vector}")

実行すると、下記のようなベクトルが得られます。

実行結果

Python: [-0.15126746  0.35255755]
JavaScript: [-0.05191365  0.37248566]
プログラミング: [-0.12384383  0.2774263 ]
実装: [-0.11643543  0.0802914 ]
デバッグ: [ 0.11005437 -0.018176  ]
AI: [-0.16327941  0.26665992]
機械学習: [-0.46703785  0.09946574]
深層学習: [-0.32870221  0.25412075]
データ分析: [-0.15491634  0.31756677]
人工知能: [-0.29019093  0.23530355]
寿司: [ 0.38669948 -0.05278358]
ラーメン: [ 0.52679815 -0.00760298]
カレー: [0.51998144 0.0546167 ]
ハンバーグ: [0.44496972 0.03363166]
パスタ: [0.55319311 0.10029806]
自動車: [-0.23238473 -0.49313654]
電車: [-0.14845397 -0.57404895]
バス: [ 0.05772    -0.41452748]
飛行機: [-0.22416269 -0.37806025]
自転車: [-0.14682776 -0.50608828]

可視化結果のイメージ

得られたベクトルをプロットすると、次のような配置になります。

単語の配置

単語をベクトル化したときの配置。各ジャンルで色分けしている。

ここで重要なのは次の点です。

  • 同ジャンルの単語同士が近く集まり、クラスター(塊)を形成する
  • クラスター同士は、ある程度の距離を取って配置される
  • プログラミング・開発系とAI・データ系は意味が近いため、比較的近くに配置される

単語の表記が似ているかどうかではなく、意味的な近さが反映されていることが分かります。

意味が近い単語を取得する(類似検索のデモ)

「エンベディング」という単語を上記と同じようにベクトル化すると、


query_vector = np.array([-0.21509223417701273, 0.12073096051365606])

という値が得られました。
これを使って、類似検索をしてみましょう。

今回は、コサイン類似度という指標を使用します。
詳細な説明は省略しますが、ベクトル同士の類似度を測るときによく使われる指標です。
実装例はこちらです。

similarity_search.py

import numpy as np

words = [
    # 省略
]

vectors_2d = np.array(
    [
        # 省略
    ]
)

query_vector = np.array([-0.21509223417701273, 0.12073096051365606])


def cosine_similarity(a: np.ndarray, b: np.ndarray) -> float:
    return np.dot(a, b) / (np.linalg.norm(a) * np.linalg.norm(b))


similarities = []

for word, vector in zip(words, vectors_2d):
    score = cosine_similarity(query_vector, vector)
    similarities.append((word, score))

# 類似度の高い順にソート
similarities.sort(key=lambda x: x[1], reverse=True)

# top3 を表示
top3 = similarities[:3]

print("=== クエリに類似した単語トップ3 ===")
for word, score in top3:
    print(f"{word}: {score:.4f}")
実行結果

=== クエリに類似した単語トップ3 ===
人工知能: 0.9977
深層学習: 0.9958
実装: 0.9911

プロットすると、このようになります。
たしかに、近い単語を取得できてそうですね。

クエリを追加した単語の配置

クエリを追加した、単語をベクトル化したときの配置。各ジャンルで色分けしている。★マークは今回のクエリ。

実際にはどのように使われているか

今回のデモでは単語を扱いましたが、文章でも同様にエンベディングを生成できます。
そのため、活用範囲は非常に広く、検索・推薦・チャットボット・マッチングなど、多くの実システムで利用されています。
ここで、弊社が開発したシステムにどのように組み込んでいるかをご紹介します。

社内ポータル向けAIチャットボット

弊社が開発した社内ポータル向けAIチャットボットでは、RAG(検索拡張生成)を用いる際に、

  • Slack上でのユーザーの問い合わせ文をエンベディング
  • 社内ドキュメントをベクトル検索
  • 質問内容に関連性の高い資料を取得

といった用途で活用しています。

詳細は以下の事例をご参照ください。
RAG(検索拡張生成)と生成AIを活用したAIチャットボットの開発

SES案件マッチングシステム

弊社のSES案件マッチングシステムでは、

  • 人材のスキル情報
  • 案件の要件情報

を似たような文章構造に変換したうえでそれぞれをエンベディング化し、意味ベースでマッチングを行っています。
キーワードが完全一致しなくても、内容的に適合度の高い組み合わせを見つけられる点が特徴です。
詳細はこちらをご覧ください。
生成AIによる構造化出力とベクトル検索で、SES案件探索を効率化

主なエンベディングモデルの紹介

ここで、OpenAIとGoogleがリリースしている、代表的なエンベディングモデルを紹介します。

OpenAIのエンベディングモデル

下記2つがよく使用されます。

  • text-embedding-3-small
  • text-embedding-3-large

主な特徴は以下の通りです。

  • テキスト生成モデルと比べてコストが低い
  • large は高精度、small はコスト重視

公式ドキュメント
OpenAI Platform > Docs > Vector embeddings

Googleのエンベディングモデル

下記がよく使用されます。

  • gemini-embedding-001

主な特徴は以下の通りです。

  • 次元数を小さくしても高いスコアを維持しやすい
  • 入力トークンの上限は2,048トークンまでと、やや小さめ。

公式ドキュメント
Gemini API ドキュメント > エンベディング

まとめ

完全一致検索はシンプルで分かりやすい一方、意味の揺れや表現の違いには弱いという課題があります。

エンベディングを用いることで、

  • 意味ベースの類似検索が可能になる
  • 曖昧な入力にも柔軟に対応できる
  • 検索やマッチングの精度を大きく向上させられる

といったメリットを得られます。
「類似検索を実現したい」「検索精度を上げたい」と考えたとき、エンベディングは最初に検討すべき技術の一つと言えるでしょう。

生成AI活用支援サービスのご紹介

Tech Funでは、お客様のフェーズに合わせ、生成AI活用に向けた支援を3つのパックでご提供しています。

  1. 無料診断パック:業務・プロセスの現状を無料で診断し、生成AI活用の可能性をレポートします。
  2. 検証(PoC)パック:診断で有効性が確認された業務を対象に、プロトタイプ構築を支援します。
  3. コンサルティングサービス:生成AI導入戦略の策定から運用体制構築までを包括的に支援します。

生成AIに限らず、Web・業務システム開発やインフラ設計など、技術領域を問わずご相談を承っています。「何から始めれば良いか分からない」という段階でも構いませんので、ぜひお気軽にお問い合わせください。

執筆・編集

Tech Fun Magazine R&Dチーム
Tech Funの生成AI研究に携わるエンジニアが、最新のAIモデル動向やプロンプト設計、実業務への応用手法など、生成AIに特化した知見を執筆・編集しています。
モデル評価や業務シナリオに応じたAI活用設計など、日々のR&D活動で得られる実践的なノウハウをわかりやすく紹介します。

ARTICLE
生成AI関連記事一覧

生成AI関連

類似検索を実現するための「エンベディング」のしくみ

生成AI関連

CS業務効率化を始める最小ステップ

生成AI関連

生成AIのテキスト生成のしくみとパラメータ

生成AI関連

生成AIの導入と定着に向けて

生成AI関連

AI議事録のしくみ

生成AI関連

「良いプロンプト」はAIに作らせよう

生成AI関連

生成AIの“知識の限界”をどう突破する?

生成AI関連

GPT-5.2 徹底解説

生成AI関連

MCPサーバーを活用する【後編:実行編】

記事一覧を見る