머신러닝 2023. 8. 30. 17:39

https://ratsgo.github.io/nlpbook/docs/language_model/tr_self_attention/

 

Self Attention

pratical tips for Natural Language Processing

ratsgo.github.io

 

posted by 여성게
:
머신러닝 2022. 2. 17. 11:13

BERT는 트랜스포머를 이용하여 구현되었으며, 위키피디아와 BooksCorpus와 같은 레이블이 없는 텍스트 데이터로 사전 훈련된 언어 모델이다. BERT는 이미 기 학습된 사전 훈련 모델에 레이블이 있는 레이어 층을 하나 쌓아서 훈련해 파이미터를 재조정하여 다른 작업(task)에서도 좋은 성능을 낼 수 있다. 이러한 다른 작업을 위해 레이어를 쌓은 후 훈련하여 파라미터를 재 조정하는 과정을 파인튜닝(fine-tuning)이라고한다.

 

언어 모델(Language Model, LM)
언어 모델이란 단어들의 시퀀스에 대한 확률 분포다. 간단히 말하면 단어들의 모음이 있을 때 해당 단어의 모임이 
어떤 확률로 등장할지를 나태나는 값이라 생각하면 된다. 예를 들면, Word2vec 모델 중 CBOW 모델은 주변 
단어들을 통한 중앙 단어 예측이 학습의 목적이다. 즉, 문장 시퀀스 중 t번째 단어를 예측하기 위해 해당 단어의
앞,뒤 c개의 단어를 사용하는데, 앞뒤로 총 2c개의 단어 모음이 있을 때 t번째 위치에 올 단어에 대한 확률 분포를
찾는 언어 모델이고, 언어 모델의 성능은 이 확률을 최대화 하는 것이다.

 

BERT의 크기

 

버트는 트랜스포머의 인코더-디코더의 구조를 따라가는 것은 아니고, 인코더만 여러 레이어로 쌓아 올린 구조이다. Base 버전에서는 총 12개, Large 버전에서는 총 24개의 인코더 레이어를 쌓았다. 그리고 각 버전은 모델의 차원수도 차이가 있고, 셀프어텐션 헤드수도 다르기때문에 아래와 같은 파라미터 개수를 갖는다.

 

BERT-Base : L=12, D=768, A=12 : 110M개의 파라미터
BERT-Large : L=24, D=1024, A=16 : 340M개의 파라미터

 

BERT의 문맥을 반영한 임베딩(Contextual Embedding)

BERT의 입력은 임베딩 층(Embedding Layer)를 지난 임베딩 벡터들이다. BERT base 기준 d_model을 768로 정의하였기 때문에 문장의 시퀀스들의 각각의 입력 차원은 768차원이다. 각 입력들은 총 12개의 레이어를 지나면서 연산된 후, 동일하게 각 단어에 대해서 768차원의 벡터를 출력하는데, 각 출력들은 모두 문맥을 고려한 벡터가 된다.

 

위 그림에서 [CLS] 토큰의 경우 초기 입력은 단순 임베딩 층을 지난 벡터에 지나지 않지만, 총 12개의 레이어를 지난 후 출력으로 나온 CLS의 벡터는 입력으로 들어간 모든 토큰을 참고한 문맥 정보를 가진 벡터가 된다. 이러한 연산은 CLS 토큰 이외의 모든 토큰도 마찬가지가 된다.

 

위 그림을 봤을 때 모든 토큰은 모든 토큰들을 참고하고 있다.

위 그림은 각 입력이 총 12개의 레이어를 지나는 모습과, 각 레이어의 세부 구성을 보여준다. 결론적으로는 각 토큰들은 12레이어를 지나면서 셀프 어텐션을 통해 문맥 정보를 담게 된다.

 

BERT의 서브워드 토크나이저(WordPiece)

BERT는 단어보다 더 작은 단위로 쪼개는 서브워드 토크나이저를 사용한다. 서브워드 토크나이저는 기본적으로 자주 등장하는 단어는 그대로 단어 집합에 추가하지만, 자주 등장하지 않는 단어의 경우에는 더 작은 단위인 서브워드로 분리되어 서브워드들이 단어 집합에 추가된다는 아이디어를 갖는다. 만약 일반적인 토크나이저라면 존재하지 않는 단어일 경우 OOV 문제가 발생하지만, 서브워드 토크나이저의 경우 해당 단어가 단어집합에 존재하지 않는다고 해서, 서브워드 또한 존재하지 않는다는 의미가 아니므로 해당 단어를 더 쪼개려고 시도한다.

 

print(tokenizer.vocab['embeddings'])
-> em, ##bed, ##ding, ##s

 

이렇게 서브워드 토크나이저를 이용하여 문장 시퀀스들에 대해 WordPiece Embedding(정수 인코딩)을 진행한다.

 

포지션 임베딩(Position Embedding)

트랜스포머 모델에서는 포지셔널 인코딩이라는 방법으로 단어의 위치를 표현했는데, 사인함수와 코사인 함수를 이용해 각 토큰 행렬에 위치정보를 더했다. BERT는 유사하지만 사인,코사인 함수를 이용하지는 않고, 학습을 통해 얻는 포지션 임베딩을 사용한다.

위 그림에서 보이듯 워드피스 임베딩을 통해 나온 벡터에 포지션 임베딩을 더해주는 행위를 하여 임베딩에 위치정보를 반영한다. 여기서 포지션 임베딩은 위치 정보를 위한 임베딩 레이어를 하나 더 사용하고, 만약 문장의 길이가 4라면 4개의 포지션 임베딩 벡터를 학습시킨다.

 

