for textmining

GloVe를 이해해보자!

|

이번 포스팅에서는 미국 스탠포드대학에서 2014년 개발한 워드 임베딩 방법론인 GloVe에 대해 알아보도록 하겠습니다. 이번 글은 고려대 강필성 교수님 강의를 참고로 했는데요. 저도 완벽히 이해한 건 아니어서 무림 고수분들의 많은 지적 환영합니다. 그럼 시작하겠습니다.

GloVe의 문제의식

GloVe는 2013년 구글에서 개발한 Word2Vec의 단점을 파고 들었습니다. Word2Vec은 중심단어로 주변단어를, 주변단어로 중심단어를 예측하는 과정에서 단어를 벡터로 임베딩하는 방법론인데요. 임베딩된 단어의 내적(inner product)이 코사인 유사도가 되도록 합니다. 또 GloVe 연구팀은 단어-문맥행렬에 특이값분해(SVD)를 실시해 데이터 차원을 효과적으로 축소하는 한편 노이즈 등을 줄여 내재적인 의미를 이끌어내는 잠재의미분석(LSA)의 단점 또한 언급했습니다. Word2Vec에 관해 좀 더 살펴보시려면 이곳이곳을, LSA에 대한 자세한 내용은 이곳을 참고하시면 좋을 것 같습니다.

어쨌든 GloVe 연구팀은 LSA와 Word2Vec에 관해 다음과 같이 각각 비판하였습니다.

While methods like LSA efficiently leverage statistical information, they do relatively poorly on the word analogy task, indicating a sub-optimal vector space structure. Methods like skip-gram may do better on the analogy ask, but they poorly utilize the statistics of the corpus since they train on separate local context windows instead of on global co-occurrence counts.

위 내용을 제 방식대로 해석하자면 LSA는 말뭉치 전체의 통계적인 정보를 모두 활용하지만, LSA 결과물을 가지고 단어/문서 간 유사도를 측정하기는 어려운 단점을 지닙니다. 반대로 Word2Vec(Skip-Gram)은 저차원 벡터공간에 임베딩된 단어벡터 사이의 유사도를 측정하는 데는 LSA에 비해 좋은 성능을 가지지만, 사용자가 지정한 윈도우(주변 단어 몇개만 볼지) 내에서만 학습/분석이 이뤄지기 때문에 말뭉치 전체의 공기정보(co-occurrence)는 반영되기 어려운 단점을 지닙니다.

이 때문에 GloVe 연구팀은 임베딩된 두 단어벡터의 내적이 말뭉치 전체에서의 동시 등장확률 로그값이 되도록 목적함수를 정의했습니다. (their dot product equals the logarithm of the words’ probability of co-occurrence) “임베딩된 단어벡터 간 유사도 측정을 수월하게 하면서도 말뭉치 전체의 통계 정보를 좀 더 잘 반영해보자”가 GloVe가 지향하는 핵심 목표라 말할 수 있을 것 같습니다.

GloVe의 목적함수

우선 연구팀이 직접 든 예시를 먼저 설명해볼게요. 아래 표를 볼까요? 아래 표는 학습 말뭉치에서 동시에 같이 등장한 단어의 빈도를 각각 세어서 전체 말뭉치의 단어 개수로 나눠준 동시등장확률(the words’ probability of co-occurrence)입니다.

위 표를 보면 ice라는 단어가 주어졌을 때 solid가 등장할 확률은 steam이 주어졌을 때 solid가 나타날 확률보다 높습니다. ‘단단한’이라는 뜻을 가진 solid는 steam보다 ice와 관련성이 높기 때문에 직관적으로 당연한 결과입니다. 그러면 $P(solid$|$ice)/P(solid$|$steam)$은 1보다 훨씬 큰 값(8.9)을 가집니다.

반대로 $P(gas$|$ice)/P(gas$|$steam)$은 1보다 훨씬 작은 값(0.0085)이네요. ice, steam과 관련성이 높거나 별로 없는 water와 fashion은 모두 1 안팎의 값이 나옵니다.

GloVe 연구팀은 특정 단어 $k$가 주어졌을 때 임베딩된 두 단어벡터의 내적이 두 단어의 동시등장확률 간 비율이 되게끔 임베딩하려고 했습니다. 예컨대 solid라는 단어가 주어졌을 때 ice와 steam 벡터 사이의 내적값이 8.9가 되도록 하자는 것이지요. 마찬가지로 gas가 주어졌을 때 ice x steam은 0.0085가 되게끔 합니다.

그런데 말뭉치 전체를 학습시키다보면 ice나 steam이 $k$가 될 수 있고, solid/gas/water/fashion 역시 위 표 기준 ice나 steam 자리에 얼마든지 들어갈 수 있습니다. 이렇게 단어 상호 간 비율 정보를 말뭉치 전체를 놓고 한꺼번에 반영하게 되면 좀 더 정확한 단어 임베딩이 될 것이라고 생각한 게 GloVe 연구팀의 아이디어인 것 같습니다.

