본문 바로가기
AI/Transformer

Transformer_5(기초부터 심화까지)

by 전주혁 2023. 6. 28.

저번 시간

우리는

Multi-Head Attention

에 대해 배웠어요.

 

이번 시간에는

코드를 통해

 

Multi-Head Attention

더 쉽게 이해해 봅시다!

 

 

 

transformer_2 포스팅에서

우리는 Self-Attention이 이루어지는 과정을

코드를 통해 이해했어요.

 

 

그래서

Self-Attention과

Multi-Head Attention의

코드 구성이 어떻게 다르고

왜 다른지 이해해 봅시다.

 

 

먼저

저번 시간 이해했었던

Self-Attention 코드를

다시 한 번 보고 갈까요?

 

 

코드에 대해 다시 간단한 설명을 하자면

쿼리, 키, 밸류 선형 레이어를 정의하고,

forward 함수에서 입력 시퀀스 내 단어 간의 관계를 반영한 어텐션 출력을 계산합니다.

 

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

 - attn_scores=torch.matmul(q, k.transpose(-2, -1))/(embed_dim ** 0.5)는 쿼리와 키 행렬의 내적(dot product)을 계산 후,

임베딩 차원의 제곱근으로 나눠 스케일링

이렇게 스케일링하는 이유는 큰 값의 닷 프로덕트가 softmax 함수에서 큰 차이를 발생시키지 않도록 하여

학습의 안정성을 높이기 위함입니다.

 

어텐션 확률 계산

 - attn_probs = F.softmax(attn_scores, dim=-1)는 계산된 어텐션 점수를 이용해 어텐션 확률을 계산합니다. 

이 확률은 각 단어 간의 상대적인 관계를 나타냅니다.

 

 

 

어텐션 출력 계산

 - attn_output = torch.matmul(attn_probs, v)는 어텐션 확률과 밸류 행렬을 곱하여 어텐션 출력을 계산합니다. 

이 출력은 입력 시퀀스 내 각 단어의 가중치가 반영된 결과물입니다.

 

 

 

어느 정도 다시 기억이 돌아오셨나요?

 

 

 

 

 

이제 Multi-Head Attention

봐 볼까요?

먼저 'Head' 란

 


헤드(Head)는 멀티-헤드 어텐션에서 어텐션 메커니즘을 수행하는 독립적인 단위를 의미합니다. 

각 헤드는 서로 다른 가중치를 사용하여 입력 데이터의 쿼리(Query), 키(Key), 밸류(Value)를 처리하고, 결과를 생성합니다.

 

 


헤드의 주요 역할과 의미는 다음과 같습니다.

 


정보의 다양성: 각 헤드가 독립적으로 작동하므로, 다양한 관점에서 입력 데이터의 정보를 포착할 수 있습니다. 

이를 통해 모델이 더 복잡하고 다양한 패턴을 학습할 수 있습니다.

병렬 처리: 멀티-헤드 어텐션은 여러 헤드를 동시에 수행할 수 있기 때문에, 

병렬 처리를 통해 계산 효율을 높일 수 있습니다.

향상된 표현력: 각 헤드에서 얻은 결과를 결합함으로써, 모델의 전체적인 표현력이 향상됩니다. 

이를 통해 더 정확한 예측 및 생성이 가능해집니다.

 

 

 


요약하면, 헤드는 멀티-헤드 어텐션에서 독립적으로 어텐션 메커니즘을 수행하는 단위로, 

다양한 관점에서 입력 데이터의 정보를 포착하고, 

병렬 처리를 통해 계산 효율을 높이며, 

모델의 표현력을 향상시키는 역할을 합니다.

 

 

 

 

 

 

그래서 똑같은 문장에 똑같은 단어라도

각 헤드마다

쿼리-키-밸류의 값이 달라집니다!

 

그래서

왜 이렇게 달라지게 하냐?

쉬운 예시를 들어서 이해를 해 봅시다

 

.

 

만약, 입력 문장이 "나는 오늘 기분이 좋아" 라고 가정해 봅시다.

입력 문장을 4개의 단어로 나눌 수 있으며,

각 단어는 임베딩되어 모델에 전달됩니다.

 

 

Self-attention의 경우,

입력 문장에 대해 한 번의 어텐션만 계산됩니다.

