如果你的 LingBot-Vision 在批量跑图片时速度达不到预期,优先怀疑的是调用方式而不是模型本身。最常用的提升手段集中在三处:把单张循环改成 batch 推理、把默认的 FP32 改成半精度、把 CPU 上的图片解码和缩放挪到独立线程里。每一步都要先用脚本量出当前的总耗时和吞吐量,再做改动,否则无法判断哪个优化真正有效。改动后的收益必须用同一批图片、相同硬件环境下多次运行取平均值来确认,不保证固定提速倍数。
判断:LingBot-Vision 批量推理慢,通常先从“单张循环送 GPU”改起。操作:逐张读取图片改为 batch 维度拼接,再配合 model.half() 或 torch.cuda.amp.autocast,以及多线程预取图片。验证:用同一图片集在优化前后分别跑多次,记录总耗时和每秒处理图片数。边界:加速效果依赖显卡型号、batch size 和图片尺寸,换机后需要重新测量。
基线脚本:单张循环推理的耗时测量
先建立可量化的基线,不要凭感觉判断快慢。下面这段脚本逐张读取图片并调用 LingBot-Vision 推理,记录总耗时和每张平均耗时。把模型加载、图片读取和推理计时分开,方便后续定位瓶颈。
import torch, time, cv2, glob
from lingbot_vision import LingBotVision
model = LingBotVision.from_pretrained("your-model-path")
model.eval()
image_paths = glob.glob("./images/*.jpg") # 换成你的图片目录
def load_image(path):
img = cv2.imread(path)
img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
return img
start = time.time()
per_image = []
for path in image_paths:
img = load_image(path)
t0 = time.time()
output = model.infer(img) # 按实际接口调整
per_image.append(time.time() - t0)
total = time.time() - start
avg = sum(per_image) / len(per_image)
throughput = len(image_paths) / total
print(f"total: {total:.2f}s, avg: {avg:.4f}s, throughput: {throughput:.2f} img/s")
记录下这个输出结果,作为后续优化的对比基准。如果每张图片平均耗时远大于预处理耗时,优先做批处理。
开启批处理模式
GPU 擅长并行处理相同 shape 的张量。批量推理的核心是把多张图片沿 batch 维度堆叠成一个大张量,一次前向传播完成多张图片的推理。注意图片需要先缩放到统一尺寸,否则无法堆叠。
import torch, time, cv2, glob, numpy as np
from lingbot_vision import LingBotVision
model = LingBotVision.from_pretrained("your-model-path")
model.eval()
image_paths = glob.glob("./images/*.jpg")
batch_size = 8 # 可调,先试 4、8、16
preprocessed = []
for path in image_paths:
img = cv2.imread(path)
img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
img = cv2.resize(img, (224, 224)) # 与模型输入一致
preprocessed.append(img)
batch_times = []
start = time.time()
for i in range(0, len(preprocessed), batch_size):
batch = preprocessed[i:i+batch_size]
batch_tensor = torch.tensor(np.array(batch)).permute(0, 3, 1, 2).float()
t0 = time.time()
outputs = model.infer_batch(batch_tensor) # 按实际接口调整
batch_times.append(time.time() - t0)
total = time.time() - start
print(f"batch total: {total:.2f}s, throughput: {len(image_paths)/total:.2f} img/s")
先将 batch_size 设置为 4、8、16、32 各跑一遍,记录吞吐量。通常 batch 越大吞吐越高,但显存占用也会增加,如果报 OOM 就降一档。
使用半精度与可能存在的加速后端
半精度(FP16)能减少显存占用和计算量。在 PyTorch 中有两种常见做法:直接把模型参数转成半精度,或在推理时使用自动混合精度。前者适合模型结构固定且算子都支持 FP16 的情况,后者更灵活,会在前向传播中自动选择 FP16 或 FP32。
import torch
model.half() # 方式一:模型参数转半精度
# 方式二:使用 autocast 做自动混合精度
from torch.cuda.amp import autocast
with torch.no_grad():
with autocast():
output = model.infer_batch(batch_tensor.half())
如果 LingBot-Vision 依赖的底层算子不支持半精度,推理可能出错或变慢,需要结合环境确认。另外,某些环境还提供 TensorRT、OpenVINO 等加速后端,这类后端通常需要额外导出和配置,不能直接替换模型路径。建议先测试半精度,再决定是否引入加速后端。
多线程或多进程数据加载
图片读取、resize、归一化等操作都在 CPU 上执行。如果 GPU 每完成一个 batch 后必须等待 CPU 准备下一批,GPU 的空闲时间就白费了。用 ThreadPoolExecutor 预读并预处理图片,放入队列,让 CPU 预处理与 GPU 推理重叠。
from concurrent.futures import ThreadPoolExecutor
import queue, threading
workers = 4 # 先按 CPU 核心数的一半设置
batch_queue = queue.Queue(maxsize=8)
def preprocess_images(paths, batch_size):
for i in range(0, len(paths), batch_size):
batch_paths = paths[i:i+batch_size]
batch = []
for p in batch_paths:
img = cv2.imread(p)
img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
img = cv2.resize(img, (224, 224))
batch.append(img)
batch_tensor = torch.tensor(np.array(batch)).permute(0, 3, 1, 2).float()
batch_queue.put(batch_tensor)
batch_queue.put(None) # 结束标记
# 在推理循环前启动预取线程
with ThreadPoolExecutor(max_workers=workers) as executor:
future = executor.submit(preprocess_images, image_paths, batch_size)
while True:
batch = batch_queue.get()
if batch is None:
break
if torch.cuda.is_available():
batch = batch.cuda()
# 推理并记录时间
with torch.no_grad():
output = model.infer_batch(batch)
worker 数量只影响 CPU 预处理侧,不是越大越好。先用 CPU 物理核心数的一半起调,如果 CPU 占用不高但 GPU 仍有空闲,再逐步增加。需要观察任务管理器或 nvidia-smi 确认 GPU 利用率是否提升。
验证优化效果并对比
回到第 1 步的脚本,把其中逐张推理的部分替换为 batch 推理 + 半精度 + 预取线程的完整流程,保持图片集和硬件环境不变。每次运行后记录总耗时与吞吐量,建议每项优化跑 3 次取平均值,避免波动。
| 方案 | 总耗时(秒) | 吞吐量(张/秒) | 显存占用(MB) |
|---|---|---|---|
| 基线:单张循环 | 待记录 | 待记录 | 待记录 |
| 开启 batch | 待记录 | 待记录 | 待记录 |
| batch + 半精度 | 待记录 | 待记录 | 待记录 |
| 全部优化 | 待记录 | 待记录 | 待记录 |
对比时重点看总耗时和吞吐量,batch 大小和 worker 数量可以在表格中留备注列。如果某一项优化反而让耗时增加,说明该改动在你的环境不适用,应回退。最终采用哪一组配置,以多次平均后吞吐最高、且显存不超限为准。