Problem — Token‑limit failures during language‑model evaluation on AWS EC2 GPU instances
When running a Hugging Face transformers evaluation pipeline on an EC2 GPU instance (e.g., p3.2xlarge, g5.12xlarge), the script aborts with errors such as:
ValueError: Token indices sequence length is longer than the model maximum sequence length (max_length=2048).
or
RuntimeError: CUDA out of memory. Tried to allocate 12.3 GiB on GPU 0.
... (subsequent handling) ...
Error: token limit exceeded – input length 4096 exceeds max allowed 2048 tokens (model: Llama‑2‑7B).
Symptoms observed in production:
- Evaluation jobs terminate after a few minutes.
- GPU memory usage spikes to 100 % just before the crash.
- Logs contain both
CUDA out of memoryandtoken limit exceededmessages. - Increasing batch size or adding more instances does not resolve the failure.
Root Cause Analysis
The failure is a combination of two tightly coupled constraints:
- Model‑defined maximum position embeddings. Every transformer model has a hard‑coded
max_position_embeddings(e.g., 2048 for Llama‑2‑7B, 512 for BERT‑large). The tokenizer will produce a token sequence whose length must not exceed this value, otherwise aValueErroris raised. - GPU memory required for the attention matrix. For a sequence of length L, the self‑attention operation allocates an L × L matrix per layer. On a
p3.2xlarge(16 GiB GPU) a 2048‑token sequence already consumes >10 GiB of memory. Any increase (e.g., 3000 tokens) exceeds the available memory, triggering a CUDA OOM. The OOM is often caught by the evaluation harness, which then reports a generic “token limit exceeded” error.
Evidence from the official AWS EC2 Instance Types shows that p3.2xlarge provides 16 GiB GPU memory, while g5.12xlarge provides 48 GiB. Even the larger p4d.24xlarge (1.1 TiB) can hit the positional‑embedding ceiling when evaluating very long documents (>3000 tokens) as reported in the Reddit discussion.
Thus, the root cause is input sequences longer than the model’s positional embedding limit, combined with insufficient GPU memory to allocate the required attention tensors for those sequences.
Investigation & Debugging Steps
- Confirm the exact token length of the failing input.
from transformers import AutoTokenizer
tokenizer = AutoTokenizer.from_pretrained("meta-llama/Llama-2-7b-hf")
text = open("sample.txt").read()
tokens = tokenizer.encode(text, return_tensors="pt")
print("Token count:", tokens.shape[1])
Expected output (example): Token count: 4096
- Check model configuration for
max_position_embeddings.
from transformers import AutoConfig
config = AutoConfig.from_pretrained("meta-llama/Llama-2-7b-hf")
print("max_position_embeddings:", config.max_position_embeddings)
Typical output: max_position_embeddings: 2048
- Inspect GPU memory usage before the failure.
nvidia-smi --query-gpu=memory.used,memory.total --format=csv
# Example output:
# 12234 MiB, 16384 MiB
- Search logs for OOM and token‑limit messages.
journalctl -u evaluation.service -n 200 | grep -E "CUDA out of memory|token limit"
# Sample excerpt:
# RuntimeError: CUDA out of memory. Tried to allocate 12.3 GiB on GPU 0.
# Error: token limit exceeded – input length 4096 exceeds max allowed 2048 tokens
- Validate that the EC2 instance type matches the expected GPU memory.
curl -s http://169.254.169.254/latest/meta-data/instance-type
# Returns: p3.2xlarge
Solution – Bringing the Evaluation Pipeline Within Limits
The fix consists of three coordinated actions:
1. Truncate or chunk inputs to respect max_position_embeddings
def safe_tokenize(text, tokenizer, max_len):
tokens = tokenizer.encode(text, add_special_tokens=True)
if len(tokens) > max_len:
# Simple truncation
tokens = tokens[:max_len]
return torch.tensor(tokens).unsqueeze(0)
# Usage
max_len = config.max_position_embeddings
input_ids = safe_tokenize(text, tokenizer, max_len)
2. Enable memory‑efficient attention or off‑loading
For PyTorch 2.x, torch.compile with attention="flash" reduces the attention matrix footprint. Alternatively, DeepSpeed ZeRO‑2 offloads optimizer states and activations to host RAM.
# Using flash attention (requires CUDA 11.8+)
model = AutoModelForCausalLM.from_pretrained(
"meta-llama/Llama-2-7b-hf",
torch_dtype=torch.float16,
device_map="auto",
attn_implementation="flash_attention_2"
)
Or with DeepSpeed:
# deepspeed_config.json
{
"zero_optimization": {
"stage": 2,
"offload_param": {"device": "cpu"},
"offload_optimizer": {"device": "cpu"}
},
"gradient_checkpointing": true
}
3. Scale up the GPU instance or use Elastic Inference
If the workload must process sequences near the positional limit (e.g., 3000 tokens for research), move to a larger instance (p4d.24xlarge) or attach AWS Elastic Inference to a smaller instance to increase effective memory for inference.
Before & After Comparison
| Aspect | Before | After |
|---|---|---|
| Input handling | Raw text → tokenizer → OOM → token‑limit error | Safe tokenization with truncation to max_position_embeddings |
| Attention implementation | Standard dense attention (L × L matrix) | Flash attention (reduced memory) or DeepSpeed ZeRO‑2 |
| Instance type | p3.2xlarge (16 GiB) |
p4d.24xlarge (1.1 TiB) or p3.2xlarge + Elastic Inference |
Verification – Confirming the Fix
- Run the same evaluation script with a known‑long input.
- Check that
nvidia-smireports no OOM and GPU memory stays below 80 %. - Confirm that the log no longer contains “token limit exceeded”.
# Expected log excerpt
INFO:root:Processing document 1/10 – token count 2048
INFO:root:Inference completed in 0.87 s, GPU memory used 12.1 GiB
Run a quick sanity test:
python evaluate.py --input sample.txt --max-length 2048
# Should exit with code 0
echo $?
# 0
Prevention – Operational Guardrails
- Input validation layer. Add a pre‑processor that rejects or automatically chunks any request exceeding
model.config.max_position_embeddings. - Monitoring. Export
torch.cuda.memory_allocatedandtorch.cuda.memory_reservedas Prometheus metrics; alert when usage exceeds 85 %. - Instance‑type policy. Enforce a minimum GPU memory threshold (e.g., 32 GiB) for any evaluation job that uses models with >2 K positional embeddings.
- Version pinning. Use the Deep Learning AMI version that matches the CUDA driver required for flash attention (see AWS Deep Learning AMI guide).
- Batch size & dynamic padding. Keep
batch_sizelow (1–2) for long sequences and enablepadding=Falseto avoid unnecessary memory allocation.
Related Topic Hub: Cloud Infrastructure Troubleshooting Hub
FAQ
- Why does the error say “token limit exceeded” after a CUDA OOM?
The evaluation harness catches the OOM, then re‑raises a higher‑levelValueErrorthat reports the root cause (input length >max_position_embeddings). Both conditions are present, but the generic message masks the underlying memory pressure. - Can I increase
max_position_embeddingswithout retraining?
Only by fine‑tuning the model with a larger positional embedding matrix (e.g., usingmodel.resize_position_embeddings(new_len)) and re‑initializing the embeddings. This requires additional GPU memory and is not a quick fix for production pipelines. - Is Elastic Inference suitable for large language models?
EI can off‑load matrix multiplications for inference, reducing GPU memory usage, but it is limited to models ≤ 2 B parameters. For 7 B+ models, scaling the GPU instance or using model parallelism (e.g.,torch.distributed) is recommended. - How do I know which attention implementation is active?
Inspectmodel.config.attn_implementationat runtime. It should reportflash_attention_2when flash attention is enabled. If it showseager, the memory‑heavy dense attention is still in use. - What monitoring metric best predicts an upcoming token‑limit failure?
Track the maximum token length observed per request (e.g.,max_input_tokens) alongside GPU memory usage. A sudden spike inmax_input_tokensthat approachesmodel.config.max_position_embeddingsis a strong predictor.