딥러닝 논문리뷰

Sentence-BERT: Sentence Embeddings using Siamese BERT-Networks

빈이름 2023. 3. 20. 23:05

1. 개요

Semantic textual similarity(STS)는 두 문장 사이의 유사도를 측정하는 NLP task입니다. 이 task 역시 BERT를 사용했을 때의 성능이 훨씬 좋았죠. BERT는 STS task를 두 문장을 한 번에 입력받은 뒤에 두 문장의 관계를 분류하는 식으로 해결합니다. 그러나 이런 방식은 실용적으로 활용되기에 너무 비효율적입니다. 논문에 따르면 원하는 문장과 가장 유사한 문장을 10,000개의 문장 중에서 찾으려고 한다면 $n(n-1)/2=49,995,000$번의 연산이 필요합니다. 이를 V100 GPU를 사용해 수행하고자 하면, 65시간이 걸린다고 합니다. 이 정도 시간이 걸린다면 검색 추천이나 알고리즘 등에 사용하기는 어렵겠죠.

Sentence-BERT(이하 SBERT)의 목적은 이 연산을 효율적인 방법으로 수행하면서도 기존의 BERT와 같은 성능을 내는 것입니다. SBERT는 2 문장의 임베딩을 각각 연산한 뒤에, cosine similarity와 같은 유사도 함수를 사용해 두 문장 임베딩의 유사도를 측정하는 식으로 수행됩니다. 이 방법의 장점은 문장 임베딩을 미리 계산해둘 수 있기 때문에 BERT에 비해 굉장히 시간과 자원을 절약할 수 있습니다. SBERT를 이용해 10,000개의 문장 가운데 가장 유사한 문장을 찾고자 한다면, 10,000개의 문장을 SBERT를 통해 미리 임베딩을 계산하는데 5초, cosine similarity를 계산하는데 0.1초밖에 걸리지 않는다고 합니다.

SBERT는 텍스트의 유사도를 측정하는 task들에서 SOTA를 달성했으며, 위키피디아 문서의 문장 중, 다른 주제의 문장을 골라내야 하는 challengin argument similarity dataset에서도 SOTA를 달성했다고 합니다.

BERT와 SBERT의 차이

2. Related Work

문장 임베딩을 활용하고자 하는 연구는 기존에도 많이 있었습니다. BERT를 사용해 유사도를 측정하는데는 앞서 말했듯이 자원적, 시간적 한계가 있기 때문에 BERT를 이용해 유의미한 문장 임베딩을 얻고자 하는 연구들도 있었습니다. 이들은 BERT의 CLS token output을 문장 임베딩으로 활용하거나, BERT output의 평균을 문장 임베딩으로 활용했습니다. 그러나 이런 방식들 중, 어떤 방식이 더 낫다라는 것에 대한 확실한 근거는 제시되지 못했습니다.(본 논문에서 이에 대한 실험도 진행합니다.)

BERT 이전에도 Encoder-Decoder 구조를 활용해 주변 문장을 예측하는 Skip-Thought나 SBERT처럼 2개의 BiLSTM 모델을 활용하는 InferSent와 같은 모델들도 있었습니다. 이런 문장 임베딩 학습에 SNLI(두 문장의 관계를 분류하는 task) 데이터셋을 활용하는 것이 효과가 좋다는 연구도 있었습니다.

이전의 연구들 가운데 가장 SBERT와 유사한 것은 poly-encoder입니다. Poly-encoder 역시 문장들의 context vector를 미리 계산하는 방식을 택합니다. 그리고 context vector들을 attention을 사용해 결과를 추론해내는 방식이죠. 그러나 poly-encoder는 이 attention 연산 때문에 두 문장의 관계를 추론하는 연산이 오래 걸립니다.

SBERT는 context vector의 유사도를 계산하는데 단순히 cosine similarity를 사용함으로써 poly-encoder보다 더 효율적으로 추론이 가능합니다. 또, 기존의 연구들은 모두 학습이 되지 않은 초기 모델부터 학습을 시작했지만, SBERT는 이미 학습된 BERT나 RoBERTa를 활용해 20분 정도의 fine-tuning 과정만을 거쳐 학습됩니다. 학습 시간에서도 훨씬 효율적이죠.

