--- language: - ko license: apache-2.0 library_name: sentence-transformers pipeline_tag: sentence-similarity tags: - sentence-transformers - sentence-similarity - feature-extraction - static-embedding - model2vec - korean - ko - klue - korsts datasets: - kakaobrain/kor_nli - mteb/KorSTS - klue/klue base_model: klue/roberta-base --- # kor-static-embedding-512 한국어 특화 **Static Embedding** 모델 — 트랜스포머 없이 토큰 임베딩 lookup + 평균만으로 동작하는 초경량 한국어 문장 임베딩. **68MB** 크기로 **BGE-M3 성능의 92%** 달성 (한국어 STS 평균 Spearman 기준), CPU에서 **158배 빠른** 추론. ## 모델 개요 | 항목 | 값 | |---|---| | 아키텍처 | `sentence_transformers.models.StaticEmbedding` ([model2vec](https://github.com/MinishLab/model2vec) 계열) | | Base 토크나이저 | `klue/roberta-base` (한국어 vocab 32K) | | 임베딩 차원 | **512** | | 파라미터 수 | 16,384,000 | | 모델 크기 | **68MB** | | 학습 데이터 | KorNLI (multi_nli + snli) + KorSTS + KLUE-STS | | 추론 환경 | CPU에서 최적 (GPU 불필요) | | 다국어 | 한국어 전용 | ## 설치 및 사용법 ### 1단계: 설치 ```bash # 가상환경 권장 python3 -m venv .venv source .venv/bin/activate # Windows: .venv\Scripts\activate # 패키지 설치 (torch 포함, CPU 전용 가능) pip install sentence-transformers ``` > 필요 패키지는 `sentence-transformers`만 설치하면 자동으로 `torch`, `transformers`, `huggingface_hub` 등 의존성이 따라옵니다. > 디스크 절약을 원하면 CPU 전용 torch: `pip install torch --index-url https://download.pytorch.org/whl/cpu` ### 2단계: 모델 로드 ```python from sentence_transformers import SentenceTransformer model = SentenceTransformer("kekeappa/kor-static-embedding-512") # 첫 실행 시 모델 자동 다운로드 (~68MB) # 캐시 위치: ~/.cache/huggingface/hub/ ``` ### 3단계: 임베딩 추출 ```python sentences = [ "오늘 날씨가 정말 좋네요.", "햇살이 따뜻하고 기분 좋은 하루입니다.", "비가 와서 우산을 챙겨야 합니다.", ] embeddings = model.encode(sentences, normalize_embeddings=True) print(embeddings.shape) # (3, 512) # 코사인 유사도 (정규화된 벡터의 내적 = 코사인) similarity_matrix = embeddings @ embeddings.T print(similarity_matrix) ``` ### 4단계: 활용 예시 #### A. 의미 검색 (Semantic Search) ```python import numpy as np # 코퍼스 인덱싱 (한 번만) corpus = [ "김치찌개 만드는 법", "딥러닝 입문 강의", "주말 등산 추천 코스", "파이썬 데이터 분석", "제주도 여행 일정", ] corpus_emb = model.encode(corpus, normalize_embeddings=True, batch_size=64) # 쿼리 (반복 가능) def search(query, top_k=3): q_emb = model.encode([query], normalize_embeddings=True) scores = (q_emb @ corpus_emb.T)[0] top_idx = np.argsort(-scores)[:top_k] return [(corpus[i], float(scores[i])) for i in top_idx] print(search("인공지능 학습")) # → [('딥러닝 입문 강의', 0.41), ('파이썬 데이터 분석', 0.18), ...] ``` #### B. 두 문장 유사도 ```python emb = model.encode(["좋은 아침입니다", "굿모닝이에요"], normalize_embeddings=True) similarity = float((emb[0] * emb[1]).sum()) print(f"유사도: {similarity:.4f}") ``` #### C. 클러스터링 (KMeans) ```python from sklearn.cluster import KMeans sentences = [ "김치찌개 끓이는 법", "된장찌개 만들기", "비빔밥 레시피", "파이썬 입문", "자바스크립트 기초", "리액트 사용법", "제주도 여행", "부산 여행 코스", "경주 역사 탐방", ] emb = model.encode(sentences, normalize_embeddings=True) labels = KMeans(n_clusters=3, random_state=42, n_init=10).fit_predict(emb) for i, s in enumerate(sentences): print(f"[{labels[i]}] {s}") ``` #### D. 벡터 DB 연동 (FAISS / Qdrant / Chroma) ```python # FAISS 예시 import faiss import numpy as np embeddings = model.encode(corpus, normalize_embeddings=True).astype("float32") index = faiss.IndexFlatIP(512) # Inner Product (정규화 했으므로 = 코사인) index.add(embeddings) # 검색 query_emb = model.encode(["인공지능"], normalize_embeddings=True).astype("float32") distances, indices = index.search(query_emb, k=3) for idx, dist in zip(indices[0], distances[0]): print(f" [{dist:.4f}] {corpus[idx]}") ``` ### 주요 옵션 | 옵션 | 설명 | 기본값 | 권장 | |---|---|---|---| | `normalize_embeddings` | L2 정규화 (코사인 유사도용) | `False` | **`True`** | | `batch_size` | 배치 크기 (CPU에서 클수록 빠름) | 32 | **128~512** | | `show_progress_bar` | tqdm 진행바 | `True` | 대량 처리 시 `True`, API 호출 시 `False` | | `convert_to_numpy` | numpy 배열로 변환 | `True` | 대부분 `True` | | `device` | "cpu" / "cuda" / "mps" | 자동 감지 | CPU 최적 (GPU 불필요) | ### 트러블슈팅 | 문제 | 원인 / 해결 | |---|---| | `ModuleNotFoundError: sentence_transformers` | `pip install sentence-transformers` | | 첫 로딩이 너무 느림 | 모델 다운로드 중 (~68MB). 캐시 후 0.3초만에 로드 | | 한국어 문장에서 점수가 너무 낮음 | `normalize_embeddings=True` 누락 확인 | | 메모리 부족 | `batch_size` 줄이기 (예: 32 → 8) | | 어순/부정문 구분 안 됨 | Static Embedding의 본질적 한계 (아래 [한계](#한계) 참조) | ## 벤치마크 (BAAI/bge-m3 비교) ### 성능 (Spearman 상관계수) | 벤치마크 | N | **kor-static-embedding-512** | BAAI/bge-m3 | 비율 | |---|---:|---:|---:|---:| | KorSTS-test | 1,376 | **0.7758** | 0.8026 | **96.7%** | | KorSTS-valid | 1,465 | **0.8248** | 0.8317 | **99.2%** | | KLUE-STS-validation | 519 | **0.7119** | 0.8773 | 81.1% | | **평균** | — | **0.7708** | 0.8372 | **92.1%** | ### 크기·자원 (% 환산, BGE-M3 = 100%) | 항목 | BGE-M3 | **kor-static-embedding-512** | 비율 | |---|---:|---:|---:| | 파라미터 수 | 100% (567.8M) | **2.89%** (16.4M) | 97.1% 절약 | | 디스크 크기 | 100% (2,168MB) | **3.14%** (68MB) | 96.9% 절약 | | 임베딩 차원 | 100% (1024) | **50%** (512) | 50% 축소 | ### 속도 상세 (CPU, Apple M2) #### 1. 모델 로드 시간 — 낮을수록 좋음 | 모델 | 로드 시간 | 비율 | |---|---:|---:| | BGE-M3 | 24,042ms (24.0초) | 100% | | **kor-static-embedding-512** | **310ms** | **1.29%** (78× 빠름) | #### 2. 단일 쿼리 지연시간 — 낮을수록 좋음 | 모델 | p50 | p95 | p99 | 비율 (p50) | |---|---:|---:|---:|---:| | BGE-M3 | 23.02ms | 24.30ms | 31.50ms | 100% | | **kor-static-embedding-512** | **0.96ms** | 2.03ms | 2.37ms | **4.19%** (24× 빠름) | #### 3. 배치 처리량 — 높을수록 좋음 | Batch | BGE-M3 | **kor-static-embedding-512** | 비율 | |---:|---:|---:|---:| | 1 | 42.5 sent/s | 1,132.9 sent/s | **2,662%** (26.6× 빠름) | | 8 | 252.1 sent/s | 6,490.3 sent/s | **2,574%** (25.7× 빠름) | | 32 | 346.3 sent/s | 20,095.5 sent/s | **5,803%** (58.0× 빠름) | | 128 | 343.3 sent/s | 39,568.9 sent/s | **11,525%** (115× 빠름) | | **512** | 324.6 sent/s | **92,468.3 sent/s** | **28,489%** (285× 빠름) | → BGE-M3는 batch 32에서 처리량 포화, **kor-static-embedding-512는 batch 512까지 선형 확장**. #### 4. 실전 시나리오 — 대규모 인덱싱 시간 | 문서 수 | BGE-M3 | **kor-static-embedding-512** | 비율 | |---:|---:|---:|---:| | 1만 건 | 38.2초 | **0.3초** | 0.82% | | 10만 건 | 6.4분 | **3.1초** | 0.82% | | 100만 건 | 1.1시간 | **31초** | 0.82% | | 1천만 건 | 10.6시간 | **5.2분** | 0.82% | | 1억 건 (추정) | 4.4일 | **52분** | 0.82% | → **100만 건 인덱싱: 1시간 → 30초** (122× 단축) #### 5. 비용·자원 절감 요약 | 항목 | 절감률 | |---|---:| | CPU 인프라 비용 (같은 처리량 기준) | **~99% 절감** | | 메모리 사용량 | **~97% 절감** | | 응답 지연 (사용자 체감) | **~96% 단축** | | 콜드 스타트 (서버리스) | 24초 → 0.3초 (**99% 단축**) | ## 학습 레시피 **Stage 1: KorNLI MultipleNegativesRankingLoss** - 데이터: `kakaobrain/kor_nli` (multi_nli + snli) - entailment를 positive, contradiction을 hard negative로 → **277,826 triplet** - Loss: `MultipleNegativesRankingLoss` - batch=2048, lr=2e-1, epoch=1 - 학습 시간: 약 25초 (A100 80GB PCIe) **Stage 2: STS regression fine-tune** - 데이터: KorSTS-train (5,691) + KLUE-STS-train (11,668) = 17,359 pairs - Loss: `CosineSimilarityLoss` - batch=64, lr=2e-2, epoch=4 - 학습 시간: 약 18초 (A100 80GB PCIe) - best checkpoint: KorSTS-valid Spearman 기준 **Stage 1 종료 시점 점수** (참고): - KorSTS-test Spearman: 0.7519 - KorSTS-valid Spearman: 0.7983 - KLUE-STS-val Spearman: 0.5757 → Stage 2 (STS regression)가 특히 KLUE 점수를 0.58 → 0.71로 크게 끌어올림. ## 적합한 용도 ✅ **권장** - 대규모 RAG의 1차 retrieval (수백만 문서를 빠르게 좁히기) - 의미 기반 검색, FAQ 매칭, 추천 시스템 - 클러스터링, 중복 제거, 카테고리 분류 - 온디바이스 / 모바일 한국어 임베딩 - 2-stage 검색: kor-static-512(1차) + BGE-M3(2차 재정렬) ❌ **부적합** - 어순·문맥 미세 차이가 중요한 작업 (어순 정보 없음) - 다국어 검색 (한국어 전용) - KLUE 같은 뉴스 도메인에서 절대 최고 성능 필요시 (BGE-M3 권장) - 8천 토큰 이상의 긴 문서 단일 임베딩 (mean pooling은 길이가 길어질수록 약해짐) ## 아키텍처 이 모델은 트랜스포머 attention을 사용하지 않습니다. 대신: ``` 입력: "오늘 날씨가 좋네요" ↓ [1] klue/roberta-base 토크나이저 → 토큰 ID 시퀀스 ↓ [2] StaticEmbedding (32000 × 512 lookup table, 16.4M params) → 각 토큰 → 512차원 벡터 ↓ [3] Mean pooling → 512차원 문장 벡터 ↓ [4] L2 정규화 (normalize_embeddings=True 시) ``` [Tom Aarsen의 Static Embeddings 블로그(HuggingFace)](https://huggingface.co/blog/static-embeddings)와 [MinishLab의 model2vec](https://github.com/MinishLab/model2vec)에서 검증된 패러다임을 한국어로 적용했습니다. ## 한계 1. **어순 무시**: "철수가 영희를 좋아한다" ↔ "영희가 철수를 좋아한다" 구분 약함 2. **다의어 처리 약함**: "은행 직원" vs "강변 은행"의 "은행"을 동일한 벡터로 처리 3. **KLUE 도메인 성능 격차**: 뉴스 도메인에서는 BGE-M3 대비 격차 큼 (0.71 vs 0.88) 4. **부정/반어 처리 약함**: "좋아하지 않는다"를 "좋아한다"와 비슷하게 볼 수 있음 이러한 한계는 모든 BoW 계열 정적 임베딩의 본질적 특성입니다. 정확도가 절대적인 경우 BGE-M3 권장. ## 인용 이 모델을 사용하신다면, 기반이 된 연구를 함께 인용해주세요: - Static Embeddings: https://huggingface.co/blog/static-embeddings - model2vec: https://github.com/MinishLab/model2vec - KorSTS / KorNLI: KakaoBrain KorNLUDatasets - KLUE: https://klue-benchmark.com ## 라이선스 Apache 2.0