while (1): study();

Ch3. Word2Vec 본문

독서

Ch3. Word2Vec

전국민실업화 2021. 7. 26. 21:54
728x90

 이전 장에서 통계 기반 방법론에 대해서 학습했고, 이번 장에서는 추론 기반 방법론에 대해 배웁니다. 가장 큰 차이점은 통계 기반 방법론이 전체 코퍼스에 대해서 1회 처리하는 데에 비해, 추론 기반 방법론은 미니배치 단위로 학습하는 것이 가능하다는 점입니다. 결과적으로 메모리 사용량을 획기적으로 줄일 수 있고, GPU로 병렬처리 시 시간도 단축할 수 있습니다. 다만 두 방법론은 모두 분포가설에서 출발하는 것은 같습니다.

 대표적인 모델로는 CBOW와 Skip-gram이 있습니다. CBOW(Continuous Bag-of-Words)는 맥락을 받아 타겟을 학습합니다. 아래와 같이 다수의 맥락을 배경지식으로 삼아 등장할 단어를 예측하는 것이 바로 CBOW입니다. 이때 입력과 연산되는 가중치, 즉 그림에서 $W_{Vxn}$은 모든 컨텍스트에 대해서 공유되며, 각각의 행이 단어의 분산표현이라고 볼 수 있습니다.

 다만 한 가지 걸리는 것은 굳이 입력 가중치를 분산표현으로 사용한다는 점입니다. 사실 출력 가중치도 열 단위가 분산 표현입니다. 최근 구글 공식 블로그에서 기계번역 연구에서 디코더의 언어모델이 인코더보다 중요하다는 글을 보기도 했고, Weight Tying에 관련한 논문을 보아도 디코더의 임베딩이 품질이 좋았다고 했으니 말입니다. 이것에 관해서는 나중에 실험해보겠습니다. 

CBOW Architecture

 CBOW를 직접 만들어서 학습을 시켜 보았습니다. 이전 장과 마찬가지로 'You say goodbye and I say hello.'라는 문장에 대해서만 학습하게 했습니다. 

CBOW

은닉층의 크기를 10으로 주고 총 10000에포크를 훈련시킨 모습입니다. 손실이 점차 하강하는 것을 보면 잘 학습되고 있음을 알 수 있습니다. 파라미터의 크기도 점점 커지고 있고, 그래디언트 크기는 점차 줄어가고 있습니다.

 

Compact simple CBOW

 다만 한 가지 걸리는 것이 있었습니다. 컨텍스트를 다수 입력받다 보니 레이어 개수가 많아지고, 가중치를 공유한다고 해도 연산량은 맥락의 개수만큼 늘어납니다. 본래 CBOW 아키텍처는 각 맥락의 은닉상태를 모두 평균냅니다. 그러나 원핫벡터의 특성상 입력을 미리 합쳐서 주어도 학습에는 전혀 지장이 없다는 것이 저의 생각이었습니다. 따라서 다수의 컨텍스트 벡터를 합쳐서 입력으로 주고, 모델도 단 하나의 Input layer만 존재하도록 수정했습니다.

입력 합치기

이 경우엔 평균을 내는 작업이 없어지므로, 역전파 과정에서도 덧셈 노드 연산과 곱셈 노드 연산을 한번씩 덜 할 수 있습니다. 학습 결과는 다음과 같습니다.

CBOW revised

똑같은 조건에서 학습시켰을 때 손실이 이전과 비슷한 수준까지 하강하는 데 비해 시간은 약 3분의 1이나 단축되었습니다! (2.01sec → 1.44sec) 또한 파라미터의 크기도 점차 커지고 있고, 그래디언트도 작아지고 있는 것을 확인할 수 있었습니다.

 

Compact simple Skip-gram

 그럼 이번엔 Skip-gram모델도 구현해보겠습니다. Skip-gram은 CBOW와는 반대로 타겟을 입력받아 맥락을 예측합니다. 즉 훨씬 더 어려운 과제라고 볼 수 있습니다. 구현은 CBOW와 굉장히 유사합니다.

Skip-gram Architecture

 하나의 타겟 단어를 받으면 그것을 배경지식 삼아서 주변의 다수의 단어들을 예측하는 것이 바로 Skip-gram입니다. 기본적인 학습 결과는 다음과 같습니다.

 최종 출력되는 손실은 모든 맥락의 손실이 더해진 값입니다. 따라서 하나의 맥락에 대해서 약 0.9정도의 손실을 보이고 있다고 할 수 있습니다. 역시 더 어려운 문제라 그런지 푸는 것을 더 힘들어하네요.

100000 epochs

10만 번을 학습해보니 지속적으로 손실이 감소하긴 합니다만, 감소폭이 적은 것을 보면 어느 정도는 최적점에 수렴한 듯 합니다. 

 CBOW가 단순히 다수의 입력에 대해 순방향 전파만 해주는 것이었다면 Skip-gram은 조금 더 시간이 오래 걸립니다. 이는 다수의 맥락에 대해 순전파 + 손실 계산까지 해주어야하기 때문입니다. 만약 Skip-gram도 CBOW처럼 컴팩트하게 만들 수 있다면 정말 좋을 것 같아서 실험해보았습니다. 학습데이터의 정답으로 주는 맥락을 각 단어가 아니라 합쳐서 주도록 하고, 모델도 이에 맞춰 구성을 변경했습니다.

def _cross_entropy_error(y, y_hat, eps=1e-4):
    if y.ndim == 1:
        y = y.reshape(1, y.size)
        y_hat = y_hat.reshape(1, y_hat.size)
    if y.size != y_hat.size:
        y = convert_one_hot(y, vocab_size=y_hat.shape[1])

    batch_size = y_hat.shape[0]
    # print(y * np.log(y_hat + eps))
    return -np.sum(y * np.log(y_hat + eps)) / batch_size

 실험 전에 기존의 교차엔트로피를 조금 손볼 필요가 있습니다. 기존에 교차 엔트로피를 구할 때 정답값의 로그 확률만 계산했기 때문에, 두개의 맥락에 대해 손실을 구하려면 raw하게 벡터를 곱하는 방식을 사용해야 합니다.

Skip-gram revised

 손실이 시작점부터 급격하게 발산하고 있음을 알 수 있습니다. 원인을 찾아보니, 소프트맥스 함수가 문제였습니다.

소프트맥스에 입력으로 주어진 점수

모델 자제가 반환하는 점수가 굉장히 높습니다. 그러다보니 exp값을 안정적으로 계산하기 위해 최대값으로 스케일링해주면 다음과 같이 변합니다. (다른 테스트 케이스의 결과라서 차이가 있습니다)

 0이 되버린 최대값 이외에 나머지 값들이 심각하게 작은 것을 알 수 있습니다. 결과적으로 음의 무한대가 나온 값들이 0으로 반환된 것입니다. 

 사실 실제 정답인 값들이 대체로 높은 점수를 받긴 했습니다. 다만 단위가 심각하게 큰 것이 문제입니다. 개인적인 생각으로는, 정답이 두 개이다보니까 하나를 예측해도 다른 쪽에서 손실이 커져버리니, 전체적으로 점수를 다 높혀버리는 방식을 모델이 선택한 것 같습니다. 그러다보니 가중치가 아예 발산해버리는 듯합니다. 아무래도 다른 방식의 스케일링이 필요할 듯하여, 최대값으로 나누는 스케일링을 가해보겠습니다.

다음과 같이 2차원이 입력으로 주어졌을 때(현재 케이스입니다) 가장 큰 값으로 나누도록 했고, 일관성을 위해서 분모도 같은 연산을 해주었습니다.

Scaling softmax

 이제는 발산하지 않고 학습이 되긴 하는 모습입니다만, 문제점은 오차가 잘 전파되지 않는다는 것입니다. 정답이 두 개이다 보니, 소프트맥스 값이 그만큼 분산되어 버리고, 따라서 역전파 과정에서 뒤로 보내져야하는 오차마저도 분산되어버려 결과적으로 학습이 잘 진행되지 않습니다. 실제로 위의 결과는 학습률을 무려 10으로 주었고, 그럼에도 불구하고 10만 에포크에 손실이 약 0.1 정도 하락한 모습입니다. 아무래도 출력을 합치는 것은 쉽지 않은 일인 듯 합니다..

 

 통계 기반 방법과 추론 기반 방법을 모두 살펴보았습니다만, 사실 둘 간의 우열을 가리기는 힘들다고 합니다. 사실 저도 추론 기반 방법만 있으면 된다고 생각했는데 의외네요. 이것에 관련해서 논문이 있다고 하니 조만간 흥미롭게 읽어볼 예정입니다.

https://aclanthology.org/Q15-1016/

 

Improving Distributional Similarity with Lessons Learned from Word Embeddings

Omer Levy, Yoav Goldberg, Ido Dagan. Transactions of the Association for Computational Linguistics, Volume 3. 2015.

aclanthology.org

 

728x90

'독서' 카테고리의 다른 글

Ch5. 순환 신경망(RNN)  (0) 2021.07.28
Ch4. Word2Vec 속도 개선  (0) 2021.07.27
Ch2. 자연어와 단어의 분산 표현  (0) 2021.07.25
Ch1. 신경망 복습  (0) 2021.07.24
Ch7. 합성곱 신경망  (0) 2021.07.22
Comments