본문으로 건너뛰기

임베딩: 기하학으로서의 의미

📍 현재 위치: Part III · 처음부터 배우는 학습(Learning from Scratch) — 11장. 앞 장 신경망(Neural Networks)에서는 벡터를 벡터로 바꾸고 경사 하강법(gradient descent)으로 학습하는 미분 가능한 함수 근사기(function approximator)를 만들었습니다. 그러나 우리의 학술 세계는 숫자가 아니라 alice, advises, p1 같은 기호(symbols)로 이루어져 있습니다. 이번 장에서는 빠져 있던 연결 통로, 즉 이런 기호를 신경망이 계산할 수 있는 벡터로 바꾸는 방법을 만듭니다.

신경망은 숫자를 더하고 곱하고 아래로 미끄러뜨리는 법만 압니다. alice가 무엇인지 전혀 알지 못합니다. 그러므로 지난 장의 장치가 우리의 지식 베이스(knowledge base)에 손을 대기 전에, 먼저 각 기호에 실수 벡터를 부여하고, 그 벡터들의 위치가 무언가를 의미하도록 배열해 주는 번역기가 필요합니다. 그 번역기가 바로 임베딩(embedding)이며, 이를 작동시키는 트릭은 기하학(geometry)이 의미를 실어 나르도록 하는 것입니다. 즉 비슷하게 행동하는 것들은 서로 가까이 놓이고, 하나의 관계는 공간을 가로지르는 고정된 이동이 됩니다. 이번 장은 이 아이디어를 밑바닥부터 유도하고, 모든 수식을 그것을 실행하는 동반 코드(companion code)의 정확한 줄과 연결한 다음, 실행 예제 위에서 이 메커니즘을 손으로 직접 따라갑니다. 하나의 무작위 초기화(random initialization)에서 시작해 한 번의 경사 하강 스텝을 거쳐 학습된 지도(map)에 이르기까지 말입니다.

쉽게 말하면

한 번도 가 본 적 없는 나라의 지도를 상상해 보세요. 그 나라의 언어는 모르지만, 지도만 봐도 많은 것을 읽어 낼 수 있습니다. 서로 교역하는 도시들은 가까이 모여 있고, 강은 마을들을 사슬처럼 연결하며, "북쪽으로 한 시간"은 수도에서 출발하든 시골 마을에서 출발하든 똑같은 화살표입니다. 임베딩은 이와 같은 방식으로 그린 의미의 지도입니다. 우리 세계 속의 모든 사람, 논문, 주제는 지도 위의 점이 됩니다. "누가 누구와 비슷한가"는 "어떤 점들이 가까운가"가 됩니다. 그리고 advises(지도하다)와 같은 관계는 어디에든 내려놓을 수 있는 하나의 화살표가 됩니다. 그 꼬리를 어떤 교수 위에 놓으면, 머리는 그 학생 위에 떨어집니다.

이 장에서 다루는 내용

  • 기호에서 벡터로: 분산 표현(distributed representation)이란 무엇인지, 벡터와 그 좌표란 무엇인지, 그리고 의미를 거리(distance)로 바꾸는 것이 왜 신경망으로 하여금 텍스트로는 결코 읽을 수 없었던 지식 베이스에 대해 추론하게 해 주는지.
  • TransE, 이동으로서의 관계: 모델 h+rt\mathbf{h} + \mathbf{r} \approx \mathbf{t}와, 유클리드 거리(Euclidean distance)로 유도된 점수 함수(score function)를 연산자 하나하나 풀어서 실제 코드와 대응시킵니다.
  • 마진 랭킹 손실(margin-ranking loss), 유도 과정: 오염된(corrupted) 트리플에 맞선 힌지(hinge) 목적함수와, 연쇄 법칙(chain rule)으로 고리 하나하나 풀어낸 그 그래디언트, 그리고 각 편미분을 정확한 업데이트 줄과 대응시킵니다.
  • 손으로 짚어 보는 한 번의 경사 하강 스텝: 실제 무작위 초기화에서 시작해, 참-거짓 한 쌍의 목적함수 값이 한 번의 스텝만에 7.347.34에서 4.764.76으로 떨어지는 과정을 완전한 수치 트레이스(numeric trace)로 보여 줍니다.
  • 실행 결과: 손실 곡선(loss curve), 18개 참 트리플 전체의 정렬된 점수표(평균이 정확히 보고된 1.6091.609가 됩니다), 그리고 alice가 누구를 지도하는지 추측하는 순위 매김 쿼리.
  • 기하학이 사는 것과 치르는 대가: 열린 세계 일반화(open-world generalization)를, 결코 침묵하지 않지만 결코 확신하지도 않는 스코어러(scorer)의 대가와 맞세우고, 그 수치로 구체화합니다.
  • 아직 풀리지 않은 부분: bob의 두 학생을 놓고, 평평한 점-화살표 기하학이 왜 논리적 위계(hierarchy)를 충실히 담아낼 수 없는지를 수치로 보여 줍니다.

기호에서 벡터로

이 권의 앞선 모든 장은 alice를 하나의 불투명한 토큰(token)으로 다루었습니다. 매칭하거나, 규칙을 사슬처럼 연쇄시키거나, 조회할 수는 있지만 결코 측정할 수는 없는 것이었습니다. Part II의 전방 연쇄(forward-chaining) 엔진은 우리의 기본 사실 23개를 갈아 47개 원자(atom)로 이루어진 유도된 모델(derived model)에 이르게 한 다음 멈추었는데, 이는 논리가 오직 무엇이 따라 나오는지만을 말할 뿐, carol이 어떤 식으로든 dave비슷하다는 개념을 전혀 갖고 있지 않기 때문입니다. 임베딩은 정반대의 입장을 취합니다. 그것은 모든 기호에 짧은 실수 목록, 즉 그 기호의 좌표를 할당하고, 저장된 어떤 사실이 아니라 바로 그 좌표야말로 의미가 깃드는 곳이라고 주장합니다.

이것들을 사용하기에 앞서 그 대상 자체에 대해 한마디 해 두겠습니다. 여기서 벡터(vector)란 그저 dd개의 실수로 이루어진 순서 있는 목록이며, v=(v1,v2,,vd)\mathbf{v} = (v_1, v_2, \ldots, v_d)로 씁니다. 굵은 글씨는 이것이 하나의 숫자가 아니라 벡터임을 표시하고, 그 개수 dd차원(dimension)입니다. 각 viv_i좌표(coordinate)이며, 아래첨자 ii는 어느 좌표를 뜻하는지 이름 붙이는 인덱스(index)로, 11부터 dd까지 뻗어 갑니다. 동반 코드에서는 d=2d = 2이므로 모든 기호는 보통의 평평한 평면 위의 점 (v1,v2)(v_1, v_2)이며, 그래서 우리는 모델 전체를 종이 위에 그릴 수 있습니다. i=1,2i = 1, 2라고 쓸 때는 그저 첫 번째와 두 번째 좌표를 차례로 가리키는 것일 뿐입니다.

이 도식, 즉 좌표로서의 의미(meaning-as-coordinates)는 분산 표현(distributed representation)이라고 불립니다: 기호의 의미는 하나의 슬롯에 담긴 하나의 깃발이 아니라 모든 좌표에 걸쳐 퍼져 있는 패턴이며, 그래서 가까운 패턴은 비슷한 것을 의미합니다 [1]. 이로부터 얻는 보상은, "누가 누구를 닮았는가"라는 흐릿한 인간의 질문이 정확한 기하학적 질문으로 바뀐다는 것입니다: 유사성은 거리다(similarity is distance). 벡터가 서로 가까이 놓인 두 개체는 비슷하다고 취급되고, 멀리 떨어진 두 개체는 그렇지 않습니다.

짝이 되는 파일 embeddings.py는 배치해야 할 기호들을 정확히 모으는 데서 시작합니다. 그것은 지식 베이스를 읽어 들여 이항 사실(binary fact)들만, 즉 advisescites처럼 두 사물을 관계 짓는 것들만 남기는데, 이는 관계라는 것이 머리(head)(시작하는 곳)와 꼬리(tail)(끝나는 곳)를 둘 다 필요로 하기 때문입니다. 이것이 _binary_triples이며, 23번째 줄부터 29번째 줄까지입니다:

def _binary_triples():
"""Every binary fact (advises, cites, affiliated, about) as an (h, r, t)
triple, plus the sorted entity and relation vocabularies."""
triples = [(f[1], f[0], f[2]) for f in FACTS if len(f) == 3]
ents = sorted({h for h, _, _ in triples} | {t for _, _, t in triples})
rels = sorted({r for _, r, _ in triples})
return triples, ents, rels

필터 if len(f) == 3은 항수(arity) 2인 사실(술어 하나에 인자 두 개)만 남기고, 재배열 (f[1], f[0], f[2])는 저장된 각 사실 (predicate, arg1, arg2)를 트리플 순서 (head, relation, tail)로 재배치합니다. 이어지는 두 개의 집합 축약(set-builder)은 개체와 관계의 정렬된 어휘를 모으는데, 여기서 기호 |는 집합의 합집합이고 {... for ...}는 집합을 만듭니다(그래서 중복은 사라집니다). 학술 세계에서 이는 정확히 13개의 개체5개의 관계에 걸친 18개의 트리플을 만들어 냅니다. 13개의 개체는 다섯 명의 사람(alice, bob, carol, dave, erin), 세 편의 논문(p1, p2, p3), 두 개의 기관(mit, cmu), 세 개의 주제(logic, ml, nesy)이며, 정렬된 5개의 관계는 about, advises, affiliated, authored, cites입니다. 다음은 코드가 만드는 전체 트리플 목록을 순서대로 나타낸 것입니다:

#트리플 (h,r,t)(h, r, t)읽는 방법
0–3advises 사슬alice→bob, bob→carol, bob→dave, carol→erin
4–7authoredalice→p1, bob→p1, carol→p2, dave→p3
8–9citesp2→p1, p3→p2
10–14affiliatedalice→mit, bob→mit, carol→cmu, dave→cmu, erin→cmu
15–17aboutp1→logic, p2→nesy, p3→ml

각 트리플은 (h,r,t)(h, r, t)로 쓰이며: "머리 hh가 관계 rr을 통해 꼬리 tt에 이른다"라고 읽으면 됩니다. 예를 들어 (alice,advises,bob)(\texttt{alice}, \texttt{advises}, \texttt{bob})처럼 말입니다. 곧 쓰게 될 기호 \in은 "~의 원소이다"라고 읽으며, 따라서 "(h,r,t)T(h, r, t) \in T"는 그 트리플이 알려진 참인 사실들의 집합 TT에 속함을 뜻합니다. 우리가 할 일은 13개의 개체와 5개의 관계 전부를 점으로 배치한 다음, 오직 기하학만으로 모든 트리플을 판단하는 것입니다.

TransE: 관계는 하나의 이동이다

어떤 기하학일까요? 있을 수 있는 것 중 가장 단순하면서도, 이 분야 전체를 출범시킨 바로 그것입니다: TransE, 즉 번역 임베딩(translating embeddings)의 줄임말입니다 [2]. 그 단 하나의 아이디어는 관계란 하나의 이동(translation)이라는 것입니다. 어떤 점에 더해 다른 곳으로 옮기는 고정된 화살표라는 것입니다.

먼저 표기법을 평이한 말로 풀어보겠습니다. 머리 개체(head entity)의 벡터를 h\mathbf{h}, 관계의 벡터를 r\mathbf{r}, 꼬리 개체(tail entity)의 벡터를 t\mathbf{t}라고 씁시다. 이 셋은 모두 같은 dd차원 공간에 살고 있습니다. TransE는 참인 트리플에 대해 다음이 성립하기를 요구합니다,

h+r    t.\mathbf{h} + \mathbf{r} \;\approx\; \mathbf{t}.

더하기 기호는 보통의 벡터 덧셈(vector addition)입니다: 두 벡터를 더한다는 것은 좌표별로 더하는 것이며, (h+r)i=hi+ri(\mathbf{h} + \mathbf{r})_i = h_i + r_i이고, 이는 기하학적으로 점 h\mathbf{h}를 화살표 r\mathbf{r}을 따라 미끄러뜨리는 것입니다. 기호 \approx는 "거의 같다"라고 읽으며, 여기서는 "거의 정확히 그 위에 떨어져야 한다"라는 뜻입니다. 그러므로 (alice,advises,bob)(\texttt{alice}, \texttt{advises}, \texttt{bob})이 참이라면, alice의 점에서 출발해 advises 화살표를 더하면 bob의 점 바로 옆에 떨어져야 합니다. 같은 advises 화살표를 bob에 더하면 carol 근처에 떨어져야 하고, carol에 더하면 erin 근처에 떨어져야 합니다. 관계 하나, 화살표 하나, 어디서나 재사용됩니다.

거리로 유도된 점수

관계 \approx는 하나의 열망일 뿐 측정값이 아닙니다. 훈련하고 평가하기 위해서는 h+r\mathbf{h} + \mathbf{r}t\mathbf{t} 위에 얼마나 가깝게 떨어지는지를 말해 주는 숫자가 필요합니다. 그 숫자가 바로 점수(score)이며, 이는 남은 간격 벡터 h+rt\mathbf{h} + \mathbf{r} - \mathbf{t}의 길이입니다:

s(h,r,t)  =  h+rt  =  i=1d(hi+riti)2.s(h, r, t) \;=\; \big\lVert\, \mathbf{h} + \mathbf{r} - \mathbf{t} \,\big\rVert \;=\; \sqrt{\, \sum_{i=1}^{d} \big(h_i + r_i - t_i\big)^2 \,}.

풀어야 할 기호가 셋 있습니다. 이중 세로줄 \lVert \cdot \rVert유클리드 노름(Euclidean norm)을 나타내며, 벡터의 보통의 직선 길이로, 피타고라스 정리에 의해 그 좌표들의 제곱합의 제곱근입니다. 대문자 시그마 i=1d\sum_{i=1}^{d}합산(summation)입니다: "인덱스 ii11부터 dd까지 돌아가면서 다음 식을 더하라"라는 뜻이며, 그래서 우리의 2차원 경우에는 i=12ci=c1+c2\sum_{i=1}^{2} c_i = c_1 + c_2입니다. 근호 x\sqrt{\phantom{x}}는 제곱근입니다. 종합하면: 간격 벡터를 만들고, 그 각 좌표를 제곱하고, 그 제곱들을 더하고, 근을 취합니다. 작은 점수는 이동이 잘 들어맞았음(그럴듯함)을 뜻하고, 큰 점수는 그렇지 않았음(그럴듯하지 않음)을 뜻합니다. 낮을수록 더 좋은데, 점수는 보상이 아니라 벌점(penalty)이기 때문입니다.

