for textmining

Word2Vec의 학습 방식

|

이번 포스팅에서는 최근 인기를 끌고 있는 단어 임베딩(embedding) 방법론인 Word2Vec에 대해 살펴보고자 합니다. Word2Vec은 말 그대로 단어를 벡터로 바꿔주는 알고리즘입니다. Neural Network Language Model(NNLM)을 계승하면서도 학습 속도와 성능을 비약적으로 끌어올려 주목을 받고 있습니다. 이번 포스팅은 Word2Vec의 기본 내용은 이미 알고 있다고 전제하고 학습 과정을 중심으로 설명하려고 하는데요, 일반적인 내용을 보시려면 이곳을, Word2Vec을 활용한 어플리케이션 사례를 보시려면 이곳을, Word2Vec의 전신 격인 NNLM을 보시려면 이곳을 참고하시면 좋을 것 같습니다. 이번 글은 고려대 강필성 교수님 강의와 이곳을 참고했음을 먼저 밝힙니다. 그럼 시작하겠습니다.

word2vec 학습 파라메터

Word2Vec의 아키텍처(skip-gram)를 도시하면 아래 그림과 같습니다. 은닉층이 하나인 간단한 뉴럴네트워크 구조입니다.

위 구조에서 핵심은 가중치행렬 $W$, $W’$ 두 개입니다. Word2Vec의 학습결과가 이 두 개의 행렬이거든요. 그림을 자세히 보시면 입력층-은닉층, 은닉층-출력층을 잇는 가중치 행렬의 모양이 서로 전치(transpose)한 것과 동일한 것을 볼 수 있습니다. 그런데 전치하면 그 모양이 같다고 해서 완벽히 동일한 행렬은 아니라는 점에 주의할 필요가 있습니다(저는 이것 때문에 엄청 헷갈렸어요). 물론 두 행렬을 하나의 행렬로 취급(tied)하는 방식으로 학습을 진행할 수 있고, 학습이 아주 잘되면 $W$와 $W’$ 가운데 어떤 걸 단어벡터로 쓰든 관계가 없다고 합니다.

그럼 입력층과 은닉층을 잇는 가중치행렬 $W$을 좀 더 자세히 살펴보겠습니다. 위 그림과 아래 그림을 비교하면서 보시면 좋을 것 같은데요, $V$는 임베딩하려는 단어의 수, $N$은 은닉층의 노드 개수(사용자 지정)입니다. Word2Vec은 최초 입력으로 one-hot-vector를 받는데요, $1×V$ 크기의 one-hot-vector의 각 요소와 은닉층의 $N$개 각 노드는 1대1 대응이 이뤄져야 하므로 가중치행렬 $W$의 크기는 $V×N$이 됩니다. 예컨대 학습 말뭉치에 단어가 1만개 있고 은닉층 노드를 300개로 지정했다고 칩시다. 그럼 가중치행렬 $W$는 좌측 하단의 오렌지색 행렬 형태가 됩니다.

Word2Vec 아키텍처는 중심단어로 주변단어를 맞추거나, 주변단어로 중심단어를 더 잘 맞추기 위해 가중치행렬인 $W$, $W’$을 조금씩 업데이트하면서 학습이 이뤄지는 구조입니다. 그런데 여기서 흥미로운 점은 $W$가 one-hot-encoding된 입력벡터와 은닉층을 이어주는 가중치행렬임과 동시에 Word2Vec의 최종 결과물인 임베딩 단어벡터의 모음이기도 하다는 사실입니다.

아래와 같이 단어가 5개뿐인 말뭉치에서 Word2Vec을 수행한다고 가정해 봅시다. 사전 등장 순서 기준으로 네번째 단어를 입력으로 하는 은닉층 값은 아래처럼 계산됩니다. 보시다시피 Word2Vec의 은닉층을 계산하는 작업은 사실상 가중치행렬 $W$에서 해당 단어에 해당하는 행벡터를 참조(lookup)해 오는 방식과 똑같습니다.

