#pickle #python #pytorch #machine-learning #serialization

repugnant-pickle

Python pickle 格式文件的低效抓取,包括一些加载 PyTorch 检查点的支持

1 个不稳定版本

0.0.1 2024年1月11日

#7#pickle

Download history 46/week @ 2024-03-17 25/week @ 2024-03-24 30/week @ 2024-03-31 8/week @ 2024-04-07 25/week @ 2024-04-14 12/week @ 2024-04-21 35/week @ 2024-04-28 35/week @ 2024-05-05 45/week @ 2024-05-12 17/week @ 2024-05-19 9/week @ 2024-05-26 11/week @ 2024-06-02 33/week @ 2024-06-09 15/week @ 2024-06-16 2/week @ 2024-06-23

每月51 次下载
web-rwkv-converter 中使用

Apache-2.0

54KB
900

repugnant-pickle

因为它就是如此,难道不是吗?

这是一个用于处理 Python pickle 格式的 Rust 包。

它还支持打开 PyTorch 文件(.pth.pt),它们基本上是包含 pickle 的 ZIP 文件。

什么?

Python pickle 格式。有些人赚得比我多得多,他们投入了大量的努力来开发版权保护方案,但这些方案在防止互操作性方面不如 Python pickle 格式有效。

为什么有人会真的创建这样的东西?我的理论是,创始人对他们的同类有着深深和持久的仇恨。他们唯一能得到任何乐趣的事情就是给尽可能多的人带来痛苦。为此:Python pickle 格式。

除非你是 Python,否则基本上无法可靠地处理。在一个理想的世界里,你只需让 pickle 独自存在即可,但遗憾的是,我们生活在这个世界上。有时你需要从一个 pickle 文件中获取一些数据,但你不想在应用程序中嵌入整个 Python 解释器或类似的东西。

这就是 repugnant-pickle 的用武之地。它尽力而为,并给你它所能提供的。

这个名字是对 BeautifulSoup(一个 HTML 抓取库)的戏仿。它没有(也不应该)试图完全解析 HTML(这是极其困难的),它只是给你一种相对简单的方式来提取所需的数据。大多数情况下,这已经足够了。

Example

以下是解析 PyTorch 文件时可能得到的输出示例

[Build(
  Global(
    Raw(GLOBAL("collections", "OrderedDict"), [
      Seq(Tuple, []),
      Seq(Tuple, [
        Seq(Dict, [
          String("emb.weight"),
          Global(Raw(GLOBAL("torch._utils", "_rebuild_tensor_v2")), [
            Seq(Tuple, [
              PersId(Seq(Tuple, [
                String("storage"),
                Raw(GLOBAL("torch", "BFloat16Storage")),
                String("0"),
                String("cuda:0"),
                Int(430348288),
              ])),
              Int(327378944),
              Seq(Tuple, [Int(50277), Int(1024)]),
              Seq(Tuple, [Int(1024), Int(1)]),
              Bool(false),
              Global(Raw(GLOBAL("collections", "OrderedDict")), [
                Seq(Tuple, [])
              ]),
            ]),
          ]),
        ]),
        Seq(Dict, [
          String("blocks.0.ln1.weight"),
          Global(Raw(GLOBAL("torch._utils", "_rebuild_tensor_v2")), [
            // Etc
          ]),
        ]),
      ]),
    ]),
  ),
)]

关于 serde-pickle 的看法?

如果你可以使用它,它会比这个包提供更好的体验。然而,它无法处理持久 ID 等一些事情。PyTorch 文件使用持久 ID,所以在这种情况下你真的没有选择。

还有一些奇怪的选项,比如强制 Rust 工具链使用 1.41 版本。

PyTorch

你可以启用 torch 功能以支持处理 PyTorch 文件。如果你的 Torch 文件包含奇怪的内容,你可能需要手动处理。查看 src/torch.rs 了解如何开始。

否则,您可以使用 repugnant_pickle::torch::RepugnantTorchTensors::new_from_file 来加载张量元数据。例如,如果它工作正常,您将得到类似以下内容

RepugnantTorchTensors(
   [
       RepugnantTorchTensor {
           name: "emb.weight",
           device: "cuda:0",
           tensor_type: BFloat16,
           storage: "archive/data/0",
           storage_len: 430348288,
           storage_offset: 327378944,
           absolute_offset: 327445248,
           shape: [50277, 1024],
           stride: [1024, 1],
           requires_grad: false,
       },
       RepugnantTorchTensor {
           name: "blocks.0.ln1.weight",
           device: "cuda:0",
           tensor_type: BFloat16,
           storage: "archive/data/0",
           storage_len: 430348288,
           storage_offset: 13639680,
           absolute_offset: 13705984,
           shape: [1024],
           stride: [1],
           requires_grad: false,
       },
   ]

您需要自己从类型和形状计算长度。例如,第一个张量的形状为 [1024, 50277],类型为 BFloat16。因此,字节数为 (1024 * 50277) * 2,数据在 Torch 文件中的偏移量为 327445248,或者在 Torch ZIP 的 archive/data/0 文件中,它将从 327378944 开始。

它在我的 .pth.pt LLM 模型上工作。

来源:相信我兄弟。

用法

查看 examples 中的示例

  • dump_raw.rs — 从文件中导出原始 Pickle 操作码。
  • dump.rs — 从文件中导出 Value
  • dump_torch.rs — 导出 PyTorch 文件的张量元数据。

注意,对于最后一个,您需要启用 torch 功能。您也可以像这样运行示例

cargo run --features torch --example dump_raw -- /path/to/torchfile.pth

警告:不要尝试在 Torch 文件上运行 dumpdump_raw。它将尝试将整个大型文件作为 pickle 加载。

注意事项

这还没有得到很好的测试。它可能不会在失败时删除您所有的猫图片,但它可能会失败。

此外,小心递归的 Pickle 文件。它不会崩溃,但它会循环直到达到递归限制(大约 250),并且您将得到嵌套数据直到那个限制。

如果您需要处理大型 Pickle 文件,理想情况下最好不要使用它,但如果您必须使用,那么您可能最好使用 src/parsers.rs 中的单个 Pickle op Nom 解析器。

依赖关系

~1.4–2.5MB
~46K SLoC