Skip to content

The diffusion model fails to converge during LoRA fine-tuning, and the loss keeps oscillating. #41

Description

@krona12

I am encountering a issue where the diffusion model's training loss fails to converge when fine-tuning with LoRA (system.use_lora=True). Instead of decreasing, the loss oscillates significantly throughout the training process.

To isolate the problem, I attempted to fine-tune the model using the minimal example dataset provided (the single object in ./data/shape_diffusion/objaverse). Using the configuration file detailed below, I ran the training for 200 epochs with the command: CUDA_VISIBLE_DEVICES=0 python train.py --config path/to/config.yaml --train --gpu 0 system.use_lora=True. The expected behavior of a steadily decreasing loss did not occur. Instead, the loss value remained unstable and oscillated for the entire duration, as shown in the attached loss curve.
I have confirmed this behavior occurs both when training with labels and without them; neither setup leads to convergence. Interestingly, despite the unstable training, the fine-tuned model checkpoint is able to produce seemingly normal inferences.

This behavior is not limited to the example data. My initial attempt was on a custom dataset built from parnet-mobility objects, which I processed using the data/watertight_and_sampling.py script. During that process, I first encountered a NaN loss, an issue similar to one reported by jseobyun. I resolved the NaN loss by filtering out the problematic sharp_surface data. However, even after fixing the data and retraining for about 100 epochs, the primary issue of the oscillating loss persisted.

Since the problem occurs on both a larger custom dataset and the minimal single-object example, I suspect that the issue may originate from the data, the training configuration, or perhaps even the code. I would appreciate any guidance or suggestions on how to resolve this convergence issue.

Loss curvs is as follows
image-20250718133844754

Configuration (step1x-3d-geometry-label-1300m.yaml)

# [INFO] 
#   | Name             | Type                    | Params
# -------------------------------------------------------------
# 0 | shape_model      | MichelangeloAutoencoder | 191 M 
# 1 | visual_condition | Dinov2CLIPEncoder       | 731 M 
# 2 | label_condition  | LabelEncoder            | 8.2 K 
# 3 | denoiser_model   | FluxDenoiser            | 1.3 B
# -------------------------------------------------------------
# 1.3 B     Trainable params
# 923 M     Non-trainable params
# 2.2 B     Total params

exp_root_dir: "outputs_test"
name: "step1x-3d-geometry-label/dinov2reglarge518-clip-label-fluxflow-dit1300m"
#tag: "${rmspace:${system.shape_model_type}+n${data.n_samples}+${system.optimizer.name}lr${system.optimizer.args.lr},_}"
tag: "objaverse-200ep-wlabel-wcaption-lora-t7181228-testpret"
seed: 0

data_type: "Objaverse-datamodule"
data:
  root_dir: "./data/shape_diffusion/objaverse"
  load_geometry: True                # whether to load geometry
  geo_data_type: "sdf" 
  with_sharp_data: True
  n_samples: 32768
  noise_sigma: 0.
  random_flip: True # whether to randomly flip the input mesh
  random_color_jitter: True # whether to add random color jitter to the input images
  random_rotate: True # whether to add random rotation to the input images

  load_image: True                # whether to load images 
  image_type: "rgb"               # rgb, normal
  idx: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19]        # front view
  n_views: 1
  background_color: [255, 255, 255]

  load_caption: True             # whether to load captions #False
  load_label: True          # whether to load labels #True 

  batch_size: 8 # TODO: change this for your own dataset
  num_workers: 4 # change this for your own dataset

system_type: "rectified-flow-system"
system:
  val_samples_json: "val_data/images/val_samples_rgb_image.json"
  guidance_scale: 7.5
  num_inference_steps: 30
  eta: 0.0

  shape_model_type: michelangelo-autoencoder
  shape_model:
    pretrained_model_name_or_path: /home/yuantingyu/models
    subfolder: Step1X-3D-Geometry-Label-1300m
    n_samples: ${data.n_samples}
    with_sharp_data: ${data.with_sharp_data}
    use_downsample: true
    num_latents: 2048
    embed_dim: 64
    point_feats: 3
    out_dim: 1
    num_freqs: 8
    include_pi: false
    heads: 12
    width: 768
    num_encoder_layers: 8
    num_decoder_layers: 16
    use_ln_post: true
    init_scale: 0.25
    qkv_bias: false
    use_flash: true #true
    use_checkpoint: false

  visual_condition_type: "dinov2-clip-encoder"
  visual_condition:
    pretrained_dino_name_or_path: /home/yuantingyu/models/dinov2-large
    pretrained_clip_name_or_path: /home/yuantingyu/models/clip-vit-large-patch14
    encode_camera: false
    n_views: ${data.n_views}
    empty_embeds_ratio: 0.1
    normalize_embeds: false
    zero_uncond_embeds: true
    image_size: 224

  label_condition_type: "label-encoder"
  label_condition:
    hidden_size: 1024
    empty_embeds_ratio: 0.1
    normalize_embeds: false
    zero_uncond_embeds: true

  denoiser_model_type: "flux-denoiser"
  denoiser_model:
    pretrained_model_name_or_path: "/home/yuantingyu/models/modelscope/Step1X-3D-Geometry-Label-1300m/transformer/diffusion_pytorch_model.safetensors"
  
    input_channels: ${system.shape_model.embed_dim}
    width: 1536
    layers: 8
    num_single_layers: 16
    num_heads: 16
    use_visual_condition: True
    visual_condition_dim: 1024
    n_views: ${data.n_views}
    use_label_condition: True
    label_condition_dim: ${system.label_condition.hidden_size}

  noise_scheduler_type: "diffusers.schedulers.FlowMatchEulerDiscreteScheduler"
  noise_scheduler:
    num_train_timesteps: 1000
    
  denoise_scheduler_type: "diffusers.schedulers.FlowMatchEulerDiscreteScheduler"
  denoise_scheduler:
    num_train_timesteps: 1000

  loggers:
    wandb:
      enable: false
      project: "step1x-3d"
      name: image-to-shape-diffusion+${name}+${tag}

  loss:
    loss_type: "mse"
    lambda_diffusion: 1.

  optimizer:
    name: AdamW
    args:
      lr: 1.e-5                    
      betas: [0.9, 0.99]
      eps: 1.e-6

trainer:
  num_nodes: 1
  max_epochs: 200

  log_every_n_steps: 5 
  num_sanity_val_steps: 1
  val_check_interval: 1.0
  enable_progress_bar: true
  precision: 32                     
  strategy: 'deepspeed_stage_2'

checkpoint:
  save_last: true
  save_top_k: -1
  every_n_epochs: 10

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions