Image-to-Image
Diffusers
Safetensors
Core ML
StableDiffusionInpaintPipeline
clover-image
inpainting
stable-diffusion
Instructions to use neonforestmist/Clover-Image-Tiny-Inpaint with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- Diffusers
How to use neonforestmist/Clover-Image-Tiny-Inpaint with Diffusers:
pip install -U diffusers transformers accelerate
import torch from diffusers import DiffusionPipeline from diffusers.utils import load_image # switch to "mps" for apple devices pipe = DiffusionPipeline.from_pretrained("neonforestmist/Clover-Image-Tiny-Inpaint", dtype=torch.bfloat16, device_map="cuda") prompt = "Turn this cat into a dog" input_image = load_image("https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/diffusers/cat.png") image = pipe(image=input_image, prompt=prompt).images[0] - Notebooks
- Google Colab
- Kaggle
| #!/usr/bin/env python3 | |
| """Run context-aware Clover Image Tiny inpainting distillation on Modal.""" | |
| from __future__ import annotations | |
| import os | |
| import subprocess | |
| import sys | |
| import time | |
| from pathlib import Path | |
| import modal | |
| APP_NAME = "clover-image-tiny-inpaint-v2" | |
| OUTPUT_VOLUME_NAME = "clover-image-tiny-inpaint-output" | |
| CACHE_VOLUME_NAME = "clover-image-tiny-inpaint-cache" | |
| OUTPUT_ROOT = Path("/outputs") | |
| CACHE_ROOT = Path("/cache") | |
| image = ( | |
| modal.Image.debian_slim(python_version="3.11") | |
| .pip_install( | |
| "accelerate==1.14.0", | |
| "datasets==4.8.5", | |
| "diffusers==0.39.0", | |
| "ftfy==6.3.1", | |
| "huggingface_hub==0.36.0", | |
| "numpy==2.2.6", | |
| "pillow==12.3.0", | |
| "safetensors==0.8.0", | |
| "torch==2.7.0", | |
| "torchvision==0.22.0", | |
| "transformers==4.57.6", | |
| ) | |
| .add_local_dir("inpainting", remote_path="/root/inpainting") | |
| ) | |
| output_volume = modal.Volume.from_name(OUTPUT_VOLUME_NAME, create_if_missing=True) | |
| cache_volume = modal.Volume.from_name(CACHE_VOLUME_NAME, create_if_missing=True) | |
| app = modal.App( | |
| APP_NAME, | |
| image=image, | |
| volumes={ | |
| str(OUTPUT_ROOT): output_volume, | |
| str(CACHE_ROOT): cache_volume, | |
| }, | |
| ) | |
| def train( | |
| *, | |
| base_model: str = "neonforestmist/Clover-Image-Tiny", | |
| base_revision: str = "63b0e9f6be9c00888ff464f342a9ef052bf76681", | |
| initial_inpaint_model: str = "neonforestmist/Clover-Image-Tiny-Inpaint", | |
| initial_inpaint_revision: str = "1b6f8ae3db51900520369d5522c7dc7c2a97e21e", | |
| teacher_model: str = "stable-diffusion-v1-5/stable-diffusion-inpainting", | |
| teacher_revision: str = "8a4288a76071f7280aedbdb3253bdb9e9d5d84bb", | |
| dataset: str = "prithivMLmods/Caption3o-Opt", | |
| dataset_revision: str = "17e893f785fcd3f5d6fc4a5d65a914b9f7b1ff5b", | |
| dataset_split: str = "train", | |
| image_column: str = "image", | |
| caption_column: str = "caption", | |
| max_train_steps: int = 12000, | |
| output_name: str = "clover-image-tiny-inpaint-v2", | |
| max_train_samples: int | None = None, | |
| learning_rate: float = 5e-6, | |
| lr_warmup_steps: int | None = None, | |
| teacher_loss_weight: float = 0.75, | |
| ground_truth_loss_weight: float = 0.25, | |
| context_loss_weight: float = 0.25, | |
| masked_loss_weight: float = 2.5, | |
| boundary_loss_weight: float = 2.0, | |
| seed: int = 20260811, | |
| checkpointing_steps: int = 500, | |
| resume: bool = False, | |
| ) -> str: | |
| """Train v2 and return its persistent Modal Volume path.""" | |
| output_dir = OUTPUT_ROOT / output_name | |
| if (output_dir / "training-complete.json").exists(): | |
| raise RuntimeError(f"Completed output already exists: {output_dir}") | |
| if output_dir.exists() and not resume: | |
| raise RuntimeError( | |
| f"Partial output already exists; choose another name or enable resume: {output_dir}" | |
| ) | |
| command = [ | |
| sys.executable, | |
| "-u", | |
| "/root/inpainting/train.py", | |
| "--pretrained_model_name_or_path", | |
| base_model, | |
| "--revision", | |
| base_revision, | |
| "--initial_inpaint_model", | |
| initial_inpaint_model, | |
| "--teacher_model_name_or_path", | |
| teacher_model, | |
| "--teacher_revision", | |
| teacher_revision, | |
| "--dataset_name", | |
| dataset, | |
| "--dataset_revision", | |
| dataset_revision, | |
| "--dataset_split", | |
| dataset_split, | |
| "--image_column", | |
| image_column, | |
| "--caption_column", | |
| caption_column, | |
| "--max_train_steps", | |
| str(max_train_steps), | |
| "--learning_rate", | |
| str(learning_rate), | |
| "--lr_warmup_steps", | |
| str( | |
| lr_warmup_steps | |
| if lr_warmup_steps is not None | |
| else min(500, max(1, max_train_steps // 20)) | |
| ), | |
| "--gradient_accumulation_steps", | |
| "4", | |
| "--gradient_checkpointing", | |
| "--mixed_precision", | |
| "bf16", | |
| "--random_flip", | |
| "--caption_dropout_probability", | |
| "0.10", | |
| "--validation_samples", | |
| "128", | |
| "--teacher_loss_weight", | |
| str(teacher_loss_weight), | |
| "--ground_truth_loss_weight", | |
| str(ground_truth_loss_weight), | |
| "--context_loss_weight", | |
| str(context_loss_weight), | |
| "--masked_loss_weight", | |
| str(masked_loss_weight), | |
| "--boundary_loss_weight", | |
| str(boundary_loss_weight), | |
| "--snr_gamma", | |
| "5.0", | |
| "--checkpointing_steps", | |
| str(checkpointing_steps), | |
| "--checkpoints_total_limit", | |
| "3", | |
| "--seed", | |
| str(seed), | |
| "--output_dir", | |
| str(output_dir), | |
| ] | |
| if initial_inpaint_revision: | |
| command.extend(["--initial_inpaint_revision", initial_inpaint_revision]) | |
| if max_train_samples is not None: | |
| command.extend(["--max_train_samples", str(max_train_samples)]) | |
| if resume: | |
| command.extend(["--resume_from_checkpoint", "latest"]) | |
| env = os.environ.copy() | |
| env.update( | |
| { | |
| "HF_HOME": str(CACHE_ROOT / "huggingface"), | |
| "HF_HUB_CACHE": str(CACHE_ROOT / "huggingface" / "hub"), | |
| "HF_DATASETS_CACHE": str(CACHE_ROOT / "huggingface" / "datasets"), | |
| "TOKENIZERS_PARALLELISM": "false", | |
| "PYTHONUNBUFFERED": "1", | |
| } | |
| ) | |
| process = subprocess.Popen(command, env=env) | |
| last_commit = time.monotonic() | |
| while process.poll() is None: | |
| time.sleep(30) | |
| if time.monotonic() - last_commit >= 300: | |
| output_volume.commit() | |
| cache_volume.commit() | |
| last_commit = time.monotonic() | |
| if process.returncode: | |
| output_volume.commit() | |
| cache_volume.commit() | |
| raise subprocess.CalledProcessError(process.returncode, command) | |
| output_volume.commit() | |
| cache_volume.commit() | |
| return str(output_dir) | |
| def main( | |
| smoke: bool = False, | |
| steps: int = 12000, | |
| output_name: str = "clover-image-tiny-inpaint-v2", | |
| initial_inpaint_model: str = "neonforestmist/Clover-Image-Tiny-Inpaint", | |
| initial_inpaint_revision: str | None = "1b6f8ae3db51900520369d5522c7dc7c2a97e21e", | |
| learning_rate: float = 5e-6, | |
| lr_warmup_steps: int | None = None, | |
| teacher_loss_weight: float = 0.75, | |
| ground_truth_loss_weight: float = 0.25, | |
| context_loss_weight: float = 0.25, | |
| masked_loss_weight: float = 2.5, | |
| boundary_loss_weight: float = 2.0, | |
| resume: bool = False, | |
| ) -> None: | |
| """Launch a small pipeline test or the full bounded A100 run.""" | |
| if smoke: | |
| steps = min(steps, 4) | |
| samples = 8 | |
| output_name = f"{output_name}-smoke-{int(time.time())}" | |
| checkpointing_steps = 2 | |
| else: | |
| samples = None | |
| checkpointing_steps = 500 | |
| result = train.remote( | |
| max_train_steps=steps, | |
| max_train_samples=samples, | |
| output_name=output_name, | |
| initial_inpaint_model=initial_inpaint_model, | |
| initial_inpaint_revision=initial_inpaint_revision, | |
| learning_rate=learning_rate, | |
| lr_warmup_steps=lr_warmup_steps, | |
| teacher_loss_weight=teacher_loss_weight, | |
| ground_truth_loss_weight=ground_truth_loss_weight, | |
| context_loss_weight=context_loss_weight, | |
| masked_loss_weight=masked_loss_weight, | |
| boundary_loss_weight=boundary_loss_weight, | |
| checkpointing_steps=checkpointing_steps, | |
| resume=resume, | |
| ) | |
| print(f"Training output is available in Modal Volume {OUTPUT_VOLUME_NAME}: {result}") | |