재랭킹 모듈을 손보다 보면 반드시 한 번은 이런 생각이 듭니다. 지금은 두 벡터의 내적으로 점수를 매기고 있는데, 내적 말고 다른 유사도 함수를 넣으면 안 되나. 「길이 차이는 무시하고 방향만 보되 가까울수록 급하게 점수가 오르는」 함수를 직접 짜서 꽂으면 되지 않나.
절반은 됩니다. 어떤 함수든 짜리 점수 표를 만들어 주기는 합니다. 문제는 그 표를 받아 쓰는 쪽 — SVM의 이차계획법이든 커널 회귀든 스펙트럴 군집화든 — 이 「이 표가 어떤 공간에서의 내적 표」라는 것을 전제로 돌아간다는 데 있습니다. 전제가 깨지면 최적화가 발산하거나, 조용히 엉뚱한 답을 내놓습니다.
이 글은 그 전제에 이름을 붙이고, 어떤 함수가 그 전제를 만족하는지 판정하는 법을 세우고, 판정을 통과한 함수가 왜 「만들지 않은 특징공간의 내적」이 되는지를 손계산으로 확인합니다. 지난 글에서 공분산 행렬의 고윳값을 읽어 임베딩의 쏠림을 쟀는데, 여기서도 읽을 것은 고윳값입니다. 다만 이번에는 가 아니라 행렬입니다.
그람 행렬
벡터 이 있을 때 그람 행렬(Gram matrix)은 모든 쌍의 내적을 담은 행렬입니다.
벡터를 행으로 쌓은 로 쓰면 한 줄입니다. 지난 글의 공분산 행렬 와 곱하는 순서만 다릅니다. 공분산은 차원끼리 짝지어 가 되고, 그람은 표본끼리 짝지어 이 됩니다.
그람 행렬에는 성질 둘이 붙어 있습니다.
대칭입니다. 내적이 이므로 입니다.
양반정치(positive semi-definite, PSD)입니다. 어떤 벡터 에 대해서도 이라는 성질이고, 증명이 한 줄입니다.
는 그냥 어떤 벡터이고, 벡터의 자기 자신과의 내적은 길이의 제곱이라 절대 음수가 될 수 없습니다. 대칭 행렬의 이차형식을 다룬 글에서 이차형식의 부호와 고윳값의 부호가 같다고 했으므로, 이것은 그람 행렬의 고윳값이 모두 0 이상이라는 말과 같습니다.
거꾸로도 성립합니다. 대칭이고 PSD인 행렬 가 있으면 로 대각화한 뒤 로 두면 입니다. 이라서 가 실수인 것이 핵심이고, 하나라도 음수면 이 구성이 무너집니다. 그래서 「대칭 + PSD」와 「어떤 벡터들의 그람 행렬」은 정확히 같은 말입니다.
아무 유사도 표나 그람 행렬이 되지는 않는다
사람이 손으로 매긴 유사도 표를 하나 봅시다. 문서 A · B · C에 대해 A와 B가 0.9로 비슷하고 B와 C도 0.9로 비슷한데 A와 C는 0.2밖에 안 된다고 적었습니다.
대칭이고 대각선이 1이라 그럴듯해 보이는데, 고윳값을 구하면 입니다. 음수가 하나 있으니 PSD가 아니고, 이 표를 재현하는 벡터 세 개는 세상에 존재하지 않습니다. 비슷함이 어느 정도 옮아가야 한다는 제약을 표가 어겼기 때문입니다.
PSD 판정에는 두 가지 방법이 있습니다.
고윳값을 직접 구합니다. 가장 확실하고, 대칭 행렬 전용 루틴(numpy.linalg.eigvalsh)이 실수 고윳값을 오름차순으로 돌려줍니다. 최소 고윳값이 0 이상이면 PSD입니다.
선행 주소행렬식을 봅니다. 왼쪽 위 부분행렬의 행렬식을 에 대해 계산한 것을 선행 주소행렬식(leading principal minor)이라고 하고, 이 값이 전부 양수인 것과 양정치(positive definite, 고윳값이 모두 0보다 큼)인 것이 서로 필요충분입니다. 실베스터 판정법이라고 부릅니다. 행렬식을 다룬 초급 글의 계산만으로 손으로 확인할 수 있는 것이 장점입니다.
의 선행 주소행렬식은 로 전부 양수이고 고윳값도 로 전부 양수입니다. 는 라 두 번째에서 이미 틀렸고, 고윳값은 입니다. 손으로 두 번째 행렬식만 계산해도 이 나와 거기서 끝납니다.
주의할 점 하나. 선행 주소행렬식이 전부 0 이상이라고 해서 PSD인 것은 아닙니다. 그 방향으로는 왼쪽 위만이 아니라 모든 주소행렬식을 봐야 하고, 그러면 개수가 로 늘어 손계산이 무의미해집니다. 실무에서는 최소 고윳값을 보는 쪽이 낫습니다.
커널: φ를 만들지 않고 내적만 얻기
이제 방향을 뒤집습니다. 「어떤 벡터들의 내적 표인가」를 판정하는 대신, 일부러 다른 공간의 내적 표를 만들어 쓰는 쪽입니다.
커널(kernel) 는 어떤 특징 사상(feature map) 가 있어서
로 쓸 수 있는 두 변수 함수입니다. 는 원본 공간의 벡터를 다른(보통 훨씬 큰) 공간의 벡터로 옮기는 함수입니다. Mercer 정리는 이것을 판정 가능한 조건으로 바꿔 줍니다 — 대칭 연속 함수 에 대해, 어떤 유한한 점들을 골라 그람 행렬 를 만들어도 그것이 항상 PSD이면 그런 가 존재합니다. 앞 절의 「대칭 + PSD ⟺ 어떤 벡터들의 그람 행렬」을 모든 유한 부분집합에 대해 요구한 것이 전부입니다.
여기서 실용적인 결론이 나옵니다.
가 존재한다는 것만 알면 되고 실제로 계산할 필요가 없습니다. 를 번 부르면 그람 행렬이 나오고, 이후의 계산이 그람 행렬만 쓴다면 를 한 번도 메모리에 올리지 않고 끝납니다. 이것이 커널 트릭이고, SVM을 다룬 글이 그 쓰임을 맡습니다. 이 글은 여기서 멈추고 그람 행렬 쪽만 봅니다.
다항 커널의 φ를 손으로 확인하기
「존재한다」는 말이 미덥지 않으면 가장 작은 예에서 직접 꺼내 보면 됩니다. 2차원 벡터에 대해 차수 2 다항 커널
를 전개합니다. , 라 두면
여섯 항이고, 항마다 쪽 조각과 쪽 조각이 곱으로 갈립니다. 그 조각을 모으면
이고, 계수 2를 씩 양쪽으로 나눠 준 것이 전부입니다. , 로 맞춰 보면
같습니다. 왼쪽은 곱셈 두 번과 덧셈 몇 번, 오른쪽은 6차원 벡터 둘을 만들어 내적입니다.
이 차이가 차원과 차수를 올리면 벌어집니다. 차원에서 차수 다항 커널의 특징 차원은 입니다.
| 원본 차원 | 차수 | 특징 차원 |
|---|---|---|
| 2 | 2 | 6 |
| 768 | 2 | 296,065 |
| 768 | 3 | 76,088,705 |
| 768 | 4 | 14,685,120,065 |
768차원에 차수 4면 특징 벡터 하나가 float32로 59GB입니다. 커널 쪽은 여전히 내적 한 번과 거듭제곱 한 번입니다.
가 하는 일을 그림으로 보면 이렇습니다.
1차원에서는 어디를 잘라도 두 색이 안 갈리는데, 축을 하나 더 붙이면 수평선 하나로 갈립니다. 차수 2 다항 커널이 암묵적으로 붙이는 축이 정확히 저 자리입니다.
RBF 커널: γ 하나가 유효 랭크를 정한다
RBF 커널(radial basis function, 가우시안 커널이라고도 합니다)은
입니다. 이쪽의 는 유한 차원이 아닙니다. 1차원에서 전개해 보면 이유가 보입니다.
마지막 줄에서 를 지수함수의 급수로 폈습니다. 항마다 쪽과 쪽이 갈리므로 번째 특징 성분은 이고, 이 무한히 갑니다. RBF는 무한 차원 특징공간의 내적이고, 그래서 를 실제로 만드는 길은 애초에 없습니다.
무한 차원이라고 해서 실제로 무한히 풍부한 것은 아닙니다. 데이터 개에 대한 그람 행렬은 이라 랭크가 아무리 커도 이고, 그중 얼마나 많은 축이 실제로 일하는지는 가 정합니다. 지난 글의 참여비를 그람 행렬의 고윳값에 그대로 적용해 재 봤습니다.
import numpy as np
rng = np.random.default_rng(4)
Z = rng.normal(size=(300, 5))
def rbf_gram(A, gamma):
d2 = ((A[:, None, :] - A[None, :, :]) ** 2).sum(-1)
return np.exp(-gamma * d2)
for gamma in (0.05, 0.3, 1.0, 3.0):
ev = np.linalg.eigvalsh(rbf_gram(Z, gamma))[::-1]
eff = ev.sum() ** 2 / (ev ** 2).sum() # 참여비 = 유효 랭크
k95 = int(np.searchsorted(np.cumsum(ev) / ev.sum(), 0.95) + 1)
print(f"gamma={gamma:<5} 최대 {ev[0]:7.2f} 최소 {ev[-1]:.1e} "
f"95%까지 {k95:3d}축 유효 랭크 {eff:6.2f}")
| 최대 고윳값 | 최소 고윳값 | 95%까지 필요한 축 | 유효 랭크 | |
|---|---|---|---|---|
| 0.05 | 192.17 | 3.6 × 10⁻⁹ | 13 | 2.35 |
| 0.3 | 53.14 | 7.7 × 10⁻⁵ | 106 | 20.24 |
| 1.0 | 12.33 | 9.7 × 10⁻³ | 222 | 130.38 |
| 3.0 | 3.02 | 8.3 × 10⁻² | 269 | 270.75 |
가 작으면 모든 쌍의 거리가 지수 안에서 0에 가까워져 그람 행렬이 「전부 1에 가까운 행렬」로 수렴합니다. 그런 행렬의 랭크는 사실상 1이고, 유효 랭크 2.35가 그것입니다. 커널을 썼지만 선형과 거의 다를 것이 없다는 뜻입니다.
가 크면 반대로 가 조금만 떨어져도 지수가 0으로 죽어 그람 행렬이 단위행렬에 가까워집니다. 유효 랭크 270.75는 점 300개가 각자 자기 축 하나씩을 갖는 상태이고, 학습 데이터는 완벽히 맞히지만 새 점은 어느 훈련점과도 안 닮아 예측이 0으로 떨어집니다.
를 고르는 일이 「매끄러움을 고르는 일」이라는 통상적인 설명은 이 표에서는 「그람 행렬의 유효 랭크를 고르는 일」로 다시 읽힙니다. 그리고 최소 고윳값 열을 보면 부수적인 사실 하나가 더 있습니다 — 에서 최소 고윳값이 이라, 그람 행렬을 그대로 역행렬 계산에 넣는 알고리즘은 여기서 터집니다. 커널 회귀가 처럼 대각선에 상수를 더하는 이유가 이것입니다.
PSD를 통과 못 하는 함수도 널리 쓰인다
「그럴듯한 유사도 함수」가 전부 커널인 것은 아닙니다. 자주 인용되는 반례가 시그모이드 커널 입니다. 위와 같은 300개 점에 , 로 써서 그람 행렬을 만들면 고윳값 300개 중 142개가 음수이고 최소가 였습니다. PSD가 아니므로 Mercer 조건을 통과하지 못하고, 대응하는 도 없습니다.
그런데도 쓰입니다. 그래서 판정을 통과 못 한 함수를 쓸 때 무엇을 잃는지 알아 두는 편이 낫습니다. SVM 같은 볼록 최적화는 그람 행렬이 PSD라는 전제 위에서 「최솟값이 하나뿐」임을 보장받는데, 음수 고윳값이 있으면 그 보장이 사라져 풀이가 국소해에 걸리거나 수렴하지 않을 수 있습니다. 실무에서는 음수 고윳값을 0으로 잘라 내거나 를 더해 PSD로 밀어 넣는 보정을 씁니다. 어느 쪽이든 원래 표를 조금 바꿔서 쓰는 것이라는 점을 알고 하는 것과 모르고 하는 것이 다릅니다.
어텐션 점수 행렬은 어디에 있나
트랜스포머의 어텐션이 계산하는 것도 유사도 행렬입니다. 어텐션 식을 해부한 글에서 쓴 대로
이고, 이것을 한 줄로 접으면
입니다. 형태가 그람 행렬 과 거의 같습니다. 가운데 하나가 끼어 있을 뿐이고, 이런 꼴 를 쌍선형 형식이라고 합니다. 「내적을 바꾼다」는 것의 가장 직접적인 형태입니다.
여기서 갈림길이 하나 있습니다. 이 대칭이고 PSD이면 로 쪼갤 수 있고, 그러면
라 인 선형 커널이 됩니다. 그람 행렬이 되는 것이고, 위에서 세운 성질이 전부 붙습니다.
실제로는 그렇지 않습니다. 와 는 따로 학습되는 서로 다른 행렬이라 은 대칭이 아닙니다. 무작위로 초기화한 행렬 둘로 실험해 보면 의 대칭 부분조차 고윳값이 에서 까지 걸쳐 PSD가 아니고, 토큰 8개짜리 점수 행렬 는 의 평균이 평균의 1.11배로 대칭과는 거리가 멉니다. 고윳값 8개 중 6개가 복소수로 나왔습니다.
그리고 이것이 결함이 아니라 요구사항입니다. 어텐션이 표현하려는 관계는 「 번 토큰이 번 토큰을 참조한다」인데, 이 관계는 대칭이 아닙니다. 대명사가 앞의 명사를 가리키는 것과 그 명사가 대명사를 가리키는 것은 다른 일입니다. 로 묶어 대칭으로 만들면 그람 행렬의 좋은 성질을 얻는 대신 그 방향성을 잃습니다.
에 붙는 것이 하나 더 있습니다. 행마다 softmax를 걸어 각 행의 합을 1로 만드는데, 이 연산도 대칭을 깹니다. 행 기준으로 정규화하므로 행의 분모와 행의 분모가 다르고, 원래 가 대칭이었더라도 결과는 비대칭이 됩니다.
정리하면 이렇습니다.
| 그람 행렬 | 커널 그람 행렬 | 어텐션 점수 | |
|---|---|---|---|
| 크기 | |||
| 대칭 | 예 | 예 | 아니오 |
| PSD | 예 | 예 (Mercer 조건) | 아니오 |
| 고윳값 | 실수, 0 이상 | 실수, 0 이상 | 복소수일 수 있음 |
| 대응하는 | 항등사상 | 존재하지만 안 만든다 | 없다 |
| 쓰는 방향 | 표본끼리의 유사도 | 다른 공간에서의 유사도 | 가 를 얼마나 볼지 |
셋은 「 유사도 행렬」이라는 같은 자리에 있고, 갈리는 지점은 전부 대칭성과 PSD 하나로 설명됩니다.
그래서 아무 유사도 함수나 꽂아도 되나
서두의 질문으로 돌아가면 답이 셋으로 갈립니다.
그람 행렬만 쓰는 알고리즘에 넣을 거라면 그 함수가 커널인지 먼저 확인해야 합니다. 확인은 대표 표본 몇백 개로 그람 행렬을 만들어 최소 고윳값을 보는 것이면 충분합니다. 음수가 나오면 그 함수는 어떤 공간의 내적도 아니고, 받아 쓰는 쪽의 보장이 무너집니다.
순위만 매길 거라면 훨씬 자유롭습니다. 위에서 만든 유사도 표는 PSD가 아니지만 「A와 B가 가깝다」는 순위 정보로는 멀쩡히 쓸 수 있습니다. 커널이어야 하는 이유는 유사도가 유용해서가 아니라 뒤에 오는 최적화가 내적 구조를 전제하기 때문입니다. 전제하는 것이 없으면 조건도 필요 없습니다.
직접 학습시킬 거라면 어텐션이 이미 그 답입니다. 의 을 데이터에서 배우게 하는 것이고, 대칭이나 PSD를 요구하지 않아 「참조한다」 같은 방향 있는 관계까지 담습니다. 대신 그람 행렬의 성질을 하나도 못 씁니다. 이 맞바꿈이 커널 방법과 학습된 어텐션을 가르는 자리입니다.
정리
- 그람 행렬 은 모든 쌍의 내적을 담은 행렬이다. 공분산 행렬은 로 차원끼리 짝짓고, 그람은 표본끼리 짝짓는다.
- 그람 행렬은 대칭이고 양반정치다. 한 줄로 끝난다. 거꾸로 대칭 PSD 행렬은 항상 어떤 벡터들의 그람 행렬이라, 「대칭 + PSD」와 「어떤 벡터들의 그람 행렬」은 같은 말이다.
- 그럴듯해 보이는 유사도 표가 PSD가 아닐 수 있다. 대각선이 1이고 대칭인 표에서 고윳값 이 나왔고, 그 표를 재현하는 벡터 셋은 존재하지 않는다.
- 판정은 최소 고윳값(
eigvalsh)이 실무의 답이다. 선행 주소행렬식(실베스터)은 손계산이 가능하지만 양정치까지만 판정한다. - 커널은 로 쓸 수 있는 함수이고, Mercer 정리가 그것을 「모든 유한 그람 행렬이 PSD」로 바꿔 준다. 는 존재만 하면 되고 만들 필요가 없다.
- 차수 2 다항 커널의 를 손으로 꺼내 확인했다. , 차수 4면 특징 차원이 146억이라 벡터 하나가 59GB인데 커널 쪽은 내적 한 번이다.
- RBF는 무한 차원이다. 를 급수로 펴면 항이 무한히 나온다. 다만 유효 랭크는 유한하고 가 그것을 정한다 — 300개 점에서 는 2.35, 은 270.75였다. 앞이 거의 선형, 뒤가 점마다 자기 축이다.
- 시그모이드 커널은 PSD가 아니다(고윳값 300개 중 142개가 음수). 쓰려면 음수를 잘라 내거나 를 더하는데, 그것은 원래 표를 바꿔서 쓰는 것이다.
- 어텐션 점수 행렬은 로 같은 자리에 있지만 이 비대칭이라 그람 행렬이 아니다. 고윳값이 복소수로 나온다. 그리고 이것이 결함이 아니다 — 「 가 를 참조한다」는 대칭이 아닌 관계라서다.
읽어주셔서 감사합니다. 😊

