Temperature, top-p và tính quyết định mà bạn không có
Temperature chia logits trước softmax — đủ để phá ý tưởng “núm sáng tạo”. Cùng greedy call hai lần vẫn cho hai đáp án.
Trên trang này
Đây là cùng một yêu cầu được gửi tới cùng một model năm lần. Cùng weights, cùng prompt, cùng máy, cùng random seed. Thứ duy nhất thay đổi là một con số.
prompt: "Q: What is the capital of France?\nA:"
T = 0.0 " Paris\nWhat is the question and does the answer answer it? The
question is: What is the capital of France?..."
T = 0.7 " Paris\nWhat is the question: Which city is the capital of
France?..."
T = 1.0 " Paris\nWhat is a good geographical qualifier for describing
Paris concerning its location?\nA: Near the Mediterranean Sea..."
T = 1.5 " Paris\nWhat clue from premise allows we to conclude that Godwin
was &, He chose Healing Crimson Colour No:white flour Pure..."
T = 2.0 "安全感金华.ITEMT]]];\naims assume parental.st-importe.valtermination
Screens قطر_Zeroหมายเลข-zA ('$ספטמבר..."Không có gì hỏng cả. Mọi token ở dòng cuối đều được rút ra hợp lệ từ phân phối xác suất của chính model trên vocabulary 151.936 mục của nó. Con số thay đổi được gọi là temperature, thường được hầu hết tài liệu mô tả như một núm chỉnh độ sáng tạo, và mô tả đó sai theo một cách mà chương này có thể chứng minh thay vì chỉ khẳng định.
Đây cũng là chương nơi ba lời hứa trước đó được trả nợ. Chương 4 đã định nghĩa logit rồi gần như chưa dùng đến. Hộp về floating-point của Chương 2 kết thúc bằng một lời dặn — hãy nhớ điều này khi Chương 17 hỏi vì sao cùng prompt, model và seed có thể tạo ra các token khác nhau. Và hộp mixture-of-experts của Chương 9 hứa một danh mục bốn nguyên nhân gây phi quyết định. Cả ba đều xuất hiện bên dưới.
Một dòng mà cả chương bám vào
Liên kết đến mục: Một dòng mà cả chương bám vàoChương 4 giới thiệu logit như một điểm số số thực chưa chuẩn hóa, mỗi lớp một điểm. Chương 8 khiến một language model tạo ra một điểm cho mỗi mục trong vocabulary. softmax biến vector đó thành xác suất:
Temperature đi vào ở đây — tên gọi được mượn từ vật lý thống kê, nơi cùng tham số đó kiểm soát mức độ một phân phối Boltzmann tập trung sắc nét vào các trạng thái năng lượng thấp của nó1 — và nó chia logits trước phép mũ:
Vị trí đó là toàn bộ cơ chế, và đáng dành hai dòng đại số để thấy vì sao nó không thể nằm ở đâu khác. Giả sử bạn thử áp temperature lên xác suất thay vào đó — nhân chúng với rồi chuẩn hóa lại. Bạn sẽ nhận được
Hằng số triệt tiêu. Nhân tỉ lệ xác suất không làm gì cả; phân phối quay lại y nguyên. Temperature chỉ có tác dụng vì nó tác động lên số mũ, nơi việc chia cho trước khi lấy mũ tương đương với nâng mỗi xác suất lên lũy thừa — một phép định hình lại phi tuyến làm thay đổi tỉ lệ giữa các mục thay vì thang chung của chúng.
Từ vị trí đó, cả hai giới hạn đều đi ra mà không cần thêm công. Khi , logit lớn nhất bỏ xa phần còn lại và sụp về token có điểm cao nhất duy nhất: greedy decoding. Khi tăng, mọi tiến về 0, mọi số mũ tiến về 1, và phân phối phẳng dần về đều trên toàn bộ vocabulary. Tại đúng , công thức chia cho 0, nên mọi triển khai đều special-case nó thành cực đại số học — kể cả widget bên dưới, chuyển sang argmax tại .
Một cảnh báo, vì sự trùng tên gây nhầm lẫn thật. Có một thứ thứ hai, không liên quan, cũng gọi là temperature trong machine learning: temperature scaling, một phương pháp hiệu chỉnh khớp một giá trị trên tập validation để độ tự tin của classifier khớp với độ chính xác của nó.2 Cùng công thức, không liên quan đến generation. Các paper nói “temperature” thường có thể đang nói về thứ đó; chương này thì không.
Đây là phân phối đó, với phép tính ngay trước mắt bạn. logits là cố định và hợp lý, nên các con số trong phần diễn giải bên dưới có thể được kiểm tra với những gì bạn thấy:
Temperature không phải núm chỉnh độ sáng tạo
Liên kết đến mục: Temperature không phải núm chỉnh độ sáng tạoCon số ␣banana là toàn bộ lập luận ở dạng thu nhỏ: tăng temperature không thể cho model một ý tưởng mà nó chưa có. logits đã được tính, thứ hạng đã cố định, và temperature giữ nguyên thứ hạng đó chính xác — dù “nóng” bao nhiêu cũng không bao giờ đưa một token bị chấm thấp vượt lên trên token bị chấm cao hơn. Tất cả những gì nó làm là phân phối lại khối lượng xuống theo thứ hạng mà chính model đã tạo. Temperature cao không làm model giàu phát kiến hơn; nó khiến model có khả năng phát ra các token mà nó đã chấm là tệ cao hơn.
Trên một vocabulary thật, điều này ngừng là chuyện lạ và trở thành lý do output temperature cao không dùng được. Đo trên Qwen/Qwen2.5-0.5B-Instruct, một forward pass, prompt ở trên, đếm số token cần để tích lũy một phần nhất định của khối lượng xác suất:
| temperature | xác suất top-1 | entropy | token giữ 80 % | 90 % | 95 % | 99 % |
|---|---|---|---|---|---|---|
| 0.5 | 99,98 % | 0,00 nats | 1 | 1 | 1 | 1 |
| 0.7 | 99,65 % | 0,03 nats | 1 | 1 | 1 | 1 |
| 1.0 | 96,01 % | 0,30 nats | 1 | 1 | 1 | 14 |
| 1.2 | 88,20 % | 0,88 nats | 1 | 2 | 13 | 252 |
| 1.5 | 62,83 % | 3,07 nats | 29 | 353 | 2.672 | 26.787 |
| 2.0 | 16,62 % | 8,19 nats | 13.516 | 32.966 | 55.231 | 101.205 |
Đọc dòng cuối thật chậm. Ở , với một câu hỏi chỉ có đúng một đáp án, 32.966 token khác nhau chia sẻ 90 % khối lượng xác suất cao nhất. Đó không phải một không gian sáng tạo rộng hơn. Đó là một model đã được số học bảo rằng hãy xem một tiểu từ tiếng Hàn và một identifier C++ là các lựa chọn còn sống cho từ đứng sau A:. Rác trong khối mở đầu là hệ quả trực tiếp, và đó không phải bug của model hay library — đó là điều request đã yêu cầu.
Khoảng hữu ích rất hẹp và phụ thuộc vào task chứ không phụ thuộc vào gu. Với câu hỏi factual, đáp án là một token và bất kỳ nhiệt nào trên khoảng 1.2 đều chỉ bơm lỗi vô ích. Với câu mở, thực sự có hơn một continuation tốt, và một chút nhiệt mua được sự đa dạng mà vẫn trôi chảy:
"Write a two-sentence story about a lighthouse."
T = 0.0 "The lighthouse stood tall and proud, its beacon illuminating the
night sky above. A lone sailor, his eyes fixed on the distant
horizon..."
T = 0.7 "In the quiet, stormy waters of the sea, a lighthouse stood
sentinel over the horizon, its golden dome casting a warm glow
on the fog-shrouded streets below..."
T = 1.0 "In the quiet night, a lone lighthouse stood sentinel over the
sea, its shining beacon a beacon of hope and solace for sailors
and fishermen across the vast and endless ocean..."
T = 1.3 "In the gentle sunlight, now reflecting upon the opening of Jack's
lighthouse, Jim Trahan, a small-time individual difficult to
define in paperwork, wondered about a career where simplicity
reigns..."Ở 1.3, model đã bịa một tên riêng và một câu không phân tích cú pháp được. Dải giữa “lần nào cũng giống hệt” và “rời rạc” vào khoảng 0.6 đến 1.1 với model này trên task này, và lời khuyên trung thực là bạn tìm nó bằng cách đo trên task của bạn, không phải sao chép một con số từ blog post.
Vì sao văn bản có xác suất cao nhất lại là văn bản tệ
Liên kết đến mục: Vì sao văn bản có xác suất cao nhất lại là văn bản tệCó một câu hỏi hiển nhiên ẩn dưới tất cả chuyện này: nếu model có một phân phối xác suất và một token là có khả năng nhất, vì sao không luôn lấy nó? Greedy decoding miễn phí, tái lập được và không cần tham số.
Vì kết quả là thế này:
prompt: "In a shocking finding, scientists discovered a herd of unicorns
living in a remote valley."
greedy: " The unicorns were so rare that they were not even recognized by
the local people. The unicorns were so rare that they were not
even recognized by the local people. The unicorns were so rare
that they were not even recognized by the local people. ..."
repeated 4-grams: 87.6 %Tám câu, một câu. Gần chín trên mười cửa sổ bốn-token đã xuất hiện trước đó trong cùng output. Đây là neural text degeneration, được Holtzman và cộng sự đặt tên và giải thích trong paper đã giới thiệu top-p.3 Model không hỏng; tối đa hóa xác suất chuỗi đơn giản là mục tiêu sai cho văn bản mở. Văn bản của con người không phải chuỗi từ có khả năng cao nhất — nó mang bất ngờ, xác suất per-token của nó lang thang, tụt xuống rồi phục hồi — trong khi đường đi xác suất tối đa là một điểm cố định mà một khi đã bước vào thì không có lý do để rời khỏi.
Đó là lý do sampling tồn tại. Đồng thời, và đây là phần thường bị bỏ qua, nó không phải một định luật phổ quát. Chương 12 đo được 24/24 đúng trên bài toán chữ hai bước với greedy decoding thuần, và sampling ở temperature 0.8 kéo kết quả xuống 81 %; self-consistency sau đó tiêu tốn gấp sáu lần token để leo lại nơi greedy đã đứng sẵn. Cả hai sự thật cùng đúng:
Open-ended generation. Không có continuation đúng duy nhất, nên continuation có khả năng cao nhất là cái bẫy — nó lặp, và 87,6 % của nó được sao chép từ chính nó. Hãy sample.
Task có một đáp án đúng. Có một continuation đúng duy nhất, nên rút ra bất cứ thứ gì khác là rút ra lỗi. 100 % của Chương 12 thành 81 % chính vì lý do này. Đừng sample.
Hầu hết production prompt thuộc loại thứ hai nhưng được cấu hình như loại thứ nhất, vì temperature bị để nguyên ở bất cứ giá trị nào example code dùng.
Hai cách cắt, và chỉ một cách thích nghi
Liên kết đến mục: Hai cách cắt, và chỉ một cách thích nghiSampling từ toàn bộ phân phối không phải điều ai thực sự làm, vì phần đuôi khổng lồ và đầy vô nghĩa. Phải cắt thứ gì đó. Có hai câu trả lời cổ điển, và chúng khác nhau ở một điểm quyết định tất cả.
Top-k giữ một số lượng candidate cố định. Sắp xếp theo xác suất, giữ mục đầu, bỏ phần còn lại, chuẩn hóa lại.4 Top-p, còn gọi là nucleus sampling, giữ một lượng khối lượng cố định: lấy token theo thứ tự giảm dần cho đến khi xác suất tích lũy đạt , rồi dừng.3 Về hình thức, nucleus là tập nhỏ nhất sao cho
Khác biệt nghe có vẻ trang trí nhưng không phải, vì hai prompt bạn gửi trong cùng một phút có hình dạng phân phối hoàn toàn khác nhau. Cả hai dưới đây là cùng model ở temperature 1:
Q: What is the capital of France?\nA: | Once upon a time, | |
|---|---|---|
| xác suất top-1 | 96,01 % | 25,39 % |
| token giữ 90 % khối lượng | 1 | 467 |
| top-k = 40 giữ | 99,61 % khối lượng | 78,87 % khối lượng |
| khối lượng ở hạng 2 đến 40 | 3,61 % | 53,48 % |
| token ở hạng 40 | ␣Av, 0,0093 % | ␣Dr, 0,128 % |
Một cố định, hai thất bại theo hướng ngược nhau. Trên prompt factual, cho phép 39 token mà cộng lại đáng 3,6 % — nó để rác lọt qua, gồm cả một candidate ở mức chín phần nghìn phần trăm, vì quy tắc đếm ô chứ không đếm bằng chứng. Trên prompt kể chuyện, cùng ném đi 21 % khối lượng mà model thật sự đã gán, vì nucleus thật ở đó rộng 467 token.
Top-p khiến đúng một con số làm cả hai việc. Đặt và nó giữ 1 token ở prompt đầu, 467 ở prompt thứ hai, vì nó đang hỏi một câu về phân phối thay vì áp một con số đếm lên nó. Xem sự thích nghi đó trực tiếp — cùng phép cắt, bốn temperature:
Widget đó cũng giải quyết một hiểu lầm đáng gọi tên, vì nó khiến người ta tốn tiền thật. Trên một phân phối tự tin, top_p = 0.9 không phải “một chút đa dạng”. Nó là greedy. Ở temperature 1, token dẫn đầu ở đây giữ 96,90 %, vốn đã trên 0.9, nên nucleus rộng đúng một token và không thứ gì khác có thể được rút ra. Các team đặt top_p thành 0.9 vì tin rằng họ đã nới lỏng gì đó rồi thắc mắc vì sao mọi response giống hệt nhau.
Đặt top-k thay vào đó và thất bại ngược lại cũng hiện rõ:
Các penalty, kèm công thức, vì nhầm lẫn chúng là chuyện phổ biến
Liên kết đến mục: Các penalty, kèm công thức, vì nhầm lẫn chúng là chuyện phổ biếnBa cơ chế khác nhau đi dưới những tên giống nhau, chúng làm những việc khác nhau, và khác biệt đó đo được. Gọi là số lần token đã xuất hiện.
Presence penalty
Liên kết đến mục: Presence penaltyTrừ một hằng số khỏi bất kỳ token nào đã từng xuất hiện. Xuất hiện một lần và xuất hiện bốn mươi lần bị phạt y như nhau. Nó là công tắc, không phải núm chỉnh.
Frequency penalty
Liên kết đến mục: Frequency penaltyTrừ theo tỉ lệ với số lần đếm. Một token dùng bốn lần bị phạt mạnh gấp bốn lần token dùng một lần, và áp lực cộng dồn khi văn bản dài lên.
Repetition penalty (CTRL)
Liên kết đến mục: Repetition penalty (CTRL)Bản gốc, từ paper CTRL.7 Nó chia thay vì trừ, với trường hợp dấu cần thiết vì chia một logit âm sẽ làm nó lớn hơn. Vì vậy độ mạnh của nó phụ thuộc vào độ lớn của logit, nghĩa là cùng tác động khác nhau tại những điểm khác nhau trong cùng một câu.
Cùng continuation suy thoái lúc trước, áp từng loại. “Steps altered” đếm bao nhiêu trong 120 bước generation chọn token khác với token mà model không penalty sẽ chọn. Run ở đây là 120 bước so với 140 trong khối trên, đó là lý do baseline không penalty đọc là 85,5 % thay vì 87,6 %:
| setting | repeated 4-grams | steps altered |
|---|---|---|
| không gì | 85,5 % | 0 / 120 |
| presence 0.5 | 65,0 % | 3 / 120 |
| presence 1.0 | 3,4 % | 11 / 120 |
| frequency 0.5 | 6,0 % | 12 / 120 |
| frequency 1.0 | 0,0 % | 20 / 120 |
| repetition 1.2 (CTRL) | 0,0 % | 35 / 120 |
Ba điều rút ra. Presence ở 0.5 thay đổi ba quyết định trong 120 và cắt lặp lại một phần tư — vòng lặp được giữ bởi một nhúm token. Frequency ở 0.5 thay đổi số quyết định nhiều gấp bốn lần với hiệu ứng lớn hơn nhiều, vì hệ số đếm tiếp tục tăng trong khi hằng số presence thì không. Và penalty CTRL ở giá trị 1.2 được sao chép rộng rãi đã viết lại 35 trong 120 quyết định, đây không phải cú khẽ đẩy; đó là một model khác.
Con số cuối cùng là phần mở cho lỗi mà không ai cảnh báo.
Penalty làm gì với văn bản vốn phải lặp
Liên kết đến mục: Penalty làm gì với văn bản vốn phải lặpCode lặp. Bảng lặp. Danh sách lặp. Structured output lặp theo định nghĩa — đó chính là structure. Một penalty không thể phân biệt model kẹt trong vòng lặp với model đang phát đúng hàng thứ tư của một bảng, vì cả hai đều trông như một token xuất hiện lại.
Cùng ba task, được tạo theo ba cách:
| task | không gì | frequency 0.5 | repetition 1.2 |
|---|---|---|---|
| bảng markdown, 6 hàng | 0 / 56 steps altered | 0 / 56 | 2 / 62 |
| hàm Python | 0 / 93 | 0 / 93 | 10 / 110 |
| danh sách gạch đầu dòng, 1 đến 12 | 0 / 50 | 0 / 50 | 0 / 50 |
Frequency penalty ở 0.5 hóa ra vô hại trên cả ba, một kết quả hữu ích và hơi bất ngờ, và nó nói một điều chính xác: vì không quyết định nào đổi, các token cấu trúc hẳn đã thắng vị trí của chúng với biên lớn hơn phần penalty bị trừ, kể cả sau khi xuất hiện năm và sáu lần. Penalty CTRL, vốn chia, thì đẩy bật chúng, và đây là thứ nó tạo ra:
repetition 1.2, markdown table:
| n | 2^n |
| --- | --- |
| 0 | 1 |
| 1 | 2 |
| 2 | 4 |Căn chỉnh vỡ: lượng padding trong mỗi ô thay đổi từ hàng này sang hàng khác, vì chuỗi dấu cách trước pipe đóng chính xác là kiểu lặp mà penalty tồn tại để phá. Chỉ là thẩm mỹ, và nó tốn thêm sáu token. Trường hợp Python thì không chỉ thẩm mỹ:
nothing / frequency 0.5:
total = 0
for i in range(1, n + 1):
total += i ** 2
return total
repetition 1.2:
# Initialize total_sum with 0
total_sum = 0
# Loop through numbers from 1 to n, incrementing by 2 each time
for i in range(1, n + 1,Penalty đẩy model khỏi total — đã dùng trong docstring — sang total_sum, nhồi output bằng comment bịa ra để tiêu ngân sách vào các token chưa dùng, rồi bước vào range ba đối số với stride. Comment nói incrementing by 2 each time, điều này sai với tổng bình phương từ 1 đến . Một repetition penalty đã tạo code sai từ một prompt vốn được trả lời đúng khi không có nó.
Quy tắc theo sau rất ngắn: penalty dành cho prose mở, và nên tắt với code, structured output, dữ liệu dạng bảng và bất cứ thứ gì có schema. Chương 18 nói đúng về nhóm thứ hai đó.
Thứ tự áp dụng, và vì sao nó đổi đáp án
Liên kết đến mục: Thứ tự áp dụng, và vì sao nó đổi đáp ánMọi triển khai thật đều áp dụng các bước theo một trình tự cụ thể:
penalties → temperature → top-k → top-p → sample
Đây không phải ghi sổ tùy tiện, và hoán đổi hai giai đoạn tạo ra các phân phối thật sự khác nhau. Hai phép đo, đều trên prompt factual.
Cắt trước hay sau temperature. Nucleus được tính trên bất kỳ phân phối nào nó nhận, và temperature thay đổi phân phối đó rất mạnh:
| top-p 0.9 sau temperature | top-p 0.9 trước temperature | |
|---|---|---|
| 1 token | 1 token | |
| 353 token | 1 token | |
| 32.966 token | 1 token |
Ở , cùng một setting danh nghĩa tạo ra candidate set gồm 32.966 hoặc 1, chỉ tùy vào giai đoạn nào chạy trước. Nếu bạn từng tự hỏi vì sao tăng temperature “không làm gì” ở một provider nhưng phá nát output ở provider khác với cùng hai con số, bảng này là một câu trả lời hợp lý.
Phạt trước hay sau temperature. Trừ một penalty rồi chia cho cho penalty hiệu dụng ; chia trước rồi trừ cho . Với presence penalty 1.0 áp lên token dẫn đầu:
| temperature | phạt, rồi temper | temper, rồi phạt |
|---|---|---|
| 0.5 | 99,858 % | 99,948 % |
| 1.0 | 89,839 % | 89,839 % |
| 2.0 | 10,783 % | 6,830 % |
Giống hệt ở , như bắt buộc. Cách nhau hệ số 1,58 ở . “Presence penalty 1.0” không phải một lượng penalty được định nghĩa rõ trừ khi bạn cũng biết temperature được áp ở đâu, và không API nào ghi tài liệu điều này.
Hiện chi tiết
Tùy chọn: toàn bộ pipeline, theo thứ tự trên.
Mười sáu dòng, và mọi thứ trong chương này nằm trong đó. Đó là cùng phép tính widget thực hiện, trên một vector logit thật thay vì mười con số cố định.
def sample(logits, counts, presence=0.0, frequency=0.0,
temperature=1.0, top_k=0, top_p=1.0, generator=None):
z = logits.clone()
idx = torch.tensor(list(counts)) # 1. penalties
if len(idx):
z[idx] -= presence
z[idx] -= frequency * torch.tensor([float(c) for c in counts.values()])
if temperature <= 0: # 2. temperature
return int(z.argmax()) # T=0 is argmax
p = torch.softmax(z / temperature, -1)
p, order = p.sort(descending=True)
if top_k: # 3. top-k
p[top_k:] = 0
p = p * ((p.cumsum(0) - p) < top_p) # 4. top-p
p = p / p.sum() # 5. renormalise
return int(order[torch.multinomial(p, 1, generator=generator)])cumsum(0) - p trong dòng top-p là khối lượng tích lũy không gồm token hiện tại, điều khiến nucleus bao gồm token vượt ngưỡng thay vì dừng ngay trước nó. Sai lệch off-by-one ở đây và top_p = 0.9 âm thầm trở thành một phép cắt hơi chặt hơn mọi triển khai khác.
Đây là một trong số ít nơi ở nửa sau của khóa học mà Python là ngôn ngữ đúng, và lý do mang tính cấu trúc hơn là phong cách: mọi dòng ở trên cần bạn cầm toàn bộ vector logits trong tay, còn qua HTTP API thì vector đó không tồn tại. Bạn có thể gửi temperature và top_p tới provider; bạn không thể tự triển khai chúng, và không thể thấy chúng đã làm gì.
Không có sampling API phổ quát
Liên kết đến mục: Không có sampling API phổ quátMỗi provider nhận một tập con khác nhau của các điều khiển này, với khoảng khác nhau, và âm thầm bỏ qua phần còn lại. Đây không phải lời phàn nàn trừu tượng. Bất kỳ application nào cho chọn model đều phải ghi khác biệt xuống đâu đó, và file làm việc đó là bản đồ của sự không tương thích. Đây là những gì một catalogue như vậy khai báo cho một tham số duy nhất trên chín nguồn text mà nó hỗ trợ:
| declared temperature range | sources |
|---|---|
| 0 to 1 | Anthropic, Google, Meta, Cerebras, PaLM |
| 0 to 1.5 | Mistral |
| 0 to 2 | OpenAI, DeepSeek, xAI |
Từ ngữ giống nhau; thang đo thì không. “Temperature bằng 1” là phân phối chưa sửa đổi ở một nơi và mức nhiệt tối đa được phép ở nơi khác, còn một nửa catalogue không thể biểu diễn giá trị mà nửa kia xem là trung tính-cộng-một-chút. Các núm còn lại cũng lệch như vậy: các mục OpenAI, DeepSeek và xAI nhận presence và frequency penalties nhưng không có topK; các mục Google, Meta, Cerebras và PaLM nhận topK và không có penalties; Anthropic nhận topK, topP và stop sequences nhưng không có penalties; và chính xác một trong chín — Mistral — nhận seed. Gửi một tham số mà provider không triển khai thường không tạo lỗi nào cả: request thành công, núm không làm gì, và bạn kết luận setting không có tác dụng.
Và hãy để ý file như vậy là gì: một tuyên bố về API của người khác, được viết vào một ngày cụ thể, rồi sau đó không có gì xác minh. Một catalogue nói 0 đến 1 cho provider hiện đã nhận 0 đến 2 sẽ âm thầm cap mọi request.
Hai điều khiển nữa thuộc cùng họ. logprobs, khi được cung cấp, trả về log-probabilities của token được chọn và thường là vài alternative hàng đầu — cửa sổ duy nhất bạn có vào phân phối mà chương này nói tới, và nền tảng của mọi heuristic về confidence xây trên closed model. Và maximum tokens cộng stop sequences kết thúc generation mà không tham chiếu xác suất: một hard cap và một so khớp chuỗi. Cả hai lộ ra dưới dạng finish_reason từ Chương 14, nơi length nghĩa là đáp án của bạn bị cắt giữa câu bởi ngân sách, không phải được model hoàn tất.
Seed, và tính quyết định mà bạn không có
Liên kết đến mục: Seed, và tính quyết định mà bạn không cóĐặt seed và sampling trở nên tái lập. Phần đó là thật, và rất dễ kiểm chứng:
seed = 1234 " Paris\nWhat is a good geographical qualifier for describing
Paris concerning its location?\nA: Near the Mediterranean Sea"
seed = 1234 " Paris\nWhat is a good geographical qualifier for describing
Paris concerning its location?\nA: Near the Mediterranean Sea"
seed = 7 " Paris is the capital of France. The appellation of Paris is
\"Île de Paris\"."
seed = 7 " Paris is the capital of France. The appellation of Paris is
\"Île de Paris\"."Byte-identical trong cùng một seed, khác nhau giữa các seed, đúng như quảng cáo. Vậy thứ seed cố định là lần rút ngẫu nhiên ở dòng cuối của hàm sample đó — token nào được chọn khi đã có một phân phối.
Thứ nó không cố định là phân phối. Và rắc rối nằm ở đó, vì vector logits mà model tạo ra không phải một đối tượng toán học; nó là output của hàng tỷ phép cộng floating-point, và các phép cộng đó có thứ tự.
Chương 2 đã để sẵn thí nghiệm này. Cùng một triệu số float32, được cộng theo các nhóm khác nhau:
sequential 998.564270020 error vs float64: 6.393e-03
pairwise (numpy) 998.570556641 error vs float64: 1.061e-04
in 4 chunks 998.570495605 error vs float64: 1.672e-04
in 8 chunks 998.570556641 error vs float64: 1.061e-04
in 16 chunks 998.570678711 error vs float64: 1.594e-05
sequential == pairwise? False
4 chunks == 8 chunks? FalseNhìn dòng cuối. Số lượng chunk thay đổi đáp án. Đó không phải chuyện lạ của numpy; đó là cơ chế, vì khi một inference server chia một phép reduction qua nhiều hay ít đơn vị song song hơn, nó đang làm đúng việc này. Và server chia như vậy tùy theo nó đang phục vụ bao nhiêu request.
Đây là hiệu ứng đó trên chính model. Cùng prompt, cùng forward pass, khác biệt duy nhất là có bao nhiêu request khác tình cờ nằm trong batch:
20 identical forward passes, batch of 1: 20 / 20 bit-for-bit identical
the same prompt inside a batch of 2: 147,321 of 151,936 logits differ
the same prompt inside a batch of 4: 146,515 of 151,936 logits differ
the same prompt inside a batch of 8: 146,515 of 151,936 logits differ
the same prompt inside a batch of 16: 147,321 of 151,936 logits differ
largest change to any logit: 2.5e-05Chạy một mình, model hoàn toàn deterministic — hai mươi pass, giống nhau đến từng bit. Đặt prompt y hệt trong batch với các request không liên quan và 97 % logits của nó thay đổi. Không gì trong request của bạn đổi. Request của người khác đã đến.
Giờ là phần trung thực, vì chuyện này thường được kể như thể đó là kết thúc. Một thay đổi chỉ đổi output nếu hai candidate token cách nhau trong khoảng đó. Trên 717 bước generation qua mười hai prompt, khoảng cách nhỏ nhất giữa hai logits đứng đầu là — lớn hơn nhiễu một trăm lần — và không bước nào đủ gần để lật. Vậy trên model này, ở float32, trên laptop, batching làm mọi logit dịch chuyển và không đổi token nào.
Đó là mô tả điều kiện thuận lợi, không phải lời trấn an, và chỉ một thay đổi điều kiện là đủ:
same weights, same prompts, greedy decoding, no seed involved
float32 vs bfloat16: 6 of 8 answers diverge
first divergence at step 23, on average
float32: "...it is scattered and dispersed into different colors,
including blue. The blue light is scattered more than other
colors, so it appears to come from the sky."
bfloat16: "...it is scattered and scattered, causing the colors of the
sun to be scattered and scattered, creating the appearance
of a blue color."Sáu trong tám đáp án phân kỳ, và một đáp án suy giảm nặng. Bảng của Chương 2 nói vì sao: bfloat16 giữ 7 bit mantissa, nên gần độ lớn logit 16, các giá trị biểu diễn được cách nhau 0.125 — 16.0, rồi 16.125, rồi 16.25 — và làm tròn có thể dịch một logit tới 0.0625. Trong khi đó 4,7 % các bước generation đo ở trên có khoảng cách top-two dưới 0.1. Đó là toàn bộ khác biệt giữa hai thí nghiệm: trong float32, nhiễu nhỏ hơn quyết định gần nhất một trăm lần, còn trong bfloat16 thì nó cùng cỡ. Production inference chạy ở 16-bit, trên phần cứng có fused kernels và thứ tự reduction không ai hứa giữ. Việc “nhiễu số học có bỏ qua được không” là câu hỏi về precision và hardware, không phải về model.
Vậy, bốn nguyên nhân, được liệt kê như Chương 9 đã hứa:
Phép cộng floating-point không có tính kết hợp
Liên kết đến mục: Phép cộng floating-point không có tính kết hợpHộp của Chương 2. Thứ tự của một tổng thay đổi giá trị của nó, nên bất kỳ thay đổi nào trong cách một reduction được chia cũng đổi logits. Đây là nền; ba nguyên nhân còn lại là các cách thay đổi thứ tự.
Dynamic batching ghép request của bạn với người lạ
Liên kết đến mục: Dynamic batching ghép request của bạn với người lạContinuous batching, từ Chương 13, là lý do inference có giá phải chăng — và nó nghĩa là hình dạng các ma trận mà token của bạn đi qua phụ thuộc vào traffic. Đo ở trên: 147.321 logits dịch chuyển vì batch size đổi.
Mixture-of-experts routing phụ thuộc vào batch
Liên kết đến mục: Mixture-of-experts routing phụ thuộc vào batchHộp của Chương 9 đã nói rồi. Router đưa ra một lựa chọn rời rạc cho mỗi token mỗi layer, chịu giới hạn capacity theo expert được tính trên batch. Một token nếu đi một mình sẽ đến expert 7, khi có bạn đồng hành lại đến expert 12. Đây không phải sai khác do làm tròn; đây là một tập weights khác.
Model đằng sau cái tên thay đổi
Liên kết đến mục: Model đằng sau cái tên thay đổiMột version string như -latest là một con trỏ, và con trỏ có thể bị trỏ lại. Providers cũng cập nhật serving stack bên dưới một định danh version cố định. Không cái nào được công bố ở độ hạt đủ để bạn tương quan nó với việc output của chính bạn thay đổi.
Tham số seed của OpenAI trung thực về điều này theo cách duy nhất nó có thể: nó đi kèm một trường system_fingerprint xác định backend configuration, và tài liệu nói determinism là best-effort, đồng thời fingerprint đổi nghĩa là kết quả có thể khác. Hãy đọc đúng bản chất của nó — một provider nói với bạn rằng họ kiểm soát cả bốn nguyên nhân trên, bạn không kiểm soát nguyên nhân nào, và thứ duy nhất họ có thể cung cấp là cho bạn biết sau sự kiện rằng có thứ đã dịch chuyển.
Tiếp theo là gì
Liên kết đến mục: Tiếp theo là gìMọi thứ ở đây đều nói về một núm và hệ quả của nó. Lùi lại một tầng, vấn đề khó hơn xuất hiện: đối tượng chúng ta đang tinh chỉnh là một phân phối xác suất, và phân phối xác suất không có interface.
Một function call thì có. Một hàng database thì có. Một handler POST mong đợi JSON body với ba trường bắt buộc thì có, và nó sẽ từ chối mọi thứ khác. Giữa model và mọi component khác trong system của bạn là một contract mà một bên không thể đưa ra lời hứa: model sẽ tạo ra thứ gì đó, được rút từ một phân phối bạn đã định hình nhưng chưa cố định, còn code phía bên kia cần một giá trị thuộc type đã biết, nếu không nó throw.
Cây cầu giữa hai thế giới đó được xây từ vật liệu của chương này chứ không phải từ parsing và retry. Nếu một token sẽ phá structure bắt buộc, bạn không sample nó rồi hy vọng — bạn đặt logit của nó thành trước khi softmax thấy nó. Constrained decoding là một mask trên cùng vector mà chúng ta đã dành cả chương để định hình lại, và nó biến “hãy trả lời bằng JSON” từ một request thành một guarantee.
Chương 18 là contract đó: tool calling, JSON Schema, structured outputs, và cần gì để biến một deterministic system thành thứ an toàn để xây dựng bên trên một hệ probabilistic.
Nguồn và phương pháp
Liên kết đến mục: Nguồn và phương phápMọi phép đo trong chương này đến từ Qwen/Qwen2.5-0.5B-Instruct trên CPU, float32 trừ khi nói khác, với sampling được triển khai đúng như trong phần tùy chọn thay vì giao cho library. Chúng là một model nhỏ, và các giá trị cụ thể là của nó; cơ chế thì không. How to generate text with different decoding methods của Von Platen (Hugging Face, 2020) là bài viết mà bài này đo đối chiếu, và vẫn là phần giới thiệu ngắn tốt nhất về cùng chất liệu. Với phần determinism: ghi chú reproducibility của PyTorch mô tả seed sửa và không sửa được gì trên một máy đơn, tài liệu của OpenAI về seed và system_fingerprint mô tả provider có thể và không thể hứa gì, và thảo luận năm 2025 của Thinking Machines về batch-invariant kernels là diễn giải công khai rõ nhất về vì sao sửa điều này ở cấp inference-server là có thể nhưng không miễn phí.
Tài liệu tham khảo
Liên kết đến mục: Tài liệu tham khảo-
Ackley, D. H., Hinton, G. E. and Sejnowski, T. J. A Learning Algorithm for Boltzmann Machines. Cognitive Science 9(1), pp. 147–169 (1985), nơi temperature trong softmax đến từ vật lý thống kê. Hinton, G., Vinyals, O. and Dean, J., Distilling the Knowledge in a Neural Network, arXiv:1503.02531 (2015), mục 2, là nơi cùng tham số xuất hiện lại trong deep learning hiện đại — như một cách phơi bày toàn bộ phân phối của teacher, tức soft labels của Chương 13 chứ không phải sampling của chương này. ↩
-
Guo, C., Pleiss, G., Sun, Y. and Weinberger, K. Q. On Calibration of Modern Neural Networks. arXiv:1706.04599 (2017). Đừng nhầm thứ này với temperature trong chương này. Temperature scaling khớp một giá trị duy nhất trên tập validation để độ tự tin của model khớp với độ chính xác; đó là phương pháp hiệu chỉnh hậu nghiệm áp lên output của classifier. Temperature sampling là điều khiển runtime đối với cách generator rút tokens. Cùng công thức, mục đích khác nhau, và không có giá trị chung. ↩
-
Holtzman, A., Buys, J., Du, L., Forbes, M. and Choi, Y. The Curious Case of Neural Text Degeneration. arXiv:1904.09751 (2019). Giới thiệu nucleus sampling và phép đo cho thấy decoding dựa trên tối đa hóa tạo ra văn bản có profile xác suất không hề giống văn bản con người. ↩ ↩2
-
Fan, A., Lewis, M. and Dauphin, Y. Hierarchical Neural Story Generation. arXiv:1805.04833 (2018). Paper đã phổ biến top-k sampling. ↩
-
Nguyen, M. et al. Turning Up the Heat: Min-p Sampling for Creative and Coherent LLM Outputs. arXiv:2407.01082 (2024). ↩
-
Meister, C., Pimentel, T., Wiher, G. and Cotterell, R. Locally Typical Sampling. arXiv:2202.00666 (2022). ↩
-
Keskar, N. S., McCann, B., Varshney, L. R., Xiong, C. and Socher, R. CTRL: A Conditional Transformer Language Model for Controllable Generation. arXiv:1909.05858 (2019). Mục 4.1 là repetition penalty gốc — loại dùng phép chia. ↩