【Python】Sentence-Transformers ライブラリ徹底解説!🚀 文のベクトル化から類似度計算まで

自然言語処理

Sentence-Transformersは、文章や画像を高性能なベクトル表現(埋め込み、Embedding)に変換するためのPythonライブラリです。特に、文章の意味的な類似性を捉えることに優れており、自然言語処理(NLP)の様々なタスクで活躍します。

Transformerベースのモデル(BERT、RoBERTaなど)を活用し、文全体の意味を考慮したベクトルを生成します。これにより、従来の単語ベースのベクトル化手法(Word2Vec、GloVeなど)では捉えきれなかった文脈やニュアンスを表現できるようになりました。Hugging Faceのエコシステムとも連携しており、膨大な数の事前学習済みモデルを利用できます。

開発は元々UKPLabによって行われ、現在はHugging Faceによってメンテナンスされています。

Sentence-Transformersの主な用途

このライブラリは、以下のようなタスクで非常に有用です。

  • セマンティック検索(意味検索): キーワードだけでなく、文の意味に基づいて関連性の高い文書や文を検索します。
  • 類似文章の特定: 似た意味を持つ文章を見つけ出します。(例:重複記事の検出、関連ニュースの推薦)
  • パラフレーズマイニング: 同じ意味を異なる表現で言い換えた文(パラフレーズ)を発見します。
  • クラスタリング: 文章を意味に基づいてグループ分けします。(例:アンケートの自由記述の分類)
  • テキスト分類: 文章を特定のカテゴリに分類します。
  • 質問応答システム: ユーザーの質問と最も意味的に近い回答候補を選択します。

これらの機能は、社内文書検索の改善、FAQシステムの精度向上、顧客レビュー分析など、ビジネスシーンでも幅広く応用可能です。

文埋め込み (Sentence Embedding) とは?

文埋め込みとは、文や段落といったテキスト全体を、固定長の密な数値ベクトル(高次元空間の点)に変換する技術です。このベクトルの各次元は、テキストの特定の意味的特徴を表しており、ベクトル空間内で意味的に近い文は近くに、意味的に遠い文は遠くに配置されるように学習されます。

Sentence-Transformersは、Transformerモデル(特にBERTなど)の出力層のベクトルをPooling(平均化など)し、さらにSiamese Network(シャムネットワーク)などの構造を用いて特定のタスク(例:意味的に類似した文ペアのベクトル距離を近く、非類似なペアの距離を遠くする)でファインチューニングすることで、質の高い文埋め込みベクトルを生成します。これにより、単にBERTの最終層の出力を平均化するよりも、文の類似度を測る上で優れた性能を発揮します。

類似度の計算

文埋め込みベクトルが得られれば、ベクトル間の類似度を計算することで、文同士の意味的な近さを定量化できます。最も一般的に用いられるのはコサイン類似度 (Cosine Similarity) です。

コサイン類似度は、2つのベクトルがなす角度のコサインを計算します。値は-1から1の範囲を取り、1に近いほどベクトル(つまり元の文)の意味が類似していることを示し、0に近いほど関連性が低く、-1に近いほど反対の意味を持つことを示します(ただし、Sentence-Transformersで生成される埋め込みベクトルでは、通常0から1の範囲で解釈されます)。

Sentence-Transformersライブラリには、このコサイン類似度を簡単に計算するためのユーティリティ関数(util.pytorch_cos_simutil.cos_sim、モデルオブジェクトのsimilarityメソッドなど)が用意されています。

Sentence-Transformersのインストールはpipを使って簡単に行えます。Python 3.9以上、PyTorch 1.11.0以上、transformers v4.34.0以上が推奨されています。

pip install -U sentence-transformers

condaを使用する場合は、以下のコマンドでインストールできます。

conda install -c conda-forge sentence-transformers

GPU (CUDA) を使用したい場合は、PyTorchの公式サイトの手順に従って、お使いのCUDAバージョンに対応したPyTorchをインストールする必要があります。

