埋め込みモデルは意味的な類似性を測定するために作られていますが、その測定値には多くのバイアス要因が影響を与えます。この記事では、テキスト埋め込みモデルにおける普遍的なバイアスの1つである入力サイズについて見ていきます。
より長いテキストの埋め込みは、実際のコンテンツの類似性に関係なく、他のテキスト埋め込みと比較した場合、一般的により高い類似度スコアを示します。確かに、本当に似ているテキスト同士は関連のないものより高い類似度スコアを示しますが、長いテキストはバイアスを導入し、単にその長さのために埋め込みが平均的により類似しているように見えてしまいます。
これには実際の影響があります。埋め込みモデルだけでは関連性を適切に測定できないということです。埋め込みベースの検索では、常に最良の一致が存在しますが、サイズバイアスにより、最良の一致や他の一致が実際に関連しているかどうかを類似度スコアで判断することはできません。例えば、コサイン類似度が 0.75 以上の一致は関連性があるとは言えません。なぜなら、完全に無関係であっても、長い文書がそのレベルで一致する可能性があるからです。
いくつかの簡単な例を使ってこれを実証し、テキスト埋め込み間のコサイン類似度が一般的な評価方法として機能しないことを示します
tagサイズバイアスの可視化
サイズバイアスがどのように現れるかを示すために、Jina AI の最新の埋め込みモデル jina-embeddings-v3 を text-matching
タスクオプションで使用します。また、広く使用されている IR データセット:CISI データセットのテキスト文書を使用します。これはKaggle からダウンロードできます。

このデータセットは IR システムのトレーニング用で、クエリとそれに一致する文書の両方が含まれています。今回は CISI.ALL
ファイルに含まれる文書のみを使用します。GitHub の代替ソースからコマンドラインで以下のようにダウンロードできます:
wget https://raw.githubusercontent.com/GianRomani/CISI-project-MLOps/refs/heads/main/CISI.ALL
CISI には 1,460 の文書が含まれています。テキストのサイズとその分布に関する基本的な統計は、以下の表とヒストグラムにまとめられています:
in Words | in Sentences | |
---|---|---|
Average document size | 119.2 | 4.34 |
Std. Deviation | 63.3 | 2.7 |
Max size | 550 | 38 |
Min size | 8 | 1 |


Python で文書を読み込み、それらの埋め込みを取得しましょう。以下のコードは CISI.ALL
ファイルがローカルディレクトリにあることを前提としています:
with open("CISI.ALL", "r", encoding="utf-8") as inp:
cisi_raw = inp.readlines()
docs = []
current_doc = ""
in_text = False
for line in cisi_raw:
if line.startswith("."):
in_text = False
if current_doc:
docs.append(current_doc.strip())
current_doc = ""
if line.startswith(".W"):
in_text = True
else:
if in_text:
current_doc += line
これにより docs
リストに 1,460 の文書が格納されます。以下のように内容を確認できます:
print(docs[0])
The present study is a history of the DEWEY Decimal
Classification. The first edition of the DDC was published
in 1876, the eighteenth edition in 1971, and future editions
will continue to appear as needed. In spite of the DDC's
long and healthy life, however, its full story has never
been told. There have been biographies of Dewey
that briefly describe his system, but this is the first
attempt to provide a detailed history of the work that
more than any other has spurred the growth of
librarianship in this country and abroad.
次に、jina-embeddings-v3 を使用して各テキストの埋め込みを構築します。これには Jina AI のウェブサイトから API キーが必要です。100 万トークンまでの埋め込みが無料で利用できるキーを取得でき、この記事の目的には十分です。
キーを変数に設定します:
api_key = "<Your Key>"
次に、jina-embeddings-v3 の text-matching
タスクを使用して埋め込みを生成します。このコードは docs
内のテキストを 10 件ずつバッチ処理します。
import requests
import json
from numpy import array
embeddings = []
url = "https://api.jina.ai/v1/embeddings"
headers = {
"Content-Type": "application/json",
"Authorization": "Bearer " + api_key
}
i = 0
while i < len(docs):
print(f"Got {len(embeddings)}...")
data = {
"model": "jina-embeddings-v3",
"task": "text-matching",
"late_chunking": False,
"dimensions": 1024,
"embedding_type": "float",
"input": docs[i:i+10]
}
response = requests.post(url, headers=headers, data=json.dumps(data))
for emb in response.json()['data']:
embeddings.append(array(emb['embedding']))
i += 10
各テキストに対して、1024 次元の埋め込みが embeddings
リストに格納されます。以下のように見ることができます:
print(embeddings[0])
array([ 0.0352382 , -0.00594871, 0.03808545, ..., -0.01147173,
-0.01710563, 0.01109511], shape=(1024,))),
次に、すべての埋め込みペア間のコサインを計算します。まず、numpy
を使用してコサイン関数 cos_sim
を定義します:
from numpy import dot
from numpy.linalg import norm
def cos_sim(a, b):
return float((a @ b.T) / (norm(a)*norm(b)))
次に、1,460 の埋め込みそれぞれを他の 1,459 と比較してコサインを計算します:
all_cosines = []
for i, emb1 in enumerate(embeddings):
for j, emb2 in enumerate(embeddings):
if i != j:
all_cosines.append(cos_sim(emb1, emb2))
結果は 2,130,140 の値のリストです。その分布は、同じ言語と register における「ランダムな」文書間のコサインを近似するはずです。以下の表とヒストグラムに結果をまとめています。
Number of texts | 1,460 |
---|---|
Number of cosines | 2,130,140 |
Average | 0.343 |
Std. Deviation | 0.116 |

