HyperAI초신경
Back to Headlines

Sentence Transformers로 희소 임베딩 모델 미세 조정 방법 소개

4일 전

Sentence Transformers는 다양한 응용 프로그램을 위해 임베딩 및 재순위 매기기 모델을 사용하고 훈련하는 데 사용되는 파이썬 라이브러리입니다. 최근 몇 번의 주요 버전에서는 훈련에 있어 중요한 개선 사항을 도입했습니다. 이번 포스트에서는 어떻게 이 라이브러리를 사용하여 희소 인코더/임베딩 모델을 미세 조정(finetune)하고, 이를 왜 수행해야 하는지 설명합니다. 이러한 과정은 하이브리드 검색이나 검색 후 재순위 매기기에 특히 유용하며, 비용과 효율성 면에서 경제적인 모델을 생성합니다. 희소 임베딩 모델이란? 임베딩 모델은 일반적으로 텍스트와 같은 입력을 벡터 표현(임베딩)으로 변환하는 모델로, 이 벡터는 입력의 의미를 포착합니다. 밀집된 임베딩 모델은 보통 384, 768, 1024 차원 등의 저차원 벡터를 생성하며, 대부분의 값이 0이 아닌 반면, 희소 임베딩 모델은 30,000+ 차원의 고차원 벡터를 생성하며, 대부분의 값이 0입니다. 각 활성 차원(값이 0이 아닌 차원)은 모델의 어휘 사전(vocabulary)에 해당하는 특정 토큰과 대응합니다. 예를 들어, naver/splade-v3라는 최신 희소 임베딩 모델을 살펴보겠습니다: ```python from sentence_transformers import SparseEncoder Hub에서 다운로드 model = SparseEncoder("naver/splade-v3") 추론 실행 sentences = [ "오늘 날씨가 좋습니다.", "날씨가 매우 맑네요!", "그는 경기장으로 운전했습니다.", ] embeddings = model.encode(sentences) print(embeddings.shape) (3, 30522) 임베딩 간 유사도 점수 얻기 similarities = model.similarity(embeddings, embeddings) print(similarities) tensor([[ 32.4323, 5.8528, 0.0258], [ 5.8528, 26.6649, 0.0302], [ 0.0258, 0.0302, 24.0839]]) ``` 해체(decode) 메서드를 사용하면 임베딩에서 가장 높은 값을 가진 상위 10개 토큰을 확인할 수 있어, 어떤 토큰이 임베딩에 가장 큰 기여를 하는지를 해석할 수 있습니다. 쿼리 및 문서 확장 신경망 기반 희소 임베딩 모델의 핵심 구성 요소 중 하나는 쿼리/문서 확장입니다. 전통적인 어휘 기반 방법(BM25 등)은 정확한 토큰만 매치하지만, 신경망 기반 희소 모델은 일반적으로 동의어나 관련 용어를 자동으로 확장합니다. 예를 들어, "오늘 날씨가 좋습니다."라는 문장은 "아름다움", "시원함", "예쁘다", "좋다" 등의 단어로 확장됩니다. "날씨가 매우 맑네요!"는 "날씨", "여름", "태양"으로 확장됩니다. 이러한 확장은 정확한 토큰 매치 없이도 의미적으로 관련된 내용을 매칭하고, 오타 처리 및 어휘 불일치 문제를 해결할 수 있게 합니다. 희소 임베딩 모델의 장점 신경망 기반 희소 임베딩 모델은 전통적인 어휘 기반 방법(BM25)과 밀집된 임베딩 모델(Sentence Transformers) 사이의 귀중한 위치를 차지합니다. 다음과 같은 장점을 가지고 있습니다: 효율성: 희소 표현(sparse representation)의 효율성을 유지하면서도 의미적 유사도를 잘 포착합니다. 해석 가능성: 각 활성 차원이 특정 토큰과 대응하기 때문에, 결과를 쉽게 해석할 수 있습니다. ** expansibility**: 쿼리/문서 확장을 통해 동의어나 관련 용어를 처리할 수 있습니다. 왜 미세 조정(finetune)을 하는가? 대부분의 희소 임베딩 모델은 쿼리/문서 확장을 사용하여 동일한 의미를 가진 텍스트를 매칭할 수 있지만, 모든 단어를 공유하지 않더라도 이를 인식해야 합니다. 예를 들어, "grocery"를 포함하는 텍스트는 "supermarket", "food", "market"로 확장될 것이지만, "cephalalgia"라는 단어가 "headache"로 확장되지 않는 등의 문제를 겪을 수 있습니다. 미세 조정을 통해 모델은 특정 도메인이나 언어에만 초점을 맞출 수 있습니다. 훈련 구성 요소 Sentence Transformers 모델을 훈련하는 데에는 다음 구성 요소들이 필요합니다: 모델: 훈련하거나 미세 조정할 모델. 이는 사전 훈련된 희소 인코더 모델이나 기본 모델일 수 있습니다. 데이터셋: 훈련 및 평가에 사용될 데이터. 손실 함수: 모델 성능을 측정하고 최적화 과정을 안내하는 함수. 훈련 인수(선택 사항): 훈련 성능과 디버깅/추적을 영향을 주는 매개변수들. 평가자(선택 사항): 훈련 전, 중, 후에 모델을 평가하는 도구. 트레이너: 모델, 데이터셋, 손실 함수, 기타 구성 요소들을 결합하여 훈련을 수행합니다. 모델 Sparse Encoder 모델은 여러 모듈(Module)로 구성되어 있으며, 이는 희소 인코더 전용 모듈이나 사용자 정의 모듈일 수 있습니다. 사전 훈련된 Sparse Encoder 모델을 미세 조정하려면, modules.json 파일이 있는 경우 모듈에 대해 걱정할 필요가 없습니다. ```python from sentence_transformers import SparseEncoder model = SparseEncoder("naver/splade-cocondenser-ensembledistil") ``` 새로운 체크포인트나 처음부터 훈련하려면, 다음과 같은 일반적인 아키텍처를 사용할 수 있습니다: Splade Splade 모델은 MLMTransformer와 SpladePooling 모듈을 사용합니다.前者加载预训练的掩码语言建模模型(例如BERT、RoBERTa、DistilBERT等),后者将MLMHead的输出池化以生成与词汇表大小相同的单个稀疏嵌入。 ```python from sentence_transformers import models, SparseEncoder MLM Transformer 초기화 (마스크 채우기 모델 사용) mlm_transformer = models.MLMTransformer("google-bert/bert-base-uncased") SpladePooling 모듈 초기화 splade_pooling = models.SpladePooling(pooling_strategy="max") Splade 모델 생성 model = SparseEncoder(modules=[mlm_transformer, splade_pooling]) ``` 이 아키텍처는 SparseEncoder에 마스크 채우기 모델 아키텍처를 제공하면 기본적으로 사용되므로, 다음과 같이 단축 방식으로 사용할 수 있습니다: ```python from sentence_transformers import SparseEncoder model = SparseEncoder("google-bert/bert-base-uncased") ``` Inference-free Splade Inference-free Splade는 쿼리와 문서에 대해 다른 모듈을 사용하는 Router 모듈을 활용합니다. 이런 아키텍처의 경우, 문서 부분은 전통적인 Splade 아키텍처(MLMTransformer + SpladePooling 모듈)이고, 쿼리 부분은 SparseStaticEmbedding 모듈입니다. 이 모듈은 쿼리 내의 모든 토큰에 대해 미리 계산된 점수를 반환합니다. ```python from sentence_transformers import SparseEncoder, models 문서 인코딩용 MLM Transformer 초기화 doc_encoder = models.MLMTransformer("google-bert/bert-base-uncased") 쿼리와 문서에 대한 서로 다른 경로를 가진 Router 모델 생성 router = models.Router.for_query_document( query_modules=[models.SparseStaticEmbedding(tokenizer=doc_encoder.tokenizer, frozen=False)], document_modules=[doc_encoder, models.SpladePooling("max")], ) Inference-free 모델 생성 model = SparseEncoder(modules=[router], similarity_fn_name="dot") ``` 이 아키텍처는 쿼리 시간 처리가 중요한 검색 애플리케이션에서 유용합니다. 쿼리 시간 처리는 가벼운 SparseStaticEmbedding 접근 방식을 사용하여 빠르게 이루어지며, 문서 인덱싱은 오프라인으로 이루어집니다. Contrastive Sparse Representation (CSR) Contrastive Sparse Representation (CSR) 모델은 밀집된 Sentence Transformers 모델 위에 SparseAutoEncoder 모듈을 적용합니다. 이는 일반적으로 Transformer와 Pooling 모듈로 구성됩니다. ```python from sentence_transformers import models, SparseEncoder Transformer 초기화 (밀집 인코더 모델) transformer = models.Transformer("google-bert/bert-base-uncased") Pooling 초기화 pooling = models.Pooling(transformer.get_word_embedding_dimension(), pooling_mode="mean") SparseAutoEncoder 모듈 초기화 sparse_auto_encoder = models.SparseAutoEncoder( input_dim=transformer.get_word_embedding_dimension(), hidden_dim=4 * transformer.get_word_embedding_dimension(), k=256, # 유지할 상위 값 개수 k_aux=512, # 보조 손실에 사용할 상위 값 개수 ) CSR 모델 생성 model = SparseEncoder(modules=[transformer, pooling, sparse_auto_encoder]) ``` 데이터셋 SparseEncoderTrainer는 Hugging Face Datasets Hub에서 데이터를 로드하거나 로컬 데이터(CSV, JSON, Parquet, Arrow, SQL 등)를 사용할 수 있습니다. Hugging Face Hub에서 데이터를 로드하려면 load_dataset 함수를 사용합니다. ```python from datasets import load_dataset train_dataset = load_dataset("sentence-transformers/natural-questions", split="train") print(train_dataset) """ Dataset({ features: ['query', 'answer'], num_rows: 100231 }) """ ``` 로컬 데이터를 로드하려면 다음과 같이 사용할 수 있습니다: ```python from datasets import load_dataset dataset = load_dataset("csv", data_files="my_file.csv") ``` 손실 함수 손실 함수는 모델이 주어진 배치 데이터에서 얼마나 잘 작동하는지를 측정하고, 최적화 과정을 안내합니다. 희소 인코더를 훈련하려면 SpladeLoss 또는 CSRLoss를 사용해야 합니다. 이들은 주 손실 함수 위에 희소성 규제를 추가하는 래퍼 손실 함수입니다. ```python from sentence_transformers import SparseEncoder, losses 모델 로드 model = SparseEncoder("distilbert/distilbert-base-uncased") 주 손실 함수와 함께 SpladeLoss 초기화 loss = losses.SpladeLoss( model=model, loss=losses.SparseMultipleNegativesRankingLoss(model=model), query_regularizer_weight=5e-5, # 쿼리 손실 가중치 document_regularizer_weight=3e-5, ) ``` 훈련 인수 SparseEncoderTrainingArguments 클래스를 사용하여 훈련 성능과 추적/디버깅에 영향을 주는 매개변수를 지정할 수 있습니다. 이는 선택 사항이지만, 실험을 통해 훈련 효율성을 개선하고 훈련 과정을 이해하는 데 도움이 될 수 있습니다. ```python from sentence_transformers import SparseEncoderTrainingArguments args = SparseEncoderTrainingArguments( output_dir="models/splade-distilbert-base-uncased-nq", num_train_epochs=1, per_device_train_batch_size=16, per_device_eval_batch_size=16, learning_rate=2e-5, warmup_ratio=0.1, fp16=True, # FP16을 지원하지 않는 GPU의 경우 False로 설정 bf16=False, # BF16을 지원하는 GPU의 경우 True로 설정 batch_sampler=BatchSamplers.NO_DUPLICATES, eval_strategy="steps", eval_steps=100, save_strategy="steps", save_steps=100, save_total_limit=2, logging_steps=100, run_name="splade-distilbert-base-uncased-nq", ) ``` 평가자 SparseEncoderTrainer에 eval_dataset을 제공하면 훈련 중 평가 손실을 얻을 수 있습니다. 하지만 더 구체적인 메트릭을 얻기 위해서는 평가자를 사용할 수 있습니다. 이는 훈련 전, 중, 후에 모델 성능을 평가하는 데 유용합니다. 트레이너 모든 구성 요소가 모여 훈련을 수행하는 SparseEncoderTrainer입니다. 다음은 이러한 구성 요소들을 결합하여 훈련하는 스크립트 예시입니다: ```python import logging from datasets import load_dataset from sentence_transformers import ( SparseEncoder, SparseEncoderTrainingArguments, SparseEncoderTrainer, ) from sentence_transformers.models import Router from sentence_transformers.sparse_encoder.evaluation import SparseNanoBEIREvaluator from sentence_transformers.sparse_encoder.losses import SparseMultipleNegativesRankingLoss, SpladeLoss logging.basicConfig(format="%(asctime)s - %(message)s", datefmt="%Y-%m-%d %H:%M:%S", level=logging.INFO) 1. 미세 조정할 모델 로드 mlm_transformer = models.MLMTransformer("distilbert/distilbert-base-uncased", tokenizer_args={"model_max_length": 512}) splade_pooling = models.SpladePooling(pooling_strategy="max", word_embedding_dimension=mlm_transformer.get_sentence_embedding_dimension()) router = models.Router.for_query_document( query_modules=[models.SparseStaticEmbedding(tokenizer=mlm_transformer.tokenizer, frozen=False)], document_modules=[mlm_transformer, splade_pooling], ) model = SparseEncoder( modules=[router], model_card_data=models.SparseEncoderModelCardData( language="en", license="apache-2.0", model_name="Inference-free SPLADE distilbert-base-uncased trained on Natural-Questions tuples", ), ) 2. 훈련용 데이터셋 로드 full_dataset = load_dataset("sentence-transformers/natural-questions", split="train").select(range(100_000)) dataset_dict = full_dataset.train_test_split(test_size=1_000, seed=12) train_dataset = dataset_dict["train"] eval_dataset = dataset_dict["test"] 3. 손실 함수 정의 loss = losses.SpladeLoss( model=model, loss=losses.SparseMultipleNegativesRankingLoss(model=model), query_regularizer_weight=0, document_regularizer_weight=3e-3, ) 4. (선택 사항) 훈련 인수 지정 args = SparseEncoderTrainingArguments( output_dir="models/inference-free-splade-distilbert-base-uncased-nq", num_train_epochs=1, per_device_train_batch_size=16, per_device_eval_batch_size=16, learning_rate=2e-5, learning_rate_mapping={r"SparseStaticEmbedding.weight": 1e-3}, warmup_ratio=0.1, fp16=True, bf16=False, batch_sampler=BatchSamplers.NO_DUPLICATES, eval_strategy="steps", eval_steps=1000, save_strategy="steps", save_steps=1000, save_total_limit=2, logging_steps=200, run_name="inference-free-splade-distilbert-base-uncased-nq", ) 5. (선택 사항) 평가자 생성 및 기본 모델 평가 dev_evaluator = SparseNanoBEIREvaluator(dataset_names=["msmarco", "nfcorpus", "nq"], batch_size=16) 6. 트레이너 생성 및 훈련 trainer = SparseEncoderTrainer( model=model, args=args, train_dataset=train_dataset, eval_dataset=eval_dataset, loss=loss, evaluator=dev_evaluator, ) trainer.train() 7. 훈련 후 모델 성능 다시 평가 dev_evaluator(model) 8. 훈련된 모델 저장 model.save_pretrained("models/inference-free-splade-distilbert-base-uncased-nq/final") 9. (선택 사항) Hugging Face Hub에 업로드 model.push_to_hub("inference-free-splade-distilbert-base-uncased-nq") ``` 이 스크립트를 실행한 후, sparse-encoder/example-inference-free-splade-distilbert-base-uncased-nq 모델이 업로드되었습니다. 이 모델은 NanoMSMARCO에서 0.5241 NDCG@10, NanoNFCorpus에서 0.3299 NDCG@10, NanoNQ에서 0.5357 NDCG@10의 점수를 받았으며, 이는 Natural Questions 데이터셋의 100,000개 쌍으로 훈련된 inference-free distilbert 기반 모델로서 좋은 결과입니다. 산업 인사이더의 평가 및 회사 프로필 희소 임베딩 모델은 검색 성능을 크게 향상시키는 동시에 효율성을 유지하는 데 효과적입니다. 특히 하이브리드 검색 방법을 사용하면, 밀집 임베딩과 희소 임베딩을 결합하여 12.3%와 18.7%의 성능 향상을 볼 수 있습니다. 이를 통해 쿼리 시간을 줄이고 비용을 절감할 수 있습니다. Sentence Transformers는 이러한 모델의 훈련과 배포를 위한 강력한 도구를 제공하며, Hugging Face Hub에서는 다양한 사전 훈련된 희소 인코더 모델을 찾을 수 있습니다.

Related Links