첫번째 단어의 임베딩 벡터 + 0번 포지션 임베딩 벡터
두번째 단어의 임베딩 벡터 + 1번 포지션 임베딩 벡터
세번째 단어의 임베딩 벡터 + 2번 포지션 임베딩 벡터
네번째 단어의 임베딩 벡터 + 3번 포지션 임베딩 벡터

 

실제 BERT에서는 문장의 최대 길이를 512로 하고 있으므로 총 512개의 포지션 임베딩 벡터가 학습된다. 지금까지 총 2개의 임베딩 레이어가 나왔는데 버트에서는 추가적으로 세그먼트 임베딩이라는 1개의 임베딩 층을 더 사용한다.

 

BERT의 pre-trained : 마스크드 언어 모델(Masked Language Model, MLM)

BERT는 사전 훈련을 위해서 입력으로 들어가는 입력 텍스트의 15%의 단어를 랜덤으로 마스킹한다. 그리고 인공 신경망에게 이 가려진 단어들을 예측하도록 한다. 더 정확하게는 15% 단어 전부를 마스킹 하지 않고 아래와 같은 규칙을 따른다.

 

80%의 단어들은 [MASK]로 변경한다.
Ex) The man went to the store → The man went to the [MASK]

10%의 단어들은 랜덤으로 단어가 변경된다.
Ex) The man went to the store → The man went to the dog

10%의 단어들은 동일하게 둔다.
Ex) The man went to the store → The man went to the store

 

전체 단어의 85%는 학습에 사용되지 않는다. 마스크드 언어 모델의 학습에 사용되는 단어는 전체 단어의 15%이다. 학습에 사용되는 12%는 마스킹된 후 원래 단어를 예측하는 방식, 1.5%는 랜덤으로 변경된 단어의 원래 단어를 예측하는 방식, 나머지 1.5%는 단어가 변경되지 않았지만 BERT는 이 단어가 원래의 단어인지 변경된 단어인지 모르기에 원래단어가 무엇이었는지 예측해본다.

'dog' 토큰은 [MASK]로 변경되었다.
'he'는 랜덤 단어 'king'으로 변경되었다.
'play'는 변경되진 않았지만 예측에 사용된다.

BERT의 pre-trained : 다음 문장 예측(Next Sentence Prediction, NSP)

BERT는 두 개의 문장을 준 후에 이 문장이 이어지는 문장인지 아닌지를 맞추는 방식으로 사전 학습한다. 

 

이어지는 문장의 경우
Sentence A : The man went to the store.
Sentence B : He bought a gallon of milk.
Label = IsNextSentence

이어지는 문장이 아닌 경우 경우
Sentence A : The man went to the store.
Sentence B : dogs are so cute.
Label = NotNextSentence

 

BERT의 입력으로 넣을 때에는 [SEP]라는 특별 토큰을 사용해 두 문장을 구분한다. 그리고 위 그림에서 보듯이 문장이 이어지는지 아닌지 등의 분류 문제에는 CLS 토큰의 출력을 이용한다. 또한 마스크드 언어 모델 학습과 다음 문장 예측은 서로 별도로 학습하는 것이 아니라, 각 문제의 loss를 합하여 학습이 동시에 이루어진다.

 

세그먼트 임베딩(Segment Embedding)

BERT는 QA등과 같은 두 개의 문장 입력이 필요한 태스크를 풀기도 하는데, 문장 구분을 위해서 세그먼트 임베딩이라는 또 다른 임베딩 층을 사용한다. 첫번째 문장에는 Sentence0 임베딩, 두번째 문장에는 Sentence1 임베딩을 더해주는 방식이며 임베딩 벡터는 두개만 사용된다.

 

결론적으로 BERT는 총 3개의 임베딩 층이 사용된다.

 

WordPiece Embedding : 실질적인 입력이 되는 워드 임베딩. 임베딩 벡터의 종류는 단어 집합의 크기로 30,522개.
Position Embedding : 위치 정보를 학습하기 위한 임베딩. 임베딩 벡터의 종류는 문장의 최대 길이인 512개.
Segment Embedding : 두 개의 문장을 구분하기 위한 임베딩. 임베딩 벡터의 종류는 문장의 최대 개수인 2개.

 

만약 BERT가 두 개의 문장을 입력받을 필요가 없는 태스크인 경우 세그먼트 임베딩을 전체 입력에 대해 Sentence 0 임베딩만 더해준다.

 

BERT를 파인 튜닝(fine-tunng) 하기

1)하나의 텍스트에 대한 텍스트 분류 유형(Single Text Claasification)

입력이 하나의 문장이고, 해당 텍스트에 대한 분류를 할때는 앞서 설명한 것과 같이 CLS 토큰의 출력을 이용한다. 즉, 파인 튜닝 단계에서 텍스트 분류 문제를 풀기 위해서 CLS 토큰 출력 위치에 Dense Layer(fully-connected layer) 층을 추가하여 분류에 대한 예측을 하게 된다.

 

2)하나의 텍스트에 대한 태깅 작업(Tagging)

하나의 텍스트에 대해 엔티티 혹은 품사를 태깅하는 작업에 대해 파인 튜닝 할때는 스페셜 토큰을 제외한 실제 입력 토큰의 출력층에 Dense Layer를 사용하여 분류에 대한 예측을 하게 된다.

 

3)하나의 텍스트에 대한 태깅 작업(Tagging)

