| """Flow-CoPD configs (single A100 80GB). Loaded via: |
| accelerate launch flow_copd/train_sd3_copd.py --config flow_copd/config_copd.py:text_a2_copd |
| """ |
| import os |
| import imp |
| import ml_collections |
|
|
| |
| _FLOW_GRPO = os.path.join(os.path.dirname(__file__), "..", "third_party", "flow_grpo") |
| _FLOW_GRPO = os.path.abspath(_FLOW_GRPO) |
| base = imp.load_source("base", os.path.join(_FLOW_GRPO, "config", "base.py")) |
|
|
| _ROOT = os.path.abspath(os.path.join(os.path.dirname(__file__), "..")) |
| TEACHERS = { |
| "text": os.path.join(_ROOT, "checkpoints/teachers/text"), |
| "pickscore": os.path.join(_ROOT, "checkpoints/teachers/pickscore"), |
| } |
| DATASET = os.path.join(_FLOW_GRPO, "dataset") |
|
|
|
|
| def get_config(name): |
| """ml_collections config_flags dispatcher: `config_copd.py:NAME` -> NAME().""" |
| return globals()[name]() |
|
|
|
|
| def copd_base(): |
| """Single-A100 OPD base: SD3.5-M + LoRA, 512 res, 10 denoising steps.""" |
| config = base.get_config() |
| config.pretrained.model = "stabilityai/stable-diffusion-3.5-medium" |
| config.use_lora = True |
| config.resolution = 512 |
| config.mixed_precision = "fp16" |
| config.activation_checkpointing = True |
|
|
| |
| config.sample.num_steps = 10 |
| config.sample.eval_num_steps = 40 |
| config.sample.guidance_scale = 4.5 |
| config.sample.train_batch_size = 4 |
| config.sample.num_image_per_prompt = 4 |
| config.sample.num_batches_per_epoch = 2 |
| config.sample.test_batch_size = 8 |
| config.sample.global_std = True |
| config.sample.noise_level = 0.7 |
|
|
| |
| |
| |
| |
| config.train.batch_size = 4 |
| config.train.gradient_accumulation_steps = 1 |
| config.train.num_inner_epochs = 1 |
| config.train.learning_rate = 3e-4 |
| config.train.timestep_fraction = 0.99 |
| config.train.cfg = False |
| config.train.ema = True |
| config.train.beta = 0.0 |
|
|
| |
| config.copd = ml_collections.ConfigDict() |
| config.copd.mode = "positive" |
| config.copd.teacher_lora_path = TEACHERS["text"] |
| config.copd.teacher_adapter_name = "teacher" |
| |
| |
| |
| |
| config.copd.teacher_guidance_scale = 4.5 |
| config.copd.weight_scheme = "uniform" |
| config.copd.lambda_neg = 1.0 |
| config.copd.neg_clamp = 1.0 |
| config.copd.adv_thresh = 0.0 |
|
|
| config.per_prompt_stat_tracking = True |
| config.num_epochs = 100000 |
| config.save_freq = 50 |
| config.eval_freq = 25 |
| config.eval_at_start = True |
| return config |
|
|
|
|
| def text_sanity(): |
| """Gate-0 smoke test of the OPD/contrastive PIPELINE on the real SD3.5 model. |
| Uses the PickScore teacher+reward (CLIP-based, paddle-free) to decouple from the |
| broken PaddleOCR install — Gate-0 only verifies sampling + teacher/student |
| velocity + OPD loss + backward run without OOM/crash. The real text/OCR runs |
| (text_a0/a2) need paddle fixed first. Skips eval + checkpoint saving.""" |
| config = copd_base() |
| config.copd.mode = "positive" |
| config.copd.teacher_lora_path = TEACHERS["pickscore"] |
| config.dataset = os.path.join(DATASET, "ocr") |
| config.prompt_fn = "general_ocr" |
| config.reward_fn = {"pickscore": 1.0} |
| config.eval_at_start = False |
| config.eval_freq = 100000 |
| config.save_freq = 100000 |
| config.sample.num_batches_per_epoch = 2 |
| config.run_name = "text_sanity" |
| config.save_dir = "logs/copd/text_sanity" |
| return config |
|
|
|
|
| |
| def sanity_contrastive(): |
| """Gate-0b: exercise the CONTRASTIVE path (copd_loss + per-step v_neg) on GPU, |
| eval/save off. Verifies the method-specific A2 code before long runs.""" |
| config = text_sanity() |
| config.copd.mode = "contrastive" |
| config.run_name = "sanity_contrastive" |
| config.save_dir = "logs/copd/sanity_contrastive" |
| return config |
|
|
|
|
| def text_a0_positive(): |
| """A0 — positive-only OPD (Flow-OPD / DiffusionOPD reproduction).""" |
| config = copd_base() |
| config.copd.mode = "positive" |
| config.copd.teacher_lora_path = TEACHERS["text"] |
| config.dataset = os.path.join(DATASET, "ocr") |
| config.prompt_fn = "general_ocr" |
| config.reward_fn = {"ocr": 1.0} |
| config.save_dir = "logs/copd/text_a0_positive" |
| config.run_name = "text_a0_positive" |
| return config |
|
|
|
|
| def text_a2_copd(): |
| """A2 — Flow-CoPD = positive OPD + contrastive negative (OUR method).""" |
| config = text_a0_positive() |
| config.copd.mode = "contrastive" |
| config.copd.lambda_neg = 1.0 |
| config.copd.neg_clamp = 1.0 |
| config.save_dir = "logs/copd/text_a2_copd" |
| config.run_name = "text_a2_copd" |
| return config |
|
|
|
|
| def text_a1_symmetric(): |
| """A1 — symmetric-negative control (same magnitude, no asymmetry).""" |
| config = text_a2_copd() |
| config.copd.mode = "contrastive" |
| config.copd.adv_thresh = -1e9 |
| config.save_dir = "logs/copd/text_a1_symmetric" |
| config.run_name = "text_a1_symmetric" |
| return config |
|
|
|
|
| |
| def pick_a0_positive(): |
| config = copd_base() |
| config.copd.mode = "positive" |
| config.copd.teacher_lora_path = TEACHERS["pickscore"] |
| config.dataset = os.path.join(DATASET, "pickscore") |
| config.prompt_fn = "general_ocr" |
| |
| |
| config.reward_fn = {"pickscore": 1.0, "aesthetic": 0.0} |
| config.eval_at_start = False |
| config.save_dir = "logs/copd/pick_a0_positive" |
| config.run_name = "pick_a0_positive" |
| return config |
|
|
|
|
| def pick_a2_copd(): |
| config = pick_a0_positive() |
| config.copd.mode = "contrastive" |
| config.save_dir = "logs/copd/pick_a2_copd" |
| config.run_name = "pick_a2_copd" |
| return config |
|
|
|
|
| def pick_a1_symmetric(): |
| """A1 — symmetric-negative control (PickScore): repel ALL trajectories equally |
| (adv_thresh huge negative => every traj treated as 'bad'), isolating the value |
| of the reward-ASYMMETRY in Flow-CoPD.""" |
| config = pick_a2_copd() |
| config.copd.adv_thresh = -1e9 |
| config.save_dir = "logs/copd/pick_a1_symmetric" |
| config.run_name = "pick_a1_symmetric" |
| return config |
|
|