开源项目演示通过量化、内存优化等手段在极限资源约束下运行图像生成模型的实现细节。
2025 年 9 月 28 日:新增了该库的 Python 和 C# 绑定!
2025 年 4 月 15 日:新增了 OpenAI Whisper 的 WebAssembly 演示(在浏览器中运行)。
2024 年 9 月 19 日:新增了该库的 WebAssembly 支持!YOLOv8 目标检测模型的演示(在浏览器中运行)。
2024 年 1 月 14 日:新增了支持初步 GPU 加速的 LLM 聊天应用(TinyLlama 1.1B 和 Mistral 7B)!更多信息见此处。
2023 年 12 月 14 日:新增了对 Stable Diffusion XL Turbo 1.0 的支持!(感谢 @AeroX2)
2023 年 10 月 3 日:新增了对 Stable Diffusion XL 1.0 Base 的支持!
Stable Diffusion XL 1.0 Base
Stable Diffusion XL Turbo 1.0
TinyLlama 1.1B 和 Mistral 7B
YOLOv8(在浏览器中运行)
OpenAI Whisper(在浏览器中运行)
OnnxStream 的特性
注意力切片与量化
如何构建(Linux/Mac/Windows/Termux/FreeBSD)
如何转换 SD 1.5 模型
这项挑战是在 Raspberry Pi Zero 2 上运行 Stable Diffusion 1.5。Stable Diffusion 1.5 包含一个拥有近 10 亿参数的大型 Transformer 模型,而 Raspberry Pi Zero 2 是一台仅配备 512MB RAM 的微型计算机。运行过程中不能增加交换空间,也不能将中间结果卸载到磁盘。Stable Diffusion 1.5 通常建议至少配备 8GB RAM/VRAM。
通常,主流机器学习框架和库都专注于尽可能降低推理延迟和/或最大化吞吐量,而这一切都以增加 RAM 占用为代价。因此,我决定编写一个极其小巧且易于修改的推理库,专门致力于最大限度降低内存消耗:OnnxStream。
OnnxStream 的核心理念是将推理引擎与负责提供模型权重的组件解耦,后者是一个派生自 WeightsProvider 的类。WeightsProvider 的特化实现可以采用任意方式加载、缓存和预取模型参数。例如,自定义 WeightsProvider 可以选择直接从 HTTP 服务器下载数据,而无需从磁盘加载或向磁盘写入任何内容(这也是“OnnxStream”中“Stream”一词的由来)。目前提供三种默认的 WeightsProvider:DiskNoCache、DiskPrefetch 和 Ram。
OnnxStream 的内存消耗甚至可以比 OnnxRuntime 低 55 倍,而延迟仅增加 50% 到 200%(在 CPU 和性能良好的 SSD 上,以 SD 1.5 的 UNET 为参照——请参阅下文的性能部分)。
这些图像由本仓库中基于 OnnxStream 的 Stable Diffusion 示例实现生成,分别使用了不同精度的 VAE 解码器。VAE 解码器是 Stable Diffusion 1.5 中唯一无法以单精度或半精度装入 Raspberry Pi Zero 2 RAM 的模型。这是由模型中的残差连接以及非常大的张量和卷积造成的。唯一的解决方案是静态量化(8 位)。第三张图像由我的 RPI Zero 2 生成,耗时约 3 小时 1.5 小时(编译时使用 MAX_SPEED 选项)。作为对比,第一张图像在我的 PC 上使用 RPI Zero 2 生成的相同潜变量生成:
采用 W16A16 精度的 VAE 解码器:
采用 W8A32 精度的 VAE 解码器:
采用 W8A8 精度的 VAE 解码器,由我的 RPI Zero 2 在约 3 小时 1.5 小时内生成(编译时使用 MAX_SPEED 选项):
OnnxStream 的 Stable Diffusion 示例实现现在支持 SDXL 1.0(不含 Refiner)。ONNX 文件导出自 Hugging Face Diffusers 库(0.19.3 版)的 SDXL 1.0 实现。
SDXL 1.0 的计算开销明显高于 SD 1.5。最显著的区别是,它可以生成 1024x1024 图像,而不是 512x512 图像。举例来说,在我配备 32GB RAM 的 12 核 PC 上,使用 Hugging Face Diffusers 生成一张 10 步图像需要 26 分钟。SDXL 通常建议至少配备 12GB VRAM。
OnnxStream 可以在不足 300MB RAM 的情况下运行 SDXL 1.0,因此能够在 RPI Zero 2 上轻松运行,无需增加交换空间,推理期间也无需向磁盘写入任何内容。在我的 RPI Zero 2 上生成一张 10 步图像大约需要 11 小时。
SDXL 1.0 使用了与 SD 1.5 相同的一组优化,但存在以下差异。
对于 UNET 模型,为了让它能在 RPI Zero 2 上以不足 300MB RAM 运行,采用了 UINT8 动态量化,但仅限于特定的大型中间张量子集。
VAE 解码器的情况比 SD 1.5 更复杂。SDXL 1.0 的 VAE 解码器大小是 SD 1.5 的 4 倍,使用 OnnxStream 以 FP32 精度运行时会消耗 4.4GB RAM。
对于 SD 1.5,VAE 解码器采用静态量化(UINT8 精度),这足以将 RAM 消耗降低到 260MB。相比之下,SDXL 1.0 的 VAE 解码器使用 FP16 算术运算时会发生溢出,并且其激活值的数值范围过大,无法通过 UINT8 量化获得质量良好的图像。
因此,我们面对的是一个会消耗 4.4GB RAM、无法以 FP16 精度运行、也无法量化为 UINT8 精度的模型。(注意:FP16 问题至少存在一种解决方案,但我没有进一步研究,因为即使以 FP16 精度运行 VAE 解码器,总内存消耗也只是减半,模型最终仍会消耗 2.2GB 而不是 4.4GB,这对于 RPI Zero 2 来说依然高得多)
解决方案的灵感来自 Hugging Face Diffusers 库中 VAE 解码器的实现,即使用分块解码。最终结果与完整解码器解码出的图像完全无法区分,而通过这种方式,可以将 RAM 消耗从 4.4GB 降低到 298MB!
这个思路很简单。扩散过程的结果是一个形状为 (1,4,128,128) 的张量。将该张量拆分为 5x5(即 25 个)形状为 (1,4,32,32) 的重叠张量,然后分别解码这些张量。每个张量与其左侧和上方的图块均有 25% 的重叠。解码结果是一个形状为 (1,3,256,256) 的张量,随后以适当方式混合到最终图像中。
例如,下面是由分块解码器生成、并在代码中手动关闭混合的图像。可以清楚地看到图像中的各个图块:
下面则是开启混合后的同一张图像。这就是最终结果:
这是另一张由我的 RPI Zero 2 在约 11 小时内生成的图像:(10 步,Euler Ancestral)
对 SDXL Turbo 的支持由热心的 @AeroX2 贡献。
SDXL 与 SDXL Turbo 的主要区别在于,Turbo 版本生成的是 512x512 图像,而不是 1024x1024 图像,但所需步数要少得多。即使仅执行一步,也能获得质量良好的图像!
为了在 RPI Zero 2 上运行 SDXL Turbo,无需在 SDXL 1.0 的基础上进行额外优化。SDXL 和 SDXL Turbo 使用相同的文本编码器和 VAE 解码器:要将内存消耗维持在 300MB 以下,必须使用分块解码。
这张图像由我的 Raspberry PI Zero 2 在 29 分钟内生成(1 步):
这张图像是 3 步生成的示例,在我的 RPI Zero 2 上耗时 50 分钟。其质量与 1 步生成的图像相同:
关于在 OnnxStream 中运行 SDXL 1.0 和 SDXL Turbo 时不同步数的对比,可在该模型的模型卡中查看(由 @AeroX2 提供)。
推理引擎与 WeightsProvider 解耦
WeightsProvider 可以是 DiskNoCache、DiskPrefetch、Ram 或自定义实现
动态量化(8 位无符号、非对称、百分位数)
静态量化(W8A8 无符号、非对称、百分位数)
轻松校准量化模型
支持 FP16(使用或不使用 FP16 算术运算)
实现了 41 个 ONNX 算子(最常用的算子)
运算按顺序执行,但几乎所有算子都支持多线程
单个实现文件 + 头文件
XNNPACK 调用封装在 XnnPack 类中(便于将来替换)
通过 cuBLAS 提供初步 GPU 支持(仅支持 FP16 和 FP32,且仅用于 LLM 应用)
提供 WebAssembly 构建版本(支持多线程和 SIMD;演示见此处)
OnnxStream 的部分(加速)基础运算依赖 XNNPACK:MatMul、Convolution、逐元素 Add/Sub/Mul/Div、Sigmoid、Softmax、MaxPool 和 Transpose。
Stable Diffusion 1.5 由三个模型组成:文本编码器(672 个运算、1.23 亿个参数)、UNET 模型(2050 个运算、8.54 亿个参数)以及 VAE 解码器(276 个运算、4900 万个参数)。假设批次大小等于 1,使用 10 步完成一次完整图像生成——通过 Euler Ancestral 调度器可以获得良好结果——需要运行文本编码器 2 次、UNET 模型 20 次(即 2*10)以及 VAE 解码器 1 次。
下表展示了 Stable Diffusion 1.5 三个模型各自的推理时间及其内存消耗(即 Windows 中的峰值工作集大小,或 Linux 中的最大常驻集大小)。
对于 UNET 模型(以 FP16 精度运行,并在 OnnxStream 中启用 FP16 算术运算时),OnnxStream 的内存占用甚至可以比 OnnxRuntime 低 55 倍,代价是延迟增加 50% 到 200%。
OnnxRuntime 的第一次运行属于预热推理,因为它的 InferenceSession 会在第一次运行之前创建,并在后续所有运行中复用。OnnxStream 不存在这种预热机制,因为从设计上讲,它采用的是纯 eager 模式(不过,后续运行可以受益于操作系统对权重文件的缓存)。
目前,OnnxStream 不支持 batch size != 1 的输入;而 OnnxRuntime 支持这一点,并且在运行 UNET 模型时,可以使用 batch size = 2 来大幅加速整个扩散过程。
在我的测试中,更改 OnnxRuntime 的 SessionOptions(例如 EnableCpuMemArena 和 ExecutionMode)并不会对结果产生显著影响。
无论是内存占用还是推理时间,OnnxRuntime 的性能都与 NCNN(我评估的另一个框架)非常相似。如果有需要,我将来会加入 NCNN 的基准测试结果。
测试是在我的开发机上运行的:Windows Server 2019、16GB RAM、8750H CPU(AVX2)、970 EVO Plus SSD,以及 VMWare 中的 8 个虚拟核心。
运行 UNET 模型时使用“注意力切片”(attention slicing),以及对 VAE 解码器使用 W8A8 量化,是将内存占用降低到能够在 RPI Zero 2 上运行的关键。
虽然互联网上有大量关于神经网络量化的信息,但几乎找不到有关“注意力切片”的资料。它的思路很简单:目标是在计算 UNET 模型中各个多头注意力的缩放点积注意力时,避免将完整的 Q @ K^T 矩阵实体化。在 UNET 模型的注意力头数量为 8 时,Q 的形状为 (8,4096,40),而 K^T 的形状为 (8,40,4096):因此,第一个 MatMul 的结果最终形状为 (8,4096,4096),这是一个大小为 512MB 的张量(采用 FP32 精度时):
解决方案是沿垂直方向拆分 Q,然后对 Q 的每个分块正常执行注意力运算。Q_sliced 的形状为 (1,x,40),其中 x 等于 4096(在本例中)除以 onnxstream::Model::m_attention_fused_ops_parts(其默认值为 2,但可以自定义)。这个简单的技巧可以将 UNET 模型的总体内存占用从 1.1GB 降低到 300MB(模型以 FP32 精度运行时)。另一种可行且无疑更高效的方案是使用 FlashAttention,但 FlashAttention 要求为每种受支持的架构(AVX、NEON 等)编写自定义内核,在我们的场景中这意味着绕过 XnnPack。
以下代码可以运行定义在 path_to_model_folder/model.txt 中的模型:(所有模型操作都定义在 model.txt 文本文件中;OnnxStream 期望在同一文件夹中找到所有权重文件,这些权重以一系列 .bin 文件的形式存储)
#include "onnxstream.h"
using namespace onnxstream;
int main()
{
Model model;
//
// Optional parameters that can be set on the Model object:
//
// model.set_weights_provider( ... ); // specifies a different weights provider (default is DiskPrefetchWeightsProvider)
// model.read_range_data( ... ); // reads a range data file (which contains the clipping ranges of the activations for a quantized model)
// model.write_range_data( ... ); // writes a range data file (useful after calibration)
// model.m_range_data_calibrate = true; // calibrates the model
// model.m_use_fp16_arithmetic = true; // uses FP16 arithmetic during inference (useful if weights are in FP16 precision)
// model.m_use_uint8_arithmetic = true; // uses UINT8 arithmetic during inference
// model.m_use_uint8_qdq = true; // uses UINT8 dynamic quantization (can reduce memory consumption of some models)
// model.m_fuse_ops_in_attention = true; // enables attention slicing
// model.m_attention_fused_ops_parts = ... ; // see the "Attention Slicing" section above
//
model.read_file("path_to_model_folder/model.txt");
tensor_vector<float> data;
... // fill the tensor_vector with the tensor data. "tensor_vector" is just an alias to a std::vector with a custom allocator.
Tensor t;
t.m_name = "input";
t.m_shape = { 1, 4, 64, 64 };
t.set_vector(std::move(data));
model.push_tensor(std::move(t));
model.run();
auto& result = model.m_data[0].get_vector<float>();
... // process the result: "result" is a reference to the first result of the inference (a tensor_vector<float> as well).
return 0;
}
model.txt 文件以 ASCII 格式包含所有模型操作,这些操作由原始 ONNX 文件导出。每一行对应一个操作:例如,下面这一行表示量化模型中的一次卷积:
Conv_4:Conv*input:input_2E_1(1,4,64,64);post_5F_quant_5F_conv_2E_weight_nchw.bin(uint8[0.0035054587850383684,134]:4,4,1,1);post_5F_quant_5F_conv_2E_bias.bin(float32:4)*output:input(1,4,64,64)*dilations:1,1;group:1;kernel_shape:1,1;pads:0,0,0,0;strides:1,1
为了从 ONNX 文件中导出供 OnnxStream 使用的 model.txt 文件及其权重(以一系列 .bin 文件的形式存储),项目提供了一个仅包含单个单元格的 notebook(onnx2txt.ipynb)。
将 Pytorch nn.Module(在我们的场景中)导出为供 OnnxStream 使用的 ONNX 模型时,必须考虑以下几点:
调用 torch.onnx.export 时,dynamic_axes 应留空,因为 OnnxStream 不支持具有动态形状的输入。
强烈建议先对导出的 ONNX 文件运行优秀的 ONNX Simplifier,然后再将其转换为 model.txt 文件。
仅限 Linux(及 Termux):需要安装以下软件包:build-essential git cmake python3(这是 Ubuntu 和 Termux 中的软件包名称,其他发行版中的名称可能有所不同)。
仅限 Windows:启动以下命令提示符:Visual Studio Tools > x64 Native Tools Command Prompt。
仅限 Mac:请确保安装 cmake:brew install cmake。
在撰写本文时,XNNPACK 不支持在 FreeBSD 上构建。不过,只需对其 CMake 文件做少量修改,就可以在 FreeBSD 上构建。
不兼容问题主要涉及以下两点:
两个变量 CMAKE_SYSTEM_NAME 和 CMAKE_SYSTEM_PROCESSOR。
两个变量 CMAKE_SYSTEM_NAME 和 CMAKE_SYSTEM_PROCESSOR。
cpuinfo 依赖项:该项目最近才加入 FreeBSD 支持,因此需要让 XNNPACK 下载一个更新的版本。
cpuinfo 依赖项:该项目最近才加入 FreeBSD 支持,因此需要让 XNNPACK 下载一个更新的版本。
例如,要在 FreeBSD 上成功构建 XNNPACK 的 1c8ee1b68f3a3e0847ec3c53c186c5909fa3fbd3 提交版本,需要进行以下修改:
diff --git a/CMakeLists.txt b/CMakeLists.txt
index d33268bd9..4efd58b86 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -88,7 +88,7 @@ ELSEIF(CMAKE_GENERATOR MATCHES "^Visual Studio " AND CMAKE_GENERATOR_PLATFORM)
ENDIF()
ELSEIF(CMAKE_SYSTEM_PROCESSOR MATCHES "^i[3-7]86$")
SET(XNNPACK_TARGET_PROCESSOR "x86")
-ELSEIF(CMAKE_SYSTEM_PROCESSOR STREQUAL "AMD64")
+ELSEIF(CMAKE_SYSTEM_PROCESSOR STREQUAL "AMD64" OR CMAKE_SYSTEM_PROCESSOR STREQUAL "amd64")
SET(XNNPACK_TARGET_PROCESSOR "x86_64")
ELSEIF(CMAKE_SYSTEM_PROCESSOR MATCHES "^armv[5-8]")
SET(XNNPACK_TARGET_PROCESSOR "arm")
@@ -249,7 +249,7 @@ ENDIF()
# ---[ Build flags
IF(NOT CMAKE_SYSTEM_NAME)
MESSAGE(FATAL_ERROR "CMAKE_SYSTEM_NAME not defined")
-ELSEIF(NOT CMAKE_SYSTEM_NAME MATCHES "^(Android|Darwin|iOS|Linux|Windows|CYGWIN|MSYS|QURT)$")
+ELSEIF(NOT CMAKE_SYSTEM_NAME MATCHES "^(Android|Darwin|iOS|Linux|Windows|CYGWIN|MSYS|QURT|FreeBSD)$")
MESSAGE(FATAL_ERROR "Unrecognized CMAKE_SYSTEM_NAME value \"${CMAKE_SYSTEM_NAME}\"")
ENDIF()
IF(CMAKE_SYSTEM_NAME MATCHES "Windows")
diff --git a/cmake/DownloadCpuinfo.cmake b/cmake/DownloadCpuinfo.cmake
index 01e4b9806..4dfff8f6f 100644
--- a/cmake/DownloadCpuinfo.cmake
+++ b/cmake/DownloadCpuinfo.cmake
@@ -17,8 +17,8 @@ ENDIF()
INCLUDE(ExternalProject)
ExternalProject_Add(cpuinfo
- URL https://github.com/pytorch/cpuinfo/archive/3c8b1533ac03dd6531ab6e7b9245d488f13a82a5.zip
- URL_HASH SHA256=5d7f00693e97bd7525753de94be63f99b0490ae6855df168f5a6b2cfc452e49e
+ URL https://github.com/pytorch/cpuinfo/archive/cebb0933058d7f181c979afd50601dc311e1bf8c.zip
+ URL_HASH SHA256=52e0ffd7998d8cb3a927d8a6e1145763744d866d2be09c4eccea27fc157b6bb0
SOURCE_DIR "${CMAKE_BINARY_DIR}/cpuinfo-source"
BINARY_DIR "${CMAKE_BINARY_DIR}/cpuinfo"
CONFIGURE_COMMAND ""
以下命令将构建 Stable Diffusion 示例(XNNPACK 会自动下载):
git clone https://github.com/vitoplantamura/OnnxStream.git
cd OnnxStream/src
mkdir build
cd build
cmake ..
cmake --build . --config Release
重要提示:MAX_SPEED 选项(默认开启,但可以在第一次调用 cmake 时、.. 之前添加 -DMAX_SPEED=OFF 将其关闭)在 Windows 上可将性能提升约 10%,而在 Raspberry Pi 上可提升 50% 以上。此选项会在构建期间消耗更多内存,并且生成的可执行文件可能无法运行(在我的测试中,Termux 就出现了这种情况)。因此,如果遇到问题,首先应尝试将 MAX_SPEED 设置为 OFF。
现在可以运行 Stable Diffusion 示例了。
对于 Stable Diffusion 1.5,可以在此处下载权重(约 2GB)。
git lfs install
git clone --depth=1 https://huggingface.co/vitoplantamura/stable-diffusion-1.5-onnxstream
对于 Stable Diffusion XL 1.0 Base,可以在此处下载权重(约 8GB):
git lfs install
git clone --depth=1 https://huggingface.co/vitoplantamura/stable-diffusion-xl-base-1.0-onnxstream
对于 Stable Diffusion XL Turbo 1.0,可以在此处下载权重(约 8GB):
git lfs install
git clone --depth=1 https://huggingface.co/vitoplantamura/stable-diffusion-xl-turbo-1.0-anyshape-onnxstream
以下是 Stable Diffusion 示例的命令行选项:
--xl 运行 Stable Diffusion XL 1.0,而不是 Stable Diffusion 1.5。
--turbo 运行 Stable Diffusion Turbo 1.0,而不是 Stable Diffusion 1.5。
--models-path 设置包含 Stable Diffusion 模型的文件夹。
--ops-printf 在推理期间,将当前操作写入 stdout。
--output 设置输出 PNG 文件。
--decode-latents 跳过扩散过程,并解码指定的潜变量文件。
--prompt 设置正向提示词。
--neg-prompt 设置负向提示词。
--num 设置要生成的图像数量。默认值为 1。
--steps 设置扩散步数。默认值为 10。
--seed 设置随机种子。
--save-latents 扩散完成后,将潜变量保存到指定文件中。
--decoder-calibrate (仅限 SD 1.5)校准量化版 VAE 解码器。
--not-tiled (仅限 SDXL 1.0 和 TURBO)不使用分块 VAE 解码器。
--res (仅限 TURBO)设置输出 PNG 文件的分辨率。默认值为 "512x512"。
--ram 将整个 UNET 模型加载到 RAM 中以加快推理速度。同时设置 --not-tiled。
--download A[uto] / F[orce] / N[ever]:自动/强制/永不(重新)下载当前模型。
--curl-parallel 设置使用 CURL 并行下载的数量。默认值为 16。
--rpi A[uto] / F[orce] / N[ot]:自动/强制/不配置模型以在 Raspberry Pi 上运行。
--rpi-lowmem 配置模型以在 Raspberry Pi Zero 2 上运行。
--threads 设置线程数;负值表示使用(核心数 - N)个线程。
--preview-steps 以低分辨率保存每个扩散步骤。
--preview-steps-x8 将预览图放大到完整分辨率。
--decode-steps 以完整分辨率解码并保存每个扩散步骤。
--embed-parameters 将生成参数(例如模型路径)存储在图像注释中。
--sampler 选择采样方法。
你可能感兴趣的选项:--xl、--turbo、--prompt、--steps、--rpi。
本指南旨在帮助你转换自定义 Stable Diffusion 模型,以便与 OnnxStream 配合使用。无论你使用的是 .safetensors 还是 .onnx,本指南都涵盖了相应的方法。
Linux 环境(已在 Ubuntu 上测试,Windows WSL 也可使用)
交换空间(所需容量因采用的方法而异)
AUTO1111 的 Stable Diffusion 实现使用了 Einsum 等操作,而 OnnxStream(目前)尚不支持这些操作。因此,建议使用兼容性更好的 Hugging Face 实现。
可选:将 .safetensors 转换为 ONNX
如果你使用的是 .safetensors 文件,可以使用此 GitHub 仓库中提供的工具将其转换为 .onnx。
不过,建议采用上文“选项 A”一节中的方法。
from diffusers import StableDiffusionPipeline
import torch
pipe = StableDiffusionPipeline.from_single_file("https://huggingface.co/YourUsername/YourModel/blob/main/Model.safetensors")
dummy_input = (torch.randn(1, 4, 64, 64), torch.randn(1), torch.randn(1, 77, 768))
input_names = ["sample", "timestep", "encoder_hidden_states"]
output_names = ["out_sample"]
torch.onnx.export(pipe.unet, dummy_input, "/path/to/save/unet_temp.onnx", verbose=False, input_names=input_names, output_names=output_names, opset_version=14, do_constant_folding=True, export_params=True)
python -m onnxruntime.tools.make_dynamic_shape_fixed --input_name sample --input_shape 1,4,64,64 model.onnx model_fixed1.onnx
python -m onnxruntime.tools.make_dynamic_shape_fixed --input_name timestep --input_shape 1 model_fixed1.onnx model_fixed2.onnx
python -m onnxruntime.tools.make_dynamic_shape_fixed --input_name encoder_hidden_states --input_shape 1,77,768 model_fixed2.onnx model_fixed3.onnx
Vito 注:只需采用上文“选项 A”中所述的方法即可实现这一点,该方法仍然是推荐方案。如果你的起点已经是 ONNX 文件,将输入形状固定下来可能会很有用。
运行 ONNX Simplifier
python -m onnx_simplifier model_fixed3.onnx model_simplified.onnx
对于大型模型,使用简化器时可能会遇到问题。在这种情况下,此工具有时可以提供帮助:https://github.com/luchangli03/onnxsim_large_model
如果模型是从 Hugging Face 导出的,则需要大约 100GB 的交换空间。
如果手动固定了输入形状,16GB RAM 应该就足够了。
此过程可能需要一些时间,请耐心等待。
最终步骤与运行模型
从 onnx2txt 获得最终模型后,将其移入标准 SD 1.5 模型的 unet_fp16 文件夹。该标准模型可在 OnnxStream 的 Windows 发行版中找到。
运行模型的命令可能如下所示:
./sd --models-path ./Converted/ --prompt "space landscape" --steps 28 --rpi
关于“Shape”运算符的说明
如果在 Onnx Simplifier 的输出或 onnx2txt.ipynb 中看到“Shape”运算符,则表明 Onnx Simplifier 可能未按预期工作。此问题通常并非由 Onnx Simplifier 本身引起,而是由 Onnx 的 Shape Inference 引起。
在这种情况下,另一种选择是修改 torch.onnx.ex 的参数并重新导出模型。