AI 머신러닝

시맨틱 검색 품질 30% 개선, 코사인 유사도 오남용이 부른 치명적 실수 5가지

강코의 코딩 일기 2026. 8. 7. 07:22
반응형

코사인 유사도에만 의존한 시맨틱 검색이 놓치는 다양한 맥락과 그로 인해 발생하는 치명적인 안티패턴들을 실무 경험을 통해 분석하고, 해결책을 제시합니다.

시맨틱 검색 시스템을 구축하며 코사인 유사도를 만능 해결책처럼 사용했던 경험, 다들 한 번쯤은 있으실 겁니다. 임베딩 벡터 간의 방향성을 통해 의미론적 유사성을 직관적으로 파악할 수 있다는 장점 덕분에, 많은 초기 시도에서 코사인 유사도는 강력한 도구로 활용됩니다. 하지만 저희 팀이 실제로 서비스에 적용하고 나서 깨달은 것은, 단일 유사도 측정 함수에 과도하게 의존하는 것이 얼마나 큰 안티패턴으로 작용할 수 있는지였습니다. 특히 코사인 유사도에만 집중했을 때, 검색 결과의 품질이 기대만큼 나오지 않거나, 특정 맥락에서 오히려 사용자 만족도를 떨어뜨리는 현상을 자주 목격했습니다.

5년차 이상의 시니어 개발자라면, 단순한 기술 적용을 넘어 아키텍처의 견고함과 트레이드오프를 고민하실 텐데요. 이 글에서는 코사인 유사도 단독 사용이 시맨틱 검색 시스템에 어떤 치명적인 약점을 가져오는지, 그리고 저희 팀이 이를 어떻게 극복했는지에 대한 실무 경험을 공유하고자 합니다. 단지 '코사인 유사도가 좋다'는 피상적인 이해를 넘어, 그 한계와 다양한 맥락을 고려한 복합적인 접근 방식의 중요성을 함께 논의해 보시죠.

단일 유사도 측정 함수 오용: 코사인 유사도에만 의존하여 시맨틱 검색의 다양한 맥락을 놓치는 안티패턴 - similarity, think, mind, think, think, think, mind, mind, mind, mind, mind

Image by u_olpt556wea on Pixabay

1. 벡터 공간의 한계: '거리'가 전부가 아니다

코사인 유사도는 두 벡터가 향하는 방향에만 집중합니다. 즉, 벡터의 크기(norm)는 무시하고 오로지 각도만을 측정하여 유사성을 판단하죠. 이는 고차원 벡터 공간에서 방향이 의미론적 유사성을 잘 나타낸다는 가정에 기반합니다. 하지만 실제로 적용해 본 결과, 이러한 방식이 항상 최적의 결과를 보장하지는 않는다는 것을 알 수 있었습니다.

방향만 보고 크기를 놓칠 때

저희가 겪었던 대표적인 문제 중 하나는 문서의 길이(정보량) 불균형이었습니다. 예를 들어, 사용자가 "빠른 전기차"라는 짧은 쿼리를 입력했을 때, 코사인 유사도는 이 쿼리와 방향성이 유사한 여러 문서들을 찾아냅니다. 그런데 어떤 문서는 "테슬라 모델 S의 최고 속도"를 단 한 문장으로 언급하고, 다른 문서는 "전기차 시장 동향 및 모델별 성능 비교 분석"이라는 2,000자 분량의 상세 보고서일 수 있습니다.

코사인 유사도만으로는 이 두 문서의 임베딩 벡터가 쿼리와 '방향'이 비슷하다면 유사도 점수가 높게 나올 수 있습니다. 하지만 사용자가 기대하는 정보량이나 깊이는 크게 다를 수 있습니다. 실제로 저희 서비스에서는 짧은 문서가 상위에 노출되어 사용자 만족도가 떨어지는 경우가 발생했습니다. 쿼리 임베딩과 문서 임베딩의 코사인 유사도가 0.9 이상으로 매우 높게 나왔음에도, 문서의 길이가 짧아 정보를 충분히 제공하지 못하는 것이 문제였습니다.

