수학

MATH / 중급 6번

내적·코사인·L2 거리는 언제 같은 순위를 주고 언제 갈리는가

내적의 대수적 정의와 기하적 의미를 잇고, 코시-슈바르츠 부등식으로 코사인의 범위를 얻습니다. 같은 세 후보에 세 지표를 걸면 1등이 셋 다 달라지는데, 정규화하면 세 순위가 정확히 하나가 됩니다. 그 동치를 증명하고 반례를 직접 만듭니다.

PALDYN Team32 MIN READ

벡터 검색 인덱스를 만들 때 지표를 하나 고릅니다. 대부분의 라이브러리가 metric="cosine" 과 metric="l2", 그리고 metric="ip"(내적)를 나란히 내놓고, 문서는 "보통 코사인을 쓰세요" 정도만 적어 둡니다.

그런데 이 셋을 바꿔 끼우면 같은 질의에 다른 문서가 1등으로 올라옵니다. 어떤 인덱스에서는 셋이 완전히 같은 순위를 주고, 어떤 인덱스에서는 통째로 갈립니다. 갈리는 조건이 무엇인지 모르면 지표를 바꿀 때마다 결과를 눈으로 확인하는 수밖에 없습니다.

이 글은 그 조건을 정확히 답합니다. 결론부터 적으면 벡터가 정규화되어 있으면 셋의 순위가 완전히 일치하고, 아니면 갈립니다. 왜 그런지를 증명하고, 갈리는 반례를 직접 만듭니다.

내적의 두 얼굴

4번 · 벡터에서 임베딩 한 줄을 벡터로 읽었고, 5번 · 노름과 거리에서 그 크기를 재는 법을 세웠습니다. 이제 둘 사이의 각도를 잽니다.

대수적 정의

내적은 두 벡터의 같은 자리끼리 곱해서 전부 더한 것입니다.

a⋅b=∑i=1naibi\mathbf{a} \cdot \mathbf{b} = \sum_{i=1}^{n} a_i b_i

3번 · Σ와 첨자의 표기 그대로이고, 코드로는 a @ b 한 줄입니다. 벡터 둘을 넣었는데 나오는 것은 벡터가 아니라 수 하나라는 점이 먼저 눈에 띄어야 합니다. 그래서 이 연산을 스칼라곱이라고도 부릅니다 — 스칼라는 방향 없이 크기만 있는 보통의 수를 가리키는 말입니다.

정의에서 곧바로 따라오는 성질이 셋 있습니다. 자리마다 곱해 더할 뿐이므로 순서를 바꿔도 같고(a⋅b=b⋅a\mathbf{a}\cdot\mathbf{b} = \mathbf{b}\cdot\mathbf{a}), 덧셈에 대해 분배되고(a⋅(b+c)=a⋅b+a⋅c\mathbf{a}\cdot(\mathbf{b}+\mathbf{c}) = \mathbf{a}\cdot\mathbf{b} + \mathbf{a}\cdot\mathbf{c}), 상수는 밖으로 나옵니다((ta)⋅b=t(a⋅b)(t\mathbf{a})\cdot\mathbf{b} = t(\mathbf{a}\cdot\mathbf{b})). 곱셈이 아니라 덧셈의 성질을 물려받은 것이라 증명할 거리도 없습니다.

한 가지만 따로 적어 둡니다. 자기 자신과의 내적은 자리마다 제곱해 더한 것이므로

a⋅a=∑iai2=∥a∥2\mathbf{a}\cdot\mathbf{a} = \sum_i a_i^2 = \|\mathbf{a}\|^2

입니다. 5번의 L2 노름이 정확히 이 값의 제곱근이었습니다. 노름의 제곱은 자기 자신과의 내적이라는 이 한 줄이 아래의 모든 계산에서 대수와 기하를 잇는 통로 노릇을 합니다.

기하적 정의

그런데 같은 값에 전혀 다른 얼굴이 있습니다.

a⋅b=∥a∥ ∥b∥cos⁡θ\mathbf{a} \cdot \mathbf{b} = \|\mathbf{a}\|\,\|\mathbf{b}\| \cos\theta

θ\theta 는 두 벡터가 이루는 각입니다. 자리마다 곱해 더한 수가 어떻게 각도를 알고 있는지가 이 글의 첫 물음입니다.

이 꼴을 ∥b∥⋅(∥a∥cos⁡θ)\|\mathbf{b}\|\cdot\big(\|\mathbf{a}\|\cos\theta\big) 로 묶어 읽으면 뜻이 더 또렷해집니다. 괄호 안은 a\mathbf{a} 를 b\mathbf{b} 의 방향 위에 수직으로 내려 그렸을 때 생기는 그림자의 길이입니다. 이것을 사영 길이라고 합니다. 그러니까 내적은 한 벡터의 그림자 길이에 다른 벡터의 길이를 곱한 값입니다. 두 벡터가 같은 쪽을 볼수록 그림자가 길어지고, 직각이면 그림자가 한 점으로 줄어 0이 됩니다.

