From 9740b9d2176c0ca5c4d932966ee86d9dda586f1c Mon Sep 17 00:00:00 2001 From: Vladimir Mandic Date: Thu, 22 Jun 2023 07:46:48 -0400 Subject: [PATCH] new training and models interface --- CHANGELOG.md | 9 +- cli/image-watermark.py | 2 +- cli/options.py | 2 +- .../ScuNET/scripts/scunet_model.py | 4 +- .../SwinIR/scripts/swinir_model.py | 4 +- .../multidiffusion-upscaler-for-automatic1111 | 2 +- extensions-builtin/sd-dynamic-thresholding | 2 +- extensions-builtin/sd-extension-system-info | 2 +- extensions-builtin/sd-webui-agent-scheduler | 2 +- extensions-builtin/sd-webui-controlnet | 2 +- html/locale_en.json | 158 ++++--- installer.py | 17 +- javascript/set-hints.js | 20 +- javascript/style.css | 2 + javascript/textualInversion.js | 8 +- modules/extras.py | 158 ++++++- modules/hypernetworks/hypernetwork.py | 6 +- modules/hypernetworks/ui.py | 3 - modules/shared.py | 8 +- modules/ui.py | 437 +----------------- modules/ui_common.py | 16 + modules/ui_extensions.py | 4 +- modules/ui_models.py | 163 +++++++ modules/ui_train.py | 360 +++++++++++++++ wiki | 2 +- 25 files changed, 844 insertions(+), 549 deletions(-) create mode 100644 modules/ui_models.py create mode 100644 modules/ui_train.py diff --git a/CHANGELOG.md b/CHANGELOG.md index 84e710afa..7294aa036 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,14 +1,15 @@ # Change Log for SD.Next -## Update for 06/19/2023 +## Update for 06/20/2023 This one is less relevant for standard users, but pretty major if you're running an actual server -But even if not, it still includes bunch of cumulative fixes since last release... +But even if not, it still includes bunch of cumulative fixes since last release - and going by number of new issues, this is probably the most stable release so far... +(next one is not going to be as stable, but it will be fun :) ) - minor improvements to extra networks ui - more hints/tooltips integrated into ui -- decidated api server - - still in developent, but highly promising for high throughput server +- new decidated api server + - but highly promising for high throughput server - improve server logging and monitoring with - server log file rotation - ring buffer with api endpoint `/sdapi/v1/log` diff --git a/cli/image-watermark.py b/cli/image-watermark.py index 7e6c4b288..a3bed1090 100755 --- a/cli/image-watermark.py +++ b/cli/image-watermark.py @@ -111,7 +111,7 @@ def watermark(params, file): if __name__ == '__main__': parser = argparse.ArgumentParser(description = 'image watermarking') parser.add_argument('command', choices = ['read', 'write']) - parser.add_argument('--wm', type=str, required=False, default='mm', help='watermark string') + parser.add_argument('--wm', type=str, required=False, default='sdnext', help='watermark string') parser.add_argument('--strip', default=False, action='store_true', help = "strip existing exif data") parser.add_argument('--verify', default=False, action='store_true', help = "verify watermark during write") parser.add_argument('--length', type=int, default=16, help="watermark length in bits") diff --git a/cli/options.py b/cli/options.py index 7bd9ebb73..2ec676637 100644 --- a/cli/options.py +++ b/cli/options.py @@ -99,7 +99,7 @@ lora = Map({ "text_encoder_lr": 5e-05, "train_batch_size": 1, "train_data_dir": "", - "training_comment": "mood-magic", + "training_comment": "", "unet_lr": 1e-04, "use_8bit_adam": False, "v_parameterization": False, diff --git a/extensions-builtin/ScuNET/scripts/scunet_model.py b/extensions-builtin/ScuNET/scripts/scunet_model.py index 45d9297b6..239307f89 100644 --- a/extensions-builtin/ScuNET/scripts/scunet_model.py +++ b/extensions-builtin/ScuNET/scripts/scunet_model.py @@ -142,8 +142,8 @@ def on_ui_settings(): import gradio as gr from modules import shared - shared.opts.add_option("SCUNET_tile", shared.OptionInfo(256, "Tile size for SCUNET upscalers.", gr.Slider, {"minimum": 0, "maximum": 512, "step": 16}, section=('upscaling', "Upscaling")).info("0 = no tiling")) - shared.opts.add_option("SCUNET_tile_overlap", shared.OptionInfo(8, "Tile overlap for SCUNET upscalers.", gr.Slider, {"minimum": 0, "maximum": 64, "step": 1}, section=('upscaling', "Upscaling")).info("Low values = visible seam")) + shared.opts.add_option("SCUNET_tile", shared.OptionInfo(256, "Tile size for SCUNET upscalers", gr.Slider, {"minimum": 0, "maximum": 512, "step": 16}, section=('upscaling', "Upscaling")).info("0 = no tiling")) + shared.opts.add_option("SCUNET_tile_overlap", shared.OptionInfo(8, "Tile overlap for SCUNET upscalers", gr.Slider, {"minimum": 0, "maximum": 64, "step": 1}, section=('upscaling', "Upscaling")).info("Low values = visible seam")) script_callbacks.on_ui_settings(on_ui_settings) diff --git a/extensions-builtin/SwinIR/scripts/swinir_model.py b/extensions-builtin/SwinIR/scripts/swinir_model.py index c3c78ac82..cd8ddb08c 100644 --- a/extensions-builtin/SwinIR/scripts/swinir_model.py +++ b/extensions-builtin/SwinIR/scripts/swinir_model.py @@ -173,8 +173,8 @@ def inference(img, model, tile, tile_overlap, window_size, scale): def on_ui_settings(): import gradio as gr - shared.opts.add_option("SWIN_tile", shared.OptionInfo(192, "Tile size for all SwinIR.", gr.Slider, {"minimum": 16, "maximum": 512, "step": 16}, section=('upscaling', "Upscaling"))) - shared.opts.add_option("SWIN_tile_overlap", shared.OptionInfo(8, "Tile overlap, in pixels for SwinIR. Low values = visible seam.", gr.Slider, {"minimum": 0, "maximum": 48, "step": 1}, section=('upscaling', "Upscaling"))) + shared.opts.add_option("SWIN_tile", shared.OptionInfo(192, "Tile size for all SwinIR", gr.Slider, {"minimum": 16, "maximum": 512, "step": 16}, section=('upscaling', "Upscaling"))) + shared.opts.add_option("SWIN_tile_overlap", shared.OptionInfo(8, "Tile overlap, in pixels for SwinIR. Low values = visible seam", gr.Slider, {"minimum": 0, "maximum": 48, "step": 1}, section=('upscaling', "Upscaling"))) script_callbacks.on_ui_settings(on_ui_settings) diff --git a/extensions-builtin/multidiffusion-upscaler-for-automatic1111 b/extensions-builtin/multidiffusion-upscaler-for-automatic1111 index b81e80570..de4888103 160000 --- a/extensions-builtin/multidiffusion-upscaler-for-automatic1111 +++ b/extensions-builtin/multidiffusion-upscaler-for-automatic1111 @@ -1 +1 @@ -Subproject commit b81e80570579c6166131320509914d50e13f833c +Subproject commit de4888103c03d88c0d32af6d453373b47291687a diff --git a/extensions-builtin/sd-dynamic-thresholding b/extensions-builtin/sd-dynamic-thresholding index 023d3e51b..fa12a88ea 160000 --- a/extensions-builtin/sd-dynamic-thresholding +++ b/extensions-builtin/sd-dynamic-thresholding @@ -1 +1 @@ -Subproject commit 023d3e51b1a96bd2cc7d6e9ec5229b08e41da730 +Subproject commit fa12a88ea071d83c654a2bc67c05fef5c5f3bb0f diff --git a/extensions-builtin/sd-extension-system-info b/extensions-builtin/sd-extension-system-info index 47df4cccb..14d5b61ae 160000 --- a/extensions-builtin/sd-extension-system-info +++ b/extensions-builtin/sd-extension-system-info @@ -1 +1 @@ -Subproject commit 47df4cccb8003ceeb22566ac38c56a0b41819806 +Subproject commit 14d5b61ae776ed7a19140deea4189ce8a105bd4b diff --git a/extensions-builtin/sd-webui-agent-scheduler b/extensions-builtin/sd-webui-agent-scheduler index 45dfe5977..b0a8d30c5 160000 --- a/extensions-builtin/sd-webui-agent-scheduler +++ b/extensions-builtin/sd-webui-agent-scheduler @@ -1 +1 @@ -Subproject commit 45dfe5977ea43a14f61f46d572615efe169ca2be +Subproject commit b0a8d30c5a7443ee19576362a84c686669699eea diff --git a/extensions-builtin/sd-webui-controlnet b/extensions-builtin/sd-webui-controlnet index 5fc952793..41011bea0 160000 --- a/extensions-builtin/sd-webui-controlnet +++ b/extensions-builtin/sd-webui-controlnet @@ -1 +1 @@ -Subproject commit 5fc952793aa97bdd0078574b3be8bc98e1fcf2cd +Subproject commit 41011bea0490d1c61decef867d76d54f631dc28a diff --git a/html/locale_en.json b/html/locale_en.json index 1a261a8a9..4c79c7587 100644 --- a/html/locale_en.json +++ b/html/locale_en.json @@ -1,30 +1,45 @@ -{ "icons": [ +{"icons": [ {"id":"","label":"📘","localized":"","hint":"Read generation parameters from prompt or last generation if prompt is empty into user interface"}, {"id":"","label":"🚮","localized":"","hint":"Clear prompt"}, {"id":"","label":"🌐","localized":"","hint":"Show/hide extra networks"}, {"id":"","label":"🧳","localized":"","hint":"Apply selected styles to current prompt"}, - {"id":"","label":"🛅","localized":"","hint":"Save style"}, + {"id":"","label":"🛅","localized":"","hint":"Save current prompt as style template"}, {"id":"","label":"🔄","localized":"","hint":"Refresh"}, {"id":"","label":"❌","localized":"","hint":"Close"}, {"id":"","label":"📒","localized":"","hint":"Fill"}, {"id":"","label":"🎲️","localized":"","hint":"Use random seed"}, {"id":"","label":"♻️","localized":"","hint":"Reuse previous seed"}, - {"id":"","label":"⇅","localized":"","hint":"Switch values"} + {"id":"","label":"⇅","localized":"","hint":"Swap image height and width values"} ], "prompts": [ {"id":"","label":"Prompt","localized":"","hint":"Type what you want to see in the image"}, {"id":"","label":"Negative prompt","localized":"","hint":"Type what you DON'T want to see in the image"} ], +"common keywords": [ + {"id":"","label":"fp16","localized":"","hint":"Number representation in 16-bit floating point format"}, + {"id":"","label":"fp32","localized":"","hint":"Number representation in 32-bit floating point format"}, + {"id":"","label":"bf16","localized":"","hint":"Number representation in alternative 16-bit floating point format"}, + {"id":"","label":"Run","localized":"","hint":""}, + {"id":"","label":"all","localized":"","hint":""}, + {"id":"","label":"none","localized":"","hint":""}, + {"id":"","label":"disabled","localized":"","hint":""} +], "tabs": [ - {"id":"","label":"From Text ","localized":"","hint":"Create image from text"}, - {"id":"","label":"From Image ","localized":"","hint":"Create image from image"}, - {"id":"","label":"Process Image ","localized":"","hint":"Process existing image"}, - {"id":"","label":"Train ","localized":"","hint":"Run training or model merging"}, - {"id":"","label":"Settings ","localized":"","hint":"Application settings"}, - {"id":"","label":"Extensions ","localized":"","hint":"Application extensions"} + {"id":"","label":"From Text","localized":"","hint":"Create image from text"}, + {"id":"","label":"From Image","localized":"","hint":"Create image from image"}, + {"id":"","label":"Process Image","localized":"","hint":"Process existing image"}, + {"id":"","label":"Train","localized":"","hint":"Run training or model merging"}, + {"id":"","label":"Models","localized":"","hint":"Convert or merge your models"}, + {"id":"","label":"Interrogator","localized":"","hint":"Run interrogate to get description of your image"}, + {"id":"","label":"System Info","localized":"","hint":"System information and benchmarking"}, + {"id":"","label":"Agent Scheduler","localized":"","hint":"Enqueue your generate requests and run them in the background"}, + {"id":"","label":"Image Browser","localized":"","hint":"Browse through your generated image database"}, + {"id":"","label":"Settings","localized":"","hint":"Application settings"}, + {"id":"","label":"Extensions","localized":"","hint":"Application extensions"} ], "action panel": [ {"id":"","label":"Generate","localized":"","hint":"Start processing"}, + {"id":"","label":"Enqueue","localized":"","hint":"Add task to background queueu in Agent Scheduler"}, {"id":"","label":"Stop","localized":"","hint":"Stop processing"}, {"id":"","label":"Skip","localized":"","hint":"Stop processing current job and continue processing"}, {"id":"","label":"Pause","localized":"","hint":"Pause processing"}, @@ -32,6 +47,11 @@ {"id":"","label":"Interrogate\nDeepBooru","localized":"","hint":"Run interrogate using DeepBooru model"} ], "extra networks": [ + {"id":"","label":"Checkpoints","localized":"","hint":""}, + {"id":"","label":"Lora","localized":"","hint":""}, + {"id":"","label":"LyCORIS","localized":"","hint":""}, + {"id":"","label":"Textual Inversion","localized":"","hint":""}, + {"id":"","label":"Hypernetworks","localized":"","hint":""}, {"id":"","label":"Save preview","localized":"","hint":"Save current image as extra network preview"}, {"id":"","label":"Save description","localized":"","hint":"Save current text as extra network description"}, {"id":"","label":"Read description","localized":"","hint":"Read stored extra network description"} @@ -52,14 +72,20 @@ {"id":"","label":"Install","localized":"","hint":"Install"}, {"id":"","label":"Search","localized":"","hint":"Search"}, {"id":"","label":"Sort by","localized":"","hint":"Sort by"}, - {"id":"","label":"Manage Extensions ","localized":"","hint":"Manage extensions"}, - {"id":"","label":"Manual install ","localized":"","hint":"Manually install extension"}, + {"id":"","label":"Manage extensions","localized":"","hint":"Manage extensions"}, + {"id":"","label":"Manual install","localized":"","hint":"Manually install extension"}, {"id":"","label":"Extension GIT repository URL","localized":"","hint":"Specify extension repository URL on GitHub"}, {"id":"","label":"Specific branch name","localized":"","hint":"Specify extension branch namem, leave blank for default"}, {"id":"","label":"Local directory name","localized":"","hint":"Directory where to install extension, leave blank for default"}, {"id":"","label":"Refresh extension list","localized":"","hint":"Refresh list of available extensions"}, {"id":"","label":"Update installed extensions","localized":"","hint":"Update installed extensions to their latest available version"}, - {"id":"","label":"Apply changes & restart server","localized":"","hint":"Apply all changes and restart server"} + {"id":"","label":"Apply changes & restart server","localized":"","hint":"Apply all changes and restart server"}, + {"id":"","label":"install","localized":"","hint":"install this extension"}, + {"id":"","label":"uninstall","localized":"","hint":"uninstall this extension"}, + {"id":"","label":"User interface defaults","localized":"","hint":"Review and set current values as default values for the user interface"}, + {"id":"","label":"View changes","localized":"","hint":"Review changes between default user interface values and and current values"}, + {"id":"","label":"Set new defaults","localized":"","hint":"Set current values as default values for the user interface"}, + {"id":"","label":"Restore system defaults","localized":"","hint":"Restore default user interface values"} ], "txt2img tab": [ {"id":"","label":"Sampling method","localized":"","hint":"Which algorithm to use to produce the image"}, @@ -88,11 +114,11 @@ {"id":"","label":"Override settings","localized":"","hint":"If you read in generation parameters through 'Process Image tab' and individual generation parameters should deviate from your system settings, this box will be populated with those settings to override your system configuration for this workflow"} ], "process tab": [ - {"id":"","label":"Single Image ","localized":"","hint":"Process single image"}, - {"id":"","label":"Process Batch ","localized":"","hint":"Process batch of images"}, - {"id":"","label":"Process Folder ","localized":"","hint":"Process all images in a folder"}, - {"id":"","label":"Scale by ","localized":"","hint":"Use this tab to resize the source image(s) by a chosen factor"}, - {"id":"","label":"Scale to ","localized":"","hint":"Use this tab to resize the source image(s) to a chosen target size"}, + {"id":"","label":"Single Image","localized":"","hint":"Process single image"}, + {"id":"","label":"Process Batch","localized":"","hint":"Process batch of images"}, + {"id":"","label":"Process Folder","localized":"","hint":"Process all images in a folder"}, + {"id":"","label":"Scale by","localized":"","hint":"Use this tab to resize the source image(s) by a chosen factor"}, + {"id":"","label":"Scale to","localized":"","hint":"Use this tab to resize the source image(s) to a chosen target size"}, {"id":"","label":"Input directory","localized":"","hint":"Folder where the images are that you want to process"}, {"id":"","label":"Output directory","localized":"","hint":"Folder where the processed images should be saved to"}, {"id":"","label":"Show result images","localized":"","hint":"Enable to show the processed images in the image pane"}, @@ -114,38 +140,36 @@ {"id":"sett_reload_sd_model","label":"Reload checkpoint","localized":"","hint":"Reload currently selected model checkpoint"} ], "settings sections": [ - {"id":"","label":"Stable Diffusion ","localized":"","hint":""}, - {"id":"","label":"Compute Settings ","localized":"","hint":""}, - {"id":"","label":"System Paths ","localized":"","hint":""}, - {"id":"","label":"Image Options ","localized":"","hint":""}, - {"id":"","label":"Image Processing ","localized":"","hint":""}, - {"id":"","label":"Output Paths ","localized":"","hint":""}, - {"id":"","label":"User interface ","localized":"","hint":""}, - {"id":"","label":"Live previews ","localized":"","hint":""}, - {"id":"","label":"Sampler Settings ","localized":"","hint":""}, - {"id":"","label":"Postprocessing ","localized":"","hint":""}, - {"id":"","label":"Training ","localized":"","hint":""}, - {"id":"","label":"Interrogate ","localized":"","hint":""}, - {"id":"","label":"Upscaling ","localized":"","hint":""}, - {"id":"","label":"Lora ","localized":"","hint":""}, - {"id":"","label":"Face restoration ","localized":"","hint":""}, - {"id":"","label":"Extra Networks ","localized":"","hint":""}, - {"id":"","label":"Token Merging ","localized":"","hint":""}, - {"id":"","label":"Licenses ","localized":"","hint":""}, + {"id":"","label":"Stable Diffusion","localized":"","hint":""}, + {"id":"","label":"Optimizations","localized":"","hint":""}, + {"id":"","label":"Compute Settings","localized":"","hint":""}, + {"id":"","label":"System Paths","localized":"","hint":""}, + {"id":"","label":"Image Options","localized":"","hint":""}, + {"id":"","label":"Image Processing","localized":"","hint":""}, + {"id":"","label":"Output Paths","localized":"","hint":""}, + {"id":"","label":"User Interface","localized":"","hint":""}, + {"id":"","label":"Live Previews","localized":"","hint":""}, + {"id":"","label":"Sampler Settings","localized":"","hint":""}, + {"id":"","label":"Postprocessing","localized":"","hint":""}, + {"id":"","label":"Training","localized":"","hint":""}, + {"id":"","label":"Interrogate","localized":"","hint":""}, + {"id":"","label":"Upscaling","localized":"","hint":""}, + {"id":"","label":"Extra Networks","localized":"","hint":""}, + {"id":"","label":"Licenses","localized":"","hint":""}, {"id":"","label":"Show all pages","localized":"","hint":""}, {"id":"","label":"Request browser notifications","localized":"","hint":""} ], "img2img tabs": [ - {"id":"","label":"Image ","localized":"","hint":""}, - {"id":"","label":"Sketch ","localized":"","hint":""}, - {"id":"","label":"Inpaint ","localized":"","hint":""}, - {"id":"","label":"Inpaint sketch ","localized":"","hint":""}, - {"id":"","label":"Inpaint upload ","localized":"","hint":""}, - {"id":"","label":"Batch ","localized":"","hint":""} + {"id":"","label":"Image","localized":"","hint":""}, + {"id":"","label":"Sketch","localized":"","hint":""}, + {"id":"","label":"Inpaint","localized":"","hint":""}, + {"id":"","label":"Inpaint sketch","localized":"","hint":""}, + {"id":"","label":"Inpaint upload","localized":"","hint":""}, + {"id":"","label":"Batch","localized":"","hint":""} ], "img2img tab": [ - {"id":"","label":"Inpaint Batch input directory","localized":"","hint":""}, - {"id":"","label":"Inpaint Batch output directory","localized":"","hint":""}, + {"id":"","label":"Inpaint batch input directory","localized":"","hint":""}, + {"id":"","label":"Inpaint batch output directory","localized":"","hint":""}, {"id":"","label":"Inpaint batch mask directory","localized":"","hint":""}, {"id":"","label":"Resize fixed","localized":"","hint":"Resize image to target resolution. Unless height and width match, you will get incorrect aspect ratio"}, {"id":"","label":"Crop and resize","localized":"","hint":"Resize the image so that entirety of target resolution is filled with the image. Crop parts that stick out"}, @@ -166,31 +190,32 @@ {"id":"","label":"Unused","localized":"","hint":""}, {"id":"","label":"Image CFG Scale","localized":"","hint":""} ], -"train tabs": [ - {"id":"","label":"Merge models ","localized":"","hint":""}, - {"id":"","label":"Create embedding ","localized":"","hint":""}, - {"id":"","label":"Create hypernetwork ","localized":"","hint":""}, - {"id":"","label":"Preprocess images ","localized":"","hint":""}, +"models tabs": [ + {"id":"","label":"Convert","localized":"","hint":""}, {"id":"","label":"Merge","localized":"","hint":""}, - {"id":"","label":"Calculate hash for all models (may take a long time)","localized":"","hint":""}, - {"id":"","label":"Create embedding","localized":"","hint":""}, - {"id":"","label":"Create hypernetwork","localized":"","hint":""}, + {"id":"","label":"Validate","localized":"","hint":""}, + {"id":"","label":"List model details","localized":"","hint":""}, + {"id":"","label":"Calculate hash for all models (may take a long time)","localized":"","hint":""} +], +"train tabs": [ {"id":"","label":"Preprocess","localized":"","hint":""}, - {"id":"","label":"Train Embedding","localized":"","hint":""}, - {"id":"","label":"Train Hypernetwork","localized":"","hint":""} + {"id":"","label":"Preprocess images","localized":"","hint":""}, + {"id":"","label":"Train embedding","localized":"","hint":""}, + {"id":"","label":"Train hypernetwork","localized":"","hint":""}, + {"id":"","label":"Create embedding","localized":"","hint":""}, + {"id":"","label":"Create hypernetwork","localized":"","hint":""} ], "train tab": [ {"id":"","label":"Primary model","localized":"","hint":""}, {"id":"","label":"Secondary model","localized":"","hint":""}, {"id":"","label":"Tertiary model","localized":"","hint":""}, {"id":"","label":"New model name","localized":"","hint":""}, - {"id":"","label":"No interpolation","localized":"","hint":"Result = A"}, - {"id":"","label":"Weighted sum","localized":"","hint":"Result = A * (1 - M) + B * M"}, - {"id":"","label":"Add difference","localized":"","hint":"Result = A + (B - C) * M"}, + {"id":"","label":"No interpolation","localized":"","hint":"Requires one model. No interpolation will be used, allows for format conversion and VAE baking"}, + {"id":"","label":"Weighted sum","localized":"","hint":"Requires two models. Weighted sum will be used for interpolation, result is calculated as A * (1 - M) + B * M"}, + {"id":"","label":"Add difference","localized":"","hint":"Requires three models. Difference between the last two models will be added to the first, result is calculated as A + (B - C) * M"}, {"id":"","label":"Interpolation ratio from Primary to Secondary","localized":"","hint":""}, {"id":"","label":"ckpt","localized":"","hint":""}, {"id":"","label":"safetensors","localized":"","hint":""}, - {"id":"","label":"Use FP16","localized":"","hint":""}, {"id":"","label":"Save metadata","localized":"","hint":""}, {"id":"","label":"Primary","localized":"","hint":""}, {"id":"","label":"Secondary","localized":"","hint":""}, @@ -210,7 +235,7 @@ {"id":"","label":"Overwrite Old Hypernetwork","localized":"","hint":""}, {"id":"","label":"Source directory","localized":"","hint":""}, {"id":"","label":"Destination directory","localized":"","hint":""}, - {"id":"","label":"Existing Caption txt Action","localized":"","hint":""}, + {"id":"","label":"Existing caption text action","localized":"","hint":""}, {"id":"","label":"Keep original size","localized":"","hint":""}, {"id":"","label":"Keep original image channels","localized":"","hint":""}, {"id":"","label":"Create flipped copies","localized":"","hint":""}, @@ -233,7 +258,6 @@ {"id":"","label":"Maximize area","localized":"","hint":""}, {"id":"","label":"Minimize error","localized":"","hint":""}, {"id":"","label":"Error threshold","localized":"","hint":""}, - {"id":"","label":"Embedding","localized":"","hint":""}, {"id":"","label":"Hypernetwork","localized":"","hint":""}, {"id":"","label":"Embedding Learning rate","localized":"","hint":""}, {"id":"","label":"Hypernetwork Learning rate","localized":"","hint":""}, @@ -249,7 +273,7 @@ {"id":"","label":"Use PNG alpha channel as loss weight","localized":"","hint":""}, {"id":"","label":"Save images with embedding in PNG chunks","localized":"","hint":""}, {"id":"","label":"Use current settings for previews","localized":"","hint":"Read parameters (prompt, etc...) from txt2img tab when making previews"}, - {"id":"","label":"Shuffle tags by ',' when creating prompts","localized":"","hint":""}, + {"id":"","label":"Shuffle tags","localized":"","hint":"Shuffle tags by ',' when creating prompts"}, {"id":"","label":"Drop out tags when creating prompts","localized":"","hint":""}, {"id":"","label":"once","localized":"","hint":""}, {"id":"","label":"deterministic","localized":"","hint":""}, @@ -296,7 +320,7 @@ {"id":"","label":"Enable upcast cross attention layer","localized":"","hint":""}, {"id":"","label":"Disable NaN check in produced images/latent spaces","localized":"","hint":""}, {"id":"","label":"Attempt VAE roll back when produced NaN values (experimental)","localized":"","hint":"Requires Torch 2.1 and NaN check enabled"}, - {"id":"","label":"Use channels last as torch memory format ","localized":"","hint":""}, + {"id":"","label":"Use channels last as torch memory format","localized":"","hint":""}, {"id":"","label":"Enable full-depth cuDNN benchmark feature","localized":"","hint":""}, {"id":"","label":"Allow TF32 math ops","localized":"","hint":""}, {"id":"","label":"Allow TF16 reduced precision math ops","localized":"","hint":""}, @@ -480,7 +504,7 @@ {"id":"","label":"Tile size for ESRGAN upscalers","localized":"","hint":"0 = no tiling"}, {"id":"","label":"Tile overlap in pixels for ESRGAN upscalers","localized":"","hint":"Low values = visible seam"}, {"id":"","label":"Tile size for SCUNET upscalers","localized":"","hint":"0 = no tiling"}, - {"id":"","label":"Tile overlap, in pixels for SCUNET upscalers","localized":"","hint":" Low values = visible seam"}, + {"id":"","label":"Tile overlap for SCUNET upscalers","localized":"","hint":" Low values = visible seam"}, {"id":"","label":"Hires fix uses width & height to set final resolution","localized":"","hint":"Hires fix uses width & height to set final resolution rather than first pass"}, {"id":"","label":"Do not fix prompt schedule for second order samplers","localized":"","hint":""}, {"id":"","label":"Use LyCoris handler for all Lora types","localized":"","hint":""}, @@ -496,18 +520,16 @@ {"id":"","label":"Add hypernetwork to prompt","localized":"","hint":""}, {"id":"","label":"Token merging ratio","localized":"","hint":"Enable redundant token merging via tomesd for speed and memory improvements, 0=disabled"}, {"id":"","label":"Token merging ratio for img2img","localized":"","hint":"Enable redundant token merging for img2img via tomesd for speed and memory improvements, 0=disabled"}, - {"id":"","label":"Token merging ratio for hires pass","localized":"","hint":"Enable redundant token merging for hires pass via tomesd for speed and memory improvements, 0=disabled"}, - {"id":"","label":"Stride - X","localized":"","hint":""}, - {"id":"","label":"Stride - Y","localized":"","hint":""} + {"id":"","label":"Token merging ratio for hires pass","localized":"","hint":"Enable redundant token merging for hires pass via tomesd for speed and memory improvements, 0=disabled"} ], "scripts": [ {"id":"","label":"Script","localized":"","hint":""}, {"id":"","label":"Swap X/Y axes","localized":"","hint":""}, {"id":"","label":"Swap Y/Z axes","localized":"","hint":""}, {"id":"","label":"Swap X/Z axes","localized":"","hint":""}, - {"id":"","label":"Resize to ","localized":"","hint":""}, - {"id":"","label":"Resize by ","localized":"","hint":""}, - {"id":"","label":"Use via API ","localized":"","hint":""}, + {"id":"","label":"Resize to","localized":"","hint":""}, + {"id":"","label":"Resize by","localized":"","hint":""}, + {"id":"","label":"Use via API","localized":"","hint":""}, {"id":"","label":"Stable Diffusion checkpoint","localized":"","hint":""}, {"id":"","label":"Styles","localized":"","hint":""}, {"id":"","label":"Put variable parts at start of prompt","localized":"","hint":""}, diff --git a/installer.py b/installer.py index 49ea01418..42fc6c0eb 100644 --- a/installer.py +++ b/installer.py @@ -81,10 +81,7 @@ def setup_logging(): "traceback.border.syntax_error": "black", "inspect.value.border": "black", })) - try: - logging.basicConfig(level=logging.ERROR, format='%(asctime)s | %(name)s | %(levelname)s | %(module)s | %(message)s', filename=log_file, filemode='a', encoding='utf-8', force=True) - except Exception: - logging.basicConfig(level=logging.ERROR, format='%(asctime)s | %(name)s | %(levelname)s | %(module)s | %(message)s') # to be able to report unsupported python version + logging.basicConfig(level=logging.ERROR, format='%(asctime)s | %(name)s | %(levelname)s | %(module)s | %(message)s', handlers=[logging.NullHandler()]) # redirect default logger to null pretty_install(console=console) traceback_install(console=console, extra_lines=1, width=console.width, word_wrap=False, indent_guides=False, suppress=[]) while log.hasHandlers() and len(log.handlers) > 0: @@ -109,6 +106,7 @@ def setup_logging(): logging.getLogger("urllib3").setLevel(logging.ERROR) logging.getLogger("httpx").setLevel(logging.ERROR) logging.getLogger("ControlNet").handlers = log.handlers + logging.getLogger("lycoris").handlers = log.handlers def print_profile(profile: cProfile.Profile, msg: str): @@ -387,7 +385,7 @@ def check_modified_files(): try: res = git('status --porcelain') files = [x[2:].strip() for x in res.split('\n')] - files = [x for x in files if len(x) > 0 and not x.startswith('extensions') and not x.startswith('wiki') and not x.endswith('.json')] + files = [x for x in files if len(x) > 0 and (not x.startswith('extensions')) and (not x.startswith('wiki')) and (not x.endswith('.json')) and (not '.log' in x)] if len(files) > 0: log.warning(f'Modified files: {files}') except Exception: @@ -406,7 +404,7 @@ def install_packages(): # install(openclip_package, 'open-clip-torch') clip_package = os.environ.get('CLIP_PACKAGE', "git+https://github.com/openai/CLIP.git") install(clip_package, 'clip') - install('onnxruntime==1.14.0', 'onnxruntime', ignore=True) + install('onnxruntime==1.15.1', 'onnxruntime', ignore=True) if args.profile: print_profile(pr, 'Packages') @@ -495,8 +493,6 @@ def install_extensions(): extensions_duplicates = [] extensions_enabled = [] extension_folders = [extensions_builtin_dir] if args.safe else [extensions_builtin_dir, extensions_dir] - if args.base: - extension_folders = [] for folder in extension_folders: if not os.path.isdir(folder): continue @@ -610,8 +606,6 @@ def check_extensions(): newest_all = os.path.getmtime('requirements.txt') from modules.paths_internal import extensions_builtin_dir, extensions_dir extension_folders = [extensions_builtin_dir] if args.safe else [extensions_builtin_dir, extensions_dir] - if args.base: - extension_folders = [] for folder in extension_folders: if not os.path.isdir(folder): continue @@ -745,7 +739,6 @@ def add_args(parser): group.add_argument('--version', default = False, action='store_true', help = "Print version information") group.add_argument('--ignore', default = False, action='store_true', help = "Ignore any errors and attempt to continue") group.add_argument('--safe', default = False, action='store_true', help = "Run in safe mode with no user extensions") - group.add_argument('--base', default = False, action='store_true', help = argparse.SUPPRESS) def parse_args(parser): @@ -765,8 +758,6 @@ def extensions_preload(parser): from modules.script_loading import preload_extensions from modules.paths_internal import extensions_builtin_dir, extensions_dir extension_folders = [extensions_builtin_dir] if args.safe else [extensions_builtin_dir, extensions_dir] - if args.base: - extension_folders = [] for ext_dir in extension_folders: t0 = time.time() preload_extensions(ext_dir, parser) diff --git a/javascript/set-hints.js b/javascript/set-hints.js index 3bbfb87b3..d62263c87 100644 --- a/javascript/set-hints.js +++ b/javascript/set-hints.js @@ -28,6 +28,23 @@ async function tooltipHide(e) { locale.el.classList.remove('tooltip-show'); } +async function validateHints(elements, data) { + let original = elements.map(e => e.textContent.trim()).sort((a, b) => a > b) + original = [...new Set(original)]; + console.log('hints-differences', { elements: original.length, hints: data.length }); + const current = data.map(e => e.label).sort((a, b) => a > b) + let missing = []; + for (let i = 0; i < original.length; i++) { + if (!current.includes(original[i])) missing.push(original[i]); + } + console.log('missing in locale:', missing) + missing = []; + for (let i = 0; i < current.length; i++) { + if (!original.includes(current[i])) missing.push(current[i]); + } + console.log('in locale but not ui:', missing) +} + async function setHints() { if (locale.finished) return; if (locale.data.length === 0) { @@ -46,7 +63,7 @@ async function setHints() { let hints = 0; locale.finished = true; for (el of elements) { - const found = locale.data.find(l => l.label === el.textContent); + const found = locale.data.find(l => l.label === el.textContent.trim()); if (found?.localized?.length > 0) { localized++; el.textContent = found.localized; @@ -65,6 +82,7 @@ async function setHints() { } } console.log('set-hints', { type: locale.type, elements: elements.length, localized, hints, data: locale.data.length }); + // validateHints(elements, locale.data) } onAfterUiUpdate(async () => { diff --git a/javascript/style.css b/javascript/style.css index d5ddac44e..15aa476fe 100644 --- a/javascript/style.css +++ b/javascript/style.css @@ -666,3 +666,5 @@ div.controlnet_main_options { display: grid; grid-template-columns: 1fr 1fr; gri #extras_generate, #extras_interrupt, #extras_skip { display: block !important; position: relative; height: 36px; } #extras_upscale { margin-top: 10px } #refresh_tac_refreshTempFiles { display: none; } +#train_tab { flex-flow: row-reverse; } +#models_tab { flex-flow: row-reverse; } diff --git a/javascript/textualInversion.js b/javascript/textualInversion.js index b6717859e..9957550a5 100644 --- a/javascript/textualInversion.js +++ b/javascript/textualInversion.js @@ -1,8 +1,8 @@ -function start_training_textual_inversion() { - gradioApp().querySelector('#ti_error').innerHTML='' +function start_train_monitoring() { + gradioApp().querySelector('#train_error').innerHTML='' var id = randomId() - const onProgress = (progress) => gradioApp().getElementById('ti_progress').innerHTML = progress.textinfo; - requestProgress(id, gradioApp().getElementById('ti_gallery'), null, onProgress, false) + const onProgress = (progress) => gradioApp().getElementById('train_progress').innerHTML = progress.textinfo; + requestProgress(id, gradioApp().getElementById('train_gallery'), null, onProgress, false) var res = Array.from(arguments); res[0] = id return res diff --git a/modules/extras.py b/modules/extras.py index 4dc50a2c7..a26908cf5 100644 --- a/modules/extras.py +++ b/modules/extras.py @@ -57,6 +57,8 @@ def run_modelmerger(id_task, primary_model_name, secondary_model_name, tertiary_ shared.state.begin() shared.state.job = 'model-merge' + save_as_half = save_as_half == 0 + def fail(message): shared.state.textinfo = message shared.state.end() @@ -108,13 +110,13 @@ def run_modelmerger(id_task, primary_model_name, secondary_model_name, tertiary_ result_is_instruct_pix2pix_model = False if theta_func2: shared.state.textinfo = "Loading B" - shared.log.info(f"Loading {secondary_model_info.filename}...") + shared.log.info(f"Model merge loading secondary model: {secondary_model_info.filename}") theta_1 = sd_models.read_state_dict(secondary_model_info.filename) else: theta_1 = None if theta_func1: shared.state.textinfo = "Loading C" - shared.log.info(f"Loading {tertiary_model_info.filename}...") + shared.log.info(f"Model merge loading tertiary model: {tertiary_model_info.filename}") theta_2 = sd_models.read_state_dict(tertiary_model_info.filename) shared.state.textinfo = 'Merging B and C' shared.state.sampling_steps = len(theta_1.keys()) @@ -131,9 +133,9 @@ def run_modelmerger(id_task, primary_model_name, secondary_model_name, tertiary_ del theta_2 shared.state.nextjob() shared.state.textinfo = f"Loading {primary_model_info.filename}..." - shared.log.info(f"Loading {primary_model_info.filename}...") + shared.log.info(f"Model merge loading primary model: {primary_model_info.filename}") theta_0 = sd_models.read_state_dict(primary_model_info.filename) - shared.log.info("Merging...") + shared.log.info("Model merge: running") shared.state.textinfo = 'Merging A and B' shared.state.sampling_steps = len(theta_0.keys()) for key in tqdm.tqdm(theta_0.keys()): @@ -164,7 +166,7 @@ def run_modelmerger(id_task, primary_model_name, secondary_model_name, tertiary_ del theta_1 bake_in_vae_filename = sd_vae.vae_dict.get(bake_in_vae, None) if bake_in_vae_filename is not None: - shared.log.info(f"Baking in VAE from {bake_in_vae_filename}") + shared.log.info(f"Model merge: baking in VAE: {bake_in_vae_filename}") shared.state.textinfo = 'Baking in VAE' vae_dict = sd_vae.load_vae_dict(bake_in_vae_filename) for key in vae_dict.keys(): @@ -234,7 +236,151 @@ def run_modelmerger(id_task, primary_model_name, secondary_model_name, tertiary_ if created_model: created_model.calculate_shorthash() create_config(output_modelname, config_source, primary_model_info, secondary_model_info, tertiary_model_info) - shared.log.info(f"Checkpoint saved to {output_modelname}.") + shared.log.info(f"Model merge saved: {output_modelname}.") shared.state.textinfo = "Checkpoint saved" shared.state.end() return [*[gr.Dropdown.update(choices=sd_models.checkpoint_tiles()) for _ in range(4)], "Checkpoint saved to " + output_modelname] + +def run_modelconvert(model, checkpoint_formats, precision, conv_type, custom_name, unet_conv, text_encoder_conv, vae_conv, others_conv, fix_clip): + + # position_ids in clip is int64. model_ema.num_updates is int32 + dtypes_to_fp16 = {torch.float32, torch.float64, torch.bfloat16} + dtypes_to_bf16 = {torch.float32, torch.float64, torch.float16} + + def conv_fp16(t: torch.Tensor): + return t.half() if t.dtype in dtypes_to_fp16 else t + + def conv_bf16(t: torch.Tensor): + return t.bfloat16() if t.dtype in dtypes_to_bf16 else t + + def conv_full(t): + return t + + _g_precision_func = { + "full": conv_full, + "fp32": conv_full, + "fp16": conv_fp16, + "bf16": conv_bf16, + } + + def check_weight_type(k: str) -> str: + if k.startswith("model.diffusion_model"): + return "unet" + elif k.startswith("first_stage_model"): + return "vae" + elif k.startswith("cond_stage_model"): + return "clip" + return "other" + + def load_model(path): + if path.endswith(".safetensors"): + m = safetensors.torch.load_file(path, device="cpu") + else: + m = torch.load(path, map_location="cpu") + state_dict = m["state_dict"] if "state_dict" in m else m + return state_dict + + + def fix_model(model, fix_clip=False): + # code from model-toolkit + nai_keys = { + 'cond_stage_model.transformer.embeddings.': 'cond_stage_model.transformer.text_model.embeddings.', + 'cond_stage_model.transformer.encoder.': 'cond_stage_model.transformer.text_model.encoder.', + 'cond_stage_model.transformer.final_layer_norm.': 'cond_stage_model.transformer.text_model.final_layer_norm.' + } + for k in list(model.keys()): + for r in nai_keys: + if type(k) == str and k.startswith(r): + new_key = k.replace(r, nai_keys[r]) + model[new_key] = model[k] + del model[k] + shared.log.warning(f"Model convert: fixed NovelAI error key: {k}") + break + if fix_clip: + i = "cond_stage_model.transformer.text_model.embeddings.position_ids" + if i in model: + correct = torch.Tensor([list(range(77))]).to(torch.int64) + now = model[i].to(torch.int64) + + broken = correct.ne(now) + broken = [i for i in range(77) if broken[0][i]] + model[i] = correct + if len(broken) != 0: + shared.log.warning(f"Model convert: fixed broken CLiP: {broken}") + + return model + + if model == "": + return "Error: you must choose a model" + if len(checkpoint_formats) == 0: + return "Error: at least choose one model save format" + + extra_opt = { + "unet": unet_conv, + "clip": text_encoder_conv, + "vae": vae_conv, + "other": others_conv + } + shared.state.begin() + shared.state.job = 'model-convert' + + model_info = sd_models.checkpoints_list[model] + shared.state.textinfo = f"Loading {model_info.filename}..." + shared.log.info(f"Model convert loading: {model_info.filename}") + state_dict = load_model(model_info.filename) + + ok = {} # {"state_dict": {}} + + conv_func = _g_precision_func[precision] + + def _hf(wk: str, t: torch.Tensor): + if not isinstance(t, torch.Tensor): + return + w_t = check_weight_type(wk) + conv_t = extra_opt[w_t] + if conv_t == "convert": + ok[wk] = conv_func(t) + elif conv_t == "copy": + ok[wk] = t + elif conv_t == "delete": + return + shared.log.info("Model convert: running") + if conv_type == "ema-only": + for k in tqdm.tqdm(state_dict): + ema_k = "___" + try: + ema_k = "model_ema." + k[6:].replace(".", "") + except: + pass + if ema_k in state_dict: + _hf(k, state_dict[ema_k]) + elif not k.startswith("model_ema.") or k in ["model_ema.num_updates", "model_ema.decay"]: + _hf(k, state_dict[k]) + elif conv_type == "no-ema": + for k, v in tqdm.tqdm(state_dict.items()): + if "model_ema." not in k: + _hf(k, v) + else: + for k, v in tqdm.tqdm(state_dict.items()): + _hf(k, v) + + ok = fix_model(ok, fix_clip=fix_clip) + output = "" + ckpt_dir = shared.cmd_opts.ckpt_dir or sd_models.model_path + save_name = f"{model_info.model_name}-{precision}" + if conv_type != "disabled": + save_name += f"-{conv_type}" + if custom_name != "": + save_name = custom_name + for fmt in checkpoint_formats: + ext = ".safetensors" if fmt == "safetensors" else ".ckpt" + _save_name = save_name + ext + save_path = os.path.join(ckpt_dir, _save_name) + shared.log.info(f"Model convert saving: {save_path}") + if fmt == "safetensors": + safetensors.torch.save_file(ok, save_path) + else: + torch.save({"state_dict": ok}, save_path) + output += f"Checkpoint saved to {save_path}
" + shared.state.end() + return output diff --git a/modules/hypernetworks/hypernetwork.py b/modules/hypernetworks/hypernetwork.py index 0453e30d0..18e58abfc 100644 --- a/modules/hypernetworks/hypernetwork.py +++ b/modules/hypernetworks/hypernetwork.py @@ -488,8 +488,8 @@ def create_hypernetwork(name, enable_sizes, overwrite_old, layer_structure=None, dropout_structure=dropout_structure ) hypernet.save(fn) - shared.reload_hypernetworks() + return name def train_hypernetwork(id_task, hypernetwork_name, learn_rate, batch_size, gradient_step, data_root, log_directory, training_width, training_height, varsize, steps, clip_grad_mode, clip_grad_value, shuffle_tags, tag_drop_out, latent_sampling_method, use_weight, create_image_every, save_hypernetwork_every, template_filename, preview_from_txt2img, preview_prompt, preview_negative_prompt, preview_steps, preview_sampler_index, preview_cfg_scale, preview_seed, preview_width, preview_height): @@ -609,7 +609,7 @@ def train_hypernetwork(id_task, hypernetwork_name, learn_rate, batch_size, gradi # previous_mean_loss = 0 # print("Mean loss of {} elements".format(size)) - steps_without_grad = 0 + _steps_without_grad = 0 last_saved_file = "" last_saved_image = "" @@ -756,7 +756,7 @@ def train_hypernetwork(id_task, hypernetwork_name, learn_rate, batch_size, gradi textual_inversion.tensorboard_add_image(tensorboard_writer, f"Validation at epoch {epoch_num}", image, hypernetwork.step) - last_saved_image, last_text_info = images.save_image(image, images_dir, "", p.seed, p.prompt, shared.opts.samples_format, processed.infotexts[0], p=p, forced_filename=forced_filename, save_to_dirs=False) + last_saved_image, _last_text_info = images.save_image(image, images_dir, "", p.seed, p.prompt, shared.opts.samples_format, processed.infotexts[0], p=p, forced_filename=forced_filename, save_to_dirs=False) last_saved_image += f", prompt: {preview_text}" shared.state.job_no = hypernetwork.step diff --git a/modules/hypernetworks/ui.py b/modules/hypernetworks/ui.py index 9b62769e3..2d6acbafb 100644 --- a/modules/hypernetworks/ui.py +++ b/modules/hypernetworks/ui.py @@ -1,7 +1,4 @@ import html -import os -import re - import gradio as gr import modules.hypernetworks.hypernetwork from modules import devices, sd_hijack, shared diff --git a/modules/shared.py b/modules/shared.py index 390d40446..9301edc3f 100644 --- a/modules/shared.py +++ b/modules/shared.py @@ -411,7 +411,7 @@ options_templates.update(options_section(('saving-paths', "Output Paths"), { "outdir_init_images": OptionInfo("outputs/init-images", "Directory for saving init images when using img2img", component_args=hide_dirs), })) -options_templates.update(options_section(('ui', "User interface"), { +options_templates.update(options_section(('ui', "User Interface"), { "gradio_theme": OptionInfo("black-orange", "UI theme", gr.Dropdown, lambda: {"choices": list_themes()}, refresh=refresh_themes), "theme_style": OptionInfo("Auto", "Theme mode", gr.Radio, {"choices": ["Auto", "Dark", "Light"]}), "tooltips": OptionInfo("UI Tooltips", "UI tooltips", gr.Radio, {"choices": ["None", "Browser default", "UI tooltips"]}), @@ -433,14 +433,14 @@ options_templates.update(options_section(('ui', "User interface"), { "ui_extra_networks_tab_reorder": OptionInfo("Checkpoints, Lora, LyCORIS, Textual Inversion, Hypernetworks", "Extra networks tab order"), })) -options_templates.update(options_section(('live-preview', "Live previews"), { +options_templates.update(options_section(('live-preview', "Live Previews"), { "show_progressbar": OptionInfo(True, "Show progressbar"), "live_previews_enable": OptionInfo(True, "Show live previews of the created image"), "show_progress_grid": OptionInfo(True, "Show previews of all images generated in a batch as a grid"), "notification_audio_enable": OptionInfo(False, "Play a sound when images are finished generating"), "notification_audio_path": OptionInfo("html/notification.mp3","Path to notification sound", component_args=hide_dirs), "show_progress_every_n_steps": OptionInfo(1, "Live preview display period", gr.Slider, {"minimum": -1, "maximum": 32, "step": 1}), - "show_progress_type": OptionInfo("TAESD", "Live preview method", gr.Radio, {"choices": ["Full VAE", "Approximate NN", "Approximate simple", "TAESD"]}), + "show_progress_type": OptionInfo("Approximate NN", "Live preview method", gr.Radio, {"choices": ["Full VAE", "Approximate NN", "Approximate simple", "TAESD"]}), "live_preview_content": OptionInfo("Combined", "Live preview subject", gr.Radio, {"choices": ["Combined", "Prompt", "Negative prompt"]}), "live_preview_refresh_period": OptionInfo(250, "Progressbar/preview update period, in milliseconds") })) @@ -511,7 +511,7 @@ options_templates.update(options_section(('upscaling', "Upscaling"), { "ESRGAN_tile": OptionInfo(192, "Tile size for ESRGAN upscalers", gr.Slider, {"minimum": 0, "maximum": 512, "step": 16}), "ESRGAN_tile_overlap": OptionInfo(8, "Tile overlap in pixels for ESRGAN upscalers", gr.Slider, {"minimum": 0, "maximum": 48, "step": 1}), "SCUNET_tile": OptionInfo(256, "Tile size for SCUNET upscalers", gr.Slider, {"minimum": 0, "maximum": 512, "step": 16}), - "SCUNET_tile_overlap": OptionInfo(8, "Tile overlap, in pixels for SCUNET upscalers", gr.Slider, {"minimum": 0, "maximum": 64, "step": 1}), + "SCUNET_tile_overlap": OptionInfo(8, "Tile overlap for SCUNET upscalers", gr.Slider, {"minimum": 0, "maximum": 64, "step": 1}), "use_old_hires_fix_width_height": OptionInfo(False, "Hires fix uses width & height to set final resolution"), "dont_fix_second_order_samplers_schedule": OptionInfo(False, "Do not fix prompt schedule for second order samplers"), })) diff --git a/modules/ui.py b/modules/ui.py index c8633dba5..5b74223b0 100644 --- a/modules/ui.py +++ b/modules/ui.py @@ -10,7 +10,7 @@ import numpy as np from PIL import Image from modules.call_queue import wrap_gradio_gpu_call, wrap_queued_call, wrap_gradio_call -from modules import sd_hijack, sd_models, script_callbacks, ui_extensions, deepbooru, sd_vae, extra_networks, ui_common, ui_postprocessing, ui_loadsave +from modules import sd_hijack, sd_models, script_callbacks, ui_extensions, deepbooru, extra_networks, ui_common, ui_postprocessing, ui_loadsave, ui_train, ui_models from modules.ui_components import FormRow, FormColumn, FormGroup, ToolButton, FormHTML # pylint: disable=unused-import from modules.paths import script_path, data_path from modules.shared import opts, cmd_opts, backend, Backend @@ -26,7 +26,6 @@ import modules.styles import modules.extras import modules.textual_inversion.ui import modules.sd_samplers -from modules.textual_inversion import textual_inversion modules.errors.install() @@ -286,16 +285,7 @@ def apply_setting(key, value): def create_refresh_button(refresh_component, refresh_method, refreshed_args, elem_id): - def refresh(): - refresh_method() - args = refreshed_args() if callable(refreshed_args) else refreshed_args - for k, v in args.items(): - setattr(refresh_component, k, v) - return gr.update(**(args or {})) - - refresh_button = ToolButton(value=refresh_symbol, elem_id=elem_id) - refresh_button.click(fn=refresh, inputs=[], outputs=[refresh_component]) - return refresh_button + return ui_common.create_refresh_button(refresh_component, refresh_method, refreshed_args, elem_id) def create_sampler_and_steps_selection(choices, tabname): @@ -497,17 +487,6 @@ def create_ui(): parameters_copypaste.add_paste_fields("txt2img", None, txt2img_paste_fields, override_settings) parameters_copypaste.register_paste_params_button(parameters_copypaste.ParamBinding(paste_button=txt2img_paste, tabname="txt2img", source_text_component=txt2img_prompt, source_image_component=None)) - txt2img_preview_params = [ - txt2img_prompt, - txt2img_negative_prompt, - steps, - sampler_index, - cfg_scale, - seed, - width, - height, - ] - token_button.click(fn=wrap_queued_call(update_token_counter), inputs=[txt2img_prompt, steps], outputs=[token_counter]) negative_token_button.click(fn=wrap_queued_call(update_token_counter), inputs=[txt2img_negative_prompt, steps], outputs=[negative_token_counter]) @@ -875,373 +854,11 @@ def create_ui(): with gr.Blocks(analytics_enabled=False) as extras_interface: ui_postprocessing.create_ui() - def update_interp_description(value): - interp_description_css = "