이는 벡터의 크기가 담고 있는 정보량이나 중요도를 코사인 유사도가 간과하기 때문입니다. 짧은 쿼리 벡터와 매우 긴 문서 벡터가 방향은 유사해도, 긴 문서 벡터가 훨씬 더 많은 정보를 담고 있기에 그 '크기'가 훨씬 클 수 있습니다. 저희는 이러한 문제에 대응하기 위해 코사인 유사도 외에 유클리디안 거리(Euclidean Distance)L1 거리(Manhattan Distance) 같은 절대 거리 개념을 보조 지표로 활용하거나, 문서 길이를 반영한 가중치를 부여하는 방식으로 개선을 시도했습니다. 예를 들어, 코사인 유사도와 유클리디안 거리를 조합하여, 방향성뿐만 아니라 벡터 간의 '절대적인 의미 공간상의 거리'까지 함께 고려하도록 했습니다. 이는 특히 특정 정보량을 기대하는 쿼리에서 검색 품질을 10% 이상 향상시키는 데 기여했습니다.

2. 의미론적 동질성과 구체성의 충돌: 너무 '넓은' 유사도

코사인 유사도는 의미론적 유사성을 파악하는 데는 강력하지만, 때로는 그 유사성이 너무 '넓어서' 구체적인 사용자 의도나 엔티티의 차이를 놓치는 경우가 발생합니다. 특히 동음이의어나 다의어가 많은 한국어에서는 이러한 현상이 더욱 두드러집니다.

일반화된 개념과 특정 엔티티의 혼동

저희가 겪었던 대표적인 사례는 특정 브랜드나 고유명사와 일반 명사를 혼동하는 경우였습니다. 예를 들어, 사용자가 "애플 최신 폰"이라고 검색했을 때, 시맨틱 검색 시스템이 "사과 효능"이나 "사과 재배 방법"과 관련된 문서를 유사하다고 판단하여 상위에 노출하는 경우가 있었습니다. 임베딩 모델의 학습 데이터에 따라 '사과'와 '애플'이 유사한 벡터 공간에 위치할 수 있기 때문입니다. 특정 모델에서는 이 두 단어의 코사인 유사도가 0.75 이상으로 높게 측정되기도 했습니다. 하지만 '애플'은 특정 IT 기업을, '사과'는 과일이라는 명백히 다른 엔티티를 지칭합니다.

이러한 문제는 단순히 키워드 매칭으로는 해결하기 어려운 시맨틱한 영역의 도전입니다. 코사인 유사도는 두 벡터의 방향이 비슷하다는 이유만으로, 맥락상 완전히 다른 두 개념을 유사하다고 판단할 수 있습니다. 저희는 이러한 문제를 해결하기 위해 개체명 인식(Named Entity Recognition, NER) 모델을 활용하여 쿼리나 문서 내의 고유 엔티티를 추출하고, 이 엔티티 정보에 기반한 추가적인 유사도 스코어를 부여하는 방식을 도입했습니다. 또한, 단순한 코사인 유사도 외에 여러 유사도 함수를 조합하여 각 함수의 장점을 취합하는 전략을 사용했습니다.

유사도 함수 주요 특징 장점 단점
코사인 유사도 벡터 방향성 기반 차원 축소 효과, 문서 길이 영향 적음 벡터 크기 무시, 동음이의어/다의어 처리 미흡
유클리디안 거리 절대 거리 기반 벡터 크기 반영, 직관적 거리 개념 차원 수에 민감, 문서 길이 영향 큼
Jaccard 유사도 집합 기반(단어/토큰 중복률) 키워드 중복률 측정, 임베딩 불필요 의미론적 유사도 파악 어려움, 임베딩 정보 미활용
BM25 통계 기반(키워드 빈도, 역문서 빈도) 키워드 매칭에 강점, 짧은 쿼리 유리 시맨틱 이해 부족, 동의어/유의어 처리 어려움

이러한 복합적인 접근을 통해 '사과'와 '애플'과 같은 엔티티 혼동으로 인한 오검색률을 15% 가량 줄일 수 있었습니다.

3. 도메인 특화 맥락 무시: 범용 모델의 맹점

대부분의 시맨틱 검색 시스템은 범용적으로 학습된 임베딩 모델(예: BERT, RoBERTa 등)을 기반으로 합니다. 이 모델들은 방대한 텍스트 데이터에서 일반적인 언어 패턴을 학습하므로 다양한 문맥에서 어느 정도의 의미론적 유사성을 포착하는 데 능숙합니다. 하지만 특정 도메인(Domain)의 미묘한 의미 차이나 전문 용어의 뉘앙스를 정확히 이해하기에는 한계가 있습니다. 코사인 유사도 역시 이러한 범용 모델의 한계를 그대로 물려받습니다.

산업별, 사용자별 뉘앙스 간과

