Sentence Transformers 2.2 : 訓練 : 概要

Sentence Transformers 2.2 : 訓練 : 概要 (翻訳/解説)

翻訳 : (株)クラスキャット セールスインフォメーション
作成日時 : 12/04/2022 (v2.2.2)

* 本ページは、UKPLab/sentence-transformers の以下のドキュメントを翻訳した上で適宜、補足説明したものです:

* サンプルコードの動作確認はしておりますが、必要な場合には適宜、追加改変しています。
* ご自由にリンクを張って頂いてかまいませんが、sales-info@classcat.com までご一報いただけると嬉しいです。

 

クラスキャット 人工知能 研究開発支援サービス

クラスキャット は人工知能・テレワークに関する各種サービスを提供しています。お気軽にご相談ください :

◆ 人工知能とビジネスをテーマに WEB セミナーを定期的に開催しています。スケジュール
  • お住まいの地域に関係なく Web ブラウザからご参加頂けます。事前登録 が必要ですのでご注意ください。

お問合せ : 本件に関するお問い合わせ先は下記までお願いいたします。

  • 株式会社クラスキャット セールス・マーケティング本部 セールス・インフォメーション
  • sales-info@classcat.com  ;  Web: www.classcat.com  ;   ClassCatJP

 

 

Sentence Transformers 2.2 : 訓練 : 概要

各タスクは特有で、その特定のタスクに対してセンテンス / テキスト埋め込みを調整することはパフォーマンスを大きく改善します。

SentenceTransformers は貴方自身のセンテンス / テキスト埋め込みモデルを微調整することを簡単にするように設計されました。それは、特定のタスクに対して埋め込みを調整するために組み合わせることができる殆どのビルディングブロックを提供しています。

残念ながらすべてのユースケースに対して動作する単一の訓練ストラテジーはありません。代わりに、どの訓練ストラテジーを使用するかは利用可能なデータとターゲットタスクに大きく依存します。

訓練 セクションでは、貴方自身の埋め込みモデルを SentenceTransformers により訓練する基本を説明します。訓練サンプル セクションでは、一般的な実世界のアプリケーションに対して埋め込みモデルを調整する方法のサンプルを提供します。

 

ネットワーク・アーキテクチャ

センテンス / テキスト埋め込みについて、私たちは可変長の入力テキストを固定サイズの埋め込みベクトルにマップしたいです。使用できる最も基本的なネットワーク・アーキテクチャは以下になります :

入力センテンスやテキストを BERT のような transformer ネットワークに供給します。BERT はテキストのすべての入力トークンに対してコンテキスト (文脈) に応じた (contextualized) 単語埋め込みを生成します。固定サイズの出力表現 (ベクトル u) を望むので、プーリング層が必要です。様々なプーリングオプションが利用可能ですが、最も基本的なものは平均プーリングです : BERT が与えたすべての contextualized 単語埋め込みを単純に平均化します。これは入力テキストの長さとは独立に 768 次元の出力ベクトルを与えてくれます。

BERT 層とプーリング層から成る、描写されたアーキテクチャは一つの最終的な SentenceTransformer です。

 

ゼロからネットワークを作成する

クイックスタート & 使用方法のサンプルで、BERT 層とプーリング層を既に装備した、事前訓練済み SentenceTransformer モデルを使用しました。

しかし個々の層を定義することによりネットワークアーキテクチャをゼロから作成できます。例えば、以下のコードは描写されたネットワークアーキテクチャを作成します :

from sentence_transformers import SentenceTransformer, models

word_embedding_model = models.Transformer('bert-base-uncased', max_seq_length=256)
pooling_model = models.Pooling(word_embedding_model.get_word_embedding_dimension())

model = SentenceTransformer(modules=[word_embedding_model, pooling_model])

最初に個々の層を定義します、このケースでは、word_embedding_model として ‘bert-base-uncased’ を定義します。その層を 256 の最大シークエンス長に制限します、それよりも長いテキストは切り捨てられます。更に、(平均) プーリング層を作成します。SentenceTransformer(modules=[word_embedding_model, pooling_model]) を呼び出して新しい SentenceTransformer モデルを作成します。modules パラメータについては、連続的に実行される層のリストを渡します。入力テキストはまず最初のエントリ (word_embedding_model) に渡されます。次にその出力は 2 番目のエントリ (pooling_model) に渡されます、これはセンテンス埋め込みを返します。

より複雑なモデルを構築することもできます :

from sentence_transformers import SentenceTransformer, models
from torch import nn

