본문 바로가기
딥러닝으로 하루하루 씹어먹기

딥러닝 TA 모델 - BERT (5-3 - run_pretraining (NLLloss(masked_lm, NSP)))

by Yoo Sung Hyun 2022. 1. 10.
728x90

2022.01.08 - [딥러닝으로 하루하루 씹어먹기] - 딥러닝 TA 모델 - BERT (5-2 - run_pretraining (Transformer Encoder-Pooler))

 

딥러닝 TA 모델 - BERT (5-2 - run_pretraining (Transformer Encoder-Pooler))

2022.01.08 - [딥러닝으로 하루하루 씹어먹기] - 딥러닝 TA 모델 - BERT (5-1 - run_pretraining (Embedding)) 딥러닝 TA 모델 - BERT (5-1 - run_pretraining (Embedding)) 2022.01.07 - [딥러닝으로 하루하루 씹..

shyu0522.tistory.com

에서 이어집니다.

 

지난 시간까지, Transformer Encoder Architecture를 통과하며, 실제로 뭔가 예측을 진행하는 과정까지 보았다.

(전이학습의 실제 output layer로 사용될 sequence_output, [CLS]토큰만 이용하는 Pooler의 pooled_output)

 

이번 시간에는 모델을 학습함에 있어 가장 중요한 loss 계산방식을 알아볼 것이며,

일단 기본 BERT에서는 2가지 계산 방식을 활용한다. (masked_lm, NSP loss)

하나씩 알아보도록 하자

1. sequence_output(전체 예측 시퀀스)을 활용하는 masked_lm loss

문제집의 실제 문항이, 우리가 흔히 영어공부를 하다 마주할 수 있는, 마치 '빈칸 채우기'와 같은 문항의 채점이라고 볼 수 있다.

실제로 mask 된 위치만 찾아와서, 해당 단어를 맞게 예측했는지를 비교한다.

 

masked 위치를 찾는 과정과, loss가 계산되는 방식이 약간 흥미로운데,

아마 masked 위치를 찾는 과정은 그냥 for문으로 작성했을 수도 있었을 것이다. 하지만, BERT 소스에서는 label에 one hot encoding을 하여, 행렬곱을 취하는 형태로 표현하였다.

이렇게 하면, masked된 곳은 1이고 안된 곳은 0이 되어서, 결국 곱하면 해당 문장에서 masked로 예상되는 위치만 찾아올 수 있게된다.

또한 실제값과의 비교가, 어떤 단어의 CER 같은 것을 판단하는 것이 아닌, 가능도 자체를 이용해서 구하게 된다.

loss값 조차도, 역로그를 이용해서, 가능도 높은 곳을 잘 찾으면 loss가 낮아지고, 가능도 높은 곳이 잘못 찾은 것이었다면, loss가 높아지게 처리되어 있어,

예측과 실제 word를 비교함에 있어, 방식은 조금 복잡할 수 있으나, 일일이 비교하는 것 보다 훨씬 효율적으로 계산되어 있다.

(역우도 함수를 이용하여, loss를 계산하는 방식을 NLLloss라고 표현하며, Torch에서는 제공되는데 Tensor에서는 없어서, BERT에서는 직접 구현해놓은 것으로 판단된다.)

Negative Log Likelihood loss 부연설명용 URL 참조

https://gaussian37.github.io/dl-concept-nll_loss/

https://github.com/pytorch/pytorch/blob/master/torch/nn/modules/loss.py?hl=64#L64

https://pytorch.org/docs/stable/generated/torch.nn.NLLLoss.html

예시가 masked lm의 과정을 잘 설명하고 있다.

결국 masked_lm loss는, 총점/maksed문항수의 형태가 되어, 잘 맞춘 평균 점수가 된다. (잘 맞출 수록 낮게 나옴)

위에 dense layer를 쌓는 이유는, sequence_output은 fully_connected가 진행되지 않은 상태이기 때문이다. (그 이후에, embedding table과의 행렬곱으로 실제 단어를 찾아나감.)

masked_lm loss의 전체 시퀀스

2. pooled_output(함축된 [CLS] 정보)만을 활용하는 NSP loss

NSP loss는 masked_lm loss보다는 조금 더 간단하다. (그냥 역우도 loss를 이용한 2진 분류 문제)

pooled_output은 이미 fully_connected를 지나서 나온 결과물이기 때문에, next_sentence (0,1) 여부를 조사하기 위한, weight, bias 연산만 존재한다.

output은 reduce_mean이므로, 역시 동일하게 다음 문장을 잘 맞춘 평균 점수가 된다. (잘 맞출 수록 작아짐)

 

이후에는 trainable 파라미터들만 체크포인트 파일에 저장할 준비를 하면서, 실제로 학습 및 모델 저장을 위한 소스들과, 검증과정이 도래했을때, 검증을 어떤 템플릿으로 log할지에 대한 부분들이 작성되어있다.

 

때문에 fine-tunning 과정에서 학습을 하는 경우, 모델을 불러오더라도, 흔히 생각하는 keras에서의 모델 레이아웃까지 전부 호출되는 걸 생각하면 안되고, 모델에서 파라미터만 불러와서, 모델을 선언하여 파라미터를 갱신시켜주는, 과거의 tensorflow 1.때 model saver 기능 처럼 활용해야한다.

 

위와 같이 loss까지 전부 다 이해했다면, 한번 이전에 돌아가서 attention에서의 가중치와 임베딩의 변화를 예측해봤던 내용을 다시 한번 읽어보고 한번 예상되는 행위를 한번 생각해보는 것도 좋은 방법일 듯 하다.

('아, 임베딩 테이블은 이런식으로 구성될 것 같고, 학습이 어떤 의도로 진행되겠구나...'하고 말이다)

 

단어장 생성 -> 문제집 생성 -> 채점 및 학습의 과정을 전부 다 여러가지 가설들을 생각해보면서 지나왔다.

BERT 모델 논문과는 다른점이 있는 것 같은가?

실제로 언어 학습이 잘 될 것 같은가?

 

BERT논문과 사실 동일하게 구성되어있으며, 실제 언어 학습도 잘 되는 것으로 보인다.

(현재 내부 카테고리 분류 기준 정확도는 85%가 넘는 것 같다. 약 20개의 카테고리일때.)

 

하지만 여러 업체 도메인을 다루다보면 문제도 생기고, 잘 안되는 부분들이 생겼을때, 단순히 논문만 리딩하고 휴징페이스를 써서 빠르게 도입하는 것과, 이미 이렇게 한번 톺아보고 접근하는 것과는 시도해볼 수 있는 절차나 시간이 훨씬 적어지지 않을까 생각된다.

 

상당히 의미있었던 시간이었고, 이후에는 실제로 학습이 진행되는 Optimizer쪽을 볼 예정이다. (어찌보면 뇌에서 기억해나가는 방식이라 딱히 사람으로 비유할만한게 안떠오르네...ㅎㅎ)

그냥 무지성 아담 아니냐! 할 수 있지만, 학습이 좀 더 잘 되게 하기 위한, learning rate, weight decay에 관한 부분이나, 전이학습에서 사실상 기본이라고 볼 수 있는 warmup등에 대한 부가설명들도 있을테니, 한번 확인하고 넘어가길 바란다.

 

작년, 재작년? 이쯤에 BERT를 파볼때만 해도, 상세한 아티클들은 없고, 대부분 논문 리딩 위주의 사상적 접근들만 많았어서, 내가 빠르게 작성해서 올리면 많이 도움이 되겠구나 하고 진행했던게 어느덧 시간이 이렇게 흘러버렸다.

(내가 main lead하고, 신입 친구들 두명이서 진행하면서, 시계열 예측이나 다른 프로젝트들도 있다보니, 많이 늦어진 감이 있다.)

그래도 열심히 톺아보고, 고민해보고 공유하는 차원에서 작성해봤으니, 많은 분들에게 도움이 되길 바란다.

728x90

댓글