3. Model

SBERT는 BERT의 output에 pooling 레이어를 적용하여 문장 임베딩을 얻어냅니다. 이 pooling을 적용하는 방법으로 3가지를 실험합니다. MEAN-strategy, MAX-strategy, CLS-token 입니다.

  • CLS-token은 기존 BERT와 같은 방식입니다. CLS 토큰의 output이 문장의 의미를 내포하고 있다고 가정하고, 해당 토큰의 output을 분류에 사용하는 것입니다.
  • MEAN-strategy는 모든 토큰의 output들의 평균을 계산한 임베딩을 분류에 사용합니다.
  • MAX-strategy는 모든 토큰의 output들 중, 가장 값이 큰 임베딩을 분류에 사용합니다.

사전 학습된 BERT를 fine-tuning하기 위해 siamese and triplet networks를 사용한다고 합니다. Siamese는 쌍둥이와 같이 2개의 BERT를 사용해 2 문장의 임베딩을 각각 계산하는 구조를 말하며, triplet은 input 문장과 유사한 문장은 가까운 임베딩 거리를, 유사하지 않은 문장은 먼 임베딩 거리를 갖도록 연산하는 구조를 말합니다.

학습은 fine-tuning에 사용될 데이터셋의 종류에 따라 달라집니다.

 

1. Classification Objective Function

NLI와 같이 두 문장의 관계를 분류하는 데이터셋을 활용하는 경우 아래와 같이 계산됩니다. BERT로 계산한 두 문장의 임베딩을 각각 $u, v$라고 할 때, 두 문장의 유사도는 다음과 같이 계산됩니다.

$$ o=softmax(W_t(u,v,|u-v|)) $$

$W_t$는 분류하고자 하는 라벨 수만큼의 output을 내는 linear layer를 의미합니다. $(u,v,|u-v|)$는 concatenate를 의미합니다. 벡터 $u$와 벡터 $v$, 두 벡터의 차이 $|u-v|$를 합치는 것입니다.(이렇게 사용한 이유는 여러가지 실험을 통해 최적의 결과를 내는 것으로 정한 것입니다.)

 

2. Regression Objective Function

STS와 같이 두 문장 사이의 유사도 점수를 메긴 데이터셋을 활용하는 경우 계산되는 함수입니다. 단순히 벡터 $u$와 벡터 $v$ 사이의 cosine similarity를 계산하여 점수를 냅니다.

$$ o=CosineSimilarity(u,v) $$

 

3. Triplet Objective Function

문장 $a$가 주어졌을 때, 이와 관련된 문장($p$)은 positive로, 관련없는 문장($n$)은 negative로 취급합니다. Triplet loss는 $a$와 $p$ 사이의 거리가 $a$와 $n$ 사이의 거리보다 작아지도록 학습됩니다. 이를 통해 모델은 유사한 문장들은 가까운 임베딩 거리에, 유사하지 않은 문장들은 먼 임베딩 거리에 배치하는 임베딩 공간을 형성하게 됩니다.

$$ max(||s_a-s_p||-||s_a-s_n||+\epsilon,0) $$

여기서 $\epsilon$은 $s_p$가 $s_n$보다 $s_a$에 무조건 더 가깝도록 해주기 위한 임의의 상수입니다. 문장 사이의 유사도는 유클리드 거리($distance(a,b)=\sqrt{(a-b)^2}$)가 사용되었으며, $\epsilon=1$로 사용했습니다.

 

이해를 돕기 위해 SBERT의 코드를 약식으로 함께 올려 놓겠습니다.

더보기
import torch
import torch.nn.functional as F
from transformers import AutoTokenizer, AutoModel

#CLS Pooling
def cls_pooling(model_output):
    return model_output[:,0,:]

'''
MEAN, MAX Pooling을 계산할 때'[PAD]' 토큰의 위치는 계산에 포함하지 않아야 함.
'''
#Mean Pooling - Take attention mask into account for correct averaging
def mean_pooling(model_output, attention_mask):
    # attention_mask의 shape을 model_output과 맞춰줘야 한다.
    input_mask_expanded = attention_mask.unsqueeze(-1).expand(model_output.size()).float()
    # input_mask_expanded과 model_output을 곱함으로써 [PAD] 토큰 위치의 값들을 0으로 만든다.
    # 그 뒤에 [PAD] 토큰 위치의 값들을 빼고 평균을 계산한다.
    return torch.sum(model_output * input_mask_expanded, dim=1) / torch.clamp(input_mask_expanded.sum(1), min=1e-9)
    
#MAX Pooling
def max_pooling(model_output, attention_mask):
    input_mask_expanded = attention_mask.unsqueeze(-1).expand(model_output.size()).float()
    # MAX Pooling 역시 [PAD] 토큰 위치의 값들은 빼고 최댓값을 계산한다.
    return torch.max(model_output * input_mask_expanded, dim=1)

Pooling layer 코드입니다. 처음 구현할 땐 [PAD] 토큰을 생각 못하고 그냥 torch.mean, torch.max 썼다가 망한 적이 있었습니다. [PAD] 토큰은 빼고 mean, max pooling을 계산해야 합니다.

tokenizer = AutoTokenizer.from_pretrained("klue/bert-base")
bert = AutoModel.from_pretrained("klue/bert-base")
linear = torch.nn.Linear(bert.config.hidden_dim*3, num_cls)
inputs_A = tokenizer(
	sentences_A, 
    padding="max_length",
    return_tensors="pt"
)
inputs_B = tokenizer(
	sentences_B, 
    padding="max_length",
    return_tensors="pt"
)

logitsA = bert(**inputs_A).last_hidden_state # shape of logits = [배치 크기, 시퀀스 길이, bert의 hidden_dim]
logitsB = bert(**inputs_B).last_hidden_state

if pooling == "CLS":
    u = cls_pooling(logitsA)
    v = cls_pooling(logitsB)
elif pooling == "MEAN":
	u = mean_pooling(logitsA, inputs_A["attention_mask"])
    v = mean_pooling(logitsB, inputs_B["attention_mask"])
elif pooling == "MAX":
	u = max_pooling(logitsA, inputs_A["attention_mask"])
    v = max_pooling(logitsB, inputs_B["attention_mask"])
    
if mode == "regression":
    output = F.cosine_similarity(u, v, dim=-1)
elif mode == "classification":
    output = torch.cat(u, v, dim=-1)
    output = torch.cat(output, torch.abs(u-v), dim=-1)
    output = linear(output)
    output = F.softmax(output, dim=-1)

Regression, Classification의 경우 위와 같이 구현할 수 있습니다.

inputs_A = tokenizer(
	anchors, 
    padding="max_length",
    return_tensors="pt"
)
inputs_P = tokenizer(
	positives, 
    padding="max_length",
    return_tensors="pt"
)
inputs_N = tokenizer(
	negatives, 
    padding="max_length",
    return_tensors="pt"
)

logitsA = bert(**inputs_A).last_hidden_state # shape of logits = [배치 크기, 시퀀스 길이, bert의 hidden_dim]
logitsP = bert(**inputs_P).last_hidden_state
logitsN = bert(**inputs_N).last_hidden_state

if pooling == "CLS":
    s_a = cls_pooling(logitsA)
    s_p = cls_pooling(logitsP)
    s_n = cls_pooling(logitsN)
elif pooling == "MEAN":
    s_a = mean_pooling(logitsA, inputs_A["attention_mask"])
    s_p = mean_pooling(logitsP, inputs_P["attention_mask"])
    s_n = mean_pooling(logitsN, inputs_N["attention_mask"])
elif pooling == "MAX":
    s_a = max_pooling(logitsA, inputs_A["attention_mask"])
    s_p = max_pooling(logitsP, inputs_P["attention_mask"])
    s_n = max_pooling(logitsN, inputs_N["attention_mask"])
    
epsilon = 1
output = torch.max(torch.sqrt((s_a - s_p)**2) - torch.sqrt((s_a - s_n)**2) + epsilon, 0)

Triplet의 경우 위와 같이 구현할 수 있습니다.

3-1. 학습 파라미터

실험 데이터셋은 SNLI와 MultiNLI를 사용했습니다.

  • SNLI : 570,000개의 문장 쌍으로 ['contradiction', 'entailment', 'neutral'] 3가지 라벨로 분류하는 task 입니다.
  • MultiNLI : 430,000개의 문장 쌍으로 SNLI와 같이 3가지 라벨로 분류해야 하는 task이며, 다양한 분야의 텍스트들로 이루어져 있습니다.
  • 배치 크기 : 16
  • Adam(learning_rate=2e-5)
  • Linear learning rate warm-up (10%)
  • MEAN-Pooling 사용.

4. Evaluation - Semantic Textual Similarity

성능 평가에 앞서, SBERT는 기존의 다른 모델들과 다르게 두 문장의 유사도를 측정하는 과정에서 복잡한 수식이 아닌 단순한 cosine-similarity를 사용한다는 점을 한 번 더 강조하고 넘어갑니다.

4-1. Unsupervised STS

Unsupervised STS는 NLI 데이터셋으로 fine-tuning된 SBERT를 추가 fine-tuning 없이 STS task에 적용한 결과를 나타냅니다. 이를 통해 SBERT의 fine-tuning 방식이 문장 임베딩 형성에 얼마나 도움을 많이 주는지를 확인할 수 있습니다. 점수는 Spearman-correlation(예측 점수와 실제 점수의 선형 관계를 측정하는 지표)을 사용했으며, 결과는 아래와 같습니다.

Unsupervised STS 결과와 다른 모델들의 결과 비교

BERT의 CLS output이나 Average output을 cosine similarity를 통해 비교한 결과는 GloVe보다도 못한 점수를 보입니다. BERT가 만들어내는 문장 임베딩 벡터는 거의 도움이 되지 않는다는 것을 나타냅니다. 반면, NLI로 학습된 SBERT는 추가 학습 없이도 하나의 STS task를 제외하고 모두 가장 높은 점수를 달성했습니다. SBERT 학습 과정을 통해 BERT가 임베딩 공간에 관련 있는 문장끼리 매핑하는 법을 배운다는 것을 알 수 있습니다. (연관 있는 문장의 임베딩끼리는 가깝게, 그렇지 않은 임베딩끼리는 멀게)

 

SICK-R에서만 최고 점수를 달성하지 못했는데, 이는 Universal Sentence Encoder의 경우 다양한 데이터셋들로 학습되어 SICK-R 데이터와 유사한 데이터들이 학습에 사용되었지만, SBERT는 이와 관련 없는 Wikipedia와 NLI dataset으로 학습되었기 때문이라고 분석하고 있습니다. 그리고 SBERT와 SRoBERTa 사이에 점수 차이가 존재하기는 하지만 큰 차이는 없었다고 합니다.

4-2. Supervised STS

Supervised STS는 STS 데이터셋으로 fine-tuning한 결과를 확인합니다. 성능은 STS benchmark(STSb) 데이터셋으로 평가 합니다. STSb데이터셋은 8,628개의 문장 쌍이 존재하고, ['captions', 'news', 'forums'] 3가지 라벨로 분류해야 하는 task 입니다. 사전학습된 BERT/RoBERTa 모델을 STSb 데이터셋으로 fine-tuning한 뒤, 결과를 비교합니다.

그리고 NLI 데이터셋으로 fine-tuning을 한번 한 뒤, STSb 데이터셋으로 fine-tuning한 결과와 STSb만으로 fine-tuning한 결과도 비교 합니다.

Supervised STS의 결과와 다른 모델들의 결과 비교

위의 결과를 보면, SBERT가 기존 모델들보다 훨씬 좋은 점수를 받은 것은 당연하고, NLI 데이터셋으로 fine-tuning을 한 뒤에 STSb 데이터셋으로 한 번 더 fine-tuning한 것이 그냥 STSb 데이터셋만으로 fine-tuning한 것보다 더 높은 점수를 받았습니다.

4-3. Argument Facet Similarity

Argument Facet Similarity(AFS) 데이터셋은 6,000개의 주장이 담긴 문장 쌍을 제공합니다. 문장들은 각각 '총기 사용', '동성혼', '사형제도'에 대한 상반되거나 같은 입장의 주장들을 담고 있으며, 두 문장이 얼마나 유사한 입장을 갖는지를 0에서 5점 사이의 점수로 평가하는 task입니다.

