NLP(2) - Word2vec 속도 개선 (Deep Learning from Scratch 2)

2026. 4. 1. 20:22Open Seminar/NLP & LLM

NLP 개론 (2) — Word2vec 속도 개선

이번 글은 제가 서강대학교 수학과 세미나에서 발표한 Word2vec 속도 개선 내용을 정리한 것입니다. 지난 세미나에서 Word2vec의 기본 구조(CBOW, Skip-gram)를 다뤘다면, 이번에는 실제 대규모 어휘에 적용할 때 발생하는 계산 병목 문제를 어떻게 해결하는지에 초점을 맞췄습니다. 참고 자료는 밑바닥부터 시작하는 딥러닝 2이며, 관련 코드는 GitHub 저장소에서 확인할 수 있습니다.

세미나 발표 표지 — NLP 개론 (2): Word2vec 속도 개선


1. 지난 세미나 복습: 단어의 분산 표현과 Word2vec

본격적인 속도 개선 이야기에 앞서, 지난 시간에 다뤘던 핵심 내용을 간단히 복습하겠습니다.

1.1 단어의 의미를 파악하는 세 가지 기법

자연어 처리에서 단어는 의미의 최소 단위(한국어에서는 형태소)이며, 컴퓨터가 단어를 이해하게 만드는 것이 핵심 과제입니다. 단어의 의미를 파악하는 기법은 크게 세 가지로 나눌 수 있습니다.

  1. 시소러스(Thesaurus): 뜻에 의해 그룹화하고 계층별로 나누어 놓은 유의어 사전. 수작업 레이블링의 어려움, 신조어 대응 불가, 높은 인건비, 미묘한 의미 차이 표현 불가 등의 문제가 있습니다.
  2. 통계 기반 기법: 대량의 말뭉치(corpus)로부터 동시발생 행렬을 만들고, PPMI·SVD 등으로 차원을 축소하여 밀집 벡터(dense vector)를 얻습니다. 다만 $n \times n$ 행렬에 SVD를 적용하면 계산 복잡도가 $O(n^3)$으로 매우 큽니다.
  3. 추론 기반 기법(Word2vec): 분포 가설을 기반으로, 맥락(context)을 입력받아 타깃 단어의 출현 확률을 출력하는 신경망을 학습합니다.

추론 기반 기법: 맥락 단어를 입력받아 각 단어의 출현 확률을 출력하는 모델

1.2 CBOW 모델 구조

Word2vec의 대표 모델인 CBOW(Continuous Bag-of-Words)는 맥락(주변 단어)으로부터 타깃(중심 단어)을 추측하는 신경망입니다.

  • 입력층: 맥락 단어들을 원핫 표현(one-hot representation)으로 변환
  • 은닉층: 입력층의 완전연결계층에 의해 변환된 값들의 평균
  • 출력층: 각 단어의 점수를 계산하고, Softmax 함수를 통해 확률로 변환

CBOW 모델의 신경망 구조: 입력(원핫 벡터) → $W_{in}$ → 은닉층 → $W_{out}$ → 출력(점수)

1.3 CBOW의 확률적 해석과 손실 함수

CBOW 모델은 확률적으로 다음과 같이 표현할 수 있습니다. 윈도우 크기가 1인 경우:

$$P(w_t \mid w_{t-1}, w_{t+1})$$

손실 함수로는 교차 엔트로피 오차(Cross-Entropy Error)를 사용합니다. 정답 레이블이 원핫 벡터이므로 단순화하면:

$$L = -\log P(w_t \mid w_{t-1}, w_{t+1})$$

전체 말뭉치에 대한 손실 함수는 다음과 같습니다:

$$L = -\frac{1}{T} \sum_{t=1}^{T} \log P(w_t \mid w_{t-1}, w_{t+1})$$

1.4 CBOW vs Skip-gram

