AI モデルの長年の課題の1つは、ニューラルネットワークがどのようにして出力を生成するのかを説明できないことです。これがどの程度 AI にとって本質的な問題なのかは必ずしも明確ではありません。人間に理由を説明するよう求めても、通常は自分でも気づかないうちに合理化し、頭の中で実際に何が起きているかを示すことなく、もっともらしい説明をするだけです。
すでに私たちは、AI モデルにもっともらしい答えを作らせる方法を知っています。この点で、AI は認めたくないかもしれませんが、人間に似ているのかもしれません。
50年前、アメリカの哲学者トーマス・ネーゲルは『コウモリであるとはどのようなことか』という影響力のあるエッセイを書きました。彼は、コウモリであることには何かしらの「そのような感じ」があるはずだと主張しました:コウモリが世界を見るように見ること、コウモリのように存在を知覚すること。しかし、ネーゲルによれば、たとえコウモリの脳や感覚、体の働きについて知り得るすべての事実を知っていたとしても、コウモリであることがどのようなものかは分からないのです。
AI の説明可能性も同じような問題です。私たちは特定の AI モデルについて知り得るすべての事実を知っています。それは単に有限精度の数値が行列の並びとして配置されているだけです。すべてのモデル出力が正しい算術の結果であることは簡単に検証できますが、その情報は説明としては役に立ちません。
この問題に対する一般的な解決策は、人間の場合と同様に存在しません。しかし、ColBERT アーキテクチャ、特に再ランク付けに使用される「後期相互作用」の方法により、モデルが特定のケースで特定の結果を出す理由について意味のある洞察を得ることができます。
この記事では、Jina-ColBERT モデル jina-colbert-v1-en と Matplotlib Python ライブラリを使用して、後期相互作用がどのように説明可能性を可能にするかを示します。
tagColBERT の概要
ColBERT は、Khattab & Zaharia (2020) において、Google が 2018 年に発表した BERT モデルの拡張として導入されました。Jina AI の Jina-ColBERT モデルは、この研究と Santhanam, et al. (2021) で提案された ColBERT v2 アーキテクチャを基にしています。ColBERT スタイルのモデルは埋め込みを作成するために使用できますが、再ランク付けモデルとして使用する場合には追加の機能があります。主な利点は「後期相互作用」で、これは標準的な埋め込みモデルとは異なる方法でテキストの意味的類似性の問題を構造化する方法です。
tag埋め込みモデル
従来の埋め込みモデルでは、2つのテキストを、それらの代表的なベクトルである「埋め込み」を生成して比較し、コサイン距離やハミング距離などの距離メトリクスを使用して比較します。2つのテキストの意味的類似性を定量化する一般的な手順は以下の通りです。
まず、2つのテキストの埋め込みを別々に作成します。各テキストに対して:
- トークナイザーがテキストを概ね単語サイズのチャンクに分割します。
- 各トークンがベクトルにマッピングされます。
- トークンベクトルは注意機構と畳み込み層を通じて相互作用し、各トークンの表現に文脈情報を追加します。
- プーリング層がこれらの修正されたトークンベクトルを単一の埋め込みベクトルに変換します。

そして、各テキストの埋め込みが作成されたら、通常コサイン距離やハミング距離を使用してそれらを比較します。

スコアリングは、トークンに関する具体的な情報なしに、2つの埋め込み全体を比較することで行われます。トークン間のすべての相互作用は、2つのテキストが比較される前に発生するため「早期」相互作用です。
tag再ランク付けモデル
再ランク付けモデルは異なる方法で動作します。
まず、テキストの埋め込みを作成する代わりに、「クエリ」と呼ばれる1つのテキストと、「対象文書」と呼ぶ他のテキストのコレクションを取り、クエリテキストに対して各対象文書のスコアを計算します。これらの数値は正規化されておらず、埋め込みの比較とは異なりますが、並べ替え可能です。クエリに対して最も高いスコアを持つ対象文書は、モデルによれば、クエリと意味的に最も関連のあるテキストです。
Jina Reranker API と Python を使用して、jina-colbert-v1-en 再ランク付けモデルでこれがどのように機能するか具体的に見てみましょう。
以下のコードは、ダウンロードまたは Google Colab で実行できるノートブックにも含まれています。
まず、Python 環境に最新バージョンの requests ライブラリをインストールする必要があります。以下のコマンドでインストールできます:
pip install requests -U
次に、Jina Reranker API ページにアクセスし、100万トークンまでのテキスト処理が可能な無料 API トークンを取得してください。以下に示すように、ページ下部から API トークンキーをコピーしてください:

