示例#1
0
    def apply(self, Xs, Ys, Rs, reverse_state):
        grad = ilayers.GradientWRT(len(Xs))
        to_low = keras.layers.Lambda(lambda x: x * 0 + self._low)
        to_high = keras.layers.Lambda(lambda x: x * 0 + self._high)

        low = [to_low(x) for x in Xs]
        high = [to_high(x) for x in Xs]

        # Get values for the division.
        A = kutils.apply(self._layer_wo_act, Xs)
        B = kutils.apply(self._layer_wo_act_positive, low)
        C = kutils.apply(self._layer_wo_act_negative, high)
        Zs = [
            keras.layers.Subtract()([a, keras.layers.Add()([b, c])])
            for a, b, c in zip(A, B, C)
        ]

        # Divide relevances with the value.
        tmp = [ilayers.SafeDivide()([a, b]) for a, b in zip(Rs, Zs)]
        # Distribute along the gradient.
        tmpA = iutils.to_list(grad(Xs + A + tmp))
        tmpB = iutils.to_list(grad(low + B + tmp))
        tmpC = iutils.to_list(grad(high + C + tmp))

        tmpA = [keras.layers.Multiply()([a, b]) for a, b in zip(Xs, tmpA)]
        tmpB = [keras.layers.Multiply()([a, b]) for a, b in zip(low, tmpB)]
        tmpC = [keras.layers.Multiply()([a, b]) for a, b in zip(high, tmpC)]

        tmp = [
            keras.layers.Subtract()([a, keras.layers.Add()([b, c])])
            for a, b, c in zip(tmpA, tmpB, tmpC)
        ]

        return tmp
示例#2
0
    def apply(self, Xs, Ys, Rs, reverse_state):
        #this method is correct, but wasteful
        grad = ilayers.GradientWRT(len(Xs))
        times_alpha0 = tensorflow.keras.layers.Lambda(
            lambda x: x * self._alpha[0])
        times_alpha1 = tensorflow.keras.layers.Lambda(
            lambda x: x * self._alpha[1])
        times_beta0 = tensorflow.keras.layers.Lambda(
            lambda x: x * self._beta[0])
        times_beta1 = tensorflow.keras.layers.Lambda(
            lambda x: x * self._beta[1])
        keep_positives = tensorflow.keras.layers.Lambda(
            lambda x: x * K.cast(K.greater(x, 0), K.floatx()))
        keep_negatives = tensorflow.keras.layers.Lambda(
            lambda x: x * K.cast(K.less(x, 0), K.floatx()))

        def f(layer, X):
            Zs = kutils.apply(layer, X)
            # Divide incoming relevance by the activations.
            tmp = [ilayers.SafeDivide()([a, b]) for a, b in zip(Rs, Zs)]
            # Propagate the relevance to the input neurons
            # using the gradient
            tmp = iutils.to_list(grad(X + Zs + tmp))
            # Re-weight relevance with the input values.
            tmp = [
                tensorflow.keras.layers.Multiply()([a, b])
                for a, b in zip(X, tmp)
            ]
            return tmp

        # Distinguish postive and negative inputs.
        Xs_pos = kutils.apply(keep_positives, Xs)
        Xs_neg = kutils.apply(keep_negatives, Xs)

        # xpos*wpos
        r_pp = f(self._layer_wo_act_positive, Xs_pos)
        # xneg*wneg
        r_nn = f(self._layer_wo_act_negative, Xs_neg)
        # a0 * r_pp + a1 * r_nn
        r_pos = [
            tensorflow.keras.layers.Add()([times_alpha0(pp),
                                           times_beta1(nn)])
            for pp, nn in zip(r_pp, r_nn)
        ]

        # xpos*wneg
        r_pn = f(self._layer_wo_act_negative, Xs_pos)
        # xneg*wpos
        r_np = f(self._layer_wo_act_positive, Xs_neg)
        # b0 * r_pn + b1 * r_np
        r_neg = [
            tensorflow.keras.layers.Add()([times_beta0(pn),
                                           times_beta1(np)])
            for pn, np in zip(r_pn, r_np)
        ]

        return [
            tensorflow.keras.layers.Subtract()([a, b])
            for a, b in zip(r_pos, r_neg)
        ]
