使用kohya_ss_GUI训练二次元lora模型

前言

上一章跟大家讲了如何搭建kohya_ss_GUI环境,那么这一章我们来体验一下如何训练二次元模型吧!小时候比较喜欢看七龙珠,就拿成人形态的悟空来举例子啦!

准备数据集

首先我们需要大概10张左右的图片来作为数据集,我们就直接从百度图片上爬取一些吧

脚本准备

#!/usr/bin/env python
# -*- coding:utf-8 -*-
import argparse
import os
import re
import sys
import urllib
import json
import socket
import urllib.request
import urllib.parse
import urllib.error
# 设置超时
import time

timeout = 5
socket.setdefaulttimeout(timeout)


class Crawler:
    # 睡眠时长
    __time_sleep = 0.1
    __amount = 0
    __start_amount = 0
    __counter = 0
    headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64; rv:23.0) Gecko/20100101 Firefox/23.0', 'Cookie': ''}
    __per_page = 30

    # 获取图片url内容等
    # t 下载图片时间间隔
    def __init__(self, t=0.1):
        self.time_sleep = t

    # 获取后缀名
    @staticmethod
    def get_suffix(name):
        m = re.search(r'\.[^\.]*$', name)
        if m.group(0) and len(m.group(0)) <= 5:
            return m.group(0)
        else:
            return '.jpeg'

    @staticmethod
    def handle_baidu_cookie(original_cookie, cookies):
        """
        :param string original_cookie:
        :param list cookies:
        :return string:
        """
        if not cookies:
            return original_cookie
        result = original_cookie
        for cookie in cookies:
            result += cookie.split(';')[0] + ';'
        result.rstrip(';')
        return result

    # 保存图片
    def save_image(self, rsp_data, word):
        if not os.path.exists("./" + word):
            os.mkdir("./" + word)
        # 判断名字是否重复,获取图片长度
        self.__counter = len(os.listdir('./' + word)) + 1
        for image_info in rsp_data['data']:
            try:
                if 'replaceUrl' not in image_info or len(image_info['replaceUrl']) < 1:
                    continue
                obj_url = image_info['replaceUrl'][0]['ObjUrl']
                thumb_url = image_info['thumbURL']
                url = 'https://image.baidu.com/search/down?tn=download&ipn=dwnl&word=download&ie=utf8&fr=result&url=%s&thumburl=%s' % (urllib.parse.quote(obj_url), urllib.parse.quote(thumb_url))
                time.sleep(self.time_sleep)
                suffix = self.get_suffix(obj_url)
                # 指定UA和referrer,减少403
                opener = urllib.request.build_opener()
                opener.addheaders = [
                    ('User-agent', 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.116 Safari/537.36'),
                ]
                urllib.request.install_opener(opener)
                # 保存图片
                filepath = './%s/%s' % (word, str(self.__counter) + str(suffix))
                urllib.request.urlretrieve(url, filepath)
                if os.path.getsize(filepath) < 5:
                    print("下载到了空文件,跳过!")
                    os.unlink(filepath)
                    continue
            except urllib.error.HTTPError as urllib_err:
                print(urllib_err)
                continue
            except Exception as err:
                time.sleep(1)
                print(err)
                print("产生未知错误,放弃保存")
                continue
            else:
                print("图片+1,已有" + str(self.__counter) + "张图片")
                self.__counter += 1
        return

    # 开始获取
    def get_images(self, word):
        search = urllib.parse.quote(word)
        # pn int 图片数
        pn = self.__start_amount
        while pn < self.__amount:
            url = 'https://image.baidu.com/search/acjson?tn=resultjson_com&ipn=rj&ct=201326592&is=&fp=result&queryWord=%s&cl=2&lm=-1&ie=utf-8&oe=utf-8&adpicid=&st=-1&z=&ic=&hd=&latest=&copyright=&word=%s&s=&se=&tab=&width=&height=&face=0&istype=2&qc=&nc=1&fr=&expermode=&force=&pn=%s&rn=%d&gsm=1e&1594447993172=' % (search, search, str(pn), self.__per_page)
            # 设置header防403
            try:
                time.sleep(self.time_sleep)
                req = urllib.request.Request(url=url, headers=self.headers)
                page = urllib.request.urlopen(req)
                self.headers['Cookie'] = self.handle_baidu_cookie(self.headers['Cookie'], page.info().get_all('Set-Cookie'))
                rsp = page.read()
                page.close()
            except UnicodeDecodeError as e:
                print(e)
                print('-----UnicodeDecodeErrorurl:', url)
            except urllib.error.URLError as e:
                print(e)
                print("-----urlErrorurl:", url)
            except socket.timeout as e:
                print(e)
                print("-----socket timout:", url)
            else:
                # 解析json
                rsp_data = json.loads(rsp, strict=False)
                if 'data' not in rsp_data:
                    print("触发了反爬机制,自动重试!")
                else:
                    self.save_image(rsp_data, word)
                    # 读取下一页
                    print("下载下一页")
                    pn += self.__per_page
        print("下载任务结束")
        return

    def start(self, word, total_page=1, start_page=1, per_page=30):
        """
        爬虫入口
        :param word: 抓取的关键词
        :param total_page: 需要抓取数据页数 总抓取图片数量为 页数 x per_page
        :param start_page:起始页码
        :param per_page: 每页数量
        :return:
        """
        self.__per_page = per_page
        self.__start_amount = (start_page - 1) * self.__per_page
        self.__amount = total_page * self.__per_page + self.__start_amount
        self.get_images(word)


if __name__ == '__main__':
    if len(sys.argv) > 1:
        parser = argparse.ArgumentParser()
        parser.add_argument("-w", "--word", type=str, help="抓取关键词", required=True)
        parser.add_argument("-tp", "--total_page", type=int, help="需要抓取的总页数", required=True)
        parser.add_argument("-sp", "--start_page", type=int, help="起始页数", required=True)
        parser.add_argument("-pp", "--per_page", type=int, help="每页大小", choices=[10, 20, 30, 40, 50, 60, 70, 80, 90, 100], default=30, nargs='?')
        parser.add_argument("-d", "--delay", type=float, help="抓取延时(间隔)", default=0.05)
        args = parser.parse_args()

        crawler = Crawler(args.delay)
        crawler.start(args.word, args.total_page, args.start_page, args.per_page)  # 抓取关键词为 “美女”,总数为 1 页(即总共 1*60=60 张),开始页码为 2
    else:
        # 如果不指定参数,那么程序会按照下面进行执行
        crawler = Crawler(0.05)  # 抓取延迟为 0.05

        crawler.start('美女', 10, 2, 30)  # 抓取关键词为 “美女”,总数为 1 页,开始页码为 2,每页30张(即总共 2*30=60 张)
        # crawler.start('二次元 美女', 10, 1)  # 抓取关键词为 “二次元 美女”
        # crawler.start('帅哥', 5)  # 抓取关键词为 “帅哥”

将以上代码命名为crawling.py

爬取图片

python crawling.py --word "龙珠悟空" --total_page 10 --start_page 1 --per_page 30

这里就会从百度图片中爬取一些龙珠里的悟空图片,看爬取的差不多了可以手动把脚本关掉然后筛选10张左右的图片。
Test

裁剪图片

这里用了 在线裁剪
Test
这里裁剪成768*512,然后点击save as zip保存为压缩包。

图像预处理

这里新建一个文件夹wukong_photo,把刚刚的压缩包解压进来。
在kohya_ss目录下创建文件夹
kohya_ss\train\wukong\7_wukong
这个目录是为我们即将要生成的图像预处理数据准备的,为什么是7_wukong中有个7呢?因为这是一种规范,7代表7步,训练二次元数据7步就可以了。

然后打开我们的stable diffusion webui
训练->图像预处理
Test
源目录指的是我们裁剪好的图片目录,目标目录指的是预处理数据的目录,也就是上面我们创建的目录。设置好宽高,勾选上“使用 deepbooru 生成说明文字(tags)”,点击预处理。
网页上提示Preprocessing finished.就说明预处理成功了。

kohya_ss训练模型

打开我们之前安装好的kohya_ss_GUI,进入Dreambooth LoRA选项卡。
Test
选择runwayml/stable-diffusion-v1-5
然后进入folders选项卡
在kohya_ss根目录创建train_model_output目录,用于后面存放我们训练输出的模型。
创建train_logs目录,用于存放训练时候产生的日志文件。
Model output name设置为wukong,这个是输出的模型的名字。
Test

进入Training parameters选项卡
如果显卡一般般的话Train batch size设置1就行了,显卡条件好的可以设置3,Epoch和Save every N epochs都设置5,Optimizer选择Lion。
Test

最大分辨率选择我们刚刚的图片分辨率768,512
Test

点击Advanced Configuration,将Clip skip设置为2(训练二次元图片选择2,真人图片一般选择1)
Test

上面的都设置好之后,点击最下面的train model按钮开始训练模型。

可以看到控制台已经开始执行任务了
Test

报错raise subprocess.CalledProcessError

你大概率可能会遇到raise subprocess.CalledProcessError的报错
那是因为训练模型的时候不能有其他的python程序在运行,关闭其他的python程序,比如stable diffusion webui

报错 页面文件太小,无法完成操作

报错页面文件太小可以尝试扩大虚拟内存,扩大C盘空间或者将虚拟内存设置到其他空间大的磁盘
windows10扩大虚拟内存

报错解决之后重新执行训练,等待ing~~
Test
训练完成!

在kohya_ss下的train_model_output文件夹可以看到我们训练出的wukong.safetensors模型
Test

将这个文件复制到stable-diffusion-webui\extensions\sd-webui-additional-networks\models\lora这个目录下面

重新启动stable diffusion webui
设置简单的提示词
1boy,wukong
勾上“启用”,以及选择好我们训练的lora模型。
点击生成
Test
可以发现我们已经生成了龙珠里的悟空啦~
如果不是很理想可以多生成几次。

结束语

很帅气有没有!龟波气功~~~~
这章只讲解了二次元的模型训练,而且没有给图片打tag,往后我们会尝试真人训练以及更深层次的讲解。

评论区
头像