rueki

워드투벡터(Word2Vec) 본문

DL/NLP

워드투벡터(Word2Vec)

륵기 2019. 7. 4. 12:24
728x90
반응형

단어 간 유사성을 고려하기 위해 단어의 의미를 벡터화 시켜주는데, 이러한 방법을 워드투벡터라고 한다.

Word2vec은 추론 기반 기법으로, 데이터의 일부를 사용하여 순차적으로 학습하는 미니배치 학습을 바탕으로 한다.

 

먼저 추론 기반 방법에 대해서 알아보자.

추론은 단어의 출현 패턴을 학습하는 것을 목적으로 하는 것이다.

you   goodbye say hello

you say goodbye i say hello 라는 문장이 있을 때 you 다음에 나올 단어는 say라는 것은 바로 알아챌 것이다.

그러나 이를 모델학습에 의한 추론으로 무슨 단어일 지, 유추할 때는

you 0.1 , say 0.8, goodbye 0.03, i 0.04 say 0.01, hello 0.01 의 확률을 가지고 있다고 가정했을 때,

제일 높은 확률을 갖는 say가 될 것이다라고 추론하는 것이다.

 

Word2vec은 위와 같은 추론 기법을 기반으로 이루어진다.

 

고양이 + 애교 = 강아지
한국 - 서울 + 도쿄 = 일본
박찬호 - 야구 + 축구 = 호나우두

 

위의 예시를 보면, 단어 간의 연산을 통해 하나의 의미를 계산해낸다.

 

앞의 글에서 언급했듯이, 희소 표현은 원-핫 벡터로 표현됨으로서, 단어의 의미를 파악할 수 없었다.

그래서 유사성 역시 표현을 할 수 없게 되었다.

대안으로 단어의 의미를 다차원 공간에 벡터화하는 방법을 찾게되는데,

이를 분산 표현(Distributed Representation)이라 한다.

이는 단어의 의미를 벡터화 하는 워드 임베딩 작업에 속해, 임베딩 벡터라고 하며,

저차원을 가지므로 밀집 벡터라고도 할 수 있다.

 

 


분산 표현

분포 가설을 가정으로 만들어진 표현으로써, 비슷한 위치에서 등장하는 단어들은 비슷한 의미를 가진다라고 가정한다.

강아지 -> 귀엽다, 예쁘다

=> '귀엽다' 와 '예쁘다' 는 의미적으로 가까운 단어가 된다.

 

워드투벡터로 임베딩 된 벡터는 벡터의 차원이 단어 집합의 크기가 될 필요가 없다.

원하는 단어를 표현하기 위해 사용자가 설정한 차원의크기만 가지면 각 차원은 실수형의 값을 가진다.

 

분산 표현은 저차원에 단어의 의미를 여러 차원에다가 분산하여 표현한다.

이를 통해 단어 간 유사도를 계산할 수 있다.


CBOW(Continuous Bag of Words)

주변에 있는 단어들을 가지고, 중간에 있는 단어들을 예측하는 방법 즉, 문맥으로 부터 target을 추측하는 신경망이다.

"The fat cat sat on the mat" 이라는 문장이 있을 때  sat을 예측하는 것이 CBOW가 하는 일이다.

중심 단어를 예측하기 위해, 앞뒤로 몇 개을 단어를 볼지 범위를 정해야하는데, 이를 윈도우(Window)라 한다.

Window가 2이면, sat이 중심단어일 때, fat, cat 과 on, the를 참고한다.

 

윈도우 크기를 정했다면, 윈도우를 계속 움직여서 주변단어와 중심 단어 선택을 바꿔가며 학습을 위한

데이터 셋을 만들 수 있는데, 이를 슬라이딩 윈도우라고 한다.

워드투벡터에서 입력은 모두 원-핫 벡터가 되어야하는데 신경망 구조를 통해 살펴 보자

원래의 신경망 구조라면 은닉층이 충분히 쌓인 신경망 구조를 보여야 하지만, 여기서는 단 하나의 은닉층만을 

볼 수가 있다.

여기서 은닉층의 크기는 N은 임베딩하고 난 벡터의 크기가 된다.

N이 5일 때, 벡터의 크기도 5가 된다.

 

입력층과 은닉층 사이의 가중치를 넣었을 때, 가중치 W는 V x N 벡터가 되며

V는 단어 집합의 크기를 말한다.

원-핫 벡터의 차원이 7이고 은닉층 차원이 5면 가중치 행렬은 7 x 5가 된다.

은닉층과 출력층 사이의 W' 벡터는 5x7의 크기를 갖게 된다.

주변단어의 원-핫 벡터에 대해 가중치 W가 곱해서 생겨진 결과 벡터들은 은닉층에서 만나 

벡터들의 평균인 벡터를 구하게 된다. 윈도우 크기가 2이면, 입력 벡터의 총 갯수는 2m이므로,

중간 단어를 예측하기 위해서는 4개가 입력 벡터로 들어가게 된다.

평균을 구할 때, 4개의 결과 벡터에 대해서 평균을 구하게 된다.

평균 벡터는 두 번째 가중치 행렬인 W'와 곱해진다. 이렇게 되면 입력값이였던 원-핫 벡터들과

차원이 동일한 벡터가 나오게 된다.

CBOW는 소프트맥스 함수를 취하는데, 소프트맥수 함수의 출력값은 0과 1사이의 실수로,

원소간 총 합은 1이된다. 이렇게 나온 벡터를 스코어 벡터(Score vector)라고 한다.

 