내적의 대수적 정의와 기하적 의미, 그리고 사영 길이로 읽는 것

두 정의의 일치

두 벡터의 차 a−b\mathbf{a} - \mathbf{b} 의 길이를 두 가지 방법으로 잽니다.

첫째, 대수적으로 잽니다. 방금 적은 대로 노름의 제곱은 자기 자신과의 내적이므로, 분배법칙을 그대로 펴면

∥a−b∥2=(a−b)⋅(a−b)=∥a∥2−2 a⋅b+∥b∥2\|\mathbf{a} - \mathbf{b}\|^2 = (\mathbf{a}-\mathbf{b})\cdot(\mathbf{a}-\mathbf{b}) = \|\mathbf{a}\|^2 - 2\,\mathbf{a}\cdot\mathbf{b} + \|\mathbf{b}\|^2

이 전개가 이 글 전체에서 가장 많이 쓰이는 식입니다. 뒤에서 세 번 더 나옵니다. 중간의 −2 a⋅b-2\,\mathbf{a}\cdot\mathbf{b} 는 −a⋅b-\mathbf{a}\cdot\mathbf{b} 와 −b⋅a-\mathbf{b}\cdot\mathbf{a} 가 같은 값이라 하나로 합쳐진 것입니다.

둘째, 기하적으로 잽니다. a\mathbf{a}, b\mathbf{b}, a−b\mathbf{a}-\mathbf{b} 는 삼각형의 세 변이고 a\mathbf{a} 와 b\mathbf{b} 사이의 낀 각이 θ\theta 입니다. 세 변의 길이와 낀 각을 잇는 코사인 제2법칙이 — 직각삼각형이 아닐 때 피타고라스 정리를 대신하는 공식입니다 — 이렇게 말합니다.

∥a−b∥2=∥a∥2+∥b∥2−2∥a∥∥b∥cos⁡θ\|\mathbf{a}-\mathbf{b}\|^2 = \|\mathbf{a}\|^2 + \|\mathbf{b}\|^2 - 2\|\mathbf{a}\|\|\mathbf{b}\|\cos\theta

두 식의 왼쪽이 같은 값이므로 오른쪽도 같아야 합니다. ∥a∥2\|\mathbf{a}\|^2 과 ∥b∥2\|\mathbf{b}\|^2 을 양쪽에서 지우고 남은 것을 −2-2 로 나누면

a⋅b=∥a∥∥b∥cos⁡θ\mathbf{a}\cdot\mathbf{b} = \|\mathbf{a}\|\|\mathbf{b}\|\cos\theta

곱해 더한 수가 곧 크기 둘과 방향의 일치도를 곱한 것입니다. 이 한 줄이 뒤의 전부를 설명합니다. 그리고 거꾸로 읽으면 각도의 정의가 됩니다 — 4차원이 넘어가면 "이루는 각"을 눈으로 그릴 수 없으므로, 수학은 이 식을 각의 정의로 삼아 차원에 상관없이 θ\theta 를 말합니다.

음수인 내적

cos⁡θ\cos\theta 는 θ\theta 가 직각을 넘으면 음수가 됩니다. 그러니 내적도 음수가 될 수 있습니다. 세 자리를 구별해 두면 부호를 읽기가 쉬워집니다.

사잇각 코사인 내적 사영 길이
0∘≤θ<90∘0^\circ \le \theta < 90^\circ 양수 양수 b\mathbf{b} 와 같은 쪽
θ=90∘\theta = 90^\circ 00 00 길이 0
90∘<θ≤180∘90^\circ < \theta \le 180^\circ 음수 음수 b\mathbf{b} 와 반대쪽

가운데 줄이 특별합니다. 내적이 0이라는 것과 두 벡터가 직각이라는 것이 같은 말이고, 이 성질에 직교라는 이름이 붙습니다. 다음 글이 통째로 이 한 줄 위에 서 있습니다.

임베딩을 다룰 때 이 부호를 잘못 읽는 자리가 하나 있습니다. 코사인이 −0.4-0.4 라는 것을 "두 문장이 반대 뜻"이라고 옮겨 적는 일입니다. 식이 말하는 것은 그보다 훨씬 좁습니다 — 두 벡터가 좌표공간에서 서로 반대쪽을 가리킨다는 기하의 진술일 뿐입니다. 그 방향이 의미의 반대와 이어지는지는 임베딩 모델이 학습에서 그렇게 배치했을 때만 성립하고, 대부분의 문장 임베딩 모델은 그런 배치를 목표로 삼지 않습니다. 실제로 많은 모델에서 관련 없는 두 문장의 코사인은 음수가 아니라 0.1 근처의 작은 양수로 나옵니다. 음수는 무관함이 아니라 반대 방향이고, 무관함에 해당하는 값은 0 근처입니다.

코시-슈바르츠 부등식

