Chuyển đến nội dung chính
Tối ưu suy luận · Cập nhật 1 tháng 11, 2026

5.2 Mô hình Draft và các phương án không dùng mô hình

So sánh mô hình nhỏ độc lập, N-gram Prompt Lookup và Suffix Decoding; hiểu tương thích Tokenizer, khớp lịch sử và độ dài động; thống nhất cách đếm tỉ lệ chấp nhận, độ dài chấp nhận trung bình và độ phủ ứng viên

Draft Model N-gram Prompt Lookup Suffix Decoding Acceptance Rate

Mục trước đã chứng minh: miễn nghiệm thu và sửa đúng, Draft đoán kém cũng không làm lệch đầu ra. Nhưng về hiệu năng, bản nháp viết chậm mà lại hay bị phủ quyết thì suy đoán thành công cốc. Câu hỏi của mục này: làm sao để chi phí “đoán một đoạn” thấp hơn thời gian tiết kiệm được nhờ “ít gọi mô hình lớn vài lần”?

Câu trả lời không nhất thiết là tải thêm một mạng neural. Đầu ra mô hình thường copy prompt, code lặp lại cùng định danh, agent đi lại các bước tương tự — chính lịch sử đó đã là nguồn bản nháp sẵn. Mục này đặt mô hình nhỏ độc lập, N-gram và Suffix Decoding lên cùng một bảng, rồi thống nhất cách đếm tỉ lệ chấp nhận; 5.4 và 5.5 dùng lại các định nghĩa đó.

⏰ Độ mới của thông tin: hành vi framework và giá trị mặc định tham số dựa trên mã nguồn và tài liệu vLLM v0.32.0 (phát hành 2026-10-18).

📑 Mục lục


1. Tiêu chuẩn Draft tốt: nhanh, đúng, nối được

Thành phần đề xuất ứng viên gọi là Proposer. Một Proposer dùng được cần đồng thời ba điểm:

  1. Rẻ: thời gian, VRAM và chi phí giao tiếp để sinh ứng viên phải nhỏ.
  2. Đúng: trên traffic thật, ứng viên thường khớp lựa chọn của Target.
  3. Nối được: token ID, ngữ cảnh, cache và quy tắc lấy mẫu gắn được vào inference engine.

Giống tuyển trợ lý: lương thấp chỉ là một mặt; còn phải xem bản thảo tổng biên sửa có mệt không, và file nộp có mở được trong phần mềm của tổng biên không.

📊 HướngỨng viên lấy từ đâuTrọng số thêmGiỏi ở cảnh
Mô hình Draft độc lậpDự đoán có điều kiện của mô hình nhỏCóĐề xuất nội dung mới chưa xuất hiện trong lịch sử
N-gram / Prompt LookupĐoạn token trùng trong lịch sửKhôngCopy, trích xuất, viết lại
Suffix DecodingKhớp cây hậu tố + tần suất tiếp tụcKhôngQuỹ đạo lặp, sửa code, request tương tự

📌 Điểm then chốt: “không dùng mô hình” chỉ nghĩa không có trọng số neural thêm. Tra cứu, bảo trì chỉ mục, truyền dữ liệu CPU↔GPU và verify của Target đều có chi phí riêng.


2. Mô hình nhỏ độc lập: không phải càng nhỏ càng tốt

2.1 Vì sao mô hình nhỏ vẫn đủ dùng

Trong một đoạn văn, không phải vị trí nào cũng khó như nhau. Dấu câu, liên từ thường gặp, cấu trúc ngữ pháp, tên biến đã xuất hiện ở trên — mô hình nhỏ cũng đoán được tám chín phần mười. Giao các “vị trí dễ” cho mô hình nhỏ; Target chỉ cần nghiệm thu.

