Truy vấn, Khóa, Giá trị
Ba Phép Biến đổi Tuyến tính từ Cùng một Đầu vào
Sau khi nhúng (hoạt động 4), mỗi vị trí mang một vector 768 chiều x_t. Chú ý bắt đầu bằng cách tạo ra ba phép chiếu khác biệt của x:
Q (truy vấn): vị trí này muốn biết điều gì?
K (key): vị trí này cung cấp gì cho các vị trí khác?
V (value): vị trí này cung cấp nội dung gì nếu được chú ý đến?
Mỗi phép chiếu đều đến từ một ma trận trọng số đã học:
Q = x · W_Q # Hình dạng W_Q: (d_model, d_k)
K = x · W_K # Hình dạng W_K: (d_model, d_k)
V = x · W_V # Hình dạng W_V: (d_model, d_k)
Ba ma trận, tất cả được huấn luyện qua lan truyền ngược. Mô hình học: tại vị trí này, truy vấn nào tốt nhất để lấy lại ngữ cảnh quá khứ hữu ích? Khóa nào quảng bá nội dung của vị trí này tốt? Giá trị nào được cung cấp nếu được chọn?
Một Phép So Sánh Với Thư Viện
Hãy tưởng tượng một danh mục thẻ thư viện. Bạn bước vào với một chủ đề trong đầu ( query của bạn). Mỗi thẻ liệt kê các từ khóa (một key). Khi chủ đề của bạn khớp với từ khóa trên thẻ, bạn lấy nội dung sách (một value). Attention thực hiện điều này cho mọi token song song: mọi vị trí truy vấn mọi vị trí khác, xếp hạng sự phù hợp, & truy xuất một tổ hợp có trọng số của các vector giá trị.
Kích Thước ANDREA-120M
| Số lượng | Giá trị | Ghi chú |
|---|---|---|
| d_model | 768 | Kích thước vector tại mọi vị trí |
| n_head | 12 | Các đầu attention song song |
| d_k | 64 | Chiều mỗi đầu (= d_model / n_head) |
| T | 1024 | Độ dài ngữ cảnh |
d_k = d_model / n_head = 768 / 12 = 64. Mỗi đầu nhìn thấy một lát 64 chiều của không gian 768 chiều đầy đủ. Hoạt động 6 (grow_a_language_model_multi_head) bao quát việc chia theo mỗi đầu một cách chi tiết.
Tính d_k
Tại sao Chia cho sqrt(d_k)
Ma trận Điểm số
Khi Q & K đã tồn tại (mỗi shape (T, d_k)), attention tính toán một ma trận điểm số:
scores = Q · K^T # shape: (T, T)
scores[i, j] = mức độ tương đồng mạnh mẽ giữa query của vị trí i với key của vị trí j. Mỗi cặp (i, j) nhận một điểm số: 1024 × 1024 = 1.048.576 điểm số mỗi attention head mỗi lần forward pass.
Tại sao cần chia
Tích vô hướng của hai vector đơn vị ngẫu nhiên d-chiều có độ lớn theo thứ tự sqrt(d). Nếu không chia tỷ lệ, scores sẽ tăng theo d_k:
- d_k = 64: tích vô hướng điển hình ở mức khoảng 8.
- d_k = 256: tích vô hướng điển hình ở mức khoảng 16.
- d_k = 4096: tích vô hướng điển hình ở mức khoảng 64.
Các điểm số lớn tạo ra softmax nhọn (một vị trí thống trị, gradient biến mất ở nơi khác). Huấn luyện bị đình trệ. Scaling sửa chữa độ lớn:
scaled_scores = (Q · K^T) / sqrt(d_k)
Đối với ANDREA-120M, sqrt(d_k) = sqrt(64) = 8. Mọi điểm số đều được chia cho 8. Độ lớn vẫn giữ ở quy mô đơn vị bất kể d_k. Softmax vẫn hoạt động tốt. Gradient chảy tốt.
Lý Do Ban Đầu Của Vaswani
Từ Attention Is All You Need (2017): 'Với các giá trị lớn của d_k, tích vô hướng sẽ tăng lớn về độ lớn, đẩy hàm softmax vào vùng có gradient cực kỳ nhỏ.' Bộ chia sqrt(d_k) chống lại sự tăng trưởng đó.
Một Góc Nhìn Về Mã
Bên trong microgpt_cuda.cu, việc scaling này xuất hiện dưới dạng một phép chia trực tiếp:
scores[i][j] = dot(Q[i], K[j]) * (1.0f / sqrtf(d_k));
Một phép nhân float cho mỗi score. Rẻ. Quan trọng.
Mở rộng tại d_model = 4096
Tại sao Vị trí i Không Thể Thấy Vị trí j > i
Một Ràng Buộc Sinh Ra Từ Quá Trình Sinh
ANDREA tạo một token tại một thời điểm. Trong quá trình suy luận, vị trí 0 tạo token đầu tiên, sau đó vị trí 1 nhìn thấy đầu ra của vị trí 0 & tạo token thứ hai, & cứ tiếp tục như vậy. Một mô hình không bao giờ có quyền truy cập vào các token tương lai trong quá trình tạo.
Quá trình huấn luyện phải phản ánh điều này. Nếu trong quá trình huấn luyện vị trí 5 có thể chú ý đến vị trí 6, mô hình sẽ học được một lối tắt: 'dự đoán token 6 bằng cách đọc token 6'. Trong quá trình suy luận, lối tắt đó biến mất (token 6 chưa tồn tại). Hành vi huấn luyện-so-với-suy-luận của mô hình sẽ lệch hướng một cách thảm khốc.
Một Mask
Một causal mask chặn sự chú ý từ bất kỳ vị trí i nào đến bất kỳ vị trí j > i nào. Triển khai: đặt scaled_scores[i][j] = -infinity ở mọi nơi j > i. Sau softmax, các mục đó trở thành exp(-inf) = 0. Mask làm cho sự chú ý đến các vị trí tương lai bằng 0 một cách sạch sẽ.
for i in range(T):
for j in range(T):
if j > i:
scaled_scores[i][j] = -1e9 # hiệu quả là -inf
Sau softmax (theo hàng), mỗi hàng tổng bằng 1, nhưng chỉ các mục [0, i] mang khối lượng xác suất. Vị trí i chỉ trộn thông tin từ các vị trí trước đó.
Hình dung Mặt nạ
Ma trận điểm có hình dạng (T, T) với mặt nạ được áp dụng trông giống như một cấu trúc tam giác dưới:
scaled_scores sau khi áp dụng mặt nạ, softmax theo hàng:
hàng 0: [1.0, 0, 0, 0, ...] # chỉ nhìn thấy chính nó
hàng 1: [0.4, 0.6, 0, 0, ...] # nhìn thấy vị trí 0, 1
dòng 2: [0.2, 0.3, 0.5, 0, ...] # thấy 0, 1, 2
dòng 3: [0.1, 0.2, 0.3, 0.4, ...] # thấy 0, 1, 2, 3
...
Phân phối xác suất tam giác dưới nghiêm ngặt theo từng dòng. Tương lai vẫn vô hình.
Tại sao Transformer Chỉ Có Decoder Cần Điều Này
Các mô hình decoder-only như ANDREA, GPT, & LLaMA đều chia sẻ một mục tiêu: dự đoán token tiếp theo từ các token trước đó. Một causal mask làm cho mục tiêu đó có thể huấn luyện song song: mọi vị trí tính toán dự đoán next-token của riêng mình cùng lúc, & không vị trí nào gian lận bằng cách nhìn trước.
Mask & Flavor
Từ Scores đến Output
Softmax: Từ Điểm Số Đến Xác Suất
Các điểm số đã được che và chia tỷ lệ vẫn nằm trong khoảng số thực. Softmax chuyển mỗi hàng thành một phân phối xác suất:
A[i][j] = exp(scaled_scores[i][j]) / sum_k exp(scaled_scores[i][k])
Ba tính chất sau được tạo ra:
- A[i][j] >= 0 cho mọi (i, j).
- sum_j A[i][j] = 1 cho mọi hàng i.
- Điểm thô lớn hơn tạo ra xác suất lớn hơn (đơn điệu).
Vector xác suất của hàng i cho mô hình biết: vị trí i nên chú ý bao nhiêu đến mỗi vị trí trước đó khi tính toán đầu ra của nó?
Tổng V Có Trọng Số
Đầu ra attention cuối cùng cho vị trí i:
output[i] = sum_j A[i][j] · V[j]
Mỗi vector giá trị V[j] được nhân với trọng số bởi xác suất attention A[i][j], sau đó tổng hợp. Đầu ra của vị trí i kết hợp các vector giá trị từ mọi vị trí trước đó, được nhân với trọng số theo mức độ liên quan.
Dưới dạng ma trận, tất cả các vị trí cùng một lúc:
Attention(Q, K, V) = softmax(mask(Q · K^T / sqrt(d_k))) · V
Một dòng. Toàn bộ cơ chế attention. Vaswani et al. đã viết dòng đó vào năm 2017; transformers chưa thay đổi cơ bản kể từ đó.
Hình dạng Đầu ra Theo Đầu
Đầu ra của một đầu attention: hình dạng (T, d_k). Với ANDREA-120M: (1024, 64). Tất cả 12 đầu tính toán song song; đầu ra của chúng được nối lại thành (1024, 768) & đưa vào phép chiếu tuyến tính cuối cùng (W_O), sau đó đến MLP của khối transformer.
Hoạt động 6 (grow_a_language_model_multi_head) bao gồm việc chia multi-head. Hoạt động 7 (grow_a_language_model_transformer_block) bao gồm mọi thứ xung quanh attention: kết nối residual, layer norm, MLP.