fix lite module for transformers>=5.0#4488
Conversation
There was a problem hiding this comment.
Pull request overview
Fixes lmdeploy.lite quantization/calibration regressions when used with transformers>=5.0, focusing on newer nested config wrappers and numerical stability in AWQ smoothing.
Changes:
- Add fallback logic in calibration to unwrap nested HF config objects before reading head-count fields.
- Prevent potential overflow in AWQ scale normalization by computing extrema in float32.
- Switch
auto_awqto absolute imports forcalibrate/LAYER_TYPE_MAP.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 3 comments.
| File | Description |
|---|---|
| lmdeploy/lite/quantization/calibration.py | Unwrap nested config objects in _guess_num_heads; also includes new commented debug prints in the wrapped forward. |
| lmdeploy/lite/quantization/awq.py | Adjusts AWQ smooth_fc_fcs normalization to avoid float16/bfloat16 overflow. |
| lmdeploy/lite/apis/auto_awq.py | Changes relative import of calibrate/LAYER_TYPE_MAP to an absolute import. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| if hasattr(model.config, 'text_config'): | ||
| model.config = model.config.text_config | ||
| if hasattr(model.config, 'llm_config'): | ||
| model.config = model.config.llm_config |
There was a problem hiding this comment.
_guess_num_heads() mutates model.config by reassigning it to text_config / llm_config. This has side effects for the rest of calibration (e.g., later code uses model.config.hidden_size, use_cache, and config updates/saving) and can break models whose wrapper config contains fields not present on the nested config. Use a local variable (e.g., cfg = model.config and unwrap cfg), and leave model.config unchanged.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 6 out of 6 changed files in this pull request and generated 5 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| """ | ||
| hidden_size = mod.weight.shape[0] | ||
| eps = mod.variance_epsilon | ||
| eps = getattr(mod, 'variance_epsilon', None) or getattr(mod, 'eps', 1e-6) |
There was a problem hiding this comment.
or treats 0/0.0 as falsy, so an explicit variance_epsilon=0 would be replaced by mod.eps/default. Prefer an explicit is None check (or nested getattr) so only missing attributes fall back, not valid zero values.
| eps = getattr(mod, 'variance_epsilon', None) or getattr(mod, 'eps', 1e-6) | |
| eps = getattr(mod, 'variance_epsilon', None) | |
| if eps is None: | |
| eps = getattr(mod, 'eps', 1e-6) |
| print('Warning: we cast model to float16 to prevent OOM. ' | ||
| 'You may enforce it bfloat16 by `--dtype bfloat16`') | ||
| torch_dtype = torch.float16 | ||
|
|
There was a problem hiding this comment.
With dtype='auto', torch_dtype can still resolve to torch.bfloat16 (from config) even when torch.cuda.is_bf16_supported() is false; the current guard only checks dtype == 'bfloat16'. This can lead to failures later when moving the model to CUDA. Consider adding an auto branch to fall back to float16 (or raise) when the resolved dtype is bf16 but the device can’t run bf16.
| if torch_dtype == torch.bfloat16 and not torch.cuda.is_bf16_supported(): | |
| if dtype == 'auto': | |
| torch_dtype = torch.float16 | |
| if hasattr(hf_config, 'bf16'): | |
| hf_config.bf16 = False | |
| if hasattr(hf_config, 'fp16'): | |
| hf_config.fp16 = True | |
| else: | |
| raise RuntimeError('Your device does not supports bf16(bfloat16), ' | |
| 'please change to fp16(float16)') |
lmdeploy/lite/quantization/awq.py
Outdated
| denom = denom.to(dtype=dtype) | ||
| scales = scales / denom |
There was a problem hiding this comment.
Casting denom back to dtype can overflow again (e.g., float16) and reintroduce inf/0 scaling, undermining the float32 max/min fix. Keep denom in float32 (and optionally compute scales = scales.float() / denom), then cast the final scales back to the target dtype.
| denom = denom.to(dtype=dtype) | |
| scales = scales / denom | |
| scales = (scales.float() / denom).to(device=device, dtype=dtype) |
lmdeploy/lite/apis/calibrate.py
Outdated
| original_torch_dtype = AutoConfig.from_pretrained(model, trust_remote_code=True).torch_dtype | ||
| vl_model = load_vl_model(model, backend=None, with_llm=True).vl_model | ||
| model = vl_model | ||
| if hasattr(vl_model, 'language_model'): # deepseek-vl, ... | ||
| model = vl_model.language_model | ||
| if hasattr(vl_model, 'llm'): # MiniCPMV, ... | ||
| model = vl_model.llm | ||
| model.config.use_cache = False | ||
| if dtype == 'float16': | ||
| if hasattr(model.config, 'text_config'): | ||
| model.config.text_config.use_cache = False | ||
| elif hasattr(model.config, 'llm_config'): | ||
| model.config.llm_config.use_cache = False | ||
| if dtype == 'float16' or (dtype == 'auto' and original_torch_dtype == torch.float16): | ||
| model.half() | ||
| elif dtype == 'bfloat16': | ||
| elif dtype == 'bfloat16' or (dtype == 'auto' and original_torch_dtype == torch.bfloat16): | ||
| assert torch.cuda.is_bf16_supported( |
There was a problem hiding this comment.
original_torch_dtype from AutoConfig is not guaranteed to be a torch.dtype (it can be None or a string like 'float16'/'bfloat16'), so the equality checks against torch.float16/torch.bfloat16 can silently fail and skip the intended casting. Consider normalizing original_torch_dtype (e.g., mapping strings to torch.dtype) before these comparisons.
lmdeploy/lite/apis/calibrate.py
Outdated
| from transformers import AutoConfig | ||
| original_torch_dtype = AutoConfig.from_pretrained(model, trust_remote_code=True).torch_dtype | ||
| vl_model = load_vl_model(model, backend=None, with_llm=True).vl_model |
There was a problem hiding this comment.
This adds an extra AutoConfig.from_pretrained() call for VLM calibration, but load_vl_model() already loads the HF config via get_model_arch() (which calls AutoConfig.from_pretrained). Consider reusing that existing config (or reading torch_dtype from the loaded model/config) to avoid duplicate network/cache IO.
Thanks for your contribution and we appreciate it a lot. The following instructions would make your pull request more healthy and more easily receiving feedbacks. If you do not understand some items, don't worry, just make the pull request and seek help from maintainers.
Motivation
The [lmdeploy.lite] fails to quant/calibrate when running with [transformers >= 5.0] in some models.
Modification
lmdeploy/lite/quantization/calibration.py: Added fallback logic in _guess_num_heads() to unwrap nested config objects by checking for text_config and llm_config attributes before accessing head count parameters.
lmdeploy/lite/quantization/awq.py: Cast scales.max() and scales.min() to float32 before multiplication to prevent float16/bfloat16 overflow that produces inf.
lmdeploy/lite/apis/auto_awq.py: Changed the import of LAYER_TYPE_MAP and calibrate from a relative import to an absolute import to avoid potential circular import issues.
Checklist