如何实现多人排队使用Stable Diffusion模型

释放双眼,带上耳机,听听看~!
本文介绍了如何通过hack sd-webui的代码来实现多人排队使用Stable Diffusion模型,并提供了部署自动扩缩容的两种方案供参考。此外,还详细说明了txt2img代码中的关键参数和其对应的webui选项。

如果我们想要自己在云服务器上部署Stable Diffusion模型,但是又不想自动扩容造成成本激增,我们可以设计排队使用的模式。stable-diffusion-webui已经很好用了,支持了自定义模型及Lora模型的加载、排队生成、完善的UI和各种插件功能,但是缺点在于无法支持多人场景,会存在一个人加载了新模型B后,一个人的界面上虽然显示的还是模型A,但是点击生成却是用的模型B生成,这是因为sd-webui的模型加载不是用户维度的,而是全局的。

要想实现支持多人排队使用的sd-webui,最好的方式还是hack原先的sd-webui的代码,找到模型接收请求参数并进行计算的核心代码,然后自己写前端,手动将这些请求参数传递到这段核心函数中去。

ps:如果想要部署支持自动扩缩容无需排队的stable diffusion自定义模型,可以参考我之前的两种方案:

StableDiffusionProcessingTxt2Img

首先我们来看最重要的txt2img的代码,核心的类就是modules.processing中的StableDiffusionProcessingTxt2Img类,它的init函数接收以下的参数:

def __init__(self, enable_hr: bool = False, denoising_strength: float = 0.75, firstphase_width: int = 0, firstphase_height: int = 0, hr_scale: float = 2.0, hr_upscaler: str = None, hr_second_pass_steps: int = 0, hr_resize_x: int = 0, hr_resize_y: int = 0, **kwargs)

代码中的缩写hr代表的就是webui中的”Hires.fix”,相关的参数对应的是webui中的这些选项:

如何实现多人排队使用Stable Diffusion模型

接下来,可以看到还有很多其他的参数没有看到,其实这些参数都是在StableDiffusionProcessingTxt2Img的父类:StableDiffusionProcessing类的init中指定的:

def __init__(self, sd_model=None, outpath_samples=None, outpath_grids=None, prompt: str = "", styles: List[str] = None, seed: int = -1, subseed: int = -1, subseed_strength: float = 0, seed_resize_from_h: int = -1, seed_resize_from_w: int = -1, seed_enable_extras: bool = True, sampler_name: str = None, batch_size: int = 1, n_iter: int = 1, steps: int = 50, cfg_scale: float = 7.0, width: int = 512, height: int = 512, restore_faces: bool = False, tiling: bool = False, do_not_save_samples: bool = False, do_not_save_grid: bool = False, extra_generation_params: Dict[Any, Any] = None, overlay_images: Any = None, negative_prompt: str = None, eta: float = None, do_not_reload_embeddings: bool = False, denoising_strength: float = 0, ddim_discretize: str = None, s_churn: float = 0.0, s_tmax: float = None, s_tmin: float = 0.0, s_noise: float = 1.0, override_settings: Dict[str, Any] = None, override_settings_restore_afterwards: bool = True, sampler_index: int = None, script_args: list = None):

    self.outpath_samples: str = outpath_samples # 生成的图片的保存路径,和下面的do_not_save_samples配合使用
    self.outpath_grids: str = outpath_grids
    self.prompt: str = prompt # 正向提示词
    self.prompt_for_display: str = None
    self.negative_prompt: str = (negative_prompt or "") # 反向提示词
    self.styles: list = styles or []
    self.seed: int = seed # 种子,-1表示使用随机种子
    self.sampler_name: str = sampler_name # 采样方法,比如"DPM++ SDE Karras"
    self.batch_size: int = batch_size # 每批生成的数量?
    self.n_iter: int = n_iter
    self.steps: int = steps # UI中的sampling steps
    self.cfg_scale: float = cfg_scale # UI中的CFG Scale,提示词相关性
    self.width: int = width # 生成图像的宽度
    self.height: int = height # 生成图像的高度
    self.restore_faces: bool = restore_faces # 是否使用面部修复
    self.tiling: bool = tiling # 是否使用可平铺(tilling)
    self.do_not_save_samples: bool = do_not_save_samples

我们对应UI界面来看这些参数的含义(我直接注释在代码里了),父类中有一些参数不是在txt2img中用到的,我就忽略了

如何实现多人排队使用Stable Diffusion模型

通过将我注释的这些参数传进去,我们就可以指定sd-webui中提供的参数了,示例代码如下:

from modules.api.api import encode_pil_to_base64
from modules import shared
from modules.processing import StableDiffusionProcessingTxt2Img, process_images
args = {
    "outpath_samples": "C:\Users\wolvz\Desktop",
    "prompt": "lora:koreanDollLikeness_v15:0.66, best quality, ultra high res, (photorealistic:1.4), 1girl, beige sweater, black choker, smile, laughing, bare shoulders, solo focus, ((full body), (brown hair:1), looking at viewer",
    "negative_prompt": "paintings, sketches, (worst quality:2), (low quality:2), (normal quality:2), lowres, normal quality, ((monochrome)), ((grayscale)), skin spots, acnes, skin blemishes, age spot, glans, (ugly:1.331), (duplicate:1.331), (morbid:1.21), (mutilated:1.21), (tranny:1.331), mutated hands, (poorly drawn hands:1.331), blurry, 3hands,4fingers,3arms, bad anatomy, missing fingers, extra digit, fewer digits, cropped, jpeg artifacts,poorly drawn face,mutation,deformed",
    "sampler_name": "DPM++ SDE Karras",
    "steps": 20,
    "cfg_scale": 8,
    "width": 512,
    "height": 768,
    "seed": -1,
}
p = StableDiffusionProcessingTxt2Img(sd_model=shared.sd_model, **args)
processed = process_images(p)
single_image_b64 = encode_pil_to_base64(processed.images[0]).decode('utf-8')

