mirror of
https://github.com/vladmandic/automatic
synced 2026-09-19 17:24:32 +02:00
major refactor: remove backend original
Signed-off-by: Vladimir Mandic <mandic00@live.com>
This commit is contained in:
@@ -22,7 +22,6 @@ ignore-paths=/usr/lib/.*$,
|
||||
modules/hijack/ddpm_edit.py,
|
||||
modules/intel,
|
||||
modules/intel/ipex,
|
||||
modules/k-diffusion,
|
||||
modules/ldsr,
|
||||
modules/onnx_impl,
|
||||
modules/pag,
|
||||
|
||||
@@ -8,7 +8,6 @@ exclude = [
|
||||
"modules/flash_attn_triton_amd",
|
||||
"modules/hidiffusion",
|
||||
"modules/intel/ipex",
|
||||
"modules/k-diffusion",
|
||||
"modules/pag",
|
||||
"modules/schedulers",
|
||||
"modules/teacache",
|
||||
|
||||
+17
-7
@@ -1,6 +1,6 @@
|
||||
# Change Log for SD.Next
|
||||
|
||||
## Update for 2025-07-04
|
||||
## Update for 2025-07-05
|
||||
|
||||
- **Models**
|
||||
- [LBM: Latent Bridge Matching](https://github.com/gojasper/LBM)
|
||||
@@ -28,19 +28,31 @@
|
||||
- better handle startup import errors
|
||||
- fix diffusers models non-unique hash
|
||||
- fix loading of manually downloaded diffuser models
|
||||
- fix api `/sdapi/v1/embeddings` endpoint
|
||||
- improve model type autodetection
|
||||
- improve model auth check for hf repos
|
||||
- improve Chroma prompt padding as per recommendations
|
||||
- **Refactoring**
|
||||
- override `gradio` installer
|
||||
- major refactoring of requirements and dependencies to unblock `numpy>=2.1.0`
|
||||
- patch `insightface`
|
||||
- patch `k-diffusion`
|
||||
- obsolete **original backend**
|
||||
- remove majority of legacy **a1111** codebase
|
||||
- remove legacy ldm codebase: `/repositories/ldm`
|
||||
- remove legacy blip codebase: `/repositories/blip`
|
||||
- remove legacy clip model: `/models/karlo`
|
||||
- remove legacy model configs: `/configs/*.yaml`
|
||||
- remove legacy submodule: `/modules/k-diffusion`
|
||||
- remove legacy hypernetworks support: `/modules/hypernetworks`
|
||||
- remove legacy lora support: `/extensions-builtin/Lora` (does not impact current lora support)
|
||||
- remove legacy clip/blip interrogate module (does not impact current interrogate support)
|
||||
- remove modern-ui remove `only-original` vs `only-diffusers` code paths
|
||||
- cleanup `/modules`: move pipeline loaders to `/pipelines` root
|
||||
- cleanup `/modules`: move code folders used by pipelines to `/pipelines/<pipeline>` folder
|
||||
- cleanup `/modules`: move code folders used by scripts to `/scripts/<script>` folder
|
||||
- cleanup `/modules`: global rename `modules.scripts` to avoid conflict with `/scripts`
|
||||
- override `gradio` installer
|
||||
- major refactoring of requirements and dependencies to unblock `numpy>=2.1.0`
|
||||
- patch `insightface`
|
||||
- stronger lint rules
|
||||
add separate `npm run lint`, `npm run todo`, `npm run test` macros
|
||||
|
||||
## Update for 2025-06-30
|
||||
|
||||
@@ -3578,7 +3590,6 @@ Also new is support for **SDXL-Turbo** as well as new **Kandinsky 3** models and
|
||||
- lightweight native implementation of T2I adapters which can guide generation towards specific image style
|
||||
- supports most T2I models, not limited to SD 1.5
|
||||
- models are auto-downloaded on first use
|
||||
- for IP adapter support in *Original* backend, use standard *ControlNet* extension
|
||||
- **AnimateDiff**
|
||||
- lightweight native implementation of AnimateDiff models:
|
||||
*AnimateDiff 1.4, 1.5 v1, 1.5 v2, AnimateFace*
|
||||
@@ -3586,7 +3597,6 @@ Also new is support for **SDXL-Turbo** as well as new **Kandinsky 3** models and
|
||||
- models are auto-downloaded on first use
|
||||
- for video saving support, see video support section
|
||||
- can be combined with IP-Adapter for even better results!
|
||||
- for AnimateDiff support in *Original* backend, use standard *AnimateDiff* extension
|
||||
- **HDR latent control**, based on [article](https://huggingface.co/blog/TimothyAlexisVass/explaining-the-sdxl-latent-space#long-prompts-at-high-guidance-scales-becoming-possible)
|
||||
- in *Advanced* params
|
||||
- allows control of *latent clamping*, *color centering* and *range maximization*
|
||||
|
||||
@@ -10,8 +10,7 @@ Main ToDo list can be found at [GitHub projects](https://github.com/users/vladma
|
||||
- Feature: LoRA add OMI format support for SD35/FLUX.1
|
||||
- Feature: Merge FramePack into core
|
||||
- Refactor: sampler options
|
||||
- Remove: legacy LoRA loader
|
||||
- Remove: Original backend
|
||||
- Refactor: cleanup `/repositories/codeformer`
|
||||
- Remove: Agent Scheduler
|
||||
- Video: API support
|
||||
- ModernUI: Lite vs Expert mode
|
||||
|
||||
@@ -61,10 +61,6 @@ options = Map({
|
||||
'lora': {
|
||||
'strength': 1.0,
|
||||
},
|
||||
'hypernetwork': {
|
||||
'keyword': '',
|
||||
'strength': 1.0,
|
||||
},
|
||||
})
|
||||
|
||||
|
||||
@@ -243,46 +239,6 @@ async def lyco(params):
|
||||
log.info({ 'lyco preview created': model, 'image': fn, 'images': len(images), 'grid': [image.width, image.height], 'time': round(t, 2), 'its': round(its, 2) })
|
||||
|
||||
|
||||
async def hypernetwork(params):
|
||||
opt = await get('/sdapi/v1/options')
|
||||
folder = opt['hypernetwork_dir']
|
||||
if not os.path.exists(folder):
|
||||
log.error({ 'hypernetwork directory not found': folder })
|
||||
return
|
||||
models = [os.path.splitext(f)[0] for f in Path(folder).glob('**/*.pt')]
|
||||
log.info({ 'hypernetworks': len(models) })
|
||||
for model in models:
|
||||
if preview_exists(folder, model) and len(params.input) == 0: # if model preview exists and not manually included
|
||||
log.info({ 'hypernetwork preview exists': model })
|
||||
continue
|
||||
fn = os.path.join(folder, model + options.format)
|
||||
images = []
|
||||
labels = []
|
||||
t0 = time.time()
|
||||
keyword = options.hypernetwork.keyword
|
||||
options.generate.prompt = options.prompt.replace('<keyword>', options.hypernetwork.keyword)
|
||||
options.generate.prompt = options.generate.prompt.replace('<embedding>', '')
|
||||
options.generate.prompt = f' <hypernet:{model}:{options.hypernetwork.strength}> ' + options.generate.prompt
|
||||
log.info({ 'hypernetwork generating': model, 'keyword': keyword, 'prompt': options.generate.prompt })
|
||||
data = await generate(options = options, quiet=True)
|
||||
if 'image' in data:
|
||||
for img in data['image']:
|
||||
images.append(img)
|
||||
labels.append(keyword)
|
||||
else:
|
||||
log.error({ 'hypernetwork': model, 'keyword': keyword, 'error': data })
|
||||
t1 = time.time()
|
||||
if len(images) == 0:
|
||||
log.error({ 'model': model, 'error': 'no images generated' })
|
||||
continue
|
||||
image = grid(images = images, labels = labels, border = 8)
|
||||
log.info({ 'saving preview': fn, 'images': len(images), 'size': [image.width, image.height] })
|
||||
image.save(fn)
|
||||
t = t1 - t0
|
||||
its = 1.0 * options.generate.steps * len(images) / t
|
||||
log.info({ 'hypernetwork preview created': model, 'image': fn, 'images': len(images), 'grid': [image.width, image.height], 'time': round(t, 2), 'its': round(its, 2) })
|
||||
|
||||
|
||||
async def embedding(params):
|
||||
opt = await get('/sdapi/v1/options')
|
||||
folder = opt['embeddings_dir']
|
||||
@@ -327,7 +283,6 @@ async def create_previews(params):
|
||||
await preview_models(params)
|
||||
await lora(params)
|
||||
await lyco(params)
|
||||
await hypernetwork(params)
|
||||
await embedding(params)
|
||||
await close()
|
||||
|
||||
|
||||
@@ -24,7 +24,6 @@
|
||||
{
|
||||
"upscaler_1": "SwinIR_4x",
|
||||
"upscaler_2": "None",
|
||||
"upscale_first": false,
|
||||
"upscaling_resize": 0,
|
||||
"gfpgan_visibility": 0,
|
||||
"codeformer_visibility": 0,
|
||||
|
||||
@@ -1,72 +0,0 @@
|
||||
model:
|
||||
base_learning_rate: 1.0e-04
|
||||
target: ldm.models.diffusion.ddpm.LatentDiffusion
|
||||
params:
|
||||
linear_start: 0.00085
|
||||
linear_end: 0.0120
|
||||
num_timesteps_cond: 1
|
||||
log_every_t: 200
|
||||
timesteps: 1000
|
||||
first_stage_key: "jpg"
|
||||
cond_stage_key: "txt"
|
||||
image_size: 64
|
||||
channels: 4
|
||||
cond_stage_trainable: false # Note: different from the one we trained before
|
||||
conditioning_key: crossattn
|
||||
monitor: val/loss_simple_ema
|
||||
scale_factor: 0.18215
|
||||
use_ema: False
|
||||
|
||||
scheduler_config: # 10000 warmup steps
|
||||
target: ldm.lr_scheduler.LambdaLinearScheduler
|
||||
params:
|
||||
warm_up_steps: [ 10000 ]
|
||||
cycle_lengths: [ 10000000000000 ] # incredibly large number to prevent corner cases
|
||||
f_start: [ 1.e-6 ]
|
||||
f_max: [ 1. ]
|
||||
f_min: [ 1. ]
|
||||
|
||||
unet_config:
|
||||
target: ldm.modules.diffusionmodules.openaimodel.UNetModel
|
||||
params:
|
||||
image_size: 32 # unused
|
||||
in_channels: 4
|
||||
out_channels: 4
|
||||
model_channels: 320
|
||||
attention_resolutions: [ 4, 2, 1 ]
|
||||
num_res_blocks: 2
|
||||
channel_mult: [ 1, 2, 4, 4 ]
|
||||
num_heads: 8
|
||||
use_spatial_transformer: True
|
||||
transformer_depth: 1
|
||||
context_dim: 768
|
||||
use_checkpoint: True
|
||||
legacy: False
|
||||
|
||||
first_stage_config:
|
||||
target: ldm.models.autoencoder.AutoencoderKL
|
||||
params:
|
||||
embed_dim: 4
|
||||
monitor: val/rec_loss
|
||||
ddconfig:
|
||||
double_z: true
|
||||
z_channels: 4
|
||||
resolution: 256
|
||||
in_channels: 3
|
||||
out_ch: 3
|
||||
ch: 128
|
||||
ch_mult:
|
||||
- 1
|
||||
- 2
|
||||
- 4
|
||||
- 4
|
||||
num_res_blocks: 2
|
||||
attn_resolutions: []
|
||||
dropout: 0.0
|
||||
lossconfig:
|
||||
target: torch.nn.Identity
|
||||
|
||||
cond_stage_config:
|
||||
target: modules.xlmr.BertSeriesModelWithTransformation
|
||||
params:
|
||||
name: "XLMR-Large"
|
||||
@@ -1,98 +0,0 @@
|
||||
# File modified by authors of InstructPix2Pix from original (https://github.com/CompVis/stable-diffusion).
|
||||
# See more details in LICENSE.
|
||||
|
||||
model:
|
||||
base_learning_rate: 1.0e-04
|
||||
target: modules.hijack.ddpm_edit.LatentDiffusion
|
||||
params:
|
||||
linear_start: 0.00085
|
||||
linear_end: 0.0120
|
||||
num_timesteps_cond: 1
|
||||
log_every_t: 200
|
||||
timesteps: 1000
|
||||
first_stage_key: edited
|
||||
cond_stage_key: edit
|
||||
# image_size: 64
|
||||
# image_size: 32
|
||||
image_size: 16
|
||||
channels: 4
|
||||
cond_stage_trainable: false # Note: different from the one we trained before
|
||||
conditioning_key: hybrid
|
||||
monitor: val/loss_simple_ema
|
||||
scale_factor: 0.18215
|
||||
use_ema: false
|
||||
|
||||
scheduler_config: # 10000 warmup steps
|
||||
target: ldm.lr_scheduler.LambdaLinearScheduler
|
||||
params:
|
||||
warm_up_steps: [ 0 ]
|
||||
cycle_lengths: [ 10000000000000 ] # incredibly large number to prevent corner cases
|
||||
f_start: [ 1.e-6 ]
|
||||
f_max: [ 1. ]
|
||||
f_min: [ 1. ]
|
||||
|
||||
unet_config:
|
||||
target: ldm.modules.diffusionmodules.openaimodel.UNetModel
|
||||
params:
|
||||
image_size: 32 # unused
|
||||
in_channels: 8
|
||||
out_channels: 4
|
||||
model_channels: 320
|
||||
attention_resolutions: [ 4, 2, 1 ]
|
||||
num_res_blocks: 2
|
||||
channel_mult: [ 1, 2, 4, 4 ]
|
||||
num_heads: 8
|
||||
use_spatial_transformer: True
|
||||
transformer_depth: 1
|
||||
context_dim: 768
|
||||
use_checkpoint: True
|
||||
legacy: False
|
||||
|
||||
first_stage_config:
|
||||
target: ldm.models.autoencoder.AutoencoderKL
|
||||
params:
|
||||
embed_dim: 4
|
||||
monitor: val/rec_loss
|
||||
ddconfig:
|
||||
double_z: true
|
||||
z_channels: 4
|
||||
resolution: 256
|
||||
in_channels: 3
|
||||
out_ch: 3
|
||||
ch: 128
|
||||
ch_mult:
|
||||
- 1
|
||||
- 2
|
||||
- 4
|
||||
- 4
|
||||
num_res_blocks: 2
|
||||
attn_resolutions: []
|
||||
dropout: 0.0
|
||||
lossconfig:
|
||||
target: torch.nn.Identity
|
||||
|
||||
cond_stage_config:
|
||||
target: ldm.modules.encoders.modules.FrozenCLIPEmbedder
|
||||
|
||||
data:
|
||||
target: main.DataModuleFromConfig
|
||||
params:
|
||||
batch_size: 128
|
||||
num_workers: 1
|
||||
wrap: false
|
||||
validation:
|
||||
target: edit_dataset.EditDataset
|
||||
params:
|
||||
path: data/clip-filtered-dataset
|
||||
cache_dir: data/
|
||||
cache_name: data_10k
|
||||
split: val
|
||||
min_text_sim: 0.2
|
||||
min_image_sim: 0.75
|
||||
min_direction_sim: 0.2
|
||||
max_samples_per_prompt: 1
|
||||
min_resize_res: 512
|
||||
max_resize_res: 512
|
||||
crop_res: 512
|
||||
output_as_edit: False
|
||||
real_input: True
|
||||
@@ -1,98 +0,0 @@
|
||||
model:
|
||||
target: sgm.models.diffusion.DiffusionEngine
|
||||
params:
|
||||
scale_factor: 0.13025
|
||||
disable_first_stage_autocast: True
|
||||
|
||||
denoiser_config:
|
||||
target: sgm.modules.diffusionmodules.denoiser.DiscreteDenoiser
|
||||
params:
|
||||
num_idx: 1000
|
||||
|
||||
weighting_config:
|
||||
target: sgm.modules.diffusionmodules.denoiser_weighting.EpsWeighting
|
||||
scaling_config:
|
||||
target: sgm.modules.diffusionmodules.denoiser_scaling.EpsScaling
|
||||
discretization_config:
|
||||
target: sgm.modules.diffusionmodules.discretizer.LegacyDDPMDiscretization
|
||||
|
||||
network_config:
|
||||
target: sgm.modules.diffusionmodules.openaimodel.UNetModel
|
||||
params:
|
||||
adm_in_channels: 2816
|
||||
num_classes: sequential
|
||||
use_checkpoint: True
|
||||
in_channels: 4
|
||||
out_channels: 4
|
||||
model_channels: 320
|
||||
attention_resolutions: [4, 2]
|
||||
num_res_blocks: 2
|
||||
channel_mult: [1, 2, 4]
|
||||
num_head_channels: 64
|
||||
use_spatial_transformer: True
|
||||
use_linear_in_transformer: True
|
||||
transformer_depth: [1, 2, 10] # note: the first is unused (due to attn_res starting at 2) 32, 16, 8 --> 64, 32, 16
|
||||
context_dim: 2048
|
||||
spatial_transformer_attn_type: softmax-xformers
|
||||
legacy: False
|
||||
|
||||
conditioner_config:
|
||||
target: sgm.modules.GeneralConditioner
|
||||
params:
|
||||
emb_models:
|
||||
# crossattn cond
|
||||
- is_trainable: False
|
||||
input_key: txt
|
||||
target: sgm.modules.encoders.modules.FrozenCLIPEmbedder
|
||||
params:
|
||||
layer: hidden
|
||||
layer_idx: 11
|
||||
# crossattn and vector cond
|
||||
- is_trainable: False
|
||||
input_key: txt
|
||||
target: sgm.modules.encoders.modules.FrozenOpenCLIPEmbedder2
|
||||
params:
|
||||
arch: ViT-bigG-14
|
||||
version: laion2b_s39b_b160k
|
||||
freeze: True
|
||||
layer: penultimate
|
||||
always_return_pooled: True
|
||||
legacy: False
|
||||
# vector cond
|
||||
- is_trainable: False
|
||||
input_key: original_size_as_tuple
|
||||
target: sgm.modules.encoders.modules.ConcatTimestepEmbedderND
|
||||
params:
|
||||
outdim: 256 # multiplied by two
|
||||
# vector cond
|
||||
- is_trainable: False
|
||||
input_key: crop_coords_top_left
|
||||
target: sgm.modules.encoders.modules.ConcatTimestepEmbedderND
|
||||
params:
|
||||
outdim: 256 # multiplied by two
|
||||
# vector cond
|
||||
- is_trainable: False
|
||||
input_key: target_size_as_tuple
|
||||
target: sgm.modules.encoders.modules.ConcatTimestepEmbedderND
|
||||
params:
|
||||
outdim: 256 # multiplied by two
|
||||
|
||||
first_stage_config:
|
||||
target: sgm.models.autoencoder.AutoencoderKLInferenceWrapper
|
||||
params:
|
||||
embed_dim: 4
|
||||
monitor: val/rec_loss
|
||||
ddconfig:
|
||||
attn_type: vanilla-xformers
|
||||
double_z: true
|
||||
z_channels: 4
|
||||
resolution: 256
|
||||
in_channels: 3
|
||||
out_ch: 3
|
||||
ch: 128
|
||||
ch_mult: [1, 2, 4, 4]
|
||||
num_res_blocks: 2
|
||||
attn_resolutions: []
|
||||
dropout: 0.0
|
||||
lossconfig:
|
||||
target: torch.nn.Identity
|
||||
@@ -1,91 +0,0 @@
|
||||
model:
|
||||
target: sgm.models.diffusion.DiffusionEngine
|
||||
params:
|
||||
scale_factor: 0.13025
|
||||
disable_first_stage_autocast: True
|
||||
|
||||
denoiser_config:
|
||||
target: sgm.modules.diffusionmodules.denoiser.DiscreteDenoiser
|
||||
params:
|
||||
num_idx: 1000
|
||||
|
||||
weighting_config:
|
||||
target: sgm.modules.diffusionmodules.denoiser_weighting.EpsWeighting
|
||||
scaling_config:
|
||||
target: sgm.modules.diffusionmodules.denoiser_scaling.EpsScaling
|
||||
discretization_config:
|
||||
target: sgm.modules.diffusionmodules.discretizer.LegacyDDPMDiscretization
|
||||
|
||||
network_config:
|
||||
target: sgm.modules.diffusionmodules.openaimodel.UNetModel
|
||||
params:
|
||||
adm_in_channels: 2560
|
||||
num_classes: sequential
|
||||
use_checkpoint: True
|
||||
in_channels: 4
|
||||
out_channels: 4
|
||||
model_channels: 384
|
||||
attention_resolutions: [4, 2]
|
||||
num_res_blocks: 2
|
||||
channel_mult: [1, 2, 4, 4]
|
||||
num_head_channels: 64
|
||||
use_spatial_transformer: True
|
||||
use_linear_in_transformer: True
|
||||
transformer_depth: 4
|
||||
context_dim: [1280, 1280, 1280, 1280] # 1280
|
||||
spatial_transformer_attn_type: softmax-xformers
|
||||
legacy: False
|
||||
|
||||
conditioner_config:
|
||||
target: sgm.modules.GeneralConditioner
|
||||
params:
|
||||
emb_models:
|
||||
# crossattn and vector cond
|
||||
- is_trainable: False
|
||||
input_key: txt
|
||||
target: sgm.modules.encoders.modules.FrozenOpenCLIPEmbedder2
|
||||
params:
|
||||
arch: ViT-bigG-14
|
||||
version: laion2b_s39b_b160k
|
||||
legacy: False
|
||||
freeze: True
|
||||
layer: penultimate
|
||||
always_return_pooled: True
|
||||
# vector cond
|
||||
- is_trainable: False
|
||||
input_key: original_size_as_tuple
|
||||
target: sgm.modules.encoders.modules.ConcatTimestepEmbedderND
|
||||
params:
|
||||
outdim: 256 # multiplied by two
|
||||
# vector cond
|
||||
- is_trainable: False
|
||||
input_key: crop_coords_top_left
|
||||
target: sgm.modules.encoders.modules.ConcatTimestepEmbedderND
|
||||
params:
|
||||
outdim: 256 # multiplied by two
|
||||
# vector cond
|
||||
- is_trainable: False
|
||||
input_key: aesthetic_score
|
||||
target: sgm.modules.encoders.modules.ConcatTimestepEmbedderND
|
||||
params:
|
||||
outdim: 256 # multiplied by one
|
||||
|
||||
first_stage_config:
|
||||
target: sgm.models.autoencoder.AutoencoderKLInferenceWrapper
|
||||
params:
|
||||
embed_dim: 4
|
||||
monitor: val/rec_loss
|
||||
ddconfig:
|
||||
attn_type: vanilla-xformers
|
||||
double_z: true
|
||||
z_channels: 4
|
||||
resolution: 256
|
||||
in_channels: 3
|
||||
out_ch: 3
|
||||
ch: 128
|
||||
ch_mult: [1, 2, 4, 4]
|
||||
num_res_blocks: 2
|
||||
attn_resolutions: []
|
||||
dropout: 0.0
|
||||
lossconfig:
|
||||
target: torch.nn.Identity
|
||||
@@ -1,70 +0,0 @@
|
||||
model:
|
||||
base_learning_rate: 1.0e-04
|
||||
target: ldm.models.diffusion.ddpm.LatentDiffusion
|
||||
params:
|
||||
linear_start: 0.00085
|
||||
linear_end: 0.0120
|
||||
num_timesteps_cond: 1
|
||||
log_every_t: 200
|
||||
timesteps: 1000
|
||||
first_stage_key: "jpg"
|
||||
cond_stage_key: "txt"
|
||||
image_size: 64
|
||||
channels: 4
|
||||
cond_stage_trainable: false # Note: different from the one we trained before
|
||||
conditioning_key: crossattn
|
||||
monitor: val/loss_simple_ema
|
||||
scale_factor: 0.18215
|
||||
use_ema: False
|
||||
|
||||
scheduler_config: # 10000 warmup steps
|
||||
target: ldm.lr_scheduler.LambdaLinearScheduler
|
||||
params:
|
||||
warm_up_steps: [ 10000 ]
|
||||
cycle_lengths: [ 10000000000000 ] # incredibly large number to prevent corner cases
|
||||
f_start: [ 1.e-6 ]
|
||||
f_max: [ 1. ]
|
||||
f_min: [ 1. ]
|
||||
|
||||
unet_config:
|
||||
target: ldm.modules.diffusionmodules.openaimodel.UNetModel
|
||||
params:
|
||||
image_size: 32 # unused
|
||||
in_channels: 4
|
||||
out_channels: 4
|
||||
model_channels: 320
|
||||
attention_resolutions: [ 4, 2, 1 ]
|
||||
num_res_blocks: 2
|
||||
channel_mult: [ 1, 2, 4, 4 ]
|
||||
num_heads: 8
|
||||
use_spatial_transformer: True
|
||||
transformer_depth: 1
|
||||
context_dim: 768
|
||||
use_checkpoint: True
|
||||
legacy: False
|
||||
|
||||
first_stage_config:
|
||||
target: ldm.models.autoencoder.AutoencoderKL
|
||||
params:
|
||||
embed_dim: 4
|
||||
monitor: val/rec_loss
|
||||
ddconfig:
|
||||
double_z: true
|
||||
z_channels: 4
|
||||
resolution: 256
|
||||
in_channels: 3
|
||||
out_ch: 3
|
||||
ch: 128
|
||||
ch_mult:
|
||||
- 1
|
||||
- 2
|
||||
- 4
|
||||
- 4
|
||||
num_res_blocks: 2
|
||||
attn_resolutions: []
|
||||
dropout: 0.0
|
||||
lossconfig:
|
||||
target: torch.nn.Identity
|
||||
|
||||
cond_stage_config:
|
||||
target: ldm.modules.encoders.modules.FrozenCLIPEmbedder
|
||||
@@ -1,70 +0,0 @@
|
||||
model:
|
||||
base_learning_rate: 7.5e-05
|
||||
target: ldm.models.diffusion.ddpm.LatentInpaintDiffusion
|
||||
params:
|
||||
linear_start: 0.00085
|
||||
linear_end: 0.0120
|
||||
num_timesteps_cond: 1
|
||||
log_every_t: 200
|
||||
timesteps: 1000
|
||||
first_stage_key: "jpg"
|
||||
cond_stage_key: "txt"
|
||||
image_size: 64
|
||||
channels: 4
|
||||
cond_stage_trainable: false # Note: different from the one we trained before
|
||||
conditioning_key: hybrid # important
|
||||
monitor: val/loss_simple_ema
|
||||
scale_factor: 0.18215
|
||||
finetune_keys: null
|
||||
|
||||
scheduler_config: # 10000 warmup steps
|
||||
target: ldm.lr_scheduler.LambdaLinearScheduler
|
||||
params:
|
||||
warm_up_steps: [ 2500 ] # NOTE for resuming. use 10000 if starting from scratch
|
||||
cycle_lengths: [ 10000000000000 ] # incredibly large number to prevent corner cases
|
||||
f_start: [ 1.e-6 ]
|
||||
f_max: [ 1. ]
|
||||
f_min: [ 1. ]
|
||||
|
||||
unet_config:
|
||||
target: ldm.modules.diffusionmodules.openaimodel.UNetModel
|
||||
params:
|
||||
image_size: 32 # unused
|
||||
in_channels: 9 # 4 data + 4 downscaled image + 1 mask
|
||||
out_channels: 4
|
||||
model_channels: 320
|
||||
attention_resolutions: [ 4, 2, 1 ]
|
||||
num_res_blocks: 2
|
||||
channel_mult: [ 1, 2, 4, 4 ]
|
||||
num_heads: 8
|
||||
use_spatial_transformer: True
|
||||
transformer_depth: 1
|
||||
context_dim: 768
|
||||
use_checkpoint: True
|
||||
legacy: False
|
||||
|
||||
first_stage_config:
|
||||
target: ldm.models.autoencoder.AutoencoderKL
|
||||
params:
|
||||
embed_dim: 4
|
||||
monitor: val/rec_loss
|
||||
ddconfig:
|
||||
double_z: true
|
||||
z_channels: 4
|
||||
resolution: 256
|
||||
in_channels: 3
|
||||
out_ch: 3
|
||||
ch: 128
|
||||
ch_mult:
|
||||
- 1
|
||||
- 2
|
||||
- 4
|
||||
- 4
|
||||
num_res_blocks: 2
|
||||
attn_resolutions: []
|
||||
dropout: 0.0
|
||||
lossconfig:
|
||||
target: torch.nn.Identity
|
||||
|
||||
cond_stage_config:
|
||||
target: ldm.modules.encoders.modules.FrozenCLIPEmbedder
|
||||
@@ -1,80 +0,0 @@
|
||||
model:
|
||||
base_learning_rate: 1.0e-04
|
||||
target: ldm.models.diffusion.ddpm.ImageEmbeddingConditionedLatentDiffusion
|
||||
params:
|
||||
embedding_dropout: 0.25
|
||||
parameterization: "v"
|
||||
linear_start: 0.00085
|
||||
linear_end: 0.0120
|
||||
log_every_t: 200
|
||||
timesteps: 1000
|
||||
first_stage_key: "jpg"
|
||||
cond_stage_key: "txt"
|
||||
image_size: 96
|
||||
channels: 4
|
||||
cond_stage_trainable: false
|
||||
conditioning_key: crossattn-adm
|
||||
scale_factor: 0.18215
|
||||
monitor: val/loss_simple_ema
|
||||
use_ema: False
|
||||
|
||||
embedder_config:
|
||||
target: ldm.modules.encoders.modules.FrozenOpenCLIPImageEmbedder
|
||||
|
||||
noise_aug_config:
|
||||
target: ldm.modules.encoders.modules.CLIPEmbeddingNoiseAugmentation
|
||||
params:
|
||||
timestep_dim: 1024
|
||||
noise_schedule_config:
|
||||
timesteps: 1000
|
||||
beta_schedule: squaredcos_cap_v2
|
||||
|
||||
unet_config:
|
||||
target: ldm.modules.diffusionmodules.openaimodel.UNetModel
|
||||
params:
|
||||
num_classes: "sequential"
|
||||
adm_in_channels: 2048
|
||||
use_checkpoint: True
|
||||
image_size: 32 # unused
|
||||
in_channels: 4
|
||||
out_channels: 4
|
||||
model_channels: 320
|
||||
attention_resolutions: [ 4, 2, 1 ]
|
||||
num_res_blocks: 2
|
||||
channel_mult: [ 1, 2, 4, 4 ]
|
||||
num_head_channels: 64 # need to fix for flash-attn
|
||||
use_spatial_transformer: True
|
||||
use_linear_in_transformer: True
|
||||
transformer_depth: 1
|
||||
context_dim: 1024
|
||||
legacy: False
|
||||
|
||||
first_stage_config:
|
||||
target: ldm.models.autoencoder.AutoencoderKL
|
||||
params:
|
||||
embed_dim: 4
|
||||
monitor: val/rec_loss
|
||||
ddconfig:
|
||||
attn_type: "vanilla-xformers"
|
||||
double_z: true
|
||||
z_channels: 4
|
||||
resolution: 256
|
||||
in_channels: 3
|
||||
out_ch: 3
|
||||
ch: 128
|
||||
ch_mult:
|
||||
- 1
|
||||
- 2
|
||||
- 4
|
||||
- 4
|
||||
num_res_blocks: 2
|
||||
attn_resolutions: [ ]
|
||||
dropout: 0.0
|
||||
lossconfig:
|
||||
target: torch.nn.Identity
|
||||
|
||||
cond_stage_config:
|
||||
target: ldm.modules.encoders.modules.FrozenOpenCLIPEmbedder
|
||||
params:
|
||||
freeze: True
|
||||
layer: "penultimate"
|
||||
@@ -1,83 +0,0 @@
|
||||
model:
|
||||
base_learning_rate: 1.0e-04
|
||||
target: ldm.models.diffusion.ddpm.ImageEmbeddingConditionedLatentDiffusion
|
||||
params:
|
||||
embedding_dropout: 0.25
|
||||
parameterization: "v"
|
||||
linear_start: 0.00085
|
||||
linear_end: 0.0120
|
||||
log_every_t: 200
|
||||
timesteps: 1000
|
||||
first_stage_key: "jpg"
|
||||
cond_stage_key: "txt"
|
||||
image_size: 96
|
||||
channels: 4
|
||||
cond_stage_trainable: false
|
||||
conditioning_key: crossattn-adm
|
||||
scale_factor: 0.18215
|
||||
monitor: val/loss_simple_ema
|
||||
use_ema: False
|
||||
|
||||
embedder_config:
|
||||
target: ldm.modules.encoders.modules.ClipImageEmbedder
|
||||
params:
|
||||
model: "ViT-L/14"
|
||||
|
||||
noise_aug_config:
|
||||
target: ldm.modules.encoders.modules.CLIPEmbeddingNoiseAugmentation
|
||||
params:
|
||||
clip_stats_path: "checkpoints/karlo_models/ViT-L-14_stats.th"
|
||||
timestep_dim: 768
|
||||
noise_schedule_config:
|
||||
timesteps: 1000
|
||||
beta_schedule: squaredcos_cap_v2
|
||||
|
||||
unet_config:
|
||||
target: ldm.modules.diffusionmodules.openaimodel.UNetModel
|
||||
params:
|
||||
num_classes: "sequential"
|
||||
adm_in_channels: 1536
|
||||
use_checkpoint: True
|
||||
image_size: 32 # unused
|
||||
in_channels: 4
|
||||
out_channels: 4
|
||||
model_channels: 320
|
||||
attention_resolutions: [ 4, 2, 1 ]
|
||||
num_res_blocks: 2
|
||||
channel_mult: [ 1, 2, 4, 4 ]
|
||||
num_head_channels: 64 # need to fix for flash-attn
|
||||
use_spatial_transformer: True
|
||||
use_linear_in_transformer: True
|
||||
transformer_depth: 1
|
||||
context_dim: 1024
|
||||
legacy: False
|
||||
|
||||
first_stage_config:
|
||||
target: ldm.models.autoencoder.AutoencoderKL
|
||||
params:
|
||||
embed_dim: 4
|
||||
monitor: val/rec_loss
|
||||
ddconfig:
|
||||
attn_type: "vanilla-xformers"
|
||||
double_z: true
|
||||
z_channels: 4
|
||||
resolution: 256
|
||||
in_channels: 3
|
||||
out_ch: 3
|
||||
ch: 128
|
||||
ch_mult:
|
||||
- 1
|
||||
- 2
|
||||
- 4
|
||||
- 4
|
||||
num_res_blocks: 2
|
||||
attn_resolutions: [ ]
|
||||
dropout: 0.0
|
||||
lossconfig:
|
||||
target: torch.nn.Identity
|
||||
|
||||
cond_stage_config:
|
||||
target: ldm.modules.encoders.modules.FrozenOpenCLIPEmbedder
|
||||
params:
|
||||
freeze: True
|
||||
layer: "penultimate"
|
||||
@@ -1,67 +0,0 @@
|
||||
model:
|
||||
base_learning_rate: 1.0e-4
|
||||
target: ldm.models.diffusion.ddpm.LatentDiffusion
|
||||
params:
|
||||
linear_start: 0.00085
|
||||
linear_end: 0.0120
|
||||
num_timesteps_cond: 1
|
||||
log_every_t: 200
|
||||
timesteps: 1000
|
||||
first_stage_key: "jpg"
|
||||
cond_stage_key: "txt"
|
||||
image_size: 64
|
||||
channels: 4
|
||||
cond_stage_trainable: false
|
||||
conditioning_key: crossattn
|
||||
monitor: val/loss_simple_ema
|
||||
scale_factor: 0.18215
|
||||
use_ema: False # we set this to false because this is an inference only config
|
||||
|
||||
unet_config:
|
||||
target: ldm.modules.diffusionmodules.openaimodel.UNetModel
|
||||
params:
|
||||
use_checkpoint: True
|
||||
use_fp16: True
|
||||
image_size: 32 # unused
|
||||
in_channels: 4
|
||||
out_channels: 4
|
||||
model_channels: 320
|
||||
attention_resolutions: [ 4, 2, 1 ]
|
||||
num_res_blocks: 2
|
||||
channel_mult: [ 1, 2, 4, 4 ]
|
||||
num_head_channels: 64 # need to fix for flash-attn
|
||||
use_spatial_transformer: True
|
||||
use_linear_in_transformer: True
|
||||
transformer_depth: 1
|
||||
context_dim: 1024
|
||||
legacy: False
|
||||
|
||||
first_stage_config:
|
||||
target: ldm.models.autoencoder.AutoencoderKL
|
||||
params:
|
||||
embed_dim: 4
|
||||
monitor: val/rec_loss
|
||||
ddconfig:
|
||||
#attn_type: "vanilla-xformers"
|
||||
double_z: true
|
||||
z_channels: 4
|
||||
resolution: 256
|
||||
in_channels: 3
|
||||
out_ch: 3
|
||||
ch: 128
|
||||
ch_mult:
|
||||
- 1
|
||||
- 2
|
||||
- 4
|
||||
- 4
|
||||
num_res_blocks: 2
|
||||
attn_resolutions: []
|
||||
dropout: 0.0
|
||||
lossconfig:
|
||||
target: torch.nn.Identity
|
||||
|
||||
cond_stage_config:
|
||||
target: ldm.modules.encoders.modules.FrozenOpenCLIPEmbedder
|
||||
params:
|
||||
freeze: True
|
||||
layer: "penultimate"
|
||||
@@ -1,68 +0,0 @@
|
||||
model:
|
||||
base_learning_rate: 1.0e-4
|
||||
target: ldm.models.diffusion.ddpm.LatentDiffusion
|
||||
params:
|
||||
parameterization: "v"
|
||||
linear_start: 0.00085
|
||||
linear_end: 0.0120
|
||||
num_timesteps_cond: 1
|
||||
log_every_t: 200
|
||||
timesteps: 1000
|
||||
first_stage_key: "jpg"
|
||||
cond_stage_key: "txt"
|
||||
image_size: 64
|
||||
channels: 4
|
||||
cond_stage_trainable: false
|
||||
conditioning_key: crossattn
|
||||
monitor: val/loss_simple_ema
|
||||
scale_factor: 0.18215
|
||||
use_ema: False # we set this to false because this is an inference only config
|
||||
|
||||
unet_config:
|
||||
target: ldm.modules.diffusionmodules.openaimodel.UNetModel
|
||||
params:
|
||||
use_checkpoint: True
|
||||
use_fp16: True
|
||||
image_size: 32 # unused
|
||||
in_channels: 4
|
||||
out_channels: 4
|
||||
model_channels: 320
|
||||
attention_resolutions: [ 4, 2, 1 ]
|
||||
num_res_blocks: 2
|
||||
channel_mult: [ 1, 2, 4, 4 ]
|
||||
num_head_channels: 64 # need to fix for flash-attn
|
||||
use_spatial_transformer: True
|
||||
use_linear_in_transformer: True
|
||||
transformer_depth: 1
|
||||
context_dim: 1024
|
||||
legacy: False
|
||||
|
||||
first_stage_config:
|
||||
target: ldm.models.autoencoder.AutoencoderKL
|
||||
params:
|
||||
embed_dim: 4
|
||||
monitor: val/rec_loss
|
||||
ddconfig:
|
||||
#attn_type: "vanilla-xformers"
|
||||
double_z: true
|
||||
z_channels: 4
|
||||
resolution: 256
|
||||
in_channels: 3
|
||||
out_ch: 3
|
||||
ch: 128
|
||||
ch_mult:
|
||||
- 1
|
||||
- 2
|
||||
- 4
|
||||
- 4
|
||||
num_res_blocks: 2
|
||||
attn_resolutions: []
|
||||
dropout: 0.0
|
||||
lossconfig:
|
||||
target: torch.nn.Identity
|
||||
|
||||
cond_stage_config:
|
||||
target: ldm.modules.encoders.modules.FrozenOpenCLIPEmbedder
|
||||
params:
|
||||
freeze: True
|
||||
layer: "penultimate"
|
||||
@@ -1,158 +0,0 @@
|
||||
model:
|
||||
base_learning_rate: 5.0e-05
|
||||
target: ldm.models.diffusion.ddpm.LatentInpaintDiffusion
|
||||
params:
|
||||
linear_start: 0.00085
|
||||
linear_end: 0.0120
|
||||
num_timesteps_cond: 1
|
||||
log_every_t: 200
|
||||
timesteps: 1000
|
||||
first_stage_key: "jpg"
|
||||
cond_stage_key: "txt"
|
||||
image_size: 64
|
||||
channels: 4
|
||||
cond_stage_trainable: false
|
||||
conditioning_key: hybrid
|
||||
scale_factor: 0.18215
|
||||
monitor: val/loss_simple_ema
|
||||
finetune_keys: null
|
||||
use_ema: False
|
||||
|
||||
unet_config:
|
||||
target: ldm.modules.diffusionmodules.openaimodel.UNetModel
|
||||
params:
|
||||
use_checkpoint: True
|
||||
image_size: 32 # unused
|
||||
in_channels: 9
|
||||
out_channels: 4
|
||||
model_channels: 320
|
||||
attention_resolutions: [ 4, 2, 1 ]
|
||||
num_res_blocks: 2
|
||||
channel_mult: [ 1, 2, 4, 4 ]
|
||||
num_head_channels: 64 # need to fix for flash-attn
|
||||
use_spatial_transformer: True
|
||||
use_linear_in_transformer: True
|
||||
transformer_depth: 1
|
||||
context_dim: 1024
|
||||
legacy: False
|
||||
|
||||
first_stage_config:
|
||||
target: ldm.models.autoencoder.AutoencoderKL
|
||||
params:
|
||||
embed_dim: 4
|
||||
monitor: val/rec_loss
|
||||
ddconfig:
|
||||
#attn_type: "vanilla-xformers"
|
||||
double_z: true
|
||||
z_channels: 4
|
||||
resolution: 256
|
||||
in_channels: 3
|
||||
out_ch: 3
|
||||
ch: 128
|
||||
ch_mult:
|
||||
- 1
|
||||
- 2
|
||||
- 4
|
||||
- 4
|
||||
num_res_blocks: 2
|
||||
attn_resolutions: [ ]
|
||||
dropout: 0.0
|
||||
lossconfig:
|
||||
target: torch.nn.Identity
|
||||
|
||||
cond_stage_config:
|
||||
target: ldm.modules.encoders.modules.FrozenOpenCLIPEmbedder
|
||||
params:
|
||||
freeze: True
|
||||
layer: "penultimate"
|
||||
|
||||
|
||||
data:
|
||||
target: ldm.data.laion.WebDataModuleFromConfig
|
||||
params:
|
||||
tar_base: null # for concat as in LAION-A
|
||||
p_unsafe_threshold: 0.1
|
||||
filter_word_list: "data/filters.yaml"
|
||||
max_pwatermark: 0.45
|
||||
batch_size: 8
|
||||
num_workers: 6
|
||||
multinode: True
|
||||
min_size: 512
|
||||
train:
|
||||
shards:
|
||||
- "pipe:aws s3 cp s3://stability-aws/laion-a-native/part-0/{00000..18699}.tar -"
|
||||
- "pipe:aws s3 cp s3://stability-aws/laion-a-native/part-1/{00000..18699}.tar -"
|
||||
- "pipe:aws s3 cp s3://stability-aws/laion-a-native/part-2/{00000..18699}.tar -"
|
||||
- "pipe:aws s3 cp s3://stability-aws/laion-a-native/part-3/{00000..18699}.tar -"
|
||||
- "pipe:aws s3 cp s3://stability-aws/laion-a-native/part-4/{00000..18699}.tar -" #{00000-94333}.tar"
|
||||
shuffle: 10000
|
||||
image_key: jpg
|
||||
image_transforms:
|
||||
- target: torchvision.transforms.Resize
|
||||
params:
|
||||
size: 512
|
||||
interpolation: 3
|
||||
- target: torchvision.transforms.RandomCrop
|
||||
params:
|
||||
size: 512
|
||||
postprocess:
|
||||
target: ldm.data.laion.AddMask
|
||||
params:
|
||||
mode: "512train-large"
|
||||
p_drop: 0.25
|
||||
# NOTE use enough shards to avoid empty validation loops in workers
|
||||
validation:
|
||||
shards:
|
||||
- "pipe:aws s3 cp s3://deep-floyd-s3/datasets/laion_cleaned-part5/{93001..94333}.tar - "
|
||||
shuffle: 0
|
||||
image_key: jpg
|
||||
image_transforms:
|
||||
- target: torchvision.transforms.Resize
|
||||
params:
|
||||
size: 512
|
||||
interpolation: 3
|
||||
- target: torchvision.transforms.CenterCrop
|
||||
params:
|
||||
size: 512
|
||||
postprocess:
|
||||
target: ldm.data.laion.AddMask
|
||||
params:
|
||||
mode: "512train-large"
|
||||
p_drop: 0.25
|
||||
|
||||
lightning:
|
||||
find_unused_parameters: True
|
||||
modelcheckpoint:
|
||||
params:
|
||||
every_n_train_steps: 5000
|
||||
|
||||
callbacks:
|
||||
metrics_over_trainsteps_checkpoint:
|
||||
params:
|
||||
every_n_train_steps: 10000
|
||||
|
||||
image_logger:
|
||||
target: main.ImageLogger
|
||||
params:
|
||||
enable_autocast: False
|
||||
disabled: False
|
||||
batch_frequency: 1000
|
||||
max_images: 4
|
||||
increase_log_steps: False
|
||||
log_first_step: False
|
||||
log_images_kwargs:
|
||||
use_ema_scope: False
|
||||
inpaint: False
|
||||
plot_progressive_rows: False
|
||||
plot_diffusion_rows: False
|
||||
N: 4
|
||||
unconditional_guidance_scale: 5.0
|
||||
unconditional_guidance_label: [""]
|
||||
ddim_steps: 50 # todo check these out for depth2img,
|
||||
ddim_eta: 0.0 # todo check these out for depth2img,
|
||||
|
||||
trainer:
|
||||
benchmark: True
|
||||
val_check_interval: 5000000
|
||||
num_sanity_val_steps: 0
|
||||
accumulate_grad_batches: 1
|
||||
@@ -1,74 +0,0 @@
|
||||
model:
|
||||
base_learning_rate: 5.0e-07
|
||||
target: ldm.models.diffusion.ddpm.LatentDepth2ImageDiffusion
|
||||
params:
|
||||
linear_start: 0.00085
|
||||
linear_end: 0.0120
|
||||
num_timesteps_cond: 1
|
||||
log_every_t: 200
|
||||
timesteps: 1000
|
||||
first_stage_key: "jpg"
|
||||
cond_stage_key: "txt"
|
||||
image_size: 64
|
||||
channels: 4
|
||||
cond_stage_trainable: false
|
||||
conditioning_key: hybrid
|
||||
scale_factor: 0.18215
|
||||
monitor: val/loss_simple_ema
|
||||
finetune_keys: null
|
||||
use_ema: False
|
||||
|
||||
depth_stage_config:
|
||||
target: ldm.modules.midas.api.MiDaSInference
|
||||
params:
|
||||
model_type: "dpt_hybrid"
|
||||
|
||||
unet_config:
|
||||
target: ldm.modules.diffusionmodules.openaimodel.UNetModel
|
||||
params:
|
||||
use_checkpoint: True
|
||||
image_size: 32 # unused
|
||||
in_channels: 5
|
||||
out_channels: 4
|
||||
model_channels: 320
|
||||
attention_resolutions: [ 4, 2, 1 ]
|
||||
num_res_blocks: 2
|
||||
channel_mult: [ 1, 2, 4, 4 ]
|
||||
num_head_channels: 64 # need to fix for flash-attn
|
||||
use_spatial_transformer: True
|
||||
use_linear_in_transformer: True
|
||||
transformer_depth: 1
|
||||
context_dim: 1024
|
||||
legacy: False
|
||||
|
||||
first_stage_config:
|
||||
target: ldm.models.autoencoder.AutoencoderKL
|
||||
params:
|
||||
embed_dim: 4
|
||||
monitor: val/rec_loss
|
||||
ddconfig:
|
||||
#attn_type: "vanilla-xformers"
|
||||
double_z: true
|
||||
z_channels: 4
|
||||
resolution: 256
|
||||
in_channels: 3
|
||||
out_ch: 3
|
||||
ch: 128
|
||||
ch_mult:
|
||||
- 1
|
||||
- 2
|
||||
- 4
|
||||
- 4
|
||||
num_res_blocks: 2
|
||||
attn_resolutions: [ ]
|
||||
dropout: 0.0
|
||||
lossconfig:
|
||||
target: torch.nn.Identity
|
||||
|
||||
cond_stage_config:
|
||||
target: ldm.modules.encoders.modules.FrozenOpenCLIPEmbedder
|
||||
params:
|
||||
freeze: True
|
||||
layer: "penultimate"
|
||||
|
||||
|
||||
@@ -1,152 +0,0 @@
|
||||
import re
|
||||
import time
|
||||
import numpy as np
|
||||
import networks
|
||||
import lora_patches
|
||||
from modules import extra_networks, shared
|
||||
|
||||
|
||||
# from https://github.com/cheald/sd-webui-loractl/blob/master/loractl/lib/utils.py
|
||||
def get_stepwise(param, step, steps):
|
||||
def sorted_positions(raw_steps):
|
||||
steps = [[float(s.strip()) for s in re.split("[@~]", x)]
|
||||
for x in re.split("[,;]", str(raw_steps))]
|
||||
if len(steps[0]) == 1: # If we just got a single number, just return it
|
||||
return steps[0][0]
|
||||
steps = [[s[0], s[1] if len(s) == 2 else 1] for s in steps] # Add implicit 1s to any steps which don't have a weight
|
||||
steps.sort(key=lambda k: k[1]) # Sort by index
|
||||
steps = [list(v) for v in zip(*steps)]
|
||||
return steps
|
||||
|
||||
def calculate_weight(m, step, max_steps, step_offset=2):
|
||||
if isinstance(m, list):
|
||||
if m[1][-1] <= 1.0:
|
||||
step = (step) / (max_steps - step_offset) if max_steps > 0 else 1.0
|
||||
v = np.interp(step, m[1], m[0])
|
||||
return v
|
||||
else:
|
||||
return m
|
||||
|
||||
stepwise = calculate_weight(sorted_positions(param), step, steps)
|
||||
return stepwise
|
||||
|
||||
|
||||
class ExtraNetworkLora(extra_networks.ExtraNetwork):
|
||||
|
||||
def __init__(self):
|
||||
super().__init__('lora')
|
||||
self.active = False
|
||||
self.model = None
|
||||
self.errors = {}
|
||||
networks.originals = lora_patches.LoraPatches()
|
||||
|
||||
def prompt(self, p):
|
||||
if shared.opts.lora_apply_tags == 0:
|
||||
return
|
||||
all_tags = []
|
||||
for loaded in networks.loaded_networks:
|
||||
page = [en for en in shared.extra_networks if en.name == 'lora'][0]
|
||||
item = page.create_item(loaded.name)
|
||||
tags = (item or {}).get("tags", {})
|
||||
loaded.tags = list(tags)
|
||||
if len(loaded.tags) == 0:
|
||||
loaded.tags.append(loaded.name)
|
||||
if shared.opts.lora_apply_tags > 0:
|
||||
loaded.tags = loaded.tags[:shared.opts.lora_apply_tags]
|
||||
all_tags.extend(loaded.tags)
|
||||
if len(all_tags) > 0:
|
||||
shared.log.debug(f"Network load: type=LoRA tags={all_tags} max={shared.opts.lora_apply_tags} apply")
|
||||
all_tags = ', '.join(all_tags)
|
||||
p.extra_generation_params["LoRA tags"] = all_tags
|
||||
if '_tags_' in p.prompt:
|
||||
p.prompt = p.prompt.replace('_tags_', all_tags)
|
||||
else:
|
||||
p.prompt = f"{p.prompt}, {all_tags}"
|
||||
if p.all_prompts is not None:
|
||||
for i in range(len(p.all_prompts)):
|
||||
if '_tags_' in p.all_prompts[i]:
|
||||
p.all_prompts[i] = p.all_prompts[i].replace('_tags_', all_tags)
|
||||
else:
|
||||
p.all_prompts[i] = f"{p.all_prompts[i]}, {all_tags}"
|
||||
|
||||
def infotext(self, p):
|
||||
names = [i.name for i in networks.loaded_networks]
|
||||
if len(names) > 0:
|
||||
p.extra_generation_params["LoRA networks"] = ", ".join(names)
|
||||
if shared.opts.lora_add_hashes_to_infotext:
|
||||
network_hashes = []
|
||||
for item in networks.loaded_networks:
|
||||
if not item.network_on_disk.shorthash:
|
||||
continue
|
||||
network_hashes.append(item.network_on_disk.shorthash)
|
||||
if len(network_hashes) > 0:
|
||||
p.extra_generation_params["LoRA hashes"] = ", ".join(network_hashes)
|
||||
|
||||
def parse(self, p, params_list, step=0):
|
||||
names = []
|
||||
te_multipliers = []
|
||||
unet_multipliers = []
|
||||
dyn_dims = []
|
||||
for params in params_list:
|
||||
assert params.items
|
||||
names.append(params.positional[0])
|
||||
te_multiplier = params.named.get("te", params.positional[1] if len(params.positional) > 1 else shared.opts.extra_networks_default_multiplier)
|
||||
if isinstance(te_multiplier, str) and "@" in te_multiplier:
|
||||
te_multiplier = get_stepwise(te_multiplier, step, p.steps)
|
||||
else:
|
||||
te_multiplier = float(te_multiplier)
|
||||
unet_multiplier = [params.positional[2] if len(params.positional) > 2 else te_multiplier] * 3
|
||||
unet_multiplier = [params.named.get("unet", unet_multiplier[0])] * 3
|
||||
unet_multiplier[0] = params.named.get("in", unet_multiplier[0])
|
||||
unet_multiplier[1] = params.named.get("mid", unet_multiplier[1])
|
||||
unet_multiplier[2] = params.named.get("out", unet_multiplier[2])
|
||||
for i in range(len(unet_multiplier)):
|
||||
if isinstance(unet_multiplier[i], str) and "@" in unet_multiplier[i]:
|
||||
unet_multiplier[i] = get_stepwise(unet_multiplier[i], step, p.steps)
|
||||
else:
|
||||
unet_multiplier[i] = float(unet_multiplier[i])
|
||||
dyn_dim = int(params.positional[3]) if len(params.positional) > 3 else None
|
||||
dyn_dim = int(params.named["dyn"]) if "dyn" in params.named else dyn_dim
|
||||
te_multipliers.append(te_multiplier)
|
||||
unet_multipliers.append(unet_multiplier)
|
||||
dyn_dims.append(dyn_dim)
|
||||
return names, te_multipliers, unet_multipliers, dyn_dims
|
||||
|
||||
def activate(self, p, params_list, step=0):
|
||||
t0 = time.time()
|
||||
self.errors.clear()
|
||||
if self.active:
|
||||
if self.model != shared.opts.sd_model_checkpoint: # reset if model changed
|
||||
self.active = False
|
||||
if len(params_list) > 0 and not self.active: # activate patches once
|
||||
shared.log.debug(f'Activate network: type=LoRA model="{shared.opts.sd_model_checkpoint}"')
|
||||
networks.originals.apply() # apply patches
|
||||
self.active = True
|
||||
self.model = shared.opts.sd_model_checkpoint
|
||||
names, te_multipliers, unet_multipliers, dyn_dims = self.parse(p, params_list, step)
|
||||
networks.load_networks(names, te_multipliers, unet_multipliers, dyn_dims)
|
||||
t1 = time.time()
|
||||
if len(networks.loaded_networks) > 0 and step == 0:
|
||||
self.infotext(p)
|
||||
self.prompt(p)
|
||||
shared.log.info(f'Network load: type=LoRA apply={[n.name for n in networks.loaded_networks]} method=legacy te={te_multipliers} unet={unet_multipliers} dims={dyn_dims} load={t1-t0:.2f}')
|
||||
|
||||
def deactivate(self, p):
|
||||
t0 = time.time()
|
||||
if shared.native and len(networks.diffuser_loaded) > 0:
|
||||
if hasattr(shared.sd_model, "unload_lora_weights") and hasattr(shared.sd_model, "text_encoder"):
|
||||
if not (shared.compiled_model_state is not None and shared.compiled_model_state.is_compiled is True):
|
||||
try:
|
||||
if shared.opts.lora_fuse_diffusers:
|
||||
shared.sd_model.unfuse_lora()
|
||||
shared.sd_model.unload_lora_weights() # fails for non-CLIP models
|
||||
except Exception:
|
||||
pass
|
||||
t1 = time.time()
|
||||
networks.timer['restore'] += t1 - t0
|
||||
if self.active and networks.debug:
|
||||
shared.log.debug(f"Network end: type=LoRA load={networks.timer['load']:.2f} apply={networks.timer['apply']:.2f} restore={networks.timer['restore']:.2f}")
|
||||
if self.errors:
|
||||
for k, v in self.errors.items():
|
||||
shared.log.error(f'LoRA: name="{k}" errors={v}')
|
||||
self.errors.clear()
|
||||
@@ -1,8 +0,0 @@
|
||||
import networks
|
||||
|
||||
list_available_loras = networks.list_available_networks
|
||||
available_loras = networks.available_networks
|
||||
available_lora_aliases = networks.available_network_aliases
|
||||
available_lora_hash_lookup = networks.available_network_hash_lookup
|
||||
forbidden_lora_aliases = networks.forbidden_network_aliases
|
||||
loaded_loras = networks.loaded_networks
|
||||
@@ -1,457 +0,0 @@
|
||||
import os
|
||||
import re
|
||||
import bisect
|
||||
from typing import Dict
|
||||
import torch
|
||||
from modules import shared
|
||||
|
||||
|
||||
debug = os.environ.get('SD_LORA_DEBUG', None) is not None
|
||||
suffix_conversion = {
|
||||
"attentions": {},
|
||||
"resnets": {
|
||||
"conv1": "in_layers_2",
|
||||
"conv2": "out_layers_3",
|
||||
"norm1": "in_layers_0",
|
||||
"norm2": "out_layers_0",
|
||||
"time_emb_proj": "emb_layers_1",
|
||||
"conv_shortcut": "skip_connection",
|
||||
}
|
||||
}
|
||||
re_digits = re.compile(r"\d+")
|
||||
re_x_proj = re.compile(r"(.*)_([qkv]_proj)$")
|
||||
re_compiled = {}
|
||||
|
||||
|
||||
def make_unet_conversion_map() -> Dict[str, str]:
|
||||
unet_conversion_map_layer = []
|
||||
|
||||
for i in range(3): # num_blocks is 3 in sdxl
|
||||
# loop over downblocks/upblocks
|
||||
for j in range(2):
|
||||
# loop over resnets/attentions for downblocks
|
||||
hf_down_res_prefix = f"down_blocks.{i}.resnets.{j}."
|
||||
sd_down_res_prefix = f"input_blocks.{3 * i + j + 1}.0."
|
||||
unet_conversion_map_layer.append((sd_down_res_prefix, hf_down_res_prefix))
|
||||
if i < 3:
|
||||
# no attention layers in down_blocks.3
|
||||
hf_down_atn_prefix = f"down_blocks.{i}.attentions.{j}."
|
||||
sd_down_atn_prefix = f"input_blocks.{3 * i + j + 1}.1."
|
||||
unet_conversion_map_layer.append((sd_down_atn_prefix, hf_down_atn_prefix))
|
||||
|
||||
for j in range(3):
|
||||
# loop over resnets/attentions for upblocks
|
||||
hf_up_res_prefix = f"up_blocks.{i}.resnets.{j}."
|
||||
sd_up_res_prefix = f"output_blocks.{3 * i + j}.0."
|
||||
unet_conversion_map_layer.append((sd_up_res_prefix, hf_up_res_prefix))
|
||||
# if i > 0: commentout for sdxl
|
||||
# no attention layers in up_blocks.0
|
||||
hf_up_atn_prefix = f"up_blocks.{i}.attentions.{j}."
|
||||
sd_up_atn_prefix = f"output_blocks.{3 * i + j}.1."
|
||||
unet_conversion_map_layer.append((sd_up_atn_prefix, hf_up_atn_prefix))
|
||||
|
||||
if i < 3:
|
||||
# no downsample in down_blocks.3
|
||||
hf_downsample_prefix = f"down_blocks.{i}.downsamplers.0.conv."
|
||||
sd_downsample_prefix = f"input_blocks.{3 * (i + 1)}.0.op."
|
||||
unet_conversion_map_layer.append((sd_downsample_prefix, hf_downsample_prefix))
|
||||
# no upsample in up_blocks.3
|
||||
hf_upsample_prefix = f"up_blocks.{i}.upsamplers.0."
|
||||
sd_upsample_prefix = f"output_blocks.{3 * i + 2}.{2}." # change for sdxl
|
||||
unet_conversion_map_layer.append((sd_upsample_prefix, hf_upsample_prefix))
|
||||
|
||||
hf_mid_atn_prefix = "mid_block.attentions.0."
|
||||
sd_mid_atn_prefix = "middle_block.1."
|
||||
unet_conversion_map_layer.append((sd_mid_atn_prefix, hf_mid_atn_prefix))
|
||||
|
||||
for j in range(2):
|
||||
hf_mid_res_prefix = f"mid_block.resnets.{j}."
|
||||
sd_mid_res_prefix = f"middle_block.{2 * j}."
|
||||
unet_conversion_map_layer.append((sd_mid_res_prefix, hf_mid_res_prefix))
|
||||
|
||||
unet_conversion_map_resnet = [
|
||||
# (stable-diffusion, HF Diffusers)
|
||||
("in_layers.0.", "norm1."),
|
||||
("in_layers.2.", "conv1."),
|
||||
("out_layers.0.", "norm2."),
|
||||
("out_layers.3.", "conv2."),
|
||||
("emb_layers.1.", "time_emb_proj."),
|
||||
("skip_connection.", "conv_shortcut."),
|
||||
]
|
||||
|
||||
unet_conversion_map = []
|
||||
for sd, hf in unet_conversion_map_layer:
|
||||
if "resnets" in hf:
|
||||
for sd_res, hf_res in unet_conversion_map_resnet:
|
||||
unet_conversion_map.append((sd + sd_res, hf + hf_res))
|
||||
else:
|
||||
unet_conversion_map.append((sd, hf))
|
||||
|
||||
for j in range(2):
|
||||
hf_time_embed_prefix = f"time_embedding.linear_{j + 1}."
|
||||
sd_time_embed_prefix = f"time_embed.{j * 2}."
|
||||
unet_conversion_map.append((sd_time_embed_prefix, hf_time_embed_prefix))
|
||||
|
||||
for j in range(2):
|
||||
hf_label_embed_prefix = f"add_embedding.linear_{j + 1}."
|
||||
sd_label_embed_prefix = f"label_emb.0.{j * 2}."
|
||||
unet_conversion_map.append((sd_label_embed_prefix, hf_label_embed_prefix))
|
||||
|
||||
unet_conversion_map.append(("input_blocks.0.0.", "conv_in."))
|
||||
unet_conversion_map.append(("out.0.", "conv_norm_out."))
|
||||
unet_conversion_map.append(("out.2.", "conv_out."))
|
||||
|
||||
sd_hf_conversion_map = {sd.replace(".", "_")[:-1]: hf.replace(".", "_")[:-1] for sd, hf in unet_conversion_map}
|
||||
return sd_hf_conversion_map
|
||||
|
||||
|
||||
class KeyConvert:
|
||||
def __init__(self):
|
||||
if not shared.native:
|
||||
self.converter = self.original
|
||||
self.is_sd2 = 'model_transformer_resblocks' in shared.sd_model.network_layer_mapping
|
||||
else:
|
||||
self.converter = self.diffusers
|
||||
self.is_sdxl = True if shared.sd_model_type == "sdxl" else False
|
||||
self.UNET_CONVERSION_MAP = make_unet_conversion_map() if self.is_sdxl else None
|
||||
self.LORA_PREFIX_UNET = "lora_unet_"
|
||||
self.LORA_PREFIX_TEXT_ENCODER = "lora_te_"
|
||||
self.OFT_PREFIX_UNET = "oft_unet_"
|
||||
# SDXL: must starts with LORA_PREFIX_TEXT_ENCODER
|
||||
self.LORA_PREFIX_TEXT_ENCODER1 = "lora_te1_"
|
||||
self.LORA_PREFIX_TEXT_ENCODER2 = "lora_te2_"
|
||||
|
||||
def original(self, key):
|
||||
key = convert_diffusers_name_to_compvis(key, self.is_sd2)
|
||||
sd_module = shared.sd_model.network_layer_mapping.get(key, None)
|
||||
if sd_module is None:
|
||||
m = re_x_proj.match(key)
|
||||
if m:
|
||||
sd_module = shared.sd_model.network_layer_mapping.get(m.group(1), None)
|
||||
# SDXL loras seem to already have correct compvis keys, so only need to replace "lora_unet" with "diffusion_model"
|
||||
if sd_module is None and "lora_unet" in key:
|
||||
key = key.replace("lora_unet", "diffusion_model")
|
||||
sd_module = shared.sd_model.network_layer_mapping.get(key, None)
|
||||
elif sd_module is None and "lora_te1_text_model" in key:
|
||||
key = key.replace("lora_te1_text_model", "0_transformer_text_model")
|
||||
sd_module = shared.sd_model.network_layer_mapping.get(key, None)
|
||||
# some SD1 Loras also have correct compvis keys
|
||||
if sd_module is None:
|
||||
key = key.replace("lora_te1_text_model", "transformer_text_model")
|
||||
sd_module = shared.sd_model.network_layer_mapping.get(key, None)
|
||||
|
||||
# SegMoE begin
|
||||
expert_key = key + "_experts_0"
|
||||
expert_module = shared.sd_model.network_layer_mapping.get(expert_key, None)
|
||||
if expert_module is not None:
|
||||
sd_module = expert_module
|
||||
key = expert_key
|
||||
if sd_module is None:
|
||||
key = key.replace("_net_", "_experts_0_net_")
|
||||
sd_module = shared.sd_model.network_layer_mapping.get(key, None)
|
||||
key = key if isinstance(key, list) else [key]
|
||||
sd_module = sd_module if isinstance(sd_module, list) else [sd_module]
|
||||
if "_experts_0" in key[0]:
|
||||
i = expert_module = 1
|
||||
while expert_module is not None:
|
||||
expert_key = key[0].replace("_experts_0", f"_experts_{i}")
|
||||
expert_module = shared.sd_model.network_layer_mapping.get(expert_key, None)
|
||||
if expert_module is not None:
|
||||
key.append(expert_key)
|
||||
sd_module.append(expert_module)
|
||||
i += 1
|
||||
# SegMoE end
|
||||
return key, sd_module
|
||||
|
||||
def diffusers(self, key):
|
||||
if self.is_sdxl:
|
||||
if "diffusion_model" in key: # Fix NTC Slider naming error
|
||||
key = key.replace("diffusion_model", "lora_unet")
|
||||
map_keys = list(self.UNET_CONVERSION_MAP.keys()) # prefix of U-Net modules
|
||||
map_keys.sort()
|
||||
search_key = key.replace(self.LORA_PREFIX_UNET, "").replace(self.OFT_PREFIX_UNET, "").replace(self.LORA_PREFIX_TEXT_ENCODER1, "").replace(self.LORA_PREFIX_TEXT_ENCODER2, "")
|
||||
position = bisect.bisect_right(map_keys, search_key)
|
||||
map_key = map_keys[position - 1]
|
||||
if search_key.startswith(map_key):
|
||||
key = key.replace(map_key, self.UNET_CONVERSION_MAP[map_key]).replace("oft", "lora") # pylint: disable=unsubscriptable-object
|
||||
if "lycoris" in key and "transformer" in key:
|
||||
key = key.replace("lycoris", "lora_transformer")
|
||||
sd_module = shared.sd_model.network_layer_mapping.get(key, None)
|
||||
if sd_module is None:
|
||||
sd_module = shared.sd_model.network_layer_mapping.get(key.replace("guidance", "timestep"), None) # FLUX1 fix
|
||||
# SegMoE begin
|
||||
expert_key = key + "_experts_0"
|
||||
expert_module = shared.sd_model.network_layer_mapping.get(expert_key, None)
|
||||
if expert_module is not None:
|
||||
sd_module = expert_module
|
||||
key = expert_key
|
||||
if sd_module is None:
|
||||
key = key.replace("_net_", "_experts_0_net_")
|
||||
sd_module = shared.sd_model.network_layer_mapping.get(key, None)
|
||||
key = key if isinstance(key, list) else [key]
|
||||
sd_module = sd_module if isinstance(sd_module, list) else [sd_module]
|
||||
if "_experts_0" in key[0]:
|
||||
i = expert_module = 1
|
||||
while expert_module is not None:
|
||||
expert_key = key[0].replace("_experts_0", f"_experts_{i}")
|
||||
expert_module = shared.sd_model.network_layer_mapping.get(expert_key, None)
|
||||
if expert_module is not None:
|
||||
key.append(expert_key)
|
||||
sd_module.append(expert_module)
|
||||
i += 1
|
||||
# SegMoE end
|
||||
if debug and sd_module is None:
|
||||
raise RuntimeError(f"LoRA key not found in network_layer_mapping: key={key} mapping={shared.sd_model.network_layer_mapping.keys()}")
|
||||
return key, sd_module
|
||||
|
||||
def __call__(self, key):
|
||||
return self.converter(key)
|
||||
|
||||
|
||||
def convert_diffusers_name_to_compvis(key, is_sd2):
|
||||
def match(match_list, regex_text):
|
||||
regex = re_compiled.get(regex_text)
|
||||
if regex is None:
|
||||
regex = re.compile(regex_text)
|
||||
re_compiled[regex_text] = regex
|
||||
r = re.match(regex, key)
|
||||
if not r:
|
||||
return False
|
||||
match_list.clear()
|
||||
match_list.extend([int(x) if re.match(re_digits, x) else x for x in r.groups()])
|
||||
return True
|
||||
|
||||
m = []
|
||||
if match(m, r"lora_unet_conv_in(.*)"):
|
||||
return f'diffusion_model_input_blocks_0_0{m[0]}'
|
||||
if match(m, r"lora_unet_conv_out(.*)"):
|
||||
return f'diffusion_model_out_2{m[0]}'
|
||||
if match(m, r"lora_unet_time_embedding_linear_(\d+)(.*)"):
|
||||
return f"diffusion_model_time_embed_{m[0] * 2 - 2}{m[1]}"
|
||||
if match(m, r"lora_unet_down_blocks_(\d+)_(attentions|resnets)_(\d+)_(.+)"):
|
||||
suffix = suffix_conversion.get(m[1], {}).get(m[3], m[3])
|
||||
return f"diffusion_model_input_blocks_{1 + m[0] * 3 + m[2]}_{1 if m[1] == 'attentions' else 0}_{suffix}"
|
||||
if match(m, r"lora_unet_mid_block_(attentions|resnets)_(\d+)_(.+)"):
|
||||
suffix = suffix_conversion.get(m[0], {}).get(m[2], m[2])
|
||||
return f"diffusion_model_middle_block_{1 if m[0] == 'attentions' else m[1] * 2}_{suffix}"
|
||||
if match(m, r"lora_unet_up_blocks_(\d+)_(attentions|resnets)_(\d+)_(.+)"):
|
||||
suffix = suffix_conversion.get(m[1], {}).get(m[3], m[3])
|
||||
return f"diffusion_model_output_blocks_{m[0] * 3 + m[2]}_{1 if m[1] == 'attentions' else 0}_{suffix}"
|
||||
if match(m, r"lora_unet_down_blocks_(\d+)_downsamplers_0_conv"):
|
||||
return f"diffusion_model_input_blocks_{3 + m[0] * 3}_0_op"
|
||||
if match(m, r"lora_unet_up_blocks_(\d+)_upsamplers_0_conv"):
|
||||
return f"diffusion_model_output_blocks_{2 + m[0] * 3}_{2 if m[0]>0 else 1}_conv"
|
||||
if match(m, r"lora_te_text_model_encoder_layers_(\d+)_(.+)"):
|
||||
if is_sd2:
|
||||
if 'mlp_fc1' in m[1]:
|
||||
return f"model_transformer_resblocks_{m[0]}_{m[1].replace('mlp_fc1', 'mlp_c_fc')}"
|
||||
elif 'mlp_fc2' in m[1]:
|
||||
return f"model_transformer_resblocks_{m[0]}_{m[1].replace('mlp_fc2', 'mlp_c_proj')}"
|
||||
else:
|
||||
return f"model_transformer_resblocks_{m[0]}_{m[1].replace('self_attn', 'attn')}"
|
||||
return f"transformer_text_model_encoder_layers_{m[0]}_{m[1]}"
|
||||
if match(m, r"lora_te2_text_model_encoder_layers_(\d+)_(.+)"):
|
||||
if 'mlp_fc1' in m[1]:
|
||||
return f"1_model_transformer_resblocks_{m[0]}_{m[1].replace('mlp_fc1', 'mlp_c_fc')}"
|
||||
elif 'mlp_fc2' in m[1]:
|
||||
return f"1_model_transformer_resblocks_{m[0]}_{m[1].replace('mlp_fc2', 'mlp_c_proj')}"
|
||||
else:
|
||||
return f"1_model_transformer_resblocks_{m[0]}_{m[1].replace('self_attn', 'attn')}"
|
||||
return key
|
||||
|
||||
|
||||
# Taken from https://github.com/huggingface/diffusers/blob/main/src/diffusers/loaders/lora_conversion_utils.py
|
||||
# Modified from 'lora_A' and 'lora_B' to 'lora_down' and 'lora_up'
|
||||
# Added early exit
|
||||
# The utilities under `_convert_kohya_flux_lora_to_diffusers()`
|
||||
# are taken from https://github.com/kohya-ss/sd-scripts/blob/a61cf73a5cb5209c3f4d1a3688dd276a4dfd1ecb/networks/convert_flux_lora.py
|
||||
# All credits go to `kohya-ss`.
|
||||
def _convert_kohya_flux_lora_to_diffusers(state_dict):
|
||||
def _convert_to_ai_toolkit(sds_sd, ait_sd, sds_key, ait_key):
|
||||
if sds_key + ".lora_down.weight" not in sds_sd:
|
||||
return
|
||||
down_weight = sds_sd.pop(sds_key + ".lora_down.weight")
|
||||
|
||||
# scale weight by alpha and dim
|
||||
rank = down_weight.shape[0]
|
||||
alpha = sds_sd.pop(sds_key + ".alpha").item() # alpha is scalar
|
||||
scale = alpha / rank # LoRA is scaled by 'alpha / rank' in forward pass, so we need to scale it back here
|
||||
|
||||
# calculate scale_down and scale_up to keep the same value. if scale is 4, scale_down is 2 and scale_up is 2
|
||||
scale_down = scale
|
||||
scale_up = 1.0
|
||||
while scale_down * 2 < scale_up:
|
||||
scale_down *= 2
|
||||
scale_up /= 2
|
||||
|
||||
ait_sd[ait_key + ".lora_down.weight"] = down_weight * scale_down
|
||||
ait_sd[ait_key + ".lora_up.weight"] = sds_sd.pop(sds_key + ".lora_up.weight") * scale_up
|
||||
|
||||
def _convert_to_ai_toolkit_cat(sds_sd, ait_sd, sds_key, ait_keys, dims=None):
|
||||
if sds_key + ".lora_down.weight" not in sds_sd:
|
||||
return
|
||||
down_weight = sds_sd.pop(sds_key + ".lora_down.weight")
|
||||
up_weight = sds_sd.pop(sds_key + ".lora_up.weight")
|
||||
sd_lora_rank = down_weight.shape[0]
|
||||
|
||||
# scale weight by alpha and dim
|
||||
alpha = sds_sd.pop(sds_key + ".alpha")
|
||||
scale = alpha / sd_lora_rank
|
||||
|
||||
# calculate scale_down and scale_up
|
||||
scale_down = scale
|
||||
scale_up = 1.0
|
||||
while scale_down * 2 < scale_up:
|
||||
scale_down *= 2
|
||||
scale_up /= 2
|
||||
|
||||
down_weight = down_weight * scale_down
|
||||
up_weight = up_weight * scale_up
|
||||
|
||||
# calculate dims if not provided
|
||||
num_splits = len(ait_keys)
|
||||
if dims is None:
|
||||
dims = [up_weight.shape[0] // num_splits] * num_splits
|
||||
else:
|
||||
assert sum(dims) == up_weight.shape[0]
|
||||
|
||||
# check upweight is sparse or not
|
||||
is_sparse = False
|
||||
if sd_lora_rank % num_splits == 0:
|
||||
ait_rank = sd_lora_rank // num_splits
|
||||
is_sparse = True
|
||||
i = 0
|
||||
for j in range(len(dims)):
|
||||
for k in range(len(dims)):
|
||||
if j == k:
|
||||
continue
|
||||
is_sparse = is_sparse and torch.all(
|
||||
up_weight[i : i + dims[j], k * ait_rank : (k + 1) * ait_rank] == 0
|
||||
)
|
||||
i += dims[j]
|
||||
# if is_sparse:
|
||||
# print(f"weight is sparse: {sds_key}")
|
||||
|
||||
# make ai-toolkit weight
|
||||
ait_down_keys = [k + ".lora_down.weight" for k in ait_keys]
|
||||
ait_up_keys = [k + ".lora_up.weight" for k in ait_keys]
|
||||
if not is_sparse:
|
||||
# down_weight is copied to each split
|
||||
ait_sd.update({k: down_weight for k in ait_down_keys})
|
||||
|
||||
# up_weight is split to each split
|
||||
ait_sd.update({k: v for k, v in zip(ait_up_keys, torch.split(up_weight, dims, dim=0))}) # noqa: C416 # pylint: disable=unnecessary-comprehension
|
||||
else:
|
||||
# down_weight is chunked to each split
|
||||
ait_sd.update({k: v for k, v in zip(ait_down_keys, torch.chunk(down_weight, num_splits, dim=0))}) # noqa: C416 # pylint: disable=unnecessary-comprehension
|
||||
|
||||
# up_weight is sparse: only non-zero values are copied to each split
|
||||
i = 0
|
||||
for j in range(len(dims)):
|
||||
ait_sd[ait_up_keys[j]] = up_weight[i : i + dims[j], j * ait_rank : (j + 1) * ait_rank].contiguous()
|
||||
i += dims[j]
|
||||
|
||||
def _convert_sd_scripts_to_ai_toolkit(sds_sd):
|
||||
ait_sd = {}
|
||||
for i in range(19):
|
||||
_convert_to_ai_toolkit(
|
||||
sds_sd,
|
||||
ait_sd,
|
||||
f"lora_unet_double_blocks_{i}_img_attn_proj",
|
||||
f"transformer.transformer_blocks.{i}.attn.to_out.0",
|
||||
)
|
||||
_convert_to_ai_toolkit_cat(
|
||||
sds_sd,
|
||||
ait_sd,
|
||||
f"lora_unet_double_blocks_{i}_img_attn_qkv",
|
||||
[
|
||||
f"transformer.transformer_blocks.{i}.attn.to_q",
|
||||
f"transformer.transformer_blocks.{i}.attn.to_k",
|
||||
f"transformer.transformer_blocks.{i}.attn.to_v",
|
||||
],
|
||||
)
|
||||
_convert_to_ai_toolkit(
|
||||
sds_sd,
|
||||
ait_sd,
|
||||
f"lora_unet_double_blocks_{i}_img_mlp_0",
|
||||
f"transformer.transformer_blocks.{i}.ff.net.0.proj",
|
||||
)
|
||||
_convert_to_ai_toolkit(
|
||||
sds_sd,
|
||||
ait_sd,
|
||||
f"lora_unet_double_blocks_{i}_img_mlp_2",
|
||||
f"transformer.transformer_blocks.{i}.ff.net.2",
|
||||
)
|
||||
_convert_to_ai_toolkit(
|
||||
sds_sd,
|
||||
ait_sd,
|
||||
f"lora_unet_double_blocks_{i}_img_mod_lin",
|
||||
f"transformer.transformer_blocks.{i}.norm1.linear",
|
||||
)
|
||||
_convert_to_ai_toolkit(
|
||||
sds_sd,
|
||||
ait_sd,
|
||||
f"lora_unet_double_blocks_{i}_txt_attn_proj",
|
||||
f"transformer.transformer_blocks.{i}.attn.to_add_out",
|
||||
)
|
||||
_convert_to_ai_toolkit_cat(
|
||||
sds_sd,
|
||||
ait_sd,
|
||||
f"lora_unet_double_blocks_{i}_txt_attn_qkv",
|
||||
[
|
||||
f"transformer.transformer_blocks.{i}.attn.add_q_proj",
|
||||
f"transformer.transformer_blocks.{i}.attn.add_k_proj",
|
||||
f"transformer.transformer_blocks.{i}.attn.add_v_proj",
|
||||
],
|
||||
)
|
||||
_convert_to_ai_toolkit(
|
||||
sds_sd,
|
||||
ait_sd,
|
||||
f"lora_unet_double_blocks_{i}_txt_mlp_0",
|
||||
f"transformer.transformer_blocks.{i}.ff_context.net.0.proj",
|
||||
)
|
||||
_convert_to_ai_toolkit(
|
||||
sds_sd,
|
||||
ait_sd,
|
||||
f"lora_unet_double_blocks_{i}_txt_mlp_2",
|
||||
f"transformer.transformer_blocks.{i}.ff_context.net.2",
|
||||
)
|
||||
_convert_to_ai_toolkit(
|
||||
sds_sd,
|
||||
ait_sd,
|
||||
f"lora_unet_double_blocks_{i}_txt_mod_lin",
|
||||
f"transformer.transformer_blocks.{i}.norm1_context.linear",
|
||||
)
|
||||
|
||||
for i in range(38):
|
||||
_convert_to_ai_toolkit_cat(
|
||||
sds_sd,
|
||||
ait_sd,
|
||||
f"lora_unet_single_blocks_{i}_linear1",
|
||||
[
|
||||
f"transformer.single_transformer_blocks.{i}.attn.to_q",
|
||||
f"transformer.single_transformer_blocks.{i}.attn.to_k",
|
||||
f"transformer.single_transformer_blocks.{i}.attn.to_v",
|
||||
f"transformer.single_transformer_blocks.{i}.proj_mlp",
|
||||
],
|
||||
dims=[3072, 3072, 3072, 12288],
|
||||
)
|
||||
_convert_to_ai_toolkit(
|
||||
sds_sd,
|
||||
ait_sd,
|
||||
f"lora_unet_single_blocks_{i}_linear2",
|
||||
f"transformer.single_transformer_blocks.{i}.proj_out",
|
||||
)
|
||||
_convert_to_ai_toolkit(
|
||||
sds_sd,
|
||||
ait_sd,
|
||||
f"lora_unet_single_blocks_{i}_modulation_lin",
|
||||
f"transformer.single_transformer_blocks.{i}.norm.linear",
|
||||
)
|
||||
|
||||
if len(sds_sd) > 0:
|
||||
return None
|
||||
|
||||
return ait_sd
|
||||
|
||||
return _convert_sd_scripts_to_ai_toolkit(state_dict)
|
||||
@@ -1,261 +0,0 @@
|
||||
import os
|
||||
import time
|
||||
import json
|
||||
import datetime
|
||||
import torch
|
||||
from safetensors.torch import save_file
|
||||
import gradio as gr
|
||||
from rich import progress as p
|
||||
from modules import shared, devices
|
||||
from modules.ui_common import create_refresh_button
|
||||
from modules.call_queue import wrap_gradio_gpu_call
|
||||
|
||||
|
||||
class SVDHandler:
|
||||
def __init__(self, maxrank=0, rank_ratio=1):
|
||||
self.network_name: str = None
|
||||
self.U: torch.Tensor = None
|
||||
self.S: torch.Tensor = None
|
||||
self.Vh: torch.Tensor = None
|
||||
self.maxrank: int = maxrank
|
||||
self.rank_ratio: float = rank_ratio
|
||||
self.rank: int = 0
|
||||
self.out_size: int = None
|
||||
self.in_size: int = None
|
||||
self.kernel_size: tuple[int, int] = None
|
||||
self.conv2d: bool = False
|
||||
|
||||
def decompose(self, weight, backupweight):
|
||||
self.conv2d = len(weight.size()) == 4
|
||||
self.kernel_size = None if not self.conv2d else weight.size()[2:4]
|
||||
self.out_size, self.in_size = weight.size()[0:2]
|
||||
diffweight = weight.clone().to(devices.device)
|
||||
diffweight -= backupweight.to(devices.device)
|
||||
if self.conv2d:
|
||||
if self.conv2d and self.kernel_size != (1, 1):
|
||||
diffweight = diffweight.flatten(start_dim=1)
|
||||
else:
|
||||
diffweight = diffweight.squeeze()
|
||||
self.U, self.S, self.Vh = torch.svd_lowrank(diffweight.to(device=devices.device, dtype=torch.float), self.maxrank, 2)
|
||||
# del diffweight
|
||||
self.U = self.U.to(device=devices.cpu, dtype=torch.bfloat16)
|
||||
self.S = self.S.to(device=devices.cpu, dtype=torch.bfloat16)
|
||||
self.Vh = self.Vh.t().to(device=devices.cpu, dtype=torch.bfloat16) # svd_lowrank outputs a transposed matrix
|
||||
|
||||
def findrank(self):
|
||||
if self.rank_ratio < 1:
|
||||
S_squared = self.S.pow(2)
|
||||
S_fro_sq = float(torch.sum(S_squared))
|
||||
sum_S_squared = torch.cumsum(S_squared, dim=0) / S_fro_sq
|
||||
index = int(torch.searchsorted(sum_S_squared, self.rank_ratio ** 2)) + 1
|
||||
index = max(1, min(index, len(self.S) - 1))
|
||||
self.rank = index
|
||||
if self.maxrank > 0:
|
||||
self.rank = min(self.rank, self.maxrank)
|
||||
else:
|
||||
self.rank = min(self.in_size, self.out_size, self.maxrank)
|
||||
|
||||
def makeweights(self):
|
||||
self.findrank()
|
||||
up = self.U[:, :self.rank] @ torch.diag(self.S[:self.rank])
|
||||
down = self.Vh[:self.rank, :]
|
||||
if self.conv2d and self.kernel_size is not None:
|
||||
up = up.reshape(self.out_size, self.rank, 1, 1)
|
||||
down = down.reshape(self.rank, self.in_size, self.kernel_size[0], self.kernel_size[1]) # pylint: disable=unsubscriptable-object
|
||||
return_dict = {f'{self.network_name}.lora_up.weight': up.contiguous(),
|
||||
f'{self.network_name}.lora_down.weight': down.contiguous(),
|
||||
f'{self.network_name}.alpha': torch.tensor(down.shape[0]),
|
||||
}
|
||||
return return_dict
|
||||
|
||||
|
||||
def loaded_lora():
|
||||
if not shared.sd_loaded:
|
||||
return ""
|
||||
loaded = set()
|
||||
if hasattr(shared.sd_model, 'unet'):
|
||||
for _name, module in shared.sd_model.unet.named_modules():
|
||||
current = getattr(module, "network_current_names", None)
|
||||
if current is not None:
|
||||
current = [item[0] for item in current]
|
||||
loaded.update(current)
|
||||
return list(loaded)
|
||||
|
||||
|
||||
def loaded_lora_str():
|
||||
return ", ".join(loaded_lora())
|
||||
|
||||
|
||||
def make_meta(fn, maxrank, rank_ratio):
|
||||
meta = {
|
||||
"model_spec.sai_model_spec": "1.0.0",
|
||||
"model_spec.title": os.path.splitext(os.path.basename(fn))[0],
|
||||
"model_spec.author": "SD.Next",
|
||||
"model_spec.implementation": "https://github.com/vladmandic/sdnext",
|
||||
"model_spec.date": datetime.datetime.now().astimezone().replace(microsecond=0).isoformat(),
|
||||
"model_spec.base_model": shared.opts.sd_model_checkpoint,
|
||||
"model_spec.dtype": str(devices.dtype),
|
||||
"model_spec.base_lora": json.dumps(loaded_lora()),
|
||||
"model_spec.config": f"maxrank={maxrank} rank_ratio={rank_ratio}",
|
||||
}
|
||||
if shared.sd_model_type == "sdxl":
|
||||
meta["model_spec.architecture"] = "stable-diffusion-xl-v1-base/lora" # sai standard
|
||||
meta["ss_base_model_version"] = "sdxl_base_v1-0" # kohya standard
|
||||
elif shared.sd_model_type == "sd":
|
||||
meta["model_spec.architecture"] = "stable-diffusion-v1/lora"
|
||||
meta["ss_base_model_version"] = "sd_v1"
|
||||
elif shared.sd_model_type == "f1":
|
||||
meta["model_spec.architecture"] = "flux-1-dev/lora"
|
||||
meta["ss_base_model_version"] = "flux1"
|
||||
elif shared.sd_model_type == "chroma":
|
||||
meta["model_spec.architecture"] = "chroma/lora"
|
||||
meta["ss_base_model_version"] = "chroma"
|
||||
elif shared.sd_model_type == "sc":
|
||||
meta["model_spec.architecture"] = "stable-cascade-v1-prior/lora"
|
||||
return meta
|
||||
|
||||
|
||||
def make_lora(fn, maxrank, auto_rank, rank_ratio, modules, overwrite):
|
||||
if not shared.sd_loaded or not shared.native:
|
||||
msg = "LoRA extract: model not loaded"
|
||||
shared.log.warning(msg)
|
||||
yield msg
|
||||
return
|
||||
if loaded_lora() == "":
|
||||
msg = "LoRA extract: no LoRA detected"
|
||||
shared.log.warning(msg)
|
||||
yield msg
|
||||
return
|
||||
if not fn:
|
||||
msg = "LoRA extract: target filename required"
|
||||
shared.log.warning(msg)
|
||||
yield msg
|
||||
return
|
||||
t0 = time.time()
|
||||
maxrank = int(maxrank)
|
||||
rank_ratio = 1 if not auto_rank else rank_ratio
|
||||
shared.log.debug(f'LoRA extract: modules={modules} maxrank={maxrank} auto={auto_rank} ratio={rank_ratio} fn="{fn}"')
|
||||
shared.state.begin('LoRA extract')
|
||||
|
||||
with p.Progress(p.TextColumn('[cyan]LoRA extract'), p.BarColumn(), p.TaskProgressColumn(), p.TimeRemainingColumn(), p.TimeElapsedColumn(), p.TextColumn('[cyan]{task.description}'), console=shared.console) as progress:
|
||||
|
||||
if 'te' in modules and getattr(shared.sd_model, 'text_encoder', None) is not None:
|
||||
modules = shared.sd_model.text_encoder.named_modules()
|
||||
task = progress.add_task(description="te1 decompose", total=len(list(modules)))
|
||||
for name, module in shared.sd_model.text_encoder.named_modules():
|
||||
progress.update(task, advance=1)
|
||||
weights_backup = getattr(module, "network_weights_backup", None)
|
||||
if weights_backup is None or getattr(module, "network_current_names", None) is None:
|
||||
continue
|
||||
prefix = "lora_te1_" if hasattr(shared.sd_model, 'text_encoder_2') else "lora_te_"
|
||||
module.svdhandler = SVDHandler(maxrank, rank_ratio)
|
||||
module.svdhandler.network_name = prefix + name.replace(".", "_")
|
||||
with devices.inference_context():
|
||||
module.svdhandler.decompose(module.weight, weights_backup)
|
||||
progress.remove_task(task)
|
||||
t1 = time.time()
|
||||
|
||||
if 'te' in modules and getattr(shared.sd_model, 'text_encoder_2', None) is not None:
|
||||
modules = shared.sd_model.text_encoder_2.named_modules()
|
||||
task = progress.add_task(description="te2 decompose", total=len(list(modules)))
|
||||
for name, module in shared.sd_model.text_encoder_2.named_modules():
|
||||
progress.update(task, advance=1)
|
||||
weights_backup = getattr(module, "network_weights_backup", None)
|
||||
if weights_backup is None or getattr(module, "network_current_names", None) is None:
|
||||
continue
|
||||
module.svdhandler = SVDHandler(maxrank, rank_ratio)
|
||||
module.svdhandler.network_name = "lora_te2_" + name.replace(".", "_")
|
||||
with devices.inference_context():
|
||||
module.svdhandler.decompose(module.weight, weights_backup)
|
||||
progress.remove_task(task)
|
||||
t2 = time.time()
|
||||
|
||||
if 'unet' in modules and getattr(shared.sd_model, 'unet', None) is not None:
|
||||
modules = shared.sd_model.unet.named_modules()
|
||||
task = progress.add_task(description="unet decompose", total=len(list(modules)))
|
||||
for name, module in shared.sd_model.unet.named_modules():
|
||||
progress.update(task, advance=1)
|
||||
weights_backup = getattr(module, "network_weights_backup", None)
|
||||
if weights_backup is None or getattr(module, "network_current_names", None) is None:
|
||||
continue
|
||||
module.svdhandler = SVDHandler(maxrank, rank_ratio)
|
||||
module.svdhandler.network_name = "lora_unet_" + name.replace(".", "_")
|
||||
with devices.inference_context():
|
||||
module.svdhandler.decompose(module.weight, weights_backup)
|
||||
progress.remove_task(task)
|
||||
t3 = time.time()
|
||||
|
||||
lora_state_dict = {}
|
||||
for sub in ['text_encoder', 'text_encoder_2', 'unet', 'transformer']:
|
||||
submodel = getattr(shared.sd_model, sub, None)
|
||||
if submodel is not None:
|
||||
modules = submodel.named_modules()
|
||||
task = progress.add_task(description=f"{sub} exctract", total=len(list(modules)))
|
||||
for _name, module in submodel.named_modules():
|
||||
progress.update(task, advance=1)
|
||||
if not hasattr(module, "svdhandler"):
|
||||
continue
|
||||
lora_state_dict.update(module.svdhandler.makeweights())
|
||||
del module.svdhandler
|
||||
progress.remove_task(task)
|
||||
t4 = time.time()
|
||||
|
||||
if not os.path.isabs(fn):
|
||||
fn = os.path.join(shared.cmd_opts.lora_dir, fn)
|
||||
if not fn.endswith('.safetensors'):
|
||||
fn += '.safetensors'
|
||||
if os.path.exists(fn):
|
||||
if overwrite:
|
||||
os.remove(fn)
|
||||
else:
|
||||
msg = f'LoRA extract: fn="{fn}" file exists'
|
||||
shared.log.warning(msg)
|
||||
yield msg
|
||||
return
|
||||
|
||||
shared.state.end()
|
||||
meta = make_meta(fn, maxrank, rank_ratio)
|
||||
shared.log.debug(f'LoRA metadata: {meta}')
|
||||
try:
|
||||
save_file(tensors=lora_state_dict, metadata=meta, filename=fn)
|
||||
except Exception as e:
|
||||
msg = f'LoRA extract error: fn="{fn}" {e}'
|
||||
shared.log.error(msg)
|
||||
yield msg
|
||||
return
|
||||
t5 = time.time()
|
||||
shared.log.debug(f'LoRA extract: time={t5-t0:.2f} te1={t1-t0:.2f} te2={t2-t1:.2f} unet={t3-t2:.2f} save={t5-t4:.2f}')
|
||||
keys = list(lora_state_dict.keys())
|
||||
msg = f'LoRA extract: fn="{fn}" keys={len(keys)}'
|
||||
shared.log.info(msg)
|
||||
yield msg
|
||||
|
||||
|
||||
def create_ui():
|
||||
def gr_show(visible=True):
|
||||
return {"visible": visible, "__type__": "update"}
|
||||
|
||||
with gr.Tab(label="Extract LoRA"):
|
||||
with gr.Row():
|
||||
loaded = gr.Textbox(placeholder="Press refresh to query loaded LoRA", label="Loaded LoRA", interactive=False)
|
||||
create_refresh_button(loaded, lambda: None, lambda: {'value': loaded_lora_str()}, "testid")
|
||||
with gr.Group():
|
||||
with gr.Row():
|
||||
modules = gr.CheckboxGroup(label="Modules to extract", value=['unet'], choices=['te', 'unet'])
|
||||
with gr.Row():
|
||||
auto_rank = gr.Checkbox(value=False, label="Automatically determine rank")
|
||||
rank_ratio = gr.Slider(label="Autorank ratio", value=1, minimum=0, maximum=1, step=0.05, visible=False)
|
||||
rank = gr.Slider(label="Maximum rank", value=32, minimum=1, maximum=256)
|
||||
with gr.Row():
|
||||
filename = gr.Textbox(label="LoRA target filename")
|
||||
overwrite = gr.Checkbox(value=False, label="Overwrite existing file")
|
||||
with gr.Row():
|
||||
extract = gr.Button(value="Extract LoRA", variant='primary')
|
||||
status = gr.HTML(value="", show_label=False)
|
||||
|
||||
auto_rank.change(fn=lambda x: gr_show(x), inputs=[auto_rank], outputs=[rank_ratio])
|
||||
extract.click(
|
||||
fn=wrap_gradio_gpu_call(make_lora, extra_outputs=[]),
|
||||
inputs=[filename, rank, auto_rank, rank_ratio, modules, overwrite],
|
||||
outputs=[status]
|
||||
)
|
||||
@@ -1,77 +0,0 @@
|
||||
import sys
|
||||
import torch
|
||||
import networks
|
||||
from modules import patches, shared, model_quant
|
||||
|
||||
|
||||
class LoraPatches:
|
||||
def __init__(self):
|
||||
self.active = False
|
||||
self.Linear_forward = None
|
||||
self.Linear_load_state_dict = None
|
||||
self.Conv2d_forward = None
|
||||
self.Conv2d_load_state_dict = None
|
||||
self.GroupNorm_forward = None
|
||||
self.GroupNorm_load_state_dict = None
|
||||
self.LayerNorm_forward = None
|
||||
self.LayerNorm_load_state_dict = None
|
||||
self.MultiheadAttention_forward = None
|
||||
self.MultiheadAttention_load_state_dict = None
|
||||
# optional quant forwards
|
||||
self.Linear4bit_forward = None # bitsandbytes
|
||||
self.QLinear_forward = None # optimum.quanto
|
||||
self.QConv2d_forward = None # optimum.quanto
|
||||
|
||||
def handle_quant(self, apply: bool):
|
||||
if 'bitsandbytes' in sys.modules: # lora should not be first to initialize quantization
|
||||
bnb = model_quant.load_bnb(silent=True)
|
||||
if bnb is not None:
|
||||
if apply:
|
||||
self.Linear4bit_forward = patches.patch(__name__, bnb.nn.Linear4bit, 'forward', networks.network_Linear4bit_forward)
|
||||
else:
|
||||
self.Linear4bit_forward = patches.undo(__name__, bnb.nn.Linear4bit, 'forward') # pylint: disable=E1128
|
||||
if 'optimum.quanto' in sys.modules:
|
||||
quanto = model_quant.load_quanto(silent=True)
|
||||
if quanto is not None:
|
||||
if apply:
|
||||
self.QLinear_forward = patches.patch(__name__, quanto.nn.QLinear, 'forward', networks.network_QLinear_forward)
|
||||
self.QConv2d_forward = patches.patch(__name__, quanto.nn.QConv2d, 'forward', networks.network_QConv2d_forward)
|
||||
else:
|
||||
self.QLinear_forward = patches.undo(__name__, quanto.nn.QLinear, 'forward') # pylint: disable=E1128
|
||||
self.QConv2d_forward = patches.undo(__name__, quanto.nn.QConv2d, 'forward') # pylint: disable=E1128
|
||||
|
||||
def apply(self):
|
||||
if self.active or shared.opts.lora_force_diffusers:
|
||||
return
|
||||
self.Linear_forward = patches.patch(__name__, torch.nn.Linear, 'forward', networks.network_Linear_forward)
|
||||
self.Linear_load_state_dict = patches.patch(__name__, torch.nn.Linear, '_load_from_state_dict', networks.network_Linear_load_state_dict)
|
||||
self.Conv2d_forward = patches.patch(__name__, torch.nn.Conv2d, 'forward', networks.network_Conv2d_forward)
|
||||
self.Conv2d_load_state_dict = patches.patch(__name__, torch.nn.Conv2d, '_load_from_state_dict', networks.network_Conv2d_load_state_dict)
|
||||
self.GroupNorm_forward = patches.patch(__name__, torch.nn.GroupNorm, 'forward', networks.network_GroupNorm_forward)
|
||||
self.GroupNorm_load_state_dict = patches.patch(__name__, torch.nn.GroupNorm, '_load_from_state_dict', networks.network_GroupNorm_load_state_dict)
|
||||
self.LayerNorm_forward = patches.patch(__name__, torch.nn.LayerNorm, 'forward', networks.network_LayerNorm_forward)
|
||||
self.LayerNorm_load_state_dict = patches.patch(__name__, torch.nn.LayerNorm, '_load_from_state_dict', networks.network_LayerNorm_load_state_dict)
|
||||
self.MultiheadAttention_forward = patches.patch(__name__, torch.nn.MultiheadAttention, 'forward', networks.network_MultiheadAttention_forward)
|
||||
self.MultiheadAttention_load_state_dict = patches.patch(__name__, torch.nn.MultiheadAttention, '_load_from_state_dict', networks.network_MultiheadAttention_load_state_dict)
|
||||
self.handle_quant(apply=True)
|
||||
networks.timer['load'] = 0
|
||||
networks.timer['apply'] = 0
|
||||
networks.timer['restore'] = 0
|
||||
self.active = True
|
||||
|
||||
def undo(self):
|
||||
if not self.active or shared.opts.lora_force_diffusers:
|
||||
return
|
||||
self.Linear_forward = patches.undo(__name__, torch.nn.Linear, 'forward') # pylint: disable=E1128
|
||||
self.Linear_load_state_dict = patches.undo(__name__, torch.nn.Linear, '_load_from_state_dict') # pylint: disable=E1128
|
||||
self.Conv2d_forward = patches.undo(__name__, torch.nn.Conv2d, 'forward') # pylint: disable=E1128
|
||||
self.Conv2d_load_state_dict = patches.undo(__name__, torch.nn.Conv2d, '_load_from_state_dict') # pylint: disable=E1128
|
||||
self.GroupNorm_forward = patches.undo(__name__, torch.nn.GroupNorm, 'forward') # pylint: disable=E1128
|
||||
self.GroupNorm_load_state_dict = patches.undo(__name__, torch.nn.GroupNorm, '_load_from_state_dict') # pylint: disable=E1128
|
||||
self.LayerNorm_forward = patches.undo(__name__, torch.nn.LayerNorm, 'forward') # pylint: disable=E1128
|
||||
self.LayerNorm_load_state_dict = patches.undo(__name__, torch.nn.LayerNorm, '_load_from_state_dict') # pylint: disable=E1128
|
||||
self.MultiheadAttention_forward = patches.undo(__name__, torch.nn.MultiheadAttention, 'forward') # pylint: disable=E1128
|
||||
self.MultiheadAttention_load_state_dict = patches.undo(__name__, torch.nn.MultiheadAttention, '_load_from_state_dict') # pylint: disable=E1128
|
||||
self.handle_quant(apply=False)
|
||||
patches.originals.pop(__name__, None)
|
||||
self.active = False
|
||||
@@ -1,66 +0,0 @@
|
||||
import torch
|
||||
|
||||
|
||||
def make_weight_cp(t, wa, wb):
|
||||
temp = torch.einsum('i j k l, j r -> i r k l', t, wb)
|
||||
return torch.einsum('i j k l, i r -> r j k l', temp, wa)
|
||||
|
||||
|
||||
def rebuild_conventional(up, down, shape, dyn_dim=None):
|
||||
up = up.reshape(up.size(0), -1)
|
||||
down = down.reshape(down.size(0), -1)
|
||||
if dyn_dim is not None:
|
||||
up = up[:, :dyn_dim]
|
||||
down = down[:dyn_dim, :]
|
||||
return (up @ down).reshape(shape)
|
||||
|
||||
|
||||
def rebuild_cp_decomposition(up, down, mid):
|
||||
up = up.reshape(up.size(0), -1)
|
||||
down = down.reshape(down.size(0), -1)
|
||||
return torch.einsum('n m k l, i n, m j -> i j k l', mid, up, down)
|
||||
|
||||
|
||||
# copied from https://github.com/KohakuBlueleaf/LyCORIS/blob/dev/lycoris/modules/lokr.py
|
||||
def factorization(dimension: int, factor:int=-1) -> tuple[int, int]:
|
||||
'''
|
||||
return a tuple of two value of input dimension decomposed by the number closest to factor
|
||||
second value is higher or equal than first value.
|
||||
|
||||
In LoRA with Kroneckor Product, first value is a value for weight scale.
|
||||
secon value is a value for weight.
|
||||
|
||||
Becuase of non-commutative property, A⊗B ≠ B⊗A. Meaning of two matrices is slightly different.
|
||||
|
||||
examples)
|
||||
factor
|
||||
-1 2 4 8 16 ...
|
||||
127 -> 1, 127 127 -> 1, 127 127 -> 1, 127 127 -> 1, 127 127 -> 1, 127
|
||||
128 -> 8, 16 128 -> 2, 64 128 -> 4, 32 128 -> 8, 16 128 -> 8, 16
|
||||
250 -> 10, 25 250 -> 2, 125 250 -> 2, 125 250 -> 5, 50 250 -> 10, 25
|
||||
360 -> 8, 45 360 -> 2, 180 360 -> 4, 90 360 -> 8, 45 360 -> 12, 30
|
||||
512 -> 16, 32 512 -> 2, 256 512 -> 4, 128 512 -> 8, 64 512 -> 16, 32
|
||||
1024 -> 32, 32 1024 -> 2, 512 1024 -> 4, 256 1024 -> 8, 128 1024 -> 16, 64
|
||||
'''
|
||||
|
||||
if factor > 0 and (dimension % factor) == 0:
|
||||
m = factor
|
||||
n = dimension // factor
|
||||
if m > n:
|
||||
n, m = m, n
|
||||
return m, n
|
||||
if factor < 0:
|
||||
factor = dimension
|
||||
m, n = 1, dimension
|
||||
length = m + n
|
||||
while m<n:
|
||||
new_m = m + 1
|
||||
while dimension%new_m != 0:
|
||||
new_m += 1
|
||||
new_n = dimension // new_m
|
||||
if new_m + new_n > length or new_m>factor:
|
||||
break
|
||||
m, n = new_m, new_n
|
||||
if m > n:
|
||||
n, m = m, n
|
||||
return m, n
|
||||
@@ -1,187 +0,0 @@
|
||||
from __future__ import annotations
|
||||
import os
|
||||
from collections import namedtuple
|
||||
import enum
|
||||
|
||||
from modules import sd_models, hashes, shared
|
||||
|
||||
NetworkWeights = namedtuple('NetworkWeights', ['network_key', 'sd_key', 'w', 'sd_module'])
|
||||
metadata_tags_order = {"ss_sd_model_name": 1, "ss_resolution": 2, "ss_clip_skip": 3, "ss_num_train_images": 10, "ss_tag_frequency": 20}
|
||||
|
||||
|
||||
class SdVersion(enum.Enum):
|
||||
Unknown = 1
|
||||
SD1 = 2
|
||||
SD2 = 3
|
||||
SD3 = 3
|
||||
SDXL = 4
|
||||
SC = 5
|
||||
F1 = 6
|
||||
|
||||
|
||||
class NetworkOnDisk:
|
||||
def __init__(self, name, filename):
|
||||
self.name = name
|
||||
self.filename = filename
|
||||
if filename.startswith(shared.cmd_opts.lora_dir):
|
||||
self.fullname = os.path.splitext(filename[len(shared.cmd_opts.lora_dir):].strip("/"))[0]
|
||||
else:
|
||||
self.fullname = name
|
||||
self.metadata = {}
|
||||
self.is_safetensors = os.path.splitext(filename)[1].lower() == ".safetensors"
|
||||
if self.is_safetensors:
|
||||
self.metadata = sd_models.read_metadata_from_safetensors(filename)
|
||||
if self.metadata:
|
||||
m = {}
|
||||
for k, v in sorted(self.metadata.items(), key=lambda x: metadata_tags_order.get(x[0], 999)):
|
||||
m[k] = v
|
||||
self.metadata = m
|
||||
self.alias = self.metadata.get('ss_output_name', self.name)
|
||||
# self.set_hash(self.metadata.get('sshs_model_hash') or hashes.sha256_from_cache(self.filename, "lora/" + self.name, use_addnet_hash=self.is_safetensors) or '')
|
||||
sha256 = hashes.sha256_from_cache(self.filename, "lora/" + self.name) or hashes.sha256_from_cache(self.filename, "lora/" + self.name, use_addnet_hash=True) or self.metadata.get('sshs_model_hash')
|
||||
self.set_hash(sha256)
|
||||
self.sd_version = self.detect_version()
|
||||
|
||||
def detect_version(self):
|
||||
base = str(self.metadata.get('ss_base_model_version', "")).lower()
|
||||
arch = str(self.metadata.get('modelspec.architecture', "")).lower()
|
||||
if base.startswith("sd_v1"):
|
||||
return 'sd1'
|
||||
if base.startswith("sdxl"):
|
||||
return 'xl'
|
||||
if base.startswith("stable_cascade"):
|
||||
return 'sc'
|
||||
if base.startswith("sd3"):
|
||||
return 'sd3'
|
||||
if base.startswith("flux"):
|
||||
return 'f1'
|
||||
|
||||
if arch.startswith("stable-diffusion-v1"):
|
||||
return 'sd1'
|
||||
if arch.startswith("stable-diffusion-xl"):
|
||||
return 'xl'
|
||||
if arch.startswith("stable-cascade"):
|
||||
return 'sc'
|
||||
if arch.startswith("flux"):
|
||||
return 'f1'
|
||||
|
||||
if "v1-5" in str(self.metadata.get('ss_sd_model_name', "")):
|
||||
return 'sd1'
|
||||
if str(self.metadata.get('ss_v2', "")) == "True":
|
||||
return 'sd2'
|
||||
if 'flux' in self.name.lower():
|
||||
return 'f1'
|
||||
if 'xl' in self.name.lower():
|
||||
return 'xl'
|
||||
|
||||
return ''
|
||||
|
||||
def set_hash(self, v):
|
||||
self.hash = v or ''
|
||||
self.shorthash = self.hash[0:8]
|
||||
|
||||
def read_hash(self):
|
||||
if not self.hash:
|
||||
self.set_hash(hashes.sha256(self.filename, "lora/" + self.name, use_addnet_hash=self.is_safetensors) or '')
|
||||
|
||||
def get_alias(self):
|
||||
import networks
|
||||
return self.name if shared.opts.lora_preferred_name == "filename" or self.alias.lower() in networks.forbidden_network_aliases else self.alias
|
||||
|
||||
|
||||
class Network: # LoraModule
|
||||
def __init__(self, name, network_on_disk: NetworkOnDisk):
|
||||
self.name = name
|
||||
self.network_on_disk = network_on_disk
|
||||
self.te_multiplier = 1.0
|
||||
self.unet_multiplier = [1.0] * 3
|
||||
self.dyn_dim = None
|
||||
self.modules = {}
|
||||
self.bundle_embeddings = {}
|
||||
self.mtime = None
|
||||
self.mentioned_name = None
|
||||
self.tags = None
|
||||
"""the text that was used to add the network to prompt - can be either name or an alias"""
|
||||
|
||||
|
||||
class ModuleType:
|
||||
def create_module(self, net: Network, weights: NetworkWeights) -> Network | None: # pylint: disable=W0613
|
||||
return None
|
||||
|
||||
|
||||
class NetworkModule:
|
||||
def __init__(self, net: Network, weights: NetworkWeights):
|
||||
self.network = net
|
||||
self.network_key = weights.network_key
|
||||
self.sd_key = weights.sd_key
|
||||
self.sd_module = weights.sd_module
|
||||
if hasattr(self.sd_module, 'weight'):
|
||||
self.shape = self.sd_module.weight.shape
|
||||
self.dim = None
|
||||
self.bias = weights.w.get("bias")
|
||||
self.alpha = weights.w["alpha"].item() if "alpha" in weights.w else None
|
||||
self.scale = weights.w["scale"].item() if "scale" in weights.w else None
|
||||
self.dora_scale = weights.w.get("dora_scale", None)
|
||||
self.dora_norm_dims = len(self.shape) - 1
|
||||
|
||||
def multiplier(self):
|
||||
unet_multiplier = 3 * [self.network.unet_multiplier] if not isinstance(self.network.unet_multiplier, list) else self.network.unet_multiplier
|
||||
if 'transformer' in self.sd_key[:20]:
|
||||
return self.network.te_multiplier
|
||||
if "down_blocks" in self.sd_key:
|
||||
return unet_multiplier[0]
|
||||
if "mid_block" in self.sd_key:
|
||||
return unet_multiplier[1]
|
||||
if "up_blocks" in self.sd_key:
|
||||
return unet_multiplier[2]
|
||||
else:
|
||||
return unet_multiplier[0]
|
||||
|
||||
def calc_scale(self):
|
||||
if self.scale is not None:
|
||||
return self.scale
|
||||
if self.dim is not None and self.alpha is not None:
|
||||
return self.alpha / self.dim
|
||||
return 1.0
|
||||
|
||||
def apply_weight_decompose(self, updown, orig_weight):
|
||||
# Match the device/dtype
|
||||
orig_weight = orig_weight.to(updown.dtype)
|
||||
dora_scale = self.dora_scale.to(device=orig_weight.device, dtype=updown.dtype)
|
||||
updown = updown.to(orig_weight.device)
|
||||
|
||||
merged_scale1 = updown + orig_weight
|
||||
merged_scale1_norm = (
|
||||
merged_scale1.transpose(0, 1)
|
||||
.reshape(merged_scale1.shape[1], -1)
|
||||
.norm(dim=1, keepdim=True)
|
||||
.reshape(merged_scale1.shape[1], *[1] * self.dora_norm_dims)
|
||||
.transpose(0, 1)
|
||||
)
|
||||
|
||||
dora_merged = (
|
||||
merged_scale1 * (dora_scale / merged_scale1_norm)
|
||||
)
|
||||
final_updown = dora_merged - orig_weight
|
||||
return final_updown
|
||||
|
||||
def finalize_updown(self, updown, orig_weight, output_shape, ex_bias=None):
|
||||
if self.bias is not None:
|
||||
updown = updown.reshape(self.bias.shape)
|
||||
updown += self.bias.to(orig_weight.device, dtype=orig_weight.dtype)
|
||||
updown = updown.reshape(output_shape)
|
||||
if len(output_shape) == 4:
|
||||
updown = updown.reshape(output_shape)
|
||||
if orig_weight.size().numel() == updown.size().numel():
|
||||
updown = updown.reshape(orig_weight.shape)
|
||||
if ex_bias is not None:
|
||||
ex_bias = ex_bias * self.multiplier()
|
||||
if self.dora_scale is not None:
|
||||
updown = self.apply_weight_decompose(updown, orig_weight)
|
||||
return updown * self.calc_scale() * self.multiplier(), ex_bias
|
||||
|
||||
def calc_updown(self, target):
|
||||
raise NotImplementedError
|
||||
|
||||
def forward(self, x, y):
|
||||
raise NotImplementedError
|
||||
@@ -1,26 +0,0 @@
|
||||
import network
|
||||
|
||||
|
||||
class ModuleTypeFull(network.ModuleType):
|
||||
def create_module(self, net: network.Network, weights: network.NetworkWeights):
|
||||
if all(x in weights.w for x in ["diff"]):
|
||||
return NetworkModuleFull(net, weights)
|
||||
return None
|
||||
|
||||
|
||||
class NetworkModuleFull(network.NetworkModule): # pylint: disable=abstract-method
|
||||
def __init__(self, net: network.Network, weights: network.NetworkWeights):
|
||||
super().__init__(net, weights)
|
||||
|
||||
self.weight = weights.w.get("diff")
|
||||
self.ex_bias = weights.w.get("diff_b")
|
||||
|
||||
def calc_updown(self, target):
|
||||
output_shape = self.weight.shape
|
||||
updown = self.weight.to(target.device, dtype=target.dtype)
|
||||
if self.ex_bias is not None:
|
||||
ex_bias = self.ex_bias.to(target.device, dtype=target.dtype)
|
||||
else:
|
||||
ex_bias = None
|
||||
|
||||
return self.finalize_updown(updown, target, output_shape, ex_bias)
|
||||
@@ -1,30 +0,0 @@
|
||||
|
||||
import network
|
||||
|
||||
class ModuleTypeGLora(network.ModuleType):
|
||||
def create_module(self, net: network.Network, weights: network.NetworkWeights):
|
||||
if all(x in weights.w for x in ["a1.weight", "a2.weight", "alpha", "b1.weight", "b2.weight"]):
|
||||
return NetworkModuleGLora(net, weights)
|
||||
return None
|
||||
|
||||
# adapted from https://github.com/KohakuBlueleaf/LyCORIS
|
||||
class NetworkModuleGLora(network.NetworkModule): # pylint: disable=abstract-method
|
||||
def __init__(self, net: network.Network, weights: network.NetworkWeights):
|
||||
super().__init__(net, weights)
|
||||
|
||||
if hasattr(self.sd_module, 'weight'):
|
||||
self.shape = self.sd_module.weight.shape
|
||||
|
||||
self.w1a = weights.w["a1.weight"]
|
||||
self.w1b = weights.w["b1.weight"]
|
||||
self.w2a = weights.w["a2.weight"]
|
||||
self.w2b = weights.w["b2.weight"]
|
||||
|
||||
def calc_updown(self, target): # pylint: disable=arguments-differ
|
||||
w1a = self.w1a.to(target.device, dtype=target.dtype)
|
||||
w1b = self.w1b.to(target.device, dtype=target.dtype)
|
||||
w2a = self.w2a.to(target.device, dtype=target.dtype)
|
||||
w2b = self.w2b.to(target.device, dtype=target.dtype)
|
||||
output_shape = [w1a.size(0), w1b.size(1)]
|
||||
updown = (w2b @ w1b) + ((target @ w2a) @ w1a)
|
||||
return self.finalize_updown(updown, target, output_shape)
|
||||
@@ -1,46 +0,0 @@
|
||||
import lyco_helpers
|
||||
import network
|
||||
|
||||
|
||||
class ModuleTypeHada(network.ModuleType):
|
||||
def create_module(self, net: network.Network, weights: network.NetworkWeights):
|
||||
if all(x in weights.w for x in ["hada_w1_a", "hada_w1_b", "hada_w2_a", "hada_w2_b"]):
|
||||
return NetworkModuleHada(net, weights)
|
||||
return None
|
||||
|
||||
|
||||
class NetworkModuleHada(network.NetworkModule): # pylint: disable=abstract-method
|
||||
def __init__(self, net: network.Network, weights: network.NetworkWeights):
|
||||
super().__init__(net, weights)
|
||||
if hasattr(self.sd_module, 'weight'):
|
||||
self.shape = self.sd_module.weight.shape
|
||||
self.w1a = weights.w["hada_w1_a"]
|
||||
self.w1b = weights.w["hada_w1_b"]
|
||||
self.dim = self.w1b.shape[0]
|
||||
self.w2a = weights.w["hada_w2_a"]
|
||||
self.w2b = weights.w["hada_w2_b"]
|
||||
self.t1 = weights.w.get("hada_t1")
|
||||
self.t2 = weights.w.get("hada_t2")
|
||||
|
||||
def calc_updown(self, target):
|
||||
w1a = self.w1a.to(target.device, dtype=target.dtype)
|
||||
w1b = self.w1b.to(target.device, dtype=target.dtype)
|
||||
w2a = self.w2a.to(target.device, dtype=target.dtype)
|
||||
w2b = self.w2b.to(target.device, dtype=target.dtype)
|
||||
output_shape = [w1a.size(0), w1b.size(1)]
|
||||
if self.t1 is not None:
|
||||
output_shape = [w1a.size(1), w1b.size(1)]
|
||||
t1 = self.t1.to(target.device, dtype=target.dtype)
|
||||
updown1 = lyco_helpers.make_weight_cp(t1, w1a, w1b)
|
||||
output_shape += t1.shape[2:]
|
||||
else:
|
||||
if len(w1b.shape) == 4:
|
||||
output_shape += w1b.shape[2:]
|
||||
updown1 = lyco_helpers.rebuild_conventional(w1a, w1b, output_shape)
|
||||
if self.t2 is not None:
|
||||
t2 = self.t2.to(target.device, dtype=target.dtype)
|
||||
updown2 = lyco_helpers.make_weight_cp(t2, w2a, w2b)
|
||||
else:
|
||||
updown2 = lyco_helpers.rebuild_conventional(w2a, w2b, output_shape)
|
||||
updown = updown1 * updown2
|
||||
return self.finalize_updown(updown, target, output_shape)
|
||||
@@ -1,25 +0,0 @@
|
||||
import network
|
||||
|
||||
|
||||
class ModuleTypeIa3(network.ModuleType):
|
||||
def create_module(self, net: network.Network, weights: network.NetworkWeights):
|
||||
if all(x in weights.w for x in ["weight"]):
|
||||
return NetworkModuleIa3(net, weights)
|
||||
return None
|
||||
|
||||
|
||||
class NetworkModuleIa3(network.NetworkModule): # pylint: disable=abstract-method
|
||||
def __init__(self, net: network.Network, weights: network.NetworkWeights):
|
||||
super().__init__(net, weights)
|
||||
self.w = weights.w["weight"]
|
||||
self.on_input = weights.w["on_input"].item()
|
||||
|
||||
def calc_updown(self, target):
|
||||
w = self.w.to(target.device, dtype=target.dtype)
|
||||
output_shape = [w.size(0), target.size(1)]
|
||||
if self.on_input:
|
||||
output_shape.reverse()
|
||||
else:
|
||||
w = w.reshape(-1, 1)
|
||||
updown = target * w
|
||||
return self.finalize_updown(updown, target, output_shape)
|
||||
@@ -1,57 +0,0 @@
|
||||
import torch
|
||||
import lyco_helpers
|
||||
import network
|
||||
|
||||
|
||||
class ModuleTypeLokr(network.ModuleType):
|
||||
def create_module(self, net: network.Network, weights: network.NetworkWeights):
|
||||
has_1 = "lokr_w1" in weights.w or ("lokr_w1_a" in weights.w and "lokr_w1_b" in weights.w)
|
||||
has_2 = "lokr_w2" in weights.w or ("lokr_w2_a" in weights.w and "lokr_w2_b" in weights.w)
|
||||
if has_1 and has_2:
|
||||
return NetworkModuleLokr(net, weights)
|
||||
return None
|
||||
|
||||
|
||||
def make_kron(orig_shape, w1, w2):
|
||||
if len(w2.shape) == 4:
|
||||
w1 = w1.unsqueeze(2).unsqueeze(2)
|
||||
w2 = w2.contiguous()
|
||||
return torch.kron(w1, w2).reshape(orig_shape)
|
||||
|
||||
|
||||
class NetworkModuleLokr(network.NetworkModule): # pylint: disable=abstract-method
|
||||
def __init__(self, net: network.Network, weights: network.NetworkWeights):
|
||||
super().__init__(net, weights)
|
||||
self.w1 = weights.w.get("lokr_w1")
|
||||
self.w1a = weights.w.get("lokr_w1_a")
|
||||
self.w1b = weights.w.get("lokr_w1_b")
|
||||
self.dim = self.w1b.shape[0] if self.w1b is not None else self.dim
|
||||
self.w2 = weights.w.get("lokr_w2")
|
||||
self.w2a = weights.w.get("lokr_w2_a")
|
||||
self.w2b = weights.w.get("lokr_w2_b")
|
||||
self.dim = self.w2b.shape[0] if self.w2b is not None else self.dim
|
||||
self.t2 = weights.w.get("lokr_t2")
|
||||
|
||||
def calc_updown(self, target):
|
||||
if self.w1 is not None:
|
||||
w1 = self.w1.to(target.device, dtype=target.dtype)
|
||||
else:
|
||||
w1a = self.w1a.to(target.device, dtype=target.dtype)
|
||||
w1b = self.w1b.to(target.device, dtype=target.dtype)
|
||||
w1 = w1a @ w1b
|
||||
if self.w2 is not None:
|
||||
w2 = self.w2.to(target.device, dtype=target.dtype)
|
||||
elif self.t2 is None:
|
||||
w2a = self.w2a.to(target.device, dtype=target.dtype)
|
||||
w2b = self.w2b.to(target.device, dtype=target.dtype)
|
||||
w2 = w2a @ w2b
|
||||
else:
|
||||
t2 = self.t2.to(target.device, dtype=target.dtype)
|
||||
w2a = self.w2a.to(target.device, dtype=target.dtype)
|
||||
w2b = self.w2b.to(target.device, dtype=target.dtype)
|
||||
w2 = lyco_helpers.make_weight_cp(t2, w2a, w2b)
|
||||
output_shape = [w1.size(0) * w2.size(0), w1.size(1) * w2.size(1)]
|
||||
if len(target.shape) == 4:
|
||||
output_shape = target.shape
|
||||
updown = make_kron(output_shape, w1, w2)
|
||||
return self.finalize_updown(updown, target, output_shape)
|
||||
@@ -1,75 +0,0 @@
|
||||
import torch
|
||||
import diffusers.models.lora as diffusers_lora
|
||||
import lyco_helpers
|
||||
import network
|
||||
from modules import devices
|
||||
|
||||
|
||||
class ModuleTypeLora(network.ModuleType):
|
||||
def create_module(self, net: network.Network, weights: network.NetworkWeights):
|
||||
if all(x in weights.w for x in ["lora_up.weight", "lora_down.weight"]):
|
||||
return NetworkModuleLora(net, weights)
|
||||
return None
|
||||
|
||||
|
||||
class NetworkModuleLora(network.NetworkModule):
|
||||
|
||||
def __init__(self, net: network.Network, weights: network.NetworkWeights):
|
||||
super().__init__(net, weights)
|
||||
self.up_model = self.create_module(weights.w, "lora_up.weight")
|
||||
self.down_model = self.create_module(weights.w, "lora_down.weight")
|
||||
self.mid_model = self.create_module(weights.w, "lora_mid.weight", none_ok=True)
|
||||
self.dim = weights.w["lora_down.weight"].shape[0]
|
||||
|
||||
def create_module(self, weights, key, none_ok=False):
|
||||
weight = weights.get(key)
|
||||
if weight is None and none_ok:
|
||||
return None
|
||||
linear_modules = [torch.nn.Linear, torch.nn.modules.linear.NonDynamicallyQuantizableLinear, torch.nn.MultiheadAttention, diffusers_lora.LoRACompatibleLinear]
|
||||
is_linear = type(self.sd_module) in linear_modules or self.sd_module.__class__.__name__ in {"NNCFLinear", "QLinear", "Linear4bit"}
|
||||
is_conv = type(self.sd_module) in [torch.nn.Conv2d, diffusers_lora.LoRACompatibleConv] or self.sd_module.__class__.__name__ in {"NNCFConv2d", "QConv2d"}
|
||||
if is_linear:
|
||||
weight = weight.reshape(weight.shape[0], -1)
|
||||
module = torch.nn.Linear(weight.shape[1], weight.shape[0], bias=False)
|
||||
elif is_conv and (key == "lora_down.weight" or key == "dyn_up"):
|
||||
if len(weight.shape) == 2:
|
||||
weight = weight.reshape(weight.shape[0], -1, 1, 1)
|
||||
if weight.shape[2] != 1 or weight.shape[3] != 1:
|
||||
module = torch.nn.Conv2d(weight.shape[1], weight.shape[0], self.sd_module.kernel_size, self.sd_module.stride, self.sd_module.padding, bias=False)
|
||||
else:
|
||||
module = torch.nn.Conv2d(weight.shape[1], weight.shape[0], (1, 1), bias=False)
|
||||
elif is_conv and key == "lora_mid.weight":
|
||||
module = torch.nn.Conv2d(weight.shape[1], weight.shape[0], self.sd_module.kernel_size, self.sd_module.stride, self.sd_module.padding, bias=False)
|
||||
elif is_conv and (key == "lora_up.weight" or key == "dyn_down"):
|
||||
module = torch.nn.Conv2d(weight.shape[1], weight.shape[0], (1, 1), bias=False)
|
||||
else:
|
||||
raise AssertionError(f'Lora unsupported: layer={self.network_key} type={type(self.sd_module).__name__}')
|
||||
with torch.no_grad():
|
||||
if weight.shape != module.weight.shape:
|
||||
weight = weight.reshape(module.weight.shape)
|
||||
module.weight.copy_(weight)
|
||||
module.weight.requires_grad_(False)
|
||||
return module
|
||||
|
||||
def calc_updown(self, target): # pylint: disable=W0237
|
||||
target_dtype = target.dtype if target.dtype != torch.uint8 else self.up_model.weight.dtype
|
||||
up = self.up_model.weight.to(target.device, dtype=target_dtype)
|
||||
down = self.down_model.weight.to(target.device, dtype=target_dtype)
|
||||
output_shape = [up.size(0), down.size(1)]
|
||||
if self.mid_model is not None:
|
||||
# cp-decomposition
|
||||
mid = self.mid_model.weight.to(target.device, dtype=target_dtype)
|
||||
updown = lyco_helpers.rebuild_cp_decomposition(up, down, mid)
|
||||
output_shape += mid.shape[2:]
|
||||
else:
|
||||
if len(down.shape) == 4:
|
||||
output_shape += down.shape[2:]
|
||||
updown = lyco_helpers.rebuild_conventional(up, down, output_shape, self.network.dyn_dim)
|
||||
return self.finalize_updown(updown, target, output_shape)
|
||||
|
||||
def forward(self, x, y):
|
||||
self.up_model.to(device=devices.device)
|
||||
self.down_model.to(device=devices.device)
|
||||
if hasattr(y, "scale"):
|
||||
return y(scale=1) + self.up_model(self.down_model(x)) * self.multiplier() * self.calc_scale()
|
||||
return y + self.up_model(self.down_model(x)) * self.multiplier() * self.calc_scale()
|
||||
@@ -1,24 +0,0 @@
|
||||
import network
|
||||
|
||||
|
||||
class ModuleTypeNorm(network.ModuleType):
|
||||
def create_module(self, net: network.Network, weights: network.NetworkWeights):
|
||||
if all(x in weights.w for x in ["w_norm", "b_norm"]):
|
||||
return NetworkModuleNorm(net, weights)
|
||||
return None
|
||||
|
||||
|
||||
class NetworkModuleNorm(network.NetworkModule): # pylint: disable=abstract-method
|
||||
def __init__(self, net: network.Network, weights: network.NetworkWeights):
|
||||
super().__init__(net, weights)
|
||||
self.w_norm = weights.w.get("w_norm")
|
||||
self.b_norm = weights.w.get("b_norm")
|
||||
|
||||
def calc_updown(self, target):
|
||||
output_shape = self.w_norm.shape
|
||||
updown = self.w_norm.to(target.device, dtype=target.dtype)
|
||||
if self.b_norm is not None:
|
||||
ex_bias = self.b_norm.to(target.device, dtype=target.dtype)
|
||||
else:
|
||||
ex_bias = None
|
||||
return self.finalize_updown(updown, target, output_shape, ex_bias)
|
||||
@@ -1,81 +0,0 @@
|
||||
import torch
|
||||
import network
|
||||
from lyco_helpers import factorization
|
||||
from einops import rearrange
|
||||
|
||||
|
||||
class ModuleTypeOFT(network.ModuleType):
|
||||
def create_module(self, net: network.Network, weights: network.NetworkWeights):
|
||||
if all(x in weights.w for x in ["oft_blocks"]) or all(x in weights.w for x in ["oft_diag"]):
|
||||
return NetworkModuleOFT(net, weights)
|
||||
return None
|
||||
|
||||
# Supports both kohya-ss' implementation of COFT https://github.com/kohya-ss/sd-scripts/blob/main/networks/oft.py
|
||||
# and KohakuBlueleaf's implementation of OFT/COFT https://github.com/KohakuBlueleaf/LyCORIS/blob/dev/lycoris/modules/diag_oft.py
|
||||
class NetworkModuleOFT(network.NetworkModule): # pylint: disable=abstract-method
|
||||
def __init__(self, net: network.Network, weights: network.NetworkWeights):
|
||||
super().__init__(net, weights)
|
||||
self.lin_module = None
|
||||
self.org_module: list[torch.Module] = [self.sd_module]
|
||||
self.scale = 1.0
|
||||
|
||||
# kohya-ss
|
||||
if "oft_blocks" in weights.w.keys():
|
||||
self.is_kohya = True
|
||||
self.oft_blocks = weights.w["oft_blocks"] # (num_blocks, block_size, block_size)
|
||||
self.alpha = weights.w["alpha"] # alpha is constraint
|
||||
self.dim = self.oft_blocks.shape[0] # lora dim
|
||||
# LyCORIS
|
||||
elif "oft_diag" in weights.w.keys():
|
||||
self.is_kohya = False
|
||||
self.oft_blocks = weights.w["oft_diag"]
|
||||
# self.alpha is unused
|
||||
self.dim = self.oft_blocks.shape[1] # (num_blocks, block_size, block_size)
|
||||
|
||||
is_linear = type(self.sd_module) in [torch.nn.Linear, torch.nn.modules.linear.NonDynamicallyQuantizableLinear]
|
||||
is_conv = type(self.sd_module) in [torch.nn.Conv2d]
|
||||
is_other_linear = type(self.sd_module) in [torch.nn.MultiheadAttention] # unsupported
|
||||
|
||||
if is_linear:
|
||||
self.out_dim = self.sd_module.out_features
|
||||
elif is_conv:
|
||||
self.out_dim = self.sd_module.out_channels
|
||||
elif is_other_linear:
|
||||
self.out_dim = self.sd_module.embed_dim
|
||||
|
||||
if self.is_kohya:
|
||||
self.constraint = self.alpha * self.out_dim
|
||||
self.num_blocks = self.dim
|
||||
self.block_size = self.out_dim // self.dim
|
||||
else:
|
||||
self.constraint = None
|
||||
self.block_size, self.num_blocks = factorization(self.out_dim, self.dim)
|
||||
|
||||
def calc_updown(self, target):
|
||||
oft_blocks = self.oft_blocks.to(target.device, dtype=target.dtype)
|
||||
eye = torch.eye(self.block_size, device=target.device)
|
||||
constraint = self.constraint.to(target.device)
|
||||
|
||||
if self.is_kohya:
|
||||
block_Q = oft_blocks - oft_blocks.transpose(1, 2) # ensure skew-symmetric orthogonal matrix
|
||||
norm_Q = torch.norm(block_Q.flatten()).to(target.device)
|
||||
new_norm_Q = torch.clamp(norm_Q, max=constraint)
|
||||
block_Q = block_Q * ((new_norm_Q + 1e-8) / (norm_Q + 1e-8))
|
||||
mat1 = eye + block_Q
|
||||
mat2 = (eye - block_Q).float().inverse()
|
||||
oft_blocks = torch.matmul(mat1, mat2)
|
||||
|
||||
R = oft_blocks.to(target.device, dtype=target.dtype)
|
||||
|
||||
# This errors out for MultiheadAttention, might need to be handled up-stream
|
||||
merged_weight = rearrange(target, '(k n) ... -> k n ...', k=self.num_blocks, n=self.block_size)
|
||||
merged_weight = torch.einsum(
|
||||
'k n m, k n ... -> k m ...',
|
||||
R,
|
||||
merged_weight
|
||||
)
|
||||
merged_weight = rearrange(merged_weight, 'k m ... -> (k m) ...')
|
||||
|
||||
updown = merged_weight.to(target.device, dtype=target.dtype) - target
|
||||
output_shape = target.shape
|
||||
return self.finalize_updown(updown, target, output_shape)
|
||||
@@ -1,48 +0,0 @@
|
||||
from modules import shared
|
||||
|
||||
|
||||
maybe_diffusers = [ # forced if lora_maybe_diffusers is enabled
|
||||
'aaebf6360f7d', # sd15-lcm
|
||||
'3d18b05e4f56', # sdxl-lcm
|
||||
'b71dcb732467', # sdxl-tcd
|
||||
'813ea5fb1c67', # sdxl-turbo
|
||||
# not really needed, but just in case
|
||||
'5a48ac366664', # hyper-sd15-1step
|
||||
'ee0ff23dcc42', # hyper-sd15-2step
|
||||
'e476eb1da5df', # hyper-sd15-4step
|
||||
'ecb844c3f3b0', # hyper-sd15-8step
|
||||
'1ab289133ebb', # hyper-sd15-8step-cfg
|
||||
'4f494295edb1', # hyper-sdxl-8step
|
||||
'ca14a8c621f8', # hyper-sdxl-8step-cfg
|
||||
'1c88f7295856', # hyper-sdxl-4step
|
||||
'fdd5dcd1d88a', # hyper-sdxl-2step
|
||||
'8cca3706050b', # hyper-sdxl-1step
|
||||
]
|
||||
|
||||
force_diffusers = [ # forced always
|
||||
'816d0eed49fd', # flash-sdxl
|
||||
'c2ec22757b46', # flash-sd15
|
||||
]
|
||||
|
||||
force_models = [ # forced always
|
||||
'sc',
|
||||
'kandinsky',
|
||||
'hunyuandit',
|
||||
'auraflow',
|
||||
]
|
||||
|
||||
force_classes = [ # forced always
|
||||
]
|
||||
|
||||
|
||||
def check_override(shorthash=''):
|
||||
force = False
|
||||
force = force or (shared.sd_model_type in force_models)
|
||||
force = force or (shared.sd_model.__class__.__name__ in force_classes)
|
||||
if len(shorthash) < 4:
|
||||
return force
|
||||
force = force or (any(x.startswith(shorthash) for x in maybe_diffusers) if shared.opts.lora_maybe_diffusers else False)
|
||||
force = force or any(x.startswith(shorthash) for x in force_diffusers)
|
||||
if force and shared.opts.lora_maybe_diffusers:
|
||||
shared.log.debug('LoRA override: force diffusers')
|
||||
return force
|
||||
@@ -1,604 +0,0 @@
|
||||
from typing import Union, List
|
||||
import os
|
||||
import re
|
||||
import time
|
||||
import concurrent
|
||||
import lora_patches
|
||||
import network
|
||||
import network_lora
|
||||
import network_hada
|
||||
import network_ia3
|
||||
import network_oft
|
||||
import network_lokr
|
||||
import network_full
|
||||
import network_norm
|
||||
import network_glora
|
||||
import network_overrides
|
||||
import lora_convert
|
||||
import torch
|
||||
import diffusers.models.lora
|
||||
from modules import shared, devices, sd_models, sd_models_compile, errors, scripts_manager, files_cache, model_quant
|
||||
|
||||
|
||||
debug = os.environ.get('SD_LORA_DEBUG', None) is not None
|
||||
originals: lora_patches.LoraPatches = None
|
||||
extra_network_lora = None
|
||||
available_networks = {}
|
||||
available_network_aliases = {}
|
||||
loaded_networks: List[network.Network] = []
|
||||
timer = { 'load': 0, 'apply': 0, 'restore': 0, 'deactivate': 0 }
|
||||
# networks_in_memory = {}
|
||||
lora_cache = {}
|
||||
diffuser_loaded = []
|
||||
diffuser_scales = []
|
||||
available_network_hash_lookup = {}
|
||||
forbidden_network_aliases = {}
|
||||
re_network_name = re.compile(r"(.*)\s*\([0-9a-fA-F]+\)")
|
||||
module_types = [
|
||||
network_lora.ModuleTypeLora(),
|
||||
network_hada.ModuleTypeHada(),
|
||||
network_ia3.ModuleTypeIa3(),
|
||||
network_oft.ModuleTypeOFT(),
|
||||
network_lokr.ModuleTypeLokr(),
|
||||
network_full.ModuleTypeFull(),
|
||||
network_norm.ModuleTypeNorm(),
|
||||
network_glora.ModuleTypeGLora(),
|
||||
]
|
||||
convert_diffusers_name_to_compvis = lora_convert.convert_diffusers_name_to_compvis # supermerger compatibility item
|
||||
|
||||
|
||||
def assign_network_names_to_compvis_modules(sd_model):
|
||||
if sd_model is None:
|
||||
return
|
||||
sd_model = getattr(shared.sd_model, "pipe", shared.sd_model) # wrapped model compatiblility
|
||||
network_layer_mapping = {}
|
||||
if shared.native:
|
||||
if hasattr(sd_model, 'text_encoder') and sd_model.text_encoder is not None:
|
||||
for name, module in sd_model.text_encoder.named_modules():
|
||||
prefix = "lora_te1_" if hasattr(sd_model, 'text_encoder_2') else "lora_te_"
|
||||
network_name = prefix + name.replace(".", "_")
|
||||
network_layer_mapping[network_name] = module
|
||||
module.network_layer_name = network_name
|
||||
if hasattr(sd_model, 'text_encoder_2'):
|
||||
for name, module in sd_model.text_encoder_2.named_modules():
|
||||
network_name = "lora_te2_" + name.replace(".", "_")
|
||||
network_layer_mapping[network_name] = module
|
||||
module.network_layer_name = network_name
|
||||
if hasattr(sd_model, 'unet'):
|
||||
for name, module in sd_model.unet.named_modules():
|
||||
network_name = "lora_unet_" + name.replace(".", "_")
|
||||
network_layer_mapping[network_name] = module
|
||||
module.network_layer_name = network_name
|
||||
if hasattr(sd_model, 'transformer'):
|
||||
for name, module in sd_model.transformer.named_modules():
|
||||
network_name = "lora_transformer_" + name.replace(".", "_")
|
||||
network_layer_mapping[network_name] = module
|
||||
if "norm" in network_name and "linear" not in network_name:
|
||||
continue
|
||||
module.network_layer_name = network_name
|
||||
else:
|
||||
if not hasattr(sd_model, 'cond_stage_model'):
|
||||
sd_model.network_layer_mapping = {}
|
||||
return
|
||||
for name, module in sd_model.cond_stage_model.wrapped.named_modules():
|
||||
network_name = name.replace(".", "_")
|
||||
network_layer_mapping[network_name] = module
|
||||
module.network_layer_name = network_name
|
||||
for name, module in sd_model.model.named_modules():
|
||||
network_name = name.replace(".", "_")
|
||||
network_layer_mapping[network_name] = module
|
||||
module.network_layer_name = network_name
|
||||
sd_model.network_layer_mapping = network_layer_mapping
|
||||
|
||||
|
||||
def load_diffusers(name, network_on_disk, lora_scale=shared.opts.extra_networks_default_multiplier) -> network.Network:
|
||||
t0 = time.time()
|
||||
name = name.replace(".", "_")
|
||||
#cached = lora_cache.get(name, None)
|
||||
shared.log.debug(f'Network load: type=LoRA name="{name}" file="{network_on_disk.filename}" detected={network_on_disk.sd_version} method=diffusers scale={lora_scale} fuse={shared.opts.lora_fuse_diffusers}')
|
||||
# if cached is not None:
|
||||
# return cached
|
||||
if not shared.native:
|
||||
return None
|
||||
if not hasattr(shared.sd_model, 'load_lora_weights'):
|
||||
shared.log.error(f'Network load: type=LoRA class={shared.sd_model.__class__} does not implement load lora')
|
||||
return None
|
||||
try:
|
||||
shared.sd_model.load_lora_weights(network_on_disk.filename, adapter_name=name)
|
||||
except Exception as e:
|
||||
if 'already in use' in str(e):
|
||||
pass
|
||||
else:
|
||||
if 'The following keys have not been correctly renamed' in str(e):
|
||||
shared.log.error(f'Network load: type=LoRA name="{name}" diffusers unsupported format')
|
||||
else:
|
||||
shared.log.error(f'Network load: type=LoRA name="{name}" {e}')
|
||||
if debug:
|
||||
errors.display(e, "LoRA")
|
||||
return None
|
||||
if name not in diffuser_loaded:
|
||||
diffuser_loaded.append(name)
|
||||
diffuser_scales.append(lora_scale)
|
||||
net = network.Network(name, network_on_disk)
|
||||
net.mtime = os.path.getmtime(network_on_disk.filename)
|
||||
# lora_cache[name] = net
|
||||
t1 = time.time()
|
||||
timer['load'] += t1 - t0
|
||||
return net
|
||||
|
||||
|
||||
def load_network(name, network_on_disk) -> network.Network:
|
||||
if not shared.sd_loaded:
|
||||
return None
|
||||
t0 = time.time()
|
||||
cached = lora_cache.get(name, None)
|
||||
if debug:
|
||||
shared.log.debug(f'Network load: type=LoRA name="{name}" file="{network_on_disk.filename}" type=lora {"cached" if cached else ""}')
|
||||
if cached is not None:
|
||||
return cached
|
||||
net = network.Network(name, network_on_disk)
|
||||
net.mtime = os.path.getmtime(network_on_disk.filename)
|
||||
sd = sd_models.read_state_dict(network_on_disk.filename, what='network')
|
||||
if shared.sd_model_type in ['f1', 'chroma']: # if kohya flux lora, convert state_dict
|
||||
sd = lora_convert._convert_kohya_flux_lora_to_diffusers(sd) or sd # pylint: disable=protected-access
|
||||
assign_network_names_to_compvis_modules(shared.sd_model)
|
||||
keys_failed_to_match = {}
|
||||
matched_networks = {}
|
||||
bundle_embeddings = {}
|
||||
convert = lora_convert.KeyConvert()
|
||||
for key_network, weight in sd.items():
|
||||
parts = key_network.split('.')
|
||||
if parts[0] == "bundle_emb":
|
||||
emb_name, vec_name = parts[1], key_network.split(".", 2)[-1]
|
||||
emb_dict = bundle_embeddings.get(emb_name, {})
|
||||
emb_dict[vec_name] = weight
|
||||
bundle_embeddings[emb_name] = emb_dict
|
||||
if len(parts) > 5: # messy handler for diffusers peft lora
|
||||
key_network_without_network_parts = '_'.join(parts[:-2])
|
||||
if not key_network_without_network_parts.startswith('lora_'):
|
||||
key_network_without_network_parts = 'lora_' + key_network_without_network_parts
|
||||
network_part = '.'.join(parts[-2:]).replace('lora_A', 'lora_down').replace('lora_B', 'lora_up')
|
||||
else:
|
||||
key_network_without_network_parts, network_part = key_network.split(".", 1)
|
||||
key, sd_module = convert(key_network_without_network_parts) # Now returns lists
|
||||
if sd_module[0] is None:
|
||||
if "bundle_emb" not in key_network:
|
||||
keys_failed_to_match[key_network] = key
|
||||
continue
|
||||
for k, module in zip(key, sd_module):
|
||||
if k not in matched_networks:
|
||||
matched_networks[k] = network.NetworkWeights(network_key=key_network, sd_key=k, w={}, sd_module=module)
|
||||
matched_networks[k].w[network_part] = weight
|
||||
network_types = []
|
||||
for key, weights in matched_networks.items():
|
||||
net_module = None
|
||||
for nettype in module_types:
|
||||
net_module = nettype.create_module(net, weights)
|
||||
if net_module is not None:
|
||||
network_types.append(nettype.__class__.__name__)
|
||||
break
|
||||
if net_module is None:
|
||||
shared.log.error(f'LoRA unhandled: name={name} key={key} weights={weights.w.keys()}')
|
||||
else:
|
||||
net.modules[key] = net_module
|
||||
if len(keys_failed_to_match) > 0:
|
||||
shared.log.warning(f'Network load: type=LoRA name="{name}" type={set(network_types)} unmatched={len(keys_failed_to_match)} matched={len(matched_networks)}')
|
||||
if debug:
|
||||
shared.log.debug(f'Network load: type=LoRA name="{name}" unmatched={keys_failed_to_match}')
|
||||
else:
|
||||
shared.log.debug(f'Network load: type=LoRA name="{name}" type={set(network_types)} keys={len(matched_networks)}')
|
||||
if len(matched_networks) == 0:
|
||||
return None
|
||||
lora_cache[name] = net
|
||||
t1 = time.time()
|
||||
net.bundle_embeddings = bundle_embeddings
|
||||
timer['load'] += t1 - t0
|
||||
return net
|
||||
|
||||
|
||||
def load_networks(names, te_multipliers=None, unet_multipliers=None, dyn_dims=None):
|
||||
networks_on_disk: list[network.NetworkOnDisk] = [available_network_aliases.get(name, None) for name in names]
|
||||
if any(x is None for x in networks_on_disk):
|
||||
list_available_networks()
|
||||
networks_on_disk: list[network.NetworkOnDisk] = [available_network_aliases.get(name, None) for name in names]
|
||||
failed_to_load_networks = []
|
||||
recompile_model = False
|
||||
if shared.compiled_model_state is not None and shared.compiled_model_state.is_compiled:
|
||||
if len(names) == len(shared.compiled_model_state.lora_model):
|
||||
for i, name in enumerate(names):
|
||||
if shared.compiled_model_state.lora_model[i] != f"{name}:{te_multipliers[i] if te_multipliers else shared.opts.extra_networks_default_multiplier}":
|
||||
recompile_model = True
|
||||
shared.compiled_model_state.lora_model = []
|
||||
break
|
||||
if not recompile_model:
|
||||
if len(loaded_networks) > 0 and debug:
|
||||
shared.log.debug('Model Compile: Skipping LoRa loading')
|
||||
return
|
||||
else:
|
||||
recompile_model = True
|
||||
shared.compiled_model_state.lora_model = []
|
||||
if recompile_model:
|
||||
backup_cuda_compile = shared.opts.cuda_compile
|
||||
sd_models.unload_model_weights(op='model')
|
||||
shared.opts.cuda_compile = []
|
||||
sd_models.reload_model_weights(op='model')
|
||||
shared.opts.cuda_compile = backup_cuda_compile
|
||||
|
||||
loaded_networks.clear()
|
||||
diffuser_loaded.clear()
|
||||
diffuser_scales.clear()
|
||||
|
||||
for i, (network_on_disk, name) in enumerate(zip(networks_on_disk, names)):
|
||||
net = None
|
||||
if network_on_disk is not None:
|
||||
shorthash = getattr(network_on_disk, 'shorthash', '').lower()
|
||||
if debug:
|
||||
shared.log.debug(f'Network load: type=LoRA name="{name}" file="{network_on_disk.filename}" hash="{shorthash}"')
|
||||
try:
|
||||
if recompile_model:
|
||||
shared.compiled_model_state.lora_model.append(f"{name}:{te_multipliers[i] if te_multipliers else shared.opts.extra_networks_default_multiplier}")
|
||||
if shared.native and (shared.opts.lora_force_diffusers or network_overrides.check_override(shorthash)): # OpenVINO only works with Diffusers LoRa loading
|
||||
net = load_diffusers(name, network_on_disk, lora_scale=te_multipliers[i] if te_multipliers else shared.opts.extra_networks_default_multiplier)
|
||||
else:
|
||||
net = load_network(name, network_on_disk)
|
||||
if net is not None:
|
||||
net.mentioned_name = name
|
||||
network_on_disk.read_hash()
|
||||
except Exception as e:
|
||||
shared.log.error(f'Network load: type=LoRA file="{network_on_disk.filename}" {e}')
|
||||
if debug:
|
||||
errors.display(e, 'LoRA')
|
||||
continue
|
||||
if net is None:
|
||||
failed_to_load_networks.append(name)
|
||||
shared.log.error(f'Network load: type=LoRA name="{name}" detected={network_on_disk.sd_version if network_on_disk is not None else None} failed')
|
||||
continue
|
||||
if shared.native:
|
||||
shared.sd_model.embedding_db.load_diffusers_embedding(None, net.bundle_embeddings)
|
||||
net.te_multiplier = te_multipliers[i] if te_multipliers else shared.opts.extra_networks_default_multiplier
|
||||
net.unet_multiplier = unet_multipliers[i] if unet_multipliers else shared.opts.extra_networks_default_multiplier
|
||||
net.dyn_dim = dyn_dims[i] if dyn_dims else shared.opts.extra_networks_default_multiplier
|
||||
loaded_networks.append(net)
|
||||
|
||||
while len(lora_cache) > shared.opts.lora_in_memory_limit:
|
||||
name = next(iter(lora_cache))
|
||||
lora_cache.pop(name, None)
|
||||
|
||||
if len(diffuser_loaded) > 0:
|
||||
shared.log.debug(f'Network load: type=LoRA loaded={diffuser_loaded} available={shared.sd_model.get_list_adapters()} active={shared.sd_model.get_active_adapters()} scales={diffuser_scales}')
|
||||
try:
|
||||
shared.sd_model.set_adapters(adapter_names=diffuser_loaded, adapter_weights=diffuser_scales)
|
||||
if shared.opts.lora_fuse_diffusers:
|
||||
shared.sd_model.fuse_lora(adapter_names=diffuser_loaded, lora_scale=1.0, fuse_unet=True, fuse_text_encoder=True) # fuse uses fixed scale since later apply does the scaling
|
||||
shared.sd_model.unload_lora_weights()
|
||||
except Exception as e:
|
||||
shared.log.error(f'Network load: type=LoRA {e}')
|
||||
if debug:
|
||||
errors.display(e, 'LoRA')
|
||||
|
||||
if len(loaded_networks) > 0 and debug:
|
||||
shared.log.debug(f'Network load: type=LoRA loaded={len(loaded_networks)} cache={list(lora_cache)}')
|
||||
|
||||
devices.torch_gc()
|
||||
|
||||
if recompile_model:
|
||||
shared.log.info("Network load: type=LoRA recompiling model")
|
||||
backup_lora_model = shared.compiled_model_state.lora_model
|
||||
if 'Model' in shared.opts.cuda_compile:
|
||||
shared.sd_model = sd_models_compile.compile_diffusers(shared.sd_model)
|
||||
|
||||
shared.compiled_model_state.lora_model = backup_lora_model
|
||||
if shared.opts.diffusers_offload_mode == "balanced":
|
||||
sd_models.apply_balanced_offload(shared.sd_model)
|
||||
|
||||
|
||||
def network_restore_weights_from_backup(self: Union[torch.nn.Conv2d, torch.nn.Linear, torch.nn.GroupNorm, torch.nn.LayerNorm, torch.nn.MultiheadAttention, diffusers.models.lora.LoRACompatibleLinear, diffusers.models.lora.LoRACompatibleConv]):
|
||||
t0 = time.time()
|
||||
weights_backup = getattr(self, "network_weights_backup", None)
|
||||
bias_backup = getattr(self, "network_bias_backup", None)
|
||||
if weights_backup is None and bias_backup is None:
|
||||
t1 = time.time()
|
||||
timer['restore'] += t1 - t0
|
||||
return
|
||||
# if debug:
|
||||
# shared.log.debug('LoRA restore weights')
|
||||
if weights_backup is not None:
|
||||
if isinstance(self, torch.nn.MultiheadAttention):
|
||||
self.in_proj_weight.copy_(weights_backup[0])
|
||||
self.out_proj.weight.copy_(weights_backup[1])
|
||||
elif hasattr(self, "qweight") and hasattr(self, "freeze"):
|
||||
self.weight = torch.nn.Parameter(weights_backup.to(self.weight.device, copy=True))
|
||||
self.freeze()
|
||||
elif getattr(self, "quant_type", None) in ['nf4', 'fp4']:
|
||||
bnb = model_quant.load_bnb('Network load: type=LoRA', silent=True)
|
||||
if bnb is not None:
|
||||
device = self.weight.device
|
||||
self.weight = bnb.nn.Params4bit(weights_backup, quant_state=self.quant_state, quant_type=self.quant_type, blocksize=self.blocksize)
|
||||
self.weight.to(device)
|
||||
else:
|
||||
self.weight.copy_(weights_backup)
|
||||
else:
|
||||
self.weight.copy_(weights_backup)
|
||||
if bias_backup is not None:
|
||||
if isinstance(self, torch.nn.MultiheadAttention):
|
||||
self.out_proj.bias.copy_(bias_backup)
|
||||
else:
|
||||
self.bias.copy_(bias_backup)
|
||||
else:
|
||||
if isinstance(self, torch.nn.MultiheadAttention):
|
||||
self.out_proj.bias = None
|
||||
else:
|
||||
self.bias = None
|
||||
t1 = time.time()
|
||||
timer['restore'] += t1 - t0
|
||||
|
||||
|
||||
def maybe_backup_weights(self: Union[torch.nn.Conv2d, torch.nn.Linear, torch.nn.GroupNorm, torch.nn.LayerNorm, torch.nn.MultiheadAttention, diffusers.models.lora.LoRACompatibleLinear, diffusers.models.lora.LoRACompatibleConv], wanted_names, current_names): # pylint: disable=W0613
|
||||
weights_backup = getattr(self, "network_weights_backup", None)
|
||||
if weights_backup is None and wanted_names != (): # pylint: disable=C1803
|
||||
if isinstance(self, torch.nn.MultiheadAttention):
|
||||
weights_backup = (self.in_proj_weight.clone().to(devices.cpu), self.out_proj.weight.clone().to(devices.cpu))
|
||||
elif getattr(self.weight, "quant_type", None) in ['nf4', 'fp4']:
|
||||
bnb = model_quant.load_bnb('Network load: type=LoRA', silent=True)
|
||||
if bnb is not None:
|
||||
with devices.inference_context():
|
||||
weights_backup = bnb.functional.dequantize_4bit(self.weight, quant_state=self.weight.quant_state, quant_type=self.weight.quant_type, blocksize=self.weight.blocksize,).to(devices.cpu)
|
||||
self.quant_state = self.weight.quant_state
|
||||
self.quant_type = self.weight.quant_type
|
||||
self.blocksize = self.weight.blocksize
|
||||
else:
|
||||
weights_backup = self.weight.clone().to(devices.cpu)
|
||||
else:
|
||||
weights_backup = self.weight.clone().to(devices.cpu)
|
||||
self.network_weights_backup = weights_backup
|
||||
bias_backup = getattr(self, "network_bias_backup", None)
|
||||
if bias_backup is None:
|
||||
if isinstance(self, torch.nn.MultiheadAttention) and self.out_proj.bias is not None:
|
||||
bias_backup = self.out_proj.bias.clone().to(devices.cpu)
|
||||
elif getattr(self, 'bias', None) is not None:
|
||||
bias_backup = self.bias.clone().to(devices.cpu)
|
||||
else:
|
||||
bias_backup = None
|
||||
self.network_bias_backup = bias_backup
|
||||
|
||||
|
||||
def network_apply_weights(self: Union[torch.nn.Conv2d, torch.nn.Linear, torch.nn.GroupNorm, torch.nn.LayerNorm, torch.nn.MultiheadAttention, diffusers.models.lora.LoRACompatibleLinear, diffusers.models.lora.LoRACompatibleConv]):
|
||||
"""
|
||||
Applies the currently selected set of networks to the weights of torch layer self.
|
||||
If weights already have this particular set of networks applied, does nothing.
|
||||
If not, restores orginal weights from backup and alters weights according to networks.
|
||||
"""
|
||||
network_layer_name = getattr(self, 'network_layer_name', None)
|
||||
if network_layer_name is None:
|
||||
return
|
||||
t0 = time.time()
|
||||
current_names = getattr(self, "network_current_names", ())
|
||||
wanted_names = tuple((x.name, x.te_multiplier, x.unet_multiplier, x.dyn_dim) for x in loaded_networks)
|
||||
if any([net.modules.get(network_layer_name, None) for net in loaded_networks]): # noqa: C419 # pylint: disable=R1729
|
||||
maybe_backup_weights(self, wanted_names, current_names)
|
||||
if current_names != wanted_names:
|
||||
network_restore_weights_from_backup(self)
|
||||
for net in loaded_networks:
|
||||
# default workflow where module is known and has weights
|
||||
module = net.modules.get(network_layer_name, None)
|
||||
if module is not None and hasattr(self, 'weight'):
|
||||
try:
|
||||
with devices.inference_context():
|
||||
weight = self.weight # calculate quant weights once
|
||||
updown, ex_bias = module.calc_updown(weight)
|
||||
if len(weight.shape) == 4 and weight.shape[1] == 9:
|
||||
# inpainting model. zero pad updown to make channel[1] 4 to 9
|
||||
updown = torch.nn.functional.pad(updown, (0, 0, 0, 0, 0, 5)) # pylint: disable=not-callable
|
||||
if getattr(self.weight, "quant_type", None) in ['nf4', 'fp4']: # or self.weight.numel() != updown.numel():
|
||||
bnb = model_quant.load_bnb('Network load: type=LoRA', silent=True)
|
||||
if bnb is not None:
|
||||
device = self.weight.device
|
||||
weight = bnb.functional.dequantize_4bit(self.weight, quant_state=self.weight.quant_state, quant_type=self.weight.quant_type, blocksize=self.weight.blocksize)
|
||||
self.weight = bnb.nn.Params4bit(weight + updown, quant_state=self.quant_state, quant_type=shared.opts.lora_quant.lower(), blocksize=self.blocksize)
|
||||
self.weight.to(device)
|
||||
else:
|
||||
self.weight = torch.nn.Parameter(weight + updown)
|
||||
else:
|
||||
self.weight = torch.nn.Parameter(weight + updown)
|
||||
if hasattr(self, "qweight") and hasattr(self, "freeze"):
|
||||
self.freeze()
|
||||
if ex_bias is not None and hasattr(self, 'bias'):
|
||||
if self.bias is None:
|
||||
self.bias = torch.nn.Parameter(ex_bias)
|
||||
else:
|
||||
self.bias += ex_bias
|
||||
except RuntimeError as e:
|
||||
extra_network_lora.errors[net.name] = extra_network_lora.errors.get(net.name, 0) + 1
|
||||
if debug:
|
||||
module_name = net.modules.get(network_layer_name, None)
|
||||
shared.log.error(f'LoRA apply weight name="{net.name}" module="{module_name}" layer="{network_layer_name}" {e}')
|
||||
errors.display(e, 'LoRA')
|
||||
raise RuntimeError('LoRA apply weight') from e
|
||||
continue
|
||||
# alternative workflow looking at _*_proj layers
|
||||
module_q = net.modules.get(network_layer_name + "_q_proj", None)
|
||||
module_k = net.modules.get(network_layer_name + "_k_proj", None)
|
||||
module_v = net.modules.get(network_layer_name + "_v_proj", None)
|
||||
module_out = net.modules.get(network_layer_name + "_out_proj", None)
|
||||
if isinstance(self, torch.nn.MultiheadAttention) and module_q and module_k and module_v and module_out:
|
||||
try:
|
||||
with devices.inference_context():
|
||||
updown_q, _ = module_q.calc_updown(self.in_proj_weight)
|
||||
updown_k, _ = module_k.calc_updown(self.in_proj_weight)
|
||||
updown_v, _ = module_v.calc_updown(self.in_proj_weight)
|
||||
updown_qkv = torch.vstack([updown_q, updown_k, updown_v])
|
||||
updown_out, ex_bias = module_out.calc_updown(self.out_proj.weight)
|
||||
self.in_proj_weight += updown_qkv
|
||||
self.out_proj.weight += updown_out
|
||||
if ex_bias is not None:
|
||||
if self.out_proj.bias is None:
|
||||
self.out_proj.bias = torch.nn.Parameter(ex_bias)
|
||||
else:
|
||||
self.out_proj.bias += ex_bias
|
||||
except RuntimeError as e:
|
||||
if debug:
|
||||
shared.log.debug(f'LoRA network="{net.name}" layer="{network_layer_name}" {e}')
|
||||
extra_network_lora.errors[net.name] = extra_network_lora.errors.get(net.name, 0) + 1
|
||||
continue
|
||||
if module is None:
|
||||
continue
|
||||
shared.log.warning(f'LoRA network="{net.name}" layer="{network_layer_name}" unsupported operation')
|
||||
extra_network_lora.errors[net.name] = extra_network_lora.errors.get(net.name, 0) + 1
|
||||
self.network_current_names = wanted_names
|
||||
t1 = time.time()
|
||||
timer['apply'] += t1 - t0
|
||||
|
||||
|
||||
def network_forward(module, input, original_forward): # pylint: disable=W0622
|
||||
"""
|
||||
Old way of applying Lora by executing operations during layer's forward.
|
||||
Stacking many loras this way results in big performance degradation.
|
||||
"""
|
||||
if len(loaded_networks) == 0:
|
||||
return original_forward(module, input)
|
||||
input = devices.cond_cast_unet(input)
|
||||
network_restore_weights_from_backup(module)
|
||||
network_reset_cached_weight(module)
|
||||
y = original_forward(module, input)
|
||||
network_layer_name = getattr(module, 'network_layer_name', None)
|
||||
for lora in loaded_networks:
|
||||
module = lora.modules.get(network_layer_name, None)
|
||||
if module is None:
|
||||
continue
|
||||
y = module.forward(input, y)
|
||||
return y
|
||||
|
||||
|
||||
def network_reset_cached_weight(self: Union[torch.nn.Conv2d, torch.nn.Linear]):
|
||||
self.network_current_names = ()
|
||||
self.network_weights_backup = None
|
||||
|
||||
|
||||
def network_Linear_forward(self, input): # pylint: disable=W0622
|
||||
network_apply_weights(self)
|
||||
return originals.Linear_forward(self, input)
|
||||
|
||||
|
||||
def network_QLinear_forward(self, input): # pylint: disable=W0622
|
||||
network_apply_weights(self)
|
||||
return torch.nn.functional.linear(input, self.qweight, bias=self.bias)
|
||||
|
||||
|
||||
def network_Linear_load_state_dict(self, *args, **kwargs):
|
||||
network_reset_cached_weight(self)
|
||||
return originals.Linear_load_state_dict(self, *args, **kwargs)
|
||||
|
||||
|
||||
def network_Linear4bit_forward(self, input): # pylint: disable=W0622
|
||||
network_apply_weights(self)
|
||||
return originals.Linear4bit_forward(self, input)
|
||||
#
|
||||
# def network_Linear4bit_load_state_dict(self, *args, **kwargs):
|
||||
# network_reset_cached_weight(self)
|
||||
# return originals.Linear4bit_load_state_dict(self, *args, **kwargs)
|
||||
|
||||
def network_Conv2d_forward(self, input): # pylint: disable=W0622
|
||||
network_apply_weights(self)
|
||||
return originals.Conv2d_forward(self, input)
|
||||
|
||||
|
||||
def network_QConv2d_forward(self, input): # pylint: disable=W0622
|
||||
network_apply_weights(self)
|
||||
return self._conv_forward(input, self.qweight, self.bias) # pylint: disable=protected-access
|
||||
|
||||
|
||||
def network_Conv2d_load_state_dict(self, *args, **kwargs):
|
||||
network_reset_cached_weight(self)
|
||||
return originals.Conv2d_load_state_dict(self, *args, **kwargs)
|
||||
|
||||
|
||||
def network_GroupNorm_forward(self, input): # pylint: disable=W0622
|
||||
network_apply_weights(self)
|
||||
return originals.GroupNorm_forward(self, input)
|
||||
|
||||
|
||||
def network_GroupNorm_load_state_dict(self, *args, **kwargs):
|
||||
network_reset_cached_weight(self)
|
||||
return originals.GroupNorm_load_state_dict(self, *args, **kwargs)
|
||||
|
||||
|
||||
def network_LayerNorm_forward(self, input): # pylint: disable=W0622
|
||||
network_apply_weights(self)
|
||||
return originals.LayerNorm_forward(self, input)
|
||||
|
||||
|
||||
def network_LayerNorm_load_state_dict(self, *args, **kwargs):
|
||||
network_reset_cached_weight(self)
|
||||
return originals.LayerNorm_load_state_dict(self, *args, **kwargs)
|
||||
|
||||
|
||||
def network_MultiheadAttention_forward(self, *args, **kwargs):
|
||||
network_apply_weights(self)
|
||||
return originals.MultiheadAttention_forward(self, *args, **kwargs)
|
||||
|
||||
|
||||
def network_MultiheadAttention_load_state_dict(self, *args, **kwargs):
|
||||
network_reset_cached_weight(self)
|
||||
return originals.MultiheadAttention_load_state_dict(self, *args, **kwargs)
|
||||
|
||||
|
||||
def list_available_networks():
|
||||
t0 = time.time()
|
||||
available_networks.clear()
|
||||
available_network_aliases.clear()
|
||||
forbidden_network_aliases.clear()
|
||||
available_network_hash_lookup.clear()
|
||||
forbidden_network_aliases.update({"none": 1, "Addams": 1})
|
||||
if not os.path.exists(shared.cmd_opts.lora_dir):
|
||||
shared.log.warning(f'LoRA directory not found: path="{shared.cmd_opts.lora_dir}"')
|
||||
|
||||
def add_network(filename):
|
||||
if not os.path.isfile(filename):
|
||||
return
|
||||
name = os.path.splitext(os.path.basename(filename))[0]
|
||||
name = name.replace('.', '_')
|
||||
try:
|
||||
entry = network.NetworkOnDisk(name, filename)
|
||||
available_networks[entry.name] = entry
|
||||
if entry.alias in available_network_aliases:
|
||||
forbidden_network_aliases[entry.alias.lower()] = 1
|
||||
if shared.opts.lora_preferred_name == 'filename':
|
||||
available_network_aliases[entry.name] = entry
|
||||
else:
|
||||
available_network_aliases[entry.alias] = entry
|
||||
if entry.shorthash:
|
||||
available_network_hash_lookup[entry.shorthash] = entry
|
||||
except OSError as e: # should catch FileNotFoundError and PermissionError etc.
|
||||
shared.log.error(f'LoRA: filename="{filename}" {e}')
|
||||
|
||||
candidates = list(files_cache.list_files(shared.cmd_opts.lora_dir, ext_filter=[".pt", ".ckpt", ".safetensors"]))
|
||||
with concurrent.futures.ThreadPoolExecutor(max_workers=shared.max_workers) as executor:
|
||||
for fn in candidates:
|
||||
executor.submit(add_network, fn)
|
||||
t1 = time.time()
|
||||
shared.log.info(f'Available LoRAs: path="{shared.cmd_opts.lora_dir}" items={len(available_networks)} folders={len(forbidden_network_aliases)} time={t1 - t0:.2f}')
|
||||
|
||||
|
||||
def infotext_pasted(infotext, params): # pylint: disable=W0613
|
||||
if "AddNet Module 1" in [x[1] for x in scripts_manager.scripts_txt2img.infotext_fields]:
|
||||
return # if the other extension is active, it will handle those fields, no need to do anything
|
||||
added = []
|
||||
for k in params:
|
||||
if not k.startswith("AddNet Model "):
|
||||
continue
|
||||
num = k[13:]
|
||||
if params.get("AddNet Module " + num) != "LoRA":
|
||||
continue
|
||||
name = params.get("AddNet Model " + num)
|
||||
if name is None:
|
||||
continue
|
||||
m = re_network_name.match(name)
|
||||
if m:
|
||||
name = m.group(1)
|
||||
multiplier = params.get("AddNet Weight A " + num, "1.0")
|
||||
added.append(f"<lora:{name}:{multiplier}>")
|
||||
if added:
|
||||
params["Prompt"] += "\n" + "".join(added)
|
||||
|
||||
|
||||
list_available_networks()
|
||||
@@ -1,66 +0,0 @@
|
||||
import re
|
||||
import networks
|
||||
import lora # pylint: disable=unused-import
|
||||
from lora_extract import create_ui
|
||||
from network import NetworkOnDisk
|
||||
from ui_extra_networks_lora import ExtraNetworksPageLora
|
||||
from extra_networks_lora import ExtraNetworkLora
|
||||
from modules import script_callbacks, extra_networks, ui_extra_networks, ui_models, shared # pylint: disable=unused-import
|
||||
|
||||
|
||||
re_lora = re.compile("<lora:([^:]+):")
|
||||
|
||||
|
||||
def before_ui():
|
||||
ui_extra_networks.register_page(ExtraNetworksPageLora())
|
||||
networks.extra_network_lora = ExtraNetworkLora()
|
||||
extra_networks.register_extra_network(networks.extra_network_lora)
|
||||
ui_models.extra_ui.append(create_ui)
|
||||
|
||||
|
||||
def create_lora_json(obj: NetworkOnDisk):
|
||||
return {
|
||||
"name": obj.name,
|
||||
"alias": obj.alias,
|
||||
"path": obj.filename,
|
||||
"metadata": obj.metadata,
|
||||
}
|
||||
|
||||
|
||||
def api_networks(_, app):
|
||||
@app.get("/sdapi/v1/loras")
|
||||
async def get_loras():
|
||||
return [create_lora_json(obj) for obj in networks.available_networks.values()]
|
||||
|
||||
@app.post("/sdapi/v1/refresh-loras")
|
||||
async def refresh_loras():
|
||||
return networks.list_available_networks()
|
||||
|
||||
|
||||
def infotext_pasted(infotext, d): # pylint: disable=unused-argument
|
||||
hashes = d.get("Lora hashes", None)
|
||||
if hashes is None:
|
||||
return
|
||||
|
||||
def network_replacement(m):
|
||||
alias = m.group(1)
|
||||
shorthash = hashes.get(alias)
|
||||
if shorthash is None:
|
||||
return m.group(0)
|
||||
network_on_disk = networks.available_network_hash_lookup.get(shorthash)
|
||||
if network_on_disk is None:
|
||||
return m.group(0)
|
||||
return f'<lora:{network_on_disk.get_alias()}:'
|
||||
|
||||
hashes = [x.strip().split(':', 1) for x in hashes.split(",")]
|
||||
hashes = {x[0].strip().replace(",", ""): x[1].strip() for x in hashes}
|
||||
d["Prompt"] = re.sub(re_lora, network_replacement, d["Prompt"])
|
||||
|
||||
|
||||
if shared.opts.lora_legacy:
|
||||
shared.log.debug('Register network: type=LoRA method=legacy')
|
||||
script_callbacks.on_app_started(api_networks)
|
||||
script_callbacks.on_before_ui(before_ui)
|
||||
script_callbacks.on_model_loaded(networks.assign_network_names_to_compvis_modules)
|
||||
script_callbacks.on_infotext_pasted(networks.infotext_pasted)
|
||||
script_callbacks.on_infotext_pasted(infotext_pasted)
|
||||
@@ -1,123 +0,0 @@
|
||||
import os
|
||||
import json
|
||||
import concurrent
|
||||
import networks
|
||||
from modules import shared, ui_extra_networks
|
||||
|
||||
|
||||
debug = os.environ.get('SD_LORA_DEBUG', None) is not None
|
||||
|
||||
|
||||
class ExtraNetworksPageLora(ui_extra_networks.ExtraNetworksPage):
|
||||
def __init__(self):
|
||||
super().__init__('Lora')
|
||||
self.list_time = 0
|
||||
shared.log.warning('Networks: type=lora method=legacy')
|
||||
|
||||
def refresh(self):
|
||||
networks.list_available_networks()
|
||||
|
||||
def get_tags(self, l, info):
|
||||
tags = {}
|
||||
try:
|
||||
if l.metadata is not None:
|
||||
modelspec_tags = l.metadata.get('modelspec.tags', {})
|
||||
possible_tags = l.metadata.get('ss_tag_frequency', {}) # tags from model metedata
|
||||
if isinstance(possible_tags, str):
|
||||
possible_tags = {}
|
||||
if isinstance(modelspec_tags, str):
|
||||
modelspec_tags = {}
|
||||
if len(list(modelspec_tags)) > 0:
|
||||
possible_tags.update(modelspec_tags)
|
||||
for k, v in possible_tags.items():
|
||||
words = k.split('_', 1) if '_' in k else [v, k]
|
||||
words = [str(w).replace('.json', '') for w in words]
|
||||
if words[0] == '{}':
|
||||
words[0] = 0
|
||||
tag = ' '.join(words[1:]).lower()
|
||||
tags[tag] = words[0]
|
||||
|
||||
def find_version():
|
||||
found_versions = []
|
||||
current_hash = l.hash[:8].upper()
|
||||
all_versions = info.get('modelVersions', [])
|
||||
for v in info.get('modelVersions', []):
|
||||
for f in v.get('files', []):
|
||||
if any(h.startswith(current_hash) for h in f.get('hashes', {}).values()):
|
||||
found_versions.append(v)
|
||||
if len(found_versions) == 0:
|
||||
found_versions = all_versions
|
||||
return found_versions
|
||||
|
||||
for v in find_version(): # trigger words from info json
|
||||
possible_tags = v.get('trainedWords', [])
|
||||
if isinstance(possible_tags, list):
|
||||
for tag_str in possible_tags:
|
||||
for tag in tag_str.split(','):
|
||||
tag = tag.strip().lower()
|
||||
if tag not in tags:
|
||||
tags[tag] = 0
|
||||
|
||||
possible_tags = info.get('tags', []) # tags from info json
|
||||
if not isinstance(possible_tags, list):
|
||||
possible_tags = list(possible_tags.values())
|
||||
for tag in possible_tags:
|
||||
tag = tag.strip().lower()
|
||||
if tag not in tags:
|
||||
tags[tag] = 0
|
||||
except Exception:
|
||||
pass
|
||||
bad_chars = [';', ':', '<', ">", "*", '?', '\'', '\"', '(', ')', '[', ']', '{', '}', '\\', '/']
|
||||
clean_tags = {}
|
||||
for k, v in tags.items():
|
||||
tag = ''.join(i for i in k if i not in bad_chars).strip()
|
||||
clean_tags[tag] = v
|
||||
|
||||
clean_tags.pop('img', None)
|
||||
clean_tags.pop('dataset', None)
|
||||
return clean_tags
|
||||
|
||||
def create_item(self, name):
|
||||
l = networks.available_networks.get(name)
|
||||
if l is None:
|
||||
shared.log.warning(f'Networks: type=lora registered={len(list(networks.available_networks))} file="{name}" not registered')
|
||||
return None
|
||||
try:
|
||||
# path, _ext = os.path.splitext(l.filename)
|
||||
name = os.path.splitext(os.path.relpath(l.filename, shared.cmd_opts.lora_dir))[0]
|
||||
item = {
|
||||
"type": 'Lora',
|
||||
"name": name,
|
||||
"filename": l.filename,
|
||||
"hash": l.shorthash,
|
||||
"prompt": json.dumps(f" <lora:{l.get_alias()}:{shared.opts.extra_networks_default_multiplier}>"),
|
||||
"metadata": json.dumps(l.metadata, indent=4) if l.metadata else None,
|
||||
"mtime": os.path.getmtime(l.filename),
|
||||
"size": os.path.getsize(l.filename),
|
||||
"version": l.sd_version,
|
||||
}
|
||||
info = self.find_info(l.filename)
|
||||
item["info"] = info
|
||||
item["description"] = self.find_description(l.filename, info) # use existing info instead of double-read
|
||||
item["tags"] = self.get_tags(l, info)
|
||||
return item
|
||||
except Exception as e:
|
||||
shared.log.error(f'Networks: type=lora file="{name}" {e}')
|
||||
if debug:
|
||||
from modules import errors
|
||||
errors.display('e', 'Lora')
|
||||
return None
|
||||
|
||||
def list_items(self):
|
||||
items = []
|
||||
with concurrent.futures.ThreadPoolExecutor(max_workers=shared.max_workers) as executor:
|
||||
future_items = {executor.submit(self.create_item, net): net for net in networks.available_networks}
|
||||
for future in concurrent.futures.as_completed(future_items):
|
||||
item = future.result()
|
||||
if item is not None:
|
||||
items.append(item)
|
||||
self.update_all_previews(items)
|
||||
return items
|
||||
|
||||
def allowed_directories_for_previews(self):
|
||||
return [shared.cmd_opts.lora_dir, shared.cmd_opts.lyco_dir]
|
||||
Submodule extensions-builtin/sd-extension-system-info updated: 539625f289...0760f3bcee
@@ -169,7 +169,7 @@ def clean_server():
|
||||
modules_to_remove = ['webui', 'modules', 'scripts', 'gradio',
|
||||
'onnx', 'torch', 'pytorch', 'lightning', 'tensor', 'diffusers', 'transformers', 'tokenize', 'safetensors', 'gguf', 'accelerate', 'peft', 'triton', 'huggingface',
|
||||
'PIL', 'cv2', 'timm', 'numpy', 'scipy', 'sympy', 'sklearn', 'skimage', 'sqlalchemy', 'flash_attn', 'bitsandbytes', 'xformers', 'matplotlib', 'optimum', 'pandas', 'pi', 'git', 're', 'altair',
|
||||
'framepack', 'nudenet', 'agent_scheduler', 'basicsr', 'k_diffusion', 'gfpgan', 'war',
|
||||
'framepack', 'nudenet', 'agent_scheduler', 'basicsr', 'gfpgan', 'war',
|
||||
'fastapi', 'urllib', 'uvicorn', 'web', 'http', 'google', 'starlette', 'socket']
|
||||
removed_removed = []
|
||||
for module_loaded in modules_loaded:
|
||||
|
||||
Binary file not shown.
+3
-5
@@ -79,7 +79,6 @@ class Api:
|
||||
self.add_api_route("/sdapi/v1/upscalers", endpoints.get_upscalers, methods=["GET"], response_model=List[models.ItemUpscaler])
|
||||
self.add_api_route("/sdapi/v1/sd-models", endpoints.get_sd_models, methods=["GET"], response_model=List[models.ItemModel])
|
||||
self.add_api_route("/sdapi/v1/controlnets", endpoints.get_controlnets, methods=["GET"], response_model=List[str])
|
||||
self.add_api_route("/sdapi/v1/hypernetworks", endpoints.get_hypernetworks, methods=["GET"], response_model=List[models.ItemHypernetwork])
|
||||
self.add_api_route("/sdapi/v1/face-restorers", endpoints.get_detailers, methods=["GET"], response_model=List[models.ItemDetailer])
|
||||
self.add_api_route("/sdapi/v1/prompt-styles", endpoints.get_prompt_styles, methods=["GET"], response_model=List[models.ItemStyle])
|
||||
self.add_api_route("/sdapi/v1/embeddings", endpoints.get_embeddings, methods=["GET"], response_model=models.ResEmbeddings)
|
||||
@@ -100,10 +99,9 @@ class Api:
|
||||
self.add_api_route("/sdapi/v1/latents", endpoints.post_latent_history, methods=["POST"], response_model=int)
|
||||
|
||||
# lora api
|
||||
if shared.native:
|
||||
self.add_api_route("/sdapi/v1/lora", loras.get_lora, methods=["GET"], response_model=dict)
|
||||
self.add_api_route("/sdapi/v1/loras", loras.get_loras, methods=["GET"], response_model=List[dict])
|
||||
self.add_api_route("/sdapi/v1/refresh-loras", loras.post_refresh_loras, methods=["POST"])
|
||||
self.add_api_route("/sdapi/v1/lora", loras.get_lora, methods=["GET"], response_model=dict)
|
||||
self.add_api_route("/sdapi/v1/loras", loras.get_loras, methods=["GET"], response_model=List[dict])
|
||||
self.add_api_route("/sdapi/v1/refresh-loras", loras.post_refresh_loras, methods=["POST"])
|
||||
|
||||
# gallery api
|
||||
gallery.register_api(self.app)
|
||||
|
||||
+11
-24
@@ -17,19 +17,16 @@ def get_upscalers():
|
||||
return [{"name": upscaler.name, "model_name": upscaler.scaler.model_name, "model_path": upscaler.data_path, "model_url": None, "scale": upscaler.scale} for upscaler in shared.sd_upscalers]
|
||||
|
||||
def get_sd_models():
|
||||
from modules import sd_checkpoint, sd_models_config
|
||||
from modules import sd_checkpoint
|
||||
checkpoints = []
|
||||
for v in sd_checkpoint.checkpoints_list.values():
|
||||
checkpoints.append({"title": v.title, "model_name": v.name, "filename": v.filename, "type": v.type, "hash": v.shorthash, "sha256": v.sha256, "config": sd_models_config.find_checkpoint_config_near_filename(v)})
|
||||
checkpoints.append({"title": v.title, "model_name": v.name, "filename": v.filename, "type": v.type, "hash": v.shorthash, "sha256": v.sha256})
|
||||
return checkpoints
|
||||
|
||||
def get_controlnets(model_type: Optional[str] = None):
|
||||
from modules.control.units.controlnet import api_list_models
|
||||
return api_list_models(model_type)
|
||||
|
||||
def get_hypernetworks():
|
||||
return [{"name": name, "path": shared.hypernetworks[name]} for name in shared.hypernetworks]
|
||||
|
||||
def get_detailers():
|
||||
return [{"name":x.name(), "cmd_dir": getattr(x, "cmd_dir", None)} for x in shared.detailers]
|
||||
|
||||
@@ -37,15 +34,10 @@ def get_prompt_styles():
|
||||
return [{ 'name': v.name, 'prompt': v.prompt, 'negative_prompt': v.negative_prompt, 'extra': v.extra, 'filename': v.filename, 'preview': v.preview} for v in shared.prompt_styles.styles.values()]
|
||||
|
||||
def get_embeddings():
|
||||
from modules import sd_hijack
|
||||
db = sd_hijack.model_hijack.embedding_db
|
||||
def convert_embedding(embedding):
|
||||
return {"step": embedding.step, "sd_checkpoint": embedding.sd_checkpoint, "sd_checkpoint_name": embedding.sd_checkpoint_name, "shape": embedding.shape, "vectors": embedding.vectors}
|
||||
|
||||
def convert_embeddings(embeddings):
|
||||
return {embedding.name: convert_embedding(embedding) for embedding in embeddings.values()}
|
||||
|
||||
return {"loaded": convert_embeddings(db.word_embeddings), "skipped": convert_embeddings(db.skipped_embeddings)}
|
||||
db = getattr(shared.sd_model, 'embedding_db', None) if shared.sd_loaded else None
|
||||
if db is None:
|
||||
return models.ResEmbeddings(loaded=[], skipped=[])
|
||||
return models.ResEmbeddings(loaded=list(db.word_embeddings.keys()), skipped=list(db.skipped_embeddings.keys()))
|
||||
|
||||
def get_extra_networks(page: Optional[str] = None, name: Optional[str] = None, filename: Optional[str] = None, title: Optional[str] = None, fullname: Optional[str] = None, hash: Optional[str] = None): # pylint: disable=redefined-builtin
|
||||
res = []
|
||||
@@ -76,21 +68,14 @@ def get_extra_networks(page: Optional[str] = None, name: Optional[str] = None, f
|
||||
|
||||
def get_interrogate():
|
||||
from modules.interrogate.openclip import refresh_clip_models
|
||||
return ['clip', 'deepdanbooru'] + refresh_clip_models()
|
||||
return ['deepdanbooru'] + refresh_clip_models()
|
||||
|
||||
def post_interrogate(req: models.ReqInterrogate):
|
||||
if req.image is None or len(req.image) < 64:
|
||||
raise HTTPException(status_code=404, detail="Image not found")
|
||||
image = helpers.decode_base64_to_image(req.image)
|
||||
image = image.convert('RGB')
|
||||
if req.model == "clip":
|
||||
try:
|
||||
from modules.interrogate import openclip
|
||||
caption = openclip.interrogator.interrogate(image)
|
||||
except Exception as e:
|
||||
caption = str(e)
|
||||
return models.ResInterrogate(caption=caption)
|
||||
elif req.model == "deepdanbooru" or req.model == 'deepbooru':
|
||||
if req.model == "deepdanbooru" or req.model == 'deepbooru':
|
||||
from modules.interrogate import deepbooru
|
||||
caption = deepbooru.model.tag(image)
|
||||
return models.ResInterrogate(caption=caption)
|
||||
@@ -123,8 +108,10 @@ def post_unload_checkpoint():
|
||||
sd_models.unload_model_weights(op='refiner')
|
||||
return {}
|
||||
|
||||
def post_reload_checkpoint():
|
||||
def post_reload_checkpoint(force:bool=False):
|
||||
from modules import sd_models
|
||||
if force:
|
||||
sd_models.unload_model_weights(op='model')
|
||||
sd_models.reload_model_weights()
|
||||
return {}
|
||||
|
||||
|
||||
@@ -262,7 +262,6 @@ class ReqProcess(BaseModel):
|
||||
upscaler_1: str = Field(default="None", title="Main upscaler", description=f"The name of the main upscaler to use, it has to be one of this list: {' , '.join([x.name for x in shared.sd_upscalers])}")
|
||||
upscaler_2: str = Field(default="None", title="Refine upscaler", description=f"The name of the secondary upscaler to use, it has to be one of this list: {' , '.join([x.name for x in shared.sd_upscalers])}")
|
||||
extras_upscaler_2_visibility: float = Field(default=0, title="Refine upscaler visibility", ge=0, le=1, allow_inf_nan=False, description="Sets the visibility of secondary upscaler, values should be between 0 and 1.")
|
||||
upscale_first: bool = Field(default=False, title="Upscale first", description="Should the upscaler run before restoring faces?")
|
||||
|
||||
class ResProcess(BaseModel):
|
||||
html_info: str = Field(title="HTML info", description="A series of HTML tags containing the process info.")
|
||||
@@ -379,10 +378,10 @@ class ResVQA(BaseModel):
|
||||
answer: Optional[str] = Field(default=None, title="Answer", description="The generated answer for the image.")
|
||||
|
||||
class ResTrain(BaseModel):
|
||||
info: str = Field(title="Train info", description="Response string from train embedding or hypernetwork task.")
|
||||
info: str = Field(title="Train info", description="Response string from train embedding task.")
|
||||
|
||||
class ResCreate(BaseModel):
|
||||
info: str = Field(title="Create info", description="Response string from create embedding or hypernetwork task.")
|
||||
info: str = Field(title="Create info", description="Response string from create embedding task.")
|
||||
|
||||
class ResPreprocess(BaseModel):
|
||||
info: str = Field(title="Preprocess info", description="Response string from preprocessing task.")
|
||||
@@ -413,8 +412,8 @@ for key in _options:
|
||||
FlagsModel = create_model("Flags", **flags)
|
||||
|
||||
class ResEmbeddings(BaseModel):
|
||||
loaded: Dict[str, ItemEmbedding] = Field(title="Loaded", description="Embeddings loaded for the current model")
|
||||
skipped: Dict[str, ItemEmbedding] = Field(title="Skipped", description="Embeddings skipped for the current model (likely due to architecture incompatibility)")
|
||||
loaded: list = Field(default=None, title="loaded", description="List of loaded embeddings")
|
||||
skipped: list = Field(default=None, title="skipped", description="List of skipped embeddings")
|
||||
|
||||
class ResMemory(BaseModel):
|
||||
ram: dict = Field(title="RAM", description="System memory stats")
|
||||
|
||||
@@ -16,8 +16,6 @@ def get_motd():
|
||||
ver = shared.get_version()
|
||||
if ver.get('updated', None) is not None:
|
||||
motd = f"version <b>{ver['hash']} {ver['updated']}</b> <span style='color: var(--primary-500)'>{ver['url'].split('/')[-1]}</span><br>"
|
||||
if not shared.native:
|
||||
motd += "<span style='color: orange'>Legacy mode</span><br>"
|
||||
if shared.opts.motd:
|
||||
try:
|
||||
res = requests.get('https://vladmandic.github.io/sdnext/motd', timeout=3)
|
||||
|
||||
@@ -15,8 +15,6 @@ supported = [
|
||||
|
||||
|
||||
def apply(p: processing.StableDiffusionProcessing):
|
||||
if not shared.native:
|
||||
return None
|
||||
if not shared.opts.cfgzero_enabled:
|
||||
return None
|
||||
cls = shared.sd_model.__class__.__name__ if shared.sd_loaded else 'None'
|
||||
|
||||
+1
-6
@@ -11,7 +11,6 @@ parser._optionals = parser.add_argument_group('Other options') # pylint: disable
|
||||
def main_args():
|
||||
# main server args
|
||||
group_config = parser.add_argument_group('Configuration')
|
||||
group_config.add_argument('--backend', type=str, default=os.environ.get("SD_BACKEND", None), choices=['original', 'diffusers'], required=False, help='force model pipeline type')
|
||||
group_config.add_argument("--config", type=str, default=os.environ.get("SD_CONFIG", os.path.join(data_path, 'config.json')), help="Use specific server configuration file, default: %(default)s")
|
||||
group_config.add_argument("--ui-config", type=str, default=os.environ.get("SD_UICONFIG", os.path.join(data_path, 'ui-config.json')), help="Use specific UI configuration file, default: %(default)s")
|
||||
group_config.add_argument("--medvram", default=os.environ.get("SD_MEDVRAM", False), action='store_true', help="Split model stages and keep only active part in VRAM, default: %(default)s")
|
||||
@@ -66,6 +65,7 @@ def main_args():
|
||||
def compatibility_args():
|
||||
# removed args are added here as hidden in fixed format for compatbility reasons
|
||||
group_compat = parser.add_argument_group('Compatibility options')
|
||||
group_compat.add_argument('--backend', type=str, default=os.environ.get("SD_BACKEND", None), choices=['diffusers', 'original'], required=False, help='obsolete')
|
||||
group_compat.add_argument("--allow-code", default=os.environ.get("SD_ALLOWCODE", False), action='store_true', help=argparse.SUPPRESS)
|
||||
group_compat.add_argument("--use-cpu", nargs='+', default=[], type=str.lower, help=argparse.SUPPRESS)
|
||||
group_compat.add_argument("-f", action='store_true', help=argparse.SUPPRESS) # allows running as root; implemented outside of webui
|
||||
@@ -104,7 +104,6 @@ def settings_args(opts, args):
|
||||
group_compat.add_argument("--vae-dir", type=str, help=argparse.SUPPRESS, default=opts.vae_dir)
|
||||
group_compat.add_argument("--embeddings-dir", type=str, help=argparse.SUPPRESS, default=opts.embeddings_dir)
|
||||
group_compat.add_argument("--embeddings-templates-dir", type=str, help=argparse.SUPPRESS, default=opts.embeddings_templates_dir)
|
||||
group_compat.add_argument("--hypernetwork-dir", type=str, help=argparse.SUPPRESS, default=opts.hypernetwork_dir)
|
||||
group_compat.add_argument("--codeformer-models-path", type=str, help=argparse.SUPPRESS, default=opts.codeformer_models_path)
|
||||
group_compat.add_argument("--gfpgan-models-path", type=str, help=argparse.SUPPRESS, default=opts.gfpgan_models_path)
|
||||
group_compat.add_argument("--esrgan-models-path", type=str, help=argparse.SUPPRESS, default=opts.esrgan_models_path)
|
||||
@@ -125,11 +124,7 @@ def settings_args(opts, args):
|
||||
group_compat.add_argument("--sub-quad-kv-chunk-size", help=argparse.SUPPRESS, default=opts.sub_quad_kv_chunk_size)
|
||||
group_compat.add_argument("--sub-quad-chunk-threshold", help=argparse.SUPPRESS, default=opts.sub_quad_chunk_threshold)
|
||||
group_compat.add_argument("--lora-dir", help=argparse.SUPPRESS, default=opts.lora_dir)
|
||||
group_compat.add_argument("--lyco-dir", help=argparse.SUPPRESS, default=opts.lyco_dir)
|
||||
group_compat.add_argument("--embeddings-dir", help=argparse.SUPPRESS, default=opts.embeddings_dir)
|
||||
group_compat.add_argument("--hypernetwork-dir", help=argparse.SUPPRESS, default=opts.hypernetwork_dir)
|
||||
group_compat.add_argument("--lyco-patch-lora", help=argparse.SUPPRESS, action='store_true', default=False)
|
||||
group_compat.add_argument("--lyco-debug", help=argparse.SUPPRESS, action='store_true', default=False)
|
||||
group_compat.add_argument("--enable-console-prompts", help=argparse.SUPPRESS, action='store_true', default=False)
|
||||
group_compat.add_argument("--safe", help=argparse.SUPPRESS, action='store_true', default=False)
|
||||
group_compat.add_argument("--use-xformers", help=argparse.SUPPRESS, action='store_true', default=False)
|
||||
|
||||
@@ -229,7 +229,7 @@ def control_set(kwargs):
|
||||
p_extra_args[k] = v
|
||||
|
||||
|
||||
def control_run(state: str = '',
|
||||
def control_run(state: str = '', # pylint: disable=keyword-arg-before-vararg
|
||||
units: List[unit.Unit] = [], inputs: List[Image.Image] = [], inits: List[Image.Image] = [], mask: Image.Image = None, unit_type: str = None, is_generator: bool = True,
|
||||
input_type: int = 0,
|
||||
prompt: str = '', negative_prompt: str = '', styles: List[str] = [],
|
||||
|
||||
@@ -18,13 +18,10 @@ def register_extra_network(extra_network):
|
||||
def register_default_extra_networks():
|
||||
from modules.ui_extra_networks_styles import ExtraNetworkStyles
|
||||
register_extra_network(ExtraNetworkStyles())
|
||||
if not shared.opts.lora_legacy:
|
||||
from modules.lora import lora_common, extra_networks_lora
|
||||
lora_common.extra_network_lora = extra_networks_lora.ExtraNetworkLora()
|
||||
register_extra_network(lora_common.extra_network_lora)
|
||||
if shared.opts.hypernetwork_enabled:
|
||||
from modules.ui_extra_networks_hypernet import ExtraNetworkHypernet
|
||||
register_extra_network(ExtraNetworkHypernet())
|
||||
|
||||
from modules.lora import lora_common, extra_networks_lora
|
||||
lora_common.extra_network_lora = extra_networks_lora.ExtraNetworkLora()
|
||||
register_extra_network(lora_common.extra_network_lora)
|
||||
|
||||
|
||||
class ExtraNetworkParams:
|
||||
|
||||
+1
-23
@@ -2,14 +2,13 @@ import os
|
||||
import html
|
||||
import json
|
||||
import time
|
||||
import shutil
|
||||
|
||||
from PIL import Image
|
||||
import torch
|
||||
import gradio as gr
|
||||
import safetensors.torch
|
||||
from modules.merging import merge, merge_utils, modules_sdxl
|
||||
from modules import shared, images, sd_models, sd_vae, sd_samplers, sd_models_config, devices
|
||||
from modules import shared, images, sd_models, sd_vae, sd_samplers, devices
|
||||
|
||||
|
||||
def run_pnginfo(image):
|
||||
@@ -24,27 +23,6 @@ def run_pnginfo(image):
|
||||
return '', geninfo, info
|
||||
|
||||
|
||||
def create_config(ckpt_result, config_source, a, b, c):
|
||||
def config(x):
|
||||
res = sd_models_config.find_checkpoint_config_near_filename(x) if x else None
|
||||
return res if res != shared.sd_default_config else None
|
||||
|
||||
if config_source == 0:
|
||||
cfg = config(a) or config(b) or config(c)
|
||||
elif config_source == 1:
|
||||
cfg = config(b)
|
||||
elif config_source == 2:
|
||||
cfg = config(c)
|
||||
else:
|
||||
cfg = None
|
||||
if cfg is None:
|
||||
return
|
||||
filename, _ = os.path.splitext(ckpt_result)
|
||||
checkpoint_filename = filename + ".yaml"
|
||||
shared.log.info("Copying config: {cfg} -> {checkpoint_filename}")
|
||||
shutil.copyfile(cfg, checkpoint_filename)
|
||||
|
||||
|
||||
def to_half(tensor, enable):
|
||||
if enable and tensor.dtype == torch.float:
|
||||
return tensor.half()
|
||||
|
||||
@@ -15,7 +15,7 @@ class Script(scripts_manager.Script):
|
||||
return 'Face: Multiple ID Transfers'
|
||||
|
||||
def show(self, is_img2img):
|
||||
return True if shared.native else False
|
||||
return True
|
||||
|
||||
def load_images(self, files):
|
||||
init_images = []
|
||||
@@ -106,8 +106,6 @@ class Script(scripts_manager.Script):
|
||||
return [mode, gallery, reswapper_model, reswapper_original, ip_model, ip_override, ip_cache, ip_strength, ip_structure, id_strength, id_conditioning, id_cache, pm_model, pm_trigger, pm_strength, pm_start, fs_cache]
|
||||
|
||||
def run(self, p: processing.StableDiffusionProcessing, mode, input_images, reswapper_model, reswapper_original, ip_model, ip_override, ip_cache, ip_strength, ip_structure, id_strength, id_conditioning, id_cache, pm_model, pm_trigger, pm_strength, pm_start, fs_cache): # pylint: disable=arguments-differ, unused-argument
|
||||
if not shared.native:
|
||||
return None
|
||||
if mode == 'None':
|
||||
return None
|
||||
if input_images is None or len(input_images) == 0:
|
||||
|
||||
@@ -71,7 +71,7 @@ def face_id(
|
||||
|
||||
if shared.opts.cuda_compile_backend == 'none':
|
||||
token_merge.apply_token_merging(p.sd_model)
|
||||
sd_hijack_freeu.apply_freeu(p, not shared.native)
|
||||
sd_hijack_freeu.apply_freeu(p)
|
||||
|
||||
script_callbacks.before_process_callback(p)
|
||||
|
||||
|
||||
@@ -6,8 +6,6 @@ from modules.hidiffusion import hidiffusion
|
||||
|
||||
|
||||
def apply(p, model_type):
|
||||
if not shared.native:
|
||||
return
|
||||
if model_type not in ['sd', 'sdxl'] and p.hidiffusion:
|
||||
shared.log.warning(f'HiDiffusion: class={shared.sd_model.__class__.__name__} not supported')
|
||||
return
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,389 +0,0 @@
|
||||
import os
|
||||
import inspect
|
||||
from statistics import stdev, mean
|
||||
from rich import progress
|
||||
import torch
|
||||
from torch import einsum
|
||||
from torch.nn.init import normal_, xavier_normal_, xavier_uniform_, kaiming_normal_, kaiming_uniform_, zeros_
|
||||
from einops import rearrange, repeat
|
||||
from modules import devices, shared, hashes, errors, files_cache
|
||||
|
||||
|
||||
loaded_hypernetworks = []
|
||||
|
||||
class HypernetworkModule(torch.nn.Module):
|
||||
activation_dict = {
|
||||
"linear": torch.nn.Identity,
|
||||
"relu": torch.nn.ReLU,
|
||||
"leakyrelu": torch.nn.LeakyReLU,
|
||||
"elu": torch.nn.ELU,
|
||||
"swish": torch.nn.Hardswish,
|
||||
"tanh": torch.nn.Tanh,
|
||||
"sigmoid": torch.nn.Sigmoid,
|
||||
}
|
||||
activation_dict.update({cls_name.lower(): cls_obj for cls_name, cls_obj in inspect.getmembers(torch.nn.modules.activation) if inspect.isclass(cls_obj) and cls_obj.__module__ == 'torch.nn.modules.activation'})
|
||||
|
||||
def __init__(self, dim, state_dict=None, layer_structure=None, activation_func=None, weight_init='Normal',
|
||||
add_layer_norm=False, activate_output=False, dropout_structure=None):
|
||||
super().__init__()
|
||||
self.multiplier = 1.0
|
||||
assert layer_structure is not None, "layer_structure must not be None"
|
||||
assert layer_structure[0] == 1, "Multiplier Sequence should start with size 1!"
|
||||
assert layer_structure[-1] == 1, "Multiplier Sequence should end with size 1!"
|
||||
linears = []
|
||||
for i in range(len(layer_structure) - 1):
|
||||
# Add a fully-connected layer
|
||||
linears.append(torch.nn.Linear(int(dim * layer_structure[i]), int(dim * layer_structure[i+1])))
|
||||
# Add an activation func except last layer
|
||||
if activation_func == "linear" or activation_func is None or (i >= len(layer_structure) - 2 and not activate_output):
|
||||
pass
|
||||
elif activation_func in self.activation_dict:
|
||||
linears.append(self.activation_dict[activation_func]())
|
||||
else:
|
||||
raise RuntimeError(f'hypernetwork uses an unsupported activation function: {activation_func}')
|
||||
# Add layer normalization
|
||||
if add_layer_norm:
|
||||
linears.append(torch.nn.LayerNorm(int(dim * layer_structure[i+1])))
|
||||
# Everything should be now parsed into dropout structure, and applied here.
|
||||
# Since we only have dropouts after layers, dropout structure should start with 0 and end with 0.
|
||||
if dropout_structure is not None and dropout_structure[i+1] > 0:
|
||||
assert 0 < dropout_structure[i+1] < 1, "Dropout probability should be 0 or float between 0 and 1!"
|
||||
linears.append(torch.nn.Dropout(p=dropout_structure[i+1]))
|
||||
# Code explanation : [1, 2, 1] -> dropout is missing when last_layer_dropout is false. [1, 2, 2, 1] -> [0, 0.3, 0, 0], when its True, [0, 0.3, 0.3, 0].
|
||||
self.linear = torch.nn.Sequential(*linears)
|
||||
if state_dict is not None:
|
||||
self.fix_old_state_dict(state_dict)
|
||||
self.load_state_dict(state_dict)
|
||||
else:
|
||||
for layer in self.linear:
|
||||
if type(layer) == torch.nn.Linear or type(layer) == torch.nn.LayerNorm:
|
||||
w, b = layer.weight.data, layer.bias.data
|
||||
if weight_init == "Normal" or type(layer) == torch.nn.LayerNorm:
|
||||
normal_(w, mean=0.0, std=0.01)
|
||||
normal_(b, mean=0.0, std=0)
|
||||
elif weight_init == 'XavierUniform':
|
||||
xavier_uniform_(w)
|
||||
zeros_(b)
|
||||
elif weight_init == 'XavierNormal':
|
||||
xavier_normal_(w)
|
||||
zeros_(b)
|
||||
elif weight_init == 'KaimingUniform':
|
||||
kaiming_uniform_(w, nonlinearity='leaky_relu' if 'leakyrelu' == activation_func else 'relu')
|
||||
zeros_(b)
|
||||
elif weight_init == 'KaimingNormal':
|
||||
kaiming_normal_(w, nonlinearity='leaky_relu' if 'leakyrelu' == activation_func else 'relu')
|
||||
zeros_(b)
|
||||
else:
|
||||
raise KeyError(f"Key {weight_init} is not defined as initialization!")
|
||||
self.to(devices.device)
|
||||
|
||||
def fix_old_state_dict(self, state_dict):
|
||||
changes = {
|
||||
'linear1.bias': 'linear.0.bias',
|
||||
'linear1.weight': 'linear.0.weight',
|
||||
'linear2.bias': 'linear.1.bias',
|
||||
'linear2.weight': 'linear.1.weight',
|
||||
}
|
||||
for fr, to in changes.items():
|
||||
x = state_dict.get(fr, None)
|
||||
if x is None:
|
||||
continue
|
||||
del state_dict[fr]
|
||||
state_dict[to] = x
|
||||
|
||||
def forward(self, x):
|
||||
return x + self.linear(x) * (self.multiplier if not self.training else 1)
|
||||
|
||||
def trainables(self):
|
||||
layer_structure = []
|
||||
for layer in self.linear:
|
||||
if type(layer) == torch.nn.Linear or type(layer) == torch.nn.LayerNorm:
|
||||
layer_structure += [layer.weight, layer.bias]
|
||||
return layer_structure
|
||||
|
||||
|
||||
#param layer_structure : sequence used for length, use_dropout : controlling boolean, last_layer_dropout : for compatibility check.
|
||||
def parse_dropout_structure(layer_structure, use_dropout, last_layer_dropout):
|
||||
if layer_structure is None:
|
||||
layer_structure = [1, 2, 1]
|
||||
if not use_dropout:
|
||||
return [0] * len(layer_structure)
|
||||
dropout_values = [0]
|
||||
dropout_values.extend([0.3] * (len(layer_structure) - 3))
|
||||
if last_layer_dropout:
|
||||
dropout_values.append(0.3)
|
||||
else:
|
||||
dropout_values.append(0)
|
||||
dropout_values.append(0)
|
||||
return dropout_values
|
||||
|
||||
|
||||
class Hypernetwork:
|
||||
filename = None
|
||||
name = None
|
||||
|
||||
def __init__(self, name=None, enable_sizes=None, layer_structure=None, activation_func=None, weight_init=None, add_layer_norm=False, use_dropout=False, activate_output=False, **kwargs):
|
||||
self.filename = None
|
||||
self.name = name
|
||||
self.layers = {}
|
||||
self.step = 0
|
||||
self.sd_checkpoint = None
|
||||
self.sd_checkpoint_name = None
|
||||
self.layer_structure = layer_structure
|
||||
self.activation_func = activation_func
|
||||
self.weight_init = weight_init
|
||||
self.add_layer_norm = add_layer_norm
|
||||
self.use_dropout = use_dropout
|
||||
self.activate_output = activate_output
|
||||
self.last_layer_dropout = kwargs.get('last_layer_dropout', True)
|
||||
self.dropout_structure = kwargs.get('dropout_structure', None)
|
||||
if self.dropout_structure is None:
|
||||
self.dropout_structure = parse_dropout_structure(self.layer_structure, self.use_dropout, self.last_layer_dropout)
|
||||
self.optimizer_name = None
|
||||
self.optimizer_state_dict = None
|
||||
self.optional_info = None
|
||||
for size in enable_sizes or []:
|
||||
self.layers[size] = (
|
||||
HypernetworkModule(size, None, self.layer_structure, self.activation_func, self.weight_init,
|
||||
self.add_layer_norm, self.activate_output, dropout_structure=self.dropout_structure),
|
||||
HypernetworkModule(size, None, self.layer_structure, self.activation_func, self.weight_init,
|
||||
self.add_layer_norm, self.activate_output, dropout_structure=self.dropout_structure),
|
||||
)
|
||||
self.eval()
|
||||
|
||||
def weights(self):
|
||||
res = []
|
||||
for layers in self.layers.values():
|
||||
for layer in layers:
|
||||
res += layer.parameters()
|
||||
return res
|
||||
|
||||
def train(self, mode=True):
|
||||
for layers in self.layers.values():
|
||||
for layer in layers:
|
||||
layer.train(mode=mode)
|
||||
for param in layer.parameters():
|
||||
param.requires_grad = mode
|
||||
|
||||
def to(self, device):
|
||||
for layers in self.layers.values():
|
||||
for layer in layers:
|
||||
layer.to(device)
|
||||
|
||||
return self
|
||||
|
||||
def set_multiplier(self, multiplier):
|
||||
for layers in self.layers.values():
|
||||
for layer in layers:
|
||||
layer.multiplier = multiplier
|
||||
|
||||
return self
|
||||
|
||||
def eval(self):
|
||||
for layers in self.layers.values():
|
||||
for layer in layers:
|
||||
layer.eval()
|
||||
for param in layer.parameters():
|
||||
param.requires_grad = False
|
||||
|
||||
def save(self, filename):
|
||||
state_dict = {}
|
||||
optimizer_saved_dict = {}
|
||||
for k, v in self.layers.items():
|
||||
state_dict[k] = (v[0].state_dict(), v[1].state_dict())
|
||||
state_dict['step'] = self.step
|
||||
state_dict['name'] = self.name
|
||||
state_dict['layer_structure'] = self.layer_structure
|
||||
state_dict['activation_func'] = self.activation_func
|
||||
state_dict['is_layer_norm'] = self.add_layer_norm
|
||||
state_dict['weight_initialization'] = self.weight_init
|
||||
state_dict['sd_checkpoint'] = self.sd_checkpoint
|
||||
state_dict['sd_checkpoint_name'] = self.sd_checkpoint_name
|
||||
state_dict['activate_output'] = self.activate_output
|
||||
state_dict['use_dropout'] = self.use_dropout
|
||||
state_dict['dropout_structure'] = self.dropout_structure
|
||||
state_dict['last_layer_dropout'] = (self.dropout_structure[-2] != 0) if self.dropout_structure is not None else self.last_layer_dropout
|
||||
state_dict['optional_info'] = self.optional_info if self.optional_info else None
|
||||
if self.optimizer_name is not None:
|
||||
optimizer_saved_dict['optimizer_name'] = self.optimizer_name
|
||||
torch.save(state_dict, filename)
|
||||
if shared.opts.save_optimizer_state and self.optimizer_state_dict:
|
||||
optimizer_saved_dict['hash'] = self.shorthash()
|
||||
optimizer_saved_dict['optimizer_state_dict'] = self.optimizer_state_dict
|
||||
torch.save(optimizer_saved_dict, f"{filename}.optim")
|
||||
|
||||
def load(self, filename):
|
||||
self.filename = filename if os.path.exists(filename) else os.path.join(shared.opts.hypernetwork_dir, filename)
|
||||
if self.name is None:
|
||||
self.name = os.path.splitext(os.path.basename(self.filename))[0]
|
||||
with progress.open(self.filename, 'rb', description=f'Load hypernetwork: [cyan]{self.filename}', auto_refresh=True, console=shared.console) as f:
|
||||
state_dict = torch.load(f, map_location='cpu')
|
||||
self.layer_structure = state_dict.get('layer_structure', [1, 2, 1])
|
||||
self.optional_info = state_dict.get('optional_info', None)
|
||||
self.activation_func = state_dict.get('activation_func', None)
|
||||
self.weight_init = state_dict.get('weight_initialization', 'Normal')
|
||||
self.add_layer_norm = state_dict.get('is_layer_norm', False)
|
||||
self.dropout_structure = state_dict.get('dropout_structure', None)
|
||||
self.use_dropout = True if self.dropout_structure is not None and any(self.dropout_structure) else state_dict.get('use_dropout', False)
|
||||
self.activate_output = state_dict.get('activate_output', True)
|
||||
self.last_layer_dropout = state_dict.get('last_layer_dropout', False)
|
||||
# Dropout structure should have same length as layer structure, Every digits should be in [0,1), and last digit must be 0.
|
||||
if self.dropout_structure is None:
|
||||
self.dropout_structure = parse_dropout_structure(self.layer_structure, self.use_dropout, self.last_layer_dropout)
|
||||
optimizer_saved_dict = torch.load(self.filename + '.optim', map_location='cpu') if os.path.exists(self.filename + '.optim') else {}
|
||||
if self.shorthash() == optimizer_saved_dict.get('hash', None):
|
||||
self.optimizer_state_dict = optimizer_saved_dict.get('optimizer_state_dict', None)
|
||||
else:
|
||||
self.optimizer_state_dict = None
|
||||
if self.optimizer_state_dict:
|
||||
self.optimizer_name = optimizer_saved_dict.get('optimizer_name', 'AdamW')
|
||||
else:
|
||||
self.optimizer_name = "AdamW"
|
||||
for size, sd in state_dict.items():
|
||||
if type(size) == int:
|
||||
self.layers[size] = (
|
||||
HypernetworkModule(size, sd[0], self.layer_structure, self.activation_func, self.weight_init,
|
||||
self.add_layer_norm, self.activate_output, self.dropout_structure),
|
||||
HypernetworkModule(size, sd[1], self.layer_structure, self.activation_func, self.weight_init,
|
||||
self.add_layer_norm, self.activate_output, self.dropout_structure),
|
||||
)
|
||||
self.name = state_dict.get('name', self.name)
|
||||
self.step = state_dict.get('step', 0)
|
||||
self.sd_checkpoint = state_dict.get('sd_checkpoint', None)
|
||||
self.sd_checkpoint_name = state_dict.get('sd_checkpoint_name', None)
|
||||
self.eval()
|
||||
|
||||
def shorthash(self):
|
||||
sha256 = hashes.sha256(self.filename, f'hypernet/{self.name}')
|
||||
return sha256[0:10] if sha256 else None
|
||||
|
||||
|
||||
def list_hypernetworks(path):
|
||||
hypernetworks = {
|
||||
os.path.splitext(os.path.basename(hypernetwork_path))[0]: hypernetwork_path
|
||||
for hypernetwork_path
|
||||
in files_cache.list_files(path, ext_filter=['.pt'], recursive=files_cache.not_hidden)
|
||||
}
|
||||
return hypernetworks
|
||||
|
||||
|
||||
def load_hypernetwork(name):
|
||||
path = shared.hypernetworks.get(name, None)
|
||||
if path is None:
|
||||
return None
|
||||
hypernetwork = Hypernetwork()
|
||||
try:
|
||||
hypernetwork.load(path)
|
||||
except Exception as e:
|
||||
errors.display(e, f'hypernetwork load: {path}')
|
||||
return None
|
||||
return hypernetwork
|
||||
|
||||
|
||||
def load_hypernetworks(names, multipliers=None):
|
||||
already_loaded = {}
|
||||
for hn in loaded_hypernetworks:
|
||||
if hn.name in names:
|
||||
already_loaded[hn.name] = hn
|
||||
loaded_hypernetworks.clear()
|
||||
for i, name in enumerate(names):
|
||||
hypernetwork = already_loaded.get(name, None)
|
||||
if hypernetwork is None:
|
||||
hypernetwork = load_hypernetwork(name)
|
||||
if hypernetwork is None:
|
||||
continue
|
||||
hypernetwork.set_multiplier(multipliers[i] if multipliers else 1.0)
|
||||
loaded_hypernetworks.append(hypernetwork)
|
||||
|
||||
|
||||
def find_closest_hypernetwork_name(search: str):
|
||||
if not search:
|
||||
return None
|
||||
search = search.lower()
|
||||
applicable = [name for name in shared.hypernetworks if search in name.lower()]
|
||||
if not applicable:
|
||||
return None
|
||||
applicable = sorted(applicable, key=lambda name: len(name))
|
||||
return applicable[0]
|
||||
|
||||
|
||||
def apply_single_hypernetwork(hypernetwork, context_k, context_v, layer=None):
|
||||
hypernetwork_layers = (hypernetwork.layers if hypernetwork is not None else {}).get(context_k.shape[2], None)
|
||||
if hypernetwork_layers is None:
|
||||
return context_k, context_v
|
||||
if layer is not None:
|
||||
layer.hyper_k = hypernetwork_layers[0]
|
||||
layer.hyper_v = hypernetwork_layers[1]
|
||||
context_k = devices.cond_cast_unet(hypernetwork_layers[0](devices.cond_cast_float(context_k)))
|
||||
context_v = devices.cond_cast_unet(hypernetwork_layers[1](devices.cond_cast_float(context_v)))
|
||||
return context_k, context_v
|
||||
|
||||
|
||||
def apply_hypernetworks(hypernetworks, context, layer=None):
|
||||
context_k = context
|
||||
context_v = context
|
||||
for hypernetwork in hypernetworks:
|
||||
context_k, context_v = apply_single_hypernetwork(hypernetwork, context_k, context_v, layer)
|
||||
return context_k, context_v
|
||||
|
||||
|
||||
def attention_CrossAttention_forward(self, x, context=None, mask=None):
|
||||
from ldm.util import default
|
||||
h = self.heads
|
||||
q = self.to_q(x)
|
||||
context = default(context, x)
|
||||
context_k, context_v = apply_hypernetworks(loaded_hypernetworks, context, self)
|
||||
k = self.to_k(context_k)
|
||||
v = self.to_v(context_v)
|
||||
q, k, v = (rearrange(t, 'b n (h d) -> (b h) n d', h=h) for t in (q, k, v))
|
||||
sim = einsum('b i d, b j d -> b i j', q, k) * self.scale
|
||||
if mask is not None:
|
||||
mask = rearrange(mask, 'b ... -> b (...)')
|
||||
max_neg_value = -torch.finfo(sim.dtype).max
|
||||
mask = repeat(mask, 'b j -> (b h) () j', h=h)
|
||||
sim.masked_fill_(~mask, max_neg_value)
|
||||
# attention, what we cannot get enough of
|
||||
attn = sim.softmax(dim=-1)
|
||||
out = einsum('b i j, b j d -> b i d', attn, v)
|
||||
out = rearrange(out, '(b h) n d -> b n (h d)', h=h)
|
||||
return self.to_out(out)
|
||||
|
||||
|
||||
def stack_conds(conds):
|
||||
if len(conds) == 1:
|
||||
return torch.stack(conds)
|
||||
# same as in reconstruct_multicond_batch
|
||||
token_count = max([x.shape[0] for x in conds])
|
||||
for i in range(len(conds)):
|
||||
if conds[i].shape[0] != token_count:
|
||||
last_vector = conds[i][-1:]
|
||||
last_vector_repeated = last_vector.repeat([token_count - conds[i].shape[0], 1])
|
||||
conds[i] = torch.vstack([conds[i], last_vector_repeated])
|
||||
return torch.stack(conds)
|
||||
|
||||
|
||||
def statistics(data):
|
||||
if len(data) < 2:
|
||||
std = 0
|
||||
else:
|
||||
std = stdev(data)
|
||||
total_information = f"loss:{mean(data):.3f}" + "\u00B1" + f"({std/ (len(data) ** 0.5):.3f})"
|
||||
recent_data = data[-32:]
|
||||
if len(recent_data) < 2:
|
||||
std = 0
|
||||
else:
|
||||
std = stdev(recent_data)
|
||||
recent_information = f"recent 32 loss:{mean(recent_data):.3f}" + "\u00B1" + f"({std / (len(recent_data) ** 0.5):.3f})"
|
||||
return total_information, recent_information
|
||||
|
||||
|
||||
def report_statistics(loss_info:dict):
|
||||
keys = sorted(loss_info.keys(), key=lambda x: sum(loss_info[x]) / len(loss_info[x]))
|
||||
for key in keys:
|
||||
try:
|
||||
print("Loss statistics for file " + key)
|
||||
info, recent = statistics(list(loss_info[key]))
|
||||
print(info)
|
||||
print(recent)
|
||||
except Exception as e:
|
||||
print(e)
|
||||
+5
-4
@@ -140,8 +140,6 @@ def save_image(image,
|
||||
prompt=None,
|
||||
extension=shared.opts.samples_format,
|
||||
info=None,
|
||||
short_filename=False,
|
||||
no_prompt=False,
|
||||
grid=False,
|
||||
pnginfo_section_name='parameters',
|
||||
p=None,
|
||||
@@ -149,7 +147,7 @@ def save_image(image,
|
||||
forced_filename=None,
|
||||
suffix='',
|
||||
save_to_dirs=None,
|
||||
): # pylint: disable=unused-argument
|
||||
):
|
||||
fn = f'{sys._getframe(2).f_code.co_name}:{sys._getframe(1).f_code.co_name}' # pylint: disable=protected-access
|
||||
debug(f'Save: fn={fn}') # pylint: disable=protected-access
|
||||
if image is None:
|
||||
@@ -162,7 +160,10 @@ def save_image(image,
|
||||
namegen = FilenameGenerator(p, seed, prompt, image, grid=grid)
|
||||
suffix = suffix if suffix is not None else ''
|
||||
basename = '' if basename is None else basename
|
||||
if shared.opts.save_to_dirs:
|
||||
if save_to_dirs is not None and isinstance(save_to_dirs, str) and len(save_to_dirs) > 0:
|
||||
dirname = save_to_dirs
|
||||
path = os.path.join(path, dirname)
|
||||
elif shared.opts.save_to_dirs:
|
||||
dirname = namegen.apply(shared.opts.directories_filename_pattern or "[prompt_words]")
|
||||
path = os.path.join(path, dirname)
|
||||
if forced_filename is None:
|
||||
|
||||
+1
-4
@@ -122,7 +122,7 @@ def process_batch(p, input_files, input_dir, output_dir, inpaint_mask_dir, args)
|
||||
geninfo, items = images.read_info_from_image(image)
|
||||
for k, v in items.items():
|
||||
image.info[k] = v
|
||||
images.save_image(image, path=output_dir, basename=basename, seed=None, prompt=None, extension=ext, info=geninfo, short_filename=True, no_prompt=True, grid=False, pnginfo_section_name="extras", existing_info=image.info, forced_filename=forced_filename)
|
||||
images.save_image(image, path=output_dir, basename=basename, seed=None, prompt=None, extension=ext, info=geninfo, grid=False, pnginfo_section_name="extras", existing_info=image.info, forced_filename=forced_filename)
|
||||
processed = scripts_manager.scripts_img2img.after(p, processed, *args)
|
||||
shared.log.debug(f'Processed: images={len(batch_image_files)} memory={memory_stats()} batch')
|
||||
|
||||
@@ -139,7 +139,6 @@ def img2img(id_task: str, state: str, mode: int,
|
||||
steps,
|
||||
sampler_index,
|
||||
mask_blur, mask_alpha,
|
||||
inpainting_fill,
|
||||
vae_type, tiling, hidiffusion,
|
||||
detailer_enabled, detailer_prompt, detailer_negative, detailer_steps, detailer_strength,
|
||||
n_iter, batch_size,
|
||||
@@ -253,7 +252,6 @@ def img2img(id_task: str, state: str, mode: int,
|
||||
init_images=[image],
|
||||
mask=mask,
|
||||
mask_blur=mask_blur,
|
||||
inpainting_fill=inpainting_fill,
|
||||
resize_mode=resize_mode,
|
||||
resize_name=resize_name,
|
||||
resize_context=resize_context,
|
||||
@@ -295,7 +293,6 @@ def img2img(id_task: str, state: str, mode: int,
|
||||
p.extra_generation_params["Mask blur"] = mask_blur
|
||||
p.extra_generation_params["Mask alpha"] = mask_alpha
|
||||
p.extra_generation_params["Mask invert"] = inpainting_mask_invert
|
||||
p.extra_generation_params["Mask content"] = inpainting_fill
|
||||
p.extra_generation_params["Mask area"] = inpaint_full_res
|
||||
p.extra_generation_params["Mask padding"] = inpaint_full_res_padding
|
||||
p.is_batch = mode == 5
|
||||
|
||||
@@ -120,8 +120,6 @@ def parse(infotext):
|
||||
|
||||
|
||||
mapping = [
|
||||
# Backend
|
||||
('Backend', 'sd_backend'),
|
||||
# Models
|
||||
('Model hash', 'sd_model_checkpoint'),
|
||||
('Refiner', 'sd_model_refiner'),
|
||||
|
||||
@@ -1,164 +0,0 @@
|
||||
# converted from <https://github.com/city96/SD-Latent-Interposer>
|
||||
|
||||
import os
|
||||
import time
|
||||
import torch
|
||||
import torch.nn as nn
|
||||
from safetensors.torch import load_file
|
||||
|
||||
|
||||
# v1 = Stable Diffusion 1.x
|
||||
# xl = Stable Diffusion Extra Large (SDXL)
|
||||
# v3 = Stable Diffusion Version Three (SD3)
|
||||
# fx = Black Forest Labs Flux dot One
|
||||
# cc = Stable Cascade (Stage C) [not used]
|
||||
# ca = Stable Cascade (Stage A/B)
|
||||
config = {
|
||||
"v1-to-xl": {"ch_in": 4, "ch_out": 4, "ch_mid": 64, "scale": 1.0, "blocks": 12},
|
||||
"v1-to-v3": {"ch_in": 4, "ch_out":16, "ch_mid": 64, "scale": 1.0, "blocks": 12},
|
||||
"xl-to-v1": {"ch_in": 4, "ch_out": 4, "ch_mid": 64, "scale": 1.0, "blocks": 12},
|
||||
"xl-to-v3": {"ch_in": 4, "ch_out":16, "ch_mid": 64, "scale": 1.0, "blocks": 12},
|
||||
"v3-to-v1": {"ch_in":16, "ch_out": 4, "ch_mid": 64, "scale": 1.0, "blocks": 12},
|
||||
"v3-to-xl": {"ch_in":16, "ch_out": 4, "ch_mid": 64, "scale": 1.0, "blocks": 12},
|
||||
"fx-to-v1": {"ch_in":16, "ch_out": 4, "ch_mid": 64, "scale": 1.0, "blocks": 12},
|
||||
"fx-to-xl": {"ch_in":16, "ch_out": 4, "ch_mid": 64, "scale": 1.0, "blocks": 12},
|
||||
"fx-to-v3": {"ch_in":16, "ch_out":16, "ch_mid": 64, "scale": 1.0, "blocks": 12},
|
||||
"ca-to-v1": {"ch_in": 4, "ch_out": 4, "ch_mid": 64, "scale": 0.5, "blocks": 12},
|
||||
"ca-to-xl": {"ch_in": 4, "ch_out": 4, "ch_mid": 64, "scale": 0.5, "blocks": 12},
|
||||
"ca-to-v3": {"ch_in": 4, "ch_out":16, "ch_mid": 64, "scale": 0.5, "blocks": 12},
|
||||
}
|
||||
|
||||
|
||||
class ResBlock(nn.Module):
|
||||
"""Block with residuals"""
|
||||
def __init__(self, ch):
|
||||
super().__init__()
|
||||
self.join = nn.ReLU()
|
||||
self.norm = nn.BatchNorm2d(ch)
|
||||
self.long = nn.Sequential(
|
||||
nn.Conv2d(ch, ch, kernel_size=3, stride=1, padding=1),
|
||||
nn.SiLU(),
|
||||
nn.Conv2d(ch, ch, kernel_size=3, stride=1, padding=1),
|
||||
nn.SiLU(),
|
||||
nn.Conv2d(ch, ch, kernel_size=3, stride=1, padding=1),
|
||||
nn.Dropout(0.1)
|
||||
)
|
||||
def forward(self, x):
|
||||
x = self.norm(x)
|
||||
return self.join(self.long(x) + x)
|
||||
|
||||
|
||||
class ExtractBlock(nn.Module):
|
||||
"""Increase no. of channels by [out/in]"""
|
||||
def __init__(self, ch_in, ch_out):
|
||||
super().__init__()
|
||||
self.join = nn.ReLU()
|
||||
self.short = nn.Conv2d(ch_in, ch_out, kernel_size=3, stride=1, padding=1)
|
||||
self.long = nn.Sequential(
|
||||
nn.Conv2d( ch_in, ch_out, kernel_size=3, stride=1, padding=1),
|
||||
nn.SiLU(),
|
||||
nn.Conv2d(ch_out, ch_out, kernel_size=3, stride=1, padding=1),
|
||||
nn.SiLU(),
|
||||
nn.Conv2d(ch_out, ch_out, kernel_size=3, stride=1, padding=1),
|
||||
nn.Dropout(0.1)
|
||||
)
|
||||
def forward(self, x):
|
||||
return self.join(self.long(x) + self.short(x))
|
||||
|
||||
|
||||
class InterposerModel(nn.Module):
|
||||
"""
|
||||
NN layout, ported from:
|
||||
https://github.com/city96/SD-Latent-Interposer/blob/main/interposer.py
|
||||
"""
|
||||
def __init__(self, ch_in=4, ch_out=4, ch_mid=64, scale=1.0, blocks=12):
|
||||
super().__init__()
|
||||
self.ch_in = ch_in
|
||||
self.ch_out = ch_out
|
||||
self.ch_mid = ch_mid
|
||||
self.blocks = blocks
|
||||
self.scale = scale
|
||||
|
||||
self.head = ExtractBlock(self.ch_in, self.ch_mid)
|
||||
self.core = nn.Sequential(
|
||||
nn.Upsample(scale_factor=self.scale, mode="nearest"),
|
||||
*[ResBlock(self.ch_mid) for _ in range(blocks)],
|
||||
nn.BatchNorm2d(self.ch_mid),
|
||||
nn.SiLU(),
|
||||
)
|
||||
self.tail = nn.Conv2d(self.ch_mid, self.ch_out, kernel_size=3, stride=1, padding=1)
|
||||
|
||||
def forward(self, x):
|
||||
y = self.head(x)
|
||||
z = self.core(y)
|
||||
return self.tail(z)
|
||||
|
||||
|
||||
def map_model_name(name: str):
|
||||
if name == 'sd':
|
||||
return 'v1'
|
||||
if name == 'sdxl':
|
||||
return 'xl'
|
||||
if name == 'sd3':
|
||||
return 'v3'
|
||||
if name in ['f1', 'chroma']:
|
||||
return 'fx'
|
||||
return name
|
||||
|
||||
|
||||
class Interposer:
|
||||
def __init__(self):
|
||||
self.version = 4.0 # network revision
|
||||
self.loaded = None # current model name
|
||||
self.model = None # current model
|
||||
self.vae = None # current VAE
|
||||
|
||||
def convert(self, src: str, dst: str, latents: torch.Tensor):
|
||||
from diffusers import AutoencoderKL
|
||||
from huggingface_hub import hf_hub_download
|
||||
from modules import shared, devices
|
||||
|
||||
src = map_model_name(src)
|
||||
dst = map_model_name(dst)
|
||||
if src == dst:
|
||||
return None
|
||||
model_name = f"{src}-to-{dst}"
|
||||
if model_name not in config:
|
||||
shared.log.error(f'Interposer: model="{model_name}" unknown')
|
||||
return None
|
||||
if (self.loaded != model_name) or (self.model is None):
|
||||
model_fn = hf_hub_download(
|
||||
repo_id="city96/SD-Latent-Interposer",
|
||||
subfolder=f"v{self.version}",
|
||||
filename=f"{model_name}_interposer-v{self.version}.safetensors",
|
||||
cache_dir=shared.opts.hfcache_dir,
|
||||
)
|
||||
self.model = InterposerModel(**config[model_name])
|
||||
self.model = self.model.to(device=devices.cpu, dtype=torch.float32)
|
||||
self.model.eval()
|
||||
self.model.load_state_dict(load_file(model_fn))
|
||||
self.loaded = model_name
|
||||
if dst == 'v1':
|
||||
vae_repo = 'stable-diffusion-v1-5/stable-diffusion-v1-5'
|
||||
self.vae = AutoencoderKL.from_pretrained(vae_repo, subfolder='vae', cache_dir=shared.opts.hfcache_dir, torch_dtype=devices.dtype)
|
||||
elif dst == 'xl':
|
||||
vae_repo = 'madebyollin/sdxl-vae-fp16-fix'
|
||||
self.vae = AutoencoderKL.from_pretrained(vae_repo, cache_dir=shared.opts.hfcache_dir, torch_dtype=devices.dtype)
|
||||
elif dst == 'v3':
|
||||
vae_repo = 'stabilityai/stable-diffusion-3.5-large'
|
||||
self.vae = AutoencoderKL.from_pretrained(vae_repo, subfolder='vae', cache_dir=shared.opts.hfcache_dir, torch_dtype=devices.dtype)
|
||||
elif dst == 'fx':
|
||||
vae_repo = 'black-forest-labs/FLUX.1-dev'
|
||||
self.vae = AutoencoderKL.from_pretrained(vae_repo, subfolder='vae', cache_dir=shared.opts.hfcache_dir, torch_dtype=devices.dtype)
|
||||
|
||||
t0 = time.time()
|
||||
if self.model is None or self.vae is None:
|
||||
return None
|
||||
with torch.no_grad():
|
||||
latent = latents.clone().cpu().float() # force fp32, always run on CPU
|
||||
output = self.model(latent)
|
||||
output = output.to(device=latents.device, dtype=latents.dtype)
|
||||
t1 = time.time()
|
||||
shared.log.debug(f'Interposer: src={src}/{list(latents.shape)} dst={dst}/{list(output.shape)} model="{os.path.basename(model_fn)}" vae="{vae_repo}" time={t1-t0:.2f}')
|
||||
# shared.log.debug(f'Interposer: src={latents.aminmax()} dst={output.aminmax()}')
|
||||
return output
|
||||
@@ -1,16 +1,10 @@
|
||||
import os
|
||||
import sys
|
||||
from collections import namedtuple
|
||||
from pathlib import Path
|
||||
import threading
|
||||
import re
|
||||
import torch
|
||||
import torch.hub # pylint: disable=ungrouped-imports
|
||||
import gradio as gr
|
||||
from PIL import Image
|
||||
from torchvision import transforms
|
||||
from torchvision.transforms.functional import InterpolationMode
|
||||
from modules import devices, paths, shared, lowvram, errors, sd_models
|
||||
from modules import devices, paths, shared, errors, sd_models
|
||||
|
||||
|
||||
caption_models = {
|
||||
@@ -37,187 +31,6 @@ re_topn = re.compile(r"\.top(\d+)\.")
|
||||
load_lock = threading.Lock()
|
||||
|
||||
|
||||
def category_types():
|
||||
return [f.stem for f in Path(interrogator.content_dir).glob('*.txt')]
|
||||
|
||||
|
||||
def download_default_clip_interrogate_categories(content_dir):
|
||||
shared.log.info("Interrogate: downloading CLIP categories...")
|
||||
tmpdir = f"{content_dir}_tmp"
|
||||
cat_types = ["artists", "flavors", "mediums", "movements"]
|
||||
try:
|
||||
os.makedirs(tmpdir, exist_ok=True)
|
||||
for category_type in cat_types:
|
||||
torch.hub.download_url_to_file(f"https://raw.githubusercontent.com/pharmapsychotic/clip-interrogator/main/clip_interrogator/data/{category_type}.txt", os.path.join(tmpdir, f"{category_type}.txt"))
|
||||
os.rename(tmpdir, content_dir)
|
||||
except Exception as e:
|
||||
errors.display(e, "downloading default CLIP interrogate categories")
|
||||
finally:
|
||||
if os.path.exists(tmpdir):
|
||||
os.removedirs(tmpdir)
|
||||
|
||||
|
||||
class InterrogateModels:
|
||||
blip_model = None
|
||||
clip_model = None
|
||||
clip_preprocess = None
|
||||
dtype = None
|
||||
running_on_cpu = None
|
||||
|
||||
def __init__(self, content_dir: str = None):
|
||||
self.loaded_categories = None
|
||||
self.skip_categories = []
|
||||
self.content_dir = content_dir or os.path.join(paths.models_path, "interrogate")
|
||||
self.running_on_cpu = False
|
||||
|
||||
def categories(self):
|
||||
if not os.path.exists(self.content_dir):
|
||||
download_default_clip_interrogate_categories(self.content_dir)
|
||||
if self.loaded_categories is not None and self.skip_categories == shared.opts.interrogate_clip_skip_categories:
|
||||
return self.loaded_categories
|
||||
self.loaded_categories = []
|
||||
|
||||
if os.path.exists(self.content_dir):
|
||||
self.skip_categories = shared.opts.interrogate_clip_skip_categories
|
||||
cat_types = []
|
||||
for filename in Path(self.content_dir).glob('*.txt'):
|
||||
cat_types.append(filename.stem)
|
||||
if filename.stem in self.skip_categories:
|
||||
continue
|
||||
m = re_topn.search(filename.stem)
|
||||
topn = 1 if m is None else int(m.group(1))
|
||||
with open(filename, "r", encoding="utf8") as file:
|
||||
lines = [x.strip() for x in file.readlines()]
|
||||
self.loaded_categories.append(Category(name=filename.stem, topn=topn, items=lines))
|
||||
return self.loaded_categories
|
||||
|
||||
def create_fake_fairscale(self):
|
||||
class FakeFairscale:
|
||||
def checkpoint_wrapper(self):
|
||||
pass
|
||||
sys.modules["fairscale.nn.checkpoint.checkpoint_activations"] = FakeFairscale
|
||||
|
||||
def load_blip_model(self):
|
||||
with load_lock:
|
||||
self.create_fake_fairscale()
|
||||
from repositories.blip import models # pylint: disable=unused-import
|
||||
from repositories.blip.models import blip
|
||||
import modules.modelloader as modelloader
|
||||
model_path = os.path.join(paths.models_path, "BLIP")
|
||||
download_name='model_base_caption_capfilt_large.pth'
|
||||
shared.log.debug(f'Interrogate load: module=BLiP model="{download_name}" path="{model_path}"')
|
||||
files = modelloader.load_models(
|
||||
model_path=model_path,
|
||||
model_url='https://storage.googleapis.com/sfr-vision-language-research/BLIP/models/model_base_caption_capfilt_large.pth',
|
||||
ext_filter=[".pth"],
|
||||
download_name=download_name,
|
||||
)
|
||||
blip_model = blip.blip_decoder(pretrained=files[0], image_size=blip_image_eval_size, vit='base', med_config=os.path.join(paths.paths["BLIP"], "configs", "med_config.json")) # pylint: disable=c-extension-no-member
|
||||
blip_model.eval()
|
||||
return blip_model
|
||||
|
||||
def load_clip_model(self):
|
||||
with load_lock:
|
||||
shared.log.debug(f'Interrogate load: module=CLiP model="{clip_model_name}" path="{shared.opts.clip_models_path}"')
|
||||
import clip
|
||||
if self.running_on_cpu:
|
||||
model, preprocess = clip.load(clip_model_name, device="cpu", download_root=shared.opts.clip_models_path)
|
||||
else:
|
||||
model, preprocess = clip.load(clip_model_name, download_root=shared.opts.clip_models_path)
|
||||
model.eval()
|
||||
sd_models.move_model(model, devices.device)
|
||||
return model, preprocess
|
||||
|
||||
def load(self):
|
||||
if self.blip_model is None:
|
||||
self.blip_model = self.load_blip_model()
|
||||
if not shared.opts.no_half and not self.running_on_cpu:
|
||||
self.blip_model = self.blip_model.half()
|
||||
if self.clip_model is None:
|
||||
self.clip_model, self.clip_preprocess = self.load_clip_model()
|
||||
if not shared.opts.no_half and not self.running_on_cpu:
|
||||
self.clip_model = self.clip_model.half()
|
||||
self.dtype = next(self.clip_model.parameters()).dtype
|
||||
sd_models.move_model(self.blip_model, devices.device)
|
||||
sd_models.move_model(self.clip_model, devices.device)
|
||||
|
||||
def send_clip_to_ram(self):
|
||||
if shared.opts.interrogate_offload:
|
||||
if self.clip_model is not None:
|
||||
sd_models.move_model(self.blip_model, devices.cpu)
|
||||
|
||||
def send_blip_to_ram(self):
|
||||
if shared.opts.interrogate_offload:
|
||||
if self.blip_model is not None:
|
||||
sd_models.move_model(self.blip_model, devices.cpu)
|
||||
|
||||
def unload(self):
|
||||
self.send_clip_to_ram()
|
||||
self.send_blip_to_ram()
|
||||
devices.torch_gc()
|
||||
|
||||
def rank(self, image_features, text_array, top_count=1):
|
||||
import clip
|
||||
devices.torch_gc()
|
||||
if shared.opts.interrogate_clip_dict_limit != 0:
|
||||
text_array = text_array[0:int(shared.opts.interrogate_clip_dict_limit)]
|
||||
top_count = min(top_count, len(text_array))
|
||||
text_tokens = clip.tokenize(list(text_array), truncate=True).to(devices.device)
|
||||
text_features = self.clip_model.encode_text(text_tokens).type(self.dtype)
|
||||
text_features /= text_features.norm(dim=-1, keepdim=True)
|
||||
similarity = torch.zeros((1, len(text_array))).to(devices.device)
|
||||
for i in range(image_features.shape[0]):
|
||||
similarity += (100.0 * image_features[i].unsqueeze(0) @ text_features.T).softmax(dim=-1)
|
||||
similarity /= image_features.shape[0]
|
||||
top_probs, top_labels = similarity.cpu().topk(top_count, dim=-1)
|
||||
return [(text_array[top_labels[0][i].numpy()], (top_probs[0][i].numpy()*100)) for i in range(top_count)]
|
||||
|
||||
def generate_caption(self, pil_image):
|
||||
gpu_image = transforms.Compose([
|
||||
transforms.Resize((blip_image_eval_size, blip_image_eval_size), interpolation=InterpolationMode.BICUBIC),
|
||||
transforms.ToTensor(),
|
||||
transforms.Normalize((0.48145466, 0.4578275, 0.40821073), (0.26862954, 0.26130258, 0.27577711))
|
||||
])(pil_image).unsqueeze(0).type(self.dtype).to(devices.device)
|
||||
with devices.inference_context():
|
||||
min_length = min(shared.opts.interrogate_clip_min_length, shared.opts.interrogate_clip_max_length)
|
||||
max_length = max(shared.opts.interrogate_clip_min_length, shared.opts.interrogate_clip_max_length)
|
||||
caption = self.blip_model.generate(gpu_image, sample=False, num_beams=shared.opts.interrogate_clip_num_beams, min_length=min_length, max_length=max_length)
|
||||
return caption[0]
|
||||
|
||||
def interrogate(self, image):
|
||||
res = ""
|
||||
shared.state.begin('Interrogate')
|
||||
try:
|
||||
self.load()
|
||||
if isinstance(image, list):
|
||||
image = image[0] if len(image) > 0 else None
|
||||
if isinstance(image, dict) and 'name' in image:
|
||||
image = Image.open(image['name'])
|
||||
if image is None:
|
||||
return ''
|
||||
image = image.convert("RGB")
|
||||
caption = self.generate_caption(image)
|
||||
res = caption
|
||||
clip_image = self.clip_preprocess(image).unsqueeze(0).type(self.dtype).to(devices.device)
|
||||
with devices.inference_context(), devices.autocast():
|
||||
image_features = self.clip_model.encode_image(clip_image).type(self.dtype)
|
||||
image_features /= image_features.norm(dim=-1, keepdim=True)
|
||||
for _name, topn, items in self.categories():
|
||||
matches = self.rank(image_features, items, top_count=topn)
|
||||
for match, score in matches:
|
||||
if shared.opts.interrogate_score:
|
||||
res += f", ({match}:{score/100:.2f})"
|
||||
else:
|
||||
res += f", {match}"
|
||||
except Exception as e:
|
||||
errors.display(e, 'interrogate')
|
||||
res += "<error>"
|
||||
self.unload()
|
||||
shared.state.end()
|
||||
return res
|
||||
|
||||
# --------- interrrogate ui
|
||||
|
||||
class BatchWriter:
|
||||
def __init__(self, folder, mode='w'):
|
||||
self.folder = folder
|
||||
@@ -324,10 +137,7 @@ def interrogate(image, mode, caption=None):
|
||||
def interrogate_image(image, clip_model, blip_model, mode):
|
||||
shared.state.begin('Interrogate')
|
||||
try:
|
||||
if not shared.native and (shared.cmd_opts.lowvram or shared.cmd_opts.medvram):
|
||||
lowvram.send_everything_to_cpu()
|
||||
devices.torch_gc()
|
||||
if shared.native and shared.sd_loaded:
|
||||
if shared.sd_loaded:
|
||||
from modules.sd_models import apply_balanced_offload # prevent circular import
|
||||
apply_balanced_offload(shared.sd_model)
|
||||
load_interrogator(clip_model, blip_model)
|
||||
@@ -406,6 +216,3 @@ def analyze_image(image, clip_model, blip_model):
|
||||
gr.update(value=trending_ranks, visible=True),
|
||||
gr.update(value=flavor_ranks, visible=True),
|
||||
]
|
||||
|
||||
|
||||
interrogator = InterrogateModels()
|
||||
|
||||
@@ -563,7 +563,7 @@ def interrogate(question:str='', system_prompt:str=None, prompt:str=None, image:
|
||||
question = prompt
|
||||
if len(question) < 2:
|
||||
question = "Describe the image."
|
||||
if shared.native and shared.sd_loaded:
|
||||
if shared.sd_loaded:
|
||||
from modules.sd_models import apply_balanced_offload # prevent circular import
|
||||
apply_balanced_offload(shared.sd_model)
|
||||
|
||||
|
||||
@@ -302,9 +302,6 @@ def apply(pipe, p: processing.StableDiffusionProcessing, adapter_names=[], adapt
|
||||
# init code
|
||||
if pipe is None:
|
||||
return False
|
||||
if not shared.native:
|
||||
shared.log.warning('IP adapter: not in diffusers mode')
|
||||
return False
|
||||
if len(adapter_images) == 0:
|
||||
shared.log.error('IP adapter: no image provided')
|
||||
adapters = [] # unload adapter if previously loaded as it will cause runtime errors
|
||||
|
||||
@@ -210,7 +210,7 @@ class ExtraNetworkLora(extra_networks.ExtraNetwork):
|
||||
shared.log.info(f'Network load: type=LoRA apply={[n.name for n in l.loaded_networks]} method={load_method} mode={"fuse" if shared.opts.lora_fuse_diffusers else "backup"} te={te_multipliers} unet={unet_multipliers} time={l.timer.summary}')
|
||||
|
||||
def deactivate(self, p):
|
||||
if shared.native and len(lora_load.diffuser_loaded) > 0:
|
||||
if len(lora_load.diffuser_loaded) > 0:
|
||||
if not (shared.compiled_model_state is not None and shared.compiled_model_state.is_compiled is True):
|
||||
unload_diffusers()
|
||||
if self.active and l.debug:
|
||||
|
||||
@@ -116,7 +116,7 @@ def make_meta(fn, maxrank, rank_ratio):
|
||||
|
||||
|
||||
def make_lora(fn, maxrank, auto_rank, rank_ratio, modules, overwrite):
|
||||
if not shared.sd_loaded or not shared.native:
|
||||
if not shared.sd_loaded:
|
||||
msg = "LoRA extract: model not loaded"
|
||||
shared.log.warning(msg)
|
||||
yield msg
|
||||
|
||||
@@ -21,8 +21,6 @@ def load_diffusers(name, network_on_disk, lora_scale=shared.opts.extra_networks_
|
||||
t0 = time.time()
|
||||
name = name.replace(".", "_")
|
||||
shared.log.debug(f'Network load: type=LoRA name="{name}" file="{network_on_disk.filename}" detected={network_on_disk.sd_version} method=diffusers scale={lora_scale} fuse={shared.opts.lora_fuse_diffusers}')
|
||||
if not shared.native:
|
||||
return None
|
||||
if not hasattr(shared.sd_model, 'load_lora_weights'):
|
||||
shared.log.error(f'Network load: type=LoRA class={shared.sd_model.__class__} does not implement load lora')
|
||||
return None
|
||||
@@ -220,10 +218,7 @@ def list_available_networks():
|
||||
available_networks[entry.name] = entry
|
||||
if entry.alias in available_network_aliases:
|
||||
forbidden_network_aliases[entry.alias.lower()] = 1
|
||||
if shared.opts.lora_preferred_name == 'filename':
|
||||
available_network_aliases[entry.name] = entry
|
||||
else:
|
||||
available_network_aliases[entry.alias] = entry
|
||||
available_network_aliases[entry.name] = entry
|
||||
if entry.shorthash:
|
||||
available_network_hash_lookup[entry.shorthash] = entry
|
||||
except OSError as e: # should catch FileNotFoundError and PermissionError etc.
|
||||
|
||||
@@ -119,10 +119,7 @@ class NetworkOnDisk:
|
||||
return None
|
||||
|
||||
def get_alias(self):
|
||||
if shared.opts.lora_preferred_name == "filename":
|
||||
return self.name
|
||||
else:
|
||||
return self.alias
|
||||
return self.name
|
||||
|
||||
|
||||
class Network: # LoraModule
|
||||
|
||||
@@ -1,94 +0,0 @@
|
||||
import torch
|
||||
from modules import devices
|
||||
|
||||
module_in_gpu = None
|
||||
cpu = torch.device("cpu")
|
||||
|
||||
|
||||
def send_everything_to_cpu():
|
||||
global module_in_gpu # pylint: disable=global-statement
|
||||
|
||||
if module_in_gpu is not None:
|
||||
module_in_gpu.to(cpu)
|
||||
|
||||
module_in_gpu = None
|
||||
|
||||
|
||||
def setup_for_low_vram(sd_model, use_medvram):
|
||||
parents = {}
|
||||
|
||||
def send_me_to_gpu(module, _):
|
||||
"""send this module to GPU; send whatever tracked module was previous in GPU to CPU;
|
||||
we add this as forward_pre_hook to a lot of modules and this way all but one of them will
|
||||
be in CPU
|
||||
"""
|
||||
global module_in_gpu # pylint: disable=global-statement
|
||||
module = parents.get(module, module)
|
||||
if module_in_gpu == module:
|
||||
return
|
||||
if module_in_gpu is not None:
|
||||
module_in_gpu.to(cpu)
|
||||
module.to(devices.device)
|
||||
module_in_gpu = module
|
||||
|
||||
# see below for register_forward_pre_hook;
|
||||
# first_stage_model does not use forward(), it uses encode/decode, so register_forward_pre_hook is
|
||||
# useless here, and we just replace those methods
|
||||
|
||||
first_stage_model = sd_model.first_stage_model
|
||||
first_stage_model_encode = sd_model.first_stage_model.encode
|
||||
first_stage_model_decode = sd_model.first_stage_model.decode
|
||||
|
||||
def first_stage_model_encode_wrap(x):
|
||||
send_me_to_gpu(first_stage_model, None)
|
||||
return first_stage_model_encode(x)
|
||||
|
||||
def first_stage_model_decode_wrap(z):
|
||||
send_me_to_gpu(first_stage_model, None)
|
||||
return first_stage_model_decode(z)
|
||||
|
||||
# for SD1, cond_stage_model is CLIP and its NN is in the tranformer frield, but for SD2, it's open clip, and it's in model field
|
||||
if hasattr(sd_model, 'cond_stage_model') and hasattr(sd_model.cond_stage_model, 'model'):
|
||||
sd_model.cond_stage_model.transformer = sd_model.cond_stage_model.model
|
||||
|
||||
# remove several big modules: cond, first_stage, depth/embedder (if applicable), and unet from the model and then
|
||||
# send the model to GPU. Then put modules back. the modules will be in CPU.
|
||||
stored = sd_model.cond_stage_model.transformer, sd_model.first_stage_model, getattr(sd_model, 'depth_model', None), getattr(sd_model, 'embedder', None), sd_model.model
|
||||
sd_model.cond_stage_model.transformer, sd_model.first_stage_model, sd_model.depth_model, sd_model.embedder, sd_model.model = None, None, None, None, None
|
||||
sd_model.to(devices.device)
|
||||
sd_model.cond_stage_model.transformer, sd_model.first_stage_model, sd_model.depth_model, sd_model.embedder, sd_model.model = stored
|
||||
|
||||
# register hooks for those the first three models
|
||||
sd_model.cond_stage_model.transformer.register_forward_pre_hook(send_me_to_gpu)
|
||||
sd_model.first_stage_model.register_forward_pre_hook(send_me_to_gpu)
|
||||
sd_model.first_stage_model.encode = first_stage_model_encode_wrap
|
||||
sd_model.first_stage_model.decode = first_stage_model_decode_wrap
|
||||
if sd_model.depth_model:
|
||||
sd_model.depth_model.register_forward_pre_hook(send_me_to_gpu)
|
||||
if sd_model.embedder:
|
||||
sd_model.embedder.register_forward_pre_hook(send_me_to_gpu)
|
||||
parents[sd_model.cond_stage_model.transformer] = sd_model.cond_stage_model
|
||||
|
||||
if hasattr(sd_model, 'cond_stage_model') and hasattr(sd_model.cond_stage_model, 'model'):
|
||||
sd_model.cond_stage_model.model = sd_model.cond_stage_model.transformer
|
||||
del sd_model.cond_stage_model.transformer
|
||||
|
||||
if use_medvram:
|
||||
sd_model.model.register_forward_pre_hook(send_me_to_gpu)
|
||||
else:
|
||||
diff_model = sd_model.model.diffusion_model
|
||||
|
||||
# the third remaining model is still too big for 4 GB, so we also do the same for its submodules
|
||||
# so that only one of them is in GPU at a time
|
||||
stored = diff_model.input_blocks, diff_model.middle_block, diff_model.output_blocks, diff_model.time_embed
|
||||
diff_model.input_blocks, diff_model.middle_block, diff_model.output_blocks, diff_model.time_embed = None, None, None, None
|
||||
sd_model.model.to(devices.device)
|
||||
diff_model.input_blocks, diff_model.middle_block, diff_model.output_blocks, diff_model.time_embed = stored
|
||||
|
||||
# install hooks for bits of third model
|
||||
diff_model.time_embed.register_forward_pre_hook(send_me_to_gpu)
|
||||
for block in diff_model.input_blocks:
|
||||
block.register_forward_pre_hook(send_me_to_gpu)
|
||||
diff_model.middle_block.register_forward_pre_hook(send_me_to_gpu)
|
||||
for block in diff_model.output_blocks:
|
||||
block.register_forward_pre_hook(send_me_to_gpu)
|
||||
+1
-1
@@ -31,7 +31,7 @@ class OptionInfo:
|
||||
self.comment_before = comment_before # HTML text that will be added after label in UI
|
||||
self.comment_after = comment_after # HTML text that will be added before label in UI
|
||||
self.submit = submit
|
||||
self.exclude = ['sd_model_checkpoint', 'sd_model_refiner', 'sd_vae', 'sd_unet', 'sd_text_encoder', 'sd_model_dict']
|
||||
self.exclude = ['sd_model_checkpoint', 'sd_model_refiner', 'sd_vae', 'sd_unet', 'sd_text_encoder']
|
||||
self.dynamic = callable(component_args)
|
||||
args = {} if self.dynamic else (component_args or {}) # executing callable here is too expensive
|
||||
self.visible = args.get('visible', True) and len(self.label) > 2
|
||||
|
||||
@@ -10,8 +10,6 @@ orig_pipeline = None
|
||||
|
||||
def apply(p: processing.StableDiffusionProcessing): # pylint: disable=arguments-differ
|
||||
global orig_pipeline # pylint: disable=global-statement
|
||||
if not shared.native:
|
||||
return None
|
||||
cls = shared.sd_model.__class__ if shared.sd_loaded else None
|
||||
if cls == StableDiffusionPAGPipeline or cls == StableDiffusionXLPAGPipeline:
|
||||
cls = unapply()
|
||||
|
||||
@@ -5,7 +5,7 @@ supported_models = ['Flux', 'HunyuanVideo', 'CogVideoX', 'Mochi']
|
||||
|
||||
|
||||
def apply_first_block_cache():
|
||||
if not shared.opts.para_cache_enabled or not shared.native:
|
||||
if not shared.opts.para_cache_enabled:
|
||||
return
|
||||
if not any(shared.sd_model.__class__.__name__.startswith(x) for x in supported_models):
|
||||
return
|
||||
|
||||
@@ -49,11 +49,7 @@ def register_paths():
|
||||
sys.path.insert(0, script_path)
|
||||
sd_path = os.path.join(script_path, 'repositories')
|
||||
path_dirs = [
|
||||
(sd_path, 'ldm', 'ldm', []),
|
||||
(sd_path, 'taming', 'Taming Transformers', []),
|
||||
(os.path.join(sd_path, 'blip'), 'models/blip.py', 'BLIP', []),
|
||||
(os.path.join(sd_path, 'codeformer'), 'inference_codeformer.py', 'CodeFormer', []),
|
||||
(os.path.join(modules_path, 'k-diffusion'), 'k_diffusion/sampling.py', 'k_diffusion', ["atstart"]),
|
||||
]
|
||||
for d, must_exist, what, _options in path_dirs:
|
||||
must_exist_path = os.path.abspath(os.path.join(script_path, d, must_exist))
|
||||
@@ -113,7 +109,6 @@ def create_paths(opts):
|
||||
create_path(fix_path('lora_dir'))
|
||||
create_path(fix_path('tunable_dir'))
|
||||
create_path(fix_path('embeddings_dir'))
|
||||
create_path(fix_path('hypernetwork_dir'))
|
||||
create_path(fix_path('onnx_temp_dir'))
|
||||
create_path(fix_path('outdir_samples'))
|
||||
create_path(fix_path('outdir_txt2img_samples'))
|
||||
|
||||
@@ -10,9 +10,6 @@ class UpscalerAuraSR(Upscaler):
|
||||
self.name = "AuraSR"
|
||||
self.user_path = dirname
|
||||
self.model = None
|
||||
if not shared.native:
|
||||
super().__init__()
|
||||
return
|
||||
self.scalers = [
|
||||
UpscalerData(name="Aura SR 4x", path="stabilityai/sd-x2-latent-upscaler", upscaler=self, model=None, scale=4),
|
||||
]
|
||||
|
||||
@@ -9,9 +9,6 @@ class UpscalerDiffusion(Upscaler):
|
||||
def __init__(self, dirname): # pylint: disable=super-init-not-called
|
||||
self.name = "SDUpscale"
|
||||
self.user_path = dirname
|
||||
if not shared.native:
|
||||
super().__init__()
|
||||
return
|
||||
self.scalers = [
|
||||
UpscalerData(name="Diffusion Latent Upscaler 2x", path="stabilityai/sd-x2-latent-upscaler", upscaler=self, model=None, scale=4),
|
||||
UpscalerData(name="Diffusion Latent Upscaler 4x", path="stabilityai/stable-diffusion-x4-upscaler", upscaler=self, model=None, scale=4),
|
||||
|
||||
@@ -354,7 +354,7 @@ class YoloRestorer(Detailer):
|
||||
shared.opts.save(shared.config_filename, silent=True)
|
||||
shared.log.debug(f'Detailer settings: models={detailers} classes={classes} strength={strength} conf={min_confidence} max={max_detected} iou={iou} size={min_size}-{max_size} padding={padding} steps={steps}')
|
||||
|
||||
with gr.Accordion(open=False, label="Detailer", elem_id=f"{tab}_detailer_accordion", elem_classes=["small-accordion"], visible=shared.native):
|
||||
with gr.Accordion(open=False, label="Detailer", elem_id=f"{tab}_detailer_accordion", elem_classes=["small-accordion"]):
|
||||
with gr.Row():
|
||||
enabled = gr.Checkbox(label="Enable detailer pass", elem_id=f"{tab}_detailer_enabled", value=False)
|
||||
with gr.Row():
|
||||
|
||||
@@ -83,9 +83,9 @@ def run_postprocessing(extras_mode, image, image_folder: List[tempfile.NamedTemp
|
||||
if save_output:
|
||||
if opts.use_original_name_batch and name is not None:
|
||||
forced_filename = os.path.splitext(os.path.basename(name))[0]
|
||||
images.save_image(pp.image, path=outpath, extension=ext or opts.samples_format, info=info, short_filename=True, no_prompt=True, grid=False, pnginfo_section_name="extras", existing_info=pp.image.info, forced_filename=forced_filename)
|
||||
images.save_image(pp.image, path=outpath, extension=ext or opts.samples_format, info=info, grid=False, pnginfo_section_name="extras", existing_info=pp.image.info, forced_filename=forced_filename)
|
||||
else:
|
||||
images.save_image(pp.image, path=outpath, extension=ext or opts.samples_format, info=info, short_filename=True, no_prompt=True, grid=False, pnginfo_section_name="extras", existing_info=pp.image.info)
|
||||
images.save_image(pp.image, path=outpath, extension=ext or opts.samples_format, info=info, grid=False, pnginfo_section_name="extras", existing_info=pp.image.info)
|
||||
if extras_mode != 2 or show_extras_results:
|
||||
outputs.append(pp.image)
|
||||
image.close()
|
||||
@@ -95,7 +95,7 @@ def run_postprocessing(extras_mode, image, image_folder: List[tempfile.NamedTemp
|
||||
return outputs, info, params
|
||||
|
||||
|
||||
def run_extras(extras_mode, resize_mode, image, image_folder, input_dir, output_dir, show_extras_results, gfpgan_visibility, codeformer_visibility, codeformer_weight, upscaling_resize, upscaling_resize_w, upscaling_resize_h, upscaling_crop, extras_upscaler_1, extras_upscaler_2, extras_upscaler_2_visibility, upscale_first: bool, save_output: bool = True): #pylint: disable=unused-argument
|
||||
def run_extras(extras_mode, resize_mode, image, image_folder, input_dir, output_dir, show_extras_results, gfpgan_visibility, codeformer_visibility, codeformer_weight, upscaling_resize, upscaling_resize_w, upscaling_resize_h, upscaling_crop, extras_upscaler_1, extras_upscaler_2, extras_upscaler_2_visibility, save_output: bool = True):
|
||||
"""old handler for API"""
|
||||
|
||||
args = scripts_manager.scripts_postproc.create_args_for_run({
|
||||
|
||||
+10
-47
@@ -1,10 +1,9 @@
|
||||
import os
|
||||
import json
|
||||
import time
|
||||
from contextlib import nullcontext
|
||||
import numpy as np
|
||||
from PIL import Image, ImageOps
|
||||
from modules import shared, devices, errors, images, scripts_manager, memstats, lowvram, script_callbacks, extra_networks, detailer, sd_models, sd_checkpoint, sd_vae, processing_helpers, timer, face_restoration, token_merge
|
||||
from modules import shared, devices, errors, images, scripts_manager, memstats, script_callbacks, extra_networks, detailer, sd_models, sd_checkpoint, sd_vae, processing_helpers, timer, face_restoration, token_merge
|
||||
from modules.sd_hijack_hypertile import context_hypertile_vae, context_hypertile_unet
|
||||
from modules.processing_class import StableDiffusionProcessing, StableDiffusionProcessingTxt2Img, StableDiffusionProcessingImg2Img, StableDiffusionProcessingControl, StableDiffusionProcessingVideo # pylint: disable=unused-import
|
||||
from modules.processing_info import create_infotext
|
||||
@@ -20,8 +19,6 @@ create_binary_mask = processing_helpers.create_binary_mask
|
||||
apply_overlay = processing_helpers.apply_overlay
|
||||
apply_color_correction = processing_helpers.apply_color_correction
|
||||
setup_color_correction = processing_helpers.setup_color_correction
|
||||
txt2img_image_conditioning = processing_helpers.txt2img_image_conditioning
|
||||
img2img_image_conditioning = processing_helpers.img2img_image_conditioning
|
||||
fix_seed = processing_helpers.fix_seed
|
||||
get_fixed_seed = processing_helpers.get_fixed_seed
|
||||
create_random_tensors = processing_helpers.create_random_tensors
|
||||
@@ -65,12 +62,6 @@ class Processed:
|
||||
self.job_timestamp = shared.state.job_timestamp
|
||||
self.clip_skip = p.clip_skip
|
||||
self.eta = p.eta
|
||||
self.ddim_discretize = p.ddim_discretize
|
||||
self.s_churn = p.s_churn
|
||||
self.s_tmin = p.s_tmin
|
||||
self.s_tmax = p.s_tmax
|
||||
self.s_noise = p.s_noise
|
||||
self.s_min_uncond = p.s_min_uncond
|
||||
self.prompt = self.prompt if type(self.prompt) != list else self.prompt[0]
|
||||
self.negative_prompt = self.negative_prompt if type(self.negative_prompt) != list else self.negative_prompt[0]
|
||||
self.seed = int(self.seed if type(self.seed) != list else self.seed[0]) if self.seed is not None else -1
|
||||
@@ -170,7 +161,7 @@ def process_images(p: StableDiffusionProcessing) -> Processed:
|
||||
if 'Model' not in shared.opts.cuda_compile:
|
||||
token_merge.apply_token_merging(p.sd_model)
|
||||
from modules import sd_hijack_freeu, para_attention, teacache
|
||||
sd_hijack_freeu.apply_freeu(p, not shared.native)
|
||||
sd_hijack_freeu.apply_freeu(p)
|
||||
para_attention.apply_first_block_cache()
|
||||
teacache.apply_teacache(p)
|
||||
|
||||
@@ -207,8 +198,6 @@ def process_images(p: StableDiffusionProcessing) -> Processed:
|
||||
shared.log.debug(f'Torch profile: {profile_args}')
|
||||
shared.profiler = torch.profiler.profile(**profile_args)
|
||||
shared.profiler.start()
|
||||
if not shared.native:
|
||||
shared.profiler.step()
|
||||
processed = process_images_inner(p)
|
||||
errors.profile_torch(shared.profiler, 'Process')
|
||||
else:
|
||||
@@ -279,25 +268,16 @@ def process_images_inner(p: StableDiffusionProcessing) -> Processed:
|
||||
else:
|
||||
assert p.prompt is not None
|
||||
|
||||
if not shared.native:
|
||||
import modules.sd_hijack # pylint: disable=redefined-outer-name
|
||||
modules.sd_hijack.model_hijack.apply_circular(p.tiling)
|
||||
modules.sd_hijack.model_hijack.clear_comments()
|
||||
comments = {}
|
||||
infotexts = []
|
||||
output_images = []
|
||||
|
||||
process_init(p)
|
||||
if not shared.native and os.path.exists(shared.opts.embeddings_dir) and not p.do_not_reload_embeddings:
|
||||
modules.sd_hijack.model_hijack.embedding_db.load_textual_inversion_embeddings(force_reload=False)
|
||||
if p.scripts is not None and isinstance(p.scripts, scripts_manager.ScriptRunner):
|
||||
p.scripts.process(p)
|
||||
|
||||
ema_scope_context = p.sd_model.ema_scope if not shared.native else nullcontext
|
||||
if not shared.native:
|
||||
shared.state.job_count = p.n_iter
|
||||
shared.state.batch_count = p.n_iter
|
||||
with devices.inference_context(), ema_scope_context():
|
||||
with devices.inference_context():
|
||||
t0 = time.time()
|
||||
if not hasattr(p, 'skip_init'):
|
||||
p.init(p.all_prompts, p.all_seeds, p.all_subseeds)
|
||||
@@ -316,9 +296,8 @@ def process_images_inner(p: StableDiffusionProcessing) -> Processed:
|
||||
shared.log.debug(f'Process interrupted: {n+1}/{p.n_iter}')
|
||||
break
|
||||
|
||||
if shared.native:
|
||||
from modules import ipadapter
|
||||
ipadapter.apply(shared.sd_model, p)
|
||||
from modules import ipadapter
|
||||
ipadapter.apply(shared.sd_model, p)
|
||||
if not hasattr(p, 'keep_prompts'):
|
||||
p.prompts = p.all_prompts[n * p.batch_size:(n+1) * p.batch_size]
|
||||
p.negative_prompts = p.all_negative_prompts[n * p.batch_size:(n+1) * p.batch_size]
|
||||
@@ -329,8 +308,6 @@ def process_images_inner(p: StableDiffusionProcessing) -> Processed:
|
||||
if len(p.prompts) == 0:
|
||||
break
|
||||
p.prompts, p.network_data = extra_networks.parse_prompts(p.prompts)
|
||||
if not shared.native:
|
||||
extra_networks.activate(p, p.network_data)
|
||||
if p.scripts is not None and isinstance(p.scripts, scripts_manager.ScriptRunner):
|
||||
p.scripts.process_batch(p, batch_number=n, prompts=p.prompts, seeds=p.seeds, subseeds=p.subseeds)
|
||||
|
||||
@@ -342,22 +319,13 @@ def process_images_inner(p: StableDiffusionProcessing) -> Processed:
|
||||
samples = processed.images
|
||||
infotexts += processed.infotexts
|
||||
if samples is None:
|
||||
if not shared.native:
|
||||
from modules.processing_original import process_original
|
||||
samples = process_original(p)
|
||||
elif shared.native:
|
||||
from modules.processing_diffusers import process_diffusers
|
||||
samples = process_diffusers(p)
|
||||
else:
|
||||
raise ValueError(f"Unknown backend {shared.backend}")
|
||||
from modules.processing_diffusers import process_diffusers
|
||||
samples = process_diffusers(p)
|
||||
timer.process.record('process')
|
||||
|
||||
if not shared.opts.keep_incomplete and shared.state.interrupted:
|
||||
samples = []
|
||||
|
||||
if not shared.native and (shared.cmd_opts.lowvram or shared.cmd_opts.medvram):
|
||||
lowvram.send_everything_to_cpu()
|
||||
devices.torch_gc()
|
||||
if p.scripts is not None and isinstance(p.scripts, scripts_manager.ScriptRunner):
|
||||
p.scripts.postprocess_batch(p, samples, batch_number=n)
|
||||
if p.scripts is not None and isinstance(p.scripts, scripts_manager.ScriptRunner):
|
||||
@@ -444,15 +412,11 @@ def process_images_inner(p: StableDiffusionProcessing) -> Processed:
|
||||
timer.process.record('post')
|
||||
del samples
|
||||
|
||||
if not shared.native:
|
||||
extra_networks.deactivate(p, p.network_data)
|
||||
|
||||
devices.torch_gc()
|
||||
|
||||
if hasattr(shared.sd_model, 'restore_pipeline') and shared.sd_model.restore_pipeline is not None:
|
||||
shared.sd_model.restore_pipeline()
|
||||
if shared.native: # reset pipeline for each iteration
|
||||
shared.sd_model = sd_models.set_diffuser_pipe(shared.sd_model, sd_models.DiffusersTaskType.TEXT_2_IMAGE)
|
||||
shared.sd_model = sd_models.set_diffuser_pipe(shared.sd_model, sd_models.DiffusersTaskType.TEXT_2_IMAGE)
|
||||
|
||||
t1 = time.time()
|
||||
|
||||
@@ -471,9 +435,8 @@ def process_images_inner(p: StableDiffusionProcessing) -> Processed:
|
||||
if shared.opts.grid_save:
|
||||
images.save_image(grid, p.outpath_grids, "", p.all_seeds[0], p.all_prompts[0], shared.opts.grid_format, info=grid_info, p=p, grid=True, suffix="-grid") # main save grid
|
||||
|
||||
if shared.native:
|
||||
from modules import ipadapter
|
||||
ipadapter.unapply(shared.sd_model, unload=getattr(p, 'ip_adapter_unload', False))
|
||||
from modules import ipadapter
|
||||
ipadapter.unapply(shared.sd_model, unload=getattr(p, 'ip_adapter_unload', False))
|
||||
|
||||
if shared.opts.include_mask:
|
||||
if shared.opts.mask_apply_overlay and p.overlay_images is not None and len(p.overlay_images) > 0:
|
||||
|
||||
@@ -3,7 +3,7 @@ import os
|
||||
import time
|
||||
import torch
|
||||
import numpy as np
|
||||
from modules import shared, devices, processing_correction, extra_networks, timer, prompt_parser_diffusers
|
||||
from modules import shared, devices, processing_correction, timer, prompt_parser_diffusers
|
||||
|
||||
|
||||
p = None
|
||||
@@ -69,8 +69,6 @@ def diffusers_callback(pipe, step: int = 0, timestep: int = 0, kwargs: dict = {}
|
||||
if shared.state.interrupted or shared.state.skipped:
|
||||
raise AssertionError('Interrupted...')
|
||||
time.sleep(0.1)
|
||||
if hasattr(p, "stepwise_lora") and shared.native:
|
||||
extra_networks.activate(p, step=step)
|
||||
if latents is None:
|
||||
return kwargs
|
||||
elif shared.opts.nan_skip:
|
||||
|
||||
+9
-108
@@ -4,12 +4,9 @@ import inspect
|
||||
import hashlib
|
||||
from typing import Any, Dict, List
|
||||
from dataclasses import dataclass, field
|
||||
import torch
|
||||
import numpy as np
|
||||
import cv2
|
||||
from PIL import Image, ImageOps
|
||||
from modules import shared, devices, images, scripts_manager, masking, sd_samplers, sd_models, processing_helpers
|
||||
from modules.sd_hijack_hypertile import hypertile_set
|
||||
from modules import shared, images, scripts_manager, masking, sd_models, processing_helpers
|
||||
|
||||
|
||||
debug = shared.log.trace if os.environ.get('SD_PROCESS_DEBUG', None) is not None else lambda *args, **kwargs: None
|
||||
@@ -258,8 +255,6 @@ class StableDiffusionProcessing:
|
||||
self.all_subseeds = None
|
||||
|
||||
# a1111 compatibility items
|
||||
if not shared.native:
|
||||
shared.opts.data['clip_skip'] = int(self.clip_skip) # for compatibility with a1111 sd_hijack_clip
|
||||
self.seed_enable_extras: bool = True
|
||||
self.is_using_inpainting_conditioning = False # a111 compatibility
|
||||
self.batch_index = 0
|
||||
@@ -268,8 +263,6 @@ class StableDiffusionProcessing:
|
||||
self.all_hr_prompts = []
|
||||
self.hr_negative_prompt = ''
|
||||
self.all_hr_negative_prompts = []
|
||||
self.truncate_x = 0
|
||||
self.truncate_y = 0
|
||||
self.comments = {}
|
||||
self.sampler = None
|
||||
self.nmask = None
|
||||
@@ -284,16 +277,6 @@ class StableDiffusionProcessing:
|
||||
self.script_args = script_args
|
||||
self.per_script_args = {}
|
||||
|
||||
# settings to processing
|
||||
self.ddim_discretize = shared.opts.ddim_discretize
|
||||
self.s_min_uncond = shared.opts.s_min_uncond
|
||||
self.s_churn = shared.opts.s_churn
|
||||
self.s_noise = shared.opts.s_noise
|
||||
self.s_min = shared.opts.s_min
|
||||
self.s_max = shared.opts.s_max
|
||||
self.s_tmin = shared.opts.s_tmin
|
||||
self.s_tmax = float('inf') # not representable as a standard ui option
|
||||
|
||||
# ip adapter
|
||||
self.ip_adapter_names = []
|
||||
self.ip_adapter_scales = [0.0]
|
||||
@@ -364,9 +347,6 @@ class StableDiffusionProcessing:
|
||||
def init(self, all_prompts=None, all_seeds=None, all_subseeds=None):
|
||||
pass
|
||||
|
||||
def sample(self, conditioning, unconditional_conditioning, seeds, subseeds, subseed_strength, prompts):
|
||||
raise NotImplementedError
|
||||
|
||||
def close(self):
|
||||
self.sampler = None
|
||||
self.scripts = None
|
||||
@@ -387,8 +367,7 @@ class StableDiffusionProcessingTxt2Img(StableDiffusionProcessing):
|
||||
super().__init__(**kwargs)
|
||||
|
||||
def init(self, all_prompts=None, all_seeds=None, all_subseeds=None):
|
||||
if shared.native:
|
||||
shared.sd_model = sd_models.set_diffuser_pipe(self.sd_model, sd_models.DiffusersTaskType.TEXT_2_IMAGE)
|
||||
shared.sd_model = sd_models.set_diffuser_pipe(self.sd_model, sd_models.DiffusersTaskType.TEXT_2_IMAGE)
|
||||
self.width = self.width or 1024
|
||||
self.height = self.height or 1024
|
||||
if all_prompts is not None:
|
||||
@@ -411,35 +390,11 @@ class StableDiffusionProcessingTxt2Img(StableDiffusionProcessing):
|
||||
elif self.hr_resize_x == 0:
|
||||
self.hr_upscale_to_x = self.hr_resize_y * self.width // self.height
|
||||
self.hr_upscale_to_y = self.hr_resize_y
|
||||
elif self.hr_resize_x > 0 and self.hr_resize_y > 0 and shared.native:
|
||||
elif self.hr_resize_x > 0 and self.hr_resize_y > 0:
|
||||
self.hr_upscale_to_x = self.hr_resize_x
|
||||
self.hr_upscale_to_y = self.hr_resize_y
|
||||
else:
|
||||
target_w = self.hr_resize_x
|
||||
target_h = self.hr_resize_y
|
||||
src_ratio = self.width / self.height
|
||||
dst_ratio = self.hr_resize_x / self.hr_resize_y
|
||||
if src_ratio < dst_ratio:
|
||||
self.hr_upscale_to_x = self.hr_resize_x
|
||||
self.hr_upscale_to_y = self.hr_resize_x * self.height // self.width
|
||||
else:
|
||||
self.hr_upscale_to_x = self.hr_resize_y * self.width // self.height
|
||||
self.hr_upscale_to_y = self.hr_resize_y
|
||||
self.truncate_x = (self.hr_upscale_to_x - target_w) // 8
|
||||
self.truncate_y = (self.hr_upscale_to_y - target_h) // 8
|
||||
if not shared.native: # diffusers are handled in processing_diffusers
|
||||
if (self.hr_upscale_to_x == self.width and self.hr_upscale_to_y == self.height) or upscaler is None or upscaler == 'None': # special case: the user has chosen to do nothing
|
||||
self.is_hr_pass = False
|
||||
return
|
||||
self.is_hr_pass = True
|
||||
hypertile_set(self, hr=True)
|
||||
shared.state.job_count = 2 * self.n_iter
|
||||
shared.log.debug(f'Init hires: upscaler="{self.hr_upscaler}" sampler="{self.hr_sampler_name}" resize={self.hr_resize_x}x{self.hr_resize_y} upscale={self.hr_upscale_to_x}x{self.hr_upscale_to_y}')
|
||||
|
||||
def sample(self, conditioning, unconditional_conditioning, seeds, subseeds, subseed_strength, prompts):
|
||||
from modules import processing_original
|
||||
return processing_original.sample_txt2img(self, conditioning, unconditional_conditioning, seeds, subseeds, subseed_strength, prompts)
|
||||
|
||||
|
||||
class StableDiffusionProcessingImg2Img(StableDiffusionProcessing):
|
||||
def __init__(self, **kwargs):
|
||||
@@ -452,9 +407,9 @@ class StableDiffusionProcessingImg2Img(StableDiffusionProcessing):
|
||||
self.width = int(8 * (self.init_images[0].width * self.scale_by // 8))
|
||||
if self.height is None or self.height == 0:
|
||||
self.height = int(8 * (self.init_images[0].height * self.scale_by // 8))
|
||||
if shared.native and getattr(self, 'image_mask', None) is not None:
|
||||
if getattr(self, 'image_mask', None) is not None:
|
||||
shared.sd_model = sd_models.set_diffuser_pipe(self.sd_model, sd_models.DiffusersTaskType.INPAINTING)
|
||||
elif shared.native and getattr(self, 'init_images', None) is not None:
|
||||
elif getattr(self, 'init_images', None) is not None:
|
||||
shared.sd_model = sd_models.set_diffuser_pipe(self.sd_model, sd_models.DiffusersTaskType.IMAGE_2_IMAGE)
|
||||
|
||||
if all_prompts is not None:
|
||||
@@ -463,14 +418,6 @@ class StableDiffusionProcessingImg2Img(StableDiffusionProcessing):
|
||||
self.all_seeds = all_seeds
|
||||
if all_subseeds is not None:
|
||||
self.all_subseeds = all_subseeds
|
||||
|
||||
if self.sampler_name == 'PLMS':
|
||||
self.sampler_name = 'Default'
|
||||
if not shared.native:
|
||||
self.sampler = sd_samplers.create_sampler(self.sampler_name, self.sd_model)
|
||||
if hasattr(self.sampler, "initialize"):
|
||||
self.sampler.initialize(self)
|
||||
|
||||
if self.image_mask is not None:
|
||||
self.ops.append('inpaint')
|
||||
elif hasattr(self, 'init_images') and self.init_images is not None:
|
||||
@@ -480,21 +427,10 @@ class StableDiffusionProcessingImg2Img(StableDiffusionProcessing):
|
||||
if self.image_mask is not None:
|
||||
if type(self.image_mask) == list:
|
||||
self.image_mask = self.image_mask[0]
|
||||
if not shared.native: # original way of processing mask
|
||||
self.image_mask = processing_helpers.create_binary_mask(self.image_mask)
|
||||
if self.inpainting_mask_invert:
|
||||
self.image_mask = ImageOps.invert(self.image_mask)
|
||||
if self.mask_blur > 0:
|
||||
np_mask = np.array(self.image_mask)
|
||||
kernel_size = 2 * int(2.5 * self.mask_blur + 0.5) + 1
|
||||
np_mask = cv2.GaussianBlur(np_mask, (kernel_size, 1), self.mask_blur)
|
||||
np_mask = cv2.GaussianBlur(np_mask, (1, kernel_size), self.mask_blur)
|
||||
self.image_mask = Image.fromarray(np_mask)
|
||||
elif shared.native:
|
||||
if 'control' in self.ops:
|
||||
self.image_mask = masking.run_mask(input_image=self.init_images, input_mask=self.image_mask, return_type='Grayscale', invert=self.inpainting_mask_invert==1) # blur/padding are handled in masking module
|
||||
else:
|
||||
self.image_mask = masking.run_mask(input_image=self.init_images, input_mask=self.image_mask, return_type='Grayscale', invert=self.inpainting_mask_invert==1, mask_blur=self.mask_blur, mask_padding=self.inpaint_full_res_padding) # old img2img
|
||||
if 'control' in self.ops:
|
||||
self.image_mask = masking.run_mask(input_image=self.init_images, input_mask=self.image_mask, return_type='Grayscale', invert=self.inpainting_mask_invert==1) # blur/padding are handled in masking module
|
||||
else:
|
||||
self.image_mask = masking.run_mask(input_image=self.init_images, input_mask=self.image_mask, return_type='Grayscale', invert=self.inpainting_mask_invert==1, mask_blur=self.mask_blur, mask_padding=self.inpaint_full_res_padding) # old img2img
|
||||
if self.inpaint_full_res: # mask only inpaint
|
||||
self.mask_for_overlay = self.image_mask
|
||||
mask = self.image_mask.convert('L')
|
||||
@@ -558,38 +494,6 @@ class StableDiffusionProcessingImg2Img(StableDiffusionProcessing):
|
||||
self.overlay_images = self.overlay_images * self.batch_size
|
||||
if self.color_corrections is not None and len(self.color_corrections) == 1:
|
||||
self.color_corrections = self.color_corrections * self.batch_size
|
||||
if shared.native:
|
||||
return # we've already set self.init_images and self.mask and we dont need any more processing
|
||||
elif not shared.native:
|
||||
self.init_images = [np.moveaxis((np.array(image).astype(np.float32) / 255.0), 2, 0) for image in self.init_images]
|
||||
if len(self.init_images) == 1:
|
||||
batch_images = np.expand_dims(self.init_images[0], axis=0).repeat(self.batch_size, axis=0)
|
||||
elif len(self.init_images) <= self.batch_size:
|
||||
batch_images = np.array(self.init_images)
|
||||
else:
|
||||
batch_images = np.array(self.init_images[:self.batch_size])
|
||||
image = torch.from_numpy(batch_images)
|
||||
image = 2. * image - 1.
|
||||
image = image.to(device=shared.device, dtype=devices.dtype_vae)
|
||||
self.init_latent = self.sd_model.get_first_stage_encoding(self.sd_model.encode_first_stage(image))
|
||||
if self.image_mask is not None:
|
||||
init_mask = latent_mask
|
||||
latmask = init_mask.convert('RGB').resize((self.init_latent.shape[3], self.init_latent.shape[2]))
|
||||
latmask = np.moveaxis(np.array(latmask, dtype=np.float32), 2, 0) / 255
|
||||
latmask = latmask[0]
|
||||
latmask = np.tile(latmask[None], (4, 1, 1))
|
||||
latmask = np.around(latmask)
|
||||
self.mask = torch.asarray(1.0 - latmask).to(device=shared.device, dtype=self.sd_model.dtype)
|
||||
self.nmask = torch.asarray(latmask).to(device=shared.device, dtype=self.sd_model.dtype)
|
||||
if self.inpainting_fill == 2:
|
||||
self.init_latent = self.init_latent * self.mask + processing_helpers.create_random_tensors(self.init_latent.shape[1:], all_seeds[0:self.init_latent.shape[0]]) * self.nmask
|
||||
elif self.inpainting_fill == 3:
|
||||
self.init_latent = self.init_latent * self.mask
|
||||
self.image_conditioning = processing_helpers.img2img_image_conditioning(self, image, self.init_latent, self.image_mask)
|
||||
|
||||
def sample(self, conditioning, unconditional_conditioning, seeds, subseeds, subseed_strength, prompts):
|
||||
from modules import processing_original
|
||||
return processing_original.sample_img2img(self, conditioning, unconditional_conditioning, seeds, subseeds, subseed_strength, prompts)
|
||||
|
||||
|
||||
class StableDiffusionProcessingControl(StableDiffusionProcessingImg2Img):
|
||||
@@ -597,9 +501,6 @@ class StableDiffusionProcessingControl(StableDiffusionProcessingImg2Img):
|
||||
debug(f'Process init: mode={self.__class__.__name__} kwargs={kwargs}') # pylint: disable=protected-access
|
||||
super().__init__(**kwargs)
|
||||
|
||||
def sample(self, conditioning, unconditional_conditioning, seeds, subseeds, subseed_strength, prompts): # abstract
|
||||
pass
|
||||
|
||||
def init_hr(self, scale = None, upscaler = None, force = False):
|
||||
scale = scale or self.scale_by
|
||||
upscaler = upscaler or self.resize_name
|
||||
|
||||
@@ -3,7 +3,6 @@ import time
|
||||
import math
|
||||
import random
|
||||
import warnings
|
||||
from einops import repeat, rearrange
|
||||
import torch
|
||||
import numpy as np
|
||||
import cv2
|
||||
@@ -247,105 +246,6 @@ def old_hires_fix_first_pass_dimensions(width, height):
|
||||
return width, height
|
||||
|
||||
|
||||
def txt2img_image_conditioning(p, x, width=None, height=None):
|
||||
width = width or p.width
|
||||
height = height or p.height
|
||||
if p.sd_model.model.conditioning_key in {'hybrid', 'concat'}: # Inpainting models
|
||||
image_conditioning = torch.zeros(x.shape[0], 3, height, width, device=x.device)
|
||||
image_conditioning = p.sd_model.get_first_stage_encoding(p.sd_model.encode_first_stage(image_conditioning))
|
||||
image_conditioning = torch.nn.functional.pad(image_conditioning, (0, 0, 0, 0, 1, 0), value=1.0) # pylint: disable=not-callable
|
||||
image_conditioning = image_conditioning.to(x.dtype)
|
||||
return image_conditioning
|
||||
elif p.sd_model.model.conditioning_key == "crossattn-adm": # UnCLIP models
|
||||
return x.new_zeros(x.shape[0], 2*p.sd_model.noise_augmentor.time_embed.dim, dtype=x.dtype, device=x.device)
|
||||
else:
|
||||
return x.new_zeros(x.shape[0], 5, 1, 1, dtype=x.dtype, device=x.device)
|
||||
|
||||
|
||||
def img2img_image_conditioning(p, source_image, latent_image, image_mask=None):
|
||||
from ldm.models.diffusion.ddpm import LatentDepth2ImageDiffusion
|
||||
source_image = devices.cond_cast_float(source_image)
|
||||
|
||||
def depth2img_image_conditioning(source_image):
|
||||
# Use the AddMiDaS helper to Format our source image to suit the MiDaS model
|
||||
from ldm.data.util import AddMiDaS
|
||||
transformer = AddMiDaS(model_type="dpt_hybrid")
|
||||
transformed = transformer({"jpg": rearrange(source_image[0], "c h w -> h w c")})
|
||||
midas_in = torch.from_numpy(transformed["midas_in"][None, ...]).to(device=shared.device)
|
||||
midas_in = repeat(midas_in, "1 ... -> n ...", n=p.batch_size)
|
||||
conditioning_image = p.sd_model.get_first_stage_encoding(p.sd_model.encode_first_stage(source_image))
|
||||
conditioning = torch.nn.functional.interpolate(
|
||||
p.sd_model.depth_model(midas_in),
|
||||
size=conditioning_image.shape[2:],
|
||||
mode="bicubic",
|
||||
align_corners=False,
|
||||
)
|
||||
(depth_min, depth_max) = torch.aminmax(conditioning)
|
||||
conditioning = 2. * (conditioning - depth_min) / (depth_max - depth_min) - 1.
|
||||
return conditioning
|
||||
|
||||
def edit_image_conditioning(source_image):
|
||||
conditioning_image = p.sd_model.encode_first_stage(source_image).mode()
|
||||
return conditioning_image
|
||||
|
||||
def unclip_image_conditioning(source_image):
|
||||
c_adm = p.sd_model.embedder(source_image)
|
||||
if p.sd_model.noise_augmentor is not None:
|
||||
noise_level = 0
|
||||
c_adm, noise_level_emb = p.sd_model.noise_augmentor(c_adm, noise_level=repeat(torch.tensor([noise_level]).to(c_adm.device), '1 -> b', b=c_adm.shape[0]))
|
||||
c_adm = torch.cat((c_adm, noise_level_emb), 1)
|
||||
return c_adm
|
||||
|
||||
def inpainting_image_conditioning(source_image, latent_image, image_mask=None):
|
||||
# Handle the different mask inputs
|
||||
if image_mask is not None:
|
||||
if torch.is_tensor(image_mask):
|
||||
conditioning_mask = image_mask
|
||||
else:
|
||||
conditioning_mask = np.array(image_mask.convert("L"))
|
||||
conditioning_mask = conditioning_mask.astype(np.float32) / 255.0
|
||||
conditioning_mask = torch.from_numpy(conditioning_mask[None, None])
|
||||
# Inpainting model uses a discretized mask as input, so we round to either 1.0 or 0.0
|
||||
conditioning_mask = torch.round(conditioning_mask)
|
||||
else:
|
||||
conditioning_mask = source_image.new_ones(1, 1, *source_image.shape[-2:])
|
||||
# Create another latent image, this time with a masked version of the original input.
|
||||
# Smoothly interpolate between the masked and unmasked latent conditioning image using a parameter.
|
||||
conditioning_mask = conditioning_mask.to(device=source_image.device, dtype=source_image.dtype)
|
||||
conditioning_image = torch.lerp(
|
||||
source_image,
|
||||
source_image * (1.0 - conditioning_mask),
|
||||
getattr(p, "inpainting_mask_weight", shared.opts.inpainting_mask_weight)
|
||||
)
|
||||
# Encode the new masked image using first stage of network.
|
||||
conditioning_image = p.sd_model.get_first_stage_encoding(p.sd_model.encode_first_stage(conditioning_image))
|
||||
# Create the concatenated conditioning tensor to be fed to `c_concat`
|
||||
conditioning_mask = torch.nn.functional.interpolate(conditioning_mask, size=latent_image.shape[-2:])
|
||||
conditioning_mask = conditioning_mask.expand(conditioning_image.shape[0], -1, -1, -1)
|
||||
image_conditioning = torch.cat([conditioning_mask, conditioning_image], dim=1)
|
||||
image_conditioning = image_conditioning.to(device=shared.device, dtype=source_image.dtype)
|
||||
return image_conditioning
|
||||
|
||||
def diffusers_image_conditioning(_source_image, latent_image, _image_mask=None):
|
||||
# shared.log.warning('Diffusers not implemented: img2img_image_conditioning')
|
||||
return latent_image.new_zeros(latent_image.shape[0], 5, 1, 1)
|
||||
|
||||
# HACK: Using introspection as the Depth2Image model doesn't appear to uniquely
|
||||
# identify itself with a field common to all models. The conditioning_key is also hybrid.
|
||||
if shared.native:
|
||||
return diffusers_image_conditioning(source_image, latent_image, image_mask)
|
||||
if isinstance(p.sd_model, LatentDepth2ImageDiffusion):
|
||||
return depth2img_image_conditioning(source_image)
|
||||
if hasattr(p.sd_model, 'cond_stage_key') and p.sd_model.cond_stage_key == "edit":
|
||||
return edit_image_conditioning(source_image)
|
||||
if hasattr(p.sampler, 'conditioning_key') and p.sampler.conditioning_key in {'hybrid', 'concat'}:
|
||||
return inpainting_image_conditioning(source_image, latent_image, image_mask=image_mask)
|
||||
if hasattr(p.sampler, 'conditioning_key') and p.sampler.conditioning_key == "crossattn-adm":
|
||||
return unclip_image_conditioning(source_image)
|
||||
# Dummy zero conditioning if we're not using inpainting or depth model.
|
||||
return latent_image.new_zeros(latent_image.shape[0], 5, 1, 1)
|
||||
|
||||
|
||||
def validate_sample(tensor):
|
||||
t0 = time.time()
|
||||
if not isinstance(tensor, np.ndarray) and not isinstance(tensor, torch.Tensor):
|
||||
@@ -359,7 +259,8 @@ def validate_sample(tensor):
|
||||
sample = tensor
|
||||
else:
|
||||
shared.log.warning(f'Decode: type={type(tensor)} unknown sample')
|
||||
sample = 255.0 * np.moveaxis(sample, 0, 2) if not shared.native else 255.0 * sample
|
||||
return tensor
|
||||
sample = 255.0 * sample
|
||||
with warnings.catch_warnings(record=True) as w:
|
||||
cast = sample.astype(np.uint8)
|
||||
if len(w) > 0:
|
||||
|
||||
@@ -42,6 +42,9 @@ def create_infotext(p: StableDiffusionProcessing, all_prompts=None, all_seeds=No
|
||||
ops = list(set(p.ops))
|
||||
args = {
|
||||
# basic
|
||||
"Pipeline": shared.sd_model.__class__.__name__,
|
||||
"TE": None if (shared.opts.sd_text_encoder is None or shared.opts.sd_text_encoder == 'Default') else shared.opts.sd_text_encoder,
|
||||
"UNet": None if (shared.opts.sd_unet is None or shared.opts.sd_unet == 'Default') else shared.opts.sd_unet,
|
||||
"Steps": p.steps,
|
||||
"Size": f"{p.width}x{p.height}" if hasattr(p, 'width') and hasattr(p, 'height') else None,
|
||||
"Sampler": p.sampler_name if p.sampler_name != 'Default' else None,
|
||||
@@ -60,7 +63,6 @@ def create_infotext(p: StableDiffusionProcessing, all_prompts=None, all_seeds=No
|
||||
"Styles": "; ".join(p.styles) if p.styles is not None and len(p.styles) > 0 else None,
|
||||
"App": 'SD.Next',
|
||||
"Version": git_commit,
|
||||
"Backend": 'Legacy' if not shared.native else None,
|
||||
"Parser": shared.opts.prompt_attention if shared.opts.prompt_attention != 'native' else None,
|
||||
"Comment": comment,
|
||||
"Operations": '; '.join(ops).replace('"', '') if len(p.ops) > 0 else 'none',
|
||||
@@ -80,12 +82,6 @@ def create_infotext(p: StableDiffusionProcessing, all_prompts=None, all_seeds=No
|
||||
args['Index'] = f'{p.iteration + 1}x{index + 1}'
|
||||
if grid is not None:
|
||||
args['Grid'] = grid
|
||||
if shared.native:
|
||||
args['Pipeline'] = shared.sd_model.__class__.__name__
|
||||
args['TE'] = None if (shared.opts.sd_text_encoder is None or shared.opts.sd_text_encoder == 'Default') else shared.opts.sd_text_encoder
|
||||
args['UNet'] = None if (shared.opts.sd_unet is None or shared.opts.sd_unet == 'Default') else shared.opts.sd_unet
|
||||
else:
|
||||
args['Pipeline'] = 'LDM'
|
||||
if 'txt2img' in p.ops:
|
||||
args["Variation seed"] = all_subseeds[index] if p.subseed_strength > 0 else None
|
||||
args["Variation strength"] = p.subseed_strength if p.subseed_strength > 0 else None
|
||||
|
||||
@@ -1,170 +0,0 @@
|
||||
import torch
|
||||
import numpy as np
|
||||
from PIL import Image
|
||||
from modules import shared, devices, processing, images, sd_vae, sd_samplers, processing_helpers, prompt_parser, token_merge
|
||||
from modules.sd_hijack_hypertile import hypertile_set
|
||||
|
||||
|
||||
create_binary_mask = processing_helpers.create_binary_mask
|
||||
apply_overlay = processing_helpers.apply_overlay
|
||||
apply_color_correction = processing_helpers.apply_color_correction
|
||||
setup_color_correction = processing_helpers.setup_color_correction
|
||||
images_tensor_to_samples = processing_helpers.images_tensor_to_samples
|
||||
txt2img_image_conditioning = processing_helpers.txt2img_image_conditioning
|
||||
img2img_image_conditioning = processing_helpers.img2img_image_conditioning
|
||||
get_fixed_seed = processing_helpers.get_fixed_seed
|
||||
create_random_tensors = processing_helpers.create_random_tensors
|
||||
decode_first_stage = processing_helpers.decode_first_stage
|
||||
old_hires_fix_first_pass_dimensions = processing_helpers.old_hires_fix_first_pass_dimensions
|
||||
validate_sample = processing_helpers.validate_sample
|
||||
|
||||
|
||||
def get_conds_with_caching(function, required_prompts, steps, cache):
|
||||
if cache[0] is not None and (required_prompts, steps) == cache[0]:
|
||||
return cache[1]
|
||||
with devices.autocast():
|
||||
cache[1] = function(shared.sd_model, required_prompts, steps)
|
||||
cache[0] = (required_prompts, steps)
|
||||
return cache[1]
|
||||
|
||||
def check_rollback_vae():
|
||||
if shared.cmd_opts.rollback_vae:
|
||||
if not torch.cuda.is_available():
|
||||
shared.log.error("Rollback VAE functionality requires compatible GPU")
|
||||
shared.cmd_opts.rollback_vae = False
|
||||
elif torch.__version__.startswith('1.') or torch.__version__.startswith('2.0'):
|
||||
shared.log.error("Rollback VAE functionality requires Torch 2.1 or higher")
|
||||
shared.cmd_opts.rollback_vae = False
|
||||
elif 0 < torch.cuda.get_device_capability()[0] < 8:
|
||||
shared.log.error('Rollback VAE functionality device capabilities not met')
|
||||
shared.cmd_opts.rollback_vae = False
|
||||
|
||||
|
||||
def process_original(p: processing.StableDiffusionProcessing):
|
||||
cached_uc = [None, None]
|
||||
cached_c = [None, None]
|
||||
sampler_config = sd_samplers.find_sampler_config(p.sampler_name)
|
||||
step_multiplier = 2 if sampler_config and sampler_config.options.get("second_order", False) else 1
|
||||
uc = get_conds_with_caching(prompt_parser.get_learned_conditioning, p.negative_prompts, p.steps * step_multiplier, cached_uc)
|
||||
c = get_conds_with_caching(prompt_parser.get_multicond_learned_conditioning, p.prompts, p.steps * step_multiplier, cached_c)
|
||||
with devices.without_autocast() if devices.unet_needs_upcast else devices.autocast():
|
||||
samples_ddim = p.sample(conditioning=c, unconditional_conditioning=uc, seeds=p.seeds, subseeds=p.subseeds, subseed_strength=p.subseed_strength, prompts=p.prompts)
|
||||
x_samples_ddim = [processing.decode_first_stage(p.sd_model, samples_ddim[i:i+1].to(dtype=devices.dtype_vae))[0].cpu() for i in range(samples_ddim.size(0))]
|
||||
try:
|
||||
for x in x_samples_ddim:
|
||||
devices.test_for_nans(x, "vae")
|
||||
except devices.NansException as e:
|
||||
check_rollback_vae()
|
||||
if not shared.opts.no_half and not shared.opts.no_half_vae and shared.cmd_opts.rollback_vae:
|
||||
shared.log.warning('Tensor with all NaNs was produced in VAE')
|
||||
devices.dtype_vae = torch.bfloat16
|
||||
vae_file, vae_source = sd_vae.resolve_vae(p.sd_model.sd_model_checkpoint)
|
||||
sd_vae.load_vae(p.sd_model, vae_file, vae_source)
|
||||
x_samples_ddim = [processing.decode_first_stage(p.sd_model, samples_ddim[i:i+1].to(dtype=devices.dtype_vae))[0].cpu() for i in range(samples_ddim.size(0))]
|
||||
for x in x_samples_ddim:
|
||||
devices.test_for_nans(x, "vae")
|
||||
else:
|
||||
raise e
|
||||
x_samples_ddim = torch.stack(x_samples_ddim).float()
|
||||
x_samples_ddim = torch.clamp((x_samples_ddim + 1.0) / 2.0, min=0.0, max=1.0)
|
||||
del samples_ddim
|
||||
return x_samples_ddim
|
||||
|
||||
|
||||
def sample_txt2img(p: processing.StableDiffusionProcessingTxt2Img, conditioning, unconditional_conditioning, seeds, subseeds, subseed_strength, prompts):
|
||||
p.ops.append('txt2img')
|
||||
hypertile_set(p)
|
||||
p.sampler = sd_samplers.create_sampler(p.sampler_name, p.sd_model)
|
||||
if hasattr(p.sampler, "initialize"):
|
||||
p.sampler.initialize(p)
|
||||
x = create_random_tensors([4, p.height // 8, p.width // 8], seeds=seeds, subseeds=subseeds, subseed_strength=p.subseed_strength, seed_resize_from_h=p.seed_resize_from_h, seed_resize_from_w=p.seed_resize_from_w, p=p)
|
||||
samples = p.sampler.sample(p, x, conditioning, unconditional_conditioning, image_conditioning=txt2img_image_conditioning(p, x))
|
||||
shared.state.nextjob()
|
||||
if not p.enable_hr or shared.state.interrupted or shared.state.skipped:
|
||||
return samples
|
||||
|
||||
p.init_hr()
|
||||
if p.is_hr_pass:
|
||||
prev_job = shared.state.job
|
||||
target_width = p.hr_upscale_to_x
|
||||
target_height = p.hr_upscale_to_y
|
||||
decoded_samples = None
|
||||
if shared.opts.samples_save and shared.opts.save_images_before_highres_fix and not p.do_not_save_samples:
|
||||
decoded_samples = decode_first_stage(p.sd_model, samples.to(dtype=devices.dtype_vae))
|
||||
decoded_samples = torch.clamp((decoded_samples + 1.0) / 2.0, min=0.0, max=1.0)
|
||||
for i, x_sample in enumerate(decoded_samples):
|
||||
x_sample = validate_sample(x_sample)
|
||||
image = Image.fromarray(x_sample)
|
||||
orig_extra_generation_params, orig_detailer = p.extra_generation_params, p.detailer_denabled
|
||||
p.extra_generation_params = {}
|
||||
p.detailer_denabled = False
|
||||
info = processing.create_infotext(p, p.all_prompts, p.all_seeds, p.all_subseeds, [], iteration=p.iteration, position_in_batch=i)
|
||||
p.extra_generation_params, p.detailer_enabled = orig_extra_generation_params, orig_detailer
|
||||
images.save_image(image, p.outpath_samples, "", seeds[i], prompts[i], shared.opts.samples_format, info=info, suffix="-before-hires")
|
||||
|
||||
if p.hr_upscaler.lower().startswith('latent'): # non-latent upscaling
|
||||
p.hr_force = True
|
||||
shared.state.job = 'Upscale'
|
||||
samples = images.resize_image(1, samples, target_width, target_height, upscaler_name=p.hr_upscaler)
|
||||
if getattr(p, "inpainting_mask_weight", shared.opts.inpainting_mask_weight) < 1.0:
|
||||
image_conditioning = img2img_image_conditioning(p, decode_first_stage(p.sd_model, samples.to(dtype=devices.dtype_vae)), samples)
|
||||
else:
|
||||
image_conditioning = txt2img_image_conditioning(p, samples.to(dtype=devices.dtype_vae))
|
||||
else:
|
||||
shared.state.job = 'Upscale'
|
||||
if decoded_samples is None:
|
||||
decoded_samples = decode_first_stage(p.sd_model, samples.to(dtype=devices.dtype_vae))
|
||||
decoded_samples = torch.clamp((decoded_samples + 1.0) / 2.0, min=0.0, max=1.0)
|
||||
batch_images = []
|
||||
for _i, x_sample in enumerate(decoded_samples):
|
||||
x_sample = validate_sample(x_sample)
|
||||
image = Image.fromarray(x_sample)
|
||||
image = images.resize_image(1, image, target_width, target_height, upscaler_name=p.hr_upscaler)
|
||||
image = np.array(image).astype(np.float32) / 255.0
|
||||
image = np.moveaxis(image, 2, 0)
|
||||
batch_images.append(image)
|
||||
resized_samples = torch.from_numpy(np.array(batch_images))
|
||||
resized_samples = resized_samples.to(device=shared.device, dtype=devices.dtype_vae)
|
||||
resized_samples = 2.0 * resized_samples - 1.0
|
||||
if shared.opts.sd_vae_sliced_encode and len(decoded_samples) > 1:
|
||||
samples = torch.stack([p.sd_model.get_first_stage_encoding(p.sd_model.encode_first_stage(torch.unsqueeze(resized_sample, 0)))[0] for resized_sample in resized_samples])
|
||||
else:
|
||||
samples = p.sd_model.get_first_stage_encoding(p.sd_model.encode_first_stage(resized_samples))
|
||||
image_conditioning = img2img_image_conditioning(p, resized_samples, samples)
|
||||
|
||||
if p.hr_force:
|
||||
shared.state.job = 'HiRes'
|
||||
if p.denoising_strength > 0:
|
||||
p.ops.append('hires')
|
||||
devices.torch_gc() # GC now before running the next img2img to prevent running out of memory
|
||||
p.sampler = sd_samplers.create_sampler(p.hr_sampler_name or p.sampler_name, p.sd_model)
|
||||
if hasattr(p.sampler, "initialize"):
|
||||
p.sampler.initialize(p)
|
||||
samples = samples[:, :, p.truncate_y//2:samples.shape[2]-(p.truncate_y+1)//2, p.truncate_x//2:samples.shape[3]-(p.truncate_x+1)//2]
|
||||
noise = create_random_tensors(samples.shape[1:], seeds=seeds, subseeds=subseeds, subseed_strength=subseed_strength, p=p)
|
||||
token_merge.apply_token_merging(p.sd_model)
|
||||
hypertile_set(p, hr=True)
|
||||
samples = p.sampler.sample_img2img(p, samples, noise, conditioning, unconditional_conditioning, steps=p.hr_second_pass_steps or p.steps, image_conditioning=image_conditioning)
|
||||
token_merge.apply_token_merging(p.sd_model)
|
||||
else:
|
||||
p.ops.append('upscale')
|
||||
x = None
|
||||
p.is_hr_pass = False
|
||||
shared.state.job = prev_job
|
||||
shared.state.nextjob()
|
||||
|
||||
return samples
|
||||
|
||||
|
||||
def sample_img2img(p, conditioning, unconditional_conditioning, seeds, subseeds, subseed_strength, prompts): # pylint: disable=unused-argument
|
||||
hypertile_set(p)
|
||||
x = create_random_tensors([4, p.height // 8, p.width // 8], seeds=seeds, subseeds=subseeds, subseed_strength=p.subseed_strength, seed_resize_from_h=p.seed_resize_from_h, seed_resize_from_w=p.seed_resize_from_w, p=p)
|
||||
x *= p.initial_noise_multiplier
|
||||
samples = p.sampler.sample_img2img(p, p.init_latent, x, conditioning, unconditional_conditioning, image_conditioning=p.image_conditioning)
|
||||
if p.mask is not None:
|
||||
samples = samples * p.nmask + p.init_latent * p.mask
|
||||
del x
|
||||
devices.torch_gc()
|
||||
shared.state.nextjob()
|
||||
|
||||
return samples
|
||||
@@ -84,18 +84,6 @@ def full_vqgan_decode(latents, model):
|
||||
return decoded
|
||||
|
||||
|
||||
def vae_interpose(latents, target):
|
||||
from modules.interposer import Interposer
|
||||
interposer = Interposer()
|
||||
converted = interposer.convert(src=shared.sd_model_type, dst=target, latents=latents)
|
||||
if converted is None:
|
||||
return None
|
||||
interposer.vae = interposer.vae.to(device=devices.device, dtype=devices.dtype)
|
||||
decoded = interposer.vae.decode(converted, return_dict=False)[0]
|
||||
interposer.vae = interposer.vae.to(device=devices.cpu)
|
||||
return decoded
|
||||
|
||||
|
||||
def full_vae_decode(latents, model):
|
||||
t0 = time.time()
|
||||
if not hasattr(model, 'vae') and hasattr(model, 'pipe'):
|
||||
@@ -107,10 +95,6 @@ def full_vae_decode(latents, model):
|
||||
devices.torch_gc(force=True)
|
||||
shared.mem_mon.reset()
|
||||
|
||||
# decoded = vae_interpose(latents, shared.opts.vae_interpose)
|
||||
# if decoded is not None:
|
||||
# return decoded
|
||||
|
||||
base_device = None
|
||||
if shared.opts.diffusers_move_unet and not getattr(model, 'has_accelerate', False):
|
||||
base_device = sd_models.move_base(model, devices.cpu)
|
||||
|
||||
@@ -355,8 +355,6 @@ def get_prompt_schedule(prompt, steps):
|
||||
|
||||
def get_tokens(pipe, msg, prompt):
|
||||
global token_dict, token_type # pylint: disable=global-statement
|
||||
if not shared.native:
|
||||
return 0
|
||||
if shared.sd_loaded and hasattr(pipe, 'tokenizer') and pipe.tokenizer is not None:
|
||||
if token_dict is None or token_type != shared.sd_model_type:
|
||||
token_type = shared.sd_model_type
|
||||
|
||||
@@ -112,11 +112,9 @@ def setup_model():
|
||||
list_models()
|
||||
sd_hijack_accelerate.hijack_hfhub()
|
||||
# sd_hijack_accelerate.hijack_torch_conv()
|
||||
if not shared.native:
|
||||
enable_midas_autodownload()
|
||||
|
||||
|
||||
def checkpoint_titles(use_short=False): # pylint: disable=unused-argument
|
||||
def checkpoint_titles():
|
||||
def convert(name):
|
||||
return int(name) if name.isdigit() else name.lower()
|
||||
def alphanumeric_key(key):
|
||||
@@ -268,12 +266,7 @@ def model_hash(filename):
|
||||
|
||||
|
||||
def select_checkpoint(op='model'):
|
||||
if op == 'dict':
|
||||
model_checkpoint = shared.opts.sd_model_dict
|
||||
elif op == 'refiner':
|
||||
model_checkpoint = shared.opts.data.get('sd_model_refiner', None)
|
||||
else:
|
||||
model_checkpoint = shared.opts.sd_model_checkpoint
|
||||
model_checkpoint = shared.opts.data.get('sd_model_refiner', None) if op == 'refiner' else shared.opts.data.get('sd_model_checkpoint', None)
|
||||
if len(model_checkpoint) < 3:
|
||||
return None
|
||||
if model_checkpoint is None or model_checkpoint == 'None':
|
||||
@@ -381,42 +374,6 @@ def read_metadata_from_safetensors(filename):
|
||||
return res
|
||||
|
||||
|
||||
def enable_midas_autodownload():
|
||||
"""
|
||||
Gives the ldm.modules.midas.api.load_model function automatic downloading.
|
||||
|
||||
When the 512-depth-ema model, and other future models like it, is loaded,
|
||||
it calls midas.api.load_model to load the associated midas depth model.
|
||||
This function applies a wrapper to download the model to the correct
|
||||
location automatically.
|
||||
"""
|
||||
from urllib import request
|
||||
import ldm.modules.midas.api
|
||||
midas_path = os.path.join(paths.models_path, 'midas')
|
||||
for k, v in ldm.modules.midas.api.ISL_PATHS.items():
|
||||
file_name = os.path.basename(v)
|
||||
ldm.modules.midas.api.ISL_PATHS[k] = os.path.join(midas_path, file_name)
|
||||
midas_urls = {
|
||||
"dpt_large": "https://github.com/intel-isl/DPT/releases/download/1_0/dpt_large-midas-2f21e586.pt",
|
||||
"dpt_hybrid": "https://github.com/intel-isl/DPT/releases/download/1_0/dpt_hybrid-midas-501f0c75.pt",
|
||||
"midas_v21": "https://github.com/AlexeyAB/MiDaS/releases/download/midas_dpt/midas_v21-f6b98070.pt",
|
||||
"midas_v21_small": "https://github.com/AlexeyAB/MiDaS/releases/download/midas_dpt/midas_v21_small-70d6b9c8.pt",
|
||||
}
|
||||
ldm.modules.midas.api.load_model_inner = ldm.modules.midas.api.load_model
|
||||
|
||||
def load_model_wrapper(model_type):
|
||||
path = ldm.modules.midas.api.ISL_PATHS[model_type]
|
||||
if not os.path.exists(path):
|
||||
if not os.path.exists(midas_path):
|
||||
os.mkdir(midas_path)
|
||||
shared.log.info(f"Downloading midas model weights for {model_type} to {path}")
|
||||
request.urlretrieve(midas_urls[model_type], path)
|
||||
shared.log.info(f"{model_type} downloaded")
|
||||
return ldm.modules.midas.api.load_model_inner(model_type)
|
||||
|
||||
ldm.modules.midas.api.load_model = load_model_wrapper
|
||||
|
||||
|
||||
def scrub_dict(dict_obj, keys):
|
||||
for key in list(dict_obj.keys()):
|
||||
if not isinstance(dict_obj, dict):
|
||||
|
||||
@@ -1,92 +0,0 @@
|
||||
import ldm.modules.encoders.modules
|
||||
import open_clip
|
||||
import torch
|
||||
import transformers.utils.hub
|
||||
|
||||
|
||||
class DisableInitialization:
|
||||
"""
|
||||
When an object of this class enters a `with` block, it starts:
|
||||
- preventing torch's layer initialization functions from working
|
||||
- changes CLIP and OpenCLIP to not download model weights
|
||||
- changes CLIP to not make requests to check if there is a new version of a file you already have
|
||||
|
||||
When it leaves the block, it reverts everything to how it was before.
|
||||
|
||||
Use it like this:
|
||||
```
|
||||
with DisableInitialization():
|
||||
do_things()
|
||||
```
|
||||
"""
|
||||
|
||||
def __init__(self, disable_clip=True):
|
||||
self.replaced = []
|
||||
self.disable_clip = disable_clip
|
||||
|
||||
def replace(self, obj, field, func):
|
||||
original = getattr(obj, field, None)
|
||||
if original is None:
|
||||
return None
|
||||
|
||||
self.replaced.append((obj, field, original))
|
||||
setattr(obj, field, func)
|
||||
|
||||
return original
|
||||
|
||||
def __enter__(self):
|
||||
def do_nothing(*args, **kwargs): # pylint: disable=unused-argument
|
||||
pass
|
||||
|
||||
def create_model_and_transforms_without_pretrained(*args, pretrained=None, **kwargs): # pylint: disable=unused-argument
|
||||
return self.create_model_and_transforms(*args, pretrained=None, **kwargs)
|
||||
|
||||
def CLIPTextModel_from_pretrained(pretrained_model_name_or_path, *model_args, **kwargs):
|
||||
res = self.CLIPTextModel_from_pretrained(None, *model_args, config=pretrained_model_name_or_path, state_dict={}, **kwargs)
|
||||
res.name_or_path = pretrained_model_name_or_path
|
||||
return res
|
||||
|
||||
def transformers_modeling_utils_load_pretrained_model(*args, **kwargs):
|
||||
args = args[0:3] + ('/', ) + args[4:] # resolved_archive_file; must set it to something to prevent what seems to be a bug
|
||||
return self.transformers_modeling_utils_load_pretrained_model(*args, **kwargs)
|
||||
|
||||
def transformers_utils_hub_get_file_from_cache(original, url, *args, **kwargs):
|
||||
|
||||
# this file is always 404, prevent making request
|
||||
if (url == 'https://huggingface.co/openai/clip-vit-large-patch14/resolve/main/added_tokens.json' or url == 'openai/clip-vit-large-patch14') and args[0] == 'added_tokens.json':
|
||||
return None
|
||||
|
||||
try:
|
||||
res = original(url, *args, local_files_only=True, **kwargs)
|
||||
if res is None:
|
||||
res = original(url, *args, local_files_only=False, **kwargs)
|
||||
return res
|
||||
except Exception:
|
||||
return original(url, *args, local_files_only=False, **kwargs)
|
||||
|
||||
def transformers_utils_hub_get_from_cache(url, *args, local_files_only=False, **kwargs): # pylint: disable=unused-argument
|
||||
return transformers_utils_hub_get_file_from_cache(self.transformers_utils_hub_get_from_cache, url, *args, **kwargs)
|
||||
|
||||
def transformers_tokenization_utils_base_cached_file(url, *args, local_files_only=False, **kwargs): # pylint: disable=unused-argument
|
||||
return transformers_utils_hub_get_file_from_cache(self.transformers_tokenization_utils_base_cached_file, url, *args, **kwargs)
|
||||
|
||||
def transformers_configuration_utils_cached_file(url, *args, local_files_only=False, **kwargs): # pylint: disable=unused-argument
|
||||
return transformers_utils_hub_get_file_from_cache(self.transformers_configuration_utils_cached_file, url, *args, **kwargs)
|
||||
|
||||
self.replace(torch.nn.init, 'kaiming_uniform_', do_nothing)
|
||||
self.replace(torch.nn.init, '_no_grad_normal_', do_nothing)
|
||||
self.replace(torch.nn.init, '_no_grad_uniform_', do_nothing)
|
||||
|
||||
if self.disable_clip:
|
||||
self.create_model_and_transforms = self.replace(open_clip, 'create_model_and_transforms', create_model_and_transforms_without_pretrained) # pylint: disable=attribute-defined-outside-init
|
||||
self.CLIPTextModel_from_pretrained = self.replace(ldm.modules.encoders.modules.CLIPTextModel, 'from_pretrained', CLIPTextModel_from_pretrained) # pylint: disable=attribute-defined-outside-init
|
||||
self.transformers_modeling_utils_load_pretrained_model = self.replace(transformers.modeling_utils.PreTrainedModel, '_load_pretrained_model', transformers_modeling_utils_load_pretrained_model) # pylint: disable=attribute-defined-outside-init
|
||||
self.transformers_tokenization_utils_base_cached_file = self.replace(transformers.tokenization_utils_base, 'cached_file', transformers_tokenization_utils_base_cached_file) # pylint: disable=attribute-defined-outside-init
|
||||
self.transformers_configuration_utils_cached_file = self.replace(transformers.configuration_utils, 'cached_file', transformers_configuration_utils_cached_file) # pylint: disable=attribute-defined-outside-init
|
||||
self.transformers_utils_hub_get_from_cache = self.replace(transformers.utils.hub, 'get_from_cache', transformers_utils_hub_get_from_cache) # pylint: disable=attribute-defined-outside-init
|
||||
|
||||
def __exit__(self, exc_type, exc_val, exc_tb):
|
||||
for obj, field, original in self.replaced:
|
||||
setattr(obj, field, original)
|
||||
|
||||
self.replaced.clear()
|
||||
+1
-298
@@ -1,298 +1,7 @@
|
||||
from types import MethodType, SimpleNamespace
|
||||
import io
|
||||
import contextlib
|
||||
from functools import wraps
|
||||
import torch
|
||||
from torch.nn.functional import silu
|
||||
import diffusers
|
||||
|
||||
from modules import shared
|
||||
if not shared.native:
|
||||
shared.log.warning('Importing LDM')
|
||||
stdout = io.StringIO()
|
||||
with contextlib.redirect_stdout(stdout):
|
||||
import ldm.modules.attention
|
||||
import ldm.modules.distributions.distributions
|
||||
import ldm.modules.diffusionmodules.model
|
||||
import ldm.modules.diffusionmodules.openaimodel
|
||||
import ldm.models.diffusion.ddim
|
||||
import ldm.models.diffusion.plms
|
||||
import ldm.modules.encoders.modules
|
||||
attention_CrossAttention_forward = ldm.modules.attention.CrossAttention.forward
|
||||
diffusionmodules_model_nonlinearity = ldm.modules.diffusionmodules.model.nonlinearity
|
||||
diffusionmodules_model_AttnBlock_forward = ldm.modules.diffusionmodules.model.AttnBlock.forward
|
||||
ldm.modules.attention.MemoryEfficientCrossAttention = ldm.modules.attention.CrossAttention
|
||||
ldm.modules.attention.BasicTransformerBlock.ATTENTION_MODES["softmax-xformers"] = ldm.modules.attention.CrossAttention
|
||||
# silence new console spam from SD2
|
||||
ldm.modules.attention.print = lambda *args: None
|
||||
ldm.modules.diffusionmodules.model.print = lambda *args: None
|
||||
|
||||
from modules import devices, sd_hijack_optimizations # pylint: disable=ungrouped-imports
|
||||
from modules.textual_inversion import textual_inversion
|
||||
from modules.hypernetworks import hypernetwork
|
||||
|
||||
current_optimizer = SimpleNamespace(**{ "name": "none" })
|
||||
|
||||
def apply_optimizations():
|
||||
undo_optimizations()
|
||||
from modules import sd_hijack_unet
|
||||
ldm.modules.diffusionmodules.model.nonlinearity = silu
|
||||
ldm.modules.diffusionmodules.openaimodel.th = sd_hijack_unet.th
|
||||
optimization_method = None
|
||||
can_use_sdp = hasattr(torch.nn.functional, "scaled_dot_product_attention") and callable(torch.nn.functional.scaled_dot_product_attention)
|
||||
if devices.device == torch.device("cpu"):
|
||||
if shared.opts.cross_attention_optimization == "Scaled-Dot-Product":
|
||||
shared.log.warning("Cross-attention: Scaled dot product is not available on CPU")
|
||||
can_use_sdp = False
|
||||
if shared.opts.cross_attention_optimization == "xFormers":
|
||||
shared.log.warning("Cross-attention: xFormers is not available on CPU")
|
||||
shared.xformers_available = False
|
||||
|
||||
shared.log.info(f"Cross-attention: optimization={shared.opts.cross_attention_optimization}")
|
||||
if shared.opts.cross_attention_optimization == "Disabled":
|
||||
optimization_method = 'none'
|
||||
if can_use_sdp and shared.opts.cross_attention_optimization == "Scaled-Dot-Product":
|
||||
optimization_method = 'sdp'
|
||||
if 'Memory attention' in shared.opts.sdp_options:
|
||||
ldm.modules.attention.CrossAttention.forward = sd_hijack_optimizations.scaled_dot_product_attention_forward
|
||||
ldm.modules.diffusionmodules.model.AttnBlock.forward = sd_hijack_optimizations.sdp_attnblock_forward
|
||||
else:
|
||||
ldm.modules.attention.CrossAttention.forward = sd_hijack_optimizations.scaled_dot_product_no_mem_attention_forward
|
||||
ldm.modules.diffusionmodules.model.AttnBlock.forward = sd_hijack_optimizations.sdp_no_mem_attnblock_forward
|
||||
if shared.xformers_available and shared.opts.cross_attention_optimization == "xFormers":
|
||||
ldm.modules.attention.CrossAttention.forward = sd_hijack_optimizations.xformers_attention_forward
|
||||
ldm.modules.diffusionmodules.model.AttnBlock.forward = sd_hijack_optimizations.xformers_attnblock_forward
|
||||
optimization_method = 'xformers'
|
||||
if shared.opts.cross_attention_optimization == "Sub-quadratic":
|
||||
ldm.modules.attention.CrossAttention.forward = sd_hijack_optimizations.sub_quad_attention_forward
|
||||
ldm.modules.diffusionmodules.model.AttnBlock.forward = sd_hijack_optimizations.sub_quad_attnblock_forward
|
||||
optimization_method = 'sub-quadratic'
|
||||
if shared.opts.cross_attention_optimization == "Split attention":
|
||||
ldm.modules.attention.CrossAttention.forward = sd_hijack_optimizations.split_cross_attention_forward_v1
|
||||
optimization_method = 'v1'
|
||||
if shared.opts.cross_attention_optimization == "InvokeAI's":
|
||||
ldm.modules.attention.CrossAttention.forward = sd_hijack_optimizations.split_cross_attention_forward_invokeAI
|
||||
optimization_method = 'invokeai'
|
||||
if shared.opts.cross_attention_optimization == "Doggettx's":
|
||||
ldm.modules.attention.CrossAttention.forward = sd_hijack_optimizations.split_cross_attention_forward
|
||||
ldm.modules.diffusionmodules.model.AttnBlock.forward = sd_hijack_optimizations.cross_attention_attnblock_forward
|
||||
optimization_method = 'doggettx'
|
||||
current_optimizer.name = optimization_method
|
||||
return optimization_method
|
||||
|
||||
|
||||
def undo_optimizations():
|
||||
if not shared.native:
|
||||
ldm.modules.attention.CrossAttention.forward = hypernetwork.attention_CrossAttention_forward
|
||||
ldm.modules.diffusionmodules.model.nonlinearity = diffusionmodules_model_nonlinearity
|
||||
ldm.modules.diffusionmodules.model.AttnBlock.forward = diffusionmodules_model_AttnBlock_forward
|
||||
|
||||
|
||||
def fix_checkpoint():
|
||||
"""checkpoints are now added and removed in embedding/hypernet code, since torch doesn't want
|
||||
checkpoints to be added when not training (there's a warning)"""
|
||||
pass # pylint: disable=unnecessary-pass
|
||||
|
||||
|
||||
def weighted_loss(sd_model, pred, target, mean=True):
|
||||
#Calculate the weight normally, but ignore the mean
|
||||
loss = sd_model._old_get_loss(pred, target, mean=False) # pylint: disable=protected-access
|
||||
|
||||
#Check if we have weights available
|
||||
weight = getattr(sd_model, '_custom_loss_weight', None)
|
||||
if weight is not None:
|
||||
loss *= weight
|
||||
|
||||
#Return the loss, as mean if specified
|
||||
return loss.mean() if mean else loss
|
||||
|
||||
def weighted_forward(sd_model, x, c, w, *args, **kwargs):
|
||||
try:
|
||||
#Temporarily append weights to a place accessible during loss calc
|
||||
sd_model._custom_loss_weight = w # pylint: disable=protected-access
|
||||
|
||||
#Replace 'get_loss' with a weight-aware one. Otherwise we need to reimplement 'forward' completely
|
||||
#Keep 'get_loss', but don't overwrite the previous old_get_loss if it's already set
|
||||
if not hasattr(sd_model, '_old_get_loss'):
|
||||
sd_model._old_get_loss = sd_model.get_loss # pylint: disable=protected-access
|
||||
sd_model.get_loss = MethodType(weighted_loss, sd_model)
|
||||
|
||||
#Run the standard forward function, but with the patched 'get_loss'
|
||||
return sd_model.forward(x, c, *args, **kwargs)
|
||||
finally:
|
||||
try:
|
||||
#Delete temporary weights if appended
|
||||
del sd_model._custom_loss_weight
|
||||
except AttributeError:
|
||||
pass
|
||||
|
||||
#If we have an old loss function, reset the loss function to the original one
|
||||
if hasattr(sd_model, '_old_get_loss'):
|
||||
sd_model.get_loss = sd_model._old_get_loss # pylint: disable=protected-access
|
||||
del sd_model._old_get_loss
|
||||
|
||||
def apply_weighted_forward(sd_model):
|
||||
#Add new function 'weighted_forward' that can be called to calc weighted loss
|
||||
sd_model.weighted_forward = MethodType(weighted_forward, sd_model)
|
||||
|
||||
def undo_weighted_forward(sd_model):
|
||||
try:
|
||||
del sd_model.weighted_forward
|
||||
except AttributeError:
|
||||
pass
|
||||
|
||||
|
||||
class StableDiffusionModelHijack:
|
||||
fixes = None
|
||||
comments = []
|
||||
layers = None
|
||||
circular_enabled = False
|
||||
clip = None
|
||||
optimization_method = None
|
||||
|
||||
embedding_db = textual_inversion.EmbeddingDatabase()
|
||||
|
||||
def __init__(self):
|
||||
self.embedding_db.add_embedding_dir(shared.opts.embeddings_dir)
|
||||
|
||||
def hijack(self, m):
|
||||
from modules import sd_hijack_clip, sd_hijack_open_clip, sd_hijack_unet, sd_hijack_xlmr, xlmr
|
||||
if type(m.cond_stage_model) == xlmr.BertSeriesModelWithTransformation:
|
||||
model_embeddings = m.cond_stage_model.roberta.embeddings
|
||||
model_embeddings.token_embedding = EmbeddingsWithFixes(model_embeddings.word_embeddings, self)
|
||||
m.cond_stage_model = sd_hijack_xlmr.FrozenXLMREmbedderWithCustomWords(m.cond_stage_model, self)
|
||||
|
||||
elif type(m.cond_stage_model) == ldm.modules.encoders.modules.FrozenCLIPEmbedder:
|
||||
model_embeddings = m.cond_stage_model.transformer.text_model.embeddings
|
||||
model_embeddings.token_embedding = EmbeddingsWithFixes(model_embeddings.token_embedding, self)
|
||||
m.cond_stage_model = sd_hijack_clip.FrozenCLIPEmbedderWithCustomWords(m.cond_stage_model, self)
|
||||
|
||||
elif type(m.cond_stage_model) == ldm.modules.encoders.modules.FrozenOpenCLIPEmbedder:
|
||||
m.cond_stage_model.model.token_embedding = EmbeddingsWithFixes(m.cond_stage_model.model.token_embedding, self)
|
||||
m.cond_stage_model = sd_hijack_open_clip.FrozenOpenCLIPEmbedderWithCustomWords(m.cond_stage_model, self)
|
||||
|
||||
apply_weighted_forward(m)
|
||||
if m.cond_stage_key == "edit":
|
||||
sd_hijack_unet.hijack_ddpm_edit()
|
||||
|
||||
if "Model" in shared.opts.ipex_optimize and not shared.native:
|
||||
try:
|
||||
import intel_extension_for_pytorch as ipex # pylint: disable=import-error, unused-import
|
||||
m.model.eval()
|
||||
m.model.training = False
|
||||
m.model = ipex.optimize(m.model, dtype=devices.dtype, inplace=True, weights_prepack=False) # pylint: disable=attribute-defined-outside-init
|
||||
shared.log.info("Applied IPEX Optimize.")
|
||||
except Exception as err:
|
||||
shared.log.warning(f"IPEX Optimize not supported: {err}")
|
||||
|
||||
if "Model" in shared.opts.cuda_compile and shared.opts.cuda_compile_backend != 'none' and not shared.native:
|
||||
try:
|
||||
import logging
|
||||
shared.log.info(f"Compiling pipeline={m.model.__class__.__name__} mode={shared.opts.cuda_compile_backend}")
|
||||
import torch._dynamo # pylint: disable=unused-import,redefined-outer-name
|
||||
log_level = logging.WARNING if shared.opts.cuda_compile_verbose else logging.CRITICAL # pylint: disable=protected-access
|
||||
if hasattr(torch, '_logging'):
|
||||
torch._logging.set_logs(dynamo=log_level, aot=log_level, inductor=log_level) # pylint: disable=protected-access
|
||||
torch._dynamo.config.verbose = shared.opts.cuda_compile_verbose # pylint: disable=protected-access
|
||||
torch._dynamo.config.suppress_errors = shared.opts.cuda_compile_errors # pylint: disable=protected-access
|
||||
torch.backends.cudnn.benchmark = True
|
||||
if shared.opts.cuda_compile_backend == 'hidet':
|
||||
import hidet # pylint: disable=import-error
|
||||
hidet.torch.dynamo_config.use_tensor_core(True)
|
||||
hidet.torch.dynamo_config.search_space(2)
|
||||
m.model = torch.compile(m.model, mode=shared.opts.cuda_compile_mode, backend=shared.opts.cuda_compile_backend, fullgraph=shared.opts.cuda_compile_fullgraph, dynamic=False)
|
||||
shared.log.info("Model complilation done.")
|
||||
except Exception as err:
|
||||
shared.log.warning(f"Model compile not supported: {err}")
|
||||
finally:
|
||||
from installer import setup_logging
|
||||
setup_logging()
|
||||
|
||||
self.optimization_method = apply_optimizations()
|
||||
self.clip = m.cond_stage_model
|
||||
|
||||
def flatten(el):
|
||||
flattened = [flatten(children) for children in el.children()]
|
||||
res = [el]
|
||||
for c in flattened:
|
||||
res += c
|
||||
return res
|
||||
|
||||
self.layers = flatten(m)
|
||||
|
||||
def undo_hijack(self, m):
|
||||
from modules import sd_hijack_clip, sd_hijack_open_clip, xlmr
|
||||
if not hasattr(m, 'cond_stage_model'):
|
||||
return # not ldm model
|
||||
if type(m.cond_stage_model) == xlmr.BertSeriesModelWithTransformation:
|
||||
m.cond_stage_model = m.cond_stage_model.wrapped
|
||||
elif type(m.cond_stage_model) == sd_hijack_clip.FrozenCLIPEmbedderWithCustomWords:
|
||||
m.cond_stage_model = m.cond_stage_model.wrapped
|
||||
model_embeddings = m.cond_stage_model.transformer.text_model.embeddings
|
||||
if type(model_embeddings.token_embedding) == EmbeddingsWithFixes:
|
||||
model_embeddings.token_embedding = model_embeddings.token_embedding.wrapped
|
||||
elif type(m.cond_stage_model) == sd_hijack_open_clip.FrozenOpenCLIPEmbedderWithCustomWords:
|
||||
m.cond_stage_model.wrapped.model.token_embedding = m.cond_stage_model.wrapped.model.token_embedding.wrapped
|
||||
m.cond_stage_model = m.cond_stage_model.wrapped
|
||||
undo_optimizations()
|
||||
undo_weighted_forward(m)
|
||||
self.apply_circular(False)
|
||||
self.layers = None
|
||||
self.clip = None
|
||||
|
||||
def apply_circular(self, enable):
|
||||
if self.circular_enabled == enable:
|
||||
return
|
||||
self.circular_enabled = enable
|
||||
for layer in [layer for layer in self.layers if type(layer) == torch.nn.Conv2d]:
|
||||
layer.padding_mode = 'circular' if enable else 'zeros'
|
||||
|
||||
def clear_comments(self):
|
||||
self.comments = []
|
||||
|
||||
def get_prompt_lengths(self, text):
|
||||
if self.clip is None:
|
||||
return 0, 0
|
||||
_chunks, token_count = self.clip.process_texts([text])
|
||||
return token_count, self.clip.get_target_prompt_token_count(token_count)
|
||||
|
||||
|
||||
class EmbeddingsWithFixes(torch.nn.Module):
|
||||
def __init__(self, wrapped, embeddings):
|
||||
super().__init__()
|
||||
self.wrapped = wrapped
|
||||
self.embeddings = embeddings
|
||||
|
||||
def forward(self, input_ids):
|
||||
batch_fixes = self.embeddings.fixes
|
||||
self.embeddings.fixes = None
|
||||
inputs_embeds = self.wrapped(input_ids)
|
||||
|
||||
if batch_fixes is None or len(batch_fixes) == 0 or max([len(x) for x in batch_fixes]) == 0:
|
||||
return inputs_embeds
|
||||
|
||||
vecs = []
|
||||
for fixes, tensor in zip(batch_fixes, inputs_embeds):
|
||||
for offset, embedding in fixes:
|
||||
emb = devices.cond_cast_unet(embedding.vec)
|
||||
emb_len = min(tensor.shape[0] - offset - 1, emb.shape[0])
|
||||
tensor = torch.cat([tensor[0:offset + 1], emb[0:emb_len], tensor[offset + 1 + emb_len:]])
|
||||
|
||||
vecs.append(tensor)
|
||||
|
||||
return torch.stack(vecs)
|
||||
|
||||
|
||||
def add_circular_option_to_conv_2d():
|
||||
conv2d_constructor = torch.nn.Conv2d.__init__
|
||||
|
||||
def conv2d_constructor_circular(self, *args, **kwargs):
|
||||
return conv2d_constructor(self, *args, padding_mode='circular', **kwargs)
|
||||
|
||||
torch.nn.Conv2d.__init__ = conv2d_constructor_circular
|
||||
|
||||
|
||||
model_hijack = StableDiffusionModelHijack()
|
||||
from modules import devices # pylint: disable=ungrouped-imports
|
||||
|
||||
|
||||
def register_buffer(self, name, attr):
|
||||
@@ -307,12 +16,6 @@ def register_buffer(self, name, attr):
|
||||
setattr(self, name, attr)
|
||||
|
||||
|
||||
if not shared.native:
|
||||
ldm.models.diffusion.ddim.DDIMSampler.register_buffer = register_buffer
|
||||
ldm.models.diffusion.plms.PLMSSampler.register_buffer = register_buffer
|
||||
ldm.modules.distributions.distributions.DiagonalGaussianDistribution.sample = lambda self: self.mean.to(self.parameters.dtype) + self.std.to(self.parameters.dtype) * torch.randn(self.mean.shape, dtype=self.parameters.dtype).to(device=self.parameters.device)
|
||||
|
||||
|
||||
# Upcast BF16 to FP32
|
||||
original_fft_fftn = torch.fft.fftn
|
||||
@wraps(torch.fft.fftn)
|
||||
|
||||
@@ -1,45 +0,0 @@
|
||||
from torch.utils.checkpoint import checkpoint
|
||||
|
||||
import ldm.modules.attention
|
||||
import ldm.modules.diffusionmodules.openaimodel
|
||||
|
||||
|
||||
def BasicTransformerBlock_forward(self, x, context=None):
|
||||
return checkpoint(self._forward, x, context) # pylint: disable=protected-access
|
||||
|
||||
|
||||
def AttentionBlock_forward(self, x):
|
||||
return checkpoint(self._forward, x) # pylint: disable=protected-access
|
||||
|
||||
|
||||
def ResBlock_forward(self, x, emb):
|
||||
return checkpoint(self._forward, x, emb) # pylint: disable=protected-access
|
||||
|
||||
|
||||
stored = []
|
||||
|
||||
|
||||
def add():
|
||||
if len(stored) != 0:
|
||||
return
|
||||
|
||||
stored.extend([
|
||||
ldm.modules.attention.BasicTransformerBlock.forward,
|
||||
ldm.modules.diffusionmodules.openaimodel.ResBlock.forward,
|
||||
ldm.modules.diffusionmodules.openaimodel.AttentionBlock.forward
|
||||
])
|
||||
|
||||
ldm.modules.attention.BasicTransformerBlock.forward = BasicTransformerBlock_forward
|
||||
ldm.modules.diffusionmodules.openaimodel.ResBlock.forward = ResBlock_forward
|
||||
ldm.modules.diffusionmodules.openaimodel.AttentionBlock.forward = AttentionBlock_forward
|
||||
|
||||
|
||||
def remove():
|
||||
if len(stored) == 0:
|
||||
return
|
||||
|
||||
ldm.modules.attention.BasicTransformerBlock.forward = stored[0]
|
||||
ldm.modules.diffusionmodules.openaimodel.ResBlock.forward = stored[1]
|
||||
ldm.modules.diffusionmodules.openaimodel.AttentionBlock.forward = stored[2]
|
||||
|
||||
stored.clear()
|
||||
@@ -1,256 +0,0 @@
|
||||
import math
|
||||
from collections import namedtuple
|
||||
import torch
|
||||
from modules import prompt_parser, devices, sd_hijack
|
||||
from modules.shared import opts
|
||||
|
||||
|
||||
class PromptChunk:
|
||||
"""
|
||||
This object contains token ids, weight (multipliers:1.4) and textual inversion embedding info for a chunk of prompt.
|
||||
If a prompt is short, it is represented by one PromptChunk, otherwise, multiple are necessary.
|
||||
Each PromptChunk contains an exact amount of tokens - 77, which includes one for start and end token,
|
||||
so just 75 tokens from prompt.
|
||||
"""
|
||||
def __init__(self):
|
||||
self.tokens = []
|
||||
self.multipliers = []
|
||||
self.fixes = []
|
||||
|
||||
|
||||
PromptChunkFix = namedtuple('PromptChunkFix', ['offset', 'embedding'])
|
||||
"""An object of this type is a marker showing that textual inversion embedding's vectors have to placed at offset in the prompt
|
||||
chunk. Thos objects are found in PromptChunk.fixes and, are placed into FrozenCLIPEmbedderWithCustomWordsBase.hijack.fixes, and finally
|
||||
are applied by sd_hijack.EmbeddingsWithFixes's forward function."""
|
||||
|
||||
|
||||
class FrozenCLIPEmbedderWithCustomWordsBase(torch.nn.Module):
|
||||
"""A pytorch module that is a wrapper for FrozenCLIPEmbedder module. it enhances FrozenCLIPEmbedder, making it possible to
|
||||
have unlimited prompt length and assign weights to tokens in prompt.
|
||||
"""
|
||||
def __init__(self, wrapped, hijack):
|
||||
super().__init__()
|
||||
self.wrapped = wrapped
|
||||
"""Original FrozenCLIPEmbedder module; can also be FrozenOpenCLIPEmbedder or xlmr.BertSeriesModelWithTransformation,
|
||||
depending on model."""
|
||||
self.hijack: sd_hijack.StableDiffusionModelHijack = hijack
|
||||
self.chunk_length = 75
|
||||
|
||||
def empty_chunk(self):
|
||||
"""creates an empty PromptChunk and returns it"""
|
||||
chunk = PromptChunk()
|
||||
chunk.tokens = [self.id_start] + [self.id_end] * (self.chunk_length + 1)
|
||||
chunk.multipliers = [1.0] * (self.chunk_length + 2)
|
||||
return chunk
|
||||
|
||||
def get_target_prompt_token_count(self, token_count):
|
||||
"""returns the maximum number of tokens a prompt of a known length can have before it requires one more PromptChunk to be represented"""
|
||||
return math.ceil(max(token_count, 1) / self.chunk_length) * self.chunk_length
|
||||
|
||||
def tokenize(self, texts):
|
||||
"""Converts a batch of texts into a batch of token ids"""
|
||||
raise NotImplementedError
|
||||
|
||||
def encode_with_transformers(self, tokens):
|
||||
"""
|
||||
converts a batch of token ids (in python lists) into a single tensor with numeric respresentation of those tokens;
|
||||
All python lists with tokens are assumed to have same length, usually 77.
|
||||
if input is a list with B elements and each element has T tokens, expected output shape is (B, T, C), where C depends on
|
||||
model - can be 768 and 1024.
|
||||
Among other things, this call will read self.hijack.fixes, apply it to its inputs, and clear it (setting it to None).
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
def encode_embedding_init_text(self, init_text, nvpt):
|
||||
"""Converts text into a tensor with this text's tokens' embeddings. Note that those are embeddings before they are passed through
|
||||
transformers. nvpt is used as a maximum length in tokens. If text produces less teokens than nvpt, only this many is returned."""
|
||||
raise NotImplementedError
|
||||
|
||||
def tokenize_line(self, line):
|
||||
"""
|
||||
this transforms a single prompt into a list of PromptChunk objects - as many as needed to
|
||||
represent the prompt.
|
||||
Returns the list and the total number of tokens in the prompt.
|
||||
"""
|
||||
parsed = prompt_parser.parse_prompt_attention(line)
|
||||
tokenized = self.tokenize([text for text, _ in parsed])
|
||||
chunks = []
|
||||
chunk = PromptChunk()
|
||||
token_count = 0
|
||||
last_comma = -1
|
||||
|
||||
def next_chunk(is_last=False):
|
||||
"""puts current chunk into the list of results and produces the next one - empty;
|
||||
if is_last is true, tokens <end-of-text> tokens at the end won't add to token_count"""
|
||||
nonlocal token_count
|
||||
nonlocal last_comma
|
||||
nonlocal chunk
|
||||
if is_last:
|
||||
token_count += len(chunk.tokens)
|
||||
else:
|
||||
token_count += self.chunk_length
|
||||
to_add = self.chunk_length - len(chunk.tokens)
|
||||
if to_add > 0:
|
||||
chunk.tokens += [self.id_end] * to_add
|
||||
chunk.multipliers += [1.0] * to_add
|
||||
chunk.tokens = [self.id_start] + chunk.tokens + [self.id_end]
|
||||
chunk.multipliers = [1.0] + chunk.multipliers + [1.0]
|
||||
last_comma = -1
|
||||
chunks.append(chunk)
|
||||
chunk = PromptChunk()
|
||||
|
||||
for tokens, (text, weight) in zip(tokenized, parsed):
|
||||
if text == 'BREAK' and weight == -1:
|
||||
next_chunk()
|
||||
continue
|
||||
position = 0
|
||||
while position < len(tokens):
|
||||
token = tokens[position]
|
||||
if token == self.comma_token:
|
||||
last_comma = len(chunk.tokens)
|
||||
# this is when we are at the end of alloted 75 tokens for the current chunk, and the current token is not a comma. opts.comma_padding_backtrack
|
||||
# is a setting that specifies that if there is a comma nearby, the text after the comma should be moved out of this chunk and into the next.
|
||||
elif opts.comma_padding_backtrack != 0 and len(chunk.tokens) == self.chunk_length and last_comma != -1 and len(chunk.tokens) - last_comma <= opts.comma_padding_backtrack:
|
||||
break_location = last_comma + 1
|
||||
reloc_tokens = chunk.tokens[break_location:]
|
||||
reloc_mults = chunk.multipliers[break_location:]
|
||||
chunk.tokens = chunk.tokens[:break_location]
|
||||
chunk.multipliers = chunk.multipliers[:break_location]
|
||||
next_chunk()
|
||||
chunk.tokens = reloc_tokens
|
||||
chunk.multipliers = reloc_mults
|
||||
if len(chunk.tokens) == self.chunk_length:
|
||||
next_chunk()
|
||||
embedding, embedding_length_in_tokens = self.hijack.embedding_db.find_embedding_at_position(tokens, position)
|
||||
if embedding is None:
|
||||
chunk.tokens.append(token)
|
||||
chunk.multipliers.append(weight)
|
||||
position += 1
|
||||
continue
|
||||
emb_len = int(embedding.vec.shape[0])
|
||||
if len(chunk.tokens) + emb_len > self.chunk_length:
|
||||
next_chunk()
|
||||
chunk.fixes.append(PromptChunkFix(len(chunk.tokens), embedding))
|
||||
chunk.tokens += [0] * emb_len
|
||||
chunk.multipliers += [weight] * emb_len
|
||||
position += embedding_length_in_tokens
|
||||
if len(chunk.tokens) > 0 or len(chunks) == 0:
|
||||
next_chunk(is_last=True)
|
||||
return chunks, token_count
|
||||
|
||||
def process_texts(self, texts):
|
||||
"""
|
||||
Accepts a list of texts and calls tokenize_line() on each, with cache. Returns the list of results and maximum
|
||||
length, in tokens, of all texts.
|
||||
"""
|
||||
token_count = 0
|
||||
cache = {}
|
||||
batch_chunks = []
|
||||
for line in texts:
|
||||
if line in cache:
|
||||
chunks = cache[line]
|
||||
else:
|
||||
chunks, current_token_count = self.tokenize_line(line)
|
||||
token_count = max(current_token_count, token_count)
|
||||
cache[line] = chunks
|
||||
batch_chunks.append(chunks)
|
||||
return batch_chunks, token_count
|
||||
|
||||
def forward(self, texts):
|
||||
"""
|
||||
Accepts an array of texts; Passes texts through transformers network to create a tensor with numerical representation of those texts.
|
||||
Returns a tensor with shape of (B, T, C), where B is length of the array; T is length, in tokens, of texts (including padding) - T will
|
||||
be a multiple of 77; and C is dimensionality of each token - for SD1 it's 768, and for SD2 it's 1024.
|
||||
An example shape returned by this function can be: (2, 77, 768).
|
||||
Webui usually sends just one text at a time through this function - the only time when texts is an array with more than one elemenet
|
||||
is when you do prompt editing: "a picture of a [cat:dog:0.4] eating ice cream"
|
||||
"""
|
||||
batch_chunks, _token_count = self.process_texts(texts)
|
||||
used_embeddings = {}
|
||||
chunk_count = max([len(x) for x in batch_chunks])
|
||||
zs = []
|
||||
for i in range(chunk_count):
|
||||
batch_chunk = [chunks[i] if i < len(chunks) else self.empty_chunk() for chunks in batch_chunks]
|
||||
tokens = [x.tokens for x in batch_chunk]
|
||||
multipliers = [x.multipliers for x in batch_chunk]
|
||||
self.hijack.fixes = [x.fixes for x in batch_chunk]
|
||||
for fixes in self.hijack.fixes:
|
||||
for _position, embedding in fixes:
|
||||
used_embeddings[embedding.name] = embedding
|
||||
z = self.process_tokens(tokens, multipliers)
|
||||
zs.append(z)
|
||||
self.hijack.embedding_db.embeddings_used = list(used_embeddings)
|
||||
return torch.hstack(zs)
|
||||
|
||||
def process_tokens(self, remade_batch_tokens, batch_multipliers):
|
||||
"""
|
||||
sends one single prompt chunk to be encoded by transformers neural network.
|
||||
remade_batch_tokens is a batch of tokens - a list, where every element is a list of tokens; usually
|
||||
there are exactly 77 tokens in the list. batch_multipliers is the same but for multipliers instead of tokens.
|
||||
Multipliers are used to give more or less weight to the outputs of transformers network. Each multiplier
|
||||
corresponds to one token.
|
||||
"""
|
||||
tokens = torch.asarray(remade_batch_tokens).to(devices.device)
|
||||
# this is for SD2: SD1 uses the same token for padding and end of text, while SD2 uses different ones.
|
||||
if self.id_end != self.id_pad:
|
||||
for batch_pos in range(len(remade_batch_tokens)):
|
||||
index = remade_batch_tokens[batch_pos].index(self.id_end)
|
||||
tokens[batch_pos, index+1:tokens.shape[1]] = self.id_pad
|
||||
|
||||
z = self.encode_with_transformers(tokens)
|
||||
# restoring original mean is likely not correct, but it seems to work well to prevent artifacts that happen otherwise
|
||||
batch_multipliers = torch.asarray(batch_multipliers).to(devices.device)
|
||||
if opts.prompt_mean_norm:
|
||||
original_mean = z.mean()
|
||||
z = z * batch_multipliers.reshape(batch_multipliers.shape + (1,)).expand(z.shape)
|
||||
new_mean = z.mean()
|
||||
z = z * (original_mean / new_mean)
|
||||
else:
|
||||
z = z * batch_multipliers.reshape(batch_multipliers.shape + (1,)).expand(z.shape)
|
||||
return z
|
||||
|
||||
|
||||
class FrozenCLIPEmbedderWithCustomWords(FrozenCLIPEmbedderWithCustomWordsBase):
|
||||
def __init__(self, wrapped, hijack):
|
||||
super().__init__(wrapped, hijack)
|
||||
self.tokenizer = wrapped.tokenizer
|
||||
vocab = self.tokenizer.get_vocab()
|
||||
self.comma_token = vocab.get(',</w>', None)
|
||||
self.token_mults = {}
|
||||
tokens_with_parens = [(k, v) for k, v in vocab.items() if '(' in k or ')' in k or '[' in k or ']' in k]
|
||||
for text, ident in tokens_with_parens:
|
||||
mult = 1.0
|
||||
for c in text:
|
||||
if c == '[':
|
||||
mult /= 1.1
|
||||
if c == ']':
|
||||
mult *= 1.1
|
||||
if c == '(':
|
||||
mult *= 1.1
|
||||
if c == ')':
|
||||
mult /= 1.1
|
||||
if mult != 1.0:
|
||||
self.token_mults[ident] = mult
|
||||
self.id_start = self.wrapped.tokenizer.bos_token_id
|
||||
self.id_end = self.wrapped.tokenizer.eos_token_id
|
||||
self.id_pad = self.id_end
|
||||
|
||||
def tokenize(self, texts):
|
||||
tokenized = self.wrapped.tokenizer(texts, truncation=False, add_special_tokens=False)["input_ids"]
|
||||
return tokenized
|
||||
|
||||
def encode_with_transformers(self, tokens):
|
||||
clip_skip = int(opts.data['clip_skip']) or 1
|
||||
outputs = self.wrapped.transformer(input_ids=tokens, output_hidden_states=-clip_skip)
|
||||
if clip_skip > 1:
|
||||
z = outputs.hidden_states[-clip_skip]
|
||||
z = self.wrapped.transformer.text_model.final_layer_norm(z)
|
||||
else:
|
||||
z = outputs.last_hidden_state
|
||||
return z
|
||||
|
||||
def encode_embedding_init_text(self, init_text, nvpt):
|
||||
embedding_layer = self.wrapped.transformer.text_model.embeddings
|
||||
ids = self.wrapped.tokenizer(init_text, max_length=nvpt, return_tensors="pt", add_special_tokens=False)["input_ids"]
|
||||
embedded = embedding_layer.token_embedding.wrapped(ids.to(embedding_layer.token_embedding.wrapped.weight.device)).squeeze(0)
|
||||
return embedded
|
||||
@@ -1,82 +0,0 @@
|
||||
from modules import sd_hijack_clip
|
||||
from modules import shared
|
||||
|
||||
|
||||
def process_text_old(self: sd_hijack_clip.FrozenCLIPEmbedderWithCustomWordsBase, texts):
|
||||
id_start = self.id_start
|
||||
id_end = self.id_end
|
||||
maxlen = self.wrapped.max_length # you get to stay at 77
|
||||
used_custom_terms = []
|
||||
remade_batch_tokens = []
|
||||
hijack_comments = []
|
||||
hijack_fixes = []
|
||||
token_count = 0
|
||||
|
||||
cache = {}
|
||||
batch_tokens = self.tokenize(texts)
|
||||
batch_multipliers = []
|
||||
for tokens in batch_tokens:
|
||||
tuple_tokens = tuple(tokens)
|
||||
|
||||
if tuple_tokens in cache:
|
||||
remade_tokens, fixes, multipliers = cache[tuple_tokens]
|
||||
else:
|
||||
fixes = []
|
||||
remade_tokens = []
|
||||
multipliers = []
|
||||
mult = 1.0
|
||||
|
||||
i = 0
|
||||
while i < len(tokens):
|
||||
token = tokens[i]
|
||||
|
||||
embedding, embedding_length_in_tokens = self.hijack.embedding_db.find_embedding_at_position(tokens, i)
|
||||
|
||||
mult_change = self.token_mults.get(token) if shared.opts.enable_emphasis else None
|
||||
if mult_change is not None:
|
||||
mult *= mult_change
|
||||
i += 1
|
||||
elif embedding is None:
|
||||
remade_tokens.append(token)
|
||||
multipliers.append(mult)
|
||||
i += 1
|
||||
else:
|
||||
emb_len = int(embedding.vec.shape[0])
|
||||
fixes.append((len(remade_tokens), embedding))
|
||||
remade_tokens += [0] * emb_len
|
||||
multipliers += [mult] * emb_len
|
||||
used_custom_terms.append((embedding.name, embedding.checksum()))
|
||||
i += embedding_length_in_tokens
|
||||
|
||||
if len(remade_tokens) > maxlen - 2:
|
||||
vocab = {v: k for k, v in self.wrapped.tokenizer.get_vocab().items()}
|
||||
ovf = remade_tokens[maxlen - 2:]
|
||||
overflowing_words = [vocab.get(int(x), "") for x in ovf]
|
||||
overflowing_text = self.wrapped.tokenizer.convert_tokens_to_string(''.join(overflowing_words))
|
||||
hijack_comments.append(f"Warning: too many input tokens; some ({len(overflowing_words)}) have been truncated:\n{overflowing_text}\n")
|
||||
|
||||
token_count = len(remade_tokens)
|
||||
remade_tokens = remade_tokens + [id_end] * (maxlen - 2 - len(remade_tokens))
|
||||
remade_tokens = [id_start] + remade_tokens[0:maxlen - 2] + [id_end]
|
||||
cache[tuple_tokens] = (remade_tokens, fixes, multipliers)
|
||||
|
||||
multipliers = multipliers + [1.0] * (maxlen - 2 - len(multipliers))
|
||||
multipliers = [1.0] + multipliers[0:maxlen - 2] + [1.0]
|
||||
|
||||
remade_batch_tokens.append(remade_tokens)
|
||||
hijack_fixes.append(fixes)
|
||||
batch_multipliers.append(multipliers)
|
||||
return batch_multipliers, remade_batch_tokens, used_custom_terms, hijack_comments, hijack_fixes, token_count
|
||||
|
||||
|
||||
def forward_old(self: sd_hijack_clip.FrozenCLIPEmbedderWithCustomWordsBase, texts):
|
||||
batch_multipliers, remade_batch_tokens, used_custom_terms, hijack_comments, hijack_fixes, _token_count = process_text_old(self, texts)
|
||||
|
||||
self.hijack.comments += hijack_comments
|
||||
|
||||
if len(used_custom_terms) > 0:
|
||||
embedding_names = ", ".join(f"{word} [{checksum}]" for word, checksum in used_custom_terms)
|
||||
self.hijack.comments.append(f"Used embeddings: {embedding_names}")
|
||||
|
||||
self.hijack.fixes = hijack_fixes
|
||||
return self.process_tokens(remade_batch_tokens, batch_multipliers)
|
||||
@@ -1,5 +1,4 @@
|
||||
import math
|
||||
import functools
|
||||
import torch
|
||||
from modules import shared, devices
|
||||
|
||||
@@ -17,7 +16,6 @@ transition_smoothness = 0.0
|
||||
|
||||
# internal state
|
||||
state_enabled = False
|
||||
cat_original = None
|
||||
|
||||
|
||||
def to_denoising_step(number, steps=None) -> int:
|
||||
@@ -145,22 +143,9 @@ def ratio_to_region(width: float, offset: float, n: int):
|
||||
return round(start), round(end), inverted
|
||||
|
||||
|
||||
def apply_freeu(p, backend_original):
|
||||
from modules.sd_hijack_unet import th
|
||||
def apply_freeu(p):
|
||||
global state_enabled # pylint: disable=global-statement
|
||||
global cat_original # pylint: disable=global-statement
|
||||
if backend_original:
|
||||
if shared.opts.freeu_enabled:
|
||||
p.extra_generation_params['FreeU'] = f'b1={shared.opts.freeu_b1} b2={shared.opts.freeu_b2} s1={shared.opts.freeu_s1} s2={shared.opts.freeu_s2}'
|
||||
if not state_enabled: # otherwise already patched
|
||||
cat_original = th.cat
|
||||
th.cat = functools.partial(free_u_cat_hijack, original_function=th.cat)
|
||||
state_enabled = True
|
||||
else:
|
||||
if cat_original is not None:
|
||||
th.cat = cat_original
|
||||
state_enabled = False
|
||||
elif hasattr(p.sd_model, 'enable_freeu'):
|
||||
if hasattr(p.sd_model, 'enable_freeu'):
|
||||
if shared.opts.freeu_enabled:
|
||||
freeu_device = get_fft_device()
|
||||
if freeu_device != devices.cpu:
|
||||
|
||||
@@ -186,7 +186,7 @@ def context_hypertile_vae(p):
|
||||
error_reported = False
|
||||
set_resolution(p)
|
||||
max_h, max_w = 0, 0
|
||||
vae = getattr(p.sd_model, "vae", None) if shared.native else getattr(p.sd_model, "first_stage_model", None)
|
||||
vae = getattr(p.sd_model, "vae", None)
|
||||
if height == 0 or width == 0:
|
||||
log.warning('Hypertile VAE disabled: resolution unknown')
|
||||
return nullcontext()
|
||||
@@ -214,7 +214,7 @@ def context_hypertile_unet(p):
|
||||
error_reported = False
|
||||
set_resolution(p)
|
||||
max_h, max_w = 0, 0
|
||||
unet = getattr(p.sd_model, "unet", None) if shared.native else getattr(p.sd_model.model, "diffusion_model", None)
|
||||
unet = getattr(p.sd_model, "unet", None)
|
||||
if height == 0 or width == 0:
|
||||
log.warning('Hypertile VAE disabled: resolution unknown')
|
||||
return nullcontext()
|
||||
|
||||
@@ -1,103 +0,0 @@
|
||||
import io
|
||||
import contextlib
|
||||
import torch
|
||||
stdout = io.StringIO()
|
||||
with contextlib.redirect_stdout(stdout):
|
||||
import ldm.models.diffusion.ddpm
|
||||
import ldm.models.diffusion.ddim
|
||||
import ldm.models.diffusion.plms
|
||||
|
||||
from ldm.models.diffusion.ddpm import LatentDiffusion # pylint: disable=unused-import
|
||||
from ldm.models.diffusion.plms import PLMSSampler # pylint: disable=unused-import
|
||||
from ldm.models.diffusion.ddim import DDIMSampler, noise_like # pylint: disable=unused-import
|
||||
from ldm.models.diffusion.sampling_util import norm_thresholding
|
||||
|
||||
@torch.no_grad()
|
||||
def p_sample_plms(self, x, c, t, index, repeat_noise=False, use_original_steps=False, quantize_denoised=False,
|
||||
temperature=1., noise_dropout=0., score_corrector=None, corrector_kwargs=None,
|
||||
unconditional_guidance_scale=1., unconditional_conditioning=None, old_eps=None, t_next=None, dynamic_threshold=None):
|
||||
b, *_, device = *x.shape, x.device
|
||||
|
||||
def get_model_output(x, t):
|
||||
if unconditional_conditioning is None or unconditional_guidance_scale == 1.:
|
||||
e_t = self.model.apply_model(x, t, c)
|
||||
else:
|
||||
x_in = torch.cat([x] * 2)
|
||||
t_in = torch.cat([t] * 2)
|
||||
|
||||
if isinstance(c, dict):
|
||||
assert isinstance(unconditional_conditioning, dict)
|
||||
c_in = {}
|
||||
for k in c:
|
||||
if isinstance(c[k], list):
|
||||
c_in[k] = [
|
||||
torch.cat([unconditional_conditioning[k][i], c[k][i]])
|
||||
for i in range(len(c[k]))
|
||||
]
|
||||
else:
|
||||
c_in[k] = torch.cat([unconditional_conditioning[k], c[k]])
|
||||
else:
|
||||
c_in = torch.cat([unconditional_conditioning, c])
|
||||
|
||||
e_t_uncond, e_t = self.model.apply_model(x_in, t_in, c_in).chunk(2)
|
||||
e_t = e_t_uncond + unconditional_guidance_scale * (e_t - e_t_uncond)
|
||||
|
||||
if score_corrector is not None:
|
||||
assert self.model.parameterization == "eps"
|
||||
e_t = score_corrector.modify_score(self.model, e_t, x, t, c, **corrector_kwargs)
|
||||
|
||||
return e_t
|
||||
|
||||
alphas = self.model.alphas_cumprod if use_original_steps else self.ddim_alphas
|
||||
alphas_prev = self.model.alphas_cumprod_prev if use_original_steps else self.ddim_alphas_prev
|
||||
sqrt_one_minus_alphas = self.model.sqrt_one_minus_alphas_cumprod if use_original_steps else self.ddim_sqrt_one_minus_alphas
|
||||
sigmas = self.model.ddim_sigmas_for_original_num_steps if use_original_steps else self.ddim_sigmas
|
||||
|
||||
def get_x_prev_and_pred_x0(e_t, index):
|
||||
# select parameters corresponding to the currently considered timestep
|
||||
a_t = torch.full((b, 1, 1, 1), alphas[index], device=device)
|
||||
a_prev = torch.full((b, 1, 1, 1), alphas_prev[index], device=device)
|
||||
sigma_t = torch.full((b, 1, 1, 1), sigmas[index], device=device)
|
||||
sqrt_one_minus_at = torch.full((b, 1, 1, 1), sqrt_one_minus_alphas[index],device=device)
|
||||
|
||||
# current prediction for x_0
|
||||
pred_x0 = (x - sqrt_one_minus_at * e_t) / a_t.sqrt()
|
||||
if quantize_denoised:
|
||||
pred_x0, _, *_ = self.model.first_stage_model.quantize(pred_x0)
|
||||
if dynamic_threshold is not None:
|
||||
pred_x0 = norm_thresholding(pred_x0, dynamic_threshold)
|
||||
# direction pointing to x_t
|
||||
dir_xt = (1. - a_prev - sigma_t**2).sqrt() * e_t
|
||||
noise = sigma_t * noise_like(x.shape, device, repeat_noise) * temperature
|
||||
if noise_dropout > 0.:
|
||||
noise = torch.nn.functional.dropout(noise, p=noise_dropout)
|
||||
x_prev = a_prev.sqrt() * pred_x0 + dir_xt + noise
|
||||
return x_prev, pred_x0
|
||||
|
||||
e_t = get_model_output(x, t)
|
||||
if len(old_eps) == 0:
|
||||
# Pseudo Improved Euler (2nd order)
|
||||
x_prev, pred_x0 = get_x_prev_and_pred_x0(e_t, index)
|
||||
e_t_next = get_model_output(x_prev, t_next)
|
||||
e_t_prime = (e_t + e_t_next) / 2
|
||||
elif len(old_eps) == 1:
|
||||
# 2nd order Pseudo Linear Multistep (Adams-Bashforth)
|
||||
e_t_prime = (3 * e_t - old_eps[-1]) / 2
|
||||
elif len(old_eps) == 2:
|
||||
# 3nd order Pseudo Linear Multistep (Adams-Bashforth)
|
||||
e_t_prime = (23 * e_t - 16 * old_eps[-1] + 5 * old_eps[-2]) / 12
|
||||
elif len(old_eps) >= 3:
|
||||
# 4nd order Pseudo Linear Multistep (Adams-Bashforth)
|
||||
e_t_prime = (55 * e_t - 59 * old_eps[-1] + 37 * old_eps[-2] - 9 * old_eps[-3]) / 24
|
||||
else:
|
||||
e_t_prime = e_t # error state
|
||||
|
||||
x_prev, pred_x0 = get_x_prev_and_pred_x0(e_t_prime, index)
|
||||
|
||||
return x_prev, pred_x0, e_t
|
||||
|
||||
|
||||
def do_inpainting_hijack():
|
||||
# p_sample_plms is needed because PLMS can't work with dicts as conditionings
|
||||
|
||||
ldm.models.diffusion.plms.PLMSSampler.p_sample_plms = p_sample_plms
|
||||
@@ -1,9 +0,0 @@
|
||||
import os.path
|
||||
|
||||
def should_hijack_ip2p(checkpoint_info):
|
||||
from modules import sd_models_config
|
||||
|
||||
ckpt_basename = os.path.basename(checkpoint_info.filename).lower()
|
||||
cfg_basename = os.path.basename(sd_models_config.find_checkpoint_config_near_filename(checkpoint_info)).lower()
|
||||
|
||||
return "pix2pix" in ckpt_basename and "pix2pix" not in cfg_basename
|
||||
@@ -1,29 +0,0 @@
|
||||
import open_clip.tokenizer
|
||||
import torch
|
||||
|
||||
from modules import sd_hijack_clip, devices
|
||||
|
||||
tokenizer = open_clip.tokenizer._tokenizer # pylint: disable=protected-access
|
||||
|
||||
|
||||
class FrozenOpenCLIPEmbedderWithCustomWords(sd_hijack_clip.FrozenCLIPEmbedderWithCustomWordsBase):
|
||||
def __init__(self, wrapped, hijack):
|
||||
super().__init__(wrapped, hijack)
|
||||
self.comma_token = [v for k, v in tokenizer.encoder.items() if k == ',</w>'][0]
|
||||
self.id_start = tokenizer.encoder["<start_of_text>"]
|
||||
self.id_end = tokenizer.encoder["<end_of_text>"]
|
||||
self.id_pad = 0
|
||||
|
||||
def tokenize(self, texts):
|
||||
tokenized = [tokenizer.encode(text) for text in texts]
|
||||
return tokenized
|
||||
|
||||
def encode_with_transformers(self, tokens):
|
||||
z = self.wrapped.encode_with_transformer(tokens)
|
||||
return z
|
||||
|
||||
def encode_embedding_init_text(self, init_text, nvpt): # pylint: disable=unused-argument
|
||||
ids = tokenizer.encode(init_text)
|
||||
ids = torch.asarray([ids], device=devices.device, dtype=torch.int)
|
||||
embedded = self.wrapped.model.token_embedding.wrapped(ids).squeeze(0)
|
||||
return embedded
|
||||
@@ -1,517 +0,0 @@
|
||||
from __future__ import annotations
|
||||
import sys
|
||||
import math
|
||||
import psutil
|
||||
import torch
|
||||
from torch import einsum
|
||||
from einops import rearrange
|
||||
from modules import shared, errors, devices
|
||||
from modules.hypernetworks import hypernetwork
|
||||
from .sub_quadratic_attention import efficient_dot_product_attention # pylint: disable=relative-beyond-top-level
|
||||
|
||||
|
||||
if not shared.native:
|
||||
from ldm.util import default
|
||||
|
||||
|
||||
if shared.opts.cross_attention_optimization == "xFormers":
|
||||
try:
|
||||
import xformers.ops # pylint: disable=import-error
|
||||
shared.xformers_available = True
|
||||
except Exception:
|
||||
pass
|
||||
elif not shared.cmd_opts.experimental:
|
||||
if sys.modules.get("xformers", None) is not None:
|
||||
shared.log.debug('Unloading xFormers')
|
||||
sys.modules["xformers"] = None
|
||||
sys.modules["xformers.ops"] = None
|
||||
|
||||
|
||||
def get_available_vram():
|
||||
if shared.device.type == 'cuda' or shared.device.type == 'xpu':
|
||||
try:
|
||||
stats = torch.cuda.memory_stats(shared.device)
|
||||
mem_active = stats['active_bytes.all.current']
|
||||
mem_reserved = stats['reserved_bytes.all.current']
|
||||
mem_free_cuda, _ = torch.cuda.mem_get_info(torch.cuda.current_device())
|
||||
mem_free_torch = mem_reserved - mem_active
|
||||
mem_free_total = mem_free_cuda + mem_free_torch
|
||||
except Exception:
|
||||
mem_free_total = 1024 * 1024 * 1024
|
||||
return mem_free_total
|
||||
elif shared.device.type == 'privateuseone':
|
||||
return torch.dml.mem_get_info(shared.device)[0]
|
||||
else:
|
||||
return psutil.virtual_memory().available
|
||||
|
||||
|
||||
# see https://github.com/basujindal/stable-diffusion/pull/117 for discussion
|
||||
def split_cross_attention_forward_v1(self, x, context=None, mask=None): # pylint: disable=unused-argument
|
||||
h = self.heads
|
||||
|
||||
q_in = self.to_q(x)
|
||||
context = default(context, x) # pylint: disable=possibly-used-before-assignment
|
||||
|
||||
context_k, context_v = hypernetwork.apply_hypernetworks(hypernetwork.loaded_hypernetworks, context)
|
||||
k_in = self.to_k(context_k)
|
||||
v_in = self.to_v(context_v)
|
||||
del context, context_k, context_v, x
|
||||
|
||||
q, k, v = (rearrange(t, 'b n (h d) -> (b h) n d', h=h) for t in (q_in, k_in, v_in))
|
||||
del q_in, k_in, v_in
|
||||
|
||||
dtype = q.dtype
|
||||
if shared.opts.upcast_attn:
|
||||
q, k, v = q.float(), k.float(), v.float()
|
||||
|
||||
with devices.without_autocast(disable=not shared.opts.upcast_attn):
|
||||
r1 = torch.zeros(q.shape[0], q.shape[1], v.shape[2], device=q.device, dtype=q.dtype)
|
||||
for i in range(0, q.shape[0], 2):
|
||||
end = i + 2
|
||||
s1 = einsum('b i d, b j d -> b i j', q[i:end], k[i:end])
|
||||
s1 *= self.scale
|
||||
s2 = s1.softmax(dim=-1)
|
||||
del s1
|
||||
r1[i:end] = einsum('b i j, b j d -> b i d', s2, v[i:end])
|
||||
del s2
|
||||
del q, k, v
|
||||
|
||||
r1 = r1.to(dtype)
|
||||
|
||||
r2 = rearrange(r1, '(b h) n d -> b n (h d)', h=h)
|
||||
del r1
|
||||
|
||||
return self.to_out(r2)
|
||||
|
||||
|
||||
# taken from https://github.com/Doggettx/stable-diffusion and modified
|
||||
def split_cross_attention_forward(self, x, context=None, mask=None): # pylint: disable=unused-argument
|
||||
h = self.heads
|
||||
q_in = self.to_q(x)
|
||||
context = default(context, x)
|
||||
|
||||
context_k, context_v = hypernetwork.apply_hypernetworks(hypernetwork.loaded_hypernetworks, context)
|
||||
k_in = self.to_k(context_k)
|
||||
v_in = self.to_v(context_v)
|
||||
|
||||
dtype = q_in.dtype
|
||||
if shared.opts.upcast_attn:
|
||||
q_in, k_in, v_in = q_in.float(), k_in.float(), v_in if v_in.device.type == 'mps' else v_in.float()
|
||||
|
||||
with devices.without_autocast(disable=not shared.opts.upcast_attn):
|
||||
k_in = k_in * self.scale
|
||||
del context, x
|
||||
q, k, v = (rearrange(t, 'b n (h d) -> (b h) n d', h=h) for t in (q_in, k_in, v_in))
|
||||
del q_in, k_in, v_in
|
||||
r1 = torch.zeros(q.shape[0], q.shape[1], v.shape[2], device=q.device, dtype=q.dtype)
|
||||
mem_free_total = get_available_vram()
|
||||
gb = 1024 ** 3
|
||||
tensor_size = q.shape[0] * q.shape[1] * k.shape[1] * q.element_size()
|
||||
modifier = 3 if q.element_size() == 2 else 2.5
|
||||
mem_required = tensor_size * modifier
|
||||
steps = 1
|
||||
if mem_required > mem_free_total:
|
||||
steps = 2 ** (math.ceil(math.log(mem_required / mem_free_total, 2)))
|
||||
if steps > 64:
|
||||
max_res = math.floor(math.sqrt(math.sqrt(mem_free_total / 2.5)) / 8) * 64
|
||||
raise RuntimeError(f'Not enough memory, use lower resolution (max approx. {max_res}x{max_res}). '
|
||||
f'Need: {mem_required / 64 / gb:0.1f}GB free, Have:{mem_free_total / gb:0.1f}GB free')
|
||||
slice_size = q.shape[1] // steps if (q.shape[1] % steps) == 0 else q.shape[1]
|
||||
for i in range(0, q.shape[1], slice_size):
|
||||
end = i + slice_size
|
||||
s1 = einsum('b i d, b j d -> b i j', q[:, i:end], k)
|
||||
s2 = s1.softmax(dim=-1, dtype=q.dtype)
|
||||
del s1
|
||||
r1[:, i:end] = einsum('b i j, b j d -> b i d', s2, v)
|
||||
del s2
|
||||
del q, k, v
|
||||
r1 = r1.to(dtype)
|
||||
r2 = rearrange(r1, '(b h) n d -> b n (h d)', h=h)
|
||||
del r1
|
||||
|
||||
return self.to_out(r2)
|
||||
|
||||
|
||||
# -- Taken from https://github.com/invoke-ai/InvokeAI and modified --
|
||||
mem_total_gb = psutil.virtual_memory().total // (1 << 30)
|
||||
|
||||
def einsum_op_compvis(q, k, v):
|
||||
s = einsum('b i d, b j d -> b i j', q, k)
|
||||
s = s.softmax(dim=-1, dtype=s.dtype)
|
||||
return einsum('b i j, b j d -> b i d', s, v)
|
||||
|
||||
def einsum_op_slice_0(q, k, v, slice_size):
|
||||
r = torch.zeros(q.shape[0], q.shape[1], v.shape[2], device=q.device, dtype=q.dtype)
|
||||
for i in range(0, q.shape[0], slice_size):
|
||||
end = i + slice_size
|
||||
r[i:end] = einsum_op_compvis(q[i:end], k[i:end], v[i:end])
|
||||
return r
|
||||
|
||||
def einsum_op_slice_1(q, k, v, slice_size):
|
||||
r = torch.zeros(q.shape[0], q.shape[1], v.shape[2], device=q.device, dtype=q.dtype)
|
||||
for i in range(0, q.shape[1], slice_size):
|
||||
end = i + slice_size
|
||||
r[:, i:end] = einsum_op_compvis(q[:, i:end], k, v)
|
||||
return r
|
||||
|
||||
def einsum_op_mps_v1(q, k, v):
|
||||
if q.shape[0] * q.shape[1] <= 2**16: # (512x512) max q.shape[1]: 4096
|
||||
return einsum_op_compvis(q, k, v)
|
||||
else:
|
||||
slice_size = math.floor(2**30 / (q.shape[0] * q.shape[1]))
|
||||
if slice_size % 4096 == 0:
|
||||
slice_size -= 1
|
||||
return einsum_op_slice_1(q, k, v, slice_size)
|
||||
|
||||
def einsum_op_mps_v2(q, k, v):
|
||||
if mem_total_gb > 8 and q.shape[0] * q.shape[1] <= 2**16:
|
||||
return einsum_op_compvis(q, k, v)
|
||||
else:
|
||||
return einsum_op_slice_0(q, k, v, 1)
|
||||
|
||||
def einsum_op_tensor_mem(q, k, v, max_tensor_mb):
|
||||
size_mb = q.shape[0] * q.shape[1] * k.shape[1] * q.element_size() // (1 << 20)
|
||||
if size_mb <= max_tensor_mb:
|
||||
return einsum_op_compvis(q, k, v)
|
||||
div = 1 << int((size_mb - 1) / max_tensor_mb).bit_length()
|
||||
if div <= q.shape[0]:
|
||||
return einsum_op_slice_0(q, k, v, q.shape[0] // div)
|
||||
return einsum_op_slice_1(q, k, v, max(q.shape[1] // div, 1))
|
||||
|
||||
def einsum_op_cuda(q, k, v):
|
||||
try:
|
||||
stats = torch.cuda.memory_stats(q.device)
|
||||
mem_active = stats['active_bytes.all.current']
|
||||
mem_reserved = stats['reserved_bytes.all.current']
|
||||
mem_free_cuda, _ = torch.cuda.mem_get_info(q.device)
|
||||
mem_free_torch = mem_reserved - mem_active
|
||||
mem_free_total = mem_free_cuda + mem_free_torch
|
||||
except Exception:
|
||||
mem_free_total = 1024 * 1024 * 1024
|
||||
# Divide factor of safety as there's copying and fragmentation
|
||||
return einsum_op_tensor_mem(q, k, v, mem_free_total / 3.3 / (1 << 20))
|
||||
|
||||
def einsum_op_dml(q, k, v):
|
||||
mem_free, mem_total = torch.dml.mem_get_info(q.device)
|
||||
mem_active = mem_total - mem_free
|
||||
mem_reserved = mem_total * 0.7
|
||||
return einsum_op_tensor_mem(q, k, v, (mem_reserved - mem_active) if mem_reserved > mem_active else 1)
|
||||
|
||||
def einsum_op(q, k, v):
|
||||
if q.device.type == 'cuda' or q.device.type == 'xpu':
|
||||
return einsum_op_cuda(q, k, v)
|
||||
|
||||
if q.device.type == 'mps':
|
||||
if mem_total_gb >= 32 and q.shape[0] % 32 != 0 and q.shape[0] * q.shape[1] < 2**18:
|
||||
return einsum_op_mps_v1(q, k, v)
|
||||
return einsum_op_mps_v2(q, k, v)
|
||||
|
||||
if q.device.type == 'privateuseone':
|
||||
return einsum_op_dml(q, k, v)
|
||||
|
||||
# Smaller slices are faster due to L2/L3/SLC caches.
|
||||
# Tested on i7 with 8MB L3 cache.
|
||||
return einsum_op_tensor_mem(q, k, v, 32)
|
||||
|
||||
def split_cross_attention_forward_invokeAI(self, x, context=None, mask=None): # pylint: disable=unused-argument
|
||||
h = self.heads
|
||||
|
||||
q = self.to_q(x)
|
||||
context = default(context, x)
|
||||
|
||||
context_k, context_v = hypernetwork.apply_hypernetworks(hypernetwork.loaded_hypernetworks, context)
|
||||
k = self.to_k(context_k)
|
||||
v = self.to_v(context_v)
|
||||
del context, context_k, context_v, x
|
||||
|
||||
dtype = q.dtype
|
||||
if shared.opts.upcast_attn:
|
||||
q, k, v = q.float(), k.float(), v if v.device.type == 'mps' else v.float()
|
||||
|
||||
with devices.without_autocast(disable=not shared.opts.upcast_attn):
|
||||
k = k * self.scale
|
||||
q, k, v = (rearrange(t, 'b n (h d) -> (b h) n d', h=h) for t in (q, k, v))
|
||||
r = einsum_op(q, k, v)
|
||||
r = r.to(dtype)
|
||||
return self.to_out(rearrange(r, '(b h) n d -> b n (h d)', h=h))
|
||||
|
||||
# -- End of code from https://github.com/invoke-ai/InvokeAI --
|
||||
|
||||
|
||||
# Based on Birch-san's modified implementation of sub-quadratic attention from https://github.com/Birch-san/diffusers/pull/1
|
||||
# The sub_quad_attention_forward function is under the MIT License listed under Memory Efficient Attention in the Licenses section of the web UI interface
|
||||
def sub_quad_attention_forward(self, x, context=None, mask=None):
|
||||
assert mask is None, "attention-mask not currently implemented for SubQuadraticCrossAttnProcessor."
|
||||
|
||||
h = self.heads
|
||||
|
||||
q = self.to_q(x)
|
||||
context = default(context, x)
|
||||
|
||||
context_k, context_v = hypernetwork.apply_hypernetworks(hypernetwork.loaded_hypernetworks, context)
|
||||
k = self.to_k(context_k)
|
||||
v = self.to_v(context_v)
|
||||
del context, context_k, context_v, x
|
||||
|
||||
q = q.unflatten(-1, (h, -1)).transpose(1,2).flatten(end_dim=1)
|
||||
k = k.unflatten(-1, (h, -1)).transpose(1,2).flatten(end_dim=1)
|
||||
v = v.unflatten(-1, (h, -1)).transpose(1,2).flatten(end_dim=1)
|
||||
|
||||
if q.device.type == 'mps':
|
||||
q, k, v = q.contiguous(), k.contiguous(), v.contiguous()
|
||||
|
||||
dtype = q.dtype
|
||||
if shared.opts.upcast_attn:
|
||||
q, k = q.float(), k.float()
|
||||
|
||||
x = sub_quad_attention(q, k, v, q_chunk_size=shared.opts.sub_quad_q_chunk_size, kv_chunk_size=shared.opts.sub_quad_kv_chunk_size, chunk_threshold=shared.opts.sub_quad_chunk_threshold, use_checkpoint=self.training)
|
||||
|
||||
x = x.to(dtype)
|
||||
|
||||
x = x.unflatten(0, (-1, h)).transpose(1,2).flatten(start_dim=2)
|
||||
|
||||
out_proj, dropout = self.to_out
|
||||
x = out_proj(x)
|
||||
x = dropout(x)
|
||||
|
||||
return x
|
||||
|
||||
def sub_quad_attention(q, k, v, q_chunk_size=1024, kv_chunk_size=None, kv_chunk_size_min=None, chunk_threshold=None, use_checkpoint=True):
|
||||
bytes_per_token = torch.finfo(q.dtype).bits//8
|
||||
batch_x_heads, q_tokens, _ = q.shape
|
||||
_, k_tokens, _ = k.shape
|
||||
qk_matmul_size_bytes = batch_x_heads * bytes_per_token * q_tokens * k_tokens
|
||||
|
||||
if chunk_threshold is None:
|
||||
chunk_threshold_bytes = int(get_available_vram() * 0.9) if q.device.type == 'mps' else int(get_available_vram() * 0.7)
|
||||
elif chunk_threshold == 0:
|
||||
chunk_threshold_bytes = None
|
||||
else:
|
||||
chunk_threshold_bytes = int(0.01 * chunk_threshold * get_available_vram())
|
||||
|
||||
if kv_chunk_size_min is None and chunk_threshold_bytes is not None:
|
||||
kv_chunk_size_min = chunk_threshold_bytes // (batch_x_heads * bytes_per_token * (k.shape[2] + v.shape[2]))
|
||||
elif kv_chunk_size_min == 0:
|
||||
kv_chunk_size_min = None
|
||||
|
||||
if chunk_threshold_bytes is not None and qk_matmul_size_bytes <= chunk_threshold_bytes:
|
||||
# the big matmul fits into our memory limit; do everything in 1 chunk,
|
||||
# i.e. send it down the unchunked fast-path
|
||||
kv_chunk_size = k_tokens
|
||||
|
||||
with devices.without_autocast(disable=q.dtype == v.dtype):
|
||||
return efficient_dot_product_attention(
|
||||
q,
|
||||
k,
|
||||
v,
|
||||
query_chunk_size=q_chunk_size,
|
||||
kv_chunk_size=kv_chunk_size,
|
||||
kv_chunk_size_min = kv_chunk_size_min,
|
||||
use_checkpoint=use_checkpoint,
|
||||
)
|
||||
|
||||
|
||||
def get_xformers_flash_attention_op(q, k, v):
|
||||
if 'Flash attention' not in shared.opts.xformers_options:
|
||||
return None
|
||||
try:
|
||||
flash_attention_op = xformers.ops.MemoryEfficientAttentionFlashAttentionOp # pylint: disable=possibly-used-before-assignment, used-before-assignment
|
||||
fw, _bw = flash_attention_op
|
||||
if fw.supports(xformers.ops.fmha.Inputs(query=q, key=k, value=v, attn_bias=None)):
|
||||
return flash_attention_op
|
||||
except Exception as e:
|
||||
errors.display_once(e, "enabling flash attention")
|
||||
return None
|
||||
|
||||
|
||||
def xformers_attention_forward(self, x, context=None, mask=None): # pylint: disable=unused-argument
|
||||
h = self.heads
|
||||
q_in = self.to_q(x)
|
||||
context = default(context, x)
|
||||
|
||||
context_k, context_v = hypernetwork.apply_hypernetworks(hypernetwork.loaded_hypernetworks, context)
|
||||
k_in = self.to_k(context_k)
|
||||
v_in = self.to_v(context_v)
|
||||
|
||||
q, k, v = (rearrange(t, 'b n (h d) -> b n h d', h=h) for t in (q_in, k_in, v_in))
|
||||
del q_in, k_in, v_in
|
||||
|
||||
dtype = q.dtype
|
||||
if shared.opts.upcast_attn:
|
||||
q, k, v = q.float(), k.float(), v.float()
|
||||
|
||||
out = xformers.ops.memory_efficient_attention(q, k, v, attn_bias=None, op=get_xformers_flash_attention_op(q, k, v))
|
||||
|
||||
out = out.to(dtype)
|
||||
|
||||
out = rearrange(out, 'b n h d -> b n (h d)', h=h)
|
||||
return self.to_out(out)
|
||||
|
||||
# Based on Diffusers usage of scaled dot product attention from https://github.com/huggingface/diffusers/blob/c7da8fd23359a22d0df2741688b5b4f33c26df21/src/diffusers/models/cross_attention.py
|
||||
# The scaled_dot_product_attention_forward function contains parts of code under Apache-2.0 license listed under Scaled Dot Product Attention in the Licenses section of the web UI interface
|
||||
def scaled_dot_product_attention_forward(self, x, context=None, mask=None):
|
||||
batch_size, sequence_length, inner_dim = x.shape
|
||||
|
||||
if mask is not None:
|
||||
mask = self.prepare_attention_mask(mask, sequence_length, batch_size)
|
||||
mask = mask.view(batch_size, self.heads, -1, mask.shape[-1])
|
||||
|
||||
h = self.heads
|
||||
q_in = self.to_q(x)
|
||||
context = default(context, x)
|
||||
|
||||
context_k, context_v = hypernetwork.apply_hypernetworks(hypernetwork.loaded_hypernetworks, context)
|
||||
k_in = self.to_k(context_k)
|
||||
v_in = self.to_v(context_v)
|
||||
|
||||
head_dim = inner_dim // h
|
||||
q = q_in.view(batch_size, -1, h, head_dim).transpose(1, 2)
|
||||
k = k_in.view(batch_size, -1, h, head_dim).transpose(1, 2)
|
||||
v = v_in.view(batch_size, -1, h, head_dim).transpose(1, 2)
|
||||
del q_in, k_in, v_in
|
||||
dtype = q.dtype
|
||||
if shared.opts.upcast_attn:
|
||||
q, k, v = q.float(), k.float(), v.float()
|
||||
|
||||
# the output of sdp = (batch, num_heads, seq_len, head_dim)
|
||||
hidden_states = torch.nn.functional.scaled_dot_product_attention(
|
||||
q, k, v, attn_mask=mask, dropout_p=0.0, is_causal=False
|
||||
)
|
||||
|
||||
hidden_states = hidden_states.transpose(1, 2).reshape(batch_size, -1, h * head_dim)
|
||||
hidden_states = hidden_states.to(dtype)
|
||||
|
||||
# linear proj
|
||||
hidden_states = self.to_out[0](hidden_states)
|
||||
# dropout
|
||||
hidden_states = self.to_out[1](hidden_states)
|
||||
return hidden_states
|
||||
|
||||
def scaled_dot_product_no_mem_attention_forward(self, x, context=None, mask=None):
|
||||
with torch.backends.cuda.sdp_kernel(enable_flash=True, enable_math=True, enable_mem_efficient=False):
|
||||
return scaled_dot_product_attention_forward(self, x, context, mask)
|
||||
|
||||
def cross_attention_attnblock_forward(self, x):
|
||||
h_ = x
|
||||
h_ = self.norm(h_)
|
||||
q1 = self.q(h_)
|
||||
k1 = self.k(h_)
|
||||
v = self.v(h_)
|
||||
|
||||
# compute attention
|
||||
b, c, h, w = q1.shape
|
||||
|
||||
q2 = q1.reshape(b, c, h*w)
|
||||
del q1
|
||||
|
||||
q = q2.permute(0, 2, 1) # b,hw,c
|
||||
del q2
|
||||
|
||||
k = k1.reshape(b, c, h*w) # b,c,hw
|
||||
del k1
|
||||
|
||||
h_ = torch.zeros_like(k, device=q.device)
|
||||
|
||||
mem_free_total = get_available_vram()
|
||||
|
||||
tensor_size = q.shape[0] * q.shape[1] * k.shape[2] * q.element_size()
|
||||
mem_required = tensor_size * 2.5
|
||||
steps = 1
|
||||
|
||||
if mem_required > mem_free_total:
|
||||
steps = 2**(math.ceil(math.log(mem_required / mem_free_total, 2)))
|
||||
|
||||
slice_size = q.shape[1] // steps if (q.shape[1] % steps) == 0 else q.shape[1]
|
||||
for i in range(0, q.shape[1], slice_size):
|
||||
end = i + slice_size
|
||||
|
||||
w1 = torch.bmm(q[:, i:end], k) # b,hw,hw w[b,i,j]=sum_c q[b,i,c]k[b,c,j]
|
||||
w2 = w1 * (int(c)**(-0.5))
|
||||
del w1
|
||||
w3 = torch.nn.functional.softmax(w2, dim=2, dtype=q.dtype)
|
||||
del w2
|
||||
|
||||
# attend to values
|
||||
v1 = v.reshape(b, c, h*w)
|
||||
w4 = w3.permute(0, 2, 1) # b,hw,hw (first hw of k, second of q)
|
||||
del w3
|
||||
|
||||
h_[:, :, i:end] = torch.bmm(v1, w4) # b, c,hw (hw of q) h_[b,c,j] = sum_i v[b,c,i] w_[b,i,j]
|
||||
del v1, w4
|
||||
|
||||
h2 = h_.reshape(b, c, h, w)
|
||||
del h_
|
||||
|
||||
h3 = self.proj_out(h2)
|
||||
del h2
|
||||
|
||||
h3 += x
|
||||
|
||||
return h3
|
||||
|
||||
def xformers_attnblock_forward(self, x):
|
||||
try:
|
||||
h_ = x
|
||||
h_ = self.norm(h_)
|
||||
q = self.q(h_)
|
||||
k = self.k(h_)
|
||||
v = self.v(h_)
|
||||
b, c, h, w = q.shape # pylint: disable=unused-variable
|
||||
q, k, v = (rearrange(t, 'b c h w -> b (h w) c') for t in (q, k, v))
|
||||
dtype = q.dtype
|
||||
if shared.opts.upcast_attn:
|
||||
q, k = q.float(), k.float()
|
||||
q = q.contiguous()
|
||||
k = k.contiguous()
|
||||
v = v.contiguous()
|
||||
out = xformers.ops.memory_efficient_attention(q, k, v, op=get_xformers_flash_attention_op(q, k, v))
|
||||
out = out.to(dtype)
|
||||
out = rearrange(out, 'b (h w) c -> b c h w', h=h)
|
||||
out = self.proj_out(out)
|
||||
return x + out
|
||||
except NotImplementedError:
|
||||
return cross_attention_attnblock_forward(self, x)
|
||||
|
||||
def sdp_attnblock_forward(self, x):
|
||||
h_ = x
|
||||
h_ = self.norm(h_)
|
||||
q = self.q(h_)
|
||||
k = self.k(h_)
|
||||
v = self.v(h_)
|
||||
b, c, h, w = q.shape # pylint: disable=unused-variable
|
||||
|
||||
# SDP optimization kenels are built for operations with multiple attention heads.
|
||||
# Four dimensional tensors are required for mem_efficient and flash attention to work.
|
||||
# We add an attention head dimension `a` to allow these kernels to be used.
|
||||
q, k, v = (rearrange(t, '(b a) c h w -> b a (h w) c', a=1) for t in (q, k, v))
|
||||
dtype = q.dtype
|
||||
if shared.opts.upcast_attn:
|
||||
q, k, v = q.float(), k.float(), v.float()
|
||||
q = q.contiguous()
|
||||
k = k.contiguous()
|
||||
v = v.contiguous()
|
||||
out = torch.nn.functional.scaled_dot_product_attention(q, k, v, dropout_p=0.0, is_causal=False)
|
||||
out = out.to(dtype)
|
||||
out = rearrange(out, 'b a (h w) c -> (b a) c h w', h=h) # remove the one attention head dimension `a`
|
||||
out = self.proj_out(out)
|
||||
return x + out
|
||||
|
||||
def sdp_no_mem_attnblock_forward(self, x):
|
||||
with torch.backends.cuda.sdp_kernel(enable_flash=True, enable_math=True, enable_mem_efficient=False):
|
||||
return sdp_attnblock_forward(self, x)
|
||||
|
||||
def sub_quad_attnblock_forward(self, x):
|
||||
h_ = x
|
||||
h_ = self.norm(h_)
|
||||
q = self.q(h_)
|
||||
k = self.k(h_)
|
||||
v = self.v(h_)
|
||||
b, c, h, w = q.shape # pylint: disable=unused-variable
|
||||
q, k, v = (rearrange(t, 'b c h w -> b (h w) c') for t in (q, k, v))
|
||||
q = q.contiguous()
|
||||
k = k.contiguous()
|
||||
v = v.contiguous()
|
||||
out = sub_quad_attention(q, k, v, q_chunk_size=shared.opts.sub_quad_q_chunk_size, kv_chunk_size=shared.opts.sub_quad_kv_chunk_size, chunk_threshold=shared.opts.sub_quad_chunk_threshold, use_checkpoint=self.training)
|
||||
out = rearrange(out, 'b (h w) c -> b c h w', h=h)
|
||||
out = self.proj_out(out)
|
||||
return x + out
|
||||
@@ -11,7 +11,7 @@ def hijack_encode_prompt(*args, **kwargs):
|
||||
res = shared.sd_model.orig_encode_prompt(*args, **kwargs)
|
||||
except Exception as e:
|
||||
shared.log.error(f'Eencode prompt: {e}')
|
||||
errors.display(e, 'Video encode prompt')
|
||||
errors.display(e, 'Encode prompt')
|
||||
res = None
|
||||
t1 = time.time()
|
||||
timer.process.add('te', t1-t0)
|
||||
|
||||
@@ -1,80 +0,0 @@
|
||||
import torch
|
||||
from packaging import version
|
||||
|
||||
from modules import devices, shared
|
||||
from modules.sd_hijack_utils import CondFunc
|
||||
|
||||
|
||||
class TorchHijackForUnet:
|
||||
"""
|
||||
This is torch, but with cat that resizes tensors to appropriate dimensions if they do not match;
|
||||
this makes it possible to create pictures with dimensions that are multiples of 8 rather than 64
|
||||
"""
|
||||
|
||||
def __getattr__(self, item):
|
||||
if item == 'cat':
|
||||
return self.cat
|
||||
|
||||
if hasattr(torch, item):
|
||||
return getattr(torch, item)
|
||||
|
||||
raise AttributeError(f"'{type(self).__name__}' object has no attribute '{item}'")
|
||||
|
||||
def cat(self, tensors, *args, **kwargs):
|
||||
if len(tensors) == 2:
|
||||
a, b = tensors
|
||||
if a.shape[-2:] != b.shape[-2:]:
|
||||
a = torch.nn.functional.interpolate(a, b.shape[-2:], mode="nearest")
|
||||
|
||||
tensors = (a, b)
|
||||
|
||||
return torch.cat(tensors, *args, **kwargs)
|
||||
|
||||
|
||||
th = TorchHijackForUnet()
|
||||
|
||||
|
||||
# Below are monkey patches to enable upcasting a float16 UNet for float32 sampling
|
||||
def apply_model(orig_func, self, x_noisy, t, cond, **kwargs):
|
||||
|
||||
if isinstance(cond, dict):
|
||||
for y in cond.keys():
|
||||
cond[y] = [x.to(devices.dtype_unet) if isinstance(x, torch.Tensor) else x for x in cond[y]]
|
||||
|
||||
with devices.autocast():
|
||||
return orig_func(self, x_noisy.to(devices.dtype_unet), t.to(devices.dtype_unet), cond, **kwargs).float()
|
||||
|
||||
|
||||
class GELUHijack(torch.nn.GELU, torch.nn.Module):
|
||||
def __init__(self, *args, **kwargs): # pylint: disable=super-init-not-called
|
||||
torch.nn.GELU.__init__(self, *args, **kwargs)
|
||||
def forward(self, input): # pylint: disable=redefined-builtin
|
||||
if devices.unet_needs_upcast:
|
||||
return torch.nn.GELU.forward(self.float(), input.float()).to(devices.dtype_unet)
|
||||
else:
|
||||
return torch.nn.GELU.forward(self, input)
|
||||
|
||||
|
||||
ddpm_edit_hijack = None
|
||||
def hijack_ddpm_edit():
|
||||
global ddpm_edit_hijack # pylint: disable=global-statement
|
||||
if not ddpm_edit_hijack:
|
||||
CondFunc('modules.hijack.ddpm_edit.LatentDiffusion.decode_first_stage', first_stage_sub, first_stage_cond) # pylint: disable=possibly-used-before-assignment
|
||||
CondFunc('modules.hijack.ddpm_edit.LatentDiffusion.encode_first_stage', first_stage_sub, first_stage_cond) # pylint: disable=possibly-used-before-assignment
|
||||
ddpm_edit_hijack = CondFunc('modules.hijack.ddpm_edit.LatentDiffusion.apply_model', apply_model, unet_needs_upcast)
|
||||
|
||||
|
||||
unet_needs_upcast = lambda *args, **kwargs: devices.unet_needs_upcast # pylint: disable=unnecessary-lambda-assignment
|
||||
if not shared.native:
|
||||
CondFunc('ldm.models.diffusion.ddpm.LatentDiffusion.apply_model', apply_model, unet_needs_upcast)
|
||||
CondFunc('ldm.modules.diffusionmodules.openaimodel.timestep_embedding', lambda orig_func, timesteps, *args, **kwargs: orig_func(timesteps, *args, **kwargs).to(torch.float32 if timesteps.dtype == torch.int64 else devices.dtype_unet), unet_needs_upcast)
|
||||
if version.parse(torch.__version__) <= version.parse("1.13.2") or torch.cuda.is_available():
|
||||
CondFunc('ldm.modules.diffusionmodules.util.GroupNorm32.forward', lambda orig_func, self, *args, **kwargs: orig_func(self.float(), *args, **kwargs), unet_needs_upcast)
|
||||
CondFunc('ldm.modules.attention.GEGLU.forward', lambda orig_func, self, x: orig_func(self.float(), x.float()).to(devices.dtype_unet), unet_needs_upcast)
|
||||
CondFunc('open_clip.transformer.ResidualAttentionBlock.__init__', lambda orig_func, *args, **kwargs: (kwargs.update({'act_layer': GELUHijack}) and False) or orig_func(*args, **kwargs), lambda _, *args, **kwargs: kwargs.get('act_layer') is None or kwargs['act_layer'] == torch.nn.GELU)
|
||||
|
||||
first_stage_cond = lambda _, self, *args, **kwargs: devices.unet_needs_upcast and self.model.diffusion_model.dtype == torch.float16 # pylint: disable=unnecessary-lambda-assignment
|
||||
first_stage_sub = lambda orig_func, self, x, **kwargs: orig_func(self, x.to(devices.dtype_vae), **kwargs) # pylint: disable=unnecessary-lambda-assignment
|
||||
CondFunc('ldm.models.diffusion.ddpm.LatentDiffusion.decode_first_stage', first_stage_sub, first_stage_cond)
|
||||
CondFunc('ldm.models.diffusion.ddpm.LatentDiffusion.encode_first_stage', first_stage_sub, first_stage_cond)
|
||||
CondFunc('ldm.models.diffusion.ddpm.LatentDiffusion.get_first_stage_encoding', lambda orig_func, *args, **kwargs: orig_func(*args, **kwargs).float(), first_stage_cond)
|
||||
@@ -1,32 +0,0 @@
|
||||
import torch
|
||||
|
||||
from modules import sd_hijack_clip, devices
|
||||
|
||||
|
||||
class FrozenXLMREmbedderWithCustomWords(sd_hijack_clip.FrozenCLIPEmbedderWithCustomWords):
|
||||
def __init__(self, wrapped, hijack):
|
||||
super().__init__(wrapped, hijack)
|
||||
|
||||
self.id_start = wrapped.config.bos_token_id
|
||||
self.id_end = wrapped.config.eos_token_id
|
||||
self.id_pad = wrapped.config.pad_token_id
|
||||
|
||||
self.comma_token = self.tokenizer.get_vocab().get(',', None) # alt diffusion doesn't have </w> bits for comma
|
||||
|
||||
def encode_with_transformers(self, tokens):
|
||||
# there's no CLIP Skip here because all hidden layers have size of 1024 and the last one uses a
|
||||
# trained layer to transform those 1024 into 768 for unet; so you can't choose which transformer
|
||||
# layer to work with - you have to use the last
|
||||
|
||||
attention_mask = (tokens != self.id_pad).to(device=tokens.device, dtype=torch.int64)
|
||||
features = self.wrapped(input_ids=tokens, attention_mask=attention_mask)
|
||||
z = features['projection_state']
|
||||
|
||||
return z
|
||||
|
||||
def encode_embedding_init_text(self, init_text, nvpt):
|
||||
embedding_layer = self.wrapped.roberta.embeddings
|
||||
ids = self.wrapped.tokenizer(init_text, max_length=nvpt, return_tensors="pt", add_special_tokens=False)["input_ids"]
|
||||
embedded = embedding_layer.token_embedding.wrapped(ids.to(devices.device)).squeeze(0)
|
||||
|
||||
return embedded
|
||||
+17
-96
@@ -10,13 +10,12 @@ import diffusers.loaders.single_file_utils
|
||||
import torch
|
||||
import huggingface_hub as hf
|
||||
from installer import log
|
||||
from modules import paths, shared, shared_state, shared_items, modelloader, devices, script_callbacks, sd_vae, sd_unet, errors, sd_models_config, sd_models_compile, sd_hijack_accelerate, sd_detect, model_quant, sd_hijack_te
|
||||
from modules import paths, shared, shared_state, shared_items, modelloader, devices, script_callbacks, sd_vae, sd_unet, errors, sd_models_compile, sd_hijack_accelerate, sd_detect, model_quant, sd_hijack_te
|
||||
from modules.timer import Timer, process as process_timer
|
||||
from modules.memstats import memory_stats
|
||||
from modules.modeldata import model_data
|
||||
from modules.sd_checkpoint import CheckpointInfo, select_checkpoint, list_models, checkpoints_list, checkpoint_titles, get_closet_checkpoint_match, model_hash, update_model_hashes, setup_model, write_metadata, read_metadata_from_safetensors # pylint: disable=unused-import
|
||||
from modules.sd_offload import disable_offload, set_diffuser_offload, apply_balanced_offload, set_accelerate # pylint: disable=unused-import
|
||||
from modules.sd_models_legacy import get_checkpoint_state_dict, load_model_weights, load_model, repair_config # pylint: disable=unused-import
|
||||
from modules.sd_models_utils import NoWatermark, get_signature, get_call, path_to_repo, patch_diffuser_config, convert_to_faketensors, read_state_dict, get_state_dict_from_checkpoint, apply_function_to_model # pylint: disable=unused-import
|
||||
|
||||
|
||||
@@ -58,19 +57,6 @@ i2i_pipes = [
|
||||
]
|
||||
|
||||
|
||||
def change_backend():
|
||||
shared.log.info(f'Backend changed: from={shared.backend} to={shared.opts.sd_backend}')
|
||||
shared.log.warning('Full server restart required to apply all changes')
|
||||
unload_model_weights()
|
||||
shared.backend = shared.Backend.ORIGINAL if shared.opts.sd_backend == 'original' else shared.Backend.DIFFUSERS
|
||||
shared.native = shared.backend == shared.Backend.DIFFUSERS
|
||||
from modules.sd_samplers import list_samplers
|
||||
list_samplers()
|
||||
list_models()
|
||||
from modules.sd_vae import refresh_vae_list
|
||||
refresh_vae_list()
|
||||
|
||||
|
||||
def copy_diffuser_options(new_pipe, orig_pipe):
|
||||
new_pipe.sd_checkpoint_info = getattr(orig_pipe, 'sd_checkpoint_info', None)
|
||||
new_pipe.sd_model_checkpoint = getattr(orig_pipe, 'sd_model_checkpoint', None)
|
||||
@@ -185,18 +171,6 @@ def move_model(model, device=None, force=False):
|
||||
if model is None or device is None:
|
||||
return
|
||||
|
||||
if not shared.native:
|
||||
if type(model).__name__ == 'LatentDiffusion':
|
||||
model = model.to(device)
|
||||
if hasattr(model, 'model'):
|
||||
model.model = model.model.to(device)
|
||||
if hasattr(model, 'first_stage_model'):
|
||||
model.first_stage_model = model.first_stage_model.to(device)
|
||||
if hasattr(model, 'cond_stage_model'):
|
||||
model.cond_stage_model = model.cond_stage_model.to(device)
|
||||
devices.torch_gc()
|
||||
return
|
||||
|
||||
if hasattr(model, 'pipe'):
|
||||
move_model(model.pipe, device, force)
|
||||
|
||||
@@ -541,7 +515,7 @@ def set_defaults(sd_model, checkpoint_info):
|
||||
sd_model.set_progress_bar_config(bar_format='Progress {rate_fmt}{postfix} {bar} {percentage:3.0f}% {n_fmt}/{total_fmt} {elapsed} {remaining}', ncols=80, colour='#327fba')
|
||||
|
||||
|
||||
def load_diffuser(checkpoint_info=None, already_loaded_state_dict=None, timer=None, op='model', revision=None): # pylint: disable=unused-argument
|
||||
def load_diffuser(checkpoint_info=None, timer=None, op='model', revision=None): # pylint: disable=unused-argument
|
||||
if timer is None:
|
||||
timer = Timer()
|
||||
logging.getLogger("diffusers").setLevel(logging.ERROR)
|
||||
@@ -1033,22 +1007,14 @@ def reload_text_encoder(initial=False):
|
||||
set_t5(pipe=shared.sd_model, module='text_encoder_3', t5=shared.opts.sd_text_encoder, cache_dir=shared.opts.diffusers_dir)
|
||||
|
||||
|
||||
def reload_model_weights(sd_model=None, info=None, reuse_dict=False, op='model', force=False, revision=None):
|
||||
load_dict = shared.opts.sd_model_dict != model_data.sd_dict
|
||||
from modules import lowvram, sd_hijack
|
||||
def reload_model_weights(sd_model=None, info=None, op='model', force=False, revision=None):
|
||||
checkpoint_info = info or select_checkpoint(op=op) # are we selecting model or dictionary
|
||||
next_checkpoint_info = info or select_checkpoint(op='dict' if load_dict else 'model') if load_dict else None
|
||||
if checkpoint_info is None:
|
||||
unload_model_weights(op=op)
|
||||
return None
|
||||
orig_state = copy.deepcopy(shared.state)
|
||||
shared.state = shared_state.State()
|
||||
shared.state.begin('Load')
|
||||
if load_dict:
|
||||
shared.log.debug(f'Load {op} dict: target="{checkpoint_info.filename}" existing={sd_model is not None} info={info}')
|
||||
else:
|
||||
model_data.sd_dict = 'None'
|
||||
# shared.log.debug(f'Load {op}: target="{checkpoint_info.filename}" existing={sd_model is not None} info={info}')
|
||||
if sd_model is None:
|
||||
sd_model = model_data.sd_model if op == 'model' or op == 'dict' else model_data.sd_refiner
|
||||
if sd_model is None: # previous model load failed
|
||||
@@ -1057,69 +1023,33 @@ def reload_model_weights(sd_model=None, info=None, reuse_dict=False, op='model',
|
||||
current_checkpoint_info = getattr(sd_model, 'sd_checkpoint_info', None)
|
||||
if current_checkpoint_info is not None and checkpoint_info is not None and current_checkpoint_info.filename == checkpoint_info.filename and not force:
|
||||
return None
|
||||
if not shared.native and (shared.cmd_opts.lowvram or shared.cmd_opts.medvram):
|
||||
lowvram.send_everything_to_cpu()
|
||||
else:
|
||||
move_model(sd_model, devices.cpu)
|
||||
if (reuse_dict or shared.opts.model_reuse_dict) and not getattr(sd_model, 'has_accelerate', False):
|
||||
shared.log.info(f'Load {op}: reusing dictionary')
|
||||
sd_hijack.model_hijack.undo_hijack(sd_model)
|
||||
else:
|
||||
unload_model_weights(op=op)
|
||||
sd_model = None
|
||||
unload_model_weights(op=op)
|
||||
sd_model = None
|
||||
timer = Timer()
|
||||
# TODO model load: implement model in-memory caching
|
||||
state_dict = get_checkpoint_state_dict(checkpoint_info, timer) if not shared.native else None
|
||||
checkpoint_config = sd_models_config.find_checkpoint_config(state_dict, checkpoint_info)
|
||||
timer.record("config")
|
||||
if sd_model is None or checkpoint_config != getattr(sd_model, 'used_config', None) or force:
|
||||
if sd_model is None or force:
|
||||
sd_model = None
|
||||
if not shared.native:
|
||||
load_model(checkpoint_info, already_loaded_state_dict=state_dict, timer=timer, op=op)
|
||||
model_data.sd_dict = shared.opts.sd_model_dict
|
||||
else:
|
||||
load_diffuser(checkpoint_info, already_loaded_state_dict=state_dict, timer=timer, op=op, revision=revision)
|
||||
if load_dict and next_checkpoint_info is not None:
|
||||
model_data.sd_dict = shared.opts.sd_model_dict
|
||||
shared.opts.data["sd_model_checkpoint"] = next_checkpoint_info.title
|
||||
reload_model_weights(reuse_dict=True) # ok we loaded dict now lets redo and load model on top of it
|
||||
load_diffuser(checkpoint_info, timer=timer, op=op, revision=revision)
|
||||
shared.state.end()
|
||||
shared.state = orig_state
|
||||
# data['sd_model_checkpoint']
|
||||
if op == 'model' or op == 'dict':
|
||||
if op == 'model':
|
||||
shared.opts.data["sd_model_checkpoint"] = checkpoint_info.title
|
||||
return model_data.sd_model
|
||||
else:
|
||||
shared.opts.data["sd_model_refiner"] = checkpoint_info.title
|
||||
return model_data.sd_refiner
|
||||
|
||||
# fallback
|
||||
shared.log.info(f"Load {op} using fallback: model={checkpoint_info.title}")
|
||||
try:
|
||||
load_model_weights(sd_model, checkpoint_info, state_dict, timer)
|
||||
except Exception:
|
||||
shared.log.error("Load model failed: restoring previous")
|
||||
load_model_weights(sd_model, current_checkpoint_info, None, timer)
|
||||
finally:
|
||||
sd_hijack.model_hijack.hijack(sd_model)
|
||||
timer.record("hijack")
|
||||
script_callbacks.model_loaded_callback(sd_model)
|
||||
timer.record("callbacks")
|
||||
if sd_model is not None and not shared.cmd_opts.lowvram and not shared.cmd_opts.medvram:
|
||||
move_model(sd_model, devices.device)
|
||||
timer.record("device")
|
||||
shared.state.end()
|
||||
shared.state = orig_state
|
||||
shared.log.info(f"Load {op}: time={timer.summary()}")
|
||||
return sd_model
|
||||
return None # should not be here
|
||||
|
||||
|
||||
def clear_caches():
|
||||
if not shared.opts.lora_legacy:
|
||||
from modules.lora import lora_common, lora_load
|
||||
lora_common.loaded_networks.clear()
|
||||
lora_common.previously_loaded_networks.clear()
|
||||
lora_load.lora_cache.clear()
|
||||
from modules.lora import lora_common, lora_load
|
||||
lora_common.loaded_networks.clear()
|
||||
lora_common.previously_loaded_networks.clear()
|
||||
lora_load.lora_cache.clear()
|
||||
|
||||
from modules import prompt_parser_diffusers, memstats, sd_offload
|
||||
sd_offload.offload_hook_instance = None
|
||||
prompt_parser_diffusers.cache.clear()
|
||||
@@ -1134,11 +1064,7 @@ def unload_model_weights(op='model'):
|
||||
shared.compiled_model_state.partitioned_modules.clear()
|
||||
if (op == 'model' or op == 'dict') and model_data.sd_model:
|
||||
shared.log.debug(f'Current {op}: {memory_stats()}')
|
||||
if not shared.native:
|
||||
from modules import sd_hijack
|
||||
move_model(model_data.sd_model, devices.cpu)
|
||||
sd_hijack.model_hijack.undo_hijack(model_data.sd_model)
|
||||
elif not ('Model' in shared.opts.cuda_compile and shared.opts.cuda_compile_backend == "openvino_fx"):
|
||||
if not ('Model' in shared.opts.cuda_compile and shared.opts.cuda_compile_backend == "openvino_fx"):
|
||||
disable_offload(model_data.sd_model)
|
||||
move_model(model_data.sd_model, 'meta')
|
||||
model_data.sd_model = None
|
||||
@@ -1146,13 +1072,8 @@ def unload_model_weights(op='model'):
|
||||
shared.log.debug(f'Unload {op}: {memory_stats()} after')
|
||||
elif (op == 'refiner') and model_data.sd_refiner:
|
||||
shared.log.debug(f'Current {op}: {memory_stats()}')
|
||||
if not shared.native:
|
||||
from modules import sd_hijack
|
||||
move_model(model_data.sd_refiner, devices.cpu)
|
||||
sd_hijack.model_hijack.undo_hijack(model_data.sd_refiner)
|
||||
else:
|
||||
disable_offload(model_data.sd_refiner)
|
||||
move_model(model_data.sd_refiner, 'meta')
|
||||
disable_offload(model_data.sd_refiner)
|
||||
move_model(model_data.sd_refiner, 'meta')
|
||||
model_data.sd_refiner = None
|
||||
devices.torch_gc(force=True)
|
||||
shared.log.debug(f'Unload {op}: {memory_stats()}')
|
||||
|
||||
@@ -1,114 +0,0 @@
|
||||
import os
|
||||
|
||||
import torch
|
||||
|
||||
from modules import paths, devices
|
||||
|
||||
sd_repo_configs_path = 'configs'
|
||||
config_default = paths.sd_default_config
|
||||
config_sd2 = os.path.join(sd_repo_configs_path, "v2-inference-512-base.yaml")
|
||||
config_sd2v = os.path.join(sd_repo_configs_path, "v2-inference-768-v.yaml")
|
||||
config_sd2_inpainting = os.path.join(sd_repo_configs_path, "v2-inpainting-inference.yaml")
|
||||
config_depth_model = os.path.join(sd_repo_configs_path, "v2-midas-inference.yaml")
|
||||
config_unclip = os.path.join(sd_repo_configs_path, "v2-1-stable-unclip-l-inference.yaml")
|
||||
config_unopenclip = os.path.join(sd_repo_configs_path, "v2-1-stable-unclip-h-inference.yaml")
|
||||
config_inpainting = os.path.join(paths.sd_configs_path, "v1-inpainting-inference.yaml")
|
||||
config_instruct_pix2pix = os.path.join(paths.sd_configs_path, "instruct-pix2pix.yaml")
|
||||
config_alt_diffusion = os.path.join(paths.sd_configs_path, "alt-diffusion-inference.yaml")
|
||||
|
||||
|
||||
def is_using_v_parameterization_for_sd2(state_dict):
|
||||
"""
|
||||
Detects whether unet in state_dict is using v-parameterization. Returns True if it is. You're welcome.
|
||||
"""
|
||||
from modules import sd_disable_initialization
|
||||
import ldm.modules.diffusionmodules.openaimodel
|
||||
|
||||
device = devices.cpu
|
||||
with sd_disable_initialization.DisableInitialization():
|
||||
unet = ldm.modules.diffusionmodules.openaimodel.UNetModel(
|
||||
use_checkpoint=True,
|
||||
use_fp16=False,
|
||||
image_size=32,
|
||||
in_channels=4,
|
||||
out_channels=4,
|
||||
model_channels=320,
|
||||
attention_resolutions=[4, 2, 1],
|
||||
num_res_blocks=2,
|
||||
channel_mult=[1, 2, 4, 4],
|
||||
num_head_channels=64,
|
||||
use_spatial_transformer=True,
|
||||
use_linear_in_transformer=True,
|
||||
transformer_depth=1,
|
||||
context_dim=1024,
|
||||
legacy=False
|
||||
)
|
||||
unet.eval()
|
||||
|
||||
with devices.inference_context():
|
||||
unet_sd = {k.replace("model.diffusion_model.", ""): v for k, v in state_dict.items() if "model.diffusion_model." in k}
|
||||
unet.load_state_dict(unet_sd, strict=True)
|
||||
unet.to(device=device, dtype=torch.float)
|
||||
|
||||
test_cond = torch.ones((1, 2, 1024), device=device) * 0.5
|
||||
x_test = torch.ones((1, 4, 8, 8), device=device) * 0.5
|
||||
|
||||
out = (unet(x_test, torch.asarray([999], device=device), context=test_cond) - x_test).mean().item()
|
||||
|
||||
return out < -1
|
||||
|
||||
|
||||
def guess_model_config_from_state_dict(sd, _filename):
|
||||
if sd is None:
|
||||
return None
|
||||
sd2_cond_proj_weight = sd.get('cond_stage_model.model.transformer.resblocks.0.attn.in_proj_weight', None)
|
||||
diffusion_model_input = sd.get('model.diffusion_model.input_blocks.0.0.weight', None)
|
||||
sd2_variations_weight = sd.get('embedder.model.ln_final.weight', None)
|
||||
|
||||
if sd.get('depth_model.model.pretrained.act_postprocess3.0.project.0.bias', None) is not None:
|
||||
return config_depth_model
|
||||
elif sd2_variations_weight is not None and sd2_variations_weight.shape[0] == 768:
|
||||
return config_unclip
|
||||
elif sd2_variations_weight is not None and sd2_variations_weight.shape[0] == 1024:
|
||||
return config_unopenclip
|
||||
|
||||
if sd2_cond_proj_weight is not None and sd2_cond_proj_weight.shape[1] == 1024:
|
||||
if diffusion_model_input.shape[1] == 9:
|
||||
return config_sd2_inpainting
|
||||
elif is_using_v_parameterization_for_sd2(sd):
|
||||
return config_sd2v
|
||||
else:
|
||||
return config_sd2
|
||||
|
||||
if diffusion_model_input is not None:
|
||||
if diffusion_model_input.shape[1] == 9:
|
||||
return config_inpainting
|
||||
if diffusion_model_input.shape[1] == 8:
|
||||
return config_instruct_pix2pix
|
||||
|
||||
if sd.get('cond_stage_model.roberta.embeddings.word_embeddings.weight', None) is not None:
|
||||
return config_alt_diffusion
|
||||
|
||||
return config_default
|
||||
|
||||
|
||||
def find_checkpoint_config(state_dict, info):
|
||||
if info is None:
|
||||
return guess_model_config_from_state_dict(state_dict, "")
|
||||
|
||||
config = find_checkpoint_config_near_filename(info)
|
||||
if config is not None:
|
||||
return config
|
||||
|
||||
return guess_model_config_from_state_dict(state_dict, info.filename)
|
||||
|
||||
|
||||
def find_checkpoint_config_near_filename(info):
|
||||
if info is None:
|
||||
return None
|
||||
|
||||
config = f"{os.path.splitext(info.filename)[0]}.yaml"
|
||||
if os.path.exists(config):
|
||||
return config
|
||||
|
||||
return None
|
||||
@@ -1,206 +0,0 @@
|
||||
import io
|
||||
import os
|
||||
import sys
|
||||
import contextlib
|
||||
|
||||
from modules import shared
|
||||
|
||||
|
||||
sd1_clip_weight = 'cond_stage_model.transformer.text_model.embeddings.token_embedding.weight'
|
||||
sd2_clip_weight = 'cond_stage_model.model.transformer.resblocks.0.attn.in_proj_weight'
|
||||
|
||||
|
||||
def get_checkpoint_state_dict(checkpoint_info, timer):
|
||||
from modules.sd_models_utils import read_state_dict
|
||||
if not os.path.isfile(checkpoint_info.filename):
|
||||
return None
|
||||
"""
|
||||
if checkpoint_info in checkpoints_loaded:
|
||||
shared.log.info("Load model: cache")
|
||||
checkpoints_loaded.move_to_end(checkpoint_info, last=True) # FIFO -> LRU cache
|
||||
return checkpoints_loaded[checkpoint_info]
|
||||
"""
|
||||
res = read_state_dict(checkpoint_info.filename, what='model')
|
||||
"""
|
||||
if shared.opts.sd_checkpoint_cache > 0 and not shared.native:
|
||||
# cache newly loaded model
|
||||
checkpoints_loaded[checkpoint_info] = res
|
||||
# clean up cache if limit is reached
|
||||
while len(checkpoints_loaded) > shared.opts.sd_checkpoint_cache:
|
||||
checkpoints_loaded.popitem(last=False)
|
||||
"""
|
||||
timer.record("load")
|
||||
return res
|
||||
|
||||
|
||||
def repair_config(sd_config):
|
||||
from modules import paths
|
||||
if "use_ema" not in sd_config.model.params:
|
||||
sd_config.model.params.use_ema = False
|
||||
if shared.opts.no_half:
|
||||
sd_config.model.params.unet_config.params.use_fp16 = False
|
||||
elif shared.opts.upcast_sampling:
|
||||
sd_config.model.params.unet_config.params.use_fp16 = True if sys.platform != 'darwin' else False
|
||||
if getattr(sd_config.model.params.first_stage_config.params.ddconfig, "attn_type", None) == "vanilla-xformers" and not shared.xformers_available:
|
||||
sd_config.model.params.first_stage_config.params.ddconfig.attn_type = "vanilla"
|
||||
# For UnCLIP-L, override the hardcoded karlo directory
|
||||
if "noise_aug_config" in sd_config.model.params and "clip_stats_path" in sd_config.model.params.noise_aug_config.params:
|
||||
karlo_path = os.path.join(paths.models_path, 'karlo')
|
||||
sd_config.model.params.noise_aug_config.params.clip_stats_path = sd_config.model.params.noise_aug_config.params.clip_stats_path.replace("checkpoints/karlo_models", karlo_path)
|
||||
|
||||
|
||||
def load_model_weights(model, checkpoint_info, state_dict, timer):
|
||||
from modules.modeldata import model_data
|
||||
from modules.memstats import memory_stats
|
||||
from modules import devices, sd_vae
|
||||
shared.log.debug(f'Load model: memory={memory_stats()}')
|
||||
timer.record("hash")
|
||||
if model_data.sd_dict == 'None':
|
||||
shared.opts.data["sd_model_checkpoint"] = checkpoint_info.title
|
||||
if state_dict is None:
|
||||
state_dict = get_checkpoint_state_dict(checkpoint_info, timer)
|
||||
try:
|
||||
model.load_state_dict(state_dict, strict=False)
|
||||
except Exception as e:
|
||||
shared.log.error(f'Load model: path="{checkpoint_info.filename}"')
|
||||
shared.log.error(' '.join(str(e).splitlines()[:2]))
|
||||
return False
|
||||
del state_dict
|
||||
timer.record("apply")
|
||||
if shared.opts.opt_channelslast:
|
||||
import torch
|
||||
model.to(memory_format=torch.channels_last)
|
||||
timer.record("channels")
|
||||
if not shared.opts.no_half:
|
||||
vae = model.first_stage_model
|
||||
depth_model = getattr(model, 'depth_model', None)
|
||||
if shared.opts.no_half_vae: # remove VAE from model when doing half() to prevent its weights from being converted to float16
|
||||
model.first_stage_model = None
|
||||
if shared.opts.upcast_sampling and depth_model: # with don't convert the depth model weights to float16
|
||||
model.depth_model = None
|
||||
model.half()
|
||||
model.first_stage_model = vae
|
||||
if depth_model:
|
||||
model.depth_model = depth_model
|
||||
if shared.opts.cuda_cast_unet:
|
||||
devices.dtype_unet = model.model.diffusion_model.dtype
|
||||
else:
|
||||
model.model.diffusion_model.to(devices.dtype_unet)
|
||||
model.first_stage_model.to(devices.dtype_vae)
|
||||
model.sd_model_hash = checkpoint_info.calculate_shorthash()
|
||||
model.sd_model_checkpoint = checkpoint_info.filename
|
||||
model.sd_checkpoint_info = checkpoint_info
|
||||
model.is_sdxl = False # a1111 compatibility item
|
||||
model.is_sd2 = hasattr(model.cond_stage_model, 'model') # a1111 compatibility item
|
||||
model.is_sd1 = not hasattr(model.cond_stage_model, 'model') # a1111 compatibility item
|
||||
model.logvar = model.logvar.to(devices.device) if hasattr(model, 'logvar') else None # fix for training
|
||||
shared.opts.data["sd_checkpoint_hash"] = checkpoint_info.sha256
|
||||
sd_vae.delete_base_vae()
|
||||
sd_vae.clear_loaded_vae()
|
||||
vae_file, vae_source = sd_vae.resolve_vae(checkpoint_info.filename)
|
||||
sd_vae.load_vae(model, vae_file, vae_source)
|
||||
timer.record("vae")
|
||||
return True
|
||||
|
||||
|
||||
def load_model(checkpoint_info=None, already_loaded_state_dict=None, timer=None, op='model'):
|
||||
from ldm.util import instantiate_from_config
|
||||
from omegaconf import OmegaConf
|
||||
from modules import devices, lowvram, sd_hijack, sd_models_config, script_callbacks
|
||||
from modules.timer import Timer
|
||||
from modules.memstats import memory_stats
|
||||
from modules.modeldata import model_data
|
||||
from modules.sd_models import unload_model_weights, move_model
|
||||
from modules.sd_checkpoint import select_checkpoint
|
||||
checkpoint_info = checkpoint_info or select_checkpoint(op=op)
|
||||
if checkpoint_info is None:
|
||||
return
|
||||
if op == 'model' or op == 'dict':
|
||||
if (model_data.sd_model is not None) and (getattr(model_data.sd_model, 'sd_checkpoint_info', None) is not None) and (checkpoint_info.hash == model_data.sd_model.sd_checkpoint_info.hash): # trying to load the same model
|
||||
return
|
||||
else:
|
||||
if (model_data.sd_refiner is not None) and (getattr(model_data.sd_refiner, 'sd_checkpoint_info', None) is not None) and (checkpoint_info.hash == model_data.sd_refiner.sd_checkpoint_info.hash): # trying to load the same model
|
||||
return
|
||||
shared.log.debug(f'Load {op}: name={checkpoint_info.filename} dict={already_loaded_state_dict is not None}')
|
||||
if timer is None:
|
||||
timer = Timer()
|
||||
current_checkpoint_info = None
|
||||
if op == 'model' or op == 'dict':
|
||||
if model_data.sd_model is not None:
|
||||
sd_hijack.model_hijack.undo_hijack(model_data.sd_model)
|
||||
current_checkpoint_info = getattr(model_data.sd_model, 'sd_checkpoint_info', None)
|
||||
unload_model_weights(op=op)
|
||||
else:
|
||||
if model_data.sd_refiner is not None:
|
||||
sd_hijack.model_hijack.undo_hijack(model_data.sd_refiner)
|
||||
current_checkpoint_info = getattr(model_data.sd_refiner, 'sd_checkpoint_info', None)
|
||||
unload_model_weights(op=op)
|
||||
|
||||
if not shared.native:
|
||||
from modules import sd_hijack_inpainting
|
||||
sd_hijack_inpainting.do_inpainting_hijack()
|
||||
|
||||
if already_loaded_state_dict is not None:
|
||||
state_dict = already_loaded_state_dict
|
||||
else:
|
||||
state_dict = get_checkpoint_state_dict(checkpoint_info, timer)
|
||||
checkpoint_config = sd_models_config.find_checkpoint_config(state_dict, checkpoint_info)
|
||||
if state_dict is None or checkpoint_config is None:
|
||||
shared.log.error(f'Load {op}: path="{checkpoint_info.filename}"')
|
||||
if current_checkpoint_info is not None:
|
||||
shared.log.info(f'Load {op}: previous="{current_checkpoint_info.filename}" restore')
|
||||
load_model(current_checkpoint_info, None)
|
||||
return
|
||||
shared.log.debug(f'Model dict loaded: {memory_stats()}')
|
||||
sd_config = OmegaConf.load(checkpoint_config)
|
||||
repair_config(sd_config)
|
||||
timer.record("config")
|
||||
shared.log.debug(f'Model config loaded: {memory_stats()}')
|
||||
sd_model = None
|
||||
stdout = io.StringIO()
|
||||
if os.environ.get('SD_LDM_DEBUG', None) is not None:
|
||||
sd_model = instantiate_from_config(sd_config.model)
|
||||
else:
|
||||
with contextlib.redirect_stdout(stdout):
|
||||
sd_model = instantiate_from_config(sd_config.model)
|
||||
for line in stdout.getvalue().splitlines():
|
||||
if len(line) > 0:
|
||||
shared.log.info(f'LDM: {line.strip()}')
|
||||
shared.log.debug(f"Model created from config: {checkpoint_config}")
|
||||
sd_model.used_config = checkpoint_config
|
||||
sd_model.has_accelerate = False
|
||||
timer.record("create")
|
||||
ok = load_model_weights(sd_model, checkpoint_info, state_dict, timer)
|
||||
if not ok:
|
||||
model_data.sd_model = sd_model
|
||||
current_checkpoint_info = None
|
||||
unload_model_weights(op=op)
|
||||
shared.log.debug(f'Model weights unloaded: {memory_stats()} op={op}')
|
||||
if op == 'refiner':
|
||||
# shared.opts.data['sd_model_refiner'] = 'None'
|
||||
shared.opts.sd_model_refiner = 'None'
|
||||
return
|
||||
else:
|
||||
shared.log.debug(f'Model weights loaded: {memory_stats()}')
|
||||
timer.record("load")
|
||||
if not shared.native and (shared.cmd_opts.lowvram or shared.cmd_opts.medvram):
|
||||
lowvram.setup_for_low_vram(sd_model, shared.cmd_opts.medvram)
|
||||
else:
|
||||
move_model(sd_model, devices.device)
|
||||
timer.record("move")
|
||||
shared.log.debug(f'Model weights moved: {memory_stats()}')
|
||||
sd_hijack.model_hijack.hijack(sd_model)
|
||||
timer.record("hijack")
|
||||
sd_model.eval()
|
||||
if op == 'refiner':
|
||||
model_data.sd_refiner = sd_model
|
||||
else:
|
||||
model_data.sd_model = sd_model
|
||||
sd_hijack.model_hijack.embedding_db.load_textual_inversion_embeddings(force_reload=True) # Reload embeddings after model load as they may or may not fit the model
|
||||
timer.record("embeddings")
|
||||
script_callbacks.model_loaded_callback(sd_model)
|
||||
timer.record("callbacks")
|
||||
shared.log.info(f"Model loaded in {timer.summary()}")
|
||||
current_checkpoint_info = None
|
||||
devices.torch_gc(force=True)
|
||||
shared.log.info(f'Model load finished: {memory_stats()}')
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user