これらの文書は、互いに関連していないにもかかわらず、通常ゼロをかなり上回るコサインを示します。閾値を 0.459(平均+標準偏差1つ分)、あるいは切り上げて 0.5 に設定し、それ未満のコサインを持つ文書ペアは大体無関係だと言いたくなるかもしれません。
しかし、より小さなテキストで同じ実験をしてみましょう。nltk
ライブラリを使用して各文書を文に分割します:
import nltk
sentences = []
for doc in docs:
sentences.extend(nltk.sent_tokenize(doc))
これにより、平均長 27.5 語、標準偏差 16.6 の 6,331 の文が得られます。以下のヒストグラムでは、文のサイズ分布が赤で、完全な文書のものが青で示されており、比較することができます。

同じモデルと方法を使用して、各文の埋め込みを取得します:
sentence_embeddings = []
i = 0
while i < len(sentences):
print(f"Got {len(sentence_embeddings)}...")
data = {
"model": "jina-embeddings-v3",
"task": "text-matching",
"late_chunking": False,
"dimensions": 1024,
"embedding_type": "float",
"input": sentences[i:i+10]
}
response = requests.post(url, headers=headers, data=json.dumps(data))
for emb in response.json()['data']:
sentence_embeddings.append(array(emb['embedding']))
i += 10
そして、各文の埋め込みと他の文の埋め込みとのコサインを取ります:
sent_cosines = []
for i, emb1 in enumerate(sentence_embeddings):
for j, emb2 in enumerate(sentence_embeddings):
if i != j:
sent_cosines.append(cos_sim(emb1, emb2))
結果として、かなり多くのコサイン値が得られました:40,075,230。以下の表にまとめています:
Number of sentences | 6,331 |
---|---|
Number of cosines | 40,075,230 |
Average | 0.254 |
Std. Deviation | 0.116 |
文と文の間のコサインは、文書全体同士の場合と比べて平均的にかなり低くなっています。以下のヒストグラムはその分布を比較したものです。文のペアは文書のペアとほぼ同じ分布を形成していますが、左側にシフトしていることがわかります。

このサイズ依存性が頑健かどうかを確認するため、文と文書の間のすべてのコサインを取得し、ヒストグラムに追加してみましょう。その情報は以下の表にまとめられています:
Number of texts | 6,331 sentences & 1,460 documents |
---|---|
Number of cosines | 9,243,260 |
Average | 0.276 |
Std. Deviation | 0.119 |
下の緑の線は、文書と文のコサインの分布を示しています。この分布が文書同士のコサインと文同士のコサインの間にちょうど収まっていることがわかります。これは、サイズ効果が比較される2つのテキストの大小両方に関係していることを示しています。

さらにテストとして、文書を10個ずつグループにして連結し、146個のより大きな文書を作成してコサインを測定してみましょう。結果は以下のようにまとめられています:
Number of texts | 146 documents |
---|---|
Number of cosines | 21,170 |
Average | 0.658 |
Std. Deviation | 0.09 |