그럼 지금까지 설명드렸던 목표를 달성하기 위한 목적함수를 살펴봐야겠네요. GloVe 연구팀은 우선 아래와 같은 식을 정의하고, 이 식을 만족하는 임의의 함수 $F$를 찾고자 했습니다.

\[F({ w }_{ i },{ w }_{ j },\tilde { { w }_{ k } } )=\frac { { P }_{ ik } }{ { P }_{ jk } }\]

GloVe 연구팀은 $P_{ik}$를 $P(k$|$i)$로 정의했습니다. $i$번째 단어 주변(윈도우 크기는 사용자 지정)에 $k$번째 단어가 등장할 조건부확률이라는 뜻입니다. 바꿔 말하면 전체 말뭉치에서 ‘사용자가 미리 정한 윈도우 내에서 $i$번째 단어와 $k$라는 단어가 동시에 등장한 빈도수($X_{ik}$)’를 ‘$X_i$(=$Σ_k{X_{ik}}$)’로 나눠준 값입니다. 위 표 기준으로 예를 들면 $P(solid$|$ice)$ 정도의 의미가 되겠네요.

그렇다면 $P_{ik}/P_{jk}$는 어떤 의미일까요? 마찬가지로 표를 기준으로 설명해드리면 $P(solid$|$ice)/P(solid$|$steam)=8.9$가 될 겁니다. 그럼 위 식을 이렇게 예시로 풀 수 있겠네요. $d$차원 벡터공간에 임베딩된 ice, steam, solid 벡터를 임의의 함수 $F$에 넣으면 8.9를 반환하는 $F$를 찾자는 겁니다.

\[F({ w }_{ ice },{ w }_{ steam },{ w }_{ solid })=\frac { { P }_{ ice,solid } }{ { P }_{ steam,solid } } =\frac { P(solid|ice) }{ P(solid|steam) } =\frac { 1.9\times { 10 }^{ -4 } }{ 2.2\times { 10 }^{ -5 } } =8.9\]

GloVe 연구팀은 위 식을 아래와 같이 변형하였습니다. 아래 식의 변형 과정을 제 방식대로 해석하면 이렇습니다. 우선 $F$ 안에 집어넣을 $w_i$, $w_j$, $w_k$ 간에 관계를 따져보기 위해 $w_i$와 $w_j$를 뺀 벡터에 $w_k$를 내적합니다. GloVe 연구팀은 임베딩된 두 단어벡터의 내적은 전체 말뭉치의 동시등장확률이 되도록 하려는 목적이 있었으므로 $P_{ik}$를 $F(W_i^TW_k)$로 정의했습니다.

\[\begin{align*} F({ w }_{ i }-{ w }_{ j },\tilde { { w }_{ k } } )&=\frac { { P }_{ ik } }{ { P }_{ jk } } \\ F({ { ({ w }_{ i }-{ w }_{ j }) }^{ T } }\tilde { { w }_{ k } } )&=\frac { { P }_{ ik } }{ { P }_{ jk } } \\ F({ { ({ w }_{ i }-{ w }_{ j }) }^{ T } }\tilde { { w }_{ k } } )&=\frac { F({ w }_{ i }^{ T }\tilde { { w }_{ k } } ) }{ F({ w }_{ j }^{ T }\tilde { { w }_{ k } } ) } \\ F({ w }_{ i }^{ T }\tilde { { w }_{ k } } -{ w }_{ j }^{ T }\tilde { { w }_{ k } } )&=\frac { F({ w }_{ i }^{ T }\tilde { { w }_{ k } } ) }{ F({ w }_{ j }^{ T }\tilde { { w }_{ k } } ) } \end{align*}\]

그런데 GloVe 연구팀에 따르면 여기까지 도출된 $F$는 다음 세 가지 조건을 만족해야 합니다. 우선 $w_i$와 $w_k$를 서로 바꾸어도 식이 같은 값을 반환해야 합니다. 이미 설명드린 바와 같이 중심 단어 $w_k$는 얼마든지 $w_i$나 $w_j$가 될 수 있기 때문입니다. 또한 말뭉치 전체에서 구한 co-occurrence matrix $X$는 대칭행렬(symmetric matrix)이므로 함수 $F$는 이러한 성질을 포함해야 합니다. 마지막으로 homomorphism 조건을 만족해야 한다고 합니다. 이를 수식으로 쓰면 아래와 같습니다.

\[{ w }_{ i }\longleftrightarrow \tilde { { w }_{ k } } \\ X{ \longleftrightarrow X }^{ T }\\ F(X-Y)=\frac { F(X) }{ F(Y) }\]

이러한 조건을 만족하는 함수는 지수함수이기 때문에 $F$를 $exp$로 치환하고 식을 정리하면 아래와 같습니다.

\[\begin{align*} exp({ w }_{ i }^{ T }\tilde { { w }_{ k } } -{ w }_{ j }^{ T }\tilde { { w }_{ k } } )&=\frac { exp({ w }_{ i }^{ T }\tilde { { w }_{ k } } ) }{ exp({ w }_{ j }^{ T }\tilde { { w }_{ k } } ) } \\ { w }_{ i }^{ T }\tilde { { w }_{ k } } &=\log { { P }_{ ik } } =\log { X_{ ik }-\log { X_{ i } } } \\ \end{align*}\]

그런데 여기에서 문제가 하나 있습니다. $w_i$와 $w_k$를 서로 바꾸어도 식이 성립해야 한다고 했는데, 정말 이렇게 되려면 위 식 마지막의 $log(P_{ik})$가 $log(P_{ki})$와 같아야 하는데요. 사실 이를 풀어쓰면 각각 $log(X_{ik}) - log(X_i)$, $log(X_{ki}) - log(X_k)$가 되어서 서로 달라지게 됩니다. 이 때문에 GloVe 연구팀은 $log(X_i)$ 이 부분을 아래와 같이 상수항($b_i$, $b_k$)으로 처리해 이 조건을 만족하도록 식을 한번 더 변환하였습니다.

\[\begin{align*} { w }_{ i }^{ T }\tilde { { w }_{ k } } &=\log { X_{ ik }-{ b }_{ i }-\tilde { { b }_{ k } } } \\ { w }_{ i }^{ T }\tilde { { w }_{ k } } +{ b }_{ i }+\tilde { { b }_{ k } }& =\log { X_{ ik }} \end{align*}\]

자, 이제 거의 다 왔습니다. 위 식에서 미지수는 좌변이고, 우변의 $log(X_{ik})$는 우리가 이미 알고 있는 값입니다. 특정 윈도우 사이즈를 두고 말뭉치 전체에서 단어별 등장 빈도를 구한 co-occurrence matrix에 로그를 취해준 행렬이기 때문입니다.

그럼 우변과의 차이를 최소로 하는 좌변의 값이 $d$차원 벡터공간에 적절히 임베딩된 단어벡터들일 겁니다. 이를 식으로 정리하면 아래와 같습니다. 아래 목적함수 $J$를 최소화하는 $w_i$, $w_j$, $b_i$, $b_j$가 우리가 원하는 미지수들입니다.

\[\begin{align*} J&=\sum _{ i,j=1 }^{ V }{ { ({ w }_{ i }^{ T }\tilde { { w }_{ j } } +{ b }_{ i }+\tilde { { b }_{ j } } -\log { X_{ ij } } ) }^{ 2 } } \\ \end{align*}\]

그런데 GloVe 연구팀은 마지막으로 목적함수에 아래와 같은 모양의 $f(x)$를 추가했습니다. $X_{ij}$가 특정 값 이상으로 튀는 경우(학습 말뭉치에서 지나치게 빈도가 높게 나타나는 단어) 이를 방지하고자 하는 장치라고 보여집니다.

\(\begin{align*} J&=\sum _{ i,j=1 }^{ V }{ { f\left( { X }_{ ij } \right) ({ w }_{ i }^{ T }\tilde { { w }_{ j } } +{ b }_{ i }+\tilde { { b }_{ j } } -\log { X_{ ij } } ) }^{ 2 } } \\ &where\quad f(x)=\begin{cases} { (\frac { x }{ { x }_{ max } } ) }^{ \alpha } \\ 1\quad otherwise \end{cases}if\quad x<{ x }_{ max } \end{align*}\)

마치며

GloVe는 우선 학습말뭉치를 대상으로 co-occurrence 행렬 $X$를 만드는 것에서부터 학습을 시작합니다. 단어 개수가 1만개 정도 되는 말뭉치라면 요소 개수가 1억(10000 x 10000)이나 되는 큰 행렬을 만들어야 하는 것이죠. 이후 지금까지 설명드린 목적함수를 최소화하는 임베딩 벡터를 찾기 위해 matrix factorization을 수행해야 합니다. 계산복잡성이 꽤 크다는 이야기이죠.

어쨌거나 GloVe는 Word2Vec과는 다른 관점과 방식으로 단어를 임베딩하는 방법론으로 주목할 만한 것 같습니다. 마지막으로 GloVe의 임베딩 시각화 예시를 인용하는 것으로 설명을 마칠까 합니다. 의견이나 질문 있으시면 언제든지 이메일이나 댓글로 알려주시기 바랍니다. 여기까지 읽어주셔서 감사드립니다.



Comments