Skip to content

Commit 05f0d24

Browse files
Add a QLoRA test to the GRPO test suite
1 parent 83879a2 commit 05f0d24

1 file changed

Lines changed: 43 additions & 0 deletions

File tree

tests/test_grpo_trainer.py

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -797,6 +797,49 @@ def test_train_peft_config(self):
797797
elif "base_layer" not in n: # We expect the peft params to be different (except for the base layer)
798798
assert not torch.equal(param, new_param), f"Parameter {n} has not changed."
799799

800+
@require_peft
801+
@require_bitsandbytes
802+
def test_train_peft_and_quantization(self):
803+
dataset = load_dataset("trl-internal-testing/zen", "standard_prompt_only", split="train")
804+
805+
training_args = GRPOConfig(
806+
output_dir=self.tmp_dir,
807+
learning_rate=0.1, # use higher lr because gradients are tiny and default lr can stall updates
808+
per_device_train_batch_size=3, # reduce the batch size to reduce memory usage
809+
num_generations=3, # reduce the number of generations to reduce memory usage
810+
max_completion_length=8, # reduce the completion length to reduce memory usage
811+
report_to="none",
812+
)
813+
quantization_config = BitsAndBytesConfig(
814+
load_in_4bit=True,
815+
bnb_4bit_use_double_quant=True,
816+
bnb_4bit_quant_type="nf4",
817+
bnb_4bit_compute_dtype=torch.bfloat16,
818+
)
819+
trainer = GRPOTrainer(
820+
model="trl-internal-testing/tiny-Qwen2ForCausalLM-2.5", # identifier, so that the trainer quantizes it
821+
reward_funcs="trl-internal-testing/tiny-Qwen2ForSequenceClassification-2.5",
822+
args=training_args,
823+
train_dataset=dataset,
824+
quantization_config=quantization_config,
825+
peft_config=LoraConfig(),
826+
)
827+
828+
previous_trainable_params = {n: param.clone() for n, param in trainer.model.named_parameters()}
829+
830+
trainer.train()
831+
832+
assert trainer.state.log_history[-1]["train_loss"] is not None
833+
834+
# Check that the peft params have changed, and that they are cast to bfloat16, as recommended by the QLoRA
835+
# paper. The base model params are not checked: bitsandbytes casts the biases of a Linear4bit in-place during
836+
# the forward pass, so some of them change in a way that is unrelated to training.
837+
for n, param in previous_trainable_params.items():
838+
new_param = trainer.model.get_parameter(n)
839+
if "lora" in n: # We expect the peft params to be different
840+
assert param.dtype == torch.bfloat16, f"Parameter {n} is not in bfloat16."
841+
assert not torch.equal(param, new_param), f"Parameter {n} has not changed."
842+
800843
@require_peft
801844
def test_liger_kernel_with_peft_lm_head_raises(self):
802845
# The Liger fused GRPO loss reads `lm_head.weight` directly, so a LoRA adapter on `lm_head` is silently

0 commit comments

Comments
 (0)