GloVe(Global Vectors for Word Representation)

( 참고 : “딥러닝을 이용한 자연어 처리 입문” (https://wikidocs.net/book/2155) )

  • 기본의 카운트 기반의 LSA & 예측 기반의 Word2Vec의 단점을 보완!
  • word2vec와 비슷한 성능!


1. LSA & Word2Vec의 한계

  • 1) LSA = DTM이나 TF-IDF 행렬과 같이 “단어의 빈도수”를 기반으로 한 방법론

  • 2) Word2Vec = 실제값 & 예측값의 오차를 줄여나가는 “예측 기반”의 방법론

  • LSA는 단어의 의미를 유추하는 작업에서 성능이 떨어진다는 단점 (ex 왕:남자 = 여왕:?)이,

    Word2Vec는 임베딩 벡터가 window 크기 내의 단어만을 고려하기 때문에 corpus의 전체적인 통계정보를 반영하지 못한다는 단점이 있음


이 둘의 단점을 보완하기 위해, 이 두가지 방법론을 모두 사용하는 GloVe!

  • LSA의 “단어 빈도수” : Window based Co-occurence Matrix
  • Word2Vec의 “예측 기반” : Loss Function


2. Window based Co-occurence Matrix

i 단어의 윈도우 크기(Window Size) 내에서 k 단어가 등장한 횟수를 i행 k열에 기재한 행렬




3. Co-occurence Probability

동시 등장 확률 P(k i) = 동시 등장 행렬로부터 특정 단어 i의 전체 등장 횟수를 카운트하고,

특정 단어 i가 등장했을 때 어떤 단어 k가 등장한 횟수를 카운트하여 계산한 조건부 확률


.


4. Loss Function

Notation

  • \(X:\) Co-occurrence Matrix

  • \(X_{i j}:\) 중심 단어 \(i\)가 등장했을 때 윈도우 내 주변 단어 \(j\)가 등장하는 횟수

  • \(X_{i}= \sum_{j} X_{i j}:\) Co-occurrence Matrix에서 \(¡\)행의 값을 모두 더한 값

  • \(P_{i k}: P(k \mid i)=\frac{X_{i k}}{X_{i}}:\) 중심 단어 \(i\)가 등장했을 때 window 안에 주변 단어 \(k\)가 등장할 확률

Ex) \(\mathrm{P}(\) solid \(\mid\) ice \()=\) 단어 ice가 등장했을 때 단어 solid가 등장할 확률

  • \(w_{i}:\) 중심 단어 \(i\)의 embedding vector
  • \(\tilde{w}_{k}:\) 주변 단어 \(k\)의 embedding vector


Main Idea : “임베딩 된 중심 단어주변 단어 벡터의 내적이, 전체 코퍼스에서 동시 등장 확률이 되도록 만드는 것”


위의 main idea를 수식으로 표현하자면, 다음과 같다.

  • dot product \(\left(w_{i} \tilde{w}_{k}\right) \approx P(k \mid i)=P_{i k}\)

  • GloVe는 위의 \(P_{ik}\)값에 log를 씌워서 모델링한다.

    \(\rightarrow\) \(\operatorname{dot} \operatorname{product}\left(w_{i} \tilde{w}_{k}\right) \approx \log P(k \mid i)=\log P_{i k}\)


위의 3. Co-occurence Probability에서의 idea대로, GloVe는 아래의 식을 손실함수로 정의한다.

\(F\left(w_{i}, w_{j}, \tilde{w}_{k}\right)=\frac{P_{i k}}{P_{j k}}\).

\(F\left(w_{i}-w_{j}, \tilde{w}_{k}\right)=\frac{P_{i k}}{P_{j k}}\) .

\(F\left((w_{i}-w_{j})^T \tilde{w}_{k}\right)=\frac{P_{i k}}{P_{j k}}\).


위의 함수\(F\) 는 “Homomorphism”(준동형)을 만족시켜야 하는데, 쉽게 말해 \(F(a+b)=F(a)F(b)\)를 만족시켜야 한다는 것이다. 그 이유는, 우리가 중심단어 \(w\)와 주변 안더 \(\tilde{w}\)를 무작위로 선택을 하더라도 자유롭게 교환 가능해야하기 때문이다.

