예제 #1
0
    def test_calc_avgpool(self):
        image_data = self._image_data()
        net = self._network('avgpool')
        input_bounds = naive_bounds.input_bounds(image_data.image, delta=.1)
        dual_obj, dual_var_lists = self._build_objective(
            net, input_bounds, image_data.label)

        # Explicitly build the expected TensorFlow graph for calculating objective.
        (
            conv2d_0,
            relu_1,  # pylint:disable=unused-variable
            avgpool_2,
            relu_3,  # pylint:disable=unused-variable
            linear_obj) = self._verifiable_layer_builder(net).build_layers()
        (mu_0, ), (lam_1, ), (mu_2, ), _ = dual_var_lists

        # Expected input bounds for each layer.
        conv2d_0_lb, conv2d_0_ub = self._expected_input_bounds(
            image_data.image, .1)
        relu_1_lb, relu_1_ub = ibp.IntervalBounds(
            conv2d_0_lb, conv2d_0_ub).apply_conv2d(None, conv2d_0.module.w,
                                                   conv2d_0.module.b, 'SAME',
                                                   (1, 1))
        avgpool_2_lb = tf.nn.relu(relu_1_lb)
        avgpool_2_ub = tf.nn.relu(relu_1_ub)
        relu_3_lb = tf.nn.avg_pool(avgpool_2_lb,
                                   ksize=[2, 2],
                                   padding='VALID',
                                   strides=(1, 1))
        relu_3_ub = tf.nn.avg_pool(avgpool_2_ub,
                                   ksize=[2, 2],
                                   padding='VALID',
                                   strides=(1, 1))

        # Expected objective value.
        objective = 0
        act_coeffs_0 = -common.conv_transpose(
            mu_0, conv2d_0.module.w, conv2d_0.input_shape, 'SAME', (1, 1))
        obj_0 = -tf.reduce_sum(mu_0 * conv2d_0.module.b, axis=(2, 3, 4))
        objective += standard_layer_calcs.linear_dual_objective(
            None, act_coeffs_0, obj_0, conv2d_0_lb, conv2d_0_ub)
        objective += standard_layer_calcs.activation_layer_dual_objective(
            tf.nn.relu, mu_0, lam_1, relu_1_lb, relu_1_ub)
        act_coeffs_2 = -common.avgpool_transpose(
            mu_2,
            result_shape=relu_1.output_shape,
            kernel_shape=(2, 2),
            strides=(1, 1))
        objective += standard_layer_calcs.linear_dual_objective(
            lam_1, act_coeffs_2, 0., avgpool_2_lb, avgpool_2_ub)
        objective_w, objective_b = common.targeted_objective(
            linear_obj.module.w, linear_obj.module.b, image_data.label)
        shaped_objective_w = tf.reshape(
            objective_w,
            [self._num_classes(), self._batch_size()] + avgpool_2.output_shape)
        objective += standard_layer_calcs.activation_layer_dual_objective(
            tf.nn.relu, mu_2, -shaped_objective_w, relu_3_lb, relu_3_ub)
        objective += objective_b

        self._assert_dual_objective_close(objective, dual_obj, image_data)
