Transformer 모델
기존 RNN 구조
By fdeloche - 자작, CC BY-SA 4.0, https://commons.wikimedia.org/w/index.php?curid=60109157
- RNN (Recurrent Neural Network)는 순차적. 병렬 처리 불가능
- $ h_t = f(h_{t-1}, x_t) $
- 이전 단계에 의존한다.
- 긴 sequence 처리할 때 계산 시간 증가.
메모리 제약으로 배치 크기를 줄여야 할 수도.
- 입력 길이가 길어지면 먼저 입력한 토큰 정보가 희석됨 → performance 하락
- 층을 깊이 쌓으면 gradient vanishing, gradient exploding → 학습 불안정
Transformer
텍스트 임베딩
토큰화
- 텍스트를 파싱하여
- 각 토큰에게 숫자 ID를 부여하고
- 어떤 토큰이 어떤 숫자 ID에 해당하는지 제시하는 vocabulary(사전) 생성
- 토큰화 단위가 너무 커지면 OOV (Out Of Vocabulary)
토큰화 단위가 너무 작으면 vocabulary 크기 증가.
텍스트 의미 유지 X
$\therefore$ 적절한 크기로 토큰화 - subword 토큰화 : 자주 나오면 단어는 있는 그대로, 가끔 나오는 단어는 더 작은 단위로 토큰화
토큰 임베딩
토큰 : 아직 의미가 없는 n차원 벡터.
임베딩 층에 벡터를 다 때려 넣어 → 이 과정에서 임베딩 층이 학습됨 → 토큰의 의미를 잘 담은 임베딩 생성
위치 임베딩
트랜스포머 : 모든 입력 동시 처리 → 순서 정보 소실
텍스트에서 순서 정보는 중요하기 때문에, 다시 추가를 해준다 (= 위치 인코딩)
- absolute position encoding (절대적)
- 수식으로 위치 정보 추가
- 임베딩으로 위치 정보 학습 (모델로 추론)
- pros) 간단하게 구현 가능
- cons) 토큰 사이의 상대적 위치 정보 활용 불가능. 긴 텍스트(학습 데이터 없음) 추론 시 성능 저하
- relative position encoding (상대적)
Attention
- self attention
- 입력된 문장 내에서 각 단어가 어떤 관련이 있는지 계산 → 각 word의 representation 조정
- 확장성 : 더 깊은 모델 구축 가능. 동일한 블록 재사용.
- 효율성 : 병렬 연산 가능. 처리 시간 감소.
- 더 긴 입력 처리에서 성능 저하 없음
- 단어와 단어 사이의 관계 계산 → 관련이 깊은 단어와 낮은 단어 구분
- 관련이 깊은 단어는 더 많이 맥락 반영 관련이 적은 단어는 더 적게 맥락 반영
벡터 내적 → 유사도 주변 맥락 반영, 유사한 의미의 키 검색
- 각 Query에 대해 모든 key와의 compatibility 계산 → softmax를 통해 가중치로 변환
- 각 Value 벡터에 해당하는 가중치 곱해
- 가중치가 적용된 Value 벡터들을 모두 더해 → $Output = \sum{(weight_i * value_i)}$
- 당연히 Output도 벡터다.
cons)
- 같은 단어(동음이의어)끼리 임베딩 동일 → 관련도가 크게 계산되면서 주변 맥락 반영을 잘 못 할 수 있음
- 간접적인 관련성을 반영하지 못함
$\therefore$ 토큰 임베딩을 변환하는 가중치 $W_Q$, $W_K$ 도입
→ 토큰과 토큰 사이 관계 계산하는 능력 학습시킴
Scaled Dot-Product Attention
트랜스포머는 dot-product attention을 사용.
\(Attention(Q, K, V)=softmax(\frac{QK^T}{\sqrt{d_k}})V\)
- $softmax(\frac{QK^T}{\sqrt{d_k}})$ : 가중치
- $V$ : 행렬. 각 행이 하나의 value 벡터. (sequence_length x $d_v$) 차원
- $d_v$ : 벡터의 차원
$1/\sqrt{d_k}$ 로 additive attention의 일부 장점도 얻음.
- $\sqrt{d_k}$ : scaling. 분산을 1로 정규화.
분산 → 표준편차 변환과 비슷하다고 생각하면 될 듯. - 쿼리, 키의 차원이 커지면 내적한 어텐션 스코어가 너무 커짐. 소프트맥스 함수를 통과할 때 문제를 일으킬 수 있음.
- 디멘션에 루트 씌운 걸로 나눠서 항상 어텐션 스코어가 제대로 나오도록 함.
- 벡터의 분산이 차원의 제곱근에 비례하여 증가함.
- $Var(y) = 1 \times d_k$
- $Var({y \over \sqrt d_k}) = 1$
- 쿼리, 키 벡터가 각각 평균 0, 분산 1이라고 가정하면 위와 같이 키의 디멘션이 나옴. 밑에 루트 붙이면 계속 1 나와서 Vanishing gradient가 안 남.
Multi-Head Attention
: 여러 어텐션 연산 동시 적용 (헤드 수 만큼)
- 쿼리, 키, 값을 헤드 수로 쪼개고,
- 각각 어텐션을 계산한 다음 입력과 같은 형태로 다시 변환.
- 선형 층을 통화시켜서 최종 결과 반환.
각 헤드에서의 Q, K, V는 $d_{model}/h$ 차원.
줄어든 차원에서 attention 연산.
결과 다 concatenate. 연결된 거 다시 $d_{model}$ 차원으로 변환.
입력 x $W_Q$ = $Q$
입력 x $W_K$ = $K$
입력 x $W_V$ = $V$
* 학습 가능한 가중치 행렬
* 각 헤드는 서로 다른 가중치 행렬 사용 중
Encoder-Decoder Attention :
- 디코더의 각 층에서 발생.
- 각 디코더 층에서는 인코더의 최종 출력을 사용.
- 인코더의 최종 출력 : 인코더 전체 (6개 층)를 통과한 것.
크로스 어텐션 : 인코더의 결과를 디코더가 활용
- 인코더의 최종 출력 : 인코더 전체 (6개 층)를 통과한 것.
Encoder Decoder
출처: 위키독스 https://wikidocs.net/162096
인코더 스택 : 6개의 동일한 인코더 층으로 구성. 각 층은 독립적으로 작동
디코더 스택 : 6개의 동일한 디코더 층으로 구성. 각 층은 독립적으로 작동 완전 마지막 리니어 층은 모델 차원(e.g.512)의 출력을 vocabulary와 같은 차원으로 변환함. 각 위치에서 다음에 올 가장 적절한 토큰 예측 가능.
- 각 층은 Multi-Head Attention 과 Feed Forward 2개의 sub-layer를 가짐
- 각 sub-layer 이후 layer-normalization을 함.
- 잔차 연결 후에 각 layer의 출력을 정규화
- 어텐션 layer마다 parameter가 다 달라서 rich contextual info가 나옴.
인코딩 : 입력 시퀀스가 인코더 스택을 한 번 통과.
- 6개의 인코더 층은 순차적으로 모두 통과.
- 각 층의 출력이 다음 층의 입력으로 사용됨.
- 각 층 내에 multi-head attention. (논문에서는 n_head = 8)
- 각 층 내에서는 self-attention 병렬 연산 처리 가능.
- 각 층 거치면서 입력의 표현이 점점 더 추상화되고, 고차원적인 특징을 포착함.
- 각 층에서 잔차 연결.
디코딩도 동일
인코더-디코더 어텐션 (Encoder-Decoder Attention)
- Q: 디코더의 self-attention 거친 후의 표현이 쿼리로 사용됨
- K: 인코더에서 최종적으로 생성된 출력이 키로 사용됨 (인코더에서 나온 입력 단어의 표현)
- V: 인코더에서 최종적으로 생성된 출력이 값으로 사용됨 (인코더에서 나온 입력 단어의 표현)
Residual Connection
: 각 sub-layer의 입력을 출력에 더해.
- 원본 정보가 직접적으로 다음 layer로 전달. 학습 향상.
- sub-layer는 원본 정보에 대한 “잔차”(=”변화”)를 학습하게 됨.
- 중요한 변환을 학습하지 못해도 최소한 원본 정보는 보존됨
- vanishing gradient 문제 완화 (역전파 시 그래디언트가 residual connection을 통해 직접적으로 이전 layer들로 전달 될 수 있음)
Position-wise Feed-Forward Networks
: 데이터의 특징을 학습하는 fully connected layer. 입력 텍스트 전체를 이해하는 역할
두 개의 선형 변환과 ReLU 활성화 함수 사용.
- 특징 추출. 불필요한 정보 제거
- 차원 변환. 입력을 고차원 공간으로 투영
- sparse → 계산량 감소
- 일부 정보(음수) 소실 → 더 중요한 특징에 집중
- 첫 번째 1x1 convolution
$xW_1 + b_1$- 임베딩 차원 $d_{model}$에서 더 높은 차원 $d_{ff}$로 변환.
- 비선형성 추가
- ReLU 활성화
- 두 번째 1x1 convolution
(결과)$W_2 + b_2$- 확장된 고차원 벡터를 다시 $d_{model}$로 축소.
- 모델이 입력과 동일한 차원의 출력 벡터를 유지하도록 함.
cf)
ReLU (Rectified Linear Unit)
- 활성화 함수 $f(x) = max(0, x)$
- feed forward 네트워크에 비선형성 도입
→ 모델이 복잡한 패턴 학습할 수 있도록
FFN 층에서의 weight
- feed-forward network 층에서는 입력 시퀀스의 각 토큰에 대해 $\textbf{독립적으로}$ 동일한 가중치가 작동.
- 가중치 $W_1, W_2$와 바이어스 $b_1, b_2$는 모든 토큰에 대해 동일하게 적용
- 단, 토큰마다 입력되는 벡터가 다르기 때문에, 출력값은 토큰마다 다르게 나옴.
- 각 토큰을 독립적으로 처리 $\rightarrow$ 병렬처리에 적합
학습
인코더 : 멀티 헤드 어텐션
디코더 : 마스크 멀티 헤드 어텐션
디코더의 본질은 생성.
앞에서 생성한 토큰을 기반으로 다음 토큰 생성.
순차적. causal(인과적), auto-regressive(자기 회귀적)
실제 텍스트 생성 시에는 지금까지 생성된 텍스트만 확인 가능.
but 학습할 때는 뒤의 내용이 다 보여
따라서, 그 이전에 생성된 토큰까지만 확인할 수 있도록 마스크 추가
이런 거 곱하면 -inf 곱해지는 부분의 값이 0이 됨
출처
“Attention Is All You Need” arXiv:1706.03762 [cs.CL]