CBOW 코드 구현 : Word2Vec의 CBOW 모델 구현

2022. 1. 7. 00:41NLP

저번 포스트에서는 논문 Efficient Estimation Of Word Representations In Vector Space를 소개하고 Word2Vec의 아키테쳐에 대해 설명했습니다. 이번에는 Word2Vec 중 CBOW 모델을 구현해보겠습니다. 

 

CBOW

Word2Vec은 단어출현 패턴을 학습해 단어의 분산표현을 도출합니다. 다중 클래스 분류이기 때문에 소프트맥스와 교차 엔트로피 오차만 사용하면 됩니다. 소프트맥스 함수를 이용해 점수를 확률로 변환하고, 그 확률과 정답레이블로부터 교차엔트로피 오차로 구한 손실을 사용해 학습합니다. 

 

CBOW는 Word2Vec에서 제시한 두개의 모델 중 하나입니다. CBOW는 주변단어로부터 중심단어를 예측합니다. 주변단어의 개수만큼 input layers가 있고 hidden layer를 거쳐 하나의 output layer로 출력됩니다. hidden layer의 뉴런은 input의 평균으로 구합니다. 

 

 

 1. CBOW 클래스

class SimpleCBOW:
    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')

        # 계층 생성
        # window 크기 만큼 입력층 만들어야함(여기에서는 2개)
        self.in_layer0 = MatMul(W_in) # 입력층
        self.in_layer1 = MatMul(W_in) # 입력층
        self.out_layer = MatMul(W_out) # 출력층
        self.loss_layer = SoftmaxWithLoss() # Softmax 계층

        # 모든 가중치와 기울기를 리스트에 모은다.
        layers = [self.in_layer0, self.in_layer1, self.out_layer]
        self.params, self.grads = [], []
        for layer in layers:
            self.params += layer.params
            self.grads += layer.grads

        # 인스턴스 변수에 단어의 분산 표현을 저장한다.
        self.word_vecs = W_in

- SimpleCBOW 클래스의 인수

vocab_size : 어휘 수

hidden_size : hidden layer의 뉴런 수

 

- W_in , W_out 두개의 가중치

각각 작은 무작위 값으로 초기화 됩니다. 

 

- 계층 생성

입력층의 Matmul 계층 주변단어 수(=윈도우 크기) 만큼 (여기서는 2개)

출력층의 Matmul 계층 1개

Softmax with Loss 계층 1개 ( softmax와 cross entropy error를 softmax with loss로 합침 )

 

2. 순전파

    def forward(self, contexts, target):
        h0 = self.in_layer0.forward(contexts[:, 0])
        h1 = self.in_layer1.forward(contexts[:, 1])
        h = (h0 + h1) * 0.5
        score = self.out_layer.forward(h)
        loss = self.loss_layer.forward(score, target)
        return loss

3. 역전파

    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

 

4. 학습

window_size = 1
hidden_size = 5 # 은닉층의 뉴런수
batch_size = 3
max_epoch = 1000

text = 'You say goodbye and I say hello.' # 학습데이터
corpus, word_to_id, id_to_word = preprocess(text)

vocab_size = len(word_to_id) # 어휘수
contexts, target = create_contexts_target(corpus, window_size) # 주변단어와 중심단어 생성
target = convert_one_hot(target, vocab_size) # one-hot encoding
contexts = convert_one_hot(contexts, vocab_size)

model_1 = SimpleCBOW(vocab_size, hidden_size)
optimizer = Adam() # 매개변수 갱신 알고리즘
trainer_1 = Trainer(model_1, optimizer)

trainer_1.fit(contexts, target, max_epoch, batch_size)

 

다음 포스팅에서는 이어서 Skip-gram 모델을 구현해보겠습니다! 

 

reference

Word2Vec  ⌜Efficient Estimation Of Word Representations In Vector Space⌟

밑바닥부터 시작하는 딥러닝2 (사이토 고키)

https://github.com/WegraLee/deep-learning-from-scratch-2/tree/master/ch03

https://www.kaggle.com/ashukr/implementation-of-word2vec-paper/data