reading a PyTorch checkpoint's tensors: a small checkpoint written here (a float32 storage, a float16 one, a transposed view, an offset view, metadata that is not a tensor) and read back; damaged ones refused
reading a PyTorch checkpoint's tensors: a small checkpoint written here (a float32 storage, a float16 one, a transposed view, an offset view, metadata that is not a tensor) and read back; damaged ones refused
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <zlib.h>
0x80, 0x02, 0x7d, 0x71, 0x00, 0x28, 0x58, 0x04, 0x00, 0x00, 0x00, 0x6d,
0x65, 0x74, 0x61, 0x71, 0x01, 0x7d, 0x71, 0x02, 0x28, 0x58, 0x05, 0x00,
0x00, 0x00, 0x65, 0x70, 0x6f, 0x63, 0x68, 0x71, 0x03, 0x4b, 0x0c, 0x58,
0x04, 0x00, 0x00, 0x00, 0x6e, 0x6f, 0x74, 0x65, 0x71, 0x04, 0x58, 0x01,
0x00, 0x00, 0x00, 0x78, 0x71, 0x05, 0x58, 0x02, 0x00, 0x00, 0x00, 0x6c,
0x72, 0x71, 0x06, 0x47, 0x3f, 0x50, 0x62, 0x4d, 0xd2, 0xf1, 0xa9, 0xfc,
0x75, 0x58, 0x0a, 0x00, 0x00, 0x00, 0x73, 0x74, 0x61, 0x74, 0x65, 0x5f,
0x64, 0x69, 0x63, 0x74, 0x71, 0x07, 0x63, 0x63, 0x6f, 0x6c, 0x6c, 0x65,
0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x0a, 0x4f, 0x72, 0x64, 0x65, 0x72,
0x65, 0x64, 0x44, 0x69, 0x63, 0x74, 0x0a, 0x71, 0x08, 0x29, 0x52, 0x71,
0x09, 0x28, 0x58, 0x0c, 0x00, 0x00, 0x00, 0x6c, 0x61, 0x79, 0x65, 0x72,
0x2e, 0x77, 0x65, 0x69, 0x67, 0x68, 0x74, 0x71, 0x0a, 0x63, 0x74, 0x6f,
0x72, 0x63, 0x68, 0x2e, 0x5f, 0x75, 0x74, 0x69, 0x6c, 0x73, 0x0a, 0x5f,
0x72, 0x65, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x74, 0x65, 0x6e, 0x73,
0x6f, 0x72, 0x5f, 0x76, 0x32, 0x0a, 0x71, 0x0b, 0x28, 0x28, 0x58, 0x07,
0x00, 0x00, 0x00, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x71, 0x0c,
0x63, 0x74, 0x6f, 0x72, 0x63, 0x68, 0x0a, 0x46, 0x6c, 0x6f, 0x61, 0x74,
0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x0a, 0x71, 0x0d, 0x58, 0x01,
0x00, 0x00, 0x00, 0x30, 0x71, 0x0e, 0x58, 0x03, 0x00, 0x00, 0x00, 0x63,
0x70, 0x75, 0x71, 0x0f, 0x4b, 0x06, 0x74, 0x71, 0x10, 0x51, 0x4b, 0x00,
0x4b, 0x02, 0x4b, 0x03, 0x86, 0x71, 0x11, 0x4b, 0x03, 0x4b, 0x01, 0x86,
0x71, 0x12, 0x89, 0x68, 0x08, 0x29, 0x52, 0x71, 0x13, 0x74, 0x71, 0x14,
0x52, 0x71, 0x15, 0x58, 0x0e, 0x00, 0x00, 0x00, 0x6c, 0x61, 0x79, 0x65,
0x72, 0x2e, 0x77, 0x65, 0x69, 0x67, 0x68, 0x74, 0x5f, 0x74, 0x71, 0x16,
0x68, 0x0b, 0x28, 0x68, 0x10, 0x51, 0x4b, 0x00, 0x4b, 0x03, 0x4b, 0x02,
0x86, 0x71, 0x17, 0x4b, 0x01, 0x4b, 0x03, 0x86, 0x71, 0x18, 0x89, 0x68,
0x08, 0x29, 0x52, 0x71, 0x19, 0x74, 0x71, 0x1a, 0x52, 0x71, 0x1b, 0x58,
0x0a, 0x00, 0x00, 0x00, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x2e, 0x62, 0x69,
0x61, 0x73, 0x71, 0x1c, 0x68, 0x0b, 0x28, 0x68, 0x10, 0x51, 0x4b, 0x04,
0x4b, 0x02, 0x85, 0x71, 0x1d, 0x4b, 0x01, 0x85, 0x71, 0x1e, 0x89, 0x68,
0x08, 0x29, 0x52, 0x71, 0x1f, 0x74, 0x71, 0x20, 0x52, 0x71, 0x21, 0x58,
0x04, 0x00, 0x00, 0x00, 0x68, 0x61, 0x6c, 0x66, 0x71, 0x22, 0x68, 0x0b,
0x28, 0x28, 0x68, 0x0c, 0x63, 0x74, 0x6f, 0x72, 0x63, 0x68, 0x0a, 0x48,
0x61, 0x6c, 0x66, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x0a, 0x71,
0x23, 0x58, 0x01, 0x00, 0x00, 0x00, 0x31, 0x71, 0x24, 0x68, 0x0f, 0x4b,
0x04, 0x74, 0x71, 0x25, 0x51, 0x4b, 0x00, 0x4b, 0x04, 0x85, 0x71, 0x26,
0x68, 0x1e, 0x89, 0x68, 0x08, 0x29, 0x52, 0x71, 0x27, 0x74, 0x71, 0x28,
0x52, 0x71, 0x29, 0x75, 0x75, 0x2e};
if (!ok) {
} else {
}
}
int opt = 0;
while ((opt = getopt(argc, argv, "V:")) != -1) {
if (opt == 'V') {
} else {
return -1;
}
}
return 0;
}
static void put_le(
unsigned char* p,
unsigned long v,
int n) {
int i = 0;
for (i = 0; i < n; i++) p[i] = (unsigned char)((v >> (8 * i)) & 0xff);
}
typedef struct {
const char* name;
const unsigned char* data;
size_t len;
FILE* f = fopen(path, "wb");
unsigned long offsets[8], crcs[8], at = 0, dir_size = 0;
unsigned char h[46];
int i = 0;
if (!f || count > 8) {
if (f) fclose(f);
return -1;
}
for (i = 0; i < count; i++) {
size_t n = strlen(m[i].name);
crcs[i] = crc32(crc32(0L, Z_NULL, 0), m[i].data, (uInt)m[i].len);
memset(h, 0, sizeof(h));
put_le(h + 18, (
unsigned long)m[i].len, 4);
put_le(h + 22, (
unsigned long)m[i].len, 4);
put_le(h + 26, (
unsigned long)n, 2);
offsets[i] = at;
fwrite(h, 1, 30, f);
fwrite(m[i].name, 1, n, f);
fwrite(m[i].data, 1, m[i].len, f);
at += 30 + (
unsigned long)n + (
unsigned long)m[i].
len;
}
for (i = 0; i < count; i++) {
size_t n = strlen(m[i].name);
memset(h, 0, sizeof(h));
put_le(h + 20, (
unsigned long)m[i].len, 4);
put_le(h + 24, (
unsigned long)m[i].len, 4);
put_le(h + 28, (
unsigned long)n, 2);
put_le(h + 42, offsets[i], 4);
fwrite(h, 1, 46, f);
fwrite(m[i].name, 1, n, f);
dir_size += 46 + (unsigned long)n;
}
memset(h, 0, 22);
put_le(h + 8, (
unsigned long)count, 2);
put_le(h + 10, (
unsigned long)count, 2);
fwrite(h, 1, 22, f);
return fclose(f) == 0 ? 0 : -1;
}
size_t i = 0;
if (!t || t->
count != n)
return 0;
for (i = 0; i < n; i++)
if (t->
data[i] != want[i])
return 0;
return 1;
}
int main(
int argc,
char** argv) {
static const char* path = "ex_pth.tmp.pth";
static const float f32[6] = {1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 6.0f};
static const unsigned char f16[8] = {0x00, 0x38, 0x00, 0xbc, 0x00, 0x40, 0xff, 0x7b};
unsigned char f32_bytes[24];
int i = 0;
fprintf(stderr, "Usage: %s [-V LOG_LEVEL]\n", argv[0]);
return 2;
}
for (i = 0; i < 6; i++) {
uint32_t bits = 0;
memcpy(&bits, &f32[i], 4);
put_le(f32_bytes + 4 * i, bits, 4);
}
members[0].
name =
"archive/data.pkl";
members[1].
name =
"archive/data/0";
members[1].
data = f32_bytes;
members[1].
len =
sizeof(f32_bytes);
members[2].
name =
"archive/data/1";
members[2].
len =
sizeof(f16);
if (pth) {
const float transposed[6] = {1.0f, 4.0f, 2.0f, 5.0f, 3.0f, 6.0f};
const float bias[2] = {5.0f, 6.0f};
const float halves[4] = {0.5f, -1.0f, 2.0f, 65504.0f};
expect_true(
"a freed checkpoint is cleared", pth == NULL);
}
remove(path);
fprintf(stderr,
"ex_pth: %d failure(s)\n",
failures);
return 1;
}
printf("ex_pth: ok\n");
return 0;
}
void process_args(int argc, char **argv)
static void expect_true(const char *label, int cond)
static const unsigned char checkpoint_pickle[]
What torch.save() writes as data.pkl for {"meta": {...}, "state_dict": OrderedDict(layer....
static void put_le(unsigned char *p, unsigned long v, int n)
put a little endian value
static int write_zip(const char *path, const MEMBER *m, int count)
write a ZIP of stored members, as torch.save() does
static int values_are(const N_PTH_TENSOR *t, const float *want, size_t n)
are a tensor's values these
const char * name
its name
const unsigned char * data
its bytes
#define n_log(__LEVEL__,...)
Logging function wrapper to get line and func.
#define LOG_ERR
error conditions
void set_log_level(const int log_level)
Set the global log level value ( static int LOG_LEVEL ).
#define LOG_INFO
informational
int dims
how many dimensions
int64_t shape[8]
their sizes
float * data
its values, contiguous, row major; owned
void n_pth_free(N_PTH **pth)
release a checkpoint's tensors
const N_PTH_TENSOR * n_pth_find(const N_PTH *pth, const char *name)
find a tensor by its path
N_PTH * n_pth_load(const char *path)
read every tensor of a checkpoint
One tensor of a checkpoint.
Common headers and low-level functions & define.
Reading the tensors of a PyTorch checkpoint (.pth, .pt) without Python.