W3 · Training / inference workflow
Bước inference: vòng sinh và bổ sung context
Vòng sinh tự hồi quy: đưa từng token vừa chọn vào mô hình, đọc lại KV cache lịch sử, thêm K, V mới rồi chọn token kế cho tới khi gặp điều kiện dừng.
1Định nghĩa
Decode là vòng lặp tự hồi quy diễn ra ngay sau khi có token trả lời đầu tiên từ bước prefill: ở mỗi vòng, mô hình chỉ nhận đúng một token vừa chọn ở bước trước làm đầu vào để chạy qua các lớp Transformer, rồi chọn token kế tiếp từ kết quả. Vòng này lặp cho tới khi gặp điều kiện dừng.
Nhờ đọc lại các vector Key/Value của prompt và các token đã sinh trong KV cache, mỗi lớp chỉ cần tính thêm một cặp Key/Value cho token hiện tại rồi nối vào cache. Bảng logits mới đi qua sampling để chọn token kế; suốt quá trình trọng số giữ nguyên cố định và chỉ có KV cache dài thêm sau mỗi bước.
Ví dụ đời thường: Decode giống người phiên dịch nói nối tiếp từng từ sau chữ đầu: cứ nói xong một từ lại liếc vào sổ ghi chú (KV cache) và từ vừa phát âm để chọn từ kế tiếp, nối dài câu tới khi diễn đạt trọn ý. Khác người phiên dịch, mỗi lần mô hình chỉ ‘nhìn’ một từ mới và sổ ghi chú; nó không được sửa những từ đã nói.
2Luồng
Đây là nửa sau của ô 5B. Vòng lặp đi qua đúng các phép tính của cột 2 với mỗi lần chỉ một token. Mũi tên quay vòng mang token vừa chọn; cache là trạng thái duy nhất lớn dần, còn trọng số giữ nguyên.
- Token mới nhất
Nhận token vừa chọn ở bước trước làm đầu vào duy nhất của bước này.
- Forward với context
Mỗi lớp tính Q, K, V của token này và đọc cache của các vị trí trước.
- Bổ sung cache
K, V của token được nối vào cache; cache dài thêm một vị trí.
- Chọn tiếp hoặc dừng
Chọn token từ logits, kiểm tra điều kiện dừng; chưa dừng thì quay lại vòng này.
Mã Mermaid
flowchart TD
step1["Token mới nhất"]
step2["Forward với context"]
step3["Bổ sung cache"]
step4["Chọn tiếp hoặc dừng"]
step1 --> step2
step2 --> step3
step3 --> step4 3Giải pháp
Vị trí trên sơ đồ: nửa sau của ô 5B "Prefill & decode" trong băng "Checkpoint → Inference", ngay sau prefill và trước ô 5C (kết thúc và trả văn bản). Mỗi vòng lặp chạy lại cột 2 của sơ đồ nhưng với một token, và cuối vòng chọn token thay vì tính loss.
Vấn đề cần giải. Mô hình sinh chữ bằng cách chọn từng token một, mỗi token phụ thuộc vào tất cả token trước. Nếu mỗi bước tính lại cả chuỗi từ đầu, công việc tăng rất nhanh theo độ dài. Cần cách chỉ tính phần mới mà vẫn có kết quả giống hệt.
Khái niệm tối thiểu. Tự hồi quy: token vừa sinh trở thành đầu vào. KV cache lưu K, V của mọi vị trí đã xử lý. Ở mỗi bước chỉ có một vị trí mới, nên attention có một Query mới so với toàn bộ Key trong cache (cũ cộng mới).
Ví dụ có số (minh họa, tiếp nối bài prefill: cache đang có 2 vị trí, token đầu là “cá”, ID 4). Bước 1: đưa [4] ở vị trí 2 vào mô hình; mỗi lớp tính Q, K, V của riêng token này, đọc K, V của vị trí 0 và 1 từ cache, nối K, V mới vào, vậy cache dài 3 vị trí (192 byte với mô hình nhỏ trước đó). Logits thu được; giả sử điểm cao nhất rơi vào eos (ID 6). Sampling chọn eos, điều kiện dừng thỏa mãn và vòng kết thúc. Câu trả lời là [4, 6], tức “cá” cộng token kết thúc.
Tại sao nhanh hơn tính lại. Giả sử prompt N = 2 và cần 4 token mới. Có cache: prefill xử lý 2 token, rồi 3 bước decode xử lý mỗi bước 1 token (token thứ 4 không cần đưa vào nếu dừng ở đó): tổng 2 + 3 = 5 lượt xử lý token. Không cache: bước sinh token thứ k phải xử lý lại N + k − 1 token, tức 2 + 3 + 4 + 5 = 14 lượt. Con số 14 so với 5 là số lượt xử lý token theo mô hình đếm đơn giản này, không phải tỉ lệ thời gian đo được.
Kiểm tra tính đúng. So logits của bước có cache với logits khi chạy lại cả chuỗi [1, 3, 4] không cache bằng cùng trọng số: chúng phải trùng trong sai số số học. Nếu trùng, ta kết luận việc tái dùng K, V cũ là đúng. Điều không thể kết luận: trùng khớp không chứng minh mô hình hiểu ngôn ngữ, cũng không chứng minh cache nhanh hơn; tốc độ phải đo riêng.
Cách đọc chi phí. Ở mỗi bước, mô hình đọc toàn bộ trọng số (phần lớn) và toàn bộ cache chỉ để xử lý một token, nên khi nhóm yêu cầu nhỏ, thời gian thường bị chi phối bởi đọc bộ nhớ chứ không phải số phép tính. Vì cache lớn dần, token ở cuối câu trả lời dài thường tốn hơn token ở đầu.
Thực thi và cầu nối sang Inference Engineering. Gộp nhiều chuỗi vào cùng một bước (batching), dùng kernel phù hợp và giảm dữ liệu đọc bằng kiểu số ít bit là các hướng cần thử. Speculative decoding cho phép xác minh nhiều token đề xuất trong một lượt chạy của mô hình chính nhưng thêm chi phí và không bảo đảm nhanh hơn với mọi tải. Xem sinh câu trả lời theo vòng lặp token, đề xuất token rồi xác minh, bộ nhớ đệm key và value, lập lịch theo từng bước và chọn token từ điểm số.
Để chạy từng bước decode với cache từ prefill và đối chiếu logits với tính lại toàn bộ, mở giai đoạn 13 · Từ checkpoint sang suy luận của Bài Lab tổng hợp. Hãy đoán trước kích thước cache sau mỗi bước.
4Profiling
- Thời gian mỗi token đầu ra
Kiểm tra tính đúng trước (logits có cache trùng không cache). Sau đó đo khoảng giữa các token sau token đầu, phân loại theo độ dài context và số yêu cầu đồng thời. Ghi p50 và p95; một số trung bình đẹp có thể che mất những token chậm.
- Thời gian đọc bộ nhớ
Dùng timeline và bộ đếm phần cứng để xem kernel có chờ dữ liệu từ bộ nhớ hay chờ lệnh từ CPU không. Chỉ khi biết điểm nghẽn mới chọn hướng tối ưu (nhóm lớn hơn, kiểu số ít bit hơn, gộp kernel).
- Tăng trưởng của cache
Ghi kích thước cache theo từng bước và so với công thức 2 × số lớp × n_kv × số vị trí × d_head × byte. Theo dõi chi phí đọc cache ở đầu và cuối một câu trả lời dài để thấy ảnh hưởng của độ dài context.
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ụ
Chatbot trả lời ngắn
Ưu tiên token đến đều và nhanh; không tăng nhóm xử lý quá mức chỉ để đạt tổng token mỗi giây cao. Dự đoán: tăng nhóm gấp đôi thì thời gian mỗi token của từng người dùng thay đổi thế nào? Có thể tăng, vì mỗi bước phải xử lý nhiều chuỗi hơn; tổng thông lượng có thể tăng nhưng người dùng cảm nhận độ trễ.
Sinh báo cáo dài
Cache tiếp tục lớn theo context. Đo thời gian của các token ở đầu và cuối response để thấy chi phí tăng khi context tăng; đừng suy luận từ một độ dài ngắn. Với mô hình nhỏ trong ví dụ, mỗi token thêm 64 byte, nên 1.000 token thêm 64.000 byte; với mô hình thật con số lớn hơn nhiều và phải tính bằng công thức với cấu hình thật.
Tự kiểm tra: tại sao cần cache
Một chuỗi hiện có 6 token và cần chọn token thứ 7; token thứ 6 vừa được chọn, 5 token trước đã có K, V trong cache. Không cache thì phải xử lý lại bao nhiêu token? Cả 6 token. Có cache thì chỉ xử lý 1 token (token thứ 6) nhưng vẫn phải đọc K, V của 5 vị trí trước.
6Thuật ngữ
- Autoregressive generation
- Sinh tự hồi quy: token vừa tạo trở thành đầu vào của bước kế tiếp.
- Decode step
- Một vòng của pha decode: forward một token, thêm K, V vào cache, lấy logits, chọn token.
- Inter-token latency (ITL)
- Khoảng thời gian người dùng chờ giữa hai token liên tiếp sau token đầu.
- Điều kiện dừng
- Token eos, chạm max_tokens, khớp chuỗi stop hoặc bị hủy; sau khi gặp, vòng kết thúc.
- Speculative decoding
- Một mô hình nhỏ đề xuất nhiều token, mô hình chính xác minh trong một lượt; có thêm chi phí và không nhanh hơn trong mọi trường hợp.
Glossary (6)
- Model weights
- Các số học được; trong decode không đổi.
- Cache
- Bộ nhớ K, V của các token đã xử lý; lớn dần theo mỗi token.
- Logits
- Điểm số cho từng token ở vị trí hiện tại; sampling chọn token từ đó.
- Kernel
- Hàm tính toán chạy trên GPU; với một token, nhiều kernel nhỏ nên dễ bị chờ bộ nhớ hoặc chờ lệnh.
- Host
- Phần chương trình điều khiển thiết bị, chạy trên CPU; nếu phát lệnh chậm thì GPU nhàn rỗi.
- Context
- Prompt cộng các token đã sinh; token mới đọc toàn bộ phần này qua cache.
