pnnx numpy file input #6285
AtomAlpaca
started this conversation in
Show and tell
Replies: 0 comments
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Uh oh!
There was an error while loading. Please reload this page.
-
Changes Introduced
utils.cpp
input
andinput
, allows to get the shape and contents of tensor from numpy file.tests/numpy
.The structure of a numpy file
A numpy file is built up from two parts, separated by a newline character(
\n
).The first part stores the information of the file and array, and the second part is the pure binary data itself.Header data
For any numpy file, the first 6 bytes are a magic string:
\x93NUMPY
The next 2 bytes are the major and minor version number of this file. e.g.
\x01\x00
.As for now, numpy file has three major version, for version 1.0, the next 2 bytes forms a little-endian unsigned short int
HEADER_LEN
, representing the length of header data, and as for version 2.0, it becomes a 4-bytes little-endian unsigned int.In version 1.0 and 2.0 the next HEADER_LEN bytes is an ASCII string which contains a Python literal expression of a dictionary. In version 3.0 the string becomes utf8-encoded to supports structured types with any unicode field names. Considering the low actual demand we didn't implement this version.
The dictionary contains three keys:
"descr: dtype.descr"
, an object can be passed as an argument tonumpy.dtype
. It is formated as<data type><endian><type length>
, e.g.f<4
represents "4 bytes long, little endian, float number".fortran_order: bool
, whether the array is stored as Fortran-contiguous.shape: tuple fo number
the shape of the array.In real numpy files, these keys may not be sorted in alphabetic order.
After this dictionary are some spaces(
\x20
) and finally the\n
, which makes thelen(magic string) + 2 + len(length) + HEADER_LEN
can be evenly divisible by 64 for alignment purposes.Data
Following the header comes the array data. If the dtype contains Python objects (i.e.
dtype.hasobject is True
), then the data is a Python pickle of the array, which will not appears in our program. Otherwise the data is the contiguous (either C- or Fortran-, depending onfortran_order
) bytes of the array.Implementation Details
Given the simplicity of the structure of the numpy format, we made a minimalist implementation instead of including a third-party library.
Most of the implementation are trivial such as the parsing jobs. We only talk about the challenging and noteworthy parts here.
Endian
The endianess of the data maybe different from the system's.
To get the system endianess we have a trick:
In little-endian machine
i
in memory will be like0x01 0x00 0x00 0x00
and in big-endian we have0x00 0x00 0x00 0x01
, so we can simply check if the first byte ofi
is0x01
.To convert the endianess we just do
std::swap(bytes[j], bytes[type_size - j - 1]);
wherej
loops from0
totype_size / 2 - 1
to every single data.Fortran order
In the simplest way, the difference between the two orders is that the coordinates grow differently when they are flatly stored in contiguous memory.
For example we have a array
a[2][2][2]
, in c order it stores likea[0][0][0], a[0][0][1], a[0][1][0]..., a[1][0][0]...
but in fortran order it becomesa[0][0][0], a[1][0][0], a[0][1][0]..., a[0][0][1]...
, just the opposite way.To convert fortran order to c order, we enumerate each coordinate, compute the address of this coordinate in memory in both orders, and copy it.
To do this we compute the "stride" of each dimension in each order, which means, "to Increase the coordinates of this dimension by 1, how many time I should move through memory". e.g. For
a[2][2][2]
, in c order stride of the third dimension is simply one, but in fortran order it would be 4.Handling the boundary conditions carefully, we can easily compute the stride.
Then we can easily compute the index.
Notes
strtok()
won't avoid data races.Two tests may read the same file at the same time when testing in multiple threads, use different file name.
Add tests
In torch we can use the
numpy()
method to save a Tensor as a numpy file, so that we can simply save and let pnnx read it.To test if fortran_order and endian convert work well, we can use
asfortranarray()
andastype()
.To run these tests we create a folder
pnnx/tests/numpy
and add these tests:Beta Was this translation helpful? Give feedback.
All reactions