SeldonIO / alibi

Algorithms for explaining machine learning models
https://docs.seldon.io/projects/alibi/en/stable/
Other
2.4k stars 252 forks source link

Avoid calling `predictor` during `__init__` #514

Open jklaise opened 3 years ago

jklaise commented 3 years ago

As @sakoush pointed out, for some upstream use cases (specifically seldon-core and mlserver) it is not desirable to call the passed predictor with random data at __init__ time like we do in a few places for the purpose of inferring properties about the predictor, e.g. https://github.com/SeldonIO/alibi/blob/d8911526e7252374ccfc4eec4aba0b807c25725e/alibi/explainers/anchor_tabular.py#L1012-L1019

This was also brought up in #499 solved by #506 to at least ensure the user to specify the expected data type so internally we pass through arrays of the right type. But, as it turns out, it may be desirable to completely avoid calling predictor at __init__. Specifically, for upstream use cases it may happen that predictor is some remote endpoint which is configured at the same time as the corresponding explainer, this effectively results in a race condition where the explainer tries to call the endpoint which isn't configured yet. This is what currently happens with seldon-core, quoting @sakoush

in our current implementation of alibi explain runtime in mlserver, we rely on alibi public methods such as load_explainer , which is called during model load time from mlserver perspective. load_explainer in many of the cases will do a dummy call to the underlying inference model (to decide whether to apply argmax on the results in the case the result is a set of classes and associated probabilities). This is going to cause a "circular" dependency in seldon-core , SC will not expose the prediction graph service unless the explainer deployment is healthy and if we do need to do a dummy call to the inference model at deployment / load time, the service endpoint is not ready yet.

This is primarily a limitation of seldon-core, however we can address it from alibi.

A remedy on the alibi side would be to ask users more about their model upfront rather than relying on inferring properties about it by passing data through. Concretely, for the Anchor* algorithms we could expose an additional kwarg:

output: Optional[Literal['class', 'proba']] = None

which indicates whether the classifier returns class predictions or probabilities. We can use this information to decide whether an ArgmaxTransformer needs to be applied to predictor without passing random data through it.

The proposal is to keep this optional so we can do inference if it is None as we do now, but it will enable avoiding calls to predictor if this parameter is known and passed to alibi.

There are a few more places in the code base where we use such inference, for example, inferring the number of classes in Counterfactual. This could also be solved in the same way by exposing

n_classes: Optional[int] = None
jklaise commented 3 years ago

@sakoush there is still an issue here that we can't necessarily validate if the combination of user-specified output and predictor are correct without calling the predictor. I.e. if output=label but in reality predictor outputs probabilities, this cannot be validated upfront without calling predictor. If there is a discrepancy, then the explainer will fail later on at explain time with some obscure message from the algorithm internals.