def true_mean_confidence_interval_by_dkwm( samples, low, high, error_rate=1e-6, name=None): """Computes a confidence interval for the mean of a scalar distribution. In batch mode, computes confidence intervals for all distributions in the batch (which need not be identically distributed). Relies on the [Dvoretzky-Kiefer-Wolfowitz-Massart inequality] (https://en.wikipedia.org/wiki/CDF-based_nonparametric_confidence_interval). The probability (over the randomness of drawing the given samples) that any true mean is outside the corresponding returned interval is no more than the given `error_rate`. The size of the intervals scale as `O(1 / sqrt(#samples))`, as `O(high - low)`, and as `O(-log(error_rate))`. Note that `error_rate` is a total error rate for all the confidence intervals in the batch. As such, if the batch is nontrivial, the error rate is not broadcast but divided (evenly) among the batch members. Args: samples: Floating-point `Tensor` of samples from the distribution(s) of interest. Entries are assumed IID across the 0th dimension. The other dimensions must broadcast with `low` and `high`. The support is bounded: `low <= samples <= high`. low: Floating-point `Tensor` of lower bounds on the distributions' supports. high: Floating-point `Tensor` of upper bounds on the distributions' supports. error_rate: *Scalar* floating-point `Tensor` admissible total rate of mistakes. name: A name for this operation (optional). Returns: low: A floating-point `Tensor` of stochastic lower bounds on the true means. high: A floating-point `Tensor` of stochastic upper bounds on the true means. """ with tf.name_scope(name, "true_mean_confidence_interval_by_dkwm", [samples, low, high, error_rate]): dtype = dtype_util.common_dtype( [samples, low, high, error_rate], tf.float32) samples = tf.convert_to_tensor(samples, name="samples", dtype=dtype) low = tf.convert_to_tensor(low, name="low", dtype=dtype) high = tf.convert_to_tensor(high, name="high", dtype=dtype) error_rate = tf.convert_to_tensor( error_rate, name="error_rate", dtype=dtype) samples = _check_shape_dominates(samples, [low, high]) tf.assert_scalar(error_rate) # Static shape error_rate = _itemwise_error_rate(error_rate, [low, high], samples) n = tf.shape(samples)[0] envelope = _dkwm_cdf_envelope(n, error_rate) min_mean = _minimum_mean(samples, envelope, low) max_mean = _maximum_mean(samples, envelope, high) return min_mean, max_mean
def op(name, data, display_name=None, description=None, collections=None): """Create a scalar summary op. Arguments: name: A unique name for the generated summary node. data: A real numeric rank-0 `Tensor`. Must have `dtype` castable to `float32`. display_name: Optional name for this summary in TensorBoard, as a constant `str`. Defaults to `name`. description: Optional long-form description for this summary, as a constant `str`. Markdown is supported. Defaults to empty. collections: Optional list of graph collections keys. The new summary op is added to these collections. Defaults to `[Graph Keys.SUMMARIES]`. Returns: A TensorFlow summary op. """ if display_name is None: display_name = name summary_metadata = metadata.create_summary_metadata( display_name=display_name, description=description) with tf.name_scope(name): with tf.control_dependencies([tf.assert_scalar(data)]): return tf.summary.tensor_summary(name='scalar_summary', tensor=tf.cast(data, tf.float32), collections=collections, summary_metadata=summary_metadata)
def gumbel_softmax(inputs: tf.Tensor, temperature: float = 1.0, symmetric: bool = False, axis: int = -1, scope: Optional[str] = None) -> tf.Tensor: if inputs.shape[axis].value <= 2: raise ValueError( 'logits must have at least size 3 on axis={}'.format(axis)) temperature = tf.convert_to_tensor(temperature, dtype=inputs.dtype) with tf.variable_scope(scope, 'gumbel_softmax', [inputs]): temperature = tf.assert_scalar(temperature) assert_op = tf.assert_greater(temperature, 0.0) with tf.control_dependencies([assert_op]): gumbel = -tf.log( -tf.log(tf.random_uniform(inputs.shape, dtype=inputs.dtype))) if symmetric: gumbel = (gumbel + tf.matrix_transpose(gumbel)) / 2.0 gumbel_logits = gumbel + inputs gumbel_softmax = tf.exp( tf.nn.log_softmax(tf.div(gumbel_logits, temperature), axis=axis)) return gumbel_softmax
def op(name, data, display_name=None, description=None, collections=None): """Create a legacy scalar summary op. Arguments: name: A unique name for the generated summary node. data: A real numeric rank-0 `Tensor`. Must have `dtype` castable to `float32`. display_name: Optional name for this summary in TensorBoard, as a constant `str`. Defaults to `name`. description: Optional long-form description for this summary, as a constant `str`. Markdown is supported. Defaults to empty. collections: Optional list of graph collections keys. The new summary op is added to these collections. Defaults to `[Graph Keys.SUMMARIES]`. Returns: A TensorFlow summary op. """ if display_name is None: display_name = name summary_metadata = metadata.create_summary_metadata( display_name=display_name, description=description) with tf.name_scope(name): with tf.control_dependencies([tf.assert_scalar(data)]): return tf.summary.tensor_summary(name='scalar_summary', tensor=tf.cast(data, tf.float32), collections=collections, summary_metadata=summary_metadata)
def scalar(name, tensor, tag=None, description=None, step=None): """Create a scalar summary op. Arguments: name: A name for the generated summary node. tensor: A real numeric rank-0 `Tensor`. Must have `dtype` castable to `float32`. tag: Optional rank-0 string `Tensor` to identify this summary in TensorBoard. Defaults to the generated name of this op. description: Optional long-form description for this summary, as a constant `str`. Markdown is supported. Defaults to empty. step: Optional `int64` monotonic step variable, which defaults to `tf.train.get_global_step`. Returns: A TensorFlow summary op. """ # TODO(nickfelt): make tag param work summary_metadata = metadata.create_summary_metadata( display_name=None, description=description) with tf.name_scope(name, values=[tensor, tag, step]) as scope: with tf.control_dependencies([tf.assert_scalar(tensor)]): return tf.contrib.summary.generic(name=scope, tensor=tf.cast( tensor, tf.float32), metadata=summary_metadata, step=step)
def _build_loss(self): policy = self.local_or_global_policy self._actions_ph = tf.placeholder(tf.int32, [None], name="actions") self._rewards_ph = tf.placeholder(tf.float32, [None], name="rewards") self._resets_ph = tf.placeholder(tf.float32, [None], name="resets") with tf.variable_scope("loss"): with tf.variable_scope("predictions"): self._predictions = slice_with_actions(policy.output_tensor, self._actions_ph) with tf.variable_scope("targets"): self._next_actions = tf.cast( tf.argmax(policy.target.values, axis=-1), tf.int32) all_next_step_predictions = slice_with_actions( policy.target.output_tensor, self._next_actions) next_step_multiplier = (1 - self._resets_ph)[..., None] self._next_step_predictions = (next_step_multiplier * self._gamma * all_next_step_predictions) self._targets = self._rewards_ph[ ..., None] + self._next_step_predictions with tf.variable_scope("quantile_loss"): nbins = policy.output_tensor.shape[-1].value cdf = np.arange(0, nbins + 1) / nbins midpoints = (cdf[:-1] + cdf[1:]) / 2 overestimation = tf.to_float( self._targets[..., None] < self._predictions[:, None]) if self._kind == "qr-dqn-0": loss = tf.reduce_sum(tf.reduce_mean( (self._targets[..., None] - self._predictions[:, None]) * (midpoints[None, None] - overestimation), axis=[0, 1]), axis=0) else: assert self._kind == "qr-dqn-1", self._kind loss = tf.reduce_sum(tf.reduce_mean( huber_loss(self._targets[..., None] - self._predictions[:, None]) * tf.abs(midpoints[None, None] - overestimation), axis=[0, 1]), axis=0) tf.assert_scalar(loss) return loss
def gradient_penalty(model: MolGANModel, real_adj: tf.Tensor, real_features: tf.Tensor, gen_adj: tf.Tensor, gen_features: tf.Tensor, discriminator_scope: str, gradient_penalty_weight: float = 10.0, target: float = 1.0, add_summaries: bool = False) -> tf.Tensor: scope = '{}GradientPenalty'.format(discriminator_scope.replace('/', '')) with tf.name_scope(scope, values=[real_adj, real_features, gen_adj, gen_features]): batch_size = gen_adj.shape[0].value eps = tf.random_uniform(shape=[batch_size ]) # batch_size times random numbers intp_inputs = {} eps_adj = eps for _ in range(real_adj.shape.ndims - 1): eps_adj = tf.expand_dims(eps_adj, axis=-1) intp_inputs['adjacency_in'] = eps_adj * real_adj + (1.0 - eps_adj) * gen_adj eps_features = eps for _ in range(real_features.shape.ndims - 1): eps_features = tf.expand_dims(eps_features, axis=-1) intp_inputs['features'] = eps_features * real_features + ( 1.0 - eps_features) * gen_features with tf.name_scope( None): # Clear scope so update ops are added properly. with tf.variable_scope(discriminator_scope, reuse=True): disc_interpolates = model.discriminator_fn( intp_inputs, mode=tf.estimator.ModeKeys.TRAIN) grads = tf.gradients( disc_interpolates, (intp_inputs['adjacency_in'], intp_inputs['features'])) penalty = tf.reduce_sum([ _penalty_from_gradient(g, batch_size, target=target, scope=sc) for g, sc in zip(grads, ['adj_grad_pen', 'feat_grad_pen']) ]) gp_weight = tf.convert_to_tensor(gradient_penalty_weight, dtype=penalty.dtype) gp_weight = tf.assert_scalar(gp_weight) penalty *= gp_weight tf.losses.add_loss(penalty) if add_summaries: tf.summary.scalar('gradient_penalty_loss', penalty) return penalty
def _buckets(data, bucket_count=None): """Create a TensorFlow op to group data into histogram buckets. Arguments: data: A `Tensor` of any shape. Must be castable to `float64`. bucket_count: Optional positive `int` or scalar `int32` `Tensor`. Returns: A `Tensor` of shape `[k, 3]` and type `float64`. The `i`th row is a triple `[left_edge, right_edge, count]` for a single bucket. The value of `k` is either `bucket_count` or `1` or `0`. """ if bucket_count is None: bucket_count = DEFAULT_BUCKET_COUNT with tf.name_scope('buckets', values=[data, bucket_count]), \ tf.control_dependencies([tf.assert_scalar(bucket_count), tf.assert_type(bucket_count, tf.int32)]): data = tf.reshape(data, shape=[-1]) # flatten data = tf.cast(data, tf.float64) is_empty = tf.equal(tf.size(data), 0) def when_empty(): return tf.constant([], shape=(0, 3), dtype=tf.float64) def when_nonempty(): min_ = tf.reduce_min(data) max_ = tf.reduce_max(data) range_ = max_ - min_ is_singular = tf.equal(range_, 0) def when_nonsingular(): bucket_width = range_ / tf.cast(bucket_count, tf.float64) offsets = data - min_ bucket_indices = tf.cast(tf.floor(offsets / bucket_width), dtype=tf.int32) clamped_indices = tf.minimum(bucket_indices, bucket_count - 1) one_hots = tf.one_hot(clamped_indices, depth=bucket_count) bucket_counts = tf.cast(tf.reduce_sum(one_hots, axis=0), dtype=tf.float64) edges = tf.lin_space(min_, max_, bucket_count + 1) left_edges = edges[:-1] right_edges = edges[1:] return tf.transpose(tf.stack( [left_edges, right_edges, bucket_counts])) def when_singular(): center = min_ bucket_starts = tf.stack([center - 0.5]) bucket_ends = tf.stack([center + 0.5]) bucket_counts = tf.stack([tf.cast(tf.size(data), tf.float64)]) return tf.transpose( tf.stack([bucket_starts, bucket_ends, bucket_counts])) return tf.cond(is_singular, when_singular, when_nonsingular) return tf.cond(is_empty, when_empty, when_nonempty)
def _buckets(data, bucket_count=None): """Create a TensorFlow op to group data into histogram buckets. Arguments: data: A `Tensor` of any shape. Must be castable to `float64`. bucket_count: Optional positive `int` or scalar `int32` `Tensor`. Returns: A `Tensor` of shape `[k, 3]` and type `float64`. The `i`th row is a triple `[left_edge, right_edge, count]` for a single bucket. The value of `k` is either `bucket_count` or `1` or `0`. """ if bucket_count is None: bucket_count = DEFAULT_BUCKET_COUNT with tf.name_scope('buckets', values=[data, bucket_count]), \ tf.control_dependencies([tf.assert_scalar(bucket_count), tf.assert_type(bucket_count, tf.int32)]): data = tf.reshape(data, shape=[-1]) # flatten data = tf.cast(data, tf.float64) is_empty = tf.equal(tf.size(data), 0) def when_empty(): return tf.constant([], shape=(0, 3), dtype=tf.float64) def when_nonempty(): min_ = tf.reduce_min(data) max_ = tf.reduce_max(data) range_ = max_ - min_ is_singular = tf.equal(range_, 0) def when_nonsingular(): bucket_width = range_ / tf.cast(bucket_count, tf.float64) offsets = data - min_ bucket_indices = tf.cast(tf.floor(offsets / bucket_width), dtype=tf.int32) clamped_indices = tf.minimum(bucket_indices, bucket_count - 1) one_hots = tf.one_hot(clamped_indices, depth=bucket_count) bucket_counts = tf.cast(tf.reduce_sum(one_hots, axis=0), dtype=tf.float64) edges = tf.lin_space(min_, max_, bucket_count + 1) left_edges = edges[:-1] right_edges = edges[1:] return tf.transpose( tf.stack([left_edges, right_edges, bucket_counts])) def when_singular(): center = min_ bucket_starts = tf.stack([center - 0.5]) bucket_ends = tf.stack([center + 0.5]) bucket_counts = tf.stack([tf.cast(tf.size(data), tf.float64)]) return tf.transpose( tf.stack([bucket_starts, bucket_ends, bucket_counts])) return tf.cond(is_singular, when_singular, when_nonsingular) return tf.cond(is_empty, when_empty, when_nonempty)
def _build_loss(self): policy = self.local_or_global_policy self._actions_ph = tf.placeholder(tf.int32, [None], name="actions") self._rewards_ph = tf.placeholder(tf.float32, [None], name="rewards") self._resets_ph = tf.placeholder(tf.float32, [None], name="resets") with tf.variable_scope("loss"): with tf.variable_scope("predictions"): self._predictions = slice_with_actions(policy.values, self._actions_ph) with tf.variable_scope("targets"): next_step_predictions = tf.reduce_max(policy.target.values, axis=-1) self._next_step_predictions = ((1 - self._resets_ph) * self._gamma * next_step_predictions) self._targets = self._rewards_ph + self._next_step_predictions loss = tf.reduce_mean( huber_loss(self._targets - self._predictions)) tf.assert_scalar(loss) return loss
def reward_loss(model: MolGANModel, reward_real: tf.Tensor, reward_generated: tf.Tensor, gan_loss: tfgan.GANLoss, lam: float, add_summaries: bool = False, scope: Optional[str] = None) -> MolGANLoss: with tf.name_scope(scope, 'ValueLoss'): loss_on_real = tf.losses.mean_squared_error(reward_real, model.valuenet_real_probs) loss_on_generated = tf.losses.mean_squared_error( reward_generated, model.valuenet_gen_probs) loss_V = 0.5 * (loss_on_real + loss_on_generated) tf.losses.add_loss(loss_V) # maximize reward on generated gen_reward = tf.reduce_mean(model.valuenet_gen_probs) gen_value_loss = -gen_reward gen_loss = gan_loss.generator_loss alpha = tf.abs(tf.stop_gradient(gen_loss / gen_value_loss)) lam = tf.assert_scalar(lam) generator_loss = lam * gen_loss + (1.0 - lam) * alpha * gen_value_loss if add_summaries: tf.summary.scalar('lambda', lam) tf.summary.scalar('alpha', alpha) tf.summary.scalar('reward_on_generated', gen_reward) tf.summary.scalar('valuenet_gen_mse_loss', loss_on_generated) tf.summary.scalar('valuenet_real_mse_loss', loss_on_real) tf.summary.scalar('valuenet_mse_loss', loss_V) return MolGANLoss(generator_loss=generator_loss, discriminator_loss=gan_loss.discriminator_loss, valuenet_loss=loss_V)
def test_loss_shape(self): tf.assert_scalar(self.model.loss_tensor)
def test_generator_loss_shape(self): tf.assert_scalar(self.model.g_loss)