본문 바로가기

AI 모델 파헤치기

Word2Vec 임베딩 수식 완전 정리 — Skip-Gram부터 네거티브 샘플링까지

Transformer 파헤치기 (1) — 단어는 어떻게 벡터가 되고, 모델은 어떻게 배우는가

GPT나 Claude 같은 모델이 텍스트를 처리하려면, 가장 먼저 "단어"라는 추상적인 기호를 컴퓨터가 계산할 수 있는 숫자로 바꿔야 한다. 이 글에서는 그 변환 과정—임베딩(embedding)—이 정확히 어떤 수학적 구조로 이루어지는지, 그리고 그렇게 만들어진 표현을 모델이 어떻게 학습하는지를 다룬다.

 

1. 단어를 숫자로 표현하는 두 가지 방법

단어를 벡터로 바꾸는 가장 단순한 방법은 원-핫 인코딩(one-hot encoding)이다. 어휘 집합(vocabulary)에 있는 단어 하나하나에 인덱스를 매기고, 해당 인덱스만 1로 표시하고 나머지는 전부 0으로 채우는 방식이다. 이렇게 만들어진 벡터를 희소 표현 벡터(sparse representation vector)라고 부른다.

강아지 = [0 0 0 0 1 0 0 0 0 0 0 0 ... 0]   # 차원 = 10,000 (어휘 집합 크기)

이 방식에는 두 가지 명확한 단점이 있다.

 

첫째, 어휘 집합이 커질수록 벡터의 차원이 한없이 늘어난다. 단어가 10만 개면 벡터도 10만 차원이 되어야 하므로 공간적 낭비가 심하다.

둘째, 더 근본적인 문제로 단어의 의미를 전혀 표현하지 못한다. 모든 단어가 서로 직교(orthogonal)하기 때문에, "강아지"와 "고양이"의 벡터 사이 거리나 "강아지"와 "자동차"의 벡터 사이 거리나 수학적으로는 완전히 동일하다. 의미적 유사성이라는 개념 자체가 존재하지 않는 셈이다.

 

이 문제를 해결하는 것이 밀집 표현 벡터(dense representation vector)다. 벡터의 차원을 어휘 집합 크기가 아니라 사용자가 임의로 설정한 값(예: 128)으로 고정하고, 0과 1이 아니라 실수값으로 단어를 표현한다.

강아지 = [0.2 1.8 1.1 -2.1 1.1 2.8 ... ]   # 차원 = 128 (사용자 지정값)

이렇게 단어를 밀집 표현 벡터로 바꾸는 과정을 임베딩(embedding)이라 하고, 그 결과로 나온 벡터를 임베딩 벡터라 부른다. 비슷한 의미를 가진 단어들은 임베딩 공간에서 가까운 위치에 놓이는 경향이 있다.

 

참고: 워드 임베딩(Word Embedding) — wikidocs

임베딩 공간의 의미 관계, 그리고 그 한계

임베딩이 잘 학습되면 단어 간의 의미 관계가 벡터 연산으로 드러난다. 가장 유명한 예시가 다음 식이다.

$E(\text{queen}) - E(\text{king}) \approx E(\text{woman}) - E(\text{man})$

다만 이 식은 근사적으로만 성립한다. 실제로는 $E(queen)$이 식이 가리키는 위치에서 약간 벗어나 있는데, "queen"이라는 단어가 "king의 여성형"이라는 의미로만 쓰이지 않기 때문이다(밴드 이름 Queen 등 다른 맥락에서도 쓰인다). 즉 임베딩 벡터는 한 단어가 가질 수 있는 모든 의미를 평균적으로 압축한 결과이고, 그래서 깔끔한 선형 관계가 항상 정확히 들어맞지는 않는다.

 

임베딩 공간의 의미 관계

 

2. 임베딩이 만들어지는 메커니즘

2.1 원-핫 벡터와 임베딩 행렬