이 두 연산은 파일 안의 정확히 두 함수입니다. 제곱 거리는 _dist2(32번째 줄부터 33번째 줄까지)이며, score는 그것을 제곱근으로 감쌉니다(101번째 줄부터 104번째 줄까지):

def _dist2(a, b):
return sum((ai - bi) ** 2 for ai, bi in zip(a, b))


def score(E, R, h, r, t) -> float:
"""Plausibility as distance: lower means the translation fits better."""
dim = len(E[h])
return math.sqrt(_dist2([E[h][i] + R[r][i] for i in range(dim)], E[t]))

E는 개체 벡터들의 딕셔너리이고 R은 관계 벡터들의 딕셔너리입니다. 컴프리헨션 [E[h][i] + R[r][i] for i in range(dim)]는 좌표별로 만든 벡터 합 h+r\mathbf{h} + \mathbf{r}이고, _dist2i(hi+riti)2\sum_i (h_i + r_i - t_i)^2를 계산하며, math.sqrt는 그 근을 취합니다. 여기 그 어디에도 advises무엇을 의미하는지 아는 것은 없으며, 오직 화살표가 어디를 가리키는지만 있을 뿐입니다. 앞으로 나올 유도 과정에 대해 한 가지 세부 사항이 중요합니다: 아래의 훈련 루프는 제곱근을 건너뛰고 제곱 거리 i()2\sum_i(\cdot)^2를 직접 최소화하는 반면, score는 근을 취한 유클리드 거리를 보고합니다. 제곱근은 엄격하게 증가하는 함수이므로 두 점수의 순서를 결코 바꾸지 않으며, 따라서 참인 트리플을 거짓인 트리플보다 낮게 순위 매기는 문제는 제곱 거리를 쓰든 그 근을 쓰든 동일한 문제입니다. 훈련 중에 근을 생략하는 것은 그저 그것이 더 저렴하고 그 도함수가 더 단순하기 때문입니다.

학술 세계를 임베딩으로 그린 두 부분짜리 다이어그램. 왼쪽에는 2차원 지도가 열세 개의 개체를 점으로 배치하는데 — 사람 alice, bob, carol, dave, erin, 논문 p1, p2, p3, 기관 mit와 cmu, 주제 logic, ml, nesy — 지도 사슬이 각 다음 노드에 닿는 파란색 advises 화살표(alice에서 bob으로, bob에서 carol로, carol에서 erin으로)로 그려져, 동일한 고정된 advises 이동 벡터가 재사용되어 사슬 전체를 걸어감을 보여준다. 오른쪽에는 "그럴듯함은 이동 거리와 같다"라는 제목의 패널이 두 개의 트리플을 가로 막대로 점수화한다: advises(alice, bob)은 거리 0.399의 짧은 초록색 막대로 참(true)이라 표시되고, advises(alice, erin)은 거리 0.929의 긴 빨간색 막대로 거짓(false)이라 표시되는데 — 거리가 낮을수록 더 그럴듯한 사실이라는 규칙 아래에서다. TransE는 모든 관계를 재사용 가능한 하나의 화살표로 그린다: 머리의 점에 관계의 화살표를 더한 것이 꼬리의 점 위에 떨어져야 하며, 그래서 짧게 남은 거리는 그럴듯한 사실을, 긴 거리는 그럴듯하지 않은 사실을 뜻한다. 저자가 AI의 도움을 받아 직접 제작한 원본 도해.

참인 트리플을 오염된 트리플보다 높이 순위 매기며 학습하기

우리는 무작위 화살표에서 출발하므로, 처음에는 기하학이 엉터리입니다. 이 파일은 각 시작 좌표를 구간 [0.6,0.6][-0.6, 0.6]에서 균등하게 뽑고, 실행이 재현 가능하도록 생성기의 시드를 고정합니다. 훈련은 화살표를 고정해 나가지만, 여기에는 미묘한 문제가 하나 있습니다. 우리 지식 베이스는 오직 참인 사실만을 나열할 뿐, (alice,advises,erin)(\texttt{alice}, \texttt{advises}, \texttt{erin})이 거짓이라고 결코 말하지 않습니다. 음성 예시(negative example)가 전혀 없다면, 게으른 모델은 모든 벡터를 원점으로 줄여 모든 것에 0점을 매기고 모든 트리플을 똑같이 그럴듯하다고 선언해 버릴 수 있습니다. TransE는 두 가지 방법을 동시에 써서 그러한 붕괴를 피합니다.

첫째, 그것은 음성 예시를 만들어냅니다. 각 참인 트리플에 대해 꼬리를 무작위로 고른 잘못된 개체 tnt_n으로 바꾸어 오염된 트리플(corrupted triple)을 만든 다음, 참인 트리플이 오염된 트리플보다 적어도 고정된 마진(margin) γ\gamma(그리스 문자 감마)만큼은 더 낮은 점수를 받아야 한다고 요구하는데, 코드에서 이 값은 11로 설정되어 있습니다. 둘째, 벡터들이 무한대로 부풀어 부정 행위를 하는 것을 금지합니다: 매 에포크(epoch)가 시작될 때마다 각 개체 벡터는 단위 길이로 재정규화(renormalize)되어, 단위원 e=1\lVert \mathbf{e} \rVert = 1 위로 투영되는데, 이는 원래의 TransE가 규정한 그대로입니다 [2]. 그 재정규화가 바로 normalize이며, 53번째 줄부터 55번째 줄까지로, 매 에포크마다 모든 개체에 적용됩니다:

def normalize(v):
n = math.sqrt(sum(x * x for x in v)) or 1.0
return [x / n for x in v]

이것은 벡터를 자기 자신의 길이 nn으로 나누어, 결과가 길이 1을 갖도록 만듭니다. (or 1.0은 정규화가 불가능한, 있을 수 없는 길이-0 벡터의 경우를 방지합니다.) 오직 개체만 재정규화되며, 관계 화살표는 이동에 가장 잘 맞는 어떤 길이로든 자유롭게 자라도록 놓아둡니다. 구체적인 사례로, alice에 대한 첫 원시 추출값은 a=(0.4133,0.3095)\mathbf{a} = (0.4133, 0.3095)이며, 그 길이는

n=0.41332+0.30952=0.1708+0.0958=0.2666=0.5163,n = \sqrt{0.4133^2 + 0.3095^2} = \sqrt{0.1708 + 0.0958} = \sqrt{0.2666} = 0.5163,

이므로, 정규화는 alice(0.4133/0.5163,  0.3095/0.5163)=(0.8004,0.5995)(0.4133/0.5163,\; 0.3095/0.5163) = (0.8004, 0.5995)로 보내며, 이는 0.80042+0.59952=1.000010.8004^2 + 0.5995^2 = 1.0000 \approx 1이므로 단위원 위의 한 점입니다.

마진 랭킹 목적함수와 그 그래디언트

