Developer's Development
3.3.3 [NLP] 자연어 데이터 준비(자연어 임베딩) 본문
자연어 임베딩 (Embedding)
단어를 고정된 크기의 실수 벡터로 표현하는 방법이다. 즉, 텍스트 데이터를 수치 데이터(벡터)로 변환하여 컴퓨터로 처리할 수 있도록 만드는 기술이다. 임베딩은 단어, 문장, 문서 간의 관계를 벡터 공간에서 나타내며 의미와 문법 정보를 함축한다. 이를 통해 단어 간의 유사도와 의미적 관계를 수치화할 수 있으며, 딥러닝 모델의 입력으로 사용된다.
👉🏻 역할
1. 단어/문장 간 관련도 계산
임베딩은 단어 또는 문장을 벡터로 표현하여 벡터 간의 코사인 유사도, 유클리드 거리 등을 계산해 관련성을 측정할 수 있게 한다.
이를 통해 단어 간 의미적 유사성을 비교할 수 있다.
2. 의미/문법 정보 함축
임베딩은 단어의 문맥적 의미와 문법 정보를 벡터 공간에 반영한다.
예를 들어, "king"과 "queen"은 의미적으로 가까운 벡터로, "king - man + woman"은 "queen"과 유사한 벡터를 나타낸다.
- 벡터화 (Vectorization)
데이터를 고정된 차원의 벡터로 변환하는 과정이다. 텍스트, 이미지, 오디오 등의 비정형 데이터를 수치화하여 머신러닝 및 딥러닝 모델이 이해할 수 있도록 한다. 데이터의 특징을 압축하고, 연산을 최적화하며, 유사도 계산을 가능하게 만든다.
👉🏻 필요성
기계 학습 모델은 숫자 데이터를 입력으로 받기 때문에 텍스트 등의 비정형 데이터를 변환해야 함
벡터화된 데이터는 수학적 연산이 가능하여, 패턴 분석 및 예측 모델 학습에 적합
벡터화 방식에 따라 모델 성능이 크게 달라질 수 있음
👉🏻 종류
- 수동 벡터화 (Feature Engineering 기반): 사람이 직접 특징을 정의하고 수치화 하는 방식
- 자동 벡터화 (딥러닝 기반): 모델이 데이터에서 직접 특징을 추출하여 벡터로 변환하는 방식
👉🏻 한계
고차원 벡터의 경우 계산 비용이 증가할 수 있음 (차원의 저주 문제)
적절한 벡터화 방식을 선택하지 않으면 정보 손실이 발생할 수 있음
특정 벡터화 방식이 모든 문제에 적합한 것은 아니므로, 데이터 특성과 목표에 맞는 방법을 선택해야 함
- 벡터화 방식 (임베딩 기법)
👉🏻 형태소 벡터화
형태소 분석은 단어의 기본 형태(어근)로 분리하여 텍스트를 벡터화하며, 이를 통해 문장의 구조적 정보를 반영할 수 있다.
👉🏻 Bag of Words (BoW)
텍스트에서 각 단어의 출현 빈도를 벡터로 표현하며, 단어 순서는 고려하지 않는다.
👉🏻 TF-IDF
단어의 출현 빈도(TF)와 문서 내 희소성(IDF)을 결합하여 단어의 중요도를 나타낸다.
👉🏻 Word2Vec
1. 단어를 고차원 벡터로 임베딩(= 고차원 벡터 공간에 매핑)하는 방법으로, 의미적 유사성을 반영한다.
2. CBOW와 Skip-gram 두 가지 모델이 있다.
- CBOW(Continuous Bag of Words) : 주변 단어를 이용해 중심 단어를 예측
- Skip-gram : 중심 단어를 이용해 주변 단어를 예측
3. 특징
- 단어의 의미적 유사도를 벡터의 거리로 표현한다.
- 연산을 통해 의미적 관계를 추론할 수 있다.
⭐ Word Embedding
단어를 고정된 차원의 벡터로 변환하는 기술로, 단어 간의 의미적 유사성을 반영하도록 학습된 벡터를 말한다.
의미적 유사성 반영, 밀집 벡터, 문맥 정보 반영, 학습 기반 벡터
👉🏻 Glove (Global Vectors for Word Representation)
단어 간 공기 행렬(= 동시 등장 행렬, co-occurrence matrix)을 사용해 임베딩(= 단어 벡터)을 학습한다.
전역적인 통계 정보를 반영하여 임베딩을 생성한다.
Word2Vec보다 대규모 데이터 학습에 적합하다.
👉🏻 Doc2Vec
문서 단위로 벡터를 생성하며, 문서 유사도 계산에 사용된다. Word2Vec의 확장 모델이다.
👉🏻 LDA
문서의 주제를 발견하는 데 사용되며, 단어가 특정 주제에 속할 확률을 기반으로 문서를 임베딩한다.
👉🏻 FastText
단어 자체뿐만 아니라 n-그램(문자 단위의 서브워드) 정보를 활용하여 임베딩을 학습한다.
단어를 문자 n-그램의 조합으로 표현하고, 회귀 단어와 신조어에 대한 처리 능력을 향상시킨다.
형태소 정보를 반영하여 유사 단어의 임베딩 유사도를 높인다.
OOV(Out-Of-Vocabulary) 문제를 완화한다.
실습 (Word Embedding)
Embedding Vector 시각화 Wevi: https://ronxin.github.io/wevi/
wevi
Training data (context|target): Presets: Update and Restart Update Learning Rate Next 20 100 500 PCA
ronxin.github.io
!pip install gensim
👉🏻 데이터 로드
import gdown
url = 'https://drive.google.com/uc?id=1DCgLPJsfyLGZ99lB-aF8EvpKIWSZYgp4'
output = 'ted_en.xml'
gdown.download(url, output)
# 전처리에 사용할 라이브러리 import
from lxml import etree # xml 데이터를 tree 구조로 파싱 가능
import re
from nltk.tokenize import word_tokenize, sent_tokenize
from nltk.corpus import stopwords
f = open('ted_en.xml', 'r', encoding='utf-8')
xml = etree.parse(f) # xml 파싱
contents = xml.xpath('//content/text()') # content 태그 하위의 text를 가져옴
print(len(contents)) # 2085
# contents[:3] # 3개 정도만 확인 1차) /n 발견!, 2차) (Laghter) 발견!
corpus = '\n'.join(contents)
# 정규표현식을 이용해 (Laghter), (Applause) 등 제거
corpus = re.sub(r'\([^)]*\)', '', corpus)
print(corpus)
sentences = sent_tokenize(corpus)
preprocessed_sentences = []
en_stopwords = stopwords.words('english')
for sentence in sentences:
sentence = sentence.lower()
re.sub(r'[^a-z0-9]', ' ', sentence) # 특수문자 제거
tokens = word_tokenize(sentence)
tokens = [token for token in tokens if token not in en_stopwords]
preprocessed_sentences.append(tokens)
preprocessed_sentences[:5]
# 데이터 전처리 (토큰화, 대소문자 정규화, 불용어 처리)
sentences = sent_tokenize(corpus)
preprocessed_sentences = []
en_stopwords = stopwords.words('english')
for sentence in sentences:
sentence = sentence.lower()
sentence = re.sub(r'[^a-z0-9]', ' ', sentence)
tokens = word_tokenize(sentence)
tokens = [token for token in tokens if token not in en_stopwords]
preprocessed_sentences.append(tokens)
preprocessed_sentences[:5]
👉🏻 Embedding 모델 학습
from gensim.models import Word2Vec
model = Word2Vec(
sentences=preprocessed_sentences, # corpus
vector_size=100, # 임베딩 벡터 차원
sg=0, # 학습 알고리즘 (0:CBOW, 1:Skip-gram)
window=5, # 주변 단어 수 (앞뒤로 n개 사용)
min_count=5 # 최소 빈도
)
model.wv.vectors.shape # (21462, 100)
import pandas as pd
pd.DataFrame(model.wv.vectors, index=model.wv.index_to_key).head(10)
# 학습된 단어 임베딩 저장
model.wv.save_word2vec_format('ted_en_w2v')
# 임베딩 모델 로드
from gensim.models import KeyedVectors
load_model = KeyedVectors.load_word2vec_format('ted_en_w2v')
👉🏻 유사도 계산
# model: Word2Vec
model.wv.most_similar('man')
# model.wv.most_similar('asdfasdfasdfasdf') # 임베딩 벡터에 없는 단어로 조회 시 KeyError 발생
# load_model: KeyedVectors == Word2Vec.wv
load_model.most_similar('man')
model.wv.similarity('man', 'husband') # 0.72449714
model.wv['man']
- 한국어 Word Embedding
NSMC (Naver Sentimente Movie Corpus)
import urllib.request
urllib.request.urlretrieve(
'https://raw.githubusercontent.com/e9t/nsmc/master/ratings.txt',
filename='naver_movie_ratings.txt'
)
ratings_df = pd.read_csv('naver_movie_ratings.txt', sep='\t')
ratings_df.isnull().sum() # document 결측치 8개 확인!
ratings_df = ratings_df.dropna(how='any')
ratings_df['document'][200:300] # 한국어 아닌거 제외하고싶당
ratings_df['document'] = ratings_df['document'].replace(r'[^0-9가-힣ㄱ-ㅎㅏ-ㅣ\s]', '', regex=True)
from konlpy.tag import Okt
from tqdm import tqdm # 진행상황을 시각화해줌
okt = Okt()
ko_stopwords = ['은', '는', '이', '가', '을', '를', '과', '와', '들', '도',
'부터', '까지', '에', '나', '너', '그', '걔', '얘']
preprocessed_data = []
for sentence in tqdm(ratings_df['document']):
tokens = okt.morphs(sentence, stem=True)
tokens = [token for token in tokens if token not in ko_stopwords]
preprocessed_data.append(tokens)
# 100%|██████████| 199992/199992 [08:28<00:00, 393.04it/s]
model = Word2Vec(
sentences=preprocessed_data,
vector_size=100,
window=5,
min_count=5,
sg=0
)
model.wv.vectors.shape # (16841, 100)
# model.wv.most_similar('극장')
# model.wv.similarity('김혜수', '원빈') # 0.7855145
# 모델 저장
model.wv.save_word2vec_format('naver_movie_ratings_w2v')
👉🏻 임베딩 시각화
아래 URL 접속해서 생성된 파일들 넣고 확인하기
https://projector.tensorflow.org/
Embedding projector - visualization of high-dimensional data
Visualize high dimensional data.
projector.tensorflow.org
# metadata.tsv(vector에 대응되는 단어 리스트), tensor.tsv(단어 벡터값) 파일 생성
!python -m gensim.scripts.word2vec2tensor --input naver_movie_ratings_w2v --output naver_movie_ratings_w2v

