Word2Vec & 동시출현행렬 & PMI/PPMI 정리

syeom·2026년 6월 6일

멀티캠퍼스 데이터분석 6월 5일 수업 내용 — Word2Vec 이론 및 실습, 동시출현행렬, PMI/PPMI 직접 구현


📌 목차

Word2Vec
1. Word2Vec 이론 & 매개변수
2. Word2Vec 실습

동시출현행렬 & PMI

  1. 동시출현행렬 & N-gram & PMI/PPMI 개념
  2. 동시출현행렬 & PMI 직접 구현

Word2Vec

1. Word2Vec 이론 & 매개변수

  • 문자를 수치형으로 변환하는 딥러닝 기반 임베딩 기술
  • TF-IDF와 달리 단어의 의미적 유사성을 벡터 공간에 반영
  • 비슷한 문맥에서 등장하는 단어는 비슷한 벡터를 가짐

주요 매개변수

매개변수기본값설명
sentencesNone토큰화된 문장 데이터 (2차원 리스트)
vector_size100임베딩 벡터 차원 수 (피처 수)
window5예측 시 고려할 주변 단어와의 거리 (문맥 크기)
sg0학습 방식 (0: CBOW / 1: Skip-gram)
min_count5최소 등장 빈도 — 이 값 미만 단어 제외
hs0계산 방식 (0: Negative Sampling / 1: Hierarchical Softmax)
epochs100반복 학습 횟수
max_vocab_sizeNone메모리 제한 시 최대 단어 개수
seed-재현 가능한 결과를 위한 시드값
workers-학습에 사용할 CPU 스레드 수

CBOW vs Skip-gram

CBOW (sg=0)Skip-gram (sg=1)
방식주변 단어 → 중심 단어 예측중심 단어 → 주변 단어 예측
속도빠름느림
성능빈출 단어에 유리희귀 단어에 유리
권장빠른 계산 필요 시일반적으로 권장

주요 속성

속성설명
wv학습된 단어 벡터 객체
wv['단어']특정 단어의 임베딩 벡터 반환
wv.index_to_key등장 빈도 순 정렬된 단어 리스트
wv.key_to_index단어 → 인덱스 매핑 딕셔너리
corpus_total_words전체 학습 단어 수
vector_size벡터 차원 수

주요 메서드

메서드설명
wv.most_similar(word, topn=10)특정 단어와 가장 유사한 단어 topn개 반환
wv.similarity(word1, word2)두 단어의 코사인 유사도 계산
wv.get_vector(word)특정 단어의 벡터 반환
model.train()추가 데이터로 학습
model.save(path)학습된 모델 저장
Word2Vec.load(path)저장된 모델 로드

2. Word2Vec 실습

설치 & 데이터 준비

# !pip install gensim
from gensim.models import Word2Vec
from sklearn.svm import SVC
from konlpy.tag import Komoran
import numpy as np

docs = [
    '오늘 날씨가 좋다 여행 가고 싶다',
    '기온이 너무 올라서 아무것도 하기 싫다',
    '수업이 너무 지루하고 졸리다',
    '음식이 너무 맛이 없고 서비스도 별로다',
    '영화가 너무 재미있어서 시간이 가는 줄 몰랐다'
]
target = [1, 0, 0, 0, 1]

토큰화

komoran   = Komoran()
allow_pos = ['NNP', 'NNG', 'VV', 'VA', 'SL', 'MAG']

tokens = []
for doc in docs:
    words = []
    for word, pos in komoran.pos(doc):
        if pos in allow_pos:
            words.append(word)
    tokens.append(words)

print(tokens)

Word2Vec 학습 (Skip-gram)

w2v = Word2Vec(
    sentences   = tokens,
    vector_size = 100,
    window      = 5,
    min_count   = 1,    # 데이터가 적으므로 1로 설정
    sg          = 1,    # Skip-gram
    epochs      = 100,
    seed        = 42,
    workers     = 2
)

# wv 속성을 변수에 저장 (자주 사용되므로)
wv = w2v.wv

# 특정 단어의 벡터 확인
print(wv['여행'])

# 유사 단어 검색
print(wv.most_similar('음식', topn=3))

# 두 단어의 코사인 유사도
print(wv.similarity('여행', '음식'))

# 학습된 단어 수 확인
print(len(wv.index_to_key))

문장 벡터 생성 — 단어 벡터 평균

Word2Vec은 단어 단위 벡터를 반환합니다. 문장 벡터를 만들려면 문장 내 단어 벡터의 평균을 사용합니다.

vectors = []

for token in tokens:
    vec = []
    for word in token:
        if word in wv.index_to_key:
            vec.append(wv[word])
    # 문장의 단어 벡터들을 평균 → 문장 벡터
    vectors.append(np.mean(vec, axis=0))

print(np.array(vectors).shape)   # (5, 100)

SVC로 분류

svc = SVC(random_state=42)
svc.fit(np.array(vectors), target)

💡 Word2Vec vs TF-IDF

TF-IDFWord2Vec
방식통계 기반 (빈도)딥러닝 기반 (문맥)
단어 간 의미 반영XO
모르는 단어 처리제외min_count 이하 제외
출력희소 행렬밀집 벡터 (Dense)

동시출현행렬 & PMI

3. 동시출현행렬 & N-gram & PMI/PPMI 개념

동시출현행렬 (Co-occurrence Matrix)

  • 특정 단어를 기준으로 주변 window 범위 안에서 어떤 단어가 등장했는지 빈도수를 기록
  • 예: window=1일 때 '날씨가' → 주변 단어 ('오늘', '너무', '내일', '조금')

N-gram과의 차이

N-gram동시출현행렬
반영 요소단어 순서단어 간 의미 관계
방향성일방향 (앞→뒤)양방향 (주변 전체)

PMI (Pointwise Mutual Information)

  • 두 단어가 우연히 함께 등장했는가? 아니면 의미적 연관성이 있는가? 측정
  • 측정값이 클수록 두 단어는 의미적으로 강하게 연결되어 있음

PPMI (Positive PMI)

  • PMI는 음수가 나오는 경우가 극히 드물지만 존재
  • 음수인 데이터를 0으로 대체한 값

4. 동시출현행렬 & PMI 직접 구현

데이터 준비 & 토큰화

import math
import pandas as pd
from konlpy.tag import Okt

docs = [
    '오늘 날씨가 너무 좋다',
    '오늘 기분이 정말 좋다',
    '내일 날씨가 조금 흐리다',
    '기분이 매우 나쁘다'
]

window_size = 2

okt    = Okt()
tokens = [okt.morphs(doc) for doc in docs]

# 단어 사전 생성 (중복 제거, 정렬)
vocab      = sorted(set(sum(tokens, [])))
vocab_dict = {word: idx for idx, word in enumerate(vocab)}

💡 list.extend() vs list.append()

  • append(item) → 원소 하나를 리스트에 추가
  • extend(iterable) → 이터러블의 각 원소를 리스트에 추가 (리스트를 펼쳐서 추가)

동시출현행렬 생성

# 0으로 채운 행렬 초기화
co_metric = [[0] * len(vocab) for _ in range(len(vocab))]

for token in tokens:
    for idx, word in enumerate(token):
        count_idx = vocab_dict[word]

        # 윈도우 범위 설정
        start = max(0, idx - window_size)
        end   = min(len(token), idx + window_size + 1)

        for i in range(start, end):
            if idx != i:   # 자기 자신 제외
                context     = token[i]
                context_idx = vocab_dict[context]
                co_metric[count_idx][context_idx] += 1

co_df = pd.DataFrame(co_metric, index=vocab, columns=vocab)
co_df

PMI 계산

total_count = co_df.sum().sum()

# P(word) : 각 단어의 전체 등장 비율
p_word = [sum(row) / total_count for row in co_metric]

# P(context) : 각 단어가 문맥으로 등장하는 비율
p_context = [sum(row) / total_count for row in co_df.T.values]

def calc_pmi(i, j):
    p_wc = co_metric[i][j] / total_count
    if p_wc == 0:
        return 0
    # PMI = log2( P(w,c) / (P(w) * P(c)) )
    # 1e-12 → 수치 안정성을 위한 아주 작은 값 추가
    return math.log2(p_wc / (p_word[i] * p_context[j]) + 1e-12)

# PMI 행렬 계산
pmi_metric = [
    [calc_pmi(i, j) for j in range(len(vocab))]
    for i in range(len(vocab))
]

pmi_df = pd.DataFrame(pmi_metric, index=vocab, columns=vocab)

# 색상 그라디언트로 시각화
pmi_df.style.background_gradient(cmap='Blues')

💡 1e-12 를 더하는 이유
log2(0)-∞ 가 되어 오류가 발생합니다.
극히 작은 값(1e-12)을 더해 수치 안정성을 확보합니다.

전체 흐름 요약

텍스트 → 토큰화 → 단어 사전(vocab) 생성
    ↓
동시출현행렬 생성 (window 범위 내 단어 빈도 카운트)
    ↓
P(w), P(c), P(w,c) 계산
    ↓
PMI = log2( P(w,c) / (P(w) × P(c)) )
    ↓
PPMI = max(PMI, 0)  ← 음수를 0으로 대체

📎 핵심 개념 요약

개념설명
Word2Vec딥러닝 기반 단어 임베딩 — 문맥 기반 의미 반영
CBOW (sg=0)주변 단어 → 중심 단어 예측 (빠름)
Skip-gram (sg=1)중심 단어 → 주변 단어 예측 (일반적 권장)
wv['단어']특정 단어의 임베딩 벡터 반환
wv.most_similar()코사인 유사도 기반 유사 단어 탐색
wv.similarity(w1, w2)두 단어 간 코사인 유사도
wv.index_to_key빈도 순 정렬된 학습 단어 리스트
np.mean(vec, axis=0)문장 내 단어 벡터 평균 → 문장 벡터
동시출현행렬window 범위 내 단어 공동 등장 빈도 행렬
PMI두 단어의 의미적 연관성 측정
PPMIPMI 음수값을 0으로 대체
list.extend(iterable)이터러블을 풀어서 리스트에 추가
sorted(set(sum(tokens, [])))중첩 리스트를 1차원으로 풀고 중복 제거 후 정렬
1e-12log(0) 방지를 위한 수치 안정성 값
df.style.background_gradient(cmap='Blues')DataFrame 값에 색상 그라디언트 적용
model.save() / Word2Vec.load()모델 저장 & 로드
profile
공부 기록

0개의 댓글