Shortcuts

lora训练指南

这个指南将会教你如何训练一个Lora模型,为SD底模添加一些新的知识。

这个教程过程总共分为以下几个部分:

  • 准备数据集

  • 训练模型

  • 使用lora生成图像

  • 保存为sd-webui格式

准备数据集

准备你要训练的数据集,数据集应该包含一些文本标注和对应的图像,文本和图像应该是一一对应的关系。 将要训练的图片放在一个文件夹中,比如放在train_data文件夹中,结构如下:

train_data
├── 1.png
├── 2.png
├── 3.png
├── ......

文本标注支持多种格式,框架可以自动识别。更详细的说明见 RainbowNeko Engine

txt格式的标注,与对应的图片文件名相同,只是后缀不同,比如1.txt2.txt等。 txt内写图片对应的标注。 可以放在和图片同一个文件夹中。

train_data
├── 1.png
├── 1.txt
├── 2.png
├── 2.txt
├── 3.png
├── 3.txt
├── ......

json格式的标注,所有图片的标注放在一个json文件中,格式如下:

{
    "1": "标注1",
    "2": "标注2",
    "3": "标注3",
    ......
}

yaml格式的标注,所有图片的标注放在一个yaml文件中,格式如下:

"1": "标注1"
"2": "标注2"
"3": "标注3"
......

模型训练

准备好数据集后,我们可以开始训练模型了。 推荐安装tensorboardwandb以更清晰的查看训练进度。

# 安装tensorboard
pip install tensorboard
# 安装wandb
pip install wandb

训练配置文件填写

cfgs/train/py/examples中有提供训练相关的模板文件,其中lora_preview.py提供了lora模型训练的模板,带有预览功能,我们可以继承这个模板文件来进行训练。

模板默认只为U-Net部分添加lora,不在text encoder部分添加lora。

lora_preview.py继承自SD_FT.py,其中主要需要修改的配置如下:

...
model_plugin=CfgWDPluginParser(cfg_plugin=dict(
    lora1=LoraLayer.wrap_model(
        _partial_=True,
        lr=1e-4, # lora学习率
        rank=4, # lora的维度
        alpha=2, # lora的权重,一般与rank相同或为rank的一半
        layers=[
            're:denoiser.*\.attn.?$',
            're:denoiser.*\.ff$',
        ]
    )
), weight_decay=0.1), # weight_decay一般为0.1或者0.01
...
train=dict(
    train_steps=1000, # 训练总步数
    # train_epochs=10, # 可以使用epochs来代替步数,指定训练轮数
    save_step=200, # 保存模型步数间隔
    
    # 优化器,如果显存不够可以换成8bit优化器
    optimizer=torch.optim.AdamW(_partial_=True, betas=(0.9, 0.99)),
    
    # 学习率曲线,默认恒定不变
    scheduler=ConstantLR(
        _partial_=True,
        warmup_steps=0,
    ),
),
...
model=dict(
    name='model',
    
    # ckpt_path是预训练底模的路径,改成需要的
    wrapper=SD15Wrapper.from_pretrained(
        models=SD15_auto_loader(ckpt_path='Lykon/DreamShaper', _partial_=True),
        _partial_=True,
    ),
),
...
evaluator=HCPPreviewer(_partial_=True,
    interval=100, # 预览间隔
    workflow=t2i_lora, # 使用哪个工作流预览图像
),

在预览图像时,底模和lora等插件都不会加载新的,会使用训练中使用的模型。

对于数据集配置:

@neko_cfg
def cfg_data():
    return dict(
        dataset1=TextImagePairDataset(_partial_=True,
            batch_size=4, # 批次大小
            loss_weight=1.0,
            source=dict(
                data_source1=Text2ImageSource(
                    img_root= 'imgs/', # 图片路径
                    label_file= '${.img_root}',  # 标注路径,默认与图片一致
                    prompt_template='prompt_template/caption.txt',
                ),
            ),
            handler=StableDiffusionHandler(
                bucket=RatioBucket, # 使用ARB
                word_names=dict(pt1='paimeng'), # 触发词是paimeng
                erase=0,
            ),
            bucket=RatioBucket.from_files(
                target_area=512*512, # 图片训练分辨率
                num_bucket=4, # 分桶数量,训练集分辨率越多,分桶数量越多
            ),
            cache=VaeCache(bs=4) # 缓存VAE编码,减少显存使用
        )
    )