Word2vec은 두 가지 모델을 제안합니다.

  • CBOW: 맥락(주변 단어) → 타깃(중심 단어) 추측
  • Skip-gram: 타깃(중심 단어) → 맥락(주변 단어) 추측

CBOW와 Skip-gram이 다루는 문제의 차이

Skip-gram의 손실 함수는 조건부 독립을 가정하여 다음과 같이 유도됩니다:

$$L = -\frac{1}{T} \sum_{t=1}^{T} \left( \log P(w_{t-1} \mid w_t) + \log P(w_{t+1} \mid w_t) \right)$$

일반적으로 Skip-gram이 단어 분산 표현의 정밀도 면에서 더 우수하고(특히 말뭉치가 커질수록), CBOW가 학습 속도 면에서 더 빠릅니다.


2. Word2vec의 계산 병목 문제

이제 본격적으로 이번 세미나의 핵심 주제인 속도 개선으로 넘어가겠습니다. 3장에서 구현한 단순 CBOW 모델은 어휘 수가 적을 때는 잘 동작하지만, 실제 대규모 말뭉치에 적용하면 심각한 계산 병목이 발생합니다.

어휘 수가 100만 개, 은닉층 뉴런이 100개인 상황에서의 CBOW 모델 — 가중치 행렬의 크기가 폭증한다

어휘 수가 100만 개이고 은닉층 뉴런이 100개라고 가정하면, 두 가지 병목 지점이 존재합니다:

병목 1: 입력층의 원핫 표현과 가중치 행렬 $W_{in}$ ($1{,}000{,}000 \times 100$)의 곱 계산

병목 2: 은닉층과 가중치 행렬 $W_{out}$ ($100 \times 1{,}000{,}000$)의 곱 및 Softmax 계층의 계산

특히 Softmax의 경우, 분모를 계산하기 위해 모든 어휘에 대해 지수 함수를 계산해야 합니다:

$$y_k = \frac{\exp(s_k)}{\sum_{i=1}^{1{,}000{,}000} \exp(s_i)}$$

100만 번의 $\exp$ 계산과 합산이 필요하니, 이대로는 실용적이지 않습니다.


3. 첫 번째 개선: Embedding 계층

3.1 문제 분석

첫 번째 병목을 자세히 들여다보면, 원핫 벡터 $\mathbf{c}$와 가중치 행렬 $W_{in}$의 곱셈은 사실상 $W_{in}$에서 특정 행 하나를 추출하는 작업과 동일합니다. 원핫 벡터는 하나의 원소만 1이고 나머지는 모두 0이기 때문입니다.

원핫 벡터 $\mathbf{c}$와 $W_{in}$의 곱은 결국 해당 행을 추출하는 것과 같다

결과가 같다면, 비효율적인 행렬 곱셈 대신 인덱싱으로 직접 행을 추출하면 됩니다.

3.2 Embedding 계층의 도입

Embedding 계층은 가중치 매개변수로부터 단어 ID에 해당하는 행(벡터)을 추출하는 계층입니다. 수학적인 연산 장치 없이, 단순히 배열 인덱싱만으로 동작합니다.

# Numpy를 이용한 행 추출 예시
W = np.random.randn(7, 3)
W[2]          # 2번째 행 추출
W[5]          # 5번째 행 추출
W[[1, 0, 3, 0]]  # 여러 행을 한 번에 추출

Embedding 계층의 순전파(forward)와 역전파(backward) 과정

3.3 역전파 시 기울기 중복 문제

Embedding 계층의 역전파에서 주의할 점이 있습니다. 입력 idx중복된 원소가 있을 경우(예: 같은 단어가 맥락에 여러 번 등장), 역전파 시 여러 기울기가 $dW$의 동일한 행에 전달되어야 합니다.

idx에 중복이 있을 때 기울기 전달 문제

idx에 중복(0, 2, 0, 4)이 있을 때, 같은 행에 여러 기울기가 전달되어야 하는 상황

