본문으로 건너뛰기

© 2026 Molayo

Zenn헤드라인2026. 06. 02. 03:37

수식이 서툴러도 이해할 수 있는 Word2Vec|2개의 행렬·Skip-gram·부정 예시 샘플링을 구체적인 예시만으로

요약

Word2Vec의 핵심 원리를 수식 없이 직관적인 예시로 설명합니다. Skip-gram, CBOW, Negative Sampling 등 주요 개념을 4개의 단어 코퍼스를 통해 쉽게 풀어냅니다.

핵심 포인트

  • 분포 가설에 기반한 단어 임베딩 원리 이해
  • 입력 측과 출력 측 2개 행렬의 역할 구분
  • Skip-gram과 CBOW의 차이점 파악
  • Negative Sampling의 개념과 필요성 습득

요약 (결론)

Word2Vec의 핵심은 한 문장으로 말하면 이것뿐입니다.

이 기사에서는 어려운 수식에 의존하지 않고, **단 4개의 단어로 구성된 코퍼스("개", "고양이", "가", "좋아")**만으로 직접 손을 움직여 가며 다음의 "왜?"라는 질문에 답해 나갑니다.

  • 왜 단어 벡터 행렬이 2개인가? 하나로는 안 되는가?
  • 처음에 벡터는 어떻게 초기화하는가?
  • Skip-gramCBOW는 무엇이 다른가?
  • **부정 예시 샘플링 (Negative Sampling)**이란 무엇인가? 왜 "정답(Positive)"이 아니라 "부정 예시(Negative)"인가?
  • 학습은 정말로 **역전파 (Backpropagation)**인가?

상세한 내용은 이후에 하나씩 구체적인 예시로 설명하겠습니다 🐶

대상 독자 및 전제 조건

이 기사는 다음과 같은 분들을 위해 작성되었습니다.

  • 머신러닝(Machine Learning)·자연어 처리(NLP)를 이제 막 배우기 시작한 입문자
  • "Word2Vec"이라는 말은 들어봤지만, 내용을 설명하지 못하는 분
  • 수식이나 기호(내적, 시그모이드 등)가 나오면 겁부터 나는 분
  • BERT나 Transformer의 전제로서, 우선 Word2Vec을 직관적으로 이해하고 싶은 분

전제 지식: 거의 필요하지 않습니다. "벡터는 숫자의 나열이다", "내적은 벡터끼리의 곱셈 같은 것이다" 정도를 어렴풋이 알고 있다면 읽을 수 있습니다.

이 기사에서 다루지 않는 것: 구현 코드, 계층적 소프트맥스 (Hierarchical Softmax)의 내부 구현, 학술적인 엄밀성. 어디까지나 "원리를 직관적으로 파악하는 것"을 우선합니다.

Word2Vec이란 무엇인가

Word2Vec은 단어를 "의미의 유사함이 거리에 반영된 벡터"로 변환하는 기법입니다 (2013년, Mikolov 등).

이를 뒷받침하는 생각은 분포 가설 (distributional hypothesis) 한 마디로 요약됩니다.

비슷한 문맥에 등장하는 단어는 의미도 비슷하다.

예를 들어 "개"와 "고양이"는 둘 다 "~를 좋아한다", "~를 키운다"와 같은 비슷한 문맥에 등장합니다. 따라서 두 단어는 가까운 벡터가 되기를 바라며, 이를 기계적으로 실현하는 것이 Word2Vec입니다.

그리고 Word2Vec의 벡터는 1개 단어당 1개로 고정됩니다. 이를 **정적 임베딩 (static embedding)**이라고 부릅니다. "강의 bank(둑)」도 "은행의 bank」도 같은 벡터가 되는 성질입니다. 문맥에 따라 변하는 BERT와의 결정적인 차이가 바로 여기에 있습니다.

전체상: 무엇을 만드는가

먼저 완성된 형태를 살펴보겠습니다. 학습이 끝나면 다음과 같은 "단어 → 벡터" 표를 얻을 수 있습니다 (차원은 설명용으로 2차원).

개 [ 0.41, 0.33]
고양이 [ 0.38, 0.36] ← 개와 거의 같은 방향 = 의미가 가까움
가 [-0.20, 0.55]
...

이 표를 어떻게 만드는가가 이 기사의 테마입니다. 절차를 대략적으로 나타내면 다음과 같습니다.