이제 이 전체 실행을 이끄는 수학입니다. 알려진 참인 트리플들을 집합 TT에 모읍니다. 각 참인 트리플 (h,r,t)T(h, r, t) \in T에 대해 우리는 오염된 꼬리 tnt_n을 뽑고 두 개의 제곱 거리를 정의합니다,

d+  =  h+rt2,d  =  h+rtn2,d^{+} \;=\; \big\lVert \mathbf{h} + \mathbf{r} - \mathbf{t} \big\rVert^2, \qquad d^{-} \;=\; \big\lVert \mathbf{h} + \mathbf{r} - \mathbf{t}_n \big\rVert^2,

참인 트리플에 대해서는 "dd-플러스", 오염에 대해서는 "dd-마이너스"라고 읽습니다. 데이터셋 전체에 걸쳐 합산한 마진 랭킹 손실(margin-ranking loss)은

L  =  (h,r,t)Tmax ⁣(0,  γ+d+d).L \;=\; \sum_{(h,r,t)\in T} \max\!\big(0,\; \gamma + d^{+} - d^{-}\big).

연산 max(0,)\max(0, \cdot)힌지(hinge)입니다: 그 인자가 양수이면 그 인자를 그대로 반환하고, 그렇지 않으면 0을 반환합니다. 이 항 전체를 탈출구가 달린 하나의 요구로 읽으십시오. 참인 트리플이 이미 가짜보다 마진보다 더 큰 차이로 이기고 있다면, 즉 dd+>γd^{-} - d^{+} \gt \gamma라면, γ+d+d<0\gamma + d^{+} - d^{-} \lt 0이 되고, 힌지는 이를 0으로 눌러 버리며, 이 트리플은 손실에 아무것도 기여하지 않습니다. 고칠 것이 없기 때문입니다. 아직 올바르게 순위 매겨지지 않은, 혹은 충분히 그렇게 되지 않은 트리플만이 벌점을 물고 밀어내기를 받습니다. 그 한 줄의 논리가 바로 훈련 루프의 continue이며, 79번째 줄부터 88번째 줄까지입니다:

for h, r, t in triples:
# corrupt the tail with a random wrong entity
tn = rng.choice(ents)
while tn == t:
tn = rng.choice(ents)
hv, rv, tv, tnv = E[h], R[r], E[t], E[tn]
d_pos = _dist2([hv[i] + rv[i] for i in range(dim)], tv)
d_neg = _dist2([hv[i] + rv[i] for i in range(dim)], tnv)
if margin + d_pos - d_neg <= 0:
continue # already ranked correctly by the margin

힌지가 활성화되어 있을 때, 즉 γ+d+d>0\gamma + d^{+} - d^{-} \gt 0일 때, 이 트리플의 손실은 =γ+d+d\ell = \gamma + d^{+} - d^{-}이며, 우리는 이를 경사 하강으로 내려갑니다. 이는 정확히 경사 하강법 장에서 나온 업데이트 규칙 θθηθ\theta \leftarrow \theta - \eta\, \nabla_\theta \ell입니다(여기서 \nabla는 그래디언트, 즉 편미분들의 벡터이며, θ\theta는 훈련되는 숫자들을 모은 것임을 떠올리십시오), η\eta는 학습률(learning rate)입니다(여기서는 lr = 0.05). 그래디언트는 유도하기 짧습니다. 두 남은 벡터를 좌표별로 도입합니다,

ui  =  hi+riti(h+rt의 i번째 좌표),vi  =  hi+ritn,i.u_i \;=\; h_i + r_i - t_i \quad(\mathbf{h}+\mathbf{r}-\mathbf{t}\text{의 } i\text{번째 좌표}), \qquad v_i \;=\; h_i + r_i - t_{n,i}.

그러면 d+=iui2d^{+} = \sum_i u_i^2이고 d=ivi2d^{-} = \sum_i v_i^2입니다. 각 제곱 거리는 제곱들의 합이므로, 그 편미분은 거듭제곱 법칙(power rule)과 연쇄 법칙(chain rule)에 의해 좌표 하나씩 구해집니다. uiu_ihih_i 또는 rir_i가 증가할 때 비율 11로 증가하고 tit_i가 증가할 때 비율 1-1로 증가하므로,

d+hi=2ui,d+ri=2ui,d+ti=2ui,\frac{\partial d^{+}}{\partial h_i} = 2 u_i, \qquad \frac{\partial d^{+}}{\partial r_i} = 2 u_i, \qquad \frac{\partial d^{+}}{\partial t_i} = -2 u_i,

이고, 오염에 대해서도 동일하게, viv_i는 오염된 꼬리 tn,it_{n,i}에 비율 1-1로 의존하므로,

dhi=2vi,dri=2vi,dtn,i=2vi.\frac{\partial d^{-}}{\partial h_i} = 2 v_i, \qquad \frac{\partial d^{-}}{\partial r_i} = 2 v_i, \qquad \frac{\partial d^{-}}{\partial t_{n,i}} = -2 v_i.

이제 =γ+d+d\ell = \gamma + d^{+} - d^{-}를 조립합니다. 마진 γ\gamma는 상수이므로 모든 도함수에서 사라집니다. 좌표별로 항을 모으면:

  hi=2ui2vi,ri=2ui2vi,ti=2ui,tn,i=+2vi.  \boxed{\; \frac{\partial \ell}{\partial h_i} = 2u_i - 2v_i, \quad \frac{\partial \ell}{\partial r_i} = 2u_i - 2v_i, \quad \frac{\partial \ell}{\partial t_i} = -2u_i, \quad \frac{\partial \ell}{\partial t_{n,i}} = +2v_i. \;}

여기서 잠시 짚어 볼 만한 것이 두 가지 있습니다. 첫째, 머리와 관계는 동일한 그래디언트 2ui2vi2u_i - 2v_i를 받는데, 이는 이 둘이 오직 자신들의 합 h+r\mathbf{h} + \mathbf{r}을 통해서만 점수에 들어가기 때문이며, 그래서 손실 안의 그 무엇도 둘을 구별할 수 없습니다. 둘째, 방향을 읽어 보십시오. η/ti=+2ηui-\eta\,\partial\ell/\partial t_i = +2\eta u_i만큼 내려가는 것은 참인 꼬리 t\mathbf{t}h+r\mathbf{h} + \mathbf{r} 쪽으로 움직여서 d+d^{+}를 끌어내리고, η/tn,i=2ηvi-\eta\,\partial\ell/\partial t_{n,i} = -2\eta v_i만큼 내려가는 것은 가짜 꼬리를 h+r\mathbf{h} + \mathbf{r}에서 멀리 움직여서 dd^{-}를 밀어올립니다. 이것이 정확히 코드의 "push d_pos down, d_neg up"(d_pos는 낮추고 d_neg는 높인다)이라는 주석이며, 89번째 줄부터 97번째 줄까지의 네 개의 업데이트 줄은 위 박스 안의 공식을 그대로 옮겨 적은 것입니다:

for i in range(dim):
diff_pos = hv[i] + rv[i] - tv[i]
diff_neg = hv[i] + rv[i] - tnv[i]
# push d_pos down, d_neg up
gh = 2 * diff_pos - 2 * diff_neg
E[h][i] -= lr * gh
R[r][i] -= lr * (2 * diff_pos - 2 * diff_neg)
E[t][i] -= lr * (-2 * diff_pos)
E[tn][i] -= lr * (2 * diff_neg)

여기서 diff_posuiu_i, diff_negviv_i, gh/hi\partial \ell / \partial h_i이며, 각 -= lr * (...)θθη/θ\theta \leftarrow \theta - \eta\,\partial\ell/\partial\theta의 좌표 하나입니다. 위의 탈출구 continue는 바깥쪽의 max(0,)\max(0,\cdot)을 구현합니다: 그 쌍이 이미 잘 순위 매겨져 있을 때는 그래디언트가 0이고 아무것도 움직이지 않습니다. 이것이 확률적 경사 하강(stochastic gradient descent, SGD)이며, 신경망 장에서 본 것과 똑같은, 조금씩 내리막을 따라가는 옵티마이저(optimizer)를 이번에는 지도 위의 점들에, 한 번에 하나의 트리플(그리고 새로 뽑은 오염 하나)씩 적용한 것입니다.

전체 루틴은 이를 수천 번의 패스(pass)에 걸쳐 반복하며, 매 라운드마다 모든 개체를 재정규화합니다. 그 시그니처(36번째 줄부터 38번째 줄까지)는 하이퍼파라미터를 고정합니다:

def train_transe(dim: int = 2, lr: float = 0.05, epochs: int = 4000,
margin: float = 1.0, seed: int = 0, eval_seed: int = 7,
loss_curve=None):

2차원(dim=2), 4000번의 에포크(epoch), 0.05의 학습률, 마진 γ=1.0\gamma = 1.0, 그리고 실행이 재현 가능하도록 고정된 seed입니다. 마지막 두 인자는 아래의 손실 트레이스(loss trace)를 위한 것입니다: eval_seed는 평가용 오염(evaluation corruption)의 단일 집합을 한 번 고정하고, loss_curve 딕셔너리를 넘기면 그 고정된 집합에 대한 에포크별 손실을 기록하며, 이는 훈련 그 자체를 결코 어지럽히지 않도록 자기 자신만의 별도 난수 생성기를 사용합니다.

손으로 짚어 보는 한 번의 경사 하강 스텝

이 메커니즘이 한 번 움직이는 것을 지켜봅시다. 첫 번째 에포크가 시작될 때, 재정규화 이후, 맨 처음 트리플 (alice,advises,bob)(\texttt{alice}, \texttt{advises}, \texttt{bob})에 관련된 네 개의 벡터는 시드가 고정된 실행에서 나온 다음의 실제 숫자들이며, 생성기가 꼬리에 대해 뽑은 무작위 오염은 p2입니다:

역할기호에포크-0 정규화 이후의 벡터
머리alice = h\mathbf{h}(0.8004,  0.5995)(0.8004,\; 0.5995)
관계advises = r\mathbf{r}(0.4956,  0.5599)(0.4956,\; 0.5599)
참인 꼬리bob = t\mathbf{t}(0.3129,  0.9498)(-0.3129,\; -0.9498)
오염된 꼬리p2 = tn\mathbf{t}_n(0.9080,  0.4189)(0.9080,\; 0.4189)

먼저 이동한 머리: h+r=(0.8004+0.4956,  0.5995+0.5599)=(1.2960,  1.1594)\mathbf{h} + \mathbf{r} = (0.8004 + 0.4956,\; 0.5995 + 0.5599) = (1.2960,\; 1.1594). 이제 두 제곱 거리를 좌표별로 계산합니다:

d+=(1.2960+0.3129)2+(1.1594+0.9498)2=1.60892+2.10922=2.5887+4.4486=7.0373,d^{+} = (1.2960 + 0.3129)^2 + (1.1594 + 0.9498)^2 = 1.6089^2 + 2.1092^2 = 2.5887 + 4.4486 = 7.0373, d=(1.29600.9080)2+(1.15940.4189)2=0.38802+0.74052=0.1505+0.5484=0.6989.d^{-} = (1.2960 - 0.9080)^2 + (1.1594 - 0.4189)^2 = 0.3880^2 + 0.7405^2 = 0.1505 + 0.5484 = 0.6989.

무작위 출발이 우리에게 안겨 주는 곤혹스러움에 주목하십시오: 거짓 꼬리 p2h+r\mathbf{h}+\mathbf{r}로부터 제곱 거리 0.700.70에 앉아 있는 반면, 참인 꼬리 bob은 훨씬 멀리 7.047.04에 앉아 있습니다. 마진 검사는 γ+d+d=1+7.03730.6989=7.3384>0\gamma + d^{+} - d^{-} = 1 + 7.0373 - 0.6989 = 7.3384 \gt 0이므로, 힌지는 활성화되어 있고 우리는 한 스텝을 밟습니다. u=h+rt=(1.6089,2.1092)u = \mathbf{h}+\mathbf{r}-\mathbf{t} = (1.6089, 2.1092)v=h+rtn=(0.3880,0.7405)v = \mathbf{h}+\mathbf{r}-\mathbf{t}_n = (0.3880, 0.7405)를 사용하면, η=0.05\eta = 0.05에서의 좌표별 업데이트는 다음과 같습니다:

좌표 iiΔh=η(2ui2vi)\Delta \mathbf{h} = -\eta(2u_i-2v_i)Δr=η(2ui2vi)\Delta \mathbf{r} = -\eta(2u_i-2v_i)Δt=+η2ui\Delta \mathbf{t} = +\eta\,2u_iΔtn=η2vi\Delta \mathbf{t}_n = -\eta\,2v_i
110.1221-0.12210.1221-0.1221+0.1609+0.16090.0388-0.0388
220.1369-0.13690.1369-0.1369+0.2109+0.21090.0741-0.0741

이를 적용하면, alice(0.6783,0.4626)(0.6783, 0.4626)으로 옮겨 가고, advises 화살표는 (0.3735,0.4231)(0.3735, 0.4231)로 짧아지며, 참인 꼬리 bob(0.1520,0.7389)(-0.1520, -0.7389)로 안쪽으로 끌려오고, 가짜 꼬리 p2(0.8692,0.3448)(0.8692, 0.3448)로 바깥으로 밀려납니다. 스텝 이후 다시 계산해 보면, 참인 제곱 거리는 7.047.04에서 4.094.09로 떨어지고 오염된 것은 0.700.70에서 0.330.33으로 떨어져서, 이 쌍의 목적함수 γ+d+d\gamma + d^{+} - d^{-}는 이 한 번의 스텝만에 7.347.34에서 4.764.76으로 떨어집니다. 트리플 하나, 오염 하나, 밀어내기 한 번, 그리고 기하학은 벌써 덜 틀려집니다. 이를 에포크마다 18번씩, 4000 에포크 동안 반복합니다.

실행 결과: 구조를 학습한 기하학

화살표를 늘어놓는 것이 실제로 이 세계의 구조를 되찾아 낼까요? 먼저, 손실이 떨어집니다. 고정된 평가용 오염 집합(eval_seed = 7에서 한 번 뽑았으므로 그 잣대 자체는 결코 움직이지 않습니다)에 대한 평균 힌지 손실(mean hinge loss)을 훈련이 진행되는 동안 추적해 보면, 학습의 대부분이 처음 백 에포크 안에서 일어남을 알 수 있습니다:

에포크015201005002000
평균 힌지 손실1.5221.5221.3191.3190.8020.8020.2240.2240.1320.1320.0620.0620.0890.089

손실은 경사 하강법 장의 볼록한(convex) 그릇 모양처럼 매끄럽게 0으로 미끄러져 내려가지 않습니다. 처음 백 에포크에 걸쳐 가파르게 떨어진 다음, 대체로 0.050.05에서 0.180.18 사이에 머무르는 잡음 섞인 띠(band) 속에 자리를 잡습니다. 표로 정리된 손실이 에포크 500의 0.0620.062에서 에포크 2000의 0.0890.089로 실제로 상승한다는 점에 주목하십시오. 이는 매끄럽게 수렴하는 옵티마이저라면 결코 보이지 않을 작은 오르막 흔들림입니다. 그 이유는 훈련이 매 에포크마다 무작위 오염을 새로 뽑고 모든 개체를 재정규화하기 때문에, 벡터들이 계속 흔들리고, 이 고정된 오염 잣대조차 0으로 미끄러지기보다는 흔들리기 때문입니다. 그 잡음은 인위적으로 만든 음성 예시(negative example)에 대한 확률적 훈련이 치르는 대가이며, 안정적인 신호는 참인 점수와 거짓인 점수 사이의 간극이고, 스크립트는 이를 곡선과 함께 보고합니다. __main__ 블록(121번째 줄부터 130번째 줄까지)은 훈련하면서 에포크별 손실로 curve 딕셔너리를 채우고, 그 곡선을 출력한 다음, 새로 오염시킨 트리플들에 대한 모든 참인 트리플의 평균 점수를 출력하고, 마지막으로 하나의 참인 지도 사실을 하나의 거짓 사실과 대조해 점수를 매깁니다:

if __name__ == "__main__": # pragma: no cover
curve = {}
E, R, triples, ents = train_transe(loss_curve=curve)
checkpoints = [0, 1, 5, 20, 100, 500, 2000]
print("epoch " + " ".join(f"{ep:>5}" for ep in checkpoints))
print("hinge loss " + " ".join(f"{curve[ep]:5.3f}" for ep in checkpoints))
true_s, corr_s = mean_true_vs_corrupt(E, R, triples, ents)
print(f"mean score true triples = {true_s:.3f} corrupted = {corr_s:.3f}")
print(f"score advises(alice, bob) = {score(E, R, 'alice', 'advises', 'bob'):.3f} (true)")
print(f"score advises(alice, erin) = {score(E, R, 'alice', 'advises', 'erin'):.3f} (false)")
epoch 0 1 5 20 100 500 2000
hinge loss 1.522 1.319 0.802 0.224 0.132 0.062 0.089
mean score true triples = 1.609 corrupted = 2.737
score advises(alice, bob) = 0.399 (true)
score advises(alice, erin) = 0.929 (false)

이 헤드라인 수치는 어떤 숨겨진 집계의 요약이 아니라, 명시적인 표 하나의 평균입니다. 다음은 훈련된 모델 아래에서 18개의 참인 트리플 전부가 갖는 유클리드 점수 s(h,r,t)s(h,r,t)를, 가장 잘 맞는 것부터 가장 못 맞는 것까지 정렬한 것입니다:

순위트리플점수순위트리플점수
1advises(alice, bob)0.3990.39910cites(p2, p1)1.5681.568
2authored(bob, p1)0.8410.84111advises(carol, erin)1.5981.598
3about(p3, ml)1.2341.23412about(p2, nesy)1.7511.751
4cites(p3, p2)1.2951.29513authored(carol, p2)1.8521.852
5authored(dave, p3)1.4131.41314advises(bob, carol)1.8941.894
6affiliated(carol, cmu)1.4271.42715affiliated(dave, cmu)2.1912.191
7authored(alice, p1)1.4561.45616about(p1, logic)2.2532.253
8advises(bob, dave)1.4841.48417affiliated(alice, mit)2.3852.385
9affiliated(erin, cmu)1.5131.51318affiliated(bob, mit)2.3992.399

18개의 점수를 더해 18로 나누면 28.953/18=1.60928.953 / 18 = 1.609가 되며, 이는 보고된 평균과 정확히 일치합니다. 이와 대조적으로 평균적인 오염된 트리플은 2.7372.737점을 받습니다: 참인 사실은 그 이동이 예측하는 지점에 무작위 가짜보다 눈에 띄게 더 가까이 앉아 있습니다.

단일 사실 검사는 더 날카로우며, 그것이야말로 이 장 전체의 핵심입니다. advises(alice, bob)은 지식 베이스 안의 사실이며, 0.3990.399점을 받아, 빡빡하게 잘 맞고 18개 중 가장 좋은 적합입니다. advises(alice, erin)은 사실이 아닙니다: 지도 사슬은 alice → bob → carol → erin으로 흐르므로, alice는 bob을 지도하지 erin을 지도하지 않습니다. 모델은 이 트리플이 거짓이라는 말을 들은 적이 한 번도 없지만, 그럼에도 0.9290.929점을 받는데, 이는 두 배가 넘게 먼 거리입니다. 이 기하학을 직접 읽어 보면 그 이유를 알 수 있습니다. 훈련된 화살표는 radvises=(1.396,0.857)\mathbf{r}_{\texttt{advises}} = (-1.396, -0.857)이고 halice=(0.698,0.172)\mathbf{h}_{\texttt{alice}} = (0.698, -0.172)이므로, 이동한 머리는 다음 위치에 떨어집니다

halice+radvises=(0.6981.396,  0.1720.857)=(0.698,  1.029).\mathbf{h}_{\texttt{alice}} + \mathbf{r}_{\texttt{advises}} = (0.698 - 1.396,\; -0.172 - 0.857) = (-0.698,\; -1.029).

모든 후보 피지도자(advisee)를 그 점으로부터의 거리로 순위 매기면, "alice는 누구를 지도하는가?"라는 질문에 오직 지도만으로 답할 수 있습니다:

후보 꼬리(0.698,1.029)(-0.698, -1.029)로부터의 거리KB에 있는가?
bob0.3990.399
erin0.9290.929아니오
carol1.0241.024아니오

참인 피지도자가 큰 차이로 1위에 오르며, "alice는 erin을 지도하지 않는다"는 규칙을 아무도 적어 놓은 적이 없습니다. 지도는 그저 참인 변은 짧고 거짓인 변은 길도록 화살표를 배치했을 뿐입니다.

기하학이 사는 것, 그리고 치르는 대가

방금 일어난 일에 주목하십시오. 우리는 지식 베이스 그 어디에도 나타나지 않고 어떤 규칙으로도 유도할 수 없는 트리플에 대해 모델에게 물었고, 그것은 그럼에도 합리적인 판단을 돌려주었습니다. 이것이 바로 임베딩의 전체 보상입니다: 열린 세계 일반화(open-world generalization), 즉 아무도 적어 놓지 않은 사실에 대해 추측할 수 있는 능력입니다. Part II의 심볼릭 엔진은 이것을 할 수 없었습니다. 그것은 47개의 원자에서 멈추었고 그 밖의 모든 것에 대해서는 침묵했습니다: advises(alice, erin)에 대해 물으면 오직 "유도 불가능"이라고만 답할 뿐, 결코 "가능성이 낮다"라고는 답하지 않습니다. 기하학은 여러분이 이름 붙일 수 있는 어떤 트리플에 대해서도 숫자를 채워 그 침묵을 메우며, 이것이 바로 지식 그래프 임베딩이 링크 예측(link prediction)과 완성(completion)의 주력 도구가 된 정확한 이유입니다 [3].

