其他分类 · 12 9 月, 2026 0

使用 llama.cpp 在 Windows 本地部署多模态大模型

本文介绍如何在 Windows 上使用 llama.cpp 部署 GGUF 大语言模型,并加载独立的 mmproj 文件启用图像理解能力。文中的命令和参数以 NVIDIA GTX 1080 + Windows 为例,也适用于其他 NVIDIA 显卡。

1. 准备模型文件

一个使用独立视觉投影器的多模态模型通常至少包含两个 GGUF 文件:

Qwen3.6-35B-A3B-Uncensored-HauhauCS-Aggressive-IQ2_M.gguf
mmproj-Qwen3.6-35B-A3B-Uncensored-HauhauCS-Aggressive-f16.gguf

其中:

  • 主 GGUF 文件包含语言模型权重。
  • mmproj 是视觉编码器/多模态投影器,负责把图片转换成语言模型可以理解的特征。
  • mmproj 不是一个可以单独对话的语言模型,必须和兼容的主模型配套使用。

建议把两个文件放在同一个模型目录中,例如:

D:\APP\OLLAMA_MODELS\import\Qwen3.6-35B-A3B-Uncensored

模型文件较大,启动前请确认磁盘空间充足,并确认下载没有损坏。发布者如果提供 SHA256 校验值,建议额外执行校验。

2. 检查 Windows 环境

检查 NVIDIA 驱动:

nvidia-smi

如果使用 NVIDIA GPU,驱动报告的 CUDA 版本应不低于所下载 llama.cpp 构建所需的 CUDA 版本。本文使用 CUDA 12.4 构建,在 CUDA 12.8 驱动环境中运行。

注意:llama.cpp 的 CUDA 预编译包不要求安装完整 CUDA Toolkit,但部分 Windows Release 会把 CUDA runtime 作为单独的压缩包发布。下载 CUDA 主程序包后,还要下载同一 Release、同一 CUDA 版本的配套包,例如:

cudart-llama-bin-win-cuda-12.4-x64.zip

把这个压缩包中的 DLL 解压到 llama-server.exe 所在目录。CUDA 12.4 x64 包通常包含:

cublas64_12.dll
cublasLt64_12.dll
cudart64_12.dll

如果漏掉 runtime DLL,程序目录里虽然有 ggml-cuda.dll,llama-server 仍可能没有加载 CUDA 后端,最终在 CPU 上运行。使用 CPU 版构建时不需要下载这个 CUDA runtime 包。从源码编译则需要额外准备 CUDA Toolkit、CMake 和编译器。

3. 下载 llama.cpp

llama.cpp 官方 GitHub Release 会提供 Windows 预编译包。对于 NVIDIA 显卡,选择类似下面的 CUDA x64 包:

llama-bXXXXX-bin-win-cuda-12.4-x64.zip

不要选择 ARM64 包。将压缩包解压到一个固定目录,例如:

D:\APP\llama.cpp

解压完成后,目录中应该能看到:

llama-server.exe
llama-cli.exe
ggml-cuda.dll
cublas64_12.dll
cublasLt64_12.dll
cudart64_12.dll

上面三个 CUDA runtime DLL 来自同一 Release 的 cudart-llama-bin-win-cuda-12.4-x64.zip,不是 CUDA Toolkit 安装器。确认实际选用的版本后,替换文件名中的版本号。

可以这样确认服务程序可用:

Set-Location D:\APP\llama.cpp
.\llama-server.exe --help

检查多模态参数:

.\llama-server.exe --help | Select-String mmproj

正常情况下可以看到 --mmproj、--mmproj-offload 等参数。

4. 启动多模态模型服务

最基本的启动命令如下:

Set-Location D:\APP\llama.cpp

.\llama-server.exe `
  --model "D:\APP\OLLAMA_MODELS\import\Qwen3.6-35B-A3B-Uncensored\Qwen3.6-35B-A3B-Uncensored-HauhauCS-Aggressive-IQ2_M.gguf" `
  --mmproj "D:\APP\OLLAMA_MODELS\import\Qwen3.6-35B-A3B-Uncensored\mmproj-Qwen3.6-35B-A3B-Uncensored-HauhauCS-Aggressive-f16.gguf" `
  --host 127.0.0.1 `
  --port 8080 `
  --ctx-size 4096 `
  --n-gpu-layers 15 `
  --mmproj-offload

参数说明:

参数 作用
--model 主语言模型 GGUF 文件路径
--mmproj 多模态投影器 GGUF 文件路径
--host 127.0.0.1 只允许本机访问,避免直接暴露到局域网
--port 8080 HTTP 服务端口
--ctx-size 4096 上下文长度。显存不足时应先使用较小值
--n-gpu-layers 15 放入 GPU 的模型层数
--mmproj-offload 尝试把多模态投影器放入 GPU

GPU 层数怎么设置

--n-gpu-layers 越大,通常速度越快,但显存占用也越高。显存不足时,降低这个值即可;剩余层会在 CPU 上运行。

可从较小值开始逐步增加 GPU 层数,每次启动后检查显存和是否出现 CUDA out of memory。不要直接照抄固定的递增序列:模型结构、上下文长度、投影器和显卡上其他程序都会影响显存需求。

本文示例中的 GTX 1080 有 8 GB 显存;加载约 11.7 GB 的 35B IQ2_M 模型时,--n-gpu-layers 15 已占用约 6.9 GB 显存,因此不建议盲目提高层数。8 GB 显存无法完整容纳这套模型,剩余权重和部分运算仍会使用 CPU 与系统内存,这是预期的混合运行方式。

5. 检查服务状态

启动后检查健康接口:

Invoke-WebRequest `
  -Uri "http://127.0.0.1:8080/health" `
  -UseBasicParsing

模型完成加载后应返回:

{"status":"ok"}

模型仍在加载时可能返回 HTTP 503,这是正常现象,等待模型加载完成即可。

查看 OpenAI 兼容接口中的模型:

Invoke-WebRequest `
  -Uri "http://127.0.0.1:8080/v1/models" `
  -UseBasicParsing

启动日志中应出现类似信息:

loaded multimodal model
model loaded
listening on http://127.0.0.1:8080

其中 loaded multimodal model 是确认 mmproj 被正确加载的关键标志。

6. 使用 llama.cpp 自带 Web UI

较新的 llama.cpp Release 会提供独立的 Web UI 资源包,例如:

llama-bXXXXX-ui.tar.gz

将资源解压到:

D:\APP\llama.cpp\webui\llama-bXXXXX

然后在启动命令中增加:

--path "D:\APP\llama.cpp\webui\llama-bXXXXX"

完整示例:

.\llama-server.exe `
  --model "D:\APP\OLLAMA_MODELS\import\Qwen3.6-35B-A3B-Uncensored\Qwen3.6-35B-A3B-Uncensored-HauhauCS-Aggressive-IQ2_M.gguf" `
  --mmproj "D:\APP\OLLAMA_MODELS\import\Qwen3.6-35B-A3B-Uncensored\mmproj-Qwen3.6-35B-A3B-Uncensored-HauhauCS-Aggressive-f16.gguf" `
  --path "D:\APP\llama.cpp\webui\llama-bXXXXX" `
  --host 127.0.0.1 `
  --port 8080 `
  --ctx-size 4096 `
  --n-gpu-layers 15 `
  --mmproj-offload

浏览器打开:

http://127.0.0.1:8080

这个 Web UI 直接使用当前 llama-server,不需要 Docker、Node.js 或单独配置后端地址。

7. 使用 OpenAI 兼容 API

llama.cpp server 提供 OpenAI 风格接口,可被很多客户端和前端使用。

文本请求

$body = @{
  model = "Qwen3.6-35B-A3B-Uncensored-HauhauCS-Aggressive-IQ2_M.gguf"
  messages = @(
    @{
      role = "user"
      content = "用一句话介绍 llama.cpp。"
    }
  )
  max_tokens = 128
  stream = $false
} | ConvertTo-Json -Depth 8

Invoke-RestMethod `
  -Uri "http://127.0.0.1:8080/v1/chat/completions" `
  -Method Post `
  -ContentType "application/json" `
  -Body $body

图片请求

OpenAI 兼容的多模态消息使用数组形式的 content:

$body = @{
  model = "Qwen3.6-35B-A3B-Uncensored-HauhauCS-Aggressive-IQ2_M.gguf"
  messages = @(
    @{
      role = "user"
      content = @(
        @{
          type = "text"
          text = "请描述这张图片。"
        }
        @{
          type = "image_url"
          image_url = @{
            url = "data:image/png;base64,<BASE64_IMAGE_DATA>"
          }
        }
      )
    }
  )
  max_tokens = 256
  stream = $false
} | ConvertTo-Json -Depth 10

Invoke-RestMethod `
  -Uri "http://127.0.0.1:8080/v1/chat/completions" `
  -Method Post `
  -ContentType "application/json" `
  -Body $body

实际使用时,把 <BASE64_IMAGE_DATA> 替换为图片的 Base64 内容。许多兼容 OpenAI API 的桌面客户端也可以配置:

API Base URL: http://127.0.0.1:8080/v1
API Key: 任意非空字符串,或按客户端要求填写
Model: 使用 /v1/models 返回的模型名称

本文将服务绑定到 127.0.0.1,因此只能本机访问。如果要让局域网其他设备访问,需要改成:

--host 0.0.0.0

同时应该配置防火墙规则和 API 鉴权,不能把无鉴权的模型服务直接暴露到公网。

8. 使用启动脚本

为了避免每次重复输入长路径,可以创建 start-qwen3.6-vision.ps1:

$ErrorActionPreference = "Stop"
$llamaDir = "D:\APP\llama.cpp"
$model = "D:\APP\OLLAMA_MODELS\import\Qwen3.6-35B-A3B-Uncensored\Qwen3.6-35B-A3B-Uncensored-HauhauCS-Aggressive-IQ2_M.gguf"
$mmproj = "D:\APP\OLLAMA_MODELS\import\Qwen3.6-35B-A3B-Uncensored\mmproj-Qwen3.6-35B-A3B-Uncensored-HauhauCS-Aggressive-f16.gguf"
$webui = "$llamaDir\webui\llama-bXXXXX"

Set-Location $llamaDir
& "$llamaDir\llama-server.exe" `
  --model $model `
  --mmproj $mmproj `
  --path $webui `
  --host 127.0.0.1 `
  --port 8080 `
  --ctx-size 4096 `
  --n-gpu-layers 15 `
  --mmproj-offload

运行:

powershell -ExecutionPolicy Bypass `
  -File D:\APP\llama.cpp\start-qwen3.6-vision.ps1

