initial setup: ComfyUI + kohya_ss scripts, LoRA config, workflows

This commit is contained in:
Johannes
2026-03-13 22:12:04 +01:00
commit 4c2972e7a2
9 changed files with 419 additions and 0 deletions

31
.gitignore vendored Normal file
View File

@@ -0,0 +1,31 @@
# models - too large for git
models/
*.safetensors
*.ckpt
*.pt
*.pth
*.bin
# generated outputs
output/
outputs/
# venvs / installs
comfyui/venv/
comfyui/__pycache__/
kohya_ss/venv/
kohya_ss/__pycache__/
kohya_ss/logs/
# training data (images)
training_data/
# ComfyUI user data / cache
comfyui/user/
comfyui/temp/
comfyui/custom_nodes/**/node_modules/
# misc
*.log
.DS_Store
Thumbs.db

72
README.md Normal file
View File

@@ -0,0 +1,72 @@
# animepics
Anime image generation + LoRA training setup.
## Stack
- **ComfyUI** — image generation UI
- **NoobAI-XL** — base anime model (SDXL-based, SFW+NSFW)
- **kohya_ss** — LoRA training
## Requirements
- Python 3.10+ installed and in PATH
- Git installed
- NVIDIA GPU with CUDA (4070 recommended)
- ~20GB free disk space for models
## Setup
Run once to install everything:
```powershell
.\setup.ps1
```
This will:
1. Clone ComfyUI and install dependencies
2. Clone kohya_ss and install dependencies
3. Install ComfyUI custom nodes (AnimateDiff, ControlNet, etc.)
4. Download the NoobAI-XL base model + VAE
## Launch
```powershell
# Start image generation UI (opens browser at localhost:8188)
.\launch_comfyui.ps1
# Start LoRA training UI (opens browser at localhost:7860)
.\launch_kohya.ps1
```
## LoRA Training
1. Put your training images in `training_data/<your_lora_name>/img/10_<trigger_word>/`
2. Copy `training/example_lora_config.toml` and edit it
3. Launch kohya and use the GUI, or run `.\train_lora.ps1 <config_file>`
## Directory Structure
```
animepics/
├── comfyui/ # ComfyUI install (gitignored venv)
├── kohya_ss/ # kohya_ss install (gitignored venv)
├── models/ # shared model storage (gitignored)
│ ├── checkpoints/ # base models (.safetensors)
│ ├── loras/ # trained LoRAs
│ ├── vae/ # VAE models
│ ├── embeddings/ # textual inversions
│ └── controlnet/ # ControlNet models
├── training_data/ # LoRA training images (gitignored)
├── output/ # generated images (gitignored)
├── training/ # LoRA training configs
└── workflows/ # ComfyUI workflow JSON files
```
## Model Downloads
Base model (NoobAI-XL Vpred):
- https://civitai.com/models/833294
Good NSFW VAE:
- Already baked into NoobAI, but sdxl_vae.safetensors from stabilityai works too

View File

@@ -0,0 +1,11 @@
# ComfyUI extra model paths — points to shared models/ directory
# so both ComfyUI and kohya_ss use the same model storage
animepics:
base_path: E:/animepics/models
checkpoints: checkpoints
loras: loras
vae: vae
embeddings: embeddings
controlnet: controlnet
upscale_models: upscale_models

12
launch_comfyui.ps1 Normal file
View File

@@ -0,0 +1,12 @@
#!/usr/bin/env pwsh
# launch_comfyui.ps1 — start ComfyUI
$root = $PSScriptRoot
Set-Location "$root/comfyui"
.\venv\Scripts\Activate.ps1
python main.py `
--listen 127.0.0.1 `
--port 8188 `
--extra-model-paths-config "$root/comfyui/extra_model_paths.yaml" `
--preview-method auto

8
launch_kohya.ps1 Normal file
View File

@@ -0,0 +1,8 @@
#!/usr/bin/env pwsh
# launch_kohya.ps1 — start kohya_ss GUI
$root = $PSScriptRoot
Set-Location "$root/kohya_ss"
.\venv\Scripts\Activate.ps1
python kohya_gui.py --listen 127.0.0.1 --server_port 7860

88
setup.ps1 Normal file
View File

