def create_lstm_per_eg_grad(batch_size, state_size, steps): inputs = [ random_ops.random_normal([batch_size, state_size]) for _ in range(steps) ] cell = rnn_cell.BasicLSTMCell(state_size) init_state = cell.zero_state(batch_size, dtypes.float32) def model_fn(inps, init_state): state = init_state for inp in inps: _, state = cell(inp, state) output = nn.l2_loss(state.c) return gradient_ops.gradients(output, variables.trainable_variables()) def loop_fn(i): loop_inputs = [ array_ops.expand_dims(array_ops.gather(x, i), 0) for x in inputs ] loop_init_state = rnn_cell.LSTMStateTuple( *[array_ops.expand_dims(array_ops.gather(x, i), 0) for x in init_state]) return model_fn(loop_inputs, loop_init_state) pfor_outputs = control_flow_ops.pfor(loop_fn, batch_size) loop_fn_dtypes = [x.dtype for x in variables.trainable_variables()] while_outputs = control_flow_ops.for_loop(loop_fn, loop_fn_dtypes, batch_size) return pfor_outputs, while_outputs
def batch_jacobian(output, inp, use_pfor=True): """Computes and stacks jacobians of `output[i,...]` w.r.t. `input[i,...]`. e.g. x = tf.constant([[1, 2], [3, 4]], dtype=tf.float32) y = x * x jacobian = batch_jacobian(y, x) # => [[[2, 0], [0, 4]], [[6, 0], [0, 8]]] Args: output: A tensor with shape [b, y1, ..., y_n]. `output[i,...]` should only depend on `inp[i,...]`. inp: A tensor with shape [b, x1, ..., x_m] use_pfor: If true, uses pfor for computing the Jacobian. Else uses a tf.while_loop. Returns: A tensor `t` with shape [b, y_1, ..., y_n, x1, ..., x_m] where `t[i, ...]` is the jacobian of `output[i, ...]` w.r.t. `inp[i, ...]`, i.e. stacked per-example jacobians. Raises: ValueError: if first dimension of `output` and `inp` do not match. """ output_shape = output.shape if not output_shape[0].is_compatible_with(inp.shape[0]): raise ValueError("Need first dimension of output shape (%s) and inp shape " "(%s) to match." % (output.shape, inp.shape)) if output_shape.is_fully_defined(): batch_size = int(output_shape[0]) output_row_size = output_shape.num_elements() // batch_size else: output_shape = array_ops.shape(output) batch_size = output_shape[0] output_row_size = array_ops.size(output) // batch_size inp_shape = array_ops.shape(inp) # Flatten output to 2-D. with ops.control_dependencies( [check_ops.assert_equal(batch_size, inp_shape[0])]): output = array_ops.reshape(output, [batch_size, output_row_size]) def loop_fn(i): y = array_ops.gather(output, i, axis=1) return gradient_ops.gradients(y, inp)[0] if use_pfor: pfor_output = control_flow_ops.pfor(loop_fn, output_row_size) else: pfor_output = control_flow_ops.for_loop(loop_fn, output.dtype, output_row_size) if pfor_output is None: return None pfor_output = array_ops.reshape(pfor_output, [output_row_size, batch_size, -1]) output = array_ops.transpose(pfor_output, [1, 0, 2]) new_shape = array_ops.concat([output_shape, inp_shape[1:]], axis=0) return array_ops.reshape(output, new_shape)
def create_mnist_autobatch(batch_size, data_format, training): images = random_ops.random_uniform([batch_size, 28, 28]) model = Mnist(data_format) manual = model(images, training=training) def loop_fn(i): image = array_ops.gather(images, i) return model(image, training=training) pfor_outputs = control_flow_ops.pfor(loop_fn, batch_size) while_outputs = control_flow_ops.for_loop( loop_fn, dtypes.float32, batch_size) return pfor_outputs, while_outputs, manual
def benchmark_basic_while(self): with ops.Graph().as_default(): def loop_fn(i): _, s = control_flow_ops.while_loop( lambda t, x: t < i, lambda t, x: (t + 1, x + i), [0, 0]) return s iters = 50 pfor_output = pfor_control_flow_ops.pfor(loop_fn, iters) for_loop_output = pfor_control_flow_ops.for_loop(loop_fn, dtypes.int32, iters) self._run(pfor_output, 100, name="pfor_basic") self._run(for_loop_output, 100, name="for_loop_basic")
def create_mnist_per_eg_jacobian(batch_size, data_format, training): images = random_ops.random_uniform([batch_size, 28, 28]) model = Mnist(data_format) def loop_fn(i, use_pfor): image = array_ops.gather(images, i) logits = array_ops.reshape(model(image, training=training), [-1]) return gradients.jacobian( logits, variables.trainable_variables(), use_pfor=use_pfor) pfor_outputs = control_flow_ops.pfor( functools.partial(loop_fn, use_pfor=True), batch_size) while_outputs = control_flow_ops.for_loop( functools.partial(loop_fn, use_pfor=False), [dtypes.float32] * len(variables.trainable_variables()), batch_size) return pfor_outputs, while_outputs
def jacobian(output, inputs, use_pfor=True): """Computes jacobian of `output` w.r.t. `inputs`. Args: output: A tensor. inputs: A tensor or a nested structure of tensor objects. use_pfor: If true, uses pfor for computing the jacobian. Else uses tf.while_loop. Returns: A tensor or a nested strucutre of tensors with the same structure as `inputs`. Each entry is the jacobian of `output` w.rt. to the corresponding value in `inputs`. If output has shape [y_1, ..., y_n] and inputs_i has shape [x_1, ..., x_m], the corresponding jacobian has shape [y_1, ..., y_n, x_1, ..., x_m]. """ flat_inputs = nest.flatten(inputs) output_tensor_shape = output.shape output_shape = array_ops.shape(output) output = array_ops.reshape(output, [-1]) def loop_fn(i): y = array_ops.gather(output, i) return gradient_ops.gradients(y, flat_inputs) try: output_size = int(output.shape[0]) except TypeError: output_size = array_ops.shape(output)[0] if use_pfor: pfor_outputs = control_flow_ops.pfor(loop_fn, output_size) else: pfor_outputs = control_flow_ops.for_loop( loop_fn, [output.dtype] * len(flat_inputs), output_size) for i, out in enumerate(pfor_outputs): if out is not None: new_shape = array_ops.concat( [output_shape, array_ops.shape(out)[1:]], axis=0) out = array_ops.reshape(out, new_shape) out.set_shape(output_tensor_shape.concatenate(flat_inputs[i].shape)) pfor_outputs[i] = out return nest.pack_sequence_as(inputs, pfor_outputs)
def benchmark_matmul(self): with ops.Graph().as_default(): n = 1024 params = 1000 x = random_ops.random_normal([n, params]) y = random_ops.random_normal([params, params]) def loop_fn(i): x_i = array_ops.expand_dims(array_ops.gather(x, i), 0) return math_ops.matmul(x_i, y) pfor_outputs = pfor_control_flow_ops.pfor(loop_fn, n) while_outputs = pfor_control_flow_ops.for_loop(loop_fn, dtypes.float32, n) manual = math_ops.matmul(x, y) self._run(manual, 1000, name="manual_matmul") self._run(pfor_outputs, 1000, name="pfor_matmul") self._run(while_outputs, 100, name="while_matmul")
def benchmark_add(self): with ops.Graph().as_default(): n = 256 params = 1000 x = random_ops.random_normal([n, params]) y = random_ops.random_normal([n, params]) def loop_fn(i): x_i = array_ops.gather(x, i) y_i = array_ops.gather(y, i) return x_i + y_i pfor_outputs = pfor_control_flow_ops.pfor(loop_fn, n) while_outputs = pfor_control_flow_ops.for_loop(loop_fn, dtypes.float32, n) manual = x + y self._run(manual, 1000, name="manual_add") self._run(pfor_outputs, 1000, name="pfor_add") self._run(while_outputs, 100, name="while_add")
def create_fc_per_eg_jacobians(batch_size, activation_size, num_layers): model = FullyConnectedModel(activation_size=activation_size, num_layers=num_layers) inp = random_ops.random_normal([batch_size, activation_size]) output = model(inp) jacobians = gradients.jacobian(output, variables.trainable_variables()) def loop_fn(i, use_pfor): inp_i = array_ops.expand_dims(array_ops.gather(inp, i), 0) output = array_ops.reshape(model(inp_i), [-1]) return gradients.jacobian( output, variables.trainable_variables(), use_pfor=use_pfor) per_eg_jacobians_pfor = control_flow_ops.pfor( functools.partial(loop_fn, use_pfor=True), batch_size) per_eg_jacobians_while = control_flow_ops.for_loop( functools.partial(loop_fn, use_pfor=False), [dtypes.float32] * len(variables.trainable_variables()), batch_size) return jacobians, per_eg_jacobians_pfor, per_eg_jacobians_while
def test_create_outside_and_write_and_scatter(self): t = tensor_array_ops.TensorArray(dtypes.int32, 10, clear_after_read=False) handle = t.handle def loop_fn(i): ta = t.write(i + 2, 2 * i).write(i, 5) ta = ta.scatter([4 + i], [4]).scatter([6 + i, 8 + i], [6 + i, 8 + i]) return ta.flow t1 = pfor_control_flow_ops.pfor(loop_fn, iters=2) out1 = tensor_array_ops.TensorArray( dtypes.int32, handle=handle, flow=t1[-1]).stack() output1 = self._run_targets(out1) t2 = pfor_control_flow_ops.for_loop(loop_fn, dtypes.float32, iters=2) out2 = tensor_array_ops.TensorArray( dtypes.int32, handle=handle, flow=t2[-1]).stack() output2 = self._run_targets(out2) self.assertAllClose(output2, output1)
def create_mnist_per_eg_grad(batch_size, data_format, training): images = random_ops.random_uniform([batch_size, 28, 28]) sparse_labels = np.random.randint( low=0, high=10, size=[batch_size]).astype(np.int32) labels = np.zeros((batch_size, 10)).astype(np.float32) labels[np.arange(batch_size), sparse_labels] = 1. model = Mnist(data_format) def loop_fn(i): image = array_ops.gather(images, i) label = array_ops.gather(labels, i) logits = array_ops.reshape(model(image, training=training), [-1]) loss = losses.softmax_cross_entropy( logits=logits, onehot_labels=label, reduction=losses.Reduction.NONE) return gradient_ops.gradients(loss, variables.trainable_variables()) pfor_outputs = control_flow_ops.pfor(loop_fn, batch_size) while_outputs = control_flow_ops.for_loop( loop_fn, [dtypes.float32] * len(variables.trainable_variables()), batch_size) return pfor_outputs, while_outputs
def create_fc_per_eg_grad(batch_size, activation_size, num_layers): inp = random_ops.random_normal([batch_size, activation_size]) layers = [ tf_layers.Dense(activation_size, activation=nn.relu) for _ in range(num_layers) ] projection = tf_layers.Dense(1) def model_fn(activation): for layer in layers: activation = layer(activation) activation = projection(activation) activation = nn.l2_loss(activation) return gradient_ops.gradients(activation, variables.trainable_variables()) def loop_fn(i): return model_fn(array_ops.expand_dims(array_ops.gather(inp, i), 0)) pfor_outputs = control_flow_ops.pfor(loop_fn, batch_size) loop_fn_dtypes = [x.dtype for x in variables.trainable_variables()] while_outputs = control_flow_ops.for_loop(loop_fn, loop_fn_dtypes, batch_size) return pfor_outputs, while_outputs
def create_mnist_per_eg_grad(batch_size, data_format, training): images = random_ops.random_uniform([batch_size, 28, 28]) sparse_labels = np.random.randint(low=0, high=10, size=[batch_size]).astype(np.int32) labels = np.zeros((batch_size, 10)).astype(np.float32) labels[np.arange(batch_size), sparse_labels] = 1. model = Mnist(data_format) def loop_fn(i): image = array_ops.gather(images, i) label = array_ops.gather(labels, i) logits = array_ops.reshape(model(image, training=training), [-1]) loss = losses.softmax_cross_entropy(logits=logits, onehot_labels=label, reduction=losses.Reduction.NONE) return gradient_ops.gradients(loss, variables.trainable_variables()) pfor_outputs = control_flow_ops.pfor(loop_fn, batch_size) while_outputs = control_flow_ops.for_loop( loop_fn, [dtypes.float32] * len(variables.trainable_variables()), batch_size) return pfor_outputs, while_outputs
def test_parallel_iterations_zero(self): with self.assertRaisesRegexp(ValueError, "positive integer"): pfor_control_flow_ops.pfor(lambda i: 1, 8, parallel_iterations=0) with self.assertRaisesRegexp(TypeError, "positive integer"): pfor_control_flow_ops.for_loop(lambda i: 1, dtypes.int32, 8, parallel_iterations=0)
def test_parallel_iterations_zero(self): with self.assertRaisesRegexp(ValueError, "positive integer"): pfor_control_flow_ops.pfor(lambda i: 1, 8, parallel_iterations=0) with self.assertRaisesRegexp(TypeError, "positive integer"): pfor_control_flow_ops.for_loop(lambda i: 1, dtypes.int32, 8, parallel_iterations=0)
def _test_loop_fn(self, loop_fn, iters, loop_fn_dtypes=dtypes.float32): t1 = pfor_control_flow_ops.pfor(loop_fn, iters=iters) t2 = pfor_control_flow_ops.for_loop(loop_fn, loop_fn_dtypes, iters=iters) self.run_and_assert_equal(t1, t2)
def batch_jacobian(output, inp, use_pfor=True, parallel_iterations=None): """Computes and stacks jacobians of `output[i,...]` w.r.t. `input[i,...]`. e.g. x = tf.constant([[1, 2], [3, 4]], dtype=tf.float32) y = x * x jacobian = batch_jacobian(y, x) # => [[[2, 0], [0, 4]], [[6, 0], [0, 8]]] Args: output: A tensor with shape [b, y1, ..., y_n]. `output[i,...]` should only depend on `inp[i,...]`. inp: A tensor with shape [b, x1, ..., x_m] use_pfor: If true, uses pfor for computing the Jacobian. Else uses a tf.while_loop. parallel_iterations: A knob to control how many iterations are vectorized and dispatched in parallel. The default value of None, when use_pfor is true, corresponds to vectorizing all the iterations. When use_pfor is false, the default value of None corresponds to parallel_iterations=10. This knob can be used to control the total memory usage. Returns: A tensor `t` with shape [b, y_1, ..., y_n, x1, ..., x_m] where `t[i, ...]` is the jacobian of `output[i, ...]` w.r.t. `inp[i, ...]`, i.e. stacked per-example jacobians. Raises: ValueError: if first dimension of `output` and `inp` do not match. """ output_shape = output.shape if not output_shape[0].is_compatible_with(inp.shape[0]): raise ValueError( f"Need first dimension of `output` shape ({output.shape}) " f"and `inp` shape ({inp.shape}) to match.") if output_shape.is_fully_defined(): batch_size = int(output_shape[0]) output_row_size = output_shape.num_elements() // batch_size else: output_shape = array_ops.shape(output) batch_size = output_shape[0] output_row_size = array_ops.size(output) // batch_size inp_shape = array_ops.shape(inp) # Flatten output to 2-D. with ops.control_dependencies( [check_ops.assert_equal(batch_size, inp_shape[0])]): output = array_ops.reshape(output, [batch_size, output_row_size]) def loop_fn(i): y = array_ops.gather(output, i, axis=1) return gradient_ops.gradients(y, inp)[0] if use_pfor: pfor_output = control_flow_ops.pfor( loop_fn, output_row_size, parallel_iterations=parallel_iterations) else: pfor_output = control_flow_ops.for_loop( loop_fn, output.dtype, output_row_size, parallel_iterations=parallel_iterations) if pfor_output is None: return None pfor_output = array_ops.reshape(pfor_output, [output_row_size, batch_size, -1]) output = array_ops.transpose(pfor_output, [1, 0, 2]) new_shape = array_ops.concat([output_shape, inp_shape[1:]], axis=0) return array_ops.reshape(output, new_shape)
def _test_loop_fn(self, loop_fn, iters, loop_fn_dtypes=dtypes.float32): t1 = pfor_control_flow_ops.pfor(loop_fn, iters=iters) t2 = pfor_control_flow_ops.for_loop(loop_fn, loop_fn_dtypes, iters=iters) self.run_and_assert_equal(t1, t2)