Core ML Stable Diffusion

在 Apple Silicon 上使用 Core ML 运行 Stable Diffusion

[博客文章] [BibTeX]

本仓库包含以下内容:

如果您在安装或运行时遇到问题,请参阅 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
详细信息(点击展开)
  • 此基准测试由 Apple 和 Hugging Face 于 2023 年 8 月使用 iOS 17.0、iPadOS 17.0 和 macOS 14.0 Seed 8 的公共 Beta 版进行。
  • 性能数据是使用 Diffusers appbenchmark 分支收集的。
  • Swift 代码尚未完全优化,引入了与 Core ML 模型执行无关的约 10% 的开销。
  • 报告了 5 次连续端到端执行的中值延迟值
  • 图像生成过程遵循标准配置:20 个推理步骤,512x512 输出图像分辨率,77 个文本 token 序列长度,无分类器引导(unet 的批处理大小为 2)。
  • 实际提示长度不会影响性能,因为 Core ML 模型是使用静态形状转换的,该静态形状计算文本 token 序列中所有 77 个元素(tokenizer.model_max_length)的正向传递,而与输入文本的实际长度无关。
  • 权重被压缩到 6 位精度。 有关详细信息,请参阅 此部分
  • 对于 GPU 和神经引擎,激活都采用 float16 精度。
  • * 表示启用了 reduceMemory 选项,该选项会及时加载和卸载模型以避免内存短缺。 这给端到端延迟增加了最多 2 秒。
  • 在基准测试表中,我们报告了每个设备上性能最佳的 --compute-unit--attention-implementation 值。 前者不会修改 Core ML 模型,可以在运行时应用。 后者修改 Core ML 模型。 请注意,性能最佳的计算单元是模型版本和硬件特定的。
  • 请注意,此仓库中的性能优化(例如 --attention-implementation)通常适用于 Transformers,而不是针对 Stable Diffusion 定制。 通过自定义内核调整可以观察到更好的性能。 因此,这些数字并不代表 峰值 硬件能力。
  • 由于模型本身的架构更改,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
详细信息(点击展开)
  • 此基准测试由 Apple 和 Hugging Face 于 2023 年 9 月使用 iOS 17.0.2 和 iPadOS 17.0.2 进行。
  • 性能数据是使用 Diffusers appbenchmark 分支收集的。
  • 报告了 5 次连续端到端执行的中值延迟值
  • 图像生成过程遵循此配置:20 个推理步骤,768x768 输出图像分辨率,77 个文本 token 序列长度,无分类器引导(unet 的批处理大小为 2)。
  • Unet.mlmodelc 按照发布的 混合位调色算法配方压缩到 4.04 位精度 here
  • Unet.mlmodelc 之外的所有模型都压缩到 16 位精度
  • @madebyollin 提供的 madebyollin/sdxl-vae-fp16-fix 被用作 VAEDecoder.mlmodelc 的源 PyTorch 模型,以便为 VAE 模型启用 float16 权重和激活量化。
  • 选择 --attention-implementation SPLIT_EINSUM 而不是 SPLIT_EINSUM_V2,因为后者的编译时间过长。
  • * 表示启用了 reduceMemory 选项,该选项会及时加载和卸载模型以避免内存短缺。 这给端到端延迟增加了显著的开销。 请注意 iPad Pro (M1)iPhone 13 Pro Max 之间端到端延迟的差异,尽管扩散速度相同。
  • 实际提示长度不会影响性能,因为 Core ML 模型是使用静态形状转换的,该静态形状计算文本 token 序列中所有 77 个元素(tokenizer.model_max_length)的正向传递,而与输入文本的实际长度无关。
  • 在基准测试表中,我们报告了每个设备上性能最佳的 --compute-unit--attention-implementation 值。 前者不会修改 Core ML 模型,可以在运行时应用。 后者修改 Core ML 模型。 请注意,性能最佳的计算单元是模型版本和硬件特定的。
  • 请注意,此仓库中的性能优化(例如 --attention-implementation)通常适用于 Transformers,而不是针对 Stable Diffusion 定制。 通过自定义内核调整可以观察到更好的性能。 因此,这些数字并不代表 峰值 硬件能力。
  • 由于模型本身的架构更改,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
详细信息(点击展开)
  • 此基准测试由 Apple 和 Hugging Face 于 2023 年 7 月使用 iOS 17.0、iPadOS 17.0 和 macOS 14.0 的公共 Beta 版进行。
  • 性能数据是通过运行 StableDiffusion Swift pipeline 收集的。
  • 报告了 3 次连续端到端执行的中值延迟值
  • 图像生成过程遵循标准配置:20 个推理步骤,1024x1024 输出图像分辨率,无分类器引导(unet 的批处理大小为 2)。
  • 权重和激活都采用 float16 精度
  • 由于模型本身的架构更改,Stable Diffusion 的不同版本之间的性能可能会有所不同。 每个报告的数字都特定于该上下文中提到的模型版本。
  • 性能可能会因其他应用程序增加的系统负载或次优的设备热状态等因素而异。 考虑到这些因素,我们不报告亚秒级的延迟差异。

权重压缩(6 位及以上)

详细信息(点击展开)

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

资源

高级权重压缩(低于 6 位)

详细信息(点击展开)