텍스트의 쌍을 입력으로 받는 대표적인 태스크로 자연어 추론(Natural language inference)이 있는데,  두 문장이 입력으로 들어오고 두문장이 논리적으로 어떤 관계에 있는지를 분류하는 것이다. 유형으로는 모순 관계(contradiction), 함의 관계(entailment), 중립 관계(neutral)이 있다. 해당 문제의 입력은 두 개의 문장이므로 SEP라는 스페셜 토큰으로 문장을 구분한다. 그리고 해당 문제는 분류 문제에 속하므로, CLS 토큰의 출력층에 Dense Layer를 쌓아서 분류를 예측한다.

 

위에서 설명한 문제 이외로도 하나의 문장과 하나의 문단을 넣어서 첫번째 문장(질문)에 해당하는 답을 문단에서 뽑아내는 질의 응답에 대한 문제등도 있다.

 

어텐션 마스크(Attention Mask)

 

BERT는 입력으로 어텐션 마스크라는 시퀀스 입력이 추가로 필요하다. 쉽게 말해 어텐션 마스크는 연산이 필요없는 패딩 토큰에 대해 어텐션을 하지 않도록 마스킹해주는 역할이다. 이 값은 0과 1값으로 가지는데, 실제로 입력 시퀀스중 [PAD] 토큰의 위치에 해당하는 어텐션 마스크는 0으로, 실제 연산에 필요한 토큰 위치는 1로 채워지게 된다.

posted by 여성게
:
머신러닝 2022. 2. 15. 20:03

트랜스포머(Transformer)는 구글이 발표한 논문인 "Attention is all you need"에서 나온 모델로 기존의 seq2seq의 구조인 인코더-디코더를 따르지만, 내부적으로 RNN 레이어 없이, 어텐션(Attention)으로만 구현한 모델이다.

 

RNN을 이용한 기존 seq2seq의 한계

기존의 seq2seq 모델은 인코더-디코더 구조로 구성되어있고, 여기서 인코더는 입력 시퀀스를 하나의 벡터 표현으로 압축하고, 디코더는 이 벡터 표현을 통해 출력 시퀀스를 만들어냈다. 하지만 이러한 구조는 인코더가 입력 시퀀스를 하나의 벡터로 압축하는 과정에서 입력 시퀀스의 정보가 일부 손실되는 단점이 있었다. 또한 RNN의 고질적인 기울기 소실 문제까지 더해졌다.

 

트랜스포머(Transformer)의 주요 하이퍼파라미터

 

트랜스포머(Transformer)

트랜스포머는 RNN을 사용하지 않지만 기존 seq2seq처럼 인코더-디코더 구조를 유지하고 있다. 이전 seq2seq 구조에서는 인코더와 디코더에서 각각 하나의 RNN이 t개의 시점(time step)을 가지는 구조였다면, 트랜스포머는 인코더와 디코더가 각각 N개층으로 쌓여있는 구조이다.

 

 

위 그림은 각 인코더, 디코더가 6개의 층으로 쌓여있는 구조이다.

 

 

큰 구조를 보면 기존의 seq2seq 구조와 비슷하다.

 

포지셔널 인코딩(Positional Encoding)

RNN이 자연어 처리에서 유용했던 이유는 단어의 위치에 따라 단어를 순차적으로 입력받아서 처리하는 RNN의 특성으로 인해 각 단어의 위치 정보를 가질 수 있다는 점에 있었다. 하지만 트랜스포머는 단어 입력을 순차적으로 받는 방식이 아니라, 단어의 위치 정보를 알려 줄 수 있는 방법이 필요했다. 그래서 트랜스포머는 단어의 위치 정보를 얻기 위해 각 단어의 임베딩 벡터에 위치 정보들을 더하여 모델의 입력으로 사용하는데 이를 포지셔널 인코딩이라고 한다.

 

 

위 그림을 보면 각 인코더, 디코드 층에 입력이 들어가기전 포지셔널 인코딩 레이어가 있는 것이 특징이다. 

포지셔널 인코딩 값들을 임베딩 벡터에 반영하기 위해 아래 두개의 함수를 이용한다.

 

 

트랜스포머는 사인 함수와 코사인 함수의 값을 임베딩 벡터에 더해주므로서 단어의 순서 정보를 더한다. 실제 계산은 임베딩 벡터가 모여 만들어진 문장 행렬과 포지셔널 인코딩 행렬의 덧셈 연산을 통해 이루어진다.

 

pos는 입력 문장에서의 임베딩 벡터의 위치를 나타내며, i는 임베딩 벡터 내의 차원의 인덱스를 의미한다. 위의 식에 따르면 임베딩 벡터 내의 각 차원의 인덱스가 짝수인 경우에는 사인함수, 홀수 인경우에는 코사인 함수를 이용한다. 위와 같이 포지셔널 인코딩 방법을 이용하면 순서정보가 보존되는데, 같은 단어라고 하더라도 문장 내의 위치가 다르면 트랜스포머의 입력으로 들어가는 임베딩 벡터의 값이 달라진다. 즉, 트랜스포머의 입력은 순서 정보가 고려된 임베딩 벡터가 된다.

 

어텐션

아래 그림은 트랜스포머에서 사용되는 세 가지의 어텐션이다.

 

 

인코더의 셀프 어텐션 : Query = Key = Value
디코더의 마스크드 셀프 어텐션 : Query = Key = Value
디코더의 인코더-디코더 어텐션 : Query : 디코더 벡터 / Key = Value : 인코더 벡터

 

 

위 그림은 세가지 어텐션이 각각 어디에서 이루어지는지를 보여주는 그림이다.

 

인코더

인코더를 하나의 층이라는 개념으로 보면, 하나의 인코더는 크게 총 2개의 셀프 어텐션과 피드 포워드 서브층으로 나뉜다.

 

인코더의 셀프 어텐션

셀프 어텐션이란 어텐션을 자기 자신에게 수행한다는 의미다. 

 