cos⁡θ\cos\theta 는 −1-1 과 11 사이입니다. 그렇다면 위 식에서 내적의 크기도 막혀 있어야 합니다. 그런데 이것을 코사인에서 끌어오면 순환 논증이 됩니다 — 각도라는 말 자체를 내적으로 정의했기 때문입니다. 그래서 대수만으로 다시 세웁니다.

판별식으로 얻는 증명

코시-슈바르츠 부등식. 모든 a\mathbf{a}, b\mathbf{b} 에 대하여 ∣a⋅b∣≤∥a∥ ∥b∥|\mathbf{a}\cdot\mathbf{b}| \le \|\mathbf{a}\|\,\|\mathbf{b}\| 이다.

증명은 이차식 하나로 끝납니다. 실수 tt 에 대해 ∥a+tb∥2\|\mathbf{a} + t\mathbf{b}\|^2 은 길이의 제곱이므로 언제나 0 이상입니다. 위의 전개를 그대로 쓰면

∥a+tb∥2=∥b∥2t2+2(a⋅b) t+∥a∥2≥0\|\mathbf{a} + t\mathbf{b}\|^2 = \|\mathbf{b}\|^2 t^2 + 2(\mathbf{a}\cdot\mathbf{b})\,t + \|\mathbf{a}\|^2 \ge 0

tt 에 대한 이차식이 모든 tt 에서 0 이상이려면 그래프가 tt 축을 뚫고 내려가면 안 되므로 판별식이 0 이하여야 합니다.

4(a⋅b)2−4∥a∥2∥b∥2≤0⟹∣a⋅b∣≤∥a∥∥b∥4(\mathbf{a}\cdot\mathbf{b})^2 - 4\|\mathbf{a}\|^2\|\mathbf{b}\|^2 \le 0 \quad \Longrightarrow \quad |\mathbf{a}\cdot\mathbf{b}| \le \|\mathbf{a}\|\|\mathbf{b}\|

■\blacksquare

증명에 쓴 것은 노름이 0 이상이라는 사실과 분배법칙뿐입니다. 각도도 그림도 안 썼으므로 차원이 몇이든, 벡터가 문장 임베딩이든 그래디언트든 그대로 성립합니다.

등호의 조건

판별식이 정확히 0이면 이차식이 tt 축에 딱 한 점에서 닿습니다. 그 점을 t0t_0 라 하면 ∥a+t0b∥2=0\|\mathbf{a} + t_0\mathbf{b}\|^2 = 0 이고, 길이가 0인 벡터는 영벡터뿐이므로

a=−t0 b\mathbf{a} = -t_0\,\mathbf{b}

입니다. 즉 등호는 한 벡터가 다른 벡터의 상수배일 때, 다시 말해 두 벡터가 평행할 때만 성립합니다. 같은 쪽으로 평행하면 cos⁡θ=1\cos\theta = 1 이고 반대쪽으로 평행하면 −1-1 입니다.

이 조건은 나중에 여러 번 쓰입니다. "이 부등식의 등호가 언제 성립하는가"를 묻는 것은 곧 "가장 좋은 답이 무엇인가"를 묻는 것과 같은 일이라, 최적화에서 방향을 고를 때마다 이 자리로 돌아옵니다.

코사인 유사도

부등식 덕분에 다음 값이 언제나 [−1,1][-1, 1] 에 있습니다.

cos_sim⁡(a,b)=a⋅b∥a∥ ∥b∥=cos⁡θ\operatorname{cos\_sim}(\mathbf{a}, \mathbf{b}) = \frac{\mathbf{a}\cdot\mathbf{b}}{\|\mathbf{a}\|\,\|\mathbf{b}\|} = \cos\theta

이것이 코사인 유사도입니다. 정의상 크기를 나눠 없앤 내적이고, 그래서 방향만 봅니다. 두 벡터를 각각 10배로 늘려도 분자가 100배, 분모도 100배가 되어 값이 그대로라는 것을 식에서 바로 읽을 수 있습니다.

값을 각으로 되돌리고 싶으면 θ=arccos⁡(cos_sim⁡)\theta = \arccos(\operatorname{cos\_sim}) 입니다. 0.9는 약 26∘26^\circ, 0.7은 약 46∘46^\circ, 0은 90∘90^\circ 입니다. 코사인이 1 근처에서는 각이 조금만 벌어져도 값이 천천히 떨어지고 0 근처에서는 빠르게 떨어지는데, 이 때문에 0.95와 0.90의 차이가 0.50과 0.45의 차이보다 각으로는 훨씬 큽니다. 유사도 값을 눈금이 고른 자로 착각하지 않는 것이 좋습니다.

갈리는 세 순위

세 후보와 세 지표

이제 갈리는 자리를 직접 만듭니다. 질의 벡터를 q=(1,0)\mathbf{q} = (1, 0) 으로 두고 후보 셋을 놓습니다.

u=(0.6, 0.8),v=(2, 0),w=(10, 10)\mathbf{u} = (0.6,\ 0.8), \qquad \mathbf{v} = (2,\ 0), \qquad \mathbf{w} = (10,\ 10)

