🤖 AI/ML

Cosine Similarity

Cosine Similarity

두 벡터 간 코사인 각도 기반 유사도. 임베딩 비교에 표준 사용.

📖 상세 설명

Cosine Similarity(코사인 유사도)는 두 벡터 사이의 각도를 측정하여 유사도를 계산하는 방법입니다. 두 벡터가 같은 방향을 가리키면 1, 직각이면 0, 반대 방향이면 -1의 값을 가집니다. 벡터의 크기(magnitude)가 아닌 방향만 고려하기 때문에 문서 길이에 영향받지 않는 텍스트 유사도 측정에 적합합니다.

코사인 유사도의 수학적 정의는 두 벡터 A와 B의 내적을 각 벡터의 크기의 곱으로 나눈 값입니다. 공식으로는 cos(theta) = (A dot B) / (||A|| * ||B||)로 표현됩니다. 이 개념은 1960년대 정보 검색 분야에서 처음 활용되기 시작하여, 현재는 자연어 처리와 추천 시스템의 핵심 도구가 되었습니다.

현대 AI에서 코사인 유사도는 임베딩 벡터 비교의 표준입니다. 단어, 문장, 문서를 고차원 벡터로 변환한 후 코사인 유사도로 의미적 유사성을 측정합니다. OpenAI의 text-embedding-3 모델은 3072차원 벡터를 생성하며, 이 벡터들 간의 코사인 유사도가 0.8 이상이면 의미적으로 유사한 것으로 판단합니다.

실무에서는 시맨틱 검색, 중복 문서 탐지, 추천 시스템, RAG의 관련 문서 검색 등에 필수적으로 사용됩니다. Pinecone, Weaviate 같은 벡터 데이터베이스는 기본 유사도 메트릭으로 코사인 유사도를 채택하고 있으며, 수백만 개의 벡터 중에서 밀리초 단위로 유사한 항목을 찾아냅니다.

💻 코드 예제

import numpy as np
from sklearn.metrics.pairwise import cosine_similarity

# 두 벡터의 코사인 유사도 계산 (직접 구현)
def cosine_sim(a, b):
    dot_product = np.dot(a, b)
    norm_a = np.linalg.norm(a)
    norm_b = np.linalg.norm(b)
    return dot_product / (norm_a * norm_b)

# 예제: 문장 임베딩 비교
vec1 = np.array([0.2, 0.5, 0.8, 0.1])  # "고양이가 잠을 잔다"
vec2 = np.array([0.3, 0.4, 0.7, 0.2])  # "강아지가 쉬고 있다"
vec3 = np.array([0.9, 0.1, 0.1, 0.9])  # "주식 시장이 폭락했다"

print(f"유사한 문장 간: {cosine_sim(vec1, vec2):.4f}")  # 0.9833
print(f"다른 주제 문장 간: {cosine_sim(vec1, vec3):.4f}")  # 0.4588

# sklearn 활용 (여러 벡터 동시 비교)
vectors = np.array([vec1, vec2, vec3])
similarity_matrix = cosine_similarity(vectors)
print(f"\n유사도 행렬:\n{similarity_matrix.round(4)}")

# OpenAI 임베딩과 함께 사용
from openai import OpenAI
client = OpenAI()

def get_embedding(text):
    response = client.embeddings.create(
        input=text,
        model="text-embedding-3-small"
    )
    return np.array(response.data[0].embedding)

# 실제 문장 비교
emb1 = get_embedding("인공지능이 세상을 바꾸고 있다")
emb2 = get_embedding("AI 기술이 혁신을 이끌고 있다")
print(f"\n의미적 유사도: {cosine_sim(emb1, emb2):.4f}")  # ~0.89

🗣️ 실무에서 이렇게 말하세요

검색 시스템 설계 회의에서

"FAQ 검색에서 키워드 매칭 대신 코사인 유사도 기반 시맨틱 검색으로 바꾸면 어떨까요? 사용자가 '비밀번호 까먹었어요'라고 입력해도 '비밀번호 재설정 방법' 문서를 찾아줄 수 있습니다. 임계값은 0.75로 시작해서 튜닝하면 됩니다."

기술 면접에서

"코사인 유사도와 유클리드 거리의 차이점을 설명드리면, 코사인은 벡터의 방향만 비교하고 유클리드는 절대적 위치를 비교합니다. 문서 길이가 다양한 텍스트 비교에서는 코사인이 유리하고, 정규화된 임베딩에서는 둘이 수학적으로 동치입니다."

코드 리뷰에서

"여기서 코사인 유사도를 직접 구현하셨는데, 대규모 배치 연산에서는 sklearn의 cosine_similarity나 PyTorch의 F.cosine_similarity를 쓰는 게 10배 이상 빠릅니다. 벡터화된 연산과 GPU 가속을 활용하니까요."

⚠️ 흔한 실수 & 주의사항

1.
영벡터(Zero Vector) 처리 누락

벡터의 크기가 0인 경우 분모가 0이 되어 계산 오류가 발생합니다. 실무에서는 임베딩 실패나 빈 문자열 입력 시 영벡터가 생길 수 있으므로 반드시 예외 처리를 추가하세요.

2.
임계값 고정

유사도 0.8 이상을 "유사"로 고정하는 것은 위험합니다. 도메인, 임베딩 모델, 언어에 따라 적절한 임계값이 다릅니다. 반드시 실제 데이터로 precision-recall 테스트 후 결정하세요.

3.
희소 벡터에 부적합

TF-IDF 같은 희소(sparse) 벡터에서는 대부분의 차원이 0이므로 코사인 유사도가 극단적인 값(0 또는 1)을 자주 반환합니다. 희소 벡터에는 Jaccard 유사도나 BM25를 고려하세요.

🔗 관련 용어

📚 더 배우기