n番煎じ
参考記事
全体的な流れ
-
https://note.com/yatoracat/n/n96d99f0b4fe5
kohya_ssの導入 -
https://sakasaai.com/local_kohya_ss/
Pony向けのkohya_ss設定 - https://civitai.com/models/351583/sdxl-pony-fast-training-guide
環境
- Stable Diffusion Web UI (pinokio版。他でも同じだと思う)(以下SDという)
- kohya-ss
- VRC
- 3Dモデル
- 学習元モデル Pony Diffusion V6 XL
- NVIDIA GeForce RTX3060 (VRAM 12GB)
- Python (バージョンいろいろ)
kohya_ssの導入
- 自作LoRAの作り方|LoRAをKohya_ssで学習する方法のとおりにやる
- インストール先はD:\LoRA\kohya_ssにした(好みで)
- kohya_ssのwebuiが開けばOK
画像の準備
全身だけじゃなくて顔などのみのアップがあったほうがいいみたいです
- 適当なpyを組んで2048 x 2048白背景に変換する
- 完成したものを次のフォルダ直下に保存する
- D:\LoRA\kohya_ss\kohya_data\1_train
- 画像ファイルの1つ上のフォルダ名は重要、他はここでなくてもよい
強制的に上書きするコードです!注意
# 同じフォルダの透過背景画像を切り抜いて2048x2048白背景にするpython
# 注意:強制的に上書きします!対象だけコピーしてから使って下さい
# 動作にPillowが必要です(pip install pillow)
import os
from PIL import Image
ORG_DIR = '.' # 同じフォルダ
MARGIN = 15
TARGET_SIZE = 2024
for filename in os.listdir(ORG_DIR):
if filename.lower().endswith('.png') and filename != os.path.basename(__file__):
path = os.path.join(ORG_DIR, filename)
img = Image.open(path).convert('RGBA')
alpha = img.split()[-1]
bbox = alpha.getbbox()
if bbox:
left = max(bbox[0] - MARGIN, 0)
upper = max(bbox[1] - MARGIN, 0)
right = min(bbox[2] + MARGIN, img.width)
lower = min(bbox[3] + MARGIN, img.height)
cropped = img.crop((left, upper, right, lower))
# 縦横比を保って長辺がTARGET_SIZEになるようリサイズ
w, h = cropped.size
scale = TARGET_SIZE / max(w, h)
resized = cropped.resize((int(w * scale), int(h * scale)), Image.LANCZOS)
# 2024x2024の白背景を作成し中央に配置
bg = Image.new('RGBA', (TARGET_SIZE, TARGET_SIZE), (255, 255, 255, 255))
x = (TARGET_SIZE - resized.width) // 2
y = (TARGET_SIZE - resized.height) // 2
bg.paste(resized, (x, y), resized)
# 完全白背景に合成して上書き保存
bg.convert('RGB').save(path, 'PNG', optimize=True, compress_level=9)
SDの起動・Taggerの準備
- Taggerを導入してSDを再起動
- 導入手順はyatoracat様記事参考
- Dataset Tag Editorはいらなかった
Tagger用モデルZ3D-E621-Convnextの導入
明らかに正しくない手順です!(動きはするけど)
- Taggerフォルダを開く(環境によって違う)
- D:\pinokio\api\automatic1111.git\app\extensions\stable-diffusion-webui-wd14-tagger\
- models\deepdanbooruフォルダを作る(必要があるかは分からないけどcopilotさんにやれといわれた)
- D:\pinokio\api\automatic1111.git\app\extensions\stable-diffusion-webui-wd14-tagger\models\deepdanbooru
- モデルデータをクローン(git導入済みならこれが楽)
git clone https://huggingface.co/toynya/Z3D-E621-Convnext
- gitが入っていない環境なら次をやる
- Z3D-E621-Convnextというフォルダを作製
- 次からすべてのファイルをダウンロードしてフォルダにいれる
- Taggerフォルダに入っているtaggerフォルダの中のutils.pyを改造(絶対間違った方法だけど動いたのでヨシ!)
"""Utility functions for the tagger module"""
import os
from typing import List, Dict
from pathlib import Path
from modules import shared, scripts # pylint: disable=import-error
from modules.shared import models_path # pylint: disable=import-error
from modules.paths import extensions_dir # pylint: disable=import-error
default_ddp_path = Path(models_path, 'deepdanbooru')
default_onnx_path = Path(models_path, 'TaggerOnnx')
from tagger.preset import Preset # pylint: disable=import-error
from tagger.interrogator import Interrogator, DeepDanbooruInterrogator, \
MLDanbooruInterrogator # pylint: disable=E0401 # noqa: E501
from tagger.interrogator import WaifuDiffusionInterrogator # pylint: disable=E0401 # noqa: E501
preset = Preset(Path(scripts.basedir(), 'presets'))
interrogators: Dict[str, Interrogator] = {
'wd14-vit.v1': WaifuDiffusionInterrogator(
'WD14 ViT v1',
repo_id='SmilingWolf/wd-v1-4-vit-tagger'
),
'wd14-vit.v2': WaifuDiffusionInterrogator(
'WD14 ViT v2',
repo_id='SmilingWolf/wd-v1-4-vit-tagger-v2',
),
'wd14-convnext.v1': WaifuDiffusionInterrogator(
'WD14 ConvNeXT v1',
repo_id='SmilingWolf/wd-v1-4-convnext-tagger'
),
'wd14-convnext.v2': WaifuDiffusionInterrogator(
'WD14 ConvNeXT v2',
repo_id='SmilingWolf/wd-v1-4-convnext-tagger-v2',
),
'wd14-convnextv2.v1': WaifuDiffusionInterrogator(
'WD14 ConvNeXTV2 v1',
# the name is misleading, but it's v1
repo_id='SmilingWolf/wd-v1-4-convnextv2-tagger-v2',
),
'wd14-swinv2-v1': WaifuDiffusionInterrogator(
'WD14 SwinV2 v1',
# again misleading name
repo_id='SmilingWolf/wd-v1-4-swinv2-tagger-v2',
),
'wd-v1-4-moat-tagger.v2': WaifuDiffusionInterrogator(
'WD14 moat tagger v2',
repo_id='SmilingWolf/wd-v1-4-moat-tagger-v2'
),
'mld-caformer.dec-5-97527': MLDanbooruInterrogator(
'ML-Danbooru Caformer dec-5-97527',
repo_id='deepghs/ml-danbooru-onnx',
model_path='ml_caformer_m36_dec-5-97527.onnx'
),
'mld-tresnetd.6-30000': MLDanbooruInterrogator(
'ML-Danbooru TResNet-D 6-30000',
repo_id='deepghs/ml-danbooru-onnx',
model_path='TResnet-D-FLq_ema_6-30000.onnx'
),
}
def refresh_interrogators() -> List[str]:
"""Refreshes the interrogators list"""
# load deepdanbooru project
ddp_path = shared.cmd_opts.deepdanbooru_projects_path
if ddp_path is None:
ddp_path = default_ddp_path
onnx_path = shared.cmd_opts.onnxtagger_path
if onnx_path is None:
onnx_path = default_onnx_path
os.makedirs(ddp_path, exist_ok=True)
os.makedirs(onnx_path, exist_ok=True)
# 拡張機能内のdeepdanbooruモデルディレクトリも検索対象に追加
extension_ddp_path = Path(extensions_dir, 'stable-diffusion-webui-wd14-tagger', 'models', 'deepdanbooru')
# 両方のディレクトリをスキャン
scan_paths = [ddp_path, extension_ddp_path]
for scan_path in scan_paths:
if not scan_path.exists():
continue
for path in os.scandir(scan_path):
with open("d:/pinokio/api/automatic1111.git/app/extensions/stable-diffusion-webui-wd14-tagger/interrogator_log.txt", "a", encoding="utf-8") as f:
f.write(f"Scanning {path} as deepdanbooru project\n")
print(f"Scanning {path} as deepdanbooru project")
if not path.is_dir():
with open("d:/pinokio/api/automatic1111.git/app/extensions/stable-diffusion-webui-wd14-tagger/interrogator_log.txt", "a", encoding="utf-8") as f:
f.write(f"Warning: {path} is not a directory, skipped\n")
continue
has_project_json = Path(path, 'project.json').is_file()
onnx_files = [x for x in os.scandir(path) if x.name.endswith('.onnx')]
csv_files = [x for x in os.scandir(path) if x.name.endswith('.csv')]
with open("d:/pinokio/api/automatic1111.git/app/extensions/stable-diffusion-webui-wd14-tagger/interrogator_log.txt", "a", encoding="utf-8") as f:
f.write(f"project.json: {has_project_json}, onnx_files: {[x.name for x in onnx_files]}, csv_files: {[x.name for x in csv_files]}\n")
if has_project_json or (len(onnx_files) == 1 and len(csv_files) >= 1):
with open("d:/pinokio/api/automatic1111.git/app/extensions/stable-diffusion-webui-wd14-tagger/interrogator_log.txt", "a", encoding="utf-8") as f:
f.write(f"Registering DeepDanbooruInterrogator: {path.name}\n")
print(f"Registering DeepDanbooruInterrogator: {path.name}")
interrogators[path.name] = DeepDanbooruInterrogator(path.name, path)
else:
with open("d:/pinokio/api/automatic1111.git/app/extensions/stable-diffusion-webui-wd14-tagger/interrogator_log.txt", "a", encoding="utf-8") as f:
f.write(f"Warning: {path} has no project.json and/or .onnx/.csv, skipped\n")
print(f"Warning: {path} has no project.json and/or .onnx/.csv, skipped")
# scan for onnx models as well
for path in os.scandir(onnx_path):
print(f"Scanning {path} as onnx model")
if not path.is_dir():
print(f"Warning: {path} is not a directory, skipped")
continue
onnx_files = [x for x in os.scandir(path) if x.name.endswith('.onnx')]
if len(onnx_files) != 1:
print(f"Warning: {path} requires exactly one .onnx model, skipped")
continue
local_path = Path(path, onnx_files[0].name)
csv = [x for x in os.scandir(path) if x.name.endswith('.csv')]
if len(csv) == 0:
print(f"Warning: {path} has no selected tags .csv file, skipped")
continue
def tag_select_csvs_up_front(k):
sum(-1 if t in k.name.lower() else 1 for t in ["tag", "select"])
csv.sort(key=tag_select_csvs_up_front)
tags_path = Path(path, csv[0])
if path.name not in interrogators:
if path.name == 'wd-v1-4-convnextv2-tagger-v2':
interrogators[path.name] = WaifuDiffusionInterrogator(
path.name,
repo_id='SmilingWolf/SW-CV-ModelZoo',
is_hf=False
)
elif path.name == 'Z3D-E621-Convnext':
interrogators[path.name] = WaifuDiffusionInterrogator(
'Z3D-E621-Convnext', is_hf=False)
else:
raise NotImplementedError(f"Add {path.name} resolution similar"
"to above here")
interrogators[path.name].local_model = str(local_path)
interrogators[path.name].local_tags = str(tags_path)
# Z3D-E621-Convnextを強制的にWaifuDiffusionInterrogatorとして登録
z3d_path = Path(extensions_dir, 'stable-diffusion-webui-wd14-tagger', 'models', 'deepdanbooru', 'Z3D-E621-Convnext')
if z3d_path.exists() and z3d_path.is_dir():
model_file = z3d_path / 'model.onnx'
tags_file = z3d_path / 'tags-selected.csv'
if model_file.exists() and tags_file.exists():
interrogators['Z3D-E621-Convnext'] = WaifuDiffusionInterrogator('Z3D-E621-Convnext', is_hf=False)
interrogators['Z3D-E621-Convnext'].local_model = str(model_file)
interrogators['Z3D-E621-Convnext'].local_tags = str(tags_file)
with open("d:/pinokio/api/automatic1111.git/app/extensions/stable-diffusion-webui-wd14-tagger/interrogator_log.txt", "a", encoding="utf-8") as f:
f.write("Force-registered Z3D-E621-Convnext as WaifuDiffusionInterrogator\n")
with open("d:/pinokio/api/automatic1111.git/app/extensions/stable-diffusion-webui-wd14-tagger/interrogator_log.txt", "a", encoding="utf-8") as f:
f.write(f"Interrogators registered: {list(interrogators.keys())}\n")
print(f"Interrogators registered: {list(interrogators.keys())}")
return sorted(interrogators.keys())
def split_str(string: str, separator=',') -> List[str]:
return [x.strip() for x in string.split(separator) if x]
タグ付け
初回
- InputDirectoryに画像のあるフォルダを設定
- Outputは指定しないほうがよい
- 改めてSDを起動し、InterrogateとしてZ3D-E621-convnextを適応
- Additional tagsに最終的に生成時使いたいキーワードを入れる
- 区切り(スペース、アンダーバー)がないほうがいいかもしれない?
- Weight thresholdを好みで変えて「Interrogate」を押す
訂正 (やると精度があがるが、とりあえずお試しなら飛ばしてもOK)
- 目的:次のタグを削除する
- 明らかに間違っているもの(例:eevee)
- 作成したいLoRAに絶対欠けてはいけない要素(例:kemono)
- 逆に言うと、次のようなタグは残す
- 変数にしたいもの(生成時、バリエーションを持たせたいもの)
- 表情
- 姿勢
- 背景
- ただ変数にしたくてもそもそも教師画像にないものはどうしようもないのでまあ、というところ
- 変数にしたいもの(生成時、バリエーションを持たせたいもの)
- 具体的な手順
- タグはその画像を説明するもの
- LoRA学習において「(学習する/一般的な)他の画像とどう違うか?」を認識するために使用される
- 一般的な他の画像とどう違うか?観点において
- 学習のため沢山の画像を用意して、AdditionalTag「rw3」を付与した
- これはrw3はこのキャラ画像です、と伝えるためのもの
- そのキャラにpawがあることが当たり前で欠かせないのなら、それはrw3の中に入るべき情報ですから、タグからは消すべき
- Taggerが自動付与したあやっているタグ(eevee)を消した
- 他と比べて誤った勉強をする事故を減らすため
- 学習する他の画像とどう違うか?という観点において:
- 立っている画像と座っている画像を用意したならそれぞれの画像がどう違うかAIに教えるべき。だから姿勢(sitting, standing)は重要
- 全体的な理論として
- 全部の画像の共通なタグが1つ、それぞれの画像について全て異なる意味のあるタグが付与されている、という状態にするとAIにとっては一番よい学習材料なのかも(理想論)
- 共通タグに対してバリエーションは多いほど汎用的なものができるので色々な背景や服・シェーダーの元画像があったほういい、いやそれをやると時間かかるし失敗しやすい、等色々な意見がある(最初は気にしなくていい)
- 終わったら自動で(画像と同じフォルダにたくさんのテキストが)保存されているのでSDはいったん閉じる
LoRA学習
準備
-
ベースモデルをダウンロード
-
Pony向け設定ファイル(json)をダウンロード
- kohyaSSを起動
- AnacondaでD:\Loraへやった場合の手順
- スタート→Anaconda Prompt
- AnacondaでD:\Loraへやった場合の手順
conda activate kohya
d:
cd LoRA\kohya_ss
gui.bat
設定
配布サイトの性能だと当初設定で15分でできるらしい
著者PCの性能だと1時間くらい
設定が終わったら下にある「Start training」を押す
そのあとの動作はAnaconda Promptのほうに表示される
- 大量の警告(特にtriton関連)が出るが気にしなくていい