이때 단순히 값을 할당(assignment)하면 마지막 기울기만 남고 이전 것은 덮어쓰여 사라집니다. 올바른 방법은 더하기(addition)로 기울기를 누적하는 것입니다.

이것이 정당한 이유는 행렬 곱의 역전파 수식에서 확인할 수 있습니다:

$$\frac{\partial L}{\partial W} = \mathbf{x}^T \cdot \frac{\partial L}{\partial \mathbf{y}}$$

$\mathbf{x}$가 원핫 벡터들의 집합이고 중복된 단어가 있다면, $\mathbf{x}^T$와의 곱셈 과정에서 자연스럽게 해당 행에 대한 기울기들이 더해지게 됩니다.

MatMul 노드의 역전파: $\frac{\partial L}{\partial W} = \mathbf{x}^T \cdot \frac{\partial L}{\partial \mathbf{y}}$

3.4 최종 구현

이를 반영한 Embedding 계층의 최종 역전파 코드는 다음과 같습니다:

def backward(self, dout):
    dW = np.zeros_like(self.W)
    np.add.at(dW, self.idx, dout)
    self.grads[0][...] = dW
    return None

첫 번째 개선 정리

  • 입력층의 MatMul 계층을 Embedding 계층으로 전환
  • 불필요한 행렬 곱셈을 생략하여 계산량과 메모리 사용량을 절약

4. 두 번째 개선: 네거티브 샘플링(Negative Sampling)

4.1 문제 재확인

두 번째 병목은 은닉층 이후의 계산입니다. 은닉층과 거대한 $W_{out}$의 곱셈, 그리고 Softmax 분모 계산 시 어휘 수만큼의 $\exp$ 연산이 필요합니다. 행렬 곱 자체를 가볍게 만들어야 합니다.

Softmax 분모의 합산 범위가 100만 — $\exp$ 계산을 100만 번 수행해야 한다

4.2 핵심 아이디어: 다중 분류 → 이진 분류

네거티브 샘플링의 핵심 아이디어는 다중 분류(multi-class classification) 문제를 이진 분류(binary classification) 문제로 근사하는 것입니다.

기존 질문: "맥락이 'you'와 'goodbye'일 때 타깃 단어는 무엇인가?" (100만 개 중 하나를 고르는 다중 분류)

변환된 질문: "맥락이 'you'와 'goodbye'일 때 타깃 단어는 'say'인가?" (Yes/No 이진 분류)

출력층이 단일 뉴런 + Sigmoid 함수로 변경된 구조 — $W_{out}$에서 'say'에 해당하는 벡터만 추출하여 내적

이렇게 바꾸면 출력층의 뉴런이 어휘 수만큼이 아니라 단 1개만 필요합니다. 은닉 벡터 $\mathbf{h}$와 $W_{out}$에서 타깃 단어에 해당하는 벡터의 내적(inner product)만 계산하면 되므로, 계산량이 극적으로 줄어듭니다.

4.3 Sigmoid 함수와 이진 교차 엔트로피 오차

이진 분류에서는 Softmax 대신 Sigmoid 함수를 사용합니다:

$$y = \frac{1}{1 + \exp(-x)}$$

손실 함수로는 이진 교차 엔트로피 오차를 사용합니다:

$$L = -(t \log y + (1-t) \log(1-y))$$

여기서 $y$는 모델의 예측 확률, $t$는 정답 레이블(긍정 예시: 1, 부정 예시: 0)입니다.

Sigmoid와 Cross Entropy Error를 합치면 역전파 기울기가 $y - t$로 매우 간단해진다

Sigmoid와 교차 엔트로피 오차를 결합하면, 역전파의 최종 기울기는 놀랍도록 간단한 형태가 됩니다:

$$\frac{\partial L}{\partial x} = y - t$$

이 $y - t$는 곧 오차(error)를 의미합니다. 오차가 크면 기울기도 커져 매개변수가 크게 수정되고, 오차가 작으면 세부적인 조정만 이루어집니다. 매우 직관적입니다.

