0
# NumPy Integration
1
2
Bidirectional conversion between ONNX tensors and NumPy arrays, supporting all ONNX data types including specialized formats like bfloat16 and float8 variants. This module enables seamless integration between ONNX models and NumPy-based data processing workflows.
3
4
## Capabilities
5
6
### Array Conversion
7
8
Convert between ONNX TensorProto and NumPy arrays with full type support.
9
10
```python { .api }
11
def to_array(tensor: TensorProto, base_dir: str = "") -> np.ndarray:
12
"""
13
Converts a tensor def object to a numpy array.
14
15
Parameters:
16
- tensor: a TensorProto object.
17
- base_dir: if external tensor exists, base_dir can help to find the path to it
18
19
Returns:
20
numpy.ndarray: NumPy array with appropriate dtype and shape
21
22
Raises:
23
ValueError: If tensor data is invalid or unsupported
24
"""
25
26
def from_array(arr: np.ndarray, name: Optional[str] = None) -> TensorProto:
27
"""
28
Converts a numpy array to a tensor def.
29
30
Parameters:
31
- arr: a numpy array.
32
- name: (optional) the name of the tensor.
33
34
Returns:
35
TensorProto: ONNX tensor representation
36
37
Raises:
38
ValueError: If array dtype is not supported by ONNX
39
"""
40
```
41
42
### Container Conversion
43
44
Convert between ONNX container types and Python collections.
45
46
```python { .api }
47
def to_list(sequence):
48
"""
49
Convert ONNX SequenceProto to Python list.
50
51
Parameters:
52
- sequence: SequenceProto to convert
53
54
Returns:
55
list: Python list containing sequence elements
56
57
Raises:
58
ValueError: If sequence contains unsupported element types
59
"""
60
61
def from_list(lst, dtype=None, name=None):
62
"""
63
Convert Python list to ONNX SequenceProto.
64
65
Parameters:
66
- lst: Python list to convert
67
- dtype: Optional element data type specification
68
- name: Optional name for the sequence
69
70
Returns:
71
SequenceProto: ONNX sequence representation
72
73
Raises:
74
ValueError: If list elements cannot be converted to ONNX types
75
"""
76
77
def to_dict(map_proto):
78
"""
79
Convert ONNX MapProto to Python dictionary.
80
81
Parameters:
82
- map_proto: MapProto to convert
83
84
Returns:
85
dict: Python dictionary with converted key-value pairs
86
87
Raises:
88
ValueError: If map contains unsupported key or value types
89
"""
90
91
def from_dict(dict_, name=None):
92
"""
93
Convert Python dictionary to ONNX MapProto.
94
95
Parameters:
96
- dict_: Python dictionary to convert
97
- name: Optional name for the map
98
99
Returns:
100
MapProto: ONNX map representation
101
102
Raises:
103
ValueError: If dictionary keys or values cannot be converted
104
"""
105
106
def to_optional(optional):
107
"""
108
Convert ONNX OptionalProto to Python optional value.
109
110
Parameters:
111
- optional: OptionalProto to convert
112
113
Returns:
114
Any or None: Converted value or None if optional is empty
115
116
Raises:
117
ValueError: If optional contains unsupported element type
118
"""
119
120
def from_optional(value, name=None):
121
"""
122
Convert Python value to ONNX OptionalProto.
123
124
Parameters:
125
- value: Python value to convert (None for empty optional)
126
- name: Optional name for the optional
127
128
Returns:
129
OptionalProto: ONNX optional representation
130
131
Raises:
132
ValueError: If value cannot be converted to ONNX type
133
"""
134
```
135
136
### Specialized Float Type Conversion
137
138
Convert between specialized floating point formats and standard types.
139
140
```python { .api }
141
def bfloat16_to_float32(bfloat16_data, dims=None):
142
"""
143
Convert bfloat16 data to float32.
144
145
Parameters:
146
- bfloat16_data: NumPy array of bfloat16 data (as uint16)
147
- dims: Optional target dimensions for reshaping
148
149
Returns:
150
numpy.ndarray: Float32 array with converted values
151
"""
152
153
def float8e4m3_to_float32(float8_data, fn=True, uz=False):
154
"""
155
Convert float8 E4M3 data to float32.
156
157
Parameters:
158
- float8_data: NumPy array of float8 E4M3 data (as uint8)
159
- fn: Whether finite values only (True) or include infinities
160
- uz: Whether to use unsigned zero representation
161
162
Returns:
163
numpy.ndarray: Float32 array with converted values
164
"""
165
166
def float8e5m2_to_float32(float8_data, fn=True, uz=False):
167
"""
168
Convert float8 E5M2 data to float32.
169
170
Parameters:
171
- float8_data: NumPy array of float8 E5M2 data (as uint8)
172
- fn: Whether finite values only (True) or include infinities
173
- uz: Whether to use unsigned zero representation
174
175
Returns:
176
numpy.ndarray: Float32 array with converted values
177
"""
178
179
def combine_pairs_to_complex(fa):
180
"""
181
Combine pairs of float values to complex numbers.
182
183
Parameters:
184
- fa: Sequence of float values (length must be even)
185
186
Returns:
187
list: List of complex numbers
188
"""
189
```
190
191
### Utility Functions
192
193
Additional utilities for tensor data manipulation and conversion.
194
195
```python { .api }
196
def convert_endian(tensor):
197
"""
198
Convert tensor data endianness in place for cross-platform compatibility.
199
200
Parameters:
201
- tensor: TensorProto to modify
202
203
Returns:
204
None: Modifies tensor in place
205
"""
206
207
def create_random_int(shape, low=0, high=100, output_type=TensorProto.INT32, seed=None):
208
"""
209
Create a tensor with random integer values.
210
211
Parameters:
212
- shape: Tensor shape (list of integers)
213
- low: Minimum value (inclusive)
214
- high: Maximum value (exclusive)
215
- output_type: ONNX data type for output
216
- seed: Random seed for reproducibility
217
218
Returns:
219
TensorProto: Tensor with random integer data
220
"""
221
```
222
223
## Usage Examples
224
225
### Basic Array Conversion
226
227
```python
228
import onnx
229
from onnx import numpy_helper, TensorProto
230
import numpy as np
231
232
# Convert NumPy array to ONNX tensor
233
np_array = np.array([[1.0, 2.0, 3.0], [4.0, 5.0, 6.0]], dtype=np.float32)
234
onnx_tensor = numpy_helper.from_array(np_array, name="my_tensor")
235
236
print(f"ONNX tensor shape: {onnx_tensor.dims}")
237
print(f"ONNX tensor type: {onnx_tensor.data_type}")
238
239
# Convert ONNX tensor back to NumPy array
240
converted_array = numpy_helper.to_array(onnx_tensor)
241
print(f"Converted array shape: {converted_array.shape}")
242
print(f"Converted array dtype: {converted_array.dtype}")
243
print(f"Data preserved: {np.array_equal(np_array, converted_array)}")
244
```
245
246
### Working with Different Data Types
247
248
```python
249
import onnx
250
from onnx import numpy_helper, TensorProto
251
import numpy as np
252
253
# Create arrays with different dtypes
254
int_array = np.array([1, 2, 3, 4, 5], dtype=np.int64)
255
float_array = np.array([1.5, 2.5, 3.5], dtype=np.float64)
256
bool_array = np.array([True, False, True], dtype=bool)
257
258
# Convert to ONNX tensors
259
int_tensor = numpy_helper.from_array(int_array, "integers")
260
float_tensor = numpy_helper.from_array(float_array, "floats")
261
bool_tensor = numpy_helper.from_array(bool_array, "booleans")
262
263
print(f"Int tensor type: {int_tensor.data_type} (should be {TensorProto.INT64})")
264
print(f"Float tensor type: {float_tensor.data_type} (should be {TensorProto.DOUBLE})")
265
print(f"Bool tensor type: {bool_tensor.data_type} (should be {TensorProto.BOOL})")
266
267
# Convert back and verify
268
print(f"Int conversion: {np.array_equal(int_array, numpy_helper.to_array(int_tensor))}")
269
print(f"Float conversion: {np.array_equal(float_array, numpy_helper.to_array(float_tensor))}")
270
print(f"Bool conversion: {np.array_equal(bool_array, numpy_helper.to_array(bool_tensor))}")
271
```
272
273
### Container Type Conversion
274
275
```python
276
import onnx
277
from onnx import numpy_helper
278
import numpy as np
279
280
# Work with sequences
281
python_list = [
282
np.array([1, 2, 3], dtype=np.float32),
283
np.array([4, 5, 6], dtype=np.float32),
284
np.array([7, 8, 9], dtype=np.float32)
285
]
286
287
# Convert to ONNX sequence
288
onnx_sequence = numpy_helper.from_list(python_list, name="tensor_sequence")
289
print(f"Sequence has {len(onnx_sequence.tensor_values)} tensors")
290
291
# Convert back to Python list
292
converted_list = numpy_helper.to_list(onnx_sequence)
293
print(f"Converted list has {len(converted_list)} arrays")
294
295
# Work with dictionaries
296
python_dict = {
297
"feature_1": np.array([0.1, 0.2, 0.3], dtype=np.float32),
298
"feature_2": np.array([0.4, 0.5, 0.6], dtype=np.float32)
299
}
300
301
# Convert to ONNX map
302
onnx_map = numpy_helper.from_dict(python_dict, name="feature_map")
303
print(f"Map has {len(onnx_map.string_keys)} keys")
304
305
# Convert back to Python dict
306
converted_dict = numpy_helper.to_dict(onnx_map)
307
print(f"Converted dict keys: {list(converted_dict.keys())}")
308
309
# Work with optional values
310
optional_value = np.array([1, 2, 3], dtype=np.int32)
311
onnx_optional = numpy_helper.from_optional(optional_value, name="maybe_tensor")
312
313
# Convert back
314
converted_optional = numpy_helper.to_optional(onnx_optional)
315
print(f"Optional value shape: {converted_optional.shape}")
316
317
# Empty optional
318
empty_optional = numpy_helper.from_optional(None, name="empty_optional")
319
converted_empty = numpy_helper.to_optional(empty_optional)
320
print(f"Empty optional: {converted_empty}") # Should be None
321
```
322
323
### Specialized Float Type Handling
324
325
```python
326
import onnx
327
from onnx import numpy_helper
328
import numpy as np
329
330
# Simulate bfloat16 data (normally this would come from a model)
331
# bfloat16 is stored as uint16 with the lower 16 bits truncated
332
float32_data = np.array([1.0, 2.5, 3.14159, -1.5], dtype=np.float32)
333
334
# Convert to bfloat16 representation (this is conceptual)
335
# In practice, you'd get this from an ONNX tensor
336
bfloat16_as_uint16 = (float32_data.view(np.uint32) >> 16).astype(np.uint16)
337
338
# Convert bfloat16 back to float32
339
recovered_float32 = numpy_helper.bfloat16_to_float32(bfloat16_as_uint16)
340
print(f"Original: {float32_data}")
341
print(f"Recovered: {recovered_float32}")
342
343
# Work with complex numbers
344
complex_data = [1.0, 2.0, 3.0, 4.0] # Represents (1+2j) and (3+4j)
345
complex_numbers = numpy_helper.combine_pairs_to_complex(complex_data)
346
print(f"Complex numbers: {complex_numbers}")
347
```
348
349
### Integration with Model Processing
350
351
```python
352
import onnx
353
from onnx import numpy_helper
354
import numpy as np
355
356
def process_model_tensors(model_path, output_path):
357
"""Process all tensors in a model using NumPy operations."""
358
359
# Load model
360
model = onnx.load_model(model_path)
361
362
# Process initializer tensors
363
for i, tensor in enumerate(model.graph.initializer):
364
print(f"Processing tensor: {tensor.name}")
365
366
# Convert to NumPy array
367
np_array = numpy_helper.to_array(tensor)
368
print(f" Original shape: {np_array.shape}, dtype: {np_array.dtype}")
369
370
# Perform NumPy operations (example: normalize weights)
371
if np_array.dtype in [np.float32, np.float64]:
372
# Normalize to zero mean, unit variance
373
normalized = (np_array - np_array.mean()) / (np_array.std() + 1e-8)
374
375
# Convert back to ONNX tensor
376
new_tensor = numpy_helper.from_array(normalized, tensor.name)
377
378
# Replace in model
379
model.graph.initializer[i].CopyFrom(new_tensor)
380
print(f" Normalized tensor: mean={normalized.mean():.6f}, std={normalized.std():.6f}")
381
382
# Save processed model
383
onnx.save_model(model, output_path)
384
print(f"Processed model saved to: {output_path}")
385
386
# Example usage (commented out)
387
# process_model_tensors("input_model.onnx", "normalized_model.onnx")
388
```
389
390
### Error Handling and Data Validation
391
392
```python
393
import onnx
394
from onnx import numpy_helper, TensorProto
395
import numpy as np
396
397
def safe_tensor_conversion(np_array, tensor_name):
398
"""Safely convert NumPy array to ONNX tensor with error handling."""
399
400
try:
401
# Check for supported dtypes
402
if np_array.dtype not in [np.float32, np.float64, np.int32, np.int64,
403
np.uint32, np.uint64, np.bool_, np.float16]:
404
print(f"Warning: dtype {np_array.dtype} may not be fully supported")
405
406
# Check for NaN or infinite values in float arrays
407
if np.issubdtype(np_array.dtype, np.floating):
408
if np.any(np.isnan(np_array)):
409
print("Warning: Array contains NaN values")
410
if np.any(np.isinf(np_array)):
411
print("Warning: Array contains infinite values")
412
413
# Convert to ONNX tensor
414
onnx_tensor = numpy_helper.from_array(np_array, tensor_name)
415
416
# Verify round-trip conversion
417
recovered_array = numpy_helper.to_array(onnx_tensor)
418
419
if not np.allclose(np_array, recovered_array, equal_nan=True):
420
print("Warning: Round-trip conversion changed values")
421
return None
422
423
print(f"Successfully converted tensor '{tensor_name}'")
424
return onnx_tensor
425
426
except Exception as e:
427
print(f"Error converting tensor '{tensor_name}': {e}")
428
return None
429
430
# Test with various arrays
431
test_arrays = [
432
(np.array([1, 2, 3], dtype=np.int32), "integers"),
433
(np.array([1.0, 2.0, np.nan], dtype=np.float32), "with_nan"),
434
(np.array([1.0, 2.0, np.inf], dtype=np.float32), "with_inf"),
435
(np.array([True, False, True], dtype=bool), "booleans"),
436
]
437
438
for arr, name in test_arrays:
439
tensor = safe_tensor_conversion(arr, name)
440
```