W3 · Training / inference workflow
Query, key và value kết hợp context như thế nào
Một lớp Self-Attention chiếu vector ẩn thành Query, Key, Value, gắn vị trí, che tương lai bằng mặt nạ nhân quả rồi trộn Value theo trọng số chú ý.
1Định nghĩa
Self-Attention là khối bên trong mỗi lớp Transformer cho phép mỗi vị trí token lấy thông tin có chọn lọc từ các vị trí đứng trước nó. Với vector ẩn đã được chuẩn hóa, khối này tạo ba vector từ mỗi token: Query (câu hỏi của vị trí hiện tại), Key (nhãn tra cứu của từng vị trí) và Value (nội dung sẽ lấy ra). Key và Value thường được chia sẻ theo nhóm trong GQA.
Tiếp theo, mô hình xoay Query và Key theo mã vị trí (RoPE), nhân vô hướng Query với Key để ra điểm tương quan, dùng mặt nạ nhân quả gán −∞ cho các vị trí tương lai rồi lấy softmax để có trọng số chú ý. Cuối cùng trọng số nhân với Value và chiếu qua ma trận đầu ra thành vector ngữ cảnh; trong suy luận, các cặp Key/Value này là dữ liệu được lưu lại trong KV cache.
Ví dụ đời thường: Một bước attention giống như lúc bạn tìm sách trong thư viện: cầm tờ phiếu ghi chủ đề cần tìm (Query), đi dọc kệ so với nhãn dán trên gáy từng cuốn (Key) để chấm điểm cuốn nào hợp nhất, rồi mở các cuốn điểm cao nhất ra chép nội dung bên trong (Value) về tổng hợp. Khác thư viện, ở đây thủ thư không cho bạn nhìn các kệ đặt phía sau vị trí của bạn, đó là mặt nạ nhân quả.
2Luồng
Đây là ô 2A trên sơ đồ. Mũi tên vào mang bảng vector (T × d) đã chuẩn hóa; mũi tên ra mang bảng cùng shape (T × d) đã được trộn thông tin giữa các vị trí, rồi sang MLP ở ô 2B. Trong suy luận, cặp K, V của các token cũ được lưu lại làm KV cache.
- Vector ẩn đã chuẩn hóa
Mỗi hàng của X là vector d của một vị trí, lấy từ embedding hoặc lớp trước sau bước norm.
- Chiếu thành Q, K, V và gắn vị trí
Nhân X với ba ma trận để có Q, K, V; áp RoPE lên Q và K nếu mô hình dùng.
- Điểm số, mask và softmax
Tính Q nhân K chuyển vị chia căn d_head, che vị trí tương lai, softmax từng hàng.
- Trộn Value và chiếu ra
Nhân trọng số với V, ghép các head, chiếu bằng ma trận đầu ra rồi cộng residual.
Mã Mermaid
flowchart TD
step1["Vector ẩn đã chuẩn hóa"]
step2["Chiếu thành Q, K, V và gắn vị trí"]
step3["Điểm số, mask và softmax"]
step4["Trộn Value và chiếu ra"]
step1 --> step2
step2 --> step3
step3 --> step4 3Giải pháp
Vị trí trên sơ đồ: ô 2A "Embedding + Attention" trong cột 2 · Forward qua Transformer. Nó nhận vector của các vị trí từ bảng embedding (hoặc từ lớp trước) sau bước chuẩn hóa ở ô 2C, và đưa kết quả sang MLP ở ô 2B qua đường cộng residual. Tổng quan cột nằm ở bài forward.
Vấn đề cần giải. Vector của một token ngay sau embedding chỉ biết mỗi mình nó: chữ “cá” trong “mèo ăn cá” và trong “mua cá” có vector giống hệt nhau. Để dự đoán chữ kế tiếp, mỗi vị trí phải nhìn được những vị trí đứng trước và lấy đúng những gì liên quan. Attention là cơ chế làm việc đó.
Khái niệm tối thiểu và cách đọc trục. Bảng X có T hàng (mỗi hàng là một vị trí) và d cột (d số mô tả vị trí đó). Ba ma trận trọng số W_Q, W_K, W_V biến X thành Q, K, V; với một head, mỗi bảng có T hàng và d_head cột. Bảng điểm S = Q nhân K chuyển vị có T hàng, T cột: hàng i là câu hỏi của vị trí i, cột j là vị trí j bị hỏi, ô (i, j) là mức hợp nhau. Softmax chạy trên từng hàng của S.
Từng thao tác và lý do. (1) Chiếu Q, K, V: dùng ba ma trận khác nhau để 'câu hỏi', 'nhãn' và 'nội dung' có thể khác nhau. (2) Gắn vị trí bằng RoPE: nếu không có, attention coi “mèo ăn” và “ăn mèo” như nhau vì chỉ so khớp nội dung. (3) Chia cho căn bậc hai của d_head: tích vô hướng càng nhiều chiều càng lớn, chia lại giữ cho softmax khỏi bão hòa (một ô chiếm gần hết trọng số). (4) Che tương lai: ô (i, j) với j > i đặt −∞ để sau softmax bằng 0. (5) Softmax từng hàng thành trọng số cộng lại bằng 1. (6) Nhân trọng số với V để ra vector ngữ cảnh, ghép các head rồi nhân với ma trận đầu ra W_O.
Ví dụ có số (minh họa, bỏ RoPE cho gọn). Ba vị trí “mèo, ăn, cá”, d_head = 2. Q = K = [[1, 0], [0, 1], [1, 1]] và V = [[1, 0], [0, 2], [3, 1]], mỗi hàng ứng với một vị trí. Hàng 0 (mèo) chỉ nhìn được chính nó: điểm [0,707], trọng số [1], đầu ra bằng V0 = [1, 0]. Hàng 1 (ăn): điểm với K0, K1 là [0, 0,707], trọng số [0,330; 0,670], đầu ra = 0,330 × [1, 0] + 0,670 × [0, 2] = [0,330; 1,340]. Hàng 2 (cá): q = [1, 1], điểm với K0, K1, K2 là [1, 1, 2] chia 1,414 = [0,707; 0,707; 1,414], trọng số [0,248; 0,248; 0,504], đầu ra = [1,759; 1,000].
Cách đọc. Hàng 2 dành 50,4% chú ý cho chính vị trí 2 (vì điểm cao nhất) và mỗi vị trí còn lại 24,8%; đầu ra là trung bình có trọng số của các Value nên nằm 'giữa' V0, V1, V2 và nghiêng về V2. Nếu bỏ mặt nạ, hàng 0 sẽ nhìn cả ba vị trí với trọng số xấp xỉ [0,401; 0,198; 0,401] và 'thấy' tương lai, đó chính là lỗi rò rỉ mà ô 1B cùng mặt nạ nhân quả ngăn chặn. Trong ma trận trọng số 3 × 3, nửa trên bên phải đường chéo phải đúng bằng 0.
Điều có thể và không thể kết luận. Bạn có thể khẳng định: trọng số mỗi hàng không âm, cộng lại bằng 1, và các vị trí bị che có trọng số 0. Bạn không thể khẳng định: bản đồ nhiệt (heatmap) của trọng số giải thích vì sao mô hình trả lời như vậy, vì còn nhiều lớp, nhiều head và các khối khác cùng ảnh hưởng đầu ra. Cũng đừng nhầm trọng số attention (chia sẻ chú ý giữa các vị trí) với xác suất của token trong bảng từ vựng (chỉ có sau LM head và softmax).
Thực thi. Attention đầy đủ tạo ma trận điểm T × T cho mỗi head; với T = 4.096 là hơn 16,7 triệu phần tử cho mỗi head ở mỗi lớp. FlashAttention chia ma trận thành các ô và tính softmax tăng dần để không phải ghi cả ma trận ra bộ nhớ lớn, vẫn cho kết quả attention tương đương (xem kernel). Trong suy luận, Key và Value của token cũ được giữ trong KV cache nên mỗi token mới chỉ tính Query, Key, Value của riêng nó. Trong huấn luyện, backward cần các giá trị này (hoặc phải tính lại), xem gradient qua một lớp.
Để tự quan sát attention weights thật của một forward và kiểm tra các vị trí bị che bằng 0, mở giai đoạn 3 · Forward (khối attention) của Bài Lab tổng hợp. Hãy tự đoán phần nào của ma trận trọng số phải bằng 0 trước khi nhìn kết quả.
4Profiling
- Tính đúng và rò rỉ nhân quả
So đầu ra với một cài đặt tham chiếu đơn giản trên cùng Q, K, V. Sau đó thử đổi một token ở vị trí tương lai và kiểm tra logits của các vị trí đứng trước không đổi trong sai số cho phép. Cả hai phép kiểm tra chỉ ra phép tính đúng, không đảm bảo mô hình học tốt.
- Bộ nhớ và thời gian theo độ dài
Đo thời gian và bộ nhớ khi tăng T với số head và d_head cố định; ma trận điểm tăng theo T bình phương nếu không dùng kernel tiết kiệm đọc ghi. Phân biệt attention trên cả prompt (T hàng truy vấn) với attention khi decode có cache (một hàng truy vấn so với T khóa), vì hai trường hợp bị giới hạn bởi những thứ khác nhau.
- Đọc trọng số một cách cẩn trọng
Nếu in trọng số, kiểm tra tổng mỗi hàng bằng 1 và phần bị che bằng 0 trước khi diễn giải. Ghi rõ lớp và head đang xem; một head của một lớp không nói lên toàn bộ mô hình. Không dùng một heatmap để khẳng định lý do của câu trả lời.
Mẹo đo lường: ghim số liệu trên workload thật, ghi cả p50/p95/p99 thay vì chỉ trung bình, và luôn ghi lại điều kiện chạy (batch, cache, đồng thời, phần cứng) trước và sau khi tối ưu. Số đo thiếu điều kiện chạy thì không so sánh được.
5Ví dụ
“Mèo ăn cá” — vị trí cuối đọc được gì
Ở bước dự đoán token sau “cá”, vị trí cuối phát câu hỏi và so với nhãn của cả “mèo”, “ăn”, “cá”. Trọng số thật phải lấy từ mô hình; ví dụ [0,248; 0,248; 0,504] ở trên chỉ là số minh họa. Tự kiểm tra: nếu tăng gấp đôi Value của vị trí 2 thì đầu ra hàng 2 thay đổi thế nào? Đáp án: trọng số attention không đổi (chúng chỉ phụ thuộc Q và K), còn phần đóng góp của V2 là 0,504 × [3, 1] = [1,51; 0,50] gấp đôi thành [3,02; 1,01]; các phần đóng góp từ V0 và V1 giữ nguyên.
Decode có cache
Khi sinh token thứ tư, chỉ có một Query mới; nó được so với bốn Key (ba cũ trong cache cộng một mới) và trộn bốn Value. Nhờ cache, Key và Value của prefix không phải tính lại, nhưng mỗi bước vẫn phải đọc toàn bộ cache. Đo thời gian token ở đầu và cuối một câu trả lời dài để thấy chi phí tăng theo độ dài context.
Tự kiểm tra: vì sao chia cho căn d_head
Giả sử d_head tăng từ 2 lên 128 trong khi các thành phần của Q và K giữ cỡ 1. Tích vô hướng cộng nhiều số hơn nên độ lớn điển hình tăng theo d_head; softmax của những điểm rất lớn gần như chọn đúng một ô và gradient tại đó rất nhỏ. Chia cho căn d_head kéo điểm về cỡ ổn định. Đây là lý do thiết kế, không phải kết quả đo trong bài này.
6Thuật ngữ
- Query, Key, Value (Q, K, V)
- Ba vector tạo ra từ cùng một token bằng ba ma trận khác nhau: Query là 'câu hỏi' của vị trí hiện tại, Key là 'nhãn tra cứu' của mỗi vị trí, Value là nội dung được lấy ra.
- Attention scores và weights
- Scores là tích vô hướng Q·K (đã chia căn bậc hai của d_head); weights là kết quả softmax của scores trên mỗi hàng, không âm và cộng lại bằng 1.
- Rotary Position Embedding (RoPE)
- Cách đưa thông tin vị trí vào bằng phép quay áp lên Q và K, để điểm số phụ thuộc vào khoảng cách tương đối giữa hai vị trí.
- Causal mask
- Gán −∞ cho điểm của các vị trí tương lai trước softmax để trọng số của chúng đúng bằng 0.
- Grouped-Query Attention (GQA)
- Nhiều Query head dùng chung một nhóm Key/Value head để giảm bộ nhớ; mô hình phải được thiết kế và huấn luyện đúng như vậy.
- Head
- Một bộ Q, K, V độc lập. Nhiều head chạy song song rồi ghép kết quả, mỗi head có thể chú ý theo kiểu khác nhau.
Glossary (6)
- Vector và ma trận
- Vector là hàng gồm vài số; ma trận là bảng số. Ở bài này mỗi hàng của ma trận X ứng với một vị trí token.
- Softmax
- Hàm biến một hàng số thành các số dương cộng lại bằng 1, điểm cao hơn nhận tỉ trọng lớn hơn.
- Mask
- Quy tắc vị trí nào được nhìn nhau; ở bài này là mask nhân quả.
- Cache
- Bộ nhớ giữ kết quả để dùng lại; trong attention thường là cặp Key và Value của các token đã xử lý.
- Context
- Các token đứng trước vị trí hiện tại mà vị trí đó được phép đọc.
- Forward pass
- Lượt tính từ đầu vào đến đầu ra theo trọng số hiện tại; attention là một phần của nó.
