Skip to main content

pytorch_reader/
lib.rs

1//! Read PyTorch checkpoint files (`.pt`, `.pth`) without PyTorch or Burn.
2//!
3//! A checkpoint is a pickle describing the saved object, with each tensor's bytes kept in a
4//! separate storage inside the same container. [`PytorchReader`] parses the pickle and hands
5//! back a [`Tensor`] per entry, carrying its name, [`DType`] and shape; the bytes are
6//! produced only when [`Tensor::read`] asks for them (from the file for ZIP and legacy
7//! containers, from memory for TAR, whose storages are read at open).
8//!
9//! ```rust,no_run
10//! use pytorch_reader::PytorchReader;
11//!
12//! let reader = PytorchReader::new("model.pt")?;
13//! for tensor in reader.tensors().values() {
14//!     println!("{}: {:?} {:?}", tensor.name, tensor.dtype(), tensor.shape());
15//! }
16//!
17//! // Bytes are read from the file here, not at open.
18//! let weight = reader.get("fc.weight").unwrap();
19//! let bytes: Vec<u8> = weight.read()?;
20//! # Ok::<(), Box<dyn std::error::Error>>(())
21//! ```
22//!
23//! A checkpoint that nests its weights under a key (`"state_dict"`, `"model"`, ...) is
24//! opened with [`PytorchReader::with_top_level_key`]. Values that are not tensors, such as a
25//! configuration dictionary saved next to the weights, can be deserialized into any `serde`
26//! type with [`PytorchReader::load_config`].
27//!
28//! # Supported containers
29//!
30//! Every layout `torch.save` has written is read, and detected from the file itself.
31//!
32//! **ZIP (PyTorch 1.6 and later).** An archive holding, under a root directory usually named
33//! after the file: `data.pkl` with the pickled tensor metadata, one entry per tensor storage
34//! under `data/`, and small text entries such as `version` and `byteorder`.
35//!
36//! **Legacy pickle stream (PyTorch 0.1.10 through 1.5).** Sequential pickles: a magic number
37//! (`0x1950a86a20f9469cfc6c`), the protocol version (1001), system info (endianness, type
38//! sizes), the saved object, then the storage key list followed by each storage as an `i64`
39//! element count and its bytes.
40//!
41//! **TAR (before PyTorch 0.1.10, e.g. early torchvision models).** An archive of `sys_info`
42//! (a system info pickle), `storages` (a count pickle, then per storage a metadata pickle,
43//! element count and bytes), `tensors` (a count pickle, then per tensor a metadata pickle and
44//! binary shape, stride and storage offset) and `pickle` (the saved object, referencing
45//! tensors by persistent id).
46//!
47//! **Plain pickle.** A pickle with a dictionary at its root, as some tools write for
48//! configuration on its own.
49//!
50//! Full-model saves (as opposed to a `state_dict`) are refused, as are checkpoints holding
51//! sparse, quantized or nested tensors, since those cannot be represented. Only little-endian
52//! files are supported.
53//!
54//! # Safety limits
55//!
56//! Parsing is hardened against malformed or hostile files. A ZIP checkpoint whose `data.pkl`
57//! claims more than [`MAX_PICKLE_SIZE`], or any checkpoint holding a tensor that would
58//! materialize more than [`MAX_TENSOR_SIZE`] bytes, is refused at open before an allocation
59//! of that size is made, and every length a file declares is checked against what the file
60//! actually holds.
61
62pub mod nested;
63mod pickle_reader;
64mod reader;
65mod storage;
66mod tensor;
67
68#[cfg(test)]
69mod tests;
70
71pub use pickle_reader::{OpCode, PickleError};
72pub use reader::{
73    ByteOrder, FileFormat, PickleValue, PytorchError, PytorchMetadata, PytorchReader,
74};
75pub use tensor::{DType, Tensor};
76
77/// Largest `data.pkl` accepted from a ZIP checkpoint (100 MiB).
78///
79/// Only that container needs a ceiling: a deflated entry can claim any decompressed size,
80/// while the other containers' pickles are parsed straight off the file. The pickle holds
81/// tensor metadata and whatever non-tensor values were saved alongside; the tensor bytes
82/// live in separate entries and are never subject to this limit.
83pub const MAX_PICKLE_SIZE: u64 = 100 * 1024 * 1024;
84
85/// Largest tensor accepted: 2 GiB on 32-bit targets, 10 GiB elsewhere.
86///
87/// A view can declare far more elements than its storage holds (an `expand` has stride 0),
88/// so the file itself gives no bound on what a tensor materializes to. A checkpoint holding
89/// a tensor whose bytes would exceed this fails to open.
90#[cfg(target_pointer_width = "32")]
91pub const MAX_TENSOR_SIZE: usize = 2 * 1024 * 1024 * 1024;
92#[cfg(not(target_pointer_width = "32"))]
93pub const MAX_TENSOR_SIZE: usize = 10 * 1024 * 1024 * 1024;