Số tham số chỉ là chỉ số chi phí thô. Độ trễ thật của Draft còn phụ thuộc số tầng, cấu trúc Attention, độ dài KV, triển khai kernel và kích thước batch. Một mô hình 0.5B rất sâu khi sinh từng token chưa chắc nhanh hơn mô hình 1B nông hơn.

2.2 Khớp với Target quan trọng hơn điểm cao của riêng nó

Khi chọn Draft, cần nhìn nó khớp Target bao nhiêu trên nghiệp vụ mục tiêu:

  • Target giỏi code còn Draft hầu như chưa gặp code: phần cú pháp đoán trúng, lựa chọn thuật toán lại bị từ chối liên tục.
  • Dữ liệu huấn luyện, cách căn chỉnh hoặc phân phối ngôn ngữ khác nhau cũng làm thói quen tiếp tục lệch. Mô hình nhỏ cùng họ, cùng bộ dữ liệu thường là lựa chọn đầu; ví dụ tài liệu vLLM là Qwen/Qwen3-8B kèm Qwen/Qwen3-0.6B.
  • Draft lớn hơn thường chấp nhận nhiều hơn, nhưng thời gian Draft thêm có thể nuốt mất lợi ích.

Vậy căn cứ chọn là số đo thực tế “(thời gian Draft + thời gian verify) / số token đã commit”; điểm bảng xếp hạng chỉ để tham khảo.

2.3 Parallel Draft: một lần forward ra nhiều ứng viên

Draft truyền thống phải chạy γ\gamma lần forward tuần tự; bản nháp càng dài càng chậm. PARD (Parallel Draft Model) đổi cách huấn luyện để Draft một lần forward dự đoán nhiều vị trí; thời gian Draft gần như không còn tăng tuyến tính theo γ\gamma. vLLM v0.32.0 bật bằng "parallel_drafting": true; ví dụ chính thức gắn amd/PARD-Qwen3-0.6B với 12 ứng viên. Kiểu này cần trọng số Draft huấn luyện riêng; mô hình nhỏ thường mở công tắc này không thành Parallel Draft.

2.4 Mô hình thêm sẽ chiếm dung lượng phục vụ

Draft độc lập ngoài trọng số còn có KV Cache, activation và workspace riêng. Tổng VRAM không đổi thì chỗ cho Target KV giảm, số request phục vụ đồng thời cũng giảm theo.

Một request nhanh hơn trong khi đồng thời tối đa nhỏ hơn có thể xảy ra cùng lúc. Thực chiến mục 5.5 sẽ đo riêng độ trễ concurrent thấp và dung lượng dịch vụ.


3. Tokenizer và ngữ cảnh: hai mô hình phải nói cùng một ngôn ngữ

3.1 Văn bản giống nhau, token ID chưa chắc giống

p(x)p(x) và q(x)q(x) ở mục 5.1 mặc định định nghĩa trên cùng không gian token. Hai Tokenizer đều mã hóa được “hello” không có nghĩa chúng cắt giống nhau, càng không có nghĩa mỗi số nguyên mang cùng nghĩa.

Cần đối chiếu ánh xạ token → ID của từ vựng, token đặc biệt (BOS/EOS/đánh dấu chat template), và chat template Target thực sự dùng. vocab_size giống nhau chỉ nói số lượng giống nhau.

Đoạn kiểm dưới cần transformers; chỉ tải file Tokenizer của hai mô hình, không tải trọng số, không chạy inference (lần đầu cần mạng):

from transformers import AutoTokenizer

target = AutoTokenizer.from_pretrained("Qwen/Qwen3-8B")
draft = AutoTokenizer.from_pretrained("Qwen/Qwen3-0.6B")

t_vocab, d_vocab = target.get_vocab(), draft.get_vocab()
diff = {tok for tok in t_vocab.keys() | d_vocab.keys()
        if t_vocab.get(tok) != d_vocab.get(tok)}
print("Số token ánh xạ không khớp:", len(diff))
print("EOS:", target.eos_token_id, draft.eos_token_id)

messages = [{"role": "user", "content": "你好"}]
same_template = target.apply_chat_template(messages, tokenize=True) == \
    draft.apply_chat_template(messages, tokenize=True)
print("Mã hóa template cùng đoạn hội thoại có khớp:", same_template)

Khi inference, Draft đọc lịch sử đã mã hóa của Target; nó không tự áp template lần nữa. Vì vậy lệch template ảnh hưởng Draft đã “thấy” định dạng đó chưa, từ đó ảnh hưởng tỉ lệ chấp nhận.

3.2 Từ vựng khác nhau: một lộ trình kỹ thuật riêng

vLLM mặc định yêu cầu hai bên dùng chung từ vựng. v0.32.0 có use_heterogeneous_vocab: true, dùng thuật toán Token-Level Intersection (TLI) hỗ trợ Draft khác họ, khác Tokenizer — nhưng hiện chỉ dùng được với phương pháp draft_model, và chỉ với mặc định draft_sample_method="greedy".

3.3 Ngữ cảnh của Draft không được âm thầm ngắn lại

Draft phải xử lý được tổng độ dài “lịch sử thực + ứng viên”. Khi max context, cấu hình RoPE và khả năng văn bản dài không khớp, request dài có thể fail thẳng, hoặc tỉ lệ chấp nhận trên văn bản dài tụt rõ. Trước khi deploy nên căn max_model_len của Draft với Target, hoặc ghi rõ thiết kế Draft chỉ nhìn một phần lịch sử.


4. N-gram: trong lịch sử tìm đoạn tiếp theo đã viết

4.1 Khớp đuôi trước, rồi chép phần sau

Giả sử lịch sử hiện kết thúc bằng [A, B, C], và vị trí sớm hơn từng xuất hiện [A, B, C, D, E, F]. Bộ tìm thấy đuôi khớp thì đưa [D, E, F] làm ứng viên cho Target.

Hình dưới đi một vòng tìm với n=3n=3, k=3k=3 (ô xanh là 3-gram trùng đuôi; ô vàng sọc nghiêng là ứng viên chép ra):

N-gram lookup: lấy A B C ở đuôi lịch sử, tìm cùng A B C sớm hơn, chép D E F theo sau làm ứng viên cho Target; không tìm thấy thì vòng này không suy đoán

Khớp diễn ra trên chuỗi token ID, không liên quan phân từ tiếng Trung hay số ký tự. Ở đây có hai tham số độc lập:

  • Cửa sổ tìm nn: trong vLLM là prompt_lookup_min và prompt_lookup_max. Không đặt cả hai thì mặc định 5; chỉ đặt một thì cái kia lấy cùng giá trị.
  • Độ dài đề xuất: num_speculative_tokens, tối đa chép bao nhiêu token tiếp theo.

Triển khai CPU của vLLM v0.32.0 tìm trong toàn bộ lịch sử request (prompt cộng nội dung đã sinh), ưu tiên khớp dài nhất trong [min,max][\text{min},\text{max}], cùng độ dài thì lấy vị trí xuất hiện sớm nhất. Còn phương pháp ngram_gpu đưa việc tìm lên GPU, V1 và V2 Model Runner đều dùng được; dưới V2 Model Runner, ngram cũng đi đường GPU, và v0.32.0 còn cho nó hỗ trợ bản nháp độ dài biến thiên.

4.2 Đánh đổi cửa sổ dài ngắn

  • Cửa sổ dài: ràng buộc mạnh, ứng viên dễ liên quan hơn, nhưng ít hit hơn.
  • Cửa sổ ngắn: dễ hit, nhưng có thể chỉ khớp một dấu phẩy cộng “的”, phần tiếp theo phân kỳ lớn.
  • Đề xuất dài: một lần chép nhiều, có thể tiết kiệm thêm lời gọi Target — hoặc vài cái đầu bị từ chối thì phần sau tính phí.