注意: 日本語モデルの一部(例:cl-tohoku/bert-base-japanese-whole-word-maskingベースのモデル)を使用する場合、追加で形態素解析器ライブラリ(fugashiと辞書ipadic)が必要になることがあります。

pip install fugashi ipadic

Sentence-Transformersの基本的な使い方は非常にシンプルです。以下のステップで文の埋め込みベクトルを生成し、類似度を計算できます。

  1. モデルの読み込み: SentenceTransformer クラスを使って、Hugging Face Hubなどから事前学習済みモデルを読み込みます。
  2. エンコード: 読み込んだモデルの encode() メソッドに、ベクトル化したい文のリストを渡します。
  3. 類似度計算: 得られたベクトルを使って、similarity() メソッドや util.pytorch_cos_sim などで類似度を計算します。

サンプルコード

from sentence_transformers import SentenceTransformer, util

# 1. 事前学習済みモデルの読み込み (多言語対応モデルの例)
# model = SentenceTransformer('all-MiniLM-L6-v2') # 英語向けの人気モデル
model = SentenceTransformer('paraphrase-multilingual-mpnet-base-v2') # 多言語対応モデル

# 2. ベクトル化したい文のリスト
sentences1 = ['今日は良い天気ですね', '明日の天気はどうなりますか?', 'この猫はとてもかわいい']
sentences2 = ['素晴らしい天気です', '天気が知りたい', 'この犬はかわいいですね']

# 3. 文をベクトルにエンコード
embeddings1 = model.encode(sentences1, convert_to_tensor=True)
embeddings2 = model.encode(sentences2, convert_to_tensor=True)

print("Embeddings shape:", embeddings1.shape) # => (3, 768) など、モデル依存の次元数

# 4. コサイン類似度の計算 (各文ペアごと)
# embeddings1 の各文と embeddings2 の各文の類似度を計算
cosine_scores = util.pytorch_cos_sim(embeddings1, embeddings2)

# 結果の表示
for i in range(len(sentences1)):
    for j in range(len(sentences2)):
        print(f"'{sentences1[i]}' vs '{sentences2[j]}': {cosine_scores[i][j]:.4f}")

print("\n--- 最も類似度が高いペア ---")
# 最も類似度の高いペアを見つける (例: sentences1[0] と sentences2 の比較)
scores_for_s1_0 = util.pytorch_cos_sim(embeddings1[0], embeddings2)[0]
best_match_idx = scores_for_s1_0.argmax()
print(f"'{sentences1[0]}' に最も近いのは '{sentences2[best_match_idx]}' (Score: {scores_for_s1_0[best_match_idx]:.4f})")

# モデル自身のsimilarityメソッドを使う場合 (バッチ内の全ペア比較)
sentences_all = sentences1 + sentences2
embeddings_all = model.encode(sentences_all)
similarities_all = model.similarity(embeddings_all, embeddings_all)
print("\n--- 全ペア間の類似度 (モデルのsimilarityメソッド) ---")
print(similarities_all)

このコードでは、まず多言語対応の事前学習済みモデル paraphrase-multilingual-mpnet-base-v2 をロードします。次に、いくつかの日本語の文をリストとして定義し、model.encode() でそれらをベクトル(埋め込み)に変換します。最後に util.pytorch_cos_sim を使って、異なるリスト間の文ペアのコサイン類似度を計算し表示しています。

model.encode()convert_to_tensor=True オプションは、結果をNumPy配列ではなくPyTorchテンソルとして返すように指示します。これは util.pytorch_cos_sim を使う場合に便利です。model.similarity() は内部でコサイン類似度を計算し、テンソルを返します。

豊富な事前学習済みモデル

Sentence-Transformersの大きな魅力の一つは、Hugging Face Hubで公開されている10,000を超える事前学習済みモデルを手軽に利用できる点です。これらのモデルは、様々な言語、タスク、データセットで訓練されており、目的に合ったモデルを選択できます。

代表的なモデルには以下のようなものがあります。