학습이 마무리되면 이 $W$의 행벡터들이 각 단어에 해당하는 임베딩 단어벡터가 됩니다.

Word2Vec 입력과 Skip-Gram

Word2Vec의 Skip-Gram(중심단어로 주변단어 예측)이 말뭉치로부터 어떻게 입력값과 정답을 만들어내는지 살펴보겠습니다. ‘The quick brown fox jumps over the lazy dog.’ 문장으로 시작하는 학습말뭉치가 있다고 칩시다. 윈도우(한번에 학습할 단어 개수) 크기가 2인 경우 아키텍처가 받는 입력과 정답은 아래와 같습니다.

자, 그럼 학습의 첫번째 스텝(첫째줄)을 볼까요? 중심단어는 처음 등장하는 단어인 ‘The’입니다. 윈도우 크기가 2이기 때문에 중심단어 앞뒤로 두개씩 봐야 하지만, ‘The’를 기준으로 이전 단어가 존재하지 않으므로 어쩔 수 없이 중심단어 뒤에 등장하는 두 개 단어(quick, brown)만 학습에 써야겠네요.

그런데 여기서 주의할 것은 학습할 때 ‘quick’과 ‘brown’을 따로 떼어서 각각 학습한다는 점입니다. 부연하자면 중심단어에 ‘The’를 넣고 ‘quick’을 주변단어 정답으로 두어서 한번 학습하고, 또 다시 ‘The’를 중심단어로 하고 ‘brown’을 주변단어로 해서 한번 더 학습한다는 얘기입니다.

이렇게 첫번째 스텝이 끝나면 중심단어를 오른쪽으로 한칸 옮겨 ‘quick’을 중심단어로 하고, ‘The’, ‘brown’, ‘fox’를 각각 주변단어 정답으로 두는 두번째 스텝을 진행하게 됩니다. 이런 식으로 말뭉치 내에 존재하는 모든 단어를 윈도우 크기로 슬라이딩해가며 학습을 하면 iteration 1회가 마무리됩니다.

주변단어로 중심단어를 예측하는 CBOW에 비해 Skip-gram의 성능이 좋은 이유가 바로 여기에 있습니다. 언뜻 생각하기에는 주변의 네 개 단어(윈도우=2인 경우)를 가지고 중심단어를 맞추는 것이 성능이 좋아보일 수 있습니다. 그러나 CBOW의 경우 중심단어(벡터)는 단 한번의 업데이트 기회만 갖습니다.

반면 윈도우 크기가 2인 Skip-gram의 경우 중심단어는 업데이트 기회를 4번이나 확보할 수 있습니다. 말뭉치 크기가 동일하더라도 학습량이 네 배 차이난다는 이야기이죠. 이 때문에 요즘은 Word2Vec을 수행할 때 Skip-gram으로 대동단결하는 분위기입니다.

Word2Vec의 학습

Word2Vec의 Skip-gram은 아래 식을 최대화하는 방향으로 학습을 진행합니다. 아래 식 좌변은 중심단어(c)가 주어졌을 때 주변단어(o)가 나타날 확률이라는 뜻입니다. 식을 최대화하려면 우변의 분자는 키우고 분모는 줄여야 합니다.

\[P(o|c)=\frac { exp({ u }_{ o }^{ T }{ v }_{ c }) }{ \sum _{ w=1 }^{ W }{ exp({ u }_{ w }^{ T }{ v }_{ c }) } }\]

우선 우변의 $v$는 입력층-은닉층을 잇는 가중치 행렬 $W$의 행벡터, $u$는 은닉층-출력층을 잇는 가중치 행렬 $W’$의 열벡터입니다.

우변 분자의 지수를 키우는 건 중심단어(c)에 해당하는 벡터와 주변단어(o)에 해당하는 벡터의 내적값을 높인다는 뜻입니다. 벡터 내적은 코사인이므로 내적값 상향은 단어벡터 간 유사도를 높인다는 의미로 이해하면 될 것 같습니다.

