从文件到分块:提高 HF 存储效率

发布于 2024 年 11 月 20 日
在 GitHub 上更新

Hugging Face 在 Git LFS 仓库中存储了超过 30 PB 的模型、数据集和空间。由于 Git 在文件级别进行存储和版本控制,对文件的任何更改都需要重新上传整个资产——当 Hub 上的 Parquet 和 CSV 文件平均大小为 200-300 MB,Safetensor 文件平均大小为 1 GB 左右,GGUF 文件甚至可以超过 8 GB 时,这些操作会非常昂贵。想象一下只修改 GGUF 文件中的一行元数据,然后等待多吉字节的文件上传;除了用户时间和传输成本外,Git LFS 还需要保存两个文件的完整版本,从而增加存储成本。

下图展示了 2022 年 3 月至 2024 年 9 月间 Hub 上模型、数据集和空间仓库中 LFS 存储的增长情况

Parquet Layout

Hugging Face 的 Xet 团队正在采用一种不同的存储方法:将文件存储为分块。通过只传输修改过的分块,我们可以显著提高存储效率和迭代速度,同时确保对不断演变的数据集和模型进行可靠访问。其工作原理如下。

内容定义分块基础

我们用于文件分块的方法称为内容定义分块(CDC)。CDC 不将文件视为一个不可分割的单元,而是使用数据定义边界,将文件分解成可变大小的分块。为了计算分块,我们应用一种 滚动哈希算法 来扫描文件的字节序列。

考虑一个包含以下内容的文件

transformerstransformerstransformers

我们使用文本进行说明,但它可以是任何字节序列。

滚动哈希算法在数据滑动窗口上计算哈希。在这种情况下,窗口长度为 4 时,哈希将首先在 `tran` 上计算,然后是 `rans`,然后是 `ansf`,依此类推,直到文件结束。

当哈希满足预定义条件时(例如)确定分块边界

hash(data) % 2^12 == 0

如果序列 `mers` 生成的哈希满足此条件,文件将被分成三个分块

transformers | transformers | transformers

这些分块的内容会被哈希,以创建分块哈希与字节之间的映射,并最终存储在内容寻址存储(CAS)中。由于所有三个分块都是相同的,我们只在 CAS 中存储一个分块以实现内置去重。🪄

插入和删除

当文件内容更改时,CDC 允许进行细粒度更新,使其能够稳健地处理插入和删除操作。让我们通过插入 `super` 来修改文件,使新文件内容变为

transformerstransformerssupertransformers

再次应用带有相同边界条件的滚动哈希后,新的分块如下所示

 transformers | transformers | supertransformers

我们不需要保存以前见过的分块;它们已经存储。然而,`supertransformers` 是一个新分块。因此,保存此文件更新版本的唯一成本是上传和存储一个新分块。

为了在实际世界中验证这种优化,我们对 XetHub 上 CDC 支持存储的先前实现与 Git LFS 进行了基准测试,发现在三个迭代开发用例中,存储和传输性能始终提高了 50%。一个例子是 CORD-19 数据集,这是一个在 2020 年至 2022 年间整理的 COVID-19 研究论文集合,包含 50 次增量更新。Xet 支持和 Git LFS 支持的仓库之间的比较总结如下

指标 Git LFS 支持的仓库 Xet 支持的仓库
平均下载时间 51 分钟 19 分钟
平均上传时间 47 分钟 24 分钟
已用存储 8.9 GB 3.52 GB

通过仅传输和保存修改过的分块,使用 CDC 的 Xet 支持的仓库(以及各种提高压缩和简化网络请求的技术)显示出显著更快的上传/下载时间,并大幅削减了捕获数据集所有版本所需的存储量。想了解更多信息?请阅读 完整的基准测试报告

CDC 对 Hub 意味着什么

CDC 如何应用于 Hugging Face Hub 上存储的文件类型?我们制作了一个简单的 去重估算器,以可视化将 CDC 应用于文件集合时可能节省的存储空间。在 openai-community/gpt2 仓库提交历史中上传的 `model.safetensors` 文件的两个版本上运行此工具,返回以下结果

Parquet Layout

绿色表示两个版本之间存在显著重叠,因此有机会在文件内部和不同版本之间进行去重。

所需 Git LFS 存储 所需 Xet 支持存储
版本 1 664 MB 509 MB
版本 2 548 MB 136 MB
总计 1.2 GB 645 MB

在这种情况下,使用我们基于 Xet 的存储后端将为第二个版本节省大量的上传/下载时间,并将总存储占用减少 53%。加上压缩,我们估计可以额外节省 10%。

我们对 Hub 上仓库的初步研究表明,对于某些微调模型和许多模型检查点,结果是积极的。微调模型只修改部分参数,因此模型的大部分在不同版本之间保持不变,这使得它们成为去重的好选择。模型检查点捕获增量训练状态,也是很好的目标,因为检查点之间的变化通常很小。两者都显示出 30-85% 的去重率。PyTorch 模型检查点约占 Hub 上总存储的 200 TB。如果实现 50% 的去重,我们将立即节省多达 100 TB 的存储,并且每月大约节省 7-8 TB。

除了降低存储成本,块级去重还提高了上传/下载速度,因为只传输修改过的块。这对于处理多个版本模型或数据集的团队来说是一个巨大的好处,因为它最大限度地减少了用户和机器的等待时间。

我们的团队目前正在为 Hub 上的 Xet 后端存储进行概念验证,并希望在 2025 年初推出一些 Xet 后端仓库。 关注我们,了解更多信息,我们将分享未来主题的学习成果,例如在全球分布式仓库中扩展 CDC,平衡网络性能、隐私边界和并行化我们的分块算法。

社区

文章作者

如果您想看到基于分块的去重实际应用,请立即加入等候名单 https://huggingface.co/join/xet

是否有处理内容定义分块攻击的机制?我查阅了 restic,它有这种机制。您可以参考 restic 提供的论文:https://eprint.iacr.org/2025/532.pdf

注册登录 发表评论