텍스트 검색과 분류를 위한 TF-IDF·Word2Vec·LDA·BERT 임베딩

TF-IDF, Word2Vec, LDA, BERT 임베딩의 특성과 전처리·인덱싱·서빙 전략을 비교해 텍스트 검색, 분류, 추천에 맞는 선택 기준을 정리한다.

2026-08-14 · 최초 발행 2024-04-29

텍스트 표현 방식이 검색과 분류의 출발점이 된다

검색, 추천, 분류, 요약 같은 텍스트 기반 기능에서는 입력 문서를 어떤 형태로 표현하느냐가 핵심이다. TF-IDF, Word2Vec, LDA, BERT 임베딩은 같은 텍스트를 다루지만 문맥을 반영하는 정도, 해석 가능성, 인프라 요구사항이 다르다.

TF-IDF는 단어 빈도와 역문서 빈도를 결합해 가중치를 계산하는 통계 기반 벡터화 방식이다. 단순하고 빠르며 희소 벡터를 만들지만 문맥은 반영하지 않는다.

Word2Vec은 CBOW 또는 Skip-gram으로 주변 단어를 예측하면서 밀집 임베딩을 학습한다. 단어 사이의 유사도를 포착할 수 있으나, 불용어와 형태소 처리 품질에 민감하고 OOV 단어 문제가 남는다.

LDA(Latent Dirichlet Allocation)는 문서를 토픽의 혼합으로, 토픽을 단어 분포로 보는 생성 확률 모형이다. 사람이 읽고 해석할 수 있는 토픽을 제공하지만 Bag-of-Words 가정 때문에 단어 순서와 문맥은 손실된다.

BERT 임베딩은 Transformer 기반의 양방향 문맥 표현을 만든다. 문장과 문서 수준의 의미 표현에는 강점이 있지만 계산 비용과 메모리 요구가 크다.

전처리와 벡터화에서 결정할 항목

텍스트 정규화에서는 lowercasing, 특수문자와 URL 제거, 인코딩 정합성 확보를 다룬다. 토크나이징은 한국어라면 MeCab, Khaiii, Okt 같은 형태소 분석기를 고려할 수 있고, 영어에서는 WordPiece/BPE 또는 spaCy를 사용한다. stopwords, rare terms, 길이 필터, 언어 감지, 깨진 텍스트 격리도 품질 통제에 포함된다.

TF-IDF는 n-gram(1–2), min_df, max_df로 노이즈를 제어하며 cosine 유사도 검색에 맞는다. Word2Vec은 window와 negative sampling을 조정하고, 사전학습 모델을 쓸지 사내 코퍼스로 학습할지 선택해야 한다. LDA에서는 num_topics, alpha, eta 정규화와 coherence 기반의 토픽 수 결정이 필요하다. BERT는 mean pooling과 CLS pooling 중 하나를 고르고 multilingual 모델 또는 도메인 특화 모델을 택한다.

선정 기준은 데이터 규모, 실시간성, 해석 가능성, 예산이다. TF-IDF를 베이스라인으로 두고, 분산 임베딩인 Word2Vec, 토픽 분석인 LDA를 병행한 뒤 BERT로 성능 한계를 넘는 흐름을 적용할 수 있다.

인덱스와 갱신 주기를 함께 설계한다

TF-IDF는 ANN(HNSW/FAISS) 또는 BM25로 인덱싱할 수 있고, 임베딩은 PGVector나 Milvus 같은 벡터DB에 저장할 수 있다. 문서가 바뀔 때 TF-IDF는 재적산 비용이 낮은 편이며, LDA와 Word2Vec은 주기적 재학습이 필요하다. BERT 기반 시스템은 변경 문서의 임베딩을 증분 생성하는 방식으로 운영한다.

운영 중에는 데이터 분포 드리프트, 토픽 일관성, 검색 품질(NDCG)을 계속 점검한다. 모델 자체만 교체하는 방식으로는 검색 품질을 안정적으로 유지하기 어렵다.

기법 성능(语义) 확장성(데이터/속도) 일관성(재현성) 안정성(노이즈) 운영 편의
TF-IDF 낮음~중간 매우 높음 매우 높음 중간 매우 높음
Word2Vec 중간 높음 중간 중간 중간
LDA 중간(해석성 강점) 중간 중간 중간 중간
BERT Embeddings 높음 중간~낮음(GPU 권장) 높음(고정 모델) 높음 중간~낮음(리소스 요구)

입력 텍스트가 결과로 이어지는 흐름

토큰/문장TF-IDFWord2VecLDABERT에러 처리 문서인코딩 오류리소스 오류입력: 원시 텍스트 배치전처리: 정규화·토크나이징벡터화 방식 선택희소 벡터 생성단어 임베딩 학습/적용코퍼스→토픽 분포 추정문장/문서 임베딩 추출인덱싱/유사도 검색토픽 해석·모델 기반 피드백출력: 검색/추천/분류 결과품질 검증샘플 제거/대체재인코딩/로깅메모리/시간 초과배치 크기 축소·스트리밍 처리

검색·분류·토픽 분석에 적용하는 방법

검색 랭킹에서는 BM25+TF-IDF로 1차 검색을 수행하고, BERT 임베딩으로 재랭킹해 NDCG@10을 개선할 수 있다. 쿼리 의도를 파악하기 위해 Query Expansion과 Word2Vec 유사어를 함께 활용한다.

문서 분류와 라벨링 자동화에서는 TF-IDF+로지스틱 회귀로 베이스라인을 만든 뒤 BERT 파인튜닝으로 F1 상승을 시도한다. 데이터가 희소할 때는 Easy Data Augmentation을 병행한다.

토픽 탐색과 모니터링에서는 LDA로 주간 토픽 트렌드를 추적하고 토픽-키워드 보드를 제공할 수 있다. Coherence와 Perplexity는 토픽 수 자동 튜닝 파이프라인의 기준으로 사용할 수 있다.

추천과 유사 문서 매칭에서는 문장 임베딩 평균으로 문서 벡터를 구성하고 Milvus/HNSW에서 ANN 검색을 수행한다. 멀티모달로 확장할 때는 텍스트 임베딩을 공통 벡터 공간에 정렬한다.

TF-IDF에서 BERT 재랭킹을 도입하면 NDCG@10 +515%p, Recall@100 +310%p를 기대할 수 있다. 분류에서는 TF-IDF 대비 BERT 임베딩 사용 시 Macro-F1 +312%p가 가능하며, 이는 도메인과 데이터 크기에 의존한다. TF-IDF 배치 인덱싱 속도는 10만 문서/분 이상이고 코어/IO에 의존하며, BERT 임베딩 생성은 GPU에서 15ms/문장 또는 CPU에서 20~50ms/문장이다.

토픽 해석 가능성이 높아지면 기획과 운영 조직 사이의 커뮤니케이션 효율을 높일 수 있다. 문맥 민감 임베딩은 롱테일 쿼리 대응에도 유리하다.

비용과 해석 가능성의 균형

한국어 형태소 분석을 적용하면 성능이 향상될 수 있지만 배포 복잡도는 증가한다. 숫자, 코드, 이모티콘을 어떻게 처리할지 정책을 표준화하는 일도 필요하다.

TF-IDF는 비용을 최소화하면서 빠르게 구축하기 좋지만 문맥 이해에는 한계가 있다. Word2Vec과 LDA는 해석성과 도메인 적합성에서 장점이 있으나 유지보수 비용은 중간 수준이다. BERT는 정확도 상한을 높일 수 있는 대신 리소스와 지연 비용이 커진다.

서빙 구조는 오프라인 임베딩 캐시와 온라인 ANN을 결합하는 하이브리드 방식이 적합하다. GPU와 CPU를 혼합 운용하고, 큐와 배치 인퍼런스로 스루풋을 확보한다.

수집부터 재학습까지의 운영 경로

입력 데이터는 크롤링, 로그, 저장소에서 수집하며 document_id, text, meta 스키마를 둔다. 이후 정규화, 토크나이징, 품질 필터를 거쳐 TF-IDF, Word2Vec, LDA, BERT 중 적합한 방식을 선택하고 병렬화한다. 생성된 결과는 벡터DB 또는 IR 인덱스로 만든다.

외부에는 /search, /similar, /classify API를 제공할 수 있다. 품질 지표 대시보드와 드리프트 알림을 두고, 시간 또는 데이터 기준의 트리거에 따라 재학습한다.

Python으로 비교하는 기본 구현

전제조건은 Python 3.10+, CPU 또는 CUDA 가능 환경이며 다음 패키지를 설치한다.

  • pip install scikit-learn==1.4.2 gensim==4.3.2 transformers==4.43.1 torch>=2.3.0 numpy pandas

데이터와 토크나이저는 단순화한 예시다. 실무에서는 한국어 형태소 분석기 적용을 권장한다.

# 파일: nlp_basics.py
# 목적: TF-IDF, Word2Vec, LDA, BERT 임베딩 간단 비교
# 환경: Python 3.10+, scikit-learn 1.4.2, gensim 4.3.2, transformers 4.43.1, torch 2.3+