분모는 줄일 수록 좋은데요, 그 의미는 윈도우 크기 내에 등장하지 않는 단어들은 중심단어와의 유사도를 감소시킨다는 정도로 이해하면 될 것 같습니다.

아래는 Word2Vec의 학습파라메터인 중심단어 벡터 업데이트 과정을 수식으로 정리한건데요, 저도 정리 용도로 남겨 두는 것이니 수식이 골치 아프신 분들은 스킵하셔도 무방할 것 같습니다. 중심단어 벡터의 그래디언트는 아래와 같습니다.

\[\begin{align*} \frac { \partial }{ \partial { v }_{ c } } \ln { P(o|c) } &=\frac { \partial }{ \partial { v }_{ c } } \ln { \frac { exp({ u }_{ o }^{ T }{ v }_{ c }) }{ \sum _{ w=1 }^{ W }{ exp({ u }_{ w }^{ T }{ v }_{ c }) } } } \\ &=\frac { \partial }{ \partial { v }_{ c } } { u }_{ o }^{ T }{ v }_{ c }-\frac { \partial }{ \partial { v }_{ c } } \ln { \sum _{ w=1 }^{ W }{ exp({ u }_{ w }^{ T }{ v }_{ c }) } } \\ &={ u }_{ o }^{ T }-\frac { 1 }{ \sum _{ w=1 }^{ W }{ exp({ u }_{ w }^{ T }{ v }_{ c }) } } (\sum _{ w=1 }^{ W }{ exp({ u }_{ w }^{ T }{ v }_{ c }) } \cdot { u }_{ w })\\ &={ u }_{ o }^{ T }-\sum _{ w=1 }^{ W }{ \frac { exp({ u }_{ w }^{ T }{ v }_{ c }) }{ \sum _{ w=1 }^{ W }{ exp({ u }_{ w }^{ T }{ v }_{ c }) } } } \cdot { u }_{ w }\\ &={ u }_{ o }^{ T }-\sum _{ w=1 }^{ W }{ P(w|c)\cdot } { u }_{ w } \end{align*}\]

이렇게 구한 중심단어 그래디언트의 반대 방향으로 조금씩 중심단어 벡터를 업데이트합니다. (여기서 알파는 사용자가 지정하는 학습률=learning rate)

\[{ v }_{ c }^{ t+1 }={ v }_{ c }^{ t }+\alpha ({ u }_{ o }^{ T }-\sum _{ w=1 }^{ W }{ P(w|c)\cdot } { u }_{ w })\]

Word2Vec의 학습 트릭

subsampling frequent words

Word2Vec의 파라메터는 앞서 설명드린대로 $W$, $W’$입니다. 각각 크기가 $V$ x $N$, $N$ x $V$인데요. 보통 말뭉치에 등장하는 단어수가 10만개 안팎이라는 점을 고려하면 $N$(임베딩 차원수, 사용자 지정)이 100차원만 되어도 2000만개(2 x 10만 x 100)나 되는 많은 숫자들을 구해야 합니다. 단어 수가 늘어날 수록 계산량이 폭증하는 구조입니다.

이 때문에 Word2Vec 연구진은 말뭉치에서 자주 등장하는 단어는 학습량을 확률적인 방식으로 줄이기로 했습니다. 등장빈도만큼 업데이트 될 기회가 많기 때문입니다.

Word2Vec 연구진은 i번째 단어($w_i$)를 학습에서 제외시키기 위한 확률은 아래와 같이 정의했습니다.

\[P({ w }_{ i })=1-\sqrt { \frac { t }{ f({ w }_{ i }) } }\]

위 식에서 $f(w_i)$는 해당 단어가 말뭉치에 등장한 비율(해당 단어 빈도/전체 단어수)를 말합니다. $t$는 사용자가 지정해주는 값인데요, 연구팀에선 0.00001을 권하고 있습니다.

