假如咱们想要自己在云服务器上布置Stable Diffusion模型,可是又不想主动扩容形成本钱激增,咱们能够规划排队运用的模式。stable-diffusion-webui现已很好用了,支撑了自定义模型及Lora模型的加载、排队生成、完善的UI和各种插件功用,可是缺陷在于无法支撑多人场景,会存在一个人加载了新模型B后,一个人的界面上虽然显现的仍是模型A,可是点击生成却是用的模型B生成,这是因为sd-webui的模型加载不是用户维度的,而是大局的。

要想完成支撑多人排队运用的sd-webui,最好的方式仍是hack原先的sd-webui的代码,找到模型接纳恳求参数并进行核算的中心代码,然后自己写前端,手动将这些恳求参数传递到这段中心函数中去。

ps:假如想要布置支撑主动扩缩容无需排队的stable diffusion自定义模型,能够参阅我之前的两种方案:

  • 将Stable Diffusion自定义model布置到服务器上并支撑主动扩容
  • 布置Stable Diffusion自定义Lora模型并支撑主动扩容(2)

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服务

声明:本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理。