지난 시간에는 통계 기반을 통해 단어의 분산 표현을 얻어내는 방법에 대해서 알아보았습니다.
[NLP#2] 통계 기반
저번 포스팅에서는 thesaurus를 통해 컴퓨터에게 자연어의 의미 전달하는 방법에 대해 알아보았습니다. 2023.05.12 - [Deep Learning] - [NLP#1] 자연어 처리란? - thesaurus 이번 글에서는 저번 시간에 이어서
just-data.tistory.com
이번 시간에는 추론 기반 방법의 방법 중 word2vec에 대해서 알아보도록 하겠습니다.
먼저 통계 기반의 문제점에 대해서 얘기해 보겠습니다.
간단하게 과정을 되새겨 보면, 동시발생행렬을 통해 PPMI 행렬을 얻게 되었고 SVD를 적용시켜 dense vector로 변환시켰습니다.
하지만 실제 자연어 처리에서 다루는 corpus의 크기는 어마어마합니다. 따라서 어휘수가 늘어나는 만큼 거대해진 행렬을 SVD에 적용시키는 것은 현실적이지 않습니다.
그리고 통계 기반 기법은 전체 데이터를 이용해서 한 번의 분석을 하게 됩니다. 그래서 중간에 단어가 추가돼서 다시 분석해야 될 때 전체를 다시 실행시켜야 된다는 단점도 존재합니다.
이러한 문제점을 보완하기 위해 고안된 추론 기반 기법은 신경망을 이용해서 미니배치로 학습하게 됩니다.
최근에 GPU의 발전으로 인해 미니배치 학습의 효율이 올라가면서 추론 기반 기법이 큰 호응을 얻고 있습니다.
※ 하지만 모든 부분에서 추론 기반 방법이 최고다라는 말은 아닙니다. 정확도 측면에서는 두 기법 모두 비슷하다는 내용의 논문들도 존재합니다.
추론 기반 기법
추론 기반 방법에서는 맥락이 주어졌을 때, target에 무슨 단어가 어울릴지를 추측하는 방법을 의미합니다. 추론 문제를 풀고 학습하면서 단어의 출현 패턴을 학습하게 됩니다.
통계 기반 기법에서 보았던 그림이랑 비슷하지 않나요?
차이점이라고 하면 이전에서는 근처 단어의 횟수에 집중하였다면 여기서는 target 단어가 어떤 단어일지를 추론하는 것이라고 할 수 있습니다.
하지만 추론 기반 기법도 통계 기반 기법처럼 분포 가설에 기초하기 때문에 기본 전제는 같다고 할 수 있습니다.
그래서 모델 관점에서 simple하게 생각해 본다면 softmax activation 함수를 통해 target 단어의 확률을 높여주는 작업이라고 할 수 있습니다.
위의 그림처럼 모델에 맥락을 input 하려면 단어를 고정 길이 벡터로 변환해야 합니다.
이때 one-hot-encoding을 사용하게 됩니다.
one-hot-encoding은 단어 집합의 크기를 벡터의 차원으로 하고, 표현하고 싶은 단어의 인덱스에 1의 값을 부여하고, 다른 인덱스에는 0을 부여하는 단어 벡터 표현 방식입니다.
이렇게 표현된 벡터를 one-hot vector(representation)라고 합니다.
예를 들어 "You say goodbye and I say hello."라는 corpus를 one-hot encoding을 사용하면 아래와 같습니다.
원핫 벡터를 이용하게 되면 다음과 같이 모델에 사용할 수 있게 됩니다.
이제 fully connected layer를 통해 corpus를 은닉층으로 변환하는 것에 대해 알아보도록 하겠습니다.
만일 단어는 7개인데 8차원으로 단어를 나타내면 어떻게 될까요? 단어의 특징이 제대로 반영이 되지 않을 수 있습니다. 따라서 corpus의 특징을 확인하기 위해서는 특징의 개수를 꽤 적게 가져가야 합니다.(농축된 feature라고 할 수 있습니다.)
※ 나중에 나오지만 이를 embedding size라고 부르게 됩니다.
그런데 기존 딥러닝 모델과 달리 위의 모델은 input이 one hot vector이기 때문에 은닉층으로 가기 위해 가중치와 곱해지면 은닉층은 해당 input의 1인 인덱스의 가중치 값 그대로 나오게 됩니다.
따라서 나중에 학습을 마쳤을 때 가중치의 각 행이 해당 단어의 분산 표현이 되게 됩니다.
※ 이 부분이 이번 포스팅에서 가장 중요한 직관입니다. 지금은 바로 이해가 되지 않을 수도 있으니까 계속 고민해 보시면 좋을 것 같습니다.
왜냐하면 one hot vector이기 때문에 가중치를 학습시키는 것은 단어 자체의 특징을 학습한다고 할 수 있습니다.
여기서 우리가 NLP를 하는 근본적인 이유를 상기시켜 봅시다.
우리는 단어를 컴퓨터에게 이해시키기 위해 통계 기반 방법이든 추론 기반 방법이든 단어의 고정 길이 벡터에 대해서 얻고 싶은 것입니다.
즉, 우리는 잘 학습된 가중치가 목적인 것입니다.
이러한 느낌을 가지고 다음으로는 word2vec의 두 가지 관점에 대해 알아보도록 하겠습니다.
CBOW
CBOW 모델은 주변 단어들로 부터 중앙 단어를 추측하는 용도의 신경망입니다.
예를 들어, you, goodbye를 통해 say를 예측하도록 만든 모델입니다.
바로 모델의 구조를 확인해 보겠습니다.
직관적으로 이해가 되시나요?
위의 사진은 양 옆의 1개의 단어를 통해 중앙 단어를 예측하는 모델의 구조입니다.
만약 양 옆의 2개의 단어를 고려한다면 총 4개의 입력층이 필요하겠죠?
※ 이렇게 옆에 몇 개의 단어를 이용할지를 의미하는 단어가 window size입니다. 또한 입력층부터 은닉층까지를 Encoder, 은닉층부터 출력층까지를 Decoder라고 부릅니다.
skip-gram
CBOW와 반대로 중앙의 단어로부터 주변의 여러 단어를 추측하는 모델입니다.
위에서 보듯, 모델의 입력층이 하나입니다. 반면 출력층은 맥락의 수만큼 존재하게 됩니다.
따라서 각 출력층의 loss를 모두 더한 값을 최종 loss로 정의합니다.
워낙 직관적으로 설계된 모델이기 때문에 모델의 간단한 설명은 여기까지 하고 이제부터는 모델의 각 부분에 대해 깊게 얘기해보고자 합니다. 이때, CBOW 모델을 기준으로 설명드리도록 하겠습니다.
학습 과정
먼저 모델에 넣을 데이터를 만들어봅시다.
CBOW를 기준으로 신경망의 input은 context입니다.
또한 정답은 중앙의 단어입니다.
위와 같은 데이터의 형태로 변환하는 과정은 다음과 같습니다.
※ window size는 1로 설정했습니다.
생성된 데이터를 모델에 넣으면서 학습을 시작하게 됩니다.
출력층의 softmax activation function을 통해 loss를 계산해서 가중치를 업데이트하게 됩니다.
이때 입력층이 두 개이기 때문에 은닉층에 들어가는 값은 두 입력층의 값의 평균이 들어가게 됩니다.
또한 입력층의 각 가중치가 모두 W_in인 부분도 주의해서 보셔야 합니다.
softmax이기 때문에 loss funciton은 cross entropy error를 사용하게 됩니다.
※ 지금은 문제없어 보이지만 아래에서 더 나은 방법으로 개선해 보겠습니다.
이때 가중치를 다음 층에 전달하기 위해서 backpropagation을 진행하게 되는데요.
forward(순전파)에서 one hot vector로 인해 가중치의 특정 행을 다음 층으로 흘려보내기 때문에 backward(역전파)도 앞층으로부터 전해진 기울기를 다음 층의 특정행으로 흘려보내게 됩니다.
하지만 학습을 마치고 나면 한 가지 궁금증이 생기게 됩니다.
결론적으로 W_in에는 맥락으로 인한 가중치가 생성되게 되고, W_out에는 target 단어로 인한 가중치가 생성됩니다.
그럼 어느 가중치를 단어의 분산표현으로 사용하면 되는지에 대한 궁금증입니다.
크게 3가지의 경우의 수가 있습니다.
- 입력층의 가중치를 이용
- 출력층의 가중치를 이용
- 양쪽 가중치를 모두 이용(합치거나 평균 등)
대중적인 선택은 '입력층의 가중치를 이용'입니다.
하지만 연구마다 주장하는 바는 다릅니다.
※ GloVe에서는 두 가중치를 더했을 때 좋은 결과를 얻었다고 합니다.
이해를 돕기 위해 위의 예시를 이용해서 word2vec 훈련하는 과정을 시각적으로 보여드리겠습니다.
훈련이 진행될수록 초기화된 벡터들이 유사한 단어들끼리 묶이는 것을 확인할 수 있습니다.
하지만 작은 corpus로 인해 정확한 단어 표현은 아닐 겁니다.
※ epoch는 500으로 설정했습니다.
다른 예시로도 해보고 싶으신 분은 아래의 홈페이지를 통해 들어가시면 사용하실 수 있습니다.
https://ronxin.github.io/wevi/
word2vec 개선
지금까지 알려드린 word2vec은 개념적인 측면에서는 크게 문제없지만 몇 개의 구현 상의 문제점이 존재합니다.
- 입력층의 행렬 곱셈 계산량 문제
- softmax 계산량 문제
이제부터 위의 문제점들을 개선해 보도록 하겠습니다.
CBOW 모델은 중간 단어의 양 옆 단어를 이용해 target 단어를 추측하였습니다. 이때, one-hot vector로 이루어진 입력층으로 이루어져 있습니다.
만약 특정 단어의 분산표현을 확인하려면 one-hot vector와 가중치를 곱해야 했습니다.
잠시 생각해 보면 조금 이상하지 않나요?
그냥 인덱싱 하면 되는 일인데 가중치의 특정 행을 추출하기 위해 행렬 곱을 해야 하는 것은 비효율적입니다.
만일 단어가 100만 개가 넘어갔을 때의 행렬 곱의 계산량과 메모리의 소비는 기하급수적으로 늘어날 것입니다.
따라서 이 문제를 해결하는 방법은 바로 앞에서 얘기했던 것처럼 인덱싱하는 것입니다. 즉, 필요한 부분만 추출하는 것입니다. 이는 코딩적인 측면에서 해결해야 하는 문제이기 때문에 개념만 알려드리고 넘어가도록 하겠습니다.
이렇게 corpus가 증가하게 되면서 생기는 문제 중 하나가 손실 함수의 계산량 문제입니다.
기존 모델에서는 softmax에 의해 분류된 출력층의 확률 값을 정답 레이블과 비교하는 것입니다.
이 문제는 softmax의 식을 살펴보면 그 이유를 알 수 있습니다.
어휘 수가 100만 개일 때의 식을 확인해 보겠습니다.
식에서 알 수 있듯이 분모의 값을 얻으려면 exp 계산을 100만 번 수행해야 합니다.
따라서 softmax를 대신할 계산 방법이 필요합니다.
이러한 계산 방법이 네거티브 샘플링(negative sampling)입니다.
네거티브 샘플링에서는 다중 분류 문제를 이진 분류 방식으로 문제를 해결합니다.
softmax에서는 맥락이 주어졌을 때 target 단어들의 확률분포를 생성해줘 라는 의미였습니다.
하지만 네거티브 샘플링에서는 맥락이 주어졌을 때 target 단어가 X야?라는 방식을 사용합니다.
따라서 출력층이 아래와 같이 softmax가 아닌 sigmoid로 정의됩니다.
이때 target 단어에 해당하는 특정 열벡터만 추출하여 sigmooid를 통해 확률이 도출됩니다.
그리고 손실함수인 cross entropy error를 통해 loss를 계산하게 됩니다.
여기서 하나의 개념만 더 추가해 보겠습니다.
지금까지는 target 단어에 대해서 긍정적 예에 대해서만 학습을 하였습니다.
그러나 부정적인 예에 대해서도 학습을 해야 합니다.
즉, 긍정적인 예에 서는 출력을 1로 가깝게 만들고, 부정적인 예에 대해서는 출력을 0에 가깝게 만드는 것입니다.
모든 부정적 예를 대상으로 학습할 수 없기 때문에 근사적인 방법으로 몇 개(5개 또는 10개)의 부정적 예를 샘플링하여 손실을 계산하게 됩니다.
여기서 샘플링은 corpus의 각 단어의 출현 횟수의 확률분포를 통해 분포대로 단어를 랜덤 샘플링하는 것입니다.(많이 나오는 단어 위주로 선택하는 것입니다.)
※ 이렇게 하는 이유는 적게 나오는 단어는 중요도가 낮기 때문입니다. 그리고 많이 쓰이지 않기 때문에 효율적이지 않죠.
최종적으로 네거티브 샘플링은 긍정적 예를 타깃으로 한 경우의 손실을 구하면서 동시에 부정적 예를 몇 개 샘플링하여 손실을 구한 총손실을 구하는 기법이라고 할 수 있습니다.
평가 방법
앞에서 word2vec을 개선시킴으로 우리는 더 빠른 속도의 word2vec을 통해 단어의 분산 표현을 얻게 되었습니다.
그럼 그 단어 분산 표현이 좋은지 어떻게 평가할까요?
단어의 분산 표현을 만드는 시스템은 여러 시스템과 함께 사용되기 마련입니다. 따라서 단어 분산 표현이 특정 시스템에 얼마나 영향을 미치는지를 통해 평가할 수 있습니다. 하지만 이는 시간이 오래 걸리기 때문에 각 시스템을 분리해서 평가를 하게 됩니다.
이때 자주 사용되는 평가 척도가 유사성 또는 유추 문제입니다.
유사성 평가에서는 사람이 작성한 단어 유사도를 검증 세트를 사용해 평가하는 것이 일반적입니다.
예를 들어 단어들의 유사한 정도를 미리 사람이 부여합니다. 이후 단어의 분산표현을 이용해 유사도를 통해 미리 지정된 점수와의 상관성을 확인하는 방법입니다.
유추 문제를 활용한 평가는 유추 문제를 출제하고 그 정답률로 정확도를 측정합니다.
예를 들어 king : queen = man : ? 와 같은 문제들이 존재합니다.
NLP#3까지 공부한 내용의 전개도를 그려보면 다음과 같습니다.
이번 포스팅에서는 내용의 방대한 양으로 인해 코드를 따로 첨부드리지 못하였습니다.
하지만 제가 참고한 책에 자세하게 설명되어 있으니 어렵지 않게 따라 하실 수 있을 것입니다.
다음 순서는 word2vec에서 더 발전된 단어의 분산 표현을 만들어주는 모델인 RNN에 대해서 알아보도록 하겠습니다.
포스팅 내용 중 다른 생각이 있는 분 혹은 수정해야 할 부분이 있으시면 댓글을 통해 그 의견을 나눠보면 너무 좋을 것 같습니다.
※ 본 포스팅의 내용은 밑바닥부터 시작하는 딥러닝2를 참고하였습니다.
'NLP' 카테고리의 다른 글
[LLM#1] What? How? 트랜스포머(Transformer) (6) | 2024.11.15 |
---|---|
[NLP#5] LSTM (0) | 2023.07.02 |
[NLP#4] 순환 신경망(RNN) (0) | 2023.06.28 |
[NLP#2] 통계 기반 (0) | 2023.05.15 |
[NLP#1] 자연어 처리란? - thesaurus (0) | 2023.05.12 |