대가는 바로 그 같은 숫자들 안에 적혀 있습니다. 점수는 하나의 거리일 뿐, 결코 증명이 아닙니다. 0.9290.9290.3990.399보다 크지만, 0도 아니고 무한대도 아닙니다: 모델은 결코 확신하지 않으며, 그저 어느 정도 더 혹은 덜 자신 있어 할 뿐입니다. Part II의 정확한 결과를 떠올려 보십시오: 전방 연쇄는 혼 귀결(Horn entailment)에 대해 건전하면서도(sound) 완전(complete)했습니다. 그것이 유도한 모든 것은 진정으로 귀결된 것이었고, 따라 나오는 것 중 어느 것도 놓치지 않았습니다. 그것이 결코 하지 않았을 일은 추측이었습니다. 그것은 열린 세계에 대해 침묵을 지켰고, 규칙이 강제하지 않은 어떤 사실에 대해서도 "유도 불가능"이라고만 답했습니다. 임베딩은 그 거울상입니다. 그것은 결코 침묵하지 않으며, 여러분이 이름 붙일 수 있는 모든 트리플에 대해 숫자를 돌려주지만, 결코 확신하지도 않는데, 어떤 단일한 답이든 틀릴 수 있기 때문입니다. 점수표 그 자체가 이 오류 가능성을 분명하게 보여 줍니다: 참인 트리플 affiliated(bob, mit)2.3992.399점을 받는데, 이는 0.9290.929점을 받는 거짓 advises(alice, erin)보다 못한 점수입니다. 그 거짓 사실을 받아들일 만한 단일 거리 임계값이라면 참인 사실 하나도 거부해 버릴 것입니다. 이 모델은 평균적으로는 잘 순위 매기지만 특정한 경우에는 틀립니다. 그것은 순위를 매길 뿐, 증명하지 않습니다. 확실성과 맞바꾼 이 커버리지라는 거래야말로, 전체 신경-기호(neuro-symbolic) 프로젝트에서 신경(neural) 쪽 극(pole)입니다: 논리는 여러분에게 보장을 주지만 추측은 주지 않고, 기하학은 여러분에게 추측을 주지만 보장은 주지 않으며, 이 시리즈의 나머지 대부분은 둘 다를 가질 방법을 찾는 탐색입니다.

3권으로 이어지는 다리

위의 장난감 모델은 곁길이 아닙니다. 그것은 온톨로지(ontology)를 임베딩하는 것이 주제 전체인 3권으로 가는 진입로입니다. 그곳에서는 실제 지식 그래프의 개체와 관계가 완전한 규모로 학습되며, 기하학은 기호 하나당 점 하나보다 훨씬 풍부해집니다. 각 개념을 하나의 점에 고정하는 대신, 3권은 영역 임베딩(region embedding)을 연구합니다: 클래스를 공간 속의 박스(box)나 (ball)로 표현하여, "모든 연구자는 사람이다"를 하나의 영역이 다른 영역을 포함하는 것으로 그릴 수 있게 합니다. TransE는 이 모든 것의 씨앗이며, "기하학으로서의 의미"의 가장 작고 완결된 예시입니다. 그리고 3권이 쌓아 올리는 모든 것은 우리의 2차원 버전이 곧바로 부딪히는 한계에 대한 응답입니다.

아직 풀리지 않은 부분

여기 이 토대 안에 있는 정직한 균열이 있으며, 우리 자신의 실행이 그 균열이 벌어지는 것을 지켜보게 해 줍니다. TransE는 각 개체에 점 하나를, 각 관계에 화살표 하나를 부여하며, 하나의 화살표는 어떤 주어진 꼬리로부터 오직 한 방향으로만 향할 수 있습니다. 그런데 bob 학생, caroldave를 지도하며, 두 트리플 모두 참입니다. bob에 더해진 하나의 advises 화살표는 둘 중 하나 근처에만 떨어질 수 있습니다. 훈련된 숫자들은 정확히 이 부담을 보여 줍니다. hbob=(0.306,0.952)\mathbf{h}_{\texttt{bob}} = (-0.306, -0.952)이고 공유된 화살표 radvises=(1.396,0.857)\mathbf{r}_{\texttt{advises}} = (-1.396, -0.857)일 때, 이동한 머리는 hbob+radvises=(1.702,1.809)\mathbf{h}_{\texttt{bob}} + \mathbf{r}_{\texttt{advises}} = (-1.702, -1.809)이며, 두 참인 꼬리는 그로부터 매우 다른 거리에 떨어집니다:

s(bob,advises,dave)=1.484,s(bob,advises,carol)=1.894.s(\texttt{bob}, \texttt{advises}, \texttt{dave}) = 1.484, \qquad s(\texttt{bob}, \texttt{advises}, \texttt{carol}) = 1.894.

화살표는 dave를 선택했습니다. 그 참인 형제 carol1.8941.894점에 발이 묶여, 18개의 참인 트리플 중 열네 번째로 좋은 수준이며, 여러 오염된 트리플보다도 나쁜 점수입니다. 이것은 단일 벡터 모델이 결코 완전히 벗어날 수 없는 일대다(one-to-many) 불일치입니다: 머리 하나, 관계 하나, 그러나 유효한 꼬리는 여럿이고, 화살표 하나로는 그 전부에 이를 수 없습니다.

같은 공간 부족의 더 깊은 형태는, 점과 화살표로 이루어진 세계가 논리적 위계(logical hierarchy)를 충실하게 실어 나를 수 없다는 것입니다. 우리 학술 세계는 모든 교수와 모든 학생이 연구자이고, 모든 연구자가 사람이라고 말하는데, 이는 우리가 researcher ⊑ person이라고 쓸 포섭(subsumption)의 사슬입니다("⊑"를 "~의 한 종류이다", 더 형식적으로는 "~의 하위 클래스이다"라고 읽으십시오). 그런 위계는 근본적으로 포함(containment)에 관한 것입니다: 사람은 큰 집합이고, 연구자는 그 안의 부분집합이며, 교수는 그보다도 더 작은 부분집합입니다. 하지만 평평한 이동 기하학에는 한 사물이 다른 사물 안에 있다는 자연스러운 개념이 없습니다. 화살표는 한 점을 다른 점 옆으로 옮길 수는 있지만, "이것들 전부가 저것들 전부 안에 든다"는 것을 표현할 수는 없습니다. 위계의 한 부분을 지키도록 화살표를 밀어붙이면 다른 부분이 뒤틀리고 맙니다. 단일 벡터 모델에는 그저 그럴 여유 공간이 없는 것입니다.

