From 832c24c24d8695804acf6bdcd0265705b526fdfe Mon Sep 17 00:00:00 2001 From: Vladimir Mandic Date: Thu, 19 Jan 2023 17:55:31 -0500 Subject: [PATCH] update interrogate and train --- TODO.md | 11 ++++----- automatic.sh | 2 +- cli/modules/interrogate.py | 23 +++++++++++++++--- cli/modules/losschart.py | 3 ++- cli/train.json | 9 ++++++- cli/train.py | 48 +++++++++++++++++++++----------------- config.json | 3 ++- ui-config.json | 4 ++-- 8 files changed, 66 insertions(+), 37 deletions(-) diff --git a/TODO.md b/TODO.md index 5824eba11..4aba38521 100644 --- a/TODO.md +++ b/TODO.md @@ -1,10 +1,8 @@ -# Merge repos +# Publish extensions -- Move sd-extensions/scripts/save-steps-animation script to automatic/extensions-builtin -- Move sd-extensions/api to automatic/cli -- [https://github.com/vladmandic/automatic] -- [https://github.com/vladmandic/sd-extensions] -- [https://github.com/vladmandic/generative-art] +- `sd-extension-aesthetic-scorer` +- `sd-extension-steps-animation` +- `sd-extension-system-info` # Investigating @@ -16,6 +14,7 @@ Need to study more to determine best out-of-the-box settings: - Impact of Codeformer - Impact of Hires fix: - e.g 25 steps and denoising strength 0.25-0.7 +- Impact of non-square target resolution ## SDAPI diff --git a/automatic.sh b/automatic.sh index eefc3ee76..c54ba3dde 100755 --- a/automatic.sh +++ b/automatic.sh @@ -4,5 +4,5 @@ export TF_CPP_MIN_LOG_LEVEL=2 export FORCE_CUDA="1" export ATTN_PRECISION=fp16 export PYTORCH_CUDA_ALLOC_CONF=garbage_collection_threshold:0.9,max_split_size_mb:512 -exec accelerate launch --num_cpu_threads_per_process=6 launch.py --api --xformers --disable-console-progressbars +exec accelerate launch --num_cpu_threads_per_process=6 launch.py --api --xformers --disable-console-progressbars "$@" # --opt-channelslast diff --git a/cli/modules/interrogate.py b/cli/modules/interrogate.py index 7d1bdcf02..0a7c00c15 100755 --- a/cli/modules/interrogate.py +++ b/cli/modules/interrogate.py @@ -16,6 +16,10 @@ from util import log, Map import sdapi as sdapi +stats = { 'captions': {}, 'keywords': {} } +exclude = ['a', 'in', 'on', 'out', 'at', 'the', 'and', 'with', 'next', 'to', 'it', 'for', 'of', 'into', 'that'] + + def decode(encoding): if encoding.startswith("data:image/"): encoding = encoding.split(";")[1].split(",")[1] @@ -25,6 +29,8 @@ def decode(encoding): def encode(f): image = Image.open(f) exif = image.getexif() + if image.mode == 'RGBA': + image = image.convert('RGB') with io.BytesIO() as stream: image.save(stream, 'JPEG', exif = exif) values = stream.getvalue() @@ -32,6 +38,13 @@ def encode(f): return encoded +def print_summary(): + captions = dict(sorted(stats['captions'].items(), key=lambda x:x[1], reverse=True)) + log.info({ 'caption stats': captions }) + keywords = dict(sorted(stats['keywords'].items(), key=lambda x:x[1], reverse=True)) + log.info({ 'keyword stats': keywords }) + + async def interrogate(f): if not filetype.is_image(f): log.info({ 'interrogate skip': f }) @@ -41,7 +54,6 @@ async def interrogate(f): # run clip json.model = 'clip' res = await sdapi.post('/sdapi/v1/interrogate', json) - # res = sdapi.postsync('/sdapi/v1/interrogate', json) caption = "" style = "" if 'caption' in res: @@ -50,18 +62,22 @@ async def interrogate(f): if ', by' in caption: style = caption.split(', by')[1].strip() log.info({ 'interrogate style': style }) + for word in caption.split(' '): + if word not in exclude: + stats['captions'][word] = stats['captions'][word] + 1 if word in stats['captions'] else 1 else: log.error({ 'interrogate clip error': res }) # run booru json.model = 'deepdanbooru' res = await sdapi.post('/sdapi/v1/interrogate', json) - # res = sdapi.postsync('/sdapi/v1/interrogate', json) keywords = {} if 'caption' in res: for term in res.caption.split(', '): term = term.replace('(', '').replace(')', '').split(':') keywords[term[0]] = term[1] keywords = dict(sorted(keywords.items(), key=lambda x:x[1], reverse=True)) + for word in keywords.items(): + stats['keywords'][word[0]] = stats['keywords'][word[0]] + 1 if word[0] in stats['keywords'] else 1 log.info({ 'interrogate keywords': keywords }) else: log.error({ 'interrogate booru error': res }) @@ -80,12 +96,13 @@ async def main(): elif os.path.isdir(arg): for root, _dirs, files in os.walk(arg): for f in files: - await interrogate(os.path.join(root, f)) + caption, keywords, _style = await interrogate(os.path.join(root, f)) else: log.error({ 'interrogate unknown file type': arg }) else: log.error({ 'interrogate file missing': arg }) await sdapi.close() + print_summary() if __name__ == "__main__": diff --git a/cli/modules/losschart.py b/cli/modules/losschart.py index 9c5562be2..43873d051 100755 --- a/cli/modules/losschart.py +++ b/cli/modules/losschart.py @@ -34,7 +34,8 @@ def plot(logdir: str, name: str): step, loss, rate = plt.np.loadtxt(f, delimiter = ',', skiprows = 1, usecols = [0, 3, 4], unpack = True) d = settings(logdir, name) - window = d.get('gradient_step', 1) * d.get('batch_size', 1) + # window = d.get('gradient_step', 1) * d.get('batch_size', 1) + window = d.get('save_embedding_every', 1) try: log.debug({ 'loss plot': name, 'output': img, 'data': f, 'records': len(step) }) except: diff --git a/cli/train.json b/cli/train.json index d6b3050bc..5db0dd189 100644 --- a/cli/train.json +++ b/cli/train.json @@ -29,7 +29,14 @@ "process_focal_crop_edges_weight": 0.5, "process_focal_crop_debug": false, "split_threshold": 0.5, - "overlap_ratio": 0.2 + "overlap_ratio": 0.2, + "process_multicrop": null, + "process_multicrop_mindim": null, + "process_multicrop_maxdim": null, + "process_multicrop_minarea": null, + "process_multicrop_maxarea": null, + "process_multicrop_objective": null, + "process_multicrop_threshold": null }, "train_embedding": { "id_task": 0, diff --git a/cli/train.py b/cli/train.py index adc309920..113952e49 100755 --- a/cli/train.py +++ b/cli/train.py @@ -264,18 +264,37 @@ async def create(params): async def train(params): log.debug({ 'train start' }) args.train_embedding.embedding_name = params.name - args.train_embedding.data_root = args.preprocess.process_dst + imgs = [f for f in os.listdir(args.preprocess.process_dst) if os.path.isfile(os.path.join(args.preprocess.process_dst, f)) and filetype.is_image(os.path.join(args.preprocess.process_dst, f))] + args.train_embedding.data_root = args.preprocess.process_dst if len(imgs) == 0: log.error({ 'train no input images in folder': args.preprocess.process_dst }) return + if params.grad == -1: - grad = (len(imgs) // args.train_embedding.batch_size) - args.train_embedding.gradient_step = max(grad, 30) + args.train_embedding.gradient_step = len(imgs) // args.train_embedding.batch_size log.info({ 'dynamic gradient step': args.train_embedding.gradient_step }) if params.steps == -1: - args.train_embedding.steps = 5000 // args.train_embedding.gradient_step + args.train_embedding.steps = params.maxsteps // args.train_embedding.gradient_step log.info({ 'dynamic steps': args.train_embedding.steps }) + + epoch_size = args.train_embedding.batch_size * args.train_embedding.gradient_step + if args.train_embedding.create_image_every == -1: + args.train_embedding.create_image_every = args.train_embedding.steps // 10 + if args.train_embedding.save_embedding_every == -1: + args.train_embedding.save_embedding_every = args.train_embedding.steps // 10 + if args.train_embedding.learn_rate == -1: + loss_args = { + "steps": args.train_embedding.steps, + "step": epoch_size, + "loss_start": params.rstart, + "loss_end": params.rend, + "loss_type": 'power', + "power": params.rdescend + } + args.train_embedding.learn_rate = gen_loss_rate_str(**loss_args) + log.debug({ 'learning rate': args.train_embedding.learn_rate, 'params': loss_args }) + log.info({ 'train embedding': { 'name': params.name, 'source': args.preprocess.process_dst, @@ -284,7 +303,7 @@ async def train(params): 'batch': args.train_embedding.batch_size, 'gradient-step': args.train_embedding.gradient_step, 'sampling': args.train_embedding.latent_sampling_method, - 'epoch-size': args.train_embedding.batch_size * args.train_embedding.gradient_step } + 'epoch-size': epoch_size } }) log.info({ 'learn-rate': args.train_embedding.learn_rate }) log.debug({ 'train args': args.train_embedding }) @@ -389,11 +408,12 @@ async def main(): parser.add_argument("--init", type = str, default = "person", required = False, help = "initialization class, default: %(default)s") parser.add_argument("--dst", type = str, default = "/tmp", required = False, help = "destination image folder for processed images, default: %(default)s") parser.add_argument("--steps", type = int, default = -1, required = False, help = "training steps, default: %(default)s") + parser.add_argument("--maxsteps", type = int, default = 2500, required = False, help = "max training steps used when dynamic gradient is active, default: %(default)s") parser.add_argument("--vectors", type = int, default = -1, required = False, help = "number of vectors per token, default: dynamic based on number of input images") parser.add_argument("--batch", type = int, default = 1, required = False, help = "batch size, default: %(default)s") parser.add_argument("--rate", type = str, default = "", required = False, help = "learning rate, default: dynamic") parser.add_argument("--rstart", type = float, default = 0.01, required = False, help = "starting learn rate if using dynamic rate, default: %(default)s") - parser.add_argument("--rend", type = float, default = 0.0001, required = False, help = "ending learn rate if using dynamic rate, default: %(default)s") + parser.add_argument("--rend", type = float, default = 0.0005, required = False, help = "ending learn rate if using dynamic rate, default: %(default)s") parser.add_argument("--rdescend", type = float, default = 2, required = False, help = "learn rate descend power when using dynamic rate, default: %(default)s") parser.add_argument("--grad", type = int, default = -1, required = False, help = "accumulate gradient over n images, default: : %(default)s") parser.add_argument("--type", type = str, default = 'subject', required = False, help = "training type: subject/style/unknown, default: %(default)s") @@ -444,22 +464,6 @@ async def main(): args.train_embedding.learn_rate = params.rate if params.grad > -1: args.train_embedding.gradient_step = params.grad - epoch_size = args.train_embedding.batch_size * args.train_embedding.gradient_step - if args.train_embedding.create_image_every == -1: - args.train_embedding.create_image_every = epoch_size - if args.train_embedding.save_embedding_every == -1: - args.train_embedding.save_embedding_every = epoch_size - if args.train_embedding.learn_rate == -1: - loss_args = { - "steps": args.train_embedding.steps, - "step": epoch_size, - "loss_start": params.rstart, - "loss_end": params.rend, - "loss_type": 'power', - "power": params.rdescend - } - args.train_embedding.learn_rate = gen_loss_rate_str(**loss_args) - log.debug({ 'learning rate': args.train_embedding.learn_rate, 'params': loss_args }) if params.type == 'subject': if params.skipcaption: args.train_embedding.template_filename = 'subject.txt' diff --git a/config.json b/config.json index 3e230f202..9dca852f8 100644 --- a/config.json +++ b/config.json @@ -128,7 +128,8 @@ "PLMS", "DPM++ 2S a", "DPM++ SDE Karras", - "DPM2 a Karras" + "DPM2 a Karras", + "LMS Karras" ], "eta_ddim": 0.0, "eta_ancestral": 1.0, diff --git a/ui-config.json b/ui-config.json index c77ea6d20..8cb36e20d 100644 --- a/ui-config.json +++ b/ui-config.json @@ -7,7 +7,7 @@ "txt2img/Style 1/visible": true, "txt2img/Style 2/value": "None", "txt2img/Style 2/visible": true, - "txt2img/Sampling method/value": "Euler a", + "txt2img/Sampling method/value": "DPM2 Karras", "txt2img/Sampling method/visible": true, "txt2img/Sampling Steps/visible": true, "txt2img/Sampling Steps/value": 20, @@ -114,7 +114,7 @@ "img2img/Output directory/value": "", "img2img/Resize mode/visible": true, "img2img/Resize mode/value": "Just resize", - "img2img/Sampling method/value": "Euler a", + "img2img/Sampling method/value": "DPM2 Karras", "img2img/Sampling method/visible": true, "img2img/Sampling Steps/visible": true, "img2img/Sampling Steps/value": 20,