{}

" - interp_descriptions = { - "No interpolation": interp_description_css.format("No interpolation will be used. Requires one model; A. Allows for format conversion and VAE baking."), - "Weighted sum": interp_description_css.format("A weighted sum will be used for interpolation. Requires two models; A and B. The result is calculated as A * (1 - M) + B * M"), - "Add difference": interp_description_css.format("The difference between the last two models will be added to the first. Requires three models; A, B and C. The result is calculated as A + (B - C) * M") - } - return interp_descriptions[value] - with gr.Blocks(analytics_enabled=False) as train_interface: - with gr.Column(elem_id='ti_train_container'): - with gr.Tabs(elem_id="train_tabs"): - with gr.Tab(label="Merge models"): - with gr.Row().style(equal_height=False): - with gr.Column(variant='compact'): - with FormRow(elem_id="modelmerger_models"): - def sd_model_choices(): - return ['None'] + modules.sd_models.checkpoint_tiles() - primary_model_name = gr.Dropdown(sd_model_choices(), elem_id="modelmerger_primary_model_name", label="Primary model", value="None") - create_refresh_button(primary_model_name, modules.sd_models.list_models, lambda: {"choices": sd_model_choices()}, "refresh_checkpoint_A") - secondary_model_name = gr.Dropdown(sd_model_choices(), elem_id="modelmerger_secondary_model_name", label="Secondary model", value="None") - create_refresh_button(secondary_model_name, modules.sd_models.list_models, lambda: {"choices": sd_model_choices()}, "refresh_checkpoint_B") - tertiary_model_name = gr.Dropdown(sd_model_choices(), elem_id="modelmerger_tertiary_model_name", label="Tertiary model", value="None") - create_refresh_button(tertiary_model_name, modules.sd_models.list_models, lambda: {"choices": sd_model_choices()}, "refresh_checkpoint_C") - custom_name = gr.Textbox(label="New model name", elem_id="modelmerger_custom_name") - with FormRow(): - interp_description = gr.HTML(value=update_interp_description("Weighted sum"), elem_id="modelmerger_interp_description") - with FormRow(): - interp_method = gr.Radio(choices=["No interpolation", "Weighted sum", "Add difference"], value="Weighted sum", label="Interpolation Method", elem_id="modelmerger_interp_method") - interp_method.change(fn=update_interp_description, inputs=[interp_method], outputs=[interp_description]) - interp_amount = gr.Slider(minimum=0.0, maximum=1.0, step=0.05, label='Interpolation ratio from Primary to Secondary', value=0.5, elem_id="modelmerger_interp_amount") - with FormRow(): - checkpoint_format = gr.Radio(choices=["ckpt", "safetensors"], value="safetensors", label="Checkpoint format", elem_id="modelmerger_checkpoint_format") - with gr.Box(): - save_as_half = gr.Checkbox(value=True, label="Use FP16", elem_id="modelmerger_save_as_half") - save_metadata = gr.Checkbox(value=True, label="Save metadata", elem_id="modelmerger_save_metadata") - with FormRow(): - with gr.Column(): - config_source = gr.Radio(choices=["Primary", "Secondary", "Tertiary", "None"], value="Primary", label="Model configuration", type="index", elem_id="modelmerger_config_method") - with gr.Column(): - with FormRow(): - bake_in_vae = gr.Dropdown(choices=["None"] + list(sd_vae.vae_dict), value="None", label="Bake in VAE", elem_id="modelmerger_bake_in_vae") - create_refresh_button(bake_in_vae, sd_vae.refresh_vae_list, lambda: {"choices": ["None"] + list(sd_vae.vae_dict)}, "modelmerger_refresh_bake_in_vae") - with FormRow(): - discard_weights = gr.Textbox(value="", label="Discard weights with matching name", elem_id="modelmerger_discard_weights") - with gr.Row(): - modelmerger_merge = gr.Button(elem_id="modelmerger_merge", value="Merge", variant='primary') - model_checkhash = gr.Button(elem_id="modelmerger_hash", value="Calculate hash for all models (may take a long time)", variant='primary') + ui_train.create_ui(txt2img_preview_params = [txt2img_prompt, txt2img_negative_prompt, steps, sampler_index, cfg_scale, seed, width, height]) - with gr.Column(variant='compact', elem_id="modelmerger_results_container"): - with gr.Group(elem_id="modelmerger_results_panel"): - modelmerger_result = gr.HTML(elem_id="modelmerger_result", show_label=False) - - with gr.Tab(label="Create embedding", id="create_embedding"): - new_embedding_name = gr.Textbox(label="Name", elem_id="train_new_embedding_name") - initialization_text = gr.Textbox(label="Initialization text", value="*", elem_id="train_initialization_text") - nvpt = gr.Slider(label="Number of vectors per token", minimum=1, maximum=75, step=1, value=1, elem_id="train_nvpt") - overwrite_old_embedding = gr.Checkbox(value=False, label="Overwrite Old Embedding", elem_id="train_overwrite_old_embedding") - - with gr.Row(): - with gr.Column(scale=3): - gr.HTML(value="") - - with gr.Column(): - create_embedding = gr.Button(value="Create embedding", variant='primary', elem_id="train_create_embedding") - - with gr.Tab(label="Create hypernetwork", id="create_hypernetwork"): - new_hypernetwork_name = gr.Textbox(label="Name", elem_id="train_new_hypernetwork_name") - new_hypernetwork_sizes = gr.CheckboxGroup(label="Modules", value=["768", "320", "640", "1280"], choices=["768", "1024", "320", "640", "1280"], elem_id="train_new_hypernetwork_sizes") - new_hypernetwork_layer_structure = gr.Textbox("1, 2, 1", label="Enter hypernetwork layer structure", placeholder="1st and last digit must be 1. ex:'1, 2, 1'", elem_id="train_new_hypernetwork_layer_structure") - new_hypernetwork_activation_func = gr.Dropdown(value="linear", label="Select activation function of hypernetwork", choices=modules.hypernetworks.ui.keys, elem_id="train_new_hypernetwork_activation_func") - new_hypernetwork_initialization_option = gr.Dropdown(value = "Normal", label="Select Layer weights initialization", choices=["Normal", "KaimingUniform", "KaimingNormal", "XavierUniform", "XavierNormal"], elem_id="train_new_hypernetwork_initialization_option") - new_hypernetwork_add_layer_norm = gr.Checkbox(label="Add layer normalization", elem_id="train_new_hypernetwork_add_layer_norm") - new_hypernetwork_use_dropout = gr.Checkbox(label="Use dropout", elem_id="train_new_hypernetwork_use_dropout") - new_hypernetwork_dropout_structure = gr.Textbox("0, 0, 0", label="Enter hypernetwork Dropout structure", placeholder="1st and last digit must be 0 and values should be between 0 and 1. ex:'0, 0.01, 0'") - overwrite_old_hypernetwork = gr.Checkbox(value=False, label="Overwrite Old Hypernetwork", elem_id="train_overwrite_old_hypernetwork") - - with gr.Row(): - with gr.Column(scale=3): - gr.HTML(value="") - - with gr.Column(): - create_hypernetwork = gr.Button(value="Create hypernetwork", variant='primary', elem_id="train_create_hypernetwork") - - with gr.Tab(label="Preprocess images", id="preprocess_images"): - process_src = gr.Textbox(label='Source directory', elem_id="train_process_src") - process_dst = gr.Textbox(label='Destination directory', elem_id="train_process_dst") - process_width = gr.Slider(minimum=64, maximum=2048, step=8, label="Width", value=512, elem_id="train_process_width") - process_height = gr.Slider(minimum=64, maximum=2048, step=8, label="Height", value=512, elem_id="train_process_height") - preprocess_txt_action = gr.Dropdown(label='Existing Caption txt Action', value="ignore", choices=["ignore", "copy", "prepend", "append"], elem_id="train_preprocess_txt_action") - - with gr.Row(): - process_keep_original_size = gr.Checkbox(label='Keep original size', elem_id="train_process_keep_original_size") - process_keep_channels = gr.Checkbox(label='Keep original image channels', elem_id="train_process_keep_channels") - process_flip = gr.Checkbox(label='Create flipped copies', elem_id="train_process_flip") - process_split = gr.Checkbox(label='Split oversized images', elem_id="train_process_split") - process_focal_crop = gr.Checkbox(label='Auto focal point crop', elem_id="train_process_focal_crop") - process_multicrop = gr.Checkbox(label='Auto-sized crop', elem_id="train_process_multicrop") - process_caption_only = gr.Checkbox(label='Create captions only', elem_id="train_process_multicrop") - process_caption = gr.Checkbox(label='Create BLIP captions', elem_id="train_process_caption") - process_caption_deepbooru = gr.Checkbox(label='Create Deepbooru captions', visible=True, elem_id="train_process_caption_deepbooru") - - with gr.Row(visible=False) as process_split_extra_row: - process_split_threshold = gr.Slider(label='Split image threshold', value=0.5, minimum=0.0, maximum=1.0, step=0.05, elem_id="train_process_split_threshold") - process_overlap_ratio = gr.Slider(label='Split image overlap ratio', value=0.2, minimum=0.0, maximum=0.9, step=0.05, elem_id="train_process_overlap_ratio") - - with gr.Row(visible=False) as process_focal_crop_row: - process_focal_crop_face_weight = gr.Slider(label='Focal point face weight', value=0.9, minimum=0.0, maximum=1.0, step=0.05, elem_id="train_process_focal_crop_face_weight") - process_focal_crop_entropy_weight = gr.Slider(label='Focal point entropy weight', value=0.15, minimum=0.0, maximum=1.0, step=0.05, elem_id="train_process_focal_crop_entropy_weight") - process_focal_crop_edges_weight = gr.Slider(label='Focal point edges weight', value=0.5, minimum=0.0, maximum=1.0, step=0.05, elem_id="train_process_focal_crop_edges_weight") - process_focal_crop_debug = gr.Checkbox(label='Create debug image', elem_id="train_process_focal_crop_debug") - - with gr.Column(visible=False) as process_multicrop_col: - gr.Markdown('Each image is center-cropped with an automatically chosen width and height.') - with gr.Row(): - process_multicrop_mindim = gr.Slider(minimum=64, maximum=2048, step=8, label="Dimension lower bound", value=384, elem_id="train_process_multicrop_mindim") - process_multicrop_maxdim = gr.Slider(minimum=64, maximum=2048, step=8, label="Dimension upper bound", value=768, elem_id="train_process_multicrop_maxdim") - with gr.Row(): - process_multicrop_minarea = gr.Slider(minimum=64*64, maximum=2048*2048, step=1, label="Area lower bound", value=64*64, elem_id="train_process_multicrop_minarea") - process_multicrop_maxarea = gr.Slider(minimum=64*64, maximum=2048*2048, step=1, label="Area upper bound", value=640*640, elem_id="train_process_multicrop_maxarea") - with gr.Row(): - process_multicrop_objective = gr.Radio(["Maximize area", "Minimize error"], value="Maximize area", label="Resizing objective", elem_id="train_process_multicrop_objective") - process_multicrop_threshold = gr.Slider(minimum=0, maximum=1, step=0.01, label="Error threshold", value=0.1, elem_id="train_process_multicrop_threshold") - - with gr.Row(): - with gr.Column(scale=3): - gr.HTML(value="") - - with gr.Column(): - with gr.Row(): - interrupt_preprocessing = gr.Button("Stop", elem_id="train_interrupt_preprocessing") - run_preprocess = gr.Button(value="Preprocess", variant='primary', elem_id="train_run_preprocess") - - process_split.change( - fn=lambda show: gr_show(show), - inputs=[process_split], - outputs=[process_split_extra_row], - ) - - process_focal_crop.change( - fn=lambda show: gr_show(show), - inputs=[process_focal_crop], - outputs=[process_focal_crop_row], - ) - - process_multicrop.change( - fn=lambda show: gr_show(show), - inputs=[process_multicrop], - outputs=[process_multicrop_col], - ) - - def get_textual_inversion_template_names(): - return sorted(textual_inversion.textual_inversion_templates) - - with gr.Tab(label="Train", id="train"): - gr.HTML(value="