Q : 입력 문장의 모든 단어 벡터들
K : 입력 문장의 모든 단어 벡터들
V : 입력 문장의 모든 단어 벡터들

 

셀프 어텐션은 입력 문장의 단어 벡터들을 가지고 수행한다고 했지만, 사실 셀프 어텐션은 인코더의 초기 입력인 dmodel의 차원을 가지는 단어 벡터들을 사용하여 셀프 어텐션을 수행하는 것이 아니라 우선 각 단어 벡터들로부터 Q벡터, K벡터, V벡터를 얻는 작업을 거친다. 이때  Q벡터, K벡터, V벡터들은 초기 입력인 dmodel의 차원을 가지는 단어 벡터들보다 더 작은 차원을 가지는데 논문에서는 512의 차원을 가졌던 단어 벡터들을 64의 차원을 가지는 벡터로 변환하였다.

 

64라는 값은 트랜스포머의 num_heads 하이퍼파라미터로 결정되는데, 트랜스포머는 dmodel 차원을 num_heads로 나눈 값을 각 Q, K, V 벡터의 차원으로 결정한다.

기존의 벡터로부터 더 작은 벡터는 가중치 행렬을 곱하므로서 완성된다. 해당 가중치 행렬은 훈련 과정에서 학습된다. 

 

스케일드 닷-프로덕트 어텐션

스케일드 닷-프로덕트 어텐션은 이전 포스팅에서 설명한 닷-프로덕트 어텐션에 값을 스케일링하는 것을 추가한것이다. 아래 그림을 보면 내적한 값에 특정 값으로 나눠준다.

 

Q, K, V 벡터를 얻었다면 지금부터는 기존의 어텐션 메커니즘과 동일하다. 각 Q벡터는 모든 K벡터에 대해 어텐션 스코어를 구하고, 어텐션 분포를 구한 뒤에 이를 사용하여 모든 V벡터를 가중합하여 어텐션 값 또는 컨텍스트 벡터를 구한다. 이를 모든 Q벡터에 대해 반복한다. 스케일드 닷-프로덕트 어텐션은 이전 포스팅에서 다뤘던 어텐션 기법과는 다르게 어텐션 스코어를 구하기 위해 Q, V벡터의 내적만 하는 것이 아니라 아래와 같은 값을 스케일하는 수식이 하나더 들어간다.

 

 

위 그림의 어텐션 스코어는 단어 I가 각 단어 I, am, a, student와 얼마나 연관이 있는지 보여주는 수치이다. 트랜스포머에서는 두 벡터의 내적값을 스케일링하는 값으로 K벡터의 차원(64)을 나타내는 dk에 루트를 씌우는 값을 이용했다. 여기서 dk는 8이라는 값을 가진다.

 

그렇게 나온 값을 소프트맥스 함수에 통과시켜 모든합이 1이되는 확률 분포를 만들고 이 확률(어텐션 가중치)를 모든 V벡터와 가중합하여 최종적인 어텐션 값을 구한다. 이 어텐션 값을 위 예제로 표현하면 단어 I에 대한 어텐션 값 또는 컨텍스트 벡터라고 할 수 있다. 이러한 과정을 모든 단어에 동일하게 반복한다. 하지만 내부적으로 따로 계산하지는 않고 모든 단어에대해 한번에 행렬 연산으로 모든 단어의 어텐션 값을 구하는 연산을 진행한다.

 

행렬 연산으로 일괄 계산하기

우선 각 단어 벡터마다 일일히 가중치 행렬을 곱하는 것이 아니라, 문장 행렬에 가중치 행렬을 곱하여 Q, K, V 행렬을 구한다.

 

 

그렇게 나온 Q행렬과 K행렬로는 어떻게 어텐션 스코어를 구할까? 위 그림과 같이 K행렬을 전치한 행렬과 Q행렬을 곱하고 스케일링 값을 나눠주면(K벡터의 루트를 씌운값) 각 단어의 어텐션스코어가 포함된 행렬이 나오게 된다. 예를 들어 I행과 student열의 값은 단어 I의 Q벡터와 student K벡터의 어텐션 스코어 값이다.

 

 

이제 각 단어의 어텐션 값은 아래 수식대로 계산하면 나오게 된다.

위의 행렬 연산에 사용된 행렬의 크기를 모두 정리하자면, 우선 입력문장의 길이를 seq_len이라고 하면, 문장 행렬의 크기는 (seq_len, dmodel)이다. 여기에 3개의 가중치 행렬을 곱해서 Q, K, V 행렬을 만든다.

 

우선 행렬의 크기를 정의하기 위해 행렬의 각 행에 해당되는 Q벡터와 K벡터의 차원을 dk라고하고, V벡터의 차원을 dv라고 하자. 그렇다면 Q행렬과 K행렬의 크기는 (seq_len, dk)이며, V행렬의 크기는 (seq_len, dv)가 되어야한다. 그렇다면 문장 행렬과 Q, K, V 행렬의 크기로 가중치 행렬의 크기 추정이 가능하다. Wq, Wk는 (dmodel, dk)의 크기를 가지며, Wv는 (dmodel, dv)의 크기를 가진다. 단 논문에서는 dk와 dv의 차원은 dmodel/num_heads와 같다. 즉, dmodel/num_heads == dk == dv이다.

 

멀티 헤드 어텐션(Multi-head Attention)

 