示例#3
0
    def apply(self, Xs, Ys, reversed_Ys, reverse_state: Dict):
        # Apply relus conditioned on backpropagated values.
        reversed_Ys = kutils.apply(self._activation, reversed_Ys)

        # Apply gradient of forward pass without relus.
        Ys_wo_relu = kutils.apply(self._layer_wo_relu, Xs)
        return ilayers.GradientWRT(len(Xs))(Xs + Ys_wo_relu + reversed_Ys)
示例#4
0
 def f(layer1, layer2, X1, X2):
     # Get activations of full positive or negative part.
     Z1 = kutils.apply(layer1, X1)
     Z2 = kutils.apply(layer2, X2)
     Zs = [
         tensorflow.keras.layers.Add()([a, b]) for a, b in zip(Z1, Z2)
     ]
     # Divide incoming relevance by the activations.
     tmp = [ilayers.SafeDivide()([a, b]) for a, b in zip(Rs, Zs)]
     # Propagate the relevance to the input neurons
     # using the gradient
     tmp1 = iutils.to_list(grad(X1 + Z1 + tmp))
     tmp2 = iutils.to_list(grad(X2 + Z2 + tmp))
     # Re-weight relevance with the input values.
     tmp1 = [
         tensorflow.keras.layers.Multiply()([a, b])
         for a, b in zip(X1, tmp1)
     ]
     tmp2 = [
         tensorflow.keras.layers.Multiply()([a, b])
         for a, b in zip(X2, tmp2)
     ]
     #combine and return
     return [
         tensorflow.keras.layers.Add()([a, b])
         for a, b in zip(tmp1, tmp2)
     ]
示例#5
0
    def apply(self, Xs, Ys, Rs, reverse_state):
        #this method is correct, but wasteful
        grad = ilayers.GradientWRT(len(Xs))
        times_alpha = tensorflow.keras.layers.Lambda(lambda x: x * self._alpha)
        times_beta = tensorflow.keras.layers.Lambda(lambda x: x * self._beta)
        keep_positives = tensorflow.keras.layers.Lambda(
            lambda x: x * K.cast(K.greater(x, 0), K.floatx()))
        keep_negatives = tensorflow.keras.layers.Lambda(
            lambda x: x * K.cast(K.less(x, 0), K.floatx()))

        def f(layer1, layer2, X1, X2):
            # Get activations of full positive or negative part.
            Z1 = kutils.apply(layer1, X1)
            Z2 = kutils.apply(layer2, X2)
            Zs = [
                tensorflow.keras.layers.Add()([a, b]) for a, b in zip(Z1, Z2)
            ]
            # Divide incoming relevance by the activations.
            tmp = [ilayers.SafeDivide()([a, b]) for a, b in zip(Rs, Zs)]
            # Propagate the relevance to the input neurons
            # using the gradient
            tmp1 = iutils.to_list(grad(X1 + Z1 + tmp))
            tmp2 = iutils.to_list(grad(X2 + Z2 + tmp))
            # Re-weight relevance with the input values.
            tmp1 = [
                tensorflow.keras.layers.Multiply()([a, b])
                for a, b in zip(X1, tmp1)
            ]
            tmp2 = [
                tensorflow.keras.layers.Multiply()([a, b])
                for a, b in zip(X2, tmp2)
            ]
            #combine and return
            return [
                tensorflow.keras.layers.Add()([a, b])
                for a, b in zip(tmp1, tmp2)
            ]

        # Distinguish postive and negative inputs.
        Xs_pos = kutils.apply(keep_positives, Xs)
        Xs_neg = kutils.apply(keep_negatives, Xs)
        # xpos*wpos + xneg*wneg
        activator_relevances = f(self._layer_wo_act_positive,
                                 self._layer_wo_act_negative, Xs_pos, Xs_neg)

        if self._beta:  #only compute beta-weighted contributions of beta is not zero
            # xpos*wneg + xneg*wpos
            inhibitor_relevances = f(self._layer_wo_act_negative,
                                     self._layer_wo_act_positive, Xs_pos,
                                     Xs_neg)
            return [
                tensorflow.keras.layers.Subtract()(
                    [times_alpha(a), times_beta(b)])
                for a, b in zip(activator_relevances, inhibitor_relevances)
            ]
        else:
            return activator_relevances