예제 #2
0
    def test_calc_conv_batchnorm(self):
        image_data = self._image_data()
        net = self._network('conv_batchnorm')
        input_bounds = naive_bounds.input_bounds(image_data.image, delta=.1)
        dual_obj, dual_var_lists = self._build_objective(
            net, input_bounds, image_data.label)

        # Explicitly build the expected TensorFlow graph for calculating objective.
        (
            conv2d_0,
            relu_1,  # pylint:disable=unused-variable
            linear_2,
            relu_3,  # pylint:disable=unused-variable
            linear_obj) = self._verifiable_layer_builder(net).build_layers()
        (mu_0, ), (lam_1, ), (mu_2, ), _ = dual_var_lists

        # Expected input bounds for each layer.
        conv2d_0_lb, conv2d_0_ub = self._expected_input_bounds(
            image_data.image, .1)
        conv2d_0_w, conv2d_0_b = layer_utils.combine_with_batchnorm(
            conv2d_0.module.w, None, conv2d_0.batch_norm)
        relu_1_lb, relu_1_ub = ibp.IntervalBounds(
            conv2d_0_lb, conv2d_0_ub).apply_conv2d(None, conv2d_0_w,
                                                   conv2d_0_b, 'VALID', (1, 1))
        linear_2_lb = snt.BatchFlatten()(tf.nn.relu(relu_1_lb))
        linear_2_ub = snt.BatchFlatten()(tf.nn.relu(relu_1_ub))
        linear_2_w, linear_2_b = layer_utils.combine_with_batchnorm(
            linear_2.module.w, None, linear_2.batch_norm)
        relu_3_lb, relu_3_ub = ibp.IntervalBounds(linear_2_lb,
                                                  linear_2_ub).apply_linear(
                                                      None, linear_2_w,
                                                      linear_2_b)

        # Expected objective value.
        objective = 0
        act_coeffs_0 = -common.conv_transpose(
            mu_0, conv2d_0_w, conv2d_0.input_shape, 'VALID', (1, 1))
        obj_0 = -tf.reduce_sum(mu_0 * conv2d_0_b, axis=(2, 3, 4))
        objective += standard_layer_calcs.linear_dual_objective(
            None, act_coeffs_0, obj_0, conv2d_0_lb, conv2d_0_ub)
        objective += standard_layer_calcs.activation_layer_dual_objective(
            tf.nn.relu, mu_0, lam_1, relu_1_lb, relu_1_ub)
        act_coeffs_2 = -tf.tensordot(mu_2, tf.transpose(linear_2_w), axes=1)
        obj_2 = -tf.tensordot(mu_2, linear_2_b, axes=1)
        objective += standard_layer_calcs.linear_dual_objective(
            snt.BatchFlatten(preserve_dims=2)(lam_1), act_coeffs_2, obj_2,
            linear_2_lb, linear_2_ub)
        objective_w, objective_b = common.targeted_objective(
            linear_obj.module.w, linear_obj.module.b, image_data.label)
        objective += standard_layer_calcs.activation_layer_dual_objective(
            tf.nn.relu, mu_2, -objective_w, relu_3_lb, relu_3_ub)
        objective += objective_b

        self._assert_dual_objective_close(objective, dual_obj, image_data)
예제 #3
0
def _materialised_conv_layer_dual_objective(w, b, padding, strides, lam_in,
                                            mu_out, lb, ub):
    """Materialised version of `conv_layer_dual_objective`."""
    # Flatten the inputs, as the materialised convolution will have no
    # spatial structure.
    mu_out_flat = snt.BatchFlatten(preserve_dims=2)(mu_out)

    # Materialise the convolution as a (sparse) fully connected linear layer.
    w_flat, b_flat = layer_utils.materialise_conv(w,
                                                  b,
                                                  lb.shape[1:].as_list(),
                                                  padding=padding,
                                                  strides=strides)

    activation_coeffs = -tf.tensordot(
        mu_out_flat, tf.transpose(w_flat), axes=1)
    dual_obj_bias = -tf.tensordot(mu_out_flat, b_flat, axes=1)

    # Flatten the inputs, as the materialised convolution will have no
    # spatial structure.
    if lam_in is not None:
        lam_in = snt.FlattenTrailingDimensions(2)(lam_in)
    lb = snt.BatchFlatten()(lb)
    ub = snt.BatchFlatten()(ub)

    return standard_layer_calcs.linear_dual_objective(lam_in,
                                                      activation_coeffs,
                                                      dual_obj_bias, lb, ub)
