tinymlc 0.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- TinyMLC/ANG/__init__.py +0 -0
- TinyMLC/ANG/args.py +86 -0
- TinyMLC/ANG/estimator.py +103 -0
- TinyMLC/ANG/estimator_hal.py +184 -0
- TinyMLC/ANG/estimator_qemu.py +257 -0
- TinyMLC/ANG/estimator_software.py +130 -0
- TinyMLC/ANG/model_builder.py +508 -0
- TinyMLC/ANG/model_generator.py +439 -0
- TinyMLC/ANG/model_info.py +283 -0
- TinyMLC/ANG/utils.py +420 -0
- TinyMLC/__init__.py +0 -0
- TinyMLC/cli.py +126 -0
- TinyMLC/codegen.py +877 -0
- TinyMLC/converter/__init__.py +0 -0
- TinyMLC/converter/export_weights.py +382 -0
- TinyMLC/converter/parser_litert.py +757 -0
- TinyMLC/converter/parser_onnx.py +649 -0
- TinyMLC/generate_lut.py +97 -0
- TinyMLC/handlers.py +325 -0
- TinyMLC/ops.py +76 -0
- TinyMLC/templates/lut.c.tpl +23 -0
- TinyMLC/templates/lut.h.tpl +67 -0
- TinyMLC/templates/model.c.tpl +314 -0
- TinyMLC/templates/model.h.tpl +66 -0
- TinyMLC/transform/__init__.py +0 -0
- TinyMLC/transform/algebraic.py +286 -0
- TinyMLC/transform/base.py +58 -0
- TinyMLC/transform/constant_folding.py +260 -0
- TinyMLC/transform/cse.py +192 -0
- TinyMLC/transform/dce.py +182 -0
- TinyMLC/transform/fusion.py +723 -0
- TinyMLC/transform/memory.py +200 -0
- TinyMLC/transform/pass_manager.py +101 -0
- TinyMLC/transform/simplify.py +515 -0
- tinymlc-0.1.0.dist-info/METADATA +49 -0
- tinymlc-0.1.0.dist-info/RECORD +47 -0
- tinymlc-0.1.0.dist-info/WHEEL +4 -0
- tinymlc-0.1.0.dist-info/entry_points.txt +2 -0
- tinymlc-0.1.0.dist-info/licenses/LICENSE +201 -0
- utils/__init__.py +0 -0
- utils/arm-none-eabi-gcc.cmake +53 -0
- utils/dump.py +86 -0
- utils/generate_onnx_models.py +183 -0
- utils/generate_tflite_models.py +236 -0
- utils/pack_macos.sh +88 -0
- utils/path.py +31 -0
- utils/riscv-none-elf-gcc.cmake +50 -0
|
File without changes
|
|
@@ -0,0 +1,382 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
# TinyMLC - Tiny Machine Learning Compiler
|
|
3
|
+
#
|
|
4
|
+
# Copyright (c) 2026 Jia Liu & TinyMLC Contributors
|
|
5
|
+
# SPDX-License-Identifier: Apache-2.0
|
|
6
|
+
#
|
|
7
|
+
# This file is part of TinyMLC.
|
|
8
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
9
|
+
# you may not use this file except in compliance with the License.
|
|
10
|
+
# You may obtain a copy of the License at:
|
|
11
|
+
#
|
|
12
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
13
|
+
#
|
|
14
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
15
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
16
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
17
|
+
# See the License for the specific language governing permissions and
|
|
18
|
+
# limitations under the License.
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
import numpy as np
|
|
22
|
+
|
|
23
|
+
from utils.dump import warning, info
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def quantize_to_int8(tensor):
|
|
27
|
+
"""
|
|
28
|
+
Quantize float32 weights to int8 (symmetric quantization)
|
|
29
|
+
|
|
30
|
+
Symmetric quantization:
|
|
31
|
+
- Value range: -127 ~ 127 (avoid -128 for symmetry)
|
|
32
|
+
- scale = max(|min|, |max|) / 127
|
|
33
|
+
- quantized = round(tensor / scale)
|
|
34
|
+
|
|
35
|
+
Args:
|
|
36
|
+
tensor: float32 weight tensor
|
|
37
|
+
|
|
38
|
+
Returns:
|
|
39
|
+
quantized: int8 quantized weights
|
|
40
|
+
scale: quantization scale
|
|
41
|
+
"""
|
|
42
|
+
min_val = tensor.min()
|
|
43
|
+
max_val = tensor.max()
|
|
44
|
+
# Symmetric quantization: scale = max(|min|, |max|) / 127
|
|
45
|
+
scale = max(abs(min_val), abs(max_val)) / 127.0
|
|
46
|
+
if scale == 0:
|
|
47
|
+
scale = 1.0
|
|
48
|
+
quantized = np.round(tensor / scale).astype(np.int8)
|
|
49
|
+
return quantized, scale
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def export_weights_to_c(weights, name, output_file):
|
|
53
|
+
"""Export int8 weights to C header file"""
|
|
54
|
+
if weights is None:
|
|
55
|
+
output_file.write(f"// {name} not found, using placeholder\n")
|
|
56
|
+
output_file.write(f"static const int8_t {name}[1] = {{0}};\n\n")
|
|
57
|
+
return
|
|
58
|
+
|
|
59
|
+
flat = weights.flatten()
|
|
60
|
+
output_file.write(f"static const int8_t {name}[{flat.size}] = {{\n ")
|
|
61
|
+
for i, val in enumerate(flat):
|
|
62
|
+
output_file.write(f"{int(val)}")
|
|
63
|
+
if i < flat.size - 1:
|
|
64
|
+
output_file.write(", ")
|
|
65
|
+
if (i + 1) % 16 == 0:
|
|
66
|
+
output_file.write("\n ")
|
|
67
|
+
output_file.write("\n};\n\n")
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def export_bias_to_c(bias, name, output_file):
|
|
71
|
+
"""Export int32 bias to C header file"""
|
|
72
|
+
if bias is None:
|
|
73
|
+
output_file.write(f"// {name} not found, using placeholder\n")
|
|
74
|
+
output_file.write(f"static const int32_t {name}[1] = {{0}};\n\n")
|
|
75
|
+
return
|
|
76
|
+
|
|
77
|
+
flat = bias.flatten()
|
|
78
|
+
output_file.write(f"static const int32_t {name}[{flat.size}] = {{\n ")
|
|
79
|
+
for i, val in enumerate(flat):
|
|
80
|
+
output_file.write(f"{int(val)}")
|
|
81
|
+
if i < flat.size - 1:
|
|
82
|
+
output_file.write(", ")
|
|
83
|
+
if (i + 1) % 8 == 0:
|
|
84
|
+
output_file.write("\n ")
|
|
85
|
+
output_file.write("\n};\n\n")
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
def export_concatenated_weights(weights_dict, output_file, array_name,
|
|
89
|
+
dtype='int8'):
|
|
90
|
+
"""Export concatenated weight array from gate dictionary.
|
|
91
|
+
|
|
92
|
+
Args:
|
|
93
|
+
weights_dict: dict with keys ['i', 'f', 'g', 'o']
|
|
94
|
+
containing weight arrays
|
|
95
|
+
output_file: file handle to write to
|
|
96
|
+
array_name: C array name
|
|
97
|
+
dtype: 'int8' or 'int32'
|
|
98
|
+
"""
|
|
99
|
+
gate_order = ['i', 'f', 'g', 'o']
|
|
100
|
+
arrays = []
|
|
101
|
+
total_size = 0
|
|
102
|
+
missing_gates = []
|
|
103
|
+
|
|
104
|
+
for gate in gate_order:
|
|
105
|
+
w = weights_dict.get(gate)
|
|
106
|
+
if w is None:
|
|
107
|
+
missing_gates.append(gate)
|
|
108
|
+
continue
|
|
109
|
+
arrays.append(w.flatten())
|
|
110
|
+
total_size += w.size
|
|
111
|
+
|
|
112
|
+
if missing_gates:
|
|
113
|
+
warning(f"{missing_gates} gate weights missing, padding with zeros")
|
|
114
|
+
# Get shape from first non-None weight
|
|
115
|
+
for gate in gate_order:
|
|
116
|
+
w = weights_dict.get(gate)
|
|
117
|
+
if w is not None:
|
|
118
|
+
shape = w.shape
|
|
119
|
+
for mg in missing_gates:
|
|
120
|
+
zero_array = np.zeros(shape, dtype=np.int8)
|
|
121
|
+
arrays.append(zero_array.flatten())
|
|
122
|
+
total_size += zero_array.size
|
|
123
|
+
break
|
|
124
|
+
|
|
125
|
+
if not arrays:
|
|
126
|
+
output_file.write(f"// {array_name} not found, using placeholder\n")
|
|
127
|
+
output_file.write(f"static const int8_t {array_name}[1] = {{0}};\n\n")
|
|
128
|
+
return
|
|
129
|
+
|
|
130
|
+
concatenated = np.concatenate(arrays)
|
|
131
|
+
total_size = len(concatenated)
|
|
132
|
+
|
|
133
|
+
c_type = 'int8_t' if dtype == 'int8' else 'int32_t'
|
|
134
|
+
output_file.write(
|
|
135
|
+
f"static const {c_type} {array_name}[{total_size}] = {{\n ")
|
|
136
|
+
for i, val in enumerate(concatenated):
|
|
137
|
+
output_file.write(f"{int(val)}")
|
|
138
|
+
if i < total_size - 1:
|
|
139
|
+
output_file.write(", ")
|
|
140
|
+
if (i + 1) % 16 == 0:
|
|
141
|
+
output_file.write("\n ")
|
|
142
|
+
output_file.write("\n};\n\n")
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
def export_concatenated_bias(bias_dict, output_file, array_name):
|
|
146
|
+
"""Export concatenated bias array from gate dictionary.
|
|
147
|
+
|
|
148
|
+
Args:
|
|
149
|
+
bias_dict: dict with keys ['i', 'f', 'g', 'o'] containing bias arrays
|
|
150
|
+
output_file: file handle to write to
|
|
151
|
+
array_name: C array name
|
|
152
|
+
"""
|
|
153
|
+
gate_order = ['i', 'f', 'g', 'o']
|
|
154
|
+
arrays = []
|
|
155
|
+
|
|
156
|
+
for gate in gate_order:
|
|
157
|
+
b = bias_dict.get(gate)
|
|
158
|
+
if b is not None:
|
|
159
|
+
arrays.append(b.flatten())
|
|
160
|
+
|
|
161
|
+
if not arrays:
|
|
162
|
+
output_file.write(f"// {array_name} not found, using placeholder\n")
|
|
163
|
+
output_file.write(f"static const int32_t {array_name}[1] = {{0}};\n\n")
|
|
164
|
+
return
|
|
165
|
+
|
|
166
|
+
concatenated = np.concatenate(arrays)
|
|
167
|
+
total_size = len(concatenated)
|
|
168
|
+
|
|
169
|
+
output_file.write(
|
|
170
|
+
f"static const int32_t {array_name}[{total_size}] = {{\n ")
|
|
171
|
+
for i, val in enumerate(concatenated):
|
|
172
|
+
output_file.write(f"{int(val)}")
|
|
173
|
+
if i < total_size - 1:
|
|
174
|
+
output_file.write(", ")
|
|
175
|
+
if (i + 1) % 8 == 0:
|
|
176
|
+
output_file.write("\n ")
|
|
177
|
+
output_file.write("\n};\n\n")
|
|
178
|
+
|
|
179
|
+
|
|
180
|
+
def export_model_weights(output_dir, model_info):
|
|
181
|
+
"""Unified weight export function for ONNX, TFLite and ANG models.
|
|
182
|
+
|
|
183
|
+
Weights are identified by source-specific keys:
|
|
184
|
+
- TFLite: "fc_tflite.weight", "fc_tflite.bias",
|
|
185
|
+
"lstm_tflite.weight_ih", etc.
|
|
186
|
+
- ONNX: "fc_onnx.weight", "fc_onnx.bias", "conv_onnx.weight", etc.
|
|
187
|
+
- ANG: tensor index as key in weights dict
|
|
188
|
+
|
|
189
|
+
Returns quant_scales dict.
|
|
190
|
+
"""
|
|
191
|
+
weights = model_info.get("weights", {})
|
|
192
|
+
ops = model_info.get("ops", [])
|
|
193
|
+
quant_scales = {}
|
|
194
|
+
|
|
195
|
+
# Input scale fixed at 1/256 (symmetric quantization, zero_point=0)
|
|
196
|
+
input_scale = 0.00390625
|
|
197
|
+
|
|
198
|
+
# For ANG models, weights are stored by tensor index
|
|
199
|
+
# We need to extract them from ops
|
|
200
|
+
def get_weight_by_idx(weights, idx):
|
|
201
|
+
"""Get weight from weights dict by tensor index"""
|
|
202
|
+
if idx is None:
|
|
203
|
+
return None
|
|
204
|
+
# ANG format: weights[str(idx)] = numpy array or list
|
|
205
|
+
if str(idx) in weights:
|
|
206
|
+
w = weights[str(idx)]
|
|
207
|
+
# Convert list to numpy array
|
|
208
|
+
if isinstance(w, list):
|
|
209
|
+
return np.array(w)
|
|
210
|
+
return w
|
|
211
|
+
# Also try int key
|
|
212
|
+
if idx in weights:
|
|
213
|
+
w = weights[idx]
|
|
214
|
+
if isinstance(w, list):
|
|
215
|
+
return np.array(w)
|
|
216
|
+
return w
|
|
217
|
+
return None
|
|
218
|
+
|
|
219
|
+
# Collect FC weights from ops
|
|
220
|
+
fc_weights = []
|
|
221
|
+
for op in ops:
|
|
222
|
+
if op.get("op_name") == "FULLY_CONNECTED":
|
|
223
|
+
input_indices = op.get("input_indices", [])
|
|
224
|
+
if len(input_indices) >= 3:
|
|
225
|
+
weight_idx = input_indices[1]
|
|
226
|
+
bias_idx = input_indices[2]
|
|
227
|
+
fc_w = get_weight_by_idx(weights, weight_idx)
|
|
228
|
+
fc_b = get_weight_by_idx(weights, bias_idx)
|
|
229
|
+
if fc_w is not None:
|
|
230
|
+
fc_weights.append((fc_w, fc_b))
|
|
231
|
+
|
|
232
|
+
# Export FC weights
|
|
233
|
+
if fc_weights:
|
|
234
|
+
all_fc_weights = []
|
|
235
|
+
all_fc_bias = []
|
|
236
|
+
for fc_w, fc_b in fc_weights:
|
|
237
|
+
all_fc_weights.append(fc_w.flatten())
|
|
238
|
+
if fc_b is not None:
|
|
239
|
+
all_fc_bias.append(fc_b.flatten())
|
|
240
|
+
else:
|
|
241
|
+
all_fc_bias.append(np.zeros(fc_w.shape[-1], dtype=np.int32))
|
|
242
|
+
|
|
243
|
+
fc_weights_concat = (np.concatenate(all_fc_weights)
|
|
244
|
+
if len(all_fc_weights) > 1 else all_fc_weights[0])
|
|
245
|
+
fc_bias_concat = (np.concatenate(all_fc_bias)
|
|
246
|
+
if len(all_fc_bias) > 1 else all_fc_bias[0])
|
|
247
|
+
|
|
248
|
+
fc_scale = 0.01
|
|
249
|
+
fc_weight_int8 = (fc_weights_concat
|
|
250
|
+
if fc_weights_concat.dtype == np.int8
|
|
251
|
+
else quantize_to_int8(fc_weights_concat)[0])
|
|
252
|
+
fc_bias_int32 = (fc_bias_concat.astype(np.int32)
|
|
253
|
+
if fc_bias_concat.dtype != np.int32
|
|
254
|
+
else fc_bias_concat)
|
|
255
|
+
|
|
256
|
+
with open(output_dir / 'fc_weights.h', 'w') as f:
|
|
257
|
+
f.write("// FC layer weights and bias extracted from ANG model\n\n")
|
|
258
|
+
export_weights_to_c(fc_weight_int8, "fc_weights", f)
|
|
259
|
+
export_bias_to_c(fc_bias_int32, "fc_bias", f)
|
|
260
|
+
info(f"Generated: {output_dir}/fc_weights.h")
|
|
261
|
+
quant_scales["fc_scale"] = fc_scale
|
|
262
|
+
|
|
263
|
+
# Collect CONV weights from ops
|
|
264
|
+
conv_weights_list = []
|
|
265
|
+
conv_bias_list = []
|
|
266
|
+
for op in ops:
|
|
267
|
+
if op.get("op_name") == "CONV_2D":
|
|
268
|
+
input_indices = op.get("input_indices", [])
|
|
269
|
+
if len(input_indices) >= 2:
|
|
270
|
+
weight_idx = input_indices[1]
|
|
271
|
+
bias_idx = input_indices[2] if len(input_indices) >= 3 else None
|
|
272
|
+
conv_w = get_weight_by_idx(weights, weight_idx)
|
|
273
|
+
conv_b = (get_weight_by_idx(weights, bias_idx)
|
|
274
|
+
if bias_idx else None)
|
|
275
|
+
if conv_w is not None:
|
|
276
|
+
conv_w = np.array(conv_w)
|
|
277
|
+
conv_weights_list.append(conv_w)
|
|
278
|
+
if conv_b is not None:
|
|
279
|
+
conv_b = np.array(conv_b)
|
|
280
|
+
conv_bias_list.append(
|
|
281
|
+
conv_b if conv_b is not None
|
|
282
|
+
else np.zeros(conv_w.shape[-1], dtype=np.int32))
|
|
283
|
+
|
|
284
|
+
# Export CONV weights
|
|
285
|
+
if conv_weights_list:
|
|
286
|
+
conv_weights_concat = np.concatenate(
|
|
287
|
+
[np.array(w).flatten() for w in conv_weights_list])
|
|
288
|
+
conv_bias_concat = np.concatenate(
|
|
289
|
+
[np.array(b).flatten() for b in conv_bias_list])
|
|
290
|
+
|
|
291
|
+
conv_weight_int8 = (conv_weights_concat
|
|
292
|
+
if conv_weights_concat.dtype == np.int8
|
|
293
|
+
else quantize_to_int8(conv_weights_concat)[0])
|
|
294
|
+
conv_bias_int32 = (conv_bias_concat.astype(np.int32)
|
|
295
|
+
if conv_bias_concat.dtype != np.int32
|
|
296
|
+
else conv_bias_concat)
|
|
297
|
+
|
|
298
|
+
with open(output_dir / 'conv_weights.h', 'w') as f:
|
|
299
|
+
f.write("// CONV_2D weights and bias extracted from ANG model\n\n")
|
|
300
|
+
export_weights_to_c(conv_weight_int8, "conv_weights", f)
|
|
301
|
+
export_bias_to_c(conv_bias_int32, "conv_bias", f)
|
|
302
|
+
info(f"Generated: {output_dir}/conv_weights.h")
|
|
303
|
+
|
|
304
|
+
# Try TFLite/ONNX format as fallback
|
|
305
|
+
fc_weight = weights.get("fc_tflite.weight") or weights.get("fc_onnx.weight")
|
|
306
|
+
fc_bias = weights.get("fc_tflite.bias") or weights.get("fc_onnx.bias")
|
|
307
|
+
if fc_weight is not None and fc_bias is not None:
|
|
308
|
+
fc_scale = 0.01
|
|
309
|
+
if fc_weight.dtype == np.int8:
|
|
310
|
+
fc_weight_int8 = fc_weight
|
|
311
|
+
fc_bias_int32 = (fc_bias.astype(np.int32)
|
|
312
|
+
if fc_bias.dtype != np.int32 else fc_bias)
|
|
313
|
+
else:
|
|
314
|
+
fc_weight_int8, fc_scale = quantize_to_int8(fc_weight)
|
|
315
|
+
fc_bias_int32 = (fc_bias / (input_scale * fc_scale)
|
|
316
|
+
).astype(np.int32)
|
|
317
|
+
|
|
318
|
+
with open(output_dir / 'fc_weights.h', 'w') as f:
|
|
319
|
+
f.write("// FC layer weights and bias extracted from model\n\n")
|
|
320
|
+
export_weights_to_c(fc_weight_int8, "fc_weights", f)
|
|
321
|
+
export_bias_to_c(fc_bias_int32, "fc_bias", f)
|
|
322
|
+
info(f"Generated: {output_dir}/fc_weights.h")
|
|
323
|
+
quant_scales["fc_scale"] = fc_scale
|
|
324
|
+
|
|
325
|
+
# LSTM weights
|
|
326
|
+
lstm_weight_ih = (
|
|
327
|
+
weights.get("lstm_tflite.weight_ih") or
|
|
328
|
+
weights.get("lstm_onnx.weight_ih"))
|
|
329
|
+
lstm_weight_hh = (
|
|
330
|
+
weights.get("lstm_tflite.weight_hh") or
|
|
331
|
+
weights.get("lstm_onnx.weight_hh"))
|
|
332
|
+
lstm_bias = (
|
|
333
|
+
weights.get("lstm_tflite.bias") or
|
|
334
|
+
weights.get("lstm_onnx.bias"))
|
|
335
|
+
if lstm_weight_ih is not None and lstm_weight_hh is not None:
|
|
336
|
+
with open(output_dir / 'lstm_weights.h', 'w') as f:
|
|
337
|
+
f.write("// LSTM gate weights and bias extracted from model\n")
|
|
338
|
+
f.write("// Order: i, f, g, o\n\n")
|
|
339
|
+
export_weights_to_c(lstm_weight_ih, "lstm_input_weights", f)
|
|
340
|
+
export_weights_to_c(lstm_weight_hh, "lstm_recurrent_weights", f)
|
|
341
|
+
if lstm_bias is not None:
|
|
342
|
+
export_bias_to_c(lstm_bias, "lstm_bias", f)
|
|
343
|
+
info(f"Generated: {output_dir}/lstm_weights.h")
|
|
344
|
+
|
|
345
|
+
# Conv weights - try both TFLite and ONNX sources
|
|
346
|
+
conv_weight = (weights.get("conv_tflite.weight") or
|
|
347
|
+
weights.get("conv_onnx.weight"))
|
|
348
|
+
conv_bias = (weights.get("conv_tflite.bias") or
|
|
349
|
+
weights.get("conv_onnx.bias"))
|
|
350
|
+
if conv_weight is not None:
|
|
351
|
+
conv_weight_int8 = (conv_weight
|
|
352
|
+
if conv_weight.dtype == np.int8
|
|
353
|
+
else quantize_to_int8(conv_weight)[0])
|
|
354
|
+
with open(output_dir / 'conv_weights.h', 'w') as f:
|
|
355
|
+
f.write("// CONV_2D weights and bias extracted from model\n\n")
|
|
356
|
+
export_weights_to_c(conv_weight_int8, "conv_weights", f)
|
|
357
|
+
if conv_bias is not None:
|
|
358
|
+
conv_bias_int32 = (conv_bias.astype(np.int32)
|
|
359
|
+
if conv_bias.dtype != np.int32 else conv_bias)
|
|
360
|
+
export_bias_to_c(conv_bias_int32, "conv_bias", f)
|
|
361
|
+
info(f"Generated: {output_dir}/conv_weights.h")
|
|
362
|
+
|
|
363
|
+
# Depthwise Conv weights
|
|
364
|
+
dw_weight = (weights.get("dw_tflite.weight") or
|
|
365
|
+
weights.get("dw_onnx.weight"))
|
|
366
|
+
dw_bias = (weights.get("dw_tflite.bias") or
|
|
367
|
+
weights.get("dw_onnx.bias"))
|
|
368
|
+
if dw_weight is not None:
|
|
369
|
+
dw_weight_int8 = (dw_weight
|
|
370
|
+
if dw_weight.dtype == np.int8
|
|
371
|
+
else quantize_to_int8(dw_weight)[0])
|
|
372
|
+
with open(output_dir / 'dw_weights.h', 'w') as f:
|
|
373
|
+
f.write("// Depthwise Conv2D weights and bias "
|
|
374
|
+
"extracted from model\n\n")
|
|
375
|
+
export_weights_to_c(dw_weight_int8, "dw_weights", f)
|
|
376
|
+
if dw_bias is not None:
|
|
377
|
+
dw_bias_int32 = (dw_bias.astype(np.int32)
|
|
378
|
+
if dw_bias.dtype != np.int32 else dw_bias)
|
|
379
|
+
export_bias_to_c(dw_bias_int32, "dw_bias", f)
|
|
380
|
+
info(f"Generated: {output_dir}/dw_weights.h")
|
|
381
|
+
|
|
382
|
+
return quant_scales
|