@@ -0,0 +1,88 @@
#!/usr/bin/env pwsh
# setup.ps1 — one-time install of ComfyUI + kohya_ss
$root = $PSScriptRoot
Set-Location $root
Write-Host "==> setting up animepics environment" -ForegroundColor Cyan
# create model dirs
$model_dirs = @(
"models/checkpoints",
"models/loras",
"models/vae",
"models/embeddings",
"models/controlnet",
"models/upscale_models",
"training_data",
"output"
)
foreach ($dir in $model_dirs) {
New-Item -ItemType Directory -Force -Path "$root/$dir" | Out-Null
}
Write-Host " created model directories" -ForegroundColor Green
# --- ComfyUI ---
Write-Host "`n==> installing ComfyUI" -ForegroundColor Cyan
if (-not (Test-Path "$root/comfyui")) {
git clone https://github.com/comfyanonymous/ComfyUI.git "$root/comfyui"
} else {
Write-Host " comfyui already cloned, pulling latest" -ForegroundColor Yellow
git -C "$root/comfyui" pull
}
Push-Location "$root/comfyui"
python -m venv venv
.\venv\Scripts\Activate.ps1
pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu124
pip install -r requirements.txt
deactivate
Pop-Location
Write-Host " ComfyUI installed" -ForegroundColor Green
# ComfyUI custom nodes
Write-Host "`n==> installing ComfyUI custom nodes" -ForegroundColor Cyan
$custom_nodes = @(
@{ name="ComfyUI-Manager"; url="https://github.com/ltdrdata/ComfyUI-Manager.git" },
@{ name="ComfyUI_IPAdapter_plus"; url="https://github.com/cubiq/ComfyUI_IPAdapter_plus.git" },
@{ name="ComfyUI-Advanced-ControlNet"; url="https://github.com/Kosinkadink/ComfyUI-Advanced-ControlNet.git" },
@{ name="was-node-suite-comfyui"; url="https://github.com/WASasquatch/was-node-suite-comfyui.git" }
)
New-Item -ItemType Directory -Force -Path "$root/comfyui/custom_nodes" | Out-Null
foreach ($node in $custom_nodes) {
$node_path = "$root/comfyui/custom_nodes/$($node.name)"
if (-not (Test-Path $node_path)) {
git clone $node.url $node_path
Write-Host " cloned $($node.name)" -ForegroundColor Green
} else {
git -C $node_path pull
Write-Host " updated $($node.name)" -ForegroundColor Yellow
}
}
# copy extra_model_paths config so ComfyUI finds our shared models dir
Copy-Item -Force "$root/config/extra_model_paths.yaml" "$root/comfyui/extra_model_paths.yaml"
Write-Host " copied extra_model_paths.yaml" -ForegroundColor Green
# --- kohya_ss ---
Write-Host "`n==> installing kohya_ss" -ForegroundColor Cyan
if (-not (Test-Path "$root/kohya_ss")) {
git clone https://github.com/bmaltais/kohya_ss.git "$root/kohya_ss"
} else {
Write-Host " kohya_ss already cloned, pulling latest" -ForegroundColor Yellow
git -C "$root/kohya_ss" pull
}
Push-Location "$root/kohya_ss"
python -m venv venv
.\venv\Scripts\Activate.ps1
pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu124
pip install -r requirements.txt
deactivate
Pop-Location
Write-Host " kohya_ss installed" -ForegroundColor Green
Write-Host "`n==> setup complete!" -ForegroundColor Cyan
Write-Host " next: download your base model into models/checkpoints/" -ForegroundColor White
Write-Host " recommended: NoobAI-XL from https://civitai.com/models/833294" -ForegroundColor White
Write-Host "`n then run: .\launch_comfyui.ps1" -ForegroundColor White

23
train_lora.ps1 Normal file
View File