수식으로 좀 더 엄밀하게 정의해보자.

  • 원-핫 벡터: $x_i \in \mathbb{R}^V$ — 단어 $w_i$의 원-핫 벡터. $V$는 어휘 집합의 크기이고, $i$번째 위치만 1이며 나머지는 0이다.
  • 임베딩 행렬: $E \in \mathbb{R}^{V \times d}$ — 행렬의 각 행(row)이 한 단어의 임베딩 벡터(차원 $d$)에 대응한다.

여기서 왜 저렇게 차원이 표현되는지 이해가 잘 안될 수 있다. 

원-핫 벡터는 단어 하나를 표현하는 방법이기에, 총 $V$개의 단어가 있다면 $[0,0,0,...,1,0,...0]$ 이런식으로 표현이되기에 $x_i \in \mathbb{R}^V$로 표현할 수 있다.

임베딩 행렬은 단어 하나가 아닌 어휘 집합에 있는 모든 단어의 임베딩 벡터를 한꺼번에 모아둔 표라고 생각하면 된다. 그렇기에 임베딩 행렬 $\mathbb{E}$는 아래와 같이 구성되기에 $E \in \mathbb{R}^{V \times d}$로 표현할 수 있다.

 

임베딩 행렬 구조

2.2 임베딩 추출식 (lookup)

특정 단어의 임베딩 벡터를 꺼내오는 연산은 다음과 같다.

$e_i = E^⊤x_i$ , 즉 임베딩 전치 행렬에 추출하려는 단어의 원-핫 인코딩 벡터인 $x_i$를 곱한 것과 같다.

$x_i$는 $i$번째 값만 1이고 나머지는 0이므로, 이 연산은 사실상 임베딩 행렬 $E$의 $i$번째 행을 그대로 골라내는 것과 같다. 즉 "원-핫 벡터 × 임베딩 행렬 전치"는 곧 "행 선택"이라는 단순한 인덱싱 연산을 행렬곱 형태로 표현한 것이다.

 

2.3 Skip-Gram 구조: 중심 단어로 주변 단어 예측하기

임베딩을 실제로 학습시키는 대표적인 방법이 Skip-Gram이다. 아이디어는 단순하다 — 한 문장 안에서 어떤 중심 단어가 주어졌을 때, 그 주변에 어떤 단어들이 나타나는지를 예측하도록 모델을 학습시키면, 결과적으로 비슷한 맥락에서 등장하는 단어들끼리 비슷한 벡터를 갖게 된다.

이를 위해 두 개의 행렬을 둔다.

$W \in \mathbb{R}^{V \times d}$, $\quad W' \in \mathbb{R}^{d \times V}$

  • $h=W^⊤x$: 입력 단어(중심 단어)의 임베딩 벡터
  • $z=W^{′⊤}h$: 어휘 전체에 대한 점수(score) 벡터

여기서 $P(w_j \mid w_t)$는 중심 단어 $w_t$가 주어졌을 때 $w_j$가 그 주변에 등장할 확률을 의미한다. 예를 들어 "공원"이 중심 단어라면, "뛰어논다"나 "강아지" 같은 단어는 $P$ 값이 높게, "우주선" 같은 단어는 낮게 나올 것이다. softmax는 점수 벡터 $z$를 이 확률분포로 바꿔주는 역할을 하며, $W$와 $W'$는 학습 전에는 무작위 값으로 초기화되고 2.4절의 학습 과정을 통해 업데이트된다.

 

 

왜 $W$의 크기를 $V \times d$로도, $V \times N$ 으로도 표기할까?

$d$(임베딩 차원 수)와 $N$(은닉층 크기)은 사실 같은 개념이다. 표기가 다른 이유는 관점의 차이다. Word2Vec을 "입력층–은닉층–출력층" 구조의 작은 신경망으로 볼 때는 은닉층 크기라는 의미로 $N$을 쓰고, 임베딩 자체를 독립적인 개념으로 다루는 요즘 관점에서는 "임베딩 공간의 차원"이라는 의미로 $d$를 쓴다.

 