Prompt Lookup hợp nhất các bài “đầu ra tái dùng nhiều đầu vào”: tóm tắt trích nguyên văn, code chỉ sửa vài dòng, trích trường theo template.

4.3 Không có xác suất thì verify thế nào

N-gram cho ra token xác định. Theo phân tích mục 5.1 §3.3, coi đó là qq one-hot; quy tắc nghiệm thu suy về “chấp nhận với xác suất p(y)p(y)”, phần dư là bỏ yy rồi chuẩn hóa lại theo pp. Kết luận không đổi: phân phối đầu ra vẫn là của Target.


5. Suffix Decoding: từ một lần khớp đến thống kê lịch sử

Ý N-gram là “trước đã viết cùng đuôi, phía sau có lẽ còn chép được”, mỗi lần chỉ nhìn một chỗ khớp. SuffixDecoding đi xa hơn: dùng cây hậu tố tổ chức lịch sử, ghi mỗi cách tiếp tục xuất hiện bao nhiêu lần, rồi chọn đường tự tin nhất.

5.1 Tần suất làm độ tin cậy rẻ

Giả sử sau một hậu tố nào đó, return xuất hiện 20 lần, raise 2 lần — đề xuất return trước rõ là đáng hơn. Tiếp tục càng phân tán thì càng không đáng đoán sâu. vLLM dùng suffix_decoding_min_token_prob (mặc định 0.1) lọc token có xác suất ước lượng quá thấp.

Các tần suất này chỉ là căn cứ xếp hạng nội bộ của Proposer, không phải xác suất có điều kiện của Target; nhận hay không vẫn do Target verify quyết định.

5.2 Phạm vi lịch sử và độ dài động

Suffix Decoding duy trì hai loại cây hậu tố:

  • Cây Prompt: cho prompt và nội dung đã sinh của request hiện tại.
  • Cây toàn cục: cache đầu ra lịch sử xuyên request, quy mô do suffix_decoding_max_cached_requests (mặc định 10000) kiểm soát; đặt 0 là tắt.

Mỗi bước đề xuất bao nhiêu token quyết định động: khớp càng dài, càng tự tin thì đoán càng nhiều. Vì vậy ở đây num_speculative_tokens là trần; tài liệu gợi ý 16 hoặc 32. Trong v0.32.0, nếu không đặt, code lấy giá trị suffix_decoding_max_tree_depth (mặc định 24). Nó nổi trên các bài lặp cao như sửa code, vòng phản tư của agent, RL rollout; khi sinh nội dung hoàn toàn mới, lịch sử nhiều cũng không giúp.

5.3 Chi phí tích hợp

🔍 Hạng mục chi phíCần đối chiếu gì
Tìm và cập nhậtXây cây, cập nhật tần suất có kéo chậm mỗi bước không
Lưu lịch sửCây toàn cục cache bao nhiêu request, chiếm bao nhiêu bộ nhớ
Phạm vi thống kêChia sẻ lịch sử xuyên request có khớp yêu cầu cô lập dữ liệu không
Phụ thuộcCần pip install arctic-inference, ghi phiên bản

💡 Gợi ý: cây toàn cục để đầu ra của một user ảnh hưởng ứng viên của request user khác. Ứng viên vẫn qua Target verify nên phân phối đầu ra không đổi; nhưng trong đa tenant, có cho phép chia sẻ kiểu đó hay không do yêu cầu cô lập nghiệp vụ quyết định.

📌 Điểm then chốt: Prefix Cache tái dùng KV đã tính, giảm Prefill lặp; N-gram/Suffix đề xuất token có thể cần sinh, vẫn phải verify. Cả hai đều “tái dùng lịch sử”, nhưng chỉ số thống kê không được trộn.


6. Thực hành: cửa sổ khớp và độ phủ ứng viên

