Exemplo n.º 1
0
    def __init__(self,
                 model,
                 data,
                 session=None,
                 batch_size=50,
                 local_smoothing=0):

        # try and import keras and tensorflow
        global tf, keras
        if tf is None:
            import tensorflow as tf
            if LooseVersion(tf.__version__) < LooseVersion("1.4.0"):
                warnings.warn(
                    "Your TensorFlow version is older than 1.4.0 and not supported."
                )
        if keras is None:
            try:
                import keras
                if LooseVersion(keras.__version__) < LooseVersion("2.1.0"):
                    warnings.warn(
                        "Your Keras version is older than 2.1.0 and not supported."
                    )
            except:
                pass

        # determine the model inputs and outputs
        self.model = model
        self.model_inputs = _get_model_inputs(model)
        self.model_output = _get_model_output(model)
        assert type(
            self.model_output
        ) != list, "The model output to be explained must be a single tensor!"
        assert len(
            self.model_output.shape
        ) < 3, "The model output must be a vector or a single value!"
        self.multi_output = True
        if len(self.model_output.shape) == 1:
            self.multi_output = False

        # check if we have multiple inputs
        self.multi_input = True
        if type(self.model_inputs) != list:
            self.model_inputs = [self.model_inputs]
        self.multi_input = len(self.model_inputs) > 1
        if type(data) != list:
            data = [data]

        self.data = data
        self._num_vinputs = {}
        self.batch_size = batch_size
        self.local_smoothing = local_smoothing

        if not tf.executing_eagerly():
            self.session = _get_session(session)
            self.graph = _get_graph(self)
            # see if there is a keras operation we need to save
            self.keras_phase_placeholder = None
            for op in self.graph.get_operations():
                if 'keras_learning_phase' in op.name:
                    self.keras_phase_placeholder = op.outputs[0]

        # save the expected output of the model
        #self.expected_value = self.run(self.model_output, self.model_inputs, self.data).mean(0)

        if not self.multi_output:
            self.gradients = [None]
        else:
            self.gradients = [None for i in range(self.model_output.shape[1])]
