GLM-Image 是一个基于多模态大语言模型的图像生成模型,由智谱AI开发。该模型采用两阶段架构:
GLM-Image支持以下功能:
| 组件 | 版本 | 说明 |
|---|---|---|
| vllm | v0.18.0 | 核心推理引擎 |
| vllm-ascend | v0.18.0rc1 | 昇腾NPU适配 |
| vllm-omni | v0.18.0 | 多模态扩展 |
| transformers | 4.37.0+ | HuggingFace模型库 |
| diffusers | 0.25.0+ | 扩散模型库 |
| torch | 2.1.0+ | PyTorch框架 |
| 部署方式 | 硬件配置 | 组网方式 | 优势 | 劣势 |
|---|---|---|---|---|
| 单卡部署 | 昇腾910B 32GB | 单卡 | 部署简单,配置方便 | 处理超大图片可能有内存溢出风险 |
| 多卡部署 | 多个昇腾910B | DP/SP | 能处理大图片,性能更好 | 部署复杂,需要配置分布式 |
下载当前仓库下的 glm-image.tar 到目标机器:
# 将镜像文件传输到目标机器
scp glm-image.tar user@target-machine:/path/to/destination/在目标机器执行:
docker load -i glm-image.tardocker run -it -u root -d --net=host \
--privileged \
--ipc=host \
--device=/dev/davinci_manager \
--device=/dev/devmm_svm \
--device=/dev/hisi_hdc \
-v /usr/local/Ascend/driver:/usr/local/Ascend/driver \
-v /usr/local/dcmi:/usr/local/dcmi \
-v /usr/local/bin/npu-smi:/usr/local/bin/npu-smi \
-v /usr/local/sbin:/usr/local/sbin \
-v /usr/local/Ascend/driver/tools/hccn_tool:/usr/local/Ascend/driver/tools/hccn_tool \
-v /opt/data/weights/modelscope:/opt/data/weights/modelscope \
--name glm-image \
glm-image:omni-v0.18.0.rc \
/bin/bash注意事项:
/opt/data/weights/modelscope 为实际的权重地址--name glm-image 可以按需修改容器名称-c "cd /vllm-workspace/glm_image && ./start_graph.sh"docker pull quay.io/ascend/vllm-ascend:v0.18.0rc1# 创建模型目录
mkdir -p /opt/data/weights/modelscope
# 下载GLM-Image模型
modelscope download --model ZhipuAI/GLM-Image --local_dir /opt/data/weights/modelscope/GLM-Imagedocker run -it -u root -d --net=host \
--privileged \
--ipc=host \
--device=/dev/davinci_manager \
--device=/dev/devmm_svm \
--device=/dev/hisi_hdc \
-v /usr/local/Ascend/driver:/usr/local/Ascend/driver \
-v /usr/local/dcmi:/usr/local/dcmi \
-v /usr/local/bin/npu-smi:/usr/local/bin/npu-smi \
-v /usr/local/sbin:/usr/local/sbin \
-v /usr/local/Ascend/driver/tools/hccn_tool:/usr/local/Ascend/driver/tools/hccn_tool \
-v /opt/data/weights/modelscope:/opt/data/weights/modelscope \
--name glm-image \
quay.io/ascend/vllm-ascend:v0.18.0rc1 \
/bin/bash进入容器后执行:
# 进入容器
docker exec -it glm-image bash
# 安装基础依赖
pip install transformers==4.37.0 diffusers==0.25.0
# 下载vllm-omni代码
cd /vllm-workspace
git clone -b v0.18.0 https://github.com/vllm-project/vllm-omni.git
cd vllm-omni
# 安装vllm-omni
VLLM_OMNI_TARGET_DEVICE=npu pip install -v -e .如果有必要的补丁文件,应用补丁:
cd /vllm-workspace
patch -p1 < vllm-omni-v0.18.0.patch注意事项:
git diff 命令生成,格式为 unified diffpatch -p1 参数表示忽略补丁文件中的第一级路径前缀--dry-run 参数先测试补丁应用使用提供的离线测试脚本:
cd /vllm-workspace/glm_image/test/offline
# 默认生成1920x1024分辨率的图像
./run.sh
# 指定分辨率生成图像
./run.sh 1536 864 # 生成1536x864分辨率的图像
./run.sh 512 512 # 生成512x512分辨率的图像run.sh 脚本参数:
output_{width}x{height}.pnge2e_{width}x{height}.log示例输出:
运行测试后,会在当前目录生成图像文件,例如:
# 生成1920x1024分辨率的图像
./run.sh
# 查看生成的图像
ls -lh output_1920x1024.png生成的图像文件位于 /vllm-workspace/glm_image/test/offline/output_{width}x{height}.png,可以直接使用图片查看器打开查看生成效果。

根据需要选择启动方式:
方式1:SP+Graph模式(推荐)
cd /vllm-workspace/glm_image/bin
./start_sp.sh方式2:graph模式(全图模式)
cd /vllm-workspace/glm_image/bin
./start_graph.shGraph模式配置文件 (glm_image_graph.yaml):
SP+全图模式配置文件 (glm_image_diffusion_sp.yaml):
关键配置参数说明:
AR模型配置(Stage 0)
model_stage: ar - 指定为AR模型阶段max_num_seqs: 1 - 最大批处理数量,图像生成通常设置为1gpu_memory_utilization: 0.7 - GPU显存利用率,根据实际情况调整max_tokens: 3601 - 最大生成token数,需要根据目标分辨率动态计算stop_token_ids: [16385] - EOS token ID,确保AR模型正确停止temperature: 0.9 - 采样温度,控制生成随机性top_p: 0.75 - 核采样参数top_k: 16512 - 视觉词汇表大小Diffusion模型配置(Stage 1)
model_stage: dit - 指定为DiT模型阶段num_inference_steps: 50 - 扩散推理步数,影响生成质量和速度guidance_scale: 1.5 - 引导系数,控制生成与提示词的符合度height/width - 生成图像的尺寸并行配置
tensor_parallel_size: 2 - 张量并行度,将模型权重切分到多个设备sequence_parallel_size: 2 - 序列并行度,将序列切分到多个设备ulysses_degree: 2 - Ulysses算法的并行度ring_degree: 1 - Ring并行度方式1:使用OpenAI兼容接口
cd /vllm-workspace/glm_image/test/online
# 默认生成1920x1024分辨率的图像
./openai.sh
# 指定分辨率
./openai.sh 1536 864方式2:使用Images API
cd /vllm-workspace/glm_image/test/online
# 默认生成1920x1024分辨率的图像
./test.sh
# 指定分辨率
./test.sh 1536 864测试脚本会生成以下文件:
output_t2i_{width}x{height}.png:生成的图像resp.json:API响应结果示例输出:
运行测试后,会在当前目录生成图像文件:
# 生成1536x864分辨率的图像
./test.sh 1536 864
# 查看生成的图像
ls -lh output_t2i_1536x864.png
# 查看API响应
cat resp.json | python3 -m json.tool生成的图像文件位于 /vllm-workspace/glm_image/test/online/output_t2i_{width}x{height}.png,可以直接使用图片查看器打开查看生成效果。

