1. 为什么下载 HuggingFace 资源会这么折腾
1.1 站点直连慢、文件碎、重试机制差,先理解病因再开药
先说个真实的场景:我之前跑一个多模态 demo,需要拉取 CLIP 的 ViT-L/14 模型,模型文件加起来大概 2.5GB 左右。按道理这大小不算离谱,但直连 huggingface.co 下载,速度经常掉到几十 KB/s,中途还会断。最离谱的是,我盯着终端看它下到 90% 然后报错退出,再跑一次又从 0% 开始。重复了三次,耐心直接归零。
这种体验不是个例。HuggingFace 生态现在的核心资产分两大类,一类是models(深度学习模型权重),另一类是datasets(数据集)。这两类文件都有几个共性特点:
- 文件体积大:模型动辄几个 GB,数据集更是可以到几十 GB 甚至 TB 级
- 文件数量碎:一个 repo 里可能包含 config.json、tokenizer.json、分片权重等几十上百个文件
- 下载协议多:官方支持 HTTPS 直链、Git LFS 两种方式,但 Git LFS 默认走的还是境外服务器
问题根源在于,文件托管服务器和你本机之间的网络链路质量不稳定,再加上 HuggingFace 默认的下载工具重试策略不够激进,一旦某个分片超时,整个下载任务就卡住。很多人以为是自己网络不行,但其实换一个下载通道,速度能翻几十倍。所以先别急着怪运营商,后面的镜像站方案基本能解决掉 90% 的抱怨。
1.2 先弄清你要下的是模型还是数据集,两条链路不一样
很多人一上来就找"通用命令",但 models 和 datasets 的下载逻辑其实有差别,搞清楚分类能省不少事。
模型仓库通常体积小、文件层级简单,核心文件是权重分片(.bin或.safetensors)。用huggingface-cli download可以直接搞定,而且现在默认就是逐文件下载,不会把整个仓库打包成 zip,所以内存占用很友好。
数据集仓库则复杂得多,常见结构是data/目录下挂着大量 parquet、jsonl 或图像文件夹,而且 HuggingFace 的数据集中心有一套独立的datasets-server存储结构,有时候你看到的是一个个单独的 parquet 分片 URL,而不是一个连续的文件流。这种情况下,单纯用官方 CLI 反而可能慢,因为官方 CLI 会为每个文件建立单独的 HTTPS 连接,文件数量一多,握手开销就会被放大。
另外,如果你用的是datasets库的load_dataset方法,它默认会走 HuggingFace Hub 的 API 去解析数据集配置,再通过自动缓存机制下载到本地。这个过程的潜规则是:它不一定会把所有文件都下载下来,而是按你的split参数按需取用。如果只是想要全量数据,不如直接把数据仓库 clone 下来,或者用huggingface-cli download --repo-type dataset。
一句话总结:模型优先用官方 CLI,数据集优先考虑镜像站 + 批量下载工具。这是我和几个同事踩了无数坑后总结出的分工策略。
2. 最基础的官方方案:huggingface-cli 讲清楚
2.1 安装、登录与 token 配置,这几步不能跳
先装huggingface_hub包,新版 CLI 已经集成在里面:
pip install -U huggingface_hub huggingface-cli version如果你还在用旧版的transformers包,可能 CLI 名字是transformers-cli,但新项目基本都统一到huggingface-cli了。
接下来是登录。访问 huggingface.co/settings/tokens 创建一个 Access Token,然后执行:
huggingface-cli login它会让你粘贴 token。粘贴之后会在本地~/.cache/huggingface/目录下生成一个token文件,后续所有 SDK 调用都会自动读取,不需要重复登录。
这里有个细节:公开模型的普通下载其实不需要 token,只有 gated model(比如 Llama 系列)才必须登录。但建议还是配好 token,因为不登录状态下,某些 API 请求会走未认证的限速通道,大文件下载容易被限速。我实测过同一个 4GB 模型,登录后下载速度比匿名下载要稳定不少,可能是服务端给认证用户分配了更高的带宽优先级。
2.2 下载命令:模型、数据集、单个文件,三种用法各有说道
模型仓库的完整下载命令是:
huggingface-cli download <repo_id> --local-dir ./model_cache比如:
huggingface-cli download openai/clip-vit-large-patch14 --local-dir ./clip_model这个命令会拉取仓库里所有 LFS 文件和非 LFS 文件,并且自动跳过本地已经存在的文件。注意,默认的缓存目录是~/.cache/huggingface/hub,如果你用--local-dir指定目录,那么文件会直接平铺到那个目录,不会经过默认缓存,适合后续要手动部署模型的情况。
数据集仓库要在命令后追加--repo-type dataset:
huggingface-cli download zuphilip/zh-wikipedia --repo-type dataset --local-dir ./zhwiki还有很多数据集是分片形式,比如webdataset格式的 tar 文件。用上面的命令也能下全。但我遇到过一个坑:某些超大数据集,官方 CLI 在下载过程中创建临时文件名时会对特殊字符转义,落地后文件名和原仓库不一致。这时候打开数据仓库页面,直接用wget拉文件反而更干净。
单文件下载可以指定文件名:
huggingface-cli download <repo_id> --include "*.safetensors" --local-dir ./models--include支持通配符,这是一个特别实用的小技巧。比如你只想要 fp16 后缀的权重,或者只想要两个分片文件,就不用把整个仓库全拉下来。
2.3 断点续传与缓存目录的心智模型
新版huggingface_hub(0.20 以上)对断点续传做了很多优化。默认情况下,如果下载中途断了,重新执行同样的命令,它会复用已经下载完成的.incomplete分片,而不是从零开始。
这个机制叫 "resume download",底层是分块下载,每个块几十 MB,已经完成的块会缓存到.cache/huggingface/download目录下。所以你下次再执行时,不用慌,直接重新跑命令就行。
但要注意:不要手动删掉.incomplete后缀的文件,如果你在别的工具里看到file.bin.incomplete,以为它是垃圾文件就删了,等于前功尽弃。它是断点续传的中间产物,只有下载完才自动消失。
另外,如果你用--local-dir指定了目标目录,缓存和最终文件会在同一个目录下共存。比如 repo 里某个文件下载完,本地会看到file.safetensors和一个隐藏的.cache目录。这是正常形态,不用清理。
3. 国内环境的最优解:hf-mirror.com 镜像站
3.1 一个环境变量搞定 90% 的下载问题
这是整个文章最核心的干货。HuggingFace 官方文档里其实藏着一个针对非欧美用户的"低调"方案:设置环境变量HF_ENDPOINT,把下载请求指向镜像站。
export HF_ENDPOINT=https://hf-mirror.com设置完之后,huggingface-cli、transformers、datasets库的所有下载请求都会自动打到这个镜像站。它的目录结构与官方完全一致,repo id、文件名、commit hash 都不用改,直接换前缀。
比如:
export HF_ENDPOINT=https://hf-mirror.com huggingface-cli download openai/clip-vit-large-patch14 --local-dir ./clip_model后续所有 Python 代码里的model = CLIPModel.from_pretrained("openai/clip-vit-large-patch14")也会自动走镜像,不需要改任何代码。
这个方案为什么好使?因为HF_ENDPOINT是 HuggingFace 官方 hub 客户端库本身支持的环境变量,属于合法配置接口,不是 hack。镜像站国内直连速度快,我自己测试下来,同一个模型在镜像站下载速度可以稳定跑满本地带宽,而直连站经常在 1MB/s 以下。
提示:如果你在某个群聊或教程里看到
HF_MIRROR之类的变量名,那是老版本用的,现在官方统一用HF_ENDPOINT。两个都设也行,但以HF_ENDPOINT为准。
3.2 不依赖环境变量的备用方案:URL 替换法
有些场景你没法设环境变量,比如在云函数里、临时 SSH 会话里,或者你只是想在浏览器里快速拿一个文件。这时候直接改写 URL 就行。
原始官方文件地址通常是:
https://huggingface.co/bert-base-uncased/resolve/main/config.json把域名从huggingface.co替换成hf-mirror.com:
https://hf-mirror.com/bert-base-uncased/resolve/main/config.json浏览器直接访问这个链接就能下载。这个规则公式化一下:
https://hf-mirror.com/<repo_id>/resolve/<branch>/<file_path>如果要在服务器上用wget批量下载,可以用这种方式手动拼 URL。我经常在写一次性脚本时用这个方案,因为不需要引入 huggingface_hub 依赖,只要一个requests库就够了。
3.3 离线包与手动下载思路
如果目标环境完全不允许执行 Python 代码,或者网络条件极端,可以试试在本地提前下载好,再通过其他方式传输。我常用的做法是:
- 在本地用
huggingface-cli download <repo_id> --local-dir ./model_pack - 将
model_pack目录打包成 tar.gz - 传输到目标机器解压
- 在目标机器上用本地路径加载模型:
model = CLIPModel.from_pretrained("./model_pack")这种方法的好处是可以完全摆脱对目标机器网络环境的依赖,坏处是文件转移本身要花额外时间,但在受限环境里这是最稳的一条路。
还有一个小实践:有些 datasets 仓库在网页上提供了datasets-server的 API 接口,可以直接通过https://datasets-server.huggingface.co/rows?dataset=xxx&config=xxx&split=xxx&offset=0&length=100这样的 URL 拉取小块数据。你要是只做小规模实验,根本不需要下载全量数据,直接调 API 取几百行就够了。
4. 提速组合拳:hf_transfer + 并发工具
4.1 hf_transfer 为什么能跑满带宽
官方 CLI 默认用httpx做下载,单连接速度有限。HuggingFace 官方后来出了一个hf_transfer加速库,底层用 Rust 实现,核心思路就是把一个大文件切成多个分片并行下载,再在本地拼接。实测在某些网络环境下,下载速度能提升 5-10 倍。
安装:
pip install hf_transfer然后设置环境变量开启:
export HF_HUB_ENABLE_HF_TRANSFER=1用之前必须明白它的几个缺点:
hf_transfer的发散重试策略较弱,网络抖动严重时反而容易失败- 它不支持从本队其他进程复用缓存,多进程同时下载同一文件会出现锁冲突
- 部分旧版本和代理环境不兼容
所以我给它的定位是:网络稳定时锦上添花,网络本身就烂时不要启用。你先用镜像站测速,如果镜像站能跑到几 MB/s 以上,再决定是否叠加hf_transfer。
我自己测试过几种组合,实际体验排序大致是:
| 方案 | 稳定性 | 极限速度 | 适用场景 |
|---|---|---|---|
| 直连 + 默认 CLI | 差 | 慢 | 不推荐 |
| 镜像站 + 默认 CLI | 高 | 中等 | 日常最稳 |
| 镜像站 + hf_transfer | 中 | 高 | 大模型/快网络 |
| 镜像站 + aria2 | 高 | 高 | 数据集批量拉取 |
4.2 数据集大批量下载:wget/aria2 方案
数据集仓库的文件列表往往很长,用huggingface-cli一个个建立连接比较吃亏。我建议的做法是:
第一步,先看仓库文件树:
huggingface-cli download <repo_id> --repo-type dataset --local-dir ./data --include "*" --print-paths--print-paths可以打印所有需要下载的文件路径,但不会真下载。把输出保存成文本:
huggingface-cli download <repo_id> --repo-type dataset --local-dir ./data --include "*" --print-paths > filelist.txt第二步,用wget的-i参数批量下载:
wget -x -nH -i filelist.txt -P ./data --continue这一步的关键是-c(或--continue)开启断点续传,以及-x -nH保持目录结构的同时去掉域名目录。如果你机器上有aria2c,可以用并行下载:
aria2c -x 16 -s 16 -j 8 -i filelist.txt -d ./data --dir=./data-x 16表示每个服务器最多 16 个连接,-j 8表示同时下载 8 个文件。这种方式在文件数量很多时,比官方 CLI 明显更快,而且中断后重新跑同一命令,已经下好的文件会跳过。
不过要注意,wget批量模式下文件名中的特殊字符可能有问题,遇到%20这种 URL 编码,建议先做一次 URL 解码。我写过一个很小的 Python 脚本,把filelist.txt里的 URL 逐行规范化再交给 wget,坑少很多。
4.3 使用 Python 代码批量下载与增量更新
如果你的项目本身用 Python 开发,直接在脚本里用 huggingface_hub 的 API 是更优雅的方式。核心接口是snapshot_download:
from huggingface_hub import snapshot_download snapshot_download( repo_id="openai/clip-vit-large-patch14", local_dir="./clip_model", repo_type="model", ignore_patterns=["*.md", "*.txt"], max_workers=4, )这个接口的好处是:
- 自动跳过本地已有文件,天然支持增量更新
max_workers控制并发线程数ignore_patterns过滤不需要的文件
如果想精确控制下载哪些文件,用hf_hub_download:
from huggingface_hub import hf_hub_download hf_hub_download( repo_id="bert-base-uncased", filename="config.json", local_dir="./bert_base", )如果再配合tqdm写一个进度条,体验就很接近专业工具了。
增量更新是我特别看重的一个能力。很多模型会经常发布新版本,比如微调后重新上传权重,但你本地已经有旧文件,如果直接全量重下就浪费带宽。snapshot_download会先和远程对比文件列表,只拉取新增或变更的文件,实测在只新增一个文件的情况下,秒级完成确认流程。
5. 长期踩坑后的常见问题速查
5.1 SSL 证书错误:Ubuntu 上最常见的坑
很多人在 Linux 服务器上跑load_dataset,会遇到这样的报错:
SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: unable to get local issuer certificate (_ssl.c:1000)这个问题的根源通常是服务器缺少系统证书,或者 Python 没找到证书链。三种解决思路:
第一种,更新系统 CA 证书:
sudo apt-get update sudo apt-get install ca-certificates sudo update-ca-certificates第二种,指定 Python 证书路径。如果你的 Python 是源码编译安装的,它可能找不到系统的证书文件,手动指定即可:
export REQUESTS_CA_BUNDLE=/etc/ssl/certs/ca-certificates.crt第三种,如果你只是想快速验证功能,可以在代码里临时关闭证书校验:
from huggingface_hub import hf_hub_download hf_hub_download( repo_id="bert-base-uncased", filename="config.json", local_dir="./bert", endpoint="https://hf-mirror.com", )但关闭校验有风险,建议只是在调试时用,不要在生产环境长期挂着。另外,如果用的是datasets库,它底层走requests,可以通过环境变量HF_DATASETS_OFFLINE=1强制离线模式,绕开网络验证。
5.2 unexpected endpoint or method 报错
这个报错我在某些 API 调用场景遇到过,尤其在配合 OpenAI 兼容接口或者自定义推理服务时:
unexpected endpoint or method. (options /v1/models). returning 200 anyway核心原因一般是你在调用模型服务时,URL 路径匹配出了问题。比如服务端实现了/v1/models接口,但客户端请求打到了/v1/chat/completions,或者反过来。它本质上是路由层面的不匹配,不是 HuggingFace 下载本身的问题。
排查思路分三步:
- 第一步,确认你组装 URL 时是否拼错了前缀,比如少了
/v1 - 第二步,确认服务端的路由注册表里有没有对应路径,很多轻量框架默认只有
/v1/models - 第三步,如果是用
huggingface_hub的 InferenceClient,检查endpoint参数是否指向了不兼容的服务地址
这个报错和 HuggingFace 下载链路没什么关系,但因为它出现在结合模型部署的场景中,我遇到过好几次,所以提一下,免得大家排查半天方向错了。
5.3 文件校验失败、下载一半断掉、缓存锁冲突
文件校验失败是最让人崩溃的坑。报错往往是:
FileNotFoundError: [Errno 2] No such file or directory: '/root/.cache/huggingface/hub/models--xxx/snapshots/xxx/config.json'或者更常见的:
An error happened while trying to locate the file on the Hub这类问题的成因基本逃不过三种:
- 下载中断后缓存文件损坏,但
.incomplete文件还在 - 多进程同时往同一个缓存目录写文件,导致哈希索引和实际文件不匹配
- 磁盘空间不足,文件写入一半失败
解决办法也很粗暴但有效:删掉本地缓存目录,重新下。如果你知道具体 repo 的缓存位置,比如~/.cache/huggingface/hub/models--bert-base-uncased,直接删这个子目录就行,不用所有缓存都清掉。
如果要在多进程环境里下载,记得给每个进程设置不同的缓存目录,用环境变量HF_HOME或HUGGINGFACE_HUB_CACHE隔离:
export HF_HOME=/data/hf_cache/worker_15.4 几个真正有用的进阶参数
最后整理几个我日常使用的参数和组合,能省很多心:
| 参数/命令 | 作用 |
|---|---|
--local-dir | 指定下载目录,文件平铺,不经过默认缓存 |
--force-download | 强制重新下载,忽略本地文件 |
--resume-download | 开启断点续传(新版默认开启) |
HF_HUB_DISABLE_TELEMETRY=1 | 关闭遥测上报,避免一些奇怪的超时 |
HF_DATASETS_OFFLINE=1 | datasets 库离线模式 |
HF_HUB_ENABLE_HF_TRANSFER=1 | 启用 Rust 加速下载 |
注意--force-download是一把双刃剑,它会连config.json都重新拉一遍。如果只更新权重文件,建议先用--print-paths看清楚文件列表,再精确匹配。
另外,如果你在 Docker 容器里跑,别忘了一个冷门坑:容器时区不对可能会导致 SSL 会话票据验证失败。这个我排查过一次,后来所有镜像里都会统一设TZ=Asia/Shanghai,换个时区问题就消失了。听起来不可思议,但确实遇到过。
6. 总结一下我绕弯路的经验
这几条建议都是我和团队在实际项目中反复验证过的:先设HF_ENDPOINT=https://hf-mirror.com,能解决国内大部分下载问题;文件多、体积大时用 wget 或 aria2 接管;真正追求速度再叠加hf_transfer,但稳定性优先时别开;遇到 SSL 报错先查系统证书,别一上来就改代码绕过校验。
最后说一个我个人的习惯:不管是模型还是数据集,下载完成后我都会立刻用snapshot_download跑一遍,看它能不能秒完成跳过。如果能,说明文件完整;如果不能,说明有文件缺失或哈希不一致,趁早清缓存重下,别留着隐患。这套流程下来,基本不会再被 HuggingFace 下载折磨。