u\mathbf{u} 는 이미 단위벡터이고, v\mathbf{v} 는 질의와 방향이 완전히 같지만 두 배 길며, w\mathbf{w} 는 45∘45^\circ 로 벌어져 있지만 아주 깁니다.

세 지표를 손으로 계산합니다. 노름부터 구하면 ∥u∥=0.36+0.64=1\|\mathbf{u}\| = \sqrt{0.36+0.64} = 1, ∥v∥=2\|\mathbf{v}\| = 2, ∥w∥=200≈14.142\|\mathbf{w}\| = \sqrt{200} \approx 14.142 입니다. 질의가 (1,0)(1,0) 이라 내적은 첫 성분을 그대로 읽는 것과 같고, L2 거리는 두 점 사이의 거리를 좌표로 재면 됩니다.

후보 내적 q⋅x\mathbf{q}\cdot\mathbf{x} 코사인 L2 거리 ∥q−x∥\|\mathbf{q}-\mathbf{x}\|
u\mathbf{u} 0.60.6 0.6/1=0.6000.6 / 1 = 0.600 0.42+0.82=0.894\sqrt{0.4^2 + 0.8^2} = 0.894
v\mathbf{v} 2.02.0 2/2=1.0002 / 2 = 1.000 12+02=1.000\sqrt{1^2 + 0^2} = 1.000
w\mathbf{w} 10.010.0 10/14.142=0.70710 / 14.142 = 0.707 92+102=13.454\sqrt{9^2 + 10^2} = 13.454

세 지표가 같은 세 후보에게 각각 다른 1등을 주는 그림

1등이 셋 다 다릅니다.

각 지표가 보는 것

  • 내적은 w\mathbf{w} 를 고릅니다. 방향이 45∘45^\circ 나 어긋났는데도 길이가 14배라 그것만으로 이깁니다. 기하적 정의에 노름 둘이 곱으로 들어 있으니 당연한 결과입니다.
  • 코사인은 v\mathbf{v} 를 고릅니다. 길이를 나눠 없앴으니 방향이 정확히 같은 것이 1등이고, v\mathbf{v} 가 두 배 길든 백 배 길든 값은 1로 같습니다.
  • L2 거리는 u\mathbf{u} 를 고릅니다. 방향은 v\mathbf{v} 가 낫지만 v\mathbf{v} 는 질의보다 한 칸 더 멀리 나가 있어 점 사이 거리가 벌어집니다.

세 지표가 각각 무엇을 보는지가 여기서 드러납니다. 내적은 방향과 크기를 함께 보고, 코사인은 방향만 보고, L2 거리는 좌표평면 위 두 점이 떨어진 정도를 봅니다. 셋 중 어느 것도 틀리지 않았습니다 — 서로 다른 질문에 답하고 있을 뿐입니다.

한 가지가 더 보입니다. 세 순위를 갈라 놓은 것은 후보들의 노름이 서로 다르다는 사실 하나입니다. u\mathbf{u} 는 1, v\mathbf{v} 는 2, w\mathbf{w} 는 14.142로 열네 배까지 벌어져 있습니다. 그렇다면 그 차이를 없애면 어떻게 될지가 다음 물음입니다.

정규화 뒤의 동치

2 − 2cos 항등식

모든 벡터를 단위벡터로 만듭니다. x^=x/∥x∥\hat{\mathbf{x}} = \mathbf{x} / \|\mathbf{x}\| 이고 ∥x^∥=1\|\hat{\mathbf{x}}\| = 1 입니다. 이 조작을 정규화라고 합니다 — 방향은 그대로 두고 길이만 1로 맞추는 일입니다.

정규화된 두 벡터에서는 세 지표가 서로의 함수가 됩니다. 맨 앞의 전개에 ∥a∥=∥b∥=1\|\mathbf{a}\| = \|\mathbf{b}\| = 1 을 넣습니다.

∥a−b∥2=1−2 a⋅b+1=2−2 a⋅b\|\mathbf{a} - \mathbf{b}\|^2 = 1 - 2\,\mathbf{a}\cdot\mathbf{b} + 1 = 2 - 2\,\mathbf{a}\cdot\mathbf{b}

그리고 코사인 유사도는 분모가 1이라 내적 그 자체입니다.

cos_sim⁡(a,b)=a⋅b1⋅1=a⋅b\operatorname{cos\_sim}(\mathbf{a},\mathbf{b}) = \frac{\mathbf{a}\cdot\mathbf{b}}{1 \cdot 1} = \mathbf{a}\cdot\mathbf{b}

셋을 한 줄에 묶으면

∥a−b∥2=2−2cos_sim⁡(a,b)=2−2 (a⋅b)\|\mathbf{a}-\mathbf{b}\|^2 = 2 - 2\operatorname{cos\_sim}(\mathbf{a},\mathbf{b}) = 2 - 2\,(\mathbf{a}\cdot\mathbf{b})

단위벡터에서 코사인이 오르면 거리가 내려가는 단조 대응

