A helper function that checks that numerical gradients of a function are equal to gradients computed in some different way (analytical gradients). Numerical gradients are computed using finite difference approximation. To reduce the number of function evaluations, the number of points u
(
function, input_values, grad_values, function_value=None, delta=1e-3, atol=1e-2, rtol=0.1
)
| 113 | |
| 114 | |
| 115 | def check_numerical_grads( |
| 116 | function, input_values, grad_values, function_value=None, delta=1e-3, atol=1e-2, rtol=0.1 |
| 117 | ): |
| 118 | """A helper function that checks that numerical gradients of a function are |
| 119 | equal to gradients computed in some different way (analytical gradients). |
| 120 | |
| 121 | Numerical gradients are computed using finite difference approximation. To |
| 122 | reduce the number of function evaluations, the number of points used is |
| 123 | gradually increased if the error value is too high (up to 5 points). |
| 124 | |
| 125 | Parameters |
| 126 | ---------- |
| 127 | function |
| 128 | A function that takes inputs either as positional or as keyword |
| 129 | arguments (either `function(*input_values)` or `function(**input_values)` |
| 130 | should be correct) and returns a scalar result. Should accept numpy |
| 131 | ndarrays. |
| 132 | |
| 133 | input_values : Dict[str, numpy.ndarray] or List[numpy.ndarray] |
| 134 | A list of values or a dict assigning values to variables. Represents the |
| 135 | point at which gradients should be computed. |
| 136 | |
| 137 | grad_values : Dict[str, numpy.ndarray] or List[numpy.ndarray] |
| 138 | Gradients computed using a different method. |
| 139 | |
| 140 | function_value : float, optional |
| 141 | Should be equal to `function(**input_values)`. |
| 142 | |
| 143 | delta : float, optional |
| 144 | A small number used for numerical computation of partial derivatives. |
| 145 | The default 1e-3 is a good choice for float32. |
| 146 | |
| 147 | atol : float, optional |
| 148 | Absolute tolerance. Gets multiplied by `sqrt(n)` where n is the size of a |
| 149 | gradient. |
| 150 | |
| 151 | rtol : float, optional |
| 152 | Relative tolerance. |
| 153 | """ |
| 154 | # If input_values is a list then function accepts positional arguments |
| 155 | # In this case transform it to a function taking kwargs of the form {"0": ..., "1": ...} |
| 156 | if not isinstance(input_values, dict): |
| 157 | input_len = len(input_values) |
| 158 | input_values = {str(idx): val for idx, val in enumerate(input_values)} |
| 159 | |
| 160 | def _function(_input_len=input_len, _orig_function=function, **kwargs): |
| 161 | return _orig_function(*(kwargs[str(i)] for i in range(input_len))) |
| 162 | |
| 163 | function = _function |
| 164 | |
| 165 | grad_values = {str(idx): val for idx, val in enumerate(grad_values)} |
| 166 | |
| 167 | if function_value is None: |
| 168 | function_value = function(**input_values) |
| 169 | |
| 170 | # a helper to modify j-th element of val by a_delta |
| 171 | def modify(val, j, a_delta): |
| 172 | val = val.copy() |
searching dependent graphs…