예제 #4
0
    def test_conv2d_layer_dual_objective(self, dtype, tol):
        num_classes = 5
        batch_size = 53
        input_height = 17
        input_width = 7
        kernel_height = 3
        kernel_width = 4
        input_channels = 3
        output_channels = 2
        padding = 'VALID'
        strides = (2, 1)

        # Output dimensions, based on convolution settings.
        output_height = 8
        output_width = 4

        w = tf.random_normal(dtype=dtype,
                             shape=(kernel_height, kernel_width,
                                    input_channels, output_channels))
        b = tf.random_normal(dtype=dtype, shape=(output_channels, ))
        lam_in = tf.random_normal(dtype=dtype,
                                  shape=(num_classes, batch_size, input_height,
                                         input_width, input_channels))
        mu_out = tf.random_normal(dtype=dtype,
                                  shape=(num_classes, batch_size,
                                         output_height, output_width,
                                         output_channels))
        lb = tf.random_normal(dtype=dtype,
                              shape=(batch_size, input_height, input_width,
                                     input_channels))
        ub = tf.random_normal(dtype=dtype,
                              shape=(batch_size, input_height, input_width,
                                     input_channels))
        lb, ub = tf.minimum(lb, ub), tf.maximum(lb, ub)

        activation_coeffs = -common.conv_transpose(
            mu_out, w, lb.shape[1:].as_list(), padding, strides)
        dual_obj_bias = -tf.reduce_sum(mu_out * b, axis=(2, 3, 4))
        dual_obj = standard_layer_calcs.linear_dual_objective(
            lam_in, activation_coeffs, dual_obj_bias, lb, ub)

        # Compare against equivalent linear layer.
        dual_obj_lin = _materialised_conv_layer_dual_objective(
            w, b, padding, strides, lam_in, mu_out, lb, ub)

        with self.test_session() as session:
            dual_obj_val, dual_obj_lin_val = session.run(
                (dual_obj, dual_obj_lin))
            self.assertAllClose(dual_obj_val,
                                dual_obj_lin_val,
                                atol=tol,
                                rtol=tol)
예제 #5
0
    def test_linear_layer_dual_objective(self, dtype, tol):
        w = tf.constant([[1.0, 2.0, 3.0], [4.0, -5.0, -6.0]], dtype=dtype)
        b = tf.constant([0.1, 0.2, 0.3], dtype=dtype)
        lb = tf.constant([[-1.0, -1.0]], dtype=dtype)
        ub = tf.constant([[1.0, 1.0]], dtype=dtype)

        lam_in = tf.constant([[[-.01, -.02]]], dtype=dtype)
        mu_out = tf.constant([[[30.0, 40.0, 50.0]]], dtype=dtype)
        # Activation coefficients: -.01 - 260, and -.02 + 380

        activation_coeffs = -tf.tensordot(mu_out, tf.transpose(w), axes=1)
        dual_obj_bias = -tf.tensordot(mu_out, b, axes=1)
        dual_obj = standard_layer_calcs.linear_dual_objective(
            lam_in, activation_coeffs, dual_obj_bias, lb, ub)

        dual_obj_exp = np.array([[(.01 + 260.0) + (-.02 + 380.0) - 26.0]])

        with self.test_session() as session:
            dual_obj_act = session.run(dual_obj)
            self.assertAllClose(dual_obj_exp, dual_obj_act, atol=tol, rtol=tol)
예제 #6
0
    def test_conv2d_layer_dual_objective_shape(self, dtype):
        num_classes = 6
        batch_size = 23
        input_height = 17
        input_width = 7
        kernel_height = 3
        kernel_width = 4
        input_channels = 3
        output_channels = 5
        padding = 'VALID'
        strides = (2, 1)

        # Output dimensions, based on convolution settings.
        output_height = 8
        output_width = 4

        w = tf.placeholder(dtype=dtype,
                           shape=(kernel_height, kernel_width, input_channels,
                                  output_channels))
        b = tf.placeholder(dtype=dtype, shape=(output_channels, ))
        lam_in = tf.placeholder(dtype=dtype,
                                shape=(num_classes, batch_size, input_height,
                                       input_width, input_channels))
        mu_out = tf.placeholder(dtype=dtype,
                                shape=(num_classes, batch_size, output_height,
                                       output_width, output_channels))
        lb = tf.placeholder(dtype=dtype,
                            shape=(batch_size, input_height, input_width,
                                   input_channels))
        ub = tf.placeholder(dtype=dtype,
                            shape=(batch_size, input_height, input_width,
                                   input_channels))

        activation_coeffs = -common.conv_transpose(
            mu_out, w, lb.shape[1:].as_list(), padding, strides)
        dual_obj_bias = -tf.reduce_sum(mu_out * b, axis=(2, 3, 4))
        dual_obj = standard_layer_calcs.linear_dual_objective(
            lam_in, activation_coeffs, dual_obj_bias, lb, ub)

        self.assertEqual(dtype, dual_obj.dtype)
        self.assertEqual((num_classes, batch_size), dual_obj.shape)
예제 #7
0
    def test_linear_layer_dual_objective_shape(self, dtype):
        num_classes = 3
        batch_size = 11
        input_size = 7
        output_size = 5

        w = tf.placeholder(dtype=dtype, shape=(input_size, output_size))
        b = tf.placeholder(dtype=dtype, shape=(output_size, ))
        lam_in = tf.placeholder(dtype=dtype,
                                shape=(num_classes, batch_size, input_size))
        mu_out = tf.placeholder(dtype=dtype,
                                shape=(num_classes, batch_size, output_size))
        lb = tf.placeholder(dtype=dtype, shape=(batch_size, input_size))
        ub = tf.placeholder(dtype=dtype, shape=(batch_size, input_size))

        activation_coeffs = -tf.tensordot(mu_out, tf.transpose(w), axes=1)
        dual_obj_bias = -tf.tensordot(mu_out, b, axes=1)
        dual_obj = standard_layer_calcs.linear_dual_objective(
            lam_in, activation_coeffs, dual_obj_bias, lb, ub)

        self.assertEqual(dtype, dual_obj.dtype)
        self.assertEqual((num_classes, batch_size), dual_obj.shape)
예제 #8
0
    def test_calc_linear(self):
        image_data = self._image_data()
        net = self._network('linear')
        input_bounds = naive_bounds.input_bounds(image_data.image, delta=.1)
        dual_obj, dual_var_lists = self._build_objective(
            net, input_bounds, image_data.label)

        # Explicitly build the expected TensorFlow graph for calculating objective.
        (
            linear_0,
            relu_1,  # pylint:disable=unused-variable
            linear_obj) = self._verifiable_layer_builder(net).build_layers()
        (mu_0, ), _ = dual_var_lists

        # Expected input bounds for each layer.
        linear_0_lb, linear_0_ub = self._expected_input_bounds(
            image_data.image, .1)
        linear_0_lb = snt.BatchFlatten()(linear_0_lb)
        linear_0_ub = snt.BatchFlatten()(linear_0_ub)
        relu_1_lb, relu_1_ub = ibp.IntervalBounds(linear_0_lb,
                                                  linear_0_ub).apply_linear(
                                                      None, linear_0.module.w,
                                                      linear_0.module.b)

        # Expected objective value.
        objective = 0
        act_coeffs_0 = -tf.tensordot(
            mu_0, tf.transpose(linear_0.module.w), axes=1)
        obj_0 = -tf.tensordot(mu_0, linear_0.module.b, axes=1)
        objective += standard_layer_calcs.linear_dual_objective(
            None, act_coeffs_0, obj_0, linear_0_lb, linear_0_ub)
        objective_w, objective_b = common.targeted_objective(
            linear_obj.module.w, linear_obj.module.b, image_data.label)
        objective += standard_layer_calcs.activation_layer_dual_objective(
            tf.nn.relu, mu_0, -objective_w, relu_1_lb, relu_1_ub)
        objective += objective_b

        self._assert_dual_objective_close(objective, dual_obj, image_data)
    def affine_layer_contrib(self, layer, dual_vars_lm1, activation_coeffs,
                             dual_obj_bias):
        """Computes the contribution of an affine layer to the dual objective.

    Compute the term::
      max_x (lam_{l-1}^T x  -  mu_l^T (W_l x + b_l))
    where W_l, b_l is the affine mapping for layer l.

    Args:
      layer: affine (linear/conv) layer.
      dual_vars_lm1: lam_{l-1}, or None for the first layer.
      activation_coeffs: mu_l^T W_l
      dual_obj_bias: mu_l^T b_l

    Returns:
      Dual objective contribution.
    """
        return standard_layer_calcs.linear_dual_objective(
            dual_vars_lm1,
            activation_coeffs,
            dual_obj_bias,
            layer.input_bounds.lower_offset,
            layer.input_bounds.upper_offset,
            inverse_temperature=self._inverse_temperature)