モデル名 特徴 主な用途 言語
all-MiniLM-L6-v2 高速かつ高品質な英語モデル。バランスが良い。 意味検索、類似度計算、クラスタリング(英語) 英語
all-mpnet-base-v2 all-MiniLM-L6-v2より高品質だが少し遅い英語モデル。 意味検索、類似度計算(英語、高精度) 英語
paraphrase-multilingual-mpnet-base-v2 多言語対応(50言語以上)の高精度モデル。 多言語の意味検索、類似度計算、クラスタリング 多言語
paraphrase-multilingual-MiniLM-L12-v2 多言語対応の高速モデル。multilingual-mpnet-base-v2より軽量。 多言語の意味検索、類似度計算(速度重視) 多言語
cl-tohoku/bert-base-japanese-whole-word-masking ベースの日本語モデル
(例: sonoisa/sentence-bert-base-ja-mean-tokens-v2)
東北大学の日本語BERTをベースにファインチューニングされたモデル。 日本語の意味検索、類似度計算 日本語
CLIPモデル (例: clip-ViT-B-32) 画像とテキストを同じベクトル空間に埋め込むマルチモーダルモデル。 テキストによる画像検索、画像による画像検索 多言語

これらのモデルは、SentenceTransformer('モデル名') のように指定するだけで簡単にロードして利用開始できます。モデルは初回利用時に自動的にダウンロードされ、キャッシュされます(デフォルトでは ~/.cache/huggingface/hub/ 以下)。

セマンティック検索 (Semantic Search)

Sentence-Transformersはセマンティック検索の実装を容易にします。セマンティック検索は、単なるキーワードの一致ではなく、検索クエリと文書の「意味」を理解して関連性の高い結果を返す検索手法です。

基本的な流れは以下の通りです。

  1. 検索対象となる文書群(コーパス)の各文書をSentence-Transformersでベクトル化し、インデックス(例:FAISSなどのベクトルデータベースや単純なリスト)を作成しておく。
  2. ユーザーからの検索クエリ(文)を同じモデルでベクトル化する。
  3. クエリベクトルとコーパスの全ベクトルとの類似度(通常はコサイン類似度)を計算する。
  4. 類似度が高い順に文書をランキングし、結果として提示する。

Sentence-Transformersはutil.semantic_searchという便利な関数を提供しており、大量の文書(約100万件まで)に対するセマンティック検索を効率的に行うことができます。

from sentence_transformers import SentenceTransformer, util

model = SentenceTransformer('paraphrase-multilingual-mpnet-base-v2')

# コーパス (検索対象の文書群)
corpus = [
    'Pythonは汎用のプログラミング言語です。',
    '機械学習ライブラリにはTensorFlowやPyTorchがあります。',
    'Sentence-Transformersは文の埋め込みを計算します。',
    '明日の東京の天気は晴れでしょう。',
    '自然言語処理はAIの重要な分野です。'
]

# コーパスのベクトル化 (事前に計算して保存しておくのが一般的)
corpus_embeddings = model.encode(corpus, convert_to_tensor=True)

# 検索クエリ
query = '文章の意味をベクトルにするライブラリは?'

# クエリのベクトル化
query_embedding = model.encode(query, convert_to_tensor=True)

# util.semantic_search で検索実行 (上位3件を取得)
# ※ corpus_embeddings は PyTorch テンソルか NumPy 配列である必要あり
hits = util.semantic_search(query_embedding, corpus_embeddings, top_k=3)
hits = hits[0] # クエリが1つの場合、結果はリストのリストになる

print(f"Query: {query}\n")
print("Top 3 most similar sentences in corpus:")
for hit in hits:
    print(f"- {corpus[hit['corpus_id']]} (Score: {hit['score']:.4f})")

この例では、与えられたクエリに対して、コーパスの中から意味的に最も関連性の高い文を上位3件検索しています。

