저번 시간
우리는
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. 먼저, 각각의 선형 레이어를 사용하여 쿼리, 키, 밸류 행렬을 얻습니다.
- 2. 그 다음, 각 헤드가 독립적으로 어텐션을 수행할 수 있도록 헤드를 별도의 차원으로 분리합니다.
- 이를 위해 view() 함수를 사용하여 원래 형태의 텐서를
- num_heads와 head_dim을 가진 새로운 형태의 텐서로 변환합니다.
- 3. 그리고 각 헤드가 독립적으로 어텐션을 수행할 수 있도록
- num_heads 차원을 두 번째 차원으로 전치합니다(transpose(1, 2)).
- 이렇게 하면 쿼리, 키, 밸류 행렬의 각 헤드가 별도로 어텐션을 수행할 수 있습니다.
이 추가 단계는 입력 시퀀스의 다양한 정보를 포착하고 학습하는 데 도움이 되는 복잡성을 높이는 것으로,
결과적으로 전체 모델의 표현력과 성능을 향상시킵니다.
이번 시간을 통해
이론으로만 알고 있었던
Multi-Head Attention
에 대한 이해가 더 높아졌을 거라고
믿어요!
혹시라도 이해가 안 되는 부분이나
궁금한 부분은 댓글 부탁드립니다!
'AI > Transformer' 카테고리의 다른 글
Transformer_7(기초부터 심화까지) (0) | 2023.06.28 |
---|---|
Transformer_6(기초부터 심화까지) (0) | 2023.06.28 |
Transformer_4(기초부터 심화까지) (0) | 2023.06.28 |
Transformer_3(기초부터 심화까지) (0) | 2023.06.28 |
Transformer_2(기초부터 심화까지) (0) | 2023.06.28 |