EveryDream2trainer/Train_Runpod.ipynb

484 lines
19 KiB
Plaintext
Raw Normal View History

2023-01-12 16:28:11 -07:00
{
"cells": [
{
"cell_type": "markdown",
"id": "2c831b5b-3025-4177-bef5-25aaec89573a",
"metadata": {},
"source": [
"## Every Dream v2 RunPod Setup\n",
"\n",
"[General Instructions](https://github.com/victorchall/EveryDream2trainer/blob/main/README.md)\n",
"\n",
"If you can sign up for Runpod here (shameless referral link): [Runpod](https://runpod.io/?ref=oko38cd0)\n",
"\n",
"If you are confused by the wall of text, join the discord here: [EveryDream Discord](https://discord.gg/uheqxU6sXN)\n",
"\n",
"### Usage\n",
2023-01-12 16:28:11 -07:00
"\n",
"1. Prepare your training data before you begin (see below)\n",
"2. Spin the `RunPod Stable Diffusion v2.1` template. The `RunPod PyTorch` template does not work due to an old version of Python. \n",
"3. Open this notebook with `File > Open from URL...` pointing to `https://raw.githubusercontent.com/victorchall/EveryDream2trainer/main/Train_Runpod.ipynb`\n",
"4. Run each cell below once, noting any instructions above the cell (the first step requires a pod restart)\n",
"5. Figure out how you want to tweak the process next\n",
"6. Rinse, Repeat\n",
"\n",
"#### A note on storage\n",
2023-01-12 16:28:11 -07:00
"Remember, on RunPod time is more expensive than storage. \n",
"\n",
"Which is good, because running a lot of experiments can generate a lot of data. Not having the right save points to recover quickly from inevitable mistakes will cost you a lot of time.\n",
"\n",
"When in doubt, give yourself ~125GB of Runpod **Volume** storage.\n",
"\n",
"#### Preparing your training data\n",
2023-01-12 16:28:11 -07:00
"You will want to have your data prepared before starting, and have a rough training plan in mind. Don't waste rental fees if you're not fully prepared to start training. \n",
"\n",
"**If this is your first time trying a full fine-tune, start small!** \n",
"Pick a single concept and 30-100 images, and see what happens. Training a small dataset like this is fast, and will give you a feel for how quickly your model (over-)trains depending on your training schedule.\n",
"\n",
2023-01-12 16:28:11 -07:00
"Your files should be captioned before you start with either the caption as the filename or in text files for each image alongside the image files. See [DATA.md](https://github.com/victorchall/EveryDream2trainer/blob/main/doc/DATA.md) for more details. Tools are available to automatically caption your files."
]
},
{
2023-02-08 17:38:04 -07:00
"cell_type": "markdown",
"id": "5123d4e6-281c-4475-99fd-328f4d5df734",
2023-01-12 16:28:11 -07:00
"metadata": {},
"source": [
2023-02-08 17:38:04 -07:00
"# For best results, restart the pod after the next cell completes\n",
2023-01-12 16:28:11 -07:00
"\n",
2023-02-08 17:38:04 -07:00
"Here we ensure that EveryDream2trainer is installed, and we disable the Automatic 1111 web-ui. But the vram consumed by the web-ui will not be fully freed until the pod restarts. This is especially important if you are training with large batch sizes."
2023-01-12 16:28:11 -07:00
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "bb6d14b7-3c37-4ec4-8559-16b4e9b8dd18",
"metadata": {},
"outputs": [],
"source": [
"import os\n",
"\n",
"%cd /workspace\n",
2023-02-08 17:38:04 -07:00
"!echo pass > /workspace/stable-diffusion-webui/relauncher.py\n",
2023-01-12 16:28:11 -07:00
"if not os.path.exists(\"EveryDream2trainer\"):\n",
" !git clone https://github.com/victorchall/EveryDream2trainer\n",
"\n",
"%cd EveryDream2trainer\n",
2023-02-08 17:38:04 -07:00
"%mkdir input\n",
2023-01-12 16:28:11 -07:00
"!python utils/get_yamls.py"
]
},
2023-02-08 17:38:04 -07:00
{
"cell_type": "code",
"execution_count": null,
"id": "0902e735",
"metadata": {},
"outputs": [],
"source": [
"# When running on a pod designed for Automatic 1111 \n",
"# we need to kill the webui process to free up mem for training\n",
"!ps x | grep -E \"(relauncher|webui)\" | awk '{print $1}' | xargs kill $1\n",
"\n",
"# check system resources, make sure your GPU actually has 24GB\n",
"# You should see something like \"0 MB / 24576 MB\" in the middle of the printout\n",
"# if you see 0 MB / 22000 MB pick a beefier instance...\n",
"!grep Swap /proc/meminfo\n",
"!swapon -s\n",
"!nvidia-smi"
]
},
2023-01-12 16:28:11 -07:00
{
"cell_type": "markdown",
"id": "0bf1e8cd",
"metadata": {},
"source": [
"# Upload training files\n",
"\n",
"Ues the navigation on the left to open the ** \"workspace / EveryDream2trainer / input\"** and upload your training files using the **up arrow button** above the file explorer, or by dragging and dropping the files from your local machine onto the file explorer.\n",
"\n",
"If you have many training files, or nested folders of training data, create a zip archive of your training data, upload this file to the input folder, then right click on the zip file and select \"Extract Archive\".\n",
"\n",
"## Optional - Configure sample prompts\n",
"You can set your own sample prompts by adding them, one line at a time, to sample_prompts.txt.\n",
"\n",
"Keep in mind a longer list of prompts will take longer to generate. You may also want to adjust you sample_steps in the training notebook to a different value to get samples left often. This is probably a good idea when training a larger dataset that you know will take longer to train, where more frequent samples will not help you.\n",
"\n",
"While your training data is uploading, go ahead to install the dependencies below\n",
"----"
2023-01-12 16:28:11 -07:00
]
},
{
"cell_type": "markdown",
"id": "589bfca0",
"metadata": {
"tags": []
},
"source": [
"## Install dependencies\n",
"\n",
"**This will take up to 15 minutes (if building xformers). Wait until it says \"DONE\" to move on.** \n",
2023-01-12 16:28:11 -07:00
"You can ignore \"warnings.\""
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "9649a02c-fb2b-44f1-842d-d1662fa5c7cd",
"metadata": {
"scrolled": true,
"tags": []
},
2023-01-12 16:28:11 -07:00
"outputs": [],
"source": [
"!python -m pip install --upgrade pip\n",
"\n",
"!pip install requests==2.25.1\n",
2023-02-08 17:38:04 -07:00
"!pip install -U -I torch==1.13.1+cu117 torchvision==0.14.1+cu117 --extra-index-url \"https://download.pytorch.org/whl/cu117\"\n",
2023-01-12 16:28:11 -07:00
"!pip install transformers==4.25.1\n",
2023-02-08 17:38:04 -07:00
"!pip install -U diffusers[torch]\n",
2023-01-12 16:28:11 -07:00
"\n",
"!pip install pynvml==11.4.1\n",
"!pip install bitsandbytes==0.35.0\n",
"!pip install ftfy==6.1.1\n",
"!pip install aiohttp==3.8.3\n",
"!pip install \"tensorboard>=2.11.0\"\n",
"!pip install protobuf==3.20.2\n",
"!pip install wandb==0.13.6\n",
"!pip install colorama==0.4.6\n",
"!pip install -U triton\n",
2023-02-08 17:38:04 -07:00
"!pip install --pre -U xformers\n",
" \n",
"print(\"DONE\")"
2023-01-12 16:28:11 -07:00
]
},
{
"cell_type": "markdown",
"id": "c230d91a",
"metadata": {},
"source": [
"## Now that dependencies are installed, ready to move on!"
]
},
{
"cell_type": "markdown",
"id": "176af7b7-ebfe-4d25-a4a2-5c03489590ab",
"metadata": {},
"source": [
"## Log into huggingface\n",
"Run the cell below and paste your token into the prompt. You can get your token from your [huggingface account page](https://huggingface.co/settings/tokens).\n",
"\n",
"The token will not show on the screen, just press enter after you paste it.\n",
"\n",
"Then run the following cell to download the base checkpoint (may take a minute)."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "2a1d04ff-8a2c-46c6-a5de-baea1b3e5a2b",
"metadata": {},
"outputs": [],
"source": [
"from huggingface_hub import notebook_login, hf_hub_download\n",
"import os\n",
"notebook_login()"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "86b66fe4-c2ca-46fa-813c-8fe390813add",
"metadata": {
"scrolled": true,
"tags": []
},
2023-01-12 16:28:11 -07:00
"outputs": [],
"source": [
2023-02-08 17:38:04 -07:00
"%cd /workspace/EveryDream2trainer\n",
2023-01-12 16:28:11 -07:00
"repo=\"panopstor/EveryDream\"\n",
"ckpt_file=\"sd_v1-5_vae.ckpt\"\n",
"\n",
"print(f\"Downloading {ckpt_file} from {repo}\")\n",
"downloaded_model_path = hf_hub_download(repo, ckpt_file, cache_dir=\"/workspace/hfcache\")\n",
"ckpt_name = os.path.splitext(os.path.basename(downloaded_model_path))[0]\n",
"print(f\"Downloaded {ckpt_name} to {downloaded_model_path}\")\n",
"\n",
"if not os.path.exists(f\"ckpt_cache/{ckpt_name}\"):\n",
" print(f\"Converting {ckpt_name} to Diffusers format\")\n",
" !python utils/convert_original_stable_diffusion_to_diffusers.py --scheduler_type ddim \\\n",
" --original_config_file v1-inference.yaml \\\n",
" --image_size 512 \\\n",
" --checkpoint_path \"{downloaded_model_path}\" \\\n",
" --prediction_type epsilon \\\n",
" --upcast_attn False \\\n",
" --dump_path \"ckpt_cache/{ckpt_name}\"\n",
"\n",
"\n",
"print(\"DONE\")"
]
},
{
"cell_type": "markdown",
"id": "f15fcd56-0418-4be1-a5c3-38aa679b1aaf",
"metadata": {},
"source": [
"# Start Training\n",
"Naming your project will help you track what the heck you're doing when you're floating in checkpoint files later.\n",
"\n",
"You may wish to consider adding \"sd1\" or \"sd2v\" or similar to remember what the base was, as you'll also have to tell your inference app what you were using, as its difficult for programs to know what inference YAML to use automatically. For instance, Automatic1111 webui requires you to copy the v2 inference YAML and rename it to match your checkpoint name so it knows how to load the file, tough it assumes SD 1.x compatible. Something to keep in mind if you start training on SD2.1.\n",
"\n",
"`max_epochs`, `sample_steps`, and `save_every_n_epochs` should be tuned to your dataset. I like to generate one or two sets of samples per save, and aim for 5 (give or take 2) saved checkpoints.\n",
"\n",
"Next cell runs training. This will take a while depending on your number of images, repeats, and max_epochs.\n",
"\n",
"You can watch for test images in the logs folder."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "6f73fb86-ebef-41e2-9382-4aa11be84be6",
"metadata": {
"scrolled": true,
"tags": []
},
2023-01-12 16:28:11 -07:00
"outputs": [],
"source": [
2023-02-08 17:38:04 -07:00
"%cd /workspace/EveryDream2trainer\n",
"!python train.py --project_name \"sd1_mymodel_000\" \\\n",
"--resume_ckpt \"sd_v1-5_vae\" \\\n",
2023-01-12 16:28:11 -07:00
"--data_root \"input\" \\\n",
"--resolution 512 \\\n",
2023-02-08 17:38:04 -07:00
"--batch_size 8 \\\n",
"--max_epochs 100 \\\n",
"--save_every_n_epochs 50 \\\n",
"--lr 1.8e-6 \\\n",
"--lr_scheduler cosine \\\n",
"--sample_steps 250 \\\n",
2023-01-12 16:28:11 -07:00
"--useadam8bit \\\n",
"--save_full_precision \\\n",
"--shuffle_tags \\\n",
"--amp \\\n",
2023-02-08 17:38:04 -07:00
"--write_schedule\n",
"\n",
"!python train.py --project_name \"sd1_mymodel_100\" \\\n",
"--resume_ckpt \"findlast\" \\\n",
2023-01-12 16:28:11 -07:00
"--data_root \"input\" \\\n",
"--resolution 512 \\\n",
"--batch_size 4 \\\n",
2023-02-08 17:38:04 -07:00
"--max_epochs 100 \\\n",
"--save_every_n_epochs 20 \\\n",
"--lr 1.0e-6 \\\n",
2023-01-12 16:28:11 -07:00
"--lr_scheduler constant \\\n",
2023-02-08 17:38:04 -07:00
"--sample_steps 150 \\\n",
2023-01-12 16:28:11 -07:00
"--useadam8bit \\\n",
"--save_full_precision \\\n",
"--shuffle_tags \\\n",
"--amp \\\n",
2023-02-08 17:38:04 -07:00
"--write_schedule"
2023-01-12 16:28:11 -07:00
]
},
{
"cell_type": "markdown",
"id": "f24eee3d-f5df-45f3-9acc-ee0206cfe6b1",
"metadata": {},
"source": [
"# HuggingFace upload\n",
"Use the cell below to upload one or more checkpoints to your personal HuggingFace account, if you want, instead of manually downloading. You should already be authorized to Huggingface by token if you used the download/token cells above.\n",
2023-01-12 16:28:11 -07:00
"\n",
"* You can get your account name from your [HuggingFace account page](https://huggingface.co/settings/account). Look for your \"username\" field and paste it below.\n",
"\n",
"* You only need to setup a repository one time. You can create it here: [Create New HF Model](https://huggingface.co/new) Make sure you write down the repo name you make for future use. You can reuse it later."
2023-01-12 16:28:11 -07:00
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "b9df5e1a-3c68-41c0-a4ed-ea0abcd19858",
2023-01-12 16:28:11 -07:00
"metadata": {},
"outputs": [],
"source": [
"import glob\n",
"import os\n",
"from huggingface_hub import HfApi\n",
"from ipywidgets import *\n",
"\n",
"all_ckpts = [f for f in glob.glob(\"*.ckpt\")]\n",
" \n",
"ckpt_picker = SelectMultiple(options=all_ckpts, layout=Layout(width=\"600px\")) \n",
"hfuser = Text(placeholder='Your HF user name')\n",
"hfrepo = Text(placeholder='Your HF repo name')\n",
2023-01-12 16:28:11 -07:00
"\n",
"api = HfApi()\n",
"upload_btn = Button(description='Upload', layout=full_width)\n",
"out = Output()\n",
"\n",
"def upload_ckpts(_):\n",
" repo_id=f\"{hfuser.value}/{hfrepo.value}\"\n",
" with out:\n",
" for ckpt in ckpt_picker.value:\n",
" print(f\"Uploading to HF: huggingface.co/{repo_id}/{ckpt}\")\n",
" response = api.upload_file(\n",
" path_or_fileobj=ckpt,\n",
" path_in_repo=ckpt,\n",
" repo_id=repo_id,\n",
" repo_type=None,\n",
" create_pr=1,\n",
" )\n",
" display(response)\n",
" print(\"DONE\")\n",
" print(\"Go to your repo and accept the PRs this created to see your files\")\n",
"\n",
"upload_btn.on_click(upload_ckpts)\n",
"box = VBox([ckpt_picker, HBox([hfuser, hfrepo]), upload_btn, out])\n",
"\n",
"display(box)"
]
},
{
"cell_type": "markdown",
"id": "c1a00d16-9b84-492f-8e6a-defe71e82b43",
"metadata": {},
"source": [
"# Test inference on your checkpoints"
2023-01-12 16:28:11 -07:00
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "efb1a8cd-6a04-44e5-a770-c23ee247ce82",
2023-01-12 16:28:11 -07:00
"metadata": {},
"outputs": [],
"source": [
"%cd /workspace/EveryDream2trainer\n",
"from ipywidgets import *\n",
"from IPython.display import display, clear_output\n",
2023-01-12 16:28:11 -07:00
"import os\n",
"import gc\n",
"import random\n",
"import torch\n",
"import inspect\n",
"\n",
"from torch import autocast\n",
"from diffusers import StableDiffusionPipeline, AutoencoderKL, UNet2DConditionModel, DDIMScheduler, DDPMScheduler, PNDMScheduler, EulerAncestralDiscreteScheduler\n",
"from transformers import CLIPTextModel, CLIPTokenizer\n",
"\n",
"\n",
"checkpoints_ts = []\n",
"for root, dirs, files in os.walk(\".\"):\n",
" for file in files:\n",
" if os.path.basename(file) == \"model_index.json\":\n",
" ts = os.path.getmtime(os.path.join(root,file))\n",
" ckpt = root\n",
" checkpoints_ts.append((ts, root))\n",
"\n",
"checkpoints = [ckpt for (_, ckpt) in sorted(checkpoints_ts, reverse=True)]\n",
"full_width = Layout(width='600px')\n",
"half_width = Layout(width='300px')\n",
"\n",
"checkpoint = Dropdown(options=checkpoints, description='Checkpoint:', layout=full_width)\n",
"prompt = Textarea(value='a photo of ', description='Prompt:', layout=full_width)\n",
"height = IntSlider(value=512, min=256, max=768, step=32, description='Height:', layout=half_width)\n",
"width = IntSlider(value=512, min=256, max=768, step=32, description='Width:', layout=half_width)\n",
"cfg = FloatSlider(value=7.0, min=0.0, max=14.0, step=0.2, description='CFG Scale:', layout=half_width)\n",
"steps = IntSlider(value=30, min=10, max=100, description='Steps:', layout=half_width)\n",
"seed = IntText(value=-1, description='Seed:', layout=half_width)\n",
"generate_btn = Button(description='Generate', layout=full_width)\n",
"out = Output()\n",
"\n",
"def generate(_):\n",
" with out:\n",
" clear_output()\n",
" display(f\"Loading model {checkpoint.value}\")\n",
" actual_seed = seed.value if seed.value != -1 else random.randint(0, 2**30)\n",
"\n",
" text_encoder = CLIPTextModel.from_pretrained(checkpoint.value, subfolder=\"text_encoder\")\n",
" vae = AutoencoderKL.from_pretrained(checkpoint.value, subfolder=\"vae\")\n",
" unet = UNet2DConditionModel.from_pretrained(checkpoint.value, subfolder=\"unet\")\n",
" tokenizer = CLIPTokenizer.from_pretrained(checkpoint.value, subfolder=\"tokenizer\", use_fast=False)\n",
" scheduler = DDIMScheduler.from_pretrained(checkpoint.value, subfolder=\"scheduler\")\n",
" text_encoder.eval()\n",
" vae.eval()\n",
" unet.eval()\n",
"\n",
" text_encoder.to(\"cuda\")\n",
" vae.to(\"cuda\")\n",
" unet.to(\"cuda\")\n",
"\n",
" pipe = StableDiffusionPipeline(\n",
" vae=vae,\n",
" text_encoder=text_encoder,\n",
" tokenizer=tokenizer,\n",
" unet=unet,\n",
" scheduler=scheduler,\n",
" safety_checker=None, # save vram\n",
" requires_safety_checker=None, # avoid nag\n",
" feature_extractor=None, # must be none of no safety checker\n",
" )\n",
"\n",
" pipe.scheduler = EulerAncestralDiscreteScheduler.from_config(pipe.scheduler.config)\n",
" \n",
" print(inspect.cleandoc(f\"\"\"\n",
" Prompt: {prompt.value}\n",
" Resolution: {width.value}x{height.value}\n",
" CFG: {cfg.value}\n",
" Steps: {steps.value}\n",
" Seed: {actual_seed}\n",
" \"\"\"))\n",
" with autocast(\"cuda\"):\n",
" image = pipe(prompt.value, \n",
" generator=torch.Generator(\"cuda\").manual_seed(actual_seed),\n",
" num_inference_steps=steps.value, \n",
" guidance_scale=cfg.value,\n",
" width=width.value,\n",
" height=height.value\n",
" ).images[0]\n",
" del pipe\n",
" gc.collect()\n",
" with torch.cuda.device(\"cuda\"):\n",
" torch.cuda.empty_cache()\n",
" torch.cuda.ipc_collect()\n",
" display(image)\n",
" \n",
"generate_btn.on_click(generate)\n",
"box = VBox(\n",
" children=[\n",
" checkpoint, prompt, \n",
" HBox([VBox([width, height]), VBox([steps, cfg])]), \n",
" seed, \n",
" generate_btn, \n",
" out]\n",
2023-01-12 16:28:11 -07:00
")\n",
"\n",
"\n",
"display(box)"
2023-01-12 16:28:11 -07:00
]
}
],
"metadata": {
"kernelspec": {
2023-02-08 17:38:04 -07:00
"display_name": "Python 3",
2023-01-12 16:28:11 -07:00
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
2023-02-08 17:38:04 -07:00
"version": "3.6.10"
2023-01-12 16:28:11 -07:00
},
"vscode": {
"interpreter": {
"hash": "2e677f113ff5b533036843965d6e18980b635d0aedc1c5cebd058006c5afc92a"
}
}
},
"nbformat": 4,
"nbformat_minor": 5
}