如果你手头有一批“中配”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 通用排查流程

遇到问题建议按以下顺序排查:

  1. 查看错误日志,确认是软件层还是硬件层。
  2. 用 nvidia-smi 检查 GPU 状态和显存占用。
  3. 用最简单的命令复现问题,例如只转码 5 秒视频。
  4. 检查输入文件是否有损坏。
  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 情况,先复制本文的示例环境跑一遍,再逐步调整参数。遇到具体问题,欢迎在评论区留言交流。

Logo

邀请您加入社区

更多推荐