만일 $f(w_i)$가 0.01로 나타나는 빈도 높은 단어(예컨대 조사 ‘은/는’)는 위 식으로 계산한 $P(w_i)$가 0.9684나 되어서 100번의 학습 기회 가운데 96번 정도는 학습에서 제외하게 됩니다. 반대로 등장 비율이 적어 $P(w_i)$가 0에 가깝다면 해당 단어가 나올 때마다 빼놓지 않고 학습을 시키는 구조입니다. subsampling은 학습량을 효과적으로 줄여 계산량을 감소시키는 전략입니다.

negative sampling

Word2Vec은 출력층이 내놓는 스코어값에 소프트맥스 함수를 적용해 확률값으로 변환한 후 이를 정답과 비교해 역전파(backpropagation)하는 구조입니다.

그런데 소프트맥스를 적용하려면 분모에 해당하는 값, 즉 중심단어와 나머지 모든 단어의 내적을 한 뒤, 이를 다시 exp를 취해줘야 합니다. 보통 전체 단어가 10만개 안팎으로 주어지니까 계산량이 어마어마해지죠.

이 때문에 소프트맥스 확률을 구할 때 전체 단어를 대상으로 구하지 않고, 일부 단어만 뽑아서 계산을 하게 되는데요. 이것이 바로 negative sampling입니다. negative sampling은 학습 자체를 아예 스킵하는 subsampling이랑은 다르다는 점에 유의하셔야 합니다.

negative sampling의 절차는 이렇습니다. 사용자가 지정한 윈도우 사이즈 내에 등장하지 않는 단어(negative sample)를 5~20개 정도 뽑습니다. 이를 정답단어와 합쳐 전체 단어처럼 소프트맥스 확률을 구하는 것입니다. 바꿔 말하면 윈도우 사이즈가 5일 경우 최대 25개 단어를 대상으로만 소프트맥스 확률을 계산하고, 파라메터 업데이트도 25개 대상으로만 이뤄진다는 이야기입니다.

윈도우 내에 등장하지 않은 어떤 단어($w_i$)가 negative sample로 뽑힐 확률은 아래처럼 정의됩니다. $f(w_i)$는 subsampling 챕터에서 설명한 정의와 동일합니다.

\[P({ w }_{ i })=\frac { { f({ w }_{ i }) }^{ { 3 }/{ 4 } } }{ \sum _{ j=0 }^{ n }{ { f({ w }_{ j }) }^{ { 3 }/{ 4 } } } }\]

참고로 subsampling과 negative sampling에 쓰는 확률값들은 고정된 값이기 때문에 학습을 시작할 때 미리 구해놓게 됩니다.

Word2Vec 학습 시각화

아래는 Word2Vec 학습 과정을 예쁘게 시각화한 사이트입니다. 이곳에 방문하시면 단어벡터의 업데이트 과정을 실시간으로 눈으로 확인하실 수 있습니다.

마치며

이상으로 Word2Vec의 학습 과정을 전반적으로 살펴보았습니다. Word2Vec은 중심단어와 주변단어 벡터의 내적이 코사인 유사도가 되도록 단어벡터를 벡터공간에 임베딩합니다. 학습 말뭉치를 만드는 방식이나 subsampling, negative sampling 등의 기법을 통해 성능은 끌어올리고 계산복잡성은 낮추었습니다. 은닉층이 하나인 뉴럴네트워크 구조이나 하이퍼볼릭탄젠트, 시그모이드 등 비선형 활성함수를 사용하지 않아서 사실상 선형모델인 점도 눈여겨볼 점인 것 같습니다. 성능과 계산복잡성 두 마리 토끼를 모두 잡는 데 성공했기 때문에 요즘 들어 많은 관심을 받고 있다고 생각합니다. 의견이나 질문 있으시면 언제든지 이메일이나 댓글로 알려주시기 바랍니다. 여기까지 읽어주셔서 진심으로 감사드립니다.



Comments