순서대로 살펴보겠습니다.

① 2개의 행렬 (입력 측 · 출력 측)

첫 번째 고비입니다. Word2Vec에는 단어 벡터를 저장하는 행렬이 2개 있습니다.

  • 입력 측 행렬 W (중심어용) … 최종적으로 "단어 벡터"로 사용하는 본체
  • 출력 측 행렬 W' (문맥어용) … 학습을 돕기 위한 발판

동일한 단어가 양쪽 행렬에 각각 한 행씩 존재합니다. 관습적으로 입력 측 벡터를

W (입력 측 / 중심어용) W' (출력 측 / 문맥어용)
개 → [........] 개 → [........]
가 → [........] 가 → [........] ← u_가 는 여기
...

왜 2개나 필요한가 (하나로는 안 되는가?)

여기서 자연스러운 의문이 생깁니다.

어차피 서로 다른 단어끼리 내적을 구할 거라면, 행렬은 하나면 되지 않을까?

날카로운 질문입니다만, 답은 **"하나로 합치면 두 가지 문제가 발생하기 때문"**입니다.

문제 ① 자기 자신과의 내적이 망가짐

하나의 단어는 어떤 때는 중심어, 어떤 때는 문맥어로 등장합니다. 행렬이 하나라면 내적의 상대가 우연히 자기 자신이 되는 상황이 발생합니다 ("도쿄 도쿄"와 같은 반복 등).

자기 자신과의 내적은 벡터 길이의 제곱이므로, 구조적으로 항상 최대치로 커지게 되어 제대로 학습할 수 없습니다. 2개의 행렬을 사용하면 입력 벡터와 출력 벡터가 분리되므로, 이러한 자기 참조의 오류가 발생하지 않습니다.

문제 ② 공기(Co-occurrence)의 방향성이 사라짐

행렬이 하나라면 내적 결과가 반드시 대칭이 됩니다.

score(犬→好き) = v_犬 · v_好き
score(好き→犬) = v_好き · v_犬 ← 순서를 바꿔도 동일 → 반드시 같음

하지만 언어의 공기(Co-occurrence)는 비대칭입니다('가' 뒤에는 '좋아해'가 나오기 쉽지만, 역은 한정적임). 2개의 행렬을 사용하면

1개의 행렬2개의 행렬
자기 자신과의 내적구조적으로 최대화되어 왜곡됨
별개의 것이므로 회피 가능
......

② 벡터의 초기화

2개의 행렬은 초기화 방식이 비대칭이라는 점이 포인트입니다.

  • 입력측 W: 작은 난수로 초기화 (각 성분을 $[-0.5/N, 0.5/N]$ 의 일양 분포 난수로)
  • 출력측 W': 0으로 초기화
W (입력측·난수) W' (출력측·0)
犬 [ 0.12, -0.08] 犬 [0, 0]
が [ 0.03, 0.07] が [0, 0]
...

'둘 다 랜덤'이라고 생각하기 쉽지만, 출력측은 0에서 시작합니다. 이유는 후반부의 '넘어지기 쉬운 지점'에서 자세히 설명하겠습니다.

③ Skip-gram と CBOW

예측하는 방향이 반대인 두 가지 방식이 있습니다. 맞히는 방향이 정반대일 뿐인 쌍둥이입니다.

문장 「犬 が 好き(개 가 좋아)」에서, 가운데 있는 「が」에 주목합니다.

CBOW: 주변으로부터 가운데를 맞히기

犬 [ ? ] 好き → 주변의 「犬」, 「好き」로부터 가운데의 「が」를 맞힘

Skip-gram: 가운데로부터 주변을 맞히기

[ が ] → 「が」로부터 주변의 「犬」, 「好き」를 맞힘
CBOWSkip-gram
예측 방향문맥어(Context word) → 중심어(Center word)
......

왜 Skip-gram은 희귀 단어(Rare word)에 강한가

CBOW는 문맥어를 평균하여 중심어를 1번 맞힐 뿐입니다. 반면 Skip-gram은 중심어 1개로부터 문맥어의 개수만큼 예측을 반복합니다.

CBOW: [犬, が, か, な]를 평균 → "好き"를 1번 예측 (평균에 묻힘)
Skip-gram: "好き" → 犬를 예측, が를 예측, … (1개 단어가 몇 번이고 주인공이 됨)

