跳到正文
原文
Hugging Face Blog·· 2025-02-12精选AI 评分60

Hugging Face 在 Hub 上线 Xet 内容定义分块,上传下载提速 2-3 倍

From Chunks to Blocks: Accelerating Uploads and Downloads on the Hub

AI 导读

Hugging Face 的 Xet 团队把内容定义分块(CDC)从理论推向生产,用于 Hub 上文件的上传与下载,部分场景提速 2-3 倍。方案在分块去重后聚合为最大 64MB 的块,使内容寻址存储条目减少 1000 倍,并用占全部 chunk 0.1% 的 key chunks 全局索引做查询。

推荐理由

原文给出块级聚合与 key chunks 的设计取舍,以及 gemma-2-9b-it-GGUF 仓库上传体积与耗时对比,可判断大文件传输的优化空间。

正文 · AI 翻译

内容定义分块(CDC)在实现 Xet 支持的仓库中的去重方面发挥着核心作用。其思路很直接:将每个文件的数据切分成块,只存储唯一的块,从而收获收益。

但在实践中,情况更为复杂。如果我们单纯追求最大化去重效果,那么设计上就应当采用尽可能小的块大小。但这样做会给基础设施以及 Hub 上的构建者带来巨大的额外开销。

在 HF Mirror 的Xet 团队,我们正在将 CDC 从理论推向生产,为 AI 构建者带来更快的上传和下载速度(在某些情况下提升了 2-3 倍)。我们的指导原则很简单:为构建和迭代模型与数据集的团队实现快速实验和协作。这意味着我们关注的不仅仅是去重;我们还在优化数据在网络中的传输方式、存储方式以及整个开发体验。

规模化去重的现实

想象一下将一个 200GB 的仓库上传到 Hub。如今,有多种方式可以完成这件事,但它们都采用以文件为中心的方法。为了给 Hub 带来更快的文件传输,我们开源了 xet-core 以及hf_xet,这是与 huggingface_hub 的集成,采用 Rust 编写的基于块的方法。

如果你考虑一个包含唯一块的 200GB 仓库,在支撑所有仓库的内容寻址存储(CAS)中,这意味着 300 万条记录(按每块约 64KB 计算)。如果上传了模型的新版本,或者仓库中创建了包含不同数据的分支,就会增加更多唯一块,从而推高 CAS 中的记录数量。

Hub 上近 45PB 的数据分布在 200 万个模型、数据集和 space 仓库中,纯粹基于块的方法可能会产生 6900 亿个块。仅以块为单位管理如此海量的内容是不可行的,原因如下:

  • 网络开销:如果每个块都单独下载或上传,每次上传和下载都会产生数百万次请求,使客户端和服务器都不堪重负。即便采用批量查询,也只是把问题转移到了存储层。
  • 基础设施开销:一个逐一追踪块的朴素 CAS 将需要数十亿条记录,导致 DynamoDB 或 S3 等服务产生高昂的月度账单。在 HF Mirror 的规模下,这笔开销会迅速累积。

简而言之,网络请求激增,数据库疲于管理元数据,编排每个块的成本飞涨,而这一切都发生在你等待文件传输的过程中。

规模化去重的设计原则

这些挑战引出了一个关键认识:

去重是一种性能优化手段,而非最终目标。

最终目标是改善构建者在模型和数据集上迭代与协作的体验。从客户端到存储层的系统组件并不需要保证去重。相反,它们将去重作为众多工具之一来辅助实现这一目标。

通过放宽去重约束,我们自然而然地得出了第二条设计原则:

避免采用与块数量成 1:1 扩展的通信或存储策略。

这是什么意思?我们以聚合的方式扩展。

通过聚合实现规模化去重

聚合将数据块分组,并以智能的方式引用它们,从而带来巧妙(且实用)的好处:

  • 块:我们不再传输和存储数据块,而是在去重后将数据捆绑成最大 64MB 的块。块仍然是内容寻址的,但这将 CAS 条目减少了 1000 倍。
  • 分片:分片提供文件和数据块之间的映射(同时引用块)。这使我们能够识别文件中的哪些部分发生了变化,并引用过去上传生成的分片。当数据块已知存在于 CAS 中时,它们会被跳过,从而减少不必要的传输和查询。

块和分片共同带来了显著的好处。然而,当有人上传一个新文件时,我们如何知道某个数据块之前是否已被上传,从而消除不必要的请求?对每个数据块执行网络查询无法扩展,并且违背了我们上面提到的“no 1:1”原则。

解决方案是关键数据块,它是所有数据块中 0.1% 的子集,根据数据块哈希通过简单的模条件选出。我们为这些关键数据块及其所在的分片提供全局索引,这样当查询该数据块时,会返回相关分片以提供本地去重。这使我们能够利用空间局部性原理。如果某个关键数据块在某个分片中被引用,那么同一分片中很可能还有其他相似的数据块引用。这进一步改善了去重,并减少了网络和数据库请求。

Key chunks

实践中的聚合去重

Hub 目前存储了超过 3.5PB 的 .gguf 文件,其中大多数是 Hub 上其他模型的量化版本。量化模型由于量化的本质——值被限制在更小的整数范围内并进行缩放——为去重提供了一个有趣的机会。这限制了权重矩阵中值的范围,自然导致更多重复。此外,许多量化模型仓库存储多个不同变体(例如 Q4_K、Q3_K、Q5_K),它们之间存在大量重叠。

实践中的一个很好的例子是 bartowski/gemma-2-9b-it-GGUF,它包含 google/gemma-2-9b-it 的 29 个量化版本,总计 191GB。为了上传,我们使用与 huggingface_hub 集成的 hf_xet 在本地执行数据块级去重,然后在块级别聚合并存储数据。

上传后,我们就可以看到一些很酷的模式!我们包含了一个可视化,显示每个块的去重比率。块越深,说明其部分在多个模型版本中被引用的频率越高。如果你前往托管此可视化的 Space,将鼠标悬停在任何热图单元格上,会以橙色突出显示所有模型中对块的引用;而点击某个单元格则会选中共享块的所有其他文件:

Quantization deduplication visualization

单个去重块可能只代表几 MB 的节省,但如你所见,有许多重叠的块!有这么多块,节省会迅速累积。Xet 支持的 gemma-2-9b-it-GGUF 仓库版本没有上传 191GB,而是为我们的测试 CAS 环境存储了 1515 个唯一块,总计约 97GB(节省了约 94GB)。

虽然存储方面的改进非常显著,但真正的收益在于这对 Hub 贡献者意味着什么。在 50MB/s 的速度下,去重优化带来了四小时的上传时间差异;速度提升接近 2 倍:

仓库 存储大小 上传时间 @ 50MB/s
原始 191 GB 509 分钟
Xet 支持 97 GB 258 分钟

同样,本地分块缓存显著加快了下载速度。如果某个文件被更改,或者添加了与本地分块缓存有大量重叠的新量化版本,你无需重新下载任何未更改的分块。这与基于文件的方式形成对比,后者必须下载整个新文件或更新后的文件。

综合来看,这展示了本地分块级去重与块级聚合相结合,如何不仅大幅简化了存储,也简化了在 Hub 上的开发工作。通过在文件传输中提供这种级别的效率,AI 构建者可以更快行动、快速迭代,并减少对基础设施瓶颈的担忧。对于任何向 Hub 推送大文件的人(无论你是在推送新的模型量化版本还是更新版的训练集),这都能帮助你转移重心到构建和分享上,而不是等待和排查问题。

我们正在紧锣密鼓地工作,将在未来几周和几个月内推出首批由 Xet 支持的仓库!在此过程中,我们将发布更多更新,把这样的速度带给 Hub 上的每一位构建者,让文件传输几乎无感。

在 Hub 上关注我们,了解更多我们的进展!

来源:Hugging Face Blog · hf-mirror.com