Danh sách bài viết

Bài 30: Sampling — Temperature, Top-K, Top-P — điều khiển độ sáng tạo

Sampling là bước cuối của một forward pass: từ probability distribution trên vocabulary, chọn ra một token cụ thể. Bài này đi từ greedy decoding, stochastic sampling, công thức temperature qua softmax, đến Top-K, Top-P (nucleus, Holtzman 2019), Min-P (2024), cách combine các tham số, max_tokens, stop sequence, seed, frequency/presence penalty, logit bias, n>1, settings khuyến nghị cho từng loại task, lưu ý cho reasoning model, common mistakes, và tổng kết Module 4 Prompt Engineering.

25/05/2026
14 phút đọc
1 lượt xem
1

Mục tiêu bài học

Sau bài này, bạn cần trả lời được:

  • Sampling là bước nào trong một forward pass của LLM, vì sao không phải lúc nào cũng pick token có probability cao nhất.
  • Temperature điều chỉnh distribution như thế nào về mặt toán; T=0, T=1, T>1 khác nhau ra sao.
  • Top-K, Top-P, Min-P khác nhau ở đâu, khi nào dùng cái nào.
  • max_tokens, stop, seed, frequency_penalty, presence_penalty, logit_bias, n có vai trò gì trong API call.
  • Settings nào hợp lý cho factual QA, translation, summarization, brainstorm, creative writing, code generation.
  • Vì sao reasoning model (o1, R1, Claude thinking) khoá hoặc hạn chế các tham số sampling.

Bài 29 đã đảm bảo output đúng schema; bài này quyết định output có biến thiên hay không và biến thiên đến mức nào. Đây là bài kết thúc Module 4 Prompt Engineering — phần tổng kết module nằm ở bước 26.

2

Recap — logits, softmax, sampling

Một forward pass của LLM sinh ra ba thứ cho mỗi vị trí token kế tiếp:

  1. Logits z — vector kích thước vocab_size (vài chục nghìn đến hơn trăm nghìn), giá trị thực, chưa chuẩn hoá.
  2. Probability p — softmax của logits, mỗi phần tử trong [0, 1], tổng bằng 1.
  3. Token được chọn — sampled từ p, append vào sequence, lặp lại.

Công thức softmax chuẩn:

\[ p_i = \frac{\exp(z_i)}{\sum_j \exp(z_j)} \]

Sampling chính là bước (3): từ phân phối p, chọn một token cụ thể. Có nhiều chiến lược, và chính là phần API cho mình điều chỉnh được. Tất cả các tham số temperature, top_p, top_k, min_p, frequency_penalty, logit_bias đều biến đổi hoặc lọc lại distribution p trước khi sample.

3

Greedy decoding

Greedy decoding: ở mỗi bước, chọn token có probability cao nhất.

\[ t_{\text{next}} = \arg\max_i p_i \]

  • Deterministic: cùng prompt → cùng output (về lý thuyết).
  • Nhanh: không cần random number, không cần lọc top-k/top-p.
  • Nhược: output dễ bị lặp, sáo mòn, dính vào các cụm từ "an toàn" cao xác suất. Mô hình ngôn ngữ với greedy đôi khi rơi vào loop kiểu "the the the..." (degenerate text — Holtzman 2019).

Trong API, greedy tương đương temperature=0. Phù hợp cho task cần đáp án xác định: classification, extraction, code, translation.

4

Stochastic sampling

Stochastic sampling: chọn token random, có weight bằng chính probability của nó.

Ví dụ với 3 token có p = [0.7, 0.2, 0.1]: 70% lần chọn token 1, 20% chọn token 2, 10% chọn token 3. Output khác nhau giữa các lần call ngay cả khi prompt giống hệt.

  • Diversity cao hơn greedy — ít lặp, đa dạng cách diễn đạt.
  • Có rủi ro: thỉnh thoảng sample phải token rare ở đuôi distribution, dẫn đến output kỳ quặc. Đây là lý do cần Top-K, Top-P, Min-P để cắt phần đuôi.

Hầu hết API mặc định stochastic (temperature=1.0). Muốn deterministic, set temperature=0 + seed.

5

Temperature — công thức

Temperature T chia logits trước softmax:

\[ p_i = \frac{\exp(z_i / T)}{\sum_j \exp(z_j / T)} \]

Ý nghĩa từng giá trị:

  • \( T = 1 \): công thức trở về softmax gốc, distribution không đổi.
  • \( T < 1 \): sharper — logits lớn càng được khuếch đại, logits nhỏ càng bị nén; xác suất tập trung vào top token.
  • \( T > 1 \): flatter — khoảng cách giữa các logit bị thu hẹp; xác suất dàn đều hơn, token đuôi có cơ hội được chọn.
  • \( T \to 0^+ \): distribution tiến về one-hot ở token có logit lớn nhất — tương đương greedy.
  • \( T \to \infty \): distribution tiến về uniform — random hoàn toàn, không khác gì gọi random.choice trên cả vocabulary.

Cảm giác trực quan: temperature giống "núm chỉnh độ tự tin" của model. T thấp = "tôi chỉ nói thứ tôi chắc chắn nhất". T cao = "tôi sẵn sàng thử cả những lựa chọn ít chắc".

Lưu ý kỹ thuật: provider thường không implement T=0 bằng cách chia cho 0 (NaN). Họ check T <= eps rồi switch sang argmax. Vì vậy T=0 trong API thực ra là greedy decoding.

6

Temperature — giá trị khuyến nghị

Temperature    Use case                                        Đặc điểm output
─────────────────────────────────────────────────────────────────────────────────
0.0            Factual QA, classification, extraction, code    Deterministic, sáo
0.3 - 0.5      Default cho hầu hết task production             Balanced
0.7 - 0.9      Creative writing, brainstorm, dialogue          Diversity cao
1.0            Default API OpenAI / Anthropic 2025-2026        Mặc định, đa dạng
1.0+           Experimental, very random                       Có thể kỳ quặc

Một vài nguyên tắc lấy từ thực nghiệm cộng đồng (OpenAI cookbook, Anthropic doc):

  • Khi parse downstream (JSON, classification), luôn bắt đầu với T=0. Chỉ tăng nếu cần variation rõ ràng.
  • Brainstorm cần T=0.7-1.0 để tránh model lặp lại cùng vài ý.
  • Creative writing có thể đi đến T=1.2, nhưng kèm Top-P cắt đuôi để tránh từ ngữ vô nghĩa.
  • T > 1.5 ít khi hữu ích — output bắt đầu thiếu mạch lạc.
7

Top-K sampling

Top-K sampling (Fan et al., 2018): chỉ giữ K token có probability cao nhất, set phần còn lại về 0, re-normalize, rồi sample.

Ý tưởng: distribution của LLM có "đuôi dài" — hàng nghìn token rất hiếm tổng xác suất nhỏ nhưng vẫn dương. Nếu không cắt, đôi khi sample phải token rác. Top-K cắt đuôi, chỉ sample trong top.

  • K điển hình: 40, 50, 100.
  • K nhỏ → gần greedy hơn.
  • K lớn → gần stochastic gốc hơn.

Nhược điểm chính: K cố định. Có lúc top 10 token đã chiếm 99% xác suất (distribution sharp) — không cần đến 50 candidate. Có lúc xác suất trải đều trên 200 token (distribution flat) — giữ chỉ 50 là quá hẹp. Đây là vấn đề Top-P giải quyết.

OpenAI Chat Completions API không expose top_k. Anthropic, Vertex AI Gemini, local model (vLLM, llama.cpp, Ollama) đều có top_k param.

8

Top-P (nucleus) sampling

Top-P, còn gọi là nucleus sampling (Holtzman et al., 2019 — "The Curious Case of Neural Text Degeneration", arXiv:1904.09751): chọn nhóm token nhỏ nhất có tổng cumulative probability ≥ P.