本节介绍了一种名为 混合位调色板化 (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(mpscuda)上最多需要几个小时。

此处显示了针对 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

详细信息(点击展开)

模型转换

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 存储库

例如:

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 注意力实现(此流水线的默认设置)。

Swift 推理

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

使用 Stable Diffusion XL

详细信息(点击展开)

模型转换

例如:

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>

Swift 推理

swift run StableDiffusionSample <prompt> --resource-path <output-mlpackages-directory/Resources> --output-path <output-dir> --compute-units {cpuAndGPU,cpuAndNeuralEngine} --xl

Python 推理

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

使用 ControlNet

详细信息(点击展开)

使用提示“一张高质量的冲浪狗照片”并以涂鸦(最左侧)为条件的示例结果

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 模型

单击以展开

🤗 Hugging Face 在以下模型上运行了 转换过程,并公开了 Hub 上可用的 Core ML 权重。如果你想转换 Hub 上尚未提供的 Stable Diffusion 版本,请参阅 将模型转换为 Core ML

如果您想使用这些模型,您可以下载权重并继续使用 Python 生成图像Swift

每个模型仓库中都有几个变体。您可以使用 gitgit 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 将是检查点保存到本地文件系统中的路径。 有关更多详细信息,请参阅此帖子

将模型转换为 Core ML

单击以展开

步骤 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> 中。 一些其他值得注意的参数

使用 Python 生成图像

单击以展开

使用基于 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。 一些值得注意的参数

使用 Swift 生成图像

单击以展开

示例 CLI 用法

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 包详细信息

此 Swift 包包含两个产品

这两个产品都需要提供 Core ML 模型和令牌化资源。 当通过目录路径指定资源时,该目录必须包含以下内容

可选地,对于图像到图像、图像修复或类似情况

可选地,它还可以包括某些版本的 Stable Diffusion 包含的安全检查器模型

可选地,对于 SDXL 精炼器

可选地,对于 ControlNet

请注意,首先检查 Unet 的分块版本。 只有在不存在时才会加载完整的 Unet.mlmodelc。 分块是 iOS 和 iPadOS 所必需的,而 macOS 则不需要。

示例 Swift 应用程序

单击以展开

🤗 Hugging Face 创建了一个基于此库的 开源演示应用程序。 它用本机 Swift 和 Swift UI 编写,并在 macOS、iOS 和 iPadOS 上运行。 您可以使用该代码作为您的应用程序的起点,或了解如何将此库集成到您自己的项目中。

Hugging Face 已将该应用程序 在 Mac App Store 中提供

常见问题解答

单击以展开
问题 1: ERROR: Failed building wheel for tokenizers or error: can't find Rust compiler

答案 1: 请查看此 潜在解决方案

问题 2: RuntimeError: {NSLocalizedDescription = "Error computing NN outputs."

答案 2: 此错误有很多潜在原因。 在这种情况下,当您的系统因其他应用程序而承受更大的内存压力时,很可能会遇到此错误。 减少其他应用程序的内存利用率可能有助于缓解此问题。

Q3: 我的 Mac 有 8GB 内存,我正在使用示例命令将模型转换为 Core ML。由于内存问题,进程被终止了。我该如何解决这个问题?

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>
Q4: 我的 Mac 有 8GB 内存,图像生成应该可以在我的机器上运行吗?

A4: 是的!尤其是 --compute-unit CPU_AND_NE 选项,在其他应用程序造成的合理系统负载下应该可以工作。请注意,示例结果中的一部分是使用配备 8GB 内存的 M2 MacBook Air 生成的。

Q5: 每次我使用 Python 管道生成图像时,加载所有 Core ML 模型需要 2-3 分钟。这是正常的吗?

A5: 无论是 .mlpackage 还是 .mlmodelc 模型,在首次加载时都会在指定特定计算单元的情况下进行编译(在 Core ML 术语中也称为“模型准备”)。.mlpackage 不会缓存此编译后的资产,因此每次模型加载都会重新触发此编译,这可能需要几分钟时间。另一方面,.mlmodelc 文件会缓存此编译后的资产,非首次加载时间将缩短到几秒钟。

为了从编译缓存中获益,您可以在 Swift(默认)和 Python(感谢 @lopez-hector贡献)图像生成管道中使用 .mlmodelc 资产而不是 .mlpackage 资产。

Q6: 我想在我的移动应用程序中部署 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 位),同时保持模型输出质量。

Q7: 如何使用相同的 Core ML 模型生成不同分辨率的图像?

A7: 当前版本的 python_coreml_stable_diffusion 不支持开箱即用的单模型多分辨率。但是,开发人员可以 fork 此项目并利用来自 coremltools 的 灵活形状 支持,通过使用 coremltools.EnumeratedShapes 来扩展 torch2coreml 脚本。请注意,虽然 text_encoder 与图像分辨率无关,但 vae_decoderunet 模型的输入和输出取决于所需的图像分辨率。

Q8: Core ML 和 PyTorch 生成的图像是否会完全相同?

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 权重和激活 除非明确覆盖。这预计不是差异的主要来源。

Q9: 模型文件非常大,如何避免应用程序二进制文件过大?

A9: 推荐的选项是提示用户在首次启动应用程序时下载这些资产。这使应用程序二进制文件的大小与部署的 Core ML 模型无关。向用户披露下载的大小非常重要,因为可能会产生用户可能不舒服的数据费用或存储影响。

Q10: `Could not initialize NNPACK! Reason: Unsupported hardware`

A10: 在此存储库的上下文中,可以安全地忽略此警告。

Q11: TracerWarning: Converting a tensor to a Python boolean might cause the trace to be incorrect

A11: 在此存储库的上下文中,可以安全地忽略此警告。

Q12: 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 了解解决方案。

BibTeX 引用

@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}
}