[NLP] Word2Vec 실습 예제
[NLP] 전통적 자연어 처리 방법들: BoW, TF-IDF, Word2Vec, FastText
자연어 처리(NLP)는 사람의 언어를 컴퓨터가 이해하고 처리할 수 있도록 하는 기술이다. 최근 NLP는 딥러닝 기반 모델(BERT, GRT etc...) 많이 넘어왔고 이미 이를 넘어선 많은 모델들이 사용되고 있다.
datanovice.tistory.com
📌 1. Word2Vec
위 포스팅에도 설명드렸습니다만 다시 한 번 정리해보면.
Word2Vec은 대규모 말뭉치(corpus)에서 단어들 간의 통계적 패턴을 학습, 각 단어를 실수 벡터 공간상의 밀집(벡터) 표현으로 매핑하는 기법. 즉, 희소 벡터가 아님.
이렇게 얻어진 단어 벡터는 단어의 의미적, 통사적 유사성을 내제하게 되어, 벡터 간 거리나 방향을 통해 단어 간 관계를 정량적으로 다룰 수 있다.
📌 2. 모델
모델은 크게 2 가지
CBOW: 주변 단어들을 입력으로 받아, 해당 위치의 중심 단어를 예측 / Skip-gram: 중심 단어를 입력으로 받아, 주변 단어들을 예
이 중 Skip-gram의 아이디어를 한 번 살펴보면..
1) 단어를 위치로 보는 아이디어.
Word2Vec은 단어 하나하나를 숫자 벡터(예: 100차원의 실수 벡터)로 바꿉니다. 해당 벡터는 마치 지도를 그릴 때 사용하는 좌표처럼, 단어들 사이의 '의미상의 거리'를 표현합니다.
예를 들어..? '왕' 좌표에서 '남자'를 뺀 뒤 '여자'를 더하면, '여왕' 좌표 근처에 가게 되는것.
2) 학습법?
'나는 왕자를 만났다' 라는 단어에서 중심 단어를 '왕자'라고 봅시다. 그럼 앞 뒤에 존재하는 '나는', '만났다'가 주변 단어가 됩니다.
Word2Vec은 '왕자'라는 벡터를 알면, 주위에 '나는', '만났다'가 올 확률이 높아지도록 벡터를 조정하는 것.
'나는'이 '왕자' 주변에 올 확률을 $P('나는'|'왕자')$라고 할 때, Word2Vec이 이 확률을 최대가 되도록 벡터를 학습시키는 것이다.
수식의 깊은 부분은 다루지 않겠지만, 핵심 수식을 직관적으로 해석 하면,
두 벡터의 내적이 클 수록 두 단어가 가까이 붙어 있어야 한다고 판단합니다. 우선 내적은 벡터 길이 x 벡터 길이 x 두 벡터 사이 각도의 코사인 값인데, 같은 방향이면 코사인이 1이며, 내적이 크다고 판단합니다.
Word2Vec 자체는 함께 등장하는 단어쌍이 같은 방향이 되도록 학습하고 때문에 학습 후 내적이 크면 의미상 가깝다라고 판단하는 것이지요.
📌 3. 실습 예제
우선 본 데이터는 steam.txt 데이터로 깃허브의 한 유저가 크롤링한 데이터 입니다.
corpus/sentiment/steam.txt at master · bab2min/corpus
개인적으로 수집한 한국어 NLP용 말뭉치 모음. Contribute to bab2min/corpus development by creating an account on GitHub.
github.com
import pandas as pd
# corpus in steam
steam_corpus = pd.read_csv('steam.txt', sep = '\t', names = ['label', 'corpus'])
label = steam_corpus['label'].astype(int).tolist()
sentences = [sent.split() for sent in steam_corpus['corpus'].tolist()]
# word2vec 학습
from gensim.models import Word2Vec
model = Word2Vec(
sentences,
vector_size = 500, # 단어 벡터 크기
window = 5, # 윈도우 크기
min_count = 5, # 단어 최소 개수
sg = 1, # 1 = skip-gram, 0 = CBOW
negative = 7,
epochs = 10,
workers = -1
)
vector = model.wv['좋아']
print(f'좋아 벡터 : {vector}')
좋아 벡터 : [ 1.45134830e-03 -1.94745755e-03 -1.22100802e-03 1.63299078e-03
1.57420640e-04 1.76236697e-03 7.19981676e-04 4.58278897e-04
-1.00485352e-03 1.87128549e-04 -8.90997879e-04 1.67881968e-04
-1.06166837e-04 -1.73554348e-03 -1.20905810e-03 1.76237826e-03
-7.23574660e-04 9.22365638e-04 -8.82045482e-04 -4.54398163e-04
-1.45657035e-03 1.16444113e-04 6.86231593e-04 -1.53845816e-03
1.86943822e-03 1.56464765e-03 -5.60326094e-04 -5.96339465e-04
7.73606531e-04 -1.09126396e-03 -8.96794081e-04 -1.50247267e-03
1.21095043e-03 -4.47144266e-04 -1.29841012e-03 3.94471164e-04
1.01805213e-04 -1.13646244e-03 1.92940736e-03 5.23113704e-04
1.86466763e-03 -1.72683666e-03 1.45622878e-03 -2.08332538e-04
-3.20613617e-04 1.00690534e-03 2.62782327e-04 1.76419923e-03
1.59366417e-03 -1.46359066e-03 -2.91909208e-04 -1.75933482e-03
-7.12272420e-04 -1.37044524e-03 1.99536467e-03 1.33066345e-03 ...
이렇게 Word2Vec 모델에서 벡터 크기를 500으로 설정하였고, 때문에 한 단어달 500 크기의 벡터가 생성됩니다.
print(f'좋아 유사 단어: {model.wv.most_similar('좋아', topn=5)}')
좋아 유사 단어: [('시작된', 0.1859043687582016), ('않는한', 0.1776987463235855), ('보더랜드를', 0.17544881999492645), ('자유도,', 0.17087121307849884), ('지인이랑', 0.16601556539535522)]
이렇게 유사 단어를 확인할 수 있는데, 간단한 예시이기 때문에 큰 의미는 없는 것 같습니다.
모델 파라미터 조정 + 형태소 분류(특히, 한국어에서)를 한다면 더욱 정확하게 유사한 단어들을 알아 볼 수 있을 것이다 판단됩니다.