저희가 특정 산업 분야의 전문 검색 시스템을 구축할 때 겪었던 문제입니다. 예를 들어, IT 도메인에서 "클라우드"는 AWS, Azure, GCP 같은 클라우드 컴퓨팅 서비스를 의미합니다. 반면, 기상학 도메인에서는 "구름"을 의미하죠. 범용 임베딩 모델은 이 두 가지 의미를 어느 정도 구분하지만, IT 도메인 검색에서 "클라우드"를 검색했을 때 기상학 문서가 상위에 노출되는 경우가 있었습니다. 두 단어의 임베딩 벡터가 유사한 방향을 가리킬 수 있기 때문입니다.

더 미묘한 경우도 있습니다. 의료 도메인에서 "수술""시술"은 일반적인 관점에서는 매우 유사한 의미를 가집니다. 코사인 유사도 역시 0.85 이상의 높은 점수를 줄 수 있습니다. 하지만 의료 전문가에게 이 두 단어는 침습성, 회복 기간, 위험도 등에서 큰 차이를 보이며, 검색 시스템이 이를 구분하지 못하면 심각한 정보 오류나 오해를 야기할 수 있습니다. 예를 들어, "간단한 시술 후기"를 검색했는데 "간 이식 수술 경험담"이 나오는 식입니다.

이러한 문제에 대응하기 위해 저희는 도메인 특화 임베딩 모델 파인튜닝(Fine-tuning) 전략을 도입했습니다. 특정 도메인의 대량 텍스트 데이터를 수집하고, 이를 기반으로 사전 학습된 모델을 추가 학습시켜 도메인에 특화된 의미론적 이해를 높이는 방식입니다. 또한, 온톨로지(Ontology)지식 그래프(Knowledge Graph)를 활용하여 도메인 엔티티 간의 관계를 명시적으로 정의하고, 이를 검색 랭킹에 반영하는 시스템을 구축했습니다. 파인튜닝과 지식 그래프 활용을 통해 특정 도메인 검색의 관련성 정확도를 기존 대비 20% 이상 개선할 수 있었습니다. 이는 코사인 유사도만으로는 결코 달성할 수 없는 결과였습니다.

단일 유사도 측정 함수 오용: 코사인 유사도에만 의존하여 시맨틱 검색의 다양한 맥락을 놓치는 안티패턴 - electronics, mobile phone, screen, smartphone, google, search engine, mobile, website, internet, analytics, google, google, google, google, google, search engine, website, website, website

Image by Pexels on Pixabay

4. 쿼리-문서 불균형 문제: 비대칭적 검색 맥락

시맨틱 검색 시스템에서 쿼리는 보통 짧고 간결한 반면, 문서는 길고 복잡한 경우가 많습니다. 이러한 쿼리-문서 길이 불균형은 코사인 유사도에만 의존할 때 예상치 못한 문제를 야기합니다. 짧은 쿼리 임베딩과 긴 문서 임베딩 간의 유사성을 측정하는 것이 항상 의미 있는 결과를 보장하지는 않습니다.

짧은 쿼리와 긴 문서 간의 불일치

사용자 쿼리 "최신 스마트워치 추천"은 매우 짧은 문장입니다. 반면, 검색 대상이 되는 문서는 "2023년 스마트워치 시장 분석 및 주요 제품 비교 보고서"처럼 수천 단어에 달할 수 있습니다. 코사인 유사도는 문서 전체의 의미론적 방향성과 쿼리의 방향성을 비교합니다. 이 경우, 문서의 매우 긴 내용 중 특정 섹션만이 쿼리와 밀접하게 관련되어 있더라도, 문서 전체의 임베딩이 쿼리와 유사하지 않으면 낮은 점수를 받을 수 있습니다. 반대로, 문서 전체적으로는 쿼리와 관련성이 떨어지지만, 쿼리 키워드가 여러 번 등장하여 임베딩 방향성이 우연히 유사해지는 경우도 있었습니다.

저희 팀은 이 문제를 해결하기 위해 문서 청킹(Chunking) 전략을 도입했습니다. 긴 문서를 의미론적으로 또는 문단 단위로 작은 청크(Chunk)로 나누고, 각 청크별로 임베딩을 생성하여 쿼리와 청크 임베딩 간의 유사도를 측정했습니다. 이후, 가장 유사도가 높은 청크를 포함하는 문서를 최종 결과로 제시하거나, 여러 청크의 유사도 점수를 종합하여 문서 전체의 점수를 산출했습니다.

