mlx-vlm 中的 ERNIE-Image 本地推理:从权重转换、量化到文生图与 SDEdit 编辑的 MLX 实践指南
【免费下载链接】mlx-vlmMLX-VLM is a package for inference and fine-tuning of Vision Language Models (VLMs) on your Mac using MLX.项目地址: https://gitcode.com/GitHub_Trending/ml/mlx-vlm
本文基于 mlx-vlm 仓库中 ERNIE-Image 模型模块文档 及其配套源码撰写。读完你可以掌握:如何把百度的 ERNIE-Image(Turbo / Base)检查点转换为 MLX 格式并量化、如何用mlx_vlm.generate命令行一键文生图、如何用 Python API 调用generate_image完成生成与图生图(img2img)编辑,以及底层 flow-matching 调度器、单流 DiT Transformer 和 SDEdit 潜空间变换的实际实现原理。
一、ERNIE-Image 是什么:mlx-vlm 中的模型构成
ERNIE-Image 是百度的文生图模型家族。mlx-vlm 在 Apple 芯片(MLX 框架)上用以下四个组件完整复现了它的推理链路:
- Ministral-3 文本编码器:生成文本条件嵌入(对应 text_encoder.py);
- ERNIE-Image 扩散 Transformer(单流 DiT):图像主干网络(对应 transformer.py);
- FLUX.2 VAE:潜空间编解码器,复用 flux2 VAE 实现,通过 weights.py 中的
load_vae加载; - flow-matching 调度器:即 scheduler.py 中的
ErnieImageFlowMatchScheduler,对应官方FlowMatchEulerDiscreteScheduler(shift=4.0)。
此外模型还支持可选的提示词增强(prompt enhancer),将短提示词扩写为完整场景描述。
两个变体与推荐参数
仓库在 config.py 中用VARIANTS字典固化了两个变体的元数据,与官方文档一致:
| Hugging Face 模型 | 变体 | 文生图 | 图生图 | 推荐设置 |
|---|---|---|---|---|
baidu/ERNIE-Image-Turbo | Turbo | 是 | 是 | 8 步,guidance 1 |
baidu/ERNIE-Image | Base | 是 | 是 | 50 步,guidance 4 |
关键区别在于 classifier-free guidance(CFG):Base 使用 CFG(guidance 4.0),Turbo 在其推荐值 guidance 1.0 下关闭 CFG。源码中这一逻辑非常直接——pipeline 用do_cfg = guidance > 1.0判断是否拼接负向提示词并做条件/无条件预测插值(见 pipeline.py 与_denoise中的unconditional + guidance * (conditional - unconditional))。
另外,宽度/高度必须是16 的正倍数,由 config.py 的validate_dimensions强制校验:小于 16 或不是 16 的倍数会直接抛出ValueError。
二、转换检查点到 MLX 格式(convert)
转换入口是 convert.py,可通过python -m mlx_vlm.models.ernie_image.convert调用。
2.1 转换为 BF16
python -m mlx_vlm.models.ernie_image.convert \ --hf-path baidu/ERNIE-Image-Turbo \ --mlx-path ./ERNIE-Image-Turbo-MLX默认以 BF16(mx.bfloat16)保存全部组件。
2.2 转换并量化
python -m mlx_vlm.models.ernie_image.convert \ --hf-path baidu/ERNIE-Image-Turbo \ --mlx-path ./ERNIE-Image-Turbo-MLX-MXFP8 \ --quantize \ --q-mode mxfp8支持的量化模式为affine、mxfp4、nvfp4、mxfp8。当省略--q-bits与--q-group-size时,会使用各模式的默认值——这些默认值定义在 convert.py 的_MODE_DEFAULTS中:
| 模式 | 默认 group_size | 默认 bits |
|---|---|---|
affine | 64 | 4 |
mxfp4 | 32 | 4 |
nvfp4 | 16 | 4 |
mxfp8 | 32 | 8 |
从源码看有两点值得注意:
- 非 affine 模式锁定默认值:
_quantization_parameters会校验,若mxfp4/nvfp4/mxfp8的 group_size/bits 与默认值不一致则报错,因为这些 block 格式对缩放布局有硬性要求; - 分组件选择性量化:转换按计划依次处理
text_encoder、transformer(仅layers.*前缀的模块)、vae,若源检查点存在pe/*.safetensors还会处理pe(提示词增强器)。每组件都要求有权重形状可被 group_size 整除才量化(见_quantize_component的compatible谓词)。
2.3 转换产物的结构
convert_ernie_image会把每个组件分片保存为 safetensors(多分片时生成model-xxxxx-of-yyyyy.safetensors与索引文件),并写入统一的元数据文件mlx_ernie_image.json(记录 variant、组件量化配置、supports_img2img等),同时用_write_missing_configs补写model_index.json、各组件config.json。这些配置缺省值本身也揭示了架构参数,例如 scheduler 默认num_train_timesteps=1000, shift=4.0,VAE 为AutoencoderKLFlux2(latent_channels=32, patch_size=[2,2])。
一个细节:如果源 VAE 缺少 encoder 权重,转换时只告警并产出仅支持文生图的检查点(supports_img2img=False);加载 img2img 时 weights.py 的_require_vae_encoder_weights会明确报错提示“缺少 VAE encoder 权重,请从完整官方检查点重新转换”。
三、CLI:文生图与图像编辑
3.1 Turbo 文生图
mlx_vlm.generate \ --output-modality image \ --model baidu/ERNIE-Image-Turbo \ --prompt "A red panda serving tea in a moonlit bamboo forest" \ --size 1024x1024 \ --steps 8 \ --guidance 1 \ --seed 42 \ --output outputs/ernie-image-turbo.png3.2 Base 文生图(带负向提示词)
mlx_vlm.generate \ --output-modality image \ --model baidu/ERNIE-Image \ --prompt "A cinematic photograph of a fox walking through fresh snow" \ --size 1024x1024 \ --steps 50 \ --guidance 4 \ --seed 42 \ --output outputs/ernie-image.png \ --gen-kwargs '{"negative_prompt":"blurry, low quality"}'Base 使用 CFG,因此可以通过negative_prompt进一步约束生成质量;而 Turbo 在 guidance=1 时不启用 CFG,负向提示词不会参与条件组合。
3.3 图像编辑(SDEdit 风格 img2img)
mlx_vlm.generate \ --output-modality image \ --task edit \ --model baidu/ERNIE-Image-Turbo \ --image input/fox.png \ --prompt "Give the fox a red Santa hat" \ --size 1024x1024 \ --steps 8 \ --guidance 3 \ --seed 42 \ --output outputs/ernie-image-edit.png \ --gen-kwargs '{"image_strength":0.7}'编辑功能的几个关键约定(均有源码佐证):
- 只接受恰好一张源图:model.py 中
edit()对len(request.image_paths) != 1直接抛错; - 它是通用 SDEdit 式重扩散,而非原生指令编辑(
native_instruction_edit: False),因此需要 guidance > 1(例如 3)才能让请求的改动强有力地施加。Turbo 的编辑默认 guidance 在 config.py 中被单独设为edit_guidance=3.0(生成推荐值保持 1.0 不变),Base 的编辑默认值沿用其生成 guidance; image_strength(别名strength)必须在 (0, 1] 区间,值越大对源图重扩散越多;默认 0.6(见 model.py 的取值与 pipeline.py 的区间校验);- 提示词增强在编辑任务默认关闭:因为增强器把提示词改写成独立场景描述,从未看过源图,会稀释编辑意图。如需重新启用,加载模型时传
use_prompt_enhancer=True。
从 SDEdit 的实现看(pipeline.py 的edit_array):源图先经 VAE encoder 编码并按 VAE 的running_mean/running_var归一化,然后按sigma与噪声线性混合得到起点潜变量,实际去噪步数取round(steps * image_strength),即 strength 越高、跳过的“保留源图”步数越少、重扩散程度越大。
四、Python API 用法
4.1 文生图
from mlx_vlm.generate.image import generate_image, load_image_generation_model model = load_image_generation_model("./ERNIE-Image-Turbo-MLX") result = generate_image( model, "A red panda serving tea in a moonlit bamboo forest", seed=42, steps=8, width=1024, height=1024, guidance=1.0, output_path="outputs/ernie-image-turbo.png", ) print(result.array.shape, result.path)4.2 图像编辑
from mlx_vlm.generate.image import generate_image, load_image_model model = load_image_model("./ERNIE-Image-Turbo-MLX", task="edit") result = generate_image( model, "Give the fox a red Santa hat", task="edit", image_paths=("input/fox.png",), seed=42, steps=8, guidance=3.0, image_strength=0.7, output_path="outputs/ernie-image-edit.png", ) print(result.array.shape, result.path)4.3 结果对象
Python 端的主要输出是一个已求值的mx.array(H×W×3 的 uint8 图像),对应ImageGenerationResult(定义于 generate/image.py)。除落盘外,还可以稍后用result.save(path)保存,或调用result.to_b64_json()编码为 base64 PNG。结果对象还携带 seed、width/height、steps、variant、guidance、prompt_tokens、peak_memory(GB)等元数据,便于脚本化验证。
五、源码级实现剖析
5.1 Pipeline 与内存管理
ErnieImagePipeline 按“文本编码 → 去噪 → VAE 解码”三阶段组织,并对内存做了显式治理(ErnieImageRuntimeConfig):
evict_text_encoder=True(默认):提示词编码完成后立即释放文本编码器并mx.clear_cache();prompt_cache_size=2:用 LRU 缓存已编码的提示词 hidden states,相同提示词重复生成时跳过编码;evict_transformer:解码完成后可选释放 Transformer 与 VAE。
生成主流程generate_array的要点:
- 校验尺寸(16 倍数)、
steps >= 1、guidance >= 0; - 若启用提示词增强,先扩写提示词并记录
last_revised_prompt; - 潜变量形状为
(1, 128, H//16, W//16)(128 通道、16 倍下采样),以seed初始化的mx.random.key采样,dtype 为 bfloat16; - 调度器
ErnieImageFlowMatchScheduler(num_inference_steps=steps)逐步step更新潜变量,最后vae.decode_packed_latents解码并映射回 [0, 255] uint8。
5.2 flow-matching 调度器
scheduler.py 是一个紧凑的 Euler flow-matching 实现:sigma 从linspace(1, 0, steps+1)出发,套用官方 shift 变换shift * s / (1 + (shift-1) * s)(默认 shift=4.0),再乘以训练步数 1000 得到 timestep;step用相邻 sigma 差值乘以模型输出做欧拉推进。仓库测试 test_ernie_image.py 中有test_ernie_scheduler_matches_official_static_shift,用于验证该调度与官方实现一致。
5.3 单流 DiT Transformer
ErnieImageTransformer 把图像 token 与文本 token拼接在一条序列中做自注意力(single-stream 设计),默认配置(config.py)为:hidden 4096、FFN 12288、36 层、32 头、输入/输出 128 通道、文本输入维 3072。位置编码采用三轴 RoPE:rope_axes_dim=(32, 48, 48)、rope_theta=256——图像 token 的三轴分别是“文本序列长度偏移、y 网格、x 网格”,文本 token 则只用一维索引(见__call__中 position_ids 的构造)。条件注入通过 AdaLN 调制(adaln_modulation线性层输出 6 组 shift/scale/gate)与 final norm 实现,注意力使用mx.core.fast.scaled_dot_product_attention与 QK-RMSNorm。
5.4 文本编码器
text_encoder.py 中的ErnieImageTextEncoder是一个 Ministral-3 架构:hidden 3072、26 层、32 Q 头 / 8 KV 头(GQA)、head_dim 128、YaRN 长上下文 RoPE 参数(factor 16.0)。注意其__call__默认取self.layers[:-1](丢弃最后一层)而不做最终 norm——这是该模型对扩散条件嵌入的实际取法。分词器从本地tokenizer/目录加载(local_files_only=True),默认截断到max_sequence_length=2048。
5.5 变体识别与加载入口
model.py 提供ErnieImageGenerationModel/ErnieImageEditModel与load/load_edit两个入口。from_model_id接受download、token、revision、evict_text_encoder、evict_transformer、max_sequence_length、prompt_cache_size、use_prompt_enhancer等 kwargs,默认evict_text_encoder=True、max_sequence_length=2048、prompt_cache_size=2。use_prompt_enhancer传None时为自动模式:仅当本地模型快照同时具备pe/*.safetensors与pe_tokenizer/tokenizer.json且任务为文生图时启用(见_should_enhance_prompt)。本地目录的变体识别优先读mlx_ernie_image.json元数据,其次按目录名中的turbo关键字判断(config.py 的variant_from_local_path)。
六、验证与测试
仓库为 ERNIE-Image 提供了专门的测试文件 mlx_vlm/tests/test_ernie_image.py,覆盖:
- 变体元数据(
("baidu/ERNIE-Image", 50, 4.0)、("baidu/ERNIE-Image-Turbo", 8, 1.0))与本地元数据优先的识别逻辑; - 尺寸必须为 16 正倍数的边界校验(
test_ernie_dimensions_must_be_positive_multiples_of_16); - 调度器与官方 shift 行为一致性、RoPE 混合约定一致性;
- 微型 Transformer 前向、权重清洗/布局转换(PyTorch NCHW 与 MLX NHWC 卷积权重)、严格量化加载(
test_ernie_strict_quantized_loading_modes); - 编辑任务对 img2img strength 的前向传递(
test_ernie_edit_model_forwards_img2img_strength)。
这些测试表明:上述 CLI 与 Python 用法对应的默认参数(8/50 步、1/4 guidance、strength 0.6 默认值等)均有回归保障。
七、适用前提与限制小结
- 本模块面向MLX 运行时(Apple 芯片);转换脚本同样在 MLX 环境中执行;
- 尺寸必须是 16 的正倍数;编辑任务的分辨率在省略 width/height 时由
_load_edit_dimension自动对齐到 16 的倍数(区间 [16, 2048]); - 图生图是通用 SDEdit,不是原生指令编辑条件,效果强度依赖 guidance 与 image_strength 的组合;
- 量化仅支持文中四种模式,且对 group_size/bits 有严格默认约束;已量化的组件不能直接再量化(需先反量化);
- 若源检查点 VAE 不含 encoder,转换产物只能文生图,img2img 会在加载时报错提示重新转换。
以上均可以在 mlx_vlm/models/ernie_image/ 目录下的 README.md、config.py、pipeline.py、convert.py、weights.py 及测试 test_ernie_image.py 中逐一对照验证。
【免费下载链接】mlx-vlmMLX-VLM is a package for inference and fine-tuning of Vision Language Models (VLMs) on your Mac using MLX.项目地址: https://gitcode.com/GitHub_Trending/ml/mlx-vlm
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考