Đoạn dưới dùng số nguyên thay token ID, triển khai bộ tìm đơn giản theo quy tắc vLLM: từ cửa sổ dài xuống ngắn, cùng độ dài lấy vị trí sớm nhất. Chỉ demo quy tắc; vLLM thực tế dùng biến thể KMP quét một lần xong.

def propose(history, min_n=2, max_n=4, k=3):
    """Trả về (độ dài cửa sổ hit, ứng viên); miss thì (None, [])."""
    for n in range(min(max_n, len(history) - 1), min_n - 1, -1):
        tail = history[-n:]
        # start < len - n loại đuôi chính nó; quét trước → sau, lấy khớp sớm nhất
        for start in range(len(history) - n):
            if history[start:start + n] == tail:
                return n, history[start + n:start + n + k]
    return None, []


h1 = [10, 11, 12, 20, 21, 99, 10, 11, 12]
print("h1:", propose(h1))                      # (3, [20, 21, 99])
print("h1 chỉ cho 4-gram:", propose(h1, min_n=4))  # (None, [])

h2 = [7, 8, 1, 7, 8, 2, 7, 8]                  # đuôi [7, 8] xuất hiện hai lần
print("h2:", propose(h2))                      # lấy chỗ sớm nhất: (2, [1, 7, 8])
print("không lặp:", propose([1, 2, 3, 4, 5]))

h1 hit 3-gram; siết thành 4-gram thì không còn ứng viên. Trong h2, đuôi [7, 8] từng theo sau bởi 1 cũng bởi 2; quy tắc chọn chỗ sớm nhất, đề xuất 1. Nếu lần này Target muốn viết 2, ứng viên sẽ bị từ chối ở vị trí đầu.

Có thể thêm vài lần 7, 8, 2 vào h2, nghĩ Suffix Decoding theo tần suất sẽ đề xuất gì — so “lấy một chỗ khớp” với “chọn tiếp tục theo tần suất lịch sử”.


7. Tỉ lệ chấp nhận: nói rõ mẫu số trước

7.1 Ba đại lượng liên quan nhưng khác nhau

Phần còn lại của chương dùng bộ định nghĩa này. Trong cửa sổ quan sát, tổng cộng đề xuất DD token ứng viên, chấp nhận CC trong số đó, qua RR vòng nghiệm thu:

Draft Acceptance Rate=CD\text{Draft Acceptance Rate}=\frac{C}{D} Mean Acceptance Length=1+CR\text{Mean Acceptance Length}=1+\frac{C}{R}

“+1” trong độ dài chấp nhận trung bình là token sửa hoặc token thêm mà mỗi vòng đều commit. Chỉ số theo request và chỉ số Prometheus của vLLM đều dùng hai định nghĩa này.

Đại lượng thứ ba là độ phủ ứng viên: bao nhiêu bước thực sự đề xuất ứng viên. N-gram không tìm thấy thì bước đó không suy đoán; nếu chỉ thống kê các vòng có ứng viên, tỉ lệ chấp nhận 90% phía sau có thể chỉ 10% bước đang suy đoán.

7.2 Độ dài cố định và động không đổi chéo được

Chỉ khi mỗi vòng đều đề xuất đúng γ\gamma ứng viên mới có D=γRD=\gamma R, lúc đó độ dài chấp nhận trung bình viết được 1+γ⋅C/D1+\gamma\cdot C/D. Suffix, Dynamic SD và các phương án độ dài mỗi vòng khác nhau phải tính trực tiếp từ DD và RR thực tế.

Nhầm lẫn phổ biến khác là tỉ lệ chấp nhận theo vị trí: “xác suất vị trí ii được chấp nhận khi các vị trí trước đã qua” và “xác suất chấp nhận liên tục từ 1 đến ii” là hai chuyện; cái sau suy giảm theo ii nhanh hơn nhiều. vllm:spec_decode_num_accepted_tokens_per_pos_total của vLLM (tách theo nhãn position) đếm cái sau.