示例#6
0
        def f(Xs):
            low = [to_low(x) for x in Xs]
            high = [to_high(x) for x in Xs]

            A = kutils.apply(self._layer_wo_act, Xs)
            B = kutils.apply(self._layer_wo_act_positive, low)
            C = kutils.apply(self._layer_wo_act_negative, high)
            return [
                keras.layers.Subtract()([a, keras.layers.Add()([b, c])])
                for a, b, c in zip(A, B, C)
            ]
    def apply(self, Xs, Ys, Rs, _reverse_state: Dict):
        input_shape = [K.int_shape(x) for x in Xs]
        if len(input_shape) != 1:
            # extend below lambda layers towards multiple parameters.
            raise ValueError(
                "BatchNormalizationReverseLayer expects Xs with len(Xs) = 1, but was len(Xs) = {}".format(  # noqa
                    len(Xs)
                )
            )
        input_shape = input_shape[0]

        # prepare broadcasting shape for layer parameters
        broadcast_shape = [1] * len(input_shape)
        broadcast_shape[self._axis] = input_shape[self._axis]
        broadcast_shape[0] = -1

        # reweight relevances as
        #        x * (y - beta)     R
        # Rin = ---------------- * ----
        #           x - mu          y
        # batch norm can be considered as 3 distinct layers of subtraction,
        # multiplication and then addition. The multiplicative scaling layer
        # has no effect on LRP and functions as a linear activation layer

        minus_mu = keras.layers.Lambda(
            lambda x: x - K.reshape(self._mean, broadcast_shape)
        )
        minus_beta = keras.layers.Lambda(
            lambda x: x - K.reshape(self._beta, broadcast_shape)
        )
        prepare_div = keras.layers.Lambda(
            lambda x: x
            + (K.cast(K.greater_equal(x, 0), K.floatx()) * 2 - 1) * K.epsilon()
        )

        x_minus_mu = kutils.apply(minus_mu, Xs)
        if self._center:
            y_minus_beta = kutils.apply(minus_beta, Ys)
        else:
            y_minus_beta = Ys

        numerator = [
            keras.layers.Multiply()([x, ymb, r])
            for x, ymb, r in zip(Xs, y_minus_beta, Rs)
        ]
        denominator = [
            keras.layers.Multiply()([xmm, y]) for xmm, y in zip(x_minus_mu, Ys)
        ]

        return [
            ilayers.SafeDivide()([n, prepare_div(d)])
            for n, d in zip(numerator, denominator)
        ]
示例#8
0
def GuidedBackpropReverseReLULayer(Xs, Ys, reversed_Ys, reverse_state: Dict):
    activation = keras.layers.Activation("relu")
    # Apply relus conditioned on backpropagated values.
    reversed_Ys = kutils.apply(activation, reversed_Ys)

    # Apply gradient of forward pass.
    return ilayers.GradientWRT(len(Xs))(Xs + Ys + reversed_Ys)
示例#9
0
 def f(layer, X):
     Zs = kutils.apply(layer, X)
     # Divide incoming relevance by the activations.
     tmp = [ilayers.SafeDivide()([a, b]) for a, b in zip(Rs, Zs)]
     # Propagate the relevance to the input neurons
     # using the gradient
     tmp = iutils.to_list(grad(X + Zs + tmp))
     # Re-weight relevance with the input values.
     tmp = [keras.layers.Multiply()([a, b]) for a, b in zip(X, tmp)]
     return tmp
示例#10
0
    def apply(self, Xs, Ys, Rs, reverse_state):
        grad = ilayers.GradientWRT(len(Xs))

        # Get activations.
        Zs = kutils.apply(self._layer_wo_act, Xs)
        # Divide incoming relevance by the activations.
        tmp = [ilayers.SafeDivide()([a, b]) for a, b in zip(Rs, Zs)]
        # Propagate the relevance to input neurons
        # using the gradient.
        tmp = iutils.to_list(grad(Xs + Zs + tmp))
        # Re-weight relevance with the input values.
        return [keras.layers.Multiply()([a, b]) for a, b in zip(Xs, tmp)]
示例#11
0
    def apply(self, Xs, _Ys, reversed_Ys, _reverse_state: Dict):
        # Reapply the prepared layers.
        act_Xs = kutils.apply(self._filter_layer, Xs)
        act_Ys = kutils.apply(self._act_layer, act_Xs)
        pattern_Ys = kutils.apply(self._pattern_layer, Xs)

        # Layers that apply the backward pass.
        grad_act = ilayers.GradientWRT(len(act_Xs))
        grad_pattern = ilayers.GradientWRT(len(Xs))

        # First step: propagate through the activation layer.
        # Workaround for linear activations.
        linear_activations = [None, keras.activations.get("linear")]
        if self._act_layer.activation in linear_activations:
            tmp = reversed_Ys
        else:
            # if linear activation this behaves strange
            tmp = iutils.to_list(grad_act(act_Xs + act_Ys + reversed_Ys))

        # Second step: propagate through the pattern layer.
        return grad_pattern(Xs + pattern_Ys + tmp)
示例#12
0
    def apply(self, Xs, Ys, Rs, reverse_state):
        grad = ilayers.GradientWRT(len(Xs))
        # Create dummy forward path to take the derivative below.
        Ys = kutils.apply(self._layer_wo_act_b, Xs)

        # Compute the sum of the weights.
        ones = ilayers.OnesLike()(Xs)
        Zs = iutils.to_list(self._layer_wo_act_b(ones))
        # Weight the incoming relevance.
        tmp = [ilayers.SafeDivide()([a, b]) for a, b in zip(Rs, Zs)]
        # Redistribute the relevances along the gradient.
        tmp = iutils.to_list(grad(Xs + Ys + tmp))
        return tmp
示例#13
0
    def apply(self, Xs, Ys, Rs, reverse_state):
        # the outputs of the pooling operation at each location is the sum of its inputs.
        # the forward message must be known in this case, and are the inputs for each pooling thing.
        # the gradient is 1 for each output-to-input connection, which corresponds to the "weights"
        # of the layer. It should thus be sufficient to reweight the relevances and and do a gradient_wrt
        grad = ilayers.GradientWRT(len(Xs))
        # Get activations.
        Zs = kutils.apply(self._layer_wo_act, Xs)
        # Divide incoming relevance by the activations.
        tmp = [ilayers.SafeDivide()([a, b]) for a, b in zip(Rs, Zs)]

        # Propagate the relevance to input neurons
        # using the gradient.
        tmp = iutils.to_list(grad(Xs + Zs + tmp))
        # Re-weight relevance with the input values.
        return [keras.layers.Multiply()([a, b]) for a, b in zip(Xs, tmp)]
示例#14
0
    def apply(self, Xs, Ys, Rs, reverse_state):
        grad = ilayers.GradientWRT(len(Xs))

        #TODO: assert all inputs are positive, instead of only keeping the positives.
        #keep_positives = keras.layers.Lambda(lambda x: x * K.cast(K.greater(x,0), K.floatx()))
        #Xs = kutils.apply(keep_positives, Xs)

        # Get activations.
        Zs = kutils.apply(self._layer_wo_act_b_positive, Xs)
        # Divide incoming relevance by the activations.
        tmp = [ilayers.SafeDivide()([a, b]) for a, b in zip(Rs, Zs)]
        # Propagate the relevance to input neurons
        # using the gradient.
        tmp = iutils.to_list(grad(Xs + Zs + tmp))
        # Re-weight relevance with the input values.
        return [keras.layers.Multiply()([a, b]) for a, b in zip(Xs, tmp)]
示例#15
0
    def apply(self, Xs, Ys, Rs, reverse_state):
        grad = ilayers.GradientWRT(len(Xs))
        # The epsilon rule aligns epsilon with the (extended) sign: 0 is considered to be positive
        prepare_div = keras.layers.Lambda(lambda x: x + (K.cast(
            K.greater_equal(x, 0), K.floatx()) * 2 - 1) * self._epsilon)

        # Get activations.
        Zs = kutils.apply(self._layer_wo_act, Xs)

        # Divide incoming relevance by the activations.
        tmp = [ilayers.Divide()([a, prepare_div(b)]) for a, b in zip(Rs, Zs)]
        # Propagate the relevance to input neurons
        # using the gradient.
        tmp = iutils.to_list(grad(Xs + Zs + tmp))
        # Re-weight relevance with the input values.
        return [keras.layers.Multiply()([a, b]) for a, b in zip(Xs, tmp)]