1. Word2Vec의 필요성 (TF-IDF의 한계)
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.metrics.pairwise import cosine_similarity
리뷰1 = "배우의 연기가 정말 훌륭했어요"
리뷰2 = "주인공의 연기력이 정말 뛰어났어요" # 사실상 같은 말
리뷰3 = "스토리가 지루하고 각본이 허술했어요" # 완전히 다른 내용
vec = TfidfVectorizer(analyzer='char_wb', ngram_range=(2,4))
X = vec.fit_transform([리뷰1, 리뷰2, 리뷰3])
유사도 = cosine_similarity(X)
print(f"리뷰1 ↔ 리뷰2 (배우연기 vs 주인공연기력): {유사도[0][1]:.4f}")
print(f"리뷰1 ↔ 리뷰3 (연기 vs 지루한스토리): {유사도[0][2]:.4f}")
- 의미 유사성 파악 불가: TF-IDF는 '배우'와 '주인공'처럼 의미는 같지만 형태가 다른 단어를 완전히 다른 특성으로 처리
- 실험 결과: 사실상 같은 의미인 두 리뷰의 TF-IDF 코사인 유사도는 0.23으로 낮게 나오는 반면, Word2Vec은 이를 0.98 이상의 높은 유사도로 잡아냄
- 한계점: 반의어 구분이나 정교한 문맥 파악은 여전히 부분적 해결에 그침
| 한계 | 예시 | Word2Vec 해결 여부 |
| 의미 유사성 모름 | "배우" ≠ "주인공" (같은 뜻) | ✅ 해결 |
| 반의어 구분 어려움 | "감동적"과 "지루하다"가 유사할 수 있음 | 부분적 해결 |
| 문맥 무시 | "연기가 훌륭하다" vs "연기가 별로다" → 유사도 높음 | ❌ 미해결 (BERT가 해결) |
2. 핵심 개념: 분포 가설(Distributional Hypothesis)
- 아이디어: "비슷한 문맥에서 함께 등장하는 단어는 비슷한 의미를 가진다"는 언어학적 가설을 신경망으로 구현한 것
- 학습 데이터 설계: '연기', '배우', '감동'이 한 문장에 자주 묶여 등장하면 Word2Vec은 이들을 서로 가까운 벡터로 학습
문장1: [주인공, 연기, 훌륭하다, 감동, 자연스럽다]
문장2: [배우, 연기력, 뛰어나다, 캐릭터, 몰입]
문장3: [주연, 조연, 연기, 앙상블, 완벽하다]
→ "연기"와 "배우"가 비슷한 문맥(훌륭하다, 감동, 몰입)에서 등장
→ Word2Vec: 두 단어를 비슷한 벡터로 학습
3. 학습 방식: Skip-gram vs CBOW
- CBOW:
- 주변 단어들로 중심 단어를 예측
- 속도가 빠르고 대용량 데이터에 적합
- Skip-gram:
- 중심 단어 하나로 주변 단어들을 예측
- 학습은 더 느리지만, 데이터가 적거나 희귀 단어를 처리할 때 성능이 더 좋아 실무에서 권장
| CBOW | Skip-gram | |
| 원리 | 주변 단어 → 중심 단어 예측 | 중심 단어 → 주변 단어 예측 |
| 속도 | 빠름 | 느림 |
| 희귀 단어 | 약함 | 강함 |
| 일반 권장 | 대용량 데이터 | 소용량 or 희귀어 포함 |
| gensim 설정 | sg=0 (기본값) | sg=1 |
4. 단어 벡터와 유사도 측정
# 긍정 단어끼리
print(f"감동 ↔ 여운: {model.wv.similarity('감동','여운'):.4f}")
print(f"훌륭하다 ↔ 완벽: {model.wv.similarity('훌륭하다','완벽하다'):.4f}")
print(f"연기 ↔ 배우: {model.wv.similarity('연기','배우'):.4f}")
# 부정 단어끼리
print(f"지루하다 ↔ 별로: {model.wv.similarity('지루하다','별로'):.4f}")
print(f"실망 ↔ 아깝다: {model.wv.similarity('실망','아깝다'):.4f}")
# 긍정 ↔ 부정 (낮아야 함)
print(f"감동 ↔ 지루하다: {model.wv.similarity('감동','지루하다'):.4f}")
- 밀집 벡터 (Dense Vector):
- 모든 차원이 의미 있는 실수로 채워진 고정 크기 배열
- 대부분 0인 희소 벡터(TF-IDF)보다 효율적
- 코사인 유사도 (Cosine Similarity):
- 벡터의 크기(리뷰의 길이)는 무시하고 방향만 비교하여 내용의 유사성을 측정
def 코사인(a, b):
return np.dot(a, b) / (np.linalg.norm(a) * np.linalg.norm(b))
# model.wv.similarity()와 동일한 결과
직접 = 코사인(model.wv["연기"], model.wv["배우"])
내장 = model.wv.similarity("연기", "배우")
# 둘 다 같은 값
5. 모델 주요 파라미터 및 튜닝
model = Word2Vec(
sentences=문장들,
vector_size=30,
window=4,
min_count=1,
epochs=500,
seed=42
)
- vector_size:
- 벡터 차원 수
- 실습용 소규모 데이터는 20~50, 실전용 대용량은 200~300 권장
- window:
- 문맥 범위
- 작으면 문법적 관계, 크면 의미적 관계 학습에 유리
- min_count:
- 최소 등장 횟수
- 실습 데이터는 1을 사용해 모든 단어를 포함시키고, 대용량 데이터는 5 이상으로 설정해 노이즈를 줄임
- epochs:
- 데이터가 적을수록 반복 학습 횟수(100~500회)를 늘려야 수렴도가 높아짐
- seed:
- 재현성 보장
- seed를 고정하면 매번 같은 초기값으로 시작 → 동일한 결과 재현 가능
6. 유사 단어 검색
검색_단어들 = ["왕", "영상미", "실망", "추천"]
for 기준 in 검색_단어들:
유사어 = model.wv.most_similar(기준, topn=4)
print(f"'{기준}' 유사어:", [w for w,_ in 유사어])
- 내부 동작
- 입력 단어 ‘기준’(왕, 영상미, 실망, 추천)의 벡터를 가져옴
- 어휘에 있는 모든 단어의 벡터와 코사인 유사도 계산
- 유사도 내림차순 정렬
- 상위 4개 반환
- OOV (Out-Of-Vocabulary):
- 학습하지 않은 단어를 만나면 에러가 발생하는 문제
- 데이터를 늘리거나 fastText를 사용하여 보완
7. 문서 벡터화 및 분류기 연결
def 문서_벡터(단어_리스트, model):
벡터들 = [model.wv[단어] for 단어 in 단어_리스트 if 단어 in model.wv]
if len(벡터들) == 0:
return np.zeros(model.vector_size) # 어휘에 없으면 영벡터
return np.mean(벡터들, axis=0) # 모든 단어 벡터의 평균
- 단어 벡터 평균: 리뷰 내 모든 단어 벡터의 평균을 구해 리뷰 전체를 하나의 벡터(문서 벡터)로 변환
8. 일반적인 경향
| 상황 | 유리한 방법 |
| 데이터 적음 (수백 개) | TF-IDF |
| 데이터 많음 (수만 개↑) | Word2Vec |
| 어휘 변화 많음 | Word2Vec |
| 빠른 구현 필요 | TF-IDF |
| 의미 유사성 중요 | Word2Vec |
- 사전 학습 모델 (Pre-trained): 뉴스나 위키피디아로 이미 학습된 한국어 Word2Vec 모델을 가져와 내 데이터에 맞춰 추가 학습(Fine-tuning)하는 것이 효율적
모델 학습 데이터 특징 Korean Word2Vec (뉴스) 국내 뉴스 기사 시사 어휘 풍부 fastText 한국어 위키피디아 OOV 처리 가능 KR-ELECTRA 대규모 코퍼스 문맥 이해 (BERT 계열)
9. 모델 저장과 로드
# 저장
model.save("/tmp/movie_review_w2v.model")
# 로드
불러온 = Word2Vec.load("/tmp/movie_review_w2v.model")
# 동일한지 확인
print(model.wv.similarity("연기", "배우"))
print(불러온.wv.similarity("연기", "배우"))
# → 완전히 같은 값
마무리
오늘 배운 흐름
TF-DIF 한계: "배우" ≠ "주인공"
↓
Word2Vec 핵심 아이디어
"비슷한 문맥 = 비슷한 벡터"
↓
학습 (3줄)
model = Word2Vec(sentences=..., vector_size=30, window=4, epochs=500)
↓
유사 단어 검색
model.wv.most_similar("연기", topn=5)
↓
두 단어 유사도
model.wv.similarity("연기", "배우") # 0.99
↓
어휘 확인 (OOV 방지)
if 단어 in model.wv:
↓
문서 벡터 (단어 벡터 평균)
np.mean([model.wv[단어] for 단어 in 리뷰], axis=0)
↓
4주차 분류기 연결
LogisticRegression().fit(문서벡터, 레이블)
↓
모델 저장 / 로드
model.save() / Word2Vec.load()
핵심 개념 한 줄 요약
| 개념 | 한 줄 설명 | 현실 팁 |
| 분포 가설 | 비슷한 문맥 = 비슷한 의미 | Word2Vec의 이론적 토대 |
| Dense Vector | 모든 차원이 의미 있는 벡터 | TF-IDF(Sparse)와 반대 |
| Skip-gram | 중심 단어로 주변 예측 | 소규모 데이터에 권장 |
| vector_size | 벡터 차원 수 | 데이터 적으면 30~50 |
| window | 문맥 범위 | 보통 5 사용 |
| epochs | 반복 학습 횟수 | 데이터 적으면 늘리기 |
| most_similar | 유사 단어 검색 | OOV 먼저 확인! |
| OOV | 학습 데이터에 없는 단어 | KeyError 주의 |
| 문서 벡터 | 단어 벡터 평균 | 단어 순서 무시하는 한계 있음 |
핵심 코드 치트시트
from gensim.models import Word2Vec
import numpy as np
# 학습
model = Word2Vec(sentences=[[단어들], ...],
vector_size=100, window=5,
min_count=2, epochs=10, seed=42)
# 단어 벡터
model.wv["연기"]
# 유사 단어
model.wv.most_similar("연기", topn=5)
# 두 단어 유사도
model.wv.similarity("연기", "배우")
# OOV 확인
if "단어" in model.wv:
v = model.wv["단어"]
# 문서 벡터
def 문서_벡터(단어_리스트, model):
벡터들 = [model.wv[w] for w in 단어_리스트 if w in model.wv]
return np.mean(벡터들, axis=0) if 벡터들 else np.zeros(model.vector_size)
# 저장 / 로드
model.save("model.model")
model = Word2Vec.load("model.model")
AI_Agent/Text _Analysis_and_LLM/[BDA]_5주차_단어임베딩.ipynb at main · 427paul/AI_Agent
Contribute to 427paul/AI_Agent development by creating an account on GitHub.
github.com
'AI Agent > 텍스트 분석과 LLM' 카테고리의 다른 글
| 7. Transformer — Attention Is All You Need (0) | 2026.07.09 |
|---|---|
| 6. 텍스트 분석을 위한 딥러닝 기초 (1) | 2026.06.30 |
| 4. 텍스트 분류 — 긍정/부정 자동 판단 (0) | 2026.06.09 |
| 2. 텍스트 전처리(Text Preprocessing) (0) | 2026.05.26 |
| 1. 텍스트 분석 입문 및 환경 설정 (0) | 2026.05.24 |