그 실패야말로 3권이 다루는 박스 대 볼(boxes-versus-balls) 질문의 정확한 동기입니다. 박스는 문자 그대로 더 작은 박스를 포함할 수 있고, 볼은 더 큰 볼 안에 들어앉을 수 있으므로, 영역(region) 임베딩은 researcher ⊑ person을 점(point)이라면 결코 해낼 수 없는 방식으로 진짜 기하학적 포함 관계로 인코딩할 수 있습니다. 어느 모양이 논리를 더 많이 지켜내는지, 즉 박스의 날카로운 모서리인지 볼의 매끄러운 대칭성인지, 그리고 훈련 가능성(trainability) 측면에서 어떤 대가를 치르는지는, 열려 있으며 활발히 논쟁 중인 질문입니다. 처음부터 만든 우리의 TransE는 왜 이 질문이 중요한지를 몸으로 느껴 볼 수 있는 가장 깔끔한 방법입니다: 실행해 보면, 평평한 기하학이 하나의 위계가 되려고 시도하다가 실패하는 모습을 지켜볼 수 있습니다.

왜 중요한가

임베딩은 기호가 학습 가능해지는 지점입니다. 앞으로 나올 권들의 모든 신경망(neural) 방법, 즉 그래프 네트워크, 미분 가능한 질의 응답, 모든 하이브리드 시스템의 신경 쪽 절반은 개체와 관계를 벡터로 바꾸는 데서 시작하는데, 경사 하강은 오직 숫자만을 끌어당길 수 있기 때문입니다. 이 장은 그 움직임의 가장 작고 정직한 버전을 보여 주었고, 그 손실과 그래디언트를 처음부터 유도했으며, 그 한 스텝을 손으로 직접 따라가 보았습니다. 그만큼 중요하게도, 이 장은 그 대가를 드러냈습니다: 학습된 기하학은 누군가 적어 놓은 것을 넘어 일반화하지만, 논리가 우리에게 주었던 확신을 오직 하나의 힌트에 불과한 거리와 맞바꿉니다. 두 사실을 동시에 마음에 품는 것, 즉 기하학은 일반화한다는 것과 기하학은 틀릴 수 있다는 것을 동시에 붙드는 것이야말로 신경-기호 분야 전체가 그 위에 세워진 정신적 태도입니다. 이제 여러분의 손에는 양쪽 극이 모두 쥐어져 있습니다: Part II의 건전하지만 침묵하는 추론과, Part III의 유창하지만 오류 가능한(fallible) 기하학입니다.

핵심 용어

  • 임베딩(Embedding): 기하학적 관계(거리, 방향)가 의미를 인코딩하도록 선택된, 기호에서 벡터로 가는 사상(map)입니다.
  • 벡터, 좌표, 차원(Vector, coordinate, dimension): 벡터는 dd개의 실수로 이루어진 순서 있는 목록입니다. 각 항목은 인덱스 ii로 이름 붙는 좌표이며, 그 개수 dd(여기서는 22)가 차원입니다.
  • 분산 표현(Distributed representation): 기호의 의미가 한 슬롯에 저장되는 대신 그 모든 좌표에 걸쳐 퍼져 있는 방식으로, 가까운 패턴은 비슷한 것을 의미합니다.
  • TransE(번역 임베딩, translating embeddings): 모든 관계가 하나의 이동 벡터(translation vector)인 모델로, 참인 트리플 (h,r,t)(h, r, t)h+rt\mathbf{h} + \mathbf{r} \approx \mathbf{t}를 만족합니다.
  • 트리플(Triple): (머리,관계,꼬리)(\text{머리}, \text{관계}, \text{꼬리})로 쓰이는 이항 사실이며, 예를 들어 (alice,advises,bob)(\texttt{alice}, \texttt{advises}, \texttt{bob})입니다.
  • 점수(Score): 유클리드 거리 h+rt\lVert \mathbf{h} + \mathbf{r} - \mathbf{t}\rVert이며, 낮을수록 더 그럴듯함을 뜻하는 벌점입니다.
  • 오염된 트리플(Corrupted triple): 참인 트리플의 꼬리를 무작위로 고른 잘못된 개체로 바꾸어 만들어 낸, 인위적인 음성 예시(negative example)입니다.
  • 마진 랭킹(힌지) 손실(Margin-ranking (hinge) loss): max(0,γ+d+d)\max(0, \gamma + d^{+} - d^{-})이며, 각 참인 트리플이 자신의 오염된 버전보다 적어도 마진 γ\gamma만큼은 더 낮은 점수를 받도록 강제하고, 그렇게 되는 순간 사라집니다.
  • 재정규화(Renormalization): 매 에포크마다 모든 개체 벡터를 단위 길이로 재조정하여, 모델이 벡터를 무한대로 부풀려 부정 행위를 하지 못하게 하는 것입니다.
  • 확률적 경사 하강(Stochastic gradient descent, SGD): 한 번에 트리플 하나씩 벡터들을 밀어내는, 작은 걸음으로 내리막을 내려가는 옵티마이저입니다.
  • 열린 세계 일반화(Open-world generalization): 데이터 어디에도 나타나지 않고 어떤 규칙으로부터도 따라 나오지 않는 트리플에 그럴듯함을 부여할 수 있는 임베딩의 능력입니다.
  • 결코 침묵하지 않음, 결코 확신하지 않음(Never silent, never certain): 임베딩의 트레이드오프입니다. 이름 붙일 수 있는 모든 트리플에 점수를 매기지만(결코 침묵하지 않음), 어떤 단일한 답이든 틀릴 수 있습니다(결코 확신하지 않음). 이는 규칙이 귀결하는 것에 대해서는 건전하면서도 완전했지만 규칙이 강제하지 않은 것에 대해서는 침묵했던 전방 연쇄의 거울상입니다.
  • 영역 임베딩(Region embedding, 박스, 볼): 클래스를 하나의 영역으로 표현하는 3권의 더 풍부한 기하학으로, 포섭 관계 researcher ⊑ person이 진짜 포함 관계가 됩니다.

이 다음으로 이어지는 것

이제 우리는 이 분야의 두 절반을 동시에 손에 쥐고 있습니다: 정확하고 증명 가능하지만 침묵하는 논리의 추론과, 근사적이고 일반화하지만 불확실한 임베딩의 기하학입니다. 이 둘은 같은 학술 세계를 표현하는 두 가지 방식이며, 표현(representation)이란 것이 대체 무엇인지, 즉 참인 문장들의 집합인지 아니면 점들의 구름인지에 대해 서로 의견이 다릅니다. 다음 장인 두 문화: 기호 대 벡터는 이 두 전통을 나란히 놓고, 각각이 상대방은 할 수 없는 무엇을 해내는지 이름 붙이며, 이 시리즈의 나머지가 풀어내고자 하는 핵심 긴장을 세웁니다: 논리처럼 추론하면서 동시에 기하학처럼 학습하는 시스템을 어떻게 만들 것인가라는 긴장입니다.


동반 코드: examples/logic/embeddings.py는 이 모델 전체를 순수 파이썬으로 구현합니다: _binary_triples는 18개의 트리플을 추출하고, score는 이동 거리(translation distance)로 그럴듯함을 측정하며, train_transe는 매 에포크마다 재정규화를 곁들인 마진 랭킹 SGD로 벡터를 학습합니다(그리고 loss_curve 딕셔너리가 주어지면, 고정된 평가 집합에 대한 에포크별 손실을 기록합니다), mean_true_vs_corrupt는 참-대-오염 간극을 보고합니다. 이 장의 모든 수치를 재현하려면 python3 examples/logic/embeddings.py를 실행하십시오.