Thuật toán:

  1. Sắp xếp token theo probability giảm dần.
  2. Cộng dồn cho đến khi vượt P.
  3. Giữ các token đã cộng, bỏ phần còn lại.
  4. Re-normalize, sample.

Đặc tính: K hiệu dụng thay đổi theo distribution. Distribution sharp → vài token đã đủ P=0.9. Distribution flat → cần nhiều token. Đây là điểm Top-K không có.

  • P điển hình: 0.9, 0.95.
  • P=1.0: không cắt — giống stochastic sampling gốc.
  • P=0.1: cắt sát — gần greedy.

Trên benchmark của Holtzman, Top-P sinh text mạch lạc hơn cả greedy lẫn Top-K trên nhiều prompt mở. Nucleus sampling đến nay vẫn là default phổ biến nhất.

9

Top-P vs Top-K

                Top-K                  Top-P (nucleus)
──────────────────────────────────────────────────────────────────
Số candidate    K cố định              Thay đổi theo distribution
Khi distribution sharp   Có thể giữ thừa token rare   Tự co lại
Khi distribution flat    Có thể cắt quá hẹp          Tự mở rộng
Param điển hình K=40-100               P=0.9-0.95
Provider expose Anthropic, Gemini,     OpenAI, Anthropic, Gemini,
                local                  Mistral, local

Khuyến nghị modern (2024-2026):

  • Default ưu tiên Top-P (linh hoạt hơn).
  • Nếu provider cho cả hai, có thể combine: top_k=50 + top_p=0.9. Top-K là safety net cứng, Top-P là filter thích nghi. Token phải qua cả hai.
  • Nếu chỉ có Top-P (OpenAI), dùng top_p.
10

Min-P sampling (2024)

Min-P sampling (Nguyen et al., 2024, arXiv:2407.01082 — "Turning Up the Heat: Min-p Sampling for Creative and Coherent LLM Outputs") đặt ngưỡng tương đối thay vì tuyệt đối hay cumulative.

Quy tắc: giữ token i nếu

\[ p_i \geq m \cdot p_{\max} \]

với m là tham số (điển hình 0.05-0.1) và p_max là probability của token cao nhất hiện tại.

  • Adaptive: ngưỡng tự co giãn theo độ "tự tin" của model.
  • Robust ở temperature cao: với T=2, Top-P 0.9 cho phép quá nhiều token kỳ quặc; Min-P 0.1 vẫn cắt được phần đuôi vì so với p_max.
  • Được implement trong llama.cpp, vLLM, Ollama, và là default phổ biến trong cộng đồng local LLM. DeepSeek và một số fine-tune Llama dùng Min-P trong recommended preset.

OpenAI / Anthropic API (đến giữa 2026) chưa expose Min-P public; chủ yếu gặp khi serve local hoặc dùng OpenRouter với open-weight model.

11

Combine các settings

Thứ tự áp dụng chuẩn trên hầu hết engine (HuggingFace generate, vLLM, llama.cpp):

  1. Lấy logits từ model.
  2. Áp repetition / frequency / presence penalty + logit_bias.
  3. Chia logits cho temperature.
  4. Softmax → probability.
  5. Lọc bằng top_k (nếu có).
  6. Lọc bằng top_p (nếu có).
  7. Lọc bằng min_p (nếu có).
  8. Re-normalize.
  9. Sample.

Combine phổ biến cho production:

  • Factual / structured: temperature=0. Top-P, Top-K vô tác dụng (greedy bỏ qua mọi filter).
  • General chat: temperature=0.7, top_p=0.9.
  • Creative: temperature=0.9, top_p=0.95 hoặc temperature=1.0, top_p=0.9.
  • Local model: thêm min_p=0.05 để chống token rác khi T > 1.

Anti-pattern hay gặp: set cả temperature=1.5 lẫn top_p=1.0 — không có filter cắt đuôi, output đầy noise.