当前大部分代码来源于社区的pr,但是由于社区的pr基于0.19.0开发,而当前昇腾库依赖0.18.0,导致大部分变更需要手动合入。
GLM-Image的精度优化主要集中在AR模型的token生成阶段,精确的token数量计算是保证生成质量的关键。
1. 精确的token数量计算
GLM-Image采用两阶段生成策略,AR模型需要生成精确数量的token来表示目标图像。Token数量的计算基于以下原理:
计算公式:
# 对于T2I模式
token_count = preview_tokens + target_tokens + eos_token
# 对于I2I模式
token_count = target_tokens + eos_token
# 具体数值(基于GLM-Image的patch_size=16和VAE配置)
1024x1024 -> 1281 tokens (1280 + 1)
1920x1024 -> 2401 tokens (2400 + 1)
2048x2048 -> 5121 tokens (5120 + 1)核心代码修改:
# vllm_omni/model_executor/models/glm_image/glm_image_ar.py
def get_num_image_tokens(
self,
*,
image_width: int,
image_height: int,
) -> int:
"""
Calculate number of image tokens for a given image size.
GLM-Image processes images through a patch embedding with patch_size=16,
then quantizes through VQ-VAE. The number of tokens is:
(image_height // patch_size) * (image_width // patch_size)
"""
hf_config = self.get_hf_config()
vision_config = hf_config.vision_config
patch_size = vision_config.patch_size
# Number of patches in each dimension
num_patches_h = image_height // patch_size
num_patches_w = image_width // patch_size
return num_patches_h * num_patches_wM-RoPE位置计算代码修改:
# vllm_omni/model_executor/models/glm_image/glm_image_ar.py
def get_mrope_input_positions(
self,
input_tokens: list[int],
mm_features: list[MultiModalFeatureSpec] | None = None,
image_grid_thw: list[list[int]] | None = None,
**kwargs,
) -> tuple[torch.Tensor, int]:
"""Compute M-RoPE position IDs for GLM-Image.
GLM-Image uses 3D positional encoding where text tokens have identical
values across all dimensions, while image tokens use 2D grid positions.
For text-to-image, also pre-computes decode positions for generated tokens.
For image-to-image, uses image_start_token_id and image_end_token_id to
find image boundaries (following reference implementation).
Token structure for i2i:
[text...] <image_start=16384> [image_token=167855 × N] <image_end=16385> [text...] <grid> <bos=16384>
Returns:
Tuple of (position_ids [3, total_len], mrope_position_delta)
"""
# 获取配置中的token ID
hf_config = self.config
image_token_id = hf_config.image_token_id # 167855, repeated for each image patch
image_start_token_id = hf_config.image_start_token_id # 16384, marks image start
image_end_token_id = hf_config.image_end_token_id # 16385, marks image end
# 解析图像网格信息
if not image_grid_thw:
target_h = kwargs.get("target_h")
target_w = kwargs.get("target_w")
if isinstance(target_h, int) and isinstance(target_w, int):
factor = 32
token_h = target_h // factor
token_w = target_w // factor
ratio = token_h / token_w if token_w > 0 else 1.0
small_h = max(1, int(math.sqrt(ratio) * (factor // 2)))
small_w = max(1, int(math.sqrt(1 / ratio) * (factor // 2)))
image_grid_thw = [[1, token_h, token_w], [1, small_h, small_w]]
# 计算位置编码
# ... (详细的位置编码计算逻辑)代码实现位置:
glm_image_graph.yaml 中的 max_tokens 参数vllm_omni/model_executor/models/glm_image/glm_image_ar.pyimage_token_id, image_start_token_id, image_end_token_id相关PR:
PR #1894: [Bugfix] resolve stage config for GLM-Image with diffusers format
Commit e33a7977: Token calculation fix for GLM-Image 1024x512 resolution
_compute_glm_image_max_tokens()辅助方法优化效果:
2. 采样参数调优
AR模型的采样参数直接影响生成token的质量,这些参数在配置文件中精心调优:
temperature: 0.9 # 控制生成随机性,0.9提供适度的多样性
top_p: 0.75 # 核采样,只保留累积概率达到75%的token
top_k: 16512 # 限制采样范围,16512是视觉词汇表大小
stop_token_ids: [16385] # EOS token,确保正确停止参数详细说明:
temperature: 0.9
top_p: 0.75
top_k: 16512
stop_token_ids: [16385]
image_end_token_id,标记图像生成的结束采样参数的配置位置:
# glm_image_graph.yaml
default_sampling_params:
temperature: 0.9
top_p: 0.75
top_k: 16512
max_tokens: 3601
stop_token_ids: [16385]
seed: 42
detokenize: false相关PR:
3. M-RoPE优化
GLM-Image使用M-RoPE(Multi-dimensional RoPE)进行位置编码,这是保证图像生成精度的关键技术。
M-RoPE原理:
M-RoPE核心代码修改:
# vllm_omni/model_executor/models/glm_image/glm_image_ar.py
class MRoPEEmbedding(nn.Module):
def __init__(self, rotary_dim: int, mrope_section: list[int], rope_theta: float = 10000.0):
"""
Multi-dimensional Rotary Position Embedding.
Args:
rotary_dim: Total rotary dimension
mrope_section: Section sizes for each dimension [8, 12, 12]
rope_theta: Base frequency for rotary embedding
"""
super().__init__()
self.rotary_dim = rotary_dim
self.mrope_section = mrope_section
# 计算逆频率
inv_freq = 1.0 / (rope_theta ** (torch.arange(0, self.rotary_dim, 2, dtype=torch.float32) / self.rotary_dim))
self.register_buffer("inv_freq", inv_freq, persistent=False)
def _apply_mrope(self, freqs: torch.Tensor) -> torch.Tensor:
"""
Apply M-RoPE section interleaving.
For mrope_section = [8, 12, 12]:
- Split freqs into chunks of size [8, 12, 12, 8, 12, 12, ...]
- Take chunk[i % 3] from each split (alternating T, H, W dimensions)
- Concatenate back
Args:
freqs: Frequency tensor [3, num_tokens, rotary_dim // 2]
Returns:
Interleaved frequencies [num_tokens, rotary_dim // 2]
"""
# freqs shape: [3, num_tokens, rotary_dim // 2]
# Split along last dimension according to mrope_section
chunks = freqs.split(self.mrope_section, dim=-1)
# Take chunk[i % 3] from each split
# chunks[i] has shape [3, num_tokens, section_size]
# We select dimension 0 (T), 1 (H), or 2 (W) based on i % 3
result = torch.cat([chunk[i % 3] for i, chunk in enumerate(chunks)], dim=-1)
return result
def forward(
self,
positions: torch.Tensor,
query: torch.Tensor,
key: torch.Tensor,
) -> tuple[torch.Tensor, torch.Tensor]:
"""
Apply rotary position embeddings to query and key.
Args:
positions: Position IDs
- Shape [num_tokens] for 1D positions (text-only)
- Shape [3, num_tokens] for 3D M-RoPE positions (T, H, W)
query: Query tensor [num_tokens, num_heads * head_dim]
key: Key tensor [num_tokens, num_kv_heads * head_dim]
Returns:
Tuple of (rotated_query, rotated_key) with same shapes as input
"""
# 处理1D位置(文本)和3D位置(图像)
if positions.ndim == 1:
# 1D positions for text tokens
freqs = self._compute_freqs(positions)
else:
# 3D positions for image tokens (T, H, W)
freqs = self._compute_3d_freqs(positions)
# 应用M-RoPE
rot_q, rot_k = self._apply_rotary_emb(query, key, freqs)
return rot_q, rot_kM-RoPE的优势:
相关PR:
PR #1894: [Bugfix] resolve stage config for GLM-Image with diffusers format
PR #2278: Update MRoPE config fallback logic
优化效果:
代码实现:
vllm_omni/model_executor/models/glm_image/glm_image_ar.pyGlmImageForConditionalGeneration优化效果:
Cache-Dit是针对DiT模型的缓存优化技术,通过缓存中间计算结果来减少重复计算。
优化原理:
DiT模型在扩散去噪过程中,相邻时间步的中间特征具有高度相似性。Cache-Dit利用这一特性:
关键参数:
cache_config = {
"Fn_compute_blocks": 1, # 前向传播计算块数
"Bn_compute_blocks": 0, # 后向传播计算块数
"max_warmup_steps": 4, # 预热步数,前4步不使用缓存
"residual_diff_threshold": 0.24, # 残差阈值,控制缓存复用条件
"max_continuous_cached_steps": 3, # 最大连续缓存步数
"enable_taylorseer": False, # 是否启用Taylor展开优化
"taylorseer_order": 1, # Taylor展开阶数
"scm_steps_mask_policy": None, # 步骤掩码策略
"scm_steps_policy": "dynamic", # 步骤策略:动态调整
}开启方式: 启动脚本上新增--cache-backend cache_dit
参数说明:
residual_diff_threshold:控制缓存复用的严格程度,值越小越严格max_continuous_cached_steps:限制连续缓存步数,避免精度损失累积max_warmup_steps:预热阶段不使用缓存,确保初始计算准确性代码实现:
vllm_omni/diffusion/cache/ 目录CachedTransformer, CacheManagerGlmImageTransformer2DModel 中集成优化效果:
全图模式是GLM-Image部署中最重要的性能优化手段,通过昇腾CANN的ACL Graph技术实现整个计算图的编译和复用,带来显著的性能提升。
什么是全图模式?
全图模式(ACL Graph)是昇腾CANN(Compute Architecture for Neural Networks)提供的一种计算图优化技术:
为什么能带来性能提升?
全图模式的性能提升主要体现在以下几个方面:
消除Python开销
算子融合优化
内存访问优化
静态形状优化
开启方式: 在配置文件中新增如下字段? compilation_config: cudagraph_capture_sizes: [1,2] # 映射到ACL Graph的捕获尺寸 cudagraph_mode: "FULL_DECODE_ONLY" # 映射到ACL Graph的运行模式
在GLM-Image的配置中,虽然使用的是cudagraph相关的参数名称,但实际上这些参数被映射到了昇腾CANN的ACL Graph实现:
compilation_config:
cudagraph_capture_sizes: [1,2] # 映射到ACL Graph的捕获尺寸
cudagraph_mode: "FULL_DECODE_ONLY" # 映射到ACL Graph的运行模式映射机制:
ACLGraphWrapper类中实现了CUDA Graph到ACL Graph的映射torch.npu.NPUGraph()进行ACL Graph的捕获和复用配置参数详细说明:
compilation_config:
cudagraph_capture_sizes: [1,2] # 捕获不同batch size的计算图
cudagraph_mode: "FULL_DECODE_ONLY" # 全图解码模式只优化解码阶段参数详解:
cudagraph_capture_sizes: [1,2]
cudagraph_mode: "FULL_DECODE_ONLY"
NONE:不使用全图模式FULL:全阶段使用全图模式PIECEWISE:分段全图模式全图模式的工作流程:
图捕获阶段(首次运行)
# 在ACLGraphWrapper中
aclgraph = torch.npu.NPUGraph() # 创建ACL Graph对象
with torch.npu.graph(aclgraph): # 开始捕获计算图
output = model_forward(*inputs) # 执行模型前向传播
# 图捕获完成,可以进行复用图复用阶段(后续运行)
# 更新输入数据(保持内存地址不变)
update_input_buffers(new_inputs)
# 复用已捕获的计算图
aclgraph.replay() # 极快的执行速度支持的分辨率:
全图模式支持多种分辨率,每种分辨率会编译独立的计算图:
代码实现:
compilation_config参数启用全图模式ACLGraphWrapper类负责图的捕获、缓存和复用NPUARWorker中应用全图模式优化token生成优化效果:
全图模式是GLM-Image部署中性能提升最显著的优化手段:
性能对比数据(示例):
| 模式 | 1920x1024生成时间 | 1024x1024生成时间 | CPU利用率 |
|---|---|---|---|
| Eager模式 | ~15s | ~8s | 高 |
| 全图模式 | ~5s | ~2.5s | 低 |
| 性能提升 | 3x | 3.2x | - |
注意事项:
序列并行(Sequence Parallelism, SP)
SP使用Ulysses算法将长序列切分到多个设备上并行处理。
Ulysses算法原理:
配置参数:
sequence_parallel_size: 2 # SP并行度
ulysses_degree: 2 # Ulysses算法度
ring_degree: 1 # Ring并行度适用场景:
代码实现:
vllm_omni/diffusion/distributed/ 目录ulysses_attention, ring_attention张量并行(Tensor Parallelism, TP)
TP将模型权重切分到多个设备上,每个设备存储部分权重。
TP原理:
配置参数:
tensor_parallel_size: 2 # TP并行度适用场景:
代码实现:
vllm/distributed/ 目录ColumnParallelLinear, RowParallelLinear性能对比:
| 配置 | 显存占用 | 推理速度 | 适用场景 |
|---|---|---|---|
| 单卡 | 32GB | 1x | 小分辨率图像(<1024x1024) |
| TP=2 | 16GB/卡 | 1.5x | 中等分辨率(1024x1024 - 1536x864) |
| SP=2 | 16GB/卡 | 1.8x | 大分辨率(>1536x864) |
| TP+SP | 8GB/卡 | 2.2x | 超大分辨率(>2048x2048) |
优化建议:
小分辨率图像(<1024x1024)
中等分辨率图像(1024x1024 - 1536x864)
大分辨率图像(>1536x864)
超大规模部署
错误信息:
[W417 12:41:53.040026660 socket.cpp:767] [c10d] The IPv6 network addresses of (localhost, 30098) cannot be retrieved (gai error: -2 - Name or service not known).解决方案:
# 修改/etc/hosts文件,添加localhost映射
echo "127.0.0.1 localhost" >> /etc/hosts错误信息:
RuntimeError: CUDA out of memory. Tried to allocate XXX MiB解决方案:
gpu_memory_utilization参数(从0.9降到0.7)max_num_seqs批处理数量错误信息:
FileNotFoundError: Model weights not found at /path/to/model解决方案:
可能原因:
解决方案:
--cache-backend cache_dit错误信息:
patch: **** Can't find file to patch at input line X解决方案:
--dry-run参数测试小分辨率图像(<1024x1024)
glm_image_graph.yaml中等分辨率图像(1024x1024 - 1536x864)
glm_image_diffusion_sp.yaml,设置TP=2, SP=1大分辨率图像(>1536x864)
glm_image_diffusion_sp.yamlGLM-Image是一个强大的多模态图像生成模型,通过vllm-omni可以在昇腾NPU上高效部署。本指南涵盖了:
通过合理配置和优化,可以在保证生成质量的同时,获得最佳的推理性能。