트랜스포머를 개발한 연구진은 한 번의 어텐션을 하는 것보다 여러번의 어텐션을 병렬로 사용하는 것이 더 효과적이라고 판단했다. 그래서 dmodel의 차원을 num_heads개로 나누어 dmodel/num_heads의 차원을 가지는 Q, K, V에 대해서 num_heads개의 병렬 어텐션을 수행한다. 다시 말해 위에서 설명한 어텐션이 8개로 병렬로 이루어지는 것인데, 이때 각각의 어텐션 값 행렬을 어텐션 헤드라고 부른다. 이때 가중치 행렬 Wq, Wk, Wv의 값은 8개의 어텐션 헤드마다 모두 다른 행렬이다.(같은 가중치로 병렬로 어텐션을 수행해봐야 같은 어텐션 값이 나올테니, 서로 다른 행렬로 수행하고 이 가중치들은 모두 훈련 과정에서 학습된다.) 즉, 서로 다른 가충치를 가지고 병렬로 어텐션을 수행하니 모든 어텐션 헤드들은 다른 시각으로 다른 정보들을 수집하게 되는 것이다.

 

 

병렬 어텐션을 모두 수행하였다면, 모든 어텐션 헤드를 연결(concatenate)한다. 모두 연결된 어텐션 헤드 행렬의 크기는 (seq_len, dmodel)이 된다. 즉, 입력으로 들어온 행렬의 크기와 같게 된다.

 

어텐션 헤드를 모두 연결한 행렬은 또 다른 가중치 행렬 Wo를 곱하게 되는데, 이렇게 나온 결과 행렬이 멀티-헤드 어텐션의 최종 출력이 된다. 즉, 멀티-헤드 어텐션 행렬은 인코더의 입력이었던 문장 행렬의 (seq_len, dmodel) 크기와 동일하다. 다시 말해 인코더의 첫번째 서브층인 멀티-헤드 어텐션 서브층을 지나서 나온 출력이 인코더의 입력 행렬크기로 유지되고 있는 것이다. 또한 다음 서브층인 포지션 와이즈 피드 포워드 층으로 들어가는 입력 크기도 그대로 유지되서 들어가야한다.

 

패딩 마스크(Padding Mask)

스케일드 닷 프로덕트 어텐션 함수 내부를 보면 mask라는 인자가 존재한다. 이는 입력 문장에 <PAD> 토큰이 있을 경우 어텐션에서 제외하기 위한 연산을 위함이다.

 

 

사실 패딩을 위한 토큰은 실질적인 의미를 담은 단어는 아니고, 시퀀스의 길이를 맞춰주기 위한 더미 토큰이기 때문에 트랜스포머에서는 Key의 경우 패딩 토큰이 존재한다면 유사도를 구하지 않도록 마스킹을 해준다.

 

마스킹을 하는 방법은 어텐션 스코어 행렬의 마스킹 위치에 매우 작은 음수값을 넣는것이다. 즉 이렇게 되면, 해당 위치는 소프트맥스 함수를 지나도 0에 아주 가까운 값이 되기에 유사도 구하는데 큰 역할을 하지 못하게 된다.

 

 

포지션-와이즈 피드 포워드 신경망(Position-wise FFNN)

포지션-와이즈 피드 포워드 신경망은 인코더와 디코더에서 공통적으로 가지고 있는 서브층이다. 쉽게 말하면 포지션-와이즈 피드 포워드 신경망은 완전 연결 FFNN(Fully-connected FFNN)이라고 해석할 수 있다.

 

위 수식을 그림으로 표현하면 아래와 같다.

 

여기서 x는 멀티 헤드 어텐션의 결과로 나온 (seq_len, dmodel)의 크기를 가지는 행렬을 말한다. 가중치 행렬 W1은 (dmodel, dff)의 크기를 가지고, 가중치 행렬 W2는 (dff, dmodel)의 크기를 가진다. 논문에서는 은닉층의 크기인 dff는 2048의 크기를 가진다.

 

여기서 매개변수 W1, W2, b1, b2는 하나의 인코더 층 내에서는 다른 문장, 다른 단어들마다 정확하게 동일한 행렬을 가진다. 하지만 서로 다른 인코더 층마다는 다른 값을 가진다.

 

여기서 기억해야할 것은 서브층을 모두 지나게 된 은닉층의 행렬크기를 보면 입력과 동일하다는 것이다.

 

잔차 연결(Residual connection)과 층 정규화(Layer Normalization)

트랜스포머에서는 두 개의 서브층을 가진 인코더에 추가적으로 사용하는 Add & Norm이라는 기법이 있다. 그림을 보면 화살표가 서브층의 이전의 입력에서 시작되어 서브층의 출력부분을 향하고 있다.

 

잔차 연결

 

위 그림을 보면, 어떤 함수의 입력값과 함수의 결과를 더하고 있는 H(x) 함수가 있다. 여기서 어떤 함수 F(x)가 트랜스포머에서는 각 서브층을 뜻하게 되는데, 쉽게 말해 잔차 연결은 서브층의 입력과 출력을 더한것과 같다. 앞서 설명했듯이 트랜스포머에서 서브층의 입력과 출력은 동일한 차원을 가지고 있으므로, 서브층의 입력과 서브층의 출력은 행렬 덧셈 연산이 가능하다. 잔차 연결은 컴퓨터 비전 분야에서 주로 사용되는 모델의 학습을 돕는 기법이다.

 

 

서브층이 멀티 헤드 어텐션이었다면 잔차 연결 연산은 위 그림과 같다.

 

층 정규화

전차 연결을 거친 결과는 이어서 층 정규화 과정을 거치게 된다. 잔차 연결의 입력을 x, 잔차 연결과 층 정규화 두 가지 연산을 모두 수행한 후의 결과 행렬을 LN이라고 했을 때, 잔차 연결 후 층 정규화 연산을 수식으로 표현하면 다음과 같다.

