unsloth/tests/utils/test_batched_leftpad_generation_gpu.py
Daniel Han a6dc10dad2
Reduce and tighten comments and docstrings across the test suite (#6429)
* Reduce and tighten comments and docstrings in tests

Shorten verbose comments and docstrings across the test suite without
changing any test logic. Remove narration that restates the next line,
collapse long module and test docstrings to a single line, and drop banner
separators. Keep regression context (issue and PR references, run ids),
skip reasons, mocking and timing rationale, license headers, lint and type
directives, and commented-out code.

Comments and docstrings only: an AST signature check confirms no code,
assertions, or string literals changed, and the suite byte-compiles cleanly.

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2026-06-18 01:07:09 -07:00

101 lines
3.5 KiB
Python

"""End-to-end GPU guard for batched left-padded generation (issues #1066, #3699).
Greedy generation in a left-padded batch must match solo batch-size-1
generation for the first PREFIX_TOKENS tokens (the bug makes padded rows
diverge into garbage immediately; a full-length match would be flaky due to
benign batch-numerics tie-flips deep in the sequence) and must not be
gibberish. Skipped without CUDA. Run: `python -m pytest
tests/utils/test_batched_leftpad_generation_gpu.py -v`.
"""
import pytest
import torch
cuda_available = torch.cuda.is_available()
pytestmark = pytest.mark.skipif(not cuda_available, reason = "requires a CUDA GPU")
MODEL_NAME = "unsloth/Qwen2.5-0.5B-Instruct"
MAX_NEW_TOKENS = 32
PREFIX_TOKENS = 16
PROMPTS = [
"Give me a short introduction to large language model.",
"Here is an experiment log: "
+ " ".join(f"run {i} completed with stable throughput and no anomalies;" for i in range(1, 41))
+ " In one sentence, what is the overall conclusion?",
]
@pytest.fixture(scope = "module")
def model_and_tokenizer():
from unsloth import FastLanguageModel
model, tokenizer = FastLanguageModel.from_pretrained(
model_name = MODEL_NAME,
max_seq_length = 2048,
load_in_4bit = True,
)
FastLanguageModel.for_inference(model)
tokenizer.padding_side = "left"
if tokenizer.pad_token_id is None:
tokenizer.pad_token_id = tokenizer.eos_token_id
return model, tokenizer
def _chat(tokenizer, prompt):
return tokenizer.apply_chat_template(
[{"role": "user", "content": prompt}],
tokenize = False,
add_generation_prompt = True,
)
def _generate(model, tokenizer, texts):
inputs = tokenizer(texts, return_tensors = "pt", padding = True, add_special_tokens = False).to(
"cuda"
)
with torch.inference_mode():
out = model.generate(
**inputs,
max_new_tokens = MAX_NEW_TOKENS,
do_sample = False,
temperature = None,
top_p = None,
top_k = None,
use_cache = True,
pad_token_id = tokenizer.pad_token_id,
)
suffixes = out[:, inputs["input_ids"].shape[1] :]
return [row.tolist() for row in suffixes]
def _looks_gibberish(text):
if not text.strip():
return True
exclam = text.count("!") / max(len(text), 1)
nonascii = sum(1 for c in text if ord(c) > 0x2FFF) / max(len(text), 1)
return exclam > 0.3 or nonascii > 0.5
def test_batched_leftpad_matches_solo_generation(model_and_tokenizer):
model, tokenizer = model_and_tokenizer
texts = [_chat(tokenizer, p) for p in PROMPTS]
solo = [_generate(model, tokenizer, [t])[0] for t in texts]
batched = _generate(model, tokenizer, texts)
for i, prompt in enumerate(PROMPTS):
solo_text = tokenizer.decode(solo[i], skip_special_tokens = True)
batch_text = tokenizer.decode(batched[i], skip_special_tokens = True)
assert batched[i][:PREFIX_TOKENS] == solo[i][:PREFIX_TOKENS], (
f"prompt {i} ({prompt[:30]!r}...) diverged from solo generation "
f"within the first {PREFIX_TOKENS} tokens inside a left-padded "
"batch; batched left-padded generation is broken again "
f"(issues #1066, #3699).\n"
f"solo : {solo_text!r}\nbatched: {batch_text!r}"
)
assert not _looks_gibberish(batch_text), (
f"prompt {i} produced gibberish in a left-padded batch "
f"(issues #1066, #3699): {batch_text!r}"
)