2019년에 발표된 SentenceTransformer는 Bi-encoder아키텍처를 갖추고 있으며 효율적인 문장 임베딩을 생성하기 위해 BERT를 조정하는 역할을 수행했습니다.
최근에 Sentence Transformer는 LLM의 RAG 파이프라인에서 Embedding에 활용 합니다. 이러한 점에서 Sentence Transformer는 일반적으로 텍스트와 텍스트 간의 의미적 유사성을 측정하고 주어진 입력 텍스트를 기반으로 적절한 텍스트 조각을 검색하는 데 사용됩니다.
1. Architecture:
Sentence Transformer는 순차적 데이터에서 문맥 관계를 포착하는 데 탁월한 트랜스포머 아키텍처를 기반으로 합니다.
트랜스포머는 Self-attention 메커니즘을 사용하여 임베딩을 생성할 때 모델이 입력 텍스트의 여러 부분에 가중치를 부여할 수 있도록 합니다. 이를 통해 장거리 종속성과 컨텍스트를 캡처하는 데 도움이 됩니다.
2. Context-Aware Embeddings:
단어를 개별적으로 표현하는 기존의 단어 임베딩과 달리 문장 변환기는 문장의 전체 문맥을 고려합니다.
따라서 전체 문장의 맥락에서 단어와 단어의 의미 사이의 관계를 이해할 수 있습니다.
3. Training Strategies:
Sentence Transformer는 비지도 또는 자가 지도 학습을 사용하여 대규모 텍스트 말뭉치에 대해 사전 학습을 거칩니다.
이 사전 학습 단계에서 모델은 일반적인 언어 패턴과 의미를 학습합니다.
Transfer Learning이 종종 사용되어 레이블이 지정된 데이터 세트가 적은 특정 작업에 대해 사전 학습된 모델을 미세 조정할 수 있습니다.
4. Semantic Similarity:
문장 또는 텍스트 구절 간의 의미적 유사성을 측정하는 데 중요한 용도로 사용됩니다.
이러한 모델에 의해 생성된 임베딩은 텍스트의 의미적 내용을 캡처하여 정확한 비교 및 유사성 점수를 매길 수 있습니다.
5. Resource Efficiency:
Reduced Dimensionality: 문장 변환기는 더 복잡한 모델에 비해 더 낮은 차원의 임베딩을 생성하는 경우가 많습니다. 따라서 메모리와 계산 요구 사항이 줄어들어 리소스가 제한된 환경에 적합합니다.
Fewer Training Samples: 더 적은 수의 레이블이 지정된 샘플로도 좋은 성능을 얻을 수 있으므로 데이터가 제한되어 있을 때 효율적입니다.
Inference Speed: 문장 트랜스포머는 일반적으로 추론 시간이 더 빠르기 때문에 빠른 응답이 필요한 실시간 애플리케이션이나 시나리오에 적합합니다.
Scalability: 리소스 요구 사항이 낮기 때문에 연산 능력이 제한된 디바이스를 포함해 다양한 디바이스에 배포할 수 있습니다.
6. Multilingual Capabilities:
여러 언어로 텍스트를 처리해야 하는 애플리케이션에 다용도로 사용할 수 있어 다양한 언어 환경에서 유용하게 사용할 수 있습니다.
Sentence Transformer Tutorial 구성
여기서는 기존 NLP Embedding 라이브러리인 TF-IDF와 Sentence Transformer를 먼저 비교해 보겠다. 그리고 Text 데이터로 Sentence Transformer를 학습시는 방법과 학습 시킨 모델을 추가 데이터로 Fine-tuning하는 절차까지 알아보겠다:
Sentence Transformer vs TF-IDF
Sentence Transformer 모델 학습
Sentence Transformer 모델 Fine-tuning
%pip install sentence_transformers
Setence Transformer vs TF-IDF
TF-IDF는 문장의 핵심 단어를 강조하는 데는 탁월하지만 문맥과 문장 구조의 미묘한 차이를 놓치는 경우가 많습니다. 이와는 대조적으로, BERT가 제공하는 SentenceTransformer는 문맥의 의미를 깊이 파고들어 텍스트를 더욱 풍부하고 미묘하게 이해할 수 있게 해줍니다. 그 여정을 통해 깨달음을 얻었습니다: TF-IDF는 빠른 표면 수준 분석이 필요한 작업에 이상적이며, SentenceTransformer는 깊은 의미론적 인사이트가 필요한 시나리오에서 빛을 발합니다.
tfidf: TF-IDF(용어 빈도 역 문서 빈도) 벡터화기를 사용하여 문장을 벡터로 변환하고 코사인 유사도를 계산합니다.
sentencetransformer: 사전 학습된 BERT 모델('bert-base-nli-mean-tokens')을 사용하여 문장 임베딩을 생성한 다음 코사인 유사도를 계산합니다.
from sentence_transformers import SentenceTransformer
from sklearn.feature_extraction.text import TfidfVectorizer
from scipy.spatial.distance import cosine
import numpy as np
Compute Similarity
compute_similariy 함수는 지정된 방법인 'tfidf' 또는 'sentencetransformer'를 기반으로 두 문장의 유사도를 계산하여 비교합니다.
def compute_similarity(sentence1, sentence2, similarity_type):
if similarity_type == 'tfidf':
# TF-IDF 벡터라이저를 초기화합니다.
vectorizer = TfidfVectorizer()
# vetorizer를 두 문장에 맞추고 TF-IDF 벡터로 변환합니다.
tfidf_matrix = vectorizer.fit_transform([sentence1, sentence2])
# Compute the cosine similarity between the two TF-IDF vectors
similarity = 1 - cosine(
tfidf_matrix[0].toarray()[0],
tfidf_matrix[1].toarray()[0]
)
elif similarity_type == 'sentencetransformer':
# SentenceTransformer 모델을 초기화합니다.
model = SentenceTransformer('bert-base-nli-mean-tokens')
# Sentence embeddings을 계산합니다.
embeddings = model.encode([sentence1, sentence2])
# 두 임베딩 간의 코사인 유사도를 계산합니다.
similarity = 1 - cosine(
embeddings[0],
embeddings[1])
else:
raise ValueError("Invalid similarity_type. Choose either 'tfidf' or 'sentencetransformer'.")
return similarity
Text Similarity
TFIDF(용어 빈도 역 문서 빈도)와 SentenceTransformer입니다.
각 방법에 따라 두 문장이 얼마나 유사한지 측정하기 위해 compute_similarity 함수를 사용하여 자연어 처리에서 의미 분석에 대한 다양한 접근 방식을 보여줍니다.
sentence1 = "Generative AI is useful for programming."
sentence2 = "Coding will be easy to implement for beginner."
print('TFIDF Similarity : ', compute_similarity(sentence1, sentence2, 'tfidf'))
print('SentenceTransformer Similarity : ', compute_similarity(sentence1, sentence2, 'sentencetransformer'))
TFIDF Similarity : 0.07874460345594514
modules.json: 0%| | 0.00/229 [00:00<?, ?B/s]
config_sentence_transformers.json: 0%| | 0.00/122 [00:00<?, ?B/s]
README.md: 0%| | 0.00/3.99k [00:00<?, ?B/s]
sentence_bert_config.json: 0%| | 0.00/53.0 [00:00<?, ?B/s]
/home/kubwa/anaconda3/envs/pytorch/lib/python3.11/site-packages/huggingface_hub/file_download.py:1132: FutureWarning: `resume_download` is deprecated and will be removed in version 1.0.0. Downloads always resume when possible. If you want to force a new download, use `force_download=True`.
warnings.warn(
config.json: 0%| | 0.00/625 [00:00<?, ?B/s]
model.safetensors: 0%| | 0.00/438M [00:00<?, ?B/s]
tokenizer_config.json: 0%| | 0.00/399 [00:00<?, ?B/s]
vocab.txt: 0%| | 0.00/232k [00:00<?, ?B/s]
tokenizer.json: 0%| | 0.00/466k [00:00<?, ?B/s]
added_tokens.json: 0%| | 0.00/2.00 [00:00<?, ?B/s]
special_tokens_map.json: 0%| | 0.00/112 [00:00<?, ?B/s]
1_Pooling/config.json: 0%| | 0.00/190 [00:00<?, ?B/s]
SentenceTransformer Similarity : 0.6271942831121081
sentence1 = "The cat quickly jumped over the small fence."
sentence2 = "The feline swiftly leaped above the low barrier."
print('TFIDF Similarity : ', compute_similarity(sentence1, sentence2, 'tfidf'))
print('SentenceTransformer Similarity : ', compute_similarity(sentence1, sentence2, 'sentencetransformer'))
TFIDF Similarity : 0.2523342014336961
/home/kubwa/anaconda3/envs/pytorch/lib/python3.11/site-packages/huggingface_hub/file_download.py:1132: FutureWarning: `resume_download` is deprecated and will be removed in version 1.0.0. Downloads always resume when possible. If you want to force a new download, use `force_download=True`.
warnings.warn(
SentenceTransformer Similarity : 0.5344065503281913
sentence1 = "We love each other."
sentence2 = "We like eating vegitable either."
print('TFIDF Similarity : ', compute_similarity(sentence1, sentence2, 'tfidf'))
print('SentenceTransformer Similarity : ', compute_similarity(sentence1, sentence2, 'sentencetransformer'))
Multiple Negative Ranking Loss (MNR Loss) : 학습 데이터에 긍정적인 관련 텍스트 쌍이 있는 경우.
Triplet Loss : 앵커 텍스트에 긍정 및 부정 텍스트가 연결된 경우.
Contrastive Loss :긍정과 부정 쌍이 모두 있는 경우.
from sentence_transformers import SentenceTransformer, models
# 1단계: 기존 언어 모델 사용
word_embedding_model = models.Transformer('distilroberta-base')
# 2단계: 토큰 임베딩에 Pooling 함수 사용
pooling_model = models.Pooling(word_embedding_model.get_word_embedding_dimension())
# 마지막으로 module 인수를 사용하여 1단계와 2단계를 결합
model = SentenceTransformer(modules=[word_embedding_model, pooling_model])
/home/kubwa/anaconda3/envs/pytorch/lib/python3.11/site-packages/huggingface_hub/file_download.py:1132: FutureWarning: `resume_download` is deprecated and will be removed in version 1.0.0. Downloads always resume when possible. If you want to force a new download, use `force_download=True`.
warnings.warn(
print(f"- The {dataset_id} dataset has {dataset['train'].num_rows} examples.")
print(f"- Each example is a {type(dataset['train'][0])} with a {type(dataset['train'][0]['set'])} as value.")
print(f"- Examples look like this: {dataset['train'][0]}")
- The embedding-data/QQP_triplets dataset has 101762 examples.
- Each example is a <class 'dict'> with a <class 'dict'> as value.
- Examples look like this: {'set': {'query': 'Why in India do we not have one on one political debate as in USA?', 'pos': ['Why cant we have a public debate between politicians in India like the one in US?'], 'neg': ['Can people on Quora stop India Pakistan debate? We are sick and tired seeing this everyday in bulk?', 'Why do politicians, instead of having a decent debate on issues going in and around the world, end up fighting always?', 'Can educated politicians make a difference in India?', 'What are some unusual aspects about politics and government in India?', 'What is debate?', 'Why does civic public communication and discourse seem so hollow in modern India?', 'What is a Parliamentary debate?', "Why do we always have two candidates at the U.S. presidential debate. yet the ballot has about 7 candidates? Isn't that a misrepresentation of democracy?", 'Why is civic public communication and discourse so hollow in modern India?', "Aren't the Presidential debates teaching our whole country terrible communication skills and why is deliberate misrepresentation even allowed?", 'Why are most Indian politicians uneducated?', 'Does Indian political leaders capable of doing face to face debates while running for office?', 'What is wrong with the Indian political system and the environment it has built in connection with the people of India? Have parties divided people more?', 'What is a debate?', 'Why do we have legislative council in india?', 'Why does the office of president of India, being politically neutral, not ask for Population control in India?', "Why don't we discuss tax and foreign policies more in Indian elections but are instead obsessed with socialist schemes?", 'Why do Indian politicians lack nationalist thinking?', 'Do you hate indian politicians?', 'Is India facing more stessful times and politically charged atmosphere when compared to Congress regime?', 'Who is the best politician in India? Why?', "We all know about the present condition of Indian politicians; they are all just using us to run their train, but still, they win elections and rule over us. Why aren't people giving their vote to NOTA?", 'Who are clean politicians in India?', 'Why are you not believing in Democracy of India?', 'What does politics in India mean? What are they actually doing?', 'What are the strongest arguments for a debate in favour of brain drain in India and what sources must be used for making a good short speech?', 'Do we really need an election commission in India?', 'Why is there no concept of political correctness in India? Is it a good thing or a bad thing?', 'Why is population control not on agenda of any political party in India?', 'Who are some of the most dangerous or worst politicians in India?']}}
Example를 InputExamples로 변환합니다.
from sentence_transformers import InputExample
train_examples = []
train_data = dataset['train']['set']
# For agility we only 1/2 of our available data
n_examples = dataset['train'].num_rows // 2
for i in range(n_examples):
example = train_data[i]
train_examples.append(InputExample(texts=[example['query'], example['pos'][0], example['neg'][0]]))
print(f"We have a {type(train_examples)} of length {len(train_examples)} containing {type(train_examples[0])}'s.")
We have a <class 'list'> of length 50881 containing <class 'sentence_transformers.readers.InputExample.InputExample'>'s.
The `save_to_hub` method is deprecated and will be removed in a future version of SentenceTransformers. Please use `push_to_hub` instead for future model uploads.
model.safetensors: 0%| | 0.00/328M [00:00<?, ?B/s]
'https://huggingface.co/nowave/nowave/commit/89095a838e91b7bcc37d48e750cb0bf5c877997a'
Fine-tuning Sentence Transfomer
Model Load
model = SentenceTransformer('embedding-data/distilroberta-base-sentence-transformer')
modules.json: 0%| | 0.00/229 [00:00<?, ?B/s]
config_sentence_transformers.json: 0%| | 0.00/124 [00:00<?, ?B/s]
README.md: 0%| | 0.00/4.04k [00:00<?, ?B/s]
sentence_bert_config.json: 0%| | 0.00/53.0 [00:00<?, ?B/s]
/home/kubwa/anaconda3/envs/pytorch/lib/python3.11/site-packages/huggingface_hub/file_download.py:1132: FutureWarning: `resume_download` is deprecated and will be removed in version 1.0.0. Downloads always resume when possible. If you want to force a new download, use `force_download=True`.
warnings.warn(
config.json: 0%| | 0.00/671 [00:00<?, ?B/s]
pytorch_model.bin: 0%| | 0.00/329M [00:00<?, ?B/s]
tokenizer_config.json: 0%| | 0.00/386 [00:00<?, ?B/s]
vocab.json: 0%| | 0.00/798k [00:00<?, ?B/s]
merges.txt: 0%| | 0.00/456k [00:00<?, ?B/s]
tokenizer.json: 0%| | 0.00/2.11M [00:00<?, ?B/s]
special_tokens_map.json: 0%| | 0.00/280 [00:00<?, ?B/s]
1_Pooling/config.json: 0%| | 0.00/190 [00:00<?, ?B/s]
print(f"Examples look like this: {dataset['train']['set'][0]}")
Examples look like this: ["The USHL completed an expansion draft on Monday as 10 players who were on the rosters of USHL teams during the 2009-10 season were selected by the League's two newest entries, the Muskegon Lumberjacks and Dubuque Fighting Saints.", 'USHL completes expansion draft']
train_examples = []
train_data = dataset['train']['set']
n_examples = dataset['train'].num_rows
for i in range(n_examples):
example = train_data[i]
train_examples.append(InputExample(texts=[example[0], example[1]]))
train_dataloader = DataLoader(
train_examples,
shuffle=True,
batch_size=64
)
Loss & Parameters
train_loss = losses.MultipleNegativesRankingLoss(model=model)
num_epochs = 10
warmup_steps = int(len(train_dataloader) * num_epochs * 0.1) #10% of train data
The `save_to_hub` method is deprecated and will be removed in a future version of SentenceTransformers. Please use `push_to_hub` instead for future model uploads.
model.safetensors: 0%| | 0.00/328M [00:00<?, ?B/s]
'https://huggingface.co/nowave/distilroberta-base-sentence-transformer/commit/292bda51be2c982ba6d0f3625a42bd01dbec9f0a'