층 정규화는 텐서의 마지막 차원에 대해서 평균과 분산을 구하고, 이를 가지고 어떤 수식을 통해 값을 정규화하여 학습을 돕는다. 여기서 텐서의 마지막 차원이란 트랜스포머에서는 dmodel 차원을 의미한다.

 

 

층 정규화를 위해서 우선, 화살표 방향으로 각각 평균과 분산을 구한다. dmodel 크기의 벡터는 아래와 같은 수식으로 정규화 된다.

 

층 정규화는 크게 두 가지 과정으로 이루어지는데, 첫번째는 평균과 분산을 통한 정규화, 두번째는 감마와 베타를 도입하는 것이다. 우선 평균과 분산을 통해 벡터 x를 정규화 해준다. 여기서 평균과 분산값은 스칼라값이다.

 

여기서 입실론은 분모가 0이 되는 것을 방지하는 값이다. 마지막으로 감마와 베타를 도입한 최종 정규화 수식은 아래와 같다.(여기서 감마와 베타는 처음에 0과 1로 초기화 된다.)

케라스에서는 층 정규화를 위해 LayerNormalization()을 제공한다. 지금까지 설명한 인코더 층에서의 출력은 디코더의 연산에 사용된다.

 

디코더의 첫번째 서브층: 셀프 어텐션과 룩-어헤드 마스크

 

위 그림과 같이 디코더도 인코더와 동일하게 포지셔널 인코딩 단계를 거친 후 입력으로 들어간다. 트랜스포머 또한 RNN셀로 이루어진 seq2seq와 마찬가지로 교사 강요를 사용하여 훈련되므로 학습 과정에서 디코더는 번역할 문장에 해당 되는 <sos> je suis étudiant의 문장 행렬을 한 번에 입력받는다. 그리고 디코더는 이 문장 행렬로부터 각 시점의 단어를 예측하도록 훈련한다. 하지만 여기서 문제가 있는데, 기존 RNN으로 이루어진 seq2seq는 매 시점마다 순차적으로 입력받으므로 다음 단어 예측에 현재 시점을 포함한 이전 시점에 입력된 단어들만 참고할 수 있다. 반면, 트랜스포머는 문장 행렬을 한번에 입력받아서 현재 시점의 단어를 예측하고자 할 때, 입력 문장 행렬로부터 미래 시점의 단어까지도 참고할 수 있는 현상이 발생되는데, 이를 위해 트랜스포머의 디코더에서는 현재 시점의 예측에서 현재 시점보다 미래에 있는 단어들을 참고하지 못하도록 룩-어헤드 마스크를 도입했다.

 

 

룩-어헤드 마스크는 디코더의 첫번째 서브층에서 이루어진다. 디코더의 첫번째 서브층인 멀티 헤드 셀프 어텐션 층은 인코더의 첫번째 서브층인 멀티 헤드 셀프 어텐션과 동일한 연산을 수행한다. 하지만 다른 점은 어텐션 스코어 행렬에서 미래 단어를 예측하지 못하도록 마스킹을 적용한다는 점만 다르다.

 

위와 같이 어텐션 스코어 행렬을 얻는다. 그 다음 이제 자신의 시점보다 미래에 있는 시점의 단어는 예측하지 못하도록 아래와 같이 마스킹한다.

 

 

디코더의 두번째 서브층: 인코더-디코더 어텐션

디코더의 두번째 서브층은 멀티 헤드 어텐션을 수행은 하지만, 앞서 설명한것과는 다르게 셀프 어텐션이 아니다. 앞서 설명한 어텐션을 포함해 인코더-디코더 어텐션의 각 Q, K, V의 정의는 아래와 같다.

 

인코더의 첫번째 서브층 : Query = Key = Value
디코더의 첫번째 서브층 : Query = Key = Value
디코더의 두번째 서브층 : Query : 디코더 행렬 / Key = Value : 인코더 행렬

디코더의 두번째 서브층을 보면 아래 그림과 같이 인코더로부터 두 개의 화살표가 그려져있다.

두 개의 화살표는 각각 Key와 Value를 의미한다. Query는 그림과 같이 디코더의 첫번째 서브층의 결과 행렬을 뜻한다.

그 외에 멀티 헤드 어텐션을 수행하는 과정은 다른 어텐션과 동일하다.

posted by 여성게
:
머신러닝 2022. 2. 15. 17:18

어텐션 메커니즘

시퀀스-투-시퀀스(seq2seq) 모델 같은 경우는 인코더에서 입력 시퀀스를 컨텍스트 벡터(context vector)라는 하나의 고정된 크기의 벡터 표현으로 문장 시퀀스를 압축하고, 디코더는 해당 컨텍스트 벡터를 이용해 출력 시퀀스를 만들어낸다.

 

하지만, 이러한 RNN에 기반한 seq2seq 모델에는 아래와 같은 문제점이 있다.

  1. 하나의 고정된 크기의 벡터에 문장 시퀀스 정보 모두를 압축하려 하기에 정보 소실이 발생한다.
  2. RNN의 고질적인 문제인 기울기 소실(vanishing gradient)문제가 존재한다.

 

즉, 위와 같은 문제로 기계번역 같은 분야에서 입력된 문장의 길이가 길어지게 되면 번역 성능이 크게 줄어든다. 하지만 어텐션이라는 아이디어로 긴 입력 시퀀스에 대한 품질이 떨어지는 것을 보정 해줄 수 있다.

 

어텐션의 아이디어

어텐션은 디코더에서 출력 단어를 예측하는 매 시점마다, 인코더에서의 전체 입력 문장을 다시 한번 참고하는 것이다. 하지만 여기서 중요한 것은 입력 문장 전체를 동일 비율로 참고하는 것이 아니라, 해당 시점에서 예측해야할 단어와 연관이 있는 입력 단어 부분에 조금 더 집중(attention)해서 보게 된다.

 