식이 주는 눈금도 읽어 둘 만합니다. 코사인이 1이면 거리가 0, 0이면 거리가 2≈1.414\sqrt{2} \approx 1.414, −1-1 이면 거리가 2입니다. 정규화된 벡터 사이의 L2 거리는 아무리 멀어도 2를 넘지 못한다는 뜻이고, 둘 다 반지름 1인 구 위에 있으니 당연합니다.

순위가 일치한다는 증명

내적이 커지면 코사인도 정확히 같은 값으로 커지고, 거리의 제곱은 그만큼 작아집니다. 2−2s2 - 2s 는 ss 에 대해 기울기가 −2-2 인 일차식이라 엄격히 감소합니다 — 중간에 방향을 바꿔 순위를 뒤집을 자리가 없습니다. 그리고 거리는 0 이상이라 제곱을 씌워도 대소가 그대로이므로 거리의 제곱 순위와 거리 순위가 같습니다. 따라서

정규화된 벡터에서는 내적 내림차순, 코사인 내림차순, L2 거리 오름차순이 정확히 같은 순위를 준다.

동점까지 포함해 같습니다 — s1=s2s_1 = s_2 이면 2−2s1=2−2s22-2s_1 = 2-2s_2 이기 때문입니다. top-k 를 뽑는다면 세 지표 중 무엇을 써도 같은 kk 개가 같은 순서로 나옵니다.

위의 세 후보를 정규화해 확인합니다.

u^=(0.6, 0.8),v^=(1, 0),w^=(0.707, 0.707)\hat{\mathbf{u}} = (0.6,\ 0.8), \qquad \hat{\mathbf{v}} = (1,\ 0), \qquad \hat{\mathbf{w}} = (0.707,\ 0.707)

후보 내적 = 코사인 2−2cos⁡2 - 2\cos L2 거리
u^\hat{\mathbf{u}} 0.6000.600 0.8000.800 0.8940.894
v^\hat{\mathbf{v}} 1.0001.000 0.0000.000 0.0000.000
w^\hat{\mathbf{w}} 0.7070.707 0.5860.586 0.7650.765

세 열의 순위가 모두 v^,w^,u^\hat{\mathbf{v}}, \hat{\mathbf{w}}, \hat{\mathbf{u}} 입니다. 가운데 열이 오른쪽 열의 제곱과 정확히 맞는 것도 확인됩니다 — 0.8942=0.8000.894^2 = 0.800, 0.7652=0.5860.765^2 = 0.586. 정규화 전에는 1등이 셋 다 달랐는데, 노름을 1로 맞추자 순위가 하나로 합쳐졌습니다.

코드로 확인

import numpy as np

q = np.array([1.0, 0.0])
C = np.array([[0.6, 0.8], [2.0, 0.0], [10.0, 10.0]])
names = np.array(['u', 'v', 'w'])

nrm = np.linalg.norm(C, axis=1)
dot, cos, l2 = C @ q, (C @ q) / nrm, np.linalg.norm(C - q, axis=1)

print(names[np.argsort(-dot)])   # ['w' 'v' 'u']
print(names[np.argsort(-cos)])   # ['v' 'w' 'u']
print(names[np.argsort(l2)])     # ['u' 'v' 'w']

정규화한 뒤 같은 것을 돌립니다.

Cn = C / nrm[:, None]
dot_n = Cn @ q
l2_n = np.linalg.norm(Cn - q, axis=1)

print(names[np.argsort(-dot_n)])          # ['v' 'w' 'u']
print(names[np.argsort(l2_n)])            # ['v' 'w' 'u']
print(np.allclose(l2_n**2, 2 - 2*dot_n))  # True

마지막 줄이 방금 증명한 항등식입니다. 셋째 줄이 True 인 한 첫 두 줄의 순위는 절대 갈릴 수 없습니다.

고차원에서의 코사인 집중

지금까지는 2차원에서 그렸지만 실제 임베딩은 768차원이나 1536차원입니다. 차원이 커지면 코사인 값이 어떻게 분포하는지를 한 번 재 두면 뒤에 쓸 일이 많습니다.

단위구 위에서 아무렇게나 뽑은 두 벡터의 코사인을 생각합니다. 대칭이므로 평균은 0이고, 분산을 계산하면 1/d1/d 가 나옵니다. 즉 표준편차가 1/d1/\sqrt{d} 입니다.

차원 dd 코사인의 표준편차 대략 몇 도에 해당하는가
2 0.707 45∘45^\circ
16 0.250 14∘14^\circ
256 0.063 3.6∘3.6^\circ
768 0.036 2.1∘2.1^\circ
rng = np.random.default_rng(0)
for d in [2, 16, 256, 768]:
    A = rng.normal(size=(20000, d)); B = rng.normal(size=(20000, d))
    A /= np.linalg.norm(A, axis=1, keepdims=True)
    B /= np.linalg.norm(B, axis=1, keepdims=True)
    c = (A * B).sum(1)
    print(d, round(float(c.std()), 4), round(1 / np.sqrt(d), 4))
