Tất cả thử thách
Khó 0 đã giải Tỷ lệ chấp nhận 0%

Self-attention nhiều đầu (multi-head)

Chiếu đầu vào thành query, key và value, chạy attention song song ở nhiều đầu, rồi kết hợp lại.

Bạn sẽ học được gì

  • Chiếu đầu vào thành query, key và value
  • Chạy attention trong nhiều head
  • Nối các head và chiếu kết quả

Đề bài

Xem bản gốc (English)

Multi-head attention cho phép Transformer chú ý đến nhiều thứ cùng lúc. Với đầu vào X (n token × d cột) và các ma trận trọng số kích thước d × d:

  1. Q = X × w_q, K = X × w_k, V = X × w_v
  2. Chia d cột thành num_heads đầu, mỗi đầu gồm d_k = d / num_heads cột liên tiếp (đầu h dùng các cột từ h×d_k đến (h+1)×d_k - 1).
  3. Với mỗi đầu, chạy scaled dot-product attention trên các cột của nó: với token i, score_j = (Q_i · K_j) / √d_k trên các cột của đầu đó, softmax trên cả n token j, và đầu ra = Σ weight_j × V_j (các cột của đầu đó).
  4. Đặt đầu ra của các đầu vào đúng cột của chúng (ghép lại) rồi nhân với w_o.

Hãy cài đặt multi_head_attention(x, w_q, w_k, w_v, w_o, num_heads).

Đầu vào

x: Ma trận đầu vào n × d.

w_q, w_k, w_v, w_o: Các ma trận trọng số d × d.

num_heads: Số đầu (d chia hết cho số này).

Đầu ra

Trả về ma trận đầu ra n × d.

Ràng buộc

1 ≤ n ≤ 30, 1 ≤ d ≤ 32, 1 ≤ num_heads ≤ d và d chia hết cho num_heads

-5 ≤ mỗi giá trị ≤ 5

Ví dụ

Ví dụ 1
Đầu vào:
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

Kết quả mong đợi:
expected = [
    [0.5, 0.5],
    [0.5, 0.5]
]
Đầu vào:
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;

Kết quả mong đợi:
std::vector<std::vector<double>> expected = {
    {0.5, 0.5},
    {0.5, 0.5}
};
Đầu vào:
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;

Kết quả mong đợi:
double[][] expected = new double[][] {
    new double[] {0.5, 0.5},
    new double[] {0.5, 0.5}
};

Giải thích

w_q toàn số 0, nên mọi điểm bằng 0 và mỗi token lấy trung bình các hàng value: V = X, nên cả hai hàng đều thành [0.5, 0.5].

Ví dụ 2
Đầu vào:
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

Kết quả mong đợi:
expected = [
    [4.0, 2.0]
]
Đầu vào:
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;

Kết quả mong đợi:
std::vector<std::vector<double>> expected = {
    {4.0, 2.0}
};
Đầu vào:
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;

Kết quả mong đợi:
double[][] expected = new double[][] {
    new double[] {4.0, 2.0}
};

Giải thích

Chỉ có một token nên mỗi đầu chỉ chú ý đến chính nó, đầu ra ghép lại là V = [2, 4]. w_o hoán đổi hai cột: [4, 2].

Tham khảo

Thử thách này đã được điều chỉnh cho CodeUtility. Hãy làm theo đề bài và các ràng buộc ở trên.

Học hỏi từ người khác

Giải pháp cộng đồng

Chưa có giải pháp nào được chia sẻ. Hãy giải thử thách này để đăng giải pháp đầu tiên.