Keep input_tokens on the model device in generate()#1509
Open
danielxmed wants to merge 1 commit into
Open
Conversation
generate() bound input_tokens to the original input tensor before input = input.to(device), so token inputs living on a different device than the model (e.g. CPU tokens with cfg.device=cuda) crashed with RuntimeError: Expected all tensors to be on the same device when concatenated with sampled tokens (freq-penalty sampling path and the final output concat). Re-alias input_tokens after the move and add a CUDA-gated regression test for the explicit cross-device case. Fixes TransformerLensOrg#1508 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description
HookedTransformer.generate()crashes on CUDA machines when the input is a token tensor on a different device than the model (e.g. CPU tokens passed to a model whosecfg.deviceis"cuda", the default when CUDA is available):Root cause:
input_tokensis bound to the original input tensor beforeinput = input.to(device)moves the input to the model's device, so it keeps pointing at the original CPU tensor. Sampled tokens are produced on the model's device, sotorch.cat((input_tokens, ...))mixes devices — both in the freq-penalty sampling path and in the final output concatenation (the latter also hitsdo_sample=False). The ordering came in with #999.Fix: after
input = input.to(device), re-aliasinput_tokens = input(guarded byinput_tokens is not None, which is exactly the str/tokens input case). Re-aliasing rather than calling.to(device)again avoids a second host-to-device copy of the same data.This is easiest to hit with tokenizer-free models (algorithmic tasks), which is why 4 of the 6 existing tests in
tests/unit/test_generate_no_tokenizer.pyfail on any CUDA-visible machine at currentdevwhile passing on CPU-only CI. With this fix they pass on both.generate_stream()andTransformerBridge.generate()are not affected (both already move their token tensor before use), so no Bridge mirroring is needed.Tests: added
test_generate_cpu_tokens_cuda_model, a CUDA-gated (skipif) regression test that explicitly passes CPU tokens to a CUDA model and covers both the sampling and greedy paths. Verified it fails without the fix and passes with it.Ran locally on a CUDA machine (RTX 5070, torch 2.10.0+cu128, Python 3.12):
pytest tests/unit/test_generate_no_tokenizer.py— 4 failed / 2 passed before, 7 passed afterpytest tests/unit -m "not slow"— passesmypy transformer_lens/HookedTransformer.py,black/isort/pyclnchecks — cleanFixes #1508
Type of change
Checklist: