中配AI集群转型指南:本地LLM推理与视频转码实践
如果你手头有一批“中配”AI 集群,这里的“中配”可能是几十张消费卡,也可能是几台旧服务器,原来的用途多半是跑模型训练、做算法比赛、跑跑数据处理。但这两年大家慢慢发现,中等算力集群在“大模型预训练”这件事上越来越吃力,可它并不是没有价值了。它其实很适合两类新任务:一类是本地 LLM 推理服务,另一类是视频转码。
本文会围绕这两条实际用途展开,讲清楚中配集群为什么适合做这些事、环境如何准备、推理和转码任务怎么落地,并给出可复制的示例代码和常见问题排查思路。无论你手上是几张 3090/4090,还是一批插着 T4/A10 的旧服务器,这篇文章都可以当作一份“集群复用指南”来参考。
1. 背景:中配AI集群为什么不再死磕训练
1.1 训练重度“内卷”,中配集群的尴尬
大模型训练尤其是预训练阶段,对算力规模、显存容量、互联带宽要求非常高。头部团队动辄上千张加速卡,配合高速互联网络,才能把千亿级模型训出来。
而中配集群通常面临三个硬伤:
- 单卡性能有限:消费级显卡或中端计算卡,算力远不如旗舰训练卡。
- 显存不足:单卡 24GB 到 48GB 比较常见,装不下大模型的完整参数。
- 互联带宽受限:PCIe 或普通万兆网络,难以支撑大规模并行训练。
如果强行用中配集群去训练大模型,容易出现训练效率低下、通信瓶颈严重、出现异常难排查等问题。久而久之,不少团队开始把中配集群从“训练岗”调离。
1.2 推理和转码更依赖“吞吐”而不是“规模”
中配集群不适合大模型预训练,但它适合以下两类新任务:
- 本地 LLM 推理:模型可以量化,单卡或双卡就能塞下一个可用的 7B/13B 模型。推理任务对互联带宽要求远低于训练,反而更看重单卡算力和显存带宽。
- 视频转码:转码任务天然可以拆分成大量独立任务,单张 GPU 处理一段视频,集群整体就是一个并行的转码车间。
也就是说,AI 集群不是“终结”了,而是找到了更适合自己的新岗位。下面我们分别看这两类任务怎么落地。
2. 环境准备与版本说明
2.1 典型中配集群拓扑
为了方便表述,本文以一个典型的中配集群为例。你可以根据自己手头的硬件做替换。
控制节点(CPU:16核,内存:64GB,系统盘:500GB SSD)
计算节点 01(GPU:NVIDIA RTX 4090 24GB,内存:64GB)
计算节点 02(GPU:NVIDIA RTX 4090 24GB,内存:64GB)
计算节点 03(GPU:NVIDIA RTX A4000 16GB,内存:64GB)
共享存储:NFS 或 MinIO 对象存储
这个配置不会太贵,却能同时覆盖 LLM 推理和视频转码两类任务。
2.2 软件栈选型
需要安装以下基础软件:
| 软件 | 作用 | 示例版本 |
|---|---|---|
| Ubuntu Server | 操作系统 | 22.04 LTS |
| NVIDIA 驱动 | GPU 基础驱动 | 535.x |
| CUDA | 通用计算库 | 12.2 |
| Python | 脚本与推理框架 | 3.10 |
| Docker | 容器化部署 | 24.x |
| FFmpeg | 视频处理 | 6.1 |
| vLLM / Ollama | LLM 推理服务 | vLLM 0.5.x |
注意:以上版本仅作为参考,实际安装时请以官方最新稳定版本为准。尤其是 NVIDIA 驱动与 CUDA 版本要匹配,否则推理框架会直接无法调用 GPU。
2.3 检查 GPU 环境
在每台计算节点上执行:
nvidia-smi
正常输出会显示 GPU 型号、驱动版本和显存使用情况。如果看不到设备,先检查驱动是否安装成功,或者是否插电、是否被系统识别。
3. 核心概念:为什么本地 LLM 能跑在中配集群上
3.1 量化是本地 LLM 的关键
大模型参数通常以 FP16 或 BF16 存储,一个 7B 模型仅参数就需要约 14GB 显存。如果加上推理过程中的 KV Cache 和激活值,开销会更高。
量化就是把模型参数从 FP16 压缩成 INT8 或 INT4,例如把 7B 模型压缩到 4GB 左右。常见量化格式包括:
- GGUF:主要用于 llama.cpp 和 Ollama,支持 CPU/GPU 混合推理。
- AWQ:激活感知量化,适合 GPU 推理。
- GPTQ:较早的 GPU 量化格式,兼容性好。
对中配集群来说,量化后模型能塞进单卡或双卡,推理性价比很高。
3.2 推理服务框架选型
本地 LLM 推理并不是简单地在 Python 里加载模型,然后一次生成一句完整答案。在实际项目中,我们需要考虑并发请求、流式输出、批量推理、显存动态分配等细节。常用框架有:
- vLLM:吞吐量高,支持 PagedAttention,适合服务化部署。
- Ollama:部署简单,但对底层调参空间小。
- TGI:Hugging Face 推出的推理服务,功能完整。
- llama.cpp:适合单机单卡和 CPU 推理,配置灵活。
对于中配集群,vLLM 是最推荐的首选,因为它能充分利用多张 GPU,并且提供兼容 OpenAI 风格的 API,接入开发项目非常方便。
3.3 从“训练卡”到“推理卡”的理念转变
训练时我们追求“每秒处理多少样本”,推理时我们更关注“每秒处理多少 token”和“单请求延迟”。推理任务多了之后,还可以做请求排队、动态批处理。中配集群如果从训练视角切换到推理视角,很多硬件劣势就变成了性价比优势。
4. 实战一:本地 LLM 推理服务搭建
4.1 安装推理框架
以 vLLM 为例,创建虚拟环境并安装依赖:
python3 -m venv ~/vllm-env
source ~/vllm-env/bin/activate
pip install --upgrade pip
pip install vllm
如果需要 CUDA 12.1 版本配套,要检查 PyPI 上的预编译 wheel 是否匹配。
4.2 下载模型
先在 Hugging Face 或 ModelScope 下载模型。中国用户使用 ModelScope 往往更快:
pip install modelscope
modelscope download --model Qwen/Qwen2.5-7B-Instruct --local_dir /data/models/qwen2.5-7b-instruct
这里使用 Qwen2.5-7B-Instruct 作为示例,你也可以换成其他开源模型,比如 Llama 3.1 8B、GLM-4-9B 等。
4.3 启动 vLLM 推理服务
单卡 24GB 可以直接加载 FP16 模型,也可以加载 AWQ 量化版。示例命令如下:
python -m vllm.entrypoints.openai.api_server \
--model /data/models/qwen2.5-7b-instruct \
--tensor-parallel-size 1 \
--gpu-memory-utilization 0.85 \
--max-model-len 8192 \
--port 8000
参数说明:
-
--model:模型路径。 -
--tensor-parallel-size:张量并行卡数,单卡设 1,多卡可调大。 -
--gpu-memory-utilization:预留给模型的最大显存比例。 -
--max-model-len:最大上下文长度。 -
--port:服务端口。
启动后,终端会打印访问地址和可用模型列表。你可以用 curl 测试:
curl http://localhost:8000/v1/completions \
-H "Content-Type: application/json" \
-d '{
"model": "/data/models/qwen2.5-7b-instruct",
"prompt": "你好,请用一句话介绍你自己",
"max_tokens": 128
}'
返回结果中
choices[0].text
就是模型生成的文本。
4.4 在 Python 项目中调用推理服务
推理服务启动后,业务代码可以使用 OpenAI SDK 调用。如果你希望替换模型供应商,只需要改 base_url:
# 文件路径:client_test.py
from openai import OpenAI
client = OpenAI(
api_key="EMPTY",
base_url="http://127.0.0.1:8000/v1"
)
resp = client.chat.completions.create(
model="/data/models/qwen2.5-7b-instruct",
messages=[
{"role": "user", "content": "帮我写一份项目周报模板"}
],
max_tokens=512,
temperature=0.7
)
print(resp.choices[0].message.content)
运行方式:
python client_test.py
这只是一个最小示例。实际项目中,你可以把这里替换为 FastAPI 网关,或者在多台计算节点上分别启动不同模型,用负载均衡路由到不同服务。
4.5 多卡并行与显存规划
如果模型超过一张卡,比如要部署 70B 量化模型,可以设置:
python -m vllm.entrypoints.openai.api_server \
--model /data/models/qwen2-5-72b-instruct-awq \
--tensor-parallel-size 4 \
--gpu-memory-utilization 0.9 \
--max-model-len 4096 \
--port 8000
这时 vLLM 会把模型切分到 4 张 GPU 上。需要特别注意的是,
--tensor-parallel-size
必须小于等于节点内 GPU 数;如果有多个节点,vLLM 还支持
--pipeline-parallel-size
或分布式推理,但对网络要求更高,中配集群跨节点跑张量并行要谨慎评估性能。
4.6 推理侧最佳实践
- 优先使用模型量化版本,提高并发吞吐。
-
显存不够时,优先缩短
--max-model-len,而不是减小显存利用率。 -
用
vllm serve命令时注意 API 路径是/v1/completions和/v1/chat/completions,不同模型接口可能有差异。 - 推理服务建议用 Docker 部署,方便迁移和版本回滚。
5. 实战二:用 AI 集群做视频转码
5.1 视频转码为什么需要 GPU
视频转码的核心操作包括解码、缩放、滤镜处理、编码。CPU 也能完成这些操作,但速度慢,尤其对 4K、8K 素材或批量处理任务来说,GPU 硬件编码器可以把转码速度提升数倍乃至数十倍。
NVIDIA GPU 的 NVENC 硬件编码器支持 H.264、HEVC/H.265,甚至 AV1 编码。解码侧也有 NVDEC 硬件解码器。中配集群上的消费卡也有较完整的 NVENC/NVDEC 能力,因此非常适合转码任务。
5.2 FFmpeg 安装与硬件检查
确认 FFmpeg 是否支持 NVIDIA 加速:
ffmpeg -hide_banner -encoders | grep nvenc
输出中应包含
h264_nvenc
、
hevc_nvenc
等编码器。如果没有,需要安装带
--enable-nvenc
配置的 FFmpeg:
apt update
apt install -y ffmpeg
nv-codec-headers
更稳妥的方式是使用官方提供的静态编译版本,或者自行编译:
./configure --enable-nonfree --enable-cuda-nvcc --enable-libnpp \
--extra-cflags=-I/usr/local/cuda/include \
--extra-ldflags=-L/usr/local/cuda/lib64
make -j8
5.3 单任务 GPU 转码命令
假设我们把一个 MP4 文件转成 H.265 编码的 MKV,分辨率不变,码率控制在 8Mbps:
ffmpeg -y \
-hwaccel cuda -hwaccel_output_format cuda \
-i input.mp4 \
-c:v hevc_nvenc \
-b:v 8M \
-maxrate 10M \
-bufsize 12M \
-c:a copy \
output.mkv
关键参数:
-
-hwaccel cuda:使用 CUDA 硬件解码。 -
-hwaccel_output_format cuda:解码后的数据留在显存,避免频繁拷贝。 -
-c:v hevc_nvenc:使用 NVENC H.265 编码器。 -
-b:v:平均码率。 -
-maxrate、-bufsize:控制码率波动。
如果视频是 4K 高帧率,建议增加
-tune hq
或
-preset p5
等预设参数,不同显卡驱动和 FFmpeg 版本预设名称可能不同。
5.4 批量并行转码脚本
集群上有大量视频文件时,一个转码命令完全不够,需要把任务调度到多张 GPU 上并行执行。
下面用 Python 写一个简单的并行转码调度器。核心思路是:遍历输入目录下的视频,给每个任务分配 GPU 编号,然后用 subprocess 调用 ffmpeg。
# 文件路径:batch_transcode.py
import os
import subprocess
import argparse
from concurrent.futures import ThreadPoolExecutor
from pathlib import Path
VIDEO_EXTS = {".mp4", ".mkv", ".mov", ".avi", ".flv"}
def transcode_one(src: Path, dst: Path, gpu_id: int, preset: str = "p5"):
cmd = [
"ffmpeg", "-y",
"-hwaccel", "cuda",
"-hwaccel_output_format", "cuda",
"-i", str(src),
"-c:v", "hevc_nvenc",
"-preset", preset,
"-b:v", "8M",
"-maxrate", "10M",
"-bufsize", "12M",
"-c:a", "copy",
str(dst),
]
env = os.environ.copy()
env["CUDA_VISIBLE_DEVICES"] = str(gpu_id)
print(f"[GPU {gpu_id}] {src.name} -> {dst.name}")
subprocess.run(cmd, env=env, check=True)
def collect_videos(input_dir: Path):
return [p for p in input_dir.rglob("*") if p.suffix.lower() in VIDEO_EXTS]
def main():
parser = argparse.ArgumentParser()
parser.add_argument("--input", required=True, type=Path)
parser.add_argument("--output", required=True, type=Path)
parser.add_argument("--gpus", default="0,1,2,3")
parser.add_argument("--threads", type=int, default=4)
args = parser.parse_args()
args.output.mkdir(parents=True, exist_ok=True)
videos = collect_videos(args.input)
gpu_list = [int(x) for x in args.gpus.split(",")]
tasks = []
for idx, video in enumerate(videos):
# 相对路径保持不变,避免同名文件相互覆盖
rel = video.relative_to(args.input)
dst = args.output / rel.with_suffix(".mkv")
dst.parent.mkdir(parents=True, exist_ok=True)
gpu_id = gpu_list[idx % len(gpu_list)]
tasks.append((video, dst, gpu_id))
with ThreadPoolExecutor(max_workers=args.threads) as pool:
futures = [
pool.submit(transcode_one, src, dst, gpu_id)
for src, dst, gpu_id in tasks
]
for future in futures:
future.result()
if __name__ == "__main__":
main()
使用方式:
python batch_transcode.py \
--input /data/raw_videos \
--output /data/transcoded_videos \
--gpus 0,1,2,3 \
--threads 4
这个脚本有几个值得注意的地方:
-
将
CUDA_VISIBLE_DEVICES设置为不同的 GPU 编号,从而让每个子进程只看到指定卡。 - 用线程池控制并发数量,防止任务过多造成显存溢出。
- 输出目录中保持源目录相对结构,便于回传和归档。
5.5 分布式任务调度的扩展思路
上面的 Python 脚本适合单机和多 GPU 并行。如果集群有多个节点,可以考虑把“任务列表”放到 Redis 队列或自定义任务表中,每个节点跑一个 worker,worker 从队列取任务并执行转码。
伪代码思路如下:
# worker 伪代码
while True:
task = redis.brpop("video_tasks", timeout=5)
if task is None:
continue
video_path, output_path = parse_task(task)
transcode_one(video_path, output_path, gpu_id)
这样集群扩展性更强,也能实现 GPU 故障隔离和任务重试。对于小型集群,先用 Python 多线程脚本就够了。
6. 常见问题与排查思路
6.1 推理服务相关问题
| 问题现象 | 常见原因 | 解决思路 |
|---|---|---|
| cuDNN 相关报错 | CUDA 版本不匹配 | 检查驱动版本,安装与驱动匹配的 CUDA toolkit |
| 显存不足 OOM | 模型太大或并发太高 | 使用量化模型,降低 max-len,减少并发数 |
| 服务启动后模型响应慢 | 模型未预热或未开启动态批处理 | 先用短请求预热,检查 vLLM 参数 |
| 生成内容乱码 | 模型分词器与模型文件不匹配 | 重新下载模型或改用 transformers 加载验证 |
| 请求超时 | 队列积压或模型推理过慢 | 增加实例数量,调整超时时间 |
6.2 视频转码相关问题
| 问题现象 | 常见原因 | 解决思路 |
|---|---|---|
| 找不到 nvenc 编码器 | FFmpeg 未开启硬件编码支持 | 安装带 nvenc 的 FFmpeg 版本 |
| 转码过程报 CUDA error | 显存不足或驱动异常 | 关闭其他 GPU 任务,重启服务后重试 |
| 画面花屏 | 解码/编码硬件不兼容 | 检查视频编码格式,必要时软解硬编 |
| 转码后音画不同步 | 音频处理方式和滤镜顺序错误 | 音频改为 copy 或确保滤镜图正确 |
| 多 GPU 并发时性能下降 | 显存带宽或 PCIe 带宽不足 | 降低并发数,优先使用 NVENC 编码 |
6.3 通用排查流程
遇到问题建议按以下顺序排查:
- 查看错误日志,确认是软件层还是硬件层。
-
用
nvidia-smi检查 GPU 状态和显存占用。 - 用最简单的命令复现问题,例如只转码 5 秒视频。
- 检查输入文件是否有损坏。
- 查看 FFmpeg 或推理框架的文档,确认参数是否支持当前版本。
7. 最佳实践与工程建议
7.1 任务划分与优先级
中配集群同时承载推理和转码时,建议按 GPU 卡或时间窗口划分任务类型。比如白天以推理服务为主,深夜跑批量转码任务。如果 GPU 数量较少,可以把某些卡固定用于推理,某些卡固定用于转码,避免显存竞争导致服务不稳定。
7.2 容器化部署
推理服务和转码任务都建议容器化。
推理服务 Dockerfile 示例:
FROM nvidia/cuda:12.2.0-base-ubuntu22.04
RUN apt update && apt install -y python3 python3-pip
WORKDIR /app
COPY requirements.txt .
RUN pip install -r requirements.txt
COPY . .
CMD ["python", "-m", "vllm.entrypoints.openai.api_server", "--model", "/models/llm", "--port", "8000"]
转码 worker 容器可以基于
linuxserver/ffmpeg
或官方
ffmpeg
镜像扩展。容器化最大的好处是环境固定,生产环境不容易出现“本地能跑,线上跑不了”的尴尬。
7.3 监控与告警
集群上跑的是长期任务,监控不能少。至少需要关注:
- GPU 利用率、显存占用、温度。
- 推理服务 QPS、平均延迟、错误率。
- 转码任务成功率、平均处理时长、失败原因。
可以使用 Prometheus + Grafana 组合,nvidia-exporter 负责导出 GPU 指标。
7.4 视频素材与结果管理
转码任务往往涉及大量大文件,建议统一使用对象存储存放输入和输出。转码任务做成异步队列的方式,让 worker 从对象存储下载素材,转码完成后上传结果。这样既方便扩展,也避免单个磁盘塞满。
7.5 安全与权限
如果是团队共用集群,需要在 Docker 或 Kubernetes 层面做资源隔离。涉及外部视频素材时,要注意版权和隐私合规问题,不要在未经授权的情况下处理他人视频。
8. 总结与下一步学习方向
这篇文章围绕中配 AI 集群“换岗”的话题,重点介绍了两个方向:本地 LLM 推理和视频转码。你可以看到,训练时代非常吃紧的显存和带宽,在推理和转码场景下正好够用。量化技术让模型变小,NVENC 让转码吞吐大幅提升,而 Python 多线程或分布式队列把集群资源串了起来。
接下来值得继续研究的方向包括:
- 熟悉 vLLM 的 PagedAttention 原理,以及如何调优吞吐量和延迟。
- 尝试在 Kubernetes 上部署推理服务和转码 worker,实现自动扩缩容。
- 学习 FFmpeg 滤镜图语法,处理复杂剪辑需求。
- 探索更高效的模型量化方式,比如 AWQ、GPTQ 与 llama.cpp 的取舍。
- 研究 CPU 与 GPU 混合调度,把集群空闲资源填满。
中配集群确实很难再回到“大模型训练”这个岗位了,但推理和转码这两个新任务的前景足够广。把它们跑好,集群依然能在业务中发挥实际价值。你也可以根据自己手头的 GPU 情况,先复制本文的示例环境跑一遍,再逐步调整参数。遇到具体问题,欢迎在评论区留言交流。
更多推荐
所有评论(0)