확률적인 관점
우선 CBOW 모델을 확률적인 개념을 적용하여 개선을 해보고자 한다. 확률의 표기법을 간단히 살펴보자. 확률의 표기법을 간단하게 살펴보자. 확률은 P(.)이라고 한다. A의 확률은 P(A)라고 표기한다는 것이다. P(A,B), A와 B가 동시에 일어날 확률을 말한다. P(A|B), 사건이 일어난 후의 확률 즉 사후확률인데 이는 다시 말해서 B라는 정보가 주어졌을 때, A가 일어날 확률을 말한다.
그럼 CBOW 모델을 확률 표기법으로 기술해보자. CBOW 모델이 하는 일은, 맥락을 주면 타깃 단어가 출현할 확률을 출력하는 것이다. 이번에는 아래와 같이 말뭉치를 w1, w2, w3, .... wt로 단어 시퀀스를 표기하고 t번째 단어에 대해 윈도우 크기가 1인 맥락을 고려하도록 하자.
이를 wt와 wt-1, wt+1간의 식으로 표현하면 아래와 같이 쓸 수 있다.
위 식을 이용하면 CBOW 모델의 손실 함수도 간결하게 표현할 수 있다. 교차 엔트로피 오차를 적용해보자.
이 식을 보듯, CBOW 모델의 손실 함수는 단순히 식-(1)의 확률에 log 를 취한 다음 마이너스를 붙이면 된다. 이를 negative log likelihood, 음의 로그 가능도라고 부른다. 추가로 식-(2)는 샘플 데이터 하나에 대한 손실 함수이며, 이를 말뭉치 전체로 확장하면 다음 식이 된다.
CBOW 모델의 학습이 수행하는 일은 이 손실 함수의 값을 가능한 작게 만드는 것이다. 그리고 이때의 가중치 매개변수가 얻고자 하는 단어의 분산 표현인 것이다.
Skip-gram 모델
word2vec은 2개의 모델을 제안하는데 하나는 지금까지 다룬 CBOW 모델이고 이번에는 skip-gram 모델을 살펴보고자 한다. skip-gram은 CBOW에서 다루는 맥락과 타깃을 역전시킨 모델이다. 아래 예시를 통해 살펴보자.
위 그림과 같이 skip-gram 모델은 중앙의 단어인 타깃으로부터 주변의 맥락을 추측한다.
skip-gram 모델은 아래와 같은 구조를 띈다.
위처럼 입력층은 하나이고 그에 반해 출력층은 맥락의 수만큼 존재한다. 따라서 각 출력층에서는 Softmax with Loss 계층 등을 이용해 개별적으로 손실을 구하고, 이 개별 손실들을 모두 더한 값을 최종 손실로 한다.
이번에는 skip-gram 모델을 확률 표기로 나타내보자. skip-gram 은 다음 식을 모델링한다.
skip-gram 모델에서는 맥락의 단어들 사이에 관련성이 없다고 가정하고, 다음과 같이 분해한다. (조건부 독립)
위 식을 교차 엔트로피 오차에 적용하여 skip-gram 모델의 손실 함수를 유도할 수 있다.
위 식에서 알 수 있듯, skip-gram 모델의 손실 함수는 맥락별 손실을 구한 다음 모두 더한다. 위 식은 샘플 데이터 하나짜리 skip-gram 의 손실 함수이다. 이를 말뭉치 전체로 확장하면 skip-gram 모델의 손실 함수는 다음과 같다.
CBOW 모델과 skip-gram 모델을 비교하면 단어 분산 표현의 정밀도 면에서 skip-gram 모델의 결과가 더 좋은 경우가 많기 때문에 skip-gram을 선택하는 것이 좋다. 특히 말뭉치가 커질수록 저빈도 단어나 유추 문제의 성능 면에서 skip-gram 모델이 더 뛰어난 경향이 있다. 반면, 학습 속도 면에서는 CBOW 모델이 더 빠르다. skip-gram 모델은 손실을 맥락의 수만큼 구해야 해서 계산 비용이 그만큼 커지기 때문이다. skip-gram 모델을 구현해보자.
import sys
sys.path.append('..')
import numpy as np
from common.layers import MatMul, SoftmaxWithLoss
class SimpleSkipGram:
def __init__(self, vocab_size, hidden_size):
V, H = vocab_size, hidden_size
# 가중치 초기화
W_in = 0.01 * np.random.randn(V, H).astype('f')
W_out = 0.01 * np.random.randn(H, V).astype('f')
# 계층 생성
self.in_layer = MatMul(W_in)
self.out_layer = MatMul(W_out)
self.loss_layer1 = SoftmaxWithLoss()
self.loss_layer2 = SoftmaxWithLoss()
# 모든 가중치와 기울기를 리스트에 모은다.
layers = [self.in_layer, self.out_layer]
self.params, self.grads = [], []
for layer in layers:
self.params += layer.params
self.grads += layer.grads
# 인스턴스 변수에 단어의 분산 표현을 저장한다.
self.word_vecs = W_in
def forward(self, contexts, target):
h = self.in_layer.forward(target)
s = self.out_layer.forward(h)
l1 = self.loss_layer1.forward(s, contexts[:, 0])
l2 = self.loss_layer2.forward(s, contexts[:, 1])
loss = l1 + l2
return loss
def backward(self, dout=1):
dl1 = self.loss_layer1.backward(dout)
dl2 = self.loss_layer2.backward(dout)
ds = dl1 + dl2
dh = self.out_layer.backward(ds)
self.in_layer.backward(dh)
return None
통계 기반 VS 추론 기반
두 기법은 학습하는 틀에서 큰 차이가 있었다. 통계 기반 기법에서는 말뭉치의 전체 통계로부터 1회 학습하여 단어의 분산 표현을 얻었다. 한편, 추론 기반 기법에서는 말뭉치를 일부분씩 여러 번 보면서 학습했다. 이는 미니배치 학습이라 불리는 자주 쓰이는 효과적인 기법이다. 이번에는 어휘에 추가할 새 단어가 생겨서 단어의 분산 표현을 갱신해야 하는 상황을 살펴보자. 통계 기반 기법에서는 계산을 처음부터 다시해야한다. 조금만 수정하고 싶어도 동시발생 행렬을 다시 만들고 SVD를 수행하는 작업을 다시 진행해야한다. 반면 추론 기반 기법은 지금까지 학습한 가중치를 초깃값으로 사용해 매개변수를 다시 학습할 수 있다.
이번에는 두 기법으로 얻는 단어의 분산 표현의 성격이나 정밀도 면에서는 어떤지 살펴보자. 우선 분산 표현은 주로 단어의 유사성이 인코딩된다. 한편 word2vec, 특히 skip-gram 모델에서는 단어의 유사성은 물론, 한층 복잡한 단어 사이의 패턴까지도 파악되어 인코딩된다. 특히 word2vec은 "king - man + woman = queen"과 같은 유추 문제를 풀 수 있다는 것이 유명한데 이런 특성을 방증하는 예시이다.
추론 기반 기법이 통계 기반 기법보다 정확하도고 흔히 오해하곤 한다. 하지만 단어의 유사성을 정량 평가해본 결과, 추론 기반과 통계 기반 기법의 유열을 가릴 수 없었다고 한다. 또한, 추론 기반 기법과 통계 기반 기법은 서로 관련되어 있다. 구체적으로는 skip-gram과 네거티브 샘플링을 이용한 모델은 모두 말뭉치 전체의 동시발생 행렬에 특수한 행렬 분해를 적용한 것과 같다.
더 나아가 word2vec 이후 추론 기반 기법과 통계 기반 기법을 융합한 GloVe 기법이 등장했다. GloVe 의 기본 아이디어는, 말뭉치 전체의 통계 정보를 손실 함수에 도입해 미니매치 학습을 하는 것이다.
'Drawing (AI) > DeepLearning' 카테고리의 다른 글
Building Distance Estimation (0) | 2024.08.14 |
---|---|
Multi-modal Learning (0) | 2024.03.20 |
딥러닝 직접 구현하기 - (word2vec 학습) (0) | 2024.02.02 |
딥러닝 직접 구현하기 - (word2vec) (0) | 2024.02.01 |
딥러닝 직접 구현하기 - (추론 기반 기법) (0) | 2024.02.01 |