如果需要把lora同时保存成HCP-Diffusion的格式和webui的格式,可以在ckpt_saver中配置:

from rainbowneko.ckpt_manager import NekoPluginSaver, SafeTensorFormat
from hcpdiff.ckpt_manager import LoraWebuiFormat

ckpt_saver=dict(
    _replace_ = True,
    lora_unet=NekoPluginSaver(
        format=SafeTensorFormat(),
        target_plugin='lora1',
    ),
    lora_unet_webui=NekoPluginSaver(
        format=LoraWebuiFormat(), # webui格式
        target_plugin='lora1',
    ),
),

cfgs/train/py/easy中有提供训练相关的简化模板文件,其中SD15_lora_preview.py提供了lora模型训练的模板,带有预览功能,我们可以基于这个模板文件来进行训练。

模板默认只为U-Net部分添加lora,不在text encoder部分添加lora。

from rainbowneko.parser import neko_cfg
from hcpdiff.easy.cfg import SD15_lora_train, cfg_data_SD_ARB, SD15_t2i
from hcpdiff.evaluate import HCPPreviewer

# 预览使用的prompt
prompt = ('paimeng, 1girl, halo, white_hair, solo, smile, blue_eyes, looking_at_viewer, open_mouth, long_sleeves, white_dress, dress, single_thighhigh,'
          ' :d, cape, hair_between_eyes, thighhighs, hair_ornament, blush, white_outline, outline, sky, scarf, cloud, white_thighhighs, arm_up,'
          ' notice_lines, paimon_(genshin_impact)')

@neko_cfg
def make_cfg():
    return dict(
        **SD15_lora_train(
            base_model='Lykon/DreamShaper', # 预训练模型路径
            train_steps=1000, # 训练总步数
            save_step=200, # 保存模型步数间隔
            rank=8, # lora的维度            
            dataset=dict(
                dataset1=cfg_data_SD_ARB( # 使用ARB使图像尽可能不被剪裁
                    img_root='imgs/', # 训练图片路径,标注和图片在同一个文件夹
                    batch_size=4, # 批次大小
                    trigger_word='paimeng', # 触发词
                    resolution=512*512, # 训练分辨率
                    num_bucket=4, # 分桶数量,训练集分辨率越多,分桶数量越多
                )
            )
        ),
        # 开启预览
        evaluator=HCPPreviewer(_partial_=True,
            interval=100, # 预览间隔
            workflow=SD15_t2i(
                pretrained_model='${model.wrapper.models.ckpt_path}',
                prompt=prompt, # 预览使用的prompt
                seed=42, # 固定随机种子
            ),
        ),
    )

在这个配置基础上,还有一些其他配置可以选择:

@neko_cfg
def make_cfg():
    return dict(
        **SD15_lora_train(
            base_model='Lykon/DreamShaper', # 预训练模型路径
            train_steps=1000, # 训练总步数
            save_step=200, # 保存模型步数间隔
            rank=8, # lora的维度
            
            lr=1e-4, # lora学习率
            alpha=8, # lora的权重,一般与rank相同或为rank的一半
            clip_skip=0, # clip跳过的层数,0表示不跳过
            with_conv=False, # 是否在卷积层添加lora
            low_vram=False, # 使用8bit优化器
            save_webui_format=True, # 储存成webui的格式
            
            dataset=dict(
                dataset1=cfg_data_SD_ARB( # 使用ARB使图像尽可能不被剪裁
                    img_root='imgs/', # 训练图片路径,标注和图片在同一个文件夹
                    batch_size=4, # 批次大小
                    trigger_word='paimeng', # 触发词
                    resolution=512*512, # 训练分辨率
                    num_bucket=4, # 分桶数量,训练集分辨率越多,分桶数量越多
                )
            )
        ),
        # 开启预览
        evaluator=HCPPreviewer(_partial_=True,
            interval=100, # 预览间隔
            workflow=SD15_t2i(
                pretrained_model='${model.wrapper.models.ckpt_path}',
                prompt=prompt, # 预览使用的prompt
                seed=42, # 固定随机种子
                
                negative_prompt='', # 负面描述
                noise_sampler=Diffusers_SD.dpmpp_2m_karras, # 采样器
                bs=4, # 批次大小
                width=512, # 图片宽度
                height=512, # 图片高度
                N_steps=20, # 采样步数
                guidance_scale=7.0, # CFG引导强度
            ),
        ),
    )

