Word2Vec은 2013 구글 연구 팀이 발표했고 가장 널리 쓰이고 있는 단어 임베딩 모델이다.
Word2Vec 기법은 두 개의 논문으로 나누어 발표되었다.
논문은 다음과 같다.
(Mikolov et al., 2013a)
https://arxiv.org/pdf/1301.3781.pdf%C3%AC%E2%80%94%20%C3%AC%E2%80%9E%C5%93
(Mikolov et al., 2013b)
https://proceedings.neurips.cc/paper/2013/file/9aa42b31882ec039965f3c4923ce901b-Paper.pdf
여기에서 (Mikolov et al., 2013a)에는 Skip-Gram 과 CBOW라는 모델이 제안된다.
Skip-Gram은 타깃 단어를 가지고 주변 문맥 단어를 예측하는 과정을 통해 학습하고,
CBOW는 주변 문맥 단어들을 가지고 타깃 단어 하나를 맞추는 과정을 통해 학습한다.
그러므로 Skip-Gram이 더 임베딩 품질이 좋은 경향이 있다.
왜냐하면 CBOW는 학습 데이터가 주변문맥단어+타깃 단어 한 쌍 뿐이지만,
Skip-Gram은 타깃단어와 주변 문맥 단어 각각의 쌍을 이루며
주변 단어가 여러개이기 때문에 같은 말뭉치로도 더 많은 학습이 가능하다.
Word2Vec Skip-gram 모델을 설명해 보겠다.
가장 먼저 학습 데이터를 구축한다.
1. 윈도우를 설정한다 ( 타깃 단어 주위로 몇개의 단어까지 주변 단어로 설정할 것인가?)
2. 타깃 단어와 주변단어의 쌍들을 positive sample로 분류, 그렇지 않은 쌍들을 negative sample로 분류한다.
하지만 여기서 문제가 있다. 타깃 단어를 하나씩 옮겨가면서(슬라이딩) 학습 데이터를 만들때, negative sample들이 너무 많아 계산이 느려지고 비효율적이다.
이를 해결하기 위해 negatvie sampling이라는 기법을 사용한다. 이는 기존 하나의 positive sample과 모든 negative sample을 계산하는 것이 아닌, 하나의 positive sample에 자신이 정한 만큼(k개)의 negative sample만 계산하는 것이다.
=>(Mikolov et al., 2013b)에 따르면 작은 데이터에는 k를 5~20, 큰 말뭉치에는 2~5가 성능이 좋았다고 한다.
그렇다면 k개의 negative sample을 어떻게 뽑을까? 여기에 변화를 주어 성능을 향상시켜 보자.
2개의 목표가 있다.
1. 말뭉치에 자주 등장하지 않는 희귀한 단어가 네거티브 샘플로 더 잘 뽑일 수 있도록 하자
(많이 등장하는거 학습해봤자 비효율적이다)
2. 너무 자주 등장하는 단어를 학습에서 제외하자 (예를들어 조사) : subsampling 이라고 부름
이를통해 우리는 계산량을 줄일 수 있다.
이제 모델을 학습해보자
Skip-gram 모델의 학습 파라미터는 행렬U,행렬V 2개 뿐이다. 이전 NPLM은 8개나 되니 많이 발전했다.
U는 타깃단어, V는 문맥단어에 대응된다.
여기서 U와 V를 곱함에 따라 U의 행에 포함된 '타깃단어'에 해당되는 벡터와 V의 열에 포함된 '문맥 단어'에 해당되는 벡터는 곱해진다.
이는 내적과 같고, 이는 코사인 유사도에 비례한다. (내적을 알면 코사인 유사도도 이해 가능)
negative sample와 positive sample을 잘 구분할 확률은 위의 벡터의 내적에 따라 변한다.
이제 이를 잘 조정하여 두 확률을 높이는 과정에서 U와V가 업데이트가 됨에 따라 단어 임베딩이 된다.
이에 대한 기하학적인 관점에서 수식을 해석하여 이해하는 부분은 내 블로그 다음 글에 설명해 놓았다.
https://nlp-study.tistory.com/26
코드를 통해 학습해보자
이전에 사용했던 형태소 분석 완료된 '네이버 영화 리뷰 말뭉치'를 이용해 학습해보겠다.
>>> from gensim.models import Word2Vec
>>> corpus = [sent.strip().split(" ") for sent in open(corpus_fname, 'r').realines()]
>>> model = Word2Vec(corpus, size=100, workers=4, sg=1)
>>>model.save(model_fname)
코드
코드 분석
1. gensim에서 Word2vec을 불러온다
2. corpus_fname(전처리 완료된 데이터)를 한 줄씩 읽으면서 sent를 통해 반복해서 배출하고 문장 앞뒤 공백 제거와 띄어쓰기 마다 문자열 잘라서 배열로 만든다. 즉 단어를 잘라서 말뭉치(corpus)에 저장
3. size(임베딩 차원수), workers(CPU 쓰레드 개수), sg(1이면 Skip-gram,0이면 CBOW)를 부여하고 corpus를 Word2Vec에 집어 넣는다.
4. model_fname에 학습 완료된 결과를 저장한다.
이제 실행을 해보자
한번 기준 단어와 코사인 유사도가 높은 단어들을 5개 출력해 보겠다. (=단어와 의미가 유사한 단어 5개 출력)
코드 실행 결과
다음과 같이 나온 것을 볼 수 있다.
여기서 희망에 비슷한 단어로 절망이 나왔는데 학습이 잘못된 것일까?
"어떤 단어 쌍이 비슷하다"는 것은 같은 속성을 매개로 강한 관련을 맺고 있다고 말할 수 있다
그래서 most_simliar 함수는 유의관계에 있는 단어를 보여준다기보다는 관련성(relevance)이 있는 단어를 보여준다.
그러므로 희망과 절망은 서로 비슷하다고 나오는 것이다.
'Tensorflow 2 NLP(자연어처리) > 단어 임베딩' 카테고리의 다른 글
[3-6] Glove란? (0) | 2021.11.13 |
---|---|
[3-5] Matrix Factorization이란? (0) | 2021.11.13 |
[3-4] 잠재 의미 분석(LSA,Latent Semantic Analysis) 이란? (0) | 2021.11.13 |
[3-3] FastText란? (0) | 2021.11.13 |
[3-1] NPLM이란? (0) | 2021.11.12 |