4.4 긍정적 예시와 부정적 예시

이진 분류로 전환했으니, 이제 학습 목표를 정리하면:

  • 긍정적 예시(positive example): 정답 단어를 입력하면 Sigmoid 출력이 1에 가깝게
  • 부정적 예시(negative example): 오답 단어를 입력하면 Sigmoid 출력이 0에 가깝게

정답 'say'는 0.993(≈1), 오답 'you'는 0.003(≈0), 'hello'는 0.021(≈0)을 출력해야 한다

하지만 모든 부정적 예시를 대상으로 학습하면, 어휘 수가 늘어남에 따라 계산량이 다시 증가합니다. 이는 원래 해결하려던 문제로 돌아가는 것입니다.

4.5 네거티브 샘플링의 핵심

해결책은 간단합니다. 모든 부정적 예시 대신, 몇 개만 선택(샘플링)하여 학습시키는 것입니다. 이것이 바로 네거티브 샘플링(Negative Sampling)입니다.

긍정적 예시 1개('say', 레이블 1)와 부정적 예시 2개('hello', 'i', 레이블 0)의 손실을 합산

구체적인 과정은 다음과 같습니다:

  1. 긍정적 예시(정답 단어) 1개에 대해 손실을 계산합니다.
  2. 부정적 예시(오답 단어) 몇 개를 샘플링하여 각각 손실을 계산합니다.
  3. 모든 손실을 합산하여 최종 손실로 사용합니다.

4.6 부정적 예시의 샘플링 방법

부정적 예시를 무작위로 뽑는 것이 아니라, 말뭉치의 단어 출현 빈도를 기반으로 한 확률 분포에 따라 샘플링합니다. 자주 등장하는 단어를 많이 추출하고, 드물게 등장하는 단어는 적게 추출합니다.

단어의 출현 빈도를 기반으로 한 확률 분포에 따라 부정적 예시를 샘플링한다

여기에 추가로, 원래 확률 분포에 0.75 제곱을 적용하는 것이 권장됩니다:

$$P'(w_i) = \frac{P(w_i)^{0.75}}{\sum_j P(w_j)^{0.75}}$$

이렇게 하면 출현 확률이 낮은 단어의 확률이 약간 높아져, 거의 샘플링되지 않는 문제를 방지할 수 있습니다. 예를 들어, 원래 확률이 0.01이었던 단어의 보정 후 확률은 약 0.026으로 높아집니다.

4.7 Embedding Dot 계층

효율적인 구현을 위해, $W_{out}$에서 특정 단어 벡터를 꺼내는 작업(Embedding)과 은닉 벡터 $\mathbf{h}$와의 내적(Dot)을 한 번에 처리하는 Embedding Dot 계층을 도입했습니다.

Embedding Dot 계층: $W$에서 필요한 행만 추출(Embedding) → 은닉 벡터와 내적(Dot)

두 번째 개선 정리

  • 다중 분류를 이진 분류로 전환 (Softmax → Sigmoid)
  • 모든 부정적 예시 대신 몇 개만 샘플링하여 학습 (네거티브 샘플링)
  • Embedding Dot 계층으로 필요한 벡터만 추출하여 내적 계산

5. 개선된 Word2vec 학습 및 평가

5.1 개선된 CBOW 모델 구현

지금까지의 두 가지 개선을 적용한 CBOW 모델의 핵심 변경점은 다음과 같습니다:

  1. 입력층: MatMul 계층 → Embedding 계층
  2. 출력층: Softmax with Loss → NegativeSamplingLoss 계층

개선된 CBOW 모델: Embedding 계층과 NegativeSamplingLoss 계층을 적용한 구현

5.2 단어 유사도 평가

PTB 데이터셋으로 학습한 후, most_similar() 함수를 이용해 특정 단어와 코사인 거리가 가장 가까운 단어들을 확인했습니다.

학습 결과: 'you'에는 인칭대명사, 'year'에는 시간 관련 단어, 'toyota'에는 다른 자동차 제조사가 나온다

결과를 보면:

  • 'you' → we, someone, i 등 인칭대명사
  • 'year' → month, week, spring, summer 등 시간 관련 단어
  • 'car' → luxury, auto 등 자동차 관련 단어
  • 'toyota' → ford, mazda, nissan 등 자동차 제조사

학습된 모델이 단어의 의미적 유사성을 잘 파악하고 있음을 확인할 수 있습니다.

5.3 유추 문제(Analogy Task)

Word2vec의 진정한 힘은 유추 문제를 풀 수 있다는 점입니다. 단어의 분산 표현에는 단순한 유사성뿐 아니라, 더 복잡한 의미적·문법적 패턴이 인코딩되어 있습니다.

"man:woman = king:?" — 벡터 연산으로 유추 문제를 풀 수 있다

수식으로 표현하면:

$$\vec{\text{king}} + \vec{\text{woman}} - \vec{\text{man}} \approx \vec{\text{queen}}$$

다양한 유추 문제의 결과: 성별 관계, 시제 변환, 단복수 변환, 비교급 관계까지 파악

실제 결과를 보면:

  • king - man + womanqueen (성별 관계)
  • take - took + gowent (시제 패턴)
  • car - cars + childchildren (단수/복수 패턴)
  • good - better + bad → more, less (비교급 관계)

단어의 분산 표현에 문법적, 의미적 성질이 잘 인코딩되어 있음을 확인할 수 있습니다.


6. Word2vec의 활용과 평가

6.1 실제 애플리케이션 예시

Word2vec은 다양한 자연어 처리 태스크의 전처리 단계로 활용됩니다. 예를 들어, 대규모 앱의 고객 문의 메일을 감정(긍정/중립/부정)에 따라 자동 분류하는 시스템을 구축할 때:

  1. 수집한 메일에 감정 레이블을 수동으로 부여
  2. Word2vec으로 메일을 벡터로 변환
  3. 변환된 벡터와 감정 레이블로 분류 시스템(SVM, 신경망 등)을 학습

6.2 단어 분산 표현의 평가 방법

학습된 단어 벡터의 품질을 평가하는 방법은 크게 두 가지입니다:

  • 단어 유사성 평가: 사람이 직접 작성한 유의도 점수 벤치마크와 Word2vec의 코사인 유사도 점수의 상관성을 비교
  • 유추 문제 평가: "king : queen = man : ?" 같은 유추 문제의 정답률로 측정. 의미적·문법적 이해도를 종합적으로 평가 가능

7. 정리

이번 세미나에서는 Word2vec의 두 가지 계산 병목을 해결하는 방법을 다뤘습니다.

핵심 요약

  • 말뭉치 어휘 수 증가에 비례해 계산량이 폭증하는 문제를 해결
  • Embedding 계층: 원핫 벡터 × 가중치 행렬 곱셈 대신, 필요한 행만 인덱싱으로 추출
  • 네거티브 샘플링: 다중 분류를 이진 분류로 전환하고, 모든 부정적 예시 대신 몇 개만 샘플링하여 학습
  • 핵심 철학: "모두 대신 일부를 처리한다"
  • 개선된 모델로 PTB 데이터셋을 학습한 결과, 단어 유사도와 유추 문제 모두에서 의미 있는 결과를 확인

Word2vec 속도 개선의 최종 정리

다음 세미나에서는 이렇게 학습된 단어 벡터를 활용하는 후속 주제를 다룰 예정입니다.

 

 

읽어주셔서 감사합니다.

 

* 이 글은 제가 만든 발표 자료 기반으로 AI를 사용하여 작성한 글 입니다. 발표자료는 아래 원본을 참고해주세요.

NLP_2.pdf
7.28MB