텍스트 벡터화: 신경망 기반 방법론

워드 임베딩을 이용한 벡터 표현 소개

자연어 처리(NLP)에서 텍스트의 의미와 문맥을 벡터로 표현하는 신경망 기반의 고급 벡터화 방법들을 심층적으로 탐구한다. 정적 워드 임베딩의 원리, 특징, 활용 방안을 다룬다.

NLP
Deep Learning
저자

Kwangmin Kim

공개

2025년 01월 05일

1 요약

이 문서는 통계 기반 벡터화의 한계를 넘어 텍스트 데이터로부터 풍부한 의미론적, 문맥적 정보를 추출하는 신경망 기반 벡터화 방법론을 소개한다.

  • DTM 방식의 한계와 신경망 접근법의 등장:
    • 전통적인 DTM(문서-단어 행렬) 방식의 문제점(차원의 저주, 희소성, 의미 관계 표현 불가)을 지적하고, 이를 극복하기 위한 신경망 기반 밀집 벡터 표현(워드 임베딩)의 필요성을 설명한다.
  • 워드 임베딩 (Word Embedding) - 정적 임베딩:
    • 핵심 원리: “같은 문맥에 나타나는 단어는 비슷한 의미를 가진다”는 분포 가설에 기반하여 단어를 저차원 밀집 벡터로 표현한다.
    • Embedding Layer: 정수 인코딩된 단어를 밀집 벡터로 변환하는 신경망의 핵심 구성 요소로, 그 구조와 Look-up Table 방식을 설명한다.
  • 실용적 응용 및 평가:
    • 임베딩 모델의 성능을 평가하는 내재적 평가(단어 유사도, 관계 유추)와 외재적 평가(다운스트림 태스크 성능) 방법을 소개한다.
  • 결론: 신경망 기반 벡터화 기법들의 발전 과정과 그 의의를 요약한다.

2 텍스트 인코딩 및 벡터화

텍스트 벡터화
├── 1. 전통적 방법 (통계 기반)
│   ├── BoW
│   ├── DTM
│   └── TF-IDF
│
├── 2. 신경망 기반 (문맥 독립)
│   ├── 문맥 독립적 임베딩
│   │   └── Embedding Layer (딥러닝 모델 내 구성 요소)
│   ├── Word2Vec (CBOW, Skip-gram)
│   ├── FastText
│   ├── GloVe
│   └── 기타 모델: Swivel, LexVec 등
│
└── 3. 문맥 기반 임베딩 (Contextual Embedding)
    └── RNN 계열
        ├── LSTM
        ├── GRU
        └── ELMo

2.0.1 신경망 사용 (2008~2018)

2.0.2 DTM 방식의 한계와 신경망 접근법의 등장

DTM 방식의 문제점: - 차원의 저주: 어휘집 크기 = 벡터 차원 (예: 50,000개 단어 → 50,000차원) - 희소성: 대부분의 값이 0인 sparse vector - 의미적 관계 부재: “왕”과 “여왕”의 관계를 벡터가 표현하지 못함 - 문제 상황:

# 기존 방식 (원-핫 인코딩)
"사과" = [1, 0, 0, 0, 0, ...]  # 50,000차원 벡터
"바나나" = [0, 1, 0, 0, 0, ...]
"과일" = [0, 0, 1, 0, 0, ...]
  • 모든 단어가 서로 똑같이 멀어 보임 (유클리드 거리 = √2)
  • “사과”와 “바나나”가 비슷한 과일이라는 정보가 없음
  • 메모리 낭비 (대부분이 0)

신경망 접근법의 혁신: - 밀집 벡터(Dense Vector): 고정된 낮은 차원 (예: 300차원)에 0/1 값이 아닌 실수 값을 가짐. - 의미적 유사도: 벡터 간 거리로 단어 유사도 측정 가능 - 문맥 학습: 주변 단어들을 통해 의미 학습