word_embedding_model = models.Transformer('bert-base-uncased', max_seq_length=256)
pooling_model = models.Pooling(word_embedding_model.get_word_embedding_dimension())
dense_model = models.Dense(in_features=pooling_model.get_sentence_embedding_dimension(), out_features=256, activation_function=nn.Tanh())

model = SentenceTransformer(modules=[word_embedding_model, pooling_model, dense_model])

ここでは、プーリング層の上に Tanh 活性を持つ全結合 dense 層を追加します、これは 256 次元への次元削減 (down-project) を行ないます。従って、このモデルによる埋め込みは 768 次元の代わりに 256 (次元) だけを持ちます。

すべての利用可能なビルディングブロックについては » Models パッケージ Reference をご覧ください。

 

訓練データ

訓練データを表現するため、訓練サンプルをストアする InputExample クラスを使用します。パラメータとして、それはテキストを取ります、これはペア (or トリプレット) を表す文字列のリストです。更に、ラベル (float か int のいずれか) を渡すこともできます。以下は単純なサンプルを示します、そこでは意味的類以度を示すラベルとともにテキストペアを InputExample に渡します。

from sentence_transformers import SentenceTransformer, InputExample
from torch.utils.data import DataLoader

model = SentenceTransformer('distilbert-base-nli-mean-tokens')
train_examples = [InputExample(texts=['My first sentence', 'My second sentence'], label=0.8),
   InputExample(texts=['Another pair', 'Unrelated sentence'], label=0.3)]
train_dataloader = DataLoader(train_examples, shuffle=True, batch_size=16)

train_examples を標準的な PyTorch DataLoader でラップします、これはデータをシャッフルして特定のサイズのバッチを生成します。

 

損失関数

モデルを微調整するとき、損失関数は重要な役割を果たします。それは、埋め込みモデルが特定の下流タスクに対してどのくらい上手く機能するかを決定します。

残念なことに、「汎用的な (“one size fits all”)」損失関数は存在しません。どの損失関数が適切であるかは、利用可能な訓練データとターゲットタスクに依存します。

ネットワークを微調整するためには、どのセンテンスペアが類似していてベクトル空間で近接するべきか、そしてどのペアが似ていなくてベクトル空間で遠くに離されるべきかを、何らかの形でネットワークに伝える必要があります。

最も単純な方法は、センテンスペアをそれらの類似度を示すスコア, e.g. スケール 0 から 1 でアノテートすることです。それから Siamese ネットワークアーキテクチャを持つネットワークを訓練します (詳細は次を参照 : Sentence-BERT: Sentence Embeddings using Siamese BERT-Networks (Sentence-BERT: Siamese BERT-Networks を使用したセンテンス埋め込み))。

各センテンスペアについて、ネットワークにセンテンス A と B を渡します、それは埋め込み u と v を生成します。これらの埋め込みの類以度はコサイン類以度を使用して計算され、その結果は gold (正解) 類以度スコアと比較されます。これはネットワークが微調整されてセンテンスの類以度を認識することを可能にします。

CosineSimilarityLoss の最小限のサンプルは以下です :

from sentence_transformers import SentenceTransformer, InputExample, losses
from torch.utils.data import DataLoader

#Define the model. Either from scratch of by loading a pre-trained model
model = SentenceTransformer('distilbert-base-nli-mean-tokens')

#Define your train examples. You need more than just two examples...
train_examples = [InputExample(texts=['My first sentence', 'My second sentence'], label=0.8),
    InputExample(texts=['Another pair', 'Unrelated sentence'], label=0.3)]

#Define your train dataset, the dataloader and the train loss
train_dataloader = DataLoader(train_examples, shuffle=True, batch_size=16)
train_loss = losses.CosineSimilarityLoss(model)

#Tune the model
model.fit(train_objectives=[(train_dataloader, train_loss)], epochs=1, warmup_steps=100)

model.fit() を呼び出すことでモデルを調整します。train_objectives のリストを渡します、これはタプル (dataloader, loss_function) から成ります。様々な損失関数による幾つかのデータセットでマルチタスクを実行するために、一つ以上のタプルを渡すことができます。

fit メソッドは以下のパラメータを受け取ります :

class sentence_transformers.SentenceTransformer

 

評価器

訓練中、パフォーマンスが向上しているかを見るために通常はパフォーマンスを測定することを望みます。このため、sentence_transformers.evaluation パッケージが存在します。それは様々な評価器を含みます、これを fit メソッドに渡すことができます。これらの評価器は訓練の間に定期的に実行されます。更に、それらはスコアを返し、最も高いスコアを持つモデルだけがディスク上にストアされます。

The usage is simple:

from sentence_transformers import evaluation
sentences1 = ['This list contains the first column', 'With your sentences', 'You want your model to evaluate on']
sentences2 = ['Sentences contains the other column', 'The evaluator matches sentences1[i] with sentences2[i]', 'Compute the cosine similarity and compares it to scores[i]']
scores = [0.3, 0.6, 0.2]

evaluator = evaluation.EmbeddingSimilarityEvaluator(sentences1, sentences2, scores)

# ... Your other code to load training data

model.fit(train_objectives=[(train_dataloader, train_loss)], epochs=1, warmup_steps=100, evaluator=evaluator, evaluation_steps=500)

 

他のデータで訓練を継続する

training_stsbenchmark_continue_training.py は微調整されたモデル上で訓練が継続されるサンプルを示します。その例では、NLI データセットでまず微調整された sentence transformer を使用してから、STS ベンチマークからの訓練データで訓練を継続します。

First, we load a pre-trained model from the server:

model = SentenceTransformer('bert-base-nli-mean-tokens')

The next steps are as before. We specify training and dev data:

train_dataloader = DataLoader(train_samples, shuffle=True, batch_size=train_batch_size)
train_loss = losses.CosineSimilarityLoss(model=model)

evaluator = EmbeddingSimilarityEvaluator.from_input_examples(sts_reader.get_examples('sts-dev.csv'))

その例では、CosineSimilarityLoss を使用します、これは 2 つのセンテンス間のコサイン類以度を計算して、このスコアを提供された正解類以度スコアと比較します。

Then we can train as before:

model.fit(train_objectives=[(train_dataloader, train_loss)],
          evaluator=evaluator,
          epochs=num_epochs,
          evaluation_steps=1000,
          warmup_steps=warmup_steps,
          output_path=model_save_path)

 

カスタム SentenceTransformer モデルのロード

訓練済みモデルのロードは簡単です。パスを指定できます :

model = SentenceTransformer('./my/path/to/model/')

Note : It is important that a / or \ is present in the path, otherwise, it is not recognized as a path.

You can also host the training output on a server and download it:

model = SentenceTransformer('http://www.server.com/path/to/model/my_model.zip')

最初の呼び出しで、モデルはダウンロードされてローカルの torch キャッシュフォルダにストアされます (~/.cache/torch/sentence_transformers)。動作させるには、モデルのすべてのファイルとサブフォルダを zip する必要があります。

 

マルチタスク訓練

このコードは異なるデータセットからの訓練データと異なる損失関数によるマルチタスク学習を可能にします。例えば、training_multi-task.py をご覧ください。

 

特殊トークンの追加

タスクに依存して、特殊トークンをトークナイザーと Transformer モデルに追加することを望むかもしれません。

これを実現するために以下のコードスニペットを使用できます :

from sentence_transformers import SentenceTransformer, models
word_embedding_model = models.Transformer('bert-base-uncased')

tokens = ["[DOC]", "[QRY]"]
word_embedding_model.tokenizer.add_tokens(tokens, special_tokens=True)
word_embedding_model.auto_model.resize_token_embeddings(len(word_embedding_model.tokenizer))

pooling_model = models.Pooling(word_embedding_model.get_word_embedding_dimension())
model = SentenceTransformer(modules=[word_embedding_model, pooling_model])

既存の SentenceTransformer モデルに対して語彙を拡張したい場合には、以下のコードを使用できます :

from sentence_transformers import SentenceTransformer, models
model = SentenceTransformer('all-MiniLM-L6-v2')
word_embedding_model = model._first_module()

tokens = ["[DOC]", "[QRY]"]
word_embedding_model.tokenizer.add_tokens(tokens, special_tokens=True)
word_embedding_model.auto_model.resize_token_embeddings(len(word_embedding_model.tokenizer))

上の例では、2 つの新しいトークン [DOC] と [QRY] がモデルに追加されています。それぞれの単語埋め込みはランダムに初期化されます。それから下流タスクでモデルを微調整することが望ましいです。

 

ベストな Transformer モデル

テキスト埋め込みモデルの品質は選択する transformer モデルに依存します。残念ながら、例えば GLUE or SuperGLUE ベンチマークでの良いパフォーマンスから、このモデルがより良い表現も生成するとは推論できません。

transformer モデルの適合性をテストするために、training_nli_v2.py スクリプトを使用して 560k の (anchor, positive, negative)-トリプレット上でバッチサイズ 64 で 1 エポック訓練しました。それから様々なドメインからの 14 の多様なテキスト類似性タスク (クラスタリング、意味検索、重複検出等) で評価しました。

以下のテーブルで様々なモデルに対するパフォーマンスとこのベンチマーク上でのそれらのパフォーマンスを見ることができます :

 

以上