非対称 (Asymmetric) vs 対称 (Symmetric) セマンティック検索:
検索タスクには、短いクエリ(質問)で長い文書(回答)を探す「非対称」なタスクと、似たような長さや種類の文同士を比較する「対称」なタスクがあります。モデル選択時には、どちらのタスクに適したモデルかを確認することが重要です(例:MS MARCOデータセットで訓練されたモデルは非対称タスクに強い傾向があります)。

ファインチューニング (Fine-tuning)

既存の事前学習済みモデルを特定のドメインやタスクに合わせて再調整(ファインチューニング)することで、性能をさらに向上させることができます。例えば、特定の業界用語が多い文書検索システムや、特殊な類似性の定義が必要なタスクなどで有効です。

ファインチューニングには通常、以下の要素が必要です。

  • ベースとなる事前学習済みモデル: Hugging Face Hubなどから選択します。日本語の場合は東北大学のBERTなどがよく使われます。
  • 学習データセット: タスクに応じた形式のデータペア(またはトリプレット)が必要です。
    • 類似ペア: (文A, 文B) のペアで、意味が類似しているもの。
    • 非類似ペア: (文A, 文B) のペアで、意味が異なるもの。
    • トリプレット: (アンカー文, ポジティブ文, ネガティブ文) の組。アンカーとポジティブは類似、アンカーとネガティブは非類似。NLI (Natural Language Inference) データセットなどがよく利用されます。
  • 損失関数 (Loss Function): データセットの形式に合わせて選択します。代表的なものに以下があります。
    • CosineSimilarityLoss: 2つの文の類似度スコア(例: 0〜1)を予測するように学習。
    • MultipleNegativesRankingLoss: バッチ内の他の文をネガティブサンプルとして利用し、ポジティブペアの類似度を最大化、ネガティブペアの類似度を最小化する効率的な損失関数。NLIデータセットなどと相性が良い。
    • TripletLoss: トリプレットデータを用い、アンカーとポジティブの距離を小さく、アンカーとネガティブの距離を大きくするように学習。

Sentence-Transformersは、これらの要素を組み合わせて学習を実行するためのSentenceTransformerTrainerクラス(バージョン3.0以降)や、各種損失関数、データローダーを提供しています。

2023年頃からは、PEFT (Parameter-Efficient Fine-Tuning) との連携も強化され、LoRA (Low-Rank Adaptation) などの手法を用いて、モデル全体ではなく一部のパラメータのみを更新することで、少ない計算リソースで効率的にファインチューニングを行うことも可能になっています。

Bi-Encoder vs. Cross-Encoder

Sentence-Transformersは、主に2種類のアーキテクチャをサポートしています。

  1. Bi-Encoder (バイエンコーダー):
    • これがSentence-Transformersの主要なモデルです。
    • 2つの文(文A、文B)を独立してエンコードし、それぞれの文ベクトル(埋め込み)u, v を生成します。
    • その後、ベクトル uv の類似度(コサイン類似度など)を計算します。
    • 利点: 各文のベクトルを事前に計算して保存・インデックス化できるため、大規模なデータセットに対する検索(セマンティック検索)やクラスタリングが非常に高速です。
    • 欠点: 文ペア間の相互作用を直接モデル化しないため、Cross-Encoderほどの精度は出ない場合があります。
  2. Cross-Encoder (クロスエンコーダー):
    • 2つの文(文A、文B)を同時に入力し、1つのTransformerモデルに通します。
    • モデルは文ペア間の相互作用(Attention)を考慮し、最終的にそのペアの類似度スコア(例:0〜1の数値)を直接出力します。
    • 利点: 文ペアの関係性を深く捉えることができるため、一般的にBi-Encoderよりも高い精度(特に再ランキングタスク)を達成します。
    • 欠点: 文ベクトルを生成しません。類似度を知りたい全ての文ペアに対してモデルの推論が必要になるため、計算コストが非常に高くなります。例えば、1万件の文の中から類似文を探す場合、約5000万通りのペア計算が必要になり、現実的ではありません。

