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

35 statements  

1""" 

2@file 

3@brief Investigate issues happening with float32. 

4""" 

5import numpy 

6from numpy.random import randint 

7 

8 

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]`. 

16 

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 

28 

29 

30def enumerate_random_inputs(inputs, n=100, dtype=numpy.float32, force=1): 

31 """ 

32 Enumerates random matrices. 

33 

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 

57 

58 

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 

64 

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 

72 

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 

87 

88 results.sort(axis=1) 

89 return results