어텐션 함수

Attention(Q, K, V) = Attention Value

 

어텐션 함수는 주어진 쿼리(Q)에 대해서 모든 키(K)와의 유사도를 각각 구한 후 각 키와 매핑된 값(V)에 반영해준다. 그리고 유사도가 반영된 값을 모두 더해 리턴하는데, 여기서 리턴된 값을 어텐션 값(Attention Value)라고 한다.

 

Q = Query : t 시점의 디코더 셀에서의 은닉 상태
K = Keys : 모든 시점의 인코더 셀의 은닉 상태들
V = Values : 모든 시점의 인코더 셀의 은닉 상태들

 

닷-프로덕트 어텐션(Dot-Product Attention)

디코더의 세번째 시점의 LSTM 셀에서 출력 단어를 예측하는 예제

 

디코더의 세번째 LSTM 셀은 출력 단어를 예측하기 위해서 인코더의 모든 입력 단어들의 정보를 다시 한번 참고한다. 인코더 쪽의 소프트맥스 함수를 통해 나온 결과값은 I, am, a, student 단어 각각이 출력 단어(suis가 입력으로 들어간 세번째 시점의 LSTM 출력 )를 예측할 때 얼마나 도움이 되는지의 정도를 수치화한 값이다. 그리고 각 입력 단어가 디코더의 예측에 도움이 되는 정도를 수치화하여 하나의 정보로 담아서 디코더로 전송한다. 결과적으로 해당 어텐션 값을 이용해 디코더는 출력 단어를 더 정확하게 예측할 확률이 높아진다.

 

어텐션 스코어

 

 

인코더의 각 시점의 은닉 상태(hidden state)를 각각 h1,h2,..hn이라고 하고, 디코더의 현재 시점 t에서의 디코더의 은닉 상태를 st라고 한다. 다시 돌아가서, 디코더의 현재 시점 t에서 필요한 입력값은 바로 이전 시점인 t-1의 은닉 상태와 이전 시점 t-1에서 나온 출력단어이다. 그런데 어텐션 메커니즘에서는 출력 단어 예측에 어텐션 값이라는 새로운 값이 필요하다. 여기서 t시점의 출력을 예측하기 위한 어텐션 값을 at라고 하자.

 

우선 어텐션 스코어(Attention score)를 구해보자. 어텐션 스코어란 현재 디코더의 시점 t에서 단어를 예측하기 위해, 인코더의 모든 은닉 상태 각각이 디코더의 현 시점의 은닉상태 st와 얼마나 유사한지를 판단하는 스코어값이다.

 

닷-프로덕트 어텐션에서는 이 스코어 값을 구하기 위해 st를 전치(transpose)하고 각 은닉 상태와 내적(dot product)을 수행한다. 아래 그림은 st과 인코더 i번째의 은닉 상태의 어텐션 스코어의 계산 방법이다. 여기서 어텐션 스코어 값은 모두 스칼라 값을 가진다.

 

 

 

소프트맥스 함수를 통해 어텐션 분포 구하기

 

위 et에 소프트 맥스 함수를 적용하여, 모든 값을 합하면 1이 되는 확률 분포를 얻어낸다. 이를 어텐션 분포라고 하며, 각각의 값은 어텐션 가충치(Attention Weight)라고 한다. 예를 들어 소프트맥스 함수를 적용하여 얻은 출력값인 I, am, a, student의 어텐션 가중치를 각각 0.1, 0.4, 0.1, 0.4라고 하고, 이들의 합은 1이다.

 

디코더의 시점 t에서의 어텐션 가중치의 모음값인 어텐션 분포를 αt이라고 할 때, αt을 식으로 정의하면 다음과 같다.

 

 

각 인코더의 어텐션 가중치와 은닉 상태를 가중합하여 어텐션 값(Attention Value)을 구한다.

 

어텐션 값을 얻기 위해 각 인코더의 은닉 상태와 어텐션 가중치 값들을 곱하고, 최종적으로 모두 더한다. 요약하자면 가중합(weighted Sum)을 진행한다. 수식으로 표현하면 아래와 같다.

 

이러한 어텐션 값은 종종 인코더의 문맥을 포함하고 있다고 하여, 컨텍스트 벡터(Context Vector)라고도 불린다.

 

어텐션 값과 디코더의 t 시점의 은닉 상태를 연결한다.(Concatenate)

 

 

어텐션 함수의 최종값인 어텐션 값 at을 구했고 그리고 at st와 결합(concatenate)하여 하나의 벡터 Vt로 만드는 작업을 수행한다. 그리고 이 Vt y^ 예측 연산의 입력으로 사용하므로서 인코더로부터 얻은 정보를 활용하여 y^를 좀 더 잘 예측할 수 있게 된다. 이것이 어텐션 메커니즘의 핵심이다. 하지만 여기서 나온 Vt를 바로 출력의 입력으로는 사용하지 못하고, 아래 그림과 같이 출력층으로 보내기 전에 신경망 연산을 통해 기존 은닉층과 같은 벡터 차원으로 만들어준 후에 나온 St^값을 입력으로 넣는다.

 

 

 

위와 같이 St^를 출력층의 입력으로 사용해 예측 벡터를 얻는다.

 

위에서 설명한 dot-product 어텐션 말고도 다양한 어텐션 종류가 있다. 하지만 그 차이는 중간에 수식 차이이다.

 

