| ''' |
| ostris/ai-toolkit on https://modal.com — rewritten for Modal >= 1.0 API. |
| |
| Run training (from inside the ai-toolkit directory) with: |
| modal run run_modal.py --config-file-list-str=/root/ai-toolkit/config/numzoo_klein.yaml |
| ''' |
|
|
| import os |
| os.environ["HF_HUB_ENABLE_HF_TRANSFER"] = "1" |
| os.environ["DISABLE_TELEMETRY"] = "YES" |
| import modal |
|
|
| |
| LOCAL_AI_TOOLKIT = os.path.dirname(os.path.abspath(__file__)) |
| REMOTE_AI_TOOLKIT = "/root/ai-toolkit" |
| MOUNT_DIR = "/root/ai-toolkit/modal_output" |
|
|
| |
| model_volume = modal.Volume.from_name("flux-lora-models", create_if_missing=True) |
|
|
| |
| |
| |
| |
| image = ( |
| modal.Image.debian_slim(python_version="3.11") |
| .apt_install("libgl1", "libglib2.0-0", "git") |
| |
| |
| .pip_install("torch==2.7.1", "torchvision==0.22.1", "torchaudio==2.7.1") |
| |
| .add_local_file(os.path.join(LOCAL_AI_TOOLKIT, "requirements.txt"), |
| "/tmp/reqs/requirements.txt", copy=True) |
| .add_local_file(os.path.join(LOCAL_AI_TOOLKIT, "requirements_base.txt"), |
| "/tmp/reqs/requirements_base.txt", copy=True) |
| .run_commands("cd /tmp/reqs && pip install -r requirements.txt") |
| |
| |
| .add_local_dir( |
| LOCAL_AI_TOOLKIT, |
| REMOTE_AI_TOOLKIT, |
| ignore=[ |
| ".git", "**/.git", "**/node_modules", "**/__pycache__", "**/*.pyc", |
| "modal_output", "**/.venv", "venv", "ui/node_modules", "output", |
| ], |
| ) |
| ) |
|
|
| app = modal.App(name="numzoo-lora-training", image=image, volumes={MOUNT_DIR: model_volume}) |
|
|
|
|
| @app.function( |
| gpu="A100", |
| timeout=7200, |
| secrets=[modal.Secret.from_name("huggingface")], |
| ) |
| def main(config_file_list_str: str, recover: bool = False, name: str = None): |
| import sys |
| sys.path.insert(0, REMOTE_AI_TOOLKIT) |
| from toolkit.job import get_job |
|
|
| config_file_list = config_file_list_str.split(",") |
| jobs_completed = 0 |
| jobs_failed = 0 |
| print(f"Running {len(config_file_list)} job{'' if len(config_file_list) == 1 else 's'}") |
|
|
| for config_file in config_file_list: |
| try: |
| job = get_job(config_file, name) |
| job.config['process'][0]['training_folder'] = MOUNT_DIR |
| os.makedirs(MOUNT_DIR, exist_ok=True) |
| print(f"Training outputs will be saved to: {MOUNT_DIR}") |
| job.run() |
| model_volume.commit() |
| job.cleanup() |
| jobs_completed += 1 |
| except Exception as e: |
| print(f"Error running job: {e}") |
| jobs_failed += 1 |
| if not recover: |
| raise |
|
|
| print("========================================") |
| print(f"Result: {jobs_completed} completed, {jobs_failed} failed") |
| print("========================================") |
|
|
|
|
| @app.local_entrypoint() |
| def run(config_file_list_str: str, recover: bool = False, name: str = None): |
| main.remote(config_file_list_str=config_file_list_str, recover=recover, name=name) |
|
|