이 데이터셋은 기존의 STS task들과는 좀 다른 것이, 두 문장이 유사한 주제에 대해서 얘기하고 있더라도, 두 문장이 주장하는 입장도 같아야 한다는 것입니다. 두 문장이 같은 주제에 대해서 얘기하고 있기 때문에 겹치는 단어도 훨씬 많을 뿐더러, 문장의 입장을 알아내야하는 더 복잡한 사고가 필요하기 때문에 더 어렵습니다.

SBERT는 2가지 방식으로 이 task를 실험합니다.

 

1. 10-fold cross-validation : 이 방식은 기존 AFS 데이터셋에 대한 연구의 방식을 따라한 것으로, 데이터셋을 10개의 fold로 나눠 각 fold를 돌아가며 validation set으로 사용하며 모델의 성능을 확인하는 방법입니다. 그러나 이 방식은 AFS 데이터셋의 서로 다른 3가지 주제들이 섞이기 때문에 모델이 학습되지 않은 다른 주제에도 잘 적용되는지를 확인하기가 어렵습니다.

 

2. Cross-topic setup : 3가지 토픽 중 2가지 토픽을 훈련셋으로, 나머지 하나의 topic을 평가셋으로 활용하는 방법입니다. 주제 별로 나누기 때문에, 학습되지 않은 주제에 대해서도 잘 작동하는지를 확인할 수 있습니다.

 

AFS task 실험 결과

결과는 위와 같습니다. Unsupervised methods들은 딥러닝을 사용하지 않은 방식들을 말합니다. 모두 BERT나 SBERT보다 낮은 점수를 보였습니다. 이 방식들의 경우 두 문장에 사용된 단어들의 유사도에 기반하는 방식이기 때문에, 두 문장에 사용되는 단어가 유사한 AFS 데이터셋에서 낮은 점수를 받을 수 밖에 없습니다. 그리고 이들은 학습이 불가능한 방식들입니다. 그러나 딥러닝 방식은 학습을 통해 두 문장의 단어가 유사하더라도 유사도가 낮은 케이스들을 학습할 수 있습니다.

 

그 다음으론 BERT와 SBERT의 성능을 비교해 보겠습니다. 10-fold Cross-Validation 방식에서는 SBERT가 BERT에 거의 필적하는 성능을 보이는 것을 확인할 수 있습니다.(1점 정도 떨어지네요) 그러나 Cross-Topic Evaluation에서는 점수가 확연하게 떨어지는 것을 볼 수 있습니다. 이는 BERT는 두 문장을 한번에 입력 받으면서 attention 연산을 통해 두 문장의 관계를 한번에 유추할 수 있기 때문입니다. 반면에 SBERT는 두 문장을 따로 입력 받으므로 두 문장의 관계를 한번에 유추해낼 수가 없죠.

 

이런 차이가 Cross-Topic Evaluation에서 더 두드러진다는 것은, SBERT의 학습 방식이 학습에 사용되는 문장들을 학습을 통해 임베딩 공간에 매핑하는 방식으로 훈련된다는 것을 나타냅니다. 학습에 사용된 토픽의 단어들은 SBERT 학습을 통해 서로 같은 입장의 주장들은 가까운 거리에, 서로 상반되는 주장들은 먼 거리에 매핑이 될 수 있습니다. 그러나 학습에 사용되지 못한 토픽의 단어들은 이 과정을 거치지 못했기 때문에 같은 입장의 주장이 먼 거리에 있거나, 상반되는 주장들이 가까운 거리에 매핑되어 있는 상태로 유지되는 것이죠. 그렇기 때문에 SBERT는 학습되지 않은 주제의 데이터셋들에 대해 BERT보다 약점을 보입니다.

SBERT로 학습이 진행된 topic의 문장들은 왼쪽과 같이 같은 입장의 문장들끼리 임베딩 공간을 형성하지만, 학습을 하지 않은 처음 보는 단어, 문장들에 대해서는 오른쪽과 같이 분리된 임베딩 공간을 형성하지 못한다.

4-4. Wikipedia Sections Distinction

Wikipedia Sections Distinction은 주어진 문장들 중, 다른 wikipedia 문서에서 발췌된 문장을 찾는 task입니다. 즉, 같은 주제의 문장들은 positive 샘플로, 다른 주제의 문장들은 negative 샘플로 간주하는 것입니다. Triplet object function을 사용하기 좋은 task입니다.