Train an embedding or Hypernetwork; you must specify a directory with a set of 1:1 ratio images

") - with FormRow(): - train_embedding_name = gr.Dropdown(label='Embedding', elem_id="train_embedding", choices=sorted(sd_hijack.model_hijack.embedding_db.word_embeddings.keys())) - create_refresh_button(train_embedding_name, sd_hijack.model_hijack.embedding_db.load_textual_inversion_embeddings, lambda: {"choices": sorted(sd_hijack.model_hijack.embedding_db.word_embeddings.keys())}, "refresh_train_embedding_name") - - train_hypernetwork_name = gr.Dropdown(label='Hypernetwork', elem_id="train_hypernetwork", choices=sorted(modules.shared.hypernetworks)) - create_refresh_button(train_hypernetwork_name, modules.shared.reload_hypernetworks, lambda: {"choices": sorted(modules.shared.hypernetworks)}, "refresh_train_hypernetwork_name") - - with FormRow(): - embedding_learn_rate = gr.Textbox(label='Embedding Learning rate', placeholder="Embedding Learning rate", value="0.005", elem_id="train_embedding_learn_rate") - hypernetwork_learn_rate = gr.Textbox(label='Hypernetwork Learning rate', placeholder="Hypernetwork Learning rate", value="0.00001", elem_id="train_hypernetwork_learn_rate") - - with FormRow(): - clip_grad_mode = gr.Dropdown(value="disabled", label="Gradient Clipping", choices=["disabled", "value", "norm"]) - clip_grad_value = gr.Textbox(placeholder="Gradient clip value", value="0.1", show_label=False) - - with FormRow(): - batch_size = gr.Number(label='Batch size', value=1, precision=0, elem_id="train_batch_size") - gradient_step = gr.Number(label='Gradient accumulation steps', value=1, precision=0, elem_id="train_gradient_step") - - dataset_directory = gr.Textbox(label='Dataset directory', placeholder="Path to directory with input images", elem_id="train_dataset_directory") - log_directory = gr.Textbox(label='Log directory', placeholder="Path to directory where to write outputs", value=f"{os.path.join('cmd_opts.data_dir', 'train/log/embeddings')}", elem_id="train_log_directory") - - with FormRow(): - template_file = gr.Dropdown(label='Prompt template', value="style_filewords.txt", elem_id="train_template_file", choices=get_textual_inversion_template_names()) - create_refresh_button(template_file, textual_inversion.list_textual_inversion_templates, lambda: {"choices": get_textual_inversion_template_names()}, "refrsh_train_template_file") - - training_width = gr.Slider(minimum=64, maximum=2048, step=8, label="Width", value=512, elem_id="train_training_width") - training_height = gr.Slider(minimum=64, maximum=2048, step=8, label="Height", value=512, elem_id="train_training_height") - varsize = gr.Checkbox(label="Do not resize images", value=False, elem_id="train_varsize") - steps = gr.Number(label='Max steps', value=1000, precision=0, elem_id="train_steps") - - with FormRow(): - create_image_every = gr.Number(label='Create interim images', value=500, precision=0, elem_id="train_create_image_every") - save_embedding_every = gr.Number(label='Create interim embeddings', value=500, precision=0, elem_id="train_save_embedding_every") - - use_weight = gr.Checkbox(label="Use PNG alpha channel as loss weight", value=False, elem_id="use_weight") - - save_image_with_stored_embedding = gr.Checkbox(label='Save images with embedding in PNG chunks', value=True, elem_id="train_save_image_with_stored_embedding") - preview_from_txt2img = gr.Checkbox(label='Use current settings for previews', value=False, elem_id="train_preview_from_txt2img") - - shuffle_tags = gr.Checkbox(label="Shuffle tags by ',' when creating prompts.", value=False, elem_id="train_shuffle_tags") - tag_drop_out = gr.Slider(minimum=0, maximum=1, step=0.1, label="Drop out tags when creating prompts.", value=0, elem_id="train_tag_drop_out") - - latent_sampling_method = gr.Radio(label='Choose latent sampling method', value="once", choices=['once', 'deterministic', 'random'], elem_id="train_latent_sampling_method") - - with gr.Row(): - train_embedding = gr.Button(value="Train Embedding", variant='primary', elem_id="train_train_embedding") - interrupt_training = gr.Button(value="Stop", elem_id="train_interrupt_training") - train_hypernetwork = gr.Button(value="Train Hypernetwork", variant='primary', elem_id="train_train_hypernetwork") - - params = script_callbacks.UiTrainTabParams(txt2img_preview_params) - - script_callbacks.ui_train_tabs_callback(params) - - with gr.Column(elem_id='ti_gallery_container'): - ti_output = gr.Text(elem_id="ti_output", value="", show_label=False) - gr.Gallery(label='Output', show_label=False, elem_id='ti_gallery').style(columns=4) - gr.HTML(elem_id="ti_progress", value="") - ti_outcome = gr.HTML(elem_id="ti_error", value="") - - create_embedding.click( - fn=modules.textual_inversion.ui.create_embedding, - inputs=[ - new_embedding_name, - initialization_text, - nvpt, - overwrite_old_embedding, - ], - outputs=[ - train_embedding_name, - ti_output, - ti_outcome, - ] - ) - - create_hypernetwork.click( - fn=modules.hypernetworks.ui.create_hypernetwork, - inputs=[ - new_hypernetwork_name, - new_hypernetwork_sizes, - overwrite_old_hypernetwork, - new_hypernetwork_layer_structure, - new_hypernetwork_activation_func, - new_hypernetwork_initialization_option, - new_hypernetwork_add_layer_norm, - new_hypernetwork_use_dropout, - new_hypernetwork_dropout_structure - ], - outputs=[ - train_hypernetwork_name, - ti_output, - ti_outcome, - ] - ) - - run_preprocess.click( - fn=wrap_gradio_gpu_call(modules.textual_inversion.ui.preprocess, extra_outputs=[gr.update()]), - _js="start_training_textual_inversion", - inputs=[ - dummy_component, - process_src, - process_dst, - process_width, - process_height, - preprocess_txt_action, - process_keep_original_size, - process_keep_channels, - process_flip, - process_split, - process_caption_only, - process_caption, - process_caption_deepbooru, - process_split_threshold, - process_overlap_ratio, - process_focal_crop, - process_focal_crop_face_weight, - process_focal_crop_entropy_weight, - process_focal_crop_edges_weight, - process_focal_crop_debug, - process_multicrop, - process_multicrop_mindim, - process_multicrop_maxdim, - process_multicrop_minarea, - process_multicrop_maxarea, - process_multicrop_objective, - process_multicrop_threshold, - ], - outputs=[ - ti_output, - ti_outcome, - ], - ) - - train_embedding.click( - fn=wrap_gradio_gpu_call(modules.textual_inversion.ui.train_embedding, extra_outputs=[gr.update()]), - _js="start_training_textual_inversion", - inputs=[ - dummy_component, - train_embedding_name, - embedding_learn_rate, - batch_size, - gradient_step, - dataset_directory, - log_directory, - training_width, - training_height, - varsize, - steps, - clip_grad_mode, - clip_grad_value, - shuffle_tags, - tag_drop_out, - latent_sampling_method, - use_weight, - create_image_every, - save_embedding_every, - template_file, - save_image_with_stored_embedding, - preview_from_txt2img, - *txt2img_preview_params, - ], - outputs=[ - ti_output, - ti_outcome, - ] - ) - - train_hypernetwork.click( - fn=wrap_gradio_gpu_call(modules.hypernetworks.ui.train_hypernetwork, extra_outputs=[gr.update()]), - _js="start_training_textual_inversion", - inputs=[ - dummy_component, - train_hypernetwork_name, - hypernetwork_learn_rate, - batch_size, - gradient_step, - dataset_directory, - log_directory, - training_width, - training_height, - varsize, - steps, - clip_grad_mode, - clip_grad_value, - shuffle_tags, - tag_drop_out, - latent_sampling_method, - use_weight, - create_image_every, - save_embedding_every, - template_file, - preview_from_txt2img, - *txt2img_preview_params, - ], - outputs=[ - ti_output, - ti_outcome, - ] - ) - - interrupt_training.click( - fn=lambda: modules.shared.state.interrupt(), - inputs=[], - outputs=[], - ) - - interrupt_preprocessing.click( - fn=lambda: modules.shared.state.interrupt(), - inputs=[], - outputs=[], - ) + with gr.Blocks(analytics_enabled=False) as models_interface: + ui_models.create_ui() def create_setting_component(key, is_quicksettings=False): def fun(): @@ -1436,9 +1053,8 @@ def create_ui(): (txt2img_interface, "From Text", "txt2img"), (img2img_interface, "From Image", "img2img"), (extras_interface, "Process Image", "process"), - # (pnginfo_interface, "Image Info", "pnginfo"), - # (modelmerger_interface, "Checkpoint Merger", "modelmerger"), (train_interface, "Train", "train"), + (models_interface, "Models", "models"), ] interfaces += script_callbacks.ui_tabs_callback() interfaces += [(settings_interface, "Settings", "settings")] @@ -1494,7 +1110,7 @@ def create_ui(): show_progress=info.refresh is not None, ) - # TODO image_cfg_scale_visibility should be on model change, not on ui create + # TODO breaks pix2pix needs better detect if pix2pix and image_cfg_scale_visibility should be on model change, not on ui create # image_cfg_scale_visibility = (modules.shared.sd_model is not None) and hasattr(modules.shared.sd_model, 'cond_stage_key') and (modules.shared.sd_model.cond_stage_key == "edit") # pix2pix # text_settings.change(fn=lambda: gr.update(visible=image_cfg_scale_visibility), inputs=[], outputs=[image_cfg_scale]) # demo.load(fn=lambda: gr.update(visible=image_cfg_scale_visibility), inputs=[], outputs=[image_cfg_scale]) @@ -1519,47 +1135,8 @@ def create_ui(): queue=False, ) - def modelmerger(*args): - try: - results = modules.extras.run_modelmerger(*args) - except Exception as e: - modules.errors.display(e, 'model merge') - modules.sd_models.list_models() # to remove the potentially missing models from the list - return [*[gr.Dropdown.update(choices=modules.sd_models.checkpoint_tiles()) for _ in range(4)], f"Error merging checkpoints: {e}"] - return results - - modelmerger_merge.click(fn=lambda: '', inputs=[], outputs=[modelmerger_result]) - modelmerger_merge.click( - fn=wrap_gradio_gpu_call(modelmerger, extra_outputs=lambda: [gr.update() for _ in range(4)]), - _js='modelmerger', - inputs=[ - dummy_component, - primary_model_name, - secondary_model_name, - tertiary_model_name, - interp_method, - interp_amount, - save_as_half, - custom_name, - checkpoint_format, - config_source, - bake_in_vae, - discard_weights, - save_metadata, - ], - outputs=[ - primary_model_name, - secondary_model_name, - tertiary_model_name, - component_dict['sd_model_checkpoint'], - modelmerger_result, - ] - ) - model_checkhash.click(fn=sd_models.update_model_hashes, inputs=[], outputs=[modelmerger_result]) - loadsave.dump_defaults() demo.ui_loadsave = loadsave - interp_description.value = update_interp_description(interp_method.value) # Required as a workaround for change() event not triggering when loading values from ui-config.json return demo diff --git a/modules/ui_common.py b/modules/ui_common.py index efc6c04a9..a724e92df 100644 --- a/modules/ui_common.py +++ b/modules/ui_common.py @@ -207,3 +207,19 @@ def create_output_panel(tabname, outdir): paste_button=paste_button, tabname=paste_tabname, source_tabname=("txt2img" if tabname == "txt2img" else None), source_image_component=result_gallery, paste_field_names=paste_field_names )) return result_gallery, generation_info, html_info, html_info_formatted, html_log + + +def create_refresh_button(refresh_component, refresh_method, refreshed_args, elem_id): + + def refresh(): + refresh_method() + args = refreshed_args() if callable(refreshed_args) else refreshed_args + for k, v in args.items(): + setattr(refresh_component, k, v) + return gr.update(**(args or {})) + + from modules.ui_components import ToolButton + refresh_symbol = '\U0001f504' # 🔄 + refresh_button = ToolButton(value=refresh_symbol, elem_id=elem_id) + refresh_button.click(fn=refresh, inputs=[], outputs=[refresh_component]) + return refresh_button diff --git a/modules/ui_extensions.py b/modules/ui_extensions.py index 7ce3e20f5..5725e8df5 100644 --- a/modules/ui_extensions.py +++ b/modules/ui_extensions.py @@ -331,6 +331,8 @@ def refresh_extensions_list_from_data(search_text, sort_column): ext['sort_enabled'] = f"{'0' if ext['enabled'] else '1'}{'1' if ext['is_builtin'] else '0'}{'1' if ext['installed'] else '0'}{ext.get('updated', '2000-01-01T00:00')}" ext['sort_update'] = f"{'1' if update_available else '0'}{'1' if ext['installed'] else '0'}{ext.get('updated', '2000-01-01T00:00')}" tags = ext.get("tags", []) + if not isinstance(tags, list): + tags = tags.split(' ') tags_string = ' '.join(tags) tags = tags + ["installed"] if installed else tags if len([x for x in tags if x in hide_tags]) > 0: @@ -377,7 +379,7 @@ def create_ui(): extensions_disabled_list = gr.Text(elem_id="extensions_disabled_list", visible=False).style(container=False) extensions_update_list = gr.Text(elem_id="extensions_update_list", visible=False).style(container=False) with gr.Tabs(elem_id="tabs_extensions"): - with gr.TabItem("Manage Extensions", id="manage"): + with gr.TabItem("Manage extensions", id="manage"): with gr.Row(elem_id="extensions_installed_top"): extension_to_install = gr.Text(elem_id="extension_to_install", visible=False) install_extension_button = gr.Button(elem_id="install_extension_button", visible=False) diff --git a/modules/ui_models.py b/modules/ui_models.py new file mode 100644 index 000000000..aa1c082f6 --- /dev/null +++ b/modules/ui_models.py @@ -0,0 +1,163 @@ +import os +import json +from datetime import datetime +import gradio as gr +from modules import sd_models, sd_vae, extras +from modules.ui_components import FormRow +from modules.ui_common import create_refresh_button +from modules.call_queue import wrap_gradio_gpu_call +import modules.errors + + +def create_ui(): + dummy_component = gr.Label(visible=False) + + with gr.Row(id="models_tab", elem_id="models_tab"): + with gr.Column(elem_id='models_output_container', scale=1): + # models_output = gr.Text(elem_id="models_output", value="", show_label=False) + gr.HTML(elem_id="models_progress", value="") + models_outcome = gr.HTML(elem_id="models_error", value="") + + with gr.Column(elem_id='models_input_container', scale=3): + + def gr_show(visible=True): + return {"visible": visible, "__type__": "update"} + + with gr.Tab(label="Convert"): + with gr.Row(): + model_name = gr.Dropdown(sd_models.checkpoint_tiles(), label="Original model") + create_refresh_button(model_name, sd_models.list_models, lambda: {"choices": sd_models.checkpoint_tiles()}, "refresh_checkpoint_Z") + with gr.Row(): + custom_name = gr.Textbox(label="New model name") + with gr.Row(): + precision = gr.Radio(choices=["fp32", "fp16", "bf16"], value="fp32", label="Model precision") + m_type = gr.Radio(choices=["disabled", "no-ema", "ema-only"], value="disabled", label="Model pruning methods") + with gr.Row(): + checkpoint_formats = gr.CheckboxGroup(choices=["ckpt", "safetensors"], value=["safetensors"], label="Model Format") + with gr.Row(): + show_extra_options = gr.Checkbox(label="Show extra options", value=False) + fix_clip = gr.Checkbox(label="Fix clip", value=False) + with gr.Row(visible=False) as extra_options: + specific_part_conv = ["copy", "convert", "delete"] + unet_conv = gr.Dropdown(specific_part_conv, value="convert", label="unet") + text_encoder_conv = gr.Dropdown(specific_part_conv, value="convert", label="text encoder") + vae_conv = gr.Dropdown(specific_part_conv, value="convert", label="vae") + others_conv = gr.Dropdown(specific_part_conv, value="convert", label="others") + + show_extra_options.change(fn=lambda x: gr_show(x), inputs=[show_extra_options], outputs=[extra_options]) + + model_converter_convert = gr.Button(label="Convert", variant='primary') + model_converter_convert.click( + fn=extras.run_modelconvert, + inputs=[ + model_name, + checkpoint_formats, + precision, m_type, custom_name, + unet_conv, + text_encoder_conv, + vae_conv, + others_conv, + fix_clip + ], + outputs=[models_outcome] + ) + + with gr.Tab(label="Merge"): + with gr.Row().style(equal_height=False): + with gr.Column(variant='compact'): + with FormRow(): + custom_name = gr.Textbox(label="New model name") + with FormRow(): + def sd_model_choices(): + return ['None'] + sd_models.checkpoint_tiles() + primary_model_name = gr.Dropdown(sd_model_choices(), label="Primary model", value="None") + create_refresh_button(primary_model_name, sd_models.list_models, lambda: {"choices": sd_model_choices()}, "refresh_checkpoint_A") + secondary_model_name = gr.Dropdown(sd_model_choices(), label="Secondary model", value="None") + create_refresh_button(secondary_model_name, sd_models.list_models, lambda: {"choices": sd_model_choices()}, "refresh_checkpoint_B") + tertiary_model_name = gr.Dropdown(sd_model_choices(), label="Tertiary model", value="None") + create_refresh_button(tertiary_model_name, sd_models.list_models, lambda: {"choices": sd_model_choices()}, "refresh_checkpoint_C") + with FormRow(): + interp_method = gr.Radio(choices=["No interpolation", "Weighted sum", "Add difference"], value="Weighted sum", label="Interpolation Method") + interp_amount = gr.Slider(minimum=0.0, maximum=1.0, step=0.05, label='Interpolation ratio from Primary to Secondary', value=0.5) + with FormRow(): + checkpoint_format = gr.Radio(choices=["ckpt", "safetensors"], value="safetensors", label="Model format") + with gr.Box(): + save_as_half = gr.Radio(choices=["fp16", "fp32"], value="fp16", label="Model precision", type="index") + with FormRow(): + config_source = gr.Radio(choices=["Primary", "Secondary", "Tertiary", "None"], value="Primary", label="Model configuration", type="index") + with FormRow(): + bake_in_vae = gr.Dropdown(choices=["None"] + list(sd_vae.vae_dict), value="None", label="Bake in VAE") + create_refresh_button(bake_in_vae, sd_vae.refresh_vae_list, lambda: {"choices": ["None"] + list(sd_vae.vae_dict)}, "modelmerger_refresh_bake_in_vae") + with FormRow(): + discard_weights = gr.Textbox(value="", label="Discard weights with matching name") + with FormRow(): + save_metadata = gr.Checkbox(value=True, label="Save metadata") + with gr.Row(): + modelmerger_merge = gr.Button(value="Merge", variant='primary') + + def modelmerger(*args): + try: + results = extras.run_modelmerger(*args) + except Exception as e: + modules.errors.display(e, 'model merge') + sd_models.list_models() # to remove the potentially missing models from the list + return [*[gr.Dropdown.update(choices=sd_models.checkpoint_tiles()) for _ in range(4)], f"Error merging checkpoints: {e}"] + return results + + modelmerger_merge.click( + fn=wrap_gradio_gpu_call(modelmerger, extra_outputs=lambda: [gr.update() for _ in range(4)]), + _js='modelmerger', + inputs=[ + dummy_component, + primary_model_name, + secondary_model_name, + tertiary_model_name, + interp_method, + interp_amount, + save_as_half, + custom_name, + checkpoint_format, + config_source, + bake_in_vae, + discard_weights, + save_metadata, + ], + outputs=[ + primary_model_name, + secondary_model_name, + tertiary_model_name, + dummy_component, + models_outcome, + ] + ) + + with gr.Tab(label="Validate"): + model_headers = ['name', 'type', 'filename', 'hash', 'added', 'size', 'metadata'] + model_data = [] + + with gr.Row(): + model_list_btn = gr.Button(value="List model details", variant='primary') + model_checkhash_btn = gr.Button(value="Calculate hash for all models (may take a long time)", variant='primary') + model_checkhash_btn.click(fn=sd_models.update_model_hashes, inputs=[], outputs=[models_outcome]) + with gr.Row(): + model_table = gr.DataFrame(model_data, label = 'Model data', show_label = True, interactive = False, wrap = True, overflow_row_behaviour = 'paginate', max_rows = 10, headers = model_headers) + + def list_models(): + total_size = 0 + for m in sd_models.checkpoints_list.values(): + txt = '' + try: + stat = os.stat(m.filename) + m_name = m.name.replace('.ckpt', '').replace('.safetensors', '') + m_type = 'ckpt' if m.name.endswith('.ckpt') else 'safe' + m_meta = len(json.dumps(m.metadata)) - 2 + m_size = round(stat.st_size / 1024 / 1024 / 1024, 3) + m_time = datetime.fromtimestamp(stat.st_mtime) + model_data.append([m_name, m_type, m.filename, m.hash, m_time, m_size, m_meta]) + total_size += stat.st_size + except Exception as e: + txt += f"Error: {m.name} {e}
" + txt += f"Model list enumerated {len(sd_models.checkpoints_list.keys())} models in {round(total_size / 1024 / 1024 / 1024, 3)} GB
" + return model_data, txt + + model_list_btn.click(fn=list_models, inputs=[], outputs=[model_table, models_outcome]) diff --git a/modules/ui_train.py b/modules/ui_train.py new file mode 100644 index 000000000..006fae954 --- /dev/null +++ b/modules/ui_train.py @@ -0,0 +1,360 @@ +import os +import gradio as gr +from modules import sd_hijack, script_callbacks, shared +from modules.ui_components import FormRow +from modules.ui_common import create_refresh_button +from modules.call_queue import wrap_gradio_gpu_call +from modules.textual_inversion import textual_inversion +import modules.errors + + +def create_ui(txt2img_preview_params): + dummy_component = gr.Label(visible=False) + + with gr.Row(id="train_tab", elem_id="train_tab"): + with gr.Column(elem_id='train_output_container', scale=1): + train_output = gr.Text(elem_id="train_output", value="", show_label=False) + gr.Gallery(label='Output', show_label=False, elem_id='train_gallery').style(columns=1) + gr.HTML(elem_id="train_progress", value="") + train_outcome = gr.HTML(elem_id="train_error", value="") + + with gr.Row(visible=True) as action_pp: + process_run = gr.Button(value="Preprocess", variant='primary') + process_stop = gr.Button("Stop") + + with gr.Row(visible=False) as action_ti: + ti_train = gr.Button(value="Train embedding", variant='primary') + ti_stop = gr.Button(value="Stop") + + with gr.Row(visible=False) as action_hn: + hn_train = gr.Button(value="Train hypernetwork", variant='primary') + hn_stop = gr.Button(value="Stop") + + with gr.Column(elem_id='train_input_container', scale=3): + + with gr.Tabs(elem_id="train_tabs"): + + def gr_show(visible=True): + return {"visible": visible, "__type__": "update"} + + def train_tab_change(tab): + if tab == 'ti': + return gr_show(False), gr_show(True), gr_show(False) + elif tab == 'hn': + return gr_show(False), gr_show(False), gr_show(True) + else: + return gr_show(True), gr_show(False), gr_show(False) + + ### preprocess tab + + with gr.Tab(label="Preprocess images", id="preprocess_images") as tab_preprocess: + tab_preprocess.select(fn=lambda x: train_tab_change('pp'), inputs=[], outputs=[action_pp, action_ti, action_hn]) + process_src = gr.Textbox(label='Source directory') + process_dst = gr.Textbox(label='Destination directory') + with gr.Row(): + process_width = gr.Slider(minimum=64, maximum=2048, step=8, label="Width", value=512) + process_height = gr.Slider(minimum=64, maximum=2048, step=8, label="Height", value=512) + preprocess_txt_action = gr.Dropdown(label='Existing caption text action', value="ignore", choices=["ignore", "copy", "prepend", "append"]) + + with gr.Box(): + gr.Markdown('## Preprocessing steps') + process_keep_original_size = gr.Checkbox(label='Keep original size') + process_keep_channels = gr.Checkbox(label='Keep original image channels') + process_flip = gr.Checkbox(label='Create flipped copies') + process_split = gr.Checkbox(label='Split oversized images') + process_focal_crop = gr.Checkbox(label='Auto focal point crop') + process_multicrop = gr.Checkbox(label='Auto-sized crop') + process_caption_only = gr.Checkbox(label='Create captions only') + process_caption = gr.Checkbox(label='Create BLIP captions') + process_caption_deepbooru = gr.Checkbox(label='Create Deepbooru captions') + + with gr.Row(visible=False) as process_split_extra_row: + process_split_threshold = gr.Slider(label='Split image threshold', value=0.5, minimum=0.0, maximum=1.0, step=0.05) + process_overlap_ratio = gr.Slider(label='Split image overlap ratio', value=0.2, minimum=0.0, maximum=0.9, step=0.05) + + with gr.Row(visible=False) as process_focal_crop_row: + process_focal_crop_face_weight = gr.Slider(label='Focal point face weight', value=0.9, minimum=0.0, maximum=1.0, step=0.05) + process_focal_crop_entropy_weight = gr.Slider(label='Focal point entropy weight', value=0.15, minimum=0.0, maximum=1.0, step=0.05) + process_focal_crop_edges_weight = gr.Slider(label='Focal point edges weight', value=0.5, minimum=0.0, maximum=1.0, step=0.05) + process_focal_crop_debug = gr.Checkbox(label='Create debug image') + + with gr.Column(visible=False) as process_multicrop_col: + gr.Markdown('## Each image is center-cropped with an automatically chosen width and height.') + with gr.Row(): + process_multicrop_mindim = gr.Slider(minimum=64, maximum=2048, step=8, label="Dimension lower bound", value=384) + process_multicrop_maxdim = gr.Slider(minimum=64, maximum=2048, step=8, label="Dimension upper bound", value=768) + with gr.Row(): + process_multicrop_minarea = gr.Slider(minimum=64*64, maximum=2048*2048, step=1, label="Area lower bound", value=64*64) + process_multicrop_maxarea = gr.Slider(minimum=64*64, maximum=2048*2048, step=1, label="Area upper bound", value=640*640) + with gr.Row(): + process_multicrop_objective = gr.Radio(["Maximize area", "Minimize error"], value="Maximize area", label="Resizing objective") + process_multicrop_threshold = gr.Slider(minimum=0, maximum=1, step=0.01, label="Error threshold", value=0.1) + + process_split.change(fn=lambda show: gr_show(show), inputs=[process_split], outputs=[process_split_extra_row]) + process_focal_crop.change(fn=lambda show: gr_show(show), inputs=[process_focal_crop], outputs=[process_focal_crop_row]) + process_multicrop.change(fn=lambda show: gr_show(show), inputs=[process_multicrop], outputs=[process_multicrop_col]) + process_stop.click(fn=lambda: shared.state.interrupt(), inputs=[], outputs=[]) + process_run.click( + fn=wrap_gradio_gpu_call(modules.textual_inversion.ui.preprocess, extra_outputs=[gr.update()]), + _js="start_train_monitoring", + inputs=[ + dummy_component, + process_src, + process_dst, + process_width, + process_height, + preprocess_txt_action, + process_keep_original_size, + process_keep_channels, + process_flip, + process_split, + process_caption_only, + process_caption, + process_caption_deepbooru, + process_split_threshold, + process_overlap_ratio, + process_focal_crop, + process_focal_crop_face_weight, + process_focal_crop_entropy_weight, + process_focal_crop_edges_weight, + process_focal_crop_debug, + process_multicrop, + process_multicrop_mindim, + process_multicrop_maxdim, + process_multicrop_minarea, + process_multicrop_maxarea, + process_multicrop_objective, + process_multicrop_threshold, + ], + outputs=[ + train_output, + train_outcome, + ], + ) + + ### train embedding tab + + with gr.Tab(label="Train embedding", id="train_embedding_tab") as tab_ti: + tab_ti.select(fn=lambda x: train_tab_change('ti'), inputs=[], outputs=[action_pp, action_ti, action_hn]) + def get_textual_inversion_template_names(): + return sorted(textual_inversion.textual_inversion_templates) + + gr.Markdown('## Select existing embedding to continue training or create a new one') + with FormRow(): + with gr.Column(): + with gr.Row(): + ti_name = gr.Dropdown(label='Select embedding', choices=sorted(sd_hijack.model_hijack.embedding_db.word_embeddings.keys())) + create_refresh_button(ti_name, sd_hijack.model_hijack.embedding_db.load_textual_inversion_embeddings, lambda: {"choices": sorted(sd_hijack.model_hijack.embedding_db.word_embeddings.keys())}, "refresh_train_embedding_name") + with gr.Column(): + ti_new_name = gr.Textbox(label="Create emebedding") + ti_init_text = gr.Textbox(label="Initialization text", value="*") + ti_vectors = gr.Slider(label="Number of vectors per token", minimum=1, maximum=75, step=1, value=1) + ti_overwrite = gr.Checkbox(value=False, label="Overwrite Old Embedding") + with gr.Row(): + ti_create = gr.Button(value="Create embedding", variant='secondary') + + with gr.Box(): + gr.Markdown('## Training parameters') + ti_learn_rate = gr.Textbox(label='Embedding Learning rate', placeholder="Embedding Learning rate", value="0.005") + with FormRow(): + ti_clip_grad_mode = gr.Dropdown(value="disabled", label="Gradient Clipping", choices=["disabled", "value", "norm"]) + ti_clip_grad_value = gr.Number(label="Gradient clip value", value=0.1) + ti_batch_size = gr.Number(label='Batch size', value=1, precision=0) + ti_gradient_step = gr.Number(label='Gradient accumulation steps', value=1, precision=0) + ti_steps = gr.Number(label='Max steps', value=1000, precision=0) + + with gr.Box(): + gr.Markdown('## Training images') + ti_dataset_directory = gr.Textbox(label='Dataset directory', placeholder="Path to directory with input images") + with FormRow(): + ti_varsize = gr.Checkbox(label="Do not resize images", value=False) + ti_width = gr.Slider(minimum=64, maximum=2048, step=8, label="Width", value=512) + ti_height = gr.Slider(minimum=64, maximum=2048, step=8, label="Height", value=512) + ti_use_weight = gr.Checkbox(label="Use PNG alpha channel as loss weight", value=False) + + with gr.Box(): + gr.Markdown('## Dataset processing') + with FormRow(): + ti_template = gr.Dropdown(label='Prompt template', value="style_filewords.txt", choices=get_textual_inversion_template_names()) + create_refresh_button(ti_template, textual_inversion.list_textual_inversion_templates, lambda: {"choices": get_textual_inversion_template_names()}, "refrsh_train_template_file") + ti_shuffle = gr.Checkbox(label="Shuffle tags", value=False) + ti_tag_drop_out = gr.Slider(minimum=0, maximum=1, step=0.1, label="Drop out tags when creating prompts", value=0) + ti_latent_sampling_method = gr.Radio(label='Choose latent sampling method', value="once", choices=['once', 'deterministic', 'random']) + + with gr.Box(): + gr.Markdown('## Training outputs') + with FormRow(): + ti_create_every = gr.Number(label='Create interim images', value=500, precision=0) + ti_save_every = gr.Number(label='Create interim embeddings', value=500, precision=0) + ti_save_image_with_stored_embedding = gr.Checkbox(label='Save images with embedding in PNG chunks', value=True) + ti_preview_from_txt2img = gr.Checkbox(label='Use current settings for previews', value=False) + ti_log_directory = gr.Textbox(label='Log directory', placeholder="Path to directory where to write outputs", value=f"{os.path.join(shared.cmd_opts.data_dir, 'train/log/embeddings')}") + + ti_stop.click(fn=lambda: shared.state.interrupt(), inputs=[], outputs=[]) + + ti_create.click( + fn=modules.textual_inversion.ui.create_embedding, + inputs=[ + ti_new_name, + ti_init_text, + ti_vectors, + ti_overwrite, + ], + outputs=[ + ti_name, + train_output, + train_outcome, + ] + ) + + ti_train.click( + fn=wrap_gradio_gpu_call(modules.textual_inversion.ui.train_embedding, extra_outputs=[gr.update()]), + _js="start_train_monitoring", + inputs=[ + dummy_component, + ti_name, + ti_learn_rate, + ti_batch_size, + ti_gradient_step, + ti_dataset_directory, + ti_log_directory, + ti_width, + ti_height, + ti_varsize, + ti_steps, + ti_clip_grad_mode, + ti_clip_grad_value, + ti_shuffle, + ti_tag_drop_out, + ti_latent_sampling_method, + ti_use_weight, + ti_create_every, + ti_save_every, + ti_template, + ti_save_image_with_stored_embedding, + ti_preview_from_txt2img, + *txt2img_preview_params, + ], + outputs=[ + train_output, + train_outcome, + ] + ) + + ### train hypernetwork tab + + with gr.Tab(label="Train hypernetwork", id="train_hypernetwork_tab") as tab_hn: + tab_hn.select(fn=lambda x: train_tab_change('hn'), inputs=[], outputs=[action_pp, action_ti, action_hn]) + gr.Markdown('## Select existing embedding to continue training or create a new one') + with FormRow(): + with gr.Column(): + with FormRow(): + hn_name = gr.Dropdown(label='Hypernetwork', choices=sorted(shared.hypernetworks)) + create_refresh_button(hn_name, shared.reload_hypernetworks, lambda: {"choices": sorted(shared.hypernetworks)}, "refresh_train_hypernetwork_name") + with gr.Column(): + hn_new_name = gr.Textbox(label="Name") + hn_new_sizes = gr.CheckboxGroup(label="Modules", value=["768", "320", "640", "1280"], choices=["768", "1024", "320", "640", "1280"]) + hn_new_layer_structure = gr.Textbox("1, 2, 1", label="Enter hypernetwork layer structure", placeholder="1st and last digit must be 1. ex:'1, 2, 1'") + with gr.Row(): + hn_new_activation_func = gr.Dropdown(value="linear", label="Select activation function of hypernetwork", choices=modules.hypernetworks.ui.keys) + hn_new_initialization_option = gr.Dropdown(value = "Normal", label="Select Layer weights initialization", choices=["Normal", "KaimingUniform", "KaimingNormal", "XavierUniform", "XavierNormal"]) + hn_new_add_layer_norm = gr.Checkbox(label="Add layer normalization") + hn_new_use_dropout = gr.Checkbox(label="Use dropout") + hn_new_dropout_structure = gr.Textbox("0, 0, 0", label="Enter hypernetwork Dropout structure", placeholder="1st and last digit must be 0 and values should be between 0 and 1. ex:'0, 0.01, 0'") + hn_overwrite = gr.Checkbox(value=False, label="Overwrite Old Hypernetwork") + with gr.Row(): + hn_create = gr.Button(value="Create hypernetwork", variant='secondary') + + with gr.Box(): + gr.Markdown('## Training parameters') + hn_learn_rate = gr.Textbox(label='Hypernetwork Learning rate', placeholder="Hypernetwork Learning rate", value="0.00001") + with FormRow(): + hn_clip_grad_mode = gr.Dropdown(value="disabled", label="Gradient Clipping", choices=["disabled", "value", "norm"]) + hn_clip_grad_value = gr.Number(label="Gradient clip value", value=0.1) + hn_batch_size = gr.Number(label='Batch size', value=1, precision=0) + hn_gradient_step = gr.Number(label='Gradient accumulation steps', value=1, precision=0) + hn_steps = gr.Number(label='Max steps', value=1000, precision=0) + + with gr.Box(): + gr.Markdown('## Training images') + hn_dataset_directory = gr.Textbox(label='Dataset directory', placeholder="Path to directory with input images") + with FormRow(): + hn_varsize = gr.Checkbox(label="Do not resize images", value=False) + hn_width = gr.Slider(minimum=64, maximum=2048, step=8, label="Width", value=512) + hn_height = gr.Slider(minimum=64, maximum=2048, step=8, label="Height", value=512) + hn_use_weight = gr.Checkbox(label="Use PNG alpha channel as loss weight", value=False) + + with gr.Box(): + gr.Markdown('## Dataset processing') + with FormRow(): + hn_template = gr.Dropdown(label='Prompt template', value="style_filewords.txt", choices=get_textual_inversion_template_names()) + create_refresh_button(hn_template, textual_inversion.list_textual_inversion_templates, lambda: {"choices": get_textual_inversion_template_names()}, "refrsh_train_template_file") + hn_shuffle_tags = gr.Checkbox(label="Shuffle tags by ',' when creating prompts.", value=False) + hn_tag_drop_out = gr.Slider(minimum=0, maximum=1, step=0.1, label="Drop out tags when creating prompts", value=0) + hn_latent_sampling_method = gr.Radio(label='Choose latent sampling method', value="once", choices=['once', 'deterministic', 'random']) + + with gr.Box(): + gr.Markdown('## Training outputs') + with FormRow(): + hn_create_every = gr.Number(label='Create interim images', value=500, precision=0) + hn_save_every = gr.Number(label='Create interim hypernetworks', value=500, precision=0) + hn_preview_from_txt2img = gr.Checkbox(label='Use current settings for previews', value=False) + hn_log_directory = gr.Textbox(label='Log directory', placeholder="Path to directory where to write outputs", value=f"{os.path.join('cmd_opts.data_dir', 'train/log/embeddings')}") + + hn_stop.click(fn=lambda: shared.state.interrupt(), inputs=[], outputs=[]) + + hn_create.click( + fn=modules.hypernetworks.ui.create_hypernetwork, + inputs=[ + hn_new_name, + hn_new_sizes, + hn_overwrite, + hn_new_layer_structure, + hn_new_activation_func, + hn_new_initialization_option, + hn_new_add_layer_norm, + hn_new_use_dropout, + hn_new_dropout_structure + ], + outputs=[ + hn_name, + train_output, + train_outcome, + ] + ) + + hn_train.click( + fn=wrap_gradio_gpu_call(modules.hypernetworks.ui.train_hypernetwork, extra_outputs=[gr.update()]), + _js="start_train_monitoring", + inputs=[ + dummy_component, + hn_name, + hn_learn_rate, + hn_batch_size, + hn_gradient_step, + hn_dataset_directory, + hn_log_directory, + hn_width, + hn_height, + hn_varsize, + hn_steps, + hn_clip_grad_mode, + hn_clip_grad_value, + hn_shuffle_tags, + hn_tag_drop_out, + hn_latent_sampling_method, + hn_use_weight, + hn_create_every, + hn_save_every, + hn_template, + hn_preview_from_txt2img, + *txt2img_preview_params, + ], + outputs=[ + train_output, + train_outcome, + ] + ) + + params = script_callbacks.UiTrainTabParams(txt2img_preview_params) + script_callbacks.ui_train_tabs_callback(params) diff --git a/wiki b/wiki index 01aabc126..f941746c0 160000 --- a/wiki +++ b/wiki @@ -1 +1 @@ -Subproject commit 01aabc1269e0f0f31f6400c4665f0d0b03b75816 +Subproject commit f941746c0ed2afcaa37c1ec77b86da4dee131bee