LESSON 30分

Sentence Transformersによる文埋め込み

「Word2Vecは単語レベルの表現だった。でも我々が必要なのは、問い合わせ文全体の意味を捉えたベクトルだ。」

田中VPoEが2つの問い合わせを並べる。

「『荷物が届きません』と『注文した商品がまだ来ていない』は同じ意味だ。Sentence Transformersならこれを正しく近いベクトルにマッピングできる。」

Sentence Transformersとは

文全体を1つの密なベクトル(通常384〜768次元)に変換するモデル。BERTをベースに、文のペアで学習することで高品質な文埋め込みを実現する。

Word2Vecの平均プーリング vs Sentence Transformers

# Word2Vecの平均プーリング: 単語ベクトルの平均
# → 「犬が猫を追う」と「猫が犬を追う」が同じベクトルに
# → 文全体の意味を適切に表現できない

# Sentence Transformers: 文全体を直接ベクトル化
# → 文の構造と意味を考慮した表現
# → 類似文は近いベクトル、異なる文は遠いベクトル

基本的な使い方

from sentence_transformers import SentenceTransformer
import numpy as np

# モデルのロード
model = SentenceTransformer('all-MiniLM-L6-v2')  # 英語用
# model = SentenceTransformer('paraphrase-multilingual-MiniLM-L12-v2')  # 多言語対応

# 文埋め込みの生成
sentences = [
    "My order hasn't arrived yet",
    "The package I ordered still hasn't come",
    "I want to return this product",
    "How do I get a refund",
]

embeddings = model.encode(sentences)
print(f"埋め込みの形状: {embeddings.shape}")
# 出力: 埋め込みの形状: (4, 384)

# コサイン類似度の計算
from sklearn.metrics.pairwise import cosine_similarity

sim_matrix = cosine_similarity(embeddings)
print("類似度行列:")
for i, s1 in enumerate(sentences):
    for j, s2 in enumerate(sentences):
        if i < j:
            print(f"  '{s1[:30]}' vs '{s2[:30]}': {sim_matrix[i][j]:.4f}")

カスタマーサポートへの応用

類似問い合わせ検索

from sentence_transformers import SentenceTransformer, util
import torch

model = SentenceTransformer('all-MiniLM-L6-v2')

# 過去の問い合わせデータベース
knowledge_base = [
    {"text": "Order not delivered after 5 days", "category": "配送", "response": "配送状況を確認いたします..."},
    {"text": "Product arrived damaged", "category": "クレーム", "response": "大変申し訳ございません..."},
    {"text": "How to return an item", "category": "返品", "response": "返品手続きの流れをご案内します..."},
    {"text": "Wrong item received", "category": "返品", "response": "誤配送のお詫びと交換手続き..."},
    {"text": "Payment charged twice", "category": "決済", "response": "二重請求の確認と返金処理..."},
]

# ナレッジベースのベクトル化(事前計算)
kb_texts = [item["text"] for item in knowledge_base]
kb_embeddings = model.encode(kb_texts, convert_to_tensor=True)

def find_similar_inquiries(query, top_k=3):
    """類似する過去の問い合わせを検索"""
    query_embedding = model.encode(query, convert_to_tensor=True)
    scores = util.cos_sim(query_embedding, kb_embeddings)[0]
    top_indices = torch.topk(scores, k=top_k).indices

    results = []
    for idx in top_indices:
        results.append({
            "text": knowledge_base[idx]["text"],
            "category": knowledge_base[idx]["category"],
            "response": knowledge_base[idx]["response"],
            "similarity": scores[idx].item(),
        })
    return results

# 検索例
query = "My package hasn't been delivered"
results = find_similar_inquiries(query)
for r in results:
    print(f"  類似度: {r['similarity']:.4f} | カテゴリ: {r['category']} | {r['text']}")

バッチ処理による高速化

# 大量のテキストを効率的にベクトル化
import time

texts = ["Sample inquiry " + str(i) for i in range(1000)]

start = time.time()
embeddings = model.encode(
    texts,
    batch_size=64,           # バッチサイズ
    show_progress_bar=True,  # 進捗表示
    normalize_embeddings=True,  # L2正規化(コサイン類似度の高速化)
)
elapsed = time.time() - start
print(f"1000件のベクトル化: {elapsed:.2f}秒")

特徴抽出手法の比較

from sklearn.feature_extraction.text import TfidfVectorizer
from sentence_transformers import SentenceTransformer
import numpy as np

# テストデータ
pairs = [
    ("My order hasn't arrived", "The package I ordered is missing"),
    ("I want to return this", "How do I send this product back"),
    ("My order hasn't arrived", "I want to return this"),
]

# TF-IDF
tfidf = TfidfVectorizer()
all_texts = [t for pair in pairs for t in pair]
tfidf_vecs = tfidf.fit_transform(all_texts)

# Sentence Transformers
st_model = SentenceTransformer('all-MiniLM-L6-v2')
st_vecs = st_model.encode(all_texts)

print("手法別の類似度比較:")
for i, (t1, t2) in enumerate(pairs):
    idx1, idx2 = i * 2, i * 2 + 1

    # TF-IDF類似度
    tfidf_sim = cosine_similarity(
        tfidf_vecs[idx1:idx1+1], tfidf_vecs[idx2:idx2+1]
    )[0][0]

    # Sentence Transformers類似度
    st_sim = cosine_similarity(
        st_vecs[idx1:idx1+1], st_vecs[idx2:idx2+1]
    )[0][0]

    print(f"\n  '{t1}' vs '{t2}'")
    print(f"    TF-IDF: {tfidf_sim:.4f}")
    print(f"    Sentence Transformers: {st_sim:.4f}")

# Sentence Transformersは意味的な類似性をより正確に捉える

手法選定ガイド

手法精度速度メモリ適用場面
TF-IDF最速ベースライン、大規模データ
Word2Vec平均中〜高速い単語の意味を考慮したい場合
Sentence Transformers最高意味的類似性が重要な場合

まとめ

項目ポイント
Sentence Transformers文全体を1つのベクトルに変換
利点意味的な類似性を正確に捉える
応用類似問い合わせ検索、自動分類
選定基準精度重視ならST、速度重視ならTF-IDF

チェックリスト

  • Sentence Transformersの仕組みを説明できる
  • Word2Vecの平均プーリングとの違いを理解した
  • 類似検索の実装方法を理解した
  • TF-IDF、Word2Vec、Sentence Transformersの使い分けができる

次のステップへ

テキストの特徴抽出手法を一通り学んだところで、次の演習でこれらの手法をKaggleデータセットに適用してみよう。

推定読了時間: 30分