⚠️ Lưu ý: tỉ lệ chấp nhận 90% không bằng tăng tốc 90%. Sinh Draft, độ dài verify, dung lượng VRAM và tải lịch trình đều vào mẫu số thời gian; mục 5.4 suy sổ đó riêng.


8. Chọn phương án kỹ thuật: theo nghiệp vụ và tài nguyên

🎯 Đặc trưng nghiệp vụThử trướcQuan sát trọng tâm
Đầu ra tái dùng nhiều promptN-gramĐộ phủ ứng viên, cửa sổ tìm, độ dài tiền tố chấp nhận
Sửa lặp, quỹ đạo tương tự nhiềuSuffixPhạm vi cây toàn cục, độ dài động, chi phí xây cây
Nội dung mới nhiều, copy ítDraft độc lập / PARDĐộ khớp với Target, thời gian Draft, VRAM thêm
Mô hình tự có hoặc có mô-đun phụ trợ tương thíchMTP / EAGLE (xem 5.3)Artifact có khớp phiên bản Target không

Lấy prompt và cấu hình lấy mẫu thật, đo độ phủ ứng viên, độ dài chấp nhận trung bình và độ trễ end-to-end, rồi mới quyết có lên phương án phức tạp hơn không. “Code” và “hội thoại” chỉ là nhãn bài; cái quyết định hiệu quả thật là mức lặp, khớp phân phối và chi phí hệ thống.

✅ Cơ hội lợi ích: ứng viên rẻ mà hay hit có thể giảm số lần gọi Target.

❌ Cái giá: mô hình độc lập chiếm VRAM; phương án không dùng mô hình phụ thuộc lịch sử tái dùng được; cả hai đều phải bảo trì thêm cache và luồng verify.


📝 Tóm tắt

  • Draft tốt phải đồng thời rẻ, đúng, nối được; ít tham số chỉ là một phần.
  • Mô hình nhỏ độc lập đoán được nội dung mới, nhưng phải tính trọng số, KV và khớp năng lực; PARD dùng Parallel Draft giảm độ trễ bản nháp.
  • vocab_size giống không bằng token ID giống — phải đối chiếu token đặc biệt, template và độ dài ngữ cảnh; từ vựng dị thể cần TLI.
  • N-gram khớp đuôi lịch sử rồi chép phần sau; cửa sổ tìm và độ dài đề xuất là hai tham số; vLLM lấy khớp dài nhất, sớm nhất.
  • Suffix dùng cây hậu tố và tần suất quyết định độ dài đề xuất động; chi phí nằm ở xây cây, cache và phụ thuộc.
  • Ứng viên xác định tương đương qq one-hot; với nghiệm thu chuẩn vẫn lossless.
  • Báo riêng tỉ lệ chấp nhận C/DC/D, độ dài chấp nhận trung bình 1+C/R1+C/R và độ phủ ứng viên.

🎯 Danh sách tự kiểm

  • Giải thích được vì sao Draft lớn hơn có thể đúng hơn nhưng chậm hơn
  • Liệt kê được các hạng mục VRAM thêm mà Draft độc lập chiếm
  • Viết được đoạn script kiểm ánh xạ token → ID của hai Tokenizer có khớp không
  • Phân biệt được cửa sổ tìm và độ dài đề xuất của N-gram, nói được mặc định của vLLM
  • Mô tả được khi nhiều chỗ khớp, vLLM N-gram chọn chỗ nào
  • Giải thích được vì sao tần suất Suffix vẫn cần Target verify, và rủi ro cô lập của cây toàn cục
  • Phân biệt được Prefix Cache với đề xuất token lịch sử
  • Dùng CC, DD, RR tính được tỉ lệ chấp nhận và độ dài chấp nhận trung bình, chỉ ra bẫy đổi chéo dưới độ dài động

📚 Tài liệu tham khảo