180만개의 triplet 문장 쌍에 대해 1에포크 학습되었으며, 222,957개의 테스트셋으로 평가했다고 합니다. 평가 지표는 positive 문장이 negative 문장보다 input(anchor)문장에 더 가까운가를 측정하는 accuracy입니다. 결과는 아래와 같으며, 이 task를 연구했던 Dor et al.의 BiLSTM 방식과 비교합니다. SBERT가 확실히 더 높은 점수를 받는 것을 확인할 수 있습니다.

5. SentEval

SentEval은 사전학습된 문장 임베딩의 성능을 확인하기 위해 자주 사용되는 task들의 집합입니다. (GLUE같은 느낌인 것 같습니다.) SBERT는 STS나 NLI 외의 task들에 사용되기 위한 모델은 아니지만, SBERT 학습 과정이 이런 task들에도 도움이 되는지를 확인하고 싶어서 진행한 실험입니다. SentEval은 아래 7가지의 task들로 이루어져 있습니다.

 

1. MR : 영화 리뷰의 감성 분석 task

2. CR : 소비자의 리뷰 감성 분석 task.

3. SUBJ : 영화 리뷰의 별점을 예측하는 task.

4. MPQA : 단락 단위의 글의 주장을 예측하는 task.

5. SST : 감성 분석 데이터셋.

6. TREC : 질문의 유형을 분류하는 task.

7. MRPC : 두 뉴스 기사가 같은 이야기를 하고 있는지를 판별하는 task.

점수를 보니 SBERT가 transfer learning의 목적을 갖고 있지 않음에도, 다양한 task들에서 SOTA를 달성하는 것을 확인할 수 있습니다. SBERT를 통해 문장 임베딩을 형성하는 것이 downstream task들에도 긍정적인 영향을 줄 수 있다는 것을 나타냅니다.

SBERT는 2가지 데이터셋에서는 SOTA를 달성하지 못했는데, 그 중 하나인 TREC의 경우, Universal Sentence Encoder가 pre-train 과정에 QA 데이터셋을 활용한 것이 영향이 있었을 것이라 분석합니다. 

 

4-1. Unsupervised STS의 결과에서는 BERT가 GloVe의 사전 임베딩 결과보다 많이 떨어지는 점수를 받았지만, SentEval에서는 GloVe와 유사한 결과를 얻을 수 있었습니다. 이는 Unsupervised STS의 경우 BERT의 output embedding을 cosine-similarity 계산에 바로 사용하여, embedding 차원 값들이 모두 동등한 가중치를 갖지만, SentEval은 BERT의 output embedding에 linear layer를 추가함으로써 각 embedding 차원 값들 중 어느 값이 더 중요한지를 계산하기 때문에 더 좋은 성능을 보여준다고 해석했습니다.

6. Ablation Study

본 문단에서는 Pooling 방식과 Classification Objective function을 사용할 때 벡터 $u$와 벡터 $v$의 concatenate 방식을 실험합니다. NLI는 SNLI, Multi-NLI 데이터셋으로 fine-tuning하고, STSb는 STSb 데이터셋으로 fine-tuning한 모델을 말하며, 점수는 STSb의 평가셋에 대한 spearman correlation 점수를 뜻합니다.

결과는 위와 같습니다. Pooling 방식의 경우 NLI와 STS 둘 다 실험해 봤습니다. 둘 다 MEAN Pooling의 성능이 가장 좋았으며, NLI는 3가지 방식이 큰 차이가 없었으나 STSb에서는 MAX Pooling의 점수가 현저히 낮았습니다. 이는 BiLSTM에서는 Max Pooling의 성능이 가장 좋았던 결과와 상반됩니다.

 

NLI의 경우 Pooling 방식보다 Concatenate 방식의 영향이 더 컸습니다. 위의 점수는 NLI task로 fine-tuning한 뒤 STSb에 적용한 결과로, 학습할 때는 다양한 Concatenate 방식으로 학습하지만, STSb에 적용할 때는 두 벡터 $u$와 $v$를 단순히 결합하는 방식$(u, v)$으로 사용합니다. 그러니 당연히 NLI fine-tuning 과정에서도 똑같이 $(u, v)$로 학습하는 것이 더 성능이 좋을 것 같지만, 의외로 이 방식으로 fine-tuning했을 때의 점수가 가장 낮았습니다. 가장 성능이 좋았던 방식은 $(u, v, |u-v|)$였습니다.

