在 Apple Silicon 上使用 Core ML 运行 Stable Diffusion
本仓库包含以下内容:
python_coreml_stable_diffusion
,一个 Python 包,用于将 PyTorch 模型转换为 Core ML 格式,并使用 Hugging Face diffusers 在 Python 中执行图像生成StableDiffusion
,一个 Swift 包,开发者可以将其作为依赖项添加到他们的 Xcode 项目中,以便在其应用程序中部署图像生成功能。该 Swift 包依赖于 python_coreml_stable_diffusion
生成的 Core ML 模型文件如果您在安装或运行时遇到问题,请参阅 FAQ 部分。开始之前,请参阅 系统要求 部分。
模型转换
macOS | Python | coremltools |
---|---|---|
13.1 | 3.8 | 7.0 |
项目构建
macOS | Xcode | Swift |
---|---|---|
13.1 | 14.3 | 5.8 |
目标设备运行时
macOS | iPadOS、iOS |
---|---|
13.1 | 16.2 |
目标设备运行时 (具有内存改进)
macOS | iPadOS、iOS |
---|---|
14.0 | 17.0 |
目标设备硬件代数
Mac | iPad | iPhone |
---|---|---|
M1 | M1 | A14 |
stabilityai/stable-diffusion-2-1-base
(512x512)
设备 | --compute-unit |
--attention-implementation |
端到端延迟(秒) | 扩散速度(迭代/秒) |
---|---|---|---|---|
iPhone 12 Mini | CPU_AND_NE |
SPLIT_EINSUM_V2 |
18.5* | 1.44 |
iPhone 12 Pro Max | CPU_AND_NE |
SPLIT_EINSUM_V2 |
15.4 | 1.45 |
iPhone 13 | CPU_AND_NE |
SPLIT_EINSUM_V2 |
10.8* | 2.53 |
iPhone 13 Pro Max | CPU_AND_NE |
SPLIT_EINSUM_V2 |
10.4 | 2.55 |
iPhone 14 | CPU_AND_NE |
SPLIT_EINSUM_V2 |
8.6 | 2.57 |
iPhone 14 Pro Max | CPU_AND_NE |
SPLIT_EINSUM_V2 |
7.9 | 2.69 |
iPad Pro (M1) | CPU_AND_NE |
SPLIT_EINSUM_V2 |
11.2 | 2.19 |
iPad Pro (M2) | CPU_AND_NE |
SPLIT_EINSUM_V2 |
7.0 | 3.07 |
benchmark
分支收集的。tokenizer.model_max_length
)的正向传递,而与输入文本的实际长度无关。*
表示启用了 reduceMemory 选项,该选项会及时加载和卸载模型以避免内存短缺。 这给端到端延迟增加了最多 2 秒。--compute-unit
和 --attention-implementation
值。 前者不会修改 Core ML 模型,可以在运行时应用。 后者修改 Core ML 模型。 请注意,性能最佳的计算单元是模型版本和硬件特定的。--attention-implementation
)通常适用于 Transformers,而不是针对 Stable Diffusion 定制。 通过自定义内核调整可以观察到更好的性能。 因此,这些数字并不代表 峰值 硬件能力。stabilityai/stable-diffusion-xl-base-1.0-ios
(768x768)
设备 | --compute-unit |
--attention-implementation |
端到端延迟(秒) | 扩散速度(迭代/秒) |
---|---|---|---|---|
iPhone 12 Pro | CPU_AND_NE |
SPLIT_EINSUM |
116* | 0.50 |
iPhone 13 Pro Max | CPU_AND_NE |
SPLIT_EINSUM |
86* | 0.68 |
iPhone 14 Pro Max | CPU_AND_NE |
SPLIT_EINSUM |
77* | 0.83 |
iPhone 15 Pro Max | CPU_AND_NE |
SPLIT_EINSUM |
31 | 0.85 |
iPad Pro (M1) | CPU_AND_NE |
SPLIT_EINSUM |
36 | 0.69 |
iPad Pro (M2) | CPU_AND_NE |
SPLIT_EINSUM |
27 | 0.98 |
benchmark
分支收集的。Unet.mlmodelc
按照发布的 混合位调色算法配方压缩到 4.04 位精度 hereUnet.mlmodelc
之外的所有模型都压缩到 16 位精度VAEDecoder.mlmodelc
的源 PyTorch 模型,以便为 VAE 模型启用 float16 权重和激活量化。--attention-implementation SPLIT_EINSUM
而不是 SPLIT_EINSUM_V2
,因为后者的编译时间过长。*
表示启用了 reduceMemory 选项,该选项会及时加载和卸载模型以避免内存短缺。 这给端到端延迟增加了显著的开销。 请注意 iPad Pro (M1)
和 iPhone 13 Pro Max
之间端到端延迟的差异,尽管扩散速度相同。tokenizer.model_max_length
)的正向传递,而与输入文本的实际长度无关。--compute-unit
和 --attention-implementation
值。 前者不会修改 Core ML 模型,可以在运行时应用。 后者修改 Core ML 模型。 请注意,性能最佳的计算单元是模型版本和硬件特定的。--attention-implementation
)通常适用于 Transformers,而不是针对 Stable Diffusion 定制。 通过自定义内核调整可以观察到更好的性能。 因此,这些数字并不代表 峰值 硬件能力。stabilityai/stable-diffusion-xl-base-1.0
(1024x1024)
设备 | --compute-unit |
--attention-implementation |
端到端延迟(秒) | 扩散速度(迭代/秒) |
---|---|---|---|---|
MacBook Pro (M1 Max) | CPU_AND_GPU |
ORIGINAL |
46 | 0.46 |
MacBook Pro (M2 Max) | CPU_AND_GPU |
ORIGINAL |
37 | 0.57 |
Mac Studio (M1 Ultra) | CPU_AND_GPU |
ORIGINAL |
25 | 0.89 |
Mac Studio (M2 Ultra) | CPU_AND_GPU |
ORIGINAL |
20 | 1.11 |
StableDiffusion
Swift pipeline 收集的。coremltools-7.0 支持 剪枝、调色板化 和 线性 8 位量化 的高级权重压缩技术。 对于这些技术,coremltools.optimize.torch.*
包含需要微调以在较高压缩率下保持准确性的 API,而 coremltools.optimize.coreml.*
包含在训练后应用且无数据的 API。
我们演示了在 coremltools.optimize.coreml.palettize_weights
中实现的无数据 训练后调色板化 如何使我们能够在移动设备上实现 Stable Diffusion 的大大提高的性能。 此 API 实现了 快速精确 k-Means 算法以进行最佳权重聚类,从而产生更准确的调色板。 在 转换 期间使用 --quantize-nbits {2,4,6,8}
会将此压缩应用于 unet 和 text_encoder 模型。
为了获得最佳结果,我们建议使用 训练时调色板化:如果微调您的模型是可行的,则使用 coremltools.optimize.torch.palettization.DKMPalettizer
。 此 API 实现了 可微 k-Means (DKM) 学习的调色板化算法。 在此练习中,为了简单和易于重现,我们坚持使用训练后调色板化。
神经引擎能够加速具有低位调色板化的模型:1、2、4、6 或 8 位。 使用 iOS 17 和 macOS 14,Core ML 模型的压缩权重可以在运行时及时解压缩(而不是在加载时提前解压缩),以匹配激活张量的精度。 这可以节省大量内存,并使模型能够在具有较小 RAM 的设备(例如 iPhone 12 Mini)上运行。 此外,从内存中提取压缩权重速度更快,从而减少了内存带宽限制层的延迟。 及时解压缩行为取决于计算单元、层类型和硬件代数。
权重精度 | --compute-unit |
stabilityai/stable-diffusion-2-1-base 生成 "一张冲浪狗的高质量照片" |
---|---|---|
6 位 | cpuAndNeuralEngine | ![]() |
16 位 | cpuAndNeuralEngine | ![]() |
16 位 | cpuAndGPU | ![]() |
请注意,16 位 (float16) 和 6 位结果之间存在细微差异。 这些差异与 float16 和 float32 之间的差异或上述计算单元之间的差异相当。 我们建议至少使用 6 位来调色板化 Stable Diffusion。 较少的位数(1、2 和 4)将需要微调或高级调色板化技术,例如 MBP。
资源
本节介绍了一种名为 混合位调色板化 (MBP) 的高级压缩算法,该算法构建在 训练后权重调色板化工具 之上,并使用来自 coremltools 的 权重元数据 API。
MBP 通过在 1、2、4、6 和 8 的神经引擎支持的位宽中选择合适的位数,构建一个逐层“调色板化配方”,以在保持所需信号强度的同时实现最小的平均位宽。 信号强度是通过将压缩模型的输出与原始 float16 模型的输出进行比较来测量的。 给定相同的随机种子和文本提示,计算去噪潜在变量之间的 PSNR。 由于该算法是自适应的,因此压缩率将取决于模型版本以及对信号损失(PSNR 下降)的容忍度。
3.41 位 | 4.50 位 | 6.55 位 | 16 位(原始) |
---|---|---|---|
![]() |
![]() |
![]() |
![]() |
例如,原始 float16 stabilityai/stable-diffusion-xl-base-1.0 模型的信号强度约为 82 dB。 天真地将 线性 8 位量化 应用于 Unet 模型会将信号强度降低到约 65 dB。 相反,应用 MBP 会产生平均 2.81 位的量化,同时保持约 67 dB 的信号强度。 与在模型转换期间使用 --quantize-nbits
相比,该技术通常会产生更好的结果,但需要一个“预分析”运行,该运行在单个 GPU(mps
或 cuda
)上最多需要几个小时。
此处显示了针对 stabilityai/stable-diffusion-xl-base-1.0
的信号强度(以 dB 为单位的 PSNR)与模型大小减少(float16 大小的百分比)的关系。{1,2,4,6,8}-bit
曲线是通过使用固定数量的 bit 的调色板逐步调色更多层生成的。这些层按照它们对端到端信号强度的独立影响的升序排列,因此累积压缩的影响尽可能延迟。混合 bit 曲线基于一旦某一层的独立影响降至低于阈值时,就回退到更高数量的 bit。请注意,除了 1-bit 外,所有基于调色的曲线在相同的模型大小下都优于线性 8-bit 量化。
以下是在另一个模型版本上应用此技术的步骤
步骤 1:运行预分析脚本以生成具有不同信号强度的“配方”
python -m python_coreml_stable_diffusion.mixed_bit_compression_pre_analysis --model-version <model-version> -o <output-dir>
对于流行的基础模型,您可能会在此处找到预先计算的预分析结果。微调后的模型可能遵循其对应基础模型的配方,但未经测试。
步骤 2:步骤 1 生成的 JSON 文件将列出“基线”,例如:
{
"model_version": "stabilityai/stable-diffusion-xl-base-1.0",
"baselines": {
"original": 82.2,
"linear_8bit": 66.025,
"recipe_6.55_bit_mixedpalette": 79.9,
"recipe_5.52_bit_mixedpalette": 78.2,
"recipe_4.89_bit_mixedpalette": 76.8,
"recipe_4.41_bit_mixedpalette": 75.5,
"recipe_4.04_bit_mixedpalette": 73.2,
"recipe_3.67_bit_mixedpalette": 72.2,
"recipe_3.32_bit_mixedpalette": 71.4,
"recipe_3.19_bit_mixedpalette": 70.4,
"recipe_3.08_bit_mixedpalette": 69.6,
"recipe_2.98_bit_mixedpalette": 68.6,
"recipe_2.90_bit_mixedpalette": 67.8,
"recipe_2.83_bit_mixedpalette": 67.0,
"recipe_2.71_bit_mixedpalette": 66.3
},
}
在这些基线中,根据您所需的信号强度选择一个配方。我们建议调色到 ~4 bit,具体取决于用例,即使较低 bit 值的信号完整性高于线性 8-bit 量化基线。
最后,按如下方式将选定的配方应用于 float16 Core ML 模型
python -m python_coreml_stable_diffusion.mixed_bit_compression_apply --mlpackage-path <path-to-float16-unet-mlpackage> -o <output-dir> --pre-analysis-json-path <path-to--pre-analysis-json> --selected-recipe <selected-recipe-string-key>
一个示例 <selected-recipe-string-key>
将是 "recipe_4.50_bit_mixedpalette"
,它可以实现平均 4.50-bit 的压缩(对于 SDXL,从约 5.2GB 压缩到约 1.46GB)。请注意,信号强度并不直接映射到图像-文本对齐。始终验证您的 MBP 压缩模型变体是否能够准确地为您的测试提示生成图像。
在配备 A17 Pro 或 M4 芯片的较新硬件上,例如 iPhone 15 Pro,将激活和权重都量化为 int8 可以利用神经引擎上的优化计算,这可以用于提高计算密集型模型中的运行时延迟。
在本节中,我们将演示如何在 Stable Diffusion UNet 模型上使用校准数据应用训练后数据校准激活量化。
与上面描述的混合位调色 (MBP) 类似,首先,运行逐层分析以确定哪些中间激活对 8 位压缩更敏感。对不太敏感的层进行权重和激活量化 (W8A8),而对更敏感的层仅进行权重量化 (W8A16)。
以下是应用此技术的步骤
步骤 1:生成校准数据
python -m python_coreml_stable_diffusion.activation_quantization --model-version <model-version> --generate-calibration-data -o <output-dir>
通过 StableDiffusionPipeline 运行一组校准文本提示,并记录 UNet 模型输入,并将其作为 pickle 文件存储在指定输出目录内的 calibration_data_<model-version>
文件夹中。
步骤 2:运行逐层敏感度分析
python -m python_coreml_stable_diffusion.activation_quantization --model-version <model-version> --layerwise-sensitivity --calibration-nsamples <num-samples> -o <output-dir>
这将在模型中所有卷积和注意力 (Einsum) 模块上运行分析。对于每个模块,通过仅量化该层的权重和激活来生成压缩版本。然后,使用相同的随机种子和文本提示,计算压缩模型和原始模型输出之间的 PSNR。
此分析在单个 GPU (cuda) 上最多需要几个小时。可以减少用于量化模型的校准样本数量,以加快该过程。
生成的 JSON 文件如下所示
{
"conv": {
"conv_in": 30.74,
"down_blocks.0.attentions.0.proj_in": 38.93,
"down_blocks.0.attentions.0.transformer_blocks.0.attn1.to_q": 48.15,
"down_blocks.0.attentions.0.transformer_blocks.0.attn1.to_k": 50.13,
"down_blocks.0.attentions.0.transformer_blocks.0.attn1.to_v": 45.70,
"down_blocks.0.attentions.0.transformer_blocks.0.attn1.to_out.0": 39.56,
...
},
"einsum": {
"down_blocks.0.attentions.0.transformer_blocks.0.attn1.einsum": 25.34,
"down_blocks.0.attentions.0.transformer_blocks.0.attn2.einsum": 31.76,
"down_blocks.0.attentions.1.transformer_blocks.0.attn1.einsum": 23.40,
"down_blocks.0.attentions.1.transformer_blocks.0.attn2.einsum": 31.56,
...
},
"model_version": "stabilityai/stable-diffusion-2-1-base"
}
步骤 3:生成量化模型
使用校准数据和逐层敏感度,可以按如下方式生成量化的 CoreML 模型
python -m python_coreml_stable_diffusion.activation_quantization --model-version <model-version> --quantize-pytorch --conv-psnr 38 --attn-psnr 26 -o <output-dir>
PSNR 阈值确定哪些层将进行激活量化。可以调整此数字以权衡输出质量和推理延迟。
Stable Diffusion 3 使用一些新的和一些旧的模型来运行。对于文本编码器,可以使用与以前类似的命令以及 --sd3-version
标志来完成转换。
python -m python_coreml_stable_diffusion.torch2coreml --model-version stabilityai/stable-diffusion-3-medium --bundle-resources-for-swift-cli --convert-text-encoder --sd3-version -o <output-dir>
对于新模型(MMDiT,具有 16 个通道的新 VAE,以及 T5 文本编码器),有许多新的 CLI 标志,它们利用了 DiffusionKit 存储库
--sd3-version
:指示转换器将其视为 Stable Diffusion 3 模型--convert-mmdit
:转换 MMDiT 模型--convert-vae-decoder
:转换新的 VAE 模型(如果设置了 --sd3-version,这将使用 16 通道版本)--include-t5
:下载并在转换中包含预转换的 T5 文本编码器例如:
python -m python_coreml_stable_diffusion.torch2coreml --model-version stabilityai/stable-diffusion-3-medium --bundle-resources-for-swift-cli --convert-vae-decoder --convert-mmdit --include-t5 --sd3-version -o <output-dir>
要以 1024x1024 分辨率转换完整流水线,可以使用以下命令
python -m python_coreml_stable_diffusion.torch2coreml --model-version stabilityai/stable-diffusion-3-medium --bundle-resources-for-swift-cli --convert-text-encoder --convert-vae-decoder --convert-mmdit --include-t5 --sd3-version --latent-h 128 --latent-w 128 -o <output-dir>
请记住,MMDiT 模型非常大,并且随着潜在分辨率的增加,转换将需要越来越多的内存和时间。
另请注意,目前 MMDiT 模型需要 fp32,因此仅支持 CPU_AND_GPU
计算单元和 ORIGINAL
注意力实现(此流水线的默认设置)。
Stable Diffusion 3 的 Swift 推理与以前的版本类似。唯一的区别是应该使用 --sd3
标志来指示该模型是 Stable Diffusion 3 模型。
swift run StableDiffusionSample <prompt> --resource-path <output-mlpackages-directory/Resources> --output-path <output-dir> --compute-units cpuAndGPU --sd3
例如:
python -m python_coreml_stable_diffusion.torch2coreml --convert-unet --convert-vae-decoder --convert-text-encoder --xl-version --model-version stabilityai/stable-diffusion-xl-base-1.0 --refiner-version stabilityai/stable-diffusion-xl-refiner-1.0 --bundle-resources-for-swift-cli --attention-implementation {ORIGINAL,SPLIT_EINSUM} -o <output-dir>
--xl-version
:指定 XL 模型时传递给转换脚本的附加参数--refiner-version
:指定 XL 精炼器模型时传递给转换脚本的附加参数,“专家去噪器集成”推理所需。--attention-implementation
:建议 ORIGINAL
用于 Mac 上的 cpuAndGPU
部署--attention-implementation
:建议 SPLIT_EINSUM
用于 iPhone 和 iPad 上的 cpuAndNeuralEngine
部署--attention-implementation
:由于编译时间过长,不建议 Stable Diffusion XL 使用 SPLIT_EINSUM_V2
--latent-h 96 --latent-w 96
,这会导致 768x768 生成,而不是默认的 1024x1024。--custom-vae-version madebyollin/sdxl-vae-fp16-fix
将恢复 VAE 的默认 float16 精度。swift run StableDiffusionSample <prompt> --resource-path <output-mlpackages-directory/Resources> --output-path <output-dir> --compute-units {cpuAndGPU,cpuAndNeuralEngine} --xl
base
模型,refiner
模型是可选的,如果资源目录中提供了该模型,则默认情况下将使用它。python -m python_coreml_stable_diffusion.pipeline --prompt <prompt> --compute-unit {CPU_AND_GPU,CPU_AND_NE} -o <output-dir> -i <output-mlpackages-directory/Resources> --model-version stabilityai/stable-diffusion-xl-base-1.0
refiner
模型使用提示“一张高质量的冲浪狗照片”并以涂鸦(最左侧)为条件的示例结果
ControlNet 允许用户使用 Stable Diffusion 根据边缘图、深度图、分割图、涂鸦和姿势等信号来调节图像生成。感谢 @ryu38 的贡献,Python CLI 和 Swift 包都支持 ControlNet 模型。有关使用 ControlNet 设置 Stable Diffusion 的详细信息,请参阅此部分。
请注意,尚不支持 Stable Diffusion XL 的 ControlNet。
在 iOS 17 和 macOS 14 中,NaturalLanguage
框架引入了 NLContextualEmbedding,它为拉丁语(20 种语言)、西里尔语(4 种语言)和 CJK(3 种语言)脚本提供基于 Transformer 的文本嵌入。名为探索自然语言多语言模型的 WWDC23 会议演示了开发人员如何使用这种强大的新模型来训练下游任务,例如使用 Stable Diffusion 的多语言图像生成。
此存储库中提供了重现此演示工作流程的代码。有几种方法可以实现此工作流程。这是一个例子
步骤 1:策划一个包含所需语言的图像-文本数据集。
步骤 2:预先计算 NLContextualEmbedding 值,并在数据集中用这些嵌入向量替换文本字符串。
步骤 3:使用你的新数据集并通过使用预先计算的 NLContextualEmbedding 值替换默认的 text_encoder,从 Hugging Face Hub 微调一个与 StableDiffusionPipeline 兼容的基础模型。
步骤 4:为了能够在不训练新层的情况下交换基础模型的 text_encoder,基础模型的 text_encoder.hidden_size
必须与 NLContextualEmbedding 的大小匹配。如果它不匹配,你将需要训练一个线性投影层以在两个维度之间进行映射。微调后,应按如下方式将此线性层转换为 CoreML
python -m python_coreml_stable_diffusion.multilingual_projection --input-path <path-to-projection-torchscript> --output-dir <output-dir>
上面的命令将在 --output-dir
下生成一个 MultilingualTextEncoderProjection.mlmodelc
文件,并且该文件应与通过 --bundle-resources-for-swift-cli
生成的其余 Core ML 模型资产并置。
步骤 5:现在可以通过在初始化流水线时将 useMultilingualTextEncoder
设置为 true 或在 CLI 中设置 --use-multilingual-text-encoder
来调用多语言系统文本编码器。请注意,模型资产是通过无线方式分发的,因此第一次调用将触发资产下载,该下载小于 100MB。
资源
🤗 Hugging Face 在以下模型上运行了 转换过程,并公开了 Hub 上可用的 Core ML 权重。如果你想转换 Hub 上尚未提供的 Stable Diffusion 版本,请参阅 将模型转换为 Core ML。
6 位量化模型(适用于 iOS 17 和 macOS 14)
混合位宽量化模型
如果您想使用这些模型,您可以下载权重并继续使用 Python 生成图像 或 Swift。
每个模型仓库中都有几个变体。您可以使用 git
和 git lfs
克隆整个仓库来下载所有变体,或者选择性地下载您需要的变体。
要使用 git
克隆仓库,请按照以下步骤操作
步骤 1: 为您的系统安装 git lfs
扩展。
git lfs
将大型文件存储在主 git 仓库之外,并在您克隆或检出后从相应的服务器下载它们。它在大多数软件包管理器中都可用,请查看 安装页面 以获取详细信息。
步骤 2: 运行此命令一次以启用 git lfs
git lfs install
步骤 3: 使用 git clone
下载包含所有模型变体的仓库副本。 对于 Stable Diffusion 1.4 版本,您可以在终端中发出以下命令
git clone https://hugging-face.cn/apple/coreml-stable-diffusion-v1-4
如果您喜欢下载特定变体而不是克隆仓库,您可以使用 huggingface_hub
Python 库。 例如,要使用 ORIGINAL
注意力实现(阅读此部分以了解详细信息)在 Python 中进行生成,您可以使用以下辅助代码
from huggingface_hub import snapshot_download
from pathlib import Path
repo_id = "apple/coreml-stable-diffusion-v1-4"
variant = "original/packages"
model_path = Path("./models") / (repo_id.split("/")[-1] + "_" + variant.replace("/", "_"))
snapshot_download(repo_id, allow_patterns=f"{variant}/*", local_dir=model_path, local_dir_use_symlinks=False)
print(f"Model downloaded at {model_path}")
model_path
将是检查点保存到本地文件系统中的路径。 有关更多详细信息,请参阅此帖子。
步骤 1: 创建一个 Python 环境并安装依赖项
conda create -n coreml_stable_diffusion python=3.8 -y
conda activate coreml_stable_diffusion
cd /path/to/cloned/ml-stable-diffusion/repository
pip install -e .
步骤 2: 登录或注册您的 Hugging Face 帐户,生成一个 用户访问令牌,并通过在终端窗口中运行 huggingface-cli login
来使用此令牌设置 Hugging Face API 访问权限。
步骤 3: 导航到您想在 Hugging Face Hub 上使用的 Stable Diffusion 版本,并接受其使用条款。 默认模型版本为 CompVis/stable-diffusion-v1-4。 模型版本可以由用户在下一步中描述的方式进行更改。
步骤 4: 从终端执行以下命令以生成 Core ML 模型文件 (.mlpackage
)
python -m python_coreml_stable_diffusion.torch2coreml --convert-unet --convert-text-encoder --convert-vae-decoder --convert-safety-checker --model-version <model-version-string-from-hub> -o <output-mlpackages-directory>
警告: 此命令将从 Hugging Face 下载几个 GB 的 PyTorch 检查点。 请确保您已连接到 Wi-Fi 并且有足够的磁盘空间。
这通常在 M1 MacBook Pro 上需要 15-20 分钟。 成功执行后,构成 Stable Diffusion 的 4 个神经网络模型将从 PyTorch 转换为 Core ML (.mlpackage
) 并保存到指定的 <output-mlpackages-directory>
中。 一些其他值得注意的参数
--model-version
:模型版本名称,如 Hugging Face Hub 上发布的那样
--refiner-version
:精炼器版本名称,如 Hugging Face Hub 上发布的那样。 这是可选的,如果指定,此参数将转换精炼器 unet 并将其与模型 unet 捆绑在一起。
--bundle-resources-for-swift-cli
:编译所有 4 个模型并将其与文本标记化所需的资源一起捆绑到 <output-mlpackages-directory>/Resources
中,该资源应作为 Swift 包的输入提供。 此标志对于基于 diffusers 的 Python 管道不是必需的。 但是,在 Python 中使用这些编译后的模型将显着加快推理速度。
--quantize-nbits
:使用全局最优 k-means 聚类算法将 unet 和 text_encoder 模型的权重量化到 2、4、6 或 8 位。 默认情况下,即使未指定此参数,所有模型也都会被权重量化到 16 位。 请参阅[此部分](#compression-6-bits-and-higher)以获取详细信息和有关权重压缩的进一步指导。
--chunk-unet
:将 Unet 模型拆分为两个大致相等的块(每个块的权重小于 1GB),以便于移动设备部署。 如果权重未量化为 6 位或更少(--quantize-nbits {2,4,6}
),则在 iOS 和 iPadOS 上进行神经引擎部署时,这是必需的。 这对于 macOS 不是必需的。 Swift CLI 能够使用 Unet 模型的分块和常规版本,但优先使用前者。 请注意,分块的 unet 与 Python 管道不兼容,因为 Python 管道仅适用于 macOS。
--attention-implementation
:默认为 SPLIT_EINSUM
,这是 在 Apple 神经引擎上部署 Transformer 中描述的实现。 --attention-implementation SPLIT_EINSUM_V2
为移动设备带来了 10-30% 的改进,仍然针对神经引擎。 --attention-implementation ORIGINAL
将切换到另一种实现,该实现应用于某些 Mac 设备上的 CPU 或 GPU 部署。 有关更多指导,请参阅性能基准部分。
--check-output-correctness
:将原始 PyTorch 模型的输出与最终 Core ML 模型的输出进行比较。 此标志会显着增加 RAM 消耗,因此建议仅用于调试目的。
--convert-controlnet
:转换在此选项后指定的 ControlNet 模型。 如果您像 --convert-controlnet lllyasviel/sd-controlnet-mlsd lllyasviel/sd-controlnet-depth
一样指定,这也可以转换多个模型。
--unet-support-controlnet
:使转换后的 UNet 模型能够接收来自 ControlNet 的额外输入。 这是使用 ControlNet 生成图像所必需的,并以与普通 UNet 不同的名称 *_control-unet.mlpackage
保存。 另一方面,此 UNet 模型在没有 ControlNet 的情况下无法工作。 对于仅 txt2img,请使用普通 UNet。
--unet-batch-one
:为 unet 使用大小为 1 的批量大小,如果您不想进行无分类器指导,即使用小于 1 的 guidance-scale
,则需要这样做。
--convert-vae-encoder
:文本到图像应用程序不需要。 为了将输入图像映射到潜在空间,图像到图像应用程序需要。
使用基于 diffusers 的示例 Python 管道运行文本到图像生成
python -m python_coreml_stable_diffusion.pipeline --prompt "a photo of an astronaut riding a horse on mars" -i <core-ml-model-directory> -o </path/to/output/image> --compute-unit ALL --seed 93
请参阅帮助菜单以获取所有可用参数:python -m python_coreml_stable_diffusion.pipeline -h
。 一些值得注意的参数
-i
:应指向上面 将模型转换为 Core ML 部分的步骤 4 中的 -o
目录。 如果您在转换期间指定了 --bundle-resources-for-swift-cli
,则使用生成的 Resources
文件夹(其中包含已编译的 .mlmodelc
文件)。 首次使用后,编译后的模型加载速度会快得多。--model-version
:如果您在将模型转换为 Core ML 时覆盖了默认模型版本,则需要在此处指定相同的模型版本。--compute-unit
:请注意,此特定实现的最佳性能计算单元可能因不同的硬件而异。 CPU_AND_GPU
或 CPU_AND_NE
可能比 ALL
快。 有关更多指导,请参阅性能基准部分。--scheduler
:如果您想尝试不同的调度程序,可以在此处指定它。 有关可用选项,请参阅帮助菜单。 您还可以通过 --num-inference-steps
指定自定义的推理步骤数,默认为 50。--controlnet
:使用此选项指定的 ControlNet 模型用于图像生成。 使用 --controlnet lllyasviel/sd-controlnet-mlsd lllyasviel/sd-controlnet-depth
格式使用此选项,并确保结合使用 --controlnet-inputs
。--controlnet-inputs
:与每个 ControlNet 模型对应的图像输入。 请按照 --controlnet
中模型的相同顺序提供图像路径,例如:--controlnet-inputs image_mlsd image_depth
。--unet-batch-one
:不要批量处理提示和负面提示的 unet 预测。 这需要 unet 已使用批量大小 1 进行转换,请参阅转换脚本中的 --unet-batch-one
选项。swift run StableDiffusionSample "a photo of an astronaut riding a horse on mars" --resource-path <output-mlpackages-directory>/Resources/ --seed 93 --output-path </path/to/output/image>
输出将基于提示和随机种子命名:例如 </path/to/output/image>/a_photo_of_an_astronaut_riding_a_horse_on_mars.93.final.png
请使用 --help
标志来了解批量生成等信息。
import StableDiffusion
...
let pipeline = try StableDiffusionPipeline(resourcesAt: resourceURL)
pipeline.loadResources()
let image = try pipeline.generateImages(prompt: prompt, seed: seed).first
在 iOS 上,构建 StableDiffusionPipeline
时,应将 reduceMemory
选项设置为 true
此 Swift 包包含两个产品
StableDiffusion
库StableDiffusionSample
命令行工具这两个产品都需要提供 Core ML 模型和令牌化资源。 当通过目录路径指定资源时,该目录必须包含以下内容
TextEncoder.mlmodelc
或 `TextEncoder2.mlmodelc(文本嵌入模型)Unet.mlmodelc
或 UnetChunk1.mlmodelc
& UnetChunk2.mlmodelc
(去噪自动编码器模型)VAEDecoder.mlmodelc
(图像解码器模型)vocab.json
(令牌化器词汇表文件)merges.text
(字节对编码文件合并)可选地,对于图像到图像、图像修复或类似情况
VAEEncoder.mlmodelc
(图像编码器模型)可选地,它还可以包括某些版本的 Stable Diffusion 包含的安全检查器模型
SafetyChecker.mlmodelc
可选地,对于 SDXL 精炼器
UnetRefiner.mlmodelc
(精炼器 unet 模型)可选地,对于 ControlNet
ControlledUNet.mlmodelc
或 ControlledUnetChunk1.mlmodelc
& ControlledUnetChunk2.mlmodelc
(启用以接收 ControlNet 值)controlnet/
(包含 ControlNet 模型的目录)LllyasvielSdControlnetMlsd.mlmodelc
(例如,来自 lllyasviel/sd-controlnet-mlsd)LllyasvielSdControlnetDepth.mlmodelc
(例如,来自 lllyasviel/sd-controlnet-depth)请注意,首先检查 Unet 的分块版本。 只有在不存在时才会加载完整的 Unet.mlmodelc
。 分块是 iOS 和 iPadOS 所必需的,而 macOS 则不需要。
🤗 Hugging Face 创建了一个基于此库的 开源演示应用程序。 它用本机 Swift 和 Swift UI 编写,并在 macOS、iOS 和 iPadOS 上运行。 您可以使用该代码作为您的应用程序的起点,或了解如何将此库集成到您自己的项目中。
Hugging Face 已将该应用程序 在 Mac App Store 中提供。
ERROR: Failed building wheel for tokenizers or error: can't find Rust compiler
答案 1: 请查看此 潜在解决方案。
RuntimeError: {NSLocalizedDescription = "Error computing NN outputs."
答案 2: 此错误有很多潜在原因。 在这种情况下,当您的系统因其他应用程序而承受更大的内存压力时,很可能会遇到此错误。 减少其他应用程序的内存利用率可能有助于缓解此问题。
A3: 为了最大限度地减少模型转换过程的内存占用,请执行以下命令:
python -m python_coreml_stable_diffusion.torch2coreml --convert-vae-encoder --model-version <model-version-string-from-hub> -o <output-mlpackages-directory> && \
python -m python_coreml_stable_diffusion.torch2coreml --convert-vae-decoder --model-version <model-version-string-from-hub> -o <output-mlpackages-directory> && \
python -m python_coreml_stable_diffusion.torch2coreml --convert-unet --model-version <model-version-string-from-hub> -o <output-mlpackages-directory> && \
python -m python_coreml_stable_diffusion.torch2coreml --convert-text-encoder --model-version <model-version-string-from-hub> -o <output-mlpackages-directory> && \
python -m python_coreml_stable_diffusion.torch2coreml --convert-safety-checker --model-version <model-version-string-from-hub> -o <output-mlpackages-directory> &&
如果您需要 --chunk-unet
,您可以执行另一个独立的命令,该命令将重用先前导出的 Unet 模型,并简单地对其进行分块处理。
python -m python_coreml_stable_diffusion.torch2coreml --convert-unet --chunk-unet -o <output-mlpackages-directory>
A4: 是的!尤其是 --compute-unit CPU_AND_NE
选项,在其他应用程序造成的合理系统负载下应该可以工作。请注意,示例结果中的一部分是使用配备 8GB 内存的 M2 MacBook Air 生成的。
A5: 无论是 .mlpackage
还是 .mlmodelc
模型,在首次加载时都会在指定特定计算单元的情况下进行编译(在 Core ML 术语中也称为“模型准备”)。.mlpackage
不会缓存此编译后的资产,因此每次模型加载都会重新触发此编译,这可能需要几分钟时间。另一方面,.mlmodelc
文件会缓存此编译后的资产,非首次加载时间将缩短到几秒钟。
为了从编译缓存中获益,您可以在 Swift(默认)和 Python(感谢 @lopez-hector 的 贡献)图像生成管道中使用 .mlmodelc
资产而不是 .mlpackage
资产。
StableDiffusion
,即 Swift 包。我应该注意什么?A6: 使用 Swift 进行图像生成 部分介绍了此包支持的最低 SDK 和操作系统版本以及设备型号。我们建议在部署目标中可用 RAM 最少的设备上仔细测试该包。
StableDiffusion
中的图像生成过程在运行时可能会产生超过 2 GB 的峰值内存,具体取决于选择的计算单元。在 iPadOS 上,我们建议在配置中使用 .cpuAndNeuralEngine
,并在构建 StableDiffusionPipeline
时使用 reduceMemory
选项,以最大限度地减少内存压力。
如果您的应用程序在图像生成期间崩溃,请考虑添加 Increased Memory Limit 功能,以告知系统您的应用程序的某些核心功能可能会通过超出支持设备上的默认应用程序内存限制来获得更好的性能。
在 iOS 上,根据 iPhone 型号、Stable Diffusion 模型版本、选定的计算单元、系统负载和应用程序的设计,这可能仍然不足以使应用程序的峰值内存保持在限制之内。请记住,由于设备在应用程序和 iOS 进程之间共享内存,因此一个应用程序使用过多内存可能会损害整个设备的用户体验。
我们 强烈建议 按照 高级权重压缩(低于 6 位) 中的方法压缩您的模型,以进行 iOS 部署。这可以将峰值 RAM 使用量减少高达 75%(从 16 位到 4 位),同时保持模型输出质量。
A7: 当前版本的 python_coreml_stable_diffusion
不支持开箱即用的单模型多分辨率。但是,开发人员可以 fork 此项目并利用来自 coremltools 的 灵活形状 支持,通过使用 coremltools.EnumeratedShapes
来扩展 torch2coreml
脚本。请注意,虽然 text_encoder
与图像分辨率无关,但 vae_decoder
和 unet
模型的输入和输出取决于所需的图像分辨率。
A8: 如果需要,可以使 PyTorch 和 Core ML 生成的图像大致相同。但是,默认情况下不能保证这一点。有几个因素可能导致 PyTorch 和 Core ML 之间的图像不同
1. 随机数生成器行为
PyTorch 和 Core ML 之间可能存在不同结果的主要来源是随机数生成器 (RNG) 行为。PyTorch 和 Numpy 具有不同的随机源。python_coreml_stable_diffusion
通常依赖 Numpy 进行 RNG(例如,潜在变量初始化),而 StableDiffusion
Swift 库默认情况下会重现此 RNG 行为。但是,基于 PyTorch 的管道(例如 Hugging Face diffusers
)依赖于 PyTorch 的 RNG 行为。感谢 @liuliu 的 贡献,可以通过指定 --rng torch/cuda
来匹配 Swift 中的 PyTorch (CPU/GPU) RNG 行为,该选项会选择 torchRNG/cudaRNG
模式。
2. PyTorch
“无法保证在 PyTorch 版本、单个提交或不同平台之间完全可重现的结果。此外,即使使用相同的种子,CPU 和 GPU 执行之间的结果也可能无法重现。” (来源)。
3. 转换期间的模型函数漂移
相应的 PyTorch 和 Core ML 模型之间的输出差异是一个潜在原因。在转换过程中会测试信号完整性(通过 python_coreml_stable_diffusion.torch2coreml
的 --check-output-correctness
参数启用),并且经验证,在随机输入下,信号完整性高于最小 PSNR 值。请注意,这只是一项健全性检查,并不保证在所有可能的输入下都存在此最小 PSNR。此外,无法保证在不同的计算单元上执行相同的 Core ML 模型时结果相同。由于示例视觉结果在 此部分中显示,因此这预计不是差异的主要来源。
4. 权重和激活数据类型
将模型从 float32 量化为较低精度的数据类型(例如 float16)时,即使使用相同的 PyTorch 模型,生成的图像也 已知在语义上略有不同。coremltools 生成的 Core ML 模型默认情况下具有 float16 权重和激活 除非明确覆盖。这预计不是差异的主要来源。
A9: 推荐的选项是提示用户在首次启动应用程序时下载这些资产。这使应用程序二进制文件的大小与部署的 Core ML 模型无关。向用户披露下载的大小非常重要,因为可能会产生用户可能不舒服的数据费用或存储影响。
`Could not initialize NNPACK! Reason: Unsupported hardware`
A10: 在此存储库的上下文中,可以安全地忽略此警告。
TracerWarning: Converting a tensor to a Python boolean might cause the trace to be incorrect
A11: 在此存储库的上下文中,可以安全地忽略此警告。
UserWarning: resource_tracker: There appear to be 1 leaked semaphore objects to clean up at shutdown
A12: 如果在 zsh: killed python -m python_coreml_stable_diffusion.torch2coreml ...
之后立即打印此警告,则很可能您的 Mac 在将模型转换为 Core ML 时内存已耗尽。请参阅上面的 Q3 了解解决方案。
@misc{stable-diffusion-coreml-apple-silicon,
title = {Stable Diffusion with Core ML on Apple Silicon},
author = {Atila Orhon and Michael Siracusa and Aseem Wadhwa},
year = {2022},
URL = {null}
}