희귀 단어는 CBOW를 사용하면 평균에 묻히지만, Skip-gram에서는 등장할 때마다 몇 번이고 직접 업데이트되기 때문에 질 좋은 벡터를 얻기 쉽습니다.

④ 부정 예시 샘플링 (Negative Sampling)

두 번째 고비입니다. 문제점 → 해결책 순으로 살펴보겠습니다.

문제점: 그대로 하면 너무 무겁다

Skip-gram의 '맞히기'를 그대로 구현하면 다음과 같습니다.

「が」로부터 주변 단어를 맞힘
→ 어휘의 모든 단어(犬/が/好き/食べる/走る/…수십만 단어)에 대해
「다음에 올 것 같은 정도」를 전부 계산하고, softmax로 확률로 변환

어휘가 수십만 개 있으면, 1개 단어를 학습할 때마다 수십만 번의 계산이 필요하게 되어 너무 무거워서 끝낼 수 없습니다.

해결책: 「전부 맞히기」를 그만두고 「○× 퀴즈」로 만들기

발상을 전환합니다. 「어휘 전체에서 정답을 맞히기」를 그만두고, 소수의 ○× 판정으로 대체합니다.

정답 쌍(실제로 이웃해 있던 것)을 1개 준비:
(犬, が) → 「이것은 진짜 쌍인가?」 → ○(YES)로 만들고 싶음 ← 정례 (Positive sample)
가짜 쌍을 몇 개 만들어냄 (어휘에서 랜덤하게 선택):
...
정직한 방법: 수십만 단어를 전부 계산해서 정답을 맞힘 (매우 무거움)
부정 예시 샘플링: 정례 1개 + 부정 예시 몇 개만 ○× 판정 (매우 가벼움)

왜 「부정 예시(Negative)」 샘플링인가 (「정례(Positive)」가 아니라)

이름의 이유는 **「샘플링(추출)하고 있는 것은 부정 예시뿐이기 때문」**입니다.

  • **정례 (Positive sample)**는 코퍼스에 그대로 적혀 있음 → 선택의 여지가 없음 (추출 불필요)
  • **부정 예시 (Negative sample)**는 코퍼스에 없음 → 어휘에서 스스로 끌어옴 (= 샘플링)

게다가 부정 예시는 완전한 일양 랜덤이 아니라, 빈출 단어가 조금 더 잘 선택되도록 확률을 조정하여 뽑습니다. 이 「어떻게 추출할 것인가」가 수법의 핵심이므로, 이름은 부정 예시 쪽을 가리키고 있는 것입니다.

⑤ 학습의 전체 흐름 (구체적인 예시)

지금까지의 부품을 조합하여, 「생텍스트 → 완성된 벡터」를 일관되게 따라가 보겠습니다.

사용하는 코퍼스

일부러 「犬(개)」와 「猫(고양이)」가 같은 문맥에 나오도록 설정했습니다 (마지막에 양자가 가까워지는 모습을 보기 위해).

문장 1: 犬 が 好き
문장 2: 猫 が 好き

①~③ 쌍(Pair)과 부정 예시 만들기

윈도우 크기 1(인접어만을 문맥으로 간주)로 「중심어 → 문맥어」 쌍(Pair)을 나열합니다.

문장 1: (犬, が) (が, 犬) (が, 好き) (好き, が)
문장 2: (猫, が) (が, 猫) (が, 好き) (好き, が)

예를 들어 (犬, が)에 부정 예시(Negative Sample)인 「好き」를 추가하면, 학습 자료는 다음과 같습니다.

(犬, が) → 레이블 1 (가까워지게 하고 싶음)
(犬, 好き) → 레이블 0 (멀어지게 하고 싶음) ※부정 예시

④〜⑥ 초기화하여, 가까워지기·멀어지기를 반복한다

각 쌍에 대해 다음과 같이 움직입니다 (계산 내용은 다음 장에서).

u_が 를 v_犬 방향으로 가깝게 만든다 (정례 (Positive Sample))
u_好き 를 v_犬과 반대 방향으로 멀게 만든다 (부정 예시 (Negative Sample))

이것을 모든 쌍 × 여러 에포크(Epoch) 동안 반복하면, 犬와 猫에게 어떤 일이 일어날까요——

  • 犬는 몇 번이고 $u_{が}$ 방향으로 끌려갑니다 (쌍 (犬, が)가 있기 때문에)
  • 猫도 몇 번이고 $u_{が}$ 방향으로 끌려갑니다 (쌍 (猫, が)가 있기 때문에)