12

OpenAI API

Chat Completions của OpenAI expose: temperature, top_p, max_tokens (legacy) / max_completion_tokens (mới), stop, seed, frequency_penalty, presence_penalty, logit_bias, n. Không expose top_k.

from openai import OpenAI

client = OpenAI()

response = client.chat.completions.create(
    model="gpt-4o-mini",
    messages=[
        {"role": "system", "content": "Bạn là trợ lý viết quảng cáo ngắn."},
        {"role": "user", "content": "Viết slogan cho quán cà phê yên tĩnh."},
    ],
    temperature=0.7,
    top_p=0.9,
    max_tokens=500,
)
print(response.choices[0].message.content)

Doc OpenAI khuyến nghị: chỉ chỉnh một trong hai temperature hoặc top_p, không cùng lúc. Thực tế cộng đồng vẫn combine, vì hai tham số nhắm vào mục tiêu khác nhau (scale vs cắt đuôi).

13

Anthropic API

Messages API của Anthropic expose: temperature (0.0-1.0), top_p, top_k, max_tokens (bắt buộc), stop_sequences. Không có frequency_penalty, presence_penalty, logit_bias, seed, n.

import anthropic

client = anthropic.Anthropic()

msg = client.messages.create(
    model="claude-opus-4-7",
    max_tokens=1024,
    temperature=0.5,
    top_p=0.9,
    messages=[
        {"role": "user",
         "content": "Tóm tắt đoạn sau trong 3 bullet ngắn: ..."},
    ],
)
print(msg.content[0].text)

Lưu ý hai khác biệt so với OpenAI:

  • temperature Anthropic giới hạn [0, 1] — không có T=1.5. Muốn diversity cao hơn, kết hợp top_p=0.99 + top_k rộng.
  • max_tokens là bắt buộc, không có default — phải truyền tường minh.
14

max_tokens / max_new_tokens

max_tokens (OpenAI / Anthropic) hay max_new_tokens (HuggingFace) đặt giới hạn cứng cho số token output. Có ba vai trò:

  • Safety: chặn runaway generation khi model rơi vào loop.
  • Cost control: token output thường đắt gấp 4-5 lần token input. Giới hạn cứng = trần chi phí mỗi call.
  • Latency control: với streaming, biết được thời gian tối đa client phải đợi.

Một số lưu ý:

  • Khi max_tokens hết trong khi model chưa "muốn dừng", response cắt giữa câu. Check finish_reason (length hay stop) để biết.
  • Với reasoning model (o1, o3, Claude thinking), max_tokens tính cả phần reasoning ẩn — đặt rộng tay (vài nghìn).
  • Anthropic max_tokens bắt buộc; OpenAI có default model-dependent.
15

Stop sequence

stop (OpenAI) / stop_sequences (Anthropic) là danh sách chuỗi; khi model sinh ra một trong các chuỗi đó, generation dừng và chuỗi đó không nằm trong response.

response = client.chat.completions.create(
    model="gpt-4o-mini",
    messages=[{"role": "user", "content": "List 3 quả: 1."}],
    stop=["4.", "\n\n"],
)

