Self-Attention
셀프 어텐션은 같은 입력에서 가져온 쿼리, 키 및 값을 사용해 시퀀스의 각 토큰이 다른 모든 토큰에 가중치를 부여하도록 합니다. Transformer의 핵심 구성 요소입니다.
셀프 어텐션은 쿼리, 키, 값이 모두 동일한 시퀀스에서 나오는 어텐션의 한 형태로, 모든 토큰은 해당 시퀀스의 다른 모든 토큰을 바탕으로 새로운 표현을 구성합니다. 셀프 어텐션 계층 하나는 한 번의 연산으로 문단의 첫 단어와 마지막 단어 또는 이미지의 대각선 양 끝을 연결할 수 있습니다. 반면 순환 신경망(RNN)은 위치마다 한 단계씩 필요하고, 합성곱 신경망(CNN)은 수용 영역을 넓히기 위해 여러 계층을 쌓아야 합니다. 피드포워드 계층과 함께 쌓으면 2017년 논문 Attention Is All You Need에서 소개된 Transformer가 형성되며, 이는 대규모 언어 모델(LLM), Vision Transformer(ViT), Transformer 기반 탐지기의 기반이 됩니다.
셀프 어텐션 작동 방식#
임베딩으로 표현된 각 입력 토큰에 학습된 행렬 세 개를 곱해 쿼리, 키, 값을 얻습니다. 토큰 i의 출력은 모든 토큰의 값 벡터를 가중 평균한 것으로, 가중치는 토큰 i의 쿼리와 모든 키 사이의 스케일링된 내적에 소프트맥스를 적용해 계산합니다. 이는 어텐션 메커니즘 페이지에서 설명한 스케일링된 내적 어텐션입니다. 쿼리, 키, 값이 동일한 토큰의 투영 결과이므로 토큰이 n개인 시퀀스는 n × n 가중치 행렬을 생성합니다. “동물이 너무 피곤해서 길을 건너지 않았다”라는 문장에서 “동물”이 너무 피곤했기 때문에 “그것”이 길을 건너지 않았다는 경우, “그것”에 해당하는 행은 “동물”에 대부분의 가중치를 두어 해당 단어의 의미를 자체 표현에 반영할 수 있습니다.
멀티헤드 셀프 어텐션은 더 작은 투영을 사용해 이러한 연산 여러 개를 병렬로 수행한 뒤 결과를 연결하므로, 각 헤드는 한 헤드에서는 구문 관계를, 다른 헤드에서는 공지시 관계를 추적하는 등 서로 다른 관계를 학습할 수 있습니다. 최초의 Transformer는 512차원 모델에서 각각 64차원인 헤드 8개를 사용했습니다.
순서 정보 부재와 인과 마스킹#
셀프 어텐션은 입력을 집합으로 처리합니다. 토큰을 섞으면 출력도 같은 방식으로 섞일 뿐 다른 변화는 없으므로, 모델은 최초 Transformer의 사인파 임베딩이나 현재 대부분의 LLM에서 사용하는 회전 위치 임베딩(RoPE)과 같은 위치 정보를 추가합니다. BERT와 같은 인코더에서는 모든 토큰이 양방향으로 어텐션을 수행합니다. 디코더 전용 LLM은 인과 마스크를 적용해 각 토큰이 자기 자신과 이전 토큰에만 어텐션을 수행하게 하므로, 모델이 왼쪽에서 오른쪽으로 텍스트를 생성하고 KV 캐시를 통해 이전 토큰의 키와 값을 재사용할 수 있습니다. 동일한 텐서를 쿼리, 키, 값으로 전달하면 PyTorch의 MultiheadAttention에서 두 가지 특성을 모두 확인할 수 있습니다:
import torch
torch.manual_seed(0)
tokens = torch.randn(1, 5, 16) # 각각 16개 특징을 가진 토큰 5개의 단일 시퀀스
attn = torch.nn.MultiheadAttention(embed_dim=16, num_heads=2, batch_first=True)
# 셀프 어텐션: 쿼리, 키, 값은 모두 동일한 토큰을 투영한 결과입니다.
out, weights = attn(tokens, tokens, tokens)
print("Attention weights shape:", tuple(weights.shape))
# 토큰을 섞으면 출력만 함께 섞이므로 위치 정보가 없으면 단어 순서를 알 수 없습니다.
perm = torch.tensor([3, 0, 4, 1, 2])
out_shuffled, _ = attn(tokens[:, perm], tokens[:, perm], tokens[:, perm])
print("Same outputs after shuffling:", torch.allclose(out_shuffled, out[:, perm], atol=1e-6))
# 인과 마스크는 디코더 전용 LLM에서처럼 이후 토큰에 대한 어텐션을 차단합니다.
mask = torch.triu(torch.ones(5, 5, dtype=torch.bool), diagonal=1)
_, causal_weights = attn(tokens, tokens, tokens, attn_mask=mask)
print(causal_weights[0].detach().round(decimals=2))(1, 5, 5) 가중치 형태, 섞기 테스트의 True, 그리고 하삼각 행렬이 출력됩니다. 첫 번째 토큰은 자기 자신에 1.00의 가중치를 두고, 두 번째 토큰은 처음 두 토큰에 0.31과 0.69의 가중치를 나누어 두며, 대각선 위의 모든 항목은 0.00입니다.
셀프 어텐션과 관련 용어 비교#
- 어텐션 메커니즘: 쿼리와 키의 유사도에 따라 값에 가중치를 적용하는 일반적인 연산입니다. 셀프 어텐션은 세 요소가 모두 하나의 시퀀스에서 나오는 경우입니다.
- 크로스 어텐션: 쿼리는 한 시퀀스에서, 키와 값은 다른 시퀀스에서 가져옵니다. 번역 디코더는 인코딩된 원본 문장에 어텐션을 수행하고, DETR 디코더의 객체 쿼리는 이미지 특징에 어텐션을 수행하며, 텍스트-이미지 확산 모델은 프롬프트에 어텐션을 수행합니다.
- 합성곱: 모든 위치에 동일한 작은 필터를 적용하므로 각 출력은 콘텐츠와 무관한 고정된 로컬 윈도우를 보며 가중치도 콘텐츠에 따라 달라지지 않습니다. 셀프 어텐션은 콘텐츠를 바탕으로 가중치를 계산하고 입력 전체를 처리합니다.
최초의 인코더-디코더 Transformer는 양방향 셀프 어텐션, 인과 셀프 어텐션, 크로스 어텐션을 결합합니다:
연산 비용과 효율적인 변형#
모든 토큰 쌍에 점수를 매기면 시간과 메모리 사용량이 시퀀스 길이의 제곱에 비례해 증가합니다. 토큰 1,000개는 헤드당 점수 100만 개를, 토큰 100,000개는 100억 개를 생성합니다. FlashAttention은 전체 행렬을 저장하지 않고 타일 단위로 정확한 셀프 어텐션을 계산하고, 그룹 쿼리 어텐션(GQA)은 헤드 간에 키와 값을 공유해 KV 캐시를 줄이며, 슬라이딩 윈도우, 희소 및 선형 어텐션은 토큰 쌍의 수를 줄입니다. 컴퓨터 비전에서는 이미지 해상도에 따라 토큰 수가 달라지므로 많은 탐지기는 특징 맵이 가장 작은 곳에서만 셀프 어텐션을 적용합니다. YOLO26은 백본 끝의 C2PSA 블록과 넥의 어텐션이 활성화된 C3k2 블록 하나에서만 멀티헤드 셀프 어텐션을 수행합니다. 두 블록 모두 640픽셀 이미지의 20×20 스트라이드 32 특징 맵, 즉 토큰 400개를 처리합니다. 해당 어텐션 모듈은 특징 맵에 1×1 합성곱을 한 번 적용해 쿼리, 키, 값을 계산하고, 값에 적용하는 3×3 깊이별 합성곱으로 위치 정보를 추가합니다.










