Add files using upload-large-folder tool
Browse files- Reward_sd15_idealized/README.md +1336 -0
- Reward_sd15_idealized/RESULTS/pickapic/one_step_rectification_config_spo/run_1/evaluation_results.txt +4 -0
- Reward_sd15_idealized/RESULTS/pickapic/one_step_rectification_config_spo/run_1/log.log +229 -0
- Reward_sd15_idealized/RESULTS/pickapic/one_step_rectification_config_spo/run_1/lr_curve.png +0 -0
- Reward_sd15_idealized/__pycache__/grad_ascent_configs.cpython-310.pyc +0 -0
- Reward_sd15_idealized/__pycache__/grad_ascent_configs.cpython-311.pyc +0 -0
- Reward_sd15_idealized/__pycache__/gradient_ascent_utils.cpython-310.pyc +0 -0
- Reward_sd15_idealized/__pycache__/gradient_ascent_utils.cpython-311.pyc +0 -0
- Reward_sd15_idealized/__pycache__/gradient_ascent_utils.cpython-313.pyc +0 -0
- Reward_sd15_idealized/__pycache__/lr_scheduler.cpython-310.pyc +0 -0
- Reward_sd15_idealized/__pycache__/lr_scheduler.cpython-311.pyc +0 -0
- Reward_sd15_idealized/config_analysis_tuning.ipynb +218 -0
- Reward_sd15_idealized/eval.py +1422 -0
- Reward_sd15_idealized/examples.sh +154 -0
- Reward_sd15_idealized/lr_scheduler.py +233 -0
- Reward_sd15_idealized/models/__init__.py +3 -0
- Reward_sd15_idealized/models/__pycache__/__init__.cpython-310.pyc +0 -0
- Reward_sd15_idealized/models/__pycache__/__init__.cpython-311.pyc +0 -0
- Reward_sd15_idealized/models/__pycache__/__init__.cpython-313.pyc +0 -0
- Reward_sd15_idealized/models/__pycache__/__init__.cpython-39.pyc +0 -0
- Reward_sd15_idealized/models/__pycache__/reward_model.cpython-310.pyc +0 -0
- Reward_sd15_idealized/models/__pycache__/reward_model.cpython-311.pyc +0 -0
- Reward_sd15_idealized/models/__pycache__/reward_model.cpython-313.pyc +0 -0
- Reward_sd15_idealized/models/__pycache__/reward_model.cpython-39.pyc +0 -0
- Reward_sd15_idealized/models/__pycache__/reward_model_sdxl.cpython-310.pyc +0 -0
- Reward_sd15_idealized/models/__pycache__/unet_2d_condition_reward.cpython-310.pyc +0 -0
- Reward_sd15_idealized/models/__pycache__/unet_2d_condition_reward.cpython-311.pyc +0 -0
- Reward_sd15_idealized/models/__pycache__/unet_2d_condition_reward.cpython-313.pyc +0 -0
- Reward_sd15_idealized/models/reward_model.py +388 -0
- Reward_sd15_idealized/models/unet_2d_condition_reward.py +1334 -0
- Reward_sd15_idealized/pipelines/__init__.py +9 -0
- Reward_sd15_idealized/pipelines/__pycache__/__init__.cpython-310.pyc +0 -0
- Reward_sd15_idealized/pipelines/__pycache__/__init__.cpython-311.pyc +0 -0
- Reward_sd15_idealized/pipelines/__pycache__/__init__.cpython-313.pyc +0 -0
- Reward_sd15_idealized/pipelines/__pycache__/sd15_gradient_ascent_pipeline.cpython-310.pyc +0 -0
- Reward_sd15_idealized/pipelines/__pycache__/sd15_gradient_ascent_pipeline.cpython-311.pyc +0 -0
- Reward_sd15_idealized/pipelines/__pycache__/sd15_gradient_ascent_pipeline.cpython-313.pyc +0 -0
- Reward_sd15_idealized/pipelines/__pycache__/sd15_reward_pipeline.cpython-310.pyc +0 -0
- Reward_sd15_idealized/pipelines/__pycache__/sd15_reward_pipeline.cpython-311.pyc +0 -0
- Reward_sd15_idealized/pipelines/__pycache__/sd15_reward_pipeline.cpython-313.pyc +0 -0
- Reward_sd15_idealized/pipelines/__pycache__/sdxl_gradient_ascent_pipeline.cpython-310.pyc +0 -0
- Reward_sd15_idealized/pipelines/__pycache__/sdxl_reward_pipeline.cpython-310.pyc +0 -0
- Reward_sd15_idealized/pipelines/sd15_gradient_ascent_pipeline.py +345 -0
- Reward_sd15_idealized/pipelines/sd15_reward_pipeline.py +316 -0
- Reward_sd15_idealized/tune_hyperparams.py +514 -0
- evaluation/LICENSE +201 -0
- evaluation/aesthetic_score.py +71 -0
- evaluation/blip/__init__.py +1 -0
- evaluation/general_score_pap_val.py +265 -0
- upload.py +270 -0
Reward_sd15_idealized/README.md
ADDED
|
@@ -0,0 +1,1336 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Reward-Guided Gradient Ascent for Stable Diffusion
|
| 2 |
+
|
| 3 |
+
A comprehensive system for improving Stable Diffusion image generation quality using gradient ascent optimization on Latent Reward Model (LRM) scores during inference.
|
| 4 |
+
|
| 5 |
+
## Table of Contents
|
| 6 |
+
|
| 7 |
+
- [Overview](#overview)
|
| 8 |
+
- [Features](#features)
|
| 9 |
+
- [Installation](#installation)
|
| 10 |
+
- [Quick Start](#quick-start)
|
| 11 |
+
- [Architecture](#architecture)
|
| 12 |
+
- [Understanding Reward Calculation](#understanding-reward-calculation)
|
| 13 |
+
- [Learning Rate Scheduling](#learning-rate-scheduling)
|
| 14 |
+
- [Configuration Presets](#configuration-presets)
|
| 15 |
+
- [Evaluation Metrics](#evaluation-metrics)
|
| 16 |
+
- [Model Variants](#model-variants)
|
| 17 |
+
- [Datasets](#datasets)
|
| 18 |
+
- [Usage Examples](#usage-examples)
|
| 19 |
+
- [API Reference](#api-reference)
|
| 20 |
+
- [Command-Line Options](#command-line-options)
|
| 21 |
+
- [Output Files](#output-files)
|
| 22 |
+
- [Troubleshooting](#troubleshooting)
|
| 23 |
+
- [Best Practices](#best-practices)
|
| 24 |
+
- [Changelog](#changelog)
|
| 25 |
+
|
| 26 |
+
---
|
| 27 |
+
|
| 28 |
+
## Overview
|
| 29 |
+
|
| 30 |
+
This project implements **test-time optimization** for Stable Diffusion using gradient ascent on the LRM reward model. Unlike the main LPO training which uses the reward model for training, this approach applies it during inference to improve generation quality without retraining.
|
| 31 |
+
|
| 32 |
+
### Key Capabilities
|
| 33 |
+
|
| 34 |
+
- **Gradient Ascent Optimization**: Iteratively improve latents using reward gradients
|
| 35 |
+
- **Learning Rate Scheduling**: Multiple strategies (constant, linear, cosine, exponential, step)
|
| 36 |
+
- **Momentum Optimization**: Standard and Nesterov momentum for better convergence
|
| 37 |
+
- **Multiple Metrics**: FID, CLIP, Aesthetic, PickScore, HPSv2, ImageReward
|
| 38 |
+
- **Model Variants**: Support for Origin, SPO, DPO, and LPO SD1.5 models
|
| 39 |
+
- **Dataset Flexibility**: COCO and Pick-a-Pic validation datasets
|
| 40 |
+
- **Configuration Presets**: 15 pre-tuned configurations for various use cases
|
| 41 |
+
|
| 42 |
+
---
|
| 43 |
+
|
| 44 |
+
## Features
|
| 45 |
+
|
| 46 |
+
### 1. **Advanced Optimization**
|
| 47 |
+
- **5 LR Schedulers**: Constant, Linear, Cosine, Exponential, Step-wise
|
| 48 |
+
- **Momentum Support**: Standard momentum and Nesterov momentum
|
| 49 |
+
- **Configurable Timestep Ranges**: Apply gradients at specific denoising steps
|
| 50 |
+
- **Dynamic Learning Rates**: LR changes during optimization for better convergence
|
| 51 |
+
|
| 52 |
+
### 2. **Comprehensive Evaluation**
|
| 53 |
+
- **6 Quality Metrics**: FID, CLIP, Aesthetic, PickScore, HPSv2, ImageReward
|
| 54 |
+
- **Baseline Comparison**: Compare with and without gradient ascent
|
| 55 |
+
- **Detailed Statistics**: Track reward improvements, gradient norms, LR history
|
| 56 |
+
- **Batch Processing**: Efficient evaluation on large datasets
|
| 57 |
+
- **Reward Visualization**: Automatic plotting of reward progression across timesteps
|
| 58 |
+
- **Timestep-Aware Tracking**: Monitor rewards at every denoising step, final t=0 latent reported
|
| 59 |
+
|
| 60 |
+
### 3. **Model Flexibility**
|
| 61 |
+
- **4 SD1.5 Variants**: Origin, SPO, DPO, LPO
|
| 62 |
+
- **Auto-Configuration**: CFG scale auto-adjusted for model variants
|
| 63 |
+
- **Easy Switching**: Change models with a single flag
|
| 64 |
+
|
| 65 |
+
### 4. **Dataset Support**
|
| 66 |
+
- **COCO Validation**: Standard benchmark with reference images
|
| 67 |
+
- **Pick-a-Pic Validation**: Large-scale human preference dataset
|
| 68 |
+
- **Streaming Support**: Handle large datasets efficiently
|
| 69 |
+
|
| 70 |
+
---
|
| 71 |
+
|
| 72 |
+
## Installation
|
| 73 |
+
|
| 74 |
+
### Requirements
|
| 75 |
+
|
| 76 |
+
```bash
|
| 77 |
+
# Core dependencies
|
| 78 |
+
pip install torch diffusers transformers torchmetrics datasets huggingface-hub
|
| 79 |
+
|
| 80 |
+
# For evaluation metrics
|
| 81 |
+
pip install pillow numpy scipy tqdm
|
| 82 |
+
|
| 83 |
+
# Optional: for better performance
|
| 84 |
+
pip install xformers # For memory-efficient attention
|
| 85 |
+
```
|
| 86 |
+
|
| 87 |
+
### Setup
|
| 88 |
+
|
| 89 |
+
```bash
|
| 90 |
+
cd /path/to/LPO/Reward
|
| 91 |
+
|
| 92 |
+
# Verify installation
|
| 93 |
+
python -c "from lr_scheduler import create_lr_scheduler; print('✓ LR Scheduler OK')"
|
| 94 |
+
python -c "from grad_ascent_configs import list_configs; print('✓ Configs:', len(list_configs()))"
|
| 95 |
+
python -c "from gradient_ascent_utils import RewardGuidedDiffusion; print('✓ Gradient Utils OK')"
|
| 96 |
+
```
|
| 97 |
+
|
| 98 |
+
---
|
| 99 |
+
|
| 100 |
+
## Quick Start
|
| 101 |
+
|
| 102 |
+
### 1. Basic COCO Evaluation (test_grad_sd1.5.py)
|
| 103 |
+
|
| 104 |
+
```bash
|
| 105 |
+
# Edit Config in test_grad_sd1.5.py:
|
| 106 |
+
# - Set device: "cuda:0" or "cuda:6"
|
| 107 |
+
# - Set max_samples: 10 for quick test, None for full dataset
|
| 108 |
+
# - Configure gradient ascent parameters
|
| 109 |
+
|
| 110 |
+
python test_grad_sd1.5.py
|
| 111 |
+
```
|
| 112 |
+
|
| 113 |
+
**Output:**
|
| 114 |
+
- Creates `RESULTS/SD1.5_GradAscent/run_1/` (auto-incremented)
|
| 115 |
+
- Generates `eval.log` with detailed metrics
|
| 116 |
+
- Saves `reward_curve.png` showing reward progression
|
| 117 |
+
|
| 118 |
+
### 2. Basic Evaluation with Preset Config (eval.py)
|
| 119 |
+
|
| 120 |
+
```bash
|
| 121 |
+
python eval.py \
|
| 122 |
+
--grad_config cosine_nesterov \
|
| 123 |
+
--metrics clip aesthetic \
|
| 124 |
+
--max_samples 10
|
| 125 |
+
```
|
| 126 |
+
|
| 127 |
+
### 2. High-Quality Evaluation
|
| 128 |
+
|
| 129 |
+
```bash
|
| 130 |
+
python eval.py \
|
| 131 |
+
--grad_config high_quality \
|
| 132 |
+
--metrics fid clip aesthetic pickscore hpsv2 \
|
| 133 |
+
--max_samples 100 \
|
| 134 |
+
--save_images \
|
| 135 |
+
--output_dir results/high_quality
|
| 136 |
+
```
|
| 137 |
+
|
| 138 |
+
### 3. Pick-a-Pic Benchmark
|
| 139 |
+
|
| 140 |
+
```bash
|
| 141 |
+
python eval.py \
|
| 142 |
+
--dataset_type pickapic \
|
| 143 |
+
--grad_config cosine_nesterov \
|
| 144 |
+
--metrics pickscore hpsv2 imagereward \
|
| 145 |
+
--max_samples 500 \
|
| 146 |
+
--output_dir results/pickapic
|
| 147 |
+
```
|
| 148 |
+
|
| 149 |
+
---
|
| 150 |
+
|
| 151 |
+
## Architecture
|
| 152 |
+
|
| 153 |
+
### System Components
|
| 154 |
+
|
| 155 |
+
```
|
| 156 |
+
Reward/
|
| 157 |
+
├── models/
|
| 158 |
+
│ ├── reward_model.py # LRM reward model wrapper
|
| 159 |
+
│ └── unet_2d_condition_reward.py # Custom UNet with reward tracking
|
| 160 |
+
├── pipelines/
|
| 161 |
+
│ ├── sd15_reward_pipeline.py # Base pipeline with reward tracking
|
| 162 |
+
│ └── sd15_gradient_ascent_pipeline.py # Pipeline with gradient ascent
|
| 163 |
+
├── lr_scheduler.py # Learning rate schedulers
|
| 164 |
+
├── gradient_ascent_utils.py # Core gradient ascent implementation
|
| 165 |
+
├── grad_ascent_configs.py # Configuration presets
|
| 166 |
+
├── eval.py # Comprehensive evaluation script
|
| 167 |
+
└── examples.sh # Example commands
|
| 168 |
+
```
|
| 169 |
+
|
| 170 |
+
### Gradient Ascent Flow
|
| 171 |
+
|
| 172 |
+
```
|
| 173 |
+
1. Load Stable Diffusion + LRM Reward Model
|
| 174 |
+
2. Start denoising process (T → 0)
|
| 175 |
+
3. At each timestep t:
|
| 176 |
+
a. Standard denoising step (predict noise, remove it)
|
| 177 |
+
b. Compute reward R(latents, prompt, t) and store in history
|
| 178 |
+
c. If t in gradient range:
|
| 179 |
+
- Enable gradients on latents
|
| 180 |
+
- Compute ∇R w.r.t. latents
|
| 181 |
+
- For each gradient step:
|
| 182 |
+
* Get current LR from scheduler
|
| 183 |
+
* Apply momentum (if enabled)
|
| 184 |
+
* Update: latents += lr * momentum(∇R)
|
| 185 |
+
- Track statistics (grad norms, reward improvement)
|
| 186 |
+
4. At final timestep (t=0):
|
| 187 |
+
- Final reward computed on clean latent
|
| 188 |
+
- This reward is reported in logs
|
| 189 |
+
5. Decode final latent (x₀) to image via VAE
|
| 190 |
+
6. Compute quality metrics on image
|
| 191 |
+
```
|
| 192 |
+
|
| 193 |
+
### Understanding Reward Calculation
|
| 194 |
+
|
| 195 |
+
**Key Concepts:**
|
| 196 |
+
|
| 197 |
+
- **Timestep-Aware Rewards**: The LRM reward model computes preference scores at ANY noise level (timestep t)
|
| 198 |
+
- **Progressive Tracking**: Rewards are calculated at every denoising step throughout generation
|
| 199 |
+
- **Final Latent Reward**: The reported metric is the reward for t=0 (the clean latent before decoding)
|
| 200 |
+
- **Not Averaged**: The final reward is specifically from the last timestep, NOT an average across all timesteps
|
| 201 |
+
|
| 202 |
+
**What gets reported:**
|
| 203 |
+
```python
|
| 204 |
+
# During generation: Rewards computed at each t (1000 → 0)
|
| 205 |
+
Step 0: t=1000, reward=3.2
|
| 206 |
+
Step 1: t=990, reward=3.5
|
| 207 |
+
...
|
| 208 |
+
Step 99: t=10, reward=5.1
|
| 209 |
+
Step 100: t=0, reward=5.4 ← This is what gets logged!
|
| 210 |
+
```
|
| 211 |
+
|
| 212 |
+
The `Reward (t=0)` in logs represents the preference score of the final clean latent that was decoded into your output image.
|
| 213 |
+
|
| 214 |
+
---
|
| 215 |
+
|
| 216 |
+
## Learning Rate Scheduling
|
| 217 |
+
|
| 218 |
+
### Available Schedulers
|
| 219 |
+
|
| 220 |
+
#### 1. **Constant LR**
|
| 221 |
+
```python
|
| 222 |
+
lr_scheduler_type="constant"
|
| 223 |
+
```
|
| 224 |
+
- Fixed learning rate throughout optimization
|
| 225 |
+
- Simple and stable
|
| 226 |
+
- Good for quick experiments
|
| 227 |
+
|
| 228 |
+
#### 2. **Linear Decay**
|
| 229 |
+
```python
|
| 230 |
+
lr_scheduler_type="linear"
|
| 231 |
+
lr_scheduler_kwargs={
|
| 232 |
+
"end_lr": 0.01, # End LR (10% of initial)
|
| 233 |
+
"start_step": 0 # When to start decay
|
| 234 |
+
}
|
| 235 |
+
```
|
| 236 |
+
- Linear decrease from initial to end LR
|
| 237 |
+
- Smooth convergence
|
| 238 |
+
- Configurable warmup period
|
| 239 |
+
|
| 240 |
+
#### 3. **Cosine Annealing** (Recommended)
|
| 241 |
+
```python
|
| 242 |
+
lr_scheduler_type="cosine"
|
| 243 |
+
lr_scheduler_kwargs={
|
| 244 |
+
"min_lr": 0.001, # Minimum LR
|
| 245 |
+
"warmup_steps": 3 # Linear warmup steps
|
| 246 |
+
}
|
| 247 |
+
```
|
| 248 |
+
- Smooth cosine decay
|
| 249 |
+
- Optional warmup phase
|
| 250 |
+
- Widely used in deep learning
|
| 251 |
+
- **Best for most use cases**
|
| 252 |
+
|
| 253 |
+
#### 4. **Exponential Decay**
|
| 254 |
+
```python
|
| 255 |
+
lr_scheduler_type="exponential"
|
| 256 |
+
lr_scheduler_kwargs={
|
| 257 |
+
"gamma": 0.9 # Decay factor per step
|
| 258 |
+
}
|
| 259 |
+
```
|
| 260 |
+
- Exponential decrease
|
| 261 |
+
- Fast initial decay
|
| 262 |
+
- Good for aggressive optimization
|
| 263 |
+
|
| 264 |
+
#### 5. **Step Decay**
|
| 265 |
+
```python
|
| 266 |
+
lr_scheduler_type="step"
|
| 267 |
+
lr_scheduler_kwargs={
|
| 268 |
+
"step_size": 5, # Steps between decays
|
| 269 |
+
"gamma": 0.5 # Multiplicative factor
|
| 270 |
+
}
|
| 271 |
+
```
|
| 272 |
+
- Step-wise LR reduction
|
| 273 |
+
- Periodic decay
|
| 274 |
+
- Good for scheduled changes
|
| 275 |
+
|
| 276 |
+
### Usage Example
|
| 277 |
+
|
| 278 |
+
```python
|
| 279 |
+
from pipelines.sd15_gradient_ascent_pipeline import StableDiffusionGradientAscentPipeline
|
| 280 |
+
|
| 281 |
+
pipeline.enable_gradient_ascent(
|
| 282 |
+
grad_timestep_range=(0, 700),
|
| 283 |
+
num_grad_steps=15,
|
| 284 |
+
grad_step_size=0.1, # Initial LR
|
| 285 |
+
lr_scheduler_type="cosine",
|
| 286 |
+
lr_scheduler_kwargs={
|
| 287 |
+
"min_lr": 0.001,
|
| 288 |
+
"warmup_steps": 3
|
| 289 |
+
}
|
| 290 |
+
)
|
| 291 |
+
```
|
| 292 |
+
|
| 293 |
+
---
|
| 294 |
+
|
| 295 |
+
## Configuration Presets
|
| 296 |
+
|
| 297 |
+
We provide 15 pre-configured optimization strategies. Use them with `--grad_config <name>`.
|
| 298 |
+
|
| 299 |
+
### Basic Configurations
|
| 300 |
+
|
| 301 |
+
| Config | LR Schedule | Momentum | Steps | Description |
|
| 302 |
+
|--------|-------------|----------|-------|-------------|
|
| 303 |
+
| `constant` | Constant | No | 5 | Simple baseline |
|
| 304 |
+
| `linear` | Linear decay | No | 10 | Smooth decay |
|
| 305 |
+
| `linear_warmstart` | Linear w/ warmup | No | 10 | Stable start |
|
| 306 |
+
| `cosine` | Cosine | No | 10 | Smooth convergence |
|
| 307 |
+
| `cosine_warmup` | Cosine w/ warmup | No | 20 | Best convergence |
|
| 308 |
+
| `exponential` | Exponential | No | 15 | Fast decay |
|
| 309 |
+
| `step` | Step-wise | No | 20 | Periodic decay |
|
| 310 |
+
|
| 311 |
+
### Momentum Configurations
|
| 312 |
+
|
| 313 |
+
| Config | LR Schedule | Momentum | Steps | Description |
|
| 314 |
+
|--------|-------------|----------|-------|-------------|
|
| 315 |
+
| `momentum` | Constant | Standard | 10 | Faster convergence |
|
| 316 |
+
| `nesterov` | Constant | Nesterov | 10 | Better convergence |
|
| 317 |
+
|
| 318 |
+
### Advanced Configurations
|
| 319 |
+
|
| 320 |
+
| Config | LR Schedule | Momentum | Steps | Description |
|
| 321 |
+
|--------|-------------|----------|-------|-------------|
|
| 322 |
+
| `cosine_momentum` | Cosine | Standard | 15 | High quality |
|
| 323 |
+
| `cosine_nesterov` | Cosine | Nesterov | 15 | **Recommended** |
|
| 324 |
+
| `linear_nesterov` | Linear | Nesterov | 15 | Stable + fast |
|
| 325 |
+
|
| 326 |
+
### Quality Presets
|
| 327 |
+
|
| 328 |
+
| Config | LR Schedule | Momentum | Steps | Use Case |
|
| 329 |
+
|--------|-------------|----------|-------|----------|
|
| 330 |
+
| `high_quality` | Cosine | Nesterov | 20 | **Best quality** |
|
| 331 |
+
| `aggressive` | Exponential | Standard | 8 | Fast results |
|
| 332 |
+
| `conservative` | Cosine | Nesterov | 25 | Most stable |
|
| 333 |
+
|
| 334 |
+
### Config Details
|
| 335 |
+
|
| 336 |
+
#### `high_quality` (Recommended for Research)
|
| 337 |
+
```python
|
| 338 |
+
{
|
| 339 |
+
"grad_timestep_range": (200, 800), # Focus on middle timesteps
|
| 340 |
+
"num_grad_steps": 20,
|
| 341 |
+
"grad_step_size": 0.08,
|
| 342 |
+
"lr_scheduler_type": "cosine",
|
| 343 |
+
"lr_scheduler_kwargs": {"min_lr": 0.005, "warmup_steps": 5},
|
| 344 |
+
"use_momentum": True,
|
| 345 |
+
"momentum": 0.95,
|
| 346 |
+
"use_nesterov": True
|
| 347 |
+
}
|
| 348 |
+
```
|
| 349 |
+
|
| 350 |
+
#### `cosine_nesterov` (Recommended for General Use)
|
| 351 |
+
```python
|
| 352 |
+
{
|
| 353 |
+
"grad_timestep_range": (0, 700),
|
| 354 |
+
"num_grad_steps": 15,
|
| 355 |
+
"grad_step_size": 0.12,
|
| 356 |
+
"lr_scheduler_type": "cosine",
|
| 357 |
+
"lr_scheduler_kwargs": {"min_lr": 0.001, "warmup_steps": 3},
|
| 358 |
+
"use_momentum": True,
|
| 359 |
+
"momentum": 0.9,
|
| 360 |
+
"use_nesterov": True
|
| 361 |
+
}
|
| 362 |
+
```
|
| 363 |
+
|
| 364 |
+
#### `aggressive` (Fast Experimentation)
|
| 365 |
+
```python
|
| 366 |
+
{
|
| 367 |
+
"grad_timestep_range": (0, 900),
|
| 368 |
+
"num_grad_steps": 8,
|
| 369 |
+
"grad_step_size": 0.15,
|
| 370 |
+
"grad_scale": 1.2,
|
| 371 |
+
"lr_scheduler_type": "exponential",
|
| 372 |
+
"lr_scheduler_kwargs": {"gamma": 0.85},
|
| 373 |
+
"use_momentum": True,
|
| 374 |
+
"momentum": 0.85,
|
| 375 |
+
"use_nesterov": False
|
| 376 |
+
}
|
| 377 |
+
```
|
| 378 |
+
|
| 379 |
+
### Listing Configs
|
| 380 |
+
|
| 381 |
+
```python
|
| 382 |
+
from grad_ascent_configs import list_configs, print_config, get_config
|
| 383 |
+
|
| 384 |
+
# List all available configs
|
| 385 |
+
print(list_configs())
|
| 386 |
+
# Output: ['aggressive', 'conservative', 'constant', 'cosine', ...]
|
| 387 |
+
|
| 388 |
+
# Print config details
|
| 389 |
+
print_config("cosine_nesterov")
|
| 390 |
+
|
| 391 |
+
# Get config dictionary
|
| 392 |
+
config = get_config("high_quality")
|
| 393 |
+
pipeline.enable_gradient_ascent(**config)
|
| 394 |
+
```
|
| 395 |
+
|
| 396 |
+
---
|
| 397 |
+
|
| 398 |
+
## Evaluation Metrics
|
| 399 |
+
|
| 400 |
+
### 1. **FID (Fréchet Inception Distance)**
|
| 401 |
+
- Measures distribution similarity between real and generated images
|
| 402 |
+
- **Lower is better**
|
| 403 |
+
- Requires reference images (COCO dataset only)
|
| 404 |
+
- Computationally expensive
|
| 405 |
+
|
| 406 |
+
```bash
|
| 407 |
+
--metrics fid
|
| 408 |
+
```
|
| 409 |
+
|
| 410 |
+
### 2. **CLIP Score**
|
| 411 |
+
- Evaluates text-image alignment using CLIP embeddings
|
| 412 |
+
- **Higher is better**
|
| 413 |
+
- Fast and reliable
|
| 414 |
+
- Good for general quality assessment
|
| 415 |
+
|
| 416 |
+
```bash
|
| 417 |
+
--metrics clip
|
| 418 |
+
```
|
| 419 |
+
|
| 420 |
+
### 3. **Aesthetic Score**
|
| 421 |
+
- Predicts aesthetic quality using CLIP + MLP
|
| 422 |
+
- **Higher is better**
|
| 423 |
+
- Trained on human aesthetic ratings
|
| 424 |
+
- Good for visual appeal
|
| 425 |
+
|
| 426 |
+
```bash
|
| 427 |
+
--metrics aesthetic
|
| 428 |
+
```
|
| 429 |
+
|
| 430 |
+
### 4. **PickScore** (New)
|
| 431 |
+
- Human preference predictor from Pick-a-Pic dataset
|
| 432 |
+
- **Higher is better**
|
| 433 |
+
- Trained on large-scale human comparisons
|
| 434 |
+
- State-of-the-art preference metric
|
| 435 |
+
|
| 436 |
+
```bash
|
| 437 |
+
--metrics pickscore
|
| 438 |
+
```
|
| 439 |
+
|
| 440 |
+
### 5. **HPSv2** (New)
|
| 441 |
+
- Human Preference Score version 2
|
| 442 |
+
- **Higher is better**
|
| 443 |
+
- Trained on aesthetic evaluations
|
| 444 |
+
- Complementary to PickScore
|
| 445 |
+
|
| 446 |
+
```bash
|
| 447 |
+
--metrics hpsv2
|
| 448 |
+
```
|
| 449 |
+
|
| 450 |
+
### 6. **ImageReward** (New)
|
| 451 |
+
- Reward model from RLHF (Reinforcement Learning from Human Feedback)
|
| 452 |
+
- **Higher is better**
|
| 453 |
+
- Comprehensive quality assessment
|
| 454 |
+
- Trained on diverse human feedback
|
| 455 |
+
|
| 456 |
+
```bash
|
| 457 |
+
--metrics imagereward
|
| 458 |
+
```
|
| 459 |
+
|
| 460 |
+
### Metric Recommendations
|
| 461 |
+
|
| 462 |
+
| Use Case | Recommended Metrics | Reason |
|
| 463 |
+
|----------|---------------------|--------|
|
| 464 |
+
| Research/Papers | `fid clip aesthetic pickscore hpsv2` | Comprehensive evaluation |
|
| 465 |
+
| Quick Iteration | `clip aesthetic` | Fast and reliable |
|
| 466 |
+
| Human Alignment | `pickscore hpsv2 imagereward` | Preference-based |
|
| 467 |
+
| Text Alignment | `clip imagereward` | Focus on prompt adherence |
|
| 468 |
+
| Visual Quality | `aesthetic pickscore` | Focus on aesthetics |
|
| 469 |
+
|
| 470 |
+
---
|
| 471 |
+
|
| 472 |
+
## Model Variants
|
| 473 |
+
|
| 474 |
+
Support for multiple SD1.5 model variants trained with different methods.
|
| 475 |
+
|
| 476 |
+
### Available Variants
|
| 477 |
+
|
| 478 |
+
#### 1. **Origin** (Default)
|
| 479 |
+
```bash
|
| 480 |
+
--model_variant origin
|
| 481 |
+
```
|
| 482 |
+
- Original Stable Diffusion v1.5 from RunwayML
|
| 483 |
+
- No additional training
|
| 484 |
+
- CFG scale: 7.5 (default)
|
| 485 |
+
- Good baseline
|
| 486 |
+
|
| 487 |
+
#### 2. **SPO** (Supervised Policy Optimization)
|
| 488 |
+
```bash
|
| 489 |
+
--model_variant spo
|
| 490 |
+
```
|
| 491 |
+
- Trained with SPO method
|
| 492 |
+
- Model: `SPO-Diffusion-Models/SPO-SD-v1-5_4k-p_10ep`
|
| 493 |
+
- **CFG scale: 5.0** (auto-adjusted)
|
| 494 |
+
- Better prompt adherence
|
| 495 |
+
|
| 496 |
+
#### 3. **Diffusion-DPO** (Direct Preference Optimization)
|
| 497 |
+
```bash
|
| 498 |
+
--model_variant diffusion_dpo
|
| 499 |
+
```
|
| 500 |
+
- Trained with DPO on human preferences
|
| 501 |
+
- Model: `mhdang/dpo-sd1.5-text2image-v1`
|
| 502 |
+
- CFG scale: 7.5
|
| 503 |
+
- Improved human alignment
|
| 504 |
+
|
| 505 |
+
#### 4. **LPO** (Latent Preference Optimization)
|
| 506 |
+
```bash
|
| 507 |
+
--model_variant lpo
|
| 508 |
+
```
|
| 509 |
+
- Trained with LPO (this project's main method)
|
| 510 |
+
- Model: `casiatao/LPO` (lpo_sd15_merge)
|
| 511 |
+
- **CFG scale: 5.0** (auto-adjusted)
|
| 512 |
+
- **Highest quality baseline**
|
| 513 |
+
|
| 514 |
+
### Comparison
|
| 515 |
+
|
| 516 |
+
| Variant | Training Method | Quality | Speed | Best For |
|
| 517 |
+
|---------|----------------|---------|-------|----------|
|
| 518 |
+
| Origin | Pre-training only | Good | Fast | Baseline |
|
| 519 |
+
| SPO | Supervised | Better | Fast | Prompt adherence |
|
| 520 |
+
| Diffusion-DPO | Preference learning | Better | Fast | Human preferences |
|
| 521 |
+
| LPO | Latent preference | **Best** | Fast | Overall quality |
|
| 522 |
+
|
| 523 |
+
### Usage Example
|
| 524 |
+
|
| 525 |
+
```bash
|
| 526 |
+
# Compare all variants
|
| 527 |
+
for variant in origin spo diffusion_dpo lpo; do
|
| 528 |
+
python eval.py \
|
| 529 |
+
--model_variant $variant \
|
| 530 |
+
--grad_config high_quality \
|
| 531 |
+
--metrics clip aesthetic pickscore \
|
| 532 |
+
--max_samples 100 \
|
| 533 |
+
--output_dir results/${variant}
|
| 534 |
+
done
|
| 535 |
+
```
|
| 536 |
+
|
| 537 |
+
---
|
| 538 |
+
|
| 539 |
+
## Datasets
|
| 540 |
+
|
| 541 |
+
### 1. **COCO Validation** (Default)
|
| 542 |
+
|
| 543 |
+
```bash
|
| 544 |
+
--dataset_type coco
|
| 545 |
+
--data_dir ./data
|
| 546 |
+
```
|
| 547 |
+
|
| 548 |
+
**Features:**
|
| 549 |
+
- Standard benchmark dataset
|
| 550 |
+
- Reference images available (for FID)
|
| 551 |
+
- ~5,000 validation samples
|
| 552 |
+
- Diverse prompts
|
| 553 |
+
|
| 554 |
+
**Structure:**
|
| 555 |
+
```
|
| 556 |
+
data/coco/
|
| 557 |
+
├── caption_val.json
|
| 558 |
+
└── images/val/
|
| 559 |
+
├── 000000000139.jpg
|
| 560 |
+
├── 000000000285.jpg
|
| 561 |
+
└── ...
|
| 562 |
+
```
|
| 563 |
+
|
| 564 |
+
### 2. **Pick-a-Pic Validation**
|
| 565 |
+
|
| 566 |
+
```bash
|
| 567 |
+
--dataset_type pickapic
|
| 568 |
+
```
|
| 569 |
+
|
| 570 |
+
**Features:**
|
| 571 |
+
- Large-scale human preference dataset
|
| 572 |
+
- Streaming (no download needed)
|
| 573 |
+
- ~500,000 validation samples
|
| 574 |
+
- Real user prompts
|
| 575 |
+
- No reference images (FID not available)
|
| 576 |
+
|
| 577 |
+
**Advantages:**
|
| 578 |
+
- More diverse prompts
|
| 579 |
+
- Real-world use cases
|
| 580 |
+
- Human preference focus
|
| 581 |
+
- Large-scale evaluation
|
| 582 |
+
|
| 583 |
+
### Dataset Recommendations
|
| 584 |
+
|
| 585 |
+
| Use Case | Dataset | Reason |
|
| 586 |
+
|----------|---------|--------|
|
| 587 |
+
| Academic Research | COCO | Standard benchmark, reproducible |
|
| 588 |
+
| FID Evaluation | COCO | Requires reference images |
|
| 589 |
+
| Human Preference | Pick-a-Pic | Trained on human comparisons |
|
| 590 |
+
| Large-scale Tests | Pick-a-Pic | 500K+ samples available |
|
| 591 |
+
| Quick Tests | COCO | Smaller, faster |
|
| 592 |
+
|
| 593 |
+
---
|
| 594 |
+
|
| 595 |
+
## Usage Examples
|
| 596 |
+
|
| 597 |
+
### Example 1: Quick Test
|
| 598 |
+
```bash
|
| 599 |
+
python eval.py \
|
| 600 |
+
--grad_config cosine_nesterov \
|
| 601 |
+
--metrics clip aesthetic \
|
| 602 |
+
--max_samples 10 \
|
| 603 |
+
--output_dir examples/quick_test
|
| 604 |
+
```
|
| 605 |
+
|
| 606 |
+
### Example 2: High-Quality Research Evaluation
|
| 607 |
+
```bash
|
| 608 |
+
python eval.py \
|
| 609 |
+
--grad_config high_quality \
|
| 610 |
+
--metrics fid clip aesthetic pickscore hpsv2 \
|
| 611 |
+
--max_samples 200 \
|
| 612 |
+
--save_images \
|
| 613 |
+
--output_dir examples/research
|
| 614 |
+
```
|
| 615 |
+
|
| 616 |
+
### Example 3: Pick-a-Pic Benchmark
|
| 617 |
+
```bash
|
| 618 |
+
python eval.py \
|
| 619 |
+
--dataset_type pickapic \
|
| 620 |
+
--grad_config cosine_nesterov \
|
| 621 |
+
--metrics pickscore hpsv2 imagereward \
|
| 622 |
+
--max_samples 500 \
|
| 623 |
+
--output_dir examples/pickapic
|
| 624 |
+
```
|
| 625 |
+
|
| 626 |
+
### Example 4: LPO Model Evaluation
|
| 627 |
+
```bash
|
| 628 |
+
python eval.py \
|
| 629 |
+
--model_variant lpo \
|
| 630 |
+
--grad_config high_quality \
|
| 631 |
+
--metrics clip aesthetic pickscore \
|
| 632 |
+
--max_samples 100 \
|
| 633 |
+
--save_images \
|
| 634 |
+
--output_dir examples/lpo_model
|
| 635 |
+
```
|
| 636 |
+
|
| 637 |
+
### Example 5: Baseline Only (No Gradient Ascent)
|
| 638 |
+
```bash
|
| 639 |
+
python eval.py \
|
| 640 |
+
--mode baseline \
|
| 641 |
+
--model_variant origin \
|
| 642 |
+
--metrics clip aesthetic pickscore \
|
| 643 |
+
--max_samples 50 \
|
| 644 |
+
--output_dir examples/baseline_only
|
| 645 |
+
```
|
| 646 |
+
|
| 647 |
+
### Example 6: Manual Configuration
|
| 648 |
+
```bash
|
| 649 |
+
python eval.py \
|
| 650 |
+
--grad_range_start 200 \
|
| 651 |
+
--grad_range_end 800 \
|
| 652 |
+
--grad_steps 15 \
|
| 653 |
+
--grad_step_size 0.08 \
|
| 654 |
+
--metrics clip aesthetic \
|
| 655 |
+
--max_samples 50 \
|
| 656 |
+
--output_dir examples/manual_config
|
| 657 |
+
```
|
| 658 |
+
|
| 659 |
+
### Example 7: Model Comparison
|
| 660 |
+
```bash
|
| 661 |
+
# Evaluate all model variants
|
| 662 |
+
for variant in origin spo diffusion_dpo lpo; do
|
| 663 |
+
python eval.py \
|
| 664 |
+
--model_variant $variant \
|
| 665 |
+
--grad_config high_quality \
|
| 666 |
+
--metrics clip aesthetic pickscore \
|
| 667 |
+
--max_samples 100 \
|
| 668 |
+
--save_images \
|
| 669 |
+
--output_dir results/comparison/${variant}
|
| 670 |
+
done
|
| 671 |
+
```
|
| 672 |
+
|
| 673 |
+
### Example 8: Conservative Optimization
|
| 674 |
+
```bash
|
| 675 |
+
python eval.py \
|
| 676 |
+
--grad_config conservative \
|
| 677 |
+
--metrics clip aesthetic pickscore hpsv2 \
|
| 678 |
+
--max_samples 100 \
|
| 679 |
+
--save_images \
|
| 680 |
+
--output_dir examples/conservative
|
| 681 |
+
```
|
| 682 |
+
|
| 683 |
+
---
|
| 684 |
+
|
| 685 |
+
## API Reference
|
| 686 |
+
|
| 687 |
+
### Pipeline Usage
|
| 688 |
+
|
| 689 |
+
```python
|
| 690 |
+
from diffusers import StableDiffusionPipeline
|
| 691 |
+
from pipelines.sd15_gradient_ascent_pipeline import StableDiffusionGradientAscentPipeline
|
| 692 |
+
from models import LRMRewardModel
|
| 693 |
+
|
| 694 |
+
# Load base pipeline
|
| 695 |
+
base_pipeline = StableDiffusionPipeline.from_pretrained(
|
| 696 |
+
"runwayml/stable-diffusion-v1-5",
|
| 697 |
+
torch_dtype=torch.float16
|
| 698 |
+
)
|
| 699 |
+
|
| 700 |
+
# Create gradient ascent pipeline
|
| 701 |
+
pipeline = StableDiffusionGradientAscentPipeline(**base_pipeline.components)
|
| 702 |
+
|
| 703 |
+
# Load reward model
|
| 704 |
+
reward_model = LRMRewardModel(
|
| 705 |
+
pretrained_model_name_or_path="runwayml/stable-diffusion-v1-5",
|
| 706 |
+
lrm_model_path="casiatao/LRM",
|
| 707 |
+
guidance_scale=7.5,
|
| 708 |
+
device="cuda"
|
| 709 |
+
)
|
| 710 |
+
pipeline.set_reward_model(reward_model)
|
| 711 |
+
|
| 712 |
+
# Enable gradient ascent with preset
|
| 713 |
+
from grad_ascent_configs import get_config
|
| 714 |
+
config = get_config("cosine_nesterov")
|
| 715 |
+
pipeline.enable_gradient_ascent(**config)
|
| 716 |
+
|
| 717 |
+
# Or configure manually
|
| 718 |
+
pipeline.enable_gradient_ascent(
|
| 719 |
+
grad_timestep_range=(200, 800),
|
| 720 |
+
num_grad_steps=15,
|
| 721 |
+
grad_step_size=0.1,
|
| 722 |
+
lr_scheduler_type="cosine",
|
| 723 |
+
lr_scheduler_kwargs={"min_lr": 0.001, "warmup_steps": 3},
|
| 724 |
+
use_momentum=True,
|
| 725 |
+
momentum=0.9,
|
| 726 |
+
use_nesterov=True
|
| 727 |
+
)
|
| 728 |
+
|
| 729 |
+
# Generate with gradient ascent
|
| 730 |
+
output = pipeline(
|
| 731 |
+
prompt="a beautiful mountain landscape at sunset",
|
| 732 |
+
num_inference_steps=50,
|
| 733 |
+
guidance_scale=7.5,
|
| 734 |
+
)
|
| 735 |
+
|
| 736 |
+
# Get gradient statistics
|
| 737 |
+
stats = pipeline.grad_guidance.get_statistics()
|
| 738 |
+
print(f"Reward improvement: {stats['avg_reward_improvement']:.4f}")
|
| 739 |
+
```
|
| 740 |
+
|
| 741 |
+
### Custom LR Scheduler
|
| 742 |
+
|
| 743 |
+
```python
|
| 744 |
+
from lr_scheduler import create_lr_scheduler
|
| 745 |
+
|
| 746 |
+
# Create cosine scheduler with warmup
|
| 747 |
+
scheduler = create_lr_scheduler(
|
| 748 |
+
scheduler_type="cosine",
|
| 749 |
+
initial_lr=0.1,
|
| 750 |
+
num_steps=20,
|
| 751 |
+
min_lr=0.001,
|
| 752 |
+
warmup_steps=5
|
| 753 |
+
)
|
| 754 |
+
|
| 755 |
+
# Use in optimization loop
|
| 756 |
+
for step in range(20):
|
| 757 |
+
current_lr = scheduler.get_lr()
|
| 758 |
+
# ... apply gradient with current_lr ...
|
| 759 |
+
scheduler.step()
|
| 760 |
+
```
|
| 761 |
+
|
| 762 |
+
### Configuration Management
|
| 763 |
+
|
| 764 |
+
```python
|
| 765 |
+
from grad_ascent_configs import get_config, list_configs, print_config
|
| 766 |
+
|
| 767 |
+
# List all available configs
|
| 768 |
+
all_configs = list_configs()
|
| 769 |
+
print(f"Available configs: {all_configs}")
|
| 770 |
+
|
| 771 |
+
# Get specific config
|
| 772 |
+
config = get_config("high_quality")
|
| 773 |
+
|
| 774 |
+
# Print config details
|
| 775 |
+
print_config("cosine_nesterov")
|
| 776 |
+
|
| 777 |
+
# Create custom config
|
| 778 |
+
custom_config = {
|
| 779 |
+
"grad_timestep_range": (300, 700),
|
| 780 |
+
"num_grad_steps": 12,
|
| 781 |
+
"grad_step_size": 0.09,
|
| 782 |
+
"lr_scheduler_type": "cosine",
|
| 783 |
+
"lr_scheduler_kwargs": {"min_lr": 0.002, "warmup_steps": 4},
|
| 784 |
+
"use_momentum": True,
|
| 785 |
+
"momentum": 0.92,
|
| 786 |
+
"use_nesterov": True
|
| 787 |
+
}
|
| 788 |
+
pipeline.enable_gradient_ascent(**custom_config)
|
| 789 |
+
```
|
| 790 |
+
|
| 791 |
+
---
|
| 792 |
+
|
| 793 |
+
## Command-Line Options
|
| 794 |
+
|
| 795 |
+
### Essential Options
|
| 796 |
+
|
| 797 |
+
```bash
|
| 798 |
+
--data_dir PATH # Path to data directory (default: ./data)
|
| 799 |
+
--dataset_type TYPE # Dataset: coco or pickapic (default: coco)
|
| 800 |
+
--model_variant VARIANT # Model: origin, spo, diffusion_dpo, lpo (default: origin)
|
| 801 |
+
--max_samples N # Max samples to evaluate (default: all)
|
| 802 |
+
--output_dir PATH # Output directory (default: eval_outputs)
|
| 803 |
+
--save_images # Save generated images
|
| 804 |
+
```
|
| 805 |
+
|
| 806 |
+
### Gradient Ascent Options
|
| 807 |
+
|
| 808 |
+
```bash
|
| 809 |
+
--grad_config NAME # Use preset config (recommended)
|
| 810 |
+
--grad_range_start N # Gradient timestep start (default: 0)
|
| 811 |
+
--grad_range_end N # Gradient timestep end (default: 700)
|
| 812 |
+
--grad_steps N # Gradient steps per timestep (default: 5)
|
| 813 |
+
--grad_step_size FLOAT # Initial learning rate (default: 0.1)
|
| 814 |
+
```
|
| 815 |
+
|
| 816 |
+
### Evaluation Options
|
| 817 |
+
|
| 818 |
+
```bash
|
| 819 |
+
--metrics METRIC [METRIC...] # Metrics to evaluate (default: clip aesthetic)
|
| 820 |
+
# Options: fid, clip, aesthetic, pickscore, hpsv2, imagereward
|
| 821 |
+
--mode MODE # baseline, gradient_ascent, or both (default: both)
|
| 822 |
+
--num_steps N # Diffusion inference steps (default: 50)
|
| 823 |
+
--cfg_scale FLOAT # CFG scale (default: 7.5, auto-adjusted for some models)
|
| 824 |
+
--batch_size N # Batch size (default: 1)
|
| 825 |
+
--log_interval N # Log every N batches (default: 10)
|
| 826 |
+
```
|
| 827 |
+
|
| 828 |
+
### Other Options
|
| 829 |
+
|
| 830 |
+
```bash
|
| 831 |
+
--lrm_model PATH # LRM model path (default: casiatao/LRM)
|
| 832 |
+
--seed N # Random seed (default: 42)
|
| 833 |
+
--cuda N # CUDA device ID (default: 0)
|
| 834 |
+
```
|
| 835 |
+
|
| 836 |
+
### Complete Example
|
| 837 |
+
|
| 838 |
+
```bash
|
| 839 |
+
python eval.py \
|
| 840 |
+
--data_dir ./data \
|
| 841 |
+
--dataset_type coco \
|
| 842 |
+
--model_variant lpo \
|
| 843 |
+
--grad_config high_quality \
|
| 844 |
+
--metrics fid clip aesthetic pickscore hpsv2 \
|
| 845 |
+
--max_samples 200 \
|
| 846 |
+
--num_steps 50 \
|
| 847 |
+
--save_images \
|
| 848 |
+
--output_dir results/comprehensive \
|
| 849 |
+
--cuda 0
|
| 850 |
+
```
|
| 851 |
+
|
| 852 |
+
---
|
| 853 |
+
|
| 854 |
+
## Output Files
|
| 855 |
+
|
| 856 |
+
After running evaluation, the following files are created in **auto-incremented run folders**:
|
| 857 |
+
|
| 858 |
+
```
|
| 859 |
+
RESULTS/SD1.5_GradAscent/
|
| 860 |
+
├── run_1/ # First run
|
| 861 |
+
│ ├── eval.log # Complete execution log
|
| 862 |
+
│ └── reward_curve.png # Reward progression plot
|
| 863 |
+
├── run_2/ # Second run
|
| 864 |
+
│ ├── eval.log
|
| 865 |
+
│ └── reward_curve.png
|
| 866 |
+
└── run_3/ # Third run
|
| 867 |
+
├── eval.log
|
| 868 |
+
└── reward_curve.png
|
| 869 |
+
```
|
| 870 |
+
|
| 871 |
+
### Auto-Incrementing Run Folders
|
| 872 |
+
|
| 873 |
+
Each execution automatically creates a new `run_<N>/` folder, preventing accidental overwrites and maintaining a complete experiment history. No manual folder management needed!
|
| 874 |
+
|
| 875 |
+
### eval.log Structure
|
| 876 |
+
|
| 877 |
+
The log contains detailed information for each batch:
|
| 878 |
+
|
| 879 |
+
```
|
| 880 |
+
======================================================================
|
| 881 |
+
COCO GRADIENT ASCENT EVALUATION (BATCHED)
|
| 882 |
+
======================================================================
|
| 883 |
+
Logging to: ./RESULTS/SD1.5_GradAscent/run_1/eval.log
|
| 884 |
+
Device: cuda:6
|
| 885 |
+
Batch size: 1
|
| 886 |
+
Metrics: fid, clip, reward, aesthetic
|
| 887 |
+
Gradient Ascent: Range=[0, 900], Steps=1, StepSize=0.01
|
| 888 |
+
======================================================================
|
| 889 |
+
|
| 890 |
+
[Batch 1/5000] Samples: 1/5000 | FID: 2.5432 | CLIP: 0.8234 | Reward (t=0): 5.2341 | Reward (Avg): 5.2341 | Aesthetic: 6.456
|
| 891 |
+
[Batch 161/5000] Samples: 161/5000 | FID: 2.3821 | CLIP: 0.8412 | Reward (t=0): 5.4123 | Reward (Avg): 5.3215 | Aesthetic: 6.523
|
| 892 |
+
...
|
| 893 |
+
|
| 894 |
+
======================================================================
|
| 895 |
+
FINAL RESULTS
|
| 896 |
+
======================================================================
|
| 897 |
+
FID: 2.3456
|
| 898 |
+
CLIP avg: 0.8378
|
| 899 |
+
Reward avg: 5.3421
|
| 900 |
+
Aesthetic: 6.489
|
| 901 |
+
======================================================================
|
| 902 |
+
```
|
| 903 |
+
|
| 904 |
+
### reward_curve.png Visualization
|
| 905 |
+
|
| 906 |
+
The reward curve plot shows two panels for the **first generated image**:
|
| 907 |
+
|
| 908 |
+
**Left Panel: Reward vs Timestep**
|
| 909 |
+
- X-axis: Denoising timestep (t)
|
| 910 |
+
- Y-axis: Reward score
|
| 911 |
+
- Green shaded region: Where gradient ascent is applied
|
| 912 |
+
- Shows how reward evolves as noise is removed
|
| 913 |
+
|
| 914 |
+
**Right Panel: Reward vs Denoising Step**
|
| 915 |
+
- X-axis: Sequential denoising step (0 to num_inference_steps)
|
| 916 |
+
- Y-axis: Reward score
|
| 917 |
+
- Same data, different perspective for easier interpretation
|
| 918 |
+
|
| 919 |
+
**Key Insights from the Plot:**
|
| 920 |
+
- **Upward trend**: Reward generally increases as denoising progresses
|
| 921 |
+
- **Sharp improvements**: Visible spikes where gradient ascent is effective
|
| 922 |
+
- **Final reward**: Last point corresponds to t=0 (decoded image reward)
|
| 923 |
+
- **Learning dynamics**: Shows if optimization is working at different noise levels
|
| 924 |
+
|
| 925 |
+
### Reward Tracking Details
|
| 926 |
+
|
| 927 |
+
The script now explicitly tracks:
|
| 928 |
+
|
| 929 |
+
1. **Timestep-specific rewards**: Computed at every denoising step
|
| 930 |
+
2. **Final latent reward**: The reward for t=0 (the latent that gets decoded)
|
| 931 |
+
3. **Running average**: Mean reward across all processed samples
|
| 932 |
+
4. **Current batch reward**: Immediate feedback per batch
|
| 933 |
+
|
| 934 |
+
Example log output:
|
| 935 |
+
```
|
| 936 |
+
Reward (t=0): 5.4123 # Reward for the final decoded latent
|
| 937 |
+
Reward (Avg): 5.3215 # Running average across all samples
|
| 938 |
+
```
|
| 939 |
+
|
| 940 |
+
### evaluation_results.json Structure
|
| 941 |
+
|
| 942 |
+
(Legacy format from eval.py - test_grad_sd1.5.py uses simplified logging)
|
| 943 |
+
|
| 944 |
+
```json
|
| 945 |
+
{
|
| 946 |
+
"mode": "both",
|
| 947 |
+
"metrics": ["clip", "aesthetic", "pickscore"],
|
| 948 |
+
"config": {
|
| 949 |
+
"num_samples": 100,
|
| 950 |
+
"num_steps": 50,
|
| 951 |
+
"cfg_scale": 7.5,
|
| 952 |
+
"grad_range": [0, 700],
|
| 953 |
+
"grad_steps": 15,
|
| 954 |
+
"grad_step_size": 0.12
|
| 955 |
+
},
|
| 956 |
+
"baseline": {
|
| 957 |
+
"avg_reward": 0.7234,
|
| 958 |
+
"clip_score": 0.8123,
|
| 959 |
+
"aesthetic_score": 6.234,
|
| 960 |
+
"pickscore": 21.45
|
| 961 |
+
},
|
| 962 |
+
"gradient_ascent": {
|
| 963 |
+
"avg_reward": 0.7891,
|
| 964 |
+
"clip_score": 0.8345,
|
| 965 |
+
"aesthetic_score": 6.456,
|
| 966 |
+
"pickscore": 22.13,
|
| 967 |
+
"stats": {
|
| 968 |
+
"num_applications": 45,
|
| 969 |
+
"total_reward_improvement": 2.956,
|
| 970 |
+
"avg_reward_improvement": 0.0657
|
| 971 |
+
}
|
| 972 |
+
},
|
| 973 |
+
"comparison": {
|
| 974 |
+
"reward_difference": 0.0657,
|
| 975 |
+
"clip_difference": 0.0222,
|
| 976 |
+
"aesthetic_difference": 0.222,
|
| 977 |
+
"pickscore_difference": 0.68
|
| 978 |
+
}
|
| 979 |
+
}
|
| 980 |
+
```
|
| 981 |
+
|
| 982 |
+
---
|
| 983 |
+
|
| 984 |
+
## Troubleshooting
|
| 985 |
+
|
| 986 |
+
### Common Issues
|
| 987 |
+
|
| 988 |
+
#### 1. Out of Memory (OOM)
|
| 989 |
+
|
| 990 |
+
**Symptoms:**
|
| 991 |
+
```
|
| 992 |
+
RuntimeError: CUDA out of memory
|
| 993 |
+
```
|
| 994 |
+
|
| 995 |
+
**Solutions:**
|
| 996 |
+
```bash
|
| 997 |
+
# Reduce batch size
|
| 998 |
+
--batch_size 1
|
| 999 |
+
|
| 1000 |
+
# Reduce max samples
|
| 1001 |
+
--max_samples 50
|
| 1002 |
+
|
| 1003 |
+
# Reduce gradient steps
|
| 1004 |
+
--grad_steps 5
|
| 1005 |
+
|
| 1006 |
+
# Use smaller config
|
| 1007 |
+
--grad_config aggressive # Only 8 steps
|
| 1008 |
+
```
|
| 1009 |
+
|
| 1010 |
+
#### 2. Slow Evaluation
|
| 1011 |
+
|
| 1012 |
+
**Symptoms:**
|
| 1013 |
+
- Takes too long to complete
|
| 1014 |
+
- Hanging on metric computation
|
| 1015 |
+
|
| 1016 |
+
**Solutions:**
|
| 1017 |
+
```bash
|
| 1018 |
+
# Skip expensive metrics
|
| 1019 |
+
--metrics clip aesthetic # Skip FID
|
| 1020 |
+
|
| 1021 |
+
# Reduce samples
|
| 1022 |
+
--max_samples 50
|
| 1023 |
+
|
| 1024 |
+
# Reduce diffusion steps
|
| 1025 |
+
--num_steps 20
|
| 1026 |
+
|
| 1027 |
+
# Use faster dataset
|
| 1028 |
+
--dataset_type pickapic # No FID computation
|
| 1029 |
+
```
|
| 1030 |
+
|
| 1031 |
+
#### 3. Poor Results / No Improvement
|
| 1032 |
+
|
| 1033 |
+
**Symptoms:**
|
| 1034 |
+
- Reward doesn't increase
|
| 1035 |
+
- Quality worse after gradient ascent
|
| 1036 |
+
|
| 1037 |
+
**Solutions:**
|
| 1038 |
+
```bash
|
| 1039 |
+
# Try better configs
|
| 1040 |
+
--grad_config high_quality
|
| 1041 |
+
--grad_config conservative
|
| 1042 |
+
|
| 1043 |
+
# Increase gradient steps
|
| 1044 |
+
--grad_steps 20
|
| 1045 |
+
|
| 1046 |
+
# Adjust timestep range (focus on middle)
|
| 1047 |
+
--grad_range_start 200 --grad_range_end 800
|
| 1048 |
+
|
| 1049 |
+
# Try different model variant
|
| 1050 |
+
--model_variant lpo
|
| 1051 |
+
```
|
| 1052 |
+
|
| 1053 |
+
#### 4. Config Not Found
|
| 1054 |
+
|
| 1055 |
+
**Symptoms:**
|
| 1056 |
+
```
|
| 1057 |
+
ValueError: Unknown config: my_config
|
| 1058 |
+
```
|
| 1059 |
+
|
| 1060 |
+
**Solutions:**
|
| 1061 |
+
```bash
|
| 1062 |
+
# List available configs
|
| 1063 |
+
python -c "from grad_ascent_configs import list_configs; print(list_configs())"
|
| 1064 |
+
|
| 1065 |
+
# Print config details
|
| 1066 |
+
python -c "from grad_ascent_configs import print_config; print_config('high_quality')"
|
| 1067 |
+
```
|
| 1068 |
+
|
| 1069 |
+
#### 5. Metric Loading Errors
|
| 1070 |
+
|
| 1071 |
+
**Symptoms:**
|
| 1072 |
+
```
|
| 1073 |
+
Warning: Could not load PickScore scorer
|
| 1074 |
+
```
|
| 1075 |
+
|
| 1076 |
+
**Solutions:**
|
| 1077 |
+
```bash
|
| 1078 |
+
# Install missing dependencies
|
| 1079 |
+
pip install transformers datasets
|
| 1080 |
+
|
| 1081 |
+
# Check HuggingFace Hub access
|
| 1082 |
+
huggingface-cli login
|
| 1083 |
+
|
| 1084 |
+
# Skip problematic metrics
|
| 1085 |
+
--metrics clip aesthetic # Skip pickscore if it fails
|
| 1086 |
+
```
|
| 1087 |
+
|
| 1088 |
+
#### 6. Dataset Not Found
|
| 1089 |
+
|
| 1090 |
+
**Symptoms:**
|
| 1091 |
+
```
|
| 1092 |
+
FileNotFoundError: Validation JSON not found
|
| 1093 |
+
```
|
| 1094 |
+
|
| 1095 |
+
**Solutions:**
|
| 1096 |
+
```bash
|
| 1097 |
+
# Check data directory structure
|
| 1098 |
+
ls data/coco/
|
| 1099 |
+
|
| 1100 |
+
# Use Pick-a-Pic instead (no local files needed)
|
| 1101 |
+
--dataset_type pickapic
|
| 1102 |
+
|
| 1103 |
+
# Provide correct data path
|
| 1104 |
+
--data_dir /path/to/your/data
|
| 1105 |
+
```
|
| 1106 |
+
|
| 1107 |
+
---
|
| 1108 |
+
|
| 1109 |
+
## Best Practices
|
| 1110 |
+
|
| 1111 |
+
### 1. **Start Small, Scale Up**
|
| 1112 |
+
|
| 1113 |
+
```bash
|
| 1114 |
+
# First: Quick test (10 samples)
|
| 1115 |
+
python eval.py --grad_config cosine_nesterov --metrics clip --max_samples 10
|
| 1116 |
+
|
| 1117 |
+
# Then: Medium test (50 samples)
|
| 1118 |
+
python eval.py --grad_config cosine_nesterov --metrics clip aesthetic --max_samples 50
|
| 1119 |
+
|
| 1120 |
+
# Finally: Full evaluation (200+ samples)
|
| 1121 |
+
python eval.py --grad_config high_quality --metrics fid clip aesthetic pickscore hpsv2 --max_samples 200
|
| 1122 |
+
```
|
| 1123 |
+
|
| 1124 |
+
### 2. **Choose Right Config for Use Case**
|
| 1125 |
+
|
| 1126 |
+
| Goal | Config | Metrics |
|
| 1127 |
+
|------|--------|---------|
|
| 1128 |
+
| Quick experiment | `cosine_nesterov` | `clip` |
|
| 1129 |
+
| Research paper | `high_quality` | `fid clip aesthetic pickscore hpsv2` |
|
| 1130 |
+
| Production | `conservative` | `pickscore hpsv2` |
|
| 1131 |
+
| Fast iteration | `aggressive` | `clip aesthetic` |
|
| 1132 |
+
|
| 1133 |
+
### 3. **Use Multiple Metrics**
|
| 1134 |
+
|
| 1135 |
+
Don't rely on a single metric. Recommended combinations:
|
| 1136 |
+
|
| 1137 |
+
```bash
|
| 1138 |
+
# Text alignment + aesthetics
|
| 1139 |
+
--metrics clip aesthetic
|
| 1140 |
+
|
| 1141 |
+
# Human preference focus
|
| 1142 |
+
--metrics pickscore hpsv2 imagereward
|
| 1143 |
+
|
| 1144 |
+
# Comprehensive (research)
|
| 1145 |
+
--metrics fid clip aesthetic pickscore hpsv2
|
| 1146 |
+
```
|
| 1147 |
+
|
| 1148 |
+
### 4. **Save Important Runs**
|
| 1149 |
+
|
| 1150 |
+
```bash
|
| 1151 |
+
# Always save images for important evaluations
|
| 1152 |
+
--save_images --output_dir results/important_run_$(date +%Y%m%d)
|
| 1153 |
+
```
|
| 1154 |
+
|
| 1155 |
+
### 5. **Monitor GPU Usage**
|
| 1156 |
+
|
| 1157 |
+
```bash
|
| 1158 |
+
# In separate terminal
|
| 1159 |
+
watch -n 1 nvidia-smi
|
| 1160 |
+
|
| 1161 |
+
# Or use
|
| 1162 |
+
gpustat -i 1
|
| 1163 |
+
```
|
| 1164 |
+
|
| 1165 |
+
### 6. **Batch Evaluation**
|
| 1166 |
+
|
| 1167 |
+
```bash
|
| 1168 |
+
# Create evaluation script
|
| 1169 |
+
cat << 'EOF' > run_evals.sh
|
| 1170 |
+
#!/bin/bash
|
| 1171 |
+
for config in cosine_nesterov high_quality conservative; do
|
| 1172 |
+
for model in origin lpo; do
|
| 1173 |
+
python eval.py \
|
| 1174 |
+
--model_variant $model \
|
| 1175 |
+
--grad_config $config \
|
| 1176 |
+
--metrics clip aesthetic pickscore \
|
| 1177 |
+
--max_samples 100 \
|
| 1178 |
+
--save_images \
|
| 1179 |
+
--output_dir results/${model}_${config}
|
| 1180 |
+
done
|
| 1181 |
+
done
|
| 1182 |
+
EOF
|
| 1183 |
+
|
| 1184 |
+
chmod +x run_evals.sh
|
| 1185 |
+
./run_evals.sh
|
| 1186 |
+
```
|
| 1187 |
+
|
| 1188 |
+
### 7. **Reproducibility**
|
| 1189 |
+
|
| 1190 |
+
```bash
|
| 1191 |
+
# Always set seed for reproducible results
|
| 1192 |
+
--seed 42
|
| 1193 |
+
|
| 1194 |
+
# Document your runs
|
| 1195 |
+
--output_dir results/experiment_name_$(date +%Y%m%d_%H%M)
|
| 1196 |
+
```
|
| 1197 |
+
|
| 1198 |
+
### 8. **Performance Tips**
|
| 1199 |
+
|
| 1200 |
+
- Use `batch_size=1` for safety (reward model compatibility)
|
| 1201 |
+
- Start with `--max_samples 10` for debugging
|
| 1202 |
+
- Use `--dataset_type pickapic` for large-scale evaluation (no FID overhead)
|
| 1203 |
+
- Skip `fid` metric if not needed (expensive)
|
| 1204 |
+
- Use `--num_steps 20-30` for faster generation (vs default 50)
|
| 1205 |
+
|
| 1206 |
+
### 9. **Config Selection Guide**
|
| 1207 |
+
|
| 1208 |
+
```python
|
| 1209 |
+
# Start here
|
| 1210 |
+
if "just_testing":
|
| 1211 |
+
config = "constant"
|
| 1212 |
+
|
| 1213 |
+
# General use
|
| 1214 |
+
elif "standard_evaluation":
|
| 1215 |
+
config = "cosine_nesterov" # Best balance
|
| 1216 |
+
|
| 1217 |
+
# Research/papers
|
| 1218 |
+
elif "need_best_quality":
|
| 1219 |
+
config = "high_quality" # 20 steps, nesterov
|
| 1220 |
+
|
| 1221 |
+
# Fast experiments
|
| 1222 |
+
elif "need_speed":
|
| 1223 |
+
config = "aggressive" # 8 steps
|
| 1224 |
+
|
| 1225 |
+
# Stability critical
|
| 1226 |
+
elif "need_stability":
|
| 1227 |
+
config = "conservative" # 25 steps, careful
|
| 1228 |
+
```
|
| 1229 |
+
|
| 1230 |
+
### 10. **Timestep Range Tips**
|
| 1231 |
+
|
| 1232 |
+
```python
|
| 1233 |
+
# Full range (default)
|
| 1234 |
+
--grad_range_start 0 --grad_range_end 700
|
| 1235 |
+
|
| 1236 |
+
# Middle timesteps (often best)
|
| 1237 |
+
--grad_range_start 200 --grad_range_end 800
|
| 1238 |
+
|
| 1239 |
+
# Early timesteps (structure)
|
| 1240 |
+
--grad_range_start 500 --grad_range_end 1000
|
| 1241 |
+
|
| 1242 |
+
# Late timesteps (details)
|
| 1243 |
+
--grad_range_start 0 --grad_range_end 400
|
| 1244 |
+
```
|
| 1245 |
+
|
| 1246 |
+
---
|
| 1247 |
+
|
| 1248 |
+
## Performance Metrics
|
| 1249 |
+
|
| 1250 |
+
### Expected Results
|
| 1251 |
+
|
| 1252 |
+
Based on COCO validation set (100 samples):
|
| 1253 |
+
|
| 1254 |
+
| Method | CLIP ↑ | Aesthetic ↑ | PickScore ↑ | Time |
|
| 1255 |
+
|--------|--------|-------------|-------------|------|
|
| 1256 |
+
| Baseline (Origin) | 0.812 | 6.23 | 21.4 | 5 min |
|
| 1257 |
+
| + Constant | 0.819 | 6.28 | 21.6 | 6 min |
|
| 1258 |
+
| + Cosine Nesterov | 0.834 | 6.45 | 22.1 | 8 min |
|
| 1259 |
+
| + High Quality | 0.841 | 6.52 | 22.4 | 12 min |
|
| 1260 |
+
| Baseline (LPO) | 0.856 | 6.67 | 22.8 | 5 min |
|
| 1261 |
+
| LPO + High Quality | 0.873 | 6.89 | 23.5 | 12 min |
|
| 1262 |
+
|
| 1263 |
+
*Results may vary based on hardware and specific prompts*
|
| 1264 |
+
|
| 1265 |
+
---
|
| 1266 |
+
|
| 1267 |
+
## Citation
|
| 1268 |
+
|
| 1269 |
+
If you use this code in your research, please cite:
|
| 1270 |
+
|
| 1271 |
+
```bibtex
|
| 1272 |
+
@article{lpo2024,
|
| 1273 |
+
title={Latent Preference Optimization for Diffusion Models},
|
| 1274 |
+
author={Your Name},
|
| 1275 |
+
journal={arXiv preprint},
|
| 1276 |
+
year={2024}
|
| 1277 |
+
}
|
| 1278 |
+
```
|
| 1279 |
+
|
| 1280 |
+
---
|
| 1281 |
+
|
| 1282 |
+
## License
|
| 1283 |
+
|
| 1284 |
+
This project follows the license of the main LPO repository.
|
| 1285 |
+
|
| 1286 |
+
---
|
| 1287 |
+
|
| 1288 |
+
## Contributing
|
| 1289 |
+
|
| 1290 |
+
Contributions are welcome! Please:
|
| 1291 |
+
|
| 1292 |
+
1. Test your changes with `--max_samples 10`
|
| 1293 |
+
2. Document new features in this README
|
| 1294 |
+
3. Add examples to `examples.sh`
|
| 1295 |
+
4. Follow existing code style
|
| 1296 |
+
|
| 1297 |
+
---
|
| 1298 |
+
|
| 1299 |
+
## Support
|
| 1300 |
+
|
| 1301 |
+
For issues and questions:
|
| 1302 |
+
|
| 1303 |
+
1. Check [Troubleshooting](#troubleshooting) section
|
| 1304 |
+
2. Review [Examples](#usage-examples)
|
| 1305 |
+
3. Open an issue on GitHub
|
| 1306 |
+
|
| 1307 |
+
---
|
| 1308 |
+
|
| 1309 |
+
## Changelog
|
| 1310 |
+
|
| 1311 |
+
### Latest Version (January 2026)
|
| 1312 |
+
|
| 1313 |
+
**New Features:**
|
| 1314 |
+
- ✨ Learning rate scheduling (constant, linear, cosine, exponential, step)
|
| 1315 |
+
- ✨ Momentum optimization (standard and Nesterov)
|
| 1316 |
+
- ✨ 15 configuration presets
|
| 1317 |
+
- ✨ Additional metrics (PickScore, HPSv2, ImageReward)
|
| 1318 |
+
- ✨ Pick-a-Pic validation dataset support
|
| 1319 |
+
- ✨ SD1.5 model variants (Origin, SPO, DPO, LPO)
|
| 1320 |
+
- ✨ Comprehensive evaluation framework
|
| 1321 |
+
- ✨ **Automatic run folder creation** - Each run creates `run_1/`, `run_2/`, etc.
|
| 1322 |
+
- ✨ **Reward curve visualization** - Automatic plotting of reward progression across timesteps
|
| 1323 |
+
- ✨ **Final timestep reward tracking** - Reports reward specifically from t=0 (decoded latent)
|
| 1324 |
+
- ✨ **Detailed reward logging** - Shows both last timestep reward and running average
|
| 1325 |
+
|
| 1326 |
+
**Improvements:**
|
| 1327 |
+
- 🚀 Better convergence with LR scheduling
|
| 1328 |
+
- 🚀 Faster optimization with momentum
|
| 1329 |
+
- 📊 More comprehensive quality assessment
|
| 1330 |
+
- 📊 Visual feedback with reward curve plots
|
| 1331 |
+
- 📚 Complete documentation
|
| 1332 |
+
- 🔍 Enhanced debugging with timestep-specific reward tracking
|
| 1333 |
+
|
| 1334 |
+
---
|
| 1335 |
+
|
| 1336 |
+
**Happy Optimizing! 🚀**
|
Reward_sd15_idealized/RESULTS/pickapic/one_step_rectification_config_spo/run_1/evaluation_results.txt
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
mode: gradient_ascent
|
| 2 |
+
metrics: ['clip', 'aesthetic', 'pickscore', 'hpsv2', 'hpsv21', 'imagereward']
|
| 3 |
+
config: {'num_samples': 500, 'num_steps': 20, 'cfg_scale': 5.0, 'grad_range': [0, 700], 'grad_steps': 5, 'grad_step_size': 0.1}
|
| 4 |
+
gradient_ascent: {'avg_reward': np.float64(0.69980224609375), 'clip_score': np.float64(26.02208717751503), 'aesthetic_score': np.float64(5.953624136924744), 'pickscore': np.float64(21.460845840454102), 'hpsv2_score': np.float16(0.2727), 'hpsv21_score': np.float16(0.274), 'imagereward_score': np.float64(0.412498972608475), 'stats': {'num_applications': 12, 'total_reward_improvement': 1.29296875, 'avg_reward_improvement': 0.10774739583333333, 'avg_grad_norm': 0.11500459335123499, 'max_grad_norm': 0.15467031300067902, 'detailed_stats': [{'timestep': 751, 'initial_reward': 0.04803466796875, 'final_reward': 0.09844970703125, 'reward_improvement': 0.0504150390625, 'grad_norms': [0.02852064184844494], 'reward_history': [0.04803466796875, 0.04803466796875], 'lr_history': [1.0], 'latent_change': 1.0}, {'timestep': 701, 'initial_reward': 0.1910400390625, 'final_reward': 0.340576171875, 'reward_improvement': 0.1495361328125, 'grad_norms': [0.11714287102222443], 'reward_history': [0.1910400390625, 0.1910400390625], 'lr_history': [1.0], 'latent_change': 1.0}, {'timestep': 651, 'initial_reward': 0.489013671875, 'final_reward': 0.6376953125, 'reward_improvement': 0.148681640625, 'grad_norms': [0.15467031300067902], 'reward_history': [0.489013671875, 0.489013671875], 'lr_history': [1.0], 'latent_change': 1.0009765625}, {'timestep': 601, 'initial_reward': 0.6181640625, 'final_reward': 0.7470703125, 'reward_improvement': 0.12890625, 'grad_norms': [0.14603829383850098], 'reward_history': [0.6181640625, 0.6181640625], 'lr_history': [1.0], 'latent_change': 1.0009765625}, {'timestep': 551, 'initial_reward': 0.6640625, 'final_reward': 0.7841796875, 'reward_improvement': 0.1201171875, 'grad_norms': [0.13933144509792328], 'reward_history': [0.6640625, 0.6640625], 'lr_history': [1.0], 'latent_change': 1.0}, {'timestep': 501, 'initial_reward': 0.650390625, 'final_reward': 0.7744140625, 'reward_improvement': 0.1240234375, 'grad_norms': [0.1410745233297348], 'reward_history': [0.650390625, 0.650390625], 'lr_history': [1.0], 'latent_change': 1.0}, {'timestep': 451, 'initial_reward': 0.63134765625, 'final_reward': 0.76025390625, 'reward_improvement': 0.12890625, 'grad_norms': [0.14325274527072906], 'reward_history': [0.63134765625, 0.63134765625], 'lr_history': [1.0], 'latent_change': 1.0}, {'timestep': 401, 'initial_reward': 0.65869140625, 'final_reward': 0.77978515625, 'reward_improvement': 0.12109375, 'grad_norms': [0.13541191816329956], 'reward_history': [0.65869140625, 0.65869140625], 'lr_history': [1.0], 'latent_change': 1.0}, {'timestep': 351, 'initial_reward': 0.7109375, 'final_reward': 0.8173828125, 'reward_improvement': 0.1064453125, 'grad_norms': [0.12057284265756607], 'reward_history': [0.7109375, 0.7109375], 'lr_history': [1.0], 'latent_change': 1.0}, {'timestep': 301, 'initial_reward': 0.76318359375, 'final_reward': 0.849609375, 'reward_improvement': 0.08642578125, 'grad_norms': [0.10099449008703232], 'reward_history': [0.76318359375, 0.76318359375], 'lr_history': [1.0], 'latent_change': 1.0}, {'timestep': 251, 'initial_reward': 0.80810546875, 'final_reward': 0.87841796875, 'reward_improvement': 0.0703125, 'grad_norms': [0.08335024118423462], 'reward_history': [0.80810546875, 0.80810546875], 'lr_history': [1.0], 'latent_change': 1.0}, {'timestep': 201, 'initial_reward': 0.8427734375, 'final_reward': 0.90087890625, 'reward_improvement': 0.05810546875, 'grad_norms': [0.06969479471445084], 'reward_history': [0.8427734375, 0.8427734375], 'lr_history': [1.0], 'latent_change': 1.0}]}}
|
Reward_sd15_idealized/RESULTS/pickapic/one_step_rectification_config_spo/run_1/log.log
ADDED
|
@@ -0,0 +1,229 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
======================================================================
|
| 2 |
+
FID EVALUATION: BASELINE vs GRADIENT ASCENT
|
| 3 |
+
======================================================================
|
| 4 |
+
|
| 5 |
+
Logging to: RESULTS/pickapic/one_step_rectification_config_spo/run_1/log.log
|
| 6 |
+
|
| 7 |
+
Device: cuda:0
|
| 8 |
+
Dataset: PICKAPIC
|
| 9 |
+
Data directory: ./data
|
| 10 |
+
Base model: stable-diffusion-v1-5/stable-diffusion-v1-5
|
| 11 |
+
Model variant: spo
|
| 12 |
+
LRM model: /g/data/rr81/LPO/lrm/lrm_15/LRM
|
| 13 |
+
HF cache dir: /scratch/rr81/ma5430/.cache/huggingface/hub
|
| 14 |
+
HF offline mode: True
|
| 15 |
+
Inference steps: 20
|
| 16 |
+
CFG scale: 3.0
|
| 17 |
+
Batch size: 1
|
| 18 |
+
Max samples: All
|
| 19 |
+
Output directory: RESULTS/pickapic/one_step_rectification_config_spo/run_1
|
| 20 |
+
Save images: False
|
| 21 |
+
Evaluation mode: gradient_ascent
|
| 22 |
+
Metrics to evaluate: CLIP, AESTHETIC, PICKSCORE, HPSV2, HPSV21, IMAGEREWARD
|
| 23 |
+
Gradient ascent config: one_step_rectification_config
|
| 24 |
+
|
| 25 |
+
======================================================================
|
| 26 |
+
1. LOADING VALIDATION DATA
|
| 27 |
+
======================================================================
|
| 28 |
+
Loading Pick-a-Pic validation prompts...
|
| 29 |
+
Loading cached Pick-a-Pic split 'validation_unique' from 1 parquet shards
|
| 30 |
+
cache=/scratch/rr81/ma5430/.cache/huggingface/hub/datasets--pickapic-anonymous--pickapic_v1
|
| 31 |
+
Loaded 500 Pick-a-Pic validation samples
|
| 32 |
+
|
| 33 |
+
======================================================================
|
| 34 |
+
2. LOADING REWARD MODEL
|
| 35 |
+
======================================================================
|
| 36 |
+
Loading base models from stable-diffusion-v1-5/stable-diffusion-v1-5...
|
| 37 |
+
Loading custom U-Net for reward prediction...
|
| 38 |
+
Loading CLIP text projection from openai/clip-vit-large-patch14...
|
| 39 |
+
✓ Loaded CLIP text projection weights
|
| 40 |
+
|
| 41 |
+
Loading LRM weights from /g/data/rr81/LPO/lrm/lrm_15/LRM...
|
| 42 |
+
✓ Loaded U-Net weights from /g/data/rr81/LPO/lrm/lrm_15/LRM/lrm_sd15/unet
|
| 43 |
+
✓ Loaded text encoder weights from /g/data/rr81/LPO/lrm/lrm_15/LRM/lrm_sd15/text_encoder
|
| 44 |
+
✓ Loaded projection layers and logit_scale from /g/data/rr81/LPO/lrm/lrm_15/LRM/lrm_sd15/state_dict.pt
|
| 45 |
+
✓ Successfully loaded all LRM weights!
|
| 46 |
+
✓ LRM Reward Model initialized successfully!
|
| 47 |
+
✓ Reward model loaded
|
| 48 |
+
|
| 49 |
+
======================================================================
|
| 50 |
+
3. LOADING PIPELINE
|
| 51 |
+
======================================================================
|
| 52 |
+
✓ Loaded SPO SD1.5 model (cfg_scale adjusted to 5.0)
|
| 53 |
+
✓ Reward model attached to pipeline
|
| 54 |
+
✓ Pipeline loaded
|
| 55 |
+
GPU memory before scorer load: 135.37 GB free / 140.06 GB total
|
| 56 |
+
Scorer device: cuda:0
|
| 57 |
+
|
| 58 |
+
======================================================================
|
| 59 |
+
3.5. LOADING CLIP AND AESTHETIC SCORERS
|
| 60 |
+
======================================================================
|
| 61 |
+
✓ CLIP scorer loaded
|
| 62 |
+
✓ Aesthetic scorer loaded
|
| 63 |
+
✓ PickScore scorer loaded
|
| 64 |
+
✓ HPSv2 scorer loaded
|
| 65 |
+
✓ HPSv2.1 scorer loaded
|
| 66 |
+
load checkpoint from /scratch/rr81/ma5430/.cache/huggingface/hub/models--THUDM--ImageReward/snapshots/5736be03b2652728fb87788c9797b0570450ab72/ImageReward.pt
|
| 67 |
+
checkpoint loaded
|
| 68 |
+
✓ ImageReward scorer loaded
|
| 69 |
+
|
| 70 |
+
======================================================================
|
| 71 |
+
4. CONFIGURING GRADIENT ASCENT
|
| 72 |
+
======================================================================
|
| 73 |
+
Loading gradient ascent config: one_step_rectification_config
|
| 74 |
+
Config loaded: {'grad_timestep_range': (200, 800), 'num_grad_steps': 1, 'grad_step_size': 1.0, 'grad_scale': 1.0, 'lr_scheduler_type': 'constant', 'use_momentum': False, 'use_nesterov': False, 'use_iso_projection': False}
|
| 75 |
+
Gradient timestep range: (200, 800)
|
| 76 |
+
Gradient steps: 1
|
| 77 |
+
Gradient step size (initial LR): 1.0
|
| 78 |
+
LR Scheduler: constant
|
| 79 |
+
✓ Gradient ascent enabled for timesteps (200, 800)
|
| 80 |
+
- Grad steps: 1
|
| 81 |
+
- Grad step size (initial LR): 1.0
|
| 82 |
+
- Grad scale: 1.0
|
| 83 |
+
- LR scheduler: constant
|
| 84 |
+
- Momentum: disabled
|
| 85 |
+
- Iso Projection: False
|
| 86 |
+
|
| 87 |
+
======================================================================
|
| 88 |
+
6. EVALUATING GRADIENT ASCENT
|
| 89 |
+
======================================================================
|
| 90 |
+
|
| 91 |
+
Generating images with gradient_ascent mode...
|
| 92 |
+
|
| 93 |
+
[gradient_ascent] Batch 10/500 | Samples: 10/500 | Reward (t=1): 0.9839 | Reward (Avg): 0.7578 | CLIP: 27.5790 | Aesthetic: 6.3113 | PickScore: 22.3422 | HPSv2: 0.2786 | HPSv2.1: 0.2986 | ImageReward: 0.4784
|
| 94 |
+
|
| 95 |
+
[gradient_ascent] Batch 20/500 | Samples: 20/500 | Reward (t=1): 0.0174 | Reward (Avg): 0.7138 | CLIP: 26.0677 | Aesthetic: 6.0572 | PickScore: 21.9946 | HPSv2: 0.2776 | HPSv2.1: 0.2915 | ImageReward: 0.4453
|
| 96 |
+
|
| 97 |
+
[gradient_ascent] Batch 30/500 | Samples: 30/500 | Reward (t=1): 0.9917 | Reward (Avg): 0.7299 | CLIP: 26.0958 | Aesthetic: 5.9759 | PickScore: 22.1637 | HPSv2: 0.2773 | HPSv2.1: 0.2852 | ImageReward: 0.4647
|
| 98 |
+
|
| 99 |
+
[gradient_ascent] Batch 40/500 | Samples: 40/500 | Reward (t=1): 0.2720 | Reward (Avg): 0.7690 | CLIP: 26.6243 | Aesthetic: 6.0120 | PickScore: 22.1630 | HPSv2: 0.2781 | HPSv2.1: 0.2908 | ImageReward: 0.5857
|
| 100 |
+
|
| 101 |
+
[gradient_ascent] Batch 50/500 | Samples: 50/500 | Reward (t=1): 0.2140 | Reward (Avg): 0.7380 | CLIP: 26.3520 | Aesthetic: 6.0178 | PickScore: 21.9486 | HPSv2: 0.2759 | HPSv2.1: 0.2847 | ImageReward: 0.4256
|
| 102 |
+
|
| 103 |
+
[gradient_ascent] Batch 60/500 | Samples: 60/500 | Reward (t=1): 0.9497 | Reward (Avg): 0.7612 | CLIP: 26.1764 | Aesthetic: 5.9950 | PickScore: 21.9419 | HPSv2: 0.2761 | HPSv2.1: 0.2830 | ImageReward: 0.2981
|
| 104 |
+
|
| 105 |
+
[gradient_ascent] Batch 70/500 | Samples: 70/500 | Reward (t=1): 0.1858 | Reward (Avg): 0.7414 | CLIP: 26.2783 | Aesthetic: 5.9921 | PickScore: 21.9503 | HPSv2: 0.2764 | HPSv2.1: 0.2825 | ImageReward: 0.3641
|
| 106 |
+
|
| 107 |
+
[gradient_ascent] Batch 80/500 | Samples: 80/500 | Reward (t=1): 0.9868 | Reward (Avg): 0.7360 | CLIP: 26.0943 | Aesthetic: 5.9858 | PickScore: 21.8417 | HPSv2: 0.2754 | HPSv2.1: 0.2791 | ImageReward: 0.3335
|
| 108 |
+
|
| 109 |
+
[gradient_ascent] Batch 90/500 | Samples: 90/500 | Reward (t=1): 0.7842 | Reward (Avg): 0.7336 | CLIP: 26.1805 | Aesthetic: 5.9752 | PickScore: 21.7347 | HPSv2: 0.2747 | HPSv2.1: 0.2791 | ImageReward: 0.3449
|
| 110 |
+
|
| 111 |
+
[gradient_ascent] Batch 100/500 | Samples: 100/500 | Reward (t=1): 0.8276 | Reward (Avg): 0.7296 | CLIP: 26.2917 | Aesthetic: 5.9789 | PickScore: 21.6981 | HPSv2: 0.2749 | HPSv2.1: 0.2795 | ImageReward: 0.3349
|
| 112 |
+
|
| 113 |
+
[gradient_ascent] Batch 110/500 | Samples: 110/500 | Reward (t=1): 0.1161 | Reward (Avg): 0.7234 | CLIP: 26.4764 | Aesthetic: 5.9772 | PickScore: 21.6814 | HPSv2: 0.2749 | HPSv2.1: 0.2791 | ImageReward: 0.3347
|
| 114 |
+
|
| 115 |
+
[gradient_ascent] Batch 120/500 | Samples: 120/500 | Reward (t=1): 0.9961 | Reward (Avg): 0.7031 | CLIP: 26.4423 | Aesthetic: 5.9550 | PickScore: 21.6546 | HPSv2: 0.2749 | HPSv2.1: 0.2786 | ImageReward: 0.3449
|
| 116 |
+
|
| 117 |
+
[gradient_ascent] Batch 130/500 | Samples: 130/500 | Reward (t=1): 0.0311 | Reward (Avg): 0.7017 | CLIP: 26.1816 | Aesthetic: 5.9739 | PickScore: 21.6772 | HPSv2: 0.2747 | HPSv2.1: 0.2791 | ImageReward: 0.3246
|
| 118 |
+
|
| 119 |
+
[gradient_ascent] Batch 140/500 | Samples: 140/500 | Reward (t=1): 0.0458 | Reward (Avg): 0.6919 | CLIP: 26.3517 | Aesthetic: 5.9473 | PickScore: 21.6609 | HPSv2: 0.2747 | HPSv2.1: 0.2778 | ImageReward: 0.3275
|
| 120 |
+
|
| 121 |
+
[gradient_ascent] Batch 150/500 | Samples: 150/500 | Reward (t=1): 0.1973 | Reward (Avg): 0.6835 | CLIP: 26.3371 | Aesthetic: 5.9298 | PickScore: 21.5766 | HPSv2: 0.2739 | HPSv2.1: 0.2756 | ImageReward: 0.2968
|
| 122 |
+
|
| 123 |
+
[gradient_ascent] Batch 160/500 | Samples: 160/500 | Reward (t=1): 0.9526 | Reward (Avg): 0.6758 | CLIP: 26.4790 | Aesthetic: 5.9465 | PickScore: 21.6101 | HPSv2: 0.2742 | HPSv2.1: 0.2761 | ImageReward: 0.3172
|
| 124 |
+
|
| 125 |
+
[gradient_ascent] Batch 170/500 | Samples: 170/500 | Reward (t=1): 0.8892 | Reward (Avg): 0.6712 | CLIP: 26.4519 | Aesthetic: 5.9403 | PickScore: 21.5942 | HPSv2: 0.2739 | HPSv2.1: 0.2764 | ImageReward: 0.3352
|
| 126 |
+
|
| 127 |
+
[gradient_ascent] Batch 180/500 | Samples: 180/500 | Reward (t=1): 0.9199 | Reward (Avg): 0.6702 | CLIP: 26.4475 | Aesthetic: 5.9484 | PickScore: 21.5408 | HPSv2: 0.2737 | HPSv2.1: 0.2759 | ImageReward: 0.3420
|
| 128 |
+
|
| 129 |
+
[gradient_ascent] Batch 190/500 | Samples: 190/500 | Reward (t=1): 0.9800 | Reward (Avg): 0.6799 | CLIP: 26.4414 | Aesthetic: 5.9557 | PickScore: 21.5410 | HPSv2: 0.2737 | HPSv2.1: 0.2761 | ImageReward: 0.3676
|
| 130 |
+
|
| 131 |
+
[gradient_ascent] Batch 200/500 | Samples: 200/500 | Reward (t=1): 0.9971 | Reward (Avg): 0.6779 | CLIP: 26.4270 | Aesthetic: 5.9503 | PickScore: 21.5414 | HPSv2: 0.2742 | HPSv2.1: 0.2776 | ImageReward: 0.3901
|
| 132 |
+
|
| 133 |
+
[gradient_ascent] Batch 210/500 | Samples: 210/500 | Reward (t=1): 0.9248 | Reward (Avg): 0.6884 | CLIP: 26.4479 | Aesthetic: 5.9510 | PickScore: 21.5482 | HPSv2: 0.2739 | HPSv2.1: 0.2776 | ImageReward: 0.3864
|
| 134 |
+
|
| 135 |
+
[gradient_ascent] Batch 220/500 | Samples: 220/500 | Reward (t=1): 0.4526 | Reward (Avg): 0.6888 | CLIP: 26.3972 | Aesthetic: 5.9576 | PickScore: 21.5436 | HPSv2: 0.2739 | HPSv2.1: 0.2776 | ImageReward: 0.3660
|
| 136 |
+
|
| 137 |
+
[gradient_ascent] Batch 230/500 | Samples: 230/500 | Reward (t=1): 0.2216 | Reward (Avg): 0.6872 | CLIP: 26.3495 | Aesthetic: 5.9620 | PickScore: 21.5255 | HPSv2: 0.2737 | HPSv2.1: 0.2769 | ImageReward: 0.3544
|
| 138 |
+
|
| 139 |
+
[gradient_ascent] Batch 240/500 | Samples: 240/500 | Reward (t=1): 0.8564 | Reward (Avg): 0.6887 | CLIP: 26.3124 | Aesthetic: 5.9582 | PickScore: 21.5032 | HPSv2: 0.2734 | HPSv2.1: 0.2764 | ImageReward: 0.3462
|
| 140 |
+
|
| 141 |
+
[gradient_ascent] Batch 250/500 | Samples: 250/500 | Reward (t=1): 0.1229 | Reward (Avg): 0.6950 | CLIP: 26.4021 | Aesthetic: 5.9620 | PickScore: 21.5312 | HPSv2: 0.2737 | HPSv2.1: 0.2764 | ImageReward: 0.3755
|
| 142 |
+
|
| 143 |
+
[gradient_ascent] Batch 260/500 | Samples: 260/500 | Reward (t=1): 0.9468 | Reward (Avg): 0.7000 | CLIP: 26.4661 | Aesthetic: 5.9651 | PickScore: 21.5443 | HPSv2: 0.2739 | HPSv2.1: 0.2769 | ImageReward: 0.3948
|
| 144 |
+
|
| 145 |
+
[gradient_ascent] Batch 270/500 | Samples: 270/500 | Reward (t=1): 0.9780 | Reward (Avg): 0.7023 | CLIP: 26.3012 | Aesthetic: 5.9742 | PickScore: 21.5138 | HPSv2: 0.2739 | HPSv2.1: 0.2764 | ImageReward: 0.3981
|
| 146 |
+
|
| 147 |
+
[gradient_ascent] Batch 280/500 | Samples: 280/500 | Reward (t=1): 0.6753 | Reward (Avg): 0.7021 | CLIP: 26.3270 | Aesthetic: 5.9737 | PickScore: 21.5205 | HPSv2: 0.2737 | HPSv2.1: 0.2761 | ImageReward: 0.4017
|
| 148 |
+
|
| 149 |
+
[gradient_ascent] Batch 290/500 | Samples: 290/500 | Reward (t=1): 0.4121 | Reward (Avg): 0.6986 | CLIP: 26.2771 | Aesthetic: 5.9719 | PickScore: 21.5051 | HPSv2: 0.2737 | HPSv2.1: 0.2759 | ImageReward: 0.3936
|
| 150 |
+
|
| 151 |
+
[gradient_ascent] Batch 300/500 | Samples: 300/500 | Reward (t=1): 0.4277 | Reward (Avg): 0.6986 | CLIP: 26.2539 | Aesthetic: 5.9735 | PickScore: 21.5175 | HPSv2: 0.2739 | HPSv2.1: 0.2764 | ImageReward: 0.4022
|
| 152 |
+
|
| 153 |
+
[gradient_ascent] Batch 310/500 | Samples: 310/500 | Reward (t=1): 0.8721 | Reward (Avg): 0.6977 | CLIP: 26.2491 | Aesthetic: 5.9809 | PickScore: 21.5185 | HPSv2: 0.2737 | HPSv2.1: 0.2759 | ImageReward: 0.3892
|
| 154 |
+
|
| 155 |
+
[gradient_ascent] Batch 320/500 | Samples: 320/500 | Reward (t=1): 0.4409 | Reward (Avg): 0.6939 | CLIP: 26.2734 | Aesthetic: 5.9789 | PickScore: 21.5331 | HPSv2: 0.2737 | HPSv2.1: 0.2756 | ImageReward: 0.3976
|
| 156 |
+
|
| 157 |
+
[gradient_ascent] Batch 330/500 | Samples: 330/500 | Reward (t=1): 0.9917 | Reward (Avg): 0.6926 | CLIP: 26.2698 | Aesthetic: 5.9769 | PickScore: 21.5238 | HPSv2: 0.2739 | HPSv2.1: 0.2759 | ImageReward: 0.3978
|
| 158 |
+
|
| 159 |
+
[gradient_ascent] Batch 340/500 | Samples: 340/500 | Reward (t=1): 0.9966 | Reward (Avg): 0.6948 | CLIP: 26.3201 | Aesthetic: 5.9800 | PickScore: 21.5359 | HPSv2: 0.2739 | HPSv2.1: 0.2761 | ImageReward: 0.4060
|
| 160 |
+
|
| 161 |
+
[gradient_ascent] Batch 350/500 | Samples: 350/500 | Reward (t=1): 0.6436 | Reward (Avg): 0.6949 | CLIP: 26.2648 | Aesthetic: 5.9745 | PickScore: 21.5429 | HPSv2: 0.2739 | HPSv2.1: 0.2761 | ImageReward: 0.4022
|
| 162 |
+
|
| 163 |
+
[gradient_ascent] Batch 360/500 | Samples: 360/500 | Reward (t=1): 0.9868 | Reward (Avg): 0.6984 | CLIP: 26.3237 | Aesthetic: 5.9739 | PickScore: 21.5439 | HPSv2: 0.2742 | HPSv2.1: 0.2766 | ImageReward: 0.4229
|
| 164 |
+
|
| 165 |
+
[gradient_ascent] Batch 370/500 | Samples: 370/500 | Reward (t=1): 0.9668 | Reward (Avg): 0.6994 | CLIP: 26.2721 | Aesthetic: 5.9688 | PickScore: 21.5360 | HPSv2: 0.2739 | HPSv2.1: 0.2764 | ImageReward: 0.4197
|
| 166 |
+
|
| 167 |
+
[gradient_ascent] Batch 380/500 | Samples: 380/500 | Reward (t=1): 0.9629 | Reward (Avg): 0.7023 | CLIP: 26.3215 | Aesthetic: 5.9748 | PickScore: 21.5425 | HPSv2: 0.2739 | HPSv2.1: 0.2769 | ImageReward: 0.4320
|
| 168 |
+
|
| 169 |
+
[gradient_ascent] Batch 390/500 | Samples: 390/500 | Reward (t=1): 0.9761 | Reward (Avg): 0.7035 | CLIP: 26.2381 | Aesthetic: 5.9748 | PickScore: 21.5369 | HPSv2: 0.2739 | HPSv2.1: 0.2764 | ImageReward: 0.4212
|
| 170 |
+
|
| 171 |
+
[gradient_ascent] Batch 400/500 | Samples: 400/500 | Reward (t=1): 0.9800 | Reward (Avg): 0.7064 | CLIP: 26.2046 | Aesthetic: 5.9692 | PickScore: 21.5326 | HPSv2: 0.2737 | HPSv2.1: 0.2761 | ImageReward: 0.4172
|
| 172 |
+
|
| 173 |
+
[gradient_ascent] Batch 410/500 | Samples: 410/500 | Reward (t=1): 0.9048 | Reward (Avg): 0.7060 | CLIP: 26.1930 | Aesthetic: 5.9673 | PickScore: 21.5222 | HPSv2: 0.2734 | HPSv2.1: 0.2756 | ImageReward: 0.4134
|
| 174 |
+
|
| 175 |
+
[gradient_ascent] Batch 420/500 | Samples: 420/500 | Reward (t=1): 0.8452 | Reward (Avg): 0.7030 | CLIP: 26.2187 | Aesthetic: 5.9654 | PickScore: 21.5167 | HPSv2: 0.2734 | HPSv2.1: 0.2754 | ImageReward: 0.4201
|
| 176 |
+
|
| 177 |
+
[gradient_ascent] Batch 430/500 | Samples: 430/500 | Reward (t=1): 0.9424 | Reward (Avg): 0.7073 | CLIP: 26.2393 | Aesthetic: 5.9670 | PickScore: 21.5194 | HPSv2: 0.2734 | HPSv2.1: 0.2754 | ImageReward: 0.4270
|
| 178 |
+
|
| 179 |
+
[gradient_ascent] Batch 440/500 | Samples: 440/500 | Reward (t=1): 0.9673 | Reward (Avg): 0.7067 | CLIP: 26.1450 | Aesthetic: 5.9661 | PickScore: 21.5111 | HPSv2: 0.2732 | HPSv2.1: 0.2754 | ImageReward: 0.4217
|
| 180 |
+
|
| 181 |
+
[gradient_ascent] Batch 450/500 | Samples: 450/500 | Reward (t=1): 0.7007 | Reward (Avg): 0.7084 | CLIP: 26.1783 | Aesthetic: 5.9595 | PickScore: 21.5085 | HPSv2: 0.2732 | HPSv2.1: 0.2749 | ImageReward: 0.4202
|
| 182 |
+
|
| 183 |
+
[gradient_ascent] Batch 460/500 | Samples: 460/500 | Reward (t=1): 0.1193 | Reward (Avg): 0.7053 | CLIP: 26.1434 | Aesthetic: 5.9576 | PickScore: 21.5003 | HPSv2: 0.2732 | HPSv2.1: 0.2747 | ImageReward: 0.4168
|
| 184 |
+
|
| 185 |
+
[gradient_ascent] Batch 470/500 | Samples: 470/500 | Reward (t=1): 0.6265 | Reward (Avg): 0.7034 | CLIP: 26.0937 | Aesthetic: 5.9501 | PickScore: 21.4891 | HPSv2: 0.2727 | HPSv2.1: 0.2744 | ImageReward: 0.4166
|
| 186 |
+
|
| 187 |
+
[gradient_ascent] Batch 480/500 | Samples: 480/500 | Reward (t=1): 0.8589 | Reward (Avg): 0.7012 | CLIP: 26.0558 | Aesthetic: 5.9510 | PickScore: 21.4883 | HPSv2: 0.2727 | HPSv2.1: 0.2742 | ImageReward: 0.4193
|
| 188 |
+
|
| 189 |
+
[gradient_ascent] Batch 490/500 | Samples: 490/500 | Reward (t=1): 0.8589 | Reward (Avg): 0.7001 | CLIP: 26.0536 | Aesthetic: 5.9529 | PickScore: 21.4798 | HPSv2: 0.2727 | HPSv2.1: 0.2739 | ImageReward: 0.4213
|
| 190 |
+
|
| 191 |
+
[gradient_ascent] Batch 500/500 | Samples: 500/500 | Reward (t=1): 0.7222 | Reward (Avg): 0.6998 | CLIP: 26.0221 | Aesthetic: 5.9536 | PickScore: 21.4608 | HPSv2: 0.2727 | HPSv2.1: 0.2739 | ImageReward: 0.4125
|
| 192 |
+
✓ Gradient Ascent Avg Reward: 0.6998
|
| 193 |
+
✓ Gradient Ascent Avg CLIP Score: 26.0221
|
| 194 |
+
✓ Gradient Ascent Avg Aesthetic Score: 5.9536
|
| 195 |
+
✓ Gradient Ascent Avg PickScore: 21.4608
|
| 196 |
+
✓ Gradient Ascent Avg HPSv2 Score: 0.2727
|
| 197 |
+
✓ Gradient Ascent Avg HPSv2.1 Score: 0.2739
|
| 198 |
+
✓ Gradient Ascent Avg ImageReward: 0.4125
|
| 199 |
+
|
| 200 |
+
Gradient Ascent Statistics:
|
| 201 |
+
Applications: 12
|
| 202 |
+
Total reward improvement: +1.2930
|
| 203 |
+
Avg reward improvement: +0.1077
|
| 204 |
+
|
| 205 |
+
✓ Saved LR curve plot to: RESULTS/pickapic/one_step_rectification_config_spo/run_1/lr_curve.png
|
| 206 |
+
Total gradient steps: 12
|
| 207 |
+
LR range: 1.000000 → 1.000000
|
| 208 |
+
|
| 209 |
+
✓ Saved Rewards curve plot to: RESULTS/pickapic/one_step_rectification_config_spo/run_1/rewards_curve.png
|
| 210 |
+
Total gradient steps: 24
|
| 211 |
+
Reward range: 0.9487 → 0.9990
|
| 212 |
+
Total improvement: +0.0503
|
| 213 |
+
|
| 214 |
+
======================================================================
|
| 215 |
+
FINAL RESULTS
|
| 216 |
+
======================================================================
|
| 217 |
+
|
| 218 |
+
Gradient Ascent:
|
| 219 |
+
Avg Reward: 0.6998
|
| 220 |
+
Avg CLIP Score: 26.0221
|
| 221 |
+
Avg Aesthetic: 5.9536
|
| 222 |
+
Avg PickScore: 21.4608
|
| 223 |
+
Avg HPSv2: 0.2727
|
| 224 |
+
Avg HPSv2.1: 0.2739
|
| 225 |
+
Avg ImageReward: 0.4125
|
| 226 |
+
|
| 227 |
+
✓ Results saved to: RESULTS/pickapic/one_step_rectification_config_spo/run_1/evaluation_results.txt
|
| 228 |
+
|
| 229 |
+
======================================================================
|
Reward_sd15_idealized/RESULTS/pickapic/one_step_rectification_config_spo/run_1/lr_curve.png
ADDED
|
Reward_sd15_idealized/__pycache__/grad_ascent_configs.cpython-310.pyc
ADDED
|
Binary file (1.94 kB). View file
|
|
|
Reward_sd15_idealized/__pycache__/grad_ascent_configs.cpython-311.pyc
ADDED
|
Binary file (2.69 kB). View file
|
|
|
Reward_sd15_idealized/__pycache__/gradient_ascent_utils.cpython-310.pyc
ADDED
|
Binary file (9.04 kB). View file
|
|
|
Reward_sd15_idealized/__pycache__/gradient_ascent_utils.cpython-311.pyc
ADDED
|
Binary file (14.7 kB). View file
|
|
|
Reward_sd15_idealized/__pycache__/gradient_ascent_utils.cpython-313.pyc
ADDED
|
Binary file (13.4 kB). View file
|
|
|
Reward_sd15_idealized/__pycache__/lr_scheduler.cpython-310.pyc
ADDED
|
Binary file (7.19 kB). View file
|
|
|
Reward_sd15_idealized/__pycache__/lr_scheduler.cpython-311.pyc
ADDED
|
Binary file (10.4 kB). View file
|
|
|
Reward_sd15_idealized/config_analysis_tuning.ipynb
ADDED
|
@@ -0,0 +1,218 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"cells": [
|
| 3 |
+
{
|
| 4 |
+
"cell_type": "code",
|
| 5 |
+
"execution_count": null,
|
| 6 |
+
"id": "a24d02a2",
|
| 7 |
+
"metadata": {},
|
| 8 |
+
"outputs": [],
|
| 9 |
+
"source": [
|
| 10 |
+
"import json\n",
|
| 11 |
+
"import pandas as pd\n",
|
| 12 |
+
"import numpy as np\n",
|
| 13 |
+
"from pathlib import Path\n",
|
| 14 |
+
"from datetime import datetime\n",
|
| 15 |
+
"import warnings\n",
|
| 16 |
+
"warnings.filterwarnings('ignore')\n",
|
| 17 |
+
"\n",
|
| 18 |
+
"# ============================================================================\n",
|
| 19 |
+
"# SECTION 1: Load and Parse Results from GPU Tuning Runs\n",
|
| 20 |
+
"# ==========================-==================================================\n",
|
| 21 |
+
"print(\"=\" * 80)\n",
|
| 22 |
+
"print(\"LOADING TUNING RESULTS FROM GPU RUNS\")\n",
|
| 23 |
+
"print(\"=\" * 80)\n",
|
| 24 |
+
"\n",
|
| 25 |
+
"results_dir = Path(\"RESULTS_TURNING/run_2\")\n",
|
| 26 |
+
"all_experiments = []\n",
|
| 27 |
+
"baseline_metrics = None\n",
|
| 28 |
+
"\n",
|
| 29 |
+
"# Collect results from all GPU runs\n",
|
| 30 |
+
"for gpu_id in range(8):\n",
|
| 31 |
+
" gpu_dir = results_dir / f\"gpu_{gpu_id}\"\n",
|
| 32 |
+
" results_file = gpu_dir / \"tuning_results.json\"\n",
|
| 33 |
+
" \n",
|
| 34 |
+
" if results_file.exists():\n",
|
| 35 |
+
" with open(results_file, 'r') as f:\n",
|
| 36 |
+
" data = json.load(f)\n",
|
| 37 |
+
" \n",
|
| 38 |
+
" # Extract baseline (same across all GPUs)\n",
|
| 39 |
+
" if baseline_metrics is None and \"baseline\" in data:\n",
|
| 40 |
+
" baseline_metrics = data[\"baseline\"][\"metrics\"]\n",
|
| 41 |
+
" print(f\"\\n📊 Baseline Metrics (cfg_scale=5.0):\")\n",
|
| 42 |
+
" for metric, value in baseline_metrics.items():\n",
|
| 43 |
+
" print(f\" {metric:15s}: {value:.6f}\")\n",
|
| 44 |
+
" \n",
|
| 45 |
+
" # Collect all experiments\n",
|
| 46 |
+
" if \"experiments\" in data:\n",
|
| 47 |
+
" all_experiments.extend(data[\"experiments\"])\n",
|
| 48 |
+
" print(f\"✓ GPU {gpu_id}: {len(data['experiments'])} results loaded\")\n",
|
| 49 |
+
"\n",
|
| 50 |
+
"print(f\"\\n✓ Total experiments loaded: {len(all_experiments)}\")\n",
|
| 51 |
+
"\n",
|
| 52 |
+
"# ============================================================================\n",
|
| 53 |
+
"# SECTION 2: Filter Top Configs with Improvements Across All Metrics\n",
|
| 54 |
+
"# ============================================================================\n",
|
| 55 |
+
"print(\"\\n\" + \"=\" * 80)\n",
|
| 56 |
+
"print(\"FILTERING CONFIGURATIONS WITH IMPROVEMENTS IN ALL METRICS\")\n",
|
| 57 |
+
"print(\"=\" * 80)\n",
|
| 58 |
+
"\n",
|
| 59 |
+
"# Define improvement metrics to track (using ImageReward instead of Reward)\n",
|
| 60 |
+
"improvement_metrics = [\n",
|
| 61 |
+
" \"aesthetic_improvement\", \n",
|
| 62 |
+
" \"imagereward_improvement\", \n",
|
| 63 |
+
" \"clip_improvement\", \n",
|
| 64 |
+
" \"pickscore_improvement\", \n",
|
| 65 |
+
" \"hpsv2_improvement\"\n",
|
| 66 |
+
" ]\n",
|
| 67 |
+
"\n",
|
| 68 |
+
"# Filter experiments with improvements in ALL metrics\n",
|
| 69 |
+
"top_configs = []\n",
|
| 70 |
+
"\n",
|
| 71 |
+
"for exp in all_experiments:\n",
|
| 72 |
+
" if \"improvements\" not in exp or \"config\" not in exp or \"metrics\" not in exp:\n",
|
| 73 |
+
" continue\n",
|
| 74 |
+
" \n",
|
| 75 |
+
" improvements = exp[\"improvements\"]\n",
|
| 76 |
+
" config = exp[\"config\"]\n",
|
| 77 |
+
" metrics = exp[\"metrics\"]\n",
|
| 78 |
+
" \n",
|
| 79 |
+
" # Check if ALL improvements are positive (>0)\n",
|
| 80 |
+
" all_positive = all(improvements.get(metric, -1) > 0 for metric in improvement_metrics)\n",
|
| 81 |
+
" \n",
|
| 82 |
+
" if all_positive:\n",
|
| 83 |
+
" # Calculate aggregate improvement score\n",
|
| 84 |
+
" avg_improvement = np.mean([improvements.get(metric, 0) for metric in improvement_metrics])\n",
|
| 85 |
+
" \n",
|
| 86 |
+
" top_configs.append({\n",
|
| 87 |
+
" \"config\": config,\n",
|
| 88 |
+
" \"metrics\": metrics,\n",
|
| 89 |
+
" \"improvements\": improvements,\n",
|
| 90 |
+
" \"avg_improvement\": avg_improvement\n",
|
| 91 |
+
" })\n",
|
| 92 |
+
"\n",
|
| 93 |
+
"print(f\"✓ Found {len(top_configs)} configurations with improvements in ALL metrics\")\n",
|
| 94 |
+
"\n",
|
| 95 |
+
"# Sort by average improvement\n",
|
| 96 |
+
"top_configs.sort(key=lambda x: x[\"avg_improvement\"], reverse=True)\n",
|
| 97 |
+
"\n",
|
| 98 |
+
"# Get top 10\n",
|
| 99 |
+
"top_10 = top_configs[:10]\n",
|
| 100 |
+
"print(f\"✓ Extracted top 10 best performing configurations\")\n",
|
| 101 |
+
"\n",
|
| 102 |
+
"# ============================================================================\n",
|
| 103 |
+
"# SECTION 3: Create Comprehensive Results Table\n",
|
| 104 |
+
"# ============================================================================\n",
|
| 105 |
+
"print(\"\\n\" + \"=\" * 80)\n",
|
| 106 |
+
"print(\"CREATING COMPREHENSIVE RESULTS TABLE\")\n",
|
| 107 |
+
"print(\"=\" * 80)\n",
|
| 108 |
+
"\n",
|
| 109 |
+
"# Build detailed table data\n",
|
| 110 |
+
"table_data = []\n",
|
| 111 |
+
"\n",
|
| 112 |
+
"for rank, result in enumerate(top_10, 1):\n",
|
| 113 |
+
" cfg = result[\"config\"]\n",
|
| 114 |
+
" metrics = result[\"metrics\"]\n",
|
| 115 |
+
" improvements = result[\"improvements\"]\n",
|
| 116 |
+
" \n",
|
| 117 |
+
" row = {\n",
|
| 118 |
+
" \"Rank\": rank,\n",
|
| 119 |
+
" \"CFG Scale\": cfg.get(\"cfg_scale\", \"N/A\"),\n",
|
| 120 |
+
" \"Grad Config\": cfg.get(\"grad_config\", \"N/A\"),\n",
|
| 121 |
+
" \"Steps\": cfg.get(\"num_grad_steps\", \"N/A\"),\n",
|
| 122 |
+
" \"LR\": cfg.get(\"grad_step_size\", \"N/A\"),\n",
|
| 123 |
+
" \"Momentum\": cfg.get(\"momentum\", \"N/A\"),\n",
|
| 124 |
+
" \"ImageReward\": f\"{metrics.get('imagereward', 0):.6f}\",\n",
|
| 125 |
+
" \"ImageReward ↑\": f\"{improvements.get('imagereward_improvement', 0):+.2f}%\",\n",
|
| 126 |
+
" \"CLIP\": f\"{metrics.get('clip', 0):.4f}\",\n",
|
| 127 |
+
" \"CLIP ↑\": f\"{improvements.get('clip_improvement', 0):+.2f}%\",\n",
|
| 128 |
+
" \"Aesthetic\": f\"{metrics.get('aesthetic', 0):.4f}\",\n",
|
| 129 |
+
" \"Aesthetic ↑\": f\"{improvements.get('aesthetic_improvement', 0):+.2f}%\",\n",
|
| 130 |
+
" \"PickScore\": f\"{metrics.get('pickscore', 0):.4f}\",\n",
|
| 131 |
+
" \"PickScore ↑\": f\"{improvements.get('pickscore_improvement', 0):+.2f}%\",\n",
|
| 132 |
+
" \"HPSv2\": f\"{metrics.get('hpsv2', 0):.4f}\",\n",
|
| 133 |
+
" \"HPSv2 ↑\": f\"{improvements.get('hpsv2_improvement', 0):+.2f}%\",\n",
|
| 134 |
+
" \"Avg Improvement\": f\"{result['avg_improvement']:+.2f}%\",\n",
|
| 135 |
+
" }\n",
|
| 136 |
+
" \n",
|
| 137 |
+
" table_data.append(row)\n",
|
| 138 |
+
"\n",
|
| 139 |
+
"df_top_10 = pd.DataFrame(table_data)\n",
|
| 140 |
+
"\n",
|
| 141 |
+
"print(\"\\n📋 TOP 10 CONFIGURATIONS WITH IMPROVEMENTS IN ALL METRICS:\")\n",
|
| 142 |
+
"print(\"=\" * 180)\n",
|
| 143 |
+
"print(df_top_10.to_string(index=False))\n",
|
| 144 |
+
"print(\"=\" * 180)\n",
|
| 145 |
+
"\n",
|
| 146 |
+
"# ============================================================================\n",
|
| 147 |
+
"# SECTION 4: Visualize and Summary Statistics\n",
|
| 148 |
+
"# ============================================================================\n",
|
| 149 |
+
"print(\"\\n\" + \"=\" * 80)\n",
|
| 150 |
+
"print(\"SUMMARY STATISTICS\")\n",
|
| 151 |
+
"print(\"=\" * 80)\n",
|
| 152 |
+
"\n",
|
| 153 |
+
"# Extract numeric improvement values for analysis\n",
|
| 154 |
+
"improvement_summary = []\n",
|
| 155 |
+
"for result in top_10:\n",
|
| 156 |
+
" improvements = result[\"improvements\"]\n",
|
| 157 |
+
" for metric in [\"imagereward_improvement\", \"clip_improvement\", \"aesthetic_improvement\", \n",
|
| 158 |
+
" \"pickscore_improvement\", \"hpsv2_improvement\"]:\n",
|
| 159 |
+
" metric_name = metric.replace(\"_improvement\", \"\").upper()\n",
|
| 160 |
+
" improvement_summary.append({\n",
|
| 161 |
+
" \"Metric\": metric_name,\n",
|
| 162 |
+
" \"Improvement %\": improvements.get(metric, 0)\n",
|
| 163 |
+
" })\n",
|
| 164 |
+
"\n",
|
| 165 |
+
"df_summary = pd.DataFrame(improvement_summary)\n",
|
| 166 |
+
"\n",
|
| 167 |
+
"print(\"\\n📊 Average Improvements by Metric (Top 10):\")\n",
|
| 168 |
+
"metric_stats = df_summary.groupby(\"Metric\")[\"Improvement %\"].agg([\"mean\", \"std\", \"min\", \"max\"])\n",
|
| 169 |
+
"print(metric_stats.round(2))\n",
|
| 170 |
+
"\n",
|
| 171 |
+
"print(\"\\n📈 Best Configuration Details:\")\n",
|
| 172 |
+
"best = top_10[0]\n",
|
| 173 |
+
"best_cfg = best[\"config\"]\n",
|
| 174 |
+
"best_metrics = best[\"metrics\"]\n",
|
| 175 |
+
"best_improvements = best[\"improvements\"]\n",
|
| 176 |
+
"\n",
|
| 177 |
+
"print(f\"\\n✓ RANK #1 - Best Performing Configuration:\")\n",
|
| 178 |
+
"print(f\" Configuration:\")\n",
|
| 179 |
+
"print(f\" • CFG Scale: {best_cfg.get('cfg_scale')}\")\n",
|
| 180 |
+
"print(f\" • Gradient Config: {best_cfg.get('grad_config')}\")\n",
|
| 181 |
+
"print(f\" • Gradient Steps: {best_cfg.get('num_grad_steps')}\")\n",
|
| 182 |
+
"print(f\" • Step Size: {best_cfg.get('grad_step_size')}\")\n",
|
| 183 |
+
"print(f\" • Momentum: {best_cfg.get('momentum')}\")\n",
|
| 184 |
+
"print(f\"\\n Metrics:\")\n",
|
| 185 |
+
"for metric in [\"imagereward\", \"clip\", \"aesthetic\", \"pickscore\", \"hpsv2\"]:\n",
|
| 186 |
+
" baseline_val = baseline_metrics.get(metric, 0)\n",
|
| 187 |
+
" current_val = best_metrics.get(metric, 0)\n",
|
| 188 |
+
" improvement = best_improvements.get(f\"{metric}_improvement\", 0)\n",
|
| 189 |
+
" print(f\" • {metric:12s}: {current_val:8.6f} (baseline: {baseline_val:8.6f}) ↑ {improvement:+6.2f}%\")\n",
|
| 190 |
+
"\n",
|
| 191 |
+
"print(\"\\n\" + \"=\" * 80)\n",
|
| 192 |
+
"print(\"✓ ANALYSIS COMPLETE - TOP 10 CONFIGURATIONS IDENTIFIED\")\n",
|
| 193 |
+
"print(\"=\" * 80)"
|
| 194 |
+
]
|
| 195 |
+
}
|
| 196 |
+
],
|
| 197 |
+
"metadata": {
|
| 198 |
+
"kernelspec": {
|
| 199 |
+
"display_name": "Python 3",
|
| 200 |
+
"language": "python",
|
| 201 |
+
"name": "python3"
|
| 202 |
+
},
|
| 203 |
+
"language_info": {
|
| 204 |
+
"codemirror_mode": {
|
| 205 |
+
"name": "ipython",
|
| 206 |
+
"version": 3
|
| 207 |
+
},
|
| 208 |
+
"file_extension": ".py",
|
| 209 |
+
"mimetype": "text/x-python",
|
| 210 |
+
"name": "python",
|
| 211 |
+
"nbconvert_exporter": "python",
|
| 212 |
+
"pygments_lexer": "ipython3",
|
| 213 |
+
"version": "3.10.18"
|
| 214 |
+
}
|
| 215 |
+
},
|
| 216 |
+
"nbformat": 4,
|
| 217 |
+
"nbformat_minor": 5
|
| 218 |
+
}
|
Reward_sd15_idealized/eval.py
ADDED
|
@@ -0,0 +1,1422 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
Evaluation script for comparing baseline and gradient ascent pipelines using multiple metrics.
|
| 3 |
+
|
| 4 |
+
This script evaluates both pipelines on COCO or Pick-a-Pic validation sets and computes
|
| 5 |
+
various preference and quality metrics.
|
| 6 |
+
"""
|
| 7 |
+
import warnings
|
| 8 |
+
warnings.filterwarnings("ignore")
|
| 9 |
+
import torch
|
| 10 |
+
import torch.nn as nn
|
| 11 |
+
import json
|
| 12 |
+
import os
|
| 13 |
+
import sys
|
| 14 |
+
import logging
|
| 15 |
+
from glob import glob
|
| 16 |
+
from pathlib import Path
|
| 17 |
+
from PIL import Image
|
| 18 |
+
from diffusers import StableDiffusionPipeline, DDIMScheduler, UNet2DConditionModel
|
| 19 |
+
from models import LRMRewardModel
|
| 20 |
+
from pipelines.sd15_gradient_ascent_pipeline import StableDiffusionGradientAscentPipeline
|
| 21 |
+
from torchmetrics.image.fid import FrechetInceptionDistance
|
| 22 |
+
from torchmetrics.multimodal import CLIPScore
|
| 23 |
+
from transformers import CLIPModel, CLIPProcessor
|
| 24 |
+
from tqdm import tqdm
|
| 25 |
+
import numpy as np
|
| 26 |
+
import argparse
|
| 27 |
+
from datasets import load_dataset
|
| 28 |
+
from grad_ascent_configs import get_config, list_configs
|
| 29 |
+
import matplotlib.pyplot as plt
|
| 30 |
+
import matplotlib
|
| 31 |
+
matplotlib.use('Agg') # Use non-interactive backend
|
| 32 |
+
|
| 33 |
+
# Import evaluation metrics
|
| 34 |
+
sys.path.append('../evaluation')
|
| 35 |
+
from huggingface_hub import hf_hub_download
|
| 36 |
+
|
| 37 |
+
import random
|
| 38 |
+
|
| 39 |
+
|
| 40 |
+
def configure_hf_runtime(hf_cache_dir=None, force_offline=False):
|
| 41 |
+
"""Set Hugging Face cache/offline environment for cluster-safe execution."""
|
| 42 |
+
cache_dir = hf_cache_dir or os.getenv("HF_HUB_CACHE") or os.getenv("HUGGINGFACE_HUB_CACHE")
|
| 43 |
+
if cache_dir:
|
| 44 |
+
os.environ["HF_HUB_CACHE"] = cache_dir
|
| 45 |
+
os.environ["HUGGINGFACE_HUB_CACHE"] = cache_dir
|
| 46 |
+
os.environ["HF_HOME"] = os.path.dirname(cache_dir)
|
| 47 |
+
|
| 48 |
+
env_offline = os.getenv("HF_HUB_OFFLINE", "0").strip().lower() in {"1", "true", "yes", "on"}
|
| 49 |
+
offline_enabled = bool(force_offline or env_offline)
|
| 50 |
+
if offline_enabled:
|
| 51 |
+
os.environ["HF_DATASETS_OFFLINE"] = "1"
|
| 52 |
+
os.environ["HF_METRICS_OFFLINE"] = "1"
|
| 53 |
+
os.environ["HF_MODULES_OFFLINE"] = "1"
|
| 54 |
+
os.environ["TRANSFORMERS_OFFLINE"] = "1"
|
| 55 |
+
os.environ["DIFFUSERS_OFFLINE"] = "1"
|
| 56 |
+
os.environ["HF_HUB_OFFLINE"] = "1"
|
| 57 |
+
|
| 58 |
+
return cache_dir, offline_enabled
|
| 59 |
+
|
| 60 |
+
|
| 61 |
+
def resolve_default_lrm_model():
|
| 62 |
+
"""Prefer local LRM checkout when available; otherwise fall back to HF repo id."""
|
| 63 |
+
project_root = Path(__file__).resolve().parents[1]
|
| 64 |
+
local_lrm = project_root / "lrm" / "lrm_15" / "LRM"
|
| 65 |
+
if local_lrm.exists():
|
| 66 |
+
return str(local_lrm)
|
| 67 |
+
return "casiatao/LRM"
|
| 68 |
+
|
| 69 |
+
|
| 70 |
+
def load_pickapic_prompts(max_samples=None, cache_dir=None, offline=False):
|
| 71 |
+
"""Load Pick-a-Pic prompts with robust offline fallback to cached parquet shards."""
|
| 72 |
+
split = "validation_unique"
|
| 73 |
+
|
| 74 |
+
if not offline:
|
| 75 |
+
try:
|
| 76 |
+
ds = load_dataset("pickapic-anonymous/pickapic_v1", split=split, streaming=True)
|
| 77 |
+
prompts = []
|
| 78 |
+
for i, sample in enumerate(ds):
|
| 79 |
+
prompts.append(sample["caption"])
|
| 80 |
+
if max_samples and i + 1 >= max_samples:
|
| 81 |
+
break
|
| 82 |
+
return prompts
|
| 83 |
+
except Exception as e:
|
| 84 |
+
print(f"Warning: online streaming load failed ({e}). Trying cached offline parquet shards.")
|
| 85 |
+
|
| 86 |
+
cache_candidates = []
|
| 87 |
+
for p in [
|
| 88 |
+
cache_dir,
|
| 89 |
+
os.getenv("HF_HUB_CACHE"),
|
| 90 |
+
os.getenv("HUGGINGFACE_HUB_CACHE"),
|
| 91 |
+
(os.path.join(os.getenv("HF_HOME"), "hub") if os.getenv("HF_HOME") else None),
|
| 92 |
+
os.path.expanduser("~/.cache/huggingface/hub"),
|
| 93 |
+
"/scratch/rr81/ma5430/.cache/huggingface/hub",
|
| 94 |
+
]:
|
| 95 |
+
if p and p not in cache_candidates:
|
| 96 |
+
cache_candidates.append(p)
|
| 97 |
+
|
| 98 |
+
for cache_root in cache_candidates:
|
| 99 |
+
repo_cache = os.path.join(cache_root, "datasets--pickapic-anonymous--pickapic_v1")
|
| 100 |
+
if not os.path.isdir(repo_cache):
|
| 101 |
+
continue
|
| 102 |
+
|
| 103 |
+
snapshot_dir = None
|
| 104 |
+
ref_main = os.path.join(repo_cache, "refs", "main")
|
| 105 |
+
if os.path.isfile(ref_main):
|
| 106 |
+
revision = open(ref_main, "r", encoding="utf-8").read().strip()
|
| 107 |
+
candidate = os.path.join(repo_cache, "snapshots", revision)
|
| 108 |
+
if os.path.isdir(candidate):
|
| 109 |
+
snapshot_dir = candidate
|
| 110 |
+
|
| 111 |
+
if snapshot_dir is None:
|
| 112 |
+
snapshots = sorted(glob(os.path.join(repo_cache, "snapshots", "*")))
|
| 113 |
+
if snapshots:
|
| 114 |
+
snapshot_dir = snapshots[-1]
|
| 115 |
+
|
| 116 |
+
if snapshot_dir is None:
|
| 117 |
+
continue
|
| 118 |
+
|
| 119 |
+
data_dir = os.path.join(snapshot_dir, "data")
|
| 120 |
+
if not os.path.isdir(data_dir):
|
| 121 |
+
continue
|
| 122 |
+
|
| 123 |
+
selected_split = split
|
| 124 |
+
parquet_files = sorted(glob(os.path.join(data_dir, f"{selected_split}-*.parquet")))
|
| 125 |
+
if not parquet_files:
|
| 126 |
+
for alt_split in ("test_unique", "test"):
|
| 127 |
+
alt_files = sorted(glob(os.path.join(data_dir, f"{alt_split}-*.parquet")))
|
| 128 |
+
if alt_files:
|
| 129 |
+
selected_split = alt_split
|
| 130 |
+
parquet_files = alt_files
|
| 131 |
+
print(f"Offline cache missing split '{split}', falling back to '{selected_split}'.")
|
| 132 |
+
break
|
| 133 |
+
|
| 134 |
+
if not parquet_files:
|
| 135 |
+
continue
|
| 136 |
+
|
| 137 |
+
print(
|
| 138 |
+
f"Loading cached Pick-a-Pic split '{selected_split}' from {len(parquet_files)} parquet shards\n"
|
| 139 |
+
f"cache={repo_cache}"
|
| 140 |
+
)
|
| 141 |
+
ds = load_dataset("parquet", data_files=parquet_files, split="train")
|
| 142 |
+
prompts = ds["caption"]
|
| 143 |
+
if max_samples:
|
| 144 |
+
prompts = prompts[:max_samples]
|
| 145 |
+
return list(prompts)
|
| 146 |
+
|
| 147 |
+
raise RuntimeError(
|
| 148 |
+
"Could not load pickapic prompts in offline mode. "
|
| 149 |
+
"Set --hf_cache_dir to a cache that contains datasets--pickapic-anonymous--pickapic_v1."
|
| 150 |
+
)
|
| 151 |
+
|
| 152 |
+
|
| 153 |
+
def resolve_scorer_device(requested_device, generation_device, min_free_gb_for_gpu=14.0):
|
| 154 |
+
"""Choose where metric scorers should run to avoid GPU OOM/cudnn init failures."""
|
| 155 |
+
if requested_device == "cpu":
|
| 156 |
+
return "cpu"
|
| 157 |
+
|
| 158 |
+
if not torch.cuda.is_available() or not str(generation_device).startswith("cuda"):
|
| 159 |
+
return "cpu"
|
| 160 |
+
|
| 161 |
+
if requested_device == "cuda":
|
| 162 |
+
return generation_device
|
| 163 |
+
|
| 164 |
+
# Auto mode: only keep scorers on GPU if enough headroom remains after loading generation models.
|
| 165 |
+
try:
|
| 166 |
+
free_bytes, total_bytes = torch.cuda.mem_get_info(torch.device(generation_device))
|
| 167 |
+
free_gb = free_bytes / (1024 ** 3)
|
| 168 |
+
total_gb = total_bytes / (1024 ** 3)
|
| 169 |
+
print(f"GPU memory before scorer load: {free_gb:.2f} GB free / {total_gb:.2f} GB total")
|
| 170 |
+
if free_gb >= min_free_gb_for_gpu:
|
| 171 |
+
return generation_device
|
| 172 |
+
print(
|
| 173 |
+
f"⚠ Low free VRAM ({free_gb:.2f} GB). Running scorers on CPU to keep diffusion stable. "
|
| 174 |
+
f"Use --scorer_device cuda to force GPU scorers."
|
| 175 |
+
)
|
| 176 |
+
return "cpu"
|
| 177 |
+
except Exception as e:
|
| 178 |
+
print(f"Warning: could not inspect CUDA free memory ({e}). Falling back to CPU scorers.")
|
| 179 |
+
return "cpu"
|
| 180 |
+
|
| 181 |
+
|
| 182 |
+
def configure_cudnn_safely(device):
|
| 183 |
+
"""Disable cuDNN when the current GPU or runtime cannot initialize it safely."""
|
| 184 |
+
if not torch.cuda.is_available() or not str(device).startswith("cuda"):
|
| 185 |
+
return
|
| 186 |
+
|
| 187 |
+
try:
|
| 188 |
+
major, minor = torch.cuda.get_device_capability(torch.device(device))
|
| 189 |
+
if (major, minor) < (7, 5):
|
| 190 |
+
print(
|
| 191 |
+
f"⚠ Detected compute capability sm_{major}{minor} (< 75). "
|
| 192 |
+
"Disabling cuDNN to prevent runtime initialization failures."
|
| 193 |
+
)
|
| 194 |
+
torch.backends.cudnn.enabled = False
|
| 195 |
+
return
|
| 196 |
+
|
| 197 |
+
# Force a cuDNN init probe early so failures are handled once at startup.
|
| 198 |
+
_ = torch.backends.cudnn.version()
|
| 199 |
+
except Exception as e:
|
| 200 |
+
print(f"⚠ cuDNN init probe failed ({e}). Disabling cuDNN for this run.")
|
| 201 |
+
torch.backends.cudnn.enabled = False
|
| 202 |
+
|
| 203 |
+
|
| 204 |
+
def seed_everything(seed: int):
|
| 205 |
+
"""Locks down all random number generators for absolute reproducibility."""
|
| 206 |
+
# 1. Python & Numpy
|
| 207 |
+
random.seed(seed)
|
| 208 |
+
np.random.seed(seed)
|
| 209 |
+
|
| 210 |
+
# 2. PyTorch Base
|
| 211 |
+
torch.manual_seed(seed)
|
| 212 |
+
if torch.cuda.is_available():
|
| 213 |
+
torch.cuda.manual_seed(seed)
|
| 214 |
+
torch.cuda.manual_seed_all(seed) # For multi-GPU
|
| 215 |
+
|
| 216 |
+
# 3. cuDNN Determinism (Crucial for consistent gradients)
|
| 217 |
+
torch.backends.cudnn.deterministic = True
|
| 218 |
+
torch.backends.cudnn.benchmark = False
|
| 219 |
+
|
| 220 |
+
# 4. Optional: Force deterministic algorithms for PyTorch 2.0+
|
| 221 |
+
# Uncomment if variance persists, but it may slow down generation slightly
|
| 222 |
+
# torch.use_deterministic_algorithms(True)
|
| 223 |
+
|
| 224 |
+
|
| 225 |
+
class MLP(nn.Module):
|
| 226 |
+
"""MLP for aesthetic scoring."""
|
| 227 |
+
def __init__(self):
|
| 228 |
+
super().__init__()
|
| 229 |
+
self.layers = nn.Sequential(
|
| 230 |
+
nn.Linear(768, 1024),
|
| 231 |
+
nn.Dropout(0.2),
|
| 232 |
+
nn.Linear(1024, 128),
|
| 233 |
+
nn.Dropout(0.2),
|
| 234 |
+
nn.Linear(128, 64),
|
| 235 |
+
nn.Dropout(0.1),
|
| 236 |
+
nn.Linear(64, 16),
|
| 237 |
+
nn.Linear(16, 1),
|
| 238 |
+
)
|
| 239 |
+
|
| 240 |
+
@torch.no_grad()
|
| 241 |
+
def forward(self, embed):
|
| 242 |
+
return self.layers(embed)
|
| 243 |
+
|
| 244 |
+
|
| 245 |
+
class AestheticScorer(torch.nn.Module):
|
| 246 |
+
"""Aesthetic scorer using CLIP and MLP."""
|
| 247 |
+
def __init__(self, dtype, device, clip_name_or_path="openai/clip-vit-large-patch14",
|
| 248 |
+
aesthetic_path="./sac+logos+ava1-l14-linearMSE.pth"):
|
| 249 |
+
super().__init__()
|
| 250 |
+
self.clip = CLIPModel.from_pretrained(clip_name_or_path)
|
| 251 |
+
self.processor = CLIPProcessor.from_pretrained(clip_name_or_path)
|
| 252 |
+
self.mlp = MLP()
|
| 253 |
+
|
| 254 |
+
# Load aesthetic weights
|
| 255 |
+
if os.path.exists(aesthetic_path):
|
| 256 |
+
state_dict = torch.load(aesthetic_path, map_location='cpu')
|
| 257 |
+
self.mlp.load_state_dict(state_dict)
|
| 258 |
+
else:
|
| 259 |
+
print(f"Warning: Aesthetic weights not found at {aesthetic_path}")
|
| 260 |
+
|
| 261 |
+
self.dtype = dtype
|
| 262 |
+
self.to(device)
|
| 263 |
+
self.eval()
|
| 264 |
+
|
| 265 |
+
@torch.no_grad()
|
| 266 |
+
def __call__(self, images):
|
| 267 |
+
device = next(self.parameters()).device
|
| 268 |
+
inputs = self.processor(images=images, return_tensors="pt")
|
| 269 |
+
inputs = {k: v.to(self.dtype).to(device) for k, v in inputs.items()}
|
| 270 |
+
embed = self.clip.get_image_features(**inputs)
|
| 271 |
+
# normalize embedding
|
| 272 |
+
embed = embed / torch.linalg.vector_norm(embed, dim=-1, keepdim=True)
|
| 273 |
+
return self.mlp(embed).squeeze(1)
|
| 274 |
+
|
| 275 |
+
|
| 276 |
+
class TeeLogger:
|
| 277 |
+
"""Logger that writes to both console and file."""
|
| 278 |
+
def __init__(self, log_file):
|
| 279 |
+
self.terminal = sys.stdout
|
| 280 |
+
self.log = open(log_file, 'w')
|
| 281 |
+
|
| 282 |
+
def write(self, message):
|
| 283 |
+
self.terminal.write(message)
|
| 284 |
+
self.log.write(message)
|
| 285 |
+
self.log.flush()
|
| 286 |
+
|
| 287 |
+
def flush(self):
|
| 288 |
+
self.terminal.flush()
|
| 289 |
+
self.log.flush()
|
| 290 |
+
|
| 291 |
+
def close(self):
|
| 292 |
+
self.log.close()
|
| 293 |
+
|
| 294 |
+
|
| 295 |
+
def setup_logging(output_dir):
|
| 296 |
+
"""Setup logging to both console and file."""
|
| 297 |
+
output_path = Path(output_dir)
|
| 298 |
+
output_path.mkdir(parents=True, exist_ok=True)
|
| 299 |
+
log_file = output_path / "log.log"
|
| 300 |
+
|
| 301 |
+
# Redirect stdout to both console and file
|
| 302 |
+
tee = TeeLogger(log_file)
|
| 303 |
+
sys.stdout = tee
|
| 304 |
+
|
| 305 |
+
return tee, log_file
|
| 306 |
+
|
| 307 |
+
|
| 308 |
+
def load_validation_data(data_dir, max_samples=None, dataset_type="coco", hf_cache_dir=None, offline=False):
|
| 309 |
+
"""Load validation prompts and image paths.
|
| 310 |
+
|
| 311 |
+
Args:
|
| 312 |
+
data_dir: Path to data directory
|
| 313 |
+
max_samples: Maximum number of samples to load
|
| 314 |
+
dataset_type: Type of dataset ("coco" or "pickapic")
|
| 315 |
+
|
| 316 |
+
Returns:
|
| 317 |
+
prompts: List of text prompts
|
| 318 |
+
image_paths: List of image paths (None for pickapic streaming dataset)
|
| 319 |
+
"""
|
| 320 |
+
if dataset_type == "coco":
|
| 321 |
+
data_dir = Path(data_dir)
|
| 322 |
+
val_json = data_dir / "coco" / "caption_val.json"
|
| 323 |
+
|
| 324 |
+
if not val_json.exists():
|
| 325 |
+
raise FileNotFoundError(f"Validation JSON not found: {val_json}")
|
| 326 |
+
|
| 327 |
+
with open(val_json, 'r') as f:
|
| 328 |
+
data = json.load(f)
|
| 329 |
+
|
| 330 |
+
# Validate that image folder exists
|
| 331 |
+
val_img_dir = data_dir / "coco" / "images" / "val"
|
| 332 |
+
if not val_img_dir.exists():
|
| 333 |
+
raise FileNotFoundError(f"Validation image directory not found: {val_img_dir}")
|
| 334 |
+
|
| 335 |
+
# Parse data
|
| 336 |
+
prompts = []
|
| 337 |
+
image_paths = []
|
| 338 |
+
for img_path, caption in data.items():
|
| 339 |
+
full_path = data_dir / "coco" / img_path
|
| 340 |
+
if full_path.exists():
|
| 341 |
+
prompts.append(caption)
|
| 342 |
+
image_paths.append(str(full_path))
|
| 343 |
+
else:
|
| 344 |
+
print(f"Warning: Image not found: {full_path}")
|
| 345 |
+
|
| 346 |
+
if max_samples:
|
| 347 |
+
prompts = prompts[:max_samples]
|
| 348 |
+
image_paths = image_paths[:max_samples]
|
| 349 |
+
|
| 350 |
+
print(f"Loaded {len(prompts)} COCO validation samples")
|
| 351 |
+
return prompts, image_paths
|
| 352 |
+
|
| 353 |
+
elif dataset_type == "pickapic":
|
| 354 |
+
print("Loading Pick-a-Pic validation prompts...")
|
| 355 |
+
prompts = load_pickapic_prompts(max_samples=max_samples, cache_dir=hf_cache_dir, offline=offline)
|
| 356 |
+
|
| 357 |
+
print(f"Loaded {len(prompts)} Pick-a-Pic validation samples")
|
| 358 |
+
return prompts, None # No reference images for Pick-a-Pic
|
| 359 |
+
|
| 360 |
+
else:
|
| 361 |
+
raise ValueError(f"Unknown dataset type: {dataset_type}. Choose 'coco' or 'pickapic'.")
|
| 362 |
+
|
| 363 |
+
|
| 364 |
+
def generate_and_evaluate(
|
| 365 |
+
pipeline,
|
| 366 |
+
prompts,
|
| 367 |
+
image_paths,
|
| 368 |
+
device,
|
| 369 |
+
dtype,
|
| 370 |
+
num_inference_steps=20,
|
| 371 |
+
guidance_scale=7.5,
|
| 372 |
+
seed=42,
|
| 373 |
+
batch_size=1,
|
| 374 |
+
apply_gradient_ascent=False,
|
| 375 |
+
mode_name="baseline",
|
| 376 |
+
log_interval=10,
|
| 377 |
+
output_dir=None,
|
| 378 |
+
save_images=False,
|
| 379 |
+
clip_scorer=None,
|
| 380 |
+
aesthetic_scorer=None,
|
| 381 |
+
pick_scorer=None,
|
| 382 |
+
hpsv2_scorer=None,
|
| 383 |
+
hpsv21_scorer=None,
|
| 384 |
+
imagereward_scorer=None,
|
| 385 |
+
compute_fid=True,
|
| 386 |
+
capture_trajectory=False
|
| 387 |
+
):
|
| 388 |
+
"""Generate images and update FID metric."""
|
| 389 |
+
pipeline.to(device)
|
| 390 |
+
|
| 391 |
+
print(f"\nGenerating images with {mode_name} mode...")
|
| 392 |
+
|
| 393 |
+
|
| 394 |
+
all_rewards = []
|
| 395 |
+
all_clip_scores = []
|
| 396 |
+
all_aesthetic_scores = []
|
| 397 |
+
all_pick_scores = []
|
| 398 |
+
all_hpsv2_scores = []
|
| 399 |
+
all_hpsv21_scores = []
|
| 400 |
+
all_imagereward_scores = []
|
| 401 |
+
lr_history_first_image = None # Store LR history for first image
|
| 402 |
+
trajectory_first_image = []
|
| 403 |
+
num_batches = (len(prompts) + batch_size - 1) // batch_size
|
| 404 |
+
|
| 405 |
+
# Create output directory if saving images
|
| 406 |
+
if save_images and output_dir:
|
| 407 |
+
mode_output_dir = Path(output_dir) / mode_name
|
| 408 |
+
mode_output_dir.mkdir(parents=True, exist_ok=True)
|
| 409 |
+
|
| 410 |
+
# Disable internal progress bars
|
| 411 |
+
pipeline.set_progress_bar_config(disable=True)
|
| 412 |
+
|
| 413 |
+
for idx, i in enumerate(tqdm(range(0, len(prompts), batch_size), desc=f"Generating {mode_name}")):
|
| 414 |
+
batch_prompts = prompts[i:i+batch_size]
|
| 415 |
+
batch_real_paths = image_paths[i:i+batch_size] if image_paths is not None else None
|
| 416 |
+
batch_num = idx + 1
|
| 417 |
+
|
| 418 |
+
# Initialize FID metric if needed
|
| 419 |
+
fid_metric = None
|
| 420 |
+
real_images_tensor = None
|
| 421 |
+
|
| 422 |
+
if compute_fid and batch_real_paths is not None:
|
| 423 |
+
fid_metric = FrechetInceptionDistance().to(device)
|
| 424 |
+
|
| 425 |
+
# Load and update FID with real images for this batch
|
| 426 |
+
real_images = []
|
| 427 |
+
for path in batch_real_paths:
|
| 428 |
+
img = Image.open(path).convert("RGB")
|
| 429 |
+
img = img.resize((512, 512)) # Inception v3 input size
|
| 430 |
+
img_array = np.array(img)
|
| 431 |
+
real_images.append(img_array)
|
| 432 |
+
|
| 433 |
+
# Convert to tensor [B, H, W, C] -> [B, C, H, W]
|
| 434 |
+
real_images_tensor = torch.from_numpy(np.stack(real_images)).permute(0, 3, 1, 2).float()
|
| 435 |
+
real_images_tensor = real_images_tensor.to(device)
|
| 436 |
+
|
| 437 |
+
# Generate images
|
| 438 |
+
generator = torch.Generator(device=device).manual_seed(seed + i)
|
| 439 |
+
|
| 440 |
+
# Only capture trajectory for the very first batch to save RAM
|
| 441 |
+
def trajectory_callback(step, timestep, latents):
|
| 442 |
+
if idx == 0 and capture_trajectory:
|
| 443 |
+
# Detach and move to CPU immediately to prevent VRAM OOM
|
| 444 |
+
trajectory_first_image.append(latents.detach().cpu().clone())
|
| 445 |
+
|
| 446 |
+
with torch.no_grad():
|
| 447 |
+
result = pipeline(
|
| 448 |
+
prompt=batch_prompts,
|
| 449 |
+
num_inference_steps=num_inference_steps,
|
| 450 |
+
guidance_scale=guidance_scale,
|
| 451 |
+
generator=generator,
|
| 452 |
+
track_rewards=True,
|
| 453 |
+
print_rewards=False,
|
| 454 |
+
apply_gradient_ascent=apply_gradient_ascent,
|
| 455 |
+
verbose_grad=False,
|
| 456 |
+
callback=trajectory_callback if capture_trajectory else None,
|
| 457 |
+
callback_steps=1
|
| 458 |
+
)
|
| 459 |
+
|
| 460 |
+
# Process generated images
|
| 461 |
+
images = result.images
|
| 462 |
+
|
| 463 |
+
# Update FID metric if computing it
|
| 464 |
+
if compute_fid and fid_metric is not None:
|
| 465 |
+
image_tensors = []
|
| 466 |
+
|
| 467 |
+
for img in images:
|
| 468 |
+
img_resized = img.resize((512, 512)) # Inception v3 input size
|
| 469 |
+
img_array = np.array(img_resized)
|
| 470 |
+
image_tensors.append(img_array)
|
| 471 |
+
|
| 472 |
+
# Convert to tensor and update FID
|
| 473 |
+
images_tensor = torch.from_numpy(np.stack(image_tensors)).permute(0, 3, 1, 2).float()
|
| 474 |
+
images_tensor = images_tensor.to(device)
|
| 475 |
+
|
| 476 |
+
if batch_size == 1:
|
| 477 |
+
real_images_tensor = torch.cat([real_images_tensor, real_images_tensor], dim=0).to(dtype=torch.uint8)
|
| 478 |
+
images_tensor = torch.cat([images_tensor, images_tensor], dim=0).to(dtype=torch.uint8)
|
| 479 |
+
fid_metric.update(real_images_tensor, real=True)
|
| 480 |
+
fid_metric.update(images_tensor, real=False)
|
| 481 |
+
|
| 482 |
+
# Track rewards - get the final timestep reward (t=0)
|
| 483 |
+
current_batch_final_reward = None
|
| 484 |
+
current_batch_final_timestep = None
|
| 485 |
+
if hasattr(pipeline, 'reward_history') and pipeline.reward_history:
|
| 486 |
+
# For each image, get the reward from the last denoising step (t=0 or closest to 0)
|
| 487 |
+
num_steps_per_image = num_inference_steps
|
| 488 |
+
|
| 489 |
+
# Get the last entry which corresponds to the final timestep of the last image in batch
|
| 490 |
+
final_entry = pipeline.reward_history[-1]
|
| 491 |
+
current_batch_final_reward = final_entry['reward_score']
|
| 492 |
+
current_batch_final_timestep = final_entry['timestep']
|
| 493 |
+
all_rewards.append(current_batch_final_reward)
|
| 494 |
+
|
| 495 |
+
# Capture LR history from first image if gradient ascent is enabled
|
| 496 |
+
if apply_gradient_ascent and idx == 0 and lr_history_first_image is None:
|
| 497 |
+
if hasattr(pipeline, 'grad_guidance') and pipeline.grad_guidance:
|
| 498 |
+
grad_stats = pipeline.grad_guidance.get_statistics()
|
| 499 |
+
if grad_stats and 'detailed_stats' in grad_stats:
|
| 500 |
+
# Extract LR history from the gradient ascent statistics
|
| 501 |
+
lr_history_first_image = {
|
| 502 |
+
'prompt': batch_prompts[0],
|
| 503 |
+
'timesteps': [],
|
| 504 |
+
'learning_rates': [], # All LR values from all gradient steps
|
| 505 |
+
'rewards': []
|
| 506 |
+
}
|
| 507 |
+
for stat in grad_stats['detailed_stats']:
|
| 508 |
+
lr_history_first_image['timesteps'].append(stat['timestep'])
|
| 509 |
+
if 'lr_history' in stat:
|
| 510 |
+
# Extend with all LR values from this timestep's gradient steps
|
| 511 |
+
lr_history_first_image['learning_rates'].extend(stat['lr_history'])
|
| 512 |
+
# Collect all rewards from reward_history for each gradient step
|
| 513 |
+
if 'reward_history' in stat:
|
| 514 |
+
lr_history_first_image['rewards'].extend(stat['reward_history'])
|
| 515 |
+
|
| 516 |
+
# Compute CLIP score
|
| 517 |
+
if clip_scorer is not None:
|
| 518 |
+
clip_device = next(clip_scorer.parameters()).device
|
| 519 |
+
# Convert PIL images to tensor format for CLIP score [C, H, W] in range [0, 1]
|
| 520 |
+
for img, prompt in zip(images, batch_prompts):
|
| 521 |
+
img_array = np.array(img).astype(np.float32)
|
| 522 |
+
img_tensor = torch.from_numpy(img_array).permute(2, 0, 1).unsqueeze(0).to(clip_device)
|
| 523 |
+
clip_score = clip_scorer(img_tensor, [prompt]).item()
|
| 524 |
+
all_clip_scores.append(clip_score)
|
| 525 |
+
|
| 526 |
+
# Compute aesthetic score
|
| 527 |
+
if aesthetic_scorer is not None:
|
| 528 |
+
aesthetic_scores = aesthetic_scorer(images)
|
| 529 |
+
if isinstance(aesthetic_scores, torch.Tensor):
|
| 530 |
+
aesthetic_scores = aesthetic_scores.cpu().numpy()
|
| 531 |
+
if aesthetic_scores.ndim == 0:
|
| 532 |
+
aesthetic_scores = [aesthetic_scores.item()]
|
| 533 |
+
all_aesthetic_scores.extend(aesthetic_scores.tolist() if hasattr(aesthetic_scores, 'tolist') else [aesthetic_scores])
|
| 534 |
+
|
| 535 |
+
# Compute PickScore
|
| 536 |
+
if pick_scorer is not None:
|
| 537 |
+
for img, prompt in zip(images, batch_prompts):
|
| 538 |
+
pick_score = pick_scorer(prompt, [img])[0]
|
| 539 |
+
all_pick_scores.append(pick_score)
|
| 540 |
+
|
| 541 |
+
# Compute HPSv2 score
|
| 542 |
+
if hpsv2_scorer is not None:
|
| 543 |
+
for img, prompt in zip(images, batch_prompts):
|
| 544 |
+
hpsv2_score = hpsv2_scorer.score(img, prompt)[0]
|
| 545 |
+
all_hpsv2_scores.append(hpsv2_score)
|
| 546 |
+
|
| 547 |
+
# Compute HPSv2.1 score
|
| 548 |
+
if hpsv21_scorer is not None:
|
| 549 |
+
for img, prompt in zip(images, batch_prompts):
|
| 550 |
+
hpsv21_score = hpsv21_scorer.score(img, prompt)[0]
|
| 551 |
+
all_hpsv21_scores.append(hpsv21_score)
|
| 552 |
+
|
| 553 |
+
# Compute ImageReward score
|
| 554 |
+
if imagereward_scorer is not None:
|
| 555 |
+
for img, prompt in zip(images, batch_prompts):
|
| 556 |
+
imagereward_score = imagereward_scorer.score(prompt, img)
|
| 557 |
+
all_imagereward_scores.append(imagereward_score)
|
| 558 |
+
|
| 559 |
+
# Save generated images if requested
|
| 560 |
+
if save_images and output_dir:
|
| 561 |
+
for img_idx, img in enumerate(images):
|
| 562 |
+
global_idx = i + img_idx
|
| 563 |
+
img_path = mode_output_dir / f"sample_{global_idx:05d}.png"
|
| 564 |
+
img.save(img_path)
|
| 565 |
+
|
| 566 |
+
# Log intermediate FID and metrics every log_interval batches
|
| 567 |
+
if batch_num % log_interval == 0 or batch_num == num_batches:
|
| 568 |
+
num_samples_processed = min(i + batch_size, len(prompts))
|
| 569 |
+
log_msg = f"\n[{mode_name}] Batch {batch_num}/{num_batches} | Samples: {num_samples_processed}/{len(prompts)}"
|
| 570 |
+
|
| 571 |
+
# Add FID if computing
|
| 572 |
+
if compute_fid and fid_metric is not None:
|
| 573 |
+
try:
|
| 574 |
+
current_fid = fid_metric.compute().item()
|
| 575 |
+
log_msg += f" | FID: {current_fid:.4f}"
|
| 576 |
+
except Exception as e:
|
| 577 |
+
log_msg += f" | FID: Computing..."
|
| 578 |
+
|
| 579 |
+
# Add reward - show both final timestep reward and average
|
| 580 |
+
if all_rewards:
|
| 581 |
+
avg_reward = np.mean(all_rewards)
|
| 582 |
+
if current_batch_final_reward is not None:
|
| 583 |
+
log_msg += f" | Reward (t={current_batch_final_timestep}): {current_batch_final_reward:.4f}"
|
| 584 |
+
log_msg += f" | Reward (Avg): {avg_reward:.4f}"
|
| 585 |
+
else:
|
| 586 |
+
log_msg += f" | Reward (Avg): {avg_reward:.4f}"
|
| 587 |
+
|
| 588 |
+
# Add CLIP if computing
|
| 589 |
+
if clip_scorer is not None and all_clip_scores:
|
| 590 |
+
log_msg += f" | CLIP: {np.mean(all_clip_scores):.4f}"
|
| 591 |
+
|
| 592 |
+
# Add aesthetic if computing
|
| 593 |
+
if aesthetic_scorer is not None and all_aesthetic_scores:
|
| 594 |
+
log_msg += f" | Aesthetic: {np.mean(all_aesthetic_scores):.4f}"
|
| 595 |
+
|
| 596 |
+
# Add PickScore
|
| 597 |
+
if pick_scorer is not None and all_pick_scores:
|
| 598 |
+
log_msg += f" | PickScore: {np.mean(all_pick_scores):.4f}"
|
| 599 |
+
|
| 600 |
+
# Add HPSv2
|
| 601 |
+
if hpsv2_scorer is not None and all_hpsv2_scores:
|
| 602 |
+
log_msg += f" | HPSv2: {np.mean(all_hpsv2_scores):.4f}"
|
| 603 |
+
|
| 604 |
+
# Add HPSv2.1
|
| 605 |
+
if hpsv21_scorer is not None and all_hpsv21_scores:
|
| 606 |
+
log_msg += f" | HPSv2.1: {np.mean(all_hpsv21_scores):.4f}"
|
| 607 |
+
|
| 608 |
+
# Add ImageReward
|
| 609 |
+
if imagereward_scorer is not None and all_imagereward_scores:
|
| 610 |
+
log_msg += f" | ImageReward: {np.mean(all_imagereward_scores):.4f}"
|
| 611 |
+
|
| 612 |
+
print(log_msg)
|
| 613 |
+
|
| 614 |
+
# Re-enable progress bars
|
| 615 |
+
pipeline.set_progress_bar_config(disable=False)
|
| 616 |
+
|
| 617 |
+
avg_reward = np.mean(all_rewards) if all_rewards else 0.0
|
| 618 |
+
avg_clip_score = np.mean(all_clip_scores) if all_clip_scores else 0.0
|
| 619 |
+
avg_aesthetic_score = np.mean(all_aesthetic_scores) if all_aesthetic_scores else 0.0
|
| 620 |
+
avg_pick_score = np.mean(all_pick_scores) if all_pick_scores else 0.0
|
| 621 |
+
avg_hpsv2_score = np.mean(all_hpsv2_scores) if all_hpsv2_scores else 0.0
|
| 622 |
+
avg_hpsv21_score = np.mean(all_hpsv21_scores) if all_hpsv21_scores else 0.0
|
| 623 |
+
avg_imagereward_score = np.mean(all_imagereward_scores) if all_imagereward_scores else 0.0
|
| 624 |
+
|
| 625 |
+
return avg_reward, fid_metric, avg_clip_score, avg_aesthetic_score, avg_pick_score, avg_hpsv2_score, avg_hpsv21_score, avg_imagereward_score, lr_history_first_image, trajectory_first_image
|
| 626 |
+
|
| 627 |
+
|
| 628 |
+
def auto_increment_path(base_path):
|
| 629 |
+
"""
|
| 630 |
+
Create an auto-incrementing run folder inside base_path.
|
| 631 |
+
Returns: base_path/run_1, base_path/run_2, etc.
|
| 632 |
+
"""
|
| 633 |
+
base_path = Path(base_path)
|
| 634 |
+
base_path.mkdir(parents=True, exist_ok=True) # Ensure base directory exists
|
| 635 |
+
|
| 636 |
+
i = 1
|
| 637 |
+
while True:
|
| 638 |
+
new_path = base_path / f"run_{i}"
|
| 639 |
+
if not new_path.exists():
|
| 640 |
+
return new_path
|
| 641 |
+
i += 1
|
| 642 |
+
|
| 643 |
+
|
| 644 |
+
def main():
|
| 645 |
+
parser = argparse.ArgumentParser(description="Evaluate baseline and gradient ascent pipelines")
|
| 646 |
+
parser.add_argument("--data_dir", type=str, default="./data", help="Path to data directory")
|
| 647 |
+
parser.add_argument("--dataset_type", type=str, default="coco", choices=["coco", "pickapic"],
|
| 648 |
+
help="Dataset to use for evaluation: coco or pickapic (default: coco)")
|
| 649 |
+
parser.add_argument("--base_model", type=str, default="stable-diffusion-v1-5/stable-diffusion-v1-5", help="Base model path")
|
| 650 |
+
parser.add_argument("--model_variant", type=str, default="origin",
|
| 651 |
+
choices=["origin", "spo", "diffusion_dpo", "lpo"],
|
| 652 |
+
help="SD1.5 model variant to use (default: origin)")
|
| 653 |
+
parser.add_argument("--lrm_model", type=str, default=None, help="LRM model path. Defaults to local lrm/lrm_15/LRM when present.")
|
| 654 |
+
parser.add_argument("--hf_cache_dir", type=str, default="/scratch/rr81/ma5430/.cache/huggingface/hub", help="Shared HF cache directory")
|
| 655 |
+
parser.add_argument("--offline", action="store_true", help="Force fully offline mode (recommended on GPU nodes)")
|
| 656 |
+
parser.add_argument("--num_steps", type=int, default=50, help="Number of inference steps")
|
| 657 |
+
parser.add_argument("--cfg_scale", type=float, default=7.5, help="Classifier-free guidance scale")
|
| 658 |
+
parser.add_argument("--seed", type=int, default=42, help="Random seed")
|
| 659 |
+
parser.add_argument("--max_samples", type=int, default=None, help="Max samples to evaluate (None for all)")
|
| 660 |
+
parser.add_argument("--batch_size", type=int, default=1, help="Batch size for generation (use 1 for reward model compatibility)")
|
| 661 |
+
parser.add_argument("--fid_batch_size", type=int, default=32, help="Batch size for FID computation")
|
| 662 |
+
parser.add_argument("--log_interval", type=int, default=10, help="Log FID and metrics every N batches")
|
| 663 |
+
parser.add_argument("--output_dir", type=str, default="eval_outputs", help="Directory to save generated images and results")
|
| 664 |
+
parser.add_argument("--save_images", action="store_true", help="Save all generated images to output directory")
|
| 665 |
+
parser.add_argument("--mode", type=str, default="both", choices=["baseline", "gradient_ascent", "both"],
|
| 666 |
+
help="Which evaluation to run: baseline, gradient_ascent, or both (default: both)")
|
| 667 |
+
|
| 668 |
+
# Metrics selection
|
| 669 |
+
parser.add_argument("--metrics", type=str, nargs="+", default=["clip", "aesthetic"],
|
| 670 |
+
choices=["fid", "clip", "aesthetic", "pickscore", "hpsv2", "hpsv21", "imagereward"],
|
| 671 |
+
help="Which metrics to evaluate (default: clip aesthetic)")
|
| 672 |
+
parser.add_argument("--scorer_device", type=str, default="auto", choices=["auto", "cpu", "cuda"],
|
| 673 |
+
help="Device for metric scorers. auto keeps scorers on GPU only when enough VRAM is free.")
|
| 674 |
+
|
| 675 |
+
# Gradient ascent config
|
| 676 |
+
parser.add_argument("--grad_config", type=str, default=None,
|
| 677 |
+
help=f"Gradient ascent config preset (available: {', '.join(list_configs())}). "
|
| 678 |
+
"If provided, overrides individual grad_* arguments.")
|
| 679 |
+
parser.add_argument("--grad_range_start", type=int, default=0, help="Gradient timestep range start")
|
| 680 |
+
parser.add_argument("--grad_range_end", type=int, default=700, help="Gradient timestep range end")
|
| 681 |
+
parser.add_argument("--grad_steps", type=int, default=5, help="Number of gradient steps per timestep (use 5 for better reward improvement)")
|
| 682 |
+
parser.add_argument("--grad_step_size", type=float, default=0.1, help="Gradient step size (initial LR)")
|
| 683 |
+
|
| 684 |
+
# Config overrides (these override values from grad_config if specified)
|
| 685 |
+
parser.add_argument("--override_momentum", type=float, default=None, help="Override momentum value from grad_config")
|
| 686 |
+
parser.add_argument("--override_num_grad_steps", type=int, default=None, help="Override num_grad_steps from grad_config")
|
| 687 |
+
parser.add_argument("--override_grad_step_size", type=float, default=None, help="Override grad_step_size from grad_config")
|
| 688 |
+
|
| 689 |
+
# Cuda
|
| 690 |
+
parser.add_argument("--cuda", type=int, default=0, help="Use CUDA device id")
|
| 691 |
+
|
| 692 |
+
args = parser.parse_args()
|
| 693 |
+
|
| 694 |
+
hf_cache_dir, offline_enabled = configure_hf_runtime(args.hf_cache_dir, force_offline=args.offline)
|
| 695 |
+
if args.lrm_model is None:
|
| 696 |
+
args.lrm_model = resolve_default_lrm_model()
|
| 697 |
+
|
| 698 |
+
seed_everything(args.seed)
|
| 699 |
+
|
| 700 |
+
# Configuration
|
| 701 |
+
device = f"cuda:{args.cuda}" if torch.cuda.is_available() else "cpu"
|
| 702 |
+
dtype = torch.float16 #if torch.cuda.is_available() else torch.float32
|
| 703 |
+
configure_cudnn_safely(device)
|
| 704 |
+
|
| 705 |
+
# Create auto-incremented output directory
|
| 706 |
+
args.output_dir = auto_increment_path(args.output_dir)
|
| 707 |
+
|
| 708 |
+
# Setup logging to file
|
| 709 |
+
tee_logger, log_file = setup_logging(args.output_dir)
|
| 710 |
+
|
| 711 |
+
print("="*70)
|
| 712 |
+
print("FID EVALUATION: BASELINE vs GRADIENT ASCENT")
|
| 713 |
+
print("="*70)
|
| 714 |
+
print(f"\nLogging to: {log_file}")
|
| 715 |
+
print(f"\nDevice: {device}")
|
| 716 |
+
print(f"Dataset: {args.dataset_type.upper()}")
|
| 717 |
+
print(f"Data directory: {args.data_dir}")
|
| 718 |
+
print(f"Base model: {args.base_model}")
|
| 719 |
+
print(f"Model variant: {args.model_variant}")
|
| 720 |
+
print(f"LRM model: {args.lrm_model}")
|
| 721 |
+
print(f"HF cache dir: {hf_cache_dir or 'default'}")
|
| 722 |
+
print(f"HF offline mode: {offline_enabled}")
|
| 723 |
+
print(f"Inference steps: {args.num_steps}")
|
| 724 |
+
print(f"CFG scale: {args.cfg_scale}")
|
| 725 |
+
print(f"Batch size: {args.batch_size}")
|
| 726 |
+
print(f"Max samples: {args.max_samples or 'All'}")
|
| 727 |
+
print(f"Output directory: {args.output_dir}")
|
| 728 |
+
print(f"Save images: {args.save_images}")
|
| 729 |
+
print(f"Evaluation mode: {args.mode}")
|
| 730 |
+
print(f"Metrics to evaluate: {', '.join(args.metrics).upper()}")
|
| 731 |
+
if args.grad_config:
|
| 732 |
+
print(f"Gradient ascent config: {args.grad_config}")
|
| 733 |
+
|
| 734 |
+
# Load validation data
|
| 735 |
+
print("\n" + "="*70)
|
| 736 |
+
print("1. LOADING VALIDATION DATA")
|
| 737 |
+
print("="*70)
|
| 738 |
+
prompts, image_paths = load_validation_data(
|
| 739 |
+
args.data_dir,
|
| 740 |
+
args.max_samples,
|
| 741 |
+
args.dataset_type,
|
| 742 |
+
hf_cache_dir=hf_cache_dir,
|
| 743 |
+
offline=offline_enabled,
|
| 744 |
+
)
|
| 745 |
+
|
| 746 |
+
# Automatically disable FID if no reference images available (e.g., Pick-a-Pic dataset)
|
| 747 |
+
can_compute_fid = image_paths is not None
|
| 748 |
+
if not can_compute_fid and "fid" in args.metrics:
|
| 749 |
+
print("\n⚠ Warning: FID metric requested but no reference images available. FID will be skipped.")
|
| 750 |
+
args.metrics = [m for m in args.metrics if m != "fid"]
|
| 751 |
+
|
| 752 |
+
# Load reward model
|
| 753 |
+
print("\n" + "="*70)
|
| 754 |
+
print("2. LOADING REWARD MODEL")
|
| 755 |
+
print("="*70)
|
| 756 |
+
reward_model = LRMRewardModel(
|
| 757 |
+
pretrained_model_name_or_path=args.base_model,
|
| 758 |
+
lrm_model_path=args.lrm_model,
|
| 759 |
+
guidance_scale=args.cfg_scale,
|
| 760 |
+
device=device
|
| 761 |
+
)
|
| 762 |
+
if dtype == torch.float16:
|
| 763 |
+
reward_model = reward_model.half()
|
| 764 |
+
reward_model.eval()
|
| 765 |
+
print("✓ Reward model loaded")
|
| 766 |
+
|
| 767 |
+
# Load pipeline
|
| 768 |
+
print("\n" + "="*70)
|
| 769 |
+
print("3. LOADING PIPELINE")
|
| 770 |
+
print("="*70)
|
| 771 |
+
|
| 772 |
+
pretrained_kwargs = {"local_files_only": offline_enabled}
|
| 773 |
+
if hf_cache_dir:
|
| 774 |
+
pretrained_kwargs["cache_dir"] = hf_cache_dir
|
| 775 |
+
|
| 776 |
+
# Load model based on variant
|
| 777 |
+
if args.model_variant == "origin":
|
| 778 |
+
base_pipeline = StableDiffusionPipeline.from_pretrained(
|
| 779 |
+
args.base_model,
|
| 780 |
+
torch_dtype=dtype,
|
| 781 |
+
safety_checker=None,
|
| 782 |
+
**pretrained_kwargs,
|
| 783 |
+
)
|
| 784 |
+
print(f"✓ Loaded origin SD1.5 model")
|
| 785 |
+
elif args.model_variant == "spo":
|
| 786 |
+
base_pipeline = StableDiffusionPipeline.from_pretrained(
|
| 787 |
+
'SPO-Diffusion-Models/SPO-SD-v1-5_4k-p_10ep',
|
| 788 |
+
torch_dtype=dtype,
|
| 789 |
+
safety_checker=None,
|
| 790 |
+
**pretrained_kwargs,
|
| 791 |
+
)
|
| 792 |
+
args.cfg_scale = 5.0 # SPO uses CFG 5.0
|
| 793 |
+
print(f"✓ Loaded SPO SD1.5 model (cfg_scale adjusted to 5.0)")
|
| 794 |
+
elif args.model_variant == "diffusion_dpo":
|
| 795 |
+
unet = UNet2DConditionModel.from_pretrained(
|
| 796 |
+
'mhdang/dpo-sd1.5-text2image-v1',
|
| 797 |
+
subfolder="unet",
|
| 798 |
+
torch_dtype=dtype,
|
| 799 |
+
**pretrained_kwargs,
|
| 800 |
+
)
|
| 801 |
+
base_pipeline = StableDiffusionPipeline.from_pretrained(
|
| 802 |
+
args.base_model,
|
| 803 |
+
torch_dtype=dtype,
|
| 804 |
+
safety_checker=None,
|
| 805 |
+
unet=unet,
|
| 806 |
+
**pretrained_kwargs,
|
| 807 |
+
)
|
| 808 |
+
print(f"✓ Loaded Diffusion-DPO SD1.5 model")
|
| 809 |
+
elif args.model_variant == "lpo":
|
| 810 |
+
unet = UNet2DConditionModel.from_pretrained(
|
| 811 |
+
'casiatao/LPO',
|
| 812 |
+
subfolder="lpo_sd15_merge/unet",
|
| 813 |
+
torch_dtype=dtype,
|
| 814 |
+
**pretrained_kwargs,
|
| 815 |
+
)
|
| 816 |
+
base_pipeline = StableDiffusionPipeline.from_pretrained(
|
| 817 |
+
args.base_model,
|
| 818 |
+
torch_dtype=dtype,
|
| 819 |
+
safety_checker=None,
|
| 820 |
+
unet=unet,
|
| 821 |
+
**pretrained_kwargs,
|
| 822 |
+
)
|
| 823 |
+
args.cfg_scale = 5.0 # LPO uses CFG 5.0
|
| 824 |
+
print(f"✓ Loaded LPO SD1.5 model (cfg_scale adjusted to 5.0)")
|
| 825 |
+
|
| 826 |
+
pipeline = StableDiffusionGradientAscentPipeline(**base_pipeline.components)
|
| 827 |
+
pipeline.scheduler = DDIMScheduler.from_config(pipeline.scheduler.config)
|
| 828 |
+
pipeline = pipeline.to(device)
|
| 829 |
+
pipeline.set_reward_model(reward_model)
|
| 830 |
+
print("✓ Pipeline loaded")
|
| 831 |
+
|
| 832 |
+
scorer_device = resolve_scorer_device(args.scorer_device, device)
|
| 833 |
+
scorer_dtype = dtype if str(scorer_device).startswith("cuda") else torch.float32
|
| 834 |
+
print(f"Scorer device: {scorer_device}")
|
| 835 |
+
|
| 836 |
+
if torch.cuda.is_available():
|
| 837 |
+
torch.cuda.empty_cache()
|
| 838 |
+
|
| 839 |
+
# Load CLIP scorer
|
| 840 |
+
print("\n" + "="*70)
|
| 841 |
+
print("3.5. LOADING CLIP AND AESTHETIC SCORERS")
|
| 842 |
+
print("="*70)
|
| 843 |
+
|
| 844 |
+
# Only load scorers for requested metrics
|
| 845 |
+
clip_scorer = None
|
| 846 |
+
aesthetic_scorer = None
|
| 847 |
+
pick_scorer = None
|
| 848 |
+
hpsv2_scorer = None
|
| 849 |
+
hpsv21_scorer = None
|
| 850 |
+
imagereward_scorer = None
|
| 851 |
+
|
| 852 |
+
if "clip" in args.metrics:
|
| 853 |
+
try:
|
| 854 |
+
clip_scorer = CLIPScore(model_name_or_path="openai/clip-vit-large-patch14").to(scorer_device)
|
| 855 |
+
print("✓ CLIP scorer loaded")
|
| 856 |
+
except Exception as e:
|
| 857 |
+
print(f"Warning: Could not load CLIP scorer: {e}")
|
| 858 |
+
clip_scorer = None
|
| 859 |
+
else:
|
| 860 |
+
print("⊘ CLIP scorer skipped (not in selected metrics)")
|
| 861 |
+
|
| 862 |
+
if "aesthetic" in args.metrics:
|
| 863 |
+
try:
|
| 864 |
+
aesthetic_scorer = AestheticScorer(dtype=scorer_dtype, device=scorer_device)
|
| 865 |
+
print("✓ Aesthetic scorer loaded")
|
| 866 |
+
except Exception as e:
|
| 867 |
+
print(f"Warning: Could not load Aesthetic scorer: {e}")
|
| 868 |
+
aesthetic_scorer = None
|
| 869 |
+
else:
|
| 870 |
+
print("⊘ Aesthetic scorer skipped (not in selected metrics)")
|
| 871 |
+
|
| 872 |
+
if "pickscore" in args.metrics:
|
| 873 |
+
try:
|
| 874 |
+
from pick_score import PickScorer
|
| 875 |
+
pick_scorer = PickScorer(
|
| 876 |
+
processor_name_or_path="laion/CLIP-ViT-H-14-laion2B-s32B-b79K",
|
| 877 |
+
model_pretrained_name_or_path="yuvalkirstain/PickScore_v1",
|
| 878 |
+
device=scorer_device
|
| 879 |
+
)
|
| 880 |
+
print("✓ PickScore scorer loaded")
|
| 881 |
+
except Exception as e:
|
| 882 |
+
print(f"Warning: Could not load PickScore scorer: {e}")
|
| 883 |
+
pick_scorer = None
|
| 884 |
+
else:
|
| 885 |
+
print("⊘ PickScore scorer skipped (not in selected metrics)")
|
| 886 |
+
|
| 887 |
+
if "hpsv2" in args.metrics:
|
| 888 |
+
try:
|
| 889 |
+
from hpsv2_score import HPSv2Scorer
|
| 890 |
+
hf_dl_kwargs = {"local_files_only": offline_enabled}
|
| 891 |
+
if hf_cache_dir:
|
| 892 |
+
hf_dl_kwargs["cache_dir"] = hf_cache_dir
|
| 893 |
+
hpsv2_scorer = HPSv2Scorer(
|
| 894 |
+
clip_pretrained_name_or_path=hf_hub_download(
|
| 895 |
+
repo_id="laion/CLIP-ViT-H-14-laion2B-s32B-b79K",
|
| 896 |
+
filename="open_clip_pytorch_model.bin",
|
| 897 |
+
**hf_dl_kwargs,
|
| 898 |
+
),
|
| 899 |
+
model_pretrained_name_or_path=hf_hub_download(
|
| 900 |
+
repo_id="xswu/HPSv2",
|
| 901 |
+
filename="HPS_v2_compressed.pt",
|
| 902 |
+
**hf_dl_kwargs,
|
| 903 |
+
),
|
| 904 |
+
device=scorer_device
|
| 905 |
+
)
|
| 906 |
+
print("✓ HPSv2 scorer loaded")
|
| 907 |
+
except Exception as e:
|
| 908 |
+
print(f"Warning: Could not load HPSv2 scorer: {e}")
|
| 909 |
+
hpsv2_scorer = None
|
| 910 |
+
else:
|
| 911 |
+
print("⊘ HPSv2 scorer skipped (not in selected metrics)")
|
| 912 |
+
|
| 913 |
+
if "hpsv21" in args.metrics:
|
| 914 |
+
try:
|
| 915 |
+
from hpsv2_score import HPSv2Scorer
|
| 916 |
+
hf_dl_kwargs = {"local_files_only": offline_enabled}
|
| 917 |
+
if hf_cache_dir:
|
| 918 |
+
hf_dl_kwargs["cache_dir"] = hf_cache_dir
|
| 919 |
+
hpsv21_scorer = HPSv2Scorer(
|
| 920 |
+
clip_pretrained_name_or_path=hf_hub_download(
|
| 921 |
+
repo_id="laion/CLIP-ViT-H-14-laion2B-s32B-b79K",
|
| 922 |
+
filename="open_clip_pytorch_model.bin",
|
| 923 |
+
**hf_dl_kwargs,
|
| 924 |
+
),
|
| 925 |
+
model_pretrained_name_or_path=hf_hub_download(
|
| 926 |
+
repo_id="xswu/HPSv2",
|
| 927 |
+
filename="HPS_v2.1_compressed.pt",
|
| 928 |
+
**hf_dl_kwargs,
|
| 929 |
+
),
|
| 930 |
+
device=scorer_device
|
| 931 |
+
)
|
| 932 |
+
print("✓ HPSv2.1 scorer loaded")
|
| 933 |
+
except Exception as e:
|
| 934 |
+
print(f"Warning: Could not load HPSv2.1 scorer: {e}")
|
| 935 |
+
hpsv21_scorer = None
|
| 936 |
+
else:
|
| 937 |
+
print("⊘ HPSv2.1 scorer skipped (not in selected metrics)")
|
| 938 |
+
|
| 939 |
+
if "imagereward" in args.metrics:
|
| 940 |
+
try:
|
| 941 |
+
from imagereward_score import load_imagereward
|
| 942 |
+
hf_dl_kwargs = {"local_files_only": offline_enabled}
|
| 943 |
+
if hf_cache_dir:
|
| 944 |
+
hf_dl_kwargs["cache_dir"] = hf_cache_dir
|
| 945 |
+
imagereward_scorer = load_imagereward(
|
| 946 |
+
model_path=hf_hub_download(repo_id="THUDM/ImageReward", filename="ImageReward.pt", **hf_dl_kwargs),
|
| 947 |
+
med_config=hf_hub_download(repo_id="THUDM/ImageReward", filename="med_config.json", **hf_dl_kwargs),
|
| 948 |
+
device=scorer_device
|
| 949 |
+
)
|
| 950 |
+
print("✓ ImageReward scorer loaded")
|
| 951 |
+
except Exception as e:
|
| 952 |
+
print(f"Warning: Could not load ImageReward scorer: {e}")
|
| 953 |
+
imagereward_scorer = None
|
| 954 |
+
else:
|
| 955 |
+
print("⊘ ImageReward scorer skipped (not in selected metrics)")
|
| 956 |
+
|
| 957 |
+
# Configure gradient ascent
|
| 958 |
+
print("\n" + "="*70)
|
| 959 |
+
print("4. CONFIGURING GRADIENT ASCENT")
|
| 960 |
+
print("="*70)
|
| 961 |
+
|
| 962 |
+
# Use config preset if provided, otherwise use individual args
|
| 963 |
+
if args.grad_config:
|
| 964 |
+
print(f"Loading gradient ascent config: {args.grad_config}")
|
| 965 |
+
grad_config = get_config(args.grad_config)
|
| 966 |
+
print(f"Config loaded: {grad_config}")
|
| 967 |
+
|
| 968 |
+
# Apply overrides if specified
|
| 969 |
+
if args.override_momentum is not None:
|
| 970 |
+
grad_config['momentum'] = args.override_momentum
|
| 971 |
+
print(f" Overriding momentum: {args.override_momentum}")
|
| 972 |
+
if args.override_num_grad_steps is not None:
|
| 973 |
+
grad_config['num_grad_steps'] = args.override_num_grad_steps
|
| 974 |
+
print(f" Overriding num_grad_steps: {args.override_num_grad_steps}")
|
| 975 |
+
if args.override_grad_step_size is not None:
|
| 976 |
+
grad_config['grad_step_size'] = args.override_grad_step_size
|
| 977 |
+
print(f" Overriding grad_step_size: {args.override_grad_step_size}")
|
| 978 |
+
else:
|
| 979 |
+
grad_config = {
|
| 980 |
+
"grad_timestep_range": (args.grad_range_start, args.grad_range_end),
|
| 981 |
+
"num_grad_steps": args.grad_steps,
|
| 982 |
+
"grad_step_size": args.grad_step_size,
|
| 983 |
+
}
|
| 984 |
+
print(f"Using manual gradient ascent configuration")
|
| 985 |
+
|
| 986 |
+
print(f"Gradient timestep range: {grad_config.get('grad_timestep_range', (args.grad_range_start, args.grad_range_end))}")
|
| 987 |
+
print(f"Gradient steps: {grad_config.get('num_grad_steps', args.grad_steps)}")
|
| 988 |
+
print(f"Gradient step size (initial LR): {grad_config.get('grad_step_size', args.grad_step_size)}")
|
| 989 |
+
if grad_config.get('lr_scheduler_type'):
|
| 990 |
+
print(f"LR Scheduler: {grad_config['lr_scheduler_type']}")
|
| 991 |
+
if grad_config.get('use_momentum'):
|
| 992 |
+
print(f"Momentum: {grad_config.get('momentum', 0.9)} (Nesterov: {grad_config.get('use_nesterov', False)})")
|
| 993 |
+
|
| 994 |
+
pipeline.enable_gradient_ascent(**grad_config)
|
| 995 |
+
|
| 996 |
+
# Initialize result variables
|
| 997 |
+
fid_score_baseline = None
|
| 998 |
+
avg_reward_baseline = None
|
| 999 |
+
clip_score_baseline = None
|
| 1000 |
+
aesthetic_score_baseline = None
|
| 1001 |
+
pick_score_baseline = None
|
| 1002 |
+
hpsv2_score_baseline = None
|
| 1003 |
+
hpsv21_score_baseline = None
|
| 1004 |
+
imagereward_score_baseline = None
|
| 1005 |
+
fid_score_grad = None
|
| 1006 |
+
avg_reward_grad = None
|
| 1007 |
+
clip_score_grad = None
|
| 1008 |
+
aesthetic_score_grad = None
|
| 1009 |
+
pick_score_grad = None
|
| 1010 |
+
hpsv2_score_grad = None
|
| 1011 |
+
hpsv21_score_grad = None
|
| 1012 |
+
imagereward_score_grad = None
|
| 1013 |
+
grad_stats = None
|
| 1014 |
+
|
| 1015 |
+
# ========== BASELINE EVALUATION ==========
|
| 1016 |
+
if args.mode in ["baseline", "both"]:
|
| 1017 |
+
print("\n" + "="*70)
|
| 1018 |
+
print("5. EVALUATING BASELINE")
|
| 1019 |
+
print("="*70)
|
| 1020 |
+
|
| 1021 |
+
# Generate and evaluate baseline
|
| 1022 |
+
avg_reward_baseline, fid_baseline, clip_score_baseline, aesthetic_score_baseline, pick_score_baseline, hpsv2_score_baseline, hpsv21_score_baseline, imagereward_score_baseline, _, baseline_trajectory = generate_and_evaluate(
|
| 1023 |
+
pipeline=pipeline,
|
| 1024 |
+
prompts=prompts,
|
| 1025 |
+
image_paths=image_paths,
|
| 1026 |
+
device=device,
|
| 1027 |
+
dtype=dtype,
|
| 1028 |
+
num_inference_steps=args.num_steps,
|
| 1029 |
+
guidance_scale=args.cfg_scale,
|
| 1030 |
+
seed=args.seed,
|
| 1031 |
+
batch_size=args.batch_size,
|
| 1032 |
+
apply_gradient_ascent=False,
|
| 1033 |
+
mode_name="baseline",
|
| 1034 |
+
log_interval=args.log_interval,
|
| 1035 |
+
output_dir=args.output_dir,
|
| 1036 |
+
save_images=args.save_images,
|
| 1037 |
+
clip_scorer=clip_scorer,
|
| 1038 |
+
aesthetic_scorer=aesthetic_scorer,
|
| 1039 |
+
pick_scorer=pick_scorer,
|
| 1040 |
+
hpsv2_scorer=hpsv2_scorer,
|
| 1041 |
+
hpsv21_scorer=hpsv21_scorer,
|
| 1042 |
+
imagereward_scorer=imagereward_scorer,
|
| 1043 |
+
compute_fid=("fid" in args.metrics and can_compute_fid),
|
| 1044 |
+
capture_trajectory=True
|
| 1045 |
+
)
|
| 1046 |
+
|
| 1047 |
+
# Compute FID for baseline if requested
|
| 1048 |
+
if "fid" in args.metrics and fid_baseline is not None:
|
| 1049 |
+
fid_score_baseline = fid_baseline.compute().item()
|
| 1050 |
+
print(f"\n✓ Baseline FID: {fid_score_baseline:.4f}")
|
| 1051 |
+
print(f"✓ Baseline Avg Reward: {avg_reward_baseline:.4f}")
|
| 1052 |
+
if "clip" in args.metrics:
|
| 1053 |
+
print(f"✓ Baseline Avg CLIP Score: {clip_score_baseline:.4f}")
|
| 1054 |
+
if "aesthetic" in args.metrics:
|
| 1055 |
+
print(f"✓ Baseline Avg Aesthetic Score: {aesthetic_score_baseline:.4f}")
|
| 1056 |
+
if "pickscore" in args.metrics and pick_score_baseline is not None:
|
| 1057 |
+
print(f"✓ Baseline Avg PickScore: {pick_score_baseline:.4f}")
|
| 1058 |
+
if "hpsv2" in args.metrics and hpsv2_score_baseline is not None:
|
| 1059 |
+
print(f"✓ Baseline Avg HPSv2 Score: {hpsv2_score_baseline:.4f}")
|
| 1060 |
+
if "hpsv21" in args.metrics and hpsv21_score_baseline is not None:
|
| 1061 |
+
print(f"✓ Baseline Avg HPSv2.1 Score: {hpsv21_score_baseline:.4f}")
|
| 1062 |
+
if "imagereward" in args.metrics and imagereward_score_baseline is not None:
|
| 1063 |
+
print(f"✓ Baseline Avg ImageReward: {imagereward_score_baseline:.4f}")
|
| 1064 |
+
|
| 1065 |
+
# ========== GRADIENT ASCENT EVALUATION ==========
|
| 1066 |
+
if args.mode in ["gradient_ascent", "both"]:
|
| 1067 |
+
print("\n" + "="*70)
|
| 1068 |
+
print("6. EVALUATING GRADIENT ASCENT")
|
| 1069 |
+
print("="*70)
|
| 1070 |
+
|
| 1071 |
+
# Generate and evaluate with gradient ascent
|
| 1072 |
+
avg_reward_grad, fid_grad, clip_score_grad, aesthetic_score_grad, pick_score_grad, hpsv2_score_grad, hpsv21_score_grad, imagereward_score_grad, lr_history, guided_trajectory = generate_and_evaluate(
|
| 1073 |
+
pipeline=pipeline,
|
| 1074 |
+
prompts=prompts,
|
| 1075 |
+
image_paths=image_paths,
|
| 1076 |
+
device=device,
|
| 1077 |
+
dtype=dtype,
|
| 1078 |
+
num_inference_steps=args.num_steps,
|
| 1079 |
+
guidance_scale=args.cfg_scale,
|
| 1080 |
+
seed=args.seed,
|
| 1081 |
+
batch_size=args.batch_size,
|
| 1082 |
+
apply_gradient_ascent=True,
|
| 1083 |
+
mode_name="gradient_ascent",
|
| 1084 |
+
log_interval=args.log_interval,
|
| 1085 |
+
output_dir=args.output_dir,
|
| 1086 |
+
save_images=args.save_images,
|
| 1087 |
+
clip_scorer=clip_scorer,
|
| 1088 |
+
aesthetic_scorer=aesthetic_scorer,
|
| 1089 |
+
pick_scorer=pick_scorer,
|
| 1090 |
+
hpsv2_scorer=hpsv2_scorer,
|
| 1091 |
+
hpsv21_scorer=hpsv21_scorer,
|
| 1092 |
+
imagereward_scorer=imagereward_scorer,
|
| 1093 |
+
compute_fid=("fid" in args.metrics and can_compute_fid),
|
| 1094 |
+
capture_trajectory=True
|
| 1095 |
+
)
|
| 1096 |
+
|
| 1097 |
+
# Compute FID for gradient ascent if requested
|
| 1098 |
+
if "fid" in args.metrics and fid_grad is not None:
|
| 1099 |
+
fid_score_grad = fid_grad.compute().item()
|
| 1100 |
+
print(f"\n✓ Gradient Ascent FID: {fid_score_grad:.4f}")
|
| 1101 |
+
print(f"✓ Gradient Ascent Avg Reward: {avg_reward_grad:.4f}")
|
| 1102 |
+
if "clip" in args.metrics:
|
| 1103 |
+
print(f"✓ Gradient Ascent Avg CLIP Score: {clip_score_grad:.4f}")
|
| 1104 |
+
if "aesthetic" in args.metrics:
|
| 1105 |
+
print(f"✓ Gradient Ascent Avg Aesthetic Score: {aesthetic_score_grad:.4f}")
|
| 1106 |
+
if "pickscore" in args.metrics and pick_score_grad is not None:
|
| 1107 |
+
print(f"✓ Gradient Ascent Avg PickScore: {pick_score_grad:.4f}")
|
| 1108 |
+
if "hpsv2" in args.metrics and hpsv2_score_grad is not None:
|
| 1109 |
+
print(f"✓ Gradient Ascent Avg HPSv2 Score: {hpsv2_score_grad:.4f}")
|
| 1110 |
+
if "hpsv21" in args.metrics and hpsv21_score_grad is not None:
|
| 1111 |
+
print(f"✓ Gradient Ascent Avg HPSv2.1 Score: {hpsv21_score_grad:.4f}")
|
| 1112 |
+
if "imagereward" in args.metrics and imagereward_score_grad is not None:
|
| 1113 |
+
print(f"✓ Gradient Ascent Avg ImageReward: {imagereward_score_grad:.4f}")
|
| 1114 |
+
|
| 1115 |
+
# Get gradient stats
|
| 1116 |
+
grad_stats = pipeline.grad_guidance.get_statistics()
|
| 1117 |
+
if grad_stats:
|
| 1118 |
+
print(f"\nGradient Ascent Statistics:")
|
| 1119 |
+
print(f" Applications: {grad_stats['num_applications']}")
|
| 1120 |
+
print(f" Total reward improvement: {grad_stats['total_reward_improvement']:+.4f}")
|
| 1121 |
+
print(f" Avg reward improvement: {grad_stats['avg_reward_improvement']:+.4f}")
|
| 1122 |
+
|
| 1123 |
+
# Plot LR curve if we captured it
|
| 1124 |
+
if lr_history is not None and lr_history['learning_rates']:
|
| 1125 |
+
plot_path = Path(args.output_dir) / "lr_curve.png"
|
| 1126 |
+
|
| 1127 |
+
# LR values are now continuous across all gradient steps
|
| 1128 |
+
lrs = lr_history['learning_rates']
|
| 1129 |
+
steps = list(range(len(lrs))) # Step indices (0 to total_steps-1)
|
| 1130 |
+
|
| 1131 |
+
plt.figure(figsize=(12, 6))
|
| 1132 |
+
plt.plot(steps, lrs, linewidth=2, color='blue', alpha=0.8)
|
| 1133 |
+
|
| 1134 |
+
# Mark the first step with a star
|
| 1135 |
+
plt.plot(steps[0], lrs[0], marker='*', markersize=20, color='gold',
|
| 1136 |
+
markeredgecolor='darkgoldenrod', markeredgewidth=2, zorder=5)
|
| 1137 |
+
|
| 1138 |
+
# Mark timestep boundaries
|
| 1139 |
+
num_timesteps = len(lr_history['timesteps'])
|
| 1140 |
+
num_grad_steps_per_timestep = len(lrs) // num_timesteps if num_timesteps > 0 else 0
|
| 1141 |
+
if num_grad_steps_per_timestep > 0:
|
| 1142 |
+
for i in range(num_timesteps + 1):
|
| 1143 |
+
step_idx = i * num_grad_steps_per_timestep
|
| 1144 |
+
if step_idx <= len(lrs):
|
| 1145 |
+
plt.axvline(x=step_idx, color='red', linestyle='--', alpha=0.3, linewidth=1)
|
| 1146 |
+
if i < num_timesteps:
|
| 1147 |
+
plt.text(step_idx, plt.ylim()[1] * 0.95, f't={lr_history["timesteps"][i]}',
|
| 1148 |
+
fontsize=8, color='red', alpha=0.7, ha='left')
|
| 1149 |
+
|
| 1150 |
+
plt.xlabel('Global Gradient Step', fontsize=12)
|
| 1151 |
+
plt.ylabel('Learning Rate', fontsize=12)
|
| 1152 |
+
plt.title(f'Learning Rate Evolution Across All Gradient Steps\\nPrompt: "{lr_history["prompt"][:60]}..."',
|
| 1153 |
+
fontsize=12, fontweight='bold')
|
| 1154 |
+
plt.grid(True, alpha=0.3)
|
| 1155 |
+
|
| 1156 |
+
# Add info text
|
| 1157 |
+
num_timesteps = len(lr_history['timesteps'])
|
| 1158 |
+
num_grad_steps_per_timestep = len(lrs) // num_timesteps if num_timesteps > 0 else 0
|
| 1159 |
+
plt.text(0.02, 0.98,
|
| 1160 |
+
f'Total timesteps: {num_timesteps}\\nGrad steps/timestep: {num_grad_steps_per_timestep}\\nTotal grad steps: {len(lrs)}',
|
| 1161 |
+
transform=plt.gca().transAxes, fontsize=10, verticalalignment='top',
|
| 1162 |
+
bbox=dict(boxstyle='round', facecolor='wheat', alpha=0.5))
|
| 1163 |
+
|
| 1164 |
+
plt.tight_layout()
|
| 1165 |
+
plt.savefig(plot_path, dpi=150, bbox_inches='tight')
|
| 1166 |
+
plt.close()
|
| 1167 |
+
print(f"\n✓ Saved LR curve plot to: {plot_path}")
|
| 1168 |
+
print(f" Total gradient steps: {len(lrs)}")
|
| 1169 |
+
print(f" LR range: {min(lrs):.6f} → {max(lrs):.6f}")
|
| 1170 |
+
|
| 1171 |
+
# Plot Rewards curve if we captured it
|
| 1172 |
+
if lr_history is not None and lr_history['rewards']:
|
| 1173 |
+
plot_path = Path(args.output_dir) / "rewards_curve.png"
|
| 1174 |
+
|
| 1175 |
+
# Reward values are now continuous across all gradient steps
|
| 1176 |
+
rewards = lr_history['rewards']
|
| 1177 |
+
steps = list(range(len(rewards))) # Step indices (0 to total_steps-1)
|
| 1178 |
+
|
| 1179 |
+
plt.figure(figsize=(12, 6))
|
| 1180 |
+
plt.plot(steps, rewards, linewidth=2, color='green', alpha=0.8)
|
| 1181 |
+
|
| 1182 |
+
# Mark the first step with a star
|
| 1183 |
+
plt.plot(steps[0], rewards[0], marker='*', markersize=20, color='gold',
|
| 1184 |
+
markeredgecolor='darkgoldenrod', markeredgewidth=2, zorder=5)
|
| 1185 |
+
|
| 1186 |
+
# Mark timestep boundaries
|
| 1187 |
+
num_timesteps = len(lr_history['timesteps'])
|
| 1188 |
+
# rewards has one extra value at the start (initial) compared to gradient steps
|
| 1189 |
+
num_grad_steps_per_timestep = (len(rewards) - num_timesteps) // num_timesteps if num_timesteps > 0 else 0
|
| 1190 |
+
if num_grad_steps_per_timestep > 0:
|
| 1191 |
+
for i in range(num_timesteps + 1):
|
| 1192 |
+
step_idx = i * (num_grad_steps_per_timestep + 1) # +1 because reward_history includes initial
|
| 1193 |
+
if step_idx <= len(rewards):
|
| 1194 |
+
plt.axvline(x=step_idx, color='red', linestyle='--', alpha=0.3, linewidth=1)
|
| 1195 |
+
if i < num_timesteps:
|
| 1196 |
+
plt.text(step_idx, plt.ylim()[1] * 0.95, f't={lr_history["timesteps"][i]}',
|
| 1197 |
+
fontsize=8, color='red', alpha=0.7, ha='left')
|
| 1198 |
+
|
| 1199 |
+
plt.xlabel('Global Gradient Step', fontsize=12)
|
| 1200 |
+
plt.ylabel('Reward Score', fontsize=12)
|
| 1201 |
+
plt.title(f'Reward Evolution Across All Gradient Steps\nPrompt: "{lr_history["prompt"][:60]}..."',
|
| 1202 |
+
fontsize=12, fontweight='bold')
|
| 1203 |
+
plt.grid(True, alpha=0.3)
|
| 1204 |
+
|
| 1205 |
+
# Add info text
|
| 1206 |
+
num_timesteps = len(lr_history['timesteps'])
|
| 1207 |
+
reward_improvement = rewards[-1] - rewards[0] if len(rewards) > 1 else 0
|
| 1208 |
+
plt.text(0.02, 0.98,
|
| 1209 |
+
f'Total timesteps: {num_timesteps}\nTotal grad steps: {len(rewards)}\n'
|
| 1210 |
+
f'Initial reward: {rewards[0]:.4f}\nFinal reward: {rewards[-1]:.4f}\n'
|
| 1211 |
+
f'Improvement: {reward_improvement:+.4f}',
|
| 1212 |
+
transform=plt.gca().transAxes, fontsize=10, verticalalignment='top',
|
| 1213 |
+
bbox=dict(boxstyle='round', facecolor='lightgreen', alpha=0.5))
|
| 1214 |
+
|
| 1215 |
+
plt.tight_layout()
|
| 1216 |
+
plt.savefig(plot_path, dpi=150, bbox_inches='tight')
|
| 1217 |
+
plt.close()
|
| 1218 |
+
print(f"\n✓ Saved Rewards curve plot to: {plot_path}")
|
| 1219 |
+
print(f" Total gradient steps: {len(rewards)}")
|
| 1220 |
+
print(f" Reward range: {min(rewards):.4f} → {max(rewards):.4f}")
|
| 1221 |
+
print(f" Total improvement: {reward_improvement:+.4f}")
|
| 1222 |
+
|
| 1223 |
+
# ---> NEW: PLOT TRAJECTORY DIVERGENCE (MANIFOLD DRIFT) <---
|
| 1224 |
+
if args.mode == "both" and 'baseline_trajectory' in locals() and 'guided_trajectory' in locals():
|
| 1225 |
+
if len(baseline_trajectory) == len(guided_trajectory) and len(baseline_trajectory) > 0:
|
| 1226 |
+
print("\n" + "="*70)
|
| 1227 |
+
print("7. CALCULATING TRAJECTORY DIVERGENCE (THEOREM 1 & 2)")
|
| 1228 |
+
print("="*70)
|
| 1229 |
+
|
| 1230 |
+
drift_path = Path(args.output_dir) / "trajectory_drift.png"
|
| 1231 |
+
|
| 1232 |
+
l2_distances = []
|
| 1233 |
+
# Calculate L2 norm ||z_t_guided - z_t_base||_2 for each step
|
| 1234 |
+
for b_lat, g_lat in zip(baseline_trajectory, guided_trajectory):
|
| 1235 |
+
dist = torch.norm(g_lat.float() - b_lat.float(), p=2).item()
|
| 1236 |
+
l2_distances.append(dist)
|
| 1237 |
+
|
| 1238 |
+
steps = list(range(len(l2_distances)))
|
| 1239 |
+
|
| 1240 |
+
plt.figure(figsize=(10, 6))
|
| 1241 |
+
plt.plot(steps, l2_distances, linewidth=2.5, color='purple', marker='o', markersize=4)
|
| 1242 |
+
|
| 1243 |
+
plt.xlabel('Denoising Step', fontsize=12)
|
| 1244 |
+
plt.ylabel('L2 Distance: ||z_guided - z_base||_2', fontsize=12)
|
| 1245 |
+
plt.title('Latent Trajectory Divergence (Manifold Drift)', fontsize=14, fontweight='bold')
|
| 1246 |
+
plt.grid(True, alpha=0.3)
|
| 1247 |
+
|
| 1248 |
+
# Add interpretation text based on your theory
|
| 1249 |
+
max_drift = max(l2_distances)
|
| 1250 |
+
plt.text(0.02, 0.98,
|
| 1251 |
+
f'Max Drift: {max_drift:.4f}\n'
|
| 1252 |
+
f'Final Drift: {l2_distances[-1]:.4f}\n'
|
| 1253 |
+
f'(Matches bounded drift from Thm 1\n'
|
| 1254 |
+
f'or ODE stiffness collapse from Thm 2)',
|
| 1255 |
+
transform=plt.gca().transAxes, fontsize=10, verticalalignment='top',
|
| 1256 |
+
bbox=dict(boxstyle='round', facecolor='thistle', alpha=0.5))
|
| 1257 |
+
|
| 1258 |
+
plt.tight_layout()
|
| 1259 |
+
plt.savefig(drift_path, dpi=150, bbox_inches='tight')
|
| 1260 |
+
plt.close()
|
| 1261 |
+
print(f"? Saved Manifold Drift curve to: {drift_path}")
|
| 1262 |
+
print(f" Max L2 Distance from baseline: {max_drift:.4f}")
|
| 1263 |
+
|
| 1264 |
+
# ========== FINAL RESULTS ==========
|
| 1265 |
+
print("\n" + "="*70)
|
| 1266 |
+
print("FINAL RESULTS")
|
| 1267 |
+
print("="*70)
|
| 1268 |
+
|
| 1269 |
+
if avg_reward_baseline is not None:
|
| 1270 |
+
print(f"\nBaseline:")
|
| 1271 |
+
if fid_score_baseline is not None:
|
| 1272 |
+
print(f" FID Score: {fid_score_baseline:.4f}")
|
| 1273 |
+
print(f" Avg Reward: {avg_reward_baseline:.4f}")
|
| 1274 |
+
if "clip" in args.metrics and clip_score_baseline is not None:
|
| 1275 |
+
print(f" Avg CLIP Score: {clip_score_baseline:.4f}")
|
| 1276 |
+
if "aesthetic" in args.metrics and aesthetic_score_baseline is not None:
|
| 1277 |
+
print(f" Avg Aesthetic: {aesthetic_score_baseline:.4f}")
|
| 1278 |
+
if "pickscore" in args.metrics and pick_score_baseline is not None:
|
| 1279 |
+
print(f" Avg PickScore: {pick_score_baseline:.4f}")
|
| 1280 |
+
if "hpsv2" in args.metrics and hpsv2_score_baseline is not None:
|
| 1281 |
+
print(f" Avg HPSv2: {hpsv2_score_baseline:.4f}")
|
| 1282 |
+
if "hpsv21" in args.metrics and hpsv21_score_baseline is not None:
|
| 1283 |
+
print(f" Avg HPSv2.1: {hpsv21_score_baseline:.4f}")
|
| 1284 |
+
if "imagereward" in args.metrics and imagereward_score_baseline is not None:
|
| 1285 |
+
print(f" Avg ImageReward: {imagereward_score_baseline:.4f}")
|
| 1286 |
+
|
| 1287 |
+
if avg_reward_grad is not None:
|
| 1288 |
+
print(f"\nGradient Ascent:")
|
| 1289 |
+
if fid_score_grad is not None:
|
| 1290 |
+
print(f" FID Score: {fid_score_grad:.4f}")
|
| 1291 |
+
print(f" Avg Reward: {avg_reward_grad:.4f}")
|
| 1292 |
+
if "clip" in args.metrics and clip_score_grad is not None:
|
| 1293 |
+
print(f" Avg CLIP Score: {clip_score_grad:.4f}")
|
| 1294 |
+
if "aesthetic" in args.metrics and aesthetic_score_grad is not None:
|
| 1295 |
+
print(f" Avg Aesthetic: {aesthetic_score_grad:.4f}")
|
| 1296 |
+
if "pickscore" in args.metrics and pick_score_grad is not None:
|
| 1297 |
+
print(f" Avg PickScore: {pick_score_grad:.4f}")
|
| 1298 |
+
if "hpsv2" in args.metrics and hpsv2_score_grad is not None:
|
| 1299 |
+
print(f" Avg HPSv2: {hpsv2_score_grad:.4f}")
|
| 1300 |
+
if "hpsv21" in args.metrics and hpsv21_score_grad is not None:
|
| 1301 |
+
print(f" Avg HPSv2.1: {hpsv21_score_grad:.4f}")
|
| 1302 |
+
if "imagereward" in args.metrics and imagereward_score_grad is not None:
|
| 1303 |
+
print(f" Avg ImageReward: {imagereward_score_grad:.4f}")
|
| 1304 |
+
|
| 1305 |
+
if avg_reward_baseline is not None and avg_reward_grad is not None:
|
| 1306 |
+
print(f"\nComparison:")
|
| 1307 |
+
if fid_score_baseline is not None and fid_score_grad is not None:
|
| 1308 |
+
fid_diff = fid_score_grad - fid_score_baseline
|
| 1309 |
+
print(f" FID Change: {fid_diff:+.4f} ({'worse' if fid_diff > 0 else 'better'}, lower is better)")
|
| 1310 |
+
reward_diff = avg_reward_grad - avg_reward_baseline
|
| 1311 |
+
print(f" Reward Change: {reward_diff:+.4f} ({'better' if reward_diff > 0 else 'worse'}, higher is better)")
|
| 1312 |
+
if "clip" in args.metrics and clip_score_baseline is not None and clip_score_grad is not None:
|
| 1313 |
+
clip_diff = clip_score_grad - clip_score_baseline
|
| 1314 |
+
print(f" CLIP Change: {clip_diff:+.4f} ({'better' if clip_diff > 0 else 'worse'}, higher is better)")
|
| 1315 |
+
if "aesthetic" in args.metrics and aesthetic_score_baseline is not None and aesthetic_score_grad is not None:
|
| 1316 |
+
aesthetic_diff = aesthetic_score_grad - aesthetic_score_baseline
|
| 1317 |
+
print(f" Aesthetic Change: {aesthetic_diff:+.4f} ({'better' if aesthetic_diff > 0 else 'worse'}, higher is better)")
|
| 1318 |
+
if "pickscore" in args.metrics and pick_score_baseline is not None and pick_score_grad is not None:
|
| 1319 |
+
pick_diff = pick_score_grad - pick_score_baseline
|
| 1320 |
+
print(f" PickScore Change: {pick_diff:+.4f} ({'better' if pick_diff > 0 else 'worse'}, higher is better)")
|
| 1321 |
+
if "hpsv2" in args.metrics and hpsv2_score_baseline is not None and hpsv2_score_grad is not None:
|
| 1322 |
+
hpsv2_diff = hpsv2_score_grad - hpsv2_score_baseline
|
| 1323 |
+
print(f" HPSv2 Change: {hpsv2_diff:+.4f} ({'better' if hpsv2_diff > 0 else 'worse'}, higher is better)")
|
| 1324 |
+
if "hpsv21" in args.metrics and hpsv21_score_baseline is not None and hpsv21_score_grad is not None:
|
| 1325 |
+
hpsv21_diff = hpsv21_score_grad - hpsv21_score_baseline
|
| 1326 |
+
print(f" HPSv2.1 Change: {hpsv21_diff:+.4f} ({'better' if hpsv21_diff > 0 else 'worse'}, higher is better)")
|
| 1327 |
+
if "imagereward" in args.metrics and imagereward_score_baseline is not None and imagereward_score_grad is not None:
|
| 1328 |
+
imagereward_diff = imagereward_score_grad - imagereward_score_baseline
|
| 1329 |
+
print(f" ImageReward Chg: {imagereward_diff:+.4f} ({'better' if imagereward_diff > 0 else 'worse'}, higher is better)")
|
| 1330 |
+
|
| 1331 |
+
# Save results to file
|
| 1332 |
+
results = {
|
| 1333 |
+
"mode": args.mode,
|
| 1334 |
+
"metrics": args.metrics,
|
| 1335 |
+
"config": {
|
| 1336 |
+
"num_samples": len(prompts),
|
| 1337 |
+
"num_steps": args.num_steps,
|
| 1338 |
+
"cfg_scale": args.cfg_scale,
|
| 1339 |
+
"grad_range": [args.grad_range_start, args.grad_range_end],
|
| 1340 |
+
"grad_steps": args.grad_steps,
|
| 1341 |
+
"grad_step_size": args.grad_step_size
|
| 1342 |
+
}
|
| 1343 |
+
}
|
| 1344 |
+
|
| 1345 |
+
if avg_reward_baseline is not None:
|
| 1346 |
+
results["baseline"] = {"avg_reward": avg_reward_baseline}
|
| 1347 |
+
if fid_score_baseline is not None:
|
| 1348 |
+
results["baseline"]["fid"] = fid_score_baseline
|
| 1349 |
+
if "clip" in args.metrics and clip_score_baseline is not None:
|
| 1350 |
+
results["baseline"]["clip_score"] = clip_score_baseline
|
| 1351 |
+
if "aesthetic" in args.metrics and aesthetic_score_baseline is not None:
|
| 1352 |
+
results["baseline"]["aesthetic_score"] = aesthetic_score_baseline
|
| 1353 |
+
if "pickscore" in args.metrics and pick_score_baseline is not None:
|
| 1354 |
+
results["baseline"]["pickscore"] = pick_score_baseline
|
| 1355 |
+
if "hpsv2" in args.metrics and hpsv2_score_baseline is not None:
|
| 1356 |
+
results["baseline"]["hpsv2_score"] = hpsv2_score_baseline
|
| 1357 |
+
if "hpsv21" in args.metrics and hpsv21_score_baseline is not None:
|
| 1358 |
+
results["baseline"]["hpsv21_score"] = hpsv21_score_baseline
|
| 1359 |
+
if "imagereward" in args.metrics and imagereward_score_baseline is not None:
|
| 1360 |
+
results["baseline"]["imagereward_score"] = imagereward_score_baseline
|
| 1361 |
+
|
| 1362 |
+
if avg_reward_grad is not None:
|
| 1363 |
+
results["gradient_ascent"] = {"avg_reward": avg_reward_grad}
|
| 1364 |
+
if fid_score_grad is not None:
|
| 1365 |
+
results["gradient_ascent"]["fid"] = fid_score_grad
|
| 1366 |
+
if "clip" in args.metrics and clip_score_grad is not None:
|
| 1367 |
+
results["gradient_ascent"]["clip_score"] = clip_score_grad
|
| 1368 |
+
if "aesthetic" in args.metrics and aesthetic_score_grad is not None:
|
| 1369 |
+
results["gradient_ascent"]["aesthetic_score"] = aesthetic_score_grad
|
| 1370 |
+
if "pickscore" in args.metrics and pick_score_grad is not None:
|
| 1371 |
+
results["gradient_ascent"]["pickscore"] = pick_score_grad
|
| 1372 |
+
if "hpsv2" in args.metrics and hpsv2_score_grad is not None:
|
| 1373 |
+
results["gradient_ascent"]["hpsv2_score"] = hpsv2_score_grad
|
| 1374 |
+
if "hpsv21" in args.metrics and hpsv21_score_grad is not None:
|
| 1375 |
+
results["gradient_ascent"]["hpsv21_score"] = hpsv21_score_grad
|
| 1376 |
+
if "imagereward" in args.metrics and imagereward_score_grad is not None:
|
| 1377 |
+
results["gradient_ascent"]["imagereward_score"] = imagereward_score_grad
|
| 1378 |
+
if grad_stats:
|
| 1379 |
+
results["gradient_ascent"]["stats"] = grad_stats
|
| 1380 |
+
|
| 1381 |
+
if avg_reward_baseline is not None and avg_reward_grad is not None:
|
| 1382 |
+
results["comparison"] = {
|
| 1383 |
+
"reward_difference": avg_reward_grad - avg_reward_baseline
|
| 1384 |
+
}
|
| 1385 |
+
if fid_score_baseline is not None and fid_score_grad is not None:
|
| 1386 |
+
results["comparison"]["fid_difference"] = fid_score_grad - fid_score_baseline
|
| 1387 |
+
if "clip" in args.metrics and clip_score_baseline is not None and clip_score_grad is not None:
|
| 1388 |
+
results["comparison"]["clip_difference"] = clip_score_grad - clip_score_baseline
|
| 1389 |
+
if "aesthetic" in args.metrics and aesthetic_score_baseline is not None and aesthetic_score_grad is not None:
|
| 1390 |
+
results["comparison"]["aesthetic_difference"] = aesthetic_score_grad - aesthetic_score_baseline
|
| 1391 |
+
if "pickscore" in args.metrics and pick_score_baseline is not None and pick_score_grad is not None:
|
| 1392 |
+
results["comparison"]["pickscore_difference"] = pick_score_grad - pick_score_baseline
|
| 1393 |
+
if "hpsv2" in args.metrics and hpsv2_score_baseline is not None and hpsv2_score_grad is not None:
|
| 1394 |
+
results["comparison"]["hpsv2_difference"] = hpsv2_score_grad - hpsv2_score_baseline
|
| 1395 |
+
if "hpsv21" in args.metrics and hpsv21_score_baseline is not None and hpsv21_score_grad is not None:
|
| 1396 |
+
results["comparison"]["hpsv21_difference"] = hpsv21_score_grad - hpsv21_score_baseline
|
| 1397 |
+
if "imagereward" in args.metrics and imagereward_score_baseline is not None and imagereward_score_grad is not None:
|
| 1398 |
+
results["comparison"]["imagereward_difference"] = imagereward_score_grad - imagereward_score_baseline
|
| 1399 |
+
|
| 1400 |
+
# Save results to output directory
|
| 1401 |
+
output_path = Path(args.output_dir)
|
| 1402 |
+
output_path.mkdir(parents=True, exist_ok=True)
|
| 1403 |
+
results_path = output_path / "evaluation_results.txt"
|
| 1404 |
+
|
| 1405 |
+
with open(results_path, "w") as f:
|
| 1406 |
+
for k, v in results.items():
|
| 1407 |
+
f.write(f"{k}: {v}\n")
|
| 1408 |
+
|
| 1409 |
+
|
| 1410 |
+
print(f"\n✓ Results saved to: {results_path}")
|
| 1411 |
+
if args.save_images:
|
| 1412 |
+
print(f"✓ Generated images saved to: {output_path}/baseline/ and {output_path}/gradient_ascent/")
|
| 1413 |
+
print("\n" + "="*70)
|
| 1414 |
+
|
| 1415 |
+
# Close logger
|
| 1416 |
+
tee_logger.close()
|
| 1417 |
+
sys.stdout = tee_logger.terminal
|
| 1418 |
+
|
| 1419 |
+
|
| 1420 |
+
if __name__ == "__main__":
|
| 1421 |
+
main()
|
| 1422 |
+
|
Reward_sd15_idealized/examples.sh
ADDED
|
@@ -0,0 +1,154 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/usr/bin/env bash
|
| 2 |
+
set -euo pipefail
|
| 3 |
+
|
| 4 |
+
clear
|
| 5 |
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
| 6 |
+
cd "$SCRIPT_DIR"
|
| 7 |
+
|
| 8 |
+
# Shared HF cache used on this cluster.
|
| 9 |
+
HF_HUB_CACHE_DIR="${HF_HUB_CACHE_DIR:-/scratch/rr81/ma5430/.cache/huggingface/hub}"
|
| 10 |
+
export HF_HUB_CACHE="$HF_HUB_CACHE_DIR"
|
| 11 |
+
export HUGGINGFACE_HUB_CACHE="$HF_HUB_CACHE_DIR"
|
| 12 |
+
export HF_HOME="$(dirname "$HF_HUB_CACHE_DIR")"
|
| 13 |
+
|
| 14 |
+
# GPU nodes have no internet, while login nodes do.
|
| 15 |
+
# Auto default: offline on GPU nodes, online on login nodes.
|
| 16 |
+
DEFAULT_OFFLINE_MODE="1"
|
| 17 |
+
if ! (command -v nvidia-smi >/dev/null 2>&1 && nvidia-smi -L >/dev/null 2>&1); then
|
| 18 |
+
DEFAULT_OFFLINE_MODE="0"
|
| 19 |
+
fi
|
| 20 |
+
OFFLINE_MODE="${OFFLINE_MODE:-$DEFAULT_OFFLINE_MODE}"
|
| 21 |
+
|
| 22 |
+
if [[ "$OFFLINE_MODE" == "1" ]]; then
|
| 23 |
+
export HF_DATASETS_OFFLINE="1"
|
| 24 |
+
export HF_METRICS_OFFLINE="1"
|
| 25 |
+
export HF_MODULES_OFFLINE="1"
|
| 26 |
+
export TRANSFORMERS_OFFLINE="1"
|
| 27 |
+
export DIFFUSERS_OFFLINE="1"
|
| 28 |
+
export HF_HUB_OFFLINE="1"
|
| 29 |
+
else
|
| 30 |
+
export HF_DATASETS_OFFLINE="0"
|
| 31 |
+
export HF_METRICS_OFFLINE="0"
|
| 32 |
+
export HF_MODULES_OFFLINE="0"
|
| 33 |
+
export TRANSFORMERS_OFFLINE="0"
|
| 34 |
+
export DIFFUSERS_OFFLINE="0"
|
| 35 |
+
export HF_HUB_OFFLINE="0"
|
| 36 |
+
fi
|
| 37 |
+
|
| 38 |
+
# Existing environment requested by user.
|
| 39 |
+
PYTHON_BIN="${PYTHON_BIN:-/g/data/rr81/aev/bin/python}"
|
| 40 |
+
if [[ ! -x "$PYTHON_BIN" ]]; then
|
| 41 |
+
echo "[examples.sh] Missing Python executable: $PYTHON_BIN" >&2
|
| 42 |
+
exit 1
|
| 43 |
+
fi
|
| 44 |
+
|
| 45 |
+
DATASET_NAME="${DATASET_NAME:-pickapic}" # coco | pickapic
|
| 46 |
+
GRAD_CONFIG="${GRAD_CONFIG:-one_step_rectification_config}"
|
| 47 |
+
MODEL_VARIANT="${MODEL_VARIANT:-spo}" # origin | spo | diffusion_dpo | lpo
|
| 48 |
+
MODE="${MODE:-gradient_ascent}" # gradient_ascent | baseline | both
|
| 49 |
+
# Empty MAX_SAMPLES means evaluate all available samples.
|
| 50 |
+
MAX_SAMPLES="${MAX_SAMPLES:-}"
|
| 51 |
+
NUM_STEPS="${NUM_STEPS:-20}"
|
| 52 |
+
CFG_SCALE="${CFG_SCALE:-3}"
|
| 53 |
+
METRICS="${METRICS:-clip aesthetic pickscore hpsv2 hpsv21 imagereward}"
|
| 54 |
+
PREFETCH_ONLY="${PREFETCH_ONLY:-0}"
|
| 55 |
+
|
| 56 |
+
# Override this path whenever you want to swap reward weights.
|
| 57 |
+
LRM_MODEL_PATH="${LRM_MODEL_PATH:-/g/data/rr81/LPO/lrm/lrm_15/LRM}"
|
| 58 |
+
|
| 59 |
+
if [[ -z "${GPU_ID:-}" ]]; then
|
| 60 |
+
if command -v nvidia-smi >/dev/null 2>&1 && nvidia-smi -L >/dev/null 2>&1; then
|
| 61 |
+
GPU_ID="$(nvidia-smi --query-gpu=index,memory.used --format=csv,noheader,nounits | sort -k2 -n | head -n1 | cut -d',' -f1 | tr -d ' ')"
|
| 62 |
+
GPU_ID="${GPU_ID:-0}"
|
| 63 |
+
else
|
| 64 |
+
GPU_ID="0"
|
| 65 |
+
echo "[examples.sh] No visible NVIDIA GPU on this node. Defaulting GPU_ID=0."
|
| 66 |
+
echo "[examples.sh] eval.py will run on CPU if CUDA is unavailable."
|
| 67 |
+
fi
|
| 68 |
+
fi
|
| 69 |
+
|
| 70 |
+
echo "Using GPU ID: $GPU_ID"
|
| 71 |
+
echo "Using LRM weights: $LRM_MODEL_PATH"
|
| 72 |
+
echo "HF offline mode: $OFFLINE_MODE"
|
| 73 |
+
|
| 74 |
+
if [[ "$PREFETCH_ONLY" == "1" ]]; then
|
| 75 |
+
echo "[examples.sh] PREFETCH_ONLY=1 -> downloading required model files to shared cache and exiting."
|
| 76 |
+
export MODEL_VARIANT
|
| 77 |
+
export METRICS
|
| 78 |
+
"$PYTHON_BIN" - <<'PY'
|
| 79 |
+
import os
|
| 80 |
+
from huggingface_hub import hf_hub_download, snapshot_download
|
| 81 |
+
|
| 82 |
+
cache_dir = os.environ["HF_HUB_CACHE"]
|
| 83 |
+
model_variant = os.environ.get("MODEL_VARIANT", "origin")
|
| 84 |
+
metrics = set(os.environ.get("METRICS", "clip aesthetic").split())
|
| 85 |
+
|
| 86 |
+
def snap(repo_id):
|
| 87 |
+
print(f"[prefetch] snapshot_download: {repo_id}")
|
| 88 |
+
snapshot_download(repo_id=repo_id, cache_dir=cache_dir, local_files_only=False)
|
| 89 |
+
|
| 90 |
+
def one(repo_id, filename):
|
| 91 |
+
print(f"[prefetch] hf_hub_download: {repo_id}/{filename}")
|
| 92 |
+
hf_hub_download(repo_id=repo_id, filename=filename, cache_dir=cache_dir, local_files_only=False)
|
| 93 |
+
|
| 94 |
+
# Base model for generation + reward backbone
|
| 95 |
+
snap("stable-diffusion-v1-5/stable-diffusion-v1-5")
|
| 96 |
+
|
| 97 |
+
# Variant-specific generation checkpoints (if used)
|
| 98 |
+
if model_variant == "spo":
|
| 99 |
+
snap("SPO-Diffusion-Models/SPO-SD-v1-5_4k-p_10ep")
|
| 100 |
+
elif model_variant == "diffusion_dpo":
|
| 101 |
+
snap("mhdang/dpo-sd1.5-text2image-v1")
|
| 102 |
+
elif model_variant == "lpo":
|
| 103 |
+
snap("casiatao/LPO")
|
| 104 |
+
|
| 105 |
+
# Required for CLIP-based metrics and LRM text projection init fallback
|
| 106 |
+
if "clip" in metrics or "aesthetic" in metrics:
|
| 107 |
+
snap("openai/clip-vit-large-patch14")
|
| 108 |
+
|
| 109 |
+
if "pickscore" in metrics:
|
| 110 |
+
snap("laion/CLIP-ViT-H-14-laion2B-s32B-b79K")
|
| 111 |
+
snap("yuvalkirstain/PickScore_v1")
|
| 112 |
+
|
| 113 |
+
if "hpsv2" in metrics or "hpsv21" in metrics:
|
| 114 |
+
one("laion/CLIP-ViT-H-14-laion2B-s32B-b79K", "open_clip_pytorch_model.bin")
|
| 115 |
+
if "hpsv2" in metrics:
|
| 116 |
+
one("xswu/HPSv2", "HPS_v2_compressed.pt")
|
| 117 |
+
if "hpsv21" in metrics:
|
| 118 |
+
one("xswu/HPSv2", "HPS_v2.1_compressed.pt")
|
| 119 |
+
|
| 120 |
+
if "imagereward" in metrics:
|
| 121 |
+
one("THUDM/ImageReward", "ImageReward.pt")
|
| 122 |
+
one("THUDM/ImageReward", "med_config.json")
|
| 123 |
+
|
| 124 |
+
print("[prefetch] done")
|
| 125 |
+
PY
|
| 126 |
+
exit 0
|
| 127 |
+
fi
|
| 128 |
+
|
| 129 |
+
read -r -a METRICS_ARR <<< "$METRICS"
|
| 130 |
+
|
| 131 |
+
CMD=(
|
| 132 |
+
"$PYTHON_BIN" eval.py
|
| 133 |
+
--model_variant "$MODEL_VARIANT"
|
| 134 |
+
--dataset_type "$DATASET_NAME"
|
| 135 |
+
--lrm_model "$LRM_MODEL_PATH"
|
| 136 |
+
--grad_config "$GRAD_CONFIG"
|
| 137 |
+
--metrics "${METRICS_ARR[@]}"
|
| 138 |
+
--num_steps "$NUM_STEPS"
|
| 139 |
+
--cfg_scale "$CFG_SCALE"
|
| 140 |
+
--hf_cache_dir "$HF_HUB_CACHE_DIR"
|
| 141 |
+
--output_dir "RESULTS/$DATASET_NAME/${GRAD_CONFIG}_${MODEL_VARIANT}"
|
| 142 |
+
--cuda "$GPU_ID"
|
| 143 |
+
--mode "$MODE"
|
| 144 |
+
)
|
| 145 |
+
|
| 146 |
+
if [[ -n "$MAX_SAMPLES" ]]; then
|
| 147 |
+
CMD+=(--max_samples "$MAX_SAMPLES")
|
| 148 |
+
fi
|
| 149 |
+
|
| 150 |
+
if [[ "$OFFLINE_MODE" == "1" ]]; then
|
| 151 |
+
CMD+=(--offline)
|
| 152 |
+
fi
|
| 153 |
+
|
| 154 |
+
"${CMD[@]}"
|
Reward_sd15_idealized/lr_scheduler.py
ADDED
|
@@ -0,0 +1,233 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
Learning rate schedulers for gradient ascent optimization.
|
| 3 |
+
|
| 4 |
+
Provides various LR scheduling strategies for reward-guided gradient ascent,
|
| 5 |
+
including cosine annealing, linear decay, and custom schedules.
|
| 6 |
+
"""
|
| 7 |
+
|
| 8 |
+
import math
|
| 9 |
+
from typing import Optional, Literal
|
| 10 |
+
|
| 11 |
+
|
| 12 |
+
class LRScheduler:
|
| 13 |
+
"""Base class for learning rate schedulers."""
|
| 14 |
+
|
| 15 |
+
def __init__(self, initial_lr: float, num_steps: int):
|
| 16 |
+
"""
|
| 17 |
+
Initialize LR scheduler.
|
| 18 |
+
|
| 19 |
+
Args:
|
| 20 |
+
initial_lr: Initial learning rate
|
| 21 |
+
num_steps: Total number of optimization steps
|
| 22 |
+
"""
|
| 23 |
+
self.initial_lr = initial_lr
|
| 24 |
+
self.num_steps = num_steps
|
| 25 |
+
self.current_step = 0
|
| 26 |
+
|
| 27 |
+
def get_lr(self) -> float:
|
| 28 |
+
"""Get current learning rate."""
|
| 29 |
+
raise NotImplementedError
|
| 30 |
+
|
| 31 |
+
def step(self):
|
| 32 |
+
"""Update scheduler state after a step."""
|
| 33 |
+
self.current_step += 1
|
| 34 |
+
|
| 35 |
+
def reset(self):
|
| 36 |
+
"""Reset scheduler state."""
|
| 37 |
+
self.current_step = 0
|
| 38 |
+
|
| 39 |
+
|
| 40 |
+
class ConstantLR(LRScheduler):
|
| 41 |
+
"""Constant learning rate (no scheduling)."""
|
| 42 |
+
|
| 43 |
+
def get_lr(self) -> float:
|
| 44 |
+
return self.initial_lr
|
| 45 |
+
|
| 46 |
+
|
| 47 |
+
class LinearLR(LRScheduler):
|
| 48 |
+
"""Linear learning rate decay."""
|
| 49 |
+
|
| 50 |
+
def __init__(
|
| 51 |
+
self,
|
| 52 |
+
initial_lr: float,
|
| 53 |
+
num_steps: int,
|
| 54 |
+
end_lr: float = 0.0,
|
| 55 |
+
start_step: int = 0,
|
| 56 |
+
):
|
| 57 |
+
"""
|
| 58 |
+
Initialize linear LR scheduler.
|
| 59 |
+
|
| 60 |
+
Args:
|
| 61 |
+
initial_lr: Starting learning rate
|
| 62 |
+
num_steps: Total number of steps
|
| 63 |
+
end_lr: Ending learning rate (default: 0.0)
|
| 64 |
+
start_step: Step to begin decay (default: 0)
|
| 65 |
+
"""
|
| 66 |
+
super().__init__(initial_lr, num_steps)
|
| 67 |
+
self.end_lr = end_lr
|
| 68 |
+
self.start_step = start_step
|
| 69 |
+
|
| 70 |
+
def get_lr(self) -> float:
|
| 71 |
+
if self.current_step < self.start_step:
|
| 72 |
+
return self.initial_lr
|
| 73 |
+
|
| 74 |
+
progress = (self.current_step - self.start_step) / (self.num_steps - self.start_step)
|
| 75 |
+
progress = min(1.0, progress)
|
| 76 |
+
|
| 77 |
+
return self.initial_lr + (self.end_lr - self.initial_lr) * progress
|
| 78 |
+
|
| 79 |
+
|
| 80 |
+
class CosineLR(LRScheduler):
|
| 81 |
+
"""Cosine annealing learning rate schedule."""
|
| 82 |
+
|
| 83 |
+
def __init__(
|
| 84 |
+
self,
|
| 85 |
+
initial_lr: float,
|
| 86 |
+
num_steps: int,
|
| 87 |
+
min_lr: float = 0.0,
|
| 88 |
+
warmup_steps: int = 0,
|
| 89 |
+
):
|
| 90 |
+
"""
|
| 91 |
+
Initialize cosine LR scheduler.
|
| 92 |
+
|
| 93 |
+
Args:
|
| 94 |
+
initial_lr: Maximum learning rate
|
| 95 |
+
num_steps: Total number of steps
|
| 96 |
+
min_lr: Minimum learning rate (default: 0.0)
|
| 97 |
+
warmup_steps: Number of linear warmup steps (default: 0)
|
| 98 |
+
"""
|
| 99 |
+
super().__init__(initial_lr, num_steps)
|
| 100 |
+
self.min_lr = min_lr
|
| 101 |
+
self.warmup_steps = warmup_steps
|
| 102 |
+
|
| 103 |
+
def get_lr(self) -> float:
|
| 104 |
+
if self.current_step < self.warmup_steps:
|
| 105 |
+
# Linear warmup
|
| 106 |
+
return self.initial_lr * (self.current_step / self.warmup_steps)
|
| 107 |
+
|
| 108 |
+
# Cosine annealing
|
| 109 |
+
progress = (self.current_step - self.warmup_steps) / (self.num_steps - self.warmup_steps)
|
| 110 |
+
progress = min(1.0, progress)
|
| 111 |
+
|
| 112 |
+
cosine_decay = 0.5 * (1 + math.cos(math.pi * progress))
|
| 113 |
+
return self.min_lr + (self.initial_lr - self.min_lr) * cosine_decay
|
| 114 |
+
|
| 115 |
+
|
| 116 |
+
class ExponentialLR(LRScheduler):
|
| 117 |
+
"""Exponential learning rate decay."""
|
| 118 |
+
|
| 119 |
+
def __init__(
|
| 120 |
+
self,
|
| 121 |
+
initial_lr: float,
|
| 122 |
+
num_steps: int,
|
| 123 |
+
gamma: float = 0.95,
|
| 124 |
+
):
|
| 125 |
+
"""
|
| 126 |
+
Initialize exponential LR scheduler.
|
| 127 |
+
|
| 128 |
+
Args:
|
| 129 |
+
initial_lr: Starting learning rate
|
| 130 |
+
num_steps: Total number of steps
|
| 131 |
+
gamma: Multiplicative decay factor per step
|
| 132 |
+
"""
|
| 133 |
+
super().__init__(initial_lr, num_steps)
|
| 134 |
+
self.gamma = gamma
|
| 135 |
+
|
| 136 |
+
def get_lr(self) -> float:
|
| 137 |
+
return self.initial_lr * (self.gamma ** self.current_step)
|
| 138 |
+
|
| 139 |
+
|
| 140 |
+
class StepLR(LRScheduler):
|
| 141 |
+
"""Step-wise learning rate decay."""
|
| 142 |
+
|
| 143 |
+
def __init__(
|
| 144 |
+
self,
|
| 145 |
+
initial_lr: float,
|
| 146 |
+
num_steps: int,
|
| 147 |
+
step_size: int,
|
| 148 |
+
gamma: float = 0.1,
|
| 149 |
+
):
|
| 150 |
+
"""
|
| 151 |
+
Initialize step LR scheduler.
|
| 152 |
+
|
| 153 |
+
Args:
|
| 154 |
+
initial_lr: Starting learning rate
|
| 155 |
+
num_steps: Total number of steps
|
| 156 |
+
step_size: Number of steps between each decay
|
| 157 |
+
gamma: Multiplicative decay factor
|
| 158 |
+
"""
|
| 159 |
+
super().__init__(initial_lr, num_steps)
|
| 160 |
+
self.step_size = step_size
|
| 161 |
+
self.gamma = gamma
|
| 162 |
+
|
| 163 |
+
def get_lr(self) -> float:
|
| 164 |
+
num_decays = self.current_step // self.step_size
|
| 165 |
+
return self.initial_lr * (self.gamma ** num_decays)
|
| 166 |
+
|
| 167 |
+
|
| 168 |
+
def create_lr_scheduler(
|
| 169 |
+
scheduler_type: Literal["constant", "linear", "cosine", "exponential", "step"],
|
| 170 |
+
initial_lr: float,
|
| 171 |
+
num_steps: int,
|
| 172 |
+
**kwargs
|
| 173 |
+
) -> LRScheduler:
|
| 174 |
+
"""
|
| 175 |
+
Factory function to create learning rate schedulers.
|
| 176 |
+
|
| 177 |
+
Args:
|
| 178 |
+
scheduler_type: Type of scheduler ("constant", "linear", "cosine", "exponential", "step")
|
| 179 |
+
initial_lr: Initial learning rate
|
| 180 |
+
num_steps: Total number of optimization steps
|
| 181 |
+
**kwargs: Additional scheduler-specific arguments
|
| 182 |
+
For linear: end_lr, start_step
|
| 183 |
+
For cosine: min_lr, warmup_steps
|
| 184 |
+
For exponential: gamma
|
| 185 |
+
For step: step_size, gamma
|
| 186 |
+
|
| 187 |
+
Returns:
|
| 188 |
+
LRScheduler instance
|
| 189 |
+
|
| 190 |
+
Examples:
|
| 191 |
+
# Constant LR
|
| 192 |
+
scheduler = create_lr_scheduler("constant", initial_lr=0.1, num_steps=100)
|
| 193 |
+
|
| 194 |
+
# Linear decay
|
| 195 |
+
scheduler = create_lr_scheduler("linear", initial_lr=0.1, num_steps=100, end_lr=0.01)
|
| 196 |
+
|
| 197 |
+
# Cosine annealing with warmup
|
| 198 |
+
scheduler = create_lr_scheduler("cosine", initial_lr=0.1, num_steps=100,
|
| 199 |
+
min_lr=0.001, warmup_steps=10)
|
| 200 |
+
"""
|
| 201 |
+
if scheduler_type == "constant":
|
| 202 |
+
return ConstantLR(initial_lr, num_steps)
|
| 203 |
+
|
| 204 |
+
elif scheduler_type == "linear":
|
| 205 |
+
return LinearLR(
|
| 206 |
+
initial_lr, num_steps,
|
| 207 |
+
end_lr=kwargs.get("end_lr", 0.0),
|
| 208 |
+
start_step=kwargs.get("start_step", 0),
|
| 209 |
+
)
|
| 210 |
+
|
| 211 |
+
elif scheduler_type == "cosine":
|
| 212 |
+
return CosineLR(
|
| 213 |
+
initial_lr, num_steps,
|
| 214 |
+
min_lr=kwargs.get("min_lr", 0.0),
|
| 215 |
+
warmup_steps=kwargs.get("warmup_steps", 0),
|
| 216 |
+
)
|
| 217 |
+
|
| 218 |
+
elif scheduler_type == "exponential":
|
| 219 |
+
return ExponentialLR(
|
| 220 |
+
initial_lr, num_steps,
|
| 221 |
+
gamma=kwargs.get("gamma", 0.95),
|
| 222 |
+
)
|
| 223 |
+
|
| 224 |
+
elif scheduler_type == "step":
|
| 225 |
+
return StepLR(
|
| 226 |
+
initial_lr, num_steps,
|
| 227 |
+
step_size=kwargs.get("step_size", 10),
|
| 228 |
+
gamma=kwargs.get("gamma", 0.1),
|
| 229 |
+
)
|
| 230 |
+
|
| 231 |
+
else:
|
| 232 |
+
raise ValueError(f"Unknown scheduler type: {scheduler_type}. "
|
| 233 |
+
f"Choose from: constant, linear, cosine, exponential, step")
|
Reward_sd15_idealized/models/__init__.py
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from .reward_model import LRMRewardModel
|
| 2 |
+
|
| 3 |
+
__all__ = ['LRMRewardModel', 'LRMRewardModelSDXL']
|
Reward_sd15_idealized/models/__pycache__/__init__.cpython-310.pyc
ADDED
|
Binary file (279 Bytes). View file
|
|
|
Reward_sd15_idealized/models/__pycache__/__init__.cpython-311.pyc
ADDED
|
Binary file (282 Bytes). View file
|
|
|
Reward_sd15_idealized/models/__pycache__/__init__.cpython-313.pyc
ADDED
|
Binary file (295 Bytes). View file
|
|
|
Reward_sd15_idealized/models/__pycache__/__init__.cpython-39.pyc
ADDED
|
Binary file (240 Bytes). View file
|
|
|
Reward_sd15_idealized/models/__pycache__/reward_model.cpython-310.pyc
ADDED
|
Binary file (9.51 kB). View file
|
|
|
Reward_sd15_idealized/models/__pycache__/reward_model.cpython-311.pyc
ADDED
|
Binary file (19.1 kB). View file
|
|
|
Reward_sd15_idealized/models/__pycache__/reward_model.cpython-313.pyc
ADDED
|
Binary file (16.4 kB). View file
|
|
|
Reward_sd15_idealized/models/__pycache__/reward_model.cpython-39.pyc
ADDED
|
Binary file (9.1 kB). View file
|
|
|
Reward_sd15_idealized/models/__pycache__/reward_model_sdxl.cpython-310.pyc
ADDED
|
Binary file (9.96 kB). View file
|
|
|
Reward_sd15_idealized/models/__pycache__/unet_2d_condition_reward.cpython-310.pyc
ADDED
|
Binary file (40.9 kB). View file
|
|
|
Reward_sd15_idealized/models/__pycache__/unet_2d_condition_reward.cpython-311.pyc
ADDED
|
Binary file (62.8 kB). View file
|
|
|
Reward_sd15_idealized/models/__pycache__/unet_2d_condition_reward.cpython-313.pyc
ADDED
|
Binary file (57.4 kB). View file
|
|
|
Reward_sd15_idealized/models/reward_model.py
ADDED
|
@@ -0,0 +1,388 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
LRM Reward Model Wrapper
|
| 3 |
+
Loads LRM weights from HuggingFace and provides interface for computing preference scores on noisy latents.
|
| 4 |
+
"""
|
| 5 |
+
|
| 6 |
+
import torch
|
| 7 |
+
from torch import nn
|
| 8 |
+
from diffusers import AutoencoderKL, DDPMScheduler
|
| 9 |
+
from transformers import CLIPTextModel, CLIPTokenizer
|
| 10 |
+
from huggingface_hub import hf_hub_download
|
| 11 |
+
import os
|
| 12 |
+
from .unet_2d_condition_reward import UNet2DConditionModel
|
| 13 |
+
|
| 14 |
+
|
| 15 |
+
def _offline_mode_enabled() -> bool:
|
| 16 |
+
return os.getenv("HF_HUB_OFFLINE", "0").strip().lower() in {"1", "true", "yes", "on"}
|
| 17 |
+
|
| 18 |
+
|
| 19 |
+
def _get_cache_dir() -> str | None:
|
| 20 |
+
return os.getenv("HF_HUB_CACHE") or os.getenv("HUGGINGFACE_HUB_CACHE")
|
| 21 |
+
|
| 22 |
+
|
| 23 |
+
def _hf_pretrained_kwargs() -> dict:
|
| 24 |
+
kwargs = {"local_files_only": _offline_mode_enabled()}
|
| 25 |
+
cache_dir = _get_cache_dir()
|
| 26 |
+
if cache_dir:
|
| 27 |
+
kwargs["cache_dir"] = cache_dir
|
| 28 |
+
return kwargs
|
| 29 |
+
|
| 30 |
+
|
| 31 |
+
class LRMRewardModel(nn.Module):
|
| 32 |
+
"""
|
| 33 |
+
Latent Reward Model (LRM) for SD1.5
|
| 34 |
+
|
| 35 |
+
This model computes preference scores directly on noisy latent images at any timestep.
|
| 36 |
+
It uses features from the U-Net and text encoder to predict how well an image aligns
|
| 37 |
+
with the prompt at different noise levels.
|
| 38 |
+
|
| 39 |
+
Args:
|
| 40 |
+
pretrained_model_name_or_path: Base SD model path (e.g., 'runwayml/stable-diffusion-v1-5')
|
| 41 |
+
lrm_model_path: Path to LRM checkpoint from HuggingFace (e.g., 'casiatao/LRM')
|
| 42 |
+
clip_model_path: Path to CLIP checkpoint for text projection initialization
|
| 43 |
+
guidance_scale: Classifier-free guidance scale (default: 7.5)
|
| 44 |
+
device: Device to load model on
|
| 45 |
+
"""
|
| 46 |
+
|
| 47 |
+
def __init__(
|
| 48 |
+
self,
|
| 49 |
+
pretrained_model_name_or_path='runwayml/stable-diffusion-v1-5',
|
| 50 |
+
lrm_model_path=None,
|
| 51 |
+
clip_model_path='openai/clip-vit-large-patch14',
|
| 52 |
+
guidance_scale=7.5,
|
| 53 |
+
device='cuda'
|
| 54 |
+
):
|
| 55 |
+
super().__init__()
|
| 56 |
+
|
| 57 |
+
self.device = device
|
| 58 |
+
self.guidance_scale = guidance_scale
|
| 59 |
+
self.multi_scale = True
|
| 60 |
+
self.multi_scale_cfg = False
|
| 61 |
+
|
| 62 |
+
print(f"Loading base models from {pretrained_model_name_or_path}...")
|
| 63 |
+
pretrained_kwargs = _hf_pretrained_kwargs()
|
| 64 |
+
|
| 65 |
+
# Load tokenizer and text encoder
|
| 66 |
+
self.tokenizer = CLIPTokenizer.from_pretrained(
|
| 67 |
+
pretrained_model_name_or_path,
|
| 68 |
+
subfolder="tokenizer",
|
| 69 |
+
**pretrained_kwargs,
|
| 70 |
+
)
|
| 71 |
+
self.text_encoder = CLIPTextModel.from_pretrained(
|
| 72 |
+
pretrained_model_name_or_path,
|
| 73 |
+
subfolder="text_encoder",
|
| 74 |
+
**pretrained_kwargs,
|
| 75 |
+
).to(device)
|
| 76 |
+
|
| 77 |
+
# Load VAE (frozen, only needed for preprocessing if using images)
|
| 78 |
+
self.vae = AutoencoderKL.from_pretrained(
|
| 79 |
+
pretrained_model_name_or_path,
|
| 80 |
+
subfolder="vae",
|
| 81 |
+
**pretrained_kwargs,
|
| 82 |
+
).to(device)
|
| 83 |
+
self.vae.requires_grad_(False)
|
| 84 |
+
|
| 85 |
+
# Load scheduler
|
| 86 |
+
self.scheduler = DDPMScheduler.from_pretrained(
|
| 87 |
+
pretrained_model_name_or_path,
|
| 88 |
+
subfolder="scheduler",
|
| 89 |
+
**pretrained_kwargs,
|
| 90 |
+
)
|
| 91 |
+
|
| 92 |
+
# Load U-Net with custom reward architecture
|
| 93 |
+
print("Loading custom U-Net for reward prediction...")
|
| 94 |
+
self.unet = UNet2DConditionModel.from_pretrained(
|
| 95 |
+
pretrained_model_name_or_path,
|
| 96 |
+
subfolder="unet",
|
| 97 |
+
**pretrained_kwargs,
|
| 98 |
+
).to(device)
|
| 99 |
+
|
| 100 |
+
# Global pooling layer
|
| 101 |
+
self.avg_pool = nn.AdaptiveAvgPool2d((1, 1))
|
| 102 |
+
|
| 103 |
+
# Projection layers
|
| 104 |
+
# Multi-scale: concatenates features from 4 down blocks + mid block = 4800 dims
|
| 105 |
+
vision_embed_dim = 4800 if self.multi_scale else 1280
|
| 106 |
+
text_embed_dim = 768
|
| 107 |
+
projection_dim = 768
|
| 108 |
+
|
| 109 |
+
self.visual_projection = nn.Linear(vision_embed_dim, projection_dim, bias=False).to(device)
|
| 110 |
+
self.text_projection = nn.Linear(text_embed_dim, projection_dim, bias=False).to(device)
|
| 111 |
+
|
| 112 |
+
# Initialize text projection from CLIP
|
| 113 |
+
print(f"Loading CLIP text projection from {clip_model_path}...")
|
| 114 |
+
try:
|
| 115 |
+
# Try loading from local path first
|
| 116 |
+
if os.path.exists(clip_model_path):
|
| 117 |
+
clip_ckpt = torch.load(clip_model_path, map_location='cpu')
|
| 118 |
+
else:
|
| 119 |
+
# Download from HuggingFace
|
| 120 |
+
clip_ckpt_path = hf_hub_download(
|
| 121 |
+
repo_id=clip_model_path,
|
| 122 |
+
filename="pytorch_model.bin",
|
| 123 |
+
**_hf_pretrained_kwargs(),
|
| 124 |
+
)
|
| 125 |
+
clip_ckpt = torch.load(clip_ckpt_path, map_location='cpu')
|
| 126 |
+
|
| 127 |
+
self.text_projection.weight.data = clip_ckpt['text_projection.weight'].contiguous().to(device)
|
| 128 |
+
print("✓ Loaded CLIP text projection weights")
|
| 129 |
+
except Exception as e:
|
| 130 |
+
print(f"Warning: Could not load CLIP weights: {e}")
|
| 131 |
+
print("Initializing text projection randomly")
|
| 132 |
+
nn.init.normal_(self.text_projection.weight, std=0.02)
|
| 133 |
+
|
| 134 |
+
# Initialize visual projection
|
| 135 |
+
nn.init.normal_(self.visual_projection.weight, std=0.02)
|
| 136 |
+
|
| 137 |
+
# Logit scale (temperature parameter)
|
| 138 |
+
self.logit_scale = nn.Parameter(torch.ones([]) * 2.6592).to(device)
|
| 139 |
+
|
| 140 |
+
# Setup classifier-free guidance
|
| 141 |
+
self.do_classifier_free_guidance = self.guidance_scale > 1.0
|
| 142 |
+
if self.do_classifier_free_guidance:
|
| 143 |
+
self.neg_prompt_ids = self.tokenizer(
|
| 144 |
+
[""],
|
| 145 |
+
return_tensors="pt",
|
| 146 |
+
padding="max_length",
|
| 147 |
+
truncation=True,
|
| 148 |
+
max_length=self.tokenizer.model_max_length,
|
| 149 |
+
).input_ids.to(device)
|
| 150 |
+
|
| 151 |
+
# Load fine-tuned LRM weights if provided
|
| 152 |
+
if lrm_model_path:
|
| 153 |
+
self.load_lrm_weights(lrm_model_path)
|
| 154 |
+
|
| 155 |
+
print("✓ LRM Reward Model initialized successfully!")
|
| 156 |
+
|
| 157 |
+
def load_lrm_weights(self, model_path):
|
| 158 |
+
"""
|
| 159 |
+
Load fine-tuned LRM weights from HuggingFace or local path
|
| 160 |
+
|
| 161 |
+
Expected structure:
|
| 162 |
+
- unet/ (directory with U-Net weights)
|
| 163 |
+
- text_encoder/ (optional, directory with text encoder weights)
|
| 164 |
+
- state_dict.pt (visual_projection, text_projection, logit_scale)
|
| 165 |
+
"""
|
| 166 |
+
print(f"\nLoading LRM weights from {model_path}...")
|
| 167 |
+
|
| 168 |
+
try:
|
| 169 |
+
# Check if it's a HuggingFace model or local path
|
| 170 |
+
if not os.path.exists(model_path):
|
| 171 |
+
# Try to download from HuggingFace
|
| 172 |
+
print(f"Downloading from HuggingFace: {model_path}")
|
| 173 |
+
# For HF models, we need to download the entire repo
|
| 174 |
+
from huggingface_hub import snapshot_download
|
| 175 |
+
model_path = snapshot_download(repo_id=model_path, **_hf_pretrained_kwargs())
|
| 176 |
+
|
| 177 |
+
# Load U-Net weights
|
| 178 |
+
unet_path = os.path.join(model_path, "lrm_sd15", "unet")
|
| 179 |
+
if os.path.exists(unet_path):
|
| 180 |
+
self.unet = UNet2DConditionModel.from_pretrained(unet_path, **_hf_pretrained_kwargs()).to(self.device)
|
| 181 |
+
print(f"✓ Loaded U-Net weights from {unet_path}")
|
| 182 |
+
else:
|
| 183 |
+
print(f"Warning: U-Net path not found: {unet_path}")
|
| 184 |
+
|
| 185 |
+
# Load text encoder weights (optional)
|
| 186 |
+
text_encoder_path = os.path.join(model_path, "lrm_sd15", "text_encoder")
|
| 187 |
+
if os.path.exists(text_encoder_path):
|
| 188 |
+
self.text_encoder = CLIPTextModel.from_pretrained(text_encoder_path, **_hf_pretrained_kwargs()).to(self.device)
|
| 189 |
+
print(f"✓ Loaded text encoder weights from {text_encoder_path}")
|
| 190 |
+
|
| 191 |
+
# Load projection layers and logit scale
|
| 192 |
+
state_dict_path = os.path.join(model_path, "lrm_sd15", "state_dict.pt")
|
| 193 |
+
if os.path.exists(state_dict_path):
|
| 194 |
+
state_dict = torch.load(state_dict_path, map_location='cpu')
|
| 195 |
+
|
| 196 |
+
self.visual_projection.load_state_dict(state_dict['visual_projection'])
|
| 197 |
+
self.text_projection.load_state_dict(state_dict['text_projection'])
|
| 198 |
+
|
| 199 |
+
# Move projection layers to device
|
| 200 |
+
self.visual_projection = self.visual_projection.to(self.device)
|
| 201 |
+
self.text_projection = self.text_projection.to(self.device)
|
| 202 |
+
|
| 203 |
+
logit_scale_val = state_dict['logit_scale']
|
| 204 |
+
if isinstance(logit_scale_val, torch.Tensor):
|
| 205 |
+
self.logit_scale.data = logit_scale_val.to(self.device)
|
| 206 |
+
else:
|
| 207 |
+
self.logit_scale.data = torch.tensor(logit_scale_val).to(self.device)
|
| 208 |
+
|
| 209 |
+
print(f"✓ Loaded projection layers and logit_scale from {state_dict_path}")
|
| 210 |
+
else:
|
| 211 |
+
print(f"Warning: state_dict.pt not found: {state_dict_path}")
|
| 212 |
+
|
| 213 |
+
print("✓ Successfully loaded all LRM weights!")
|
| 214 |
+
|
| 215 |
+
except Exception as e:
|
| 216 |
+
print(f"Error loading LRM weights: {e}")
|
| 217 |
+
print("Continuing with base model weights...")
|
| 218 |
+
|
| 219 |
+
def encode_prompt(self, prompt):
|
| 220 |
+
"""Tokenize text prompt"""
|
| 221 |
+
if isinstance(prompt, str):
|
| 222 |
+
prompt = [prompt]
|
| 223 |
+
|
| 224 |
+
text_inputs = self.tokenizer(
|
| 225 |
+
prompt,
|
| 226 |
+
padding="max_length",
|
| 227 |
+
max_length=self.tokenizer.model_max_length,
|
| 228 |
+
truncation=True,
|
| 229 |
+
return_tensors="pt",
|
| 230 |
+
)
|
| 231 |
+
return text_inputs.input_ids.to(self.device)
|
| 232 |
+
|
| 233 |
+
def get_text_features(self, text_input_ids):
|
| 234 |
+
"""
|
| 235 |
+
Extract text features from prompt
|
| 236 |
+
Returns: (encoder_hidden_states, text_features)
|
| 237 |
+
"""
|
| 238 |
+
if self.do_classifier_free_guidance:
|
| 239 |
+
# Concatenate conditional and unconditional prompts
|
| 240 |
+
text_input_ids = torch.cat([
|
| 241 |
+
text_input_ids,
|
| 242 |
+
self.neg_prompt_ids.repeat(text_input_ids.shape[0], 1)
|
| 243 |
+
], dim=0)
|
| 244 |
+
|
| 245 |
+
outputs = self.text_encoder(text_input_ids, return_dict=False)
|
| 246 |
+
encoder_hidden_states = outputs[0] # Sequence of hidden states
|
| 247 |
+
pooled_output = outputs[1] # Pooled output (last token)
|
| 248 |
+
|
| 249 |
+
if self.do_classifier_free_guidance:
|
| 250 |
+
pooled_output_text, pooled_output_ucond = pooled_output.chunk(2, dim=0)
|
| 251 |
+
text_features = self.text_projection(pooled_output_text)
|
| 252 |
+
else:
|
| 253 |
+
text_features = self.text_projection(pooled_output)
|
| 254 |
+
|
| 255 |
+
return encoder_hidden_states, text_features
|
| 256 |
+
|
| 257 |
+
def get_image_features(self, encoder_hidden_states, noisy_latents, timesteps):
|
| 258 |
+
"""
|
| 259 |
+
Extract visual features from noisy latents using U-Net
|
| 260 |
+
|
| 261 |
+
Args:
|
| 262 |
+
encoder_hidden_states: Text conditioning from CLIP
|
| 263 |
+
noisy_latents: Noisy latent images [B, C, H, W]
|
| 264 |
+
timesteps: Denoising timesteps [B]
|
| 265 |
+
|
| 266 |
+
Returns:
|
| 267 |
+
image_features: Visual embeddings [B, projection_dim]
|
| 268 |
+
"""
|
| 269 |
+
if self.do_classifier_free_guidance:
|
| 270 |
+
noisy_latents = torch.cat([noisy_latents] * 2, dim=0)
|
| 271 |
+
timesteps = torch.cat([timesteps] * 2, dim=0)
|
| 272 |
+
|
| 273 |
+
# Forward through U-Net (only down blocks + mid block, no up blocks)
|
| 274 |
+
mid_output, down_block_res_samples = self.unet(
|
| 275 |
+
noisy_latents,
|
| 276 |
+
timesteps,
|
| 277 |
+
encoder_hidden_states=encoder_hidden_states,
|
| 278 |
+
return_dict=False,
|
| 279 |
+
use_up_blocks=False
|
| 280 |
+
)
|
| 281 |
+
|
| 282 |
+
if self.multi_scale:
|
| 283 |
+
# Extract multi-scale features from down blocks
|
| 284 |
+
# Indices correspond to: [320, 64, 64], [640, 32, 32], [1280, 16, 16], [1280, 8, 8]
|
| 285 |
+
first_stage_output = down_block_res_samples[2] # 320 channels
|
| 286 |
+
second_stage_output = down_block_res_samples[5] # 640 channels
|
| 287 |
+
third_stage_output = down_block_res_samples[8] # 1280 channels
|
| 288 |
+
fourth_stage_output = down_block_res_samples[11] # 1280 channels
|
| 289 |
+
|
| 290 |
+
# Global average pooling
|
| 291 |
+
pooled_first = self.avg_pool(first_stage_output).squeeze(dim=[2, 3])
|
| 292 |
+
pooled_second = self.avg_pool(second_stage_output).squeeze(dim=[2, 3])
|
| 293 |
+
pooled_third = self.avg_pool(third_stage_output).squeeze(dim=[2, 3])
|
| 294 |
+
pooled_fourth = self.avg_pool(fourth_stage_output).squeeze(dim=[2, 3])
|
| 295 |
+
pooled_mid = self.avg_pool(mid_output).squeeze(dim=[2, 3])
|
| 296 |
+
|
| 297 |
+
# Apply VFE (Visual Feature Enhancement) on mid block
|
| 298 |
+
if self.do_classifier_free_guidance:
|
| 299 |
+
pooled_mid_text, pooled_mid_ucond = pooled_mid.chunk(2, dim=0)
|
| 300 |
+
pooled_mid = pooled_mid_ucond + self.guidance_scale * (pooled_mid_text - pooled_mid_ucond)
|
| 301 |
+
|
| 302 |
+
# For other blocks, optionally apply CFG or just use conditional branch
|
| 303 |
+
if self.multi_scale_cfg:
|
| 304 |
+
pooled_first_text, pooled_first_ucond = pooled_first.chunk(2, dim=0)
|
| 305 |
+
pooled_first = pooled_first_ucond + self.guidance_scale * (pooled_first_text - pooled_first_ucond)
|
| 306 |
+
|
| 307 |
+
pooled_second_text, pooled_second_ucond = pooled_second.chunk(2, dim=0)
|
| 308 |
+
pooled_second = pooled_second_ucond + self.guidance_scale * (pooled_second_text - pooled_second_ucond)
|
| 309 |
+
|
| 310 |
+
pooled_third_text, pooled_third_ucond = pooled_third.chunk(2, dim=0)
|
| 311 |
+
pooled_third = pooled_third_ucond + self.guidance_scale * (pooled_third_text - pooled_third_ucond)
|
| 312 |
+
|
| 313 |
+
pooled_fourth_text, pooled_fourth_ucond = pooled_fourth.chunk(2, dim=0)
|
| 314 |
+
pooled_fourth = pooled_fourth_ucond + self.guidance_scale * (pooled_fourth_text - pooled_fourth_ucond)
|
| 315 |
+
else:
|
| 316 |
+
# Use only conditional (text-conditioned) branch
|
| 317 |
+
pooled_first, _ = pooled_first.chunk(2, dim=0)
|
| 318 |
+
pooled_second, _ = pooled_second.chunk(2, dim=0)
|
| 319 |
+
pooled_third, _ = pooled_third.chunk(2, dim=0)
|
| 320 |
+
pooled_fourth, _ = pooled_fourth.chunk(2, dim=0)
|
| 321 |
+
|
| 322 |
+
# Concatenate all scales: 320 + 640 + 1280 + 1280 + 1280 = 4800
|
| 323 |
+
concat_pooled = torch.cat([
|
| 324 |
+
pooled_first, pooled_second, pooled_third, pooled_fourth, pooled_mid
|
| 325 |
+
], dim=-1)
|
| 326 |
+
|
| 327 |
+
image_features = self.visual_projection(concat_pooled)
|
| 328 |
+
else:
|
| 329 |
+
# Single scale (mid block only)
|
| 330 |
+
pooled_mid = self.avg_pool(mid_output).squeeze(dim=[2, 3])
|
| 331 |
+
if self.do_classifier_free_guidance:
|
| 332 |
+
pooled_mid_text, pooled_mid_ucond = pooled_mid.chunk(2, dim=0)
|
| 333 |
+
pooled_mid = pooled_mid_ucond + self.guidance_scale * (pooled_mid_text - pooled_mid_ucond)
|
| 334 |
+
image_features = self.visual_projection(pooled_mid)
|
| 335 |
+
|
| 336 |
+
return image_features
|
| 337 |
+
|
| 338 |
+
def get_reward_score(self, noisy_latents, prompt, timesteps, enable_grad=False):
|
| 339 |
+
"""
|
| 340 |
+
Compute preference score for noisy latents at given timesteps
|
| 341 |
+
|
| 342 |
+
Args:
|
| 343 |
+
noisy_latents: Noisy latent images [B, C, H, W]
|
| 344 |
+
prompt: Text prompt(s) (string or list of strings)
|
| 345 |
+
timesteps: Denoising timesteps [B] or scalar
|
| 346 |
+
enable_grad: If True, allows gradient computation (for gradient ascent)
|
| 347 |
+
|
| 348 |
+
Returns:
|
| 349 |
+
scores: Preference scores [B]
|
| 350 |
+
"""
|
| 351 |
+
def _compute():
|
| 352 |
+
# Ensure inputs are on correct device
|
| 353 |
+
latents = noisy_latents.to(self.device, dtype=self.unet.dtype)
|
| 354 |
+
|
| 355 |
+
# Handle timesteps
|
| 356 |
+
if isinstance(timesteps, int):
|
| 357 |
+
ts = torch.tensor([timesteps] * latents.shape[0])
|
| 358 |
+
else:
|
| 359 |
+
ts = timesteps
|
| 360 |
+
ts = ts.to(self.device)
|
| 361 |
+
|
| 362 |
+
# Encode prompt
|
| 363 |
+
text_input_ids = self.encode_prompt(prompt)
|
| 364 |
+
|
| 365 |
+
# Get text and image features
|
| 366 |
+
encoder_hidden_states, text_features = self.get_text_features(text_input_ids)
|
| 367 |
+
image_features = self.get_image_features(encoder_hidden_states, latents, ts)
|
| 368 |
+
|
| 369 |
+
# Normalize features
|
| 370 |
+
image_features = image_features / torch.norm(image_features, dim=-1, keepdim=True)
|
| 371 |
+
text_features = text_features / torch.norm(text_features, dim=-1, keepdim=True)
|
| 372 |
+
|
| 373 |
+
# Compute similarity scores
|
| 374 |
+
scores = self.logit_scale.exp() * (text_features @ image_features.T)[0]
|
| 375 |
+
scores = torch.sigmoid(scores) # Scale to [0, 1]
|
| 376 |
+
return scores
|
| 377 |
+
# return scores
|
| 378 |
+
|
| 379 |
+
# If enable_grad is True, compute with gradients; otherwise use no_grad
|
| 380 |
+
if enable_grad:
|
| 381 |
+
return _compute()
|
| 382 |
+
else:
|
| 383 |
+
with torch.no_grad():
|
| 384 |
+
return _compute()
|
| 385 |
+
|
| 386 |
+
def forward(self, noisy_latents, prompt, timesteps):
|
| 387 |
+
"""Alias for get_reward_score for nn.Module compatibility"""
|
| 388 |
+
return self.get_reward_score(noisy_latents, prompt, timesteps)
|
Reward_sd15_idealized/models/unet_2d_condition_reward.py
ADDED
|
@@ -0,0 +1,1334 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright 2024 The HuggingFace Team. All rights reserved.
|
| 2 |
+
#
|
| 3 |
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
| 4 |
+
# you may not use this file except in compliance with the License.
|
| 5 |
+
# You may obtain a copy of the License at
|
| 6 |
+
#
|
| 7 |
+
# http://www.apache.org/licenses/LICENSE-2.0
|
| 8 |
+
#
|
| 9 |
+
# Unless required by applicable law or agreed to in writing, software
|
| 10 |
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
| 11 |
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 12 |
+
# See the License for the specific language governing permissions and
|
| 13 |
+
# limitations under the License.
|
| 14 |
+
from dataclasses import dataclass
|
| 15 |
+
from typing import Any, Dict, List, Optional, Tuple, Union
|
| 16 |
+
|
| 17 |
+
import torch
|
| 18 |
+
import torch.nn as nn
|
| 19 |
+
import torch.utils.checkpoint
|
| 20 |
+
|
| 21 |
+
from diffusers.configuration_utils import ConfigMixin, register_to_config
|
| 22 |
+
from diffusers.loaders import PeftAdapterMixin, UNet2DConditionLoadersMixin
|
| 23 |
+
from diffusers.loaders.single_file_model import FromOriginalModelMixin
|
| 24 |
+
from diffusers.utils import USE_PEFT_BACKEND, BaseOutput, deprecate, logging, scale_lora_layers, unscale_lora_layers
|
| 25 |
+
from diffusers.models.activations import get_activation
|
| 26 |
+
from diffusers.models.attention_processor import (
|
| 27 |
+
ADDED_KV_ATTENTION_PROCESSORS,
|
| 28 |
+
CROSS_ATTENTION_PROCESSORS,
|
| 29 |
+
Attention,
|
| 30 |
+
AttentionProcessor,
|
| 31 |
+
AttnAddedKVProcessor,
|
| 32 |
+
AttnProcessor,
|
| 33 |
+
FusedAttnProcessor2_0,
|
| 34 |
+
)
|
| 35 |
+
from diffusers.models.embeddings import (
|
| 36 |
+
GaussianFourierProjection,
|
| 37 |
+
GLIGENTextBoundingboxProjection,
|
| 38 |
+
ImageHintTimeEmbedding,
|
| 39 |
+
ImageProjection,
|
| 40 |
+
ImageTimeEmbedding,
|
| 41 |
+
TextImageProjection,
|
| 42 |
+
TextImageTimeEmbedding,
|
| 43 |
+
TextTimeEmbedding,
|
| 44 |
+
TimestepEmbedding,
|
| 45 |
+
Timesteps,
|
| 46 |
+
)
|
| 47 |
+
from diffusers.models.modeling_utils import ModelMixin
|
| 48 |
+
from diffusers.models.unets.unet_2d_blocks import (
|
| 49 |
+
get_down_block,
|
| 50 |
+
get_mid_block,
|
| 51 |
+
get_up_block,
|
| 52 |
+
)
|
| 53 |
+
|
| 54 |
+
|
| 55 |
+
logger = logging.get_logger(__name__) # pylint: disable=invalid-name
|
| 56 |
+
|
| 57 |
+
|
| 58 |
+
@dataclass
|
| 59 |
+
class UNet2DConditionOutput(BaseOutput):
|
| 60 |
+
"""
|
| 61 |
+
The output of [`UNet2DConditionModel`].
|
| 62 |
+
|
| 63 |
+
Args:
|
| 64 |
+
sample (`torch.Tensor` of shape `(batch_size, num_channels, height, width)`):
|
| 65 |
+
The hidden states output conditioned on `encoder_hidden_states` input. Output of last layer of model.
|
| 66 |
+
"""
|
| 67 |
+
|
| 68 |
+
sample: torch.Tensor = None
|
| 69 |
+
|
| 70 |
+
|
| 71 |
+
class UNet2DConditionModel(
|
| 72 |
+
ModelMixin, ConfigMixin, FromOriginalModelMixin, UNet2DConditionLoadersMixin, PeftAdapterMixin
|
| 73 |
+
):
|
| 74 |
+
r"""
|
| 75 |
+
A conditional 2D UNet model that takes a noisy sample, conditional state, and a timestep and returns a sample
|
| 76 |
+
shaped output.
|
| 77 |
+
|
| 78 |
+
This model inherits from [`ModelMixin`]. Check the superclass documentation for it's generic methods implemented
|
| 79 |
+
for all models (such as downloading or saving).
|
| 80 |
+
|
| 81 |
+
Parameters:
|
| 82 |
+
sample_size (`int` or `Tuple[int, int]`, *optional*, defaults to `None`):
|
| 83 |
+
Height and width of input/output sample.
|
| 84 |
+
in_channels (`int`, *optional*, defaults to 4): Number of channels in the input sample.
|
| 85 |
+
out_channels (`int`, *optional*, defaults to 4): Number of channels in the output.
|
| 86 |
+
center_input_sample (`bool`, *optional*, defaults to `False`): Whether to center the input sample.
|
| 87 |
+
flip_sin_to_cos (`bool`, *optional*, defaults to `True`):
|
| 88 |
+
Whether to flip the sin to cos in the time embedding.
|
| 89 |
+
freq_shift (`int`, *optional*, defaults to 0): The frequency shift to apply to the time embedding.
|
| 90 |
+
down_block_types (`Tuple[str]`, *optional*, defaults to `("CrossAttnDownBlock2D", "CrossAttnDownBlock2D", "CrossAttnDownBlock2D", "DownBlock2D")`):
|
| 91 |
+
The tuple of downsample blocks to use.
|
| 92 |
+
mid_block_type (`str`, *optional*, defaults to `"UNetMidBlock2DCrossAttn"`):
|
| 93 |
+
Block type for middle of UNet, it can be one of `UNetMidBlock2DCrossAttn`, `UNetMidBlock2D`, or
|
| 94 |
+
`UNetMidBlock2DSimpleCrossAttn`. If `None`, the mid block layer is skipped.
|
| 95 |
+
up_block_types (`Tuple[str]`, *optional*, defaults to `("UpBlock2D", "CrossAttnUpBlock2D", "CrossAttnUpBlock2D", "CrossAttnUpBlock2D")`):
|
| 96 |
+
The tuple of upsample blocks to use.
|
| 97 |
+
only_cross_attention(`bool` or `Tuple[bool]`, *optional*, default to `False`):
|
| 98 |
+
Whether to include self-attention in the basic transformer blocks, see
|
| 99 |
+
[`~models.attention.BasicTransformerBlock`].
|
| 100 |
+
block_out_channels (`Tuple[int]`, *optional*, defaults to `(320, 640, 1280, 1280)`):
|
| 101 |
+
The tuple of output channels for each block.
|
| 102 |
+
layers_per_block (`int`, *optional*, defaults to 2): The number of layers per block.
|
| 103 |
+
downsample_padding (`int`, *optional*, defaults to 1): The padding to use for the downsampling convolution.
|
| 104 |
+
mid_block_scale_factor (`float`, *optional*, defaults to 1.0): The scale factor to use for the mid block.
|
| 105 |
+
dropout (`float`, *optional*, defaults to 0.0): The dropout probability to use.
|
| 106 |
+
act_fn (`str`, *optional*, defaults to `"silu"`): The activation function to use.
|
| 107 |
+
norm_num_groups (`int`, *optional*, defaults to 32): The number of groups to use for the normalization.
|
| 108 |
+
If `None`, normalization and activation layers is skipped in post-processing.
|
| 109 |
+
norm_eps (`float`, *optional*, defaults to 1e-5): The epsilon to use for the normalization.
|
| 110 |
+
cross_attention_dim (`int` or `Tuple[int]`, *optional*, defaults to 1280):
|
| 111 |
+
The dimension of the cross attention features.
|
| 112 |
+
transformer_layers_per_block (`int`, `Tuple[int]`, or `Tuple[Tuple]` , *optional*, defaults to 1):
|
| 113 |
+
The number of transformer blocks of type [`~models.attention.BasicTransformerBlock`]. Only relevant for
|
| 114 |
+
[`~models.unets.unet_2d_blocks.CrossAttnDownBlock2D`], [`~models.unets.unet_2d_blocks.CrossAttnUpBlock2D`],
|
| 115 |
+
[`~models.unets.unet_2d_blocks.UNetMidBlock2DCrossAttn`].
|
| 116 |
+
reverse_transformer_layers_per_block : (`Tuple[Tuple]`, *optional*, defaults to None):
|
| 117 |
+
The number of transformer blocks of type [`~models.attention.BasicTransformerBlock`], in the upsampling
|
| 118 |
+
blocks of the U-Net. Only relevant if `transformer_layers_per_block` is of type `Tuple[Tuple]` and for
|
| 119 |
+
[`~models.unets.unet_2d_blocks.CrossAttnDownBlock2D`], [`~models.unets.unet_2d_blocks.CrossAttnUpBlock2D`],
|
| 120 |
+
[`~models.unets.unet_2d_blocks.UNetMidBlock2DCrossAttn`].
|
| 121 |
+
encoder_hid_dim (`int`, *optional*, defaults to None):
|
| 122 |
+
If `encoder_hid_dim_type` is defined, `encoder_hidden_states` will be projected from `encoder_hid_dim`
|
| 123 |
+
dimension to `cross_attention_dim`.
|
| 124 |
+
encoder_hid_dim_type (`str`, *optional*, defaults to `None`):
|
| 125 |
+
If given, the `encoder_hidden_states` and potentially other embeddings are down-projected to text
|
| 126 |
+
embeddings of dimension `cross_attention` according to `encoder_hid_dim_type`.
|
| 127 |
+
attention_head_dim (`int`, *optional*, defaults to 8): The dimension of the attention heads.
|
| 128 |
+
num_attention_heads (`int`, *optional*):
|
| 129 |
+
The number of attention heads. If not defined, defaults to `attention_head_dim`
|
| 130 |
+
resnet_time_scale_shift (`str`, *optional*, defaults to `"default"`): Time scale shift config
|
| 131 |
+
for ResNet blocks (see [`~models.resnet.ResnetBlock2D`]). Choose from `default` or `scale_shift`.
|
| 132 |
+
class_embed_type (`str`, *optional*, defaults to `None`):
|
| 133 |
+
The type of class embedding to use which is ultimately summed with the time embeddings. Choose from `None`,
|
| 134 |
+
`"timestep"`, `"identity"`, `"projection"`, or `"simple_projection"`.
|
| 135 |
+
addition_embed_type (`str`, *optional*, defaults to `None`):
|
| 136 |
+
Configures an optional embedding which will be summed with the time embeddings. Choose from `None` or
|
| 137 |
+
"text". "text" will use the `TextTimeEmbedding` layer.
|
| 138 |
+
addition_time_embed_dim: (`int`, *optional*, defaults to `None`):
|
| 139 |
+
Dimension for the timestep embeddings.
|
| 140 |
+
num_class_embeds (`int`, *optional*, defaults to `None`):
|
| 141 |
+
Input dimension of the learnable embedding matrix to be projected to `time_embed_dim`, when performing
|
| 142 |
+
class conditioning with `class_embed_type` equal to `None`.
|
| 143 |
+
time_embedding_type (`str`, *optional*, defaults to `positional`):
|
| 144 |
+
The type of position embedding to use for timesteps. Choose from `positional` or `fourier`.
|
| 145 |
+
time_embedding_dim (`int`, *optional*, defaults to `None`):
|
| 146 |
+
An optional override for the dimension of the projected time embedding.
|
| 147 |
+
time_embedding_act_fn (`str`, *optional*, defaults to `None`):
|
| 148 |
+
Optional activation function to use only once on the time embeddings before they are passed to the rest of
|
| 149 |
+
the UNet. Choose from `silu`, `mish`, `gelu`, and `swish`.
|
| 150 |
+
timestep_post_act (`str`, *optional*, defaults to `None`):
|
| 151 |
+
The second activation function to use in timestep embedding. Choose from `silu`, `mish` and `gelu`.
|
| 152 |
+
time_cond_proj_dim (`int`, *optional*, defaults to `None`):
|
| 153 |
+
The dimension of `cond_proj` layer in the timestep embedding.
|
| 154 |
+
conv_in_kernel (`int`, *optional*, default to `3`): The kernel size of `conv_in` layer.
|
| 155 |
+
conv_out_kernel (`int`, *optional*, default to `3`): The kernel size of `conv_out` layer.
|
| 156 |
+
projection_class_embeddings_input_dim (`int`, *optional*): The dimension of the `class_labels` input when
|
| 157 |
+
`class_embed_type="projection"`. Required when `class_embed_type="projection"`.
|
| 158 |
+
class_embeddings_concat (`bool`, *optional*, defaults to `False`): Whether to concatenate the time
|
| 159 |
+
embeddings with the class embeddings.
|
| 160 |
+
mid_block_only_cross_attention (`bool`, *optional*, defaults to `None`):
|
| 161 |
+
Whether to use cross attention with the mid block when using the `UNetMidBlock2DSimpleCrossAttn`. If
|
| 162 |
+
`only_cross_attention` is given as a single boolean and `mid_block_only_cross_attention` is `None`, the
|
| 163 |
+
`only_cross_attention` value is used as the value for `mid_block_only_cross_attention`. Default to `False`
|
| 164 |
+
otherwise.
|
| 165 |
+
"""
|
| 166 |
+
|
| 167 |
+
_supports_gradient_checkpointing = True
|
| 168 |
+
_no_split_modules = ["BasicTransformerBlock", "ResnetBlock2D", "CrossAttnUpBlock2D"]
|
| 169 |
+
|
| 170 |
+
@register_to_config
|
| 171 |
+
def __init__(
|
| 172 |
+
self,
|
| 173 |
+
sample_size: Optional[int] = None,
|
| 174 |
+
in_channels: int = 4,
|
| 175 |
+
out_channels: int = 4,
|
| 176 |
+
center_input_sample: bool = False,
|
| 177 |
+
flip_sin_to_cos: bool = True,
|
| 178 |
+
freq_shift: int = 0,
|
| 179 |
+
down_block_types: Tuple[str] = (
|
| 180 |
+
"CrossAttnDownBlock2D",
|
| 181 |
+
"CrossAttnDownBlock2D",
|
| 182 |
+
"CrossAttnDownBlock2D",
|
| 183 |
+
"DownBlock2D",
|
| 184 |
+
),
|
| 185 |
+
mid_block_type: Optional[str] = "UNetMidBlock2DCrossAttn",
|
| 186 |
+
up_block_types: Tuple[str] = ("UpBlock2D", "CrossAttnUpBlock2D", "CrossAttnUpBlock2D", "CrossAttnUpBlock2D"),
|
| 187 |
+
only_cross_attention: Union[bool, Tuple[bool]] = False,
|
| 188 |
+
block_out_channels: Tuple[int] = (320, 640, 1280, 1280),
|
| 189 |
+
layers_per_block: Union[int, Tuple[int]] = 2,
|
| 190 |
+
downsample_padding: int = 1,
|
| 191 |
+
mid_block_scale_factor: float = 1,
|
| 192 |
+
dropout: float = 0.0,
|
| 193 |
+
act_fn: str = "silu",
|
| 194 |
+
norm_num_groups: Optional[int] = 32,
|
| 195 |
+
norm_eps: float = 1e-5,
|
| 196 |
+
cross_attention_dim: Union[int, Tuple[int]] = 1280,
|
| 197 |
+
transformer_layers_per_block: Union[int, Tuple[int], Tuple[Tuple]] = 1,
|
| 198 |
+
reverse_transformer_layers_per_block: Optional[Tuple[Tuple[int]]] = None,
|
| 199 |
+
encoder_hid_dim: Optional[int] = None,
|
| 200 |
+
encoder_hid_dim_type: Optional[str] = None,
|
| 201 |
+
attention_head_dim: Union[int, Tuple[int]] = 8,
|
| 202 |
+
num_attention_heads: Optional[Union[int, Tuple[int]]] = None,
|
| 203 |
+
dual_cross_attention: bool = False,
|
| 204 |
+
use_linear_projection: bool = False,
|
| 205 |
+
class_embed_type: Optional[str] = None,
|
| 206 |
+
addition_embed_type: Optional[str] = None,
|
| 207 |
+
addition_time_embed_dim: Optional[int] = None,
|
| 208 |
+
num_class_embeds: Optional[int] = None,
|
| 209 |
+
upcast_attention: bool = False,
|
| 210 |
+
resnet_time_scale_shift: str = "default",
|
| 211 |
+
resnet_skip_time_act: bool = False,
|
| 212 |
+
resnet_out_scale_factor: float = 1.0,
|
| 213 |
+
time_embedding_type: str = "positional",
|
| 214 |
+
time_embedding_dim: Optional[int] = None,
|
| 215 |
+
time_embedding_act_fn: Optional[str] = None,
|
| 216 |
+
timestep_post_act: Optional[str] = None,
|
| 217 |
+
time_cond_proj_dim: Optional[int] = None,
|
| 218 |
+
conv_in_kernel: int = 3,
|
| 219 |
+
conv_out_kernel: int = 3,
|
| 220 |
+
projection_class_embeddings_input_dim: Optional[int] = None,
|
| 221 |
+
attention_type: str = "default",
|
| 222 |
+
class_embeddings_concat: bool = False,
|
| 223 |
+
mid_block_only_cross_attention: Optional[bool] = None,
|
| 224 |
+
cross_attention_norm: Optional[str] = None,
|
| 225 |
+
addition_embed_type_num_heads: int = 64,
|
| 226 |
+
):
|
| 227 |
+
super().__init__()
|
| 228 |
+
|
| 229 |
+
self.sample_size = sample_size
|
| 230 |
+
|
| 231 |
+
if num_attention_heads is not None:
|
| 232 |
+
raise ValueError(
|
| 233 |
+
"At the moment it is not possible to define the number of attention heads via `num_attention_heads` because of a naming issue as described in https://github.com/huggingface/diffusers/issues/2011#issuecomment-1547958131. Passing `num_attention_heads` will only be supported in diffusers v0.19."
|
| 234 |
+
)
|
| 235 |
+
|
| 236 |
+
# If `num_attention_heads` is not defined (which is the case for most models)
|
| 237 |
+
# it will default to `attention_head_dim`. This looks weird upon first reading it and it is.
|
| 238 |
+
# The reason for this behavior is to correct for incorrectly named variables that were introduced
|
| 239 |
+
# when this library was created. The incorrect naming was only discovered much later in https://github.com/huggingface/diffusers/issues/2011#issuecomment-1547958131
|
| 240 |
+
# Changing `attention_head_dim` to `num_attention_heads` for 40,000+ configurations is too backwards breaking
|
| 241 |
+
# which is why we correct for the naming here.
|
| 242 |
+
num_attention_heads = num_attention_heads or attention_head_dim
|
| 243 |
+
|
| 244 |
+
# Check inputs
|
| 245 |
+
self._check_config(
|
| 246 |
+
down_block_types=down_block_types,
|
| 247 |
+
up_block_types=up_block_types,
|
| 248 |
+
only_cross_attention=only_cross_attention,
|
| 249 |
+
block_out_channels=block_out_channels,
|
| 250 |
+
layers_per_block=layers_per_block,
|
| 251 |
+
cross_attention_dim=cross_attention_dim,
|
| 252 |
+
transformer_layers_per_block=transformer_layers_per_block,
|
| 253 |
+
reverse_transformer_layers_per_block=reverse_transformer_layers_per_block,
|
| 254 |
+
attention_head_dim=attention_head_dim,
|
| 255 |
+
num_attention_heads=num_attention_heads,
|
| 256 |
+
)
|
| 257 |
+
|
| 258 |
+
# input
|
| 259 |
+
conv_in_padding = (conv_in_kernel - 1) // 2
|
| 260 |
+
self.conv_in = nn.Conv2d(
|
| 261 |
+
in_channels, block_out_channels[0], kernel_size=conv_in_kernel, padding=conv_in_padding
|
| 262 |
+
)
|
| 263 |
+
|
| 264 |
+
# time
|
| 265 |
+
time_embed_dim, timestep_input_dim = self._set_time_proj(
|
| 266 |
+
time_embedding_type,
|
| 267 |
+
block_out_channels=block_out_channels,
|
| 268 |
+
flip_sin_to_cos=flip_sin_to_cos,
|
| 269 |
+
freq_shift=freq_shift,
|
| 270 |
+
time_embedding_dim=time_embedding_dim,
|
| 271 |
+
)
|
| 272 |
+
|
| 273 |
+
self.time_embedding = TimestepEmbedding(
|
| 274 |
+
timestep_input_dim,
|
| 275 |
+
time_embed_dim,
|
| 276 |
+
act_fn=act_fn,
|
| 277 |
+
post_act_fn=timestep_post_act,
|
| 278 |
+
cond_proj_dim=time_cond_proj_dim,
|
| 279 |
+
)
|
| 280 |
+
|
| 281 |
+
self._set_encoder_hid_proj(
|
| 282 |
+
encoder_hid_dim_type,
|
| 283 |
+
cross_attention_dim=cross_attention_dim,
|
| 284 |
+
encoder_hid_dim=encoder_hid_dim,
|
| 285 |
+
)
|
| 286 |
+
|
| 287 |
+
# class embedding
|
| 288 |
+
self._set_class_embedding(
|
| 289 |
+
class_embed_type,
|
| 290 |
+
act_fn=act_fn,
|
| 291 |
+
num_class_embeds=num_class_embeds,
|
| 292 |
+
projection_class_embeddings_input_dim=projection_class_embeddings_input_dim,
|
| 293 |
+
time_embed_dim=time_embed_dim,
|
| 294 |
+
timestep_input_dim=timestep_input_dim,
|
| 295 |
+
)
|
| 296 |
+
|
| 297 |
+
self._set_add_embedding(
|
| 298 |
+
addition_embed_type,
|
| 299 |
+
addition_embed_type_num_heads=addition_embed_type_num_heads,
|
| 300 |
+
addition_time_embed_dim=addition_time_embed_dim,
|
| 301 |
+
cross_attention_dim=cross_attention_dim,
|
| 302 |
+
encoder_hid_dim=encoder_hid_dim,
|
| 303 |
+
flip_sin_to_cos=flip_sin_to_cos,
|
| 304 |
+
freq_shift=freq_shift,
|
| 305 |
+
projection_class_embeddings_input_dim=projection_class_embeddings_input_dim,
|
| 306 |
+
time_embed_dim=time_embed_dim,
|
| 307 |
+
)
|
| 308 |
+
|
| 309 |
+
if time_embedding_act_fn is None:
|
| 310 |
+
self.time_embed_act = None
|
| 311 |
+
else:
|
| 312 |
+
self.time_embed_act = get_activation(time_embedding_act_fn)
|
| 313 |
+
|
| 314 |
+
self.down_blocks = nn.ModuleList([])
|
| 315 |
+
self.up_blocks = nn.ModuleList([])
|
| 316 |
+
|
| 317 |
+
if isinstance(only_cross_attention, bool):
|
| 318 |
+
if mid_block_only_cross_attention is None:
|
| 319 |
+
mid_block_only_cross_attention = only_cross_attention
|
| 320 |
+
|
| 321 |
+
only_cross_attention = [only_cross_attention] * len(down_block_types)
|
| 322 |
+
|
| 323 |
+
if mid_block_only_cross_attention is None:
|
| 324 |
+
mid_block_only_cross_attention = False
|
| 325 |
+
|
| 326 |
+
if isinstance(num_attention_heads, int):
|
| 327 |
+
num_attention_heads = (num_attention_heads,) * len(down_block_types)
|
| 328 |
+
|
| 329 |
+
if isinstance(attention_head_dim, int):
|
| 330 |
+
attention_head_dim = (attention_head_dim,) * len(down_block_types)
|
| 331 |
+
|
| 332 |
+
if isinstance(cross_attention_dim, int):
|
| 333 |
+
cross_attention_dim = (cross_attention_dim,) * len(down_block_types)
|
| 334 |
+
|
| 335 |
+
if isinstance(layers_per_block, int):
|
| 336 |
+
layers_per_block = [layers_per_block] * len(down_block_types)
|
| 337 |
+
|
| 338 |
+
if isinstance(transformer_layers_per_block, int):
|
| 339 |
+
transformer_layers_per_block = [transformer_layers_per_block] * len(down_block_types)
|
| 340 |
+
|
| 341 |
+
if class_embeddings_concat:
|
| 342 |
+
# The time embeddings are concatenated with the class embeddings. The dimension of the
|
| 343 |
+
# time embeddings passed to the down, middle, and up blocks is twice the dimension of the
|
| 344 |
+
# regular time embeddings
|
| 345 |
+
blocks_time_embed_dim = time_embed_dim * 2
|
| 346 |
+
else:
|
| 347 |
+
blocks_time_embed_dim = time_embed_dim
|
| 348 |
+
|
| 349 |
+
# down
|
| 350 |
+
output_channel = block_out_channels[0]
|
| 351 |
+
for i, down_block_type in enumerate(down_block_types):
|
| 352 |
+
input_channel = output_channel
|
| 353 |
+
output_channel = block_out_channels[i]
|
| 354 |
+
is_final_block = i == len(block_out_channels) - 1
|
| 355 |
+
|
| 356 |
+
down_block = get_down_block(
|
| 357 |
+
down_block_type,
|
| 358 |
+
num_layers=layers_per_block[i],
|
| 359 |
+
transformer_layers_per_block=transformer_layers_per_block[i],
|
| 360 |
+
in_channels=input_channel,
|
| 361 |
+
out_channels=output_channel,
|
| 362 |
+
temb_channels=blocks_time_embed_dim,
|
| 363 |
+
add_downsample=not is_final_block,
|
| 364 |
+
resnet_eps=norm_eps,
|
| 365 |
+
resnet_act_fn=act_fn,
|
| 366 |
+
resnet_groups=norm_num_groups,
|
| 367 |
+
cross_attention_dim=cross_attention_dim[i],
|
| 368 |
+
num_attention_heads=num_attention_heads[i],
|
| 369 |
+
downsample_padding=downsample_padding,
|
| 370 |
+
dual_cross_attention=dual_cross_attention,
|
| 371 |
+
use_linear_projection=use_linear_projection,
|
| 372 |
+
only_cross_attention=only_cross_attention[i],
|
| 373 |
+
upcast_attention=upcast_attention,
|
| 374 |
+
resnet_time_scale_shift=resnet_time_scale_shift,
|
| 375 |
+
attention_type=attention_type,
|
| 376 |
+
resnet_skip_time_act=resnet_skip_time_act,
|
| 377 |
+
resnet_out_scale_factor=resnet_out_scale_factor,
|
| 378 |
+
cross_attention_norm=cross_attention_norm,
|
| 379 |
+
attention_head_dim=attention_head_dim[i] if attention_head_dim[i] is not None else output_channel,
|
| 380 |
+
dropout=dropout,
|
| 381 |
+
)
|
| 382 |
+
self.down_blocks.append(down_block)
|
| 383 |
+
|
| 384 |
+
# mid
|
| 385 |
+
self.mid_block = get_mid_block(
|
| 386 |
+
mid_block_type,
|
| 387 |
+
temb_channels=blocks_time_embed_dim,
|
| 388 |
+
in_channels=block_out_channels[-1],
|
| 389 |
+
resnet_eps=norm_eps,
|
| 390 |
+
resnet_act_fn=act_fn,
|
| 391 |
+
resnet_groups=norm_num_groups,
|
| 392 |
+
output_scale_factor=mid_block_scale_factor,
|
| 393 |
+
transformer_layers_per_block=transformer_layers_per_block[-1],
|
| 394 |
+
num_attention_heads=num_attention_heads[-1],
|
| 395 |
+
cross_attention_dim=cross_attention_dim[-1],
|
| 396 |
+
dual_cross_attention=dual_cross_attention,
|
| 397 |
+
use_linear_projection=use_linear_projection,
|
| 398 |
+
mid_block_only_cross_attention=mid_block_only_cross_attention,
|
| 399 |
+
upcast_attention=upcast_attention,
|
| 400 |
+
resnet_time_scale_shift=resnet_time_scale_shift,
|
| 401 |
+
attention_type=attention_type,
|
| 402 |
+
resnet_skip_time_act=resnet_skip_time_act,
|
| 403 |
+
cross_attention_norm=cross_attention_norm,
|
| 404 |
+
attention_head_dim=attention_head_dim[-1],
|
| 405 |
+
dropout=dropout,
|
| 406 |
+
)
|
| 407 |
+
|
| 408 |
+
# count how many layers upsample the images
|
| 409 |
+
self.num_upsamplers = 0
|
| 410 |
+
|
| 411 |
+
# up
|
| 412 |
+
reversed_block_out_channels = list(reversed(block_out_channels))
|
| 413 |
+
reversed_num_attention_heads = list(reversed(num_attention_heads))
|
| 414 |
+
reversed_layers_per_block = list(reversed(layers_per_block))
|
| 415 |
+
reversed_cross_attention_dim = list(reversed(cross_attention_dim))
|
| 416 |
+
reversed_transformer_layers_per_block = (
|
| 417 |
+
list(reversed(transformer_layers_per_block))
|
| 418 |
+
if reverse_transformer_layers_per_block is None
|
| 419 |
+
else reverse_transformer_layers_per_block
|
| 420 |
+
)
|
| 421 |
+
only_cross_attention = list(reversed(only_cross_attention))
|
| 422 |
+
|
| 423 |
+
output_channel = reversed_block_out_channels[0]
|
| 424 |
+
for i, up_block_type in enumerate(up_block_types):
|
| 425 |
+
is_final_block = i == len(block_out_channels) - 1
|
| 426 |
+
|
| 427 |
+
prev_output_channel = output_channel
|
| 428 |
+
output_channel = reversed_block_out_channels[i]
|
| 429 |
+
input_channel = reversed_block_out_channels[min(i + 1, len(block_out_channels) - 1)]
|
| 430 |
+
|
| 431 |
+
# add upsample block for all BUT final layer
|
| 432 |
+
if not is_final_block:
|
| 433 |
+
add_upsample = True
|
| 434 |
+
self.num_upsamplers += 1
|
| 435 |
+
else:
|
| 436 |
+
add_upsample = False
|
| 437 |
+
|
| 438 |
+
up_block = get_up_block(
|
| 439 |
+
up_block_type,
|
| 440 |
+
num_layers=reversed_layers_per_block[i] + 1,
|
| 441 |
+
transformer_layers_per_block=reversed_transformer_layers_per_block[i],
|
| 442 |
+
in_channels=input_channel,
|
| 443 |
+
out_channels=output_channel,
|
| 444 |
+
prev_output_channel=prev_output_channel,
|
| 445 |
+
temb_channels=blocks_time_embed_dim,
|
| 446 |
+
add_upsample=add_upsample,
|
| 447 |
+
resnet_eps=norm_eps,
|
| 448 |
+
resnet_act_fn=act_fn,
|
| 449 |
+
resolution_idx=i,
|
| 450 |
+
resnet_groups=norm_num_groups,
|
| 451 |
+
cross_attention_dim=reversed_cross_attention_dim[i],
|
| 452 |
+
num_attention_heads=reversed_num_attention_heads[i],
|
| 453 |
+
dual_cross_attention=dual_cross_attention,
|
| 454 |
+
use_linear_projection=use_linear_projection,
|
| 455 |
+
only_cross_attention=only_cross_attention[i],
|
| 456 |
+
upcast_attention=upcast_attention,
|
| 457 |
+
resnet_time_scale_shift=resnet_time_scale_shift,
|
| 458 |
+
attention_type=attention_type,
|
| 459 |
+
resnet_skip_time_act=resnet_skip_time_act,
|
| 460 |
+
resnet_out_scale_factor=resnet_out_scale_factor,
|
| 461 |
+
cross_attention_norm=cross_attention_norm,
|
| 462 |
+
attention_head_dim=attention_head_dim[i] if attention_head_dim[i] is not None else output_channel,
|
| 463 |
+
dropout=dropout,
|
| 464 |
+
)
|
| 465 |
+
self.up_blocks.append(up_block)
|
| 466 |
+
prev_output_channel = output_channel
|
| 467 |
+
|
| 468 |
+
# out
|
| 469 |
+
if norm_num_groups is not None:
|
| 470 |
+
self.conv_norm_out = nn.GroupNorm(
|
| 471 |
+
num_channels=block_out_channels[0], num_groups=norm_num_groups, eps=norm_eps
|
| 472 |
+
)
|
| 473 |
+
|
| 474 |
+
self.conv_act = get_activation(act_fn)
|
| 475 |
+
|
| 476 |
+
else:
|
| 477 |
+
self.conv_norm_out = None
|
| 478 |
+
self.conv_act = None
|
| 479 |
+
|
| 480 |
+
conv_out_padding = (conv_out_kernel - 1) // 2
|
| 481 |
+
self.conv_out = nn.Conv2d(
|
| 482 |
+
block_out_channels[0], out_channels, kernel_size=conv_out_kernel, padding=conv_out_padding
|
| 483 |
+
)
|
| 484 |
+
|
| 485 |
+
self._set_pos_net_if_use_gligen(attention_type=attention_type, cross_attention_dim=cross_attention_dim)
|
| 486 |
+
|
| 487 |
+
def _check_config(
|
| 488 |
+
self,
|
| 489 |
+
down_block_types: Tuple[str],
|
| 490 |
+
up_block_types: Tuple[str],
|
| 491 |
+
only_cross_attention: Union[bool, Tuple[bool]],
|
| 492 |
+
block_out_channels: Tuple[int],
|
| 493 |
+
layers_per_block: Union[int, Tuple[int]],
|
| 494 |
+
cross_attention_dim: Union[int, Tuple[int]],
|
| 495 |
+
transformer_layers_per_block: Union[int, Tuple[int], Tuple[Tuple[int]]],
|
| 496 |
+
reverse_transformer_layers_per_block: bool,
|
| 497 |
+
attention_head_dim: int,
|
| 498 |
+
num_attention_heads: Optional[Union[int, Tuple[int]]],
|
| 499 |
+
):
|
| 500 |
+
if len(down_block_types) != len(up_block_types):
|
| 501 |
+
raise ValueError(
|
| 502 |
+
f"Must provide the same number of `down_block_types` as `up_block_types`. `down_block_types`: {down_block_types}. `up_block_types`: {up_block_types}."
|
| 503 |
+
)
|
| 504 |
+
|
| 505 |
+
if len(block_out_channels) != len(down_block_types):
|
| 506 |
+
raise ValueError(
|
| 507 |
+
f"Must provide the same number of `block_out_channels` as `down_block_types`. `block_out_channels`: {block_out_channels}. `down_block_types`: {down_block_types}."
|
| 508 |
+
)
|
| 509 |
+
|
| 510 |
+
if not isinstance(only_cross_attention, bool) and len(only_cross_attention) != len(down_block_types):
|
| 511 |
+
raise ValueError(
|
| 512 |
+
f"Must provide the same number of `only_cross_attention` as `down_block_types`. `only_cross_attention`: {only_cross_attention}. `down_block_types`: {down_block_types}."
|
| 513 |
+
)
|
| 514 |
+
|
| 515 |
+
if not isinstance(num_attention_heads, int) and len(num_attention_heads) != len(down_block_types):
|
| 516 |
+
raise ValueError(
|
| 517 |
+
f"Must provide the same number of `num_attention_heads` as `down_block_types`. `num_attention_heads`: {num_attention_heads}. `down_block_types`: {down_block_types}."
|
| 518 |
+
)
|
| 519 |
+
|
| 520 |
+
if not isinstance(attention_head_dim, int) and len(attention_head_dim) != len(down_block_types):
|
| 521 |
+
raise ValueError(
|
| 522 |
+
f"Must provide the same number of `attention_head_dim` as `down_block_types`. `attention_head_dim`: {attention_head_dim}. `down_block_types`: {down_block_types}."
|
| 523 |
+
)
|
| 524 |
+
|
| 525 |
+
if isinstance(cross_attention_dim, list) and len(cross_attention_dim) != len(down_block_types):
|
| 526 |
+
raise ValueError(
|
| 527 |
+
f"Must provide the same number of `cross_attention_dim` as `down_block_types`. `cross_attention_dim`: {cross_attention_dim}. `down_block_types`: {down_block_types}."
|
| 528 |
+
)
|
| 529 |
+
|
| 530 |
+
if not isinstance(layers_per_block, int) and len(layers_per_block) != len(down_block_types):
|
| 531 |
+
raise ValueError(
|
| 532 |
+
f"Must provide the same number of `layers_per_block` as `down_block_types`. `layers_per_block`: {layers_per_block}. `down_block_types`: {down_block_types}."
|
| 533 |
+
)
|
| 534 |
+
if isinstance(transformer_layers_per_block, list) and reverse_transformer_layers_per_block is None:
|
| 535 |
+
for layer_number_per_block in transformer_layers_per_block:
|
| 536 |
+
if isinstance(layer_number_per_block, list):
|
| 537 |
+
raise ValueError("Must provide 'reverse_transformer_layers_per_block` if using asymmetrical UNet.")
|
| 538 |
+
|
| 539 |
+
def _set_time_proj(
|
| 540 |
+
self,
|
| 541 |
+
time_embedding_type: str,
|
| 542 |
+
block_out_channels: int,
|
| 543 |
+
flip_sin_to_cos: bool,
|
| 544 |
+
freq_shift: float,
|
| 545 |
+
time_embedding_dim: int,
|
| 546 |
+
) -> Tuple[int, int]:
|
| 547 |
+
if time_embedding_type == "fourier":
|
| 548 |
+
time_embed_dim = time_embedding_dim or block_out_channels[0] * 2
|
| 549 |
+
if time_embed_dim % 2 != 0:
|
| 550 |
+
raise ValueError(f"`time_embed_dim` should be divisible by 2, but is {time_embed_dim}.")
|
| 551 |
+
self.time_proj = GaussianFourierProjection(
|
| 552 |
+
time_embed_dim // 2, set_W_to_weight=False, log=False, flip_sin_to_cos=flip_sin_to_cos
|
| 553 |
+
)
|
| 554 |
+
timestep_input_dim = time_embed_dim
|
| 555 |
+
elif time_embedding_type == "positional":
|
| 556 |
+
time_embed_dim = time_embedding_dim or block_out_channels[0] * 4
|
| 557 |
+
|
| 558 |
+
self.time_proj = Timesteps(block_out_channels[0], flip_sin_to_cos, freq_shift)
|
| 559 |
+
timestep_input_dim = block_out_channels[0]
|
| 560 |
+
else:
|
| 561 |
+
raise ValueError(
|
| 562 |
+
f"{time_embedding_type} does not exist. Please make sure to use one of `fourier` or `positional`."
|
| 563 |
+
)
|
| 564 |
+
|
| 565 |
+
return time_embed_dim, timestep_input_dim
|
| 566 |
+
|
| 567 |
+
def _set_encoder_hid_proj(
|
| 568 |
+
self,
|
| 569 |
+
encoder_hid_dim_type: Optional[str],
|
| 570 |
+
cross_attention_dim: Union[int, Tuple[int]],
|
| 571 |
+
encoder_hid_dim: Optional[int],
|
| 572 |
+
):
|
| 573 |
+
if encoder_hid_dim_type is None and encoder_hid_dim is not None:
|
| 574 |
+
encoder_hid_dim_type = "text_proj"
|
| 575 |
+
self.register_to_config(encoder_hid_dim_type=encoder_hid_dim_type)
|
| 576 |
+
logger.info("encoder_hid_dim_type defaults to 'text_proj' as `encoder_hid_dim` is defined.")
|
| 577 |
+
|
| 578 |
+
if encoder_hid_dim is None and encoder_hid_dim_type is not None:
|
| 579 |
+
raise ValueError(
|
| 580 |
+
f"`encoder_hid_dim` has to be defined when `encoder_hid_dim_type` is set to {encoder_hid_dim_type}."
|
| 581 |
+
)
|
| 582 |
+
|
| 583 |
+
if encoder_hid_dim_type == "text_proj":
|
| 584 |
+
self.encoder_hid_proj = nn.Linear(encoder_hid_dim, cross_attention_dim)
|
| 585 |
+
elif encoder_hid_dim_type == "text_image_proj":
|
| 586 |
+
# image_embed_dim DOESN'T have to be `cross_attention_dim`. To not clutter the __init__ too much
|
| 587 |
+
# they are set to `cross_attention_dim` here as this is exactly the required dimension for the currently only use
|
| 588 |
+
# case when `addition_embed_type == "text_image_proj"` (Kandinsky 2.1)`
|
| 589 |
+
self.encoder_hid_proj = TextImageProjection(
|
| 590 |
+
text_embed_dim=encoder_hid_dim,
|
| 591 |
+
image_embed_dim=cross_attention_dim,
|
| 592 |
+
cross_attention_dim=cross_attention_dim,
|
| 593 |
+
)
|
| 594 |
+
elif encoder_hid_dim_type == "image_proj":
|
| 595 |
+
# Kandinsky 2.2
|
| 596 |
+
self.encoder_hid_proj = ImageProjection(
|
| 597 |
+
image_embed_dim=encoder_hid_dim,
|
| 598 |
+
cross_attention_dim=cross_attention_dim,
|
| 599 |
+
)
|
| 600 |
+
elif encoder_hid_dim_type is not None:
|
| 601 |
+
raise ValueError(
|
| 602 |
+
f"encoder_hid_dim_type: {encoder_hid_dim_type} must be None, 'text_proj' or 'text_image_proj'."
|
| 603 |
+
)
|
| 604 |
+
else:
|
| 605 |
+
self.encoder_hid_proj = None
|
| 606 |
+
|
| 607 |
+
def _set_class_embedding(
|
| 608 |
+
self,
|
| 609 |
+
class_embed_type: Optional[str],
|
| 610 |
+
act_fn: str,
|
| 611 |
+
num_class_embeds: Optional[int],
|
| 612 |
+
projection_class_embeddings_input_dim: Optional[int],
|
| 613 |
+
time_embed_dim: int,
|
| 614 |
+
timestep_input_dim: int,
|
| 615 |
+
):
|
| 616 |
+
if class_embed_type is None and num_class_embeds is not None:
|
| 617 |
+
self.class_embedding = nn.Embedding(num_class_embeds, time_embed_dim)
|
| 618 |
+
elif class_embed_type == "timestep":
|
| 619 |
+
self.class_embedding = TimestepEmbedding(timestep_input_dim, time_embed_dim, act_fn=act_fn)
|
| 620 |
+
elif class_embed_type == "identity":
|
| 621 |
+
self.class_embedding = nn.Identity(time_embed_dim, time_embed_dim)
|
| 622 |
+
elif class_embed_type == "projection":
|
| 623 |
+
if projection_class_embeddings_input_dim is None:
|
| 624 |
+
raise ValueError(
|
| 625 |
+
"`class_embed_type`: 'projection' requires `projection_class_embeddings_input_dim` be set"
|
| 626 |
+
)
|
| 627 |
+
# The projection `class_embed_type` is the same as the timestep `class_embed_type` except
|
| 628 |
+
# 1. the `class_labels` inputs are not first converted to sinusoidal embeddings
|
| 629 |
+
# 2. it projects from an arbitrary input dimension.
|
| 630 |
+
#
|
| 631 |
+
# Note that `TimestepEmbedding` is quite general, being mainly linear layers and activations.
|
| 632 |
+
# When used for embedding actual timesteps, the timesteps are first converted to sinusoidal embeddings.
|
| 633 |
+
# As a result, `TimestepEmbedding` can be passed arbitrary vectors.
|
| 634 |
+
self.class_embedding = TimestepEmbedding(projection_class_embeddings_input_dim, time_embed_dim)
|
| 635 |
+
elif class_embed_type == "simple_projection":
|
| 636 |
+
if projection_class_embeddings_input_dim is None:
|
| 637 |
+
raise ValueError(
|
| 638 |
+
"`class_embed_type`: 'simple_projection' requires `projection_class_embeddings_input_dim` be set"
|
| 639 |
+
)
|
| 640 |
+
self.class_embedding = nn.Linear(projection_class_embeddings_input_dim, time_embed_dim)
|
| 641 |
+
else:
|
| 642 |
+
self.class_embedding = None
|
| 643 |
+
|
| 644 |
+
def _set_add_embedding(
|
| 645 |
+
self,
|
| 646 |
+
addition_embed_type: str,
|
| 647 |
+
addition_embed_type_num_heads: int,
|
| 648 |
+
addition_time_embed_dim: Optional[int],
|
| 649 |
+
flip_sin_to_cos: bool,
|
| 650 |
+
freq_shift: float,
|
| 651 |
+
cross_attention_dim: Optional[int],
|
| 652 |
+
encoder_hid_dim: Optional[int],
|
| 653 |
+
projection_class_embeddings_input_dim: Optional[int],
|
| 654 |
+
time_embed_dim: int,
|
| 655 |
+
):
|
| 656 |
+
if addition_embed_type == "text":
|
| 657 |
+
if encoder_hid_dim is not None:
|
| 658 |
+
text_time_embedding_from_dim = encoder_hid_dim
|
| 659 |
+
else:
|
| 660 |
+
text_time_embedding_from_dim = cross_attention_dim
|
| 661 |
+
|
| 662 |
+
self.add_embedding = TextTimeEmbedding(
|
| 663 |
+
text_time_embedding_from_dim, time_embed_dim, num_heads=addition_embed_type_num_heads
|
| 664 |
+
)
|
| 665 |
+
elif addition_embed_type == "text_image":
|
| 666 |
+
# text_embed_dim and image_embed_dim DON'T have to be `cross_attention_dim`. To not clutter the __init__ too much
|
| 667 |
+
# they are set to `cross_attention_dim` here as this is exactly the required dimension for the currently only use
|
| 668 |
+
# case when `addition_embed_type == "text_image"` (Kandinsky 2.1)`
|
| 669 |
+
self.add_embedding = TextImageTimeEmbedding(
|
| 670 |
+
text_embed_dim=cross_attention_dim, image_embed_dim=cross_attention_dim, time_embed_dim=time_embed_dim
|
| 671 |
+
)
|
| 672 |
+
elif addition_embed_type == "text_time":
|
| 673 |
+
self.add_time_proj = Timesteps(addition_time_embed_dim, flip_sin_to_cos, freq_shift)
|
| 674 |
+
self.add_embedding = TimestepEmbedding(projection_class_embeddings_input_dim, time_embed_dim)
|
| 675 |
+
elif addition_embed_type == "image":
|
| 676 |
+
# Kandinsky 2.2
|
| 677 |
+
self.add_embedding = ImageTimeEmbedding(image_embed_dim=encoder_hid_dim, time_embed_dim=time_embed_dim)
|
| 678 |
+
elif addition_embed_type == "image_hint":
|
| 679 |
+
# Kandinsky 2.2 ControlNet
|
| 680 |
+
self.add_embedding = ImageHintTimeEmbedding(image_embed_dim=encoder_hid_dim, time_embed_dim=time_embed_dim)
|
| 681 |
+
elif addition_embed_type is not None:
|
| 682 |
+
raise ValueError(f"addition_embed_type: {addition_embed_type} must be None, 'text' or 'text_image'.")
|
| 683 |
+
|
| 684 |
+
def _set_pos_net_if_use_gligen(self, attention_type: str, cross_attention_dim: int):
|
| 685 |
+
if attention_type in ["gated", "gated-text-image"]:
|
| 686 |
+
positive_len = 768
|
| 687 |
+
if isinstance(cross_attention_dim, int):
|
| 688 |
+
positive_len = cross_attention_dim
|
| 689 |
+
elif isinstance(cross_attention_dim, (list, tuple)):
|
| 690 |
+
positive_len = cross_attention_dim[0]
|
| 691 |
+
|
| 692 |
+
feature_type = "text-only" if attention_type == "gated" else "text-image"
|
| 693 |
+
self.position_net = GLIGENTextBoundingboxProjection(
|
| 694 |
+
positive_len=positive_len, out_dim=cross_attention_dim, feature_type=feature_type
|
| 695 |
+
)
|
| 696 |
+
|
| 697 |
+
@property
|
| 698 |
+
def attn_processors(self) -> Dict[str, AttentionProcessor]:
|
| 699 |
+
r"""
|
| 700 |
+
Returns:
|
| 701 |
+
`dict` of attention processors: A dictionary containing all attention processors used in the model with
|
| 702 |
+
indexed by its weight name.
|
| 703 |
+
"""
|
| 704 |
+
# set recursively
|
| 705 |
+
processors = {}
|
| 706 |
+
|
| 707 |
+
def fn_recursive_add_processors(name: str, module: torch.nn.Module, processors: Dict[str, AttentionProcessor]):
|
| 708 |
+
if hasattr(module, "get_processor"):
|
| 709 |
+
processors[f"{name}.processor"] = module.get_processor()
|
| 710 |
+
|
| 711 |
+
for sub_name, child in module.named_children():
|
| 712 |
+
fn_recursive_add_processors(f"{name}.{sub_name}", child, processors)
|
| 713 |
+
|
| 714 |
+
return processors
|
| 715 |
+
|
| 716 |
+
for name, module in self.named_children():
|
| 717 |
+
fn_recursive_add_processors(name, module, processors)
|
| 718 |
+
|
| 719 |
+
return processors
|
| 720 |
+
|
| 721 |
+
def set_attn_processor(self, processor: Union[AttentionProcessor, Dict[str, AttentionProcessor]]):
|
| 722 |
+
r"""
|
| 723 |
+
Sets the attention processor to use to compute attention.
|
| 724 |
+
|
| 725 |
+
Parameters:
|
| 726 |
+
processor (`dict` of `AttentionProcessor` or only `AttentionProcessor`):
|
| 727 |
+
The instantiated processor class or a dictionary of processor classes that will be set as the processor
|
| 728 |
+
for **all** `Attention` layers.
|
| 729 |
+
|
| 730 |
+
If `processor` is a dict, the key needs to define the path to the corresponding cross attention
|
| 731 |
+
processor. This is strongly recommended when setting trainable attention processors.
|
| 732 |
+
|
| 733 |
+
"""
|
| 734 |
+
count = len(self.attn_processors.keys())
|
| 735 |
+
|
| 736 |
+
if isinstance(processor, dict) and len(processor) != count:
|
| 737 |
+
raise ValueError(
|
| 738 |
+
f"A dict of processors was passed, but the number of processors {len(processor)} does not match the"
|
| 739 |
+
f" number of attention layers: {count}. Please make sure to pass {count} processor classes."
|
| 740 |
+
)
|
| 741 |
+
|
| 742 |
+
def fn_recursive_attn_processor(name: str, module: torch.nn.Module, processor):
|
| 743 |
+
if hasattr(module, "set_processor"):
|
| 744 |
+
if not isinstance(processor, dict):
|
| 745 |
+
module.set_processor(processor)
|
| 746 |
+
else:
|
| 747 |
+
module.set_processor(processor.pop(f"{name}.processor"))
|
| 748 |
+
|
| 749 |
+
for sub_name, child in module.named_children():
|
| 750 |
+
fn_recursive_attn_processor(f"{name}.{sub_name}", child, processor)
|
| 751 |
+
|
| 752 |
+
for name, module in self.named_children():
|
| 753 |
+
fn_recursive_attn_processor(name, module, processor)
|
| 754 |
+
|
| 755 |
+
def set_default_attn_processor(self):
|
| 756 |
+
"""
|
| 757 |
+
Disables custom attention processors and sets the default attention implementation.
|
| 758 |
+
"""
|
| 759 |
+
if all(proc.__class__ in ADDED_KV_ATTENTION_PROCESSORS for proc in self.attn_processors.values()):
|
| 760 |
+
processor = AttnAddedKVProcessor()
|
| 761 |
+
elif all(proc.__class__ in CROSS_ATTENTION_PROCESSORS for proc in self.attn_processors.values()):
|
| 762 |
+
processor = AttnProcessor()
|
| 763 |
+
else:
|
| 764 |
+
raise ValueError(
|
| 765 |
+
f"Cannot call `set_default_attn_processor` when attention processors are of type {next(iter(self.attn_processors.values()))}"
|
| 766 |
+
)
|
| 767 |
+
|
| 768 |
+
self.set_attn_processor(processor)
|
| 769 |
+
|
| 770 |
+
def set_attention_slice(self, slice_size: Union[str, int, List[int]] = "auto"):
|
| 771 |
+
r"""
|
| 772 |
+
Enable sliced attention computation.
|
| 773 |
+
|
| 774 |
+
When this option is enabled, the attention module splits the input tensor in slices to compute attention in
|
| 775 |
+
several steps. This is useful for saving some memory in exchange for a small decrease in speed.
|
| 776 |
+
|
| 777 |
+
Args:
|
| 778 |
+
slice_size (`str` or `int` or `list(int)`, *optional*, defaults to `"auto"`):
|
| 779 |
+
When `"auto"`, input to the attention heads is halved, so attention is computed in two steps. If
|
| 780 |
+
`"max"`, maximum amount of memory is saved by running only one slice at a time. If a number is
|
| 781 |
+
provided, uses as many slices as `attention_head_dim // slice_size`. In this case, `attention_head_dim`
|
| 782 |
+
must be a multiple of `slice_size`.
|
| 783 |
+
"""
|
| 784 |
+
sliceable_head_dims = []
|
| 785 |
+
|
| 786 |
+
def fn_recursive_retrieve_sliceable_dims(module: torch.nn.Module):
|
| 787 |
+
if hasattr(module, "set_attention_slice"):
|
| 788 |
+
sliceable_head_dims.append(module.sliceable_head_dim)
|
| 789 |
+
|
| 790 |
+
for child in module.children():
|
| 791 |
+
fn_recursive_retrieve_sliceable_dims(child)
|
| 792 |
+
|
| 793 |
+
# retrieve number of attention layers
|
| 794 |
+
for module in self.children():
|
| 795 |
+
fn_recursive_retrieve_sliceable_dims(module)
|
| 796 |
+
|
| 797 |
+
num_sliceable_layers = len(sliceable_head_dims)
|
| 798 |
+
|
| 799 |
+
if slice_size == "auto":
|
| 800 |
+
# half the attention head size is usually a good trade-off between
|
| 801 |
+
# speed and memory
|
| 802 |
+
slice_size = [dim // 2 for dim in sliceable_head_dims]
|
| 803 |
+
elif slice_size == "max":
|
| 804 |
+
# make smallest slice possible
|
| 805 |
+
slice_size = num_sliceable_layers * [1]
|
| 806 |
+
|
| 807 |
+
slice_size = num_sliceable_layers * [slice_size] if not isinstance(slice_size, list) else slice_size
|
| 808 |
+
|
| 809 |
+
if len(slice_size) != len(sliceable_head_dims):
|
| 810 |
+
raise ValueError(
|
| 811 |
+
f"You have provided {len(slice_size)}, but {self.config} has {len(sliceable_head_dims)} different"
|
| 812 |
+
f" attention layers. Make sure to match `len(slice_size)` to be {len(sliceable_head_dims)}."
|
| 813 |
+
)
|
| 814 |
+
|
| 815 |
+
for i in range(len(slice_size)):
|
| 816 |
+
size = slice_size[i]
|
| 817 |
+
dim = sliceable_head_dims[i]
|
| 818 |
+
if size is not None and size > dim:
|
| 819 |
+
raise ValueError(f"size {size} has to be smaller or equal to {dim}.")
|
| 820 |
+
|
| 821 |
+
# Recursively walk through all the children.
|
| 822 |
+
# Any children which exposes the set_attention_slice method
|
| 823 |
+
# gets the message
|
| 824 |
+
def fn_recursive_set_attention_slice(module: torch.nn.Module, slice_size: List[int]):
|
| 825 |
+
if hasattr(module, "set_attention_slice"):
|
| 826 |
+
module.set_attention_slice(slice_size.pop())
|
| 827 |
+
|
| 828 |
+
for child in module.children():
|
| 829 |
+
fn_recursive_set_attention_slice(child, slice_size)
|
| 830 |
+
|
| 831 |
+
reversed_slice_size = list(reversed(slice_size))
|
| 832 |
+
for module in self.children():
|
| 833 |
+
fn_recursive_set_attention_slice(module, reversed_slice_size)
|
| 834 |
+
|
| 835 |
+
def _set_gradient_checkpointing(self, module, value=False):
|
| 836 |
+
if hasattr(module, "gradient_checkpointing"):
|
| 837 |
+
module.gradient_checkpointing = value
|
| 838 |
+
|
| 839 |
+
def enable_freeu(self, s1: float, s2: float, b1: float, b2: float):
|
| 840 |
+
r"""Enables the FreeU mechanism from https://arxiv.org/abs/2309.11497.
|
| 841 |
+
|
| 842 |
+
The suffixes after the scaling factors represent the stage blocks where they are being applied.
|
| 843 |
+
|
| 844 |
+
Please refer to the [official repository](https://github.com/ChenyangSi/FreeU) for combinations of values that
|
| 845 |
+
are known to work well for different pipelines such as Stable Diffusion v1, v2, and Stable Diffusion XL.
|
| 846 |
+
|
| 847 |
+
Args:
|
| 848 |
+
s1 (`float`):
|
| 849 |
+
Scaling factor for stage 1 to attenuate the contributions of the skip features. This is done to
|
| 850 |
+
mitigate the "oversmoothing effect" in the enhanced denoising process.
|
| 851 |
+
s2 (`float`):
|
| 852 |
+
Scaling factor for stage 2 to attenuate the contributions of the skip features. This is done to
|
| 853 |
+
mitigate the "oversmoothing effect" in the enhanced denoising process.
|
| 854 |
+
b1 (`float`): Scaling factor for stage 1 to amplify the contributions of backbone features.
|
| 855 |
+
b2 (`float`): Scaling factor for stage 2 to amplify the contributions of backbone features.
|
| 856 |
+
"""
|
| 857 |
+
for i, upsample_block in enumerate(self.up_blocks):
|
| 858 |
+
setattr(upsample_block, "s1", s1)
|
| 859 |
+
setattr(upsample_block, "s2", s2)
|
| 860 |
+
setattr(upsample_block, "b1", b1)
|
| 861 |
+
setattr(upsample_block, "b2", b2)
|
| 862 |
+
|
| 863 |
+
def disable_freeu(self):
|
| 864 |
+
"""Disables the FreeU mechanism."""
|
| 865 |
+
freeu_keys = {"s1", "s2", "b1", "b2"}
|
| 866 |
+
for i, upsample_block in enumerate(self.up_blocks):
|
| 867 |
+
for k in freeu_keys:
|
| 868 |
+
if hasattr(upsample_block, k) or getattr(upsample_block, k, None) is not None:
|
| 869 |
+
setattr(upsample_block, k, None)
|
| 870 |
+
|
| 871 |
+
def fuse_qkv_projections(self):
|
| 872 |
+
"""
|
| 873 |
+
Enables fused QKV projections. For self-attention modules, all projection matrices (i.e., query, key, value)
|
| 874 |
+
are fused. For cross-attention modules, key and value projection matrices are fused.
|
| 875 |
+
|
| 876 |
+
<Tip warning={true}>
|
| 877 |
+
|
| 878 |
+
This API is 🧪 experimental.
|
| 879 |
+
|
| 880 |
+
</Tip>
|
| 881 |
+
"""
|
| 882 |
+
self.original_attn_processors = None
|
| 883 |
+
|
| 884 |
+
for _, attn_processor in self.attn_processors.items():
|
| 885 |
+
if "Added" in str(attn_processor.__class__.__name__):
|
| 886 |
+
raise ValueError("`fuse_qkv_projections()` is not supported for models having added KV projections.")
|
| 887 |
+
|
| 888 |
+
self.original_attn_processors = self.attn_processors
|
| 889 |
+
|
| 890 |
+
for module in self.modules():
|
| 891 |
+
if isinstance(module, Attention):
|
| 892 |
+
module.fuse_projections(fuse=True)
|
| 893 |
+
|
| 894 |
+
self.set_attn_processor(FusedAttnProcessor2_0())
|
| 895 |
+
|
| 896 |
+
def unfuse_qkv_projections(self):
|
| 897 |
+
"""Disables the fused QKV projection if enabled.
|
| 898 |
+
|
| 899 |
+
<Tip warning={true}>
|
| 900 |
+
|
| 901 |
+
This API is 🧪 experimental.
|
| 902 |
+
|
| 903 |
+
</Tip>
|
| 904 |
+
|
| 905 |
+
"""
|
| 906 |
+
if self.original_attn_processors is not None:
|
| 907 |
+
self.set_attn_processor(self.original_attn_processors)
|
| 908 |
+
|
| 909 |
+
def get_time_embed(
|
| 910 |
+
self, sample: torch.Tensor, timestep: Union[torch.Tensor, float, int]
|
| 911 |
+
) -> Optional[torch.Tensor]:
|
| 912 |
+
timesteps = timestep
|
| 913 |
+
if not torch.is_tensor(timesteps):
|
| 914 |
+
# TODO: this requires sync between CPU and GPU. So try to pass timesteps as tensors if you can
|
| 915 |
+
# This would be a good case for the `match` statement (Python 3.10+)
|
| 916 |
+
is_mps = sample.device.type == "mps"
|
| 917 |
+
if isinstance(timestep, float):
|
| 918 |
+
dtype = torch.float32 if is_mps else torch.float64
|
| 919 |
+
else:
|
| 920 |
+
dtype = torch.int32 if is_mps else torch.int64
|
| 921 |
+
timesteps = torch.tensor([timesteps], dtype=dtype, device=sample.device)
|
| 922 |
+
elif len(timesteps.shape) == 0:
|
| 923 |
+
timesteps = timesteps[None].to(sample.device)
|
| 924 |
+
|
| 925 |
+
# broadcast to batch dimension in a way that's compatible with ONNX/Core ML
|
| 926 |
+
timesteps = timesteps.expand(sample.shape[0])
|
| 927 |
+
|
| 928 |
+
t_emb = self.time_proj(timesteps)
|
| 929 |
+
# `Timesteps` does not contain any weights and will always return f32 tensors
|
| 930 |
+
# but time_embedding might actually be running in fp16. so we need to cast here.
|
| 931 |
+
# there might be better ways to encapsulate this.
|
| 932 |
+
t_emb = t_emb.to(dtype=sample.dtype)
|
| 933 |
+
return t_emb
|
| 934 |
+
|
| 935 |
+
def get_class_embed(self, sample: torch.Tensor, class_labels: Optional[torch.Tensor]) -> Optional[torch.Tensor]:
|
| 936 |
+
class_emb = None
|
| 937 |
+
if self.class_embedding is not None:
|
| 938 |
+
if class_labels is None:
|
| 939 |
+
raise ValueError("class_labels should be provided when num_class_embeds > 0")
|
| 940 |
+
|
| 941 |
+
if self.config.class_embed_type == "timestep":
|
| 942 |
+
class_labels = self.time_proj(class_labels)
|
| 943 |
+
|
| 944 |
+
# `Timesteps` does not contain any weights and will always return f32 tensors
|
| 945 |
+
# there might be better ways to encapsulate this.
|
| 946 |
+
class_labels = class_labels.to(dtype=sample.dtype)
|
| 947 |
+
|
| 948 |
+
class_emb = self.class_embedding(class_labels).to(dtype=sample.dtype)
|
| 949 |
+
return class_emb
|
| 950 |
+
|
| 951 |
+
def get_aug_embed(
|
| 952 |
+
self, emb: torch.Tensor, encoder_hidden_states: torch.Tensor, added_cond_kwargs: Dict[str, Any]
|
| 953 |
+
) -> Optional[torch.Tensor]:
|
| 954 |
+
aug_emb = None
|
| 955 |
+
if self.config.addition_embed_type == "text":
|
| 956 |
+
aug_emb = self.add_embedding(encoder_hidden_states)
|
| 957 |
+
elif self.config.addition_embed_type == "text_image":
|
| 958 |
+
# Kandinsky 2.1 - style
|
| 959 |
+
if "image_embeds" not in added_cond_kwargs:
|
| 960 |
+
raise ValueError(
|
| 961 |
+
f"{self.__class__} has the config param `addition_embed_type` set to 'text_image' which requires the keyword argument `image_embeds` to be passed in `added_cond_kwargs`"
|
| 962 |
+
)
|
| 963 |
+
|
| 964 |
+
image_embs = added_cond_kwargs.get("image_embeds")
|
| 965 |
+
text_embs = added_cond_kwargs.get("text_embeds", encoder_hidden_states)
|
| 966 |
+
aug_emb = self.add_embedding(text_embs, image_embs)
|
| 967 |
+
elif self.config.addition_embed_type == "text_time":
|
| 968 |
+
# SDXL - style
|
| 969 |
+
if "text_embeds" not in added_cond_kwargs:
|
| 970 |
+
raise ValueError(
|
| 971 |
+
f"{self.__class__} has the config param `addition_embed_type` set to 'text_time' which requires the keyword argument `text_embeds` to be passed in `added_cond_kwargs`"
|
| 972 |
+
)
|
| 973 |
+
text_embeds = added_cond_kwargs.get("text_embeds")
|
| 974 |
+
if "time_ids" not in added_cond_kwargs:
|
| 975 |
+
raise ValueError(
|
| 976 |
+
f"{self.__class__} has the config param `addition_embed_type` set to 'text_time' which requires the keyword argument `time_ids` to be passed in `added_cond_kwargs`"
|
| 977 |
+
)
|
| 978 |
+
time_ids = added_cond_kwargs.get("time_ids")
|
| 979 |
+
time_embeds = self.add_time_proj(time_ids.flatten())
|
| 980 |
+
time_embeds = time_embeds.reshape((text_embeds.shape[0], -1))
|
| 981 |
+
add_embeds = torch.concat([text_embeds, time_embeds], dim=-1)
|
| 982 |
+
add_embeds = add_embeds.to(emb.dtype)
|
| 983 |
+
aug_emb = self.add_embedding(add_embeds)
|
| 984 |
+
elif self.config.addition_embed_type == "image":
|
| 985 |
+
# Kandinsky 2.2 - style
|
| 986 |
+
if "image_embeds" not in added_cond_kwargs:
|
| 987 |
+
raise ValueError(
|
| 988 |
+
f"{self.__class__} has the config param `addition_embed_type` set to 'image' which requires the keyword argument `image_embeds` to be passed in `added_cond_kwargs`"
|
| 989 |
+
)
|
| 990 |
+
image_embs = added_cond_kwargs.get("image_embeds")
|
| 991 |
+
aug_emb = self.add_embedding(image_embs)
|
| 992 |
+
elif self.config.addition_embed_type == "image_hint":
|
| 993 |
+
# Kandinsky 2.2 - style
|
| 994 |
+
if "image_embeds" not in added_cond_kwargs or "hint" not in added_cond_kwargs:
|
| 995 |
+
raise ValueError(
|
| 996 |
+
f"{self.__class__} has the config param `addition_embed_type` set to 'image_hint' which requires the keyword arguments `image_embeds` and `hint` to be passed in `added_cond_kwargs`"
|
| 997 |
+
)
|
| 998 |
+
image_embs = added_cond_kwargs.get("image_embeds")
|
| 999 |
+
hint = added_cond_kwargs.get("hint")
|
| 1000 |
+
aug_emb = self.add_embedding(image_embs, hint)
|
| 1001 |
+
return aug_emb
|
| 1002 |
+
|
| 1003 |
+
def process_encoder_hidden_states(
|
| 1004 |
+
self, encoder_hidden_states: torch.Tensor, added_cond_kwargs: Dict[str, Any]
|
| 1005 |
+
) -> torch.Tensor:
|
| 1006 |
+
if self.encoder_hid_proj is not None and self.config.encoder_hid_dim_type == "text_proj":
|
| 1007 |
+
encoder_hidden_states = self.encoder_hid_proj(encoder_hidden_states)
|
| 1008 |
+
elif self.encoder_hid_proj is not None and self.config.encoder_hid_dim_type == "text_image_proj":
|
| 1009 |
+
# Kandinsky 2.1 - style
|
| 1010 |
+
if "image_embeds" not in added_cond_kwargs:
|
| 1011 |
+
raise ValueError(
|
| 1012 |
+
f"{self.__class__} has the config param `encoder_hid_dim_type` set to 'text_image_proj' which requires the keyword argument `image_embeds` to be passed in `added_conditions`"
|
| 1013 |
+
)
|
| 1014 |
+
|
| 1015 |
+
image_embeds = added_cond_kwargs.get("image_embeds")
|
| 1016 |
+
encoder_hidden_states = self.encoder_hid_proj(encoder_hidden_states, image_embeds)
|
| 1017 |
+
elif self.encoder_hid_proj is not None and self.config.encoder_hid_dim_type == "image_proj":
|
| 1018 |
+
# Kandinsky 2.2 - style
|
| 1019 |
+
if "image_embeds" not in added_cond_kwargs:
|
| 1020 |
+
raise ValueError(
|
| 1021 |
+
f"{self.__class__} has the config param `encoder_hid_dim_type` set to 'image_proj' which requires the keyword argument `image_embeds` to be passed in `added_conditions`"
|
| 1022 |
+
)
|
| 1023 |
+
image_embeds = added_cond_kwargs.get("image_embeds")
|
| 1024 |
+
encoder_hidden_states = self.encoder_hid_proj(image_embeds)
|
| 1025 |
+
elif self.encoder_hid_proj is not None and self.config.encoder_hid_dim_type == "ip_image_proj":
|
| 1026 |
+
if "image_embeds" not in added_cond_kwargs:
|
| 1027 |
+
raise ValueError(
|
| 1028 |
+
f"{self.__class__} has the config param `encoder_hid_dim_type` set to 'ip_image_proj' which requires the keyword argument `image_embeds` to be passed in `added_conditions`"
|
| 1029 |
+
)
|
| 1030 |
+
|
| 1031 |
+
if hasattr(self, "text_encoder_hid_proj") and self.text_encoder_hid_proj is not None:
|
| 1032 |
+
encoder_hidden_states = self.text_encoder_hid_proj(encoder_hidden_states)
|
| 1033 |
+
|
| 1034 |
+
image_embeds = added_cond_kwargs.get("image_embeds")
|
| 1035 |
+
image_embeds = self.encoder_hid_proj(image_embeds)
|
| 1036 |
+
encoder_hidden_states = (encoder_hidden_states, image_embeds)
|
| 1037 |
+
return encoder_hidden_states
|
| 1038 |
+
|
| 1039 |
+
def forward(
|
| 1040 |
+
self,
|
| 1041 |
+
sample: torch.Tensor,
|
| 1042 |
+
timestep: Union[torch.Tensor, float, int],
|
| 1043 |
+
encoder_hidden_states: torch.Tensor,
|
| 1044 |
+
class_labels: Optional[torch.Tensor] = None,
|
| 1045 |
+
timestep_cond: Optional[torch.Tensor] = None,
|
| 1046 |
+
attention_mask: Optional[torch.Tensor] = None,
|
| 1047 |
+
cross_attention_kwargs: Optional[Dict[str, Any]] = None,
|
| 1048 |
+
added_cond_kwargs: Optional[Dict[str, torch.Tensor]] = None,
|
| 1049 |
+
down_block_additional_residuals: Optional[Tuple[torch.Tensor]] = None,
|
| 1050 |
+
mid_block_additional_residual: Optional[torch.Tensor] = None,
|
| 1051 |
+
down_intrablock_additional_residuals: Optional[Tuple[torch.Tensor]] = None,
|
| 1052 |
+
encoder_attention_mask: Optional[torch.Tensor] = None,
|
| 1053 |
+
use_up_blocks: bool = False,
|
| 1054 |
+
return_dict: bool = True,
|
| 1055 |
+
) -> Union[UNet2DConditionOutput, Tuple]:
|
| 1056 |
+
r"""
|
| 1057 |
+
The [`UNet2DConditionModel`] forward method.
|
| 1058 |
+
|
| 1059 |
+
Args:
|
| 1060 |
+
sample (`torch.Tensor`):
|
| 1061 |
+
The noisy input tensor with the following shape `(batch, channel, height, width)`.
|
| 1062 |
+
timestep (`torch.Tensor` or `float` or `int`): The number of timesteps to denoise an input.
|
| 1063 |
+
encoder_hidden_states (`torch.Tensor`):
|
| 1064 |
+
The encoder hidden states with shape `(batch, sequence_length, feature_dim)`.
|
| 1065 |
+
class_labels (`torch.Tensor`, *optional*, defaults to `None`):
|
| 1066 |
+
Optional class labels for conditioning. Their embeddings will be summed with the timestep embeddings.
|
| 1067 |
+
timestep_cond: (`torch.Tensor`, *optional*, defaults to `None`):
|
| 1068 |
+
Conditional embeddings for timestep. If provided, the embeddings will be summed with the samples passed
|
| 1069 |
+
through the `self.time_embedding` layer to obtain the timestep embeddings.
|
| 1070 |
+
attention_mask (`torch.Tensor`, *optional*, defaults to `None`):
|
| 1071 |
+
An attention mask of shape `(batch, key_tokens)` is applied to `encoder_hidden_states`. If `1` the mask
|
| 1072 |
+
is kept, otherwise if `0` it is discarded. Mask will be converted into a bias, which adds large
|
| 1073 |
+
negative values to the attention scores corresponding to "discard" tokens.
|
| 1074 |
+
cross_attention_kwargs (`dict`, *optional*):
|
| 1075 |
+
A kwargs dictionary that if specified is passed along to the `AttentionProcessor` as defined under
|
| 1076 |
+
`self.processor` in
|
| 1077 |
+
[diffusers.models.attention_processor](https://github.com/huggingface/diffusers/blob/main/src/diffusers/models/attention_processor.py).
|
| 1078 |
+
added_cond_kwargs: (`dict`, *optional*):
|
| 1079 |
+
A kwargs dictionary containing additional embeddings that if specified are added to the embeddings that
|
| 1080 |
+
are passed along to the UNet blocks.
|
| 1081 |
+
down_block_additional_residuals: (`tuple` of `torch.Tensor`, *optional*):
|
| 1082 |
+
A tuple of tensors that if specified are added to the residuals of down unet blocks.
|
| 1083 |
+
mid_block_additional_residual: (`torch.Tensor`, *optional*):
|
| 1084 |
+
A tensor that if specified is added to the residual of the middle unet block.
|
| 1085 |
+
down_intrablock_additional_residuals (`tuple` of `torch.Tensor`, *optional*):
|
| 1086 |
+
additional residuals to be added within UNet down blocks, for example from T2I-Adapter side model(s)
|
| 1087 |
+
encoder_attention_mask (`torch.Tensor`):
|
| 1088 |
+
A cross-attention mask of shape `(batch, sequence_length)` is applied to `encoder_hidden_states`. If
|
| 1089 |
+
`True` the mask is kept, otherwise if `False` it is discarded. Mask will be converted into a bias,
|
| 1090 |
+
which adds large negative values to the attention scores corresponding to "discard" tokens.
|
| 1091 |
+
return_dict (`bool`, *optional*, defaults to `True`):
|
| 1092 |
+
Whether or not to return a [`~models.unets.unet_2d_condition.UNet2DConditionOutput`] instead of a plain
|
| 1093 |
+
tuple.
|
| 1094 |
+
|
| 1095 |
+
Returns:
|
| 1096 |
+
[`~models.unets.unet_2d_condition.UNet2DConditionOutput`] or `tuple`:
|
| 1097 |
+
If `return_dict` is True, an [`~models.unets.unet_2d_condition.UNet2DConditionOutput`] is returned,
|
| 1098 |
+
otherwise a `tuple` is returned where the first element is the sample tensor.
|
| 1099 |
+
"""
|
| 1100 |
+
# By default samples have to be AT least a multiple of the overall upsampling factor.
|
| 1101 |
+
# The overall upsampling factor is equal to 2 ** (# num of upsampling layers).
|
| 1102 |
+
# However, the upsampling interpolation output size can be forced to fit any upsampling size
|
| 1103 |
+
# on the fly if necessary.
|
| 1104 |
+
default_overall_up_factor = 2**self.num_upsamplers
|
| 1105 |
+
|
| 1106 |
+
# upsample size should be forwarded when sample is not a multiple of `default_overall_up_factor`
|
| 1107 |
+
forward_upsample_size = False
|
| 1108 |
+
upsample_size = None
|
| 1109 |
+
|
| 1110 |
+
# import time
|
| 1111 |
+
# torch.cuda.synchronize()
|
| 1112 |
+
# start_time = time.time()
|
| 1113 |
+
|
| 1114 |
+
for dim in sample.shape[-2:]:
|
| 1115 |
+
if dim % default_overall_up_factor != 0:
|
| 1116 |
+
# Forward upsample size to force interpolation output size.
|
| 1117 |
+
forward_upsample_size = True
|
| 1118 |
+
break
|
| 1119 |
+
|
| 1120 |
+
# ensure attention_mask is a bias, and give it a singleton query_tokens dimension
|
| 1121 |
+
# expects mask of shape:
|
| 1122 |
+
# [batch, key_tokens]
|
| 1123 |
+
# adds singleton query_tokens dimension:
|
| 1124 |
+
# [batch, 1, key_tokens]
|
| 1125 |
+
# this helps to broadcast it as a bias over attention scores, which will be in one of the following shapes:
|
| 1126 |
+
# [batch, heads, query_tokens, key_tokens] (e.g. torch sdp attn)
|
| 1127 |
+
# [batch * heads, query_tokens, key_tokens] (e.g. xformers or classic attn)
|
| 1128 |
+
if attention_mask is not None:
|
| 1129 |
+
# assume that mask is expressed as:
|
| 1130 |
+
# (1 = keep, 0 = discard)
|
| 1131 |
+
# convert mask into a bias that can be added to attention scores:
|
| 1132 |
+
# (keep = +0, discard = -10000.0)
|
| 1133 |
+
attention_mask = (1 - attention_mask.to(sample.dtype)) * -10000.0
|
| 1134 |
+
attention_mask = attention_mask.unsqueeze(1)
|
| 1135 |
+
|
| 1136 |
+
# convert encoder_attention_mask to a bias the same way we do for attention_mask
|
| 1137 |
+
if encoder_attention_mask is not None:
|
| 1138 |
+
encoder_attention_mask = (1 - encoder_attention_mask.to(sample.dtype)) * -10000.0
|
| 1139 |
+
encoder_attention_mask = encoder_attention_mask.unsqueeze(1)
|
| 1140 |
+
|
| 1141 |
+
# 0. center input if necessary
|
| 1142 |
+
if self.config.center_input_sample:
|
| 1143 |
+
sample = 2 * sample - 1.0
|
| 1144 |
+
|
| 1145 |
+
# 1. time
|
| 1146 |
+
t_emb = self.get_time_embed(sample=sample, timestep=timestep)
|
| 1147 |
+
emb = self.time_embedding(t_emb, timestep_cond)
|
| 1148 |
+
aug_emb = None
|
| 1149 |
+
|
| 1150 |
+
class_emb = self.get_class_embed(sample=sample, class_labels=class_labels)
|
| 1151 |
+
if class_emb is not None:
|
| 1152 |
+
if self.config.class_embeddings_concat:
|
| 1153 |
+
emb = torch.cat([emb, class_emb], dim=-1)
|
| 1154 |
+
else:
|
| 1155 |
+
emb = emb + class_emb
|
| 1156 |
+
|
| 1157 |
+
aug_emb = self.get_aug_embed(
|
| 1158 |
+
emb=emb, encoder_hidden_states=encoder_hidden_states, added_cond_kwargs=added_cond_kwargs
|
| 1159 |
+
)
|
| 1160 |
+
if self.config.addition_embed_type == "image_hint":
|
| 1161 |
+
aug_emb, hint = aug_emb
|
| 1162 |
+
sample = torch.cat([sample, hint], dim=1)
|
| 1163 |
+
|
| 1164 |
+
emb = emb + aug_emb if aug_emb is not None else emb
|
| 1165 |
+
|
| 1166 |
+
if self.time_embed_act is not None:
|
| 1167 |
+
emb = self.time_embed_act(emb)
|
| 1168 |
+
|
| 1169 |
+
encoder_hidden_states = self.process_encoder_hidden_states(
|
| 1170 |
+
encoder_hidden_states=encoder_hidden_states, added_cond_kwargs=added_cond_kwargs
|
| 1171 |
+
)
|
| 1172 |
+
|
| 1173 |
+
# 2. pre-process
|
| 1174 |
+
sample = self.conv_in(sample)
|
| 1175 |
+
|
| 1176 |
+
# 2.5 GLIGEN position net
|
| 1177 |
+
if cross_attention_kwargs is not None and cross_attention_kwargs.get("gligen", None) is not None:
|
| 1178 |
+
cross_attention_kwargs = cross_attention_kwargs.copy()
|
| 1179 |
+
gligen_args = cross_attention_kwargs.pop("gligen")
|
| 1180 |
+
cross_attention_kwargs["gligen"] = {"objs": self.position_net(**gligen_args)}
|
| 1181 |
+
|
| 1182 |
+
# 3. down
|
| 1183 |
+
# we're popping the `scale` instead of getting it because otherwise `scale` will be propagated
|
| 1184 |
+
# to the internal blocks and will raise deprecation warnings. this will be confusing for our users.
|
| 1185 |
+
if cross_attention_kwargs is not None:
|
| 1186 |
+
cross_attention_kwargs = cross_attention_kwargs.copy()
|
| 1187 |
+
lora_scale = cross_attention_kwargs.pop("scale", 1.0)
|
| 1188 |
+
else:
|
| 1189 |
+
lora_scale = 1.0
|
| 1190 |
+
|
| 1191 |
+
if USE_PEFT_BACKEND:
|
| 1192 |
+
# weight the lora layers by setting `lora_scale` for each PEFT layer
|
| 1193 |
+
scale_lora_layers(self, lora_scale)
|
| 1194 |
+
|
| 1195 |
+
is_controlnet = mid_block_additional_residual is not None and down_block_additional_residuals is not None
|
| 1196 |
+
# using new arg down_intrablock_additional_residuals for T2I-Adapters, to distinguish from controlnets
|
| 1197 |
+
is_adapter = down_intrablock_additional_residuals is not None
|
| 1198 |
+
# maintain backward compatibility for legacy usage, where
|
| 1199 |
+
# T2I-Adapter and ControlNet both use down_block_additional_residuals arg
|
| 1200 |
+
# but can only use one or the other
|
| 1201 |
+
if not is_adapter and mid_block_additional_residual is None and down_block_additional_residuals is not None:
|
| 1202 |
+
deprecate(
|
| 1203 |
+
"T2I should not use down_block_additional_residuals",
|
| 1204 |
+
"1.3.0",
|
| 1205 |
+
"Passing intrablock residual connections with `down_block_additional_residuals` is deprecated \
|
| 1206 |
+
and will be removed in diffusers 1.3.0. `down_block_additional_residuals` should only be used \
|
| 1207 |
+
for ControlNet. Please make sure use `down_intrablock_additional_residuals` instead. ",
|
| 1208 |
+
standard_warn=False,
|
| 1209 |
+
)
|
| 1210 |
+
down_intrablock_additional_residuals = down_block_additional_residuals
|
| 1211 |
+
is_adapter = True
|
| 1212 |
+
|
| 1213 |
+
# torch.cuda.synchronize()
|
| 1214 |
+
# logger.info(f"unet preprocess: {time.time() - start_time}")
|
| 1215 |
+
|
| 1216 |
+
# torch.cuda.synchronize()
|
| 1217 |
+
# start_time = time.time()
|
| 1218 |
+
down_block_res_samples = (sample,)
|
| 1219 |
+
for downsample_block in self.down_blocks:
|
| 1220 |
+
if hasattr(downsample_block, "has_cross_attention") and downsample_block.has_cross_attention:
|
| 1221 |
+
# For t2i-adapter CrossAttnDownBlock2D
|
| 1222 |
+
additional_residuals = {}
|
| 1223 |
+
if is_adapter and len(down_intrablock_additional_residuals) > 0:
|
| 1224 |
+
additional_residuals["additional_residuals"] = down_intrablock_additional_residuals.pop(0)
|
| 1225 |
+
|
| 1226 |
+
sample, res_samples = downsample_block(
|
| 1227 |
+
hidden_states=sample,
|
| 1228 |
+
temb=emb,
|
| 1229 |
+
encoder_hidden_states=encoder_hidden_states,
|
| 1230 |
+
attention_mask=attention_mask,
|
| 1231 |
+
cross_attention_kwargs=cross_attention_kwargs,
|
| 1232 |
+
encoder_attention_mask=encoder_attention_mask,
|
| 1233 |
+
**additional_residuals,
|
| 1234 |
+
)
|
| 1235 |
+
else:
|
| 1236 |
+
sample, res_samples = downsample_block(hidden_states=sample, temb=emb)
|
| 1237 |
+
if is_adapter and len(down_intrablock_additional_residuals) > 0:
|
| 1238 |
+
sample += down_intrablock_additional_residuals.pop(0)
|
| 1239 |
+
|
| 1240 |
+
down_block_res_samples += res_samples
|
| 1241 |
+
|
| 1242 |
+
if is_controlnet:
|
| 1243 |
+
new_down_block_res_samples = ()
|
| 1244 |
+
|
| 1245 |
+
for down_block_res_sample, down_block_additional_residual in zip(
|
| 1246 |
+
down_block_res_samples, down_block_additional_residuals
|
| 1247 |
+
):
|
| 1248 |
+
down_block_res_sample = down_block_res_sample + down_block_additional_residual
|
| 1249 |
+
new_down_block_res_samples = new_down_block_res_samples + (down_block_res_sample,)
|
| 1250 |
+
|
| 1251 |
+
down_block_res_samples = new_down_block_res_samples
|
| 1252 |
+
# torch.cuda.synchronize()
|
| 1253 |
+
# logger.info(f"unet down time: {time.time() - start_time}")
|
| 1254 |
+
# torch.cuda.synchronize()
|
| 1255 |
+
# start_time = time.time()
|
| 1256 |
+
# 4. mid
|
| 1257 |
+
if self.mid_block is not None:
|
| 1258 |
+
if hasattr(self.mid_block, "has_cross_attention") and self.mid_block.has_cross_attention:
|
| 1259 |
+
sample = self.mid_block(
|
| 1260 |
+
sample,
|
| 1261 |
+
emb,
|
| 1262 |
+
encoder_hidden_states=encoder_hidden_states,
|
| 1263 |
+
attention_mask=attention_mask,
|
| 1264 |
+
cross_attention_kwargs=cross_attention_kwargs,
|
| 1265 |
+
encoder_attention_mask=encoder_attention_mask,
|
| 1266 |
+
)
|
| 1267 |
+
else:
|
| 1268 |
+
sample = self.mid_block(sample, emb)
|
| 1269 |
+
|
| 1270 |
+
# To support T2I-Adapter-XL
|
| 1271 |
+
if (
|
| 1272 |
+
is_adapter
|
| 1273 |
+
and len(down_intrablock_additional_residuals) > 0
|
| 1274 |
+
and sample.shape == down_intrablock_additional_residuals[0].shape
|
| 1275 |
+
):
|
| 1276 |
+
sample += down_intrablock_additional_residuals.pop(0)
|
| 1277 |
+
|
| 1278 |
+
if is_controlnet:
|
| 1279 |
+
sample = sample + mid_block_additional_residual
|
| 1280 |
+
# torch.cuda.synchronize()
|
| 1281 |
+
# logger.info(f"unet mid time: {time.time() - start_time}")
|
| 1282 |
+
mid_sample = sample
|
| 1283 |
+
|
| 1284 |
+
if use_up_blocks:
|
| 1285 |
+
# 5. up
|
| 1286 |
+
up_block_res_samples = ()
|
| 1287 |
+
for i, upsample_block in enumerate(self.up_blocks):
|
| 1288 |
+
is_final_block = i == len(self.up_blocks) - 1
|
| 1289 |
+
|
| 1290 |
+
res_samples = down_block_res_samples[-len(upsample_block.resnets) :]
|
| 1291 |
+
down_block_res_samples = down_block_res_samples[: -len(upsample_block.resnets)]
|
| 1292 |
+
|
| 1293 |
+
# if we have not reached the final block and need to forward the
|
| 1294 |
+
# upsample size, we do it here
|
| 1295 |
+
if not is_final_block and forward_upsample_size:
|
| 1296 |
+
upsample_size = down_block_res_samples[-1].shape[2:]
|
| 1297 |
+
|
| 1298 |
+
if hasattr(upsample_block, "has_cross_attention") and upsample_block.has_cross_attention:
|
| 1299 |
+
sample = upsample_block(
|
| 1300 |
+
hidden_states=sample,
|
| 1301 |
+
temb=emb,
|
| 1302 |
+
res_hidden_states_tuple=res_samples,
|
| 1303 |
+
encoder_hidden_states=encoder_hidden_states,
|
| 1304 |
+
cross_attention_kwargs=cross_attention_kwargs,
|
| 1305 |
+
upsample_size=upsample_size,
|
| 1306 |
+
attention_mask=attention_mask,
|
| 1307 |
+
encoder_attention_mask=encoder_attention_mask,
|
| 1308 |
+
)
|
| 1309 |
+
else:
|
| 1310 |
+
sample = upsample_block(
|
| 1311 |
+
hidden_states=sample,
|
| 1312 |
+
temb=emb,
|
| 1313 |
+
res_hidden_states_tuple=res_samples,
|
| 1314 |
+
upsample_size=upsample_size,
|
| 1315 |
+
)
|
| 1316 |
+
up_block_res_samples += (sample, )
|
| 1317 |
+
|
| 1318 |
+
# # 6. post-process
|
| 1319 |
+
# if self.conv_norm_out:
|
| 1320 |
+
# sample = self.conv_norm_out(sample)
|
| 1321 |
+
# sample = self.conv_act(sample)
|
| 1322 |
+
# sample = self.conv_out(sample)
|
| 1323 |
+
|
| 1324 |
+
if USE_PEFT_BACKEND:
|
| 1325 |
+
# remove `lora_scale` from each PEFT layer
|
| 1326 |
+
unscale_lora_layers(self, lora_scale)
|
| 1327 |
+
|
| 1328 |
+
if not return_dict:
|
| 1329 |
+
if use_up_blocks:
|
| 1330 |
+
return (mid_sample, down_block_res_samples, up_block_res_samples)
|
| 1331 |
+
else:
|
| 1332 |
+
return (mid_sample, down_block_res_samples)
|
| 1333 |
+
|
| 1334 |
+
return UNet2DConditionOutput(sample=sample)
|
Reward_sd15_idealized/pipelines/__init__.py
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from .sd15_reward_pipeline import StableDiffusionRewardPipeline
|
| 2 |
+
from .sd15_gradient_ascent_pipeline import StableDiffusionGradientAscentPipeline
|
| 3 |
+
|
| 4 |
+
__all__ = [
|
| 5 |
+
'StableDiffusionRewardPipeline',
|
| 6 |
+
'StableDiffusionGradientAscentPipeline',
|
| 7 |
+
'StableDiffusionXLRewardPipeline',
|
| 8 |
+
'StableDiffusionXLGradientAscentPipeline',
|
| 9 |
+
]
|
Reward_sd15_idealized/pipelines/__pycache__/__init__.cpython-310.pyc
ADDED
|
Binary file (457 Bytes). View file
|
|
|
Reward_sd15_idealized/pipelines/__pycache__/__init__.cpython-311.pyc
ADDED
|
Binary file (482 Bytes). View file
|
|
|
Reward_sd15_idealized/pipelines/__pycache__/__init__.cpython-313.pyc
ADDED
|
Binary file (470 Bytes). View file
|
|
|
Reward_sd15_idealized/pipelines/__pycache__/sd15_gradient_ascent_pipeline.cpython-310.pyc
ADDED
|
Binary file (10.1 kB). View file
|
|
|
Reward_sd15_idealized/pipelines/__pycache__/sd15_gradient_ascent_pipeline.cpython-311.pyc
ADDED
|
Binary file (15.8 kB). View file
|
|
|
Reward_sd15_idealized/pipelines/__pycache__/sd15_gradient_ascent_pipeline.cpython-313.pyc
ADDED
|
Binary file (14.8 kB). View file
|
|
|
Reward_sd15_idealized/pipelines/__pycache__/sd15_reward_pipeline.cpython-310.pyc
ADDED
|
Binary file (8.85 kB). View file
|
|
|
Reward_sd15_idealized/pipelines/__pycache__/sd15_reward_pipeline.cpython-311.pyc
ADDED
|
Binary file (14.9 kB). View file
|
|
|
Reward_sd15_idealized/pipelines/__pycache__/sd15_reward_pipeline.cpython-313.pyc
ADDED
|
Binary file (13.4 kB). View file
|
|
|
Reward_sd15_idealized/pipelines/__pycache__/sdxl_gradient_ascent_pipeline.cpython-310.pyc
ADDED
|
Binary file (11.6 kB). View file
|
|
|
Reward_sd15_idealized/pipelines/__pycache__/sdxl_reward_pipeline.cpython-310.pyc
ADDED
|
Binary file (11.6 kB). View file
|
|
|
Reward_sd15_idealized/pipelines/sd15_gradient_ascent_pipeline.py
ADDED
|
@@ -0,0 +1,345 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
Stable Diffusion Pipeline with Gradient Ascent Reward Guidance
|
| 3 |
+
|
| 4 |
+
This pipeline extends the reward-tracking pipeline to support gradient ascent
|
| 5 |
+
on the reward score during generation for improved preference alignment.
|
| 6 |
+
"""
|
| 7 |
+
|
| 8 |
+
import torch
|
| 9 |
+
from typing import Optional, Union, List, Callable, Tuple
|
| 10 |
+
from diffusers.pipelines.stable_diffusion import StableDiffusionPipelineOutput
|
| 11 |
+
from pipelines.sd15_reward_pipeline import StableDiffusionRewardPipeline
|
| 12 |
+
from gradient_ascent_utils import RewardGuidedDiffusion
|
| 13 |
+
|
| 14 |
+
|
| 15 |
+
class StableDiffusionGradientAscentPipeline(StableDiffusionRewardPipeline):
|
| 16 |
+
"""
|
| 17 |
+
SD Pipeline with gradient ascent reward guidance.
|
| 18 |
+
|
| 19 |
+
This pipeline performs gradient ascent on the LRM reward score at specified
|
| 20 |
+
timesteps to guide generation toward higher preference scores.
|
| 21 |
+
|
| 22 |
+
Usage:
|
| 23 |
+
from diffusers import StableDiffusionPipeline
|
| 24 |
+
base = StableDiffusionPipeline.from_pretrained("runwayml/stable-diffusion-v1-5")
|
| 25 |
+
pipeline = StableDiffusionGradientAscentPipeline(**base.components)
|
| 26 |
+
pipeline.set_reward_model(reward_model)
|
| 27 |
+
|
| 28 |
+
# Enable gradient ascent
|
| 29 |
+
pipeline.enable_gradient_ascent(
|
| 30 |
+
grad_timestep_range=(500, 700),
|
| 31 |
+
num_grad_steps=5,
|
| 32 |
+
grad_step_size=0.1
|
| 33 |
+
)
|
| 34 |
+
|
| 35 |
+
output = pipeline(
|
| 36 |
+
prompt="a beautiful landscape",
|
| 37 |
+
num_inference_steps=20,
|
| 38 |
+
)
|
| 39 |
+
"""
|
| 40 |
+
|
| 41 |
+
def __init__(
|
| 42 |
+
self,
|
| 43 |
+
vae,
|
| 44 |
+
text_encoder,
|
| 45 |
+
tokenizer,
|
| 46 |
+
unet,
|
| 47 |
+
scheduler,
|
| 48 |
+
safety_checker=None,
|
| 49 |
+
feature_extractor=None,
|
| 50 |
+
image_encoder=None,
|
| 51 |
+
requires_safety_checker: bool = False,
|
| 52 |
+
):
|
| 53 |
+
super().__init__(
|
| 54 |
+
vae=vae,
|
| 55 |
+
text_encoder=text_encoder,
|
| 56 |
+
tokenizer=tokenizer,
|
| 57 |
+
unet=unet,
|
| 58 |
+
scheduler=scheduler,
|
| 59 |
+
safety_checker=safety_checker,
|
| 60 |
+
feature_extractor=feature_extractor,
|
| 61 |
+
image_encoder=image_encoder,
|
| 62 |
+
requires_safety_checker=requires_safety_checker,
|
| 63 |
+
)
|
| 64 |
+
self.gradient_ascent_enabled = False
|
| 65 |
+
self.grad_guidance = None
|
| 66 |
+
|
| 67 |
+
def enable_gradient_ascent(
|
| 68 |
+
self,
|
| 69 |
+
grad_timestep_range: Tuple[int, int] = (500, 700),
|
| 70 |
+
grad_scale: float = 1.0,
|
| 71 |
+
num_grad_steps: int = 5,
|
| 72 |
+
grad_step_size: float = 0.1,
|
| 73 |
+
lr_scheduler_type: str = "constant",
|
| 74 |
+
lr_scheduler_kwargs: Optional[dict] = None,
|
| 75 |
+
use_momentum: bool = False,
|
| 76 |
+
momentum: float = 0.9,
|
| 77 |
+
use_nesterov: bool = False,
|
| 78 |
+
use_iso_projection: bool = False
|
| 79 |
+
):
|
| 80 |
+
"""
|
| 81 |
+
Enable gradient ascent reward guidance.
|
| 82 |
+
|
| 83 |
+
Args:
|
| 84 |
+
grad_timestep_range: Tuple of (min_t, max_t) for applying gradients
|
| 85 |
+
grad_scale: Scale factor for gradient magnitude
|
| 86 |
+
num_grad_steps: Number of gradient ascent iterations per timestep
|
| 87 |
+
grad_step_size: Step size for each gradient update (initial LR)
|
| 88 |
+
lr_scheduler_type: Type of LR scheduler ("constant", "linear", "cosine", "exponential", "step")
|
| 89 |
+
lr_scheduler_kwargs: Additional kwargs for LR scheduler
|
| 90 |
+
use_momentum: Whether to use momentum
|
| 91 |
+
momentum: Momentum coefficient (typically 0.9)
|
| 92 |
+
use_nesterov: Whether to use Nesterov momentum
|
| 93 |
+
use_iso_projection: Whether to use Iso Projection
|
| 94 |
+
"""
|
| 95 |
+
if self.reward_model is None:
|
| 96 |
+
raise ValueError("Reward model must be set before enabling gradient ascent. "
|
| 97 |
+
"Call set_reward_model() first.")
|
| 98 |
+
|
| 99 |
+
self.grad_guidance = RewardGuidedDiffusion(
|
| 100 |
+
reward_model=self.reward_model,
|
| 101 |
+
grad_scale=grad_scale,
|
| 102 |
+
grad_timestep_range=grad_timestep_range,
|
| 103 |
+
num_grad_steps=num_grad_steps,
|
| 104 |
+
grad_step_size=grad_step_size,
|
| 105 |
+
lr_scheduler_type=lr_scheduler_type,
|
| 106 |
+
lr_scheduler_kwargs=lr_scheduler_kwargs or {},
|
| 107 |
+
use_momentum=use_momentum,
|
| 108 |
+
momentum=momentum,
|
| 109 |
+
use_nesterov=use_nesterov,
|
| 110 |
+
use_iso_projection=use_iso_projection
|
| 111 |
+
)
|
| 112 |
+
self.gradient_ascent_enabled = True
|
| 113 |
+
print(f"✓ Gradient ascent enabled for timesteps {grad_timestep_range}")
|
| 114 |
+
print(f" - Grad steps: {num_grad_steps}")
|
| 115 |
+
print(f" - Grad step size (initial LR): {grad_step_size}")
|
| 116 |
+
print(f" - Grad scale: {grad_scale}")
|
| 117 |
+
print(f" - LR scheduler: {lr_scheduler_type}")
|
| 118 |
+
if use_momentum:
|
| 119 |
+
print(f" - Momentum: {momentum} (Nesterov: {use_nesterov})")
|
| 120 |
+
else:
|
| 121 |
+
print(f" - Momentum: disabled")
|
| 122 |
+
print(f" - Iso Projection: {use_iso_projection}")
|
| 123 |
+
|
| 124 |
+
def disable_gradient_ascent(self):
|
| 125 |
+
"""Disable gradient ascent."""
|
| 126 |
+
self.gradient_ascent_enabled = False
|
| 127 |
+
self.grad_guidance = None
|
| 128 |
+
print("✓ Gradient ascent disabled")
|
| 129 |
+
|
| 130 |
+
@torch.no_grad()
|
| 131 |
+
def __call__(
|
| 132 |
+
self,
|
| 133 |
+
prompt: Union[str, List[str]] = None,
|
| 134 |
+
height: Optional[int] = None,
|
| 135 |
+
width: Optional[int] = None,
|
| 136 |
+
num_inference_steps: int = 50,
|
| 137 |
+
guidance_scale: float = 7.5,
|
| 138 |
+
negative_prompt: Optional[Union[str, List[str]]] = None,
|
| 139 |
+
num_images_per_prompt: Optional[int] = 1,
|
| 140 |
+
eta: float = 0.0,
|
| 141 |
+
generator: Optional[Union[torch.Generator, List[torch.Generator]]] = None,
|
| 142 |
+
latents: Optional[torch.Tensor] = None,
|
| 143 |
+
track_rewards: bool = True,
|
| 144 |
+
print_rewards: bool = True,
|
| 145 |
+
apply_gradient_ascent: bool = True,
|
| 146 |
+
verbose_grad: bool = True,
|
| 147 |
+
output_type: Optional[str] = "pil",
|
| 148 |
+
return_dict: bool = True,
|
| 149 |
+
callback: Optional[Callable[[int, int, torch.Tensor], None]] = None,
|
| 150 |
+
callback_steps: int = 1,
|
| 151 |
+
**kwargs,
|
| 152 |
+
):
|
| 153 |
+
"""
|
| 154 |
+
Generate images with optional gradient ascent guidance.
|
| 155 |
+
|
| 156 |
+
Args:
|
| 157 |
+
prompt: Text prompt(s)
|
| 158 |
+
height: Image height
|
| 159 |
+
width: Image width
|
| 160 |
+
num_inference_steps: Number of denoising steps
|
| 161 |
+
guidance_scale: CFG scale
|
| 162 |
+
negative_prompt: Negative prompt(s)
|
| 163 |
+
num_images_per_prompt: Number of images per prompt
|
| 164 |
+
eta: DDIM eta parameter
|
| 165 |
+
generator: Random generator
|
| 166 |
+
latents: Initial latents
|
| 167 |
+
track_rewards: Whether to track reward scores
|
| 168 |
+
print_rewards: Whether to print rewards during generation
|
| 169 |
+
apply_gradient_ascent: Whether to apply gradient ascent (if enabled)
|
| 170 |
+
verbose_grad: Whether to print gradient ascent details
|
| 171 |
+
output_type: Output format
|
| 172 |
+
return_dict: Whether to return dict
|
| 173 |
+
callback: Callback function
|
| 174 |
+
callback_steps: Callback frequency
|
| 175 |
+
|
| 176 |
+
Returns:
|
| 177 |
+
StableDiffusionPipelineOutput with images and reward scores
|
| 178 |
+
"""
|
| 179 |
+
# Prepare inputs
|
| 180 |
+
height = height or self.unet.config.sample_size * self.vae_scale_factor
|
| 181 |
+
width = width or self.unet.config.sample_size * self.vae_scale_factor
|
| 182 |
+
|
| 183 |
+
# Encode prompt
|
| 184 |
+
batch_size = 1 if isinstance(prompt, str) else len(prompt)
|
| 185 |
+
prompt = [prompt] if isinstance(prompt, str) else prompt
|
| 186 |
+
|
| 187 |
+
text_embeddings = self._encode_prompt(
|
| 188 |
+
prompt,
|
| 189 |
+
self.device,
|
| 190 |
+
num_images_per_prompt,
|
| 191 |
+
guidance_scale > 1.0,
|
| 192 |
+
negative_prompt,
|
| 193 |
+
)
|
| 194 |
+
|
| 195 |
+
# Prepare timesteps
|
| 196 |
+
self.scheduler.set_timesteps(num_inference_steps, device=self.device)
|
| 197 |
+
timesteps = self.scheduler.timesteps
|
| 198 |
+
|
| 199 |
+
# Reset gradient ascent scheduler for new generation
|
| 200 |
+
if self.grad_guidance is not None:
|
| 201 |
+
self.grad_guidance.reset_statistics()
|
| 202 |
+
|
| 203 |
+
# Prepare latents
|
| 204 |
+
latents = self._prepare_latents(
|
| 205 |
+
batch_size * num_images_per_prompt,
|
| 206 |
+
self.unet.config.in_channels,
|
| 207 |
+
height,
|
| 208 |
+
width,
|
| 209 |
+
text_embeddings.dtype,
|
| 210 |
+
self.device,
|
| 211 |
+
generator,
|
| 212 |
+
latents,
|
| 213 |
+
)
|
| 214 |
+
|
| 215 |
+
# Prepare extra kwargs
|
| 216 |
+
extra_step_kwargs = self.prepare_extra_step_kwargs(generator, eta)
|
| 217 |
+
|
| 218 |
+
# Reset statistics
|
| 219 |
+
self.reward_history = []
|
| 220 |
+
if self.gradient_ascent_enabled and self.grad_guidance:
|
| 221 |
+
self.grad_guidance.reset_statistics()
|
| 222 |
+
|
| 223 |
+
# Print header
|
| 224 |
+
if print_rewards or verbose_grad:
|
| 225 |
+
print("\n" + "="*60)
|
| 226 |
+
print(f"Starting generation: '{prompt[0]}'")
|
| 227 |
+
if self.gradient_ascent_enabled and apply_gradient_ascent:
|
| 228 |
+
print(f"Gradient ascent: ENABLED")
|
| 229 |
+
print("="*60)
|
| 230 |
+
|
| 231 |
+
# Denoising loop
|
| 232 |
+
for i, t in enumerate(self.progress_bar(timesteps)):
|
| 233 |
+
# Apply gradient ascent BEFORE scheduler step
|
| 234 |
+
if (self.gradient_ascent_enabled and
|
| 235 |
+
apply_gradient_ascent and
|
| 236 |
+
self.grad_guidance and
|
| 237 |
+
self.grad_guidance.should_apply_gradient(t.item())):
|
| 238 |
+
|
| 239 |
+
if verbose_grad:
|
| 240 |
+
print(f"\n🎯 Applying gradient ascent at step {i}/{num_inference_steps}, t={t.item()}")
|
| 241 |
+
|
| 242 |
+
# Enable gradients temporarily
|
| 243 |
+
with torch.enable_grad():
|
| 244 |
+
latents, grad_stats = self.grad_guidance.apply_gradient_ascent(
|
| 245 |
+
latents,
|
| 246 |
+
prompt[0],
|
| 247 |
+
t.item(),
|
| 248 |
+
base_noise=None,
|
| 249 |
+
verbose=verbose_grad,
|
| 250 |
+
total_denoising_steps=num_inference_steps,
|
| 251 |
+
)
|
| 252 |
+
|
| 253 |
+
latent_model_input = torch.cat([latents] * 2) if guidance_scale > 1.0 else latents
|
| 254 |
+
latent_model_input = self.scheduler.scale_model_input(latent_model_input, t)
|
| 255 |
+
|
| 256 |
+
noise_pred = self.unet(
|
| 257 |
+
latent_model_input,
|
| 258 |
+
t,
|
| 259 |
+
encoder_hidden_states=text_embeddings,
|
| 260 |
+
).sample
|
| 261 |
+
|
| 262 |
+
# Perform CFG
|
| 263 |
+
if guidance_scale > 1.0:
|
| 264 |
+
noise_pred_uncond, noise_pred_text = noise_pred.chunk(2)
|
| 265 |
+
noise_pred = noise_pred_uncond + guidance_scale * (noise_pred_text - noise_pred_uncond)
|
| 266 |
+
|
| 267 |
+
# Compute reward score (after potential gradient update)
|
| 268 |
+
if track_rewards and self.reward_model is not None:
|
| 269 |
+
try:
|
| 270 |
+
with torch.no_grad():
|
| 271 |
+
timestep_value = t.item() if isinstance(t, torch.Tensor) else t
|
| 272 |
+
score = self.reward_model.get_reward_score(
|
| 273 |
+
latents,
|
| 274 |
+
prompt[0],
|
| 275 |
+
timestep_value
|
| 276 |
+
)
|
| 277 |
+
|
| 278 |
+
score_value = score.item() if score.numel() == 1 else score.mean().item()
|
| 279 |
+
self.reward_history.append({
|
| 280 |
+
'step': i,
|
| 281 |
+
'timestep': timestep_value,
|
| 282 |
+
'reward_score': score_value
|
| 283 |
+
})
|
| 284 |
+
|
| 285 |
+
if print_rewards:
|
| 286 |
+
print(f"Step {i:3d}/{num_inference_steps} | t={timestep_value:4d} | Reward: {score_value:7.4f}")
|
| 287 |
+
|
| 288 |
+
except Exception as e:
|
| 289 |
+
print(f"Warning: Could not compute reward at step {i}: {e}")
|
| 290 |
+
|
| 291 |
+
# Scheduler step
|
| 292 |
+
latents = self.scheduler.step(noise_pred, t, latents, **extra_step_kwargs).prev_sample
|
| 293 |
+
|
| 294 |
+
# Callback
|
| 295 |
+
if callback is not None and i % callback_steps == 0:
|
| 296 |
+
callback(i, t, latents)
|
| 297 |
+
|
| 298 |
+
# Print footer
|
| 299 |
+
if print_rewards or verbose_grad:
|
| 300 |
+
print("="*60)
|
| 301 |
+
if self.reward_history:
|
| 302 |
+
final_reward = self.reward_history[-1]['reward_score']
|
| 303 |
+
print(f"Generation complete! Final reward: {final_reward:.4f}")
|
| 304 |
+
|
| 305 |
+
if self.gradient_ascent_enabled and self.grad_guidance:
|
| 306 |
+
stats = self.grad_guidance.get_statistics()
|
| 307 |
+
if stats:
|
| 308 |
+
print(f"\nGradient Ascent Statistics:")
|
| 309 |
+
print(f" Applications: {stats['num_applications']}")
|
| 310 |
+
print(f" Total reward improvement: {stats['total_reward_improvement']:+.4f}")
|
| 311 |
+
print(f" Avg reward improvement: {stats['avg_reward_improvement']:+.4f}")
|
| 312 |
+
print(f" Avg gradient norm: {stats['avg_grad_norm']:.4f}")
|
| 313 |
+
print("="*60 + "\n")
|
| 314 |
+
|
| 315 |
+
# Decode latents
|
| 316 |
+
latents = 1 / self.vae.config.scaling_factor * latents
|
| 317 |
+
image = self.vae.decode(latents).sample
|
| 318 |
+
image = (image / 2 + 0.5).clamp(0, 1)
|
| 319 |
+
image = image.cpu().permute(0, 2, 3, 1).float().numpy()
|
| 320 |
+
|
| 321 |
+
# Convert to PIL
|
| 322 |
+
if output_type == "pil":
|
| 323 |
+
image = self.numpy_to_pil(image)
|
| 324 |
+
|
| 325 |
+
# Create output
|
| 326 |
+
if not return_dict:
|
| 327 |
+
return (image,)
|
| 328 |
+
|
| 329 |
+
return StableDiffusionPipelineOutput(
|
| 330 |
+
images=image,
|
| 331 |
+
nsfw_content_detected=None,
|
| 332 |
+
)
|
| 333 |
+
|
| 334 |
+
def _prepare_latents(self, batch_size, num_channels, height, width, dtype, device, generator, latents):
|
| 335 |
+
"""Prepare initial latents."""
|
| 336 |
+
shape = (batch_size, num_channels, height // self.vae_scale_factor, width // self.vae_scale_factor)
|
| 337 |
+
|
| 338 |
+
if latents is None:
|
| 339 |
+
latents = torch.randn(shape, generator=generator, device=device, dtype=dtype)
|
| 340 |
+
else:
|
| 341 |
+
latents = latents.to(device)
|
| 342 |
+
|
| 343 |
+
# Scale initial latents
|
| 344 |
+
latents = latents * self.scheduler.init_noise_sigma
|
| 345 |
+
return latents
|
Reward_sd15_idealized/pipelines/sd15_reward_pipeline.py
ADDED
|
@@ -0,0 +1,316 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
Modified Stable Diffusion Pipeline with Reward Tracking
|
| 3 |
+
|
| 4 |
+
This pipeline extends the standard SD pipeline to compute and display
|
| 5 |
+
reward scores at each denoising step using the LRM reward model.
|
| 6 |
+
"""
|
| 7 |
+
|
| 8 |
+
import torch
|
| 9 |
+
from typing import Optional, Union, List, Callable
|
| 10 |
+
from diffusers import StableDiffusionPipeline, DDIMScheduler
|
| 11 |
+
from diffusers.pipelines.stable_diffusion import StableDiffusionPipelineOutput
|
| 12 |
+
import numpy as np
|
| 13 |
+
from PIL import Image
|
| 14 |
+
|
| 15 |
+
|
| 16 |
+
class StableDiffusionRewardPipeline(StableDiffusionPipeline):
|
| 17 |
+
"""
|
| 18 |
+
Extended Stable Diffusion Pipeline with LRM Reward Tracking
|
| 19 |
+
|
| 20 |
+
This pipeline computes preference scores at each denoising timestep and optionally
|
| 21 |
+
displays them during generation.
|
| 22 |
+
|
| 23 |
+
Usage:
|
| 24 |
+
from diffusers import StableDiffusionPipeline
|
| 25 |
+
base = StableDiffusionPipeline.from_pretrained("runwayml/stable-diffusion-v1-5")
|
| 26 |
+
pipeline = StableDiffusionRewardPipeline(**base.components)
|
| 27 |
+
pipeline.set_reward_model(reward_model)
|
| 28 |
+
|
| 29 |
+
output = pipeline(
|
| 30 |
+
prompt="a beautiful landscape",
|
| 31 |
+
num_inference_steps=20,
|
| 32 |
+
track_rewards=True
|
| 33 |
+
)
|
| 34 |
+
"""
|
| 35 |
+
|
| 36 |
+
def __init__(
|
| 37 |
+
self,
|
| 38 |
+
vae,
|
| 39 |
+
text_encoder,
|
| 40 |
+
tokenizer,
|
| 41 |
+
unet,
|
| 42 |
+
scheduler,
|
| 43 |
+
safety_checker=None,
|
| 44 |
+
feature_extractor=None,
|
| 45 |
+
image_encoder=None,
|
| 46 |
+
requires_safety_checker: bool = False,
|
| 47 |
+
):
|
| 48 |
+
super().__init__(
|
| 49 |
+
vae=vae,
|
| 50 |
+
text_encoder=text_encoder,
|
| 51 |
+
tokenizer=tokenizer,
|
| 52 |
+
unet=unet,
|
| 53 |
+
scheduler=scheduler,
|
| 54 |
+
safety_checker=safety_checker,
|
| 55 |
+
feature_extractor=feature_extractor,
|
| 56 |
+
image_encoder=image_encoder,
|
| 57 |
+
requires_safety_checker=requires_safety_checker,
|
| 58 |
+
)
|
| 59 |
+
self.reward_model = None
|
| 60 |
+
self.reward_history = []
|
| 61 |
+
|
| 62 |
+
def set_reward_model(self, reward_model):
|
| 63 |
+
"""Set the LRM reward model for score tracking"""
|
| 64 |
+
self.reward_model = reward_model
|
| 65 |
+
self.reward_model.eval() # Set to eval mode
|
| 66 |
+
print("✓ Reward model attached to pipeline")
|
| 67 |
+
|
| 68 |
+
@torch.no_grad()
|
| 69 |
+
def __call__(
|
| 70 |
+
self,
|
| 71 |
+
prompt: Union[str, List[str]] = None,
|
| 72 |
+
height: Optional[int] = None,
|
| 73 |
+
width: Optional[int] = None,
|
| 74 |
+
num_inference_steps: int = 50,
|
| 75 |
+
guidance_scale: float = 7.5,
|
| 76 |
+
negative_prompt: Optional[Union[str, List[str]]] = None,
|
| 77 |
+
num_images_per_prompt: Optional[int] = 1,
|
| 78 |
+
eta: float = 0.0,
|
| 79 |
+
generator: Optional[Union[torch.Generator, List[torch.Generator]]] = None,
|
| 80 |
+
latents: Optional[torch.Tensor] = None,
|
| 81 |
+
track_rewards: bool = True,
|
| 82 |
+
print_rewards: bool = True,
|
| 83 |
+
output_type: Optional[str] = "pil",
|
| 84 |
+
return_dict: bool = True,
|
| 85 |
+
callback: Optional[Callable[[int, int, torch.Tensor], None]] = None,
|
| 86 |
+
callback_steps: int = 1,
|
| 87 |
+
**kwargs,
|
| 88 |
+
):
|
| 89 |
+
"""
|
| 90 |
+
Generate images with optional reward tracking
|
| 91 |
+
|
| 92 |
+
Args:
|
| 93 |
+
prompt: Text prompt(s) for generation
|
| 94 |
+
height: Image height (default: 512)
|
| 95 |
+
width: Image width (default: 512)
|
| 96 |
+
num_inference_steps: Number of denoising steps
|
| 97 |
+
guidance_scale: CFG scale
|
| 98 |
+
negative_prompt: Negative prompt(s)
|
| 99 |
+
num_images_per_prompt: Number of images per prompt
|
| 100 |
+
eta: DDIM eta parameter (0 = deterministic)
|
| 101 |
+
generator: Random generator for reproducibility
|
| 102 |
+
latents: Initial latent codes (optional)
|
| 103 |
+
track_rewards: Whether to compute reward scores
|
| 104 |
+
print_rewards: Whether to print rewards during generation
|
| 105 |
+
output_type: "pil", "np", or "latent"
|
| 106 |
+
return_dict: Whether to return a dict or tuple
|
| 107 |
+
callback: Custom callback function
|
| 108 |
+
callback_steps: Frequency of callback calls
|
| 109 |
+
|
| 110 |
+
Returns:
|
| 111 |
+
StableDiffusionPipelineOutput with images and optional reward history
|
| 112 |
+
"""
|
| 113 |
+
# Default height and width to unet config
|
| 114 |
+
height = height or self.unet.config.sample_size * self.vae_scale_factor
|
| 115 |
+
width = width or self.unet.config.sample_size * self.vae_scale_factor
|
| 116 |
+
|
| 117 |
+
# Reset reward history
|
| 118 |
+
self.reward_history = []
|
| 119 |
+
|
| 120 |
+
# 1. Check inputs
|
| 121 |
+
if isinstance(prompt, str):
|
| 122 |
+
batch_size = 1
|
| 123 |
+
prompt = [prompt]
|
| 124 |
+
elif isinstance(prompt, list):
|
| 125 |
+
batch_size = len(prompt)
|
| 126 |
+
else:
|
| 127 |
+
raise ValueError("Prompt must be a string or list of strings")
|
| 128 |
+
|
| 129 |
+
# 2. Encode input prompt
|
| 130 |
+
text_embeddings = self._encode_prompt(
|
| 131 |
+
prompt,
|
| 132 |
+
self.device,
|
| 133 |
+
num_images_per_prompt,
|
| 134 |
+
guidance_scale > 1.0,
|
| 135 |
+
negative_prompt,
|
| 136 |
+
)
|
| 137 |
+
|
| 138 |
+
# 3. Prepare timesteps
|
| 139 |
+
self.scheduler.set_timesteps(num_inference_steps, device=self.device)
|
| 140 |
+
timesteps = self.scheduler.timesteps
|
| 141 |
+
|
| 142 |
+
# 4. Prepare latent variables
|
| 143 |
+
num_channels_latents = self.unet.config.in_channels
|
| 144 |
+
latents = self.prepare_latents(
|
| 145 |
+
batch_size * num_images_per_prompt,
|
| 146 |
+
num_channels_latents,
|
| 147 |
+
height,
|
| 148 |
+
width,
|
| 149 |
+
text_embeddings.dtype,
|
| 150 |
+
self.device,
|
| 151 |
+
generator,
|
| 152 |
+
latents,
|
| 153 |
+
)
|
| 154 |
+
|
| 155 |
+
# 5. Prepare extra step kwargs
|
| 156 |
+
extra_step_kwargs = self.prepare_extra_step_kwargs(generator, eta)
|
| 157 |
+
|
| 158 |
+
# 6. Denoising loop
|
| 159 |
+
num_warmup_steps = len(timesteps) - num_inference_steps * self.scheduler.order
|
| 160 |
+
|
| 161 |
+
if print_rewards and track_rewards and self.reward_model is not None:
|
| 162 |
+
print("\n" + "="*60)
|
| 163 |
+
print(f"Starting generation: '{prompt[0]}'")
|
| 164 |
+
print("="*60)
|
| 165 |
+
|
| 166 |
+
with self.progress_bar(total=num_inference_steps) as progress_bar:
|
| 167 |
+
for i, t in enumerate(timesteps):
|
| 168 |
+
# Expand latents if doing classifier free guidance
|
| 169 |
+
latent_model_input = torch.cat([latents] * 2) if guidance_scale > 1.0 else latents
|
| 170 |
+
latent_model_input = self.scheduler.scale_model_input(latent_model_input, t)
|
| 171 |
+
|
| 172 |
+
# Predict noise residual
|
| 173 |
+
noise_pred = self.unet(
|
| 174 |
+
latent_model_input,
|
| 175 |
+
t,
|
| 176 |
+
encoder_hidden_states=text_embeddings,
|
| 177 |
+
).sample
|
| 178 |
+
|
| 179 |
+
# Perform guidance
|
| 180 |
+
if guidance_scale > 1.0:
|
| 181 |
+
noise_pred_uncond, noise_pred_text = noise_pred.chunk(2)
|
| 182 |
+
noise_pred = noise_pred_uncond + guidance_scale * (noise_pred_text - noise_pred_uncond)
|
| 183 |
+
|
| 184 |
+
# Compute reward score BEFORE stepping
|
| 185 |
+
if track_rewards and self.reward_model is not None:
|
| 186 |
+
try:
|
| 187 |
+
# Get reward score for current latents at timestep t
|
| 188 |
+
with torch.no_grad():
|
| 189 |
+
# Convert timestep to proper format
|
| 190 |
+
timestep_value = t.item() if isinstance(t, torch.Tensor) else t
|
| 191 |
+
score = self.reward_model.get_reward_score(
|
| 192 |
+
latents,
|
| 193 |
+
prompt[0], # Use first prompt
|
| 194 |
+
timestep_value
|
| 195 |
+
)
|
| 196 |
+
|
| 197 |
+
score_value = score.item() if score.numel() == 1 else score.mean().item()
|
| 198 |
+
self.reward_history.append({
|
| 199 |
+
'step': i,
|
| 200 |
+
'timestep': timestep_value,
|
| 201 |
+
'reward_score': score_value
|
| 202 |
+
})
|
| 203 |
+
|
| 204 |
+
if print_rewards:
|
| 205 |
+
print(f"Step {i:3d}/{num_inference_steps} | t={t:4d} | Reward: {score_value:7.4f}")
|
| 206 |
+
|
| 207 |
+
except Exception as e:
|
| 208 |
+
if print_rewards:
|
| 209 |
+
print(f"Warning: Could not compute reward at step {i}: {e}")
|
| 210 |
+
|
| 211 |
+
# Compute previous noisy sample x_t -> x_t-1
|
| 212 |
+
latents = self.scheduler.step(noise_pred, t, latents, **extra_step_kwargs).prev_sample
|
| 213 |
+
|
| 214 |
+
# Call callback
|
| 215 |
+
if callback is not None and i % callback_steps == 0:
|
| 216 |
+
callback(i, t, latents)
|
| 217 |
+
|
| 218 |
+
if i == len(timesteps) - 1 or ((i + 1) > num_warmup_steps and (i + 1) % self.scheduler.order == 0):
|
| 219 |
+
progress_bar.update()
|
| 220 |
+
|
| 221 |
+
if print_rewards and track_rewards and self.reward_model is not None:
|
| 222 |
+
print("="*60)
|
| 223 |
+
final_score = self.reward_history[-1]['reward_score'] if self.reward_history else 0.0
|
| 224 |
+
print(f"Generation complete! Final reward: {final_score:.4f}")
|
| 225 |
+
print("="*60 + "\n")
|
| 226 |
+
|
| 227 |
+
# 7. Decode latents
|
| 228 |
+
if output_type == "latent":
|
| 229 |
+
image = latents
|
| 230 |
+
has_nsfw_concept = None
|
| 231 |
+
elif output_type in ["pil", "np"]:
|
| 232 |
+
# Decode with VAE
|
| 233 |
+
latents = 1 / self.vae.config.scaling_factor * latents
|
| 234 |
+
image = self.vae.decode(latents).sample
|
| 235 |
+
|
| 236 |
+
# Convert to PIL
|
| 237 |
+
image = (image / 2 + 0.5).clamp(0, 1)
|
| 238 |
+
image = image.cpu().permute(0, 2, 3, 1).float().numpy()
|
| 239 |
+
|
| 240 |
+
if output_type == "pil":
|
| 241 |
+
image = self.numpy_to_pil(image)
|
| 242 |
+
|
| 243 |
+
# Run safety checker
|
| 244 |
+
has_nsfw_concept = None
|
| 245 |
+
if hasattr(self, 'safety_checker') and self.safety_checker is not None:
|
| 246 |
+
safety_checker_input = self.feature_extractor(
|
| 247 |
+
self.numpy_to_pil(image) if isinstance(image, np.ndarray) else image,
|
| 248 |
+
return_tensors="pt"
|
| 249 |
+
).to(self.device)
|
| 250 |
+
image, has_nsfw_concept = self.safety_checker(
|
| 251 |
+
images=image,
|
| 252 |
+
clip_input=safety_checker_input.pixel_values.to(text_embeddings.dtype)
|
| 253 |
+
)
|
| 254 |
+
|
| 255 |
+
# 8. Return output
|
| 256 |
+
if not return_dict:
|
| 257 |
+
return (image, has_nsfw_concept)
|
| 258 |
+
|
| 259 |
+
output = StableDiffusionPipelineOutput(
|
| 260 |
+
images=image,
|
| 261 |
+
nsfw_content_detected=has_nsfw_concept
|
| 262 |
+
)
|
| 263 |
+
|
| 264 |
+
# Attach reward history to output
|
| 265 |
+
if track_rewards:
|
| 266 |
+
output.reward_history = self.reward_history
|
| 267 |
+
|
| 268 |
+
return output
|
| 269 |
+
|
| 270 |
+
def get_reward_history(self):
|
| 271 |
+
"""Get the reward history from the last generation"""
|
| 272 |
+
return self.reward_history
|
| 273 |
+
|
| 274 |
+
def plot_reward_curve(self, save_path=None):
|
| 275 |
+
"""
|
| 276 |
+
Plot the reward curve from the last generation
|
| 277 |
+
|
| 278 |
+
Args:
|
| 279 |
+
save_path: Optional path to save the plot
|
| 280 |
+
"""
|
| 281 |
+
if not self.reward_history:
|
| 282 |
+
print("No reward history available. Run generation with track_rewards=True first.")
|
| 283 |
+
return
|
| 284 |
+
|
| 285 |
+
import matplotlib.pyplot as plt
|
| 286 |
+
|
| 287 |
+
steps = [h['step'] for h in self.reward_history]
|
| 288 |
+
timesteps = [h['timestep'] for h in self.reward_history]
|
| 289 |
+
scores = [h['reward_score'] for h in self.reward_history]
|
| 290 |
+
|
| 291 |
+
fig, (ax1, ax2) = plt.subplots(2, 1, figsize=(10, 8))
|
| 292 |
+
|
| 293 |
+
# Plot reward vs step
|
| 294 |
+
ax1.plot(steps, scores, marker='o', linewidth=2, markersize=4)
|
| 295 |
+
ax1.set_xlabel('Denoising Step')
|
| 296 |
+
ax1.set_ylabel('Reward Score')
|
| 297 |
+
ax1.set_title('Reward Score vs Denoising Step')
|
| 298 |
+
ax1.grid(True, alpha=0.3)
|
| 299 |
+
|
| 300 |
+
# Plot reward vs timestep
|
| 301 |
+
ax2.plot(timesteps, scores, marker='o', linewidth=2, markersize=4, color='orange')
|
| 302 |
+
ax2.set_xlabel('Timestep')
|
| 303 |
+
ax2.set_ylabel('Reward Score')
|
| 304 |
+
ax2.set_title('Reward Score vs Timestep')
|
| 305 |
+
ax2.grid(True, alpha=0.3)
|
| 306 |
+
ax2.invert_xaxis() # Higher timesteps first (left to right: noise -> clean)
|
| 307 |
+
|
| 308 |
+
plt.tight_layout()
|
| 309 |
+
|
| 310 |
+
if save_path:
|
| 311 |
+
plt.savefig(save_path, dpi=150, bbox_inches='tight')
|
| 312 |
+
print(f"Plot saved to {save_path}")
|
| 313 |
+
|
| 314 |
+
plt.show()
|
| 315 |
+
|
| 316 |
+
return fig
|
Reward_sd15_idealized/tune_hyperparams.py
ADDED
|
@@ -0,0 +1,514 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
Hyperparameter tuning script for gradient ascent optimization.
|
| 3 |
+
|
| 4 |
+
This script performs a systematic search over hyperparameter combinations
|
| 5 |
+
to find the optimal configuration for maximum evaluation scores.
|
| 6 |
+
"""
|
| 7 |
+
|
| 8 |
+
import subprocess
|
| 9 |
+
import json
|
| 10 |
+
import argparse
|
| 11 |
+
from pathlib import Path
|
| 12 |
+
from datetime import datetime
|
| 13 |
+
import itertools
|
| 14 |
+
import numpy as np
|
| 15 |
+
from typing import Dict, List, Any
|
| 16 |
+
import re
|
| 17 |
+
|
| 18 |
+
|
| 19 |
+
class HyperparameterTuner:
|
| 20 |
+
"""Hyperparameter tuner for gradient ascent."""
|
| 21 |
+
|
| 22 |
+
def __init__(
|
| 23 |
+
self,
|
| 24 |
+
output_dir: str = "tuning_results",
|
| 25 |
+
max_samples: int = 30,
|
| 26 |
+
num_steps: int = 20,
|
| 27 |
+
dataset_type: str = "pickapic",
|
| 28 |
+
model_variant: str = "lpo",
|
| 29 |
+
cuda_id: int = 0,
|
| 30 |
+
metrics: List[str] = None
|
| 31 |
+
):
|
| 32 |
+
self.output_dir = Path(output_dir)
|
| 33 |
+
self.output_dir.mkdir(parents=True, exist_ok=True)
|
| 34 |
+
|
| 35 |
+
self.max_samples = max_samples
|
| 36 |
+
self.num_steps = num_steps
|
| 37 |
+
self.dataset_type = dataset_type
|
| 38 |
+
self.model_variant = model_variant
|
| 39 |
+
self.cuda_id = cuda_id
|
| 40 |
+
self.metrics = metrics or ["clip", "aesthetic", "pickscore", "hpsv2", "imagereward"]
|
| 41 |
+
|
| 42 |
+
# Store results
|
| 43 |
+
self.results = []
|
| 44 |
+
self.baseline_results = None
|
| 45 |
+
|
| 46 |
+
def define_search_space(self) -> List[Dict[str, Any]]:
|
| 47 |
+
"""Define the hyperparameter search space - FULL GRID SEARCH.
|
| 48 |
+
|
| 49 |
+
Tests all combinations of parameters including momentum overrides for configs that support it.
|
| 50 |
+
"""
|
| 51 |
+
|
| 52 |
+
# Define all parameter values
|
| 53 |
+
cfg_scales = [3.0, 5.0, 7.5] #
|
| 54 |
+
|
| 55 |
+
# All available gradient configs from grad_ascent_configs.py
|
| 56 |
+
grad_configs = [
|
| 57 |
+
# "constant",
|
| 58 |
+
# "linear",
|
| 59 |
+
"cosine_nesterov",
|
| 60 |
+
# "low_to_high_nesterov",
|
| 61 |
+
# "high_to_low_nesterov",
|
| 62 |
+
"low_to_high_momentum",
|
| 63 |
+
"high_to_low_momentum",
|
| 64 |
+
]
|
| 65 |
+
|
| 66 |
+
num_grad_steps_list = [1, 2] # 5, 7, 10
|
| 67 |
+
grad_step_sizes = [0.001, 0.005, 0.01, 0.05] #
|
| 68 |
+
momentums = [0.5, 0.8, 0.9] #
|
| 69 |
+
|
| 70 |
+
# Generate ALL combinations using itertools.product
|
| 71 |
+
configs = []
|
| 72 |
+
for cfg, grad_cfg, num_steps, step_size, momentum in itertools.product(
|
| 73 |
+
cfg_scales, grad_configs, num_grad_steps_list, grad_step_sizes, momentums
|
| 74 |
+
):
|
| 75 |
+
configs.append({
|
| 76 |
+
"cfg_scale": cfg,
|
| 77 |
+
"grad_config": grad_cfg,
|
| 78 |
+
"num_grad_steps": num_steps,
|
| 79 |
+
"grad_step_size": step_size,
|
| 80 |
+
"momentum": momentum,
|
| 81 |
+
})
|
| 82 |
+
|
| 83 |
+
print(f"\nGenerated {len(configs)} total configurations")
|
| 84 |
+
print(f" cfg_scales: {len(cfg_scales)}")
|
| 85 |
+
print(f" grad_configs: {len(grad_configs)}")
|
| 86 |
+
print(f" num_grad_steps: {len(num_grad_steps_list)}")
|
| 87 |
+
print(f" grad_step_sizes: {len(grad_step_sizes)}")
|
| 88 |
+
print(f" momentums: {len(momentums)}")
|
| 89 |
+
print(f" Total: {len(cfg_scales)} × {len(grad_configs)} × {len(num_grad_steps_list)} × {len(grad_step_sizes)} × {len(momentums)} = {len(configs)}")
|
| 90 |
+
|
| 91 |
+
return configs
|
| 92 |
+
|
| 93 |
+
def run_baseline(self) -> Dict[str, float]:
|
| 94 |
+
"""Run baseline evaluation once."""
|
| 95 |
+
print("\n" + "="*80)
|
| 96 |
+
print("RUNNING BASELINE EVALUATION")
|
| 97 |
+
print("="*80)
|
| 98 |
+
|
| 99 |
+
# Use median cfg_scale for baseline
|
| 100 |
+
cfg_scale = 5.0
|
| 101 |
+
|
| 102 |
+
output_dir = self.output_dir / "baseline"
|
| 103 |
+
|
| 104 |
+
cmd = [
|
| 105 |
+
"python", "eval.py",
|
| 106 |
+
"--model_variant", self.model_variant,
|
| 107 |
+
"--dataset_type", self.dataset_type,
|
| 108 |
+
"--max_samples", str(self.max_samples),
|
| 109 |
+
"--num_steps", str(self.num_steps),
|
| 110 |
+
"--cfg_scale", str(cfg_scale),
|
| 111 |
+
"--output_dir", str(output_dir),
|
| 112 |
+
"--cuda", str(self.cuda_id),
|
| 113 |
+
"--mode", "baseline",
|
| 114 |
+
"--metrics", *self.metrics,
|
| 115 |
+
]
|
| 116 |
+
|
| 117 |
+
print(f"Command: {' '.join(cmd)}")
|
| 118 |
+
|
| 119 |
+
try:
|
| 120 |
+
result = subprocess.run(cmd, capture_output=True, text=True, check=True)
|
| 121 |
+
|
| 122 |
+
# Parse results from output
|
| 123 |
+
metrics = self._parse_metrics(result.stdout, "baseline")
|
| 124 |
+
|
| 125 |
+
print(f"\nBaseline Results:")
|
| 126 |
+
for metric, value in metrics.items():
|
| 127 |
+
print(f" {metric}: {value:.4f}")
|
| 128 |
+
|
| 129 |
+
self.baseline_results = {
|
| 130 |
+
"cfg_scale": cfg_scale,
|
| 131 |
+
"metrics": metrics,
|
| 132 |
+
}
|
| 133 |
+
|
| 134 |
+
return metrics
|
| 135 |
+
|
| 136 |
+
except subprocess.CalledProcessError as e:
|
| 137 |
+
print(f"Error running baseline: {e}")
|
| 138 |
+
print(f"Stdout: {e.stdout}")
|
| 139 |
+
print(f"Stderr: {e.stderr}")
|
| 140 |
+
return {}
|
| 141 |
+
|
| 142 |
+
def run_experiment(self, config: Dict[str, Any]) -> Dict[str, Any]:
|
| 143 |
+
"""Run a single experiment with given hyperparameters."""
|
| 144 |
+
|
| 145 |
+
# Create output directory for this config
|
| 146 |
+
config_name = f"cfg{config['cfg_scale']}_" \
|
| 147 |
+
f"{config['grad_config']}_" \
|
| 148 |
+
f"steps{config['num_grad_steps']}_" \
|
| 149 |
+
f"lr{config['grad_step_size']}_" \
|
| 150 |
+
f"mom{config['momentum']}"
|
| 151 |
+
|
| 152 |
+
output_dir = self.output_dir / config_name
|
| 153 |
+
|
| 154 |
+
# Build command
|
| 155 |
+
cmd = [
|
| 156 |
+
"python", "eval.py",
|
| 157 |
+
"--model_variant", self.model_variant,
|
| 158 |
+
"--dataset_type", self.dataset_type,
|
| 159 |
+
"--grad_config", config["grad_config"],
|
| 160 |
+
"--max_samples", str(self.max_samples),
|
| 161 |
+
"--num_steps", str(self.num_steps),
|
| 162 |
+
"--cfg_scale", str(config["cfg_scale"]),
|
| 163 |
+
"--output_dir", str(output_dir),
|
| 164 |
+
"--cuda", str(self.cuda_id),
|
| 165 |
+
"--mode", "gradient_ascent",
|
| 166 |
+
"--metrics", *self.metrics,
|
| 167 |
+
# Override config parameters
|
| 168 |
+
"--override_num_grad_steps", str(config["num_grad_steps"]),
|
| 169 |
+
"--override_grad_step_size", str(config["grad_step_size"]),
|
| 170 |
+
"--override_momentum", str(config["momentum"]),
|
| 171 |
+
]
|
| 172 |
+
|
| 173 |
+
print(f"\nRunning experiment: {config_name}")
|
| 174 |
+
print(f"Config: {config}")
|
| 175 |
+
|
| 176 |
+
try:
|
| 177 |
+
result = subprocess.run(cmd, capture_output=True, text=True, check=True)
|
| 178 |
+
|
| 179 |
+
# Parse metrics from output
|
| 180 |
+
metrics = self._parse_metrics(result.stdout, "gradient_ascent")
|
| 181 |
+
|
| 182 |
+
# Compute improvement over baseline
|
| 183 |
+
improvements = {}
|
| 184 |
+
if self.baseline_results:
|
| 185 |
+
baseline_metrics = self.baseline_results["metrics"]
|
| 186 |
+
for metric, value in metrics.items():
|
| 187 |
+
if metric in baseline_metrics:
|
| 188 |
+
baseline_val = baseline_metrics[metric]
|
| 189 |
+
if baseline_val != 0:
|
| 190 |
+
improvement = ((value - baseline_val) / abs(baseline_val)) * 100
|
| 191 |
+
improvements[f"{metric}_improvement"] = improvement
|
| 192 |
+
|
| 193 |
+
result_dict = {
|
| 194 |
+
"config": config,
|
| 195 |
+
"metrics": metrics,
|
| 196 |
+
"improvements": improvements,
|
| 197 |
+
"output_dir": str(output_dir),
|
| 198 |
+
"timestamp": datetime.now().isoformat(),
|
| 199 |
+
}
|
| 200 |
+
|
| 201 |
+
print(f"Results:")
|
| 202 |
+
for metric, value in metrics.items():
|
| 203 |
+
print(f" {metric}: {value:.4f}")
|
| 204 |
+
if improvements:
|
| 205 |
+
print(f"Improvements over baseline:")
|
| 206 |
+
for metric, value in improvements.items():
|
| 207 |
+
print(f" {metric}: {value:+.2f}%")
|
| 208 |
+
|
| 209 |
+
return result_dict
|
| 210 |
+
|
| 211 |
+
except subprocess.CalledProcessError as e:
|
| 212 |
+
print(f"Error running experiment: {e}")
|
| 213 |
+
print(f"Stderr: {e.stderr}")
|
| 214 |
+
return {
|
| 215 |
+
"config": config,
|
| 216 |
+
"error": str(e),
|
| 217 |
+
"timestamp": datetime.now().isoformat(),
|
| 218 |
+
}
|
| 219 |
+
|
| 220 |
+
def _parse_metrics(self, output: str, mode: str) -> Dict[str, float]:
|
| 221 |
+
"""Parse metrics from eval.py output."""
|
| 222 |
+
metrics = {}
|
| 223 |
+
|
| 224 |
+
# Look for the summary section
|
| 225 |
+
lines = output.split('\n')
|
| 226 |
+
|
| 227 |
+
# Pattern to match metric lines like " Reward: 0.1234"
|
| 228 |
+
metric_patterns = {
|
| 229 |
+
"reward": r"Reward:\s+([-+]?\d*\.?\d+)",
|
| 230 |
+
"clip": r"CLIP Score:\s+([-+]?\d*\.?\d+)",
|
| 231 |
+
"aesthetic": r"Aesthetic Score:\s+([-+]?\d*\.?\d+)",
|
| 232 |
+
"pickscore": r"PickScore:\s+([-+]?\d*\.?\d+)",
|
| 233 |
+
"hpsv2": r"HPSv2 Score:\s+([-+]?\d*\.?\d+)",
|
| 234 |
+
"hpsv21": r"HPSv2\.1 Score:\s+([-+]?\d*\.?\d+)",
|
| 235 |
+
"imagereward": r"ImageReward:\s+([-+]?\d*\.?\d+)",
|
| 236 |
+
"fid": r"FID:\s+([-+]?\d*\.?\d+)",
|
| 237 |
+
}
|
| 238 |
+
|
| 239 |
+
for line in lines:
|
| 240 |
+
for metric_name, pattern in metric_patterns.items():
|
| 241 |
+
match = re.search(pattern, line)
|
| 242 |
+
if match:
|
| 243 |
+
metrics[metric_name] = float(match.group(1))
|
| 244 |
+
|
| 245 |
+
return metrics
|
| 246 |
+
|
| 247 |
+
def compute_aggregate_score(self, metrics: Dict[str, float]) -> float:
|
| 248 |
+
"""
|
| 249 |
+
Compute aggregate score for ranking configurations.
|
| 250 |
+
|
| 251 |
+
Uses weighted combination of metrics (higher is better for most,
|
| 252 |
+
except FID which is lower is better).
|
| 253 |
+
"""
|
| 254 |
+
weights = {
|
| 255 |
+
"reward": 1.0,
|
| 256 |
+
"clip": 0.8,
|
| 257 |
+
"aesthetic": 0.8,
|
| 258 |
+
"pickscore": 1.0,
|
| 259 |
+
"hpsv2": 1.0,
|
| 260 |
+
"hpsv21": 1.0,
|
| 261 |
+
"imagereward": 1.0,
|
| 262 |
+
"fid": -0.5, # Negative weight (lower FID is better)
|
| 263 |
+
}
|
| 264 |
+
|
| 265 |
+
score = 0.0
|
| 266 |
+
total_weight = 0.0
|
| 267 |
+
|
| 268 |
+
for metric, value in metrics.items():
|
| 269 |
+
if metric in weights:
|
| 270 |
+
score += weights[metric] * value
|
| 271 |
+
total_weight += abs(weights[metric])
|
| 272 |
+
|
| 273 |
+
# Normalize by total weight
|
| 274 |
+
if total_weight > 0:
|
| 275 |
+
score /= total_weight
|
| 276 |
+
|
| 277 |
+
return score
|
| 278 |
+
|
| 279 |
+
def run_search(
|
| 280 |
+
self,
|
| 281 |
+
search_type: str = "grid",
|
| 282 |
+
start_idx: int = 0,
|
| 283 |
+
end_idx: int = None
|
| 284 |
+
) -> List[Dict[str, Any]]:
|
| 285 |
+
"""
|
| 286 |
+
Run hyperparameter search.
|
| 287 |
+
|
| 288 |
+
Args:
|
| 289 |
+
search_type: Type of search ("grid" or "random")
|
| 290 |
+
start_idx: Starting index for experiments (for GPU distribution)
|
| 291 |
+
end_idx: Ending index for experiments (for GPU distribution)
|
| 292 |
+
"""
|
| 293 |
+
all_configs = self.define_search_space()
|
| 294 |
+
|
| 295 |
+
print("\n" + "="*80)
|
| 296 |
+
print("HYPERPARAMETER SEARCH CONFIGURATION")
|
| 297 |
+
print("="*80)
|
| 298 |
+
print(f"Dataset: {self.dataset_type}")
|
| 299 |
+
print(f"Model: {self.model_variant}")
|
| 300 |
+
print(f"Samples: {self.max_samples}")
|
| 301 |
+
print(f"Inference steps: {self.num_steps}")
|
| 302 |
+
print(f"Metrics: {', '.join(self.metrics)}")
|
| 303 |
+
|
| 304 |
+
# Select subset of configs if indices provided
|
| 305 |
+
if search_type == "grid":
|
| 306 |
+
configs = all_configs
|
| 307 |
+
elif search_type == "random":
|
| 308 |
+
# Random sample from all configs
|
| 309 |
+
n_samples = min(50, len(all_configs))
|
| 310 |
+
indices = np.random.choice(len(all_configs), n_samples, replace=False)
|
| 311 |
+
configs = [all_configs[i] for i in indices]
|
| 312 |
+
else:
|
| 313 |
+
raise ValueError(f"Unknown search type: {search_type}")
|
| 314 |
+
|
| 315 |
+
# Apply index slicing for GPU distribution
|
| 316 |
+
if end_idx is None:
|
| 317 |
+
end_idx = len(configs)
|
| 318 |
+
configs = configs[start_idx:end_idx]
|
| 319 |
+
|
| 320 |
+
print(f"\nTotal configurations: {len(all_configs)}")
|
| 321 |
+
print(f"Assigned to this worker: {len(configs)} (indices {start_idx} to {end_idx})")
|
| 322 |
+
|
| 323 |
+
# Run baseline first
|
| 324 |
+
if self.baseline_results is None:
|
| 325 |
+
self.run_baseline()
|
| 326 |
+
|
| 327 |
+
# Run experiments
|
| 328 |
+
print("\n" + "="*80)
|
| 329 |
+
print("RUNNING EXPERIMENTS")
|
| 330 |
+
print("="*80)
|
| 331 |
+
|
| 332 |
+
for i, config in enumerate(configs, 1):
|
| 333 |
+
print(f"\n{'='*80}")
|
| 334 |
+
print(f"Experiment {i}/{len(configs)}")
|
| 335 |
+
print(f"{'='*80}")
|
| 336 |
+
|
| 337 |
+
result = self.run_experiment(config)
|
| 338 |
+
self.results.append(result)
|
| 339 |
+
|
| 340 |
+
# Save intermediate results
|
| 341 |
+
self._save_results()
|
| 342 |
+
|
| 343 |
+
return self.results
|
| 344 |
+
|
| 345 |
+
def _generate_grid_configs(self, search_space: Dict[str, List[Any]]) -> List[Dict[str, Any]]:
|
| 346 |
+
"""Generate all combinations for grid search."""
|
| 347 |
+
keys = list(search_space.keys())
|
| 348 |
+
values = list(search_space.values())
|
| 349 |
+
|
| 350 |
+
configs = []
|
| 351 |
+
for combination in itertools.product(*values):
|
| 352 |
+
config = dict(zip(keys, combination))
|
| 353 |
+
configs.append(config)
|
| 354 |
+
|
| 355 |
+
return configs
|
| 356 |
+
|
| 357 |
+
def _generate_random_configs(
|
| 358 |
+
self,
|
| 359 |
+
search_space: Dict[str, List[Any]],
|
| 360 |
+
n_samples: int = 20
|
| 361 |
+
) -> List[Dict[str, Any]]:
|
| 362 |
+
"""Generate random configurations for random search."""
|
| 363 |
+
configs = []
|
| 364 |
+
|
| 365 |
+
for _ in range(n_samples):
|
| 366 |
+
config = {}
|
| 367 |
+
for param, values in search_space.items():
|
| 368 |
+
config[param] = np.random.choice(values)
|
| 369 |
+
configs.append(config)
|
| 370 |
+
|
| 371 |
+
return configs
|
| 372 |
+
|
| 373 |
+
def _save_results(self):
|
| 374 |
+
"""Save results to JSON file."""
|
| 375 |
+
results_file = self.output_dir / "tuning_results.json"
|
| 376 |
+
|
| 377 |
+
data = {
|
| 378 |
+
"baseline": self.baseline_results,
|
| 379 |
+
"experiments": self.results,
|
| 380 |
+
"timestamp": datetime.now().isoformat(),
|
| 381 |
+
"config": {
|
| 382 |
+
"max_samples": self.max_samples,
|
| 383 |
+
"num_steps": self.num_steps,
|
| 384 |
+
"dataset_type": self.dataset_type,
|
| 385 |
+
"model_variant": self.model_variant,
|
| 386 |
+
}
|
| 387 |
+
}
|
| 388 |
+
|
| 389 |
+
with open(results_file, 'w') as f:
|
| 390 |
+
json.dump(data, f, indent=2)
|
| 391 |
+
|
| 392 |
+
print(f"\nResults saved to: {results_file}")
|
| 393 |
+
|
| 394 |
+
def analyze_results(self) -> Dict[str, Any]:
|
| 395 |
+
"""Analyze results and find best configuration."""
|
| 396 |
+
if not self.results:
|
| 397 |
+
print("No results to analyze!")
|
| 398 |
+
return {}
|
| 399 |
+
|
| 400 |
+
print("\n" + "="*80)
|
| 401 |
+
print("ANALYSIS: FINDING BEST CONFIGURATION")
|
| 402 |
+
print("="*80)
|
| 403 |
+
|
| 404 |
+
# Filter out failed experiments
|
| 405 |
+
successful_results = [r for r in self.results if "metrics" in r]
|
| 406 |
+
|
| 407 |
+
if not successful_results:
|
| 408 |
+
print("No successful experiments!")
|
| 409 |
+
return {}
|
| 410 |
+
|
| 411 |
+
# Compute aggregate scores
|
| 412 |
+
for result in successful_results:
|
| 413 |
+
metrics = result["metrics"]
|
| 414 |
+
result["aggregate_score"] = self.compute_aggregate_score(metrics)
|
| 415 |
+
|
| 416 |
+
# Sort by aggregate score
|
| 417 |
+
successful_results.sort(key=lambda x: x["aggregate_score"], reverse=True)
|
| 418 |
+
|
| 419 |
+
# Print top 5 configurations
|
| 420 |
+
print("\nTop 5 Configurations:")
|
| 421 |
+
print("="*80)
|
| 422 |
+
|
| 423 |
+
for i, result in enumerate(successful_results[:5], 1):
|
| 424 |
+
print(f"\n#{i} - Aggregate Score: {result['aggregate_score']:.4f}")
|
| 425 |
+
print(f"Config: {result['config']}")
|
| 426 |
+
print(f"Metrics:")
|
| 427 |
+
for metric, value in result['metrics'].items():
|
| 428 |
+
print(f" {metric}: {value:.4f}")
|
| 429 |
+
if result.get('improvements'):
|
| 430 |
+
print(f"Improvements over baseline:")
|
| 431 |
+
for metric, value in result['improvements'].items():
|
| 432 |
+
print(f" {metric}: {value:+.2f}%")
|
| 433 |
+
|
| 434 |
+
# Save best config
|
| 435 |
+
best_result = successful_results[0]
|
| 436 |
+
best_config_file = self.output_dir / "best_config.json"
|
| 437 |
+
|
| 438 |
+
with open(best_config_file, 'w') as f:
|
| 439 |
+
json.dump({
|
| 440 |
+
"config": best_result["config"],
|
| 441 |
+
"metrics": best_result["metrics"],
|
| 442 |
+
"aggregate_score": best_result["aggregate_score"],
|
| 443 |
+
"improvements": best_result.get("improvements", {}),
|
| 444 |
+
}, f, indent=2)
|
| 445 |
+
|
| 446 |
+
print(f"\n✓ Best configuration saved to: {best_config_file}")
|
| 447 |
+
|
| 448 |
+
return best_result
|
| 449 |
+
|
| 450 |
+
|
| 451 |
+
def main():
|
| 452 |
+
parser = argparse.ArgumentParser(description="Hyperparameter tuning for gradient ascent")
|
| 453 |
+
parser.add_argument("--output_dir", type=str, default="tuning_results",
|
| 454 |
+
help="Directory to save tuning results")
|
| 455 |
+
parser.add_argument("--max_samples", type=int, default=30,
|
| 456 |
+
help="Number of samples to use for tuning")
|
| 457 |
+
parser.add_argument("--num_steps", type=int, default=20,
|
| 458 |
+
help="Number of inference steps (fixed)")
|
| 459 |
+
parser.add_argument("--dataset_type", type=str, default="pickapic",
|
| 460 |
+
choices=["coco", "pickapic"],
|
| 461 |
+
help="Dataset to use")
|
| 462 |
+
parser.add_argument("--model_variant", type=str, default="lpo",
|
| 463 |
+
choices=["origin", "spo", "diffusion_dpo", "lpo"],
|
| 464 |
+
help="Model variant to use")
|
| 465 |
+
parser.add_argument("--cuda", type=int, default=0,
|
| 466 |
+
help="CUDA device ID")
|
| 467 |
+
parser.add_argument("--search_type", type=str, default="grid",
|
| 468 |
+
choices=["grid", "random"],
|
| 469 |
+
help="Type of hyperparameter search")
|
| 470 |
+
parser.add_argument("--metrics", type=str, nargs="+",
|
| 471 |
+
default=["clip", "aesthetic", "pickscore", "hpsv2", "imagereward"],
|
| 472 |
+
help="Metrics to evaluate")
|
| 473 |
+
parser.add_argument("--start_idx", type=int, default=0,
|
| 474 |
+
help="Starting index for experiments (for GPU distribution)")
|
| 475 |
+
parser.add_argument("--end_idx", type=int, default=None,
|
| 476 |
+
help="Ending index for experiments (for GPU distribution)")
|
| 477 |
+
|
| 478 |
+
args = parser.parse_args()
|
| 479 |
+
|
| 480 |
+
# Create tuner
|
| 481 |
+
tuner = HyperparameterTuner(
|
| 482 |
+
output_dir=args.output_dir,
|
| 483 |
+
max_samples=args.max_samples,
|
| 484 |
+
num_steps=args.num_steps,
|
| 485 |
+
dataset_type=args.dataset_type,
|
| 486 |
+
model_variant=args.model_variant,
|
| 487 |
+
cuda_id=args.cuda,
|
| 488 |
+
metrics=args.metrics,
|
| 489 |
+
)
|
| 490 |
+
|
| 491 |
+
# Run search
|
| 492 |
+
results = tuner.run_search(
|
| 493 |
+
search_type=args.search_type,
|
| 494 |
+
start_idx=args.start_idx,
|
| 495 |
+
end_idx=args.end_idx
|
| 496 |
+
)
|
| 497 |
+
|
| 498 |
+
# Analyze results
|
| 499 |
+
best_result = tuner.analyze_results()
|
| 500 |
+
|
| 501 |
+
print("\n" + "="*80)
|
| 502 |
+
print("TUNING COMPLETE!")
|
| 503 |
+
print("="*80)
|
| 504 |
+
print(f"Total experiments: {len(results)}")
|
| 505 |
+
print(f"Results directory: {args.output_dir}")
|
| 506 |
+
|
| 507 |
+
if best_result:
|
| 508 |
+
print(f"\nBest configuration:")
|
| 509 |
+
print(json.dumps(best_result["config"], indent=2))
|
| 510 |
+
print(f"\nAggregate score: {best_result['aggregate_score']:.4f}")
|
| 511 |
+
|
| 512 |
+
|
| 513 |
+
if __name__ == "__main__":
|
| 514 |
+
main()
|
evaluation/LICENSE
ADDED
|
@@ -0,0 +1,201 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
Apache License
|
| 2 |
+
Version 2.0, January 2004
|
| 3 |
+
http://www.apache.org/licenses/
|
| 4 |
+
|
| 5 |
+
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
| 6 |
+
|
| 7 |
+
1. Definitions.
|
| 8 |
+
|
| 9 |
+
"License" shall mean the terms and conditions for use, reproduction,
|
| 10 |
+
and distribution as defined by Sections 1 through 9 of this document.
|
| 11 |
+
|
| 12 |
+
"Licensor" shall mean the copyright owner or entity authorized by
|
| 13 |
+
the copyright owner that is granting the License.
|
| 14 |
+
|
| 15 |
+
"Legal Entity" shall mean the union of the acting entity and all
|
| 16 |
+
other entities that control, are controlled by, or are under common
|
| 17 |
+
control with that entity. For the purposes of this definition,
|
| 18 |
+
"control" means (i) the power, direct or indirect, to cause the
|
| 19 |
+
direction or management of such entity, whether by contract or
|
| 20 |
+
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
| 21 |
+
outstanding shares, or (iii) beneficial ownership of such entity.
|
| 22 |
+
|
| 23 |
+
"You" (or "Your") shall mean an individual or Legal Entity
|
| 24 |
+
exercising permissions granted by this License.
|
| 25 |
+
|
| 26 |
+
"Source" form shall mean the preferred form for making modifications,
|
| 27 |
+
including but not limited to software source code, documentation
|
| 28 |
+
source, and configuration files.
|
| 29 |
+
|
| 30 |
+
"Object" form shall mean any form resulting from mechanical
|
| 31 |
+
transformation or translation of a Source form, including but
|
| 32 |
+
not limited to compiled object code, generated documentation,
|
| 33 |
+
and conversions to other media types.
|
| 34 |
+
|
| 35 |
+
"Work" shall mean the work of authorship, whether in Source or
|
| 36 |
+
Object form, made available under the License, as indicated by a
|
| 37 |
+
copyright notice that is included in or attached to the work
|
| 38 |
+
(an example is provided in the Appendix below).
|
| 39 |
+
|
| 40 |
+
"Derivative Works" shall mean any work, whether in Source or Object
|
| 41 |
+
form, that is based on (or derived from) the Work and for which the
|
| 42 |
+
editorial revisions, annotations, elaborations, or other modifications
|
| 43 |
+
represent, as a whole, an original work of authorship. For the purposes
|
| 44 |
+
of this License, Derivative Works shall not include works that remain
|
| 45 |
+
separable from, or merely link (or bind by name) to the interfaces of,
|
| 46 |
+
the Work and Derivative Works thereof.
|
| 47 |
+
|
| 48 |
+
"Contribution" shall mean any work of authorship, including
|
| 49 |
+
the original version of the Work and any modifications or additions
|
| 50 |
+
to that Work or Derivative Works thereof, that is intentionally
|
| 51 |
+
submitted to Licensor for inclusion in the Work by the copyright owner
|
| 52 |
+
or by an individual or Legal Entity authorized to submit on behalf of
|
| 53 |
+
the copyright owner. For the purposes of this definition, "submitted"
|
| 54 |
+
means any form of electronic, verbal, or written communication sent
|
| 55 |
+
to the Licensor or its representatives, including but not limited to
|
| 56 |
+
communication on electronic mailing lists, source code control systems,
|
| 57 |
+
and issue tracking systems that are managed by, or on behalf of, the
|
| 58 |
+
Licensor for the purpose of discussing and improving the Work, but
|
| 59 |
+
excluding communication that is conspicuously marked or otherwise
|
| 60 |
+
designated in writing by the copyright owner as "Not a Contribution."
|
| 61 |
+
|
| 62 |
+
"Contributor" shall mean Licensor and any individual or Legal Entity
|
| 63 |
+
on behalf of whom a Contribution has been received by Licensor and
|
| 64 |
+
subsequently incorporated within the Work.
|
| 65 |
+
|
| 66 |
+
2. Grant of Copyright License. Subject to the terms and conditions of
|
| 67 |
+
this License, each Contributor hereby grants to You a perpetual,
|
| 68 |
+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
| 69 |
+
copyright license to reproduce, prepare Derivative Works of,
|
| 70 |
+
publicly display, publicly perform, sublicense, and distribute the
|
| 71 |
+
Work and such Derivative Works in Source or Object form.
|
| 72 |
+
|
| 73 |
+
3. Grant of Patent License. Subject to the terms and conditions of
|
| 74 |
+
this License, each Contributor hereby grants to You a perpetual,
|
| 75 |
+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
| 76 |
+
(except as stated in this section) patent license to make, have made,
|
| 77 |
+
use, offer to sell, sell, import, and otherwise transfer the Work,
|
| 78 |
+
where such license applies only to those patent claims licensable
|
| 79 |
+
by such Contributor that are necessarily infringed by their
|
| 80 |
+
Contribution(s) alone or by combination of their Contribution(s)
|
| 81 |
+
with the Work to which such Contribution(s) was submitted. If You
|
| 82 |
+
institute patent litigation against any entity (including a
|
| 83 |
+
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
| 84 |
+
or a Contribution incorporated within the Work constitutes direct
|
| 85 |
+
or contributory patent infringement, then any patent licenses
|
| 86 |
+
granted to You under this License for that Work shall terminate
|
| 87 |
+
as of the date such litigation is filed.
|
| 88 |
+
|
| 89 |
+
4. Redistribution. You may reproduce and distribute copies of the
|
| 90 |
+
Work or Derivative Works thereof in any medium, with or without
|
| 91 |
+
modifications, and in Source or Object form, provided that You
|
| 92 |
+
meet the following conditions:
|
| 93 |
+
|
| 94 |
+
(a) You must give any other recipients of the Work or
|
| 95 |
+
Derivative Works a copy of this License; and
|
| 96 |
+
|
| 97 |
+
(b) You must cause any modified files to carry prominent notices
|
| 98 |
+
stating that You changed the files; and
|
| 99 |
+
|
| 100 |
+
(c) You must retain, in the Source form of any Derivative Works
|
| 101 |
+
that You distribute, all copyright, patent, trademark, and
|
| 102 |
+
attribution notices from the Source form of the Work,
|
| 103 |
+
excluding those notices that do not pertain to any part of
|
| 104 |
+
the Derivative Works; and
|
| 105 |
+
|
| 106 |
+
(d) If the Work includes a "NOTICE" text file as part of its
|
| 107 |
+
distribution, then any Derivative Works that You distribute must
|
| 108 |
+
include a readable copy of the attribution notices contained
|
| 109 |
+
within such NOTICE file, excluding those notices that do not
|
| 110 |
+
pertain to any part of the Derivative Works, in at least one
|
| 111 |
+
of the following places: within a NOTICE text file distributed
|
| 112 |
+
as part of the Derivative Works; within the Source form or
|
| 113 |
+
documentation, if provided along with the Derivative Works; or,
|
| 114 |
+
within a display generated by the Derivative Works, if and
|
| 115 |
+
wherever such third-party notices normally appear. The contents
|
| 116 |
+
of the NOTICE file are for informational purposes only and
|
| 117 |
+
do not modify the License. You may add Your own attribution
|
| 118 |
+
notices within Derivative Works that You distribute, alongside
|
| 119 |
+
or as an addendum to the NOTICE text from the Work, provided
|
| 120 |
+
that such additional attribution notices cannot be construed
|
| 121 |
+
as modifying the License.
|
| 122 |
+
|
| 123 |
+
You may add Your own copyright statement to Your modifications and
|
| 124 |
+
may provide additional or different license terms and conditions
|
| 125 |
+
for use, reproduction, or distribution of Your modifications, or
|
| 126 |
+
for any such Derivative Works as a whole, provided Your use,
|
| 127 |
+
reproduction, and distribution of the Work otherwise complies with
|
| 128 |
+
the conditions stated in this License.
|
| 129 |
+
|
| 130 |
+
5. Submission of Contributions. Unless You explicitly state otherwise,
|
| 131 |
+
any Contribution intentionally submitted for inclusion in the Work
|
| 132 |
+
by You to the Licensor shall be under the terms and conditions of
|
| 133 |
+
this License, without any additional terms or conditions.
|
| 134 |
+
Notwithstanding the above, nothing herein shall supersede or modify
|
| 135 |
+
the terms of any separate license agreement you may have executed
|
| 136 |
+
with Licensor regarding such Contributions.
|
| 137 |
+
|
| 138 |
+
6. Trademarks. This License does not grant permission to use the trade
|
| 139 |
+
names, trademarks, service marks, or product names of the Licensor,
|
| 140 |
+
except as required for reasonable and customary use in describing the
|
| 141 |
+
origin of the Work and reproducing the content of the NOTICE file.
|
| 142 |
+
|
| 143 |
+
7. Disclaimer of Warranty. Unless required by applicable law or
|
| 144 |
+
agreed to in writing, Licensor provides the Work (and each
|
| 145 |
+
Contributor provides its Contributions) on an "AS IS" BASIS,
|
| 146 |
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
| 147 |
+
implied, including, without limitation, any warranties or conditions
|
| 148 |
+
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
| 149 |
+
PARTICULAR PURPOSE. You are solely responsible for determining the
|
| 150 |
+
appropriateness of using or redistributing the Work and assume any
|
| 151 |
+
risks associated with Your exercise of permissions under this License.
|
| 152 |
+
|
| 153 |
+
8. Limitation of Liability. In no event and under no legal theory,
|
| 154 |
+
whether in tort (including negligence), contract, or otherwise,
|
| 155 |
+
unless required by applicable law (such as deliberate and grossly
|
| 156 |
+
negligent acts) or agreed to in writing, shall any Contributor be
|
| 157 |
+
liable to You for damages, including any direct, indirect, special,
|
| 158 |
+
incidental, or consequential damages of any character arising as a
|
| 159 |
+
result of this License or out of the use or inability to use the
|
| 160 |
+
Work (including but not limited to damages for loss of goodwill,
|
| 161 |
+
work stoppage, computer failure or malfunction, or any and all
|
| 162 |
+
other commercial damages or losses), even if such Contributor
|
| 163 |
+
has been advised of the possibility of such damages.
|
| 164 |
+
|
| 165 |
+
9. Accepting Warranty or Additional Liability. While redistributing
|
| 166 |
+
the Work or Derivative Works thereof, You may choose to offer,
|
| 167 |
+
and charge a fee for, acceptance of support, warranty, indemnity,
|
| 168 |
+
or other liability obligations and/or rights consistent with this
|
| 169 |
+
License. However, in accepting such obligations, You may act only
|
| 170 |
+
on Your own behalf and on Your sole responsibility, not on behalf
|
| 171 |
+
of any other Contributor, and only if You agree to indemnify,
|
| 172 |
+
defend, and hold each Contributor harmless for any liability
|
| 173 |
+
incurred by, or claims asserted against, such Contributor by reason
|
| 174 |
+
of your accepting any such warranty or additional liability.
|
| 175 |
+
|
| 176 |
+
END OF TERMS AND CONDITIONS
|
| 177 |
+
|
| 178 |
+
APPENDIX: How to apply the Apache License to your work.
|
| 179 |
+
|
| 180 |
+
To apply the Apache License to your work, attach the following
|
| 181 |
+
boilerplate notice, with the fields enclosed by brackets "[]"
|
| 182 |
+
replaced with your own identifying information. (Don't include
|
| 183 |
+
the brackets!) The text should be enclosed in the appropriate
|
| 184 |
+
comment syntax for the file format. We also recommend that a
|
| 185 |
+
file or class name and description of purpose be included on the
|
| 186 |
+
same "printed page" as the copyright notice for easier
|
| 187 |
+
identification within third-party archives.
|
| 188 |
+
|
| 189 |
+
Copyright [yyyy] [name of copyright owner]
|
| 190 |
+
|
| 191 |
+
Licensed under the Apache License, Version 2.0 (the "License");
|
| 192 |
+
you may not use this file except in compliance with the License.
|
| 193 |
+
You may obtain a copy of the License at
|
| 194 |
+
|
| 195 |
+
http://www.apache.org/licenses/LICENSE-2.0
|
| 196 |
+
|
| 197 |
+
Unless required by applicable law or agreed to in writing, software
|
| 198 |
+
distributed under the License is distributed on an "AS IS" BASIS,
|
| 199 |
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 200 |
+
See the License for the specific language governing permissions and
|
| 201 |
+
limitations under the License.
|
evaluation/aesthetic_score.py
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
Adapted from https://github.com/christophschuhmann/improved-aesthetic-predictor. Originally Apache License, Version 2.0, January 2004.
|
| 3 |
+
"""
|
| 4 |
+
|
| 5 |
+
import torch
|
| 6 |
+
import torch.nn as nn
|
| 7 |
+
import numpy as np
|
| 8 |
+
from transformers import CLIPModel, CLIPProcessor
|
| 9 |
+
from PIL import Image
|
| 10 |
+
from io import BytesIO
|
| 11 |
+
|
| 12 |
+
def open_image(image):
|
| 13 |
+
if isinstance(image, bytes):
|
| 14 |
+
image = Image.open(BytesIO(image))
|
| 15 |
+
elif isinstance(image, str):
|
| 16 |
+
image = Image.open(image)
|
| 17 |
+
image = image.convert("RGB")
|
| 18 |
+
return image
|
| 19 |
+
|
| 20 |
+
|
| 21 |
+
class MLP(nn.Module):
|
| 22 |
+
def __init__(self):
|
| 23 |
+
super().__init__()
|
| 24 |
+
self.layers = nn.Sequential(
|
| 25 |
+
nn.Linear(768, 1024),
|
| 26 |
+
nn.Dropout(0.2),
|
| 27 |
+
nn.Linear(1024, 128),
|
| 28 |
+
nn.Dropout(0.2),
|
| 29 |
+
nn.Linear(128, 64),
|
| 30 |
+
nn.Dropout(0.1),
|
| 31 |
+
nn.Linear(64, 16),
|
| 32 |
+
nn.Linear(16, 1),
|
| 33 |
+
)
|
| 34 |
+
|
| 35 |
+
@torch.no_grad()
|
| 36 |
+
def forward(self, embed):
|
| 37 |
+
return self.layers(embed)
|
| 38 |
+
|
| 39 |
+
|
| 40 |
+
class AestheticScorer(torch.nn.Module):
|
| 41 |
+
def __init__(self, dtype, clip_name_or_path, aesthetic_name_or_path):
|
| 42 |
+
super().__init__()
|
| 43 |
+
self.clip = CLIPModel.from_pretrained(clip_name_or_path)
|
| 44 |
+
self.processor = CLIPProcessor.from_pretrained(clip_name_or_path)
|
| 45 |
+
self.mlp = MLP()
|
| 46 |
+
state_dict = torch.load(aesthetic_name_or_path, map_location='cpu')
|
| 47 |
+
self.mlp.load_state_dict(state_dict)
|
| 48 |
+
self.dtype = dtype
|
| 49 |
+
self.eval()
|
| 50 |
+
|
| 51 |
+
@torch.no_grad()
|
| 52 |
+
def __call__(self, images):
|
| 53 |
+
device = next(self.parameters()).device
|
| 54 |
+
inputs = self.processor(images=images, return_tensors="pt")
|
| 55 |
+
inputs = {k: v.to(self.dtype).to(device) for k, v in inputs.items()}
|
| 56 |
+
embed = self.clip.get_image_features(**inputs)
|
| 57 |
+
# normalize embedding
|
| 58 |
+
embed = embed / torch.linalg.vector_norm(embed, dim=-1, keepdim=True)
|
| 59 |
+
return self.mlp(embed).squeeze(1)
|
| 60 |
+
|
| 61 |
+
|
| 62 |
+
if __name__ == "__main__":
|
| 63 |
+
clip_name_or_path = "openai/clip-vit-large-patch14"
|
| 64 |
+
aesthetic_name_or_path = "./sac+logos+ava1-l14-linearMSE.pth"
|
| 65 |
+
aesthetic_scorer = AestheticScorer(torch.float32, clip_name_or_path, aesthetic_name_or_path)
|
| 66 |
+
image0 = open_image('./image0.png')
|
| 67 |
+
image1 = open_image('./image1.png')
|
| 68 |
+
print(aesthetic_scorer(image0))
|
| 69 |
+
print(aesthetic_scorer(image1))
|
| 70 |
+
print(aesthetic_scorer([image0, image1]))
|
| 71 |
+
|
evaluation/blip/__init__.py
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
from .blip_pretrain import *
|
evaluation/general_score_pap_val.py
ADDED
|
@@ -0,0 +1,265 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from datasets import load_dataset
|
| 2 |
+
from pick_score import PickScorer
|
| 3 |
+
from aesthetic_score import AestheticScorer
|
| 4 |
+
from hpsv2_score import HPSv2Scorer
|
| 5 |
+
from imagereward_score import load_imagereward
|
| 6 |
+
from diffusers import AutoencoderKL, StableDiffusionPipeline, \
|
| 7 |
+
StableDiffusionXLPipeline, DDIMScheduler, \
|
| 8 |
+
UNet2DConditionModel
|
| 9 |
+
import torch
|
| 10 |
+
import os
|
| 11 |
+
import json
|
| 12 |
+
from tqdm import tqdm
|
| 13 |
+
from huggingface_hub import hf_hub_download
|
| 14 |
+
from argparse import ArgumentParser
|
| 15 |
+
|
| 16 |
+
|
| 17 |
+
def load_origin_sd_v1_5(scheduler, inference_dtype):
|
| 18 |
+
pipe = StableDiffusionPipeline.from_pretrained(
|
| 19 |
+
'stable-diffusion-v1-5/stable-diffusion-v1-5',
|
| 20 |
+
torch_dtype=inference_dtype,
|
| 21 |
+
scheduler=scheduler,
|
| 22 |
+
safety_checker=None,
|
| 23 |
+
)
|
| 24 |
+
guidance_scale = 7.5
|
| 25 |
+
return pipe, guidance_scale
|
| 26 |
+
|
| 27 |
+
|
| 28 |
+
def load_spo_sd_v1_5(scheduler, inference_dtype):
|
| 29 |
+
pipe = StableDiffusionPipeline.from_pretrained(
|
| 30 |
+
'SPO-Diffusion-Models/SPO-SD-v1-5_4k-p_10ep',
|
| 31 |
+
torch_dtype=inference_dtype,
|
| 32 |
+
scheduler=scheduler,
|
| 33 |
+
safety_checker=None,
|
| 34 |
+
)
|
| 35 |
+
guidance_scale = 5.0
|
| 36 |
+
return pipe, guidance_scale
|
| 37 |
+
|
| 38 |
+
|
| 39 |
+
def load_diffusion_dpo_sd_v1_5(scheduler, inference_dtype):
|
| 40 |
+
unet = UNet2DConditionModel.from_pretrained('mhdang/dpo-sd1.5-text2image-v1', subfolder="unet", torch_dtype=inference_dtype)
|
| 41 |
+
pipe = StableDiffusionPipeline.from_pretrained(
|
| 42 |
+
'stable-diffusion-v1-5/stable-diffusion-v1-5',
|
| 43 |
+
torch_dtype=inference_dtype,
|
| 44 |
+
scheduler=scheduler,
|
| 45 |
+
safety_checker=None,
|
| 46 |
+
unet=unet,
|
| 47 |
+
)
|
| 48 |
+
guidance_scale = 7.5
|
| 49 |
+
return pipe, guidance_scale
|
| 50 |
+
|
| 51 |
+
|
| 52 |
+
def load_lpo_sd_v1_5(scheduler, inference_dtype):
|
| 53 |
+
unet = UNet2DConditionModel.from_pretrained(
|
| 54 |
+
'casiatao/LPO',
|
| 55 |
+
subfolder="lpo_sd15_merge/unet",
|
| 56 |
+
torch_dtype=inference_dtype
|
| 57 |
+
)
|
| 58 |
+
pipe = StableDiffusionPipeline.from_pretrained(
|
| 59 |
+
'stable-diffusion-v1-5/stable-diffusion-v1-5',
|
| 60 |
+
torch_dtype=inference_dtype,
|
| 61 |
+
scheduler=scheduler,
|
| 62 |
+
safety_checker=None,
|
| 63 |
+
unet=unet
|
| 64 |
+
)
|
| 65 |
+
guidance_scale = 5.0
|
| 66 |
+
return pipe, guidance_scale
|
| 67 |
+
|
| 68 |
+
|
| 69 |
+
def load_origin_sdxl(scheduler, inference_dtype):
|
| 70 |
+
pipe = StableDiffusionXLPipeline.from_pretrained(
|
| 71 |
+
'stabilityai/stable-diffusion-xl-base-1.0',
|
| 72 |
+
torch_dtype=inference_dtype,
|
| 73 |
+
scheduler=scheduler,
|
| 74 |
+
)
|
| 75 |
+
vae = AutoencoderKL.from_pretrained(
|
| 76 |
+
'madebyollin/sdxl-vae-fp16-fix',
|
| 77 |
+
torch_dtype=torch.float16,
|
| 78 |
+
)
|
| 79 |
+
pipe.vae = vae
|
| 80 |
+
guidance_scale = 5.0
|
| 81 |
+
return pipe, guidance_scale
|
| 82 |
+
|
| 83 |
+
|
| 84 |
+
def load_spo_sdxl(scheduler, inference_dtype):
|
| 85 |
+
pipe = StableDiffusionXLPipeline.from_pretrained(
|
| 86 |
+
'SPO-Diffusion-Models/SPO-SDXL_4k-p_10ep',
|
| 87 |
+
torch_dtype=inference_dtype,
|
| 88 |
+
scheduler=scheduler,
|
| 89 |
+
)
|
| 90 |
+
vae = AutoencoderKL.from_pretrained(
|
| 91 |
+
'madebyollin/sdxl-vae-fp16-fix',
|
| 92 |
+
torch_dtype=torch.float16,
|
| 93 |
+
)
|
| 94 |
+
pipe.vae = vae
|
| 95 |
+
guidance_scale = 5.0
|
| 96 |
+
return pipe, guidance_scale
|
| 97 |
+
|
| 98 |
+
|
| 99 |
+
def load_diffusion_dpo_sdxl(scheduler, inference_dtype):
|
| 100 |
+
unet = UNet2DConditionModel.from_pretrained('mhdang/dpo-sdxl-text2image-v1', subfolder="unet", torch_dtype=inference_dtype)
|
| 101 |
+
pipe = StableDiffusionXLPipeline.from_pretrained(
|
| 102 |
+
'stabilityai/stable-diffusion-xl-base-1.0',
|
| 103 |
+
torch_dtype=inference_dtype,
|
| 104 |
+
scheduler=scheduler,
|
| 105 |
+
unet=unet,
|
| 106 |
+
)
|
| 107 |
+
vae = AutoencoderKL.from_pretrained(
|
| 108 |
+
'madebyollin/sdxl-vae-fp16-fix',
|
| 109 |
+
torch_dtype=torch.float16,
|
| 110 |
+
)
|
| 111 |
+
pipe.vae = vae
|
| 112 |
+
guidance_scale = 5.0
|
| 113 |
+
return pipe, guidance_scale
|
| 114 |
+
|
| 115 |
+
|
| 116 |
+
def load_lpo_sdxl(scheduler, inference_dtype):
|
| 117 |
+
unet = UNet2DConditionModel.from_pretrained(
|
| 118 |
+
'casiatao/LPO',
|
| 119 |
+
subfolder="lpo_sdxl_merge/unet",
|
| 120 |
+
torch_dtype=inference_dtype
|
| 121 |
+
)
|
| 122 |
+
vae = AutoencoderKL.from_pretrained(
|
| 123 |
+
'madebyollin/sdxl-vae-fp16-fix',
|
| 124 |
+
torch_dtype=torch.float16,
|
| 125 |
+
)
|
| 126 |
+
pipe = StableDiffusionXLPipeline.from_pretrained(
|
| 127 |
+
'stabilityai/stable-diffusion-xl-base-1.0',
|
| 128 |
+
torch_dtype=inference_dtype,
|
| 129 |
+
scheduler=scheduler,
|
| 130 |
+
unet=unet,
|
| 131 |
+
vae=vae
|
| 132 |
+
)
|
| 133 |
+
guidance_scale = 5.0
|
| 134 |
+
return pipe, guidance_scale
|
| 135 |
+
|
| 136 |
+
|
| 137 |
+
|
| 138 |
+
model_dict = {
|
| 139 |
+
'origin_sd15': load_origin_sd_v1_5,
|
| 140 |
+
'spo_sd15': load_spo_sd_v1_5,
|
| 141 |
+
'diffusion_dpo_sd15': load_diffusion_dpo_sd_v1_5,
|
| 142 |
+
'lpo_sd15': load_lpo_sd_v1_5,
|
| 143 |
+
'origin_sdxl': load_origin_sdxl,
|
| 144 |
+
'spo_sdxl': load_spo_sdxl,
|
| 145 |
+
'diffusion_dpo_sdxl': load_diffusion_dpo_sdxl,
|
| 146 |
+
'lpo_sdxl': load_lpo_sdxl,
|
| 147 |
+
}
|
| 148 |
+
|
| 149 |
+
|
| 150 |
+
if __name__ == "__main__":
|
| 151 |
+
# hyperparameter
|
| 152 |
+
parser = ArgumentParser()
|
| 153 |
+
parser.add_argument("--model_name", type=str, default="origin_sdxl")
|
| 154 |
+
parser.add_argument("--batch_size", type=int, default=1)
|
| 155 |
+
parser.add_argument("--num_image_per_prompt", type=int, default=4)
|
| 156 |
+
parser.add_argument("--sample_steps", type=int, default=20)
|
| 157 |
+
parser.add_argument("--seed", type=int, default=42)
|
| 158 |
+
parser.add_argument("--device", type=str, default="cuda")
|
| 159 |
+
args = parser.parse_args()
|
| 160 |
+
|
| 161 |
+
model_name = args.model_name
|
| 162 |
+
batch_size = args.batch_size
|
| 163 |
+
num_image_per_prompt = args.num_image_per_prompt
|
| 164 |
+
sample_steps = args.sample_steps
|
| 165 |
+
seed = args.seed
|
| 166 |
+
device = args.device
|
| 167 |
+
|
| 168 |
+
# load preference model
|
| 169 |
+
pickscorer = PickScorer(processor_name_or_path="laion/CLIP-ViT-H-14-laion2B-s32B-b79K", model_pretrained_name_or_path="yuvalkirstain/PickScore_v1", device=device)
|
| 170 |
+
|
| 171 |
+
aesthetic_scorer = AestheticScorer(torch.float32, "openai/clip-vit-large-patch14", "./sac+logos+ava1-l14-linearMSE.pth")
|
| 172 |
+
aesthetic_scorer = aesthetic_scorer.to(device)
|
| 173 |
+
|
| 174 |
+
|
| 175 |
+
hpsv2_scorer = HPSv2Scorer(
|
| 176 |
+
clip_pretrained_name_or_path=hf_hub_download(repo_id="laion/CLIP-ViT-H-14-laion2B-s32B-b79K", filename="open_clip_pytorch_model.bin"),
|
| 177 |
+
model_pretrained_name_or_path=hf_hub_download(repo_id="xswu/HPSv2", filename="HPS_v2_compressed.pt"),
|
| 178 |
+
device=device
|
| 179 |
+
)
|
| 180 |
+
|
| 181 |
+
hpsv21_scorer = HPSv2Scorer(
|
| 182 |
+
clip_pretrained_name_or_path=hf_hub_download(repo_id="laion/CLIP-ViT-H-14-laion2B-s32B-b79K", filename="open_clip_pytorch_model.bin"),
|
| 183 |
+
model_pretrained_name_or_path=hf_hub_download(repo_id="xswu/HPSv2", filename="HPS_v2.1_compressed.pt"),
|
| 184 |
+
device=device
|
| 185 |
+
)
|
| 186 |
+
|
| 187 |
+
imagereward_scorer = load_imagereward(
|
| 188 |
+
model_path=hf_hub_download(repo_id="THUDM/ImageReward", filename="ImageReward.pt"),
|
| 189 |
+
med_config=hf_hub_download(repo_id="THUDM/ImageReward", filename="med_config.json"),
|
| 190 |
+
device=device
|
| 191 |
+
)
|
| 192 |
+
|
| 193 |
+
load_model_func = model_dict[model_name]
|
| 194 |
+
|
| 195 |
+
# load diffusion model
|
| 196 |
+
inference_dtype = torch.float16
|
| 197 |
+
scheduler = DDIMScheduler.from_pretrained("stabilityai/stable-diffusion-xl-base-1.0", subfolder="scheduler")
|
| 198 |
+
pipe, guidance_scale = load_model_func(scheduler, inference_dtype)
|
| 199 |
+
pipe.to(device)
|
| 200 |
+
pipe.set_progress_bar_config(disable=True)
|
| 201 |
+
|
| 202 |
+
# load dataset
|
| 203 |
+
val_dataset = load_dataset("pickapic-anonymous/pickapic_v1", split="validation_unique", streaming=True)
|
| 204 |
+
|
| 205 |
+
# calculate preference score
|
| 206 |
+
caption_list = []
|
| 207 |
+
for i, sample in enumerate(val_dataset):
|
| 208 |
+
caption_list.append(sample['caption'])
|
| 209 |
+
|
| 210 |
+
batch_num = len(caption_list) // batch_size if len(caption_list) % batch_size == 0 else len(caption_list) // batch_size + 1
|
| 211 |
+
batched_caption_list = [caption_list[i*batch_size:(i+1)*batch_size] for i in range(batch_num)]
|
| 212 |
+
|
| 213 |
+
pickscore_list = []
|
| 214 |
+
aesthetic_score_list = []
|
| 215 |
+
hpsv2score_list = []
|
| 216 |
+
hpsv21score_list = []
|
| 217 |
+
imagereward_list = []
|
| 218 |
+
|
| 219 |
+
for batch_prompt in tqdm(batched_caption_list):
|
| 220 |
+
generator=torch.Generator(device=device).manual_seed(seed)
|
| 221 |
+
images = pipe(
|
| 222 |
+
batch_prompt,
|
| 223 |
+
guidance_scale=guidance_scale,
|
| 224 |
+
num_inference_steps=sample_steps,
|
| 225 |
+
generator=generator,
|
| 226 |
+
output_type='pil',
|
| 227 |
+
num_images_per_prompt=num_image_per_prompt,
|
| 228 |
+
).images
|
| 229 |
+
|
| 230 |
+
for prompt, image in zip(batch_prompt, images):
|
| 231 |
+
pickscore = pickscorer(prompt, [image])[0]
|
| 232 |
+
pickscore_list.append(pickscore)
|
| 233 |
+
|
| 234 |
+
aesthetic_score = aesthetic_scorer(image)[0].item()
|
| 235 |
+
aesthetic_score_list.append(aesthetic_score)
|
| 236 |
+
|
| 237 |
+
hpsv2_score = hpsv2_scorer.score(image, prompt)[0]
|
| 238 |
+
hpsv2score_list.append(hpsv2_score)
|
| 239 |
+
|
| 240 |
+
hpsv21_score = hpsv21_scorer.score(image, prompt)[0]
|
| 241 |
+
hpsv21score_list.append(hpsv21_score)
|
| 242 |
+
|
| 243 |
+
imagereward_score = imagereward_scorer.score(prompt, image)
|
| 244 |
+
imagereward_list.append(imagereward_score)
|
| 245 |
+
|
| 246 |
+
|
| 247 |
+
res_save_dir = './eval_results/pick_a_pic_val_score'
|
| 248 |
+
os.makedirs(res_save_dir, exist_ok=True)
|
| 249 |
+
|
| 250 |
+
file_name = f"{model_name}_ddim_cfg{guidance_scale}_step{sample_steps}_seed{seed}_{num_image_per_prompt}image_batch{batch_size}.json"
|
| 251 |
+
with open(os.path.join(res_save_dir, file_name), 'w', encoding='utf-8') as f:
|
| 252 |
+
json.dump({
|
| 253 |
+
'pickscore': torch.mean(torch.tensor(pickscore_list)).item(),
|
| 254 |
+
'aestheticscore': torch.mean(torch.tensor(aesthetic_score_list)).item(),
|
| 255 |
+
'hpsv2score': torch.mean(torch.tensor(hpsv2score_list)).item(),
|
| 256 |
+
'hpsv21score': torch.mean(torch.tensor(hpsv21score_list)).item(),
|
| 257 |
+
'imagerewardscore': torch.mean(torch.tensor(imagereward_list)).item(),
|
| 258 |
+
}, f, indent=4)
|
| 259 |
+
|
| 260 |
+
print(f"Pickscore: {torch.mean(torch.tensor(pickscore_list))}")
|
| 261 |
+
print(f"Aestheticscore: {torch.mean(torch.tensor(aesthetic_score_list))}")
|
| 262 |
+
print(f"HPSv2score: {torch.mean(torch.tensor(hpsv2score_list))}")
|
| 263 |
+
print(f"HPSv21score: {torch.mean(torch.tensor(hpsv21score_list))}")
|
| 264 |
+
print(f"Imagerewardscore: {torch.mean(torch.tensor(imagereward_list))}")
|
| 265 |
+
|
upload.py
ADDED
|
@@ -0,0 +1,270 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/usr/bin/env python3
|
| 2 |
+
"""Upload this codebase to Hugging Face Hub while excluding the .back folder.
|
| 3 |
+
|
| 4 |
+
Examples:
|
| 5 |
+
/g/data/rr81/aev/bin/python upload.py
|
| 6 |
+
/g/data/rr81/aev/bin/python upload.py --repo-id aryadomain/all_code_base
|
| 7 |
+
/g/data/rr81/aev/bin/python upload.py --method large
|
| 8 |
+
/g/data/rr81/aev/bin/python upload.py --repo-id my-user/all_code_base
|
| 9 |
+
"""
|
| 10 |
+
|
| 11 |
+
from __future__ import annotations
|
| 12 |
+
|
| 13 |
+
import argparse
|
| 14 |
+
import inspect
|
| 15 |
+
from pathlib import Path
|
| 16 |
+
from typing import List
|
| 17 |
+
|
| 18 |
+
from huggingface_hub import HfApi
|
| 19 |
+
|
| 20 |
+
|
| 21 |
+
def build_ignore_patterns(extra_ignore: List[str]) -> List[str]:
|
| 22 |
+
# Always exclude local metadata and the requested backup folder.
|
| 23 |
+
patterns = [
|
| 24 |
+
".git",
|
| 25 |
+
".git/**",
|
| 26 |
+
".back",
|
| 27 |
+
".back/**",
|
| 28 |
+
"**/.back/**",
|
| 29 |
+
]
|
| 30 |
+
patterns.extend(extra_ignore)
|
| 31 |
+
return patterns
|
| 32 |
+
|
| 33 |
+
|
| 34 |
+
def parse_args() -> argparse.Namespace:
|
| 35 |
+
parser = argparse.ArgumentParser(
|
| 36 |
+
description="Upload a folder to Hugging Face Hub, excluding .back."
|
| 37 |
+
)
|
| 38 |
+
parser.add_argument(
|
| 39 |
+
"--source-dir",
|
| 40 |
+
type=str,
|
| 41 |
+
default=".",
|
| 42 |
+
help="Local folder to upload (default: current directory).",
|
| 43 |
+
)
|
| 44 |
+
parser.add_argument(
|
| 45 |
+
"--repo-name",
|
| 46 |
+
type=str,
|
| 47 |
+
default="all_code_base",
|
| 48 |
+
help="Repo name used when --repo-id is not provided.",
|
| 49 |
+
)
|
| 50 |
+
parser.add_argument(
|
| 51 |
+
"--repo-id",
|
| 52 |
+
type=str,
|
| 53 |
+
default="aryadomain/all_code_base",
|
| 54 |
+
help="Full Hugging Face repo id like user_or_org/repo_name (default: aryadomain/all_code_base).",
|
| 55 |
+
)
|
| 56 |
+
parser.add_argument(
|
| 57 |
+
"--namespace",
|
| 58 |
+
type=str,
|
| 59 |
+
default=None,
|
| 60 |
+
help="Optional user/org namespace override when using --repo-name.",
|
| 61 |
+
)
|
| 62 |
+
parser.add_argument(
|
| 63 |
+
"--repo-type",
|
| 64 |
+
type=str,
|
| 65 |
+
default="model",
|
| 66 |
+
choices=["model", "dataset", "space"],
|
| 67 |
+
help="Hub repo type.",
|
| 68 |
+
)
|
| 69 |
+
parser.add_argument(
|
| 70 |
+
"--private",
|
| 71 |
+
action="store_true",
|
| 72 |
+
help="Create the repo as private.",
|
| 73 |
+
)
|
| 74 |
+
parser.add_argument(
|
| 75 |
+
"--revision",
|
| 76 |
+
type=str,
|
| 77 |
+
default="main",
|
| 78 |
+
help="Target branch/revision (default: main).",
|
| 79 |
+
)
|
| 80 |
+
parser.add_argument(
|
| 81 |
+
"--commit-message",
|
| 82 |
+
type=str,
|
| 83 |
+
default="Upload codebase excluding .back",
|
| 84 |
+
help="Commit message for upload.",
|
| 85 |
+
)
|
| 86 |
+
parser.add_argument(
|
| 87 |
+
"--extra-ignore",
|
| 88 |
+
nargs="*",
|
| 89 |
+
default=[],
|
| 90 |
+
help="Additional ignore patterns for upload_folder.",
|
| 91 |
+
)
|
| 92 |
+
parser.add_argument(
|
| 93 |
+
"--dry-run",
|
| 94 |
+
action="store_true",
|
| 95 |
+
help="Print resolved settings and exit without uploading.",
|
| 96 |
+
)
|
| 97 |
+
parser.add_argument(
|
| 98 |
+
"--method",
|
| 99 |
+
type=str,
|
| 100 |
+
default="large",
|
| 101 |
+
choices=["auto", "folder", "large"],
|
| 102 |
+
help=(
|
| 103 |
+
"Upload method: auto chooses large upload for big folders, "
|
| 104 |
+
"folder forces upload_folder, large forces upload_large_folder compatibility path."
|
| 105 |
+
),
|
| 106 |
+
)
|
| 107 |
+
parser.add_argument(
|
| 108 |
+
"--large-threshold-gb",
|
| 109 |
+
type=float,
|
| 110 |
+
default=10.0,
|
| 111 |
+
help="In auto mode, switch to large upload when folder size exceeds this many GB.",
|
| 112 |
+
)
|
| 113 |
+
return parser.parse_args()
|
| 114 |
+
|
| 115 |
+
|
| 116 |
+
def resolve_repo_id(api: HfApi, args: argparse.Namespace) -> str:
|
| 117 |
+
if args.repo_id:
|
| 118 |
+
return args.repo_id
|
| 119 |
+
|
| 120 |
+
if args.namespace:
|
| 121 |
+
return f"{args.namespace}/{args.repo_name}"
|
| 122 |
+
|
| 123 |
+
who = api.whoami()
|
| 124 |
+
username = who.get("name")
|
| 125 |
+
if not username:
|
| 126 |
+
raise RuntimeError("Could not resolve username from Hugging Face login.")
|
| 127 |
+
return f"{username}/{args.repo_name}"
|
| 128 |
+
|
| 129 |
+
|
| 130 |
+
def folder_size_bytes(folder: Path) -> int:
|
| 131 |
+
total = 0
|
| 132 |
+
for p in folder.rglob("*"):
|
| 133 |
+
if p.is_file() and ".back" not in p.parts:
|
| 134 |
+
try:
|
| 135 |
+
total += p.stat().st_size
|
| 136 |
+
except OSError:
|
| 137 |
+
pass
|
| 138 |
+
return total
|
| 139 |
+
|
| 140 |
+
|
| 141 |
+
def upload_folder_compat(
|
| 142 |
+
api: HfApi,
|
| 143 |
+
repo_id: str,
|
| 144 |
+
repo_type: str,
|
| 145 |
+
source_dir: Path,
|
| 146 |
+
revision: str,
|
| 147 |
+
commit_message: str,
|
| 148 |
+
ignore_patterns: List[str],
|
| 149 |
+
):
|
| 150 |
+
kwargs = {
|
| 151 |
+
"repo_id": repo_id,
|
| 152 |
+
"repo_type": repo_type,
|
| 153 |
+
"folder_path": str(source_dir),
|
| 154 |
+
"path_in_repo": ".",
|
| 155 |
+
"revision": revision,
|
| 156 |
+
"commit_message": commit_message,
|
| 157 |
+
"ignore_patterns": ignore_patterns,
|
| 158 |
+
}
|
| 159 |
+
sig = inspect.signature(api.upload_folder)
|
| 160 |
+
if "multi_commits" in sig.parameters:
|
| 161 |
+
kwargs["multi_commits"] = True
|
| 162 |
+
if "multi_commits_verbose" in sig.parameters:
|
| 163 |
+
kwargs["multi_commits_verbose"] = True
|
| 164 |
+
return api.upload_folder(**kwargs)
|
| 165 |
+
|
| 166 |
+
|
| 167 |
+
def upload_large_compat(
|
| 168 |
+
api: HfApi,
|
| 169 |
+
repo_id: str,
|
| 170 |
+
repo_type: str,
|
| 171 |
+
source_dir: Path,
|
| 172 |
+
revision: str,
|
| 173 |
+
ignore_patterns: List[str],
|
| 174 |
+
):
|
| 175 |
+
if not hasattr(api, "upload_large_folder"):
|
| 176 |
+
raise RuntimeError("Installed huggingface_hub does not provide upload_large_folder")
|
| 177 |
+
|
| 178 |
+
sig = inspect.signature(api.upload_large_folder)
|
| 179 |
+
kwargs = {
|
| 180 |
+
"repo_id": repo_id,
|
| 181 |
+
"repo_type": repo_type,
|
| 182 |
+
"folder_path": str(source_dir),
|
| 183 |
+
}
|
| 184 |
+
if "revision" in sig.parameters:
|
| 185 |
+
kwargs["revision"] = revision
|
| 186 |
+
if "path_in_repo" in sig.parameters:
|
| 187 |
+
kwargs["path_in_repo"] = "."
|
| 188 |
+
if "ignore_patterns" in sig.parameters:
|
| 189 |
+
kwargs["ignore_patterns"] = ignore_patterns
|
| 190 |
+
|
| 191 |
+
return api.upload_large_folder(**kwargs)
|
| 192 |
+
|
| 193 |
+
|
| 194 |
+
def main() -> None:
|
| 195 |
+
args = parse_args()
|
| 196 |
+
source_dir = Path(args.source_dir).resolve()
|
| 197 |
+
|
| 198 |
+
if not source_dir.exists() or not source_dir.is_dir():
|
| 199 |
+
raise FileNotFoundError(f"Source directory not found or not a directory: {source_dir}")
|
| 200 |
+
|
| 201 |
+
api = HfApi()
|
| 202 |
+
repo_id = resolve_repo_id(api, args)
|
| 203 |
+
ignore_patterns = build_ignore_patterns(args.extra_ignore)
|
| 204 |
+
total_size = folder_size_bytes(source_dir)
|
| 205 |
+
total_size_gb = total_size / (1024 ** 3)
|
| 206 |
+
|
| 207 |
+
if args.method == "auto":
|
| 208 |
+
use_large = total_size_gb >= args.large_threshold_gb
|
| 209 |
+
else:
|
| 210 |
+
use_large = args.method == "large"
|
| 211 |
+
|
| 212 |
+
print("Source directory:", source_dir)
|
| 213 |
+
print("Repo id:", repo_id)
|
| 214 |
+
print("Repo type:", args.repo_type)
|
| 215 |
+
print("Private:", args.private)
|
| 216 |
+
print("Revision:", args.revision)
|
| 217 |
+
print("Ignore patterns:", ignore_patterns)
|
| 218 |
+
print(f"Folder size (excluding .back): {total_size_gb:.2f} GB")
|
| 219 |
+
print("Upload method:", "large" if use_large else "folder")
|
| 220 |
+
|
| 221 |
+
if args.dry_run:
|
| 222 |
+
print("Dry run requested. Exiting before create/upload.")
|
| 223 |
+
return
|
| 224 |
+
|
| 225 |
+
api.create_repo(
|
| 226 |
+
repo_id=repo_id,
|
| 227 |
+
repo_type=args.repo_type,
|
| 228 |
+
private=args.private,
|
| 229 |
+
exist_ok=True,
|
| 230 |
+
)
|
| 231 |
+
|
| 232 |
+
if use_large:
|
| 233 |
+
try:
|
| 234 |
+
commit_info = upload_large_compat(
|
| 235 |
+
api=api,
|
| 236 |
+
repo_id=repo_id,
|
| 237 |
+
repo_type=args.repo_type,
|
| 238 |
+
source_dir=source_dir,
|
| 239 |
+
revision=args.revision,
|
| 240 |
+
ignore_patterns=ignore_patterns,
|
| 241 |
+
)
|
| 242 |
+
except Exception as e:
|
| 243 |
+
print(f"Large upload path failed ({e}). Falling back to upload_folder with multi-commit mode.")
|
| 244 |
+
commit_info = upload_folder_compat(
|
| 245 |
+
api=api,
|
| 246 |
+
repo_id=repo_id,
|
| 247 |
+
repo_type=args.repo_type,
|
| 248 |
+
source_dir=source_dir,
|
| 249 |
+
revision=args.revision,
|
| 250 |
+
commit_message=args.commit_message,
|
| 251 |
+
ignore_patterns=ignore_patterns,
|
| 252 |
+
)
|
| 253 |
+
else:
|
| 254 |
+
commit_info = upload_folder_compat(
|
| 255 |
+
api=api,
|
| 256 |
+
repo_id=repo_id,
|
| 257 |
+
repo_type=args.repo_type,
|
| 258 |
+
source_dir=source_dir,
|
| 259 |
+
revision=args.revision,
|
| 260 |
+
commit_message=args.commit_message,
|
| 261 |
+
ignore_patterns=ignore_patterns,
|
| 262 |
+
)
|
| 263 |
+
|
| 264 |
+
print("Upload completed.")
|
| 265 |
+
print("Commit:", commit_info)
|
| 266 |
+
print("Repo URL: https://huggingface.co/" + repo_id)
|
| 267 |
+
|
| 268 |
+
|
| 269 |
+
if __name__ == "__main__":
|
| 270 |
+
main()
|