@@ -0,0 +1,23 @@
#!/usr/bin/env pwsh
# train_lora.ps1 — run LoRA training from a config file
# usage: .\train_lora.ps1 training/my_lora.toml
param(
[Parameter(Mandatory=$true)]
[string]$config_file
)
$root = $PSScriptRoot
if (-not (Test-Path "$root/$config_file")) {
Write-Host "config file not found: $config_file" -ForegroundColor Red
exit 1
}
Set-Location "$root/kohya_ss"
.\venv\Scripts\Activate.ps1
accelerate launch `
--num_cpu_threads_per_process 2 `
train_network.py `
--config_file "$root/$config_file"

View File

@@ -0,0 +1,54 @@
# example LoRA training config for kohya_ss (SDXL)
# copy this, rename it, and edit the paths/settings for your lora
# then train with: .\train_lora.ps1 training/my_lora.toml
[model_arguments]
pretrained_model_name_or_path = "E:/animepics/models/checkpoints/noobai-xl.safetensors"
# set to true for vpred models (NoobAI-XL uses vpred)
v_parameterization = true
zero_terminal_snr = true
[saving_arguments]
save_every_n_epochs = 1
save_model_as = "safetensors"
output_dir = "E:/animepics/models/loras"
output_name = "my_lora_v1"
[dataset_arguments]
# dataset dir structure: training_data/<lora_name>/img/<repeats>_<trigger>/
train_data_dir = "E:/animepics/training_data/my_lora/img"
resolution = "1024,1024"
enable_bucket = true
min_bucket_reso = 512
max_bucket_reso = 2048
bucket_reso_steps = 64
caption_extension = ".txt"
shuffle_caption = true
keep_tokens = 1
[training_arguments]
output_dir = "E:/animepics/models/loras"
logging_dir = "E:/animepics/kohya_ss/logs"
max_train_epochs = 10
train_batch_size = 1
gradient_accumulation_steps = 1
gradient_checkpointing = true
mixed_precision = "bf16"
save_precision = "bf16"
seed = 42
max_token_length = 225
xformers = true
# learning rates — good defaults for NoobAI-XL
learning_rate = 0.0001
unet_lr = 0.0001
text_encoder_lr = 0.00005
lr_scheduler = "cosine_with_restarts"
lr_warmup_steps = 100
optimizer_type = "AdamW8bit"
[network_arguments]
network_module = "networks.lora"
network_dim = 32 # rank — higher = more capacity, 16-64 is typical
network_alpha = 16 # usually half of dim
# optional: train only specific layers
# network_args = ["conv_dim=16", "conv_alpha=8"]

View File

@@ -0,0 +1,120 @@
{
"last_node_id": 9,
"last_link_id": 9,
"nodes": [
{
"id": 4,
"type": "CheckpointLoaderSimple",
"pos": [26, 474],
"size": [315, 98],
"flags": {},
"order": 0,
"mode": 0,
"outputs": [
{"name": "MODEL", "type": "MODEL", "links": [1], "slot_index": 0},
{"name": "CLIP", "type": "CLIP", "links": [3, 5], "slot_index": 1},
{"name": "VAE", "type": "VAE", "links": [8], "slot_index": 2}
],
"properties": {"Node name for S&R": "CheckpointLoaderSimple"},
"widgets_values": ["noobai-xl.safetensors"]
},
{
"id": 6,
"type": "CLIPTextEncode",
"pos": [415, 186],
"size": [422, 164],
"flags": {},
"order": 2,
"mode": 0,
"inputs": [{"name": "clip", "type": "CLIP", "link": 3}],
"outputs": [{"name": "CONDITIONING", "type": "CONDITIONING", "links": [4], "slot_index": 0}],
"properties": {"Node name for S&R": "CLIPTextEncode"},
"widgets_values": ["1girl, anime, masterpiece, best quality, highly detailed, beautiful face, solo"]
},
{
"id": 7,
"type": "CLIPTextEncode",
"pos": [415, 400],
"size": [422, 164],
"flags": {},
"order": 3,
"mode": 0,
"inputs": [{"name": "clip", "type": "CLIP", "link": 5}],
"outputs": [{"name": "CONDITIONING", "type": "CONDITIONING", "links": [6], "slot_index": 0}],
"properties": {"Node name for S&R": "CLIPTextEncode"},
"widgets_values": ["worst quality, low quality, bad anatomy, extra limbs, poorly drawn face, ugly, watermark, text"]
},
{
"id": 5,
"type": "EmptyLatentImage",
"pos": [473, 609],
"size": [315, 106],
"flags": {},
"order": 1,
"mode": 0,
"outputs": [{"name": "LATENT", "type": "LATENT", "links": [2], "slot_index": 0}],
"properties": {"Node name for S&R": "EmptyLatentImage"},
"widgets_values": [832, 1216, 1]
},
{
"id": 3,
"type": "KSampler",
"pos": [863, 186],
"size": [315, 474],
"flags": {},
"order": 4,
"mode": 0,
"inputs": [
{"name": "model", "type": "MODEL", "link": 1},
{"name": "positive", "type": "CONDITIONING", "link": 4},
{"name": "negative", "type": "CONDITIONING", "link": 6},
{"name": "latent_image", "type": "LATENT", "link": 2}
],
"outputs": [{"name": "LATENT", "type": "LATENT", "links": [7], "slot_index": 0}],
"properties": {"Node name for S&R": "KSampler"},
"widgets_values": [42, "fixed", 28, 7, "dpmpp_2m", "karras", 1]
},
{
"id": 8,
"type": "VAEDecode",
"pos": [1209, 186],
"size": [210, 46],
"flags": {},
"order": 5,
"mode": 0,
"inputs": [
{"name": "samples", "type": "LATENT", "link": 7},
{"name": "vae", "type": "VAE", "link": 8}
],
"outputs": [{"name": "IMAGE", "type": "IMAGE", "links": [9], "slot_index": 0}],
"properties": {"Node name for S&R": "VAEDecode"}
},
{
"id": 9,
"type": "SaveImage",
"pos": [1451, 186],
"size": [210, 58],
"flags": {},
"order": 6,
"mode": 0,
"inputs": [{"name": "images", "type": "IMAGE", "link": 9}],
"properties": {"Node name for S&R": "SaveImage"},
"widgets_values": ["anime"]
}
],
"links": [
[1, 4, 0, 3, 0, "MODEL"],
[2, 5, 0, 3, 3, "LATENT"],
[3, 4, 1, 6, 0, "CLIP"],
[4, 6, 0, 3, 1, "CONDITIONING"],
[5, 4, 1, 7, 0, "CLIP"],
[6, 7, 0, 3, 2, "CONDITIONING"],
[7, 3, 0, 8, 0, "LATENT"],
[8, 4, 2, 8, 1, "VAE"],
[9, 8, 0, 9, 0, "IMAGE"]
],
"groups": [],
"config": {},
"extra": {"ds": {"scale": 0.8, "offset": [0, 0]}},
"version": 0.4
}