위에서 St는 Query, Hi는 keys, Wa와 Wb는 학습 가능한 가중치 행렬이다. 즉, 쿼리는 디코더의 현재 시점의 은닉 상태, 키는 모든 인코더의 은닉 상태를 뜻하며 이러한 값들을 이용해 최종적인 어텐션 값을 구하게 된다.

posted by 여성게
:
머신러닝 2022. 1. 20. 10:49


해당 포스팅은 위 이미지의 책의 챕터3을 읽은 리뷰입니다. :)

자연어 처리 개요

단어 표현

텍스트를 자연어 처리를 위한 모델에 적용할 수 있게 언어적인 특성을 반영해서 단어를 수치화하는 방법을 찾는 것이다. 보통 단어를 수치화할 때는 주로 벡터로 표현한다. 따라서 단어 표현은 "단어 임베딩(word embedding)" 또는 "단어 벡터(word vector)"로 표현한다.

단어 임베딩 기법

  • 원-핫 인코딩(one-hot encoding) : 단어를 하나의 벡터로 표현하며 각 단어는 벡터 값 가운데 하나만 1이라는 값을 가지고 나머지는 모두 0값을 가짐.
  • 분포 가설(Distributed hypothesis)
    • 카운트 기반 방법 : 어떤 글의 문맥 안에 단어가 동시에 등장하는 횟수를 세는 방법(동시 등장 횟수를 하나의 행렬로 나타낸 뒤 그 행렬을 수치화해서 단어 벡터를 만듬)
    • 예측 방법 
      • Word2vec
        • CBOW(Continuous Bag of Words) : 어떤 단어를 문백 안의 주변 단어들을 통해 예측하는 방법(창욱은 냉장고에서 _____ 꺼내서 먹었다.)
        • Skip-Gram : 어떤 단어를 가지고 특정 문맥 안의 주변 단어들을 예측하는 방법(____ ________ 음식을 _____ _____)
      • NNLM(Neural Network Language Model)
      • RNNLM(Recurrent Neural Network Language Model)
    • 카운트 기반 방법과 예측 방법 모두를 차용해서 쓰는 단어 임베딩 기법
      • Glove

텍스트 분류

텍스트 분류는 자연어 처리 문제 중 가장 대표적이고 많이 접하는 문제다. 자연어 처리 기술을 활용해 특정 텍스트를 사람들이 정한 몇 가지 범주(class) 중 어느 범주에 속하는지 분류하는 문제다.

분류해야 할 범주의 수에 따라 문제를 구분

  • 이진 분류(binary classification) : 2가지 범주에 대해 구분하는 문제
  • 다중 범주 분류(Multi classfication) : 3가지 이상의 범주에 대해 구분하는 문제

지도 학습을 통한 텍스트 분류


위 그림과 같이 지도 학습은 샘플(데이터)에 대해 각각 속한 범주에 대한 값(레이블)이 이미 주어져 있다. 따라서 주어진 범주로 글들을 모두 학습한 후 학습한 결과를 이용해 새로운 글의 범주를 예측하는 방법이다.

  • KNN
  • 나이브 베이즈 분류
  • 서포트 벡터 머신
  • 신경망
  • 선형 분류
  • 로지스틱 분류
  • 랜덤 포레스트

비지도 학습을 통한 텍스트 분류



비지도 학습에서는 데이터만 존재하고, 각 데이터는 범주로 미리 나눠져 있지 않다. 따라서 특성을 찾아내서 적당한 범주를 만들어 각 데이터를 나눈다.
비지도 학습을 통한 분류는 어떤 특정한 분류가 있는 것이 아니라 데이터의 특성에 따라 비슷한 데이터끼리 묶어주는 개념이다.

  • K-means Clustering
  • Hierarchical Clustering

텍스트 유사도

텍스트 유사도란 말 그대로 텍스트가 얼마나 유사한지를 표현하는 방식 중 하나다. 유사도를 판단하는 척도는 매우 주관적이기 때문에 데이터를 구성하기가 쉽지 않고 정량화하는 데 한계가 있다. 이를 최대한 정량화해서 모델을 만드는 것이 중요하다.
일반적으로 유사도를 측정하기 위해 정량화하는 방법에는 여러 가지가 있다. 단순히 같은 단어의 개수를 사용해서 유사도를 판단하는 방법, 형태소로 나누어 형태소를 비교하는 방법, 자소 단위로 나누어 단어를 비교하는 방법 등 다양하다.
또한 딥러닝을 기반으로 텍스트의 유사도를 측정하는 방식도 있다. 단어, 형태소, 유사도의 종류에 상관 없이 텍스트를 벡터화한 후 벡터화된 각 문장 간의 유사도를 측정하는 방식이다.

자카드 유사도

두 집합(각 문자을 형태소 분리한 형태소의 집합)의 교집합인 공통된 단어의 개수를 두 집합의 합집합, 즉 전체 단어의 수로 나눈다.

유클리디언 유사도


n차원 공간에서 두 점 사이의 최단 거리를 구하는 접근법이다.

맨하탄 유사도


유클리디언 유사도처럼 출발점과 도착점까지를 가로지르지 않고 갈수 있는 최단 거리를 구하는 공식이다.

코사인 유사도


코사인 유사도는 두 개의 벡터값에서 코사인 각도를 구하는 방법이다. 코사인 유사도 값은 -1과 1 사이의 값을 가지고 1에 가까울수록 유사하다는 것을 의미한다. 다른 유사도 접근법에 비해 일반적으로 성능이 좋은데,
이는 단순히 좌표상의 거리를 구하는 다른 유사도 측정 방법에 비해 코사인 유사도는 말 그대로 두 벡터 간의 각도를 구하는 것이라서 방향성의 개념이 더해지기 때문이다.

posted by 여성게
: