[Python] Why the outputs while training and testing are different?

Discussão em 'Python' iniciado por Stack, Abril 8, 2025.

  1. Stack

    Stack Membro Participativo

    I tried fine-tuning the LLaMA-2-7B model using the FreedomIntelligence/medical-o1-reasoning-SFT dataset in an instruction-answer format. For now, I'm intentionally overfitting the dataset to observe the training flow.

    After training, when I run inference immediately, the model behaves as expected. However, when I load the LoRA-adapted model later for testing, the output is different from what I previously saw. Specifically, the outputs from the test_model function (after training) and the test_merged_model function (after loading the model later) do not match.

    First output (during training)


    > <s> [INST] <<SYS>> You are a helpful assistant. <</SYS>> user A
    > patient with psoriasis was treated with systemic steroids, and upon
    > discontinuation of the treatment, developed generalized pustules all
    > over the body. What is the most likely cause of this condition?
    > assistant [/INST] <think>think I'm thinking about this patient with
    > psoriasis who's been on systemic steroids. Okay, so psoriasis is this
    > chronic skin condition that can be pretty stubborn and flares up now
    > and then. They stopped their steroid treatment, and then suddenly
    > there are these generalized pustules appearing all over the place.
    > What's going on here?
    >
    > Alright, pustules are like little bumps that are filled with pus—they
    > can be a sign of different skin issues, but in someone with psoriasis,
    > a major change like stopping treatment can really mess with things.
    > Steroids are powerful at tamping down inflammation and managing the
    > immune system. When you take someone off steroids, particularly
    > suddenly, the body can react in unexpected ways. It's like the immune
    > system gets a bit confused and overreacts—a rebound effect.
    >
    > Oh, right, I remember reading that systemic steroids can sometimes
    > mask the severity of psoriasis, keeping it under control. But pulling
    > those steroids too fast can allow the psoriasis to come back stronger,
    > even shifting into another form. This sounds familiar—there's a type
    > called pustular psoriasis. If I recall, that's when the psoriasis
    > turns into this more extreme version with pustules.
    >
    > So connecting the dots: it's likely that stopping the steroids led to
    > a rebound and we might be seeing a case of generalized pustular
    > psoriasis. That makes sense given the scenario—I've seen this
    > mentioned before where physicians need to be cautious about how they
    > taper steroids off in psoriasis patients to prevent exactly this kind
    > of escalation. It fits pretty well with what I'd expect based on what
    > I know about steroid withdrawal and psoriasis flare-ups.</think>
    > <answer>answer The development of generalized pustules in a patient
    > with psoriasis following the discontinuation of systemic steroid
    > treatment is most likely due to a condition called generalized
    > pustular psoriasis (GPP). This condition can occur as a rebound effect
    > when systemic steroids are suddenly stopped or rapidly tapered down.
    > In patients with psoriasis, steroids can suppress the immune system
    > and mitigate symptoms. When these drugs are withdrawn too quickly, the
    > immune system can overreact, leading to more severe symptoms,
    > including the transformation of psoriasis into a pustular form.
    > Careful management and slow tapering of steroids are critical in
    > psoriasis patients to prevent such exacerbations.</answer></s>

    Second output (after training, loading lora model)


    > <s>[INST]<<SYS>>You are a helpful assistant.<</SYS>> user A patient
    > with psoriasis was treated with systemic steroids, and upon
    > discontinuation of the treatment, developed generalized pustules all
    > over the body. What is the most likely cause of this condition?
    > assistant [/INST]<think>thinkink I'thinkisinkis this patient with
    > psoriasis who's been on systemic steroids going on systemic steroid
    > treatment, and then. What are pretty cause. They stopped their steroid
    > treatment, and then. What's going on now and then. What are these
    > generalized pustules are like little bumps that can be a sign of these
    > generalized pustules that can be a sign, and then suddenly there are
    > these are filled pustules that can be a sign of different skin issues,
    > but in someone with psoriasis, a major change like stopping treatment
    > can really mess with the immune system. Steroids are powerful at
    > tamping down the immune system and keeping it under control. When you
    > take someone off steroids, particularly suddenly, the body can react
    > in unexpected ways. It's like the immune system gets a bit confused
    > and overreacts, a rebound effect. Alright, so connecting the dots:
    > it's likely that stopping the steroids led to a rebound effect where
    > the psoriasis got worse. This makes sense given the scenario—I've seen
    > this mentioned before where physicians need to be cautious about how
    > they taper steroids off in psoriasis patients to prevent exactly this
    > kind of escalation. It fits pretty well with what I'd expect based on
    > the available information.</think> The development of generalized
    > pustules in a patient with psoriasis following the discontinuation of
    > systemic steroid treatment is most likely due to a condition called
    > generalized pustular psoriasis (GPP). This condition can occur as a
    > rebound effect when systemic steroids are suddenly stopped or rapidly
    > tapered down. In patients with psoriasis, steroids can suppress the
    > immune system and mitigate symptoms. When these drugs are withdrawn
    > too quickly, the immune system can overreact, leading to more severe
    > symptoms, including the transformation of psoriasis into a pustular
    > form. Careful management and slow tapering of steroids are critical in
    > psoriasis patients to prevent the escalation of symptoms.</answer></s>


    I'm not sure why the behavior is inconsistent between the two.

    """
    Practical Introduction to Llama 2 Fine-Tuning with S1K Dataset using Standard Trainer

    Fine-tune a 7B parameter Llama 2 model using QLoRA on a T4 GPU with limited VRAM.
    This script uses parameter-efficient fine-tuning techniques to enable training
    on consumer-grade hardware, using medical reasoning dataset.
    """

    import torch
    from datasets import load_dataset
    from transformers import (
    AutoModelForCausalLM,
    AutoTokenizer,
    BitsAndBytesConfig,
    TrainingArguments,
    Trainer,
    pipeline,
    logging,
    DataCollatorForLanguageModeling,
    LlamaTokenizer
    )
    from peft import LoraConfig, PeftModel, get_peft_model, prepare_model_for_kbit_training
    from tqdm import tqdm

    # Configuration
    # Model and dataset settings
    MODEL_NAME = "meta-llama/Llama-2-7b-hf"
    # MODEL_NAME = "NousResearch/Llama-2-7b-hf"
    DATASET_NAME = "FreedomIntelligence/medical-o1-reasoning-SFT"
    NEW_MODEL_NAME = "llama-2-7b-medical-reasoning"
    OUTPUT_DIR = "./results-medical-reasoning"

    # QLoRA parameters
    LORA_R = 64
    LORA_ALPHA = 16
    LORA_DROPOUT = 0.1

    # Quantization parameters
    USE_4BIT = True
    BNB_4BIT_COMPUTE_DTYPE = "float16"
    BNB_4BIT_QUANT_TYPE = "nf4"
    USE_NESTED_QUANT = False

    # Training parameters
    NUM_TRAIN_EPOCHS = 100
    FP16 = True
    BF16 = False
    PER_DEVICE_TRAIN_BATCH_SIZE = 4
    PER_DEVICE_EVAL_BATCH_SIZE = 4
    GRADIENT_ACCUMULATION_STEPS = 4
    GRADIENT_CHECKPOINTING = True
    MAX_GRAD_NORM = 0.3
    LEARNING_RATE = 2e-4
    WEIGHT_DECAY = 0.001
    OPTIM = "paged_adamw_32bit"
    LR_SCHEDULER_TYPE = "constant"
    MAX_STEPS = -1 # Override epochs if positive
    WARMUP_RATIO = 0.03
    GROUP_BY_LENGTH = True
    SAVE_STEPS = 25
    LOGGING_STEPS = 25

    # Sequence parameters
    MAX_SEQ_LENGTH = 10000 # More reasonable max length
    PACKING = False
    DEVICE_MAP = {"": 0} # Load on GPU 0

    # Generation parameters - consistent across all model tests
    GENERATION_CONFIG = {
    "max_length": 2000,
    "do_sample": False,
    "temperature": 0.0,
    "num_beams": 1,
    "top_p": 1.0,
    "top_k": 50,
    "repetition_penalty": 1.0,
    }

    # Prompt formatting functions
    def generate_prompt_llama(question, think, answer):
    formatted_prompt = "[INST]<<SYS>>You are a helpful assistant.<</SYS>>\n"
    formatted_prompt += f"user\n{question}\nassistant\n[/INST]"
    formatted_prompt += f"<think>think\n{think}</think>\n<answer>answer\n{answer}</answer></s>"
    return formatted_prompt

    def generate_prompt_llama_answer(question, think, answer):
    formatted_prompt = f"<think>think\n{think}</think>\n<answer>answer\n{answer}</answer></s>"
    return formatted_prompt

    def generate_prompt_llama_system(question):
    formatted_prompt = "[INST]<<SYS>>You are a helpful assistant.<</SYS>>\n"
    formatted_prompt += f"user\n{question}\nassistant\n[/INST]"
    return formatted_prompt

    def main():
    """Main function to run the fine-tuning process."""
    print("Starting Llama 2 fine-tuning process with medical reasoning dataset")

    # 1. Load dataset
    print(f"Loading {DATASET_NAME} dataset...")
    dataset = load_dataset(DATASET_NAME, 'en')
    dataset = dataset['train'].select(range(3,4)) # For testing purposes
    print(f"Dataset loaded: {dataset}")
    print(f"Dataset length: {len(dataset)}")
    print(f"Dataset structure: {dataset.features}")

    # 2. Configure quantization
    print("Configuring BitsAndBytes for 4-bit quantization...")
    compute_dtype = getattr(torch, BNB_4BIT_COMPUTE_DTYPE)
    bnb_config = BitsAndBytesConfig(
    load_in_4bit=USE_4BIT,
    bnb_4bit_quant_type=BNB_4BIT_QUANT_TYPE,
    bnb_4bit_compute_dtype=compute_dtype,
    bnb_4bit_use_double_quant=USE_NESTED_QUANT,
    )

    # Check GPU compatibility with bfloat16
    if compute_dtype == torch.float16 and USE_4BIT:
    if torch.cuda.is_available():
    major, _ = torch.cuda.get_device_capability()
    if major >= 8:
    print("=" * 80)
    print("Your GPU supports bfloat16: accelerate training with bf16=True")
    print("=" * 80)

    # 3. Load model and tokenizer
    print(f"Loading {MODEL_NAME} in 4-bit precision...")
    model = AutoModelForCausalLM.from_pretrained(
    MODEL_NAME,
    quantization_config=bnb_config,
    device_map=DEVICE_MAP,
    trust_remote_code=True,
    use_cache=False # Set here directly
    )

    # Configure model settings for training
    model.config.pretraining_tp = 1

    print("Loading tokenizer...")
    tokenizer = LlamaTokenizer.from_pretrained(MODEL_NAME, trust_remote_code=True)
    num_added_toks = tokenizer.add_tokens(['[INST]', '[/INST]', '<<SYS>>', '<</SYS>>'], special_tokens=True)

    # Use distinct end-of-text and padding tokens
    special_tokens_dict = {
    'eos_token': '</s>',
    'pad_token': '<|pad|>'
    }
    # Add special tokens and resize model embeddings
    num_added_tokens = tokenizer.add_special_tokens(special_tokens_dict)
    print(f"Added {num_added_tokens} special tokens to the tokenizer")

    # Important: Resize model embeddings to match new tokenizer size
    model.resize_token_embeddings(len(tokenizer))

    # Set padding side for the tokenizer
    tokenizer.padding_side = "right" # Fix overflow issues with fp16 training

    # Update model config with token IDs
    model.config.pad_token_id = tokenizer.pad_token_id
    model.config.eos_token_id = tokenizer.eos_token_id

    # Print token information for debugging
    print(f"Pad token: {tokenizer.pad_token}, ID: {tokenizer.pad_token_id}")
    print(f"EOS token: {tokenizer.eos_token}, ID: {tokenizer.eos_token_id}")

    # Prepare model for k-bit training - CRITICAL STEP!
    model = prepare_model_for_kbit_training(model)

    # 4. Configure LoRA
    print("Configuring LoRA...")
    peft_config = LoraConfig(
    lora_alpha=LORA_ALPHA,
    lora_dropout=LORA_DROPOUT,
    r=LORA_R,
    bias="none",
    task_type="CAUSAL_LM",
    target_modules=["q_proj", "k_proj", "v_proj", "o_proj", "gate_proj", "down_proj", "up_proj"],
    )

    # Apply LoRA to the model
    model = get_peft_model(model, peft_config)
    model.print_trainable_parameters()

    # 5. Process dataset
    print("Processing dataset...")

    def preprocess_function(examples):
    """Process a batch of examples."""
    full_prompts = [
    generate_prompt_llama(q, traj, att)
    for q, traj, att in zip(
    examples["Question"],
    examples["Complex_CoT"],
    examples["Response"]
    )
    ]
    print("Full prompt example:", full_prompts[0])

    # Tokenize inputs with proper padding and truncation
    tokenized = tokenizer(
    full_prompts,
    truncation=True,
    max_length=MAX_SEQ_LENGTH,
    padding=False, # DataCollator will handle padding
    return_tensors=None, # Return python lists, not tensors
    )

    # For causal language modeling, labels are the same as input_ids
    tokenized["labels"] = tokenized["input_ids"].copy()

    return tokenized

    # Apply preprocessing to dataset
    tokenized_dataset = dataset.map(
    preprocess_function,
    batched=True,
    remove_columns=dataset.column_names,
    desc="Tokenizing dataset",
    )

    print("Decoded sample:", tokenizer.decode(tokenized_dataset[0]["input_ids"], skip_special_tokens=False))
    print(f"Sample input_ids shape: {len(tokenized_dataset[0]['input_ids'])}")

    # 6. Data collator - critical for properly batching sequences
    data_collator = DataCollatorForLanguageModeling(
    tokenizer=tokenizer,
    mlm=False, # We're doing causal language modeling
    )

    # 7. Set up training arguments
    print("Setting up training arguments...")
    training_args = TrainingArguments(
    output_dir=OUTPUT_DIR,
    num_train_epochs=NUM_TRAIN_EPOCHS,
    per_device_train_batch_size=PER_DEVICE_TRAIN_BATCH_SIZE,
    per_device_eval_batch_size=PER_DEVICE_EVAL_BATCH_SIZE,
    gradient_accumulation_steps=GRADIENT_ACCUMULATION_STEPS,
    optim=OPTIM,
    save_steps=SAVE_STEPS,
    logging_steps=LOGGING_STEPS,
    learning_rate=LEARNING_RATE,
    weight_decay=WEIGHT_DECAY,
    fp16=FP16,
    bf16=BF16,
    max_grad_norm=MAX_GRAD_NORM,
    max_steps=MAX_STEPS,
    warmup_ratio=WARMUP_RATIO,
    group_by_length=GROUP_BY_LENGTH,
    lr_scheduler_type=LR_SCHEDULER_TYPE,
    report_to="tensorboard",
    gradient_checkpointing=GRADIENT_CHECKPOINTING,
    remove_unused_columns=False, # Important for LoRA fine-tuning
    )

    # 8. Initialize standard Trainer
    print("Initializing standard Trainer...")
    trainer = Trainer(
    model=model,
    args=training_args,
    train_dataset=tokenized_dataset,
    data_collator=data_collator,
    )

    # 9. Train model
    print("Starting training...")
    trainer.train()

    # 10. Save trained model
    print(f"Saving model to {NEW_MODEL_NAME}...")
    trainer.save_model(NEW_MODEL_NAME)
    tokenizer.save_pretrained(NEW_MODEL_NAME)

    print("Fine-tuning complete!")

    # 11. Test model
    test_model(model, tokenizer)

    # 12. Merge weights (requires restarting with fresh VRAM)
    print("====================================================================:")
    print("Note: To merge LoRA weights with the base model:")
    print("1. Restart your environment to clear VRAM")
    print("2. Run the merge_weights() function")

    merge_weights()


    def test_model(model, tokenizer):
    """Test the model with a sample question."""
    logging.set_verbosity(logging.CRITICAL)
    prompt = 'A patient with psoriasis was treated with systemic steroids, and upon discontinuation of the treatment, developed generalized pustules all over the body. What is the most likely cause of this condition?'

    # Format for inference
    formatted_prompt = generate_prompt_llama_system(prompt)

    print("\nTesting model with prompt:", prompt)

    # Testing using model.generate()
    input_ids = tokenizer(formatted_prompt, return_tensors="pt").input_ids.to(model.device)

    model.eval()

    with torch.no_grad():
    # Use the global generation config
    output_ids = model.generate(
    input_ids=input_ids,
    max_length=GENERATION_CONFIG["max_length"],
    do_sample=GENERATION_CONFIG["do_sample"],
    temperature=GENERATION_CONFIG["temperature"],
    num_beams=GENERATION_CONFIG["num_beams"],
    top_p=GENERATION_CONFIG["top_p"],
    top_k=GENERATION_CONFIG["top_k"],
    repetition_penalty=GENERATION_CONFIG["repetition_penalty"],
    pad_token_id=tokenizer.pad_token_id,
    eos_token_id=tokenizer.eos_token_id,
    )

    print("\nGenerated output:")
    print(tokenizer.decode(output_ids[0], skip_special_tokens=False))


    def merge_weights():
    """Merge LoRA weights with base model (run after restarting environment)."""
    import torch
    from transformers import AutoModelForCausalLM, AutoTokenizer
    from peft import PeftModel

    print("Loading tokenizer first...")
    tokenizer = AutoTokenizer.from_pretrained(NEW_MODEL_NAME, trust_remote_code=True)

    # Calculate the new vocabulary size
    new_vocab_size = len(tokenizer)
    print(f"New vocabulary size: {new_vocab_size}")

    print("Loading base model in FP16...")
    base_model = AutoModelForCausalLM.from_pretrained(
    MODEL_NAME,
    low_cpu_mem_usage=True,
    return_dict=True,
    torch_dtype=torch.float16,
    device_map={"": 0},
    )

    # Resize model embeddings BEFORE loading LoRA weights
    base_model.resize_token_embeddings(len(tokenizer))
    print(f"Resized base model embeddings to {len(tokenizer)}")

    print(f"Loading LoRA weights from {NEW_MODEL_NAME}...")
    model = PeftModel.from_pretrained(base_model, NEW_MODEL_NAME)

    print("Merging weights...")
    model = model.merge_and_unload()

    # Set padding configuration
    tokenizer.padding_side = "right"

    # Print token info
    print(f"Pad token: {tokenizer.pad_token}, ID: {tokenizer.pad_token_id}")
    print(f"EOS token: {tokenizer.eos_token}, ID: {tokenizer.eos_token_id}")

    # Save merged model
    merged_model_name = f"{NEW_MODEL_NAME}-merged"
    print(f"Saving merged model to {merged_model_name}...")
    model.save_pretrained(merged_model_name)
    tokenizer.save_pretrained(merged_model_name)

    print("Model weights successfully merged!")

    # Test the merged model
    test_merged_model(model, tokenizer)

    return model, tokenizer


    def test_merged_model(model, tokenizer):
    """Test the merged model."""
    # Use the exact same test code and parameters as test_model
    logging.set_verbosity(logging.CRITICAL)
    prompt = 'A patient with psoriasis was treated with systemic steroids, and upon discontinuation of the treatment, developed generalized pustules all over the body. What is the most likely cause of this condition?'

    # Format for inference
    formatted_prompt = generate_prompt_llama_system(prompt)

    print("\nTesting merged model with prompt:", prompt)

    # Testing using model.generate()
    input_ids = tokenizer(formatted_prompt, return_tensors="pt").input_ids.to(model.device)

    model.eval()

    with torch.no_grad():
    # Use the identical generation config as the original test
    output_ids = model.generate(
    input_ids=input_ids,
    max_length=GENERATION_CONFIG["max_length"],
    do_sample=GENERATION_CONFIG["do_sample"],
    temperature=GENERATION_CONFIG["temperature"],
    num_beams=GENERATION_CONFIG["num_beams"],
    top_p=GENERATION_CONFIG["top_p"],
    top_k=GENERATION_CONFIG["top_k"],
    repetition_penalty=GENERATION_CONFIG["repetition_penalty"],
    pad_token_id=tokenizer.pad_token_id,
    eos_token_id=tokenizer.eos_token_id,
    )

    print("\nGenerated output:")
    print(tokenizer.decode(output_ids[0], skip_special_tokens=False))


    if __name__ == "__main__":
    main()here

    Continue reading...

Compartilhe esta Página