0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

VRCケモノアバターのPony LoRAを作成

Last updated at Posted at 2025-08-16

n番煎じ

参考記事

全体的な流れ

環境

  • Stable Diffusion Web UI (pinokio版。他でも同じだと思う)(以下SDという)
  • kohya-ss
  • VRC
  • 3Dモデル
  • 学習元モデル Pony Diffusion V6 XL
  • NVIDIA GeForce RTX3060 (VRAM 12GB)
  • Python (バージョンいろいろ)

kohya_ssの導入

画像の準備

  • VRCにログイン、ワールド背景透過・自分を追従 4Kで写真を撮る
    image.png

全身だけじゃなくて顔などのみのアップがあったほうがいいみたいです

  • 適当な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)

image.png

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が入っていない環境なら次をやる
  • 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」を押す

image.png

ざっと出てくる
image.png

訂正 (やると精度があがるが、とりあえずお試しなら飛ばしてもOK)

  • 目的:次のタグを削除する
    • 明らかに間違っているもの(例:eevee)
    • 作成したいLoRAに絶対欠けてはいけない要素(例:kemono)
  • 逆に言うと、次のようなタグは残す
    • 変数にしたいもの(生成時、バリエーションを持たせたいもの)
      • 表情
      • 姿勢
      • 背景
    • ただ変数にしたくてもそもそも教師画像にないものはどうしようもないのでまあ、というところ
  • 具体的な手順
    • 出てきたタグを機械翻訳(読めればそのまま)して「いらないもの」のリストを作る
    • たとえば, を改行にしてAI翻訳してスプシでやる
      image.png
    • いらないものリストをTaggerの下のほうにあるExclude tagに書く(ちょっと内容がよろしくないので文字ではのせません)
      image.png
    • 再度Interrogate。減ってればOK
      • image.png
  • タグはその画像を説明するもの
  • LoRA学習において「(学習する/一般的な)他の画像とどう違うか?」を認識するために使用される
  • 一般的な他の画像とどう違うか?観点において
    • 学習のため沢山の画像を用意して、AdditionalTag「rw3」を付与した
    • これはrw3はこのキャラ画像です、と伝えるためのもの
      • そのキャラにpawがあることが当たり前で欠かせないのなら、それはrw3の中に入るべき情報ですから、タグからは消すべき
    • Taggerが自動付与したあやっているタグ(eevee)を消した
      • 他と比べて誤った勉強をする事故を減らすため
  • 学習する他の画像とどう違うか?という観点において:
    • 立っている画像と座っている画像を用意したならそれぞれの画像がどう違うかAIに教えるべき。だから姿勢(sitting, standing)は重要
  • 全体的な理論として
    • 全部の画像の共通なタグが1つ、それぞれの画像について全て異なる意味のあるタグが付与されている、という状態にするとAIにとっては一番よい学習材料なのかも(理想論)
    • 共通タグに対してバリエーションは多いほど汎用的なものができるので色々な背景や服・シェーダーの元画像があったほういい、いやそれをやると時間かかるし失敗しやすい、等色々な意見がある(最初は気にしなくていい)
  • 終わったら自動で(画像と同じフォルダにたくさんのテキストが)保存されているのでSDはいったん閉じる

LoRA学習

準備

    conda activate kohya
    d:
    cd LoRA\kohya_ss
    gui.bat

設定

  • LoRAタブを開く(重要)
    image.png
  • Configrationを開いて準備でダウンロードしたjsonを読み込む

image.png

次を適宜直していく
image.png
image.png
image.png
image.png

配布サイトの性能だと当初設定で15分でできるらしい
著者PCの性能だと1時間くらい

設定が終わったら下にある「Start training」を押す
そのあとの動作はAnaconda Promptのほうに表示される

  • 大量の警告(特にtriton関連)が出るが気にしなくていい
0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?