训练

hcp_train_1gpu --cfg cfgs/train/py/examples/lora_preview.py

配置文件中的值,可以在cli中修改:

hcp_train_1gpu --cfg cfgs/train/py/examples/lora_preview.py train.train_epochs=10 # 修改训练轮数

多GPU训练需要在cfgs/launcher/multi.yaml中指定训练用到的GPU id和GPU数量,然后运行:

hcp_train --cfg cfgs/train/py/examples/lora_preview.py

配置文件中的值,可以在cli中修改:

hcp_train --cfg cfgs/train/py/examples/lora_preview.py train.train_epochs=10 # 修改训练轮数

训练完毕后,输出的所有结果都会存到一个文件夹中:

exps/2023-07-26-01-05-35
├── cfg.py # 配置文件
├── ckpts # lora模型文件
│   ├── lora_unet-100.safetensors
│   ├── lora_unet-200.safetensors
│   ├── ...
├── tblog # tensorboard日志
│   └── events.out.tfevents.1690346085.myenvironment.210494.0
└── train.log

进阶配置

自定义lora参数和添加lora的层:

lora_unet=LoraLayer.wrap_model(
    _partial_=True,
    lr=1e-4, # lora学习率
    rank=4, # lora的维度
    alpha=2, # lora的权重,一般与rank相同或为rank的一半
    layers=[
        # k,v层和ff层 (使用正则表达式指定层的名称)
        're:.*\.to_k$'
        're:.*\.to_v$'
        're:.*\.ff$'
    ]
),
lora_TE=LoraLayer.wrap_model(
    _partial_=True,
    lr=2e-5, # lora学习率
    rank=2, # lora的维度
    alpha=2, # lora的权重,一般与rank相同或为rank的一半
    layers=[
        're:.*self_attn$' # 注意力层
        're:.*mlp$' # mlp层
    ]
)

使用lora生成图像

训练完毕后,我们可以使用lora生成图像。详细的生成配置文件编写请参考生成配置文件编写

这里可以使用简化的配置文件cfgs/workflow/easy/t2i_lora.py:

from hcpdiff.easy.cfg import SD15_t2i_lora
from rainbowneko.parser import neko_cfg

@neko_cfg
def make_cfg():
    return SD15_t2i_lora(
        pretrained_model='Lykon/DreamShaper', # 预训练底模
        lora_info=[
            ('exps/2023-07-26-01-05-35/ckpts/lora_unet-1000.safetensors', 1.0), # (lora文件路径, weight)
        ],
        prompt='masterpiece, best quality, 1girl, cat ears, outside',
        bs=4,
        width=512,
        height=512,
        guidance_scale=7.0
    )

运行配置文件生成图片:

hcp_run --cfg cfgs/workflow/easy/t2i_lora.py

进阶配置

在配置中,也可以更换采样器和VAE:

from diffusers import AutoencoderKL, DPMSolverMultistepScheduler
from hcpdiff.diffusion.sampler import DiffusersSampler

wrapper=SD15Wrapper.from_pretrained(
    _partial_=True,
    models=SD15_auto_loader(
        ckpt_path=base_model,
        vae=AutoencoderKL.from_pretrained('any3/vae'), # 更换VAE
        noise_sampler=DiffusersSampler( # 更换采样器
            DPMSolverMultistepScheduler(
                beta_start=0.00085,
                beta_end=0.012,
                beta_schedule='scaled_linear',
                algorithm_type='sde-dpmsolver++',
                use_karras_sigmas=True,
            )
        ),
        _partial_=True
    ),
),