스코어 벡터는 우리가 실제로 값을 알고있는 벡터인 중심 단어 원-핫 벡터의 값에 가까워져야 한다.

두 벡터가 가까워지게 하기 위해서는 CBOW는 cross-entropy 함수를 사용한다. 이는 손실함수로 생각하면 된다.

스코어 벡터와, 원-핫 벡터를 입력값으로 넣고 표현한 식이다.

이 식은 loss function으로 적합한데, c를 중심 단어에서 1을 가진 차원의값의 인덱스라고 하면

은 스코어 벡터가 y를 정확하게 예측한 경우가 된다.

위의 식에 대입했을 때, -1 log(1) = 0 이 되기 때문에 스코어 벡터가 y를 정확하게 예측한 경우의

cross-entropy의 값은 0이 된다.

 

역전파(Back Propagation)를 수행하면 W와 W'가 학습이 되는데, 학습이 다 되었다면 N차원의 크기를 갖는 W의 행이나 W'의 열로부터 어떤 것을 임베딩 벡터로 사용할지를 결정하면 됩니다. 때로는 W와 W'의 평균치를 가지고 임베딩 벡터를 선택하기도 한다.

 


Skip-gram은 중심 단어에서 주변 단어를 예측하려고 한다. 즉 target으로 부터 주변의 문맥 단어를 추측하는 것이다.

모델의 구성은 위와 같으며, 입력층은 하나가 되고, 출력층은, 문맥의 수만큼 존재한다. 따라서 각 출력층에서는 Softmax를 이용해 각각의 손실을 구하고, 이 손실들의 합을 최종 손실로 합ㄴ다.


 

영어 데이터로 Word2Vec을 진행해보자

gensim 패키지가 Word2Vec을 지원하고 있어, 임베딩 벡터로 변환시켜보자

 

데이터는 아래 의 링크서 다운 받을 수 있다고 한다. 파일은 xml 파일이다.

https://wit3.fbk.eu/get.php?path=XML_releases/xml/ted_en-20160408.zip&filename=ted_en-20160408.zip

 

import nltk
from nltk.tokenize import sent_tokenize,word_tokenize
import re
from lxml import etree

targetXML = open(r'파일 경로','r', encoding='UTF8')
target_text = etree.parse(targetXML)
parse_text = '\n'.join(target_text.xpath('//content/text()'))
# xml 파일로부터 <content>와 </content> 사이의 내용만 가져옵니다.
content_text = re.sub(r'\([^)]*\)','',parse_text)
# 정규 표현식의 sub 모듈을 통해 content 중간에 등장하는 (Audio), (Laughter) 등의 배경음 부분을 제거합니다.
# 해당 코드는 괄호로 구성된 내용을 제거하는 코드입니다.

sent_text=sent_tokenize(content_text)

normalized_text = []
for string in sent_text:
    tokens = re.sub(r"[^a-z0-9]+", " ", string.lower())
    normalized_text.append(tokens)
    
result = []
result = [word_tokenize(sentence) for sentence in normalized_text]

print(result[:10])
[['here', 'are', 'two', 'reasons', 'companies', 'fail', 'they', 'only', 'do', 'more', 'of', 'the', 'same', 'or', 'they', 'only', 'do', 'what', 's', 'new'],
['to', 'me', 'the', 'real', 'real', 'solution', 'to', 'quality', 'growth', 'is', 'figuring', 'out', 'the', 'balance', 'between', 'two', 'activities', 'exploration', 'and', 'exploitation'],
['both', 'are', 'necessary', 'but', 'it', 'can', 'be', 'too', 'much', 'of', 'a', 'good', 'thing'], 
['consider', 'facit'], ['i', 'm', 'actually', 'old', 'enough', 'to', 'remember', 'them'],
['facit', 'was', 'a', 'fantastic', 'company'], ['they', 'were', 'born', 'deep', 'in', 'the', 'swedish', 'forest', 'and', 'they', 'made', 'the', 'best', 'mechanical', 'calculators', 'in', 'the', 'world'], 
['everybody', 'used', 'them'], ['and', 'what', 'did', 'facit', 'do', 'when', 'the', 'electronic', 'calculator', 'came', 'along'],
['they', 'continued', 'doing', 'exactly', 'the', 'same']]

단어 토큰화까지 진행한 결과를 볼 수가 있으며 gensim에서 제공하는 word2vec을 사용해보자

from gensim.models import Word2Vec
model = Word2Vec(sentences = result,size=100, window = 5, min_count=5, workers=4, sg=0)

size = 워드 벡터의 특징 값. 즉, 임베딩 된 벡터의 차원. -> 임베딩 벡터에서 사용자가 벡터 차원 설정
window = 컨텍스트 윈도우 크기
min_count = 단어 최소 빈도 수 제한 (빈도가 적은 단어들은 학습하지 않는다.)
workers = 학습을 위한 프로세스 수
sg = 0은 CBOW, 1은 Skip-gram.

 

유사 단어 출력하는 라이브러리 - model.wv.most_similar

a=model.wv.most_similar("man")
print(a)
[('woman', 0.8644892573356628), ('guy', 0.8053544759750366), ('lady', 0.7756980657577515), 
('girl', 0.7639989852905273), ('boy', 0.755024790763855), ('soldier', 0.7180885672569275), 
('poet', 0.7167243361473083), ('gentleman', 0.7003819942474365), 
('kid', 0.6754928827285767), ('adam', 0.657413125038147)]

이를 통해 유사도를 계산해 보았다.

 

 


 

728x90
반응형
Comments