두 단어 모두 「같은 문맥」을 공유하게 됩니다. 아무도 「犬와 猫는 비슷하다」라고 가르쳐주지 않았는데도, 공유하는 문맥을 통해 간접적으로 비슷해집니다——이것이 분포 가설 (Distributional Hypothesis)의 핵심입니다.

학습 전: 犬 ● 猫 ● (제각각)
↓ 여러 에포크 동안 업데이트
학습 후: 犬 ●● 猫 (가까워짐)

⑦ 벡터를 추출한다

수렴하면, 입력 측 W의 각 행을 해당 단어의 벡터로 채택합니다 (출력 측은 버립니다). 코사인 유사도 (Cosine Similarity)를 구하면 犬와 猫가 높은 값을 갖게 됩니다. 이것으로 완성입니다 🎉

⑥ 학습은 역전파 (Backpropagation)

Word2Vec의 학습도 명실상부한 **역전파 (Backpropagation, 오차 역전파)**입니다. 다만 네트워크가 극도로 얕기 때문에 몇 단계 만에 끝납니다. 앞 장의 「가까워지기·멀어지기」의 내용을 계산해 봅시다.

전제

중심어: 犬, 정례: が, 부정 예시: 好き, 학습률 η = 0.1
v_犬 = [0.12, -0.08] (입력 측 · 업데이트 전)
u_が = [0, 0] (출력 측 · 제로)
...

계산 그래프

역전파는 이 화살표를 역방향으로 따라가는 작업입니다.

기호의 의미만 파악해 둡시다.

  • $\cdot$ (내적 (Inner Product)) … 성분별로 곱해서 더함. 「방향의 유사성」을 하나의 수치로 만듦
  • $\sigma$ (시그모이드 (Sigmoid)) … 수치를 0~1 사이로 압축하는 함수. $\sigma(0)=0.5$

스텝 1: 순전파 (Forward Propagation, 예측값 출력)

출력 측이 0이므로, 첫 번째 예측값은 반드시 0.5가 됩니다.

스텝 2: 오차 구하기 (역전파의 출발점)

Loss (시그모이드 + 교차 엔트로피 (Cross-Entropy))를 스코어(Score)로 미분하면, 깔끔하게 (예측 − 목표) 형태가 됩니다. 이것이 역전파의 첫 번째 신호입니다.

마이너스($-$)는 「예측이 부족함 = 더 크게」, 플러스($+$)는 「예측이 너무 큼 = 더 작게」라는 의미입니다.

스텝 3: 각 벡터로 기울기 (Gradient) 역전파

내적 $\cdot$ 을 계산하고,

더해줍니다.

스텝 4: 파라미터 업데이트

업데이트 식은 파라미터 ← 파라미터 − η × 기울기 입니다.

u_が ← [0,0] − 0.1 × [−0.06, 0.04] = [ 0.006, −0.004]
u_好き ← [0,0] − 0.1 × [ 0.06, -0.04] = [−0.006, 0.004]
v_犬 ← [0.12,-0.08] − 0.1 × [0,0] = [0.12, -0.08] (변화 없음)

결과를 확인하면:

  • ✅ $u_{が}$ 는 $v_{犬}$ 와 같은 방향이 되었다 → 정례가 가까워짐
  • ✅ $u_{好き}$ 는 $v_{犬}$ 와 반대 방향이 되었다 → 부정 예시가 멀어짐
  • $v_{犬}$ 는 이번 회차에서는 그대로 유지됨. 다음 회차부터 움직이기 시작함

「정례를 끌어당기고, 부정 예시를 밀어낸다」는 직관이 역전파 계산에서 그대로 나타났습니다.

놓치기 쉬운 포인트

함정 ①: 왜 출력 측은 제로 초기화(Zero Initialization)인가?

「첫 번째 스텝에서 중심어가 움직이지 않는다면 손해 아닌가?」라고 느낄 수 있습니다. 이는 좋은 질문입니다.

결론은 **「1스텝 움직이지 않는 것 자체가 장점이라기보다, 제로 초기화의 부작용을 피하기 위함」**입니다. 진짜 장점은 제로 초기화 쪽에 있습니다.

만약 출력 측도 난수(Random)로 초기화한다면? (노이즈로 인한 앞서나감)