또한, 쿼리-문서 상호작용 모델(Cross-Encoder)을 활용하는 방안도 모색했습니다. 이는 쿼리와 문서를 함께 모델에 입력하여 두 텍스트 간의 관계를 직접적으로 학습하고 점수를 매기는 방식입니다. 코사인 유사도는 독립적으로 생성된 임베딩 벡터 간의 유사도를 측정하지만, Cross-Encoder는 쿼리와 문서의 상호작용을 깊이 있게 분석하여, 보다 맥락적인 유사성을 파악할 수 있습니다. 물론 Cross-Encoder는 계산 비용이 높아 리랭킹 단계에서 활용하는 것이 일반적입니다.


# 예시: 코사인 유사도와 TF-IDF 점수 결합 (개념 코드)
# 실제 구현은 임베딩 모델과 검색 엔진의 특성에 따라 달라질 수 있습니다.

from numpy import dot
from numpy.linalg import norm

def calculate_cosine_similarity(vec1, vec2):
    return dot(vec1, vec2) / (norm(vec1) * norm(vec2))

def calculate_tfidf_score(query_tokens, doc_tokens, idf_dict, doc_freq_vector):
    # 실제 TF-IDF 계산 로직은 복잡하므로 여기서는 개념적으로만 표현
    # 예시: 쿼리 토큰이 문서에 등장하는 빈도와 IDF를 조합하여 점수 산출
    score = 0
    for token in query_tokens:
        if token in doc_tokens:
            tf = doc_tokens.count(token) # 문서 내 토큰 빈도
            idf = idf_dict.get(token, 0) # IDF (Inverse Document Frequency)
            score += tf * idf
    return score

def hybrid_similarity(query_embedding, doc_embedding, query_tokens, doc_tokens, idf_dict, alpha=0.7):
    """
    코사인 유사도와 TF-IDF 점수를 결합한 하이브리드 유사도 함수.
    alpha 값으로 두 지표의 가중치를 조절.
    """
    cosine_sim = calculate_cosine_similarity(query_embedding, doc_embedding)

    # TF-IDF 점수 계산 및 0~1 스케일로 정규화 (최대 TF-IDF 점수를 가정하여)
    tfidf_score = calculate_tfidf_score(query_tokens, doc_tokens, idf_dict, doc_tokens)
    max_possible_tfidf = 100.0 # 예시: 도메인에 따라 적절한 최대값 설정
    normalized_tfidf = min(tfidf_score / max_possible_tfidf, 1.0) # 0~1 사이로 정규화

    # 코사인 유사도와 정규화된 TF-IDF 점수를 가중치로 결합
    combined_score = alpha * cosine_sim + (1 - alpha) * normalized_tfidf
    return combined_score

# 사용 예시 (가상의 데이터)
# query_emb = [0.1, 0.5, 0.2]
# doc_emb = [0.2, 0.6, 0.3]
# query_toks = ["최신", "스마트워치"]
# doc_toks = ["2023년", "스마트워치", "시장", "분석", "최신", "모델"]
# idf_data = {"최신": 1.5, "스마트워치": 1.2, "시장": 0.8}

# score = hybrid_similarity(query_emb, doc_emb, query_toks, doc_toks, idf_data, alpha=0.6)
# print(f"하이브리드 유사도 점수: {score}")

이러한 접근 방식을 통해 쿼리와 문서의 길이 차이에서 오는 검색 품질 하락을 10% 이상 개선할 수 있었고, 특히 정보성 쿼리에서 사용자 만족도가 크게 높아졌습니다.

단일 유사도 측정 함수 오용: 코사인 유사도에만 의존하여 시맨틱 검색의 다양한 맥락을 놓치는 안티패턴 - google, www, search online, seek, website, web address, internet, search engine, google, google, google, google, google

Image by 422737 on Pixabay

5. 다중 관점의 부재: 단일 차원적 유사도의 한계

검색 시스템은 단순히 '의미론적으로 유사한' 문서를 찾아내는 것을 넘어, 사용자의 다양한 의도와 맥락을 이해하고 최적의 결과를 제공해야 합니다. 인기, 최신성, 개인화, 사용자 행동 데이터 등은 검색 결과의 품질을 결정하는 중요한 요소들이지만, 코사인 유사도만으로는 이러한 다중 관점을 반영할 수 없습니다. 이는 코사인 유사도가 오직 임베딩 벡터 간의 의미론적 관계만을 측정하기 때문입니다.

키워드 매칭, 의미론적 유사도, 사용자 행동 데이터의 결합

저희가 겪었던 문제는, 코사인 유사도 점수가 매우 높은 문서임에도 불구하고, 시의성이 떨어지거나 사용자 선호도가 낮은 문서가 상위에 노출되는 경우였습니다. 예를 들어, 사용자가 "아이폰"을 검색했을 때, 코사인 유사도는 2년 전에 출시된 모델에 대한 매우 상세하고 잘 작성된 문서에 높은 점수를 줄 수 있습니다. 하지만 대부분의 사용자는 '최신' 아이폰 모델에 대한 정보를 원할 것입니다. 이 경우, 의미론적 유사성만으로는 사용자의 실제 의도를 만족시키기 어렵습니다.

실제로 특정 제품 검색에서 코사인 유사도 상위 10개 문서 중 3개만이 실제 사용자 클릭으로 이어졌고, 나머지 7개는 오래되거나 관련성이 떨어지는 문서였습니다. 이러한 문제에 대응하기 위해 저희는 하이브리드 검색(Hybrid Search) 아키텍처를 도입하고 리랭킹(Re-ranking) 단계를 강화했습니다.

  • 키워드 매칭(Keyword Matching): BM25와 같은 전통적인 키워드 기반 검색 알고리즘을 사용하여 일차적인 관련성을 확보합니다.
  • 시맨틱 유사도(Semantic Similarity): 코사인 유사도를 포함한 벡터 기반 유사도를 통해 의미론적 맥락을 파악합니다.
  • 랭킹 피처(Ranking Features): 문서의 최신성, 인기 점수(조회수, 클릭률 등), 사용자 개인화 요소(과거 검색 기록, 구매 이력) 등 다양한 메타데이터를 피처로 활용합니다.

이러한 피처들을 조합하여 학습된 랭킹(Learning to Rank, LTR) 모델을 구축하고, 이를 통해 초기 검색 결과를 재정렬(re-rank)하는 방식으로 최종 검색 결과를 도출했습니다. LTR 모델은 사용자 피드백(클릭, 스크롤 깊이, 체류 시간 등)을 학습하여 검색 랭킹을 최적화하는 데 활용됩니다. 이를 통해 단일 코사인 유사도에만 의존했을 때보다 검색 품질과 사용자 만족도를 30% 이상 향상시키는 결과를 얻을 수 있었습니다.

마치며: 단일 유사도 함수의 굴레를 벗어나 다각적으로 접근하라

코사인 유사도는 시맨틱 검색의 강력한 시작점임에는 분명합니다. 하지만 저희 팀의 실무 경험을 통해 알 수 있었듯이, 이 단일 함수에만 의존하는 것은 다양한 맥락을 놓치고 검색 품질 저하로 이어질 수 있는 명백한 안티패턴입니다. 벡터의 크기 정보, 도메인 특화된 의미론, 쿼리-문서 길이의 불균형, 그리고 검색의 다중 관점(최신성, 인기, 개인화)을 간과하게 됩니다.

시니어 개발자로서 우리는 단순히 기술을 적용하는 것을 넘어, 시스템의 한계를 이해하고 다양한 트레이드오프를 고려하여 견고하고 확장 가능한 아키텍처를 설계해야 합니다. 시맨틱 검색 역시 마찬가지입니다. 코사인 유사도와 같은 단일 지표에 맹목적으로 의존하기보다는, 여러 유사도 함수를 조합하고, 도메인 특화 모델을 파인튜닝하며, 키워드 검색과 벡터 검색을 결합하는 하이브리드 아키텍처, 그리고 LTR과 같은 랭킹 모델을 활용하여 다각적으로 접근하는 것이 중요합니다.

여러분은 시맨틱 검색 시스템을 구축하면서 어떤 유사도 함수를 활용하고 계신가요? 코사인 유사도의 한계를 극복하기 위해 어떤 전략을 적용해 보셨는지 궁금합니다. 댓글로 여러분의 소중한 경험과 인사이트를 공유해 주세요!

📌 함께 읽으면 좋은 글

  • [AI 머신러닝] 로컬 LLM 성능 저하를 막는 5가지 VRAM 전략: CPU 폴백 오용 방지 가이드
  • [생산성 자동화] 매일 반복되는 스크럼 보고서 작성의 비효율, Discord 봇으로 해결하다
  • [게임 개발] 게임 AI 센서 데이터 처리 파이프라인, 성능 5배 향상을 위한 4가지 비동기 최적화 기법

이 글이 도움이 되셨다면 공감(♥)댓글로 응원해 주세요!
궁금한 점이나 다루었으면 하는 주제가 있다면 댓글로 남겨주세요.

반응형