Skip to content

Commit 15d9d13

Browse files
nihuilovedream-mslancerstadiumAtomAlpaca
committed
pnnx: support npy input tensors
Co-authored-by: lovedream-ms <209071654+lovedream-ms@users.noreply.github.com> Co-authored-by: lancerstadium <57707690+lancerstadium@users.noreply.github.com> Co-authored-by: AtomAlpaca <66774326+AtomAlpaca@users.noreply.github.com>
1 parent d0d5063 commit 15d9d13

15 files changed

Lines changed: 1171 additions & 33 deletions

tools/pnnx/src/ir.cpp

Lines changed: 62 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,51 @@ static size_t type_to_elemsize(int type)
107107
return 0; // null
108108
}
109109

110+
static std::string python_string_literal(const std::string& s)
111+
{
112+
std::string r = "'";
113+
for (char ch : s)
114+
{
115+
if (ch == '\\' || ch == '\'')
116+
{
117+
r += '\\';
118+
r += ch;
119+
}
120+
else if (ch == '\n')
121+
{
122+
r += "\\n";
123+
}
124+
else if (ch == '\r')
125+
{
126+
r += "\\r";
127+
}
128+
else if (ch == '\t')
129+
{
130+
r += "\\t";
131+
}
132+
else
133+
{
134+
r += ch;
135+
}
136+
}
137+
r += "'";
138+
return r;
139+
}
140+
141+
static void write_python_load_input_helper(FILE* pyfp)
142+
{
143+
fprintf(pyfp, "\n");
144+
fprintf(pyfp, "def _pnnx_load_input(path):\n");
145+
fprintf(pyfp, " arr = np.load(path)\n");
146+
fprintf(pyfp, " if arr.dtype.byteorder not in ('=', '|'):\n");
147+
fprintf(pyfp, " native = '<' if np.little_endian else '>'\n");
148+
fprintf(pyfp, " if arr.dtype.byteorder != native:\n");
149+
fprintf(pyfp, " arr = arr.byteswap().view(arr.dtype.newbyteorder('='))\n");
150+
fprintf(pyfp, " if not arr.flags.c_contiguous:\n");
151+
fprintf(pyfp, " arr = np.ascontiguousarray(arr)\n");
152+
fprintf(pyfp, " return torch.from_numpy(arr)\n");
153+
}
154+
110155
static int string_to_type(const char* s)
111156
{
112157
if (strcmp(s, "f32") == 0) return 1;
@@ -1456,7 +1501,7 @@ static std::string make_index_expression(const Operator* op)
14561501
return index_expr;
14571502
}
14581503