랜덤한 방향으로 끌려가게 됩니다. 데이터와 무관한 노이즈에 밀려, 나중에 이를 상쇄하기 위한 추가적인 학습이 필요해집니다.

난수 초기화: $v$는 스텝 1부터 「노이즈」에 밀려남 (나중에 이를 상쇄해야 하는 번거로움 발생)
제로 초기화: $v$는 「실제 데이터의 신호」가 쌓인 후에 움직임 (불필요한 시도가 없음)

즉, 「처음에는 학습이 진행되지 않는다」는 것은, 바꿔 말하면 **「노이즈에 의해 앞서나가지 않는다」**는 뜻입니다.

「둘 다 제로」로는 안 됨 (대칭성 깨기)

둘 다 제로라면 그래디언트(Gradient)가 어떤 벡터도 영원히 움직이지 않습니다. 따라서 적어도 한쪽은 난수로 설정하여 대칭성을 깨뜨릴 필요가 있으며, 그 역할을 입력 측 $W$가 담당하고, 출력 측 $W'$는 안심하고 제로로 둘 수 있다는 역할 분담이 이루어집니다.

또 다른 장점으로는, 출력 측이 제로이면 첫 예측이 모든 쌍에 대해 「어느 쌍이 진짜인지 반반(50:50)이다」라는 편향 없는 출발점에서 학습을 시작할 수 있습니다.

함정 ②: 「예측」이 아니라 「진위 판정」

「Skip-gram은 중심어로부터 문맥어를 예측한다」라고 설명되지만, 우리가 계산해 온 부정 예시 샘플링 (Negative Sampling) 버전은 엄밀히 말하면 예측하고 있는 것이 아닙니다.

구분본래의 Skip-gram부정 예시 샘플링 버전 (실용)
하는 일중심어 $
ightarrow$ 문맥어 예측쌍이 진짜인지 판정
출력어휘 전체에 대한 확률 분포각 쌍이 진짜일 확률
계산softmax (전체 어휘, 무거움)$\sigma$ (몇 개의 쌍만, 가벼움)

(개, 가) $\rightarrow$ 1, (개, 좋아) $\rightarrow$ 0

과 같이 계산했던 것은 「예측」이 아니라 「진짜 쌍인지 가짜 쌍인지의 분류」였습니다. 예측하는 느낌이 들지 않았던 이유는 바로 이 때문입니다.

요약

Word2Vec의 핵심 포인트를 되짚어 봅니다.

  • 2개의 행렬: 중심어의 얼굴 ($u$)과 문맥어의 얼굴 ($v$)을 나누어, 「자기 자신과의 내적 파탄」과 「공기(Co-occurrence)의 비대칭성이 사라지는 문제」를 회피. 마지막에 남는 것은 입력 측뿐.
  • 초기화: 입력 측 = 작은 난수, 출력 측 = 제로, 라는 비대칭 설계.
  • Skip-gram vs CBOW: 예측의 방향이 반대일 뿐. 희귀 단어에 강한 Skip-gram이 정석.
  • 부정 예시 샘플링 (Negative Sampling): 매우 무거운 softmax를 「정례(Positive) $\circ$ · 부정례(Negative) $\times$ 판정」으로 대체한 고속화 기법. 추출이 필요한 것은 부정례뿐이므로 「부정례」 샘플링.
  • 학습: 내적 $\rightarrow$ $\sigma$ $\rightarrow$ loss로 이어지는 얕은 네트워크에 대한 역전파 (Backpropagation). 4단계로 완결.

이 「내적 $\rightarrow$ $\sigma$ $\rightarrow$ 역전파」의 감각은 그대로 Transformer의 셀프 어텐션 (Self-Attention, 내적으로 주목도를 측정) 이해로 이어집니다. 다음 단계로 BERT를 공부하면, 끊김 없이 이해할 수 있을 것입니다 🚀

기호 일람표

기호읽기의미
$v$브이입력 측 (중심어용) 행렬에서 추출한 벡터
$u$출력 측 (문맥어용) 행렬에서 추출한 벡터
$\sigma$시그마시그모이드 (Sigmoid) 함수
$\delta$델타오차 신호 (Error signal)
$\eta$이타학습률 (Learning rate). 1회에 얼마나 움직일지의 폭

Discussion

AI 자동 생성 콘텐츠

본 콘텐츠는 Zenn ML의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.

원문 바로가기
0

댓글

0