指定模型进行加载

现在还差最后一个参数,就是在sd-webui的顶部,可以指定模型,而我们现在的示例代码中,模型其实是从shared.sd_model取的,就会造成模型不是用户维度而是全局共享的问题。我们需要做的其实是当处理用户请求的时候,如果模型和当前使用的模型不一致,需要重新加载新的模型。

重新加载模型可以直接使用modules/sd_models.py中的reload_model_weights(sd_model=None, info=None)函数,而我们只需要传入info这个参数就行,用info参数来指定我们想要加载的模型,而在这个函数中,会自动判断我们想要加载的模型和当前模型是否一致,一致的话则不会重新加载。

从函数签名很难看出来info字段是一个什么样的参数,但是,经过我对代码的研究,我发现info其实就是下面这个类:

class CheckpointInfo:
    def __init__(self, filename):
        self.filename = filename
        abspath = os.path.abspath(filename)

        if shared.cmd_opts.ckpt_dir is not None and abspath.startswith(shared.cmd_opts.ckpt_dir):
            name = abspath.replace(shared.cmd_opts.ckpt_dir, '')
        elif abspath.startswith(model_path):
            name = abspath.replace(model_path, '')
        else:
            name = os.path.basename(filename)

        if name.startswith("\") or name.startswith("/"):
            name = name[1:]

        self.name = name
        self.name_for_extra = os.path.splitext(os.path.basename(filename))[0]
        self.model_name = os.path.splitext(name.replace("/", "_").replace("\", "_"))[0]
        self.hash = model_hash(filename)

        self.sha256 = hashes.sha256_from_cache(self.filename, "checkpoint/" + name)
        self.shorthash = self.sha256[0:10] if self.sha256 else None

        self.title = name if self.shorthash is None else f'{name} [{self.shorthash}]'

        self.ids = [self.hash, self.model_name, self.title, name, f'{name} [{self.hash}]'] + ([self.shorthash, self.sha256, f'{self.name} [{self.shorthash}]'] if self.shorthash else [])

init里的一大串其实都不用管,我们只需要指定filename就行了。所以用如下的示例代码就可以手动重新加载一个指定的模型:

from modules import sd_models

checkpoint_info = sd_models.CheckpointInfo("chilloutmix_NiPrunedFp32Fix.safetensors")
sd_models.reload_model_weights(info=checkpoint_info)

排队、显示进度

首先,支持多人排队使用,这个其实我们自己来实现就行,在内存里自己定义一个锁。比如定义一个变量queue_lock = threading.Lock(),在生成之前使用with queue_lock:就行。这样当多个请求到达时,会先处理先到达的请求,后面的请求等前面处理完之后才会开始处理。

还有一个问题是需要在模型生成过程中实时显示生成进度以及支持中断操作,这两个我们可以直接参考modules/api.py中的progressapiinterruptapi,可以看到获取剩余时间其实就是对modules.shared.state中的job_countsampling_stepsampling_stepstime_start等变量进行了一些计算得到的:

如何实现多人排队使用Stable Diffusion模型

而这些变量的赋值其实是在上面说的核心函数process_image中进行的。

可能有人会问,这里获取进度是从shared中的变量获取之后计算的,而shared是全局共享的,假设有多人同时请求,每个人需要获取各自的进度,不就没法实现了吗?其实,当多人请求时,因为前面实现的排队机制,只有一个请求会进入处理状态,其他都是等待状态,也就是只有处理中的请求才需要来获取进度和剩余时间;其余请求都是等待中的状态。我们可以在shared中记录一个task_id,来请求进度的时候,先判断请求的task_id和shared.task_id是否相等,相等说明该请求在处理中,否则说明在等待中。

至于中断操作,也很简单:

def interruptapi(self):
    shared.state.interrupt()
    return {}

以上就是实现stable diffusion txt2img多人排队使用的最简单的代码解读,我们hack了一部分sd-webui的源码,在此基础上,我们可以实现自己的UI,并且在上面加上我们自定义的各种功能,比如账号登录注册,充值会员,社区分享,多台GPU机器负载均衡等等。

最后,关于部署,我写了一篇6000字的手把手部署教程,有需要可以参考:部署支持多人在线排队的Stable Diffusion服务

本网站的内容主要来自互联网上的各种资源,仅供参考和信息分享之用,不代表本网站拥有相关版权或知识产权。如您认为内容侵犯您的权益,请联系我们,我们将尽快采取行动,包括删除或更正。
AI教程

Hugging Face将停止接受密码认证方式的通知

2023-11-28 20:22:14

AI教程

ChatGPT:一个神奇的聊天模型

2023-11-28 20:26:14

个人中心
购物车
优惠劵
今日签到
有新私信 私信列表
搜索