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

Scaled dot-product attention

Cài đặt phép attention, trái tim của Transformer và các mô hình ngôn ngữ lớn.

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

  • Chấm điểm query với key
  • Áp dụng softmax để có trọng số attention
  • Trộn value theo các trọng số đó

Đề bài

Xem bản gốc (English)

Attention cho phép mỗi vị trí trong chuỗi nhìn vào mọi vị trí khác và trộn thông tin của chúng. Đây là lõi của kiến trúc Transformer mà các mô hình ngôn ngữ lớn sử dụng:

Attention(Q, K, V) = softmax(Q × Kᵀ / √d) × V

Với mỗi hàng query q:

  1. Chấm điểm từng hàng key k: score = (q · k) / √d, với d là số cột của K.
  2. Biến điểm thành trọng số bằng softmax (trừ điểm lớn nhất trước khi tính e^x).
  3. Hàng đầu ra là tổng có trọng số của các hàng value: Σ weight_j × values[j].

Hãy cài đặt attention(queries, keys, values).

Đầu vào

queries: n hàng, mỗi hàng d số.

keys: m hàng, mỗi hàng d số.

values: m hàng (mỗi key một hàng), mỗi hàng v số.

Đầu ra

Trả về ma trận đầu ra kích thước n × v.

Ràng buộc

1 ≤ n, m ≤ 50, 1 ≤ d, v ≤ 32

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

Ví dụ

Ví dụ 1
Đầu vào:
keys = [
    [1.0, 0.0],
    [1.0, 0.0]
]
values = [
    [2.0, 4.0],
    [6.0, 8.0]
]
queries = [
    [1.0, 0.0]
]

Kết quả mong đợi:
expected = [
    [4.0, 6.0]
]
Đầu vào:
std::vector<std::vector<double>> keys = {
    {1.0, 0.0},
    {1.0, 0.0}
};
std::vector<std::vector<double>> values = {
    {2.0, 4.0},
    {6.0, 8.0}
};
std::vector<std::vector<double>> queries = {
    {1.0, 0.0}
};

Kết quả mong đợi:
std::vector<std::vector<double>> expected = {
    {4.0, 6.0}
};
Đầu vào:
double[][] keys = new double[][] {
    new double[] {1.0, 0.0},
    new double[] {1.0, 0.0}
};
double[][] values = new double[][] {
    new double[] {2.0, 4.0},
    new double[] {6.0, 8.0}
};
double[][] queries = new double[][] {
    new double[] {1.0, 0.0}
};

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

Giải thích

Hai key cho cùng một điểm, nên trọng số softmax là 0.5 và 0.5. Đầu ra là trung bình của các hàng value: [(2 + 6) / 2, (4 + 8) / 2] = [4, 6].

Ví dụ 2
Đầu vào:
keys = [
    [3.0, 1.0],
    [-2.0, 5.0],
    [7.0, 7.0]
]
values = [
    [1.0],
    [2.0],
    [6.0]
]
queries = [
    [0.0, 0.0]
]

Kết quả mong đợi:
expected = [
    [3.0]
]
Đầu vào:
std::vector<std::vector<double>> keys = {
    {3.0, 1.0},
    {-2.0, 5.0},
    {7.0, 7.0}
};
std::vector<std::vector<double>> values = {
    {1.0},
    {2.0},
    {6.0}
};
std::vector<std::vector<double>> queries = {
    {0.0, 0.0}
};

Kết quả mong đợi:
std::vector<std::vector<double>> expected = {
    {3.0}
};
Đầu vào:
double[][] keys = new double[][] {
    new double[] {3.0, 1.0},
    new double[] {-2.0, 5.0},
    new double[] {7.0, 7.0}
};
double[][] values = new double[][] {
    new double[] {1.0},
    new double[] {2.0},
    new double[] {6.0}
};
double[][] queries = new double[][] {
    new double[] {0.0, 0.0}
};

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

Giải thích

Query toàn số 0 cho điểm 0 với mọi key, nên attention chia đều: (1 + 2 + 6) / 3 = 3.

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.