Use case:

  • Parse-friendly: ép model dừng khi gặp delimiter ("\n\n", "###", "END").
  • Few-shot pattern: nếu prompt có "Q: ... A: ..." lặp, set stop=["\nQ:"] để model không tự bịa câu hỏi mới.
  • Anthropic prefill: prefill { + stop_sequences=["}\n\n"] để cắt phần dư sau JSON (bài 24).

OpenAI cho tối đa 4 stop sequence, Anthropic tối đa 4. Mỗi sequence ≤ vài chục ký tự.

16

Seed — deterministic stochastic

OpenAI giới thiệu seed tại DevDay 2023: cùng prompt + cùng seed + cùng system_fingerprint → cùng output. Đây là randomness có kiểm soát — giữ tính ngẫu nhiên nhưng reproduce được.

response = client.chat.completions.create(
    model="gpt-4o-mini",
    messages=[{"role": "user", "content": "Viết một câu chuyện 30 từ."}],
    temperature=0.9,
    seed=42,
)
print(response.system_fingerprint)  # vd: "fp_44709d6fcb"

Lưu ý từ doc OpenAI: "best effort". Khi backend infra thay đổi (model patch, GPU kernel, batch size), system_fingerprint đổi và output có thể khác — ngay cả cùng seed. Vì vậy đừng dựa vào seed cho regression test dài hạn; dùng để debug session ngắn, A/B test prompt, eval reproducibility trong 1 ngày.

Anthropic API chưa có seed public (đến giữa 2026). Vertex AI Gemini có. Local model (vLLM, HuggingFace) có thông qua torch.manual_seed.

17

Frequency penalty và Presence penalty

Hai tham số OpenAI dùng để chống lặp:

  • Frequency penalty (frequency_penalty, [-2.0, 2.0]): trừ logit của token đã xuất hiện, càng nhiều lần trừ càng mạnh. Token lặp 5 lần bị trừ nặng hơn token lặp 1 lần.
  • Presence penalty (presence_penalty, [-2.0, 2.0]): trừ một lượng cố định cho token đã xuất hiện ít nhất 1 lần, bất kể bao nhiêu lần.

Công thức tóm tắt:

\[ z_i' = z_i - \alpha \cdot c_i - \beta \cdot \mathbb{1}[c_i > 0] \]

với c_i là count token i trong context hiện tại, α là frequency penalty, β là presence penalty.

  • Giá trị dương → khuyến khích đa dạng từ vựng / chủ đề.
  • Giá trị âm → khuyến khích lặp (hiếm khi cần).
  • Khuyến nghị OpenAI: bắt đầu với 0.1-0.5. > 1.0 dễ làm output mất mạch lạc.

Anthropic không có hai tham số này — Claude xử lý chống lặp ở chính phase training, ít khi cần knob ngoài.

18

Logit bias

logit_bias (OpenAI) là dict {token_id: bias}, với bias trong [-100, 100], cộng thẳng vào logit của token đó trước softmax.

import tiktoken

enc = tiktoken.encoding_for_model("gpt-4o-mini")
ids = enc.encode("forbidden")   # ban các token này

response = client.chat.completions.create(
    model="gpt-4o-mini",
    messages=[{"role": "user", "content": "Mô tả Hà Nội ngắn."}],
    logit_bias={tid: -100 for tid in ids},
)

Use case:

  • Hard constraint: bias -100 gần như chặn hẳn một token (ban từ).
  • Force choice: bias +100 cho token mong muốn — hay dùng cho classification (vd ép chỉ trả về " yes" / " no").
  • Style steering nhẹ: bias +1 đến +3 cho token thuộc domain.

Hạn chế: phải biết token_id (qua tiktoken), bias ở mức token chứ không phải word. "không" có thể là 1 token, "không thể" là 2 — phải bias từng cái.

Anthropic không có logit_bias. Cách thay thế: dùng stop_sequences để cấm xuất hiện, hoặc constraint qua prompt.

19

N=1 vs N>1

OpenAI n param: số response độc lập sinh ra cho cùng một request.

response = client.chat.completions.create(
    model="gpt-4o-mini",
    messages=[{"role": "user", "content": "Viết slogan."}],
    temperature=0.9,
    n=3,
)
for choice in response.choices:
    print(choice.message.content)
  • n=1 (default): 1 response.
  • n=3: 3 response độc lập. Input chỉ tính phí 1 lần, output tính 3 lần.
  • Use case: pick best, ensemble vote (self-consistency — bài 28), brainstorm UI cho user chọn.

Anthropic không có n. Workaround: gọi N lần song song (asyncio.gather). Tốn token nhập 3 lần thay vì 1, nhưng được response thực sự độc lập (khác seed, khác system fingerprint).

Trade-off chi phí: n=3 với OpenAI tiết kiệm hơn 3 call riêng (input tính 1 lần), nhưng kém linh hoạt — cả 3 dùng cùng setting.

20

Settings theo task

Task                     Temperature    Top-P     Ghi chú
──────────────────────────────────────────────────────────────────────────
Factual QA               0.0            1.0       Greedy, không cần filter
Classification           0.0            1.0       + logit_bias cho hard label
Extraction (JSON)        0.0 - 0.2      1.0       Kết hợp Structured Output
Translation              0.0 - 0.3      1.0       Tránh sáng tạo
Summarization            0.3 - 0.5      0.9       Cân đối paraphrase và bám gốc
Code generation          0.0 - 0.3      0.95      Thấp để bám syntax
Chat tổng quát           0.7            0.9       Default cho assistant
Brainstorm ý tưởng       0.8 - 1.0      0.95      Cần diversity
Creative writing         0.8 - 1.2      0.9-0.95  Frequency penalty 0.3+
Dialogue / roleplay      0.7 - 0.9      0.9       + presence penalty 0.3

Cách dùng bảng: pick row gần với task của mình, bắt đầu với giá trị giữa khoảng, đo metric (human eval, BLEU, accuracy) khi tinh chỉnh. Đừng chỉnh nhiều knob cùng lúc; thay đổi 1 param, đo, rồi đến param kế tiếp.

21

Reproducibility

Reproduce output LLM khó hơn vẻ ngoài. Checklist nên có:

  • Set temperature=0 — bỏ randomness từ sampling.
  • Set seed (nếu provider hỗ trợ) — fix seed của RNG khi không phải greedy.
  • Pin model version chính xác: gpt-4o-2024-08-06, không phải gpt-4o chung chung — alias có thể trỏ sang version mới.
  • Log system_fingerprint: nếu fingerprint đổi giữa 2 call cùng seed, biết là infra đã đổi, có thể giải thích sự khác biệt.
  • Pin tokenizer version nếu tự pre-process (tiktoken, sentencepiece) — count token có thể lệch giữa version.

Một số provider vẫn có nondeterminism ngay cả T=0 — do batching trên GPU (floating point không associative), routing giữa các node, model parallelism. Đây là lý do seed được mô tả "best effort", không "guaranteed". Với task cần reproducibility nghiêm ngặt (audit, compliance), self-host model là cách duy nhất.

22

Sampling cho reasoning model

Reasoning model (OpenAI o1/o3, DeepSeek R1, Claude với extended thinking) có pattern khác:

  • OpenAI o1, o3: API hiện chỉ cho phép temperature=1.0, top_p=1.0; không hỗ trợ frequency_penalty, presence_penalty, logit_bias. Lý do: model được train với RL trên rollout đa dạng, sampling được tune sẵn — provider khoá để tránh user phá.
  • DeepSeek R1: doc khuyến nghị temperature=0.5-0.7, top_p=0.95. T=0 với R1 có thể làm phần reasoning kẹt lại do mất diversity ở chain-of-thought.
  • Claude với extended thinking: temperature vẫn dùng được, nhưng phần thinking nội bộ có sampling riêng. Khuyến nghị T=1.0 cho thinking, để model thoải mái khám phá.

Trên reasoning model, thay vì chỉnh sampling, ta chỉnh "thinking budget" (số token cho phép cho reasoning). Đây là tham số mới và quan trọng hơn temperature ở mức use case.

23

Visualize effect

Cách thực hành nhanh nhất để cảm nhận temperature: chạy cùng prompt với nhiều T, in 3 sample mỗi T.

from openai import OpenAI

client = OpenAI()
prompt = "Viết một câu mô tả thành phố Hà Nội buổi tối."

for T in [0.0, 0.5, 1.0, 1.5]:
    print(f"\n=== T={T} ===")
    for i in range(3):
        resp = client.chat.completions.create(
            model="gpt-4o-mini",
            messages=[{"role": "user", "content": prompt}],
            temperature=T,
            max_tokens=80,
        )
        print(f"[{i+1}] {resp.choices[0].message.content}")

Quan sát điển hình:

  • T=0: cả 3 lần ra cùng câu. Bám sát "đèn vàng, phố cổ, hồ Gươm".
  • T=0.5: vẫn 1 mô típ chung, vài từ ngữ thay đổi.
  • T=1.0: 3 câu khác hẳn — góc nhìn khác (con phố, ngõ nhỏ, ven hồ).
  • T=1.5: bắt đầu có câu kỳ quặc, đôi khi sai ngữ pháp.
24

Common mistakes

  • Temperature quá cao cho factual: T=0.9 cho QA → model "sáng tạo" số liệu, hallucinate ngày tháng, tên người. Default T=0 cho factual.
  • Temperature quá thấp cho creative: T=0 cho viết truyện → mỗi nhân vật cùng kiểu thoại, lặp cụm từ. Đẩy lên 0.8-1.0.
  • High T + high top_p + không penalty: T=1.5, top_p=1.0 → output noise nặng. Thêm top_p=0.9 hoặc min_p=0.1 để cắt đuôi.
  • Quên set seed khi debug: bug chỉ xuất hiện 1/10 lần — không reproduce được. Mỗi lần dev, set seed.
  • Đặt max_tokens quá thấp: response cắt giữa JSON, parser fail. Đo distribution length thực tế, set max gấp 2-3 lần p95.
  • Chỉnh cả temperature, top_p, top_k, penalty cùng lúc: không biết param nào gây thay đổi. Đổi 1 cái, đo, rồi đến cái kế tiếp.
  • Dùng frequency_penalty=2.0: model bắt đầu né cả từ thông dụng ("là", "của") — câu mất tự nhiên. Hạn chế ≤ 0.5.
25

Modern tips (2025-2026)

  • Default API hiện tại: cả OpenAI lẫn Anthropic mặc định temperature=1.0 cho Chat / Messages API. Nếu chỉ gọi thử mà không override, output đang ở chế độ "đa dạng".
  • Override theo task: production chuyên biệt gần như luôn nên override. Default API thiết kế cho chat tổng quát, không tối ưu cho structured / factual.
  • Đo trước khi tune: viết một eval set 20-50 case, chạy với grid (T, top_p), chọn cấu hình tốt nhất theo metric, fix lại.
  • Tách prompt khỏi sampling: prompt change ảnh hưởng nhiều hơn sampling change. Đừng dán nhãn vấn đề là "temperature sai" khi thật ra prompt thiếu instruction.
  • Reasoning model: bỏ thói quen chỉnh temperature. Chỉnh thinking budget và prompt cấu trúc tốt hơn nhiều.
  • Local model: pre-set cộng đồng cho từng base model thường tốt; check repo HuggingFace (file generation_config.json).
26

Tổng kết Module 4 — Prompt Engineering

Module 4 đi qua 10 bài (B21-B30), xây dựng toolkit để "giao tiếp" với LLM một cách có chủ đích:

  1. Bài 21 — System / User / Assistant: ba role cơ bản của chat API.
  2. Bài 22 — Context: cách cung cấp background, dữ liệu nguồn, ràng buộc miền.
  3. Bài 23 — Task description: viết yêu cầu rõ "model phải làm gì".
  4. Bài 24 — Output format: ép cấu trúc response (JSON, XML, table, plain).
  5. Bài 25 — Zero-shot prompting: chỉ instruction, không ví dụ.
  6. Bài 26 — Few-shot prompting: 2-5 ví dụ để model học pattern.
  7. Bài 27 — Chain-of-Thought: bật reasoning step-by-step.
  8. Bài 28 — Self-Consistency và Tree-of-Thought: nhiều path, vote / chọn tốt nhất.
  9. Bài 29 — Structured Output: schema-bound output (Pydantic, JSON Schema, Instructor).
  10. Bài 30 — Sampling: tham số quyết định độ ngẫu nhiên của output.

Sau Module 4, bạn có đủ vocabulary để diễn đạt mọi loại yêu cầu prompt và biết cách điều chỉnh từng tham số API. Tiếp theo là phần đưa các kiến thức này vào app thật.

Module 5 (Bài 31+) — API & Apps thực tế: setup OpenAI / Anthropic SDK, streaming, retry, async, cost tracking, function calling, các integration phổ biến (LangChain, LlamaIndex, Vercel AI SDK). Bài 31 mở đầu với OpenAI API end-to-end.

27

Code Python tổng hợp

Bốn snippet ngắn, chạy được với OpenAI Python SDK v1.x.

(a) Sampling với 5 temperature, observe diversity:

from openai import OpenAI

client = OpenAI()
prompt = "Một câu mô tả buổi sáng ở quán cà phê."

for T in [0.0, 0.3, 0.7, 1.0, 1.3]:
    print(f"\n--- T={T} ---")
    for _ in range(3):
        r = client.chat.completions.create(
            model="gpt-4o-mini",
            messages=[{"role": "user", "content": prompt}],
            temperature=T, max_tokens=60,
        )
        print(r.choices[0].message.content)

(b) Combine temperature + top_p:

r = client.chat.completions.create(
    model="gpt-4o-mini",
    messages=[{"role": "user",
               "content": "List 5 slogan cho ứng dụng học code."}],
    temperature=0.9,
    top_p=0.9,
    max_tokens=200,
)
print(r.choices[0].message.content)

(c) Frequency penalty test:

prompt = "Viết một đoạn 80 từ về Hà Nội."

for fp in [0.0, 0.5, 1.0]:
    r = client.chat.completions.create(
        model="gpt-4o-mini",
        messages=[{"role": "user", "content": prompt}],
        temperature=0.7,
        frequency_penalty=fp,
        max_tokens=150,
    )
    print(f"\nfrequency_penalty={fp}\n{r.choices[0].message.content}")

(d) Seed for reproducibility:

for _ in range(2):
    r = client.chat.completions.create(
        model="gpt-4o-mini",
        messages=[{"role": "user", "content": "Viết một câu chuyện 30 từ."}],
        temperature=0.9,
        seed=42,
        max_tokens=60,
    )
    print(r.system_fingerprint, "→", r.choices[0].message.content)

Snippet (d): hai call cùng seed thường ra cùng output nếu system_fingerprint không đổi. Nếu output khác mà fingerprint khác, infra đã rotate; không phải bug ở code.

28

Bài tập

  1. Chọn một prompt cố định ("Viết một câu mở đầu cho bài blog về AI"). Chạy với T = 0, 0.3, 0.7, 1.0, mỗi T sinh 5 sample. Đo: tỷ lệ trùng nhau (exact match) trong 5 sample, và độ dài trung bình. Trình bày kết quả thành bảng.
  2. Cùng prompt ở bài 1, fix T=0.9, thử top_p=0.5, top_p=0.9, top_p=1.0. So sánh diversity (đếm unique n-gram) và mức độ "lệch chủ đề".
  3. Set seed=42, T=0.9, gọi 5 lần liên tiếp với cùng prompt. Log system_fingerprint và content. Kiểm tra: có lần nào output khác? Nếu có, fingerprint có khác không?
  4. Pick 3 task: (a) trích xuất danh sách công việc từ một email, (b) viết một email chúc mừng sinh nhật, (c) sinh function Python sort một list. Đề xuất setting temperature + top_p + max_tokens hợp lý cho mỗi task, viết một câu giải thích vì sao chọn vậy.
  5. (Tùy chọn) Implement min-P sampling thuần Python (nhận logits, m, sample). Test với một distribution sharp ([10, 9, 1, 1, 1]) và một distribution flat ([5, 4.9, 4.8, 4.7, 4.6]), so sánh số token còn lại sau filter với m=0.05m=0.2.