- 사전 훈련된 임베딩
import gdown
url = 'https://drive.google.com/uc?id=11MWLNUBLOJWpJePTbOJwCtcgEryPGKGj'
output = 'GoogleNews_vecs.bin.gz'
gdown.download(url, output)
from gensim.models import KeyedVectors
google_news_wv = KeyedVectors.load_word2vec_format('GoogleNews_vecs.bin.gz', binary=True)
google_news_wv.vectors.shape # (3000000, 300)
google_news_wv.similarity('man', 'husband') # 0.34499747 /아까보다 작당
google_news_wv.most_similar('man', topn=5) # 유사도가 높은 상위 n개만 가져옴
# 두 리스트 간의 평균 유사도 계산
google_news_wv.n_similarity(['king', 'queen'], ['man', 'woman']) # 0.24791394
google_news_wv.similar_by_word('man', topn=5)
# 단어가 임베딩에 존재하는지 확인
google_news_wv.has_index_for('man') # True
실습 (FastText)
- gensim FastText
# 데이터 오픈 및 전처리
f = open('ted_en.xml', 'r', encoding='utf-8')
xml = etree.parse(f)
contents = xml.xpath('//content/text()')
corpus = '\n'.join(contents)
corpus = re.sub(r'\([^)]*\)', '', corpus)
sentences = sent_tokenize(corpus)
preprocessed_sentences = []
en_stopwords = stopwords.words('english')
for sentence in sentences:
sentence = sentence.lower()
re.sub(r'[^a-z0-9]', ' ', sentence)
tokens = word_tokenize(sentence)
tokens = [token for token in tokens if token not in en_stopwords]
preprocessed_sentences.append(tokens)
from gensim.models import Word2Vec
w2v_model = Word2Vec(
sentences=preprocessed_sentences,
vector_size=100,
window=5,
min_count=5,
sg=0
)
w2v_model.wv.vectors.shape
w2v_model.wv.most_similar('father') # 유사도 높은 단어 확인
# w2v_model.wv.most_similar('luckfather')
# KeyError: "Key 'luckfather' not present in vocabulary" == OOV 이슈
fasttext_model = FastText(
sentences=preprocessed_sentences,
vector_size=100,
window=5,
min_count=5,
sg=0
)
fasttext_model.wv.vectors.shape # (22025, 100)
fasttext_model.wv.most_similar('father')
fasttext_model.wv.most_similar('luckfather') # OOV 이슈 발생하지 않음
# OOV 단어도 subword 기반으로 검색해 vector 반환
fasttext_model.wv['luckyfather']
- fasttext 패키지 활용
!pip install fasttext-wheel
import fasttext
model = fasttext.train_unsupervised(
'naver_movie_ratings.txt',
model='skipgram',
minCount=1,
dim=100,
minn=3, # subword 최소 ngram
maxn=5 # sumword 최대 ngram
)
model.get_word_vector('극장') # '극장'이라는 단어의 벡터 값들 반환
model.get_subwords('영화관')
"""
(['영화관', '<영화', '<영화관', '<영화관>', '영화관', '영화관>', '화관>'],
array([ 2062, 1921845, 1442415, 1378913, 2245977, 1515139, 1352938]))
"""
'LLM' 카테고리의 다른 글
| 3.3.6 [NLP] 자연어 딥러닝(언어 모델링) (0) | 2025.08.22 |
|---|---|
| 3.3.5 [NLP] 자연어 딥러닝(텍스트 분류) (0) | 2025.08.22 |
| 3.3.4 [NLP] 자연어 딥러닝(시퀀스 모델링) (1) | 2025.08.21 |
| 3.3.2 [NLP] 자연어 데이터 준비(텍스트 전처리) (8) | 2025.08.21 |
| 3.3.1 [NLP] 자연어 데이터 준비(개요 및 기초) (8) | 2025.08.18 |