크로스 어텐션
Cross-Attention
두 다른 시퀀스 간의 관계를 학습하는 어텐션. 인코더-디코더 연결에 사용.
Cross-Attention
두 다른 시퀀스 간의 관계를 학습하는 어텐션. 인코더-디코더 연결에 사용.
크로스 어텐션(Cross-Attention)은 두 개의 서로 다른 시퀀스 간의 관계를 학습하는 어텐션 메커니즘입니다. 셀프 어텐션(Self-Attention)이 하나의 시퀀스 내에서 토큰 간 관계를 계산하는 반면, 크로스 어텐션은 쿼리(Query)가 한 시퀀스에서, 키(Key)와 값(Value)이 다른 시퀀스에서 오는 구조입니다. 이를 통해 인코더-디코더 아키텍처에서 두 모달리티 간의 정보 교환이 가능해집니다.
크로스 어텐션은 2017년 "Attention Is All You Need" 논문에서 Transformer 아키텍처와 함께 소개되었습니다. 원래 기계 번역을 위해 설계되어, 디코더가 인코더의 출력을 참조할 수 있게 했습니다. 이후 이 개념은 이미지 캡셔닝, 텍스트-이미지 생성(Stable Diffusion), 비전-언어 모델(CLIP, GPT-4V), 음성 인식(Whisper) 등 멀티모달 AI의 핵심 구성요소가 되었습니다.
수학적으로 크로스 어텐션은 Attention(Q, K, V) = softmax(QK^T / sqrt(d_k))V로 표현됩니다. 여기서 Q는 디코더(타겟) 시퀀스에서 생성되고, K와 V는 인코더(소스) 시퀀스에서 생성됩니다. 예를 들어 Stable Diffusion에서 텍스트 프롬프트의 임베딩이 K와 V가 되고, 이미지의 잠재 표현이 Q가 되어 텍스트 조건에 맞는 이미지를 생성합니다. 이 과정에서 텍스트의 어떤 부분이 이미지의 어떤 영역에 영향을 주는지 학습됩니다.
2024-2025년 실무에서 크로스 어텐션은 RAG(Retrieval-Augmented Generation), 멀티모달 LLM, 비디오 생성 모델 등에서 핵심 역할을 합니다. 특히 Llama 3.2 Vision, Claude 3의 이미지 이해, Sora의 비디오 생성 등에서 텍스트와 시각 정보를 연결하는 데 사용됩니다. Flash Attention, Memory Efficient Attention 등의 최적화 기법과 결합하여 긴 시퀀스에서도 효율적으로 동작합니다.
PyTorch로 구현한 크로스 어텐션 모듈입니다.
# 크로스 어텐션 구현
# pip install torch
import torch
import torch.nn as nn
import torch.nn.functional as F
import math
class CrossAttention(nn.Module):
"""
크로스 어텐션 모듈
- query_dim: 쿼리 시퀀스의 차원 (디코더 측)
- context_dim: 컨텍스트 시퀀스의 차원 (인코더 측)
- heads: 멀티헤드 어텐션의 헤드 수
"""
def __init__(self, query_dim, context_dim, heads=8, dim_head=64, dropout=0.0):
super().__init__()
self.heads = heads
self.dim_head = dim_head
inner_dim = heads * dim_head
self.scale = dim_head ** -0.5
# Q는 query(디코더)에서, K/V는 context(인코더)에서 생성
self.to_q = nn.Linear(query_dim, inner_dim, bias=False)
self.to_k = nn.Linear(context_dim, inner_dim, bias=False)
self.to_v = nn.Linear(context_dim, inner_dim, bias=False)
self.to_out = nn.Sequential(
nn.Linear(inner_dim, query_dim),
nn.Dropout(dropout)
)
def forward(self, x, context):
"""
x: 쿼리 시퀀스 [batch, seq_len_q, query_dim]
context: 컨텍스트 시퀀스 [batch, seq_len_ctx, context_dim]
"""
batch_size = x.shape[0]
# Q, K, V 계산
q = self.to_q(x) # [batch, seq_q, inner_dim]
k = self.to_k(context) # [batch, seq_ctx, inner_dim]
v = self.to_v(context) # [batch, seq_ctx, inner_dim]
# 멀티헤드로 reshape
q = q.view(batch_size, -1, self.heads, self.dim_head).transpose(1, 2)
k = k.view(batch_size, -1, self.heads, self.dim_head).transpose(1, 2)
v = v.view(batch_size, -1, self.heads, self.dim_head).transpose(1, 2)
# 어텐션 스코어 계산
attn = torch.matmul(q, k.transpose(-2, -1)) * self.scale
attn = F.softmax(attn, dim=-1)
# 어텐션 적용
out = torch.matmul(attn, v)
out = out.transpose(1, 2).reshape(batch_size, -1, self.heads * self.dim_head)
return self.to_out(out)
# 사용 예시
batch_size = 2
decoder_seq_len = 10 # 디코더 시퀀스 길이
encoder_seq_len = 20 # 인코더 시퀀스 길이
query_dim = 512
context_dim = 768
# 크로스 어텐션 레이어 생성
cross_attn = CrossAttention(query_dim=query_dim, context_dim=context_dim)
# 입력 생성
decoder_hidden = torch.randn(batch_size, decoder_seq_len, query_dim)
encoder_output = torch.randn(batch_size, encoder_seq_len, context_dim)
# 크로스 어텐션 수행
output = cross_attn(decoder_hidden, encoder_output)
print(f"입력 (디코더): {decoder_hidden.shape}")
print(f"컨텍스트 (인코더): {encoder_output.shape}")
print(f"출력: {output.shape}") # [2, 10, 512]
Hugging Face Transformers에서 크로스 어텐션 활용 예제입니다.
# 이미지 캡셔닝에서의 크로스 어텐션 활용
# pip install transformers pillow torch
from transformers import BlipProcessor, BlipForConditionalGeneration
from PIL import Image
import requests
# BLIP 모델 로드 (이미지-텍스트 크로스 어텐션 사용)
processor = BlipProcessor.from_pretrained("Salesforce/blip-image-captioning-base")
model = BlipForConditionalGeneration.from_pretrained("Salesforce/blip-image-captioning-base")
# 샘플 이미지 로드
url = "http://images.cocodataset.org/val2017/000000039769.jpg"
image = Image.open(requests.get(url, stream=True).raw)
# 이미지 캡션 생성 (내부적으로 크로스 어텐션 사용)
inputs = processor(image, return_tensors="pt")
outputs = model.generate(**inputs, max_new_tokens=50)
# 결과 디코딩
caption = processor.decode(outputs[0], skip_special_tokens=True)
print(f"생성된 캡션: {caption}")
# 크로스 어텐션 가중치 확인 (선택적)
with torch.no_grad():
outputs = model(**inputs, output_attentions=True)
# decoder의 cross_attentions 확인 가능
if hasattr(outputs, 'cross_attentions') and outputs.cross_attentions:
print(f"크로스 어텐션 레이어 수: {len(outputs.cross_attentions)}")
크로스 어텐션에서 Query는 타겟(디코더) 시퀀스, Key/Value는 소스(인코더) 시퀀스에서 옵니다. 셀프 어텐션과 혼동하여 모두 같은 시퀀스에서 생성하면 크로스 어텐션이 아닙니다.
인코더와 디코더의 시퀀스 길이는 다를 수 있지만, 배치 크기와 임베딩 차원은 맞아야 합니다. 특히 멀티모달에서 이미지 패치 수와 텍스트 토큰 수가 다른 것은 정상입니다.
크로스 어텐션 구현 시 Q, K, V의 차원과 출처를 명확히 문서화하세요. 디버깅할 때는 어텐션 가중치를 시각화하여 모델이 올바른 관계를 학습하는지 확인하세요.