SOFTMOA TECHNOLOGY

임베딩 코사인 유사도 계산에서 0 벡터와 차원 불일치 처리 방법

소프트모아가 정리한 기술 기록입니다.

Contents
작성일 2026. 10. 27.

임베딩 검색 점수가 1보다 커지거나 0 벡터에서 계산이 멈춘다면, 모델을 바꾸기 전에 유사도 함수를 확인해야 합니다. 코사인 유사도는 내적을 두 벡터의 길이로 나눈 값입니다. 차원이 다르거나 길이가 0인 입력은 점수를 억지로 만들지 말고 오류로 분리합니다.[1]

같은 방향인데 점수가 10배라면 내적부터 확인합니다

질문 벡터를 [1.0, 0.0], 후보 A를 [0.8, 0.6], B를 [8.0, 6.0]으로 둡니다. 실제 모델 출력이 아닌 설명용 2차원 숫자입니다. 내적은 A가 0.8, B가 8.0이지만 두 후보의 방향은 같습니다. 코사인으로 계산하면 둘 다 0.8입니다. 내적 자체가 잘못된 지표는 아니지만, 코사인을 구한다면서 내적만 반환하면 벡터 크기가 점수에 섞입니다.[1]

두 벡터를 각각 길이 1로 정규화한 뒤 내적하면 코사인 값을 얻습니다. 다만 길이가 0이면 이 나눗셈을 정의할 수 없습니다. 분모에 작은 상수를 더하거나 실패를 0점으로 바꾸면, 잘못된 입력과 정상적인 직교 벡터를 구분하기 어렵습니다.[1]

외부 API 없이 입력과 점수를 대조하는 코드

아래 내용을 cosine_check.py로 저장합니다. Python 3.10 이상에서 사용할 문법이며, 이 글에서는 macOS의 Python 3.14.8로 실행했습니다. 입력은 float로 이루어진 리스트, 차원은 DIM=2로 제한합니다. 정수·문자열을 자동 변환하지 않는 예제 정책입니다. math.hypot으로 벡터 길이를 구하고, math.isfinite로 NaN과 무한대를 거릅니다.[2]

import math

DIM = 2

def unit(v):
    if len(v) != DIM:
        raise ValueError("DIMENSION")
    if any(type(x) is not float or not math.isfinite(x) for x in v):
        raise ValueError("BAD_NUMBER")
    norm = math.hypot(*v)
    if norm == 0 or not math.isfinite(norm):
        raise ValueError("BAD_NORM")
    return [x / norm for x in v]

q = unit([1.0, 0.0])

def cosine(v):
    return math.fsum(a * b for a, b in zip(q, unit(v), strict=True))

rows = {
    "A": [0.8, 0.6], "B": [8.0, 6.0],
    "Z": [0.0, 0.0], "D": [0.8],
    "F": [float("nan"), 0.6],
    "N": [-1.0, 0.0], "O": [0.0, 1.0],
}
for name in ("A", "B"):
    dot = sum(a * b for a, b in zip(q, rows[name], strict=True))
    print(f"raw_dot {name}={dot:.3f}")
for name, v in rows.items():
    try:
        print(f"{name} {v} -> {cosine(v):.3f}")
    except ValueError as e:
        print(f"{name} {v} -> REJECT:{e}")
print(f"D corrected [0.8, 0.6] -> {cosine([0.8, 0.6]):.3f}")

저장한 디렉터리에서 버전을 확인하고 실행합니다. 파일에는 직접 만든 숫자만 있으며, 모델 호출이나 검색 인덱스 변경은 없습니다.

python3 --version
python3 cosine_check.py

실행 결과는 다음과 같습니다. 소수 셋째 자리 표시는 출력 형식일 뿐, 계산 중간값을 반올림한 것은 아닙니다.

raw_dot A=0.800
raw_dot B=8.000
A [0.8, 0.6] -> 0.800
B [8.0, 6.0] -> 0.800
Z [0.0, 0.0] -> REJECT:BAD_NORM
D [0.8] -> REJECT:DIMENSION
F [nan, 0.6] -> REJECT:BAD_NUMBER
N [-1.0, 0.0] -> -1.000
O [0.0, 1.0] -> 0.000
D corrected [0.8, 0.6] -> 0.800

거부된 3건과 유효한 0점을 구분합니다

  • A와 B는 길이가 달라도 코사인 값이 0.800으로 같습니다. raw_dot 두 줄이 수정 전 계산의 차이를 드러냅니다.
  • Z는 길이가 0이라 BAD_NORM입니다. O는 길이가 1인 정상 벡터로 질문과 직교하므로 0.000입니다. 두 결과를 같은 0점으로 저장하지 않습니다.
  • D는 차원이 하나 부족하고 F에는 NaN이 있어 거부됩니다. 짧은 쪽에 맞춰 끝나는 기본 zip 대신 strict=True도 사용했습니다. 차원 검사가 빠지는 수정이 생기더라도 길이 불일치를 조용히 넘기지 않기 위해서입니다.[6]
  • N의 -1.000은 반대 방향이라는 계산 결과입니다. 음수라고 오류 처리하거나, 0.800을 정답 확률 80%로 읽으면 안 됩니다.[1]

처음 7개 후보 중 점수 4개, 거부 3개가 나왔습니다. D를 [0.8, 0.6]으로 고친 추가 계산은 0.800이지만, 원래 rows의 D나 Z·F를 고친 것은 아닙니다. 마지막 줄은 대체 입력을 따로 확인했을 뿐입니다. 운영에서는 어떤 원본을 재생성하고 교체했는지 별도 이력으로 남겨야 합니다.

차원이 같아도 모델이 다르면 비교를 보류합니다

이 검사는 숫자 계산을 확인하는 작은 재현 예제입니다. 차원만 같다고 서로 다른 임베딩 모델의 출력을 같은 공간의 벡터로 취급해서는 안 됩니다. 질문과 문서의 모델·버전·전처리 조건을 먼저 대조하고, 해당 모델이 요구하는 유사도 지표를 확인한 뒤 적용합니다. 코사인 계산 오류가 확인되지 않았다면 무조건 정규화를 추가하지 않습니다.

같은 후보의 로컬 점수와 실제 검색 점수를 대조할 때도 유사도인지 거리인지부터 확인합니다. 이 코드는 데이터베이스의 점수 변환, 근사 검색의 누락, 문서 접근 권한이나 답변의 사실성을 검증하지 않습니다. 점수가 계산된다는 이유만으로 검색 결과를 승인하지 말고, 실제 질의와 정답 문서로 별도 평가해야 합니다.

확인한 공식 문서

소프트모아의 개발 서비스는 softmoa.com에서 확인할 수 있습니다.

소프트모아는 해당 시스템을 구축합니다. 문의하기

Share this

AI

Have a questions?

견적 및 기술문의

mobile : 010-7931-4813

Contact Form