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

Self-attention nhân quả (có mask)

Cài đặt attention có mask mà các mô hình GPT dùng, trong đó mỗi token chỉ được nhìn chính nó và các token trước.

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

  • Che các token tương lai bằng mặt nạ nhân quả
  • Chỉ áp dụng softmax trên vị trí được phép
  • Hiểu cách mô hình ngôn ngữ sinh văn bản

Đề bài

Xem bản gốc (English)

Mô hình ngôn ngữ không được nhìn trộm các từ tương lai. Attention nhân quả che tương lai: hàng query i chỉ được chú ý đến các hàng key từ 0 đến i.

Với mỗi hàng i (queries, keys và values đều có n hàng):

  1. Chấm điểm mỗi key được phép j ≤ i: score = (queries[i] · keys[j]) / √d, với d là số cột của keys.
  2. Áp dụng softmax chỉ trên các điểm được phép đó (trừ điểm lớn nhất trước khi tính e^x).
  3. Hàng đầu ra i = Σ weight_j × values[j] với j ≤ i.

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

Đầu vào

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

values: n 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 ≤ 50, 1 ≤ d, v ≤ 32

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

Ví dụ

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

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

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

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

Giải thích

Mọi điểm đều bằng 0, nên mỗi hàng lấy trung bình các value nó được thấy: hàng 0 chỉ thấy 3, hàng 1 lấy trung bình 3 và 6 (4.5), hàng 2 lấy trung bình cả ba (6).

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

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

Kết quả mong đợi:
std::vector<std::vector<double>> expected = {
    {1.0, 2.0},
    {2.0, 3.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[] {1.0, 2.0},
    new double[] {3.0, 4.0}
};
double[][] queries = new double[][] {
    new double[] {1.0, 0.0},
    new double[] {1.0, 0.0}
};

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

Giải thích

Hàng 0 chỉ thấy chính nó nên sao chép values[0] = [1, 2]. Hàng 1 cho hai key cùng điểm nên lấy trung bình: [2, 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.