왜 $W$와 $W^{'}$를 굳이 나누어 쓸까?

이중 행렬을 쓰면 표현력이 커지고 학습이 더 유연해진다. $W$는 "중심 단어로서의 의미"를 담당하고, $W^{'}$는 "주변 단어(출력)로서의 역할"을 담당하는 분류기 파라미터다.

  • $W \in \mathbb{R}^{V \times d}$ : 입력 단어를 임베딩으로 바꾸는 lookup 표. $h = W^⊤x_t$는 중심 단어의 의미 벡터.
  • $W^{'} \in \mathbb{R}^{d \times V}$ : 출력(주변 단어)을 구분하는 분류기 파라미터로, 열마다 한 단어에 대응한다.

분리의 장점은 세 가지로 정리할 수 있다.

  1. 표현력 증가 — 중심 단어로서의 의미와 주변 단어로서의 역할을 다르게 학습할 수 있다.
  2. 최적화 안정성 — 입력과 출력의 역할이 분리되어 학습이 더 유연해진다.
  3. 실무 활용 — 학습이 끝난 뒤 최종 임베딩으로는 $W$만 쓰거나, $\frac{1}{2}(W + W'^\top)$처럼 입력/출력 벡터를 평균 내어 쓰기도 한다. 두 관점을 결합하면 일반화 성능이 경험적으로 조금 더 좋아지는 경우가 있다.

 

참고: Word2Vec: (3) Skip-gram 개념 및 원리

 

2.4 학습 원리: 손실함수와 그래디언트 직관

손실함수는 전체 말뭉치에서 (중심 단어, 주변 단어) 쌍 $(t, j) \in C$에 대한 음의 로그우도(negative log-likelihood)다.

 

$\mathcal{L} = -\sum_{(t,j) \in C} \log P(w_j \mid w_t)$

 

이 손실을 최소화하는 방향으로 파라미터를 업데이트하는데, 그래디언트가 정확히 어떤 형태로 나오는지 단계별로 따라가보자.

 

1단계: 한 쌍에 대해서만 떼어보기

전체 손실은 모든 (중심, 주변) 쌍에 대한 합이지만, 그래디언트는 결국 한 쌍씩 따로 계산해서 더하는 것과 같다. 한 쌍 $(t, j)$만 떼서 보면:

 

2단계: $z_k$에 대해 미분하기

이 식을 $\ell = -z_j + \log \sum_k \exp(z_k)$로 다시 쓰면 미분이 쉬워진다. 두 항을 각각 $z_k$로 미분하면:

  • 첫째 항 $-z_j$를 $z_k$로 미분하면, $k=j$일 때만 -1이고 그 외엔 0이다 — 즉 $-\mathbb{1}[k=j]$
  • 둘째 항 $\log \sum_k \exp(z_k)$를 $z_k$로 미분하면 $\dfrac{\exp(z_k)}{\sum_m \exp(z_m)} = P(w_k \mid w_t)$가 된다

두 항을 더하면 다음 결과를 얻는다.

$\frac{\partial \mathcal{L}}{\partial z_k} = P(w_k \mid w_t) - \mathbb{1}[k = j]$

여기서 $\mathbb{1}[k=j]$는 $k$와 $j$가 같을 때만 1이고 그 외엔 0인 지시함수다. 이 결과는 "softmax + 음의 로그우도" 조합에서 항상 나오는 표준적인 형태로, cross-entropy 손실의 그래디언트와 동일하다.

 

3단계: 부호가 의미하는 업데이트 방향

경사하강법은 그래디언트의 반대 방향으로 파라미터를 움직인다. 그래디언트가 양수면 그 변수를 줄이고, 음수면 늘린다.

  • 정답인 경우 ($k=j$): $P(w_j \mid w_t)$는 보통 1보다 작은 확률값이므로$P - 1 < 0$. 그래디언트가 음수이므로 경사하강법은 $z_j$를 올리는 방향으로 업데이트한다. 즉 모델이 정답 단어에 대해 더 높은 점수를 주도록 조정된다.
  • 오답인 경우 ($k \neq j$): $P(w_k \mid w_t)$는 0보다 큰 양수이므로 $P - 0 > 0$. 그래디언트가 양수이므로 $z_k$를 내리는 방향으로 업데이트한다.

4단계: 이게 임베딩 벡터에 미치는 영향

$z_k = \langle h, W'_{:k} \rangle$이므로 ($W'_{:j}$는 단어 $w_j$가 출력(주변 단어)으로 등장할 때의 파라미터 벡터를 의미한다), $z_k$를 올리거나 내리는 것은 결국 $h$와 $W'_{:k}$ 사이의 내적을 조정하는 것과 같다. 내적은 $\langle h, W'_{:k} \rangle = \|h\| \|W'_{:k}\| \cos\theta$로 쓸 수 있으므로 (여기서 $\theta$는 두 벡터 사이 각도), 벡터 크기 변화를 무시하면 내적이 커진다는 것은 두 벡터의 각도가 좁아진다는 뜻이고, 내적이 작아진다는 것은 각도가 벌어진다는 뜻이다.

그래서:

  • 정답일 땐 $h$와 $W'_{:j}$의 각도가 줄고 내적이 커지는 방향으로 업데이트된다.
  • 오답일 땐 $h$와 $W'_{:k}$의 각도가 커지고 내적이 작아지는 방향으로 업데이트된다.

5단계: 왜 의미적으로 비슷한 단어가 가까워지는가

같은 단어 $w_j$가 여러 다른 중심 단어 $w_{t_1}, w_{t_2}, \dots$의 주변에 자주 등장한다고 해보자. 그러면 매번 그 중심 단어들의 벡터 $h_{t_1}, h_{t_2}, \dots$가 전부 $W'_{:j}$ 쪽으로 가까워지도록 업데이트되므로, 결과적으로 $h_{t_1}, h_{t_2}, \dots$끼리도 서로 비슷한 방향을 향하게 된다 — 같은 단어 주변에 자주 등장하는 단어들끼리는 임베딩 공간에서 가까워지는 것이다. 이것이 "비슷한 문맥에서 쓰이는 단어는 비슷한 의미를 가질 것"이라는 분포 가설(distributional hypothesis)이 Skip-Gram 안에서 수학적으로 구현되는 방식이다. 결과적으로 자주 함께 등장하는 단어들의 내적(=유사도)이 커지고, 임베딩 공간 안에서 서로 가까워진다. 이것이 Skip-Gram이 의미적으로 유사한 단어를 가깝게 배치하게 되는 핵심 메커니즘이다.

2.5 계산량 문제와 대안

$softmax$의 분모가 어휘 집합 전체 $V$에 대한 합이기 때문에, 어휘가 클수록 매 스텝마다 계산량이 폭증한다. 이를 해결하는 두 가지 대표적인 방법이 있다.

 

1. 네거티브 샘플링(SGNS, Skip-Gram with Negative Sampling)

전체 소프트맥스 대신 다음 손실함수로 대체한다.

$\mathcal{L}_{NS} = -\log\sigma(\langle h, v^+\rangle) - \sum_{n=1}^{K} \log\sigma(-\langle h, v_n^-\rangle)$

  • $v^+ = W'_{:j}$ : 정답 주변 단어의 벡터
  • $v_n^-$ : 노이즈 분포에서 뽑은 $K$개의 음성(negative) 단어 벡터

정답 내적은 키우고 음성 내적은 낮추는 방향으로 학습하므로, 전체 어휘를 매번 계산할 필요 없이 빠르고 메모리 효율적으로 학습할 수 있다.

 

2. 계층적 소프트맥스(Hierarchical Softmax)

허프만 트리(Huffman tree)로 단어를 부호화해, 확률 계산을 $O(\log V)$로 줄이는 방법이다.

 

참고: 허프만(Huffman) 트리를 이용한 텍스트 압축

 

흥미로운 점은, SGNS가 이론적으로 Shifted PMI(Pointwise Mutual Information) 행렬을 저차원으로 근사하는 행렬 분해와 연결된다는 사실이다.

$ \langle W_{t:}, W'_{:j} \rangle \approx \text{PMI}(w_t, w_j) - \log K $

즉 SGNS는 결국 단어 간 공동출현(co-occurrence) 통계를 잘 보존하도록 학습된다는 해석이 가능하다.

 

3. 딥러닝 훈련의 기본 규칙

 

여기까지가 단어 하나를 벡터로 표현하고 학습시키는 방법이었다면, 이제 이 벡터들이 실제 모델 안에서 어떻게 처리되는지로 넘어가보자.

사실 3. 딥러닝 훈련의 기본 규칙에서는 다음 장에 설명할 내용들을 위한 기본적인 내용 설명에 가깝기에 다음 편을 위한 배경 지식 정도로 가볍게 읽으면 된다.

 

텍스트 처리 모델에서 마지막 layer는 가능한 모든 단어 집합에 대한 확률 분포를 출력한다. 즉 마지막 벡터는 "다음에 올 단어가 무엇일지"를 표현하는 확률값들의 리스트로 쓰인다. 

마지막 layer의 벡터는 다음에 올 단어의 확률값을 담고 있다

 

여기서 한 가지 자연스러운 질문이 생긴다. 왜 모델은 마지막 토큰의 벡터만을 사용해서 다음 단어를 예측할까?

 

답은 단순히 효율성 때문이다. 문장 전체의 모든 토큰 위치에서 매번 전체 어휘에 대한 확률분포를 계산할 필요 없이, 마지막 위치 하나만 계산하면 충분하기 때문이다(물론 학습 시에는 모든 위치에서의 손실을 계산하지만, 추론 시 다음 토큰 생성에는 마지막 위치만 필요하다).

 

모델 내부에서 입력 텍스트 행렬과 가중치 행렬을 곱하는 계산을 하기 전에, 먼저 입력 텍스트를 토큰화(tokenization)하는 과정이 선행된다. 텍스트를 토큰 단위로 쪼개고, 각 토큰을 앞서 살펴본 임베딩 행렬을 통해 벡터로 변환하는 것이다.

문장의 토큰화 진행

 

그리고 단어의 의미를 제대로 파악하려면 그 단어가 놓인 문맥을 함께 봐야 한다. "나는 오늘 학교에서"라는 문장에서 다음 단어를 예측하려면, "학교에서" 벡터 하나만 봐서는 부족하고 앞에 "나는 오늘"이 있다는 정보도 함께 반영되어 있어야 한다. 모델이 한 번에 얼마나 많은 단어를 참고할 수 있는지를 결정하는 값이 바로 context size(혹은 context window)다.

 

여기서도 한 가지 의문이 생긴다. 임베딩 직후의 "학교에서" 벡터는 그 단어 자체의 의미만 담고 있을 뿐, "나는 오늘"이라는 앞 문맥을 알 리가 없다. 그렇다면 마지막 토큰 벡터에 문맥이 녹아들어 있다는 건 어떻게 가능한 걸까? 이 질문에 대한 답이 바로 다음 편에서 다룰 Self-Attention이다. Self-Attention은 문장 안의 모든 토큰들이 서로의 벡터를 참고해서 각자의 표현을 업데이트하는 과정이고, 마지막 위치의 벡터에 문맥이 녹아있는 건 이 과정의 결과다.


다음 편에서는 softmax 함수의 temperature 개념부터 시작해서, Transformer의 핵심이라 할 수 있는 Self-Attention 메커니즘 — Query, Key, Value가 무엇이고 왜 필요한지 — 을 다룬다.

https://thisisgonnabegreat.tistory.com/entry/%EB%AA%A8%EB%8D%B8-%ED%8C%8C%ED%97%A4%EC%B9%98%EA%B8%B0-Transformer%ED%8E%B8-1

 

Transformer Self-Attention 수식 완전 정리 — Query Key Value 직관적 이해

Transformer 파헤치기 (2) — Softmax와 Self-Attention: Transformer의 심장https://thisisgonnabegreat.tistory.com/entry/%EB%AA%A8%EB%8D%B8-%ED%8C%8C%ED%97%A4%EC%B9%98%EA%B8%B0-Transformer%ED%8E%B8 Word2Vec 임베딩 수식 완전 정리 — Skip-Gram

thisisgonnabegreat.tistory.com

 

 

오늘 작성한 내용은 3blue1brown 유튜브 채널의 transformer 관련 영상을 기반으로 작성하였습니다.

수정이 필요한 내용이 있다면 댓글 부탁드립니다.