使い分けの目安

  • Bi-Encoder: 大規模な文書検索(Information Retrieval)、クラスタリング、類似文推薦など、速度とスケーラビリティが重要な場合。まずBi-Encoderで候補を絞り込む用途。
  • Cross-Encoder: 少数の候補ペアに対する高精度な類似度判定、再ランキング(Reranking)タスク。例えば、Bi-Encoderで絞り込んだ上位100件の結果を、Cross-Encoderで再度ランキングし直して精度を高める、といった使い方。

Cross-Encoderの使い方は以下のようになります。

from sentence_transformers.cross_encoder import CrossEncoder

# 事前学習済みのCross-Encoderモデルをロード
model = CrossEncoder('cross-encoder/stsb-roberta-base') # STSベンチマークで訓練されたモデル

# スコアリングしたい文のペアリスト
sentence_pairs = [
    ['今日の天気は晴れです。', '外は太陽が出ています。'],
    ['Pythonはプログラミング言語です。', '猫が道を歩いています。'],
    ['リンゴは果物です。', 'バナナも果物の一種です。']
]

# predict() メソッドで類似度スコアを計算
scores = model.predict(sentence_pairs)

for i in range(len(sentence_pairs)):
    print(f"{sentence_pairs[i]}: {scores[i]:.4f}")

Cross-Encoderは、文ペアの類似度を直接予測します。

クラスタリング

Sentence-Transformersで得られた文埋め込みベクトルを使えば、K-Meansなどのクラスタリングアルゴリズムを適用して、意味的に類似した文をグループ化することができます。これは、顧客からのフィードバックの分類、ニュース記事のトピック分類などに役立ちます。

sklearn.cluster.KMeans などと組み合わせて利用できます。また、Sentence-Transformersライブラリ自体にも、高速なクラスタリングを行うためのユーティリティ関数 util.community_detection が含まれています。これは、大量の埋め込みベクトルを効率的にクラスタリングするのに適しています。

from sentence_transformers import SentenceTransformer, util
from sklearn.cluster import KMeans
import numpy as np

model = SentenceTransformer('paraphrase-multilingual-mpnet-base-v2')

# クラスタリング対象の文
sentences = [
    'リンゴは赤くて美味しい果物です。',
    'バナナは黄色い果物で、栄養価が高い。',
    'プログラミング言語にはPythonやJavaがあります。',
    '今日の天気は曇り時々雨でしょう。',
    'オレンジはビタミンCが豊富な柑橘類です。',
    '機械学習はAIの一分野です。',
    '明日は晴れるといいな。',
    'ソフトウェア開発は楽しい仕事です。'
]

# 文のベクトル化
embeddings = model.encode(sentences)

# K-Meansでクラスタリング (例: 3つのクラスタに分類)
num_clusters = 3
clustering_model = KMeans(n_clusters=num_clusters, random_state=42, n_init=10) # n_initを指定して警告抑制
clustering_model.fit(embeddings)
cluster_assignment = clustering_model.labels_

clustered_sentences = [[] for _ in range(num_clusters)]
for sentence_id, cluster_id in enumerate(cluster_assignment):
    clustered_sentences[cluster_id].append(sentences[sentence_id])

# 結果表示
for i, cluster in enumerate(clustered_sentences):
    print(f"\nCluster {i+1}")
    for sentence in cluster:
        print(f"- {sentence}")

# --- util.community_detection を使ったクラスタリング ---
# こちらは大規模データに適している
print("\n--- Community Detection ---")
# min_community_size: 各クラスタの最小サイズ
# threshold: コサイン類似度の閾値。これ以上の類似度を持つノード間にエッジを張る
clusters = util.community_detection(embeddings, min_community_size=2, threshold=0.6)

for i, cluster in enumerate(clusters):
    print(f"\nCommunity {i+1}")
    for sentence_id in cluster:
        print(f"- {sentences[sentence_id]}")

画像検索 (Image Search)

Sentence-Transformersは、CLIP (Contrastive Language–Image Pre-training) のようなマルチモーダルモデルもサポートしています。これにより、テキストと画像を同じベクトル空間に埋め込むことが可能になります。

