Spaces:
Running
Running
""" | |
Modified main.py on DiffMorpher, LCM-LoRA support + param additions + logging + optimizations for speed-up | |
""" | |
import os | |
import torch | |
import numpy as np | |
from PIL import Image | |
from argparse import ArgumentParser | |
from model import DiffMorpherPipeline | |
import time | |
import logging | |
import gc | |
# os.environ["HF_HOME"] = "/app/hf_cache" | |
# os.environ["DIFFUSERS_CACHE"] = "/app/hf_cache" | |
# os.environ["TORCH_HOME"] = "/app/torch_cache" | |
# os.environ["TRANSFORMERS_CACHE"] = "/app/hf_cache" | |
# os.environ["HF_DATASETS_CACHE"] = "/app/hf_cache/datasets" | |
logs_folder = "logs" | |
os.makedirs(logs_folder, exist_ok=True) | |
# Create a unique log filename using the current time | |
log_filename = os.path.join(logs_folder, f"execution_{time.strftime('%Y%m%d_%H%M%S')}.log") | |
logging.basicConfig( | |
filename=log_filename, | |
level=logging.INFO, | |
format='%(asctime)s - %(levelname)s - %(message)s' | |
) | |
start_time = time.time() | |
parser = ArgumentParser() | |
parser.add_argument( | |
"--model_path", type=str, default="stabilityai/stable-diffusion-2-1-base", | |
help="Pretrained model to use (default: %(default)s)" | |
) | |
# Available SDV1-5 versions: | |
# sd-legacy/stable-diffusion-v1-5 | |
# lykon/dreamshaper-7 | |
# Original DiffMorpher SD: | |
# stabilityai/stable-diffusion-2-1-base | |
# Quantized models to try (non-functional, possible extension for future) | |
# DarkFlameUniverse/Stable-Diffusion-2-1-Base-8bit | |
# Xerox32/SD2.1-base-Int8 | |
parser.add_argument( | |
"--image_path_0", type=str, default="", | |
help="Path of the first image (default: %(default)s)" | |
) | |
parser.add_argument( | |
"--prompt_0", type=str, default="", | |
help="Prompt of the first image (default: %(default)s)" | |
) | |
parser.add_argument( | |
"--image_path_1", type=str, default="", | |
help="Path of the second image (default: %(default)s)" | |
) | |
parser.add_argument( | |
"--prompt_1", type=str, default="", | |
help="Prompt of the second image (default: %(default)s)" | |
) | |
parser.add_argument( | |
"--output_path", type=str, default="./results", | |
help="Path of the output image (default: %(default)s)" | |
) | |
parser.add_argument( | |
"--save_lora_dir", type=str, default="./lora", | |
help="Path for saving LoRA weights (default: %(default)s)" | |
) | |
parser.add_argument( | |
"--load_lora_path_0", type=str, default="", | |
help="Path of the LoRA weights for the first image (default: %(default)s)" | |
) | |
parser.add_argument( | |
"--load_lora_path_1", type=str, default="", | |
help="Path of the LoRA weights for the second image (default: %(default)s)" | |
) | |
parser.add_argument( | |
"--num_inference_steps", type=int, default=50, | |
help="Number of inference steps (default: %(default)s)") | |
parser.add_argument( | |
"--guidance_scale", type=float, default=1, # To match current diffmorpher | |
help="Guidance scale for classifier-free guidance (default: %(default)s)" | |
) | |
parser.add_argument("--use_adain", action="store_true", help="Use AdaIN (default: %(default)s)") | |
parser.add_argument("--use_reschedule", action="store_true", help="Use reschedule sampling (default: %(default)s)") | |
parser.add_argument("--lamb", type=float, default=0.6, help="Lambda for self-attention replacement (default: %(default)s)") | |
parser.add_argument("--fix_lora_value", type=float, default=None, help="Fix lora value (default: LoRA Interp., not fixed)") | |
parser.add_argument("--save_inter", action="store_true", help="Save intermediate results (default: %(default)s)") | |
parser.add_argument("--num_frames", type=int, default=16, help="Number of frames to generate (default: %(default)s)") | |
parser.add_argument("--duration", type=int, default=100, help="Duration of each frame (default: %(default)s ms)") | |
parser.add_argument("--no_lora", action="store_true", help="Disable style LoRA (default: %(default)s)") | |
# New argument for LCM LoRA acceleration | |
parser.add_argument("--use_lcm", action="store_true", help="Enable LCM-LoRA acceleration for faster sampling") | |
args = parser.parse_args() | |
os.makedirs(args.output_path, exist_ok=True) | |
# Clear any existing PyTorch GPU allocations | |
# torch.cuda.empty_cache() | |
# gc.collect() | |
# Set environment variable for memory allocation | |
os.environ["PYTORCH_CUDA_ALLOC_CONF"] = "expandable_segments:True" | |
# Create the pipeline from the given model path | |
pipeline = DiffMorpherPipeline.from_pretrained(args.model_path, torch_dtype=torch.float32) | |
# memory optimisations for vae and attention slicing - breaks computations into smaller chunks to fit better in mem | |
# can lead to more efficient caching and memory access. better memory locality | |
# found that its helpful with GPUs with limited VRAM memory in particular. | |
pipeline.enable_vae_slicing() | |
pipeline.enable_attention_slicing() | |
pipeline.to("cuda") | |
# Add these AFTER device movement | |
torch.backends.cudnn.benchmark = True # finds efficient convolution algo by running short benchmark, minimal speed-up. | |
torch.set_float32_matmul_precision("high") # Better for modern GPUs, reduces about 7 seconds of inference time. | |
# Integrate LCM-LoRA if flagged, OUTSIDE any of the style LoRA loading / training steps. | |
if args.use_lcm: | |
from lcm_lora.lcm_schedule import LCMScheduler | |
# Replace scheduler using LCM's configuration | |
pipeline.scheduler = LCMScheduler.from_config(pipeline.scheduler.config) | |
# Load the LCM LoRA weights (LCM provides an add-on network) | |
pipeline.load_lora_weights("latent-consistency/lcm-lora-sdv1-5") | |
# Set the lcm_inference_steps | |
args.num_inference_steps = 8 # Override with LCM-recommended steps | |
# set CFG (range allowed by legacy code: 0 to 1, 1 performs best) | |
args.guidance_scale = 1 | |
# Run the pipeline inference using existing parameters | |
images = pipeline( | |
img_path_0=args.image_path_0, | |
img_path_1=args.image_path_1, | |
prompt_0=args.prompt_0, | |
prompt_1=args.prompt_1, | |
save_lora_dir=args.save_lora_dir, | |
load_lora_path_0=args.load_lora_path_0, | |
load_lora_path_1=args.load_lora_path_1, | |
use_adain=args.use_adain, | |
use_reschedule=args.use_reschedule, | |
lamd=args.lamb, | |
output_path=args.output_path, | |
num_frames=args.num_frames, | |
num_inference_steps = args.num_inference_steps, # enforce when LCM enabled | |
fix_lora=args.fix_lora_value, | |
save_intermediates=args.save_inter, | |
use_lora=not args.no_lora, | |
use_lcm = args.use_lcm, | |
guidance_scale=args.guidance_scale, # enforce when LCM enabled | |
) | |
# Save the resulting GIF output from the sequence of images | |
images[0].save(f"{args.output_path}/output.gif", save_all=True, | |
append_images=images[1:], duration=args.duration, loop=0) | |
# Ensure memory is freed after completion | |
pipeline = None | |
torch.cuda.empty_cache() | |
gc.collect() | |
end_time = time.time() | |
elapsed_time = end_time - start_time | |
# Log the execution details and parameters | |
logging.info(f"Total execution time: {elapsed_time:.2f} seconds") | |
logging.info(f"Model Path: {args.model_path}") | |
logging.info(f"Image Path 0: {args.image_path_0}") | |
logging.info(f"Image Path 1: {args.image_path_1}") | |
logging.info(f"Use LCM: {args.use_lcm}") | |
logging.info(f"Number of inference steps: {args.num_inference_steps}") | |
logging.info(f"Guidance scale: {args.guidance_scale}") | |
print(f"Total execution time: {elapsed_time:.2f} seconds, log file saved as {log_filename}") |