7. Computational Efficiency

SBERT가 얼마나 효율적인가를 평가해보는 단락입니다. 비교는 GloVe, InferSent, Universal Sentence Encoder입니다.

 

  • GloVe는 딥러닝보다 머신러닝 방식에 가깝습니다. 단어의 등장 빈도와 예측 학습을 통해 임베딩을 학습합니다
  • InferSent는 SBERT와 유사합니다. 다만 Transformer가 아닌 단일 BiLSTM 레이어를 통해 embedding을 생성합니다.
  • Universal Sentence Encoder는 NLI, QA 등의 task들을 함께 학습하는 multi-task learning을 통해 문장 임베딩을 학습하는 방법으로 transformer와 단순 Dense Layer(DAN) 2가지 버전의 모델을 갖고 있습니다. 논문에서 비교하는 대상이 transformer 구조인지 DAN 구조인지 써있지는 않지만 속도를 보면 transformer 버전과 비교를 하는 것 같습니다.
  • SBERT는 일반 inference와 smart-batching inference를 함께 실험했습니다. smart-batching은 길이가 유사한 문장들을 하나의 배치로 묶어 '[PAD]' 토큰이 최대한 적게 발생하도록 하는 방식입니다. '[PAD]' 토큰이 적게 발생한다는 것은 그만큼 모델이 볼 문장의 토큰 수가 줄어든다는 말이기 때문에 속도가 더 빨라집니다.

결과는 위 표와 같습니다. CPU와 GPU 속도를 각각 측정하였으며 값이 클수록 빠른 것입니다. 속도는 GloVe가 압도적으로 빠릅니다. 그럴수 밖에 없는게 다른 방식들에 비해 연산이 압도적으로 단순합니다. (한 번의 행렬곱 연산으로 끝납니다.)

그 외의 딥러닝 방식들을 비교해 봅시다. CPU에서는 InferSent가 가장 빠릅니다. 이는 InferSent에 사용된 단일 BiLSTM 레이어가 transformer 레이어에 비해 훨씬 단순하기 때문입니다.

그러나 GPU를 활용했을 때, smart batching을 활용한 SBERT가 InferSent보다 더 빠른 것을 확인할 수 있었습니다. Smart-batching을 통해 낭비되는 시퀀스의 길이를 줄인 것이 도움이 된 것 같습니다.

8. 결론

  • SBERT는 두 문장을 입력으로 받는 task에서, BERT의 비효율성을 극복하고자 제시된 방법입니다.
  • SBERT는 pre-train된 BERT를 NLI, STS와 같이 문장 쌍을 필요로 하는 task를 이용한 'fine-tuning'을 통해 학습됩니다.
  • SBERT의 학습 과정은 BERT를 이용해 문장 단위의 임베딩을 임베딩 공간에 매핑하는 것과 같습니다.
  • SBERT는 문장 임베딩을 따로 계산한 뒤 cosine-similarity를 사용해 유사도를 측정하기 때문에, 미리 임베딩 벡터를 계산해 놓을 수 있다는 장점이 있습니다.

개인적으로는 문장의 임베딩을 미리 계산해 놓을 수 있다는 점이 실제 서비스에서도 적용될 수 있는 중요한 요소라 생각 되어서 흥미롭게 봤습니다. 아쉬운 점은 BERT처럼 학습되지 않은 도메인에 대한 일반화 성능이 떨어진다는 것인 것 같습니다. 하지만 실제 서비스에서 추천 시스템 등에 활용한다고 하면, 여러 도메인보단 특정 도메인에 특화된 데이터를 쓸테니 크게 실감될 단점은 아닌 것 같습니다.

 

Sentence-BERT: Sentence Embeddings using Siamese BERT-Networks

BERT (Devlin et al., 2018) and RoBERTa (Liu et al., 2019) has set a new state-of-the-art performance on sentence-pair regression tasks like semantic textual similarity (STS). However, it requires that both sentences are fed into the network, which causes a

arxiv.org