이렇게 하면 단어 간의 관계를 학습할 수 있지만,

한 번의 어텐션만으로 문장의 모든 다양한 특징을 포착하기 어려울 수 있습니다.

 

 

Multi-head Attention의 경우,

여러 개의 어텐션 헤드를 사용하여 입력 문장의 다양한 특징을 캡처하려고 합니다.

예를 들어, 각 어텐션 헤드가 다음과 같은 다양한 정보를 포착할 수 있습니다.

 

 

 

헤드 1: 문법적인 관계를 포착 (예: 주어-동사, 목적어-동사 관계 등)

헤드 2: 문장 내 단어의 순서를 포착 (예: "나는" 다음에 "오늘"이 온다는 것 등)

헤드 3: 문장 내 단어들 사이의 의미 관계를 포착 (예: "기분이"와 "좋아" 사이의 관계 등)

 

 

 

쿼리, 키, 밸류 행렬을 생성하고 각 헤드에 대해 독립적으로 어텐션을 수행함으로써

이러한 다양한 정보를 동시에 학습할 수 있게 됩니다.

이렇게 하면 전체 모델의 표현력과 성능이 향상됩니다.

 

따라서 Multi-head Attention은 입력 문장의 다양한 특징을 동시에 고려하는 데 도움이 되며,

이를 위해 쿼리, 키, 밸류 행렬을 생성하고 각 헤드에 대해 독립적으로 어텐션을 수행하는 추가 작업이 필요합니다.

 

 

자! 이제 Multi-Head Attention을 사용하는 이유에 대해 이해가 되셨나요?!

 

 

 

이제

Multi-Head Attention

코드를 봅시다.

 

 

이전의 Self-Attention 코드에 비해

많이 어지럽죠?

 

쿼리-키-밸류 부분이

self-attention에 비해

어지럽게 바뀐 것을 바로 캐치하셨나요?

 

 

 

self-attention에서는

 

q = self.query(x)

k = self.key(x)

v = self.value(x)

 

 

 

multi-head Attention은    

 

q = self.query(x).view(batch_size, -1, self.num_heads, self.head_dim)

k = self.key(x).view(batch_size, -1, self.num_heads, self.head_dim)

v = self.value(x).view(batch_size, -1, self.num_heads, self.head_dim)

    q = q.transpose(1, 2)

    k = k.transpose(1, 2)

    v = v.transpose(1, 2)

 

 

이렇게 2 단계로 나누어 집니다.

 

 

 

Multi-head Attention이

Self-attention과

다른 이유는

여러 개의 어텐션 헤드를 사용하기 때문입니다.

 

 

각 어텐션 헤드가 독립적으로 어텐션을 수행하려면,

각 헤드에 대해 쿼리, 키, 밸류 행렬을 따로 준비해야 합니다.

 

이를 위해 추가 단계가 필요합니다.

 

  1. 1. 먼저, 각각의 선형 레이어를 사용하여 쿼리, 키, 밸류 행렬을 얻습니다.
  2.  
  3. 2. 그 다음, 각 헤드가 독립적으로 어텐션을 수행할 수 있도록 헤드를 별도의 차원으로 분리합니다.
  4. 이를 위해 view() 함수를 사용하여 원래 형태의 텐서를
  5. num_heads와 head_dim을 가진 새로운 형태의 텐서로 변환합니다.
  6.  
  7. 3. 그리고 각 헤드가 독립적으로 어텐션을 수행할 수 있도록
  8. num_heads 차원을 두 번째 차원으로 전치합니다(transpose(1, 2)).
  9.  
  10. 이렇게 하면 쿼리, 키, 밸류 행렬의 각 헤드가 별도로 어텐션을 수행할 수 있습니다.

 

 

이 추가 단계는 입력 시퀀스의 다양한 정보를 포착하고 학습하는 데 도움이 되는 복잡성을 높이는 것으로,

결과적으로 전체 모델의 표현력과 성능을 향상시킵니다.

 

 

이번 시간을 통해

이론으로만 알고 있었던

Multi-Head Attention

에 대한 이해가 더 높아졌을 거라고

믿어요!

 

 

혹시라도 이해가 안 되는 부분이나

궁금한 부분은 댓글 부탁드립니다!