9. 常见问题

--mmproj 参数不存在

说明使用的 llama.cpp 版本太旧,或者下载的不是完整的 llama-server 构建。升级到较新的官方 Windows Release,并执行:

.\llama-server.exe --help | Select-String mmproj

服务返回 503 Loading model

模型仍在加载。35B 模型和视觉投影器都需要读取大量文件,等待日志出现:

model loaded
listening on http://127.0.0.1:8080

再访问 /health。

设置了 --n-gpu-layers,但看起来仍在用 CPU

--n-gpu-layers 15 是请求 llama.cpp 尝试把最多 15 层放到 GPU,不代表整个模型都在 GPU 上。先检查 llama-server 进程是否真的加载了 CUDA 后端:

$p = Get-Process -Name llama-server
$p.Modules |
  Where-Object { $_.ModuleName -match 'ggml-cuda|cudart64_12|cublas64_12|cublasLt64_12' } |
  Select-Object ModuleName, FileName

CUDA 运行时正常时,结果应包含 ggml-cuda.dll、cudart64_12.dll 和 cuBLAS DLL。若只看到 ggml-cpu-*.dll,检查 CUDA runtime 压缩包是否与 llama.cpp 主程序来自同一 Release、CUDA 版本和 x64 架构,并确认其中的 DLL 已解压到 llama-server.exe 同目录。补齐 DLL 后必须完全退出并重新启动 llama-server。

也可以在模型加载后比较空闲显存与运行时显存:

nvidia-smi --query-gpu=memory.used,memory.total,utilization.gpu --format=csv -l 1

观察任务管理器时,应查看 GPU 的 CUDA 或 Compute 图表,而不是只看默认的 3D 图表。Windows WDDM 下,nvidia-smi 的进程列表有时不会清楚显示 CUDA 进程,因此要结合 llama-server 的已加载模块和显存变化判断。确认 CUDA 已加载后,CPU 和系统内存仍然繁忙并不代表 GPU 没工作:当模型大于显存时,llama.cpp 会让部分层留在 CPU 上。

CUDA out of memory

依次尝试:

  1. 降低 --n-gpu-layers。
  2. 降低 --ctx-size,例如改成 2048。
  3. 去掉 --mmproj-offload,让投影器留在 CPU。
  4. 关闭占用显存的其他程序。

页面打开但不能发送图片

检查以下几点:

  • 启动命令包含 --mmproj。
  • 日志中出现 loaded multimodal model。
  • 主模型和 mmproj 来自同一个模型版本。
  • 使用的是支持多模态消息的 Web UI 或 API 客户端。

模型只输出思考内容

某些模型默认启用 reasoning。可以提高 max_tokens,或者根据当前模型模板和 llama.cpp 版本关闭思考保留功能:

--no-reasoning-preserve

并非所有模型都适合强行关闭 reasoning,建议先观察模型实际行为。

Ollama 能否直接加载这两个文件

Ollama 和 llama.cpp 的模型导入方式不同。mmproj 不能作为普通 Modelfile 的 PARAMETER 使用;即使主模型能被 Ollama 注册,也不代表外置视觉投影器已被加载。对于带独立 mmproj 的 GGUF,llama.cpp 的显式参数更直接:

--model 主模型.gguf --mmproj 投影器.gguf

10. 安全和性能建议

  • 默认绑定 127.0.0.1,只允许本机访问。
  • 不要把无鉴权的服务直接暴露到公网。
  • 先使用较小上下文确认服务稳定,再逐步增大 --ctx-size。
  • 根据显存逐步调高 --n-gpu-layers,不要一开始就强行使用 --n-gpu-layers all。
  • 图片理解任务建议保留足够的 max_tokens,否则模型可能只输出思考过程就达到上限。
  • 保留主模型和 mmproj 的来源、量化方式和版本信息,避免混用不兼容组件。