埋め込みとベクトル検索: BERT と文変換器
このシリーズの最初の記事では、RAG アーキテクチャとその役割について説明しました。 LLM の幻覚を解決します。すべての RAG システムの心臓部は、 検索: 潜在的に知識ベースを見つける能力 アプリケーションに最も関連性の高い巨大なドキュメント。この能力のベースとなっているのが、 完全にオン 埋め込み そしてさらに ベクトル検索.
埋め込みとは、テキストの意味を数値的に表現したものです。 単語、文、文書間の意味的関係を捉える数値 (ベクトル)。 埋め込みの品質は検索の品質を直接決定するため、 RAG システム全体の品質。間違った埋め込みモデルを選択すると、 砂の上に家の基礎を築きます。
このシリーズの 2 番目の記事では、 AIエンジニアリングと高度なRAG、 Word2Vec による埋め込みの起源から、 BERT 革命から現代の Sentence Transformers まで。生成方法を見ていきます 埋め込み、ベクトル空間でテキストを比較する方法、エンジンを構築する方法 FAISS を使用したセマンティック検索と、ユースケースに適したモデルを選択する方法。
シリーズ概要
| # | アイテム | 集中 |
|---|---|---|
| 1 | RAGの説明 | 基礎と完全なアーキテクチャ |
| 2 | 現在位置 - 埋め込みとセマンティック検索 | テキストがベクトルになる仕組み |
| 3 | ベクトルデータベースの詳細 | ストレージ、インデックス作成、類似性検索 |
| 4 | LangChain と Python を使用した RAG | 実用的なエンドツーエンドの実装 |
| 5 | ハイブリッド検索と再ランキング | ハイブリッドキーワード + セマンティック検索 |
| 6 | コンテキストウィンドウとプロンプトエンジニアリング | LLM のコンテキストを最適化する |
| 7 | 本番環境の RAG | スケーリング、モニタリング、評価 |
| 8 | ナレッジグラフとRAG | ナレッジグラフ + 検索 |
| 9 | マルチエージェントシステム | 協調型 AI エージェント |
| 10 | RAGの未来 | トレンド、調査、次のステップ |
何を学ぶか
- エンベディングとは何ですか? 数値形式で意味をどのように表現しますか?
- Word2Vec から BERT、Sentence Transformers への進化
- バニラ BERT が類似性に対して機能しない理由と、SBERT が問題を解決する方法
- 数十のオプションから適切な埋め込みモデルを選択する方法
- Python で文変換と FAISS を使用したセマンティック検索を実装する
- ベクトル類似性メトリックとそれぞれをいつ使用するか
- 主要なベクトル検索エンジン間のアーキテクチャの比較
- 特定のドメインの埋め込みを微調整する方法
1. 埋め込みとは何ですか
Un 埋め込み それは離散オブジェクトを変換する数学関数です (単語、文章、文書、画像) を 1 つの実数ベクトルに変換 固定次元を持つ連続空間。基本的に、人間が読めるテキストを変換します。 意味上の関係を維持しながら、機械が理解できる数値のリストに変換する 原文の中では。
基本的な考え方は、同様の意味を持つテキストはベクトル内で近いベクトルを持つ必要があるということです。 空間、一方、異なる意味を持つテキストは遠いベクトルを持たなければなりません。この物件 それは呼ばれます 意味論的同型性: 意味関係の構造 単語間の要素はベクトル空間の幾何学的形状に保存されます。
1.1 ワンホットエンコーディングから高密度ベクトルへ
なぜ埋め込みが必要なのかを理解するために、最も単純な代替方法を考えてみましょう。 の ワンホットエンコーディング。 50,000語の語彙を持ち、あらゆる単語を これは、1 が 1 つだけ、残りがすべて 0 である 50,000 次元のベクトルで表されます。
ONE-HOT ENCODING (vocabolario di 50.000 parole):
"gatto" = [0, 0, ..., 1, ..., 0, 0] (50.000 dimensioni, un solo 1)
"cane" = [0, 0, ..., 0, ..., 1, 0] (50.000 dimensioni, un solo 1)
Distanza tra "gatto" e "cane" = stessa di "gatto" e "frigorifero"
Nessuna informazione semantica!
DENSE EMBEDDING (es. 384 dimensioni):
"gatto" = [0.23, -0.45, 0.89, ..., 0.12] (384 dimensioni, tutti numeri reali)
"cane" = [0.21, -0.42, 0.91, ..., 0.15] (384 dimensioni, tutti numeri reali)
Distanza tra "gatto" e "cane" = PICCOLA (animali domestici)
Distanza tra "gatto" e "frigorifero" = GRANDE (concetti diversi)
Il significato è catturato nella geometria!
ワンホット エンコーディングの問題は明らかです。ベクトルが巨大です (次元数 語彙に等しい)、散在(ほぼすべてゼロ)、そして何よりも、すべてが互いに直交しています。 意味に関係なく、2 つの単語は同じ距離にあります。ありません 「猫」と「猫」、または「猫」と「経済」を区別する方法。
I 密な埋め込み 3 つの問題をすべて解決します。ベクトルは次のとおりです。 コンパクト (数百次元)、高密度 (すべての値が重要)、 それらは、ジオメトリの意味論的な関係を捉えます。似たような単語にはベクトルが近く、 空間内の方向は言語概念に対応します。
1.2 意味空間
埋め込みの興味深い特性は、 意味関係はい 幾何学的な関係に変化する。典型的な例は算術です ベクトル: ベクトル「王」マイナス「男性」プラス「女性」は、非常に近いベクトルを生成します。 「女王」へ。これは魔法ではありません。空間が概念を捉えたということです。 一つの方向としては「ジェンダー」、もう一つの方向としては「王権」という概念。
Relazioni semantiche come operazioni vettoriali:
vec("re") - vec("uomo") + vec("donna") ~ vec("regina")
vec("Parigi") - vec("Francia") + vec("Italia") ~ vec("Roma")
vec("buono") - vec("migliore") + vec("grande") ~ vec("più grande")
Cluster nello spazio:
[gatto, cane, cavallo, pesce] --> vicini (animali)
[Python, Java, C++, Rust] --> vicini (linguaggi di programmazione)
[felice, contento, gioioso] --> vicinissimi (sinonimi)
基本的な直感
埋め込みとは本質的には コンプレッサーの意味。かかる 文章の意味を、あらゆるニュアンスを含めて、一点で理解する 多次元空間で。他のすべての点に対するその点の位置 ポイントは、モデルが学習したすべての意味関係をキャプチャします。これは、 意味論的研究全体の基礎となります。
2. 古典的な Word 埋め込み: Word2Vec、GloVe、FastText
エンベディングの現代史は 2013 年に始まります。 Word2古い、 Tomas Mikolov と Google の同僚によって出版されました。革命的なアイデアはシンプルでした。 単語が出現する文脈からその単語の意味を学ぶことができます。彼が言ったように 言語学者のジョン・ファースは1957年にこう述べています。「単語は、それがどのような関係にあるかによってわかるでしょう。」
2.1 Word2Vec: CBOW とスキップグラム
Word2Vec は、埋め込みを学習するための 2 つのニューラル アーキテクチャを提案しています。
- CBOW (連続バッグ・オブ・ワード): 文脈単語のウィンドウが与えられた場合、中心となる単語を予測します。例: 「__ が大声で鳴いている」場合、「猫」を予測します。
- スキップグラム: 中心となる単語が与えられた場合、文脈の単語を予測します。例: 「cat」が与えられた場合、「the」、「meow」、「loud」を予測します。
CBOW (Continuous Bag of Words):
Input: parole di contesto ["il", "___", "miagola", "forte"]
Output: parola target "gatto"
Contesto ──> [Embedding Layer] ──> Media vettori ──> [Softmax] ──> "gatto"
Veloce, buono per parole frequenti
SKIP-GRAM:
Input: parola target "gatto"
Output: parole di contesto ["il", "miagola", "forte"]
"gatto" ──> [Embedding Layer] ──> [Softmax] ──> parole contesto
Più lento, migliore per parole rare
Parametri tipici:
- Dimensioni embedding: 100-300
- Finestra di contesto: 5-10 parole
- Vocabolario: 100k-1M parole
- Training: miliardi di parole (Wikipedia, Common Crawl)
2.2 GloVe と FastText
グローブ (Global Vectors for Word Representation、スタンフォード 2014) 別のアプローチ: グローバル共起行列を構築し、それを次のように因数分解します。 埋め込みを取得します。 Word2Vec のウィンドウを使用して、グローバルな関係をキャプチャします。 ローカルなので負ける可能性があります。
ファストテキスト (Facebook 2016) のレベルで作業することで Word2Vec を拡張します。 サブワード (文字の n グラム)。 「埋め込み」という言葉を表します また、そのコンポーネント:「emb」、「mbe」、「bed」、「edd」などによっても生成されます。 トレーニング中に決して見られなかった単語の埋め込みも可能です (単語 語彙力不足)、 これは、イタリア語のような豊富な形態論を持つ言語にとって、重要な利点です。
従来の単語埋め込みの比較
| モデル | Anno | アプローチ | Forza | 限界 |
|---|---|---|---|---|
| Word2古い | 2013年 | ローカルコンテキスト予測 | 早くて効果的 | OOV なし、コンテキストなし |
| グローブ | 2014年 | グローバル共起 | 世界的な関係 | OOV なし、コンテキストなし |
| ファストテキスト | 2016年 | 文字の N グラム | OOVを管理する | 単語ごとに 1 つのベクトル |
2.3 基本的な制限: ワードごとに 1 つのベクトル
すべての古典的な単語の埋め込みには構造上の制限があります。 単語ごとに単一のベクトルコンテキストに関係なく。 「机」という言葉は、「学校の机」と「机」の両方に同じように埋め込まれています。 イタリアの」。単語の意味は依存するため、これは深刻な問題です ほとんどの場合、それが現れる文脈によって決まります。
さらに、これらのモデルは次のレベルで動作します。 一言: 違います 文または段落の埋め込みを生成できます。を表すには、 文では、ベクトルの平均化などの初歩的な戦略に頼る必要があります。 それを構成する単語が失われ、順序や構文構造に関する情報が失われます。
RAG に Word2Vec を使用しない理由
従来の単語の埋め込みは、次の理由から最新のセマンティック検索には不適切です。 (1) コンテキストをキャプチャしない、(2) 文レベルの埋め込みを生成しない、 (3) ベクトル平均では重要な情報が失われます。 「犬が人を噛む」というフレーズ 「男が犬を噛む」も同じ埋め込みになります。 RAG にはモデルが必要です 文脈の中で文全体の意味を理解できる人。
3. コンテキスト埋め込み: BERT 革命
2018 年に Google が公開した バート (双方向エンコーダ表現 トランスフォーマーより)そして風景を根本的に変えます。 BERT が生成する コンテキストに応じた埋め込み: 各単語の表現は依存します それが現れる文の文脈全体から。 「学校の机」の「机」という言葉 「il banca d'Italia」とは異なる埋め込みになります。
3.1 BERT の仕組み
BERT はアーキテクチャベースです トランスエンコーダ。入力を受け付けます 一連のトークンを作成し、トークンごとに 768 の文脈化されたベクトルを生成します。 ディメンション (BERT ベース) または 1024 ディメンション (BERT ラージ)。 BERT の威力は次のとおりです。 の仕組み 自意識: 各トークンは、 独自の計算を行うための、左と右の両方 (双方向) のシーケンス 表現。
Input: [CLS] il banco di scuola [SEP]
Tokenizzazione:
[CLS] il ban ##co di scuo ##la [SEP]
12 layer di Transformer Encoder:
Layer 1: Self-attention + Feed-forward
Layer 2: Self-attention + Feed-forward
...
Layer 12: Self-attention + Feed-forward
Output: un vettore 768-dim per OGNI token
[CLS] --> [0.23, -0.45, ..., 0.12] (vettore "riassunto")
il --> [0.11, -0.32, ..., 0.08]
ban --> [0.56, 0.12, ..., 0.34] (contestualizzato!)
##co --> [0.45, 0.09, ..., 0.29]
di --> [0.03, -0.21, ..., 0.05]
scuo --> [0.67, 0.33, ..., 0.41]
##la --> [0.59, 0.28, ..., 0.38]
[SEP] --> [0.02, -0.05, ..., 0.01]
3.2 プーリング戦略: トークンからフレーズへ
BERT は次のベクトルを生成します。 各トークンただし、セマンティック検索にはそれが必要です ある 文全体に対する単一のベクトル。入手方法は?いくつかあります の戦略 プーリング:
BERT のプーリング戦略
| 戦略 | 説明 | 品質 |
|---|---|---|
| [CLS]トークン | 特別なトークンキャリア [CLS] を使用する | 類似点としては平凡 |
| 平均プーリング | すべてのトークンベクトルの平均 | 全体的に良い |
| 最大プーリング | 各寸法の最大値 | 顕著な特徴を捉える |
| 加重平均 | アテンションウェイトによる加重平均 | 良い、もっと複雑 |
3.3 BERT Vanilla が類似性に対して機能しない理由
BERT の強力な機能にもかかわらず、BERT を直接使用して次の類似性を計算します。 文章は 非常に非効率的で、不十分な結果しか得られない。 理由は 2 つあります。
- 計算効率の悪さ: N 個の文を相互に比較するには、 すべての文のペアを BERT に渡す必要があります。 10,000文あれば必要になる 10,000 x 10,000 / 2 = 5,000 万回のフォワード パス。 1 回あたり約 65 ミリ秒の時間 カップルなら約65時間かかります
- 縮退した埋め込み空間: プーリングによって生成される埋め込み BERT ではコサイン類似度が最適化されていません。すべての文は次の傾向があります 狭い空間領域に収まり、文を区別することが困難になる 異なる文から類似したもの。この現象はと呼ばれます 異方性
クロスエンコーディングの問題
BERT は次のように設計されています クロスエンコーダ: 2 つの文を入力として受け取ります そして類似性スコアを生成します。文のペアごとに再計算する必要があります すべてをゼロから。 10,000 個のドキュメントとクエリのコーパスを使用するには、10,000 個のドキュメントが必要です。 フォワードパス。考えられるすべての比較のインデックスを構築するには、コストがかかります。 二次関数的に成長します。このため、BERT はリアルタイムの取得には実用的ではありません。
4. 文トランスフォーマー: 解決策
2019年、ニルス・ライマースとイリーナ・グレヴィチが出版した 文-BERT(SBERT)、 これにより、両方の問題がエレガントに解決されます。重要なアイデアは、BERT を クロスエンコーダ a バイエンコーダー: 文のペアを渡す代わりに、 各文は独立して密なベクトルにエンコードされます。類似性あり 次に、単純なベクトル演算 (コサイン類似度) を使用して計算します。
4.1 シャムアーキテクチャとトリプレットネットワーク
SBERT はアーキテクチャを使用してトレーニングされます。 シャムネットワーク: 2部 同一の BERT モデル (重みを共有) は 2 つの文を別々に処理します。運送業者 結果は、ベクトルに「近づく」損失関数と比較されます。 類似した文と、異なる文のベクトルを「削除」します。
CROSS-ENCODER (BERT vanilla):
["frase A", "frase B"] ──> [BERT] ──> punteggio similarità
Deve processare OGNI coppia. O(n^2) per n frasi.
BI-ENCODER (SBERT):
"frase A" ──> [BERT + Pooling] ──> vettore_A (384-dim)
"frase B" ──> [BERT + Pooling] ──> vettore_B (384-dim)
similarità = cosine(vettore_A, vettore_B)
Ogni frase viene codificata UNA VOLTA. O(n) per n frasi.
I vettori possono essere pre-calcolati e indicizzati!
TRAINING con Siamese Network:
Frase 1 ──> [BERT] ──> pool ──> u
├──> loss(u, v, label)
Frase 2 ──> [BERT] ──> pool ──> v
Label = 1 se frasi simili, 0 se diverse
Loss: Contrastive Loss o Cosine Similarity Loss
Le トリプレットネットワーク アンカー、 肯定的な (類似した) 例と否定的な (異なる) 例です。損失(トリプレットマージン損失) モデルのアンカー正の距離が距離よりも小さくなるようにプッシュします。 少なくとも事前に定義されたマージンだけアンカーネガティブです。
4.2 BERTと比較したSBERTの利点
性能比較
| メトリック | BERT クロスエンコーダー | SBERTバイエンコーダ |
|---|---|---|
| 類似10,000文 | ~65時間 | ~5秒 |
| ベクトルの事前計算 | 不可能 | はい、一度だけです |
| インデックス作成 | 適用できない | FAISS、HNSWなど |
| STS ベンチマークの品質 | ~87 (スピアマン) | ~85 (スピアマン) |
| 生産用途 | リランキングのみ | 検索+ランキング |
SBERT は精度をわずかに犠牲にします (STS ベンチマークで約 2 ポイント)。 でも1つ稼いでください 10,000倍の速度。実際の選択としては、 は必須です: クロスエンコーダーは SBERT の上位 k 個の結果の再ランカーとして使用できます。 両方のアプローチの長所を組み合わせた (パターンと呼ばれる) 取得して再ランク付けする).
5. 埋め込みモデルの比較
エンベディング モデルのエコシステムは広大で、急速に進化しています。の選択 適切なモデルは、セマンティック検索、クラスタリング、分類などのユースケースによって異なります。 多言語 RAG、予算、遅延。ここでは、最も関連性の高いモデルの詳細な比較を示します。
埋め込みモデルの比較 (2024-2025)
| モデル | プロバイダー | 寸法 | 最大トークン | 平均MTEB | 注意事項 |
|---|---|---|---|---|---|
| all-MiniLM-L6-v2 | スバート | 384 | 256 | 56.26 | 非常に高速でプロトタイピングに最適 |
| all-mpnet-base-v2 | スバート | 768 | 384 | 57.78 | オープンソースの最高の品質/速度比 |
| e5-ラージ-v2 | マイクロソフト | 1024 | 512 | 62.20 | 高品質、接頭辞「クエリ:」/「パッセージ:」が必要 |
| BGE-M3 | バーイ | 1024 | 8192 | 63.55 | 多言語、密+疎+コルベール |
| テキスト埋め込み-3-small | OpenAI | 1536年 | 8191 | 62.26 | クラウド API、安価 (0.02 ドル/100 万トークン) |
| テキスト埋め込み-3-大 | OpenAI | 3072 | 8191 | 64.59 | クラウド API、最高品質 (0.13 ドル/100 万トークン) |
| embed-v3 (英語) | コヒア | 1024 | 512 | 64.47 | 研究に最適なクラウド API |
| nomic-embed-text-v1.5 | ノミック | 768 | 8192 | 62.28 | オープンソース、長いコンテキスト、マトリョーシカ |
MTEB ベンチマークの見方
MTEB (Massive Text Embedding Benchmark) は、 埋め込みモデルを評価します。 8 つのカテゴリ (分類、 クラスタリング、ペア分類、再ランキング、検索、STS、要約)。スコア 中はすべてのカテゴリの平均ですが、RAG の場合はサブスコアです 検索 それが最も関連性があります。ユースケースの特定のスコアを常に確認してください。
5.1 選択基準
- ラピッドプロトタイピング: all-MiniLM-L6-v2 - 高速、軽量、無料
- オープンソースのプロダクション: all-mpnet-base-v2 または e5-large-v2
- 最高のクラウド品質: text-embedding-3-large (OpenAI) または embed-v3 (Cohere)
- 多言語/イタリア語: BGE-M3 または多言語 e5-large
- 長いコンテキスト (長いドキュメント): BGE-M3 (8192 トークン) または nomic-embed-text-v1.5
- 限られた予算: text-embedding-3-small (OpenAI) または自己ホスト型オープンソース テンプレート
6. ベクトル類似性メトリクス
ベクトルを取得したら、2 つのベクトルの大きさを測定する関数が必要です。 「似ている」。メトリックの選択は、結果とパフォーマンスの両方に影響します。
6.1 コサイン類似度
La 少し似ている 2 つのベクトル間の角度の余弦を測定します。 範囲は -1 (反対のベクトル) から +1 (同一のベクトル) までで、直交ベクトルの場合は 0 です。 ベクトルの大きさを無視し、方向のみに焦点を当てます。それが計量です。 テキストの埋め込みに最も使用されます。
COSINE SIMILARITY:
A . B sum(a_i * b_i)
cos(A,B) = ─────── = ──────────────────────────────
|A| |B| sqrt(sum(a_i^2)) * sqrt(sum(b_i^2))
Range: [-1, +1]
+1 = identici, 0 = ortogonali, -1 = opposti
Invariante alla scala (normalizzata)
DOT PRODUCT (Prodotto Scalare):
dot(A,B) = sum(a_i * b_i)
Range: (-inf, +inf)
Sensibile alla magnitudine dei vettori
Più veloce (nessuna normalizzazione)
Equivalente a cosine se i vettori sono già normalizzati (norma L2 = 1)
EUCLIDEAN DISTANCE (L2):
d(A,B) = sqrt(sum((a_i - b_i)^2))
Range: [0, +inf)
0 = identici, valori alti = molto diversi
Sensibile alla magnitudine
Nota: DISTANZA, non similarità (valori bassi = più simili)
いつどのメトリックを使用するか
| メトリック | いつ使用するか | パフォーマンス |
|---|---|---|
| コサイン類似度 | テキスト埋め込み、正規化されていないテンプレートのデフォルト | 平均 (正規化が必要) |
| 内積 | 速度が必要な場合は、すでに正規化された埋め込み (センテンス トランスフォーマー) | 高(操作が容易) |
| ユークリッド(L2) | 規模が大きい場合、クラスタリング | 平均 |
便利なヒント: Sentence Transformers を使用すると、ベクトルを正規化できます。 統一基準に従って。この場合、コサイン類似度とドット積は等価であり、 ドット積の方が速いです。ほとんどのベクトル データベースは 3 つすべてをサポートしています メトリクスを作成し、インデックスの作成時に選択できます。
7. Pythonによる実践的な実装
コードに進みましょう。を使用して完全なセマンティック検索システムを実装します。
sentence-transformers 埋め込み用 e FAISS のために
インデックス作成とベクトル検索。
7.1 セットアップとインストール
# Crea ambiente virtuale
python -m venv embedding-env
source embedding-env/bin/activate
# Installa le dipendenze principali
pip install sentence-transformers==3.3.1
pip install faiss-cpu==1.9.0 # Per CPU (usa faiss-gpu per GPU)
pip install numpy==2.1.3
pip install pandas==2.2.3
pip install tqdm==4.67.1
# Opzionale: per visualizzazione
pip install matplotlib==3.9.3
pip install scikit-learn==1.6.0
7.2 文トランスフォーマーを使用したエンベディングの生成
from sentence_transformers import SentenceTransformer
import numpy as np
# Carica il modello (download automatico al primo utilizzo)
model = SentenceTransformer("all-MiniLM-L6-v2")
# Frasi di esempio
sentences = [
"Il gatto dorme sul divano",
"Il felino riposa sul sofa",
"Python è un linguaggio di programmazione",
"Java è usato per lo sviluppo enterprise",
"La ricerca semantica usa gli embeddings",
]
# Genera embeddings
embeddings = model.encode(sentences, show_progress_bar=True)
# Ogni embedding è un vettore numpy
print(f"Tipo: {type(embeddings)}")
print(f"Shape: {embeddings.shape}") # (5, 384) - 5 frasi, 384 dimensioni
print(f"Norma L2 del primo vettore: {np.linalg.norm(embeddings[0]):.4f}")
# Visualizza i primi 10 valori del primo embedding
print(f"\nPrimi 10 valori: {embeddings[0][:10]}")
7.3 フレーズ間の類似性の計算
from sentence_transformers import SentenceTransformer, util
import numpy as np
model = SentenceTransformer("all-MiniLM-L6-v2")
# Frasi da confrontare
sentences = [
"Il gatto dorme sul divano",
"Il felino riposa sul sofa",
"Python è un linguaggio di programmazione",
"La pioggia cade sulla citta",
]
embeddings = model.encode(sentences, convert_to_tensor=True)
# Matrice di similarità completa
cosine_scores = util.cos_sim(embeddings, embeddings)
print("Matrice di Similarità Coseno:")
print("-" * 60)
for i in range(len(sentences)):
for j in range(len(sentences)):
score = cosine_scores[i][j].item()
if i != j:
print(f" {sentences[i][:30]:<30} vs {sentences[j][:30]:<30}")
print(f" Similarità: {score:.4f}")
# Calcolo manuale della cosine similarity per verifica
def cosine_similarity_manual(vec_a, vec_b):
"""Calcola cosine similarity manualmente."""
dot_product = np.dot(vec_a, vec_b)
norm_a = np.linalg.norm(vec_a)
norm_b = np.linalg.norm(vec_b)
return dot_product / (norm_a * norm_b)
emb_np = model.encode(sentences)
manual_score = cosine_similarity_manual(emb_np[0], emb_np[1])
print(f"\nVerifica manuale (frase 0 vs 1): {manual_score:.4f}")
期待される出力
「猫はソファで寝ています」と「猫はソファで休んでいます」という文には、 高い類似性 (~0.85)。同じ概念を異なる単語で表現しているためです。 「Python はプログラミング言語です」との類似性は低くなります (~0.10)。 コンセプトが全く違うから。これが研究の力です セマンティクスとキーワード検索。
7.4 FAISS によるセマンティック検索
import faiss
import numpy as np
from sentence_transformers import SentenceTransformer
class SemanticSearchEngine:
"""Motore di ricerca semantica basato su FAISS."""
def __init__(self, model_name: str = "all-MiniLM-L6-v2"):
self.model = SentenceTransformer(model_name)
self.dimension = self.model.get_sentence_embedding_dimension()
self.index = None
self.documents = []
def build_index(self, documents: list[str], use_gpu: bool = False):
"""Costruisce l'indice FAISS dai documenti."""
self.documents = documents
# Genera embeddings per tutti i documenti
embeddings = self.model.encode(
documents,
show_progress_bar=True,
normalize_embeddings=True, # Normalizza per cosine similarity
batch_size=64,
)
embeddings = embeddings.astype("float32")
# Crea indice FAISS (Inner Product = Cosine per vettori normalizzati)
self.index = faiss.IndexFlatIP(self.dimension)
if use_gpu:
# Trasferisci indice su GPU per ricerca più veloce
res = faiss.StandardGpuResources()
self.index = faiss.index_cpu_to_gpu(res, 0, self.index)
self.index.add(embeddings)
print(f"Indice costruito: {self.index.ntotal} documenti indicizzati")
def search(self, query: str, top_k: int = 5) -> list[dict]:
"""Cerca i documenti più simili alla query."""
# Genera embedding della query
query_embedding = self.model.encode(
[query],
normalize_embeddings=True,
).astype("float32")
# Ricerca nell'indice FAISS
scores, indices = self.index.search(query_embedding, top_k)
results = []
for score, idx in zip(scores[0], indices[0]):
if idx != -1: # -1 indica nessun risultato
results.append({
"document": self.documents[idx],
"score": float(score),
"index": int(idx),
})
return results
def save_index(self, path: str):
"""Salva l'indice su disco."""
faiss.write_index(self.index, path)
def load_index(self, path: str):
"""Carica l'indice da disco."""
self.index = faiss.read_index(path)
# Utilizzo
if __name__ == "__main__":
# Corpus di documenti
documents = [
"Python è un linguaggio di programmazione ad alto livello, interpretato e multiparadigma",
"Java è un linguaggio orientato agli oggetti progettato per essere portabile",
"Il machine learning è un sottoinsieme dell'intelligenza artificiale",
"I database relazionali usano SQL per le query sui dati strutturati",
"Docker permette di containerizzare le applicazioni per il deployment",
"Kubernetes orchestra i container in ambienti di produzione distribuiti",
"React è una libreria JavaScript per costruire interfacce utente",
"Angular è un framework TypeScript per applicazioni web enterprise",
"I transformer sono l'architettura alla base dei modelli linguistici moderni",
"FAISS è una libreria per la ricerca efficiente di similarità tra vettori",
"La sicurezza informatica protegge i sistemi da accessi non autorizzati",
"Il cloud computing fornisce risorse computazionali on-demand via internet",
]
engine = SemanticSearchEngine()
engine.build_index(documents)
# Ricerca semantica
queries = [
"Come funziona l'intelligenza artificiale?",
"Framework per sviluppo web frontend",
"Come distribuire applicazioni in modo scalabile?",
]
for query in queries:
print(f"\nQuery: '{query}'")
print("-" * 50)
results = engine.search(query, top_k=3)
for i, result in enumerate(results, 1):
print(f" {i}. [{result['score']:.4f}] {result['document']}")
7.5 大規模なデータセットのバッチ処理
import faiss
import numpy as np
from sentence_transformers import SentenceTransformer
from tqdm import tqdm
import json
from pathlib import Path
class LargeScaleEmbedder:
"""Gestisce embedding e indicizzazione per grandi dataset."""
def __init__(self, model_name: str = "all-mpnet-base-v2"):
self.model = SentenceTransformer(model_name)
self.dimension = self.model.get_sentence_embedding_dimension()
def encode_in_batches(
self,
texts: list[str],
batch_size: int = 256,
output_path: str | None = None,
) -> np.ndarray:
"""Genera embeddings in batch con salvataggio incrementale."""
all_embeddings = []
for start_idx in tqdm(range(0, len(texts), batch_size)):
batch = texts[start_idx : start_idx + batch_size]
batch_embeddings = self.model.encode(
batch,
normalize_embeddings=True,
batch_size=batch_size,
show_progress_bar=False,
)
all_embeddings.append(batch_embeddings)
# Salvataggio incrementale (checkpoint)
if output_path and (start_idx % (batch_size * 10) == 0):
partial = np.vstack(all_embeddings)
np.save(f"{output_path}_partial.npy", partial)
result = np.vstack(all_embeddings).astype("float32")
if output_path:
np.save(f"{output_path}.npy", result)
print(f"Embeddings salvati: {result.shape}")
return result
def build_ivf_index(
self,
embeddings: np.ndarray,
n_clusters: int = 100,
n_probe: int = 10,
) -> faiss.Index:
"""Costruisce un indice IVF per ricerca approssimata veloce."""
# Quantizzatore per assegnare vettori ai cluster
quantizer = faiss.IndexFlatIP(self.dimension)
# Indice IVF con Inner Product
index = faiss.IndexIVFFlat(
quantizer, self.dimension, n_clusters, faiss.METRIC_INNER_PRODUCT
)
# Training: impara la struttura dei cluster
print(f"Training indice IVF con {n_clusters} cluster...")
index.train(embeddings)
# Aggiungi i vettori
index.add(embeddings)
index.nprobe = n_probe # Numero di cluster da esplorare in ricerca
print(f"Indice IVF costruito: {index.ntotal} vettori, {n_clusters} cluster")
return index
# Esempio di utilizzo con un grande corpus
if __name__ == "__main__":
embedder = LargeScaleEmbedder(model_name="all-MiniLM-L6-v2")
# Simula un grande corpus (in produzione: carica da file/database)
corpus = [f"Documento di esempio numero {i} sul tema tecnologia" for i in range(100_000)]
# Genera embeddings in batch
embeddings = embedder.encode_in_batches(
corpus,
batch_size=512,
output_path="corpus_embeddings",
)
# Costruisci indice IVF (più veloce di Flat per grandi corpus)
index = embedder.build_ivf_index(embeddings, n_clusters=256, n_probe=16)
# Ricerca
query_embedding = embedder.model.encode(
["intelligenza artificiale e machine learning"],
normalize_embeddings=True,
).astype("float32")
scores, indices = index.search(query_embedding, 5)
for score, idx in zip(scores[0], indices[0]):
print(f" [{score:.4f}] {corpus[idx]}")
7.6 E5 テンプレートによる埋め込み (プレフィックスベース)
from sentence_transformers import SentenceTransformer
import numpy as np
# I modelli E5 richiedono prefix specifici
model = SentenceTransformer("intfloat/e5-large-v2")
# Per i documenti nel corpus: prefix "passage: "
documents = [
"passage: Python è un linguaggio di programmazione versatile e potente",
"passage: I database NoSQL sono progettati per dati non strutturati",
"passage: Kubernetes gestisce il deployment di applicazioni containerizzate",
"passage: Il deep learning usa reti neurali con molti layer",
]
# Per le query di ricerca: prefix "query: "
queries = [
"query: Come funziona l'apprendimento automatico?",
"query: Strumenti per gestire i container",
]
# Genera embeddings
doc_embeddings = model.encode(documents, normalize_embeddings=True)
query_embeddings = model.encode(queries, normalize_embeddings=True)
# Calcola similarità
for i, query in enumerate(queries):
scores = np.dot(query_embeddings[i], doc_embeddings.T)
best_idx = np.argmax(scores)
print(f"Query: {query}")
print(f" Miglior match: {documents[best_idx]}")
print(f" Score: {scores[best_idx]:.4f}\n")
接頭辞に注意する
E5 および BGE モデルでは、クエリとドキュメントを区別するために特定のプレフィックスが必要です。
E5の場合: query: e passage: 。 BGE の場合:
Represent this sentence: クエリ用。プレフィックスを忘れると劣化します
パフォーマンスが大幅に向上します。必ずモデルのドキュメントを確認してください。
8. ベクトル検索エンジン: アーキテクチャの比較
FAISS は低レベルの実装には優れていますが、運用環境では必要になります。 永続性、メタデータ フィルタリング、水平スケーラビリティを管理するソリューション そしてリアルタイムの更新。主なベクターデータベースとその特徴を以下に示します。
ベクターデータベースの比較
| データベース | タイプ | 言語 | インデックス | 理想的な使用例 |
|---|---|---|---|---|
| フェイス | 本棚 | C++/Python | フラット、IVF、HNSW、PQ | 純粋な研究、組み込み、プロトタイピング |
| クドラント | データベース | さび | ニューサウスウェールズ州 | プロダクション、複雑なフィルター、高性能 |
| 松ぼっくり | クラウド管理 | 所有者 | 所有者 | ゼロオペレーション、起動、自動スケーリング |
| ミルバス | データベース | Go/C++ | フラット、体外受精、HNSW、DiskANN | エンタープライズ規模、数十億のベクトル |
| ベクター | 拡大 | C | IVFフラット、ニューサウスウェールズ州 | すでに PostgreSQL を使用している、中程度のデータセット |
| クロマDB | データベース | Python/Rust | ニューサウスウェールズ州 | プロトタイピング、LangChain 統合 |
| ウィアビエイト | データベース | Go | ニューサウスウェールズ州 | マルチモーダル、GraphQL API |
8.1 Qdrant を使用した例
from qdrant_client import QdrantClient
from qdrant_client.models import (
Distance,
VectorParams,
PointStruct,
Filter,
FieldCondition,
MatchValue,
)
from sentence_transformers import SentenceTransformer
# Inizializza client (in-memory per test, o URL per produzione)
client = QdrantClient(":memory:") # oppure QdrantClient("http://localhost:6333")
model = SentenceTransformer("all-MiniLM-L6-v2")
# Crea collection
client.create_collection(
collection_name="articles",
vectors_config=VectorParams(
size=384, # Dimensione vettore all-MiniLM-L6-v2
distance=Distance.COSINE,
),
)
# Documenti con metadati
documents = [
{"text": "Python è ottimo per il data science", "category": "programming", "year": 2024},
{"text": "React è il framework frontend più popolare", "category": "web", "year": 2024},
{"text": "Kubernetes gestisce i container in produzione", "category": "devops", "year": 2023},
{"text": "TensorFlow è usato per il deep learning", "category": "ai", "year": 2024},
{"text": "PostgreSQL è un database relazionale avanzato", "category": "database", "year": 2023},
]
# Indicizzazione
points = []
for i, doc in enumerate(documents):
embedding = model.encode(doc["text"]).tolist()
points.append(
PointStruct(
id=i,
vector=embedding,
payload={"text": doc["text"], "category": doc["category"], "year": doc["year"]},
)
)
client.upsert(collection_name="articles", points=points)
# Ricerca semantica semplice
query = "machine learning e intelligenza artificiale"
query_vector = model.encode(query).tolist()
results = client.search(
collection_name="articles",
query_vector=query_vector,
limit=3,
)
print(f"Query: '{query}'")
for result in results:
print(f" [{result.score:.4f}] {result.payload['text']} ({result.payload['category']})")
# Ricerca con filtro per metadati
filtered_results = client.search(
collection_name="articles",
query_vector=query_vector,
query_filter=Filter(
must=[FieldCondition(key="year", match=MatchValue(value=2024))]
),
limit=3,
)
print(f"\nRicerca filtrata (solo 2024):")
for result in filtered_results:
print(f" [{result.score:.4f}] {result.payload['text']}")
9. インデックス作成アルゴリズム: 速度と呼び出しのトレードオフ
数百万のベクトルにわたる正確な(総当たり)検索は遅すぎます。アルゴリズム の 近似最近隣 (ANN) 彼らはわずかな割合を犠牲にします 精度が向上し、桁違いの速度が得られます。これらのアルゴリズムを理解する ベクトル データベースを正しく構成することが重要です。
9.1 フラットインデックス (完全一致検索)
インデックス フラット クエリをデータベース内のすべての単一ベクトルと比較します。 100% の再現率が保証されますが、コストは O(n*d) です。ここで、n はキャリアの数、d はキャリアの数です。 次元性。小規模なデータセット (最大 100k ベクトル) の場合にのみ実用的です。
9.2 IVF (逆ファイルインデックス)
体外受精 K 平均法を使用してベクトル空間をクラスターに分割します。現時点では
検索では、クエリに最も近いクラスターのみが調査されます。パラメータ
nprobe 探索するクラスターの数を制御します: 値を高くすると再現率が増加します
しかし、検索が遅くなります。
9.3 HNSW (階層型ナビゲート可能なスモールワールド)
ニューサウスウェールズ州 これは、最新のベクトル データベースで最も人気のあるアルゴリズムです。それは構築します 各ノードが隣接ノードに接続されたベクトルであるマルチレベルのナビゲート可能なグラフ 一番近い。検索は最上位レベルから開始されます (少数のノード、長い接続) そして基本レベル (すべてのノード、短い接続) に下がり、範囲が狭くなります。 徐々に研究領域を広げていきます。
FLAT (Brute Force):
Costruzione: O(n) | Ricerca: O(n*d) | Recall: 100%
Memoria: n*d*4 bytes | Aggiornamento: O(1)
Ideale per: <100k vettori, quando il recall perfetto e necessario
IVF (Inverted File):
Costruzione: O(n*k) | Ricerca: O(nprobe*n/k*d) | Recall: 90-99%
Memoria: n*d*4 bytes | Aggiornamento: richiede re-training
Parametri: nlist (cluster), nprobe (cluster esplorati)
Ideale per: 100k-10M vettori, quando serve controllo fine
HNSW (Hierarchical Navigable Small World):
Costruzione: O(n*log(n)) | Ricerca: O(log(n)*d) | Recall: 95-99.9%
Memoria: n*d*4 + grafo | Aggiornamento: O(log(n))
Parametri: M (connessioni), ef_construction, ef_search
Ideale per: qualsiasi scala, migliore latenza, più memoria
Product Quantization (PQ):
Comprime vettori 768-dim in ~64 bytes (12x compressione)
Recall inferiore ma enorme risparmio memoria
Combinabile con IVF: IVF-PQ per miliardi di vettori
選択のための実践的なガイド
| スケールデータセット | 推奨アルゴリズム | 典型的なリコール |
|---|---|---|
| <100,000 キャリア | フラット (総当たり) | 100% |
| 100k - 1M | ニューサウスウェールズ州 | 98-99% |
| 1M~100M | HNSW または IVF-HNSW | 95-99% |
| 100M~1B | IVF-PQ または DiskANN | 90-95% |
| >1B | 分散型 IVF-PQ (Milvus) | 85-95% |
10. 埋め込みの微調整
事前トレーニングされたモデルは一般的なユースケースではうまく機能しますが、ドメインでは機能しません 専門家(医学、法律、金融、コード) 微調整 検索の品質を大幅に向上させることができます。原理は簡単です。 モデルをドメインのデータと語彙に適応させます。
10.1 いつ微調整するか
- 専門用語: ドメインで一般的なトレーニング データにはない技術用語が使用されています
- さまざまな意味上の関係: あなたのドメインでは、通常は無関係な単語が密接な関係を持っています
- 過小評価されている言語: 基本モデルでは、あなたの言語で十分なテキストが表示されませんでした
- 取得が不十分: チャンクは良好であるにもかかわらず、セマンティック検索の結果が満足のいくものではない
10.2 対照学習による実装
from sentence_transformers import (
SentenceTransformer,
InputExample,
losses,
evaluation,
)
from torch.utils.data import DataLoader
# Carica modello base
model = SentenceTransformer("all-MiniLM-L6-v2")
# Prepara i dati di training: coppie (frase_a, frase_b, similarità)
# similarità: 1.0 = semanticamente identiche, 0.0 = completamente diverse
train_examples = [
# Coppie positive (dominio medico)
InputExample(texts=["infarto del miocardio", "attacco cardiaco acuto"], label=0.95),
InputExample(texts=["ipertensione arteriosa", "pressione alta cronica"], label=0.90),
InputExample(texts=["cefalea tensiva", "mal di testa da stress"], label=0.85),
InputExample(texts=["diabete mellito tipo 2", "diabete dell'adulto"], label=0.90),
# Coppie negative
InputExample(texts=["infarto del miocardio", "frattura del femore"], label=0.10),
InputExample(texts=["ipertensione arteriosa", "gastrite cronica"], label=0.05),
# Aggiungi migliaia di coppie per risultati ottimali...
]
# DataLoader
train_dataloader = DataLoader(train_examples, shuffle=True, batch_size=16)
# Loss function: Cosine Similarity Loss
train_loss = losses.CosineSimilarityLoss(model)
# Valutazione (opzionale ma consigliata)
eval_examples = [
InputExample(texts=["tachicardia sinusale", "battito cardiaco accelerato"], label=0.85),
InputExample(texts=["polmonite batterica", "infezione polmonare"], label=0.80),
]
evaluator = evaluation.EmbeddingSimilarityEvaluator.from_input_examples(
eval_examples, name="medical-eval"
)
# Fine-tuning
model.fit(
train_objectives=[(train_dataloader, train_loss)],
evaluator=evaluator,
epochs=3,
warmup_steps=100,
evaluation_steps=500,
output_path="models/medical-embedding-model",
)
print("Fine-tuning completato! Modello salvato.")
# Test del modello fine-tuned
finetuned_model = SentenceTransformer("models/medical-embedding-model")
emb1 = finetuned_model.encode("infarto miocardico acuto")
emb2 = finetuned_model.encode("attacco di cuore")
from sentence_transformers import util
score = util.cos_sim([emb1], [emb2])
print(f"Similarità post fine-tuning: {score.item():.4f}")
微調整にはどのくらいのデータが必要ですか?
原則として: 最低1,000ペア 改善が見られるようにするには、 5,000~10,000足 確かな結果を得るために、 50,000 ペア以上 最適な結果を得るために。データの量よりも質が重要: ペア ノイズが多い、またはラベルが不十分であると、モデルの品質が低下します。十分な注釈付きデータがない場合は、 のようなテクニックを検討してください ハードネガティブマイニング または合成生成 LLMを使用して。
11. イタリア語の埋め込み
ほとんどの埋め込みモデルは、主に英語のテキストでトレーニングされます。 イタリア語でのアプリケーションの場合、モデルの選択は特に重要です。見てみましょう 利用可能なオプションとそのパフォーマンス。
11.1 多言語モデル
多言語モデルは数十の言語で同時にトレーニングされます。品質 イタリア語の場合は一般的に良好ですが、英語のパフォーマンスよりは低くなります。 イタリア語に推奨されるモデルは次のとおりです。
- 多言語-e5-large: 優れた多言語品質。接頭辞「query:」/「passage:」が必要です。
- BGE-M3: 均一な品質で 100 以上の言語をサポート、密+疎+コルベールをサポート
- 言い換え-多言語-MiniLM-L12-v2: 50 以上の言語で速度と品質を両立
- text-embedding-3-small/large (OpenAI): API を介した優れた多言語パフォーマンス
11.2 品質向上のための戦略
- イタリアのデータを微調整する: イタリア語で注釈を付けた数千ペアでもパフォーマンスを大幅に向上させることができます
- データの増強: LLM を使用して文書のイタリア語言い換えを生成する
- ハイブリッド検索: セマンティック検索と BM25 (キーワード) を組み合わせて、言語に関するモデルの弱点を補います。
- クエリの翻訳: 高品質の英語モデルの場合は、埋め込む前にクエリを英語に翻訳します (実用的な回避策)
イタリア語の模擬試験
イタリア語プロジェクトのテンプレートを選択する前に、必ずテストを実行してください。 クエリと実際のドキュメントを含むマニュアル。小規模な評価データセットを作成する (期待される応答を含む 50 ~ 100 個のクエリ) と、precision@k と remember@k を測定します。モデル 英語のMTEBスコアが高いと、成績が大幅に悪くなる可能性があります イタリア語について。 MTEB ベンチマークには多言語サブタスクが含まれています - チェック 特にそれら。
12. RAG コンテキストでの埋め込み
埋め込みとは、 財団 各 RAG システムの。の品質 取得、つまりパイプライン全体の取得は、次の品質に直接依存します。 ベクトル表現。これが、埋め込みがアーキテクチャに統合される方法です ラグ完成。
FASE DI INDICIZZAZIONE (offline):
Documenti ──> Chunking ──> [EMBEDDING MODEL] ──> Vettori ──> Vector DB
|
Stesso modello per query!
FASE DI QUERY (online):
Query utente ──> [EMBEDDING MODEL] ──> Vettore query
|
v
[VECTOR DB: similarity search]
|
v
Top-k chunk rilevanti
|
v
[LLM + Contesto] ──> Risposta con citazioni
REGOLE D'ORO:
1. Usare SEMPRE lo stesso modello per indicizzazione e query
2. La qualità degli embeddings determina la qualità del retrieval
3. Scegliere il modello in base al dominio e alla lingua
4. Normalizzare i vettori per cosine similarity
5. Monitorare retrieval precision e recall in produzione
RAG システムで最もよくある間違いは、選択の重要性を過小評価することです。 埋め込みモデル。 LLM プロンプトの最適化に数週間を費やすチーム 彼らは、さまざまな埋め込みモデルをテストしたことがないことがよくあります。実際にやってみると、 all-MiniLM-L6-v2 を e5-large-v2 や BGE-M3 などのモデルに追加すると、 検索率は 15 ~ 25% であり、最終的な回答の品質に直接影響します。
RAG のチェックリスト埋め込み
- データセットで少なくとも 3 つの異なるモデルをテストしましたか?
- クエリと期待される応答を含む評価セットはありますか?
- モデルはあなたの言語を十分な品質でサポートしていますか?
- 次元はストレージの予算と互換性がありますか?
- コンテキストの最大長 (最大トークン) はチャンクにとって適切ですか?
- ドメインの微調整を検討したことがありますか?
- 本番環境での取得の品質を監視していますか?
13. 生産におけるコストと拡張性
セルフホスト型の埋め込みとクラウド API のどちらを選択するかは、コストに大きな影響を与えます。 レイテンシと運用の複雑さについて。取るべき重要な要素を分析しましょう 情報に基づいた決定。
埋め込み API のコスト比較 (2024 ~ 2025 年)
| プロバイダー | モデル | 100万トークンの価格 | 寸法 |
|---|---|---|---|
| OpenAI | テキスト埋め込み-3-small | $0.02 | 1536年 |
| OpenAI | テキスト埋め込み-3-大 | $0.13 | 3072 |
| コヒア | 埋め込みv3 | $0.10 | 1024 |
| 自己ホスト型 | all-MiniLM-L6-v2 | GPUコスト | 384 |
| 自己ホスト型 | BGE-M3 | GPUコスト | 1024 |
13.1 セルフホスト型とクラウド API
- クラウド API (OpenAI、Cohere): インフラストラクチャゼロ、従量課金制、ネットワーク遅延、外部サービスへの依存、データプライバシーは評価対象
- セルフホスト型 (専用 GPU): 高い初期コスト (GPU 〜 1 ~ 3 ドル/時間)、トークンあたりのコストは無料、遅延は最小限、完全なデータ制御、運用の複雑さ
経験則: 1,000 万トークン/月未満の場合、クラウド API そちらの方が安いです。 1 億トークン/月を超えると、セルフホスティングは次のようになります。 便利な。 10M ~ 100M の間は、必要な遅延とプライバシーのニーズによって異なります。
13.2 ベクターストレージのコスト
各キャリアが占有 dimensioni * 4 bytes (float32)。 768モデルの場合
サイズおよび 100 万ドキュメント: 768 * 4 * 1M = ~3 GB ベクトル メモリのみ、
どのメタデータ、インデックス、オーバーヘッドを追加する必要があるか。積量子化を使用すると、次のことが可能になります
最大 8 ~ 16 倍まで圧縮しますが、リコールは失われます。
簡単なコスト見積もり
100 万のドキュメントを含む RAG システムの場合、チャンクあたり 500 トークンでチャンク化すると、 768 次元モデル: ベクトル用に約 3 GB の RAM + HNSW インデックス用に 2 ~ 4 GB の RAM が必要です。 8 GB の RAM (月額約 50 ~ 100 ドル) を備えたクラウド インスタンスで十分です。世代にとって OpenAI API による初期埋め込み (text-embedding-3-small): 約 5 億トークン = 10ドル。セルフホスト モデルと T4 GPU の場合: 約 2 ~ 3 時間のコンピューティング = 3 ~ 5 ドル。
結論と次のステップ
この記事では、テキスト埋め込みの進化全体について説明しました。 Word2Vec および GloVe の静的単語埋め込み、BERT のコンテキスト埋め込みによる、 セマンティック検索用に最適化された最新の Sentence Transformers まで。私たちは持っています バニラ BERT が検索に適さない理由と、SBERT がその問題をどのように解決するかを見ていきました。 バイエンコーダーアーキテクチャを採用。
Python を使用した完全なセマンティック検索エンジン、文変換を実装しました。 FAISS は、バッチ処理と大規模なデータセットの戦略を模索しています。比べてみた 主要なベクトルデータベースとインデックスアルゴリズム、トレードオフの分析 スピード、リコール、記憶の間。
覚えておくべき重要なポイント:
- 埋め込みモデルの選択は最も重要な決定です RAG システムでは - LLM モデルより重要
- センテンストランスフォーマー(バイエンコーダー) それらは検索の標準です。再ランキング用の BERT クロスエンコーダー
- より多くのモデルをテストする 選択する前に実際のデータセットで
- FAISS と HNSW 数百万のベクトルにわたる検索を効率的かつ実行可能にします
- 微調整 特殊なドメインの品質を劇的に向上させることができます
- イタリア語の場合、 多言語モデル (BGE-M3、multilingual-e5) を使用し、微調整を検討する
次の記事で
シリーズの 3 回目の記事では、 ベクターデータベース: 内部アーキテクチャ、高度なインデックス構成、シャーディング戦略 レプリケーション、メタデータのフィルタリング、および適切なデータベースの選択方法 あなたのユースケース。 Qdrant、ChromaDB、pgvector を使用した完全な実装を見ていきます。