Exemplo n.º 2
0
    def __init__(self, model, data, session=None, learning_phase_flags=None):
        """ An explainer object for a deep model using a given background dataset.

        Note that the complexity of the method scales linearly with the number of background data
        samples. Passing the entire training dataset as `data` will give very accurate expected
        values, but be unreasonably expensive. The variance of the expectation estimates scale by
        roughly 1/sqrt(N) for N background data samples. So 100 samples will give a good estimate,
        and 1000 samples a very good estimate of the expected values.

        Parameters
        ----------
        model : tf.keras.Model or (input : [tf.Operation], output : tf.Operation)
            A keras model object or a pair of TensorFlow operations (or a list and an op) that
            specifies the input and output of the model to be explained. Note that SHAP values
            are specific to a single output value, so you get an explanation for each element of
            the output tensor (which must be a flat rank one vector).

        data : [numpy.array] or [pandas.DataFrame] or function
            The background dataset to use for integrating out features. DeepExplainer integrates
            over all these samples for each explanation. The data passed here must match the input
            operations given to the model. If a function is supplied, it must be a function that
            takes a particular input example and generates the background dataset for that example
        session : None or tensorflow.Session
            The TensorFlow session that has the model we are explaining. If None is passed then
            we do our best to find the right session, first looking for a keras session, then
            falling back to the default TensorFlow session.

        learning_phase_flags : None or list of tensors
            If you have your own custom learning phase flags pass them here. When explaining a prediction
            we need to ensure we are not in training mode, since this changes the behavior of ops like
            batch norm or dropout. If None is passed then we look for tensors in the graph that look like
            learning phase flags (this works for Keras models). Note that we assume all the flags should
            have a value of False during predictions (and hence explanations).
            
        """
        # try and import keras and tensorflow
        global tf, tf_ops, tf_backprop, tf_execute, tf_gradients_impl
        if tf is None:
            from tensorflow.python.framework import ops as tf_ops  # pylint: disable=E0611
            from tensorflow.python.ops import gradients_impl as tf_gradients_impl  # pylint: disable=E0611
            from tensorflow.python.eager import backprop as tf_backprop
            from tensorflow.python.eager import execute as tf_execute
            if not hasattr(tf_gradients_impl, "_IsBackpropagatable"):
                from tensorflow.python.ops import gradients_util as tf_gradients_impl
            import tensorflow as tf
            if LooseVersion(tf.__version__) < LooseVersion("1.4.0"):
                warnings.warn(
                    "Your TensorFlow version is older than 1.4.0 and not supported."
                )
        global keras
        if keras is None:
            try:
                import keras
                warnings.warn(
                    "keras is no longer supported, please use tf.keras instead."
                )
            except:
                pass

        # determine the model inputs and outputs
        self.model_inputs = _get_model_inputs(model)
        self.model_output = _get_model_output(model)
        assert type(
            self.model_output
        ) != list, "The model output to be explained must be a single tensor!"
        assert len(
            self.model_output.shape
        ) < 3, "The model output must be a vector or a single value!"
        self.multi_output = True
        if len(self.model_output.shape) == 1:
            self.multi_output = False

        if tf.executing_eagerly():
            if type(model) is tuple or type(model) is list:
                assert len(
                    model
                ) == 2, "When a tuple is passed it must be of the form (inputs, outputs)"
                self.model = Model(model[0], model[1])
            else:
                self.model = model

        # check if we have multiple inputs
        self.multi_input = True
        if type(self.model_inputs) != list or len(self.model_inputs) == 1:
            self.multi_input = False
            if type(self.model_inputs) != list:
                self.model_inputs = [self.model_inputs]
        if type(data) != list and (hasattr(data, '__call__') == False):
            data = [data]
        self.data = data

        self._vinputs = {
        }  # used to track what op inputs depends on the model inputs
        self.orig_grads = {}

        if not tf.executing_eagerly():
            self.session = _get_session(session)

        self.graph = _get_graph(self)

        # if no learning phase flags were given we go looking for them
        # ...this will catch the one that keras uses
        # we need to find them since we want to make sure learning phase flags are set to False
        if learning_phase_flags is None:
            self.learning_phase_ops = []
            for op in self.graph.get_operations():
                if 'learning_phase' in op.name and op.type == "Const" and len(
                        op.outputs[0].shape) == 0:
                    if op.outputs[0].dtype == tf.bool:
                        self.learning_phase_ops.append(op)
            self.learning_phase_flags = [
                op.outputs[0] for op in self.learning_phase_ops
            ]
        else:
            self.learning_phase_ops = [t.op for t in learning_phase_flags]

        # save the expected output of the model
        # if self.data is a function, set self.expected_value to None
        if (hasattr(self.data, '__call__')):
            self.expected_value = None
        else:
            if self.data[0].shape[0] > 5000:
                warnings.warn(
                    "You have provided over 5k background samples! For better performance consider using smaller random sample."
                )
            if not tf.executing_eagerly():
                self.expected_value = self.run(self.model_output,
                                               self.model_inputs,
                                               self.data).mean(0)
            else:
                if type(self.model) is tuple:
                    self.fModel(cnn.inputs,
                                cnn.get_layer(theNameYouWant).outputs)
                self.expected_value = tf.reduce_mean(self.model(self.data[0]),
                                                     0)
                # PJT
                # data_tensor = tf.convert_to_tensor(self.data[0])
                # self.expected_value = tf.reduce_mean(self.model(data_tensor), 0)
                # self.expected_value = tf.reduce_mean(self.model.predict(self.data[0]), 0).numpy()

        if not tf.executing_eagerly():
            self._init_between_tensors(self.model_output.op, self.model_inputs)

        # make a blank array that will get lazily filled in with the SHAP value computation
        # graphs for each output. Lazy is important since if there are 1000 outputs and we
        # only explain the top 5 it would be a waste to build graphs for the other 995
        if not self.multi_output:
            self.phi_symbolics = [None]
        else:
            noutputs = self.model_output.shape.as_list()[1]
            if noutputs is not None:
                self.phi_symbolics = [None for i in range(noutputs)]
            else:
                raise Exception(
                    "The model output tensor to be explained cannot have a static shape in dim 1 of None!"
                )