# 신경망 모델: 워드 임베딩 (300차원)
"사과" = [0.2, -0.4, 0.7, 0.1, ...]     # 300개 실수
"바나나" = [0.3, -0.5, 0.6, 0.2, ...]   # 비슷한 값들
"과일" = [0.25, -0.45, 0.65, 0.15, ...] # 과일 카테고리
  • 문맥 고려 방법 (Neural / Context-dependent)
  • 신경망을 통해 단어의 의미를 주변 문맥을 고려하여 학습하고, 이를 밀집 벡터(Dense Vector)로 표현.

2.0.3 워드 임베딩 (Word Embedding)

  • 문맥 속에서 각 단어가 어떻게 사용되는지까지 신경망을 통해 벡터값을 구해 벡터에 담아내려 시도.

  • 체험: 단어 유사도 측정 - https://projector.tensorflow.org/

  • 학습 후에는 각 단어 벡터 간의 유사도(의미반영)를 계산할 수 있다.

  • 즉, 신경망 기반의 벡터화라는 것은 벡터의 값이 학습에 의해 결정된다는 것을 의미.

  • 워드 임베딩 모델의 예시

    • Word2Vec, GloVe, FastText, 모델 내 Embedding Layer 사용.
  • 어떻게 단어를 벡터화?

    • 단어가 정수화 되면 차원이 정해진 임의의 가중치 테이블의 내적으로 벡터화된다.
    • 이때, 이 가중치 테이블을 embedding table (= embedding layer) 이라고 하고 각 행이 단어를 의미한다.
    • 따라서, embedding table의 행의 크기 = vocab_size가 되고 내적의 결과가 단어의 벡터가 되어 딥러닝 입력값으로 사용된다.
    • 딥러닝의 학습을 통해 이 embedding table (가중치 행렬)의 값이 최적화되고 이 값이 단어의 벡터가 된다.
    • 딥러닝 자연어 처리 시 거의 항상 하게 되는 작업
    • vocab_size (고차원, 20k~30k) x embedding_dim (저차원, 128,256,512 등) 크기의 가중치 행렬이 학습되어 임베딩 벡터가 된다.
    • 단어 -> 정수 인코딩 -> Embedding Layer -> 임베딩 벡터(=밀집 벡터)
    • 자연어 처리에서 단어를 정수로 바꿔주는 이유가 Embedding Layer를 통해 밀집 벡터로 변환하기 위해서이다.
    • Lookup table: 정수 인코딩을 밀집 벡터로 변환하는 테이블
  • 워드 임베딩 2가지 유형

    • 랜덤 초기화 임베딩
      • NNLM(Neural Network Language Model)과 마찬가지로 초기에 랜덤값의 가중치를 가지고 오차를 구하는 과정에서 embedding table의 값이 학습
      • NNLM은 이전 단어가 주어졌을 때, 다음 단어를 구하는 학습과정에서 오차를 줄이면서 학습되었으나 텍스트 분류, 개체명 인식등 수많은 task에서도 오차를 줄이며 학습 가능
      • task에 맞도록 embedding vector값이 최적화된다.
      • pytorch, keras 등 딥러닝 프레임워크에서 랜덤 초기화된 embedding layer를 제공한다.
      • 모델이 역전파하는 과정에서 embedding layer의 가중치가 학습되어 최적화된다.
    • 사전 훈련된 임베딩 (Pre-trained Word Embedding)
      • 이미 만들어진 임베딩 테이블을 사용
      • 정해진 특정 알고리즘에 방대한 데이터를 입력으로 학습시킨 후 여러 task의 입력으로 사용
      • 대표적인 알고리즘으로 word2vec, glove, fasttext가 있음
      • 이미 방대한 양의 텍스트 데이터로 훈련되어져 있는 임베딩 벡터값들을 갖고와서 딥러닝 모델의 입력값으로 사용
      • 이때 이 임베딩 벡터들은 word2vec, glove, fasttext 등 특정 알고리즘으로 훈련되어져 있는 임베딩 벡터값들이다.
      • 이미 학습된 임베딩 벡터를 사용하므로 모델 학습 시간이 줄어들고 더 좋은 성능을 보임
  • 핵심 원리

    • 분포 가설(Distributional Hypothesis):
      • “같은 문맥에서 나타나는 단어들은 유사한 의미를 가진다”
      • 수학적으로 표현하면: \(\text{similarity}(w_i, w_j) \propto \text{context\_overlap}(w_i, w_j)\)
    • “같은 문맥에 나타나는 단어들은 비슷한 의미를 가진다”
    • 예시:
    문장1: "나는 사과를 먹었다"
    문장2: "나는 바나나를 먹었다"  
    문장3: "나는 딸기를 먹었다"

    → “사과”, “바나나”, “딸기”는 같은 위치(문맥)에 나타남 → 비슷한 벡터를 가져야 함

  • 임베딩 벡터의 의미

    • 벡터 간 유사도

      cosine_similarity(v_사과, v_바나나) = 0.8  # 높음 (비슷함)
      cosine_similarity(v_사과, v_컴퓨터) = 0.1  # 낮음 (다름)
      • 수식: \(\text{similarity}(\mathbf{v}_1, \mathbf{v}_2) = \frac{\mathbf{v}_1 \cdot \mathbf{v}_2}{||\mathbf{v}_1|| \cdot ||\mathbf{v}_2||}\)
      • 직관적 해석
        • 1에 가까울수록 비슷한 의미
        • 0에 가까울수록 관련 없음
        • -1에 가까울수록 반대 의미
    • 벡터 연산의 마법

      • 유명한 예시:
        • \(\vec{\text{king}} - \vec{\text{man}} + \vec{\text{woman}} \approx \vec{\text{queen}}\)
        • \(\vec{\text{king}} - \vec{\text{man}}\): “남성성”을 제거 → “왕권” 개념만 남음
        • \(+ \vec{\text{woman}}\): “여성성” 추가
        • 결과: “여성 + 왕권” → “여왕”
      • 수학적 설명:
        • 각 벡터를 의미 성분들의 조합으로 생각:
        king = [왕권: 0.9, 남성: 0.8, 권력: 0.7, ...]
        man = [남성: 0.9, 성인: 0.6, ...]  
        woman = [여성: 0.9, 성인: 0.6, ...]
        • 연산 후:
        king - man + woman ≈ [왕권: 0.9, 여성: 0.9, 권력: 0.7, ...]
        → “queen”과 가장 유사!
  • 실제 학습 예시

    • 초기 상태 (랜덤)

      "사과" = [0.1, -0.3, 0.7, ...]  (랜덤)
      "바나나" = [-0.8, 0.2, -0.1, ...] (랜덤)
      • 서로 전혀 관련 없어 보임
    • 학습 진행

      문장들을 계속 보면서:
      "사과를 먹었다", "바나나를 먹었다", "딸기를 먹었다"
      ...
      
      * 점차 비슷한 벡터로 수렴:
      

      “사과” = [0.2, -0.4, 0.6, …] “바나나” = [0.3, -0.5, 0.7, …]
      “딸기” = [0.25, -0.45, 0.65, …] ```

    • 학습 완료 후

      • 의미가 비슷한 단어들 → 벡터 공간에서 가까운 위치
      • 반대 의미 단어들 → 먼 위치 또는 반대 방향
      • 유추 관계 → 벡터 연산으로 표현 가능
  • 핵심: 신경망이 “문맥”이라는 단서를 통해 단어의 의미를 수치로 학습

2.0.3.1 Embedding Layer 구조 분석

  • Embedding Layer란?
    • 정수 인코딩 → 밀집 벡터 변환기
# 이런 변환을 해주는 것
15 → [0.2, -0.4, 0.7, 0.1, -0.3]  # 300차원 벡터
23 → [0.1, 0.3, -0.2, 0.8, 0.5]   # 300차원 벡터  
7  → [-0.1, 0.6, 0.4, -0.2, 0.9]  # 300차원 벡터
  • 왜 정수 인코딩이 필요한가?
    • 문제: 컴퓨터는 “사과”라는 글자를 직접 처리할 수 없음
    • 해결 과정:
# 1단계: 단어 → 정수 (정수 인코딩)
"사과"15
"바나나"23  
"딸기"7

# 2단계: 정수 → 벡터 (Embedding Layer)
15 → [0.2, -0.4, 0.7, ...]
23 → [0.1, 0.3, -0.2, ...]
7  → [-0.1, 0.6, 0.4, ...]
  • Look-up Table의 구체적 동작

    • Embedding Matrix 구조: \(\mathbf{E} \in \mathbb{R}^{V \times d}\)
    • 실제 예시:
    # V = 5 (어휘 크기), d = 3 (임베딩 차원)
    embedding_matrix = [
       [0.1, 0.2, 0.3],    # 단어 ID 0의 벡터
       [0.4, 0.5, 0.6],    # 단어 ID 1의 벡터  
       [0.7, 0.8, 0.9],    # 단어 ID 2의 벡터
       [0.2, -0.1, 0.4],   # 단어 ID 3의 벡터
       [0.5, 0.3, -0.2],   # 단어 ID 4의 벡터
    ]
  • 행렬의 의미:

    • 행(row): 각 단어의 임베딩 벡터
    • 열(column): 임베딩 벡터의 각 차원
    • 전체: 모든 단어의 벡터를 저장하는 “사전”
  • Look-up 연산 과정

    • 입력: input_ids = [2, 0, 4]
    # 1단계: 각 ID에 해당하는 행을 추출
    embedding_matrix[2] → [0.7, 0.8, 0.9]    # ID 2의 벡터
    embedding_matrix[0] → [0.1, 0.2, 0.3]    # ID 0의 벡터  
    embedding_matrix[4] → [0.5, 0.3, -0.2]   # ID 4의 벡터
    
    # 2단계: 결과 (3개 벡터)
    output = [
       [0.7, 0.8, 0.9],     # 첫 번째 단어
       [0.1, 0.2, 0.3],     # 두 번째 단어
       [0.5, 0.3, -0.2]     # 세 번째 단어  
    ]
  • 수학적 의미

    • \(\text{embedding}(i) = \mathbf{E}[i, :]\)
      • \(i\): 단어의 정수 ID
      • \(\mathbf{E}[i, :]\): 행렬 E의 i번째 행 전체
      • 결과: i번째 단어의 임베딩 벡터
    • 구체적 예시:
    i = 2  # "딸기"의 ID라고 가정
    embedding(2) = E[2, :] = [0.7, 0.8, 0.9]  # 2번째 행
  • 실제 PyTorch 코드

import torch
import torch.nn as nn

# 1. Embedding Layer 생성
vocab_size = 1000      # 어휘 크기
embedding_dim = 300    # 벡터 차원
embedding = nn.Embedding(vocab_size, embedding_dim)

# 2. 내부 구조 확인
print(embedding.weight.shape)  # torch.Size([1000, 300])
# → 1000×300 크기의 look-up table

# 3. 입력 데이터
input_ids = torch.tensor([15, 23, 7])  # 3개 단어의 ID

# 4. 임베딩 변환
output = embedding(input_ids)
print(output.shape)  # torch.Size([3, 300])
# → 3개 단어 × 300차원 벡터
  • Look-up Table이 학습되는 과정
    • 초기화 (랜덤)
    # 처음에는 랜덤 값들
    embedding_matrix = torch.randn(vocab_size, embedding_dim)
  • 학습 과정
# 예: "사과는 맛있다"라는 문장 학습
input_ids = [15, 23, 7]  # [사과는, 맛있다, <END>]

# 1. 현재 임베딩으로 예측
embeddings = embedding_matrix[input_ids]  # Look-up
prediction = model(embeddings)

# 2. 손실 계산
loss = criterion(prediction, target)

# 3. 역전파로 embedding_matrix 업데이트  
loss.backward()  # embedding_matrix의 gradient 계산
optimizer.step()  # embedding_matrix 값들 업데이트
  • 핵심: 학습이 진행되면서 embedding_matrix의 각 행(단어 벡터)이 점점 더 의미 있는 값으로 변함!

  • 왜 “Look-up Table”이라고 부르는가?

    • 전통적인 사전과 비교
    # 일반 사전
    사전 = {
       "사과": "빨간 과일",
       "바나나": "노란 과일", 
       "컴퓨터": "전자 기기"
    }
    의미 = 사전["사과"]  # "빨간 과일"
    
    # Embedding Table  
    임베딩_테이블 = {
       15: [0.2, -0.4, 0.7, ...],   # "사과"
       23: [0.1, 0.3, -0.2, ...],   # "바나나"
       78: [-0.5, 0.8, 0.1, ...]    # "컴퓨터"
    }
    벡터 = 임베딩_테이블[15]  # [0.2, -0.4, 0.7, ...]
    • 차이점:
      • 일반 사전: 단어 → 설명 (텍스트)
      • 임베딩 테이블: 단어 ID → 숫자 벡터
  • 전체 과정 정리

# 전체 파이프라인
"사과는 맛있다" 
→ ["사과는", "맛있다"]           # 토큰화
→ [15, 23]                      # 정수 인코딩  
→ [[0.2, -0.4, 0.7],           # Embedding Layer (Look-up)
   [0.1, 0.3, -0.2]]
→ 신경망 처리                    # 후속 레이어들
  • 핵심
    • Embedding Layer는 단순히 “정수 ID를 인덱스로 사용해서 미리 저장된 벡터를 가져오는” 매우 단순한 연산.
    • 하지만 이 벡터들이 학습을 통해 의미 있는 값으로 변하기 때문에 강력한 도구가 되는 것

3 결론

본 문서에서는 자연어 처리(NLP) 분야에서 텍스트 데이터의 의미를 효과적으로 포착하기 위해 통계 기반 방법의 한계를 넘어, 신경망은 단어와 문맥의 복잡한 관계를 학습하여 풍부한 정보를 담은 벡터 표현을 생성한다.

  • 워드 임베딩의 발전:
    • 정적 임베딩 (Word2Vec, GloVe, FastText): ’분포 가설’에 기반하여 단어를 저차원 밀집 벡터로 표현함으로써 단어 간 의미적 유사성과 관계(예: 유추)를 포착했다. Embedding Layer는 이러한 변환의 핵심이며, FastText는 하위 단어(subword) 정보를 활용하여 OOV 문제와 형태론적 특징 처리에 강점을 보였다.
    • 이러한 초기 신경망 기반 방법들은 단어의 의미를 고정된 벡터로 표현하여 NLP 성능을 크게 향상시켰다.
  • 벡터화 방법 선택의 중요성:
    • 단순한 단어 유사도 측정부터 복잡한 문서 이해 및 생성에 이르기까지, 해결하고자 하는 문제의 특성, 데이터의 규모와 성격, 그리고 사용하려는 모델의 요구사항을 종합적으로 고려하여 적절한 벡터화 전략을 선택하는 것이 중요하다.
    • 이러한 신경망 기반 벡터화 기법들은 현대 대규모 언어 모델(LLM) 발전의 핵심적인 토대가 되었으며, 자연어 이해 및 생성 능력의 비약적인 발전을 이끌고 있다.

Subscribe

Enjoy this blog? Get notified of new posts by email: