Hugging Face Kernels 库主要更新
Hugging Face 性能优化库 Kernels 发布重大更新,具体功能细节缺失,建议查看原文了解性能收益。
Hugging Face 性能优化库 Kernels 发布重大更新,具体功能细节缺失,建议查看原文了解性能收益。
过去几个月里,我们一直在朝着这个目标努力。在此过程中,我们也几乎彻底重新设计了整个项目。本文将总结我们已经发布的主要更新,以及接下来的计划。
我们在 Hub 上引入了一种名为“kernel”的新仓库类型。借助这种仓库,我们可以更好地满足对计算环境有特定要求的用户。例如,用户可以直观了解某个 kernel 支持哪些加速器、操作系统和后端版本。
现在,用户可以在 Hub 上浏览所有可用的 kernels。
让这些 kernels 成为 Hub 的一等公民,也能为整个 AI 生态带来益处。用户现在可以观察 kernels、模型以及使用它们的应用之间的发展趋势,kernels 也因此更容易被用户发现。
Kernels 运行的是原生代码,拥有与加载它们的 Python 进程相同的权限,因此恶意 kernel 可能造成实质性危害。正因如此,安全性始终是 Kernels 项目最重视的问题之一。
这也是我们很早就开始关注可复现性的原因:你应当能够自行重新编译一个 kernel,并验证编译结果是否与公开源代码一致。我们使用 Nix 来实现这一点,因为它通过对构建配方进行封闭式求值,并使用高度隔离的 sandbox,保证构建过程的纯净性。我们还会将源代码的 Git SHA1 嵌入 kernel 本身,进一步增强其来源可追溯性。
最近几个月,我们又增加了两层防御机制:可信 kernel 发布者和代码签名。
在推出新仓库类型的同时,我们也引入了“可信发布者”。由于 kernels 会在机器上执行代码,并拥有与使用它们的 Python 进程相同的权限,因此攻击者可能上传恶意 kernel,再诱导你使用,从而入侵机器。为了帮助你避开这类恶意 kernels,kernels package 现在默认只加载可信发布者提供的 kernels。可信发布者是指受到社区信任、被认为会善意行事的组织。
我们仍然希望支持加载来自非可信发布者组织或用户的 kernels,但从 Hub 加载此类 kernel 时,你必须通过 trust_remote_code 参数明确选择启用:
from kernels import get_kernel
kernel_module = get_kernel(
“Atlas-Inference/gdn”, version=1, trust_remote_code=True
)
默认情况下,用户无法在 Hub 上发布 kernel 仓库,必须先申请成为 kernel 发布者。用户和组织可以在账户设置中申请访问权限。这样,我们就有时间逐一审核这些请求。
我们正在增加的另一层安全机制是代码签名。它可以防范这样一种场景:某个可信发布者的 Hub 凭据遭到泄露,攻击者借此向其 kernel 仓库上传恶意 kernel。在代码签名机制中,kernel 使用仅 kernel 开发者知晓的私钥进行签名,并使用公开可用的公钥完成验证。在 Hub 遭到入侵的场景下,攻击者并不拥有签名所需的私钥,因此无法为恶意 kernel 签名。
为了进一步提升安全性,我们使用 Sigstore 的 cosign,通过临时私钥完成签名。由于这些签名密钥只在有限时间内有效,即使私钥发生泄露,攻击者通常也无法继续使用。我们还会验证 kernel 是否由可信 GitHub 仓库中的可信 GitHub workflow 签名。
kernel-builder 已经支持 kernel 签名,我们也提供了 kernels verify-signature 来验证 kernel。目前,Kernels 在加载 kernel 时还不会验证签名,因为我们希望在全面推出这项功能之前进行更多测试。关于如何为自己的 kernels 配置代码签名,可以参阅 kernels 0.16.0 的 release notes。
此前,kernels 与 kernel-builder 之间交织着许多工具函数。现在,我们对 kernels 和 kernel-builder 的 CLI 职责进行了更清晰的划分。这里的核心认知是:kernels 是一个用于加载 kernel 并为其使用做好准备的库,因此不应包含任何与“构建”kernel 有关的内容。
经过这次调整,kernels 和 kernel-builder 现在都更加精简,职责也更加明确。更多信息请参阅相关文档。
经过改进的 CLI 体验,也让我们能够更好地顺应 Agent 式 kernel 开发的兴起。后文将对此展开介绍。
我们扩展了对不同框架的支持,其中最明显的变化包括:
我们为 kernels 和 kernel-builder 增加了对 Torch Stable ABI 的支持。Torch Stable ABI 允许 kernel 开发者以某个特定 Torch 版本为目标,并在大约两年的时间里兼容该版本之后发布的所有版本。例如,一个以 Torch 2.9 Stable ABI 为目标的 kernel,可以支持 Torch >= 2.9。
Apache TVM FFI 是除 Torch 之外首个获得支持的框架。TVM FFI 是一种标准化的 kernel ABI,可以与 PyTorch、Jax 和 CuPy 等其他框架互操作。这让 kernel 开发者能够构建跨框架运行的 kernels。
kernel-builder 和 kernels 顺应了 Agent 式 kernel 开发的兴起。在这种开发方式中,人们借助 Agent 从零开始创建经过优化的 kernel。二者共同支持这样一套工作流:Agent 可以搭建 kernel 脚手架、执行构建和 benchmark,并通过多轮迭代持续优化 kernel。
Agent 式 kernel 开发仍处于早期阶段,合适的开发循环还会不断演进。因此,简单清晰的基础能力尤为重要:工具应当易于组合,以适配人们选择的各种 Agent 工作流或框架。
kernel-builder 有助于规范 kernel 源代码的脚手架结构,并支持可复现构建。这为 Agent 提供了可预测的项目布局和可重复执行的工作流。它的 CLI 也以适合 Agent 使用为设计目标,例如提供非交互式命令,以及便于 Agent 通过程序解析的输出。为此,我们还准备了针对不同后端的 skills,帮助 Agent 应对各类后端的特有行为。这些 skills 可以记录特定后端的工具链、编译路径和性能注意事项。
成功构建 kernel 并不是唯一目标,我们还必须确保它能在目标硬件上相较 baseline 带来真正的性能提升。因此,构建成功只是验证过程的第一步。通常,目标硬件可能包括许多不同的加速器,甚至包括同一种加速器的不同产品系列。
因此,在适用的情况下,跨硬件厂商和不同代际设备评估结果非常重要。我们与 HF Jobs 的深度集成,可以简化这一 benchmark 流程。Agent 可以利用这项集成运行 benchmark suites、收集性能结果,并与预先定义的 baseline 进行比较。
通过这种方式,Agent 可以在不同硬件配置上运行测试,获得有关生成 kernels 性能表现的可靠反馈,并确定接下来需要采取哪些措施。这些反馈随后可以指导下一轮优化迭代。
下面是一些 Agent 增强型 kernels 的示例,它们展示了能够通过这套工作流开发和评估的 kernel 类型:
drbh/yamoesayakpaul/qk-norm-rope使用 kernel-builder 构建 kernels 时,环境配置可能令人望而生畏。为了降低使用门槛,我们现在提供了一个安装脚本,可以一键完成环境设置。如果你更喜欢使用临时实例,也值得参考我们的 Terraform 配置指南。
Kernels 构建完成后,我们会为每个 kernel 创建一份 system card,用于展示实用信息,包括如何使用它,以及它对外公开了哪些接口。当 kernel 被推送到 Hub 后,这份 system card 就会成为该 kernel 的 front matter。
为了更好地规划工作,你可能会多次提出这个问题。可以使用 has_kernel() 方法进行判断:
from kernels import has_kernel
print(has_kernel("kernels-community/activation", version=1))
该方法会返回一个 bool。如果你希望进一步了解某个 kernel 不受支持的具体原因,可以使用 get_kernel_variants():
from kernels import get_kernel_variants, VariantAccepted
for decision in get_kernel_variants("kernels-community/activation", version=1):
name = decision.variant.variant_str
if isinstance(decision, VariantAccepted):
print(f"{name}: compatible")
else:
print(f"{name}: rejected ({decision.reason})")
它应该会输出以下内容,具体结果取决于你使用的机器:
torch212-cxx11-cu130-aarch64-linux: compatible
torch210-cu128-x86_64-windows: rejected (CPU (x86_64) does not match system CPU (aarch64))
torch211-cu128-x86_64-windows: rejected (CPU (x86_64) does not match system CPU (aarch64))
torch212-metal-aarch64-darwin: rejected (OS (darwin) does not match system OS (linux))
torch211-metal-aarch64-darwin: rejected (OS (darwin) does not match system OS (linux))
torch210-metal-aarch64-darwin: rejected (OS (darwin) does not match system OS (linux))
torch29-metal-aarch64-darwin: rejected (OS (darwin) does not match system OS (linux))
…
manylinux_2_28 支持kernel-builder 几乎从项目诞生之初,就一直以 manylinux_2_28 为构建目标。过去,我们使用基于 glibc 2.28 编译的现代 gcc 工具链来支持 manylinux。为了避免与旧版 libstdc++ 之间的兼容性问题,我们采用了静态链接 libstdc++ 的方式。
然而,这种做法最近引发了一些问题。libstdc++ 的部分功能会使用全局初始化。当多个 libstdc++ 版本同时出现时,这可能导致数据损坏,例如 PyTorch 动态链接的 libstdc++ 与某个 kernel 静态链接的 libstdc++ 同时存在。一些最近推出的 kernels 使用了会触发全局初始化的功能,例如 C++ 正则表达式,从而造成数据损坏,并进一步引发 segfault 等问题。
为了解决这个问题,kernels 现在会动态链接 libstdc++。为了确保与旧版 libstdc++ 的兼容性,我们现在使用官方 manylinux_2_28 工具链编译 kernels。
Kernels 项目的目标,是同时服务 kernel 开发者和自定义 kernels 的用户。我们一直期待收到社区关于如何继续改进项目的反馈,也欢迎大家参与贡献!
致谢:感谢 Aritra 审阅本文。