多模态模型部署
High Contrast
Dark Mode
Light Mode
Sepia
Forest
2 min read309 words

多模态模型部署

多模态模型(Vision-Language, Audio-Language)的部署比纯文本 LLM 复杂:输入预处理多样、GPU 显存占用更大、推理延迟更高。

部署架构

graph TB A[多模态请求] --> B[输入路由] B --> C[图像预处理
Resize/Normalize] B --> D[音频预处理
Whisper/Encoder] B --> E[文本预处理
Tokenizer] C --> F[多模态推理引擎] D --> F E --> F F --> G[输出后处理] G --> H[文本生成] G --> I[图像生成] style A fill:#e3f2fd,stroke:#1976d2,stroke-width:3px style F fill:#e8f5e9,stroke:#388e3c,stroke-width:2px

多模态 vs 纯文本部署

维度 纯文本 LLM 多模态 LLM
输入处理 Tokenizer only 图/音/文分别处理
显存占用 高(Vision Encoder 额外开销)
batch 策略 Token-level batching 混合 batch(图+文对齐)
推理延迟 100-500ms 500ms-2s(含 Encoder)
缓存 KV Cache KV Cache + Image Cache
量化 成熟 部分支持(Encoder 精度敏感)

多模态服务框架

"""
多模态模型推理服务
"""
from dataclasses import dataclass, field
from enum import Enum
from typing import Any
import base64
import io
class ModalityType(Enum):
TEXT = "text"
IMAGE = "image"
AUDIO = "audio"
VIDEO = "video"
@dataclass
class MultimodalInput:
"""多模态输入"""
modality: ModalityType
data: Any
metadata: dict = field(default_factory=dict)
@dataclass
class ProcessedInput:
"""预处理后的输入"""
embeddings: list[float]
tokens: int
modality: ModalityType
class ImagePreprocessor:
"""图像预处理"""
def __init__(self, target_size: tuple[int, int] = (336, 336)):
self.target_size = target_size
def process(self, image_data: bytes) -> ProcessedInput:
"""处理图像"""
# 解码 → 缩放 → 归一化 → 分 patch
patches = self._split_patches(image_data)
tokens = len(patches) * 256  # 每个 patch ≈ 256 tokens
return ProcessedInput(
embeddings=patches,
tokens=tokens,
modality=ModalityType.IMAGE,
)
def _split_patches(self, image_data: bytes) -> list:
"""将图像分割为 patches(简化示意)"""
# 实际实现使用 PIL/torchvision
return [0.0] * 576  # 简化:576 个 patch embeddings
class MultimodalServer:
"""多模态推理服务"""
# 不同模态的 Token 换算
TOKEN_RATIOS = {
ModalityType.TEXT: 1,
ModalityType.IMAGE: 256,     # 1张图 ≈ 256-1024 tokens
ModalityType.AUDIO: 25,      # 1秒音频 ≈ 25 tokens
}
def __init__(self, model_name: str, max_batch_size: int = 8):
self.model_name = model_name
self.max_batch_size = max_batch_size
self.image_processor = ImagePreprocessor()
self._request_count = 0
def infer(self, inputs: list[MultimodalInput]) -> str:
"""推理"""
processed = []
total_tokens = 0
for inp in inputs:
if inp.modality == ModalityType.IMAGE:
p = self.image_processor.process(inp.data)
elif inp.modality == ModalityType.TEXT:
p = ProcessedInput(
embeddings=[],
tokens=len(str(inp.data)) // 4,
modality=ModalityType.TEXT,
)
else:
continue
processed.append(p)
total_tokens += p.tokens
self._request_count += 1
# 实际推理(调用模型的 forward)
return self._forward(processed, total_tokens)
def _forward(self, inputs: list[ProcessedInput], total_tokens: int) -> str:
"""模型前向推理(简化示意)"""
return f"[多模态推理结果] 处理 {len(inputs)} 个输入,共 {total_tokens} tokens"
def estimate_cost(self, inputs: list[MultimodalInput]) -> dict:
"""估算成本"""
total_tokens = 0
for inp in inputs:
ratio = self.TOKEN_RATIOS.get(inp.modality, 1)
if inp.modality == ModalityType.IMAGE:
total_tokens += ratio * 4  # 高分辨率可达 4x
elif inp.modality == ModalityType.TEXT:
total_tokens += len(str(inp.data)) // 4
elif inp.modality == ModalityType.AUDIO:
duration = inp.metadata.get("duration_sec", 10)
total_tokens += ratio * duration
return {
"estimated_tokens": total_tokens,
"estimated_cost_usd": total_tokens * 0.00001,
}

GPU 显存规划

模型 参数量 FP16 显存 INT4 显存 推荐 GPU
LLaVA-1.5-7B 7B 14 GB 5 GB RTX 4090
LLaVA-1.5-13B 13B 26 GB 9 GB A100 40G
Qwen-VL-Chat 9.6B 20 GB 7 GB A100 40G
InternVL2-26B 26B 52 GB 16 GB A100 80G
GPT-4V (API) 云 API

优化策略

graph LR A[多模态优化] --> B[图像缓存
相同图不重复 Encode] A --> C[动态分辨率
按需降采样] A --> D[Encoder 量化
INT8 通常可行] A --> E[异步流水线
预处理与推理并行] style A fill:#e3f2fd,stroke:#1976d2,stroke-width:3px

本章小结

主题 要点
输入复杂度 不同模态需不同预处理管线
显存规划 Vision Encoder 额外占 20-30% 显存
Token 换算 1 张图 ≈ 256-1024 tokens
优化方向 图像缓存 + 动态分辨率 + 异步流水线

下一章:LLM 生产趋势