以下のクエリテキストを使用します:
- 「象は1日に150kgの食事をする。」
そしてこのクエリを3つのテキストと比較します:
- 「象は1日に150kgの食事をする。」
- 「平均的な象は毎日約150kgの植物を消費する。」
- 「スペインの雨は主に平野に降る。」
最初の文書はクエリと同一で、2番目は1番目の言い換えで、最後のテキストは全く関係ありません。
以下の Python コードを使用してスコアを取得します。変数 jina_api_key に Jina Reranker API トークンを割り当ててください:
import requests
url = "<https://api.jina.ai/v1/rerank>"
jina_api_key = "<YOUR JINA RERANKER API TOKEN HERE>"
headers = {
"Content-Type": "application/json",
"Authorization": f"Bearer {jina_api_key}"
}
data = {
"model": "jina-colbert-v1-en",
"query": "Elephants eat 150 kg of food per day.",
"documents": [
"Elephants eat 150 kg of food per day.",
"Every day, the average elephant consumes roughly 150 kg of food.",
"The rain in Spain falls mainly on the plain.",
],
"top_n": 3
}
response = requests.post(url, headers=headers, json=data)
for item in response.json()['results']:
print(f"{item['relevance_score']} : {item['document']['text']}")
Python ファイルまたはノートブックでこのコードを実行すると、以下のような結果が得られるはずです:
11.15625 : Elephants eat 150 kg of food per day.
9.6328125 : Every day, the average elephant consumes roughly 150 kg of food.
1.568359375 : The rain in Spain falls mainly on the plain.
予想通り、完全一致が最も高いスコアを持ち、言い換えが2番目に高く、全く関係のないテキストはずっと低いスコアを持っています。
tagColBERT を使用したスコアリング
埋め込みベースのスコアリングと ColBERT 再ランク付けが異なる点は、スコアリングプロセス中に2つのテキストのトークンが相互に比較されることです。2つのテキストは独自の埋め込みを持つことはありません。
まず、埋め込みモデルと同じアーキテクチャを使用して、テキストからの文脈情報を含む各トークンの新しい表現を作成します。次に、クエリの各トークンと文書の各トークンを比較します。
クエリの各トークンに対して、それと最も強い相互作用を持つ文書のトークンを特定し、それらの相互作用スコアを合計して最終的な数値を計算します。

