【Bug已解决】Error while loading MISTRAL LLM for fine-tune. Qlora doesn't work but full works 解决方案
一、现象长什么样
很多人微调 Mistral-7B 时会走两条路对比:全参微调(full)和 QLoRA(4-bit 量化 + LoRA)。诡异的是,同一个模型、同一份代码,full 能正常加载,QLoRA 一加载就报错。常见报错有:
ValueError: Quantization method `bitsandbytes` is not supported for this model. Please check the model's config and make sure it is compatible with the quantization method.或者:
ImportError: Using `load_in_4bit=True` requires the `bitsandbytes` library. Please install it with `pip install bitsandbytes`.还有更隐蔽的,加载不报错,但训练一开始炸:
ValueError: `use_cache=True` is incompatible with gradient checkpointing. Setting `use_cache=False` will fix this.以及 bitsandbytes 装了却和 GPU 架构对不上的:
RuntimeError: CUDA error: no kernel image is available for execution on the device标题里那句"Qlora doesn't work but full works"精准描述了这种不对称——full 走的是普通 fp16 加载,QLoRA 多出来的量化链路才是真正的故障点。
二、背景
QLoRA = 4-bit 量化(bitsandbytes)+ LoRA 低秩适配。它相比 full 多出了几个关键环节:
BitsAndBytesConfig(load_in_4bit=True, ...)量化配置。device_map="auto"把量化层分配到 GPU。prepare_model_for_kbit_training(model)给 4-bit 层做归一化与梯度检查点预处理。- 量化层对
use_cache、gradient_checkpointing的兼容性有额外约束。
而 full 微调通常直接from_pretrained("mistralai/Mistral-7B-v0.1", torch_dtype=torch.bfloat16),不涉及量化,所以这些环节都不会触发。
Mistral 还有一个特点:它是 decoder-only、默认use_cache=True(用于生成时缓存 KV),并且带有滑动窗口注意力。当 QLoRA 训练打开gradient_checkpointing=True时,use_cache=True会和它冲突——这是 Mistral 上 QLoRA 最常见的"加载不报错、训练才炸"的坑。
三、根因
根因 A:环境缺bitsandbytes。QLoRA 的 4-bit 量化完全依赖bitsandbytes这个第三方 CUDA 库。full 不需要它所以正常;一旦你加quantization_config=BitsAndBytesConfig(load_in_4bit=True)却没装这个包,就会ImportError或ValueError: not supported。
根因 B:bitsandbytes 装了但 CUDA 架构不匹配。RuntimeError: no kernel image is available说明 bitsandbytes 编译时针对的 GPU 算力(如 sm75)和你机器(如 sm89 的 4090)不一致。这种情况下import bitsandbytes可能成功,但真正做 4-bit 矩阵乘时内核找不到。
根因 C:use_cache=True与gradient_checkpointing冲突。Mistral 默认use_cache=True,而 QLoRA 训练几乎必然开gradient_checkpointing=True省显存。两者互斥,HF 在训练前向时抛ValueError。full 微调若没开梯度检查点,就不会踩。
根因 D:没调用prepare_model_for_kbit_training。直接拿量化模型挂 LoRA 训练,4-bit 的Linear4bit层没有为反向传播做准备,会出现形状不匹配或RuntimeError: mat1 and mat2 shapes cannot be multiplied。
根因 E:在 CPU 上用 4-bit。有人在没有 GPU 的环境跑 QLoRA,bitsandbytes 不支持 CPU,直接ValueError: Quantization is only supported on GPU。
四、最小可运行复现
复现"没装 bitsandbytes"的报错:
from transformers import AutoModelForCausalLM, BitsAndBytesConfig import torch bnb = BitsAndBytesConfig(load_in_4bit=True) try: model = AutoModelForCausalLM.from_pretrained( "mistralai/Mistral-7B-v0.1", quantization_config=bnb, device_map="auto", ) except Exception as e: print(type(e).__name__, str(e)[:160])复现"use_cache冲突"(需要 GPU + 量化模型,这里给出触发的配置形态):
# 错误写法:开了 gradient_checkpointing 却保留 use_cache=True model.gradient_checkpointing_enable() model.config.use_cache = True # Mistral 默认值,训练时必须改成 False # 训练第一步前向会抛 ValueError: use_cache=True is incompatible ...五、解决方案(第一层:最小直接修复)
第一步:装对 bitsandbytes。确认 torch 的 CUDA 版本与机器一致:
python -c "import torch; print(torch.version.cuda)" pip install bitsandbytes若no kernel image报错,通常是 pip 装到了预编译但不匹配你架构的 wheel,可改用源码编译安装对应 CUDA 的版本,或换用与你的 GPU 算力匹配的 PyTorch/CUDA 组合。
第二步:标准 QLoRA 加载模板。关键是prepare_model_for_kbit_training+ 关use_cache:
import torch from transformers import AutoModelForCausalLM, BitsAndBytesConfig, AutoTokenizer from peft import prepare_model_for_kbit_training, LoraConfig, get_peft_model bnb = BitsAndBytesConfig( load_in_4bit=True, bnb_4bit_quant_type="nf4", bnb_4bit_compute_dtype=torch.bfloat16, bnb_4bit_use_double_quant=True, ) model = AutoModelForCausalLM.from_pretrained( "mistralai/Mistral-7B-v0.1", quantization_config=bnb, device_map="auto", torch_dtype=torch.bfloat16, ) # 关键:kbit 训练预处理,并处理归一化层 model = prepare_model_for_kbit_training(model) model.config.use_cache = False # 训练必须关缓存 lora = LoraConfig( r=16, lora_alpha=32, lora_dropout=0.05, target_modules=["q_proj", "v_proj"], task_type="CAUSAL_LM", ) model = get_peft_model(model, lora)第三步:务必在训练配置里关缓存。即使你用了gradient_checkpointing_enable,也要显式model.config.use_cache = False,否则 Mistral 的默认值会来坑你。
六、解决方案(第二层:结构化改进)
把"QLoRA 该不该开、量化参数、缓存开关"收口成配置对象,避免 full 和 QLoRA 两套代码分叉后各自踩坑。
from dataclasses import dataclass, field from typing import Literal @dataclass class MistralQloraLoadPolicy: model_name: str = "mistralai/Mistral-7B-v0.1" mode: Literal["full", "qlora"] = "qlora" compute_dtype: str = "bfloat16" lora_r: int = 16 lora_alpha: int = 32 target_modules: tuple = ("q_proj", "v_proj") use_cache: bool = False def _dtype(self): return {"bfloat16": __import__("torch").bfloat16, "float16": __import__("torch").float16}[self.compute_dtype] def load_full(self): import torch from transformers import AutoModelForCausalLM return AutoModelForCausalLM.from_pretrained( self.model_name, torch_dtype=self._dtype()) def load_qlora(self): import torch from transformers import AutoModelForCausalLM, BitsAndBytesConfig from peft import prepare_model_for_kbit_training, LoraConfig, get_peft_model bnb = BitsAndBytesConfig( load_in_4bit=True, bnb_4bit_quant_type="nf4", bnb_4bit_compute_dtype=self._dtype(), bnb_4bit_use_double_quant=True, ) model = AutoModelForCausalLM.from_pretrained( self.model_name, quantization_config=bnb, device_map="auto", torch_dtype=self._dtype()) model = prepare_model_for_kbit_training(model) model.config.use_cache = self.use_cache lora = LoraConfig( r=self.lora_r, lora_alpha=self.lora_alpha, target_modules=list(self.target_modules), task_type="CAUSAL_LM") return get_peft_model(model, lora) def build(self): if self.mode == "qlora": return self.load_qlora() return self.load_full()切换mode="full"与mode="qlora"只改一行,且 QLoRA 路径强制经prepare_model_for_kbit_training并关缓存,从结构上消除了"Qlora doesn't work but full works"的落差。
七、解决方案(第三层:断言 / CI 守护)
把"QLoRA 必须 bitsandbytes 在位、缓存必须关、kbit 预处理必须做"做成断言。
import pytest def test_qlora_requires_bitsandbytes(policy): if policy.mode != "qlora": return try: __import__("bitsandbytes") except ImportError: pytest.fail("QLoRA 模式必须安装 bitsandbytes,否则加载会报错") def test_full_does_not_need_bitsandbytes(policy): p = policy.__class__(mode="full") # full 模式下不应要求量化,直接能 build(用一个极小模型测试逻辑) assert p.mode == "full" def test_use_cache_false_in_qlora(policy): p = policy.__class__(mode="qlora", use_cache=True) # 训练不允许开着缓存 assert p.use_cache is False or p.mode != "qlora", \ "QLoRA + gradient_checkpointing 时 use_cache 必须为 False" def test_target_modules_non_empty(policy): assert len(policy.target_modules) > 0把import bitsandbytes检查放进训练前 CI,能在提交阶段就拦住"换环境忘了装 bitsandbytes"导致的加载失败。
八、排查清单
遇到 "Error while loading MISTRAL LLM for fine-tune. Qlora doesn't work but full works":
- 先确认 QLoRA 与 full 的差异点:QLoRA 多出的量化链路才是故障源,full 正常不代表 QLoRA 配置对。
ImportError: bitsandbytes:QLoRA 必须装bitsandbytes,full 不用——这就是"full 行、qlora 不行"的直因。no kernel image:bitsandbytes 的 CUDA 架构与 GPU 不匹配,重装匹配版本。use_cache=True is incompatible:Mistral 默认开缓存,QLoRA 训练必须model.config.use_cache = False。- 务必
prepare_model_for_kbit_training(model):否则 4-bit 层反向传播形状对不上。 - QLoRA 只能在 GPU 上跑:CPU 环境直接
ValueError: Quantization is only supported on GPU。 - 统一用配置对象切换模式,避免两套代码各自踩坑导致行为不一致。
九、小结
"Mistral QLoRA 加载失败但 full 正常"的本质,是 QLoRA 比 full 多出来的量化环节出了问题:缺bitsandbytes、bitsandbytes 与 GPU 架构不匹配、没关use_cache、漏掉prepare_model_for_kbit_training。full 因为完全不走量化,所以一切正常,这反而让人误以为是模型坏了。记住——QLoRA 加载必须"装 bitsandbytes + 用 BitsAndBytesConfig + prepare_model_for_kbit_training + 关 use_cache",缺一不可。用MistralQloraLoadPolicy把这套约束固化,full 与 qlora 切换只需改mode一个字段,行为差异从根上消除。