from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.metrics.pairwise import cosine_similarity
from gensim.models import Word2Vec
from gensim.corpora import Dictionary
from gensim.models.ldamodel import LdaModel
from transformers import AutoTokenizer, AutoModel
import torch
import numpy as np

docs = [
    "고객 지원 티켓 분류 자동화가 필요합니다.",
    "BERT 임베딩을 사용하면 문맥 이해가 향상됩니다.",
    "LDA 토픽 모델링으로 문서 군집과 키워드 추출이 가능합니다.",
    "TF-IDF는 빠르고 간단하지만 의미 이해는 제한적입니다.",
    "Word2Vec은 단어 간 유사도를 잘 포착합니다."
]

# 간단 토크나이저: 공백 기반
def simple_tokenize(text):
    return text.lower().replace('.', '').split()

tokens = [simple_tokenize(t) for t in docs]
texts = [" ".join(t) for t in tokens]

# 1) TF-IDF
tfidf = TfidfVectorizer(ngram_range=(1,2), min_df=1, max_df=0.95)
X_tfidf = tfidf.fit_transform(texts)
print("TF-IDF shape:", X_tfidf.shape)
print("TF-IDF cosine(sim[0,1]):", float(cosine_similarity(X_tfidf[0], X_tfidf[1])))

# 2) Word2Vec (사내 코퍼스 소규모 학습 예시)
w2v = Word2Vec(sentences=tokens, vector_size=100, window=5, min_count=1, workers=2, epochs=50)
print("Word2Vec 단어벡터 예시:", w2v.wv['bert'][:5] if 'bert' in w2v.wv else "OOV")

def sent_embed_w2v(tok, model):
    vecs = [model.wv[w] for w in tok if w in model.wv]
    return np.mean(vecs, axis=0) if vecs else np.zeros(model.vector_size)

X_w2v = np.vstack([sent_embed_w2v(t, w2v) for t in tokens])
print("Word2Vec 문장 임베딩 shape:", X_w2v.shape)

# 3) LDA (Bag-of-Words + 토픽 분해)
dictionary = Dictionary(tokens)
corpus = [dictionary.doc2bow(t) for t in tokens]
lda = LdaModel(corpus=corpus, id2word=dictionary, num_topics=3, random_state=42, passes=10)
topics = lda.print_topics(num_words=5)
print("LDA Topics:", topics)

# 4) BERT Embeddings (KLUE BERT, CPU/GPU 자동)
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
model_name = "klue/bert-base"  # 한국어 특화 모델
tokenizer = AutoTokenizer.from_pretrained(model_name)
model = AutoModel.from_pretrained(model_name).to(device)
model.eval()

@torch.no_grad()
def bert_embed(sent_list):
    enc = tokenizer(sent_list, padding=True, truncation=True, max_length=128, return_tensors="pt").to(device)
    out = model(**enc)
    # Mean Pooling
    attn = enc['attention_mask'].unsqueeze(-1)
    masked = out.last_hidden_state * attn
    emb = masked.sum(dim=1) / attn.sum(dim=1).clamp(min=1e-9)
    return emb.cpu().numpy()

X_bert = bert_embed(docs)
print("BERT 임베딩 shape:", X_bert.shape)
print("BERT cosine(sim[0,1]):", float(cosine_similarity(X_bert[0].reshape(1,-1), X_bert[1].reshape(1,-1))))

한국어 형태소 분석을 적용할 때는 simple_tokenize 대신 MeCab 품사 기반 토큰을 사용한다. BERT 임베딩 캐시는 파일 또는 Feature Store에 저장해 재사용하고, 온라인 요청에서는 배치 인퍼런스로 지연을 줄인다. CUDA가 없으면 CPU 자동 폴백을 사용하며, 처리량 요구가 높다면 FP16·ONNX 최적화를 고려한다.

단계적으로 확장하는 선택 기준

파일럿에서는 TF-IDF 베이스라인을 만들고 품질, 지연, 비용을 측정한다. 검색과 매칭은 임베딩+ANN으로 확장하고, 분류와 재랭킹에는 BERT를 우선 검토한다. LDA는 데이터 품질과 트렌드를 관찰하는 체계에 활용할 수 있다.

임베딩 캐시, 증분 업데이트, 모델 레지스트리를 운영에 포함하면 변경 문서와 모델 버전을 관리하기 쉬워진다. 정확도, 지연, 비용 요구사항에 맞춰 단계적으로 채택하고 전처리 표준화와 모니터링 자동화를 함께 갖추는 것이 안정적인 텍스트 처리 운영의 기반이 된다.

텍스트 처리임베딩토픽 모델링자연어 처리벡터 검색