이를 만족시키는 \(F\)는 아래와 같이 표현할 수 있다.

\(F\left(\left(w_{i}-w_{j}\right)^{T} \tilde{w}_{k}\right)=F\left(w_{i}^{T} \tilde{w}_{k}-w_{j}^{T} \tilde{w}_{k}\right)=\frac{F\left(w_{i}^{T} \tilde{w}_{k}\right)}{F\left(w_{j}^{T} \tilde{w}_{k}\right)}\).

위 조건을 만족시키는 대표적인 함수 \(F\)가 바로 exponential function이다. \(F\)를 이와 같이 지정하고, 위 식을 다시 적으면 아래와 같다.

\(\exp \left(w_{i}^{T} \tilde{w}_{k}-w_{j}^{T} \tilde{w}_{k}\right)=\frac{\exp \left(w_{i}^{T} \tilde{w}_{k}\right)}{\exp \left(w_{j}^{T} \tilde{w}_{k}\right)}\).

\(\exp \left(w_{i}^{T} \tilde{w}_{k}\right)=P_{i k}=\frac{X_{i k}}{X_{i}}\).


이를 통해, 우리는 아래와 같은 식을 얻을 수 있다.

\(w_{i}^{T} \tilde{w}_{k}=\log P_{i k}=\log \left(\frac{X_{i k}}{X_{i}}\right)=\log X_{i k}-\log X_{i}\).


위 식에서, 우리는 어떠한 단어 \(w_{i}\) 와 \(\tilde{w}_{k}\)를 고르던지 위 식이 성립해야 한다. 따라서, \(w_{i}\) 와 \(\tilde{w}_{k}\) 또한 위 식에서 교환가능해야 하지만, 이를 막는 것이 \(\log X_{i}\) term이다. ( \(\log X_{i k}\)의 경우, \log X_{k i}와 동일하므로 상관없다 )

따라서, 위의 \(\log X_{i}\) term을 없애고, 이 대신 bias term을 추가하여 아래와 같은 식을 완성한다.

\(w_{i}^{T} \tilde{w}_{k}+b_{i}+\tilde{b_{k}}=\log X_{i k}\).


위의 과정들을 통해 산출한 최종적인 Loss Function은 다음과 같다.

  • Loss function \(=\sum_{m=n=1}^{V}\left(w_{m}^{T} \tilde{w}_{n}+b_{m}+\tilde{b_{n}}-\log X_{m n}\right)^{2}\).


위의 Loss Function을 보완한 더 나은 Loss Function이 있다. 이는, 동시 등장 빈도가 낮은 \(X_{ik}\)값의 경우, 학습에 거의 도움이 되지 않기 때문에, 이를 고려하여 weight를 부여하기 위해 다음과 같은 weighting function \(f(X_{ik})\)를 도입한다.

  • \(f(x)=\min \left(1,\left(x / x_{\max }\right)^{3 / 4}\right)\).

.

따라서, 최종적인 Loss Function은 아래와 같다.

  • Loss Function : \(\sum_{m, n=1}^{V} f\left(X_{m n}\right)\left(w_{m}^{T} \tilde{w}_{n}+b_{m}+\tilde{b_{n}}-\log X_{m n}\right)^{2}\).


5. 실습

from glove import Corpus, Glove
  • word2vec 실습에서 사용한 데이터로
import re
from lxml import etree
from nltk.tokenize import word_tokenize,sent_tokenize

targetXML = open('ted_en-20160408.xml','r',encoding='UTF8')
target_text = etree.parse(targetXML)
parse_text = '\n'.join(target_text.xpath('//content/text()')) # <content> ~ </content> 사이 내용 가져오기

# (Audio), (Laughter) 등의 배경음 부분을 제거
content_text = re.sub(r'\([^)]*\)','',parse_text)

# 문장 토큰화
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 = [word_tokenize(sentence) for sentence in normalized_text]
corpus = Corpus()
corpus.fit(result,window=5)

glove = Glove(no_components=100, learning_rate=0.05)
glove.fit(corpus.matrix, epochs=20, no_threads=4, verbose=True)
glove.add_dictionary(corpus.dictionary)
model_result1 = glove.most_similar('university')