def triangle_wave(frequency): """Emit a triangle wave at the given frequency.""" xs = tf.reshape(tf.range(_samples(), dtype=tf.float32), [1, _samples(), 1]) ts = xs / FLAGS.sample_rate # # A triangle wave looks like this: # # /\ /\ # / \ / \ # \ / \ / # \/ \/ # # If we look at just half a period (the first four slashes in the # diagram above), we can see that it looks like a transformed absolute # value function. # # Let's start by computing the times relative to the start of each # half-wave pulse (each individual "mountain" or "valley", of which # there are four in the above diagram). half_pulse_index = ts * (frequency * 2) half_pulse_angle = half_pulse_index % 1.0 # in [0, 1] # # Now, we can see that each positive half-pulse ("mountain") has # amplitude given by A(z) = 0.5 - abs(z - 0.5), and then normalized: absolute_amplitude = (0.5 - tf.abs(half_pulse_angle - 0.5)) / 0.5 # # But every other half-pulse is negative, so we should invert these. half_pulse_parity = tf.sign(1 - (half_pulse_index % 2.0)) amplitude = half_pulse_parity * absolute_amplitude # # This is precisely the desired result, so we're done! return amplitude
def bisine_wahwah_wave(frequency): """Emit two sine waves with balance oscillating left and right.""" # # This is clearly intended to build on the bisine wave defined above, # so we can start by generating that. waves_a = bisine_wave(frequency) # # Then, by reversing axis 2, we swap the stereo channels. By mixing # this with `waves_a`, we'll be able to create the desired effect. waves_b = tf.reverse(waves_a, axis=[2]) # # Let's have the balance oscillate from left to right four times. iterations = 4 # # Now, we compute the balance for each sample: `ts` has values # in [0, 1] that indicate how much we should use `waves_a`. xs = tf.reshape(tf.range(_samples(), dtype=tf.float32), [1, _samples(), 1]) thetas = xs / _samples() * iterations ts = (tf.sin(math.pi * 2 * thetas) + 1) / 2 # # Finally, we can mix the two together, and we're done. wave = ts * waves_a + (1.0 - ts) * waves_b # # Alternately, we can make the effect more pronounced by exaggerating # the sample data. Let's emit both variations. exaggerated_wave = wave**3.0 return tf.concat([wave, exaggerated_wave], axis=0)
def test_new_style_audio(self): audio = tf.reshape(tf.linspace(0.0, 100.0, 4 * 10 * 2), (4, 10, 2)) op = audio_summary.op('k488', tf.cast(audio, tf.float32), sample_rate=44100, display_name='Piano Concerto No.23', description='In **A major**.') value = self._value_from_op(op) assert value.HasField('tensor'), value self._assert_noop(value)
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 test_audio(self): audio = tf.reshape(tf.linspace(0.0, 100.0, 4 * 10 * 2), (4, 10, 2)) old_op = tf.summary.audio('k488', audio, 44100) old_value = self._value_from_op(old_op) assert old_value.HasField('audio'), old_value new_value = data_compat.migrate_value(old_value) self.assertEqual('k488/audio/0', new_value.tag) expected_metadata = audio_metadata.create_summary_metadata( display_name='k488/audio/0', description='', encoding=audio_metadata.Encoding.Value('WAV')) self.assertEqual(expected_metadata, new_value.metadata) self.assertTrue(new_value.HasField('tensor')) data = tf.make_ndarray(new_value.tensor) self.assertEqual((1, 2), data.shape) self.assertEqual(tf.compat.as_bytes(old_value.audio.encoded_audio_string), data[0][0]) self.assertEqual(b'', data[0][1]) # empty label
def start_runs(logdir, steps, run_name, thresholds, mask_every_other_prediction=False): """Generate a PR curve with precision and recall evenly weighted. Arguments: logdir: The directory into which to store all the runs' data. steps: The number of steps to run for. run_name: The name of the run. thresholds: The number of thresholds to use for PR curves. mask_every_other_prediction: Whether to mask every other prediction by alternating weights between 0 and 1. """ tf.reset_default_graph() tf.set_random_seed(42) # Create a normal distribution layer used to generate true color labels. distribution = tf.distributions.Normal(loc=0., scale=142.) # Sample the distribution to generate colors. Lets generate different numbers # of each color. The first dimension is the count of examples. # The calls to sample() are given fixed random seed values that are "magic" # in that they correspond to the default seeds for those ops when the PR # curve test (which depends on this code) was written. We've pinned these # instead of continuing to use the defaults since the defaults are based on # node IDs from the sequence of nodes added to the graph, which can silently # change when this code or any TF op implementations it uses are modified. # TODO(nickfelt): redo the PR curve test to avoid reliance on random seeds. # Generate reds. number_of_reds = 100 true_reds = tf.clip_by_value( tf.concat([ 255 - tf.abs(distribution.sample([number_of_reds, 1], seed=11)), tf.abs(distribution.sample([number_of_reds, 2], seed=34)) ], axis=1), 0, 255) # Generate greens. number_of_greens = 200 true_greens = tf.clip_by_value( tf.concat([ tf.abs(distribution.sample([number_of_greens, 1], seed=61)), 255 - tf.abs(distribution.sample([number_of_greens, 1], seed=82)), tf.abs(distribution.sample([number_of_greens, 1], seed=105)) ], axis=1), 0, 255) # Generate blues. number_of_blues = 150 true_blues = tf.clip_by_value( tf.concat([ tf.abs(distribution.sample([number_of_blues, 2], seed=132)), 255 - tf.abs(distribution.sample([number_of_blues, 1], seed=153)) ], axis=1), 0, 255) # Assign each color a vector of 3 booleans based on its true label. labels = tf.concat([ tf.tile(tf.constant([[True, False, False]]), (number_of_reds, 1)), tf.tile(tf.constant([[False, True, False]]), (number_of_greens, 1)), tf.tile(tf.constant([[False, False, True]]), (number_of_blues, 1)), ], axis=0) # We introduce 3 normal distributions. They are used to predict whether a # color falls under a certain class (based on distances from corners of the # color triangle). The distributions vary per color. We have the distributions # narrow over time. initial_standard_deviations = [v + FLAGS.steps for v in (158, 200, 242)] iteration = tf.placeholder(tf.int32, shape=[]) red_predictor = tf.distributions.Normal( loc=0., scale=tf.cast(initial_standard_deviations[0] - iteration, dtype=tf.float32)) green_predictor = tf.distributions.Normal( loc=0., scale=tf.cast(initial_standard_deviations[1] - iteration, dtype=tf.float32)) blue_predictor = tf.distributions.Normal( loc=0., scale=tf.cast(initial_standard_deviations[2] - iteration, dtype=tf.float32)) # Make predictions (assign 3 probabilities to each color based on each color's # distance to each of the 3 corners). We seek double the area in the right # tail of the normal distribution. examples = tf.concat([true_reds, true_greens, true_blues], axis=0) probabilities_colors_are_red = (1 - red_predictor.cdf( tf.norm(examples - tf.constant([255., 0, 0]), axis=1))) * 2 probabilities_colors_are_green = (1 - green_predictor.cdf( tf.norm(examples - tf.constant([0, 255., 0]), axis=1))) * 2 probabilities_colors_are_blue = (1 - blue_predictor.cdf( tf.norm(examples - tf.constant([0, 0, 255.]), axis=1))) * 2 predictions = (probabilities_colors_are_red, probabilities_colors_are_green, probabilities_colors_are_blue) # This is the crucial piece. We write data required for generating PR curves. # We create 1 summary per class because we create 1 PR curve per class. for i, color in enumerate(('red', 'green', 'blue')): description = ( 'The probabilities used to create this PR curve are ' 'generated from a normal distribution. Its standard ' 'deviation is initially %0.0f and decreases over time.' % initial_standard_deviations[i]) weights = None if mask_every_other_prediction: # Assign a weight of 0 to every even-indexed prediction. Odd-indexed # predictions are assigned a default weight of 1. consecutive_indices = tf.reshape(tf.range(tf.size(predictions[i])), tf.shape(predictions[i])) weights = tf.cast(consecutive_indices % 2, dtype=tf.float32) summary.op(name=color, labels=labels[:, i], predictions=predictions[i], num_thresholds=thresholds, weights=weights, display_name='classifying %s' % color, description=description) merged_summary_op = tf.summary.merge_all() events_directory = os.path.join(logdir, run_name) sess = tf.Session() writer = tf.summary.FileWriter(events_directory, sess.graph) for step in xrange(steps): feed_dict = { iteration: step, } merged_summary = sess.run(merged_summary_op, feed_dict=feed_dict) writer.add_summary(merged_summary, step) writer.close()
def sine_wave(frequency): """Emit a sine wave at the given frequency.""" xs = tf.reshape(tf.range(_samples(), dtype=tf.float32), [1, _samples(), 1]) ts = xs / FLAGS.sample_rate return tf.sin(2 * math.pi * frequency * ts)
def op(name, labels, predictions, num_thresholds=None, weights=None, display_name=None, description=None, collections=None): """Create a PR curve summary op for a single binary classifier. Computes true/false positive/negative values for the given `predictions` against the ground truth `labels`, against a list of evenly distributed threshold values in `[0, 1]` of length `num_thresholds`. Each number in `predictions`, a float in `[0, 1]`, is compared with its corresponding boolean label in `labels`, and counts as a single tp/fp/tn/fn value at each threshold. This is then multiplied with `weights` which can be used to reweight certain values, or more commonly used for masking values. Args: name: A tag attached to the summary. Used by TensorBoard for organization. labels: The ground truth values. A Tensor of `bool` values with arbitrary shape. predictions: A float32 `Tensor` whose values are in the range `[0, 1]`. Dimensions must match those of `labels`. num_thresholds: Number of thresholds, evenly distributed in `[0, 1]`, to compute PR metrics for. Should be `>= 2`. This value should be a constant integer value, not a Tensor that stores an integer. weights: Optional float32 `Tensor`. Individual counts are multiplied by this value. This tensor must be either the same shape as or broadcastable to the `labels` tensor. 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 summary operation for use in a TensorFlow graph. The float32 tensor produced by the summary operation is of dimension (6, num_thresholds). The first dimension (of length 6) is of the order: true positives, false positives, true negatives, false negatives, precision, recall. """ if num_thresholds is None: num_thresholds = _DEFAULT_NUM_THRESHOLDS if weights is None: weights = 1.0 dtype = predictions.dtype with tf.name_scope(name, values=[labels, predictions, weights]): tf.assert_type(labels, tf.bool) # We cast to float to ensure we have 0.0 or 1.0. f_labels = tf.cast(labels, dtype) # Ensure predictions are all in range [0.0, 1.0]. predictions = tf.minimum(1.0, tf.maximum(0.0, predictions)) # Get weighted true/false labels. true_labels = f_labels * weights false_labels = (1.0 - f_labels) * weights # Before we begin, flatten predictions. predictions = tf.reshape(predictions, [-1]) # Shape the labels so they are broadcast-able for later multiplication. true_labels = tf.reshape(true_labels, [-1, 1]) false_labels = tf.reshape(false_labels, [-1, 1]) # To compute TP/FP/TN/FN, we are measuring a binary classifier # C(t) = (predictions >= t) # at each threshold 't'. So we have # TP(t) = sum( C(t) * true_labels ) # FP(t) = sum( C(t) * false_labels ) # # But, computing C(t) requires computation for each t. To make it fast, # observe that C(t) is a cumulative integral, and so if we have # thresholds = [t_0, ..., t_{n-1}]; t_0 < ... < t_{n-1} # where n = num_thresholds, and if we can compute the bucket function # B(i) = Sum( (predictions == t), t_i <= t < t{i+1} ) # then we get # C(t_i) = sum( B(j), j >= i ) # which is the reversed cumulative sum in tf.cumsum(). # # We can compute B(i) efficiently by taking advantage of the fact that # our thresholds are evenly distributed, in that # width = 1.0 / (num_thresholds - 1) # thresholds = [0.0, 1*width, 2*width, 3*width, ..., 1.0] # Given a prediction value p, we can map it to its bucket by # bucket_index(p) = floor( p * (num_thresholds - 1) ) # so we can use tf.scatter_add() to update the buckets in one pass. # Compute the bucket indices for each prediction value. bucket_indices = tf.cast(tf.floor(predictions * (num_thresholds - 1)), tf.int32) # Bucket predictions. tp_buckets = tf.reduce_sum( tf.one_hot(bucket_indices, depth=num_thresholds) * true_labels, axis=0) fp_buckets = tf.reduce_sum( tf.one_hot(bucket_indices, depth=num_thresholds) * false_labels, axis=0) # Set up the cumulative sums to compute the actual metrics. tp = tf.cumsum(tp_buckets, reverse=True, name='tp') fp = tf.cumsum(fp_buckets, reverse=True, name='fp') # fn = sum(true_labels) - tp # = sum(tp_buckets) - tp # = tp[0] - tp # Similarly, # tn = fp[0] - fp tn = fp[0] - fp fn = tp[0] - tp precision = tp / tf.maximum(_MINIMUM_COUNT, tp + fp) recall = tp / tf.maximum(_MINIMUM_COUNT, tp + fn) return _create_tensor_summary(name, tp, fp, tn, fn, precision, recall, num_thresholds, display_name, description, collections)