目次
文書類似度計算で直面する課題
日本語の文書検索や推薦システムを構築する際、以下のような問題に直面していませんか?
1. 精度の問題
- 単語レベルの一致だけでは文書の意味的類似度を正確に測れない
- 同じ意味でも表現が異なる文書を関連文書として発見できない
- 従来のTF-IDFやBM25では意味的な類似度が取得できない
2. 計算速度の問題
- BERTモデルで毎回文書をエンコードすると時間がかかりすぎる
- 大量の文書との類似度計算がリアルタイムで実行できない
- 文書検索のレスポンス時間が数秒〜数十秒かかる
3. 日本語対応の問題
- 英語向けのモデルでは日本語の意味的類似度が正確に取得できない
- 日本語特有の表現や文法構造に対応していない
- カスタムモデルの構築が困難
実際に遭遇した文書類似度の課題事例
失敗事例:従来手法での限界
# TF-IDFによる類似度計算の例
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.metrics.pairwise import cosine_similarity
# 以下のような文書では類似度が正しく計算されない
doc1 = "機械学習の精度を向上させる方法"
doc2 = "AIモデルの性能を改善する手法"
# 結果: 低い類似度(単語が異なるため)
# BERTの直接利用も計算コストが高い
# 毎回エンコードが必要で、大量データに不向き
この問題を解決するのがSentence BERTです。
Sentence BERTによる問題解決アプローチ
特徴1:高精度な意味的類似度計算
- 文脈を考慮した文書の分散表現を生成
- 単語レベルではなく文書レベルでの意味理解
- 表現が異なる同義文書の正確な類似度測定
特徴2:高速な類似度計算
- 事前計算した分散表現による高速検索
- リアルタイムでの類似文書発見
- 大量文書での高速類似度計算
特徴3:日本語への最適化
- 日本語BERTモデルをベースとした学習
- 日本語特有の表現に対応
- カスタムデータセットでの追加学習可能
解決策の具体的な実装
1. 環境構築
pip install sentence-transformers
pip install transformers fugashi ipadic
2. 日本語Sentence BERTモデルの利用
from transformers import BertJapaneseTokenizer, BertModel
import torch
class SentenceBertJapanese:
def __init__(self, model_name_or_path, device=None):
self.tokenizer = BertJapaneseTokenizer.from_pretrained(model_name_or_path)
self.model = BertModel.from_pretrained(model_name_or_path)
self.model.eval()
if device is None:
device = "cuda" if torch.cuda.is_available() else "cpu"
self.device = torch.device(device)
self.model.to(device)
def _mean_pooling(self, model_output, attention_mask):
token_embeddings = model_output[0]
input_mask_expanded = attention_mask.unsqueeze(-1).expand(token_embeddings.size()).float()
return torch.sum(token_embeddings * input_mask_expanded, 1) / torch.clamp(input_mask_expanded.sum(1), min=1e-9)
@torch.no_grad()
def encode(self, sentences, batch_size=8):
all_embeddings = []
iterator = range(0, len(sentences), batch_size)
for batch_idx in iterator:
batch = sentences[batch_idx:batch_idx + batch_size]
encoded_input = self.tokenizer.batch_encode_plus(batch, padding="longest",
truncation=True, return_tensors="pt").to(self.device)
model_output = self.model(**encoded_input)
sentence_embeddings = self._mean_pooling(model_output, encoded_input["attention_mask"]).to('cpu')
all_embeddings.extend(sentence_embeddings)
return torch.stack(all_embeddings)
# 使用例
MODEL_NAME = "sonoisa/sentence-bert-base-ja-mean-tokens-v2"
model = SentenceBertJapanese(MODEL_NAME)
3. 文書類似度の計算
from sklearn.metrics.pairwise import cosine_similarity
import numpy as np
# 文書例
documents = [
"機械学習の精度を向上させる方法",
"AIモデルの性能を改善する手法",
"深層学習の最適化技術",
"今日の天気は晴れです"
]
# 分散表現の取得
embeddings = model.encode(documents)
# 類似度行列の計算
similarity_matrix = cosine_similarity(embeddings)
print("類似度行列:")
print(similarity_matrix)
# 結果: doc1とdoc2は高い類似度を示す
4. 高速文書検索システム
class DocumentSearchEngine:
def __init__(self, model):
self.model = model
self.document_embeddings = None
self.documents = None
def index_documents(self, documents):
"""文書の事前エンコード(一度だけ実行)"""
self.documents = documents
self.document_embeddings = self.model.encode(documents)
def search(self, query, top_k=5):
"""クエリに類似した文書を高速検索"""
query_embedding = self.model.encode([query])
similarities = cosine_similarity(query_embedding, self.document_embeddings)[0]
# 上位k件を取得
top_indices = np.argsort(similarities)[::-1][:top_k]
results = []
for idx in top_indices:
results.append({
'document': self.documents[idx],
'similarity': similarities[idx]
})
return results
# 使用例
search_engine = DocumentSearchEngine(model)
search_engine.index_documents(large_document_corpus)
# 高速検索(ミリ秒オーダー)
results = search_engine.search("機械学習の性能向上", top_k=3)
問題解決の具体的な効果
Before(従来手法)
- 精度: 表面的な単語一致のみ(類似度50-60%)
- 計算速度: 毎回BERTエンコード(数秒/クエリ)
- 日本語対応: 英語モデルによる不正確な結果
After(Sentence BERT導入後)
- 精度: 意味的類似度による高精度(類似度80-90%以上)
- 計算速度: 事前計算による高速検索(ミリ秒オーダー)
- 日本語対応: 日本語特化モデルによる正確な結果
実践的な応用例
1. 文書推薦システム
def recommend_documents(user_document, document_corpus, top_k=5):
search_engine = DocumentSearchEngine(model)
search_engine.index_documents(document_corpus)
return search_engine.search(user_document, top_k)
2. 重複文書検出
def detect_duplicates(documents, threshold=0.8):
embeddings = model.encode(documents)
similarity_matrix = cosine_similarity(embeddings)
duplicates = []
for i in range(len(documents)):
for j in range(i+1, len(documents)):
if similarity_matrix[i][j] > threshold:
duplicates.append((i, j, similarity_matrix[i][j]))
return duplicates
3. カスタムモデルの構築
from sentence_transformers import SentenceTransformer, InputExample, losses
from torch.utils.data import DataLoader
# 独自データでの追加学習
train_examples = []
for index, row in custom_df.iterrows():
label = row['category_id']
train_examples.append(InputExample(texts=[row.text], label=float(label)))
# 学習の実行
train_dataloader = DataLoader(train_examples, batch_size=16, shuffle=True)
train_loss = losses.BatchAllTripletLoss(model)
model.fit(train_objectives=[(train_dataloader, train_loss)], epochs=30)
定量的な改善効果
検索精度の改善
- 従来手法(TF-IDF): 平均精度60-70%
- Sentence BERT: 平均精度85-95%
- 意味的類似文書の発見率: 300%向上
計算速度の改善
- 従来手法(リアルタイムBERT): 2-5秒/クエリ
- Sentence BERT(事前計算): 10-50ミリ秒/クエリ
- 処理速度: 100-500倍高速化
開発効率の改善
- 実装時間: 複雑な特徴量エンジニアリング不要
- 保守性: シンプルなAPIによる高い保守性
- スケーラビリティ: 大量文書への対応が容易
まとめ
日本語文書の意味的類似度計算の課題は、Sentence BERTを活用することで以下のように解決できます:
- 精度問題 → 文脈を考慮した意味的類似度で高精度を実現
- 速度問題 → 事前計算による高速検索でリアルタイム対応
- 日本語対応問題 → 日本語特化モデルで正確な結果を取得
特に、文書検索・推薦システム・重複検出など、意味的類似度が重要な用途で大幅な性能向上を実現できます。
従来の表面的な単語一致から、深層学習による意味理解へのパラダイムシフトにより、より知的な文書処理システムの構築が可能になります。
宣伝
「こんなことAIで自動化できないかな?」といった、ふわっとした段階からのご相談も大歓迎です。
▼具体的なご相談・開発依頼はこちらから