Slider training functioning, time to perfect it
This commit is contained in:
@@ -3,6 +3,8 @@
|
|||||||
import time
|
import time
|
||||||
from collections import OrderedDict
|
from collections import OrderedDict
|
||||||
import os
|
import os
|
||||||
|
from typing import List
|
||||||
|
|
||||||
from toolkit.kohya_model_util import load_vae
|
from toolkit.kohya_model_util import load_vae
|
||||||
from toolkit.lora_special import LoRASpecialNetwork
|
from toolkit.lora_special import LoRASpecialNetwork
|
||||||
from toolkit.paths import REPOS_ROOT
|
from toolkit.paths import REPOS_ROOT
|
||||||
@@ -99,6 +101,21 @@ class ModelConfig:
|
|||||||
raise ValueError('name_or_path must be specified')
|
raise ValueError('name_or_path must be specified')
|
||||||
|
|
||||||
|
|
||||||
|
class SliderTargetConfig:
|
||||||
|
def __init__(self, **kwargs):
|
||||||
|
self.target_class: str = kwargs.get('target_class', None)
|
||||||
|
self.positive: str = kwargs.get('positive', None)
|
||||||
|
self.negative: str = kwargs.get('negative', None)
|
||||||
|
|
||||||
|
|
||||||
|
class SliderConfig:
|
||||||
|
def __init__(self, **kwargs):
|
||||||
|
targets = kwargs.get('targets', [])
|
||||||
|
targets = [SliderTargetConfig(**target) for target in targets]
|
||||||
|
self.targets: List[SliderTargetConfig] = targets
|
||||||
|
self.resolutions: List[List[int]] = kwargs.get('resolutions', [[512, 512]])
|
||||||
|
|
||||||
|
|
||||||
class PromptSettingsOld:
|
class PromptSettingsOld:
|
||||||
def __init__(self, **kwargs):
|
def __init__(self, **kwargs):
|
||||||
self.target: str = kwargs.get('target', None)
|
self.target: str = kwargs.get('target', None)
|
||||||
@@ -113,6 +130,24 @@ class PromptSettingsOld:
|
|||||||
self.dynamic_crops: bool = kwargs.get('dynamic_crops', False) # default is False. only used when model is XL
|
self.dynamic_crops: bool = kwargs.get('dynamic_crops', False) # default is False. only used when model is XL
|
||||||
|
|
||||||
|
|
||||||
|
class EncodedPromptPair:
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
target_class,
|
||||||
|
positive,
|
||||||
|
negative,
|
||||||
|
neutral,
|
||||||
|
width=512,
|
||||||
|
height=512
|
||||||
|
):
|
||||||
|
self.target_class = target_class
|
||||||
|
self.positive = positive
|
||||||
|
self.negative = negative
|
||||||
|
self.neutral = neutral
|
||||||
|
self.width = width
|
||||||
|
self.height = height
|
||||||
|
|
||||||
|
|
||||||
class TrainSliderProcess(BaseTrainProcess):
|
class TrainSliderProcess(BaseTrainProcess):
|
||||||
def __init__(self, process_id: int, job, config: OrderedDict):
|
def __init__(self, process_id: int, job, config: OrderedDict):
|
||||||
super().__init__(process_id, job, config)
|
super().__init__(process_id, job, config)
|
||||||
@@ -127,10 +162,9 @@ class TrainSliderProcess(BaseTrainProcess):
|
|||||||
self.save_config = SaveConfig(**self.get_conf('save', {}))
|
self.save_config = SaveConfig(**self.get_conf('save', {}))
|
||||||
self.sample_config = SampleConfig(**self.get_conf('sample', {}))
|
self.sample_config = SampleConfig(**self.get_conf('sample', {}))
|
||||||
self.logging_config = LogingConfig(**self.get_conf('logging', {}))
|
self.logging_config = LogingConfig(**self.get_conf('logging', {}))
|
||||||
|
self.slider_config = SliderConfig(**self.get_conf('slider', {}))
|
||||||
self.sd = None
|
self.sd = None
|
||||||
|
|
||||||
self.prompt_settings = self.get_prompt_settings()
|
|
||||||
|
|
||||||
# added later
|
# added later
|
||||||
self.network = None
|
self.network = None
|
||||||
self.scheduler = None
|
self.scheduler = None
|
||||||
@@ -142,14 +176,6 @@ class TrainSliderProcess(BaseTrainProcess):
|
|||||||
param.data = -param.data
|
param.data = -param.data
|
||||||
self.is_flipped = not self.is_flipped
|
self.is_flipped = not self.is_flipped
|
||||||
|
|
||||||
def get_prompt_settings(self):
|
|
||||||
prompts = self.get_conf('prompts', required=True)
|
|
||||||
prompt_settings = [PromptSettingsOld(**prompt) for prompt in prompts]
|
|
||||||
# for i, prompt in enumerate(prompts):
|
|
||||||
# prompt_settings[i].fill_prompts(prompt)
|
|
||||||
|
|
||||||
return prompt_settings
|
|
||||||
|
|
||||||
def sample(self, step=None):
|
def sample(self, step=None):
|
||||||
sample_folder = os.path.join(self.save_root, 'samples')
|
sample_folder = os.path.join(self.save_root, 'samples')
|
||||||
if not os.path.exists(sample_folder):
|
if not os.path.exists(sample_folder):
|
||||||
@@ -352,44 +378,38 @@ class TrainSliderProcess(BaseTrainProcess):
|
|||||||
max_iterations=self.train_config.steps,
|
max_iterations=self.train_config.steps,
|
||||||
lr_min=self.train_config.lr / 100, # not sure why leco did this, but ill do it to
|
lr_min=self.train_config.lr / 100, # not sure why leco did this, but ill do it to
|
||||||
)
|
)
|
||||||
criteria = torch.nn.MSELoss()
|
loss_function = torch.nn.MSELoss()
|
||||||
|
|
||||||
if self.logging_config.verbose:
|
|
||||||
print("Prompts")
|
|
||||||
for settings in self.prompt_settings:
|
|
||||||
print(settings)
|
|
||||||
|
|
||||||
# debug
|
|
||||||
# debug_util.check_requires_grad(network)
|
|
||||||
# debug_util.check_training_mode(network)
|
|
||||||
|
|
||||||
cache = PromptEmbedsCache()
|
cache = PromptEmbedsCache()
|
||||||
prompt_pairs: list[PromptEmbedsPair] = []
|
prompt_pairs: list[LatentPair] = []
|
||||||
|
|
||||||
|
# get encoded latents for our prompts
|
||||||
with torch.no_grad():
|
with torch.no_grad():
|
||||||
for settings in self.prompt_settings:
|
neutral = ""
|
||||||
self.print(settings)
|
for target in self.slider_config.targets:
|
||||||
for prompt in [
|
for resolution in self.slider_config.resolutions:
|
||||||
settings.target,
|
width, height = resolution
|
||||||
settings.positive,
|
for prompt in [
|
||||||
settings.neutral,
|
target.target_class,
|
||||||
settings.unconditional,
|
target.positive,
|
||||||
]:
|
target.negative,
|
||||||
if cache[prompt] == None:
|
neutral # empty neutral
|
||||||
cache[prompt] = train_util.encode_prompts(
|
]:
|
||||||
tokenizer, text_encoder, [prompt]
|
if cache[prompt] == None:
|
||||||
)
|
cache[prompt] = train_util.encode_prompts(
|
||||||
|
tokenizer, text_encoder, [prompt]
|
||||||
|
)
|
||||||
|
|
||||||
prompt_pairs.append(
|
prompt_pairs.append(
|
||||||
PromptEmbedsPair(
|
EncodedPromptPair(
|
||||||
criteria,
|
target_class=cache[target.target_class],
|
||||||
cache[settings.target],
|
positive=cache[target.positive],
|
||||||
cache[settings.positive],
|
negative=cache[target.negative],
|
||||||
cache[settings.unconditional],
|
neutral=cache[neutral],
|
||||||
cache[settings.neutral],
|
width=width,
|
||||||
settings,
|
height=height,
|
||||||
|
)
|
||||||
)
|
)
|
||||||
)
|
|
||||||
|
|
||||||
# move to cpu to save vram
|
# move to cpu to save vram
|
||||||
# tokenizer.to("cpu")
|
# tokenizer.to("cpu")
|
||||||
@@ -400,7 +420,6 @@ class TrainSliderProcess(BaseTrainProcess):
|
|||||||
self.print("Generating baseline samples before training")
|
self.print("Generating baseline samples before training")
|
||||||
self.sample(0)
|
self.sample(0)
|
||||||
|
|
||||||
self.progress_bar = tqdm(range(self.train_config.steps))
|
|
||||||
self.progress_bar = tqdm(
|
self.progress_bar = tqdm(
|
||||||
total=self.train_config.steps,
|
total=self.train_config.steps,
|
||||||
desc=self.job.name,
|
desc=self.job.name,
|
||||||
@@ -408,6 +427,29 @@ class TrainSliderProcess(BaseTrainProcess):
|
|||||||
)
|
)
|
||||||
self.step_num = 0
|
self.step_num = 0
|
||||||
for step in range(self.train_config.steps):
|
for step in range(self.train_config.steps):
|
||||||
|
|
||||||
|
# get a random pair
|
||||||
|
prompt_pair: EncodedPromptPair = prompt_pairs[
|
||||||
|
torch.randint(0, len(prompt_pairs), (1,)).item()
|
||||||
|
]
|
||||||
|
|
||||||
|
height = prompt_pair.height
|
||||||
|
width = prompt_pair.width
|
||||||
|
positive = prompt_pair.positive
|
||||||
|
target_class = prompt_pair.target_class
|
||||||
|
neutral = prompt_pair.neutral
|
||||||
|
negative = prompt_pair.negative
|
||||||
|
|
||||||
|
# swap every other step and invert lora to spread slider
|
||||||
|
do_swap = step % 2 == 0
|
||||||
|
|
||||||
|
if do_swap:
|
||||||
|
negative = prompt_pair.positive
|
||||||
|
positive = prompt_pair.negative
|
||||||
|
# set the network in a negative weight
|
||||||
|
self.network.multiplier = -1.0
|
||||||
|
|
||||||
|
|
||||||
with torch.no_grad():
|
with torch.no_grad():
|
||||||
noise_scheduler.set_timesteps(
|
noise_scheduler.set_timesteps(
|
||||||
self.train_config.max_denoising_steps, device=self.device_torch
|
self.train_config.max_denoising_steps, device=self.device_torch
|
||||||
@@ -415,34 +457,17 @@ class TrainSliderProcess(BaseTrainProcess):
|
|||||||
|
|
||||||
optimizer.zero_grad()
|
optimizer.zero_grad()
|
||||||
|
|
||||||
prompt_pair: PromptEmbedsPair = prompt_pairs[
|
# ger a random number of steps
|
||||||
torch.randint(0, len(prompt_pairs), (1,)).item()
|
|
||||||
]
|
|
||||||
|
|
||||||
# 1 ~ 49 random from 1 to 49
|
|
||||||
timesteps_to = torch.randint(
|
timesteps_to = torch.randint(
|
||||||
1, self.train_config.max_denoising_steps, (1,)
|
1, self.train_config.max_denoising_steps, (1,)
|
||||||
).item()
|
).item()
|
||||||
|
|
||||||
height, width = (
|
|
||||||
prompt_pair.resolution,
|
|
||||||
prompt_pair.resolution,
|
|
||||||
)
|
|
||||||
if prompt_pair.dynamic_resolution:
|
|
||||||
height, width = train_util.get_random_resolution_in_bucket(
|
|
||||||
prompt_pair.resolution
|
|
||||||
)
|
|
||||||
|
|
||||||
if self.logging_config.verbose:
|
|
||||||
self.print("guidance_scale:", prompt_pair.guidance_scale)
|
|
||||||
self.print("resolution:", prompt_pair.resolution)
|
|
||||||
self.print("dynamic_resolution:", prompt_pair.dynamic_resolution)
|
|
||||||
if prompt_pair.dynamic_resolution:
|
|
||||||
self.print("bucketed resolution:", (height, width))
|
|
||||||
self.print("batch_size:", prompt_pair.batch_size)
|
|
||||||
|
|
||||||
latents = train_util.get_initial_latents(
|
latents = train_util.get_initial_latents(
|
||||||
noise_scheduler, prompt_pair.batch_size, height, width, 1
|
noise_scheduler,
|
||||||
|
self.train_config.batch_size,
|
||||||
|
height,
|
||||||
|
width,
|
||||||
|
1
|
||||||
).to(self.device_torch, dtype=dtype)
|
).to(self.device_torch, dtype=dtype)
|
||||||
|
|
||||||
with self.network:
|
with self.network:
|
||||||
@@ -453,9 +478,9 @@ class TrainSliderProcess(BaseTrainProcess):
|
|||||||
noise_scheduler,
|
noise_scheduler,
|
||||||
latents, # pass simple noise latents
|
latents, # pass simple noise latents
|
||||||
train_util.concat_embeddings(
|
train_util.concat_embeddings(
|
||||||
prompt_pair.unconditional,
|
positive, # unconditional
|
||||||
prompt_pair.target,
|
target_class, # target
|
||||||
prompt_pair.batch_size,
|
self.train_config.batch_size,
|
||||||
),
|
),
|
||||||
start_timesteps=0,
|
start_timesteps=0,
|
||||||
total_timesteps=timesteps_to,
|
total_timesteps=timesteps_to,
|
||||||
@@ -468,16 +493,16 @@ class TrainSliderProcess(BaseTrainProcess):
|
|||||||
int(timesteps_to * 1000 / self.train_config.max_denoising_steps)
|
int(timesteps_to * 1000 / self.train_config.max_denoising_steps)
|
||||||
]
|
]
|
||||||
|
|
||||||
# with network: Only empty LoRA is enabled outside with network :
|
# with network: 0 weight LoRA is enabled outside "with network:"
|
||||||
positive_latents = train_util.predict_noise(
|
positive_latents = train_util.predict_noise( # positive_latents
|
||||||
unet,
|
unet,
|
||||||
noise_scheduler,
|
noise_scheduler,
|
||||||
current_timestep,
|
current_timestep,
|
||||||
denoised_latents,
|
denoised_latents,
|
||||||
train_util.concat_embeddings(
|
train_util.concat_embeddings(
|
||||||
prompt_pair.unconditional,
|
positive, # unconditional
|
||||||
prompt_pair.positive,
|
negative, # positive
|
||||||
prompt_pair.batch_size,
|
self.train_config.batch_size,
|
||||||
),
|
),
|
||||||
guidance_scale=1,
|
guidance_scale=1,
|
||||||
).to("cpu", dtype=torch.float32)
|
).to("cpu", dtype=torch.float32)
|
||||||
@@ -487,9 +512,9 @@ class TrainSliderProcess(BaseTrainProcess):
|
|||||||
current_timestep,
|
current_timestep,
|
||||||
denoised_latents,
|
denoised_latents,
|
||||||
train_util.concat_embeddings(
|
train_util.concat_embeddings(
|
||||||
prompt_pair.unconditional,
|
positive, # unconditional
|
||||||
prompt_pair.neutral,
|
neutral, # neutral
|
||||||
prompt_pair.batch_size,
|
self.train_config.batch_size,
|
||||||
),
|
),
|
||||||
guidance_scale=1,
|
guidance_scale=1,
|
||||||
).to("cpu", dtype=torch.float32)
|
).to("cpu", dtype=torch.float32)
|
||||||
@@ -499,16 +524,12 @@ class TrainSliderProcess(BaseTrainProcess):
|
|||||||
current_timestep,
|
current_timestep,
|
||||||
denoised_latents,
|
denoised_latents,
|
||||||
train_util.concat_embeddings(
|
train_util.concat_embeddings(
|
||||||
prompt_pair.unconditional,
|
positive, # unconditional
|
||||||
prompt_pair.unconditional,
|
positive, # unconditional
|
||||||
prompt_pair.batch_size,
|
self.train_config.batch_size,
|
||||||
),
|
),
|
||||||
guidance_scale=1,
|
guidance_scale=1,
|
||||||
).to("cpu", dtype=torch.float32)
|
).to("cpu", dtype=torch.float32)
|
||||||
# if self.logging_config.verbose:
|
|
||||||
# self.print("positive_latents:", positive_latents[0, 0, :5, :5])
|
|
||||||
# self.print("neutral_latents:", neutral_latents[0, 0, :5, :5])
|
|
||||||
# self.print("unconditional_latents:", unconditional_latents[0, 0, :5, :5])
|
|
||||||
|
|
||||||
with self.network:
|
with self.network:
|
||||||
target_latents = train_util.predict_noise(
|
target_latents = train_util.predict_noise(
|
||||||
@@ -517,9 +538,9 @@ class TrainSliderProcess(BaseTrainProcess):
|
|||||||
current_timestep,
|
current_timestep,
|
||||||
denoised_latents,
|
denoised_latents,
|
||||||
train_util.concat_embeddings(
|
train_util.concat_embeddings(
|
||||||
prompt_pair.unconditional,
|
positive, # unconditional
|
||||||
prompt_pair.target,
|
target_class, # target
|
||||||
prompt_pair.batch_size,
|
self.train_config.batch_size,
|
||||||
),
|
),
|
||||||
guidance_scale=1,
|
guidance_scale=1,
|
||||||
).to("cpu", dtype=torch.float32)
|
).to("cpu", dtype=torch.float32)
|
||||||
@@ -531,12 +552,23 @@ class TrainSliderProcess(BaseTrainProcess):
|
|||||||
neutral_latents.requires_grad = False
|
neutral_latents.requires_grad = False
|
||||||
unconditional_latents.requires_grad = False
|
unconditional_latents.requires_grad = False
|
||||||
|
|
||||||
loss = prompt_pair.loss(
|
erase = True
|
||||||
target_latents=target_latents,
|
guidance_scale = 1.0
|
||||||
positive_latents=positive_latents,
|
|
||||||
neutral_latents=neutral_latents,
|
offset = guidance_scale * (positive_latents - unconditional_latents)
|
||||||
unconditional_latents=unconditional_latents,
|
|
||||||
|
offset_neutral = neutral_latents
|
||||||
|
if erase:
|
||||||
|
offset_neutral -= offset
|
||||||
|
else:
|
||||||
|
# enhance
|
||||||
|
offset_neutral += offset
|
||||||
|
|
||||||
|
loss = loss_function(
|
||||||
|
target_latents,
|
||||||
|
offset_neutral,
|
||||||
)
|
)
|
||||||
|
|
||||||
loss_float = loss.item()
|
loss_float = loss.item()
|
||||||
if self.train_config.optimizer.startswith('dadaptation'):
|
if self.train_config.optimizer.startswith('dadaptation'):
|
||||||
learning_rate = (
|
learning_rate = (
|
||||||
@@ -561,6 +593,9 @@ class TrainSliderProcess(BaseTrainProcess):
|
|||||||
)
|
)
|
||||||
flush()
|
flush()
|
||||||
|
|
||||||
|
# reset network
|
||||||
|
self.network.multiplier = 1.0
|
||||||
|
|
||||||
# don't do on first step
|
# don't do on first step
|
||||||
if self.step_num != self.start_step:
|
if self.step_num != self.start_step:
|
||||||
# pause progress bar
|
# pause progress bar
|
||||||
@@ -594,8 +629,11 @@ class TrainSliderProcess(BaseTrainProcess):
|
|||||||
# end of step
|
# end of step
|
||||||
self.step_num = step
|
self.step_num = step
|
||||||
|
|
||||||
|
print("")
|
||||||
|
|
||||||
self.save()
|
self.save()
|
||||||
|
|
||||||
|
|
||||||
del (
|
del (
|
||||||
unet,
|
unet,
|
||||||
noise_scheduler,
|
noise_scheduler,
|
||||||
|
|||||||
Reference in New Issue
Block a user