Coverage for mlprodict/onnx_tools/model_checker.py: 100%
Shortcuts on this page
r m x toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
Shortcuts on this page
r m x toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1"""
2@file
3@brief Investigate issues happening with float32.
4"""
5import numpy
6from numpy.random import randint
9def astype_range(arr, dtype=numpy.float32, force=1):
10 """
11 Computes ranges for every number in an array
12 once converted into *float32*. The function returns
13 two matrices which produces two numbers
14 *a* et *b*, the number rounded to float32
15 is in interval :math:`[a, b]`.
17 @param arr array
18 @param dtype type to convert to
19 @param force does something like *[i] +/- force |i - [i]|*
20 @return minimum, maximum
21 """
22 conv = arr.astype(dtype)
23 delta = numpy.abs(arr - conv)
24 delta = numpy.maximum(numpy.abs(arr) * 1e-7, delta)
25 maxa = (conv + delta * force).astype(dtype)
26 mina = (conv - delta * force).astype(dtype)
27 return mina, maxa
30def enumerate_random_inputs(inputs, n=100, dtype=numpy.float32, force=1):
31 """
32 Enumerates random matrices.
34 @param inputs inputs (dictionary)
35 @param n number of iterations
36 @param dtype type to convert to
37 @param force does something like *[i] +/- force |i - [i]|*
38 """
39 keys = list(inputs)
40 ranges = {k: astype_range(v, dtype=dtype, force=force)
41 for k, v in inputs.items()}
42 for _ in range(n):
43 new_inputs = {}
44 for k in keys:
45 rnd = randint(0, 2, inputs[k].size).reshape( # pylint: disable=E1101
46 inputs[k].shape) # pylint: disable=E1101
47 if rnd.min() == rnd.max() or rnd.max() != 1:
48 raise RuntimeError( # pragma: no cover
49 "Minimum and maximum are equal or maximum is not 1. "
50 "Randomness failed.")
51 rnd = rnd.astype(dtype)
52 ma1 = ranges[k][0] * rnd
53 ma2 = ranges[k][1] * (-(rnd - 1))
54 inp = (ma1 + ma2)
55 new_inputs[k] = inp
56 yield new_inputs
59def onnx_shaker(oinf, inputs, output_fct, n=100, dtype=numpy.float32, force=1):
60 """
61 Shakes a model :epkg:`ONNX`.
62 Explores the ranges for every prediction.
63 Uses @see fn astype_range
65 @param oinf object of type @see cl OnnxInference
66 @param inputs inputs
67 @param output_fct output function which extracts
68 a single array from the output
69 @param dtype type to convert to
70 @param force does something like *[i] +/- force |i - [i]|*
71 @return ranges for each predictions
73 See notebook :ref:`onnxshakerrst` for an example of use.
74 """
75 results = None
76 for i, new_inputs in enumerate(enumerate_random_inputs(
77 inputs, n=n, dtype=dtype, force=force)):
78 res_ = oinf.run(new_inputs)
79 res = output_fct(res_)
80 sq = numpy.squeeze(res)
81 if len(sq.shape) != 1:
82 raise ValueError( # pragma: no cover
83 "The function only works with shape={}".format(sq.shape))
84 if results is None:
85 results = numpy.empty((sq.shape[0], n), dtype=sq.dtype)
86 results[:, i] = sq
88 results.sort(axis=1)
89 return results