def test_init(self): cases = [('One', Settings()), ('Two', Settings(x=1))] for name, defaults in cases: t = SettingsType(name, defaults) self.assertEqual(t.name, name) self.assertEqual(t.defaults, defaults)
def test_initializer(self): s = Settings() self.assertEqual(len(s.__dict__), 0) s = Settings(x=1, y=2) self.assertEqual(s.x, 1) self.assertEqual(s.y, 2) defaults = Settings(x=1, y=2) s = Settings(defaults, x=3) self.assertEqual(s.x, 3) self.assertEqual(s.y, 2)
def compute_spectrogram_normalization_settings(settings): # Get settings with waveform time shifting and spectrogram # normalization disabled. s = Settings(settings, random_waveform_time_shifting_enabled=False, spectrogram_normalization_enabled=False) num_examples = s.pretraining_num_examples batch_size = s.pretraining_batch_size num_batches = int(round(num_examples / batch_size)) dataset = create_spectrogram_dataset(s.dataset_name, DATASET_PART_TRAINING, DATASET_MODE_TRAINING, s, batch_size=batch_size) iterator = dataset.make_one_shot_iterator() next_batch = iterator.get_next() with tf.Session() as session: print(('Computing spectrogram normalization settings from {} ' 'examples...').format(num_batches * batch_size)) start_time = time.time() num_values = 0 values_sum = 0 squares_sum = 0 for _ in range(num_batches): features, _ = session.run(next_batch) grams = features['spectrogram'] num_values += grams.size values_sum += grams.sum() squares_sum += (grams**2).sum() mean = values_sum / num_values std_dev = math.sqrt(squares_sum / num_values - mean**2) # print( # 'Batch {} of {}: ({}, {})'.format( # i + 1, num_batches, mean, std_dev)) elapsed_time = time.time() - start_time print(('Computed spectrogram normalization settings in {:.1f} ' 'seconds.').format(elapsed_time)) # The float conversions in the following ensure that the assigned # type is Python's `float` instead of a NumPy type. The latter # doesn't play nicely with `yaml_utils.dump`. scale_factor = float(1 / std_dev) offset = float(-mean / std_dev) return scale_factor, offset
def complete_settings(settings): # Copy settings so we don't modify the originals. s = Settings(settings) # Get the nominal start time of the portion of a dataset waveform # that is used for training. When time shifting data augmentation # is enabled, a random offset is added to the nominal start time # for each training example to determine the actual start time. s.waveform_start_time = \ s.event_onset_window_start_time - s.waveform_initial_padding if s.spectrogram_clipping_enabled and \ s.spectrogram_clipping_pretraining_enabled and \ not s.warm_start_enabled: min_value, max_value = compute_spectrogram_clipping_settings(s) s.spectrogram_clipping_min = min_value s.spectrogram_clipping_max = max_value if s.spectrogram_normalization_enabled and \ s.spectrogram_normalization_pretraining_enabled and \ not s.warm_start_enabled: scale_factor, offset = compute_spectrogram_normalization_settings(s) s.spectrogram_normalization_scale_factor = scale_factor s.spectrogram_normalization_offset = offset return s
def show_spectrogram_dataset(classifier_name): total_num_examples = 2**9 batch_size = 2**6 settings = get_completed_settings(classifier_name) # Show unclipped and unnormalized spectrograms. s = Settings(settings, spectrogram_clipping_enabled=False, spectrogram_normalization_enabled=False) dataset = create_spectrogram_dataset(s.dataset_name, DATASET_PART_TRAINING, DATASET_MODE_TRAINING, s, batch_size=batch_size) num_batches = int(round(total_num_examples / batch_size)) dataset_utils.show_dataset(dataset, num_batches)
def _create_detector_class(threshold_type, threshold): threshold_string = f'{threshold:02d}' class_name = f'Detector{threshold_type}{threshold_string}' extension_name = ( f'BirdVoxDetect {birdvoxdetect.__version__} {threshold_type} ' f'{threshold_string}') if threshold_type == 'AT': detector_name = 'birdvoxdetect-v03_T-1800_trial-37_network_epoch-023' else: detector_name = 'birdvoxdetect-v03_trial-12_network_epoch-068' settings = Settings(detector_name=detector_name, threshold=threshold) class_dict = {'extension_name': extension_name, '_settings': settings} cls = type(class_name, (_Detector, ), class_dict) globals()[class_name] = cls
def __init__(self, settings): self._settings = settings s = settings sample_rate = s.waveform_sample_rate # Get waveform trimming start and end indices. self._start_time_index = signal_utils.seconds_to_frames( s.waveform_start_time, sample_rate) waveform_length = signal_utils.seconds_to_frames( s.waveform_duration, sample_rate) self._end_time_index = self._start_time_index + waveform_length # Get spectrogram settings. window_size = signal_utils.seconds_to_frames(s.spectrogram_window_size, sample_rate) hop_size = signal_utils.seconds_to_frames(s.spectrogram_hop_size, sample_rate) dft_size = tfa_utils.get_dft_size(window_size) self._spectrogram_settings = Settings( window=data_windows.create_window('Hann', window_size), hop_size=hop_size, dft_size=dft_size, reference_power=1) # Get spectrogram shape. num_spectra = tfa_utils.get_num_analysis_records( waveform_length, window_size, hop_size) num_bins = dft_size // 2 + 1 self._spectrogram_shape = (num_spectra, num_bins) self._augmented_spectrogram_shape = (1, ) + self._spectrogram_shape # Get spectrogram trimming start and end indices. self._start_freq_index = _freq_to_dft_bin_num( settings.spectrogram_start_freq, sample_rate, dft_size) self._end_freq_index = _freq_to_dft_bin_num( settings.spectrogram_end_freq, sample_rate, dft_size) + 1
def _load_settings(self): path = classifier_utils.get_settings_file_path(self._clip_type) text = path.read_text() d = yaml_utils.load(text) return Settings.create_from_dict(d)
def test_create_from_yaml_file(self): settings = Settings.create_from_yaml_file(_SETTINGS_FILE_PATH) self._check_settings(settings)
def test_create_from_commented_out_yaml_file(self): settings = Settings.create_from_yaml_file( _COMMENTED_OUT_SETTINGS_FILE_PATH) self._check_empty_settings(settings)
def test_create_from_yaml(self): contents = os_utils.read_file(_SETTINGS_FILE_PATH) settings = Settings.create_from_yaml(contents) self._check_settings(settings)
def _load_settings(self): path = classifier_utils.get_settings_file_path(self.clip_type) logging.info('Loading classifier settings from "{}"...'.format(path)) text = path.read_text() d = yaml_utils.load(text) return Settings.create_from_dict(d)
def compute_spectrogram_clipping_settings(settings): # Get new settings with waveform time shifting, spectrogram clipping, # and spectrogram normalization disabled. s = Settings(settings, random_waveform_time_shifting_enabled=False, spectrogram_clipping_enabled=False, spectrogram_normalization_enabled=False) num_examples = s.pretraining_num_examples batch_size = s.pretraining_batch_size num_batches = int(round(num_examples / batch_size)) hist_min = s.pretraining_histogram_min hist_max = s.pretraining_histogram_max num_bins = s.pretraining_histogram_num_bins bin_size = (hist_max - hist_min) / num_bins log_epsilon = math.log(settings.spectrogram_log_epsilon) dataset = create_spectrogram_dataset(s.dataset_name, DATASET_PART_TRAINING, DATASET_MODE_TRAINING, s, batch_size=batch_size) iterator = dataset.make_one_shot_iterator() next_batch = iterator.get_next() with tf.Session() as session: print( 'Computing spectrogram clipping range from {} examples...'.format( num_batches * batch_size)) start_time = time.time() histogram = np.zeros(num_bins) for _ in range(num_batches): features, _ = session.run(next_batch) grams = features['spectrogram'] h, edges = np.histogram(grams, num_bins, (hist_min, hist_max)) histogram += h # If one of the histogram bins includes the log power to which # zero spectrogram values are mapped, zero that bin to ensure that # it doesn't interfere with computing a good minimum power value. if hist_min <= log_epsilon and log_epsilon <= hist_max: bin_num = int(math.floor((log_epsilon - hist_min) / bin_size)) # print('Zeroing histogram bin {}.'.format(bin_num)) histogram[bin_num] = 0 # Compute clipping powers. cumsum = histogram.cumsum() / histogram.sum() threshold = s.pretraining_clipped_values_fraction / 2 min_index = np.searchsorted(cumsum, threshold, side='right') max_index = np.searchsorted(cumsum, 1 - threshold) + 1 min_value = edges[min_index] max_value = edges[max_index] # print( # 'Batch {} of {}: ({}, {})'.format( # i + 1, num_batches, min_value, max_value)) elapsed_time = time.time() - start_time print('Computed spectrogram clipping range in {:.1f} seconds.'.format( elapsed_time)) # Plot spectrogram value distribution and clipping limits. if s.pretraining_value_distribution_plotting_enabled: distribution = histogram / histogram.sum() plt.figure(1) plt.plot(edges[:-1], distribution) plt.axvline(min_value, color='r') plt.axvline(max_value, color='r') plt.xlim((edges[0], edges[-1])) plt.title('Distribution of Spectrogram Values') plt.xlabel('Log Power') plt.show() # The float conversions in the following ensure that the assigned # type is Python's `float` instead of a NumPy type. The latter # doesn't play nicely with `yaml_utils.dump`. min_value = float(min_value) max_value = float(max_value) return min_value, max_value
def create_settings_from_yaml(self, s): settings = Settings.create_from_yaml(s) return Settings(self.defaults, settings)
def _assert_created_settings(self, settings): expected = Settings(x=1, y=3, z=4) self.assertEqual(settings, expected)
def _thrush_settings(threshold): return Settings(_THRUSH_SETTINGS, threshold=threshold / 100)
def _tseep_settings(threshold): return Settings(_TSEEP_SETTINGS, threshold=threshold / 100)
def _load_classifier_settings(self): s = self._settings path = classifier_utils.get_settings_file_path(s.clip_type) logging.info('Loading classifier settings from "{}"...'.format(path)) return Settings.create_from_yaml_file(path)
from vesper.util.sample_buffer import SampleBuffer from vesper.util.settings import Settings import vesper.mpg_ranch.nfc_coarse_classifier_3_0.classifier_utils \ as classifier_utils import vesper.mpg_ranch.nfc_coarse_classifier_3_0.dataset_utils \ as dataset_utils import vesper.util.open_mp_utils as open_mp_utils import vesper.util.signal_utils as signal_utils # TODO: Consider specifying threshold on a scale from 0 to 100 rather # than on a scale from 0 to 1, since that's how scores are presented # in the UI. _TSEEP_SETTINGS = Settings(clip_type='Tseep', input_chunk_size=3600, hop_size=50, threshold=.9, initial_clip_padding=.1, clip_duration=.4) _THRUSH_SETTINGS = Settings(clip_type='Thrush', input_chunk_size=3600, hop_size=50, threshold=.9, initial_clip_padding=.2, clip_duration=.6) # Constants controlling detection score output. The output is written to # a stereo audio file with detector audio input samples in one channel # and detection scores in the other. It is useful for detector debugging, # but should be disabled in production. _SCORE_OUTPUT_ENABLED = False
TSEEP_SETTINGS = Settings( clip_type='Tseep', class_count=dataset_utils.CLASS_COUNT, waveform_sample_rate=24000, # Call start time settings. During training, waveforms are sliced so # that call start times are uniformly distributed in the interval # from `waveform_slice_min_call_start_time` to # `waveform_slice_max_call_start_time`. Set the min and max start # times equal to make all calls start at a particular time. # waveform_slice_min_call_start_time=.200, # waveform_slice_max_call_start_time=.200, waveform_slice_min_call_start_time=.000, waveform_slice_max_call_start_time=.400, waveform_slice_duration=.600, # `True` if and only if the waveform amplitude scaling data # augmentation is enabled. This augmentation scales each waveform # randomly to distribute the waveform log RMS amplitudes uniformly # within a roughly 48 dB window. waveform_amplitude_scaling_data_augmentation_enabled=False, # spectrogram settings spectrogram_window_size=.005, spectrogram_hop_size=20, spectrogram_log_epsilon=1e-10, # spectrogram frequency axis slicing settings spectrogram_start_freq=4000, spectrogram_end_freq=10500, # The maximum spectrogram frequency shift for data augmentation, # in bins. Set this to zero to disable this augmentation. max_spectrogram_frequency_shift=0, spectrogram_background_normalization_percentile_rank=40, # training settings training_batch_size=128, training_epoch_step_count=12, # epoch size is batch size times step count training_epoch_count=100, model_save_period=5, # epochs dropout_rate=.3, # validation settings validation_batch_size=1, validation_step_count=1000, )
def create_settings_from_dict(self, d): settings = Settings.create_from_dict(d) return Settings(self.defaults, settings)
TSEEP_SETTINGS = Settings( clip_type='Tseep', bound_type='Start', waveform_sample_rate=24000, positive_example_probability=.5, positive_example_call_start_offset=.025, waveform_slice_duration=.080, # `True` if and only if the waveform amplitude scaling data # augmentation is enabled. This augmentation scales each waveform # randomly to distribute the waveform log RMS amplitudes uniformly # within a roughly 48 dB window. waveform_amplitude_scaling_data_augmentation_enabled=False, # spectrogram settings spectrogram_window_size=.005, spectrogram_hop_size=20, spectrogram_log_epsilon=1e-10, # spectrogram frequency axis slicing settings spectrogram_start_freq=4000, spectrogram_end_freq=10500, # The maximum spectrogram frequency shift for data augmentation, # in bins. Set this to zero to disable this augmentation. max_spectrogram_frequency_shift=2, spectrogram_background_normalization_percentile_rank=30, # training settings training_batch_size=128, training_epoch_step_count=100, # epoch size is batch size times step count training_epoch_count=30, model_save_period=5, # epochs dropout_rate=.25, # validation settings validation_batch_size=1, validation_step_count=1000, # evaluation plot settings max_evaluation_inlier_diff=20, # offsets for converting inference value to spectrogram index call_start_index_offset=23, call_end_index_offset=22, )
def create_settings_from_yaml_file(self, file_path): settings = Settings.create_from_yaml_file(file_path) return Settings(self.defaults, settings)
def _create_test_settings_type(self): defaults = Settings(x=1, y=2) return SettingsType('Bobo', defaults)
def _create_at_settings(threshold): return Settings(threshold_adaptation_enabled=True, threshold=threshold)
from pathlib import Path import os import sys from vesper.util.settings import Settings from vesper.util.settings_type import SettingsType import vesper.archive_paths as archive_paths # TODO: Provide an upgrade path from SQLite for PostgreSQL, then deprecate # SQLite, and then eliminate the `database.engine` setting. _DEFAULT_SETTINGS = Settings.create_from_yaml(''' database: engine: SQLite ''') _SETTINGS_TYPE = SettingsType('Archive Settings', _DEFAULT_SETTINGS) _SETTINGS_FILE_NAME = 'Archive Settings.yaml' def _create_settings(): archive_dir_path = Path(os.getcwd()) settings = _load_settings_file(archive_dir_path) archive_paths.initialize(archive_dir_path, settings) return settings
def test_create_from_dict(self): contents = os_utils.read_file(_SETTINGS_FILE_PATH) d = yaml_utils.load(contents) settings = Settings.create_from_dict(d) self._check_settings(settings)
in which the server starts. The archive settings are the composition of a set of default settings (hard-coded in this module) and settings (optionally) specified in the file "Archive Settings.yaml" in the archive directory. """ from pathlib import Path import os import sys from vesper.util.settings import Settings from vesper.util.settings_type import SettingsType import vesper.archive_paths as archive_paths _DEFAULT_SETTINGS = Settings.create_from_yaml(''' database: engine: SQLite ''') _SETTINGS_TYPE = SettingsType('Archive Settings', _DEFAULT_SETTINGS) _SETTINGS_FILE_NAME = 'Archive Settings.yaml' def _create_settings(): archive_dir_path = Path(os.getcwd()) settings = _load_settings_file(archive_dir_path) archive_paths.initialize(archive_dir_path, settings) return settings def _load_settings_file(archive_dir_path):
def test_create_from_empty_yaml(self): settings = Settings.create_from_yaml('') self._check_empty_settings(settings)
def load_training_settings(training_name): file_path = get_training_settings_file_path(training_name) logging.info(f'Loading annotator settings from "{file_path}"...') text = file_path.read_text() dict_ = yaml_utils.load(text) return Settings.create_from_dict(dict_)
def test_create_from_commented_out_yaml(self): settings = Settings.create_from_yaml('#') self._check_empty_settings(settings)