使用 ModelScope 管理本地模型缓存

太阳作者太阳
原创内容采用 CC-4.0 协议发布,转载请注明出处
ModelScopeGGUFQwenllama.cpp模型管理本地部署

我原先只把 ModelScope 当成下载命令用,模型多起来后,缓存里同时出现了旧目录、新目录和中断下载留下的临时文件。modelscope cache scan 显示约 128 GiB,磁盘统计却接近 160 GiB;同一个 Qwen3.6-27B 还出现了两份路径。

最后的处理很直接:确认没有需要保留的模型后,清空混杂的缓存,再用当前 CLI 重新下载。新版目录恢复正常,扫描结果也能按仓库列出。本文记录这套管理方式,使用的版本是 ModelScope 1.39.1modelscope-hub 0.2.0

用 uv tool 安装

ModelScope 是长期使用的命令行工具,我用 uv tool 给它单独建环境:

uv tool install modelscope

uv tool list --show-paths
modelscope --version
modelscope --help

如果已经安装,可以升级:

uv tool upgrade modelscope

modelscope --version 在这组安装中会同时涉及完整 SDK 与 Hub CLI。实际排查时不要只说“最新版”,把两边的版本都记下来:

uv tool run --from modelscope python -c \
  "import modelscope, modelscope_hub; print('modelscope', modelscope.__version__); print('modelscope-hub', modelscope_hub.__version__)"

固定缓存根目录

模型文件较大,我不使用默认缓存,而是通过环境变量固定到模型盘:

echo 'export MODELSCOPE_CACHE="/path/to/modelscope"' >> ~/.bashrc
source ~/.bashrc

printf '%s\n' "$MODELSCOPE_CACHE"
modelscope list --envs

设置好 MODELSCOPE_CACHE 后,下载、扫描、校验和清理都会使用同一个根目录。正常命令不需要反复追加 --cache-dir

--cache-dir 是单次覆盖参数,适合临时测试:

modelscope cache scan --cache-dir /path/to/another-cache

如果使用 --local-dir,文件会直接下载到指定目录,绕过受管理的缓存布局。想让 cache scancache clear 统一管理模型时,不要混用 --local-dir

查看本地模型

先让 ModelScope 扫描缓存:

modelscope cache scan

清理并重新下载后,我的缓存能正确识别两个仓库:

repo_id                   repo_type  revision  files  size
unsloth/Qwen3.8-27B-GGUF  model      master    2      16.2 GiB
unsloth/Qwen3.6-27B-GGUF  model      master    2      16.5 GiB

还可以核对文件完整性:

modelscope cache verify unsloth/Qwen3.6-27B-GGUF

cache scan 统计的是 ModelScope 能识别的仓库,不等于缓存根目录下所有文件的磁盘占用。两者差距明显时,再看完整目录:

du -sh -- "$MODELSCOPE_CACHE"
du -h --max-depth=3 -- "$MODELSCOPE_CACHE" | sort -h

下载未完成的临时文件、旧版缓存和手工放进去的文件可能不会作为正常仓库计入扫描结果。这就是扫描显示 128 GiB、目录实际占用约 160 GiB 的原因之一。

新版缓存路径

modelscope-hub 0.2.0 的受管理模型目录是:

$MODELSCOPE_CACHE/models/{OWNER}--{REPO}/snapshots/{REVISION}/

例如:

$MODELSCOPE_CACHE/models/unsloth--Qwen3.6-27B-GGUF/snapshots/master/

当前 Hub 实现会把仓库 ID 中的 / 替换为 --,并把具体版本放进 snapshots。旧版 ModelScope 使用过另一套结构:

$MODELSCOPE_CACHE/models/{OWNER}/{REPO_WITH_DOTS_REPLACED_BY_TRIPLE_UNDERSCORES}/

例如 Qwen3.6 可能变成 Qwen3___6。为了兼容 1.37 及更早版本,新版下载器仍会探测非空的旧目录并复用它。机器上先有旧缓存、后来又用新布局下载,就可能看到名称相近的两份模型。这不是 Qwen3.6 有两个正式版本,而是缓存布局变过。

旧缓存还会影响扫描粒度。我遇到过这样的结果:扫描 $MODELSCOPE_CACHE 时只显示 unsloth,临时把 $MODELSCOPE_CACHE/models 传给 --cache-dir 后,才看到下面的具体仓库。后一个命令可以用来诊断旧目录,但不能据此把环境变量改成 .../modelscope/models。清空混杂缓存并按新版重新下载后,直接运行 modelscope cache scan 就能从正确的根目录识别仓库。

只下载 llama.cpp 需要的文件

我准备用 llama.cpp 运行 GGUF,不需要把仓库中的所有量化版本下载回来。仓库 ID 使用位置参数,--include 只选需要的文件:

modelscope download unsloth/Qwen3.6-27B-GGUF \
  --include "*Q4_K_M*" "*mmproj-F16*"

modelscope download unsloth/Qwen3.8-27B-GGUF \
  --include "*Q4_K_M*" "*mmproj-F16*"

本次实际下载结果如下:

Qwen3.6-27B-Q4_K_M.gguf     16817244384 bytes
mmproj-F16.gguf               927607360 bytes

Qwen3.8-27B-UD-Q4_K_M.gguf  16464440224 bytes
mmproj-F16.gguf               927607488 bytes

文件名里的 UD 是发布者使用的量化命名,不改变它作为主模型权重的用途。启动 Qwen3.8 时要照实际文件名填写,不能把 Qwen3.6 的命名规则硬套过去。

Q4_K_M 为什么适合作为默认选择

Q4_K_M 是 llama.cpp 的 K-quant 混合量化。它不是把每个张量都机械压成同一种四位格式,而是按量化规则给部分重要张量保留更高精度。相同模型下,它通常比 Q5、Q6 和 Q8 更省磁盘与内存,又比更激进的 Q2、Q3 少一些质量损失。

llama.cpp 自己在按量化类型选取 Hugging Face 文件时,也把 Q4_K_M 作为默认值。对 24GB 显存运行 27B 模型来说,它是我更愿意先试的平衡点。这里的“平衡”来自文件大小、内存占用和输出质量之间的取舍,不代表每个模型、后端和任务上都最快或最好。

如果显存充足而且更看重质量,可以试 Q5_K_M 或 Q6_K;内存吃紧时再考虑更小的量化。选择前先看具体文件大小,不要只看名称中的位数。

mmproj-F16.gguf 的作用

Qwen3.6-27B-Q4_K_M.gguf 是语言模型主体。mmproj-F16.gguf 是多模态投影器,负责把图片编码结果映射到语言模型能处理的表示。

需要视觉输入时,两者一起传给 llama-server:

./build/bin/llama-server \
  --model "$MODELSCOPE_CACHE/models/unsloth--Qwen3.6-27B-GGUF/snapshots/master/Qwen3.6-27B-Q4_K_M.gguf" \
  --mmproj "$MODELSCOPE_CACHE/models/unsloth--Qwen3.6-27B-GGUF/snapshots/master/mmproj-F16.gguf"

只做文本推理时可以不下载它,并在启动时使用 --no-mmproj。mmproj 不是第二份语言模型,也不能脱离对应模型单独推理。

用 ModelScope 清理缓存

优先使用 ModelScope 自己的清理命令。删除单个模型:

modelscope cache clear \
  --repo-id unsloth/Qwen3.6-27B-GGUF \
  --repo-type model \
  --yes

清理所有受管理的模型缓存:

modelscope cache clear --repo-type model --yes

数据集是另一种 repo type,需要单独处理:

modelscope cache clear --repo-type dataset --yes

不加 --yes 时会保留确认步骤,第一次操作更适合这样做。清理前先运行 modelscope cache scan,确认 repo ID 和类型。

什么时候需要完整重建缓存

下面几种情况叠在一起时,逐个仓库清理往往比完整重建更麻烦:

  • 同时存在旧版 owner/repo___name 与新版 owner--repo/snapshots
  • 出现 .___temp、中断下载或扫描无法识别的目录。
  • ducache scan 相差几十 GiB,确认差值不是仍需保留的普通文件。
  • 受管理清理完成后,磁盘占用仍没有明显下降。

完整删除前先解析并打印目标路径,不要对空变量、/、用户主目录或整个挂载点执行递归删除:

CACHE_ROOT="$(realpath -m -- "${MODELSCOPE_CACHE:?MODELSCOPE_CACHE 未设置}")"
printf '准备重建缓存:%s\n' "$CACHE_ROOT"
du -sh -- "$CACHE_ROOT"
modelscope cache scan

确认输出确实是专用的 ModelScope 缓存目录,停止所有正在下载或读取模型的进程,再做一次路径保护和人工确认:

case "$CACHE_ROOT" in
  /|/home|/root|/vol1|/mnt|/data|"$HOME")
    printf '拒绝删除过宽的路径:%s\n' "$CACHE_ROOT" >&2
    exit 1
    ;;
esac

read -r -p "输入完整路径 $CACHE_ROOT 以确认删除:" CONFIRM_PATH
[ "$CONFIRM_PATH" = "$CACHE_ROOT" ] || exit 1

rm -rf --one-file-system -- "$CACHE_ROOT"
mkdir -p -- "$CACHE_ROOT"

缓存中的模型需要重新下载;混在里面的自有文件不会自动恢复,所以不要把其他资料放进这个根目录。

重建后重新执行下载和扫描:

modelscope download unsloth/Qwen3.6-27B-GGUF \
  --include "*Q4_K_M*" "*mmproj-F16*"

modelscope cache scan

这次完整重建后,Qwen3.6 和 Qwen3.8 都落在新版 owner--repo/snapshots/master 路径,扫描总量与所下载的四个文件相符。

参考资料