これは他の分布よりもはるかに右側にあります。コサインの閾値を0.5に設定すると、これらの文書のほぼすべてが互いに関連していると判断されてしまいます。このサイズの文書から無関係な文書を除外するには、閾値をもっと高く、おそらく0.9程度に設定する必要があります。しかし、そうすると小さな文書間の良好なマッチングも間違いなく除外されてしまうでしょう。
これは、文書のサイズを考慮に入れずに、マッチングの良さを推定するために最小コサイン閾値を使用することが全くできないことを示しています。
tagサイズバイアスの原因は何か?
埋め込みにおけるサイズバイアスは、長文コンテキストモデルにおける位置バイアスとは異なります。アーキテクチャによって引き起こされるものではありません。本質的にサイズに関するものでもありません。例えば、同じ文書のコピーを何度も連結して長い文書を作成した場合、サイズバイアスは表れません。
問題は、長いテキストはより多くのことを述べているということです。トピックや目的によって制約されていたとしても、より多くの言葉を書くことの要点は、より多くのことを述べることにあります。
通常人々が作成するような長いテキストは、必然的により多くの意味空間に「広がる」埋め込みを生成します。テキストがより多くのことを述べている場合、そのテキストの主題に関係なく、その埋め込みは平均的に他のベクトルとの角度が小さくなります。
tag関連性の測定
この投稿の教訓は、意味ベクトル間のコサインを単独で使用して、何かが良いマッチングかどうかを判断することはできないということです。利用可能なものの中で最良のマッチングであることを示すだけです。最良のマッチングの有用性と妥当性を確認するには、コサインの計算以外の何かをする必要があります。
正規化を試すことはできます。サイズバイアスを経験的に測定できれば、それを相殺することができるかもしれません。しかし、このアプローチは非常に堅牢ではないかもしれません。あるデータセットで機能することは、別のデータセットでは機能しない可能性が高いです。
jina-embeddings-v3で提供される非対称クエリ-文書エンコーディングは、埋め込みモデルのサイズバイアスを減少させますが、完全には排除しません。非対称エンコーディングの目的は、文書をあまり「広がらない」ようにエンコードし、クエリをより広がるようにエンコードすることです。
以下のヒストグラムの赤い線は、jina-embeddings-v3を使用した非対称エンコーディングによる文書間のコサインの分布を示しています - 各文書はretrieval.query
とretrieval.passage
フラグを使用してエンコードされ、各文書のクエリ埋め込みは、同じ文書から生成されていない他のすべての文書のpassage埋め込みと比較されます。平均コサインは0.200で、標準偏差は0.124です。
これらのコサインは、上で見たtext-matching
フラグを使用した同じ文書のコサインよりもかなり小さくなっています。以下のヒストグラムでそれを示しています。

しかし、非対称エンコーディングはサイズバイアスを排除していません。以下のヒストグラムは、非対称エンコーディングを使用した完全な文書と文のコサインを比較しています。

文のコサインの平均は0.124で、非対称エンコーディングを使用した場合、文のコサインの平均と文書のコサインの平均の差は0.076です。対称エンコーディングでの平均の差は0.089です。サイズバイアスの変化は無視できる程度です。
非対称エンコーディングは情報検索のための埋め込みを改善しますが、マッチングの関連性を測定する点では改善されていません。
tag将来の可能性
リランカーアプローチ(例:jina-reranker-v2-base-multilingualおよびjina-reranker-m0)は、クエリと文書のマッチングをスコアリングする代替手法であり、すでにクエリの精度を向上させることがわかっています。リランカースコアは正規化されていないため、客観的な類似度の測定としても機能しません。しかし、これらは異なる方法で計算され、リランカースコアを関連性の良い推定値とするような正規化方法が可能かもしれません。
もう1つの選択肢は、強力な推論能力を持つ大規模言語モデルを使用して、候補がクエリに対して良いマッチングかどうかを直接評価することです。単純に言えば、タスク特化型の大規模言語モデルに「1から10のスケールで、この文書はこのクエリに対して良いマッチングですか?」と尋ねることができます。既存のモデルはこのタスクに適していないかもしれませんが、焦点を絞ったトレーニングとより洗練されたプロンプト技術は有望です。
モデルが関連性を測定することは不可能ではありませんが、埋め込みモデルとは異なるパラダイムが必要です。
tagモデルの長所を活かす
上で説明したサイズバイアス効果は、埋め込みモデルの基本的な制限の1つを示しています:これらは物事を比較するのには優れていますが、絶対的な関連性を測定するのは信頼できません。この制限は設計上の欠陥ではなく、これらのモデルがどのように機能するかの本質的な特徴です。
では、これは何を意味するのでしょうか?
まず、コサイン閾値に対して懐疑的になるべきです。これらは単に機能しません。コサイン類似度は、客観的に見える浮動小数点数を出力します。しかし、何かが数値を出力するからといって、それが何かを客観的に測定しているわけではありません。
第二に、ハイブリッドソリューションを検討してください。埋め込みは、大規模なアイテムセットから有望な候補を効率的に絞り込むことができます。その後、リランカーや LLM、あるいは人間の評価者のようなより洗練された(そして計算コストの高い)テクニックを適用して、実際の関連性を判断することができます。
第三に、システムを設計する際は、機能ではなくタスクの観点から考えてください。ベンチマークで客観的に最も賢く、最高のスコアを出すモデルでも、それを購入した目的のジョブをこなせなければ、お金の無駄遣いです。
モデルの限界を理解することは悲観的なことではありません - これはアプリケーションにおけるより広い原則を反映しています:モデルの得意なことと不得意なことを理解することは、信頼性の高い効果的なシステムを構築する上で重要です。ハンマーでネジを締めようとしないのと同じように、埋め込みモデルを扱えないタスクに使用すべきではありません。あなたのツールの長所を理解しましょう。