応用例:

  • テキストによる画像検索: 「赤いスポーツカー」というテキストクエリで、赤いスポーツカーの画像を検索する。
  • 画像によるテキスト検索: ある画像のベクトルと類似したベクトルを持つテキスト(説明文など)を探す。
  • 画像による画像検索: ある画像のベクトルと類似したベクトルを持つ他の画像を検索する。
from sentence_transformers import SentenceTransformer, util
from PIL import Image
import glob # ファイル検索用

# マルチモーダルモデル CLIP をロード
model = SentenceTransformer('clip-ViT-B-32')

# --- テキストによる画像検索 ---
img_folder = 'path/to/your/images/' # 画像が保存されているフォルダ
img_names = list(glob.glob(img_folder + '*.jpg')) # 例: JPG画像を取得
print(f"Found {len(img_names)} images.")

# 画像をエンコード (事前に計算しておくと良い)
img_embeddings = model.encode([Image.open(filepath) for filepath in img_names], batch_size=128, convert_to_tensor=True, show_progress_bar=True)

# テキストクエリ
query = "two dogs playing in the snow" # 雪の中で遊ぶ2匹の犬
text_embedding = model.encode(query, convert_to_tensor=True)

# コサイン類似度で検索
cos_scores = util.pytorch_cos_sim(text_embedding, img_embeddings)[0]
top_results = torch.topk(cos_scores, k=3) # 上位3件

print(f"\nQuery: {query}")
print("Most similar images:")
for score, idx in zip(top_results[0], top_results[1]):
    print(f"- {img_names[idx]} (Score: {score:.4f})")

# --- 画像による画像検索 ---
# 例: 最初の画像をクエリとして使用
query_image_path = img_names[0]
query_image_embedding = img_embeddings[0]

# 他の画像との類似度を計算
cos_scores_img = util.pytorch_cos_sim(query_image_embedding, img_embeddings)[0]
top_results_img = torch.topk(cos_scores_img, k=4) # 自分自身も含まれるのでk=4などにする

print(f"\nQuery Image: {query_image_path}")
print("Most similar images (including self):")
for score, idx in zip(top_results_img[0], top_results_img[1]):
    if idx != 0: # 自分自身を除外する場合
         print(f"- {img_names[idx]} (Score: {score:.4f})")

注意: 上記コードを実行するには、PIL (Pillow) ライブラリが必要です (pip install Pillow)。また、画像ファイルへのパスは適切に設定してください。

Sentence-Transformersは、文の意味を捉えたベクトル表現(埋め込み)を簡単に生成・利用できる強力なPythonライブラリです。豊富な事前学習済みモデル、セマンティック検索、類似度計算、クラスタリング、ファインチューニング、Cross-Encoderによる高精度スコアリング、さらにはマルチモーダルな機能まで、幅広いNLPタスクに対応可能です。

今後の展望としては、以下のような点が期待されます。

  • より高性能・高効率なモデルの登場: モデルアーキテクチャや学習方法の改善により、さらに精度が高く、計算コストの低いモデルが登場するでしょう。
  • ドメイン特化モデルの充実: 医療、法律、金融など、特定の専門分野に特化した事前学習済みモデルやファインチューニング手法の開発が進むと考えられます。
  • マルチモーダル能力の向上: テキストと画像だけでなく、音声や動画など、より多様なモダリティを統合的に扱うモデルの研究開発が進むでしょう。
  • 大規模言語モデル(LLM)との連携強化: RAG (Retrieval-Augmented Generation) の文脈で、Sentence-Transformersによる高精度な情報検索が、LLMの回答生成能力を補完する役割はますます重要になります。
  • 効率的なファインチューニング手法: PEFTなどの技術がさらに発展し、より少ないデータや計算資源で効果的なファインチューニングが可能になることが期待されます。

Sentence-Transformersは、自然言語処理の応用を加速させるための重要なツールであり、今後もその進化から目が離せません。ぜひ、このライブラリを活用して、あなたのプロジェクトや研究に役立ててみてください!💪

コメント

タイトルとURLをコピーしました