[Basic NLP]sentent-Transformersライブラリを使用してSBERTを学習する方法
Intro
この記事は前の文章で紹介したSENTCEBERTモデルの微調整方法についての記事です.
まずSENTCEBERTを学習するデータセット(STS,NLI)を簡単に紹介し,STS単一データによるfinetunningとNLI学習モデルにSTSを追加するcontinue学習方法と結果を論文と文−変換器式を基準として紹介する.
本明細書で使用する実験室実践コード SBERT-only-STS-training SBERT-continue-learning-by-Softmaxloss-NLI SBERT-continue-learning-by- MNRloss-NLI 1.SBERT学習データ
SBERTを学習するためのデータには、文間の類似度を測定するためのSTSデータセットと、文間の関係を理解するためのNLIデータセットが含まれる.
この2つのデータセットの利用可能なソースは多くないが、現在公信力が使用されているデータセットは、Kaka Brainで公開されているKorNLUと、KLUEプロジェクトで公開されているKLUEデータムセット2種である.種々の開示された言語モデルから,主にこれらのデータセットによってモデルの性能を評価する.
1.1. STS (Semantic Textual Similarity)
STSデータは2つの文対と2つの文の間の類似度スコアからなり,これらのスコアを学習することで文と文の間の類似度を予測できる.KLUE-STSデータセットの例を次に示します.
1.2. NLI (Natural Language Inference)
NLIデータはまた、2つの文が互いに伴う(含む)関係、矛盾(矛盾)関係、中立(中性)関係であることを示すラベル値からなる2つの文対を提供している.
次に、KLUE-NLL Iデータセットの例を示します.「label」値については、数値形式に変換され、0は含まれ、1は中立、2は矛盾を表します.(KornlIデータのラベル値はテキストです.)
2.SBERT学習方法
論文では,SBERTの学習方法は大きく2つあり,1つ目はSTSデータのみで学習する方法,2つ目は最適化されたモデルをNLIデータでSTSに追加して学習する継続学習方法であると考えられる.
2.1. STS単一データによるFine-Tuning
このセクションのcolab練習コードについては、リンクを参照してください.
学習SBERTの最も基本的で最も強力な方法はSTSデータによる微調整であり,回帰目標関数による学習である.
学習方法
1.2つの文のペアを入力
2.各入力シーケンスを予習BERTに従って埋め込みベクトルに変換する
3.変換後の埋め込みベクトルに対してPoyoung演算(通常Mean-pooling)を行い、文埋め込みベクトルに変換する
4.変換された2つの文imbedingベクトルをcosine類似度により2つの文ベクトルの類似度値を算出する(-1~1)
2.1.1. Load Dataset
実習に使用するデータはklue-stsデータセットです.このデータセットは「train」、「validation」のみで構成されますが、「train」の3つのデータセットの10%は検証のためにサンプリングされ、既存の「validation」はテストのために使用されます.
STS fine−tuningのための事前学習言語モデルをロードするプロセスは,Huggingface model hubに開示されているklue/roberta-baseモデルを用いた.
冷却層について,論文実験基準で最も性能の良い平均プールを定義した.
STS学習時のloss関数は
上記で定義したテストベリファイアを用いてモデル性能を評価した結果,約0.88の性能を示した.
このセクションのcolab練習コードについては、リンクを参照してください.
STS単一データを用いて学習を行うほか,NLIを用いて学習を行った後にSTSを用いて追加学習を行う方法もある.
論文では,STS単一データ学習時と比較して,NLI学習後にSTS学習に続く手法が約3〜4点高い性能を示した(下表2参照),この戦略は特にBERTクロスコーディング方式に大きな影響を及ぼしたと考えられる.
まず,NLIを学習するために,論文は基本的にSoftmax objective関数を用いて3つのクラス(含む,矛盾,中性)を学習する.
しかし,文-transformerのNLI学習例によれば,実験結果は,ソフトmax lossを用いた場合と比較して,複数の負のRanking loss(MNRloss)学習により,より優れた性能を示した.
(実際のklueデータセット学習の結果から,MNRlossで学習した場合,性能も若干向上した.
MNR lossはtriple lossに似ており、(アンカー-文、正-文、負-文)フォーマットに従い、学習を導いてアンカーと正の距離を近づけ、アンカーと負の距離を遠くする.
(本練習ではMNR lossも使用します.)
学習方法
1.NLIデータを三元グループ(アンカー、正、負)にまとめる
2.各入力シーケンスは、予め学習されたBERTモデルに従って埋め込みベクトルに変換される
3.変換後の埋め込みベクトルに対してPoyoung演算(通常Mean-pooling)を行い、文埋め込みベクトルに変換する
2.MNR損失を目標とする関数でNLIデータセットを微調整する
3.最適化されたモデルをNLIでロードし、STSデータとしてさらに学習する
4.変換された2つの文imbedingベクトルをcosine類似度により2つの文ベクトルの類似度値を算出する(-1~1)
以下の実践コードは、上述したSTS finetunningに関する内容を省略する.
2.2.1. Load Dataset (NLI)
学習を継続するために、すべてのnli、stsデータがロードされ、nliではトレーニングデータのみがロードされます.
MNRlossによる学習のためにtriple(アンカー文,正文,否定文)形式で調整した後,同様に
NLI微調整のための事前学習言語モデルをロードするプロセスは,Huggingfaceモデルセンターに公開されたklue/roberta-baseモデルを用いた.
冷却層について,論文実験基準で最も性能の良い平均プールを定義した.
前述したように,loss関数では
これはNLIデータセットを通して調整されたモデルにSTSを追加するプロセスである.学習が完了したモデルをロードすると、
上記で定義したテストベリファイアを用いてモデル性能を評価した結果,モデル性能は約0.89であり,単一STS学習時と比較して約1%向上した.
STSのみを学習し、ソフトmax lossのcontinue learning、MNR lossのcontinue learningの3つのケースを利用して実験を行った結果、わずかな差はあるものの、MNR lossを利用したcontinue learning方式の性能が最も優れていることが分かった.
通常、stsパフォーマンス評価のために、上述したkornluデータセットが使用されています.
しかし、kornluデータにとっては、klueよりもデータの数が多いものの、データの複雑さは非常に低く、各種の実際のデータの文埋め込みを目的とすれば、klueデータを利用した方が経験的により品質が良く、性能も良い.
もしそうなら「klueもkornluも勉強していいんじゃないですか?」klueとkornluの類似性の測定基準,すなわちラベルの基準が異なると,かえってモデルに混乱の結果をもたらすと考える人もいるので,経験的には単一データを用いることが望ましい.
この記事は前の文章で紹介したSENTCEBERTモデルの微調整方法についての記事です.
まずSENTCEBERTを学習するデータセット(STS,NLI)を簡単に紹介し,STS単一データによるfinetunningとNLI学習モデルにSTSを追加するcontinue学習方法と結果を論文と文−変換器式を基準として紹介する.
本明細書で使用する実験室実践コード
SBERTを学習するためのデータには、文間の類似度を測定するためのSTSデータセットと、文間の関係を理解するためのNLIデータセットが含まれる.
この2つのデータセットの利用可能なソースは多くないが、現在公信力が使用されているデータセットは、Kaka Brainで公開されているKorNLUと、KLUEプロジェクトで公開されているKLUEデータムセット2種である.種々の開示された言語モデルから,主にこれらのデータセットによってモデルの性能を評価する.
1.1. STS (Semantic Textual Similarity)
STSデータは2つの文対と2つの文の間の類似度スコアからなり,これらのスコアを学習することで文と文の間の類似度を予測できる.KLUE-STSデータセットの例を次に示します.
1.2. NLI (Natural Language Inference)
NLIデータはまた、2つの文が互いに伴う(含む)関係、矛盾(矛盾)関係、中立(中性)関係であることを示すラベル値からなる2つの文対を提供している.
次に、KLUE-NLL Iデータセットの例を示します.「label」値については、数値形式に変換され、0は含まれ、1は中立、2は矛盾を表します.(KornlIデータのラベル値はテキストです.)
2.SBERT学習方法
論文では,SBERTの学習方法は大きく2つあり,1つ目はSTSデータのみで学習する方法,2つ目は最適化されたモデルをNLIデータでSTSに追加して学習する継続学習方法であると考えられる.
2.1. STS単一データによるFine-Tuning
このセクションのcolab練習コードについては、リンクを参照してください.
学習SBERTの最も基本的で最も強力な方法はSTSデータによる微調整であり,回帰目標関数による学習である.
学習方法
1.2つの文のペアを入力
2.各入力シーケンスを予習BERTに従って埋め込みベクトルに変換する
3.変換後の埋め込みベクトルに対してPoyoung演算(通常Mean-pooling)を行い、文埋め込みベクトルに変換する
4.変換された2つの文imbedingベクトルをcosine類似度により2つの文ベクトルの類似度値を算出する(-1~1)
2.1.1. Load Dataset
実習に使用するデータはklue-stsデータセットです.このデータセットは「train」、「validation」のみで構成されますが、「train」の3つのデータセットの10%は検証のためにサンプリングされ、既存の「validation」はテストのために使用されます.
from datasets import load_dataset
# load KLUE-STS Dataset
klue_sts_train = load_dataset("klue", "sts", split='train[:90%]')
klue_sts_valid = load_dataset("klue", "sts", split='train[-10%:]') # train의 10%를 validation set으로 사용
klue_sts_test = load_dataset("klue", "sts", split='validation')
print('Length of Train : ',len(klue_sts_train)) # 10501
print('Length of Valid : ',len(klue_sts_valid)) # 1167
print('Length of Test : ',len(klue_sts_test)) # 519
2.1.2. PreprocessingInputExample()
クラスにより,2つの文対とラベルを組み合わせてモデルが学習できる形式に変換する.from sentence_transformers.readers import InputExample
def make_sts_input_example(dataset):
'''
Transform to InputExample
'''
input_examples = []
for i, data in enumerate(dataset):
sentence1 = data['sentence1']
sentence2 = data['sentence2']
score = (data['labels']['label']) / 5.0 # normalize 0 to 5
input_examples.append(InputExample(texts=[sentence1, sentence2], label=score))
return input_examples
sts_train_examples = make_sts_input_example(klue_sts_train)
sts_valid_examples = make_sts_input_example(klue_sts_valid)
sts_test_examples = make_sts_input_example(klue_sts_test)
学習用trainデータをDataLoader()
にグループ化してバッチ学習を行い、EmbeddingSimilarityEvaluator()
により学習時に使用される検証検証検証検証器とモデル評価用test検証器を作成した.from torch.utils.data import DataLoader
from sentence_transformers.evaluation import EmbeddingSimilarityEvaluator
# Train Dataloader
train_dataloader = DataLoader(
sts_train_examples,
shuffle=True,
batch_size=train_batch_size, # 32 (논문에서는 16)
)
# Evaluator by sts-validation
dev_evaluator = EmbeddingSimilarityEvaluator.from_input_examples(
sts_valid_examples,
name="sts-dev",
)
# Evaluator by sts-test
test_evaluator = EmbeddingSimilarityEvaluator.from_input_examples(
sts_test_examples,
name="sts-test",
)
2.1.3. Load Pretrained ModelSTS fine−tuningのための事前学習言語モデルをロードするプロセスは,Huggingface model hubに開示されているklue/roberta-baseモデルを用いた.
冷却層について,論文実験基準で最も性能の良い平均プールを定義した.
from sentence_transformers import SentenceTransformer, models
# Load Embedding Model
embedding_model = models.Transformer(
model_name_or_path="klue/robert-base",
max_seq_length=256,
do_lower_case=True
)
# Only use Mean Pooling -> Pooling all token embedding vectors of sentence.
pooling_model = models.Pooling(
embedding_model.get_word_embedding_dimension(),
pooling_mode_mean_tokens=True,
pooling_mode_cls_token=False,
pooling_mode_max_tokens=False,
)
model = SentenceTransformer(modules=[embedding_model, pooling_model])
2.1.4. Training by STSSTS学習時のloss関数は
CosineSimilarityLoss()
を用い,論文と同様に4つの期間を設定し,learning-rate warm-upはtrainの10%を設定した.from sentence_transformers import losses
# config
sts_num_epochs = 4
train_batch_size = 32
sts_model_save_path = 'output/training_sts-'+pretrained_model_name.replace("/", "-")+'-'+datetime.now().strftime("%Y-%m-%d_%H-%M-%S")
# Use CosineSimilarityLoss
train_loss = losses.CosineSimilarityLoss(model=model)
# linear learning-rate warmup steps
warmup_steps = math.ceil(len(sts_train_examples) * sts_num_epochs / train_batch_size * 0.1) #10% of train data for warm-up
# Training
model.fit(
train_objectives=[(train_dataloader, train_loss)],
evaluator=dev_evaluator,
epochs=sts_num_epochs,
evaluation_steps=int(len(train_dataloader)*0.1),
warmup_steps=warmup_steps,
output_path=sts_model_save_path
)
2.1.5. Evaluation上記で定義したテストベリファイアを用いてモデル性能を評価した結果,約0.88の性能を示した.
# evaluation sts-test
test_evaluator(model, output_path=sts_model_save_path)
2022-02-25 02:15:39 - EmbeddingSimilarityEvaluator: Evaluating the model on sts-test dataset:
2022-02-25 02:15:43 - Cosine-Similarity : Pearson: 0.8870 Spearman: 0.8873
2022-02-25 02:15:43 - Manhattan-Distance: Pearson: 0.8862 Spearman: 0.8835
2022-02-25 02:15:43 - Euclidean-Distance: Pearson: 0.8869 Spearman: 0.8844
2022-02-25 02:15:43 - Dot-Product-Similarity: Pearson: 0.8775 Spearman: 0.8745
0.887279591001845
2.2. NLIとSTSデータによる継続学習このセクションのcolab練習コードについては、リンクを参照してください.
STS単一データを用いて学習を行うほか,NLIを用いて学習を行った後にSTSを用いて追加学習を行う方法もある.
論文では,STS単一データ学習時と比較して,NLI学習後にSTS学習に続く手法が約3〜4点高い性能を示した(下表2参照),この戦略は特にBERTクロスコーディング方式に大きな影響を及ぼしたと考えられる.
まず,NLIを学習するために,論文は基本的にSoftmax objective関数を用いて3つのクラス(含む,矛盾,中性)を学習する.
しかし,文-transformerのNLI学習例によれば,実験結果は,ソフトmax lossを用いた場合と比較して,複数の負のRanking loss(MNRloss)学習により,より優れた性能を示した.
(実際のklueデータセット学習の結果から,MNRlossで学習した場合,性能も若干向上した.
MNR lossはtriple lossに似ており、(アンカー-文、正-文、負-文)フォーマットに従い、学習を導いてアンカーと正の距離を近づけ、アンカーと負の距離を遠くする.
(本練習ではMNR lossも使用します.)
学習方法
1.NLIデータを三元グループ(アンカー、正、負)にまとめる
2.各入力シーケンスは、予め学習されたBERTモデルに従って埋め込みベクトルに変換される
3.変換後の埋め込みベクトルに対してPoyoung演算(通常Mean-pooling)を行い、文埋め込みベクトルに変換する
2.MNR損失を目標とする関数でNLIデータセットを微調整する
3.最適化されたモデルをNLIでロードし、STSデータとしてさらに学習する
4.変換された2つの文imbedingベクトルをcosine類似度により2つの文ベクトルの類似度値を算出する(-1~1)
以下の実践コードは、上述したSTS finetunningに関する内容を省略する.
2.2.1. Load Dataset (NLI)
学習を継続するために、すべてのnli、stsデータがロードされ、nliではトレーニングデータのみがロードされます.
# load KLUE-NLI Dataset
klue_nli_train = load_dataset("klue", "nli", split='train')
print('Length of Train : ',len(klue_nli_train)) # 24998
2.2.2. Preprocessing (NLI)MNRlossによる学習のためにtriple(アンカー文,正文,否定文)形式で調整した後,同様に
InputExample()
に変換した.def make_nli_triplet_input_example(dataset):
'''
Transform to Triplet format and InputExample
'''
# transform to Triplet format
train_data = {}
def add_to_samples(sent1, sent2, label):
if sent1 not in train_data:
train_data[sent1] = {'contradiction': set(), 'entailment': set(), 'neutral': set()}
train_data[sent1][label].add(sent2)
for i, data in enumerate(dataset):
sent1 = data['hypothesis'].strip()
sent2 = data['premise'].strip()
if data['label'] == 0:
label = 'entailment'
elif data['label'] == 1:
label = 'neutral'
else:
label = 'contradiction'
add_to_samples(sent1, sent2, label)
add_to_samples(sent2, sent1, label) #Also add the opposite
# transform to InputExmaples
input_examples = []
for sent1, others in train_data.items():
if len(others['entailment']) > 0 and len(others['contradiction']) > 0:
input_examples.append(InputExample(texts=[sent1, random.choice(list(others['entailment'])), random.choice(list(others['contradiction']))]))
input_examples.append(InputExample(texts=[random.choice(list(others['entailment'])), sent1, random.choice(list(others['contradiction']))]))
return input_examples
nli_train_examples = make_nli_triplet_input_example(klue_nli_train)
nli_train_examples[0].texts # ['힛걸 진심 최고다 그 어떤 히어로보다 멋지다', '힛걸 진심 최고로 멋지다.', '힛걸 그 어떤 히어로보다 별로다.']
後続学習用のtrainデータをDataLoader()に変換してバッチ学習を行う.上記NLIを学習する際には、STSデータセットを検証データとして使用するため、個別のベリファイアは作成されません.# Train Dataloader
train_dataloader = DataLoader(
nli_train_examples,
shuffle=True,
batch_size=train_batch_size,
)
2.2.3. Load Pretrained ModelNLI微調整のための事前学習言語モデルをロードするプロセスは,Huggingfaceモデルセンターに公開されたklue/roberta-baseモデルを用いた.
冷却層について,論文実験基準で最も性能の良い平均プールを定義した.
from sentence_transformers import SentenceTransformer, models
# Load Embedding Model
embedding_model = models.Transformer(
model_name_or_path="klue/robert-base",
max_seq_length=256,
do_lower_case=True
)
# Only use Mean Pooling -> Pooling all token embedding vectors of sentence.
pooling_model = models.Pooling(
embedding_model.get_word_embedding_dimension(),
pooling_mode_mean_tokens=True,
pooling_mode_cls_token=False,
pooling_mode_max_tokens=False,
)
model = SentenceTransformer(modules=[embedding_model, pooling_model])
2.2.4. Training by NLI前述したように,loss関数では
MultipleNegativesRankingLoss()
を用い,論文と同様に,第1段階ではlearng−rate warm−upでtrainの10%を設定した.from sentence_transformers import losses
# config
sts_num_epochs = 1
train_batch_size = 32
nli_model_save_path = 'output/training_nli_by_MNRloss_'+pretrained_model_name.replace("/", "-")+'-'+datetime.now()
# Use MultipleNegativesRankingLoss
train_loss = losses.MultipleNegativesRankingLoss(model)
# warmup steps
warmup_steps = math.ceil(len(nli_train_examples) * nli_num_epochs / train_batch_size * 0.1) #10% of train data for warm-up
logging.info("Warmup-steps: {}".format(warmup_steps))
# Training
model.fit(
train_objectives=[(train_dataloader, train_loss)],
evaluator=dev_evaluator,
epochs=nli_num_epochs,
evaluation_steps=int(len(train_dataloader)*0.1),
warmup_steps=warmup_steps,
output_path=nli_model_save_path,
use_amp=False #Set to True, if your GPU supports FP16 operations
)
2.2.5. Continue Learning by STSこれはNLIデータセットを通して調整されたモデルにSTSを追加するプロセスである.学習が完了したモデルをロードすると、
# Load model of fine-tuning by NLI
model = SentenceTransformer(nli_model_save_path)
STSデータセットで継続学習を実行します.# config
sts_num_epochs = 4
train_batch_size = 32
sts_model_save_path = 'output/training_sts_continue_training-'+pretrained_model_name.replace("/", "-")+'-'+datetime.now().strftime("%Y-%m-%d_%H-%M-%S")
# Use CosineSimilarityLoss
train_loss = losses.CosineSimilarityLoss(model=model)
# warmup steps
warmup_steps = math.ceil(len(sts_train_examples) * sts_num_epochs / train_batch_size * 0.1) #10% of train data for warm-up
logging.info("Warmup-steps: {}".format(warmup_steps))
# Training
model.fit(
train_objectives=[(train_dataloader, train_loss)],
evaluator=dev_evaluator,
epochs=sts_num_epochs,
evaluation_steps=int(len(train_dataloader)*0.1),
warmup_steps=warmup_steps,
output_path=sts_model_save_path
)
2.2.6. Evaluation上記で定義したテストベリファイアを用いてモデル性能を評価した結果,モデル性能は約0.89であり,単一STS学習時と比較して約1%向上した.
# evaluation sts-test
test_evaluator(model, output_path=sts_model_save_path)
2022-02-25 04:28:11 - EmbeddingSimilarityEvaluator: Evaluating the model on sts-test dataset:
2022-02-25 04:28:15 - Cosine-Similarity : Pearson: 0.8962 Spearman: 0.8964
2022-02-25 04:28:15 - Manhattan-Distance: Pearson: 0.8895 Spearman: 0.8845
2022-02-25 04:28:15 - Euclidean-Distance: Pearson: 0.8908 Spearman: 0.8859
2022-02-25 04:28:15 - Dot-Product-Similarity: Pearson: 0.8847 Spearman: 0.8810
0.896394981925387
3. ConclusionSTSのみを学習し、ソフトmax lossのcontinue learning、MNR lossのcontinue learningの3つのケースを利用して実験を行った結果、わずかな差はあるものの、MNR lossを利用したcontinue learning方式の性能が最も優れていることが分かった.
通常、stsパフォーマンス評価のために、上述したkornluデータセットが使用されています.
しかし、kornluデータにとっては、klueよりもデータの数が多いものの、データの複雑さは非常に低く、各種の実際のデータの文埋め込みを目的とすれば、klueデータを利用した方が経験的により品質が良く、性能も良い.
もしそうなら「klueもkornluも勉強していいんじゃないですか?」klueとkornluの類似性の測定基準,すなわちラベルの基準が異なると,かえってモデルに混乱の結果をもたらすと考える人もいるので,経験的には単一データを用いることが望ましい.
Reference
この問題について([Basic NLP]sentent-Transformersライブラリを使用してSBERTを学習する方法), 我々は、より多くの情報をここで見つけました https://velog.io/@jaehyeong/Basic-NLP-sentence-transformers-라이브러리를-활용한-SBERT-학습-방법テキストは自由に共有またはコピーできます。ただし、このドキュメントのURLは参考URLとして残しておいてください。
Collection and Share based on the CC Protocol