# 2 0.7061 0.7071
# 16 0.2489 0.25
# 256 0.0627 0.0625
# 768 0.0362 0.0361

차원이 커질수록 무작위 두 벡터의 코사인이 0 주위로 모인다

768차원에서는 무작위로 뽑은 두 벡터가 거의 예외 없이 88∘88^\circ 와 92∘92^\circ 사이에 있습니다. 서로 아무 상관 없는 벡터들은 고차원에서 사실상 전부 직교한다는 말이고, 이것을 집중 현상이라고 부릅니다. 검색에서 코사인 0.3이 낮아 보여도 무작위 수준인 0.036의 여덟 배라는 점을 같이 보아야 하는 이유가 여기 있습니다. 이 관찰을 제대로 다루는 것은 「중급 66번 · 고차원 기하」의 몫이고, 여기서는 씨앗만 심어 둡니다.

지표 선택과 어텐션

정규화했을 때의 기준

인덱스에 넣기 전에 정규화했다면 세 지표 중 무엇을 골라도 결과가 같습니다. 그러면 남는 기준은 정확도가 아니라 속도입니다 — 내적이 나눗셈과 제곱근이 없어 가장 쌉니다. 실제로 많은 벡터 DB가 "코사인"을 고르면 내부에서 벡터를 정규화한 뒤 내적으로 계산합니다. 사용자에게는 다른 이름으로 보이지만 같은 연산입니다.

한 가지 단서가 있습니다. 코사인은 노름의 차이로 정의된 거리가 아닙니다. 5번에서 짚었듯 삼각부등식이 보장되지 않으므로, 거리를 전제하는 자료구조에 그대로 꽂으면 안 됩니다. 정규화 뒤 L2 로 바꿔 넣는 것이 그래서 흔한 수법입니다 — 위 항등식이 그 변환의 근거입니다.

정규화하지 않았을 때의 기준

정규화하지 않았다면 지표가 곧 정책입니다. 긴 벡터를 밀어 올리고 싶으면 내적, 방향만 보고 싶으면 코사인입니다.

어느 쪽이 옳은가는 임베딩 모델이 노름에 무엇을 담았는지에 달렸습니다. 어떤 모델은 긴 문서일수록 노름이 커지게 학습되고, 어떤 모델은 자신이 확신하는 입력일수록 노름이 커집니다. 앞쪽이라면 내적을 쓰는 순간 긴 문서가 무조건 유리해지고, 뒤쪽이라면 내적이 모델의 확신을 점수에 반영해 주는 셈이라 오히려 이득입니다. 모델 카드를 읽지 않고 지표부터 고를 수 없는 것이 이 때문입니다. 그 선택의 실전 기준은 벡터 유사도 지표: 코사인·유클리드·내적의 모든 것가 다룹니다. 이 글이 맡은 것은 그 위층 — 언제 고를 필요조차 없는가입니다.

QK^T 다시 읽기

1번 · 어텐션 식의 해부에서 softmax⁡(QK⊤/dk)V\operatorname{softmax}(QK^\top / \sqrt{d_k})V 의 QK⊤QK^\top 를 "질의와 키의 관련도"라고 읽고 지나갔습니다. 이제 그 줄을 정확히 읽을 수 있습니다.

QK⊤QK^\top 의 (i,j)(i, j) 칸은 qi⋅kj\mathbf{q}_i \cdot \mathbf{k}_j 이고, 방금 얻은 식으로는

qi⋅kj=∥qi∥ ∥kj∥cos⁡θij\mathbf{q}_i \cdot \mathbf{k}_j = \|\mathbf{q}_i\|\,\|\mathbf{k}_j\|\cos\theta_{ij}

관련도라는 말의 정체가 이 곱입니다 — 방향의 일치도 cos⁡θij\cos\theta_{ij} 에 두 벡터의 크기를 곱한 값입니다. 그리고 어텐션은 q\mathbf{q} 와 k\mathbf{k} 를 정규화하지 않습니다. 그러니 위의 반례가 그대로 살아 있습니다 — 방향이 어긋나 있어도 노름이 크면 그 키가 점수를 가져갑니다. 노름이 점수에 개입하는 것은 어텐션에서 버그가 아니라 설계이고, 모델은 WQW_Q·WKW_K 를 학습하며 그 크기까지 함께 정합니다.

다만 그렇게 되면 점수의 분산이 차원과 함께 커진다는 다른 문제가 생기고, 식에 dk\sqrt{d_k} 가 붙어 있는 것이 그 처방입니다. 그 이야기는 「중급 40번 · √d_k는 어디서 나왔나」가 맡습니다.

연습 문제

