[技術日誌]モデル別で文章の類似度を判定する

8/9分の技術日誌

モデル別で文章の類似度を判定する

環境構築

Google Colabratryを使用してるので、環境構築は不要です。

SentenceTransformers

Sentence Transformers (別名 SBERT) は、最先端のテキストおよび画像埋め込みモデルにアクセスし、使用し、トレーニングするための Python モジュールです。Sentence Transformer モデル (クイックスタート) を使用して埋め込みを計算したり、Cross-Encoder モデル (クイックスタート)を使用して類似度スコアを計算したりするために使用できます。これにより、セマンティック検索セマンティックテキスト類似性言い換えマイニングなど、幅広いアプリケーションが可能になります。

🤗 Hugging Face では、 Massive Text Embeddings Benchmark (MTEB) リーダーボードの最先端のモデルを多数含む、5,000 を超える事前トレーニング済みの Sentence Transformers モデルの幅広い選択肢をすぐに使用できます。さらに、Sentence Transformers を使用して独自のモデルをトレーニングまたは微調整することも簡単で、特定のユースケースに合わせてカスタム モデルを作成できます。

下記のコマンドでライブラリをインストールします。

!pip install sentence-transformers

データセットの準備

モデルによっては、全く違う文章でも類似度の最大値が1として0.8を超える数値を算出するものもあります。
全く違う文章の類似度は低く、似た文章の類似度は高く判定するように、数値を差をつけて推定してくれるモデルを選定する必要があります。
よって、文章の類似度算出を検証するにあたって、類似度が異なる文章を用意する必要があります。

本記事では、Yahoo! JAPAN研究所の日本語言語理解ベンチマークJGLUEを用います。
2つの文章の類似度を5段階評価した、ラベル付きのデータです。

JSTS(Japanese Semantic Textual Similarity)とJNLI(Japanese Natural Language Inference)はともに2つの文が与えられ、JSTSは2文間の類似度(0から5までの値をとり、5が最も類似しています)を、JNLIは含意(entailment)、矛盾 (contradiction)、中立(neutral)のいずれの推論関係かを推定するタスクです。文ペアはヤフーが公開しているYJ Captions Datasetを用い、類似度ならびに推論関係をクラウドソーシングで付与しました。

https://techblog.yahoo.co.jp/entry/2022122030379907/

下記のサイトを参考に、0.0~1.0で0.1刻みで文章のサンプル抽出します。

import pandas as pd

# データセットの準備
# https://qiita.com/masa_yam/items/4cb31ccb0be6e86af404
# https://github.com/yahoojapan/JGLUE/blob/main/datasets/jsts-v1.1/valid-v1.1.json
df_json = pd.read_json('https://raw.githubusercontent.com/yahoojapan/JGLUE/main/datasets/jsts-v1.1/valid-v1.1.json', lines=True)
df_json.head()
import numpy as np

"""
サンプルを抽出
今回は、label を 1.0 刻みで分割してその中から1件ずつデータを取ります。
後で比較しやすいように label は 0.0 ~ 1.0 の範囲に変換します。
"""

seed = 42
offset = 1.0

df_sample = df_json[df_json['label']==0.0].sample(random_state=seed)
for i in np.arange(0.0, 5.0, offset):
    if i==0.0:
        df_sample = pd.concat([df_sample, df_json.query(f'{i} < label < {i+offset}').sample(random_state=seed)])
    else:
        df_sample = pd.concat([df_sample, df_json.query(f'{i} <= label < {i+offset}').sample(random_state=seed)])
df_sample = pd.concat([df_sample, df_json[df_json['label']==5.0].sample(random_state=seed)])
# まったく同じ文章のペアのものも入れておく
df_sample = pd.concat([df_sample, df_json[(df_json['sentence1']==df_json['sentence2']) & (df_json['label']==5.0)].sample(random_state=seed)])
df_sample.drop(['sentence_pair_id', 'yjcaptions_id'], axis=1, inplace=True)
df_sample.reset_index(drop=True, inplace=True)

# 比較しやすいように label を [0, 1] の区間に変換する
df_sample['label'] = df_sample['label'].apply(lambda x: x/5.0)
df_sample = df_sample[1:-1].reset_index(drop=True)
df_sample

類似度の算出

import math
import pandas as pd

import matplotlib
import matplotlib.pyplot as plt

import seaborn as sns
sns.set()

from sentence_transformers import SentenceTransformer, util


# 文章類似度モデル
# 下記のリンクからいくつかピックアップ
# https://www.sbert.net/docs/sentence_transformer/pretrained_models.html
# https://huggingface.co/pkshatech/GLuCoSE-base-ja
model_name_list = ['all-MiniLM-L12-v2',
                    'all-MiniLM-L6-v2',
                    'all-distilroberta-v1',
                    'all-mpnet-base-v2',
                    'distiluse-base-multilingual-cased-v1',
                    'distiluse-base-multilingual-cased-v2',
                    'intfloat/multilingual-e5-large',
                    'multi-qa-MiniLM-L6-cos-v1',
                    'multi-qa-distilbert-cos-v1',
                    'multi-qa-mpnet-base-dot-v1',
                    'paraphrase-MiniLM-L12-v2',
                    'paraphrase-MiniLM-L3-v2',
                    'paraphrase-MiniLM-L6-v2',
                    'paraphrase-albert-small-v2',
                    'paraphrase-mpnet-base-v2',
                    'paraphrase-multilingual-MiniLM-L12-v2',
                    'paraphrase-multilingual-mpnet-base-v2',
                    'paraphrase-xlm-r-multilingual-v1',
                    'pkshatech/GLuCoSE-base-ja',
                    'stsb-xlm-r-multilingual']

for model_name in model_name_list:
    # 文章類似度モデル
    model_sentence = SentenceTransformer(model_name)

    # 相関・コサイン類似度を格納するリスト
    cosine_score_list = []
    # コサイン類似度を計算
    for i in range(len(df_sample)):
        # 行のデータを取得
        sentence1 = df_sample["sentence1"][i]
        sentence2 = df_sample["sentence2"][i]
        # 文章をベクトルに変換
        embeddings1 = model_sentence.encode(sentence1, convert_to_tensor=True)
        embeddings2 = model_sentence.encode(sentence2, convert_to_tensor=True)

        # コサイン類似度の計算
        cosine_score = util.pytorch_cos_sim(embeddings1, embeddings2)[0][0]
        cosine_score = round(float(cosine_score), 4)
        cosine_score_list.append(cosine_score)

    # データフレームに整形
    df_sample[model_name] = cosine_score_list

# 類似度が高いほど青色が濃ゆくなる
display(df_sample.style.background_gradient(axis=None))

ざっとまとめ

下記のモデルがマシそう。
・multi-qa-mpnet-base-dot-v1
・pkshatech/GLuCoSE-base-ja

この記事が気に入ったらサポートをしてみませんか?