この相互作用は「後期」と呼ばれます:2つのテキストを比較する際に、トークン間で相互作用が発生します。ただし、この「後期」相互作用は「早期」相互作用を排除するものではありません。比較されるトークンベクトルのペアには、すでにそれぞれの文脈に関する情報が含まれています。
この後期相互作用の仕組みは、その情報が文脈に依存するものであっても、トークンレベルの情報を保持します。これにより、文脈化されたトークンのペアのうちどれが最終的なスコアに貢献しているかを特定できるため、ColBERT モデルがどのようにスコアを計算しているかを部分的に確認することができます。
tagヒートマップを使用したランキングの説明
ヒートマップは、Jina-ColBERT がスコアを作成する際の動作を視覚化するのに便利な技術です。このセクションでは、seaborn と matplotlib ライブラリを使用して、jina-colbert-v1-en の後期相互作用層からヒートマップを作成し、クエリトークンと対象テキストトークンがどのように相互作用するかを示します。
tagセットアップ
jina-colbert-v1-en モデルにアクセスし、seaborn、matplotlib、Pillow を使用してヒートマップを作成するためのコードを含む Python ライブラリファイルを作成しました。このライブラリは GitHub から直接ダウンロードするか、提供されているノートブックをご自身のシステムで使用するか、Google Colab で使用することができます。
まず、必要なものをインストールします。Python 環境に requests ライブラリの最新バージョンが必要です。まだインストールしていない場合は、次のコマンドを実行してください:
pip install requests -U
次に、コアライブラリをインストールします:
pip install matplotlib seaborn torch Pillow
次に、GitHub から jina_colbert_heatmaps.py をダウンロードします。ウェブブラウザ経由でダウンロードするか、wget がインストールされている場合はコマンドラインで実行できます:
wget https://raw.githubusercontent.com/jina-ai/workshops/main/notebooks/heatmaps/jina_colbert_heatmaps.py
ライブラリを配置したら、この記事の残りの部分では以下の関数を1つ宣言するだけで済みます:
from jina_colbert_heatmaps import JinaColbertHeatmapMaker
def create_heatmap(query, document, figsize=None):
heat_map_maker = JinaColbertHeatmapMaker(jina_api_key=jina_api_key)
# get token embeddings for the query
query_emb = heat_map_maker.embed(query, is_query=True)
# get token embeddings for the target document
document_emb = heat_map_maker.embed(document, is_query=False)
return heat_map_maker.compute_heatmap(document_emb[0], query_emb[0], figsize)
tag結果
ヒートマップを作成できるようになったので、いくつか作成して何が分かるか見てみましょう。
Python で以下のコマンドを実行してください:
create_heatmap("Elephants eat 150 kg of food per day.", "Elephants eat 150 kg of food per day.")結果は以下のようなヒートマップになります:

これは、同一のテキストを比較する際のトークンペア間の活性化レベルを示すヒートマップです。各マスは、それぞれのテキストからの2つのトークン間の相互作用を示しています。追加トークンの [CLS] と [SEP] はそれぞれテキストの開始と終了を示し、q と d は [CLS] トークンの直後にクエリと対象文書それぞれに挿入されます。これにより、モデルはトークンとテキストの開始・終了の間の相互作用を考慮できるだけでなく、トークン表現がクエリと対象のどちらにあるかを区別することができます。
マスが明るいほど、2つのトークン間の相互作用が強く、意味的な関連性が高いことを示しています。各トークンペアの相互作用スコアは -1.0 から 1.0 の範囲です。赤い枠で囲まれたマスは、最終スコアに寄与するものです:クエリの各トークンについて、文書内の任意のトークンとの最高の相互作用レベルが採用される値となります。
最もマッチするもの(最も明るい部分)と赤枠で囲まれた最大値は、ほぼすべて対角線上に正確に配置され、非常に強い相互作用を示しています。唯一の例外は、「技術的な」トークンである [CLS]、q、d、および英語の高頻度「ストップワード」で独立した情報をほとんど持たない "of" という単語です。
構造的に似た文「Cats eat 50 g of food per day.」を見て、トークンがどのように相互作用するか確認してみましょう:
create_heatmap("Elephants eat 150 kg of food per day.", "Cats eat 50 g of food per day.")
ここでも、単語が頻繁に同じで文の構造がほぼ同一であるため、最もマッチするものは主に対角線上にあります。"cats" と "elephants" も共通の文脈のため、あまり強くはありませんがマッチしています。
文脈の類似性が低いほど、マッチ度は悪くなります。「Employees eat at the company canteen.」というテキストを考えてみましょう:
create_heatmap("Elephants eat 150 kg of food per day.", "Employees eat at the company canteen.")
構造的には似ていますが、ここで強いマッチを示すのは "eat" の2つのインスタンス間だけです。これらの文は構造的に非常に類似していても、話題の面では大きく異なります。
赤枠で囲まれたマスの色の暗さを見ると、モデルが「Elephants eat 150 kg of food per day」に対するマッチとしてどのようにランク付けするかが分かり、jina-colbert-v1-en はこの直感を以下のように確認します:
| スコア | テキスト |
|---|---|
| 11.15625 | Elephants eat 150 kg of food per day. |
| 8.3671875 | Cats eat 50 g of food per day. |
| 3.734375 | Employees eat at the company canteen. |
では、"Elephants eat 150 kg of food per day." を、本質的に同じ意味だが異なる表現の文「Every day, the average elephant consumes roughly 150 kg of food.」と比較してみましょう:
create_heatmap("Elephants eat 150 kg of food per day.", "Every day, the average elephant consumes roughly 150 kg of food.")
最初の文章の「eat」と2番目の文章の「consume」の間に強い関連性があることに注目してください。語彙が異なっていても、Jina-ColBERT は共通の意味を認識することができます。
また、「every day」は完全に異なる位置にあっても「per day」と強く一致します。価値の低い単語「of」のみが異常な不一致となっています。
では、同じクエリを全く関係のないテキストと比較してみましょう:"The rain in Spain falls mainly on the plain."
create_heatmap("Elephants eat 150 kg of food per day.", "The rain in Spain falls mainly on the plain.")
この組み合わせでは「ベストマッチ」の相互作用のスコアがはるかに低く、2つのテキスト間の単語の相互作用もほとんどないことがわかります。直感的に、"Every day, the average elephant consumes roughly 150 kg of food" と比べて低いスコアが予想されますが、jina-colbert-v1-en もそれに同意します:
| Score | Text |
|---|---|
| 9.6328125 | Every day, the average elephant consumes roughly 150 kg of food. |
| 1.568359375 | The rain in Spain falls mainly on the plain. |
tag長いテキスト
これらは ColBERT スタイルの再ランク付けモデルの仕組みを示すためのおもちゃの例です。情報検索の文脈では、検索拡張生成のような場合、クエリは通常短いテキストであり、マッチング候補の文書はより長く、モデルの入力コンテキストウィンドウと同じくらいの長さになることがあります。
Jina-ColBERT モデルはすべて 8192 トークンの入力コンテキストをサポートしており、これは約 16 ページの標準的な 1 行スペースのテキストに相当します。
これらの非対称なケースでもヒートマップを生成できます。例えば、インド象に関する Wikipedia ページの最初のセクションを見てみましょう:

jina-colbert-v1-en に渡される平文を確認するには、このリンクをクリックしてください。
このテキストは 364 単語あるので、ヒートマップは正方形にはなりません:
create_heatmap("Elephants eat 150 kg of food per day.", wikipedia_elephants, figsize=(50,7))
「elephants」がテキスト中の多くの場所と一致していることがわかります。象についてのテキストなので、これは驚くことではありません。しかし、さらに強い相互作用がある領域も見ることができます:

ここで何が起きているのでしょうか?Jina-ColBERT を使うと、これが長いテキストのどの部分に対応しているかを見つけることができます。2 段落目の 4 番目の文章であることがわかります:
The species is classified as a megaherbivore and consume up to 150 kg (330 lb) of plant matter per day.
これはクエリテキストと同じ情報を述べています。この文だけのヒートマップを見ると、強い一致が見られます:

Jina-ColBERT は、長いテキストのどの部分がクエリと一致したのかを正確に確認する手段を提供します。これによってデバッグが改善されるだけでなく、説明可能性も高まります。マッチがどのように行われたかを理解するのに高度な知識は必要ありません。
tagJina-ColBERT による AI の結果の説明
埋め込みは現代の AI の中核技術です。私たちが行うほとんどすべてのことは、入力データの複雑で学習可能な関係が高次元空間の幾何学で表現できるという考えに基づいています。しかし、数千から数百万次元の空間的関係を人間が理解するのは非常に難しいことです。
ColBERT はそのレベルの抽象化からの一歩後退です。AI モデルが何をするのかを説明する完全な答えではありませんが、結果に責任を持つデータの部分を直接指し示してくれます。
時として、AI はブラックボックスでなければなりません。すべての重要な処理を行う巨大な行列は、人間が頭の中に収めるには大きすぎます。しかし、ColBERT アーキテクチャはそのボックスに少し光を当て、より多くのことが可能であることを示しています。
Jina-ColBERT モデルは現在英語のみ(jina-colbert-v1-en)で利用可能ですが、より多くの言語と使用コンテキストが開発中です。最先端の情報検索を実行するだけでなく、なぜ何かがマッチしたのかを説明できるこのモデルシリーズは、AI 技術をアクセスしやすく、かつ有用なものにするという Jina AI のコミットメントを示しています。






