โจทย์
Multi-head attention ทำให้ Transformer สนใจหลายสิ่งได้พร้อมกัน เมื่อมีอินพุต X (n token × d คอลัมน์) และเมทริกซ์น้ำหนักขนาด d × d:
- Q = X × w_q, K = X × w_k, V = X × w_v
- แบ่ง d คอลัมน์เป็น
num_headshead head ละ d_k = d / num_heads คอลัมน์ติดกัน (head h ใช้คอลัมน์ h×d_k ถึง (h+1)×d_k - 1) - สำหรับแต่ละ 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 นั้น)
- นำเอาต์พุตของแต่ละ 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
ตัวอย่าง
อินพุต:
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]
อินพุต:
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 กรุณาทำตามคำชี้แจงและข้อจำกัดด้านบน