모든 챌린지
어려움 0명 해결 정답률 0%

멀티헤드 셀프 어텐션

입력을 쿼리, 키, 값으로 투영하고 여러 헤드에서 병렬로 어텐션을 실행한 뒤 결합합니다.

배우게 될 내용

  • 입력을 쿼리, 키, 값으로 사영하기
  • 여러 헤드에서 어텐션 실행하기
  • 헤드를 이어 붙이고 결과 사영하기

문제

원문 보기 (영어)

멀티헤드 어텐션은 트랜스포머가 여러 가지에 동시에 주의를 기울이게 합니다. 입력 X(n개 토큰 × d개 열)와 d × d 크기의 가중치 행렬에 대해:

  1. Q = X × w_q, K = X × w_k, V = X × w_v
  2. d개의 열을 num_heads개의 헤드로 나눕니다. 각 헤드는 연속된 d_k = d / num_heads개의 열을 가집니다 (헤드 h는 열 h×d_k부터 (h+1)×d_k - 1까지 사용).
  3. 각 헤드에서 그 열들로 스케일드 닷-프로덕트 어텐션을 실행합니다: 토큰 i에 대해 그 헤드의 열로 score_j = (Q_i · K_j) / √d_k를 계산하고, n개의 모든 토큰 j에 대해 소프트맥스를 취한 뒤, 출력 = Σ weight_j × V_j (그 헤드의 열).
  4. 각 헤드의 출력을 원래 열에 다시 놓고(연결하고) w_o를 곱합니다.

multi_head_attention(x, w_q, w_k, w_v, w_o, num_heads)를 구현하세요.

입력

x: n × d 입력 행렬.

w_q, w_k, w_v, w_o: d × d 가중치 행렬.

num_heads: 헤드 수 (d는 이 수로 나누어떨어짐).

출력

n × d 출력 행렬을 반환하세요.

제약 조건

1 ≤ n ≤ 30, 1 ≤ d ≤ 32, 1 ≤ num_heads ≤ d이며 d는 num_heads로 나누어떨어짐

-5 ≤ 각 값 ≤ 5

예시

예시 1
입력:
x = [
    [1.0, 0.0],
    [0.0, 1.0]
]
w_k = [
    [1.0, 0.0],
    [0.0, 1.0]
]
w_o = [
    [1.0, 0.0],
    [0.0, 1.0]
]
w_q = [
    [0.0, 0.0],
    [0.0, 0.0]
]
w_v = [
    [1.0, 0.0],
    [0.0, 1.0]
]
num_heads = 1

예상 출력:
expected = [
    [0.5, 0.5],
    [0.5, 0.5]
]
입력:
std::vector<std::vector<double>> x = {
    {1.0, 0.0},
    {0.0, 1.0}
};
std::vector<std::vector<double>> w_k = {
    {1.0, 0.0},
    {0.0, 1.0}
};
std::vector<std::vector<double>> w_o = {
    {1.0, 0.0},
    {0.0, 1.0}
};
std::vector<std::vector<double>> w_q = {
    {0.0, 0.0},
    {0.0, 0.0}
};
std::vector<std::vector<double>> w_v = {
    {1.0, 0.0},
    {0.0, 1.0}
};
long long num_heads = 1;

예상 출력:
std::vector<std::vector<double>> expected = {
    {0.5, 0.5},
    {0.5, 0.5}
};
입력:
double[][] x = new double[][] {
    new double[] {1.0, 0.0},
    new double[] {0.0, 1.0}
};
double[][] w_k = new double[][] {
    new double[] {1.0, 0.0},
    new double[] {0.0, 1.0}
};
double[][] w_o = new double[][] {
    new double[] {1.0, 0.0},
    new double[] {0.0, 1.0}
};
double[][] w_q = new double[][] {
    new double[] {0.0, 0.0},
    new double[] {0.0, 0.0}
};
double[][] w_v = new double[][] {
    new double[] {1.0, 0.0},
    new double[] {0.0, 1.0}
};
long num_heads = 1;

예상 출력:
double[][] expected = new double[][] {
    new double[] {0.5, 0.5},
    new double[] {0.5, 0.5}
};

설명

w_q가 모두 0이므로 모든 점수가 0이고 각 토큰은 값 행들의 평균이 됩니다: V = X이므로 두 행 모두 [0.5, 0.5]가 됩니다.

예시 2
입력:
x = [
    [2.0, 4.0]
]
w_k = [
    [1.0, 0.0],
    [0.0, 1.0]
]
w_o = [
    [0.0, 1.0],
    [1.0, 0.0]
]
w_q = [
    [1.0, 0.0],
    [0.0, 1.0]
]
w_v = [
    [1.0, 0.0],
    [0.0, 1.0]
]
num_heads = 2

예상 출력:
expected = [
    [4.0, 2.0]
]
입력:
std::vector<std::vector<double>> x = {
    {2.0, 4.0}
};
std::vector<std::vector<double>> w_k = {
    {1.0, 0.0},
    {0.0, 1.0}
};
std::vector<std::vector<double>> w_o = {
    {0.0, 1.0},
    {1.0, 0.0}
};
std::vector<std::vector<double>> w_q = {
    {1.0, 0.0},
    {0.0, 1.0}
};
std::vector<std::vector<double>> w_v = {
    {1.0, 0.0},
    {0.0, 1.0}
};
long long num_heads = 2;

예상 출력:
std::vector<std::vector<double>> expected = {
    {4.0, 2.0}
};
입력:
double[][] x = new double[][] {
    new double[] {2.0, 4.0}
};
double[][] w_k = new double[][] {
    new double[] {1.0, 0.0},
    new double[] {0.0, 1.0}
};
double[][] w_o = new double[][] {
    new double[] {0.0, 1.0},
    new double[] {1.0, 0.0}
};
double[][] w_q = new double[][] {
    new double[] {1.0, 0.0},
    new double[] {0.0, 1.0}
};
double[][] w_v = new double[][] {
    new double[] {1.0, 0.0},
    new double[] {0.0, 1.0}
};
long num_heads = 2;

예상 출력:
double[][] expected = new double[][] {
    new double[] {4.0, 2.0}
};

설명

토큰이 하나뿐이므로 각 헤드는 자기 자신에게만 주의를 기울이고, 연결된 출력은 V = [2, 4]입니다. w_o가 두 열을 바꾸므로 [4, 2]가 됩니다.

참고 자료

이 문제는 CodeUtility에 맞게 수정되었습니다. 위의 설명과 제한 조건을 따르세요.

다른 사람의 풀이 보기

커뮤니티 솔루션

아직 공개된 풀이가 없습니다. 문제를 풀고 첫 풀이를 공유하세요.