NLP 자연어 처리 심화
Transformer부터 LLM 파인튜닝까지 현대 NLP 기술을 심화 학습합니다. BERT, GPT, LangChain 등을 실습합니다.
학습 항목
본 콘텐츠의 이미지 및 내용은 AI로 생성되었습니다.
이미지 로딩 중...
텍스트 표현 임베딩 완벽 가이드
컴퓨터가 텍스트를 이해하는 방법인 임베딩에 대해 알아봅니다. One-hot Encoding의 한계부터 Word2Vec, GloVe, FastText, 그리고 한국어 임베딩까지 실무에서 바로 활용할 수 있는 내용을 다룹니다.
목차
1. One-hot Encoding 한계
김개발 씨는 자연어 처리 프로젝트에 처음 투입되었습니다. 텍스트 데이터를 모델에 넣으려면 숫자로 바꿔야 한다는 걸 알게 되었고, 가장 먼저 배운 것이 바로 One-hot Encoding이었습니다.
하지만 막상 적용해보니 이상한 점들이 보이기 시작했습니다.
One-hot Encoding은 단어를 0과 1로만 이루어진 벡터로 표현하는 가장 기초적인 방법입니다. 마치 출석부에서 특정 학생만 체크하는 것처럼, 해당 단어의 위치만 1이고 나머지는 모두 0입니다.
단순하지만 치명적인 단점들이 숨어 있습니다.
다음 코드를 살펴봅시다.
import numpy as np
# 단어 사전 정의
vocab = ['왕', '여왕', '남자', '여자', '사과', '바나나']
word_to_idx = {word: idx for idx, word in enumerate(vocab)}
# One-hot Encoding 함수
def one_hot_encode(word, vocab_size):
vector = np.zeros(vocab_size)
vector[word_to_idx[word]] = 1
return vector
# '왕'과 '여왕'의 벡터
king_vec = one_hot_encode('왕', len(vocab)) # [1, 0, 0, 0, 0, 0]
queen_vec = one_hot_encode('여왕', len(vocab)) # [0, 1, 0, 0, 0, 0]
# 코사인 유사도 계산 - 결과는 0 (유사성 없음)
similarity = np.dot(king_vec, queen_vec)
print(f"왕과 여왕의 유사도: {similarity}") # 0.0
김개발 씨는 입사 후 첫 NLP 프로젝트를 맡게 되었습니다. 사용자 리뷰를 분석해서 긍정인지 부정인지 판단하는 감성 분석 모델을 만들어야 했습니다.
텍스트를 숫자로 바꾸는 방법을 검색하다가 One-hot Encoding이라는 것을 알게 되었습니다. "이거 정말 간단하네요!" 김개발 씨는 기뻐했습니다.
단어마다 고유한 위치를 부여하고, 그 위치만 1로 표시하면 되니까요. 마치 학교 출석부에서 자기 이름 옆에만 동그라미를 치는 것과 같았습니다.
하지만 곧 이상한 점을 발견했습니다. "좋다"와 "훌륭하다"는 비슷한 의미인데, One-hot으로 표현하면 완전히 다른 벡터가 됩니다.
코사인 유사도를 계산해보면 0이 나옵니다. 컴퓨터 입장에서는 "좋다"와 "나쁘다"의 관계나, "좋다"와 "훌륭하다"의 관계나 똑같이 아무런 연관이 없는 것으로 보이는 겁니다.
선배 개발자 박시니어 씨가 지나가다 김개발 씨의 고민을 들었습니다. "아, One-hot의 가장 큰 문제를 발견했구나.
의미적 유사성을 표현할 수 없다는 거야." 박시니어 씨는 화이트보드에 그림을 그리며 설명을 이어갔습니다. "문제가 하나 더 있어.
만약 단어가 10만 개라면 어떻게 될까?" 김개발 씨는 잠시 생각했습니다. "벡터 하나당 10만 차원이 되겠네요...
그중에 1은 딱 하나고 나머지 99,999개는 전부 0이고요." "맞아. 이걸 희소 벡터라고 해.
저장 공간도 낭비되고, 계산도 비효율적이지." 박시니어 씨가 고개를 끄덕였습니다. 이것이 바로 차원의 저주입니다.
차원이 높아질수록 데이터 포인트들 사이의 거리가 비슷해지고, 모델이 패턴을 학습하기 어려워집니다. 마치 넓은 사막에 사람들이 한 명씩 흩어져 있으면 누가 누구랑 가까운지 판단하기 힘든 것과 같습니다.
또 다른 문제는 단어 순서나 문맥을 무시한다는 점입니다. "개가 사람을 물었다"와 "사람이 개를 물었다"를 One-hot으로 표현하면 단어 빈도만 같을 뿐, 의미의 차이를 전혀 알 수 없습니다.
그렇다면 One-hot Encoding은 쓸모없는 걸까요? 그렇지는 않습니다.
범주형 데이터가 적을 때, 예를 들어 성별이나 요일처럼 종류가 한정적일 때는 여전히 유용합니다. 하지만 자연어처럼 어휘가 방대하고 의미적 관계가 중요한 분야에서는 분명한 한계가 있습니다.
김개발 씨는 고개를 끄덕였습니다. "그럼 단어의 의미까지 담을 수 있는 방법은 없을까요?" 박시니어 씨가 미소를 지었습니다.
"있지. 그게 바로 임베딩이야.
다음에 Word2Vec에 대해 알려줄게."
실전 팁
💡 - One-hot Encoding은 어휘 크기가 작고 의미적 관계가 중요하지 않을 때만 사용하세요
- 대규모 텍스트 데이터에는 반드시 밀집 벡터 임베딩을 사용해야 합니다
2. Word2Vec CBOW Skipgram
박시니어 씨의 설명을 들은 김개발 씨는 임베딩에 대해 더 알고 싶어졌습니다. 다음 날, 박시니어 씨는 약속대로 Word2Vec에 대해 설명해주었습니다.
"단어의 의미를 벡터에 담을 수 있다니, 정말 신기해요!"
Word2Vec은 Google에서 2013년에 발표한 혁신적인 단어 임베딩 기법입니다. 핵심 아이디어는 "비슷한 문맥에서 등장하는 단어는 비슷한 의미를 가진다"는 분포 가설입니다.
CBOW와 Skip-gram이라는 두 가지 학습 방식이 있으며, 단어를 저차원의 밀집 벡터로 표현합니다.
다음 코드를 살펴봅시다.
from gensim.models import Word2Vec
# 학습용 문장 데이터
sentences = [
['왕', '은', '왕관', '을', '쓴다'],
['여왕', '은', '왕관', '을', '쓴다'],
['왕자', '는', '왕', '의', '아들', '이다'],
['공주', '는', '여왕', '의', '딸', '이다'],
['남자', '는', '강하다'],
['여자', '는', '아름답다']
]
# Skip-gram 모델 학습 (sg=1)
model = Word2Vec(sentences, vector_size=100, window=2,
min_count=1, sg=1, epochs=100)
# 단어 벡터 확인
king_vector = model.wv['왕']
print(f"'왕' 벡터 차원: {king_vector.shape}") # (100,)
# 유사한 단어 찾기
similar_words = model.wv.most_similar('왕', topn=3)
print(f"'왕'과 유사한 단어: {similar_words}")
# 단어 간 연산: 왕 - 남자 + 여자 = ?
result = model.wv.most_similar(positive=['왕', '여자'],
negative=['남자'], topn=1)
박시니어 씨는 커피를 한 모금 마시고 설명을 시작했습니다. "Word2Vec의 핵심 아이디어는 아주 간단해.
주변에 어떤 단어가 있는지 보면 그 단어의 의미를 알 수 있다는 거야." 김개발 씨는 고개를 갸웃했습니다. "무슨 뜻이에요?" "예를 들어볼게.
'나는 오늘 아침에 __을 마셨다'라는 문장에서 빈칸에 들어갈 단어는 뭘까?" "커피요? 아니면 우유?" "맞아.
우리가 빈칸을 채울 수 있는 이유는 주변 문맥을 보고 있기 때문이야. Word2Vec도 바로 이 원리를 사용해." 박시니어 씨는 화이트보드에 두 가지 그림을 그렸습니다.
"Word2Vec에는 두 가지 방식이 있어. 첫 번째는 **CBOW(Continuous Bag of Words)**야." CBOW는 주변 단어들을 보고 중심 단어를 예측합니다.
마치 빈칸 채우기 문제와 같습니다. "오늘", "아침에", "마셨다"라는 주변 단어들을 입력으로 넣으면, 모델이 "커피"라는 중심 단어를 예측하도록 학습하는 것입니다.
"두 번째는 Skip-gram이야. CBOW랑 정반대지." 박시니어 씨가 말을 이었습니다.
Skip-gram은 중심 단어 하나를 보고 주변 단어들을 예측합니다. "커피"라는 단어를 넣으면 "마시다", "아침", "뜨겁다" 같은 주변에 나올 법한 단어들을 예측하는 것입니다.
김개발 씨가 물었습니다. "둘 중에 뭐가 더 좋아요?" "상황에 따라 달라.
CBOW는 학습 속도가 빠르고 자주 나오는 단어에 좋아. 반면 Skip-gram은 드물게 나오는 단어도 잘 학습해.
보통 데이터가 충분하면 Skip-gram을 많이 써." 가장 놀라운 점은 단어 벡터로 의미 연산이 가능하다는 것입니다. "왕 - 남자 + 여자"를 계산하면 "여왕"에 가까운 벡터가 나옵니다.
성별이라는 개념이 벡터 공간에서 일정한 방향으로 표현되기 때문입니다. 박시니어 씨는 덧붙였습니다.
"이게 가능한 이유는 모델이 대량의 텍스트를 학습하면서 단어 간의 관계를 벡터 공간에 인코딩했기 때문이야." Word2Vec의 벡터 차원은 보통 100에서 300 사이를 사용합니다. One-hot의 수만 차원에 비하면 훨씬 작지만, 이 안에 풍부한 의미 정보가 담깁니다.
이런 벡터를 **밀집 벡터(Dense Vector)**라고 부릅니다. 김개발 씨의 눈이 반짝였습니다.
"정말 신기하네요! 그런데 학습 데이터는 어디서 구해요?" "위키피디아나 뉴스 기사 같은 대용량 텍스트 코퍼스를 사용해.
아니면 이미 학습된 모델을 가져다 쓸 수도 있어. 그건 나중에 알려줄게."
실전 팁
💡 - 데이터가 적으면 CBOW, 충분하면 Skip-gram을 선택하세요
- window 크기가 클수록 넓은 문맥을 고려하지만 학습 시간이 길어집니다
- vector_size는 100~300 사이가 일반적입니다
3. GloVe와 FastText
Word2Vec을 익힌 김개발 씨는 더 다양한 임베딩 방법이 있는지 궁금해졌습니다. 박시니어 씨는 GloVe와 FastText를 소개해주었습니다.
"각각 장단점이 있으니까 상황에 맞게 골라 쓰면 돼."
**GloVe(Global Vectors)**는 Stanford에서 개발한 방법으로, 전체 코퍼스의 통계 정보를 활용합니다. FastText는 Facebook에서 만들었으며, 단어를 더 작은 조각인 subword로 쪼개서 학습합니다.
두 방법 모두 Word2Vec의 한계를 보완합니다.
다음 코드를 살펴봅시다.
# GloVe 사전 학습 모델 사용
import gensim.downloader as api
# GloVe 모델 로드 (처음 실행시 다운로드)
glove_model = api.load('glove-wiki-gigaword-100')
# 단어 유사도 확인
print(glove_model.most_similar('computer', topn=5))
# FastText 모델 학습
from gensim.models import FastText
sentences = [
['자연어', '처리', '는', '어렵다'],
['자연어', '이해', '는', '중요하다'],
['딥러닝', '으로', '자연어', '를', '처리한다']
]
# FastText 모델 (subword 정보 활용)
ft_model = FastText(sentences, vector_size=100, window=3,
min_count=1, min_n=2, max_n=5)
# OOV(미등록 단어)도 벡터 생성 가능!
oov_vector = ft_model.wv['자연어처리'] # 학습에 없던 단어
print(f"OOV 단어 벡터 생성 성공: {oov_vector.shape}")
"Word2Vec만 알면 충분한 거 아니에요?" 김개발 씨가 물었습니다. 박시니어 씨가 고개를 저었습니다.
"Word2Vec은 훌륭하지만 한계가 있어. 첫째, 주변 단어만 보고 학습하기 때문에 전체 코퍼스의 통계 정보를 제대로 활용하지 못해." GloVe는 이 문제를 해결합니다.
먼저 전체 코퍼스에서 **단어 동시 출현 행렬(Co-occurrence Matrix)**을 만듭니다. 이 행렬은 두 단어가 얼마나 자주 함께 나타나는지를 기록합니다.
예를 들어 "얼음"과 "차갑다"는 자주 함께 등장하지만, "얼음"과 "뜨겁다"는 거의 같이 나오지 않습니다. GloVe는 이런 전역적인 통계 정보를 학습에 반영합니다.
"GloVe의 장점은 학습이 빠르고 안정적이라는 거야. 대신 미리 만들어진 동시 출현 행렬이 필요하지." 박시니어 씨가 설명했습니다.
김개발 씨가 또 질문했습니다. "그럼 FastText는 뭐가 다른가요?" "Word2Vec의 또 다른 문제점을 생각해봐.
만약 '스마트폰'이라는 단어가 학습 데이터에 없었다면?" "아, 벡터를 만들 수 없겠네요!" "맞아. 이걸 OOV(Out of Vocabulary) 문제라고 해.
FastText는 이걸 해결해." FastText는 단어를 더 작은 조각인 subword로 분해합니다. 예를 들어 "스마트폰"을 "스마", "마트", "트폰" 등으로 쪼갭니다.
각 조각의 벡터를 합쳐서 전체 단어의 벡터를 만듭니다. 이 방식의 장점은 명확합니다.
학습 때 본 적 없는 "스마트워치"라는 단어가 들어와도, "스마", "마트" 같은 조각들은 이미 학습되어 있으므로 합리적인 벡터를 만들어낼 수 있습니다. "특히 형태소가 풍부한 언어에서 FastText가 빛을 발해.
한국어처럼 '먹다', '먹었다', '먹고', '먹으면' 같이 변형이 많은 언어 말이야." 박시니어 씨가 덧붙였습니다. 김개발 씨는 정리했습니다.
"GloVe는 전역 통계를 쓰고, FastText는 subword로 OOV를 해결하는군요!" "정확해. 실무에서는 프로젝트 특성에 맞게 선택하면 돼.
새로운 단어가 많이 등장하는 도메인이면 FastText가 좋고, 안정적인 성능이 필요하면 GloVe 사전 학습 모델을 쓰는 것도 방법이야." 참고로 GloVe는 Stanford에서 다양한 크기의 사전 학습 모델을 제공합니다. Wikipedia와 뉴스 기사로 학습된 모델을 바로 다운받아 사용할 수 있습니다.
실전 팁
💡 - OOV 문제가 심각한 도메인에서는 FastText를 우선 고려하세요
- GloVe 사전 학습 모델은 빠르게 시작하기 좋습니다
- FastText의 min_n, max_n 파라미터로 subword 크기를 조절할 수 있습니다
4. 임베딩 시각화 tSNE
김개발 씨는 임베딩 벡터가 제대로 학습되었는지 확인하고 싶었습니다. 100차원짜리 벡터를 어떻게 눈으로 볼 수 있을까요?
박시니어 씨가 t-SNE라는 시각화 기법을 알려주었습니다.
**t-SNE(t-distributed Stochastic Neighbor Embedding)**는 고차원 데이터를 2차원이나 3차원으로 축소하여 시각화하는 기법입니다. 비슷한 벡터는 가까이, 다른 벡터는 멀리 배치하여 임베딩의 품질을 직관적으로 확인할 수 있습니다.
다음 코드를 살펴봅시다.
import numpy as np
from sklearn.manifold import TSNE
import matplotlib.pyplot as plt
# 예시: Word2Vec 모델의 단어 벡터 추출
words = ['왕', '여왕', '왕자', '공주', '남자', '여자',
'사과', '바나나', '오렌지', '포도']
vectors = np.array([model.wv[word] for word in words])
# t-SNE로 2차원 축소
tsne = TSNE(n_components=2, random_state=42,
perplexity=5, n_iter=1000)
vectors_2d = tsne.fit_transform(vectors)
# 시각화
plt.figure(figsize=(10, 8))
plt.scatter(vectors_2d[:, 0], vectors_2d[:, 1])
# 단어 라벨 표시
for i, word in enumerate(words):
plt.annotate(word, xy=(vectors_2d[i, 0], vectors_2d[i, 1]),
fontsize=12)
plt.title('Word Embedding Visualization with t-SNE')
plt.savefig('embedding_tsne.png', dpi=150)
plt.show()
"100차원을 어떻게 눈으로 봐요?" 김개발 씨가 당황한 표정으로 물었습니다. 박시니어 씨가 웃으며 대답했습니다.
"당연히 직접 볼 수는 없지. 그래서 차원 축소 기법을 사용하는 거야.
가장 많이 쓰이는 게 바로 t-SNE야." t-SNE를 이해하려면 먼저 차원 축소의 개념을 알아야 합니다. 100차원의 데이터를 2차원으로 줄인다는 것은, 정보를 일부 잃더라도 데이터의 핵심 구조를 보존하면서 낮은 차원으로 표현한다는 뜻입니다.
"비유를 들어볼게. 서울 지하철 노선도 본 적 있지?" 박시니어 씨가 물었습니다.
"네, 당연히요." "실제 지하철 노선은 3차원 공간에서 구불구불하게 달려. 하지만 노선도는 2차원 평면에 그려져 있잖아.
실제 거리나 방향은 왜곡되지만, 어떤 역이 어떤 역과 연결되어 있는지라는 핵심 정보는 보존돼. t-SNE도 비슷해." t-SNE의 핵심 아이디어는 이렇습니다.
고차원에서 가까운 점들은 저차원에서도 가까이, 먼 점들은 멀리 배치합니다. 이를 위해 확률 분포를 사용하는데, 특히 꼬리가 두꺼운 t-분포를 사용해서 데이터 포인트들이 뭉치지 않고 잘 펼쳐지도록 합니다.
김개발 씨는 코드를 실행해보았습니다. 화면에 점들이 흩어진 그래프가 나타났는데, 신기하게도 "왕", "여왕", "왕자", "공주"가 한쪽에 모여 있고, "사과", "바나나", "오렌지", "포도"가 다른 쪽에 모여 있었습니다.
"우와, 정말 비슷한 단어끼리 모여있네요!" "그렇지. 이렇게 시각화하면 임베딩이 의미를 제대로 학습했는지 한눈에 확인할 수 있어." t-SNE를 사용할 때 주의할 점도 있습니다.
perplexity 파라미터는 각 점이 몇 개의 이웃을 고려할지를 결정합니다. 보통 5에서 50 사이를 사용하며, 데이터 크기에 따라 조절해야 합니다.
또한 t-SNE는 전역적인 구조보다 지역적인 구조를 잘 보존합니다. 즉, 가까운 점들의 관계는 잘 유지하지만, 멀리 떨어진 군집 간의 거리는 의미가 없을 수 있습니다.
"t-SNE 외에 UMAP이라는 방법도 있어. 더 빠르고 전역 구조도 어느 정도 보존해.
요즘은 UMAP을 더 많이 쓰는 추세야." 박시니어 씨가 덧붙였습니다. 김개발 씨는 직접 여러 파라미터를 바꿔가며 실험해보았습니다.
시각화를 통해 임베딩의 품질을 확인하니, 모델을 개선할 방향이 눈에 보이기 시작했습니다.
실전 팁
💡 - perplexity는 데이터 크기의 제곱근 정도로 시작해보세요
- t-SNE는 랜덤 시드에 따라 결과가 달라지므로 random_state를 고정하세요
- 대규모 데이터에서는 UMAP이 더 효율적입니다
5. 사전 학습 임베딩 활용
김개발 씨는 임베딩을 직접 학습하려니 데이터도 많이 필요하고 시간도 오래 걸렸습니다. 박시니어 씨가 더 효율적인 방법을 알려주었습니다.
"이미 학습된 임베딩을 가져다 쓰면 돼!"
**사전 학습 임베딩(Pre-trained Embedding)**은 대규모 코퍼스로 미리 학습된 단어 벡터입니다. 직접 학습할 필요 없이 바로 사용하거나, 자신의 데이터로 추가 학습(Fine-tuning)할 수 있습니다.
시간과 컴퓨팅 자원을 크게 절약할 수 있습니다.
다음 코드를 살펴봅시다.
import gensim.downloader as api
from tensorflow.keras.layers import Embedding
from tensorflow.keras.preprocessing.text import Tokenizer
import numpy as np
# 사전 학습된 GloVe 모델 로드
glove = api.load('glove-wiki-gigaword-100')
# 내 데이터의 단어 사전
texts = ['딥러닝은 재미있다', '자연어 처리를 배운다']
tokenizer = Tokenizer()
tokenizer.fit_on_texts(texts)
word_index = tokenizer.word_index
# 임베딩 행렬 생성
vocab_size = len(word_index) + 1
embedding_dim = 100
embedding_matrix = np.zeros((vocab_size, embedding_dim))
for word, idx in word_index.items():
if word in glove:
embedding_matrix[idx] = glove[word]
# Keras Embedding 레이어에 적용 (가중치 고정)
embedding_layer = Embedding(
vocab_size, embedding_dim,
weights=[embedding_matrix],
trainable=False # Fine-tuning 시 True로 변경
)
"직접 학습하려면 데이터가 얼마나 필요해요?" 김개발 씨가 물었습니다. 박시니어 씨가 대답했습니다.
"좋은 품질의 임베딩을 얻으려면 최소 수억 개의 단어가 필요해. 구글이나 페이스북은 수십억 개의 단어로 학습하지." 김개발 씨는 한숨을 쉬었습니다.
"저한테는 그런 데이터가 없는데요..." "걱정 마. 그래서 사전 학습 임베딩을 쓰는 거야.
대기업이나 연구 기관에서 이미 학습해둔 걸 공개하거든." 사전 학습 임베딩을 사용하면 여러 장점이 있습니다. 첫째, 시간과 비용을 절약할 수 있습니다.
대규모 학습에는 고성능 GPU로도 며칠이 걸릴 수 있습니다. 둘째, 풍부한 언어 지식을 바로 활용할 수 있습니다.
Wikipedia 전체나 뉴스 기사 수년 치를 학습한 임베딩에는 우리가 직접 모을 수 없는 방대한 언어 패턴이 담겨 있습니다. "사용 방법은 크게 두 가지야." 박시니어 씨가 설명을 이어갔습니다.
첫 번째는 그대로 사용하는 것입니다. 임베딩 레이어의 가중치를 고정(trainable=False)하고 사전 학습 벡터를 그대로 씁니다.
데이터가 적을 때 과적합을 방지할 수 있습니다. 두 번째는 Fine-tuning입니다.
사전 학습 임베딩을 초기값으로 설정하되, 학습 과정에서 가중치가 업데이트되도록 합니다. 자신의 도메인에 맞게 임베딩을 조정할 수 있습니다.
"언제 어떤 방법을 써야 해요?" 김개발 씨가 물었습니다. "데이터가 적으면 고정, 많으면 Fine-tuning이 일반적이야.
그리고 도메인이 일반적이면 고정, 특수하면 Fine-tuning을 고려해봐." 예를 들어 의료 분야 텍스트를 다룬다면, 일반 임베딩에서 "heart"는 감정적 의미가 강할 수 있지만, 의료 데이터로 Fine-tuning하면 신체 기관으로서의 의미가 강화됩니다. 대표적인 사전 학습 임베딩으로는 Word2Vec(Google News 300차원), GloVe(Wikipedia, Common Crawl), FastText(157개 언어) 등이 있습니다.
모두 무료로 다운로드할 수 있습니다. 김개발 씨는 바로 GloVe 모델을 다운받아 자신의 프로젝트에 적용해보았습니다.
직접 학습한 것보다 훨씬 좋은 성능이 나왔습니다. "이렇게 쉬울 줄 몰랐어요!" 박시니어 씨가 미소 지었습니다.
"거인의 어깨 위에 서는 거지. 이미 있는 걸 잘 활용하는 것도 실력이야."
실전 팁
💡 - 데이터가 1만 개 미만이면 임베딩을 고정하는 것이 안전합니다
- gensim.downloader로 다양한 사전 학습 모델을 쉽게 다운받을 수 있습니다
- 특수 도메인에서는 반드시 Fine-tuning을 고려하세요
6. 한국어 임베딩
김개발 씨는 한 가지 의문이 생겼습니다. "지금까지 배운 건 영어 위주인데, 한국어는 어떻게 해요?" 한국어는 영어와 다른 특성이 많아서 별도의 접근이 필요했습니다.
한국어 임베딩은 한국어의 특성인 교착어 구조, 다양한 어미 변화, 띄어쓰기 불규칙성 등을 고려해야 합니다. 형태소 분석기를 사용한 전처리가 중요하며, KoNLPy, Mecab 등의 도구와 함께 한국어 전용 사전 학습 임베딩을 활용합니다.
다음 코드를 살펴봅시다.
from konlpy.tag import Mecab
from gensim.models import Word2Vec
import urllib.request
# 한국어 형태소 분석기 (Mecab)
mecab = Mecab()
# 문장을 형태소로 분리
sentence = "자연어 처리는 정말 재미있습니다"
morphs = mecab.morphs(sentence)
print(f"형태소 분리: {morphs}")
# ['자연어', '처리', '는', '정말', '재미있', '습니다']
# 한국어 코퍼스로 Word2Vec 학습
korean_sentences = [
mecab.morphs("오늘 날씨가 좋습니다"),
mecab.morphs("내일 비가 올 것 같습니다"),
mecab.morphs("자연어 처리를 공부합니다"),
mecab.morphs("딥러닝은 재미있습니다")
]
model = Word2Vec(korean_sentences, vector_size=100,
window=3, min_count=1, sg=1)
# 사전 학습된 한국어 임베딩 사용 (예시)
# 실제로는 다음 URL에서 다운로드:
# https://github.com/Kyubyong/wordvectors (한국어 Word2Vec)
# https://github.com/ratsgo/embedding (다양한 한국어 임베딩)
박시니어 씨가 말했습니다. "좋은 질문이야.
한국어는 영어랑 완전히 다른 특성이 있거든." 첫 번째 차이점은 교착어라는 점입니다. 영어의 "I"는 항상 "I"지만, 한국어의 "나"는 "나는", "나를", "나에게", "나의" 등 다양한 형태로 변합니다.
단순히 띄어쓰기로 단어를 나누면 이런 변형들이 모두 다른 단어로 취급됩니다. "그래서 형태소 분석이 필요한 거야." 박시니어 씨가 설명했습니다.
"형태소는 의미를 가진 가장 작은 단위야. '나에게'를 '나', '에게'로 분리하면 '나'라는 핵심 의미를 파악할 수 있지." 한국어 형태소 분석기로는 Mecab, Komoran, Okt(Twitter) 등이 있습니다.
이들은 모두 KoNLPy 라이브러리를 통해 사용할 수 있습니다. 그중 Mecab이 가장 빠르고 정확도도 높아서 많이 사용됩니다.
두 번째 차이점은 띄어쓰기 불규칙성입니다. 영어는 단어 사이를 반드시 띄어쓰지만, 한국어는 띄어쓰기를 틀리게 쓰는 경우가 많습니다.
"자연어처리"와 "자연어 처리"가 같은 의미인데 다르게 처리될 수 있습니다. "그래서 subword 기반 접근이 도움이 돼.
FastText나 BPE(Byte Pair Encoding) 같은 방법이지." 박시니어 씨가 덧붙였습니다. 세 번째로 사전 학습 임베딩도 한국어 전용 모델을 써야 합니다.
다행히 훌륭한 오픈소스 프로젝트들이 있습니다. Kyubyong Park의 한국어 Word2Vec은 한국어 Wikipedia와 뉴스로 학습되었습니다.
ratsgo의 embedding 저장소에서는 Word2Vec, FastText, ELMo 등 다양한 한국어 임베딩을 다운받을 수 있습니다. 김개발 씨가 물었습니다.
"요즘 BERT도 많이 쓴다던데요?" "맞아. KoBERT, KoELECTRA 같은 한국어 사전 학습 언어 모델도 있어.
임베딩을 넘어서 문맥까지 이해하는 모델이지. 나중에 따로 배워볼 만해." 한국어 임베딩을 사용할 때 주의할 점이 있습니다.
형태소 분석기마다 결과가 다르므로, 학습과 추론에서 같은 분석기를 사용해야 합니다. 또한 도메인별 신조어나 전문 용어는 사용자 사전에 추가해야 제대로 분석됩니다.
김개발 씨는 Mecab을 설치하고 한국어 데이터를 전처리해보았습니다. 형태소 단위로 분리하니 같은 의미의 단어들이 제대로 모이기 시작했습니다.
"한국어는 확실히 한 단계가 더 필요하네요!" 박시니어 씨가 고개를 끄덕였습니다. "맞아.
하지만 그 한 단계가 성능을 크게 좌우해. 전처리를 얼마나 잘 하느냐가 한국어 NLP의 핵심이야."
실전 팁
💡 - Mecab 설치가 까다로우면 Docker 이미지를 활용하세요
- 도메인 전문 용어는 사용자 사전에 반드시 추가하세요
- 한국어 BERT 계열 모델(KoBERT, KoELECTRA)도 함께 학습해두면 좋습니다
이상으로 학습을 마칩니다. 위 내용을 직접 코드로 작성해보면서 익혀보세요!