연습 1 — 순위가 갈리는 예 만들기

  1. q=(1,0)\mathbf{q} = (1, 0) 에 대해 후보 a=(0.8, 0.6)\mathbf{a} = (0.8,\ 0.6), b=(3, 0)\mathbf{b} = (3,\ 0), c=(5, 5)\mathbf{c} = (5,\ 5) 의 내적·코사인·L2 거리를 각각 구하고 세 지표의 1등을 적으세요.
    노름은 ∥a∥=1\|\mathbf{a}\|=1, ∥b∥=3\|\mathbf{b}\|=3, ∥c∥=50≈7.071\|\mathbf{c}\|=\sqrt{50}\approx 7.071 입니다. 내적은 0.80.8, 33, 55 이고 코사인은 0.80.8, 11, 0.7070.707 이며 L2 거리는 0.04+0.36=0.632\sqrt{0.04+0.36}=0.632, 22, 16+25≈6.403\sqrt{16+25}\approx 6.403 입니다. 1등은 내적이 c\mathbf{c}, 코사인이 b\mathbf{b}, L2 가 a\mathbf{a} 로 셋 다 다릅니다.
  2. q=(1,0)\mathbf{q} = (1, 0) 에 대해 내적과 코사인은 같은 후보를 1등으로 주는데 L2 거리만 다른 후보를 1등으로 주는 두 후보를 직접 만드세요.
    한 예는 x=(2, 0)\mathbf{x}=(2,\ 0) 과 y=(0.9, 0.3)\mathbf{y}=(0.9,\ 0.3) 입니다. 내적은 22 와 0.90.9, 코사인은 11 과 0.9/0.9≈0.9490.9/\sqrt{0.9}\approx 0.949 라 둘 다 x\mathbf{x} 가 1등이지만, L2 거리는 11 과 0.01+0.09≈0.316\sqrt{0.01+0.09}\approx 0.316 이라 y\mathbf{y} 가 1등입니다. x\mathbf{x} 가 방향은 정확한데 질의보다 한 칸 더 나가 있어 거리가 벌어진 것입니다.

연습 2 — 코시-슈바르츠와 전개

  1. a=(3, 4)\mathbf{a} = (3,\ 4), b=(1, 2)\mathbf{b} = (1,\ 2) 에서 ∣a⋅b∣|\mathbf{a}\cdot\mathbf{b}| 와 ∥a∥∥b∥\|\mathbf{a}\|\|\mathbf{b}\| 를 구하고 등호가 성립하는지 답하세요.
    a⋅b=3+8=11\mathbf{a}\cdot\mathbf{b} = 3 + 8 = 11 이고 ∥a∥=5\|\mathbf{a}\|=5, ∥b∥=5≈2.236\|\mathbf{b}\|=\sqrt{5}\approx 2.236 이므로 곱은 55≈11.1805\sqrt{5}\approx 11.180 입니다. 11≤11.18011 \le 11.180 으로 부등식이 성립하고 등호는 아닙니다. 등호가 되려면 b\mathbf{b} 가 a\mathbf{a} 의 상수배여야 하는데 (1,2)(1,2) 는 (3,4)(3,4) 의 상수배가 아닙니다.
  2. a=(2, 1)\mathbf{a} = (2,\ 1), b=(6, 3)\mathbf{b} = (6,\ 3) 에서 같은 두 값을 구하고, 등호가 성립한다면 b=ta\mathbf{b} = t\mathbf{a} 의 tt 를 적으세요.
    a⋅b=12+3=15\mathbf{a}\cdot\mathbf{b} = 12 + 3 = 15 이고 ∥a∥=5\|\mathbf{a}\|=\sqrt{5}, ∥b∥=45=35\|\mathbf{b}\|=\sqrt{45}=3\sqrt{5} 이므로 곱도 5⋅35=15\sqrt{5}\cdot 3\sqrt{5} = 15 입니다. 등호가 성립하고 t=3t = 3 입니다.
  3. a=(3, 4)\mathbf{a} = (3,\ 4), b=(1, 2)\mathbf{b} = (1,\ 2) 에서 ∥a−b∥2\|\mathbf{a}-\mathbf{b}\|^2 을 전개식 ∥a∥2−2a⋅b+∥b∥2\|\mathbf{a}\|^2 - 2\mathbf{a}\cdot\mathbf{b} + \|\mathbf{b}\|^2 으로 구한 뒤, 좌표를 직접 빼서도 구해 두 값을 맞춰 보세요.
    전개식으로는 25−2⋅11+5=25−22+5=825 - 2\cdot 11 + 5 = 25 - 22 + 5 = 8 입니다. 좌표로는 a−b=(2, 2)\mathbf{a}-\mathbf{b} = (2,\ 2) 이므로 22+22=82^2 + 2^2 = 8 입니다. 같은 값입니다.

연습 3 — 정규화 뒤의 일치

  1. a=(5, 12)\mathbf{a} = (5,\ 12) 를 정규화한 뒤 q^=(1, 0)\hat{\mathbf{q}} = (1,\ 0) 과의 내적·코사인·L2 거리를 구하고, L2 거리의 제곱이 2−2cos⁡2 - 2\cos 과 같은지 확인하세요.
    ∥a∥=25+144=13\|\mathbf{a}\| = \sqrt{25+144} = 13 이므로 a^=(5/13, 12/13)≈(0.3846, 0.9231)\hat{\mathbf{a}} = (5/13,\ 12/13) \approx (0.3846,\ 0.9231) 입니다. 내적과 코사인은 둘 다 0.38460.3846 이고, L2 거리의 제곱은 (1−0.3846)2+0.92312≈0.3787+0.8521=1.2308(1-0.3846)^2 + 0.9231^2 \approx 0.3787 + 0.8521 = 1.2308 입니다. 2−2(0.3846)=1.23082 - 2(0.3846) = 1.2308 로 같습니다.
  2. 정규화된 두 벡터의 코사인이 0.250.25 일 때 L2 거리를 구하세요.
    ∥a−b∥2=2−2(0.25)=1.5\|\mathbf{a}-\mathbf{b}\|^2 = 2 - 2(0.25) = 1.5 이므로 거리는 1.5≈1.225\sqrt{1.5} \approx 1.225 입니다.

정리

  • 내적은 ∑aibi\sum a_i b_i 이자 ∥a∥∥b∥cos⁡θ\|\mathbf{a}\|\|\mathbf{b}\|\cos\theta 입니다. 둘이 같다는 것은 ∥a−b∥2\|\mathbf{a}-\mathbf{b}\|^2 을 대수와 기하 두 방법으로 재서 견주면 나옵니다.
  • ∥a−b∥2=∥a∥2−2a⋅b+∥b∥2\|\mathbf{a}-\mathbf{b}\|^2 = \|\mathbf{a}\|^2 - 2\mathbf{a}\cdot\mathbf{b} + \|\mathbf{b}\|^2 이 이 글의 중심 식입니다.
  • 코시-슈바르츠 ∣a⋅b∣≤∥a∥∥b∥|\mathbf{a}\cdot\mathbf{b}| \le \|\mathbf{a}\|\|\mathbf{b}\| 는 ∥a+tb∥2≥0\|\mathbf{a}+t\mathbf{b}\|^2 \ge 0 의 판별식에서 나오고, 등호는 두 벡터가 평행할 때만 성립합니다. 덕분에 코사인이 [−1,1][-1,1] 에 갇힙니다.
  • 내적이 음수라는 것은 무관하다는 뜻이 아니라 방향이 반대라는 뜻입니다. 무관함에 해당하는 값은 0 근처입니다.
  • 정규화하지 않으면 세 지표가 갈립니다. q=(1,0)\mathbf{q}=(1,0) 에 (0.6,0.8)(0.6,0.8)·(2,0)(2,0)·(10,10)(10,10) 을 놓으면 1등이 셋 다 다릅니다.
  • 정규화하면 ∥a−b∥2=2−2cos⁡\|\mathbf{a}-\mathbf{b}\|^2 = 2 - 2\cos 이라 세 순위가 동점까지 포함해 정확히 일치합니다.
  • 차원이 커지면 무작위 두 벡터의 코사인이 표준편차 1/d1/\sqrt{d} 로 0에 모입니다. 768차원에서는 0.036입니다.
  • QK⊤QK^\top 는 방향의 일치도에 두 노름을 곱한 값이고, 어텐션은 정규화하지 않으므로 노름이 점수에 그대로 개입합니다.

다음 글은 각도의 특수한 값 하나 — 직각 — 를 파고듭니다. 내적이 0이라는 조건 하나에서 한 벡터를 다른 벡터로 설명하고 남는 것이 무엇인지가 거기서 나옵니다.


읽어주셔서 감사합니다. 😊

LATEST

수학의 최신 글

수학2026.09.07

양자화 오차: 격자 사상, 오차 분산, 이상치 채널

실수를 2^b개 격자에 사상할 때 오차의 분산이 왜 Δ²/12인지 유도하고, 그것이 비트당 6.02dB라는 SNR로 번역되는 과정을 실측과 대조했습니다. 이상치 하나가 나머지 값의 유효 비트를 어떻게 먹는지, 그리고 int4에서 성능이 무너지는 지점을 오차 예산으로 미리 계산하는 법까지.

중급18 MIN
수학2026.09.07

수치적으로 안정한 계산 패턴 모음

최댓값 빼기, 로그 공간, log1p·expm1, 분산의 두 공식, 정규화의 ε, fp32 누산, 역행렬 대신 solve — 프레임워크가 몰래 해 주는 일곱 가지를 하나씩 꺼내 각각 어떤 고장을 막는지 직접 재 봤습니다. 수식을 그대로 옮긴 코드가 왜 라이브러리보다 나쁜지에 대한 목록입니다.

중급22 MIN
수학2026.09.07

부동소수점은 어디서 새는가: 반올림, 상쇄, 더하는 순서

0.1 + 0.2가 0.3이 아닌 이유부터 시작해 머신 엡실론을 유도하고, 같은 16비트인데 fp16과 bf16이 서로 다른 지점에서 터지는 이유, 비슷한 수를 뺄 때 유효자리가 사라지는 파괴적 상쇄, 그리고 1,000만 개를 순서만 바꿔 더했을 때 오차가 백만 배 갈리는 실험까지 직접 재 봤습니다.

중급23 MIN