1459-
int Graph::python(const std::string& pypath, const std::string& pnnxbinpath, const std::vector<std::vector<int64_t> >& input_shapes)
1504+
int Graph::python(const std::string& pypath, const std::string& pnnxbinpath, const std::vector<std::vector<int64_t> >& input_shapes, const std::vector<std::string>& input_npy_paths)
14601505
{
14611506
FILE* pyfp = fopen(pypath.c_str(), "wb");
14621507
if (!pyfp)
@@ -1477,6 +1522,9 @@ int Graph::python(const std::string& pypath, const std::string& pnnxbinpath, con
14771522
fprintf(pyfp, "except:\n");
14781523
fprintf(pyfp, " pass\n");
14791524

1525+
if (!input_npy_paths.empty())
1526+
write_python_load_input_helper(pyfp);
1527+
14801528
fprintf(pyfp, "\n");
14811529

14821530
fprintf(pyfp, "class Model(nn.Module):\n");
@@ -2771,6 +2819,7 @@ int Graph::python(const std::string& pypath, const std::string& pnnxbinpath, con
27712819
fprintf(pyfp, " torch.manual_seed(0)\n");
27722820

27732821
int input_shapes_i = 0;
2822+
int input_npy_i = 0;
27742823

27752824
std::vector<std::string> input_names;
27762825
for (const Operator* op : ops)
@@ -2779,6 +2828,18 @@ int Graph::python(const std::string& pypath, const std::string& pnnxbinpath, con
27792828
continue;
27802829

27812830
const Operand* r = op->outputs[0];
2831+
std::string input_name = std::string("v_") + sanitize_identifier(r->name);
2832+
2833+
if (input_npy_i < (int)input_npy_paths.size())
2834+
{
2835+
std::string path_literal = python_string_literal(input_npy_paths[input_npy_i]);
2836+
fprintf(pyfp, " %s = _pnnx_load_input(%s)\n", input_name.c_str(), path_literal.c_str());
2837+
input_names.push_back(input_name);
2838+
input_npy_i++;
2839+
if (!input_shapes.empty())
2840+
input_shapes_i++;
2841+
continue;
2842+
}
27822843

27832844
std::vector<int> input_shape;
27842845
if (input_shapes.empty())
@@ -2794,7 +2855,6 @@ int Graph::python(const std::string& pypath, const std::string& pnnxbinpath, con
27942855
}
27952856
}
27962857

2797-
std::string input_name = std::string("v_") + sanitize_identifier(r->name);
27982858
if (type_is_integer(r->type))
27992859
{
28002860
fprintf(pyfp, " %s = torch.randint(10, (", input_name.c_str());

tools/pnnx/src/ir.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -324,7 +324,7 @@ class Graph
324324
int load(const std::string& parampath, const std::string& binpath);
325325
int save(const std::string& parampath, const std::string& binpath);
326326

327-
int python(const std::string& pypath, const std::string& binpath, const std::vector<std::vector<int64_t> >& input_shapes);
327+
int python(const std::string& pypath, const std::string& binpath, const std::vector<std::vector<int64_t> >& input_shapes, const std::vector<std::string>& input_npy_paths);
328328

329329
int parse(const std::string& param);
330330

tools/pnnx/src/load_tnn.cpp

Lines changed: 58 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,26 @@ static int get_tnn_tensor_type(int dt)
141141
return 0; // unknown type
142142
}
143143

144+
static int input_type_to_pnnx_type(const std::string& t)
145+
{
146+
if (t == "f32") return 1;
147+
if (t == "f64") return 2;
148+
if (t == "f16") return 3;
149+
if (t == "i32") return 4;
150+
if (t == "i64") return 5;
151+
if (t == "i16") return 6;
152+
if (t == "i8") return 7;
153+
if (t == "u8") return 8;
154+
if (t == "bool") return 9;
155+
if (t == "c64") return 10;
156+
if (t == "c128") return 11;
157+
if (t == "c32") return 12;
158+
if (t == "bf16") return 13;
159+
160+
fprintf(stderr, "unsupported input type %s\n", t.c_str());
161+
return 0;
162+
}
163+
144164
Attribute::Attribute(FILE* bp)
145165
{
146166
unsigned int magic;
@@ -178,7 +198,9 @@ Attribute::Attribute(FILE* bp)
178198
fread((void*)data.data(), 1, length, bp);
179199
}
180200

181-
int load_tnn(const std::string& tnnpath, Graph& pnnx_graph)
201+
int load_tnn(const std::string& tnnpath, Graph& pnnx_graph,
202+
const std::vector<std::vector<int64_t> >& input_shapes,
203+
const std::vector<std::string>& input_types)
182204
{
183205
fprintf(stderr, "############# pass_level0 tnn\n");
184206

@@ -244,14 +266,48 @@ int load_tnn(const std::string& tnnpath, Graph& pnnx_graph)
244266
int datatype = 0;
245267
sscanf(pline, "%d%n", &datatype, &ncomsumed);
246268

269+
int tensor_type = get_tnn_tensor_type(datatype);
270+
271+
if (!input_shapes.empty())
272+
{
273+
if (input_shapes.size() != 1)
274+
{
275+
fprintf(stderr, "tnn input expect 1 tensor but got %d\n", (int)input_shapes.size());
276+
return -1;
277+
}
278+
279+
shape.clear();
280+
const std::vector<int64_t>& input_shape = input_shapes[0];
281+
for (size_t i = 0; i < input_shape.size(); i++)
282+
{
283+
if (input_shape[i] < 0 || input_shape[i] > INT_MAX)
284+
{
285+
fprintf(stderr, "invalid tnn input shape dimension %ld\n", input_shape[i]);
286+
return -1;
287+
}
288+
shape.push_back((int)input_shape[i]);
289+
}
290+
291+
const int input_tensor_type = input_type_to_pnnx_type(input_types[0]);
292+
if (input_tensor_type == 0)
293+
return -1;
294+
if (tensor_type != 0 && tensor_type != input_tensor_type)
295+
{
296+
fprintf(stderr, "tnn input type mismatch, model type %d but got %s\n", tensor_type, input_types[0].c_str());
297+
return -1;
298+
}
299+
300+
tensor_type = input_tensor_type;
301+
}
302+
247303
Operator* op = pnnx_graph.new_operator("pnnx.Input", "input0");
248304

249305
Operand* r = pnnx_graph.new_operand(blob_name);
250306

251307
r->producer = op;
252308

253309
r->shape = shape;
254-
r->type = get_tnn_tensor_type(datatype);
310+
r->type = tensor_type;
255311

256312
op->outputs.push_back(r);
257313
}

tools/pnnx/src/load_tnn.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,9 @@
88

99
namespace pnnx {
1010

11-
int load_tnn(const std::string& tnnpath, Graph& g);
11+
int load_tnn(const std::string& tnnpath, Graph& g,
12+
const std::vector<std::vector<int64_t> >& input_shapes,
13+
const std::vector<std::string>& input_types);
1214

1315
} // namespace pnnx
1416

0 commit comments

Comments
 (0)