ยาก 0 ข้อที่แก้แล้ว อัตราผ่าน 0%

Multi-head self-attention

ฉายอินพุตเป็น query, key และ value รัน attention ในหลาย head พร้อมกัน แล้วรวมผลเข้าด้วยกัน

สิ่งที่คุณจะได้เรียนรู้

  • ฉายอินพุตเป็น query, key และ value
  • รัน attention ในหลาย head
  • ต่อ head เข้าด้วยกันแล้วฉายผลลัพธ์

โจทย์

Multi-head attention ทำให้ Transformer สนใจหลายสิ่งได้พร้อมกัน เมื่อมีอินพุต X (n token × d คอลัมน์) และเมทริกซ์น้ำหนักขนาด d × d:

  1. Q = X × w_q, K = X × w_k, V = X × w_v
  2. แบ่ง d คอลัมน์เป็น num_heads head head ละ d_k = d / num_heads คอลัมน์ติดกัน (head h ใช้คอลัมน์ h×d_k ถึง (h+1)×d_k - 1)
  3. สำหรับแต่ละ head ให้รัน scaled dot-product attention บนคอลัมน์ของมัน: สำหรับ token i, score_j = (Q_i · K_j) / √d_k บนคอลัมน์ของ head นั้น, softmax บนทุก token j ทั้ง n ตัว และเอาต์พุต = Σ weight_j × V_j (คอลัมน์ของ head นั้น)
  4. นำเอาต์พุตของแต่ละ head กลับไปใส่ในคอลัมน์ของมัน (concatenation) แล้วคูณด้วย w_o

จง implement 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: จำนวน head (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 และแต่ละ token เฉลี่ยแถว value: 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}
};

คำอธิบาย

เมื่อมี token เดียว แต่ละ head สนใจแค่ตัวเอง ดังนั้นเอาต์พุตที่ต่อกันคือ V = [2, 4] แล้ว w_o สลับสองคอลัมน์: [4, 2]

แหล่งอ้างอิง

โจทย์นี้ปรับให้เหมาะกับ CodeUtility กรุณาทำตามคำชี้แจงและข้อจำกัดด้านบน

เรียนรู้จากคนอื่น

แนวทางจากชุมชน

ยังไม่มีวิธีทำที่เผยแพร่ ลองแก้โจทย์นี้เพื่อแชร์วิธีแรก