From 2d0d2a829077af778774199750f1ed14ff2e3e39 Mon Sep 17 00:00:00 2001 From: Jeffrey Hetherly Date: Wed, 3 Jan 2018 20:08:17 +0000 Subject: [PATCH 01/16] added sce + ratio mask model --- magnolia/python/models/__init__.py | 4 + .../models/dnndenoise/L41_regression_model.py | 1 + magnolia/python/models/dnndenoise/sce_mask.py | 339 ++++++++++++++++++ .../denoising/RatioMaskeSCE/__init__.py | 0 .../denoising/RatioMaskeSCE/training.py | 98 +++++ magnolia/python/utils/mix2file.py | 10 +- 6 files changed, 451 insertions(+), 1 deletion(-) create mode 100644 magnolia/python/models/dnndenoise/sce_mask.py create mode 100644 magnolia/python/training/denoising/RatioMaskeSCE/__init__.py create mode 100644 magnolia/python/training/denoising/RatioMaskeSCE/training.py diff --git a/magnolia/python/models/__init__.py b/magnolia/python/models/__init__.py index 9f10f20..52981fc 100644 --- a/magnolia/python/models/__init__.py +++ b/magnolia/python/models/__init__.py @@ -1,9 +1,13 @@ +from .dnnseparate.jflec import JFLEC from .dnndenoise.chimera import Chimera from .dnndenoise.L41_regression_model import L41RegressionModel +from .dnndenoise.sce_mask import RatioMaskSCE __all__ = [ + "JFLEC", "Chimera", "L41RegressionModel", + "RatioMaskSCE", ] def make_model(model_name, config): diff --git a/magnolia/python/models/dnndenoise/L41_regression_model.py b/magnolia/python/models/dnndenoise/L41_regression_model.py index 7a7ee89..5a647f1 100644 --- a/magnolia/python/models/dnndenoise/L41_regression_model.py +++ b/magnolia/python/models/dnndenoise/L41_regression_model.py @@ -244,6 +244,7 @@ def get_cost(self, X_in, y_in, sig_in, I_in): @staticmethod def scale_signal(S, amin=1e-5, log_base=10, ref_value=1.0): + # NOTE: should possibly min/max scale log_base = np.log(log_base) log_spec = 20.0 * tf.log(tf.maximum(amin, S)) / log_base log_spec -= 20.0 * tf.log(tf.maximum(amin, ref_value)) / log_base diff --git a/magnolia/python/models/dnndenoise/sce_mask.py b/magnolia/python/models/dnndenoise/sce_mask.py new file mode 100644 index 0000000..4f1fa40 --- /dev/null +++ b/magnolia/python/models/dnndenoise/sce_mask.py @@ -0,0 +1,339 @@ +import logging.config +import numpy as np +import tensorflow as tf + +from magnolia.models.model_base import ModelBase +from magnolia.utils import tf_utils + + +logger = logging.getLogger('model') + + +class RatioMaskSCE(ModelBase): + """ + Chimera network from [1] but uses the SCE loss from Lab41. + Defaults correspond to the parameters used by the best + performing model in the paper. + + [1] Luo, Yi., et al. "Deep Clustering and Conventional Networks for Music + Separation: Stronger Together" Published in Acoustics, Speech, and + Signal Processing (ICASSP) 2017; doi:10.1109/ICASSP.2017.7952118 + + Hyperparameters: + F: Number of frequency bins in the input data + num_reco_sources: Number sources to reconstruct + num_training_sources: Number sources in the training set + layer_size: Size of BLSTM layers + embedding_size: Dimension of embedding vector + alpha: Relative mixture of cost terms + nonlinearity: Nonlinearity to use in BLSTM layers + device: Which device to run the model on + """ + + def initialize(self): + self.F = self.config['model_params']['F'] + self.num_reco_sources = self.config['model_params']['num_reco_sources'] # should always be 2 + self.num_training_sources = self.config['model_params']['num_training_sources'] + self.layer_size = self.config['model_params']['layer_size'] + self.embedding_size = self.config['model_params']['embedding_size'] + self.normalize = self.config['model_params']['normalize'] + self.alpha = self.config['model_params']['alpha'] + self.nonlinearity = eval(self.config['model_params']['nonlinearity']) + self.collapse_sources = self.config['model_params']['collapse_sources'] + + self.batch_count = 0 + self.nbatches = [] + self.costs = [] + self.t_costs = [] + self.v_costs = [] + self.last_saved = 0 + + + def build_graph(self, graph): + with graph.as_default(): + with tf.device(self.config['device']): + # Placeholder tensor for the input data + self.X = tf.placeholder("float", [None, None, self.F]) + # Placeholder tensor for the unscaled input data + self.X_clean = tf.placeholder("float", [None, None, self.F]) + + # Placeholder tensor for the labels/targets + self.y = tf.placeholder("float", [None, None, self.F, None]) + # Placeholder tensor for the unscaled labels/targets + self.y_clean = tf.placeholder("float", [None, None, self.F, None]) + + # Placeholder for the speaker indicies + self.I = tf.placeholder(tf.int32, [None, None]) + + # Define the speaker vectors to use during training + self.speaker_vectors = tf_utils.weight_variable( + [self.num_training_sources, self.embedding_size], + tf.sqrt(2/self.embedding_size)) + + # Model methods + self.network + self.cost + self.optimizer + + return graph + + + def learn_from_epoch(self, epoch_id, + validate_every, + stop_threshold, + training_mixer, + validation_mixer, + batch_formatter, + model_save_base): + # FIXME: + # Find the number of batches already elapsed (Useful for resuming training) + start = 0 + if len(self.nbatches) != 0: + start = self.nbatches[-1] + + batch_count = self.batch_count + # Training epoch loop + for batch in iter(training_mixer): + unscaled_spectral_sum_batch, scaled_spectral_sum_batch, spectral_masks_batch, spectral_sources_batch = batch_formatter(batch[0], batch[1], batch[2]) + # should be dimensions of (batch size, source) + uids_batch = batch[3] + + # override ids for simply signal/noise + if self.collapse_sources: + uids_batch[:, 0] = 0 + uids_batch[:, 1] = 1 + + # Train the model on one batch and get the cost + c = self.train_on_batch(scaled_spectral_sum_batch, unscaled_spectral_sum_batch, + spectral_masks_batch, spectral_sources_batch, + uids_batch) + + # Store the training cost + self.costs.append(c) + + # Store the current batch_count number + + # Evaluate the model on the validation data + if (batch_count + 1) % validate_every == 0: + # Store the training cost + self.t_costs.append(np.mean(self.costs)) + # Reset the cost over the last 10 batches + self.costs = [] + + # Compute average validation score + all_c_v = [] + for vbatch in iter(validation_mixer): + unscaled_spectral_sum_batch, scaled_spectral_sum_batch, spectral_masks_batch, spectral_sources_batch = batch_formatter(vbatch[0], vbatch[1], vbatch[2]) + # dimensions of (batch size, source) + uids_batch = vbatch[3] + + # override ids for simply signal/noise + if self.collapse_sources: + uids_batch[:, 0] = 0 + uids_batch[:, 1] = 1 + + # Get the cost on the validation batch + c_v = self.get_cost(scaled_spectral_sum_batch, unscaled_spectral_sum_batch, + spectral_masks_batch, spectral_sources_batch, + uids_batch) + all_c_v.append(c_v) + + ave_c_v = np.mean(all_c_v) + + # Check if the validation cost is below the minimum validation cost, and if so, save it. + if len(self.v_costs) > 0 and ave_c_v < min(self.v_costs) and len(self.nbatches) > 0: + logger.info("Saving the model because validation score is {} below the old minimum.".format(min(self.v_costs) - ave_c_v)) + + # Save the model to the specified path + self.save(model_save_base) + + # Record the batch that the model was last saved on + self.last_saved = self.nbatches[-1] + + # Store the validation cost + self.v_costs.append(ave_c_v) + + # Store the current batch number + self.nbatches.append(batch_count + 1 + start) + + # Compute scale quantities for plotting + length = len(self.nbatches) + cutoff = int(0.5*length) + lowline = [min(self.v_costs)]*length + + logger.info("Training cost on batch {} is {}.".format(self.nbatches[-1], self.t_costs[-1])) + logger.info("Validation cost on batch {} is {}.".format(self.nbatches[-1], self.v_costs[-1])) + logger.info("Last saved {} batches ago.".format(self.nbatches[-1] - self.last_saved)) + + # Stop training if the number of iterations since the last save point exceeds the threshold + if self.nbatches[-1] - self.last_saved > stop_threshold: + logger.info("Early stopping criteria met!") + break + + batch_count += 1 + + self.batch_count = batch_count + + + def infer(self, **kw_args): + pass + + + @tf_utils.scope_decorator + def network(self): + """ + Construct the op for the network used in [1]. This consists of four + BLSTM layers followed by a dense layer giving a set of T-F vectors of + dimension embedding_size + """ + + # Get the shape of the input + shape = tf.shape(self.X) + + # BLSTM layer one + BLSTM_1 = tf_utils.BLSTM_(self.X, self.layer_size, 'one', + activation=self.nonlinearity) + + # BLSTM layer two + BLSTM_2 = tf_utils.BLSTM_(BLSTM_1, self.layer_size, 'two', + activation=self.nonlinearity) + + # BLSTM layer three + BLSTM_3 = tf_utils.BLSTM_(BLSTM_2, self.layer_size, 'three', + activation=self.nonlinearity) + + # BLSTM layer four + BLSTM_4 = tf_utils.BLSTM_(BLSTM_3, self.layer_size, 'four', + activation=self.nonlinearity) + + # Feedforward layer + feedforward = tf_utils.conv1d_layer(BLSTM_4, + [1, self.layer_size, self.embedding_size*self.F]) + + # Reshape the feedforward output to have shape (T,F,D) + z = tf.reshape(feedforward, + [shape[0], shape[1], self.F, self.embedding_size]) + + # SCE head + embedding = self.nonlinearity(z) + # Normalize the T-F vectors to get the network output + embedding = tf.nn.l2_normalize(embedding, 3) + + # MI head + # Feedforward layer + feedforward_fc = tf_utils.conv2d_layer(z, + [1, 1, self.embedding_size, self.num_reco_sources]) + # perform a softmax along the source dimension + mi_head = tf.nn.softmax(feedforward_fc, dim=3) + + return embedding, mi_head + + @tf_utils.scope_decorator + def cost(self): + """ + Constuct the cost function op for the cost function used in sce + and the mask inference head + """ + + # Get the shape of the input + shape = tf.shape(self.y) + + sce_output, mi_output = self.network + + # Reshape I so that it is of the correct dimension + I = tf.expand_dims( self.I, axis=2 ) + + # Normalize the speaker vectors and collect the speaker vectors + # correspinding to the speakers in batch + if self.normalize: + speaker_vectors = tf.nn.l2_normalize(self.speaker_vectors, 1) + else: + speaker_vectors = self.speaker_vectors + Vspeakers = tf.gather_nd(speaker_vectors, I) + + # Expand the dimensions in preparation for broadcasting + Vspeakers_broad = tf.expand_dims(Vspeakers, 1) + Vspeakers_broad = tf.expand_dims(Vspeakers_broad, 1) + embedding_broad = tf.expand_dims(sce_output, 3) + + # Compute the dot product between the emebedding vectors and speaker + # vectors + dot = tf.reduce_sum(Vspeakers_broad * embedding_broad, 4) + + # Compute the cost for every element + sce_cost = -tf.log(tf.nn.sigmoid(self.y * dot)) + + # Average the cost over all speakers in the input + sce_cost = tf.reduce_mean(sce_cost, 3) + + # Average the cost over all batches + sce_cost = tf.reduce_mean(sce_cost, 0) + + # Average the cost over all T-F elements. Here is where weighting to + # account for gradient confidence can occur + sce_cost = tf.reduce_mean(sce_cost) + + # broadcast product along source dimension + mi_cost = tf.square(self.y_clean - mi_output*tf.expand_dims(self.X_clean, -1)) + + return self.alpha*tf.reduce_mean(sce_cost) + (1.0 - self.alpha)*tf.reduce_mean(mi_cost) + + @tf_utils.scope_decorator + def optimizer(self): + """ + Constructs the optimizer op used to train the network + """ + opt = tf.train.AdamOptimizer() + return opt.minimize(self.cost) + + # def save(self, path): + # """ + # Saves the model to the specified path. + # """ + # self.saver.save(self.sess, path) + + # def load(self, path): + # """ + # Load the model from the specified path. + # """ + # self.saver.restore(self.sess, path) + + def train_on_batch(self, X_train, X_train_clean, y_train, y_train_clean, I_train): + """ + Train the model on a batch with input X and target y. Returns the cost + computed on this batch. + """ + + cost, _ = self.sess.run([self.cost, self.optimizer], + {self.X: X_train, self.y: y_train, + self.X_clean: X_train_clean, + self.y_clean: y_train_clean, + self.I: I_train}) + + return cost + + def get_masks(self, X_in): + """ + Compute the masks for the input spectrograms + """ + + masks = self.sess.run(self.network, {self.X: X_in})[1] + return masks + + def get_vectors(self, X_in): + """ + Compute the embedding vectors for the input spectrograms + """ + + vectors = self.sess.run(self.network, {self.X: X_in})[0] + return vectors + + def get_cost(self, X_in, X_clean_in, y_in, y_clean_in, I_in): + """ + Computes the cost of a batch, but does not update any model parameters. + """ + cost = self.sess.run(self.cost, {self.X: X_in, self.y: y_in, + self.X_clean: X_clean_in, + self.y_clean: y_clean_in, + self.I: I_in}) + return cost diff --git a/magnolia/python/training/denoising/RatioMaskeSCE/__init__.py b/magnolia/python/training/denoising/RatioMaskeSCE/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/magnolia/python/training/denoising/RatioMaskeSCE/training.py b/magnolia/python/training/denoising/RatioMaskeSCE/training.py new file mode 100644 index 0000000..784749d --- /dev/null +++ b/magnolia/python/training/denoising/RatioMaskeSCE/training.py @@ -0,0 +1,98 @@ +# Generic imports +import argparse +import logging.config +import json + +import numpy as np +import pandas as pd +import tensorflow as tf + +# Import the RatioMaskSCE separation model +from magnolia.models import make_model + +# Import utilities for using the model +from magnolia.training.data_iteration.mix_iterator import MixIterator +from magnolia.utils.training import preprocess_chimera_batch + + +def main(): + # parse command line arguments + parser = argparse.ArgumentParser(description='Train the SCE + ratio mask network.') + # parser.add_argument('--model_settings', '-s', + # default='../../../../data/models_settings/chimera_template.json', + # help='model settings JSON file') + # parser.add_argument('--_settings', '-s', + # default='../../../../data/models_settings/chimera_template.json', + # help='model settings JSON file') + parser.add_argument('--logger_settings', '-l', + default='../../../../data/logging_settings/logging.conf', + help='logging configuration file') + args = parser.parse_args() + + # Load logging configuration + logging.config.fileConfig(args.logger_settings) + logger = logging.getLogger('model') + + # Number of epochs + num_epochs = 2 + # Threshold for stopping if the model hasn't improved for this many consecutive batches + stop_threshold = 10000 + # validate every number of these batches + validate_every = 100 + train_batchsize = 256 + train_mixes = ['/local_data/magnolia/pipeline_data/date_2017_09_27_time_13_25/settings/mixing_LibriSpeech_UrbanSound8K_train.json'] + train_from_disk = False + validate_batchsize = 200 + validate_mixes = ['/local_data/magnolia/pipeline_data/date_2017_09_27_time_13_25/settings/mixing_LibriSpeech_UrbanSound8K_validate.json'] + validate_from_disk = False + model_params = { + 'layer_size': 500, + 'embedding_size': 10, + 'alpha': 0.1, + 'nonlinearity': 'tf.tanh', + 'num_reco_sources': 2, + 'normalize': False, + 'collapse_sources': False, + } + model_location = '/gpu:0' + uid_settings = '/local_data/magnolia/pipeline_data/date_2017_09_27_time_13_25/settings/assign_uids_LibriSpeech_UrbanSound8K.json' + model_save_base = '/local_data/magnolia/experiment_data/date_2017_09_28_time_13_14/aux/model_saves/mask_sce' + + + training_mixer = MixIterator(mixes_settings_filenames=train_mixes, + batch_size=train_batchsize, + read_waveform=False, + from_disk=train_from_disk) + + validation_mixer = MixIterator(mixes_settings_filenames=validate_mixes, + batch_size=validate_batchsize, + read_waveform=False, + from_disk=validate_from_disk) + + # get frequency dimension + frequency_dim = training_mixer.sample_dimensions()[0] + # TODO: throw an exception + assert(frequency_dim == validation_mixer.sample_dimensions()[0]) + + # get number of sources + settings = json.load(open(uid_settings)) + uid_file = settings['output_file'] + uid_csv = pd.read_csv(uid_file) + number_of_sources = uid_csv['uid'].max() + 1 + + model_params['F'] = frequency_dim + model_params['num_training_sources'] = number_of_sources + config = {'model_params': model_params, + 'device': model_location} + model = make_model('RatioMaskSCE', config) + + model.train(validate_every=validate_every, + stop_threshold=stop_threshold, + training_mixer=training_mixer, + validation_mixer=validation_mixer, + batch_formatter=preprocess_chimera_batch, + model_save_base=model_save_base) + + +if __name__ == '__main__': + main() diff --git a/magnolia/python/utils/mix2file.py b/magnolia/python/utils/mix2file.py index 69a3f09..62bce64 100644 --- a/magnolia/python/utils/mix2file.py +++ b/magnolia/python/utils/mix2file.py @@ -56,10 +56,12 @@ def main(): total_length = int(sample_length*sample_rate) for i in range(args.sample): - spec, bin_masks, source_specs, uids, snrs = next(mixer_iter) + spec, bin_masks, source_specs, wf, wf_sources, uids, snrs = next(mixer_iter) spec = spec[0] bin_masks = bin_masks[0] + wf = wf[0] + wf_sources = wf_sources[0] uids = uids[0] snrs = snrs[0] @@ -71,6 +73,9 @@ def main(): istft_args=istft_args) lr.output.write_wav(mix_file_name, y, sample_rate, norm=True) + mix_file_name = '{}_waveform_mix.wav'.format(os.path.splitext(args.output_file)[0]) + lr.output.write_wav(mix_file_name, wf, sample_rate, norm=True) + for i in range(bin_masks.shape[0]): source_file_name = '{}_{}.wav'.format(os.path.splitext(args.output_file)[0], uids[i]) source_spec = apply_binary_mask(bin_masks[i], spec) @@ -79,6 +84,9 @@ def main(): istft_args=istft_args) lr.output.write_wav(source_file_name, source_y, sample_rate, norm=True) + source_file_name = '{}_{}_waveform.wav'.format(os.path.splitext(args.output_file)[0], uids[i]) + lr.output.write_wav(source_file_name, wf_sources[i], sample_rate, norm=True) + if __name__ == '__main__': main() From fd69f5e04ee5ca0b2897468a7470d9e0901af7fe Mon Sep 17 00:00:00 2001 From: Jeffrey Hetherly Date: Wed, 10 Jan 2018 23:14:02 +0000 Subject: [PATCH 02/16] adding clustering lab41 model --- magnolia/python/models/dnndenoise/chimera.py | 27 +- .../python/models/dnndenoise/cluster_mask.py | 326 ++++++++++++++++++ magnolia/python/models/dnndenoise/sce_mask.py | 29 +- .../__init__.py | 0 .../training.py | 4 +- 5 files changed, 347 insertions(+), 39 deletions(-) create mode 100644 magnolia/python/models/dnndenoise/cluster_mask.py rename magnolia/python/training/denoising/{RatioMaskeSCE => RatioMaskSCE}/__init__.py (100%) rename magnolia/python/training/denoising/{RatioMaskeSCE => RatioMaskSCE}/training.py (98%) diff --git a/magnolia/python/models/dnndenoise/chimera.py b/magnolia/python/models/dnndenoise/chimera.py index 3345097..2fa6b4c 100644 --- a/magnolia/python/models/dnndenoise/chimera.py +++ b/magnolia/python/models/dnndenoise/chimera.py @@ -128,22 +128,17 @@ def learn_from_epoch(self, epoch_id, # Store the validation cost self.v_costs.append(ave_c_v) - # Store the current batch number - self.nbatches.append(batch_count + 1 + start) - - # Compute scale quantities for plotting - length = len(self.nbatches) - cutoff = int(0.5*length) - lowline = [min(self.v_costs)]*length - - logger.info("Training cost on batch {} is {}.".format(self.nbatches[-1], self.t_costs[-1])) - logger.info("Validation cost on batch {} is {}.".format(self.nbatches[-1], self.v_costs[-1])) - logger.info("Last saved {} batches ago.".format(self.nbatches[-1] - self.last_saved)) - - # Stop training if the number of iterations since the last save point exceeds the threshold - if self.nbatches[-1] - self.last_saved > stop_threshold: - logger.info("Early stopping criteria met!") - break + # Store the current batch number + self.nbatches.append(batch_count + start) + + logger.info("Training cost on batch {} is {}.".format(self.nbatches[-1], self.t_costs[-1])) + logger.info("Validation cost on batch {} is {}.".format(self.nbatches[-1], self.v_costs[-1])) + logger.info("Last saved {} batches ago.".format(self.nbatches[-1] - self.last_saved)) + + # Stop training if the number of iterations since the last save point exceeds the threshold + if self.nbatches[-1] - self.last_saved > stop_threshold: + logger.info("Early stopping criteria met!") + break batch_count += 1 diff --git a/magnolia/python/models/dnndenoise/cluster_mask.py b/magnolia/python/models/dnndenoise/cluster_mask.py new file mode 100644 index 0000000..ea33db2 --- /dev/null +++ b/magnolia/python/models/dnndenoise/cluster_mask.py @@ -0,0 +1,326 @@ +import logging.config +import numpy as np +import tensorflow as tf + +from magnolia.models.model_base import ModelBase +from magnolia.utils import tf_utils + + +logger = logging.getLogger('model') + + +class RatioMaskCluster(ModelBase): + """ + Chimera network from [1] but uses the soft C-means loss. + Defaults correspond to the parameters used by the best + performing model in the paper. + + [1] Luo, Yi., et al. "Deep Clustering and Conventional Networks for Music + Separation: Stronger Together" Published in Acoustics, Speech, and + Signal Processing (ICASSP) 2017; doi:10.1109/ICASSP.2017.7952118 + + Hyperparameters: + F: Number of frequency bins in the input data + num_reco_sources: Number sources to reconstruct + num_training_sources: Number sources in the training set + layer_size: Size of BLSTM layers + embedding_size: Dimension of embedding vector + alpha: Relative mixture of cost terms + nonlinearity: Nonlinearity to use in BLSTM layers + device: Which device to run the model on + """ + + def initialize(self): + self.F = self.config['model_params']['F'] + self.num_reco_sources = self.config['model_params']['num_reco_sources'] # should always be 2 + self.num_training_sources = self.config['model_params']['num_training_sources'] + self.layer_size = self.config['model_params']['layer_size'] + self.embedding_size = self.config['model_params']['embedding_size'] + self.normalize = self.config['model_params']['normalize'] + self.alpha = self.config['model_params']['alpha'] + self.nonlinearity = eval(self.config['model_params']['nonlinearity']) + self.collapse_sources = self.config['model_params']['collapse_sources'] + + self.batch_count = 0 + self.costs = [] + self.t_costs = [] + self.v_costs = [] + self.last_saved = 0 + + + def build_graph(self, graph): + with graph.as_default(): + with tf.device(self.config['device']): + # Placeholder tensor for the input data + self.X = tf.placeholder("float", [None, None, self.F]) + # Placeholder tensor for the unscaled input data + self.X_clean = tf.placeholder("float", [None, None, self.F]) + + # Placeholder tensor for the labels/targets + self.y = tf.placeholder("float", [None, None, self.F, None]) + # Placeholder tensor for the unscaled labels/targets + self.y_clean = tf.placeholder("float", [None, None, self.F, None]) + + # Placeholder for the speaker indicies + self.I = tf.placeholder(tf.int32, [None, None]) + + # Define the speaker vectors to use during training + self.speaker_vectors = tf_utils.weight_variable( + [self.num_training_sources, self.embedding_size], + tf.sqrt(2/self.embedding_size)) + + # Model methods + self.network + self.cost + self.optimizer + + return graph + + + def learn_from_epoch(self, epoch_id, + validate_every, + stop_threshold, + training_mixer, + validation_mixer, + batch_formatter, + model_save_base): + + batch_count = self.batch_count + # Training epoch loop + for batch in iter(training_mixer): + unscaled_spectral_sum_batch, scaled_spectral_sum_batch, spectral_masks_batch, spectral_sources_batch = batch_formatter(batch[0], batch[1], batch[2]) + # should be dimensions of (batch size, source) + uids_batch = batch[3] + + # override ids for simply signal/noise + if self.collapse_sources: + uids_batch[:, 0] = 0 + uids_batch[:, 1] = 1 + + # Train the model on one batch and get the cost + c = self.train_on_batch(scaled_spectral_sum_batch, unscaled_spectral_sum_batch, + spectral_masks_batch, spectral_sources_batch, + uids_batch) + + # Store the training cost + self.costs.append(c) + + # Evaluate the model on the validation data + if (batch_count + 1) % validate_every == 0: + # Store the training cost + self.t_costs.append(np.mean(self.costs)) + # Reset the cost over the last 10 batches + self.costs = [] + + # Compute average validation score + all_c_v = [] + for vbatch in iter(validation_mixer): + unscaled_spectral_sum_batch, scaled_spectral_sum_batch, spectral_masks_batch, spectral_sources_batch = batch_formatter(vbatch[0], vbatch[1], vbatch[2]) + # dimensions of (batch size, source) + uids_batch = vbatch[3] + + # override ids for simply signal/noise + if self.collapse_sources: + uids_batch[:, 0] = 0 + uids_batch[:, 1] = 1 + + # Get the cost on the validation batch + c_v = self.get_cost(scaled_spectral_sum_batch, unscaled_spectral_sum_batch, + spectral_masks_batch, spectral_sources_batch, + uids_batch) + all_c_v.append(c_v) + + ave_c_v = np.mean(all_c_v) + + # Check if the validation cost is below the minimum validation cost, and if so, save it. + if len(self.v_costs) > 0 and ave_c_v < min(self.v_costs):# and len(self.nbatches) > 0: + logger.info("Saving the model because validation score is {} below the old minimum.".format(min(self.v_costs) - ave_c_v)) + + # Save the model to the specified path + self.save(model_save_base) + + # Record the batch that the model was last saved on + self.last_saved = batch_count#self.nbatches[-1] + + # Store the validation cost + self.v_costs.append(ave_c_v) + + # Store the current batch number + #self.nbatches.append(batch_count) + + logger.info("Training cost on batch {} is {}.".format(batch_count, self.t_costs[-1])) + logger.info("Validation cost on batch {} is {}.".format(batch_count, self.v_costs[-1])) + logger.info("Last saved {} batches ago.".format(batch_count - self.last_saved)) + + # Stop training if the number of iterations since the last save point exceeds the threshold + if batch_count - self.last_saved > stop_threshold: + logger.info("Early stopping criteria met!") + break + + batch_count += 1 + + self.batch_count = batch_count + + + def infer(self, **kw_args): + pass + + + @tf_utils.scope_decorator + def network(self): + """ + Construct the op for the network used in [1]. This consists of four + BLSTM layers followed by a dense layer giving a set of T-F vectors of + dimension embedding_size + """ + + # Get the shape of the input + shape = tf.shape(self.X) + + # BLSTM layer one + BLSTM_1 = tf_utils.BLSTM_(self.X, self.layer_size, 'one', + activation=self.nonlinearity) + + # BLSTM layer two + BLSTM_2 = tf_utils.BLSTM_(BLSTM_1, self.layer_size, 'two', + activation=self.nonlinearity) + + # BLSTM layer three + BLSTM_3 = tf_utils.BLSTM_(BLSTM_2, self.layer_size, 'three', + activation=self.nonlinearity) + + # BLSTM layer four + BLSTM_4 = tf_utils.BLSTM_(BLSTM_3, self.layer_size, 'four', + activation=self.nonlinearity) + + # Feedforward layer + feedforward = tf_utils.conv1d_layer(BLSTM_4, + [1, self.layer_size, self.embedding_size*self.F]) + + # Reshape the feedforward output to have shape (T,F,D) + z = tf.reshape(feedforward, + [shape[0], shape[1], self.F, self.embedding_size]) + + # SCE head + embedding = self.nonlinearity(z) + # Normalize the T-F vectors to get the network output + embedding = tf.nn.l2_normalize(embedding, 3) + + # MI head + # Feedforward layer + feedforward_fc = tf_utils.conv2d_layer(z, + [1, 1, self.embedding_size, self.num_reco_sources]) + # perform a softmax along the source dimension + mi_head = tf.nn.softmax(feedforward_fc, dim=3) + + return embedding, mi_head + + @tf_utils.scope_decorator + def cost(self): + """ + Constuct the cost function op for the cost function used in sce + and the mask inference head + """ + + # Get the shape of the input + shape = tf.shape(self.y) + + sce_output, mi_output = self.network + + # Reshape I so that it is of the correct dimension + I = tf.expand_dims( self.I, axis=2 ) + + # Normalize the speaker vectors and collect the speaker vectors + # correspinding to the speakers in batch + if self.normalize: + speaker_vectors = tf.nn.l2_normalize(self.speaker_vectors, 1) + else: + speaker_vectors = self.speaker_vectors + Vspeakers = tf.gather_nd(speaker_vectors, I) + + # Expand the dimensions in preparation for broadcasting + Vspeakers_broad = tf.expand_dims(Vspeakers, 1) + Vspeakers_broad = tf.expand_dims(Vspeakers_broad, 1) + embedding_broad = tf.expand_dims(sce_output, 3) + + # Compute the dot product between the emebedding vectors and speaker + # vectors + dot = tf.reduce_sum(Vspeakers_broad * embedding_broad, 4) + + # Compute the cost for every element + sce_cost = -tf.log(tf.nn.sigmoid(self.y * dot)) + + # Average the cost over all speakers in the input + sce_cost = tf.reduce_mean(sce_cost, 3) + + # Average the cost over all batches + sce_cost = tf.reduce_mean(sce_cost, 0) + + # Average the cost over all T-F elements. Here is where weighting to + # account for gradient confidence can occur + sce_cost = tf.reduce_mean(sce_cost) + + # broadcast product along source dimension + mi_cost = tf.square(self.y_clean - mi_output*tf.expand_dims(self.X_clean, -1)) + + return self.alpha*sce_cost + (1.0 - self.alpha)*tf.reduce_mean(mi_cost) + + @tf_utils.scope_decorator + def optimizer(self): + """ + Constructs the optimizer op used to train the network + """ + opt = tf.train.AdamOptimizer() + return opt.minimize(self.cost) + + # def save(self, path): + # """ + # Saves the model to the specified path. + # """ + # self.saver.save(self.sess, path) + + # def load(self, path): + # """ + # Load the model from the specified path. + # """ + # self.saver.restore(self.sess, path) + + def train_on_batch(self, X_train, X_train_clean, y_train, y_train_clean, I_train): + """ + Train the model on a batch with input X and target y. Returns the cost + computed on this batch. + """ + + cost, _ = self.sess.run([self.cost, self.optimizer], + {self.X: X_train, self.y: y_train, + self.X_clean: X_train_clean, + self.y_clean: y_train_clean, + self.I: I_train}) + + return cost + + def get_masks(self, X_in): + """ + Compute the masks for the input spectrograms + """ + + masks = self.sess.run(self.network, {self.X: X_in})[1] + return masks + + def get_vectors(self, X_in): + """ + Compute the embedding vectors for the input spectrograms + """ + + vectors = self.sess.run(self.network, {self.X: X_in})[0] + return vectors + + def get_cost(self, X_in, X_clean_in, y_in, y_clean_in, I_in): + """ + Computes the cost of a batch, but does not update any model parameters. + """ + cost = self.sess.run(self.cost, {self.X: X_in, self.y: y_in, + self.X_clean: X_clean_in, + self.y_clean: y_clean_in, + self.I: I_in}) + return cost diff --git a/magnolia/python/models/dnndenoise/sce_mask.py b/magnolia/python/models/dnndenoise/sce_mask.py index 4f1fa40..b67fddc 100644 --- a/magnolia/python/models/dnndenoise/sce_mask.py +++ b/magnolia/python/models/dnndenoise/sce_mask.py @@ -42,7 +42,6 @@ def initialize(self): self.collapse_sources = self.config['model_params']['collapse_sources'] self.batch_count = 0 - self.nbatches = [] self.costs = [] self.t_costs = [] self.v_costs = [] @@ -85,11 +84,6 @@ def learn_from_epoch(self, epoch_id, validation_mixer, batch_formatter, model_save_base): - # FIXME: - # Find the number of batches already elapsed (Useful for resuming training) - start = 0 - if len(self.nbatches) != 0: - start = self.nbatches[-1] batch_count = self.batch_count # Training epoch loop @@ -111,8 +105,6 @@ def learn_from_epoch(self, epoch_id, # Store the training cost self.costs.append(c) - # Store the current batch_count number - # Evaluate the model on the validation data if (batch_count + 1) % validate_every == 0: # Store the training cost @@ -141,32 +133,27 @@ def learn_from_epoch(self, epoch_id, ave_c_v = np.mean(all_c_v) # Check if the validation cost is below the minimum validation cost, and if so, save it. - if len(self.v_costs) > 0 and ave_c_v < min(self.v_costs) and len(self.nbatches) > 0: + if len(self.v_costs) > 0 and ave_c_v < min(self.v_costs):# and len(self.nbatches) > 0: logger.info("Saving the model because validation score is {} below the old minimum.".format(min(self.v_costs) - ave_c_v)) # Save the model to the specified path self.save(model_save_base) # Record the batch that the model was last saved on - self.last_saved = self.nbatches[-1] + self.last_saved = batch_count#self.nbatches[-1] # Store the validation cost self.v_costs.append(ave_c_v) # Store the current batch number - self.nbatches.append(batch_count + 1 + start) - - # Compute scale quantities for plotting - length = len(self.nbatches) - cutoff = int(0.5*length) - lowline = [min(self.v_costs)]*length + #self.nbatches.append(batch_count) - logger.info("Training cost on batch {} is {}.".format(self.nbatches[-1], self.t_costs[-1])) - logger.info("Validation cost on batch {} is {}.".format(self.nbatches[-1], self.v_costs[-1])) - logger.info("Last saved {} batches ago.".format(self.nbatches[-1] - self.last_saved)) + logger.info("Training cost on batch {} is {}.".format(batch_count, self.t_costs[-1])) + logger.info("Validation cost on batch {} is {}.".format(batch_count, self.v_costs[-1])) + logger.info("Last saved {} batches ago.".format(batch_count - self.last_saved)) # Stop training if the number of iterations since the last save point exceeds the threshold - if self.nbatches[-1] - self.last_saved > stop_threshold: + if batch_count - self.last_saved > stop_threshold: logger.info("Early stopping criteria met!") break @@ -276,7 +263,7 @@ def cost(self): # broadcast product along source dimension mi_cost = tf.square(self.y_clean - mi_output*tf.expand_dims(self.X_clean, -1)) - return self.alpha*tf.reduce_mean(sce_cost) + (1.0 - self.alpha)*tf.reduce_mean(mi_cost) + return self.alpha*sce_cost + (1.0 - self.alpha)*tf.reduce_mean(mi_cost) @tf_utils.scope_decorator def optimizer(self): diff --git a/magnolia/python/training/denoising/RatioMaskeSCE/__init__.py b/magnolia/python/training/denoising/RatioMaskSCE/__init__.py similarity index 100% rename from magnolia/python/training/denoising/RatioMaskeSCE/__init__.py rename to magnolia/python/training/denoising/RatioMaskSCE/__init__.py diff --git a/magnolia/python/training/denoising/RatioMaskeSCE/training.py b/magnolia/python/training/denoising/RatioMaskSCE/training.py similarity index 98% rename from magnolia/python/training/denoising/RatioMaskeSCE/training.py rename to magnolia/python/training/denoising/RatioMaskSCE/training.py index 784749d..0a3c79c 100644 --- a/magnolia/python/training/denoising/RatioMaskeSCE/training.py +++ b/magnolia/python/training/denoising/RatioMaskSCE/training.py @@ -34,7 +34,7 @@ def main(): logger = logging.getLogger('model') # Number of epochs - num_epochs = 2 + num_epochs = 20 # try 20 # Threshold for stopping if the model hasn't improved for this many consecutive batches stop_threshold = 10000 # validate every number of these batches @@ -48,7 +48,7 @@ def main(): model_params = { 'layer_size': 500, 'embedding_size': 10, - 'alpha': 0.1, + 'alpha': 0.9, # try 0.9 'nonlinearity': 'tf.tanh', 'num_reco_sources': 2, 'normalize': False, From 7c903b461de1be261429af7fc18676e4f88b7092 Mon Sep 17 00:00:00 2001 From: Jeffrey Hetherly Date: Thu, 11 Jan 2018 22:43:45 +0000 Subject: [PATCH 03/16] added evaluation scripts for new models --- .../denoising/Chimera/separate_mix.py | 40 +++- .../Chimera/separate_sample_from_mix.py | 40 +++- .../denoising/RatioMaskeSCE/__init__.py | 0 .../denoising/RatioMaskeSCE/separate_mix.py | 194 ++++++++++++++++++ .../RatioMaskeSCE/separate_sample_from_mix.py | 170 +++++++++++++++ magnolia/python/models/__init__.py | 2 + magnolia/python/models/model_base.py | 18 +- magnolia/python/utils/clustering_utils.py | 4 +- magnolia/python/utils/postprocessing.py | 4 +- 9 files changed, 450 insertions(+), 22 deletions(-) create mode 100644 magnolia/python/inference/denoising/RatioMaskeSCE/__init__.py create mode 100644 magnolia/python/inference/denoising/RatioMaskeSCE/separate_mix.py create mode 100644 magnolia/python/inference/denoising/RatioMaskeSCE/separate_sample_from_mix.py diff --git a/magnolia/python/inference/denoising/Chimera/separate_mix.py b/magnolia/python/inference/denoising/Chimera/separate_mix.py index dcdbded..7699ee6 100644 --- a/magnolia/python/inference/denoising/Chimera/separate_mix.py +++ b/magnolia/python/inference/denoising/Chimera/separate_mix.py @@ -1,28 +1,54 @@ # Generic imports +# Generic imports import os +import argparse +import logging.config import json + import numpy as np import pandas as pd import librosa as lr import tqdm # Import the Chimera separation model -from magnolia.dnnseparate.chimera import Chimera +from magnolia.models import make_model # Import utilities for using the model from magnolia.utils.postprocessing import convert_preprocessing_parameters -from magnolia.features.preprocessing import undo_preprocessing -from magnolia.iterate.mix_iterator import MixIterator +from magnolia.preprocessing.preprocessing import undo_preprocessing +from magnolia.training.data_iteration.mix_iterator import MixIterator from magnolia.utils.clustering_utils import chimera_clustering_separate, chimera_mask + def standardize_waveform(y): return (y - y.mean())/y.std() def main(): + # parse command line arguments + parser = argparse.ArgumentParser(description='Denoise mixed samples using the Chimera network.') + # parser.add_argument('--model_settings', '-s', + # default='../../../../data/models_settings/chimera_template.json', + # help='model settings JSON file') + # parser.add_argument('--_settings', '-s', + # default='../../../../data/models_settings/chimera_template.json', + # help='model settings JSON file') + parser.add_argument('--logger_settings', '-l', + default='../../../../data/logging_settings/logging.conf', + help='logging configuration file') + args = parser.parse_args() + + # Load logging configuration + logging.config.fileConfig(args.logger_settings) + logger = logging.getLogger('model') + # from model settings model_params = { + 'layer_size': 500, + 'embedding_size': 10, + 'alpha': 0.1, + 'nonlinearity': 'tf.tanh', } uid_settings = '/local_data/magnolia/pipeline_data/date_2017_09_27_time_13_25/settings/assign_uids_LibriSpeech_UrbanSound8K.json' model_save_base = '/local_data/magnolia/experiment_data/date_2017_09_28_time_13_14/aux/model_saves/chimera' @@ -38,6 +64,7 @@ def main(): mixer = MixIterator(mixes_settings_filenames=mixes, batch_size=1, + read_waveform=False, from_disk=from_disk) # get frequency dimension @@ -49,9 +76,10 @@ def main(): uid_csv = pd.read_csv(uid_file) number_of_sources = uid_csv['uid'].max() + 1 - model = Chimera(**model_params, - F=frequency_dim, - device=model_location) + model_params['F'] = frequency_dim + config = {'model_params': model_params, + 'device': model_location} + model = make_model('Chimera', config) model.load(model_save_base) diff --git a/magnolia/python/inference/denoising/Chimera/separate_sample_from_mix.py b/magnolia/python/inference/denoising/Chimera/separate_sample_from_mix.py index 3f9d19c..0d36145 100644 --- a/magnolia/python/inference/denoising/Chimera/separate_sample_from_mix.py +++ b/magnolia/python/inference/denoising/Chimera/separate_sample_from_mix.py @@ -1,17 +1,20 @@ # Generic imports import os +import argparse +import logging.config import json + import numpy as np import pandas as pd import librosa as lr # Import the Chimera separation model -from magnolia.dnnseparate.chimera import Chimera +from magnolia.models import make_model # Import utilities for using the model from magnolia.utils.postprocessing import convert_preprocessing_parameters -from magnolia.features.preprocessing import undo_preprocessing -from magnolia.iterate.mix_iterator import MixIterator +from magnolia.preprocessing.preprocessing import undo_preprocessing +from magnolia.training.data_iteration.mix_iterator import MixIterator from magnolia.utils.clustering_utils import chimera_clustering_separate, chimera_mask @@ -20,8 +23,29 @@ def standardize_waveform(y): def main(): + # parse command line arguments + parser = argparse.ArgumentParser(description='Denoise mixed sample using the Chimera network.') + # parser.add_argument('--model_settings', '-s', + # default='../../../../data/models_settings/chimera_template.json', + # help='model settings JSON file') + # parser.add_argument('--_settings', '-s', + # default='../../../../data/models_settings/chimera_template.json', + # help='model settings JSON file') + parser.add_argument('--logger_settings', '-l', + default='../../../../data/logging_settings/logging.conf', + help='logging configuration file') + args = parser.parse_args() + + # Load logging configuration + logging.config.fileConfig(args.logger_settings) + logger = logging.getLogger('model') + # from model settings model_params = { + 'layer_size': 500, + 'embedding_size': 10, + 'alpha': 0.1, + 'nonlinearity': 'tf.tanh', } uid_settings = '/local_data/magnolia/pipeline_data/date_2017_09_27_time_13_25/settings/assign_uids_LibriSpeech_UrbanSound8K.json' model_save_base = '/local_data/magnolia/experiment_data/date_2017_09_28_time_13_14/aux/model_saves/chimera' @@ -30,7 +54,7 @@ def main(): model_settings = '' mixes = ['/local_data/magnolia/pipeline_data/date_2017_09_27_time_13_25/settings/mixing_LibriSpeech_UrbanSound8K_test_in_sample.json'] from_disk = True - mix_number = 1 + mix_number = 1010 output_path = '/local_data/magnolia/experiment_data/date_2017_09_28_time_13_14/aux/sample_wav_files/chimera' @@ -38,6 +62,7 @@ def main(): mixer = MixIterator(mixes_settings_filenames=mixes, batch_size=1, + read_waveform=False, from_disk=from_disk) # get frequency dimension @@ -49,9 +74,10 @@ def main(): uid_csv = pd.read_csv(uid_file) number_of_sources = uid_csv['uid'].max() + 1 - model = Chimera(**model_params, - F=frequency_dim, - device=model_location) + model_params['F'] = frequency_dim + config = {'model_params': model_params, + 'device': model_location} + model = make_model('Chimera', config) model.load(model_save_base) diff --git a/magnolia/python/inference/denoising/RatioMaskeSCE/__init__.py b/magnolia/python/inference/denoising/RatioMaskeSCE/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/magnolia/python/inference/denoising/RatioMaskeSCE/separate_mix.py b/magnolia/python/inference/denoising/RatioMaskeSCE/separate_mix.py new file mode 100644 index 0000000..e4e505c --- /dev/null +++ b/magnolia/python/inference/denoising/RatioMaskeSCE/separate_mix.py @@ -0,0 +1,194 @@ +# Generic imports +# Generic imports +import os +import argparse +import logging.config +import json + +import numpy as np +import pandas as pd +import librosa as lr +import tqdm + +# Import the RatioMaskSCE separation model +from magnolia.models import make_model + +# Import utilities for using the model +from magnolia.utils.postprocessing import convert_preprocessing_parameters +from magnolia.preprocessing.preprocessing import undo_preprocessing +from magnolia.training.data_iteration.mix_iterator import MixIterator +from magnolia.utils.clustering_utils import chimera_clustering_separate, chimera_mask + + + +def standardize_waveform(y): + return (y - y.mean())/y.std() + + +def main(): + # parse command line arguments + parser = argparse.ArgumentParser(description='Denoise mixed samples using the RatioMaskSCE network.') + # parser.add_argument('--model_settings', '-s', + # default='../../../../data/models_settings/chimera_template.json', + # help='model settings JSON file') + # parser.add_argument('--_settings', '-s', + # default='../../../../data/models_settings/chimera_template.json', + # help='model settings JSON file') + parser.add_argument('--logger_settings', '-l', + default='../../../../data/logging_settings/logging.conf', + help='logging configuration file') + args = parser.parse_args() + + # Load logging configuration + logging.config.fileConfig(args.logger_settings) + logger = logging.getLogger('model') + + # from model settings + model_params = { + 'layer_size': 500, + 'embedding_size': 10, + 'alpha': 0.9, # try 0.9 + 'nonlinearity': 'tf.tanh', + 'num_reco_sources': 2, + 'normalize': False, + 'collapse_sources': False, + } + uid_settings = '/local_data/magnolia/pipeline_data/date_2017_09_27_time_13_25/settings/assign_uids_LibriSpeech_UrbanSound8K.json' + model_save_base = '/local_data/magnolia/experiment_data/date_2017_09_28_time_13_14/aux/model_saves/mask_sce' + + model_location = '/cpu:0' + model_settings = '' + # mixes = ['/local_data/magnolia/pipeline_data/date_2017_09_27_time_13_25/settings/mixing_LibriSpeech_UrbanSound8K_test_in_sample.json'] + mixes = ['/local_data/magnolia/pipeline_data/date_2017_09_27_time_13_25/settings/mixing_LibriSpeech_UrbanSound8K_test_out_of_sample.json'] + from_disk = True + # output_path = '/local_data/magnolia/experiment_data/date_2017_09_28_time_13_14/aux/evaluations/mask_sce/in_sample_test' + output_path = '/local_data/magnolia/experiment_data/date_2017_09_28_time_13_14/aux/evaluations/mask_sce/out_of_sample_test' + eval_sr = 8000 + + mixer = MixIterator(mixes_settings_filenames=mixes, + batch_size=1, + read_waveform=False, + from_disk=from_disk) + + # get frequency dimension + frequency_dim = mixer.sample_dimensions()[0] + + # get number of sources + settings = json.load(open(uid_settings)) + uid_file = settings['output_file'] + uid_csv = pd.read_csv(uid_file) + number_of_sources = uid_csv['uid'].max() + 1 + + model_params['F'] = frequency_dim + model_params['num_training_sources'] = number_of_sources + config = {'model_params': model_params, + 'device': model_location} + model = make_model('RatioMaskSCE', config) + + model.load(model_save_base) + + mix_settings = json.load(open(mixes[0])) + + signal = mix_settings['signals'][0] + preprocessing_settings = json.load(open(signal['preprocessing_settings'])) + stft_args = preprocessing_settings['processing_parameters']['stft_args'] + istft_args = convert_preprocessing_parameters(stft_args) + preemphasis_coeff = preprocessing_settings['processing_parameters']['preemphasis_coeff'] + n_fft = 2048 + if 'n_fft' in stft_args: + n_fft = stft_args['n_fft'] + + + os.makedirs(output_path, exist_ok=True) + mix_count = 0 + for _ in tqdm.trange(mixer.epoch_size()): + spec, bin_masks, source_specs, uids, snrs = next(mixer) + model_spec = spec + spec = spec[0] + bin_masks = bin_masks[0] + source_specs = source_specs[0] + uids = uids[0] + snrs = snrs[0] + + # print('SNR of mix {}: {}'.format(mix_count + 1, snrs)) + + y_mix = undo_preprocessing(spec, mixer.sample_length_in_bits(), + preemphasis_coeff=preemphasis_coeff, + istft_args=istft_args) + + + # NOTE: this is only to make comparisons to the reconstructed waveforms later + y_mix[-n_fft:] = 0.0 + y_mix = lr.core.resample(y_mix, mixer.sample_rate(), eval_sr, scale=True) + y_mix = standardize_waveform(y_mix) + + filename = os.path.join(output_path, 'mix_{}_snr_{:.2f}.wav'.format(mix_count + 1, snrs)) + lr.output.write_wav(filename, y_mix, eval_sr, norm=True) + + originals = {} + for i, source_spec in enumerate(source_specs): + y = undo_preprocessing(source_spec, mixer.sample_length_in_bits(), + preemphasis_coeff=preemphasis_coeff, + istft_args=istft_args) + # NOTE: this is only to make comparisons to the reconstructed waveforms later + y[-n_fft:] = 0.0 + y = lr.core.resample(y, mixer.sample_rate(), eval_sr, scale=True) + y = standardize_waveform(y) + + originals[i] = y + + # use dc-head of model + clustering to source-separate the spectrogram + source_specs = chimera_clustering_separate(model_spec, model, mixer.number_of_samples_in_mixes()) + + for i, source_spec in enumerate(source_specs): + y = undo_preprocessing(source_spec, mixer.sample_length_in_bits(), + preemphasis_coeff=preemphasis_coeff, + istft_args=istft_args) + # NOTE: this is only because the masking creates a chirp in the last + # fft frame (likely due to the binary mask) + y[-n_fft:] = 0.0 + y = lr.core.resample(y, mixer.sample_rate(), eval_sr, scale=True) + y = standardize_waveform(y) + + # match this waveform with an original source waveform + min_key = 0 + min_mse = np.inf + for key in originals: + mse = np.mean((y - originals[key])**2) + if mse < min_mse: + min_key = key + min_mse = mse + + # print('Separated sample for source {}'.format(i + 1)) + filename = os.path.join(output_path, 'mix_{}_original_source_{}.wav'.format(mix_count + 1, min_key + 1)) + lr.output.write_wav(filename, originals[min_key], eval_sr, norm=True) + filename = os.path.join(output_path, 'mix_{}_dc_separated_source_{}.wav'.format(mix_count + 1, min_key + 1)) + lr.output.write_wav(filename, y, eval_sr, norm=True) + + y_original = originals.pop(min_key, None) + if y_original is None: + print("something went horribly wrong") + + # use mi-head of model to source-separate the spectrogram + source_specs = chimera_mask(model_spec, model)[0] + + for i in range(source_specs.shape[2]): + source_spec = source_specs[:, :, i] + + y = undo_preprocessing(source_spec, mixer.sample_length_in_bits(), + preemphasis_coeff=preemphasis_coeff, + istft_args=istft_args) + # NOTE: this is only because the masking creates a chirp in the last + # fft frame (likely due to the binary mask) + y[-n_fft:] = 0.0 + y = lr.core.resample(y, mixer.sample_rate(), eval_sr, scale=True) + y = standardize_waveform(y) + + filename = os.path.join(output_path, 'mix_{}_mi_separated_source_{}.wav'.format(mix_count + 1, i + 1)) + lr.output.write_wav(filename, y, eval_sr, norm=True) + + mix_count += 1 + + +if __name__ == '__main__': + main() diff --git a/magnolia/python/inference/denoising/RatioMaskeSCE/separate_sample_from_mix.py b/magnolia/python/inference/denoising/RatioMaskeSCE/separate_sample_from_mix.py new file mode 100644 index 0000000..bdec6f1 --- /dev/null +++ b/magnolia/python/inference/denoising/RatioMaskeSCE/separate_sample_from_mix.py @@ -0,0 +1,170 @@ +# Generic imports +import os +import argparse +import logging.config +import json + +import numpy as np +import pandas as pd +import librosa as lr + +# Import the RatioMaskSCE separation model +from magnolia.models import make_model + +# Import utilities for using the model +from magnolia.utils.postprocessing import convert_preprocessing_parameters +from magnolia.preprocessing.preprocessing import undo_preprocessing +from magnolia.training.data_iteration.mix_iterator import MixIterator +from magnolia.utils.clustering_utils import chimera_clustering_separate, chimera_mask + + +def standardize_waveform(y): + return (y - y.mean())/y.std() + + +def main(): + # parse command line arguments + parser = argparse.ArgumentParser(description='Denoise mixed sample using the RatioMaskSCE network.') + # parser.add_argument('--model_settings', '-s', + # default='../../../../data/models_settings/chimera_template.json', + # help='model settings JSON file') + # parser.add_argument('--_settings', '-s', + # default='../../../../data/models_settings/chimera_template.json', + # help='model settings JSON file') + parser.add_argument('--logger_settings', '-l', + default='../../../../data/logging_settings/logging.conf', + help='logging configuration file') + args = parser.parse_args() + + # Load logging configuration + logging.config.fileConfig(args.logger_settings) + logger = logging.getLogger('model') + + # from model settings + model_params = { + 'layer_size': 500, + 'embedding_size': 10, + 'alpha': 0.9, # try 0.9 + 'nonlinearity': 'tf.tanh', + 'num_reco_sources': 2, + 'normalize': False, + 'collapse_sources': False, + } + uid_settings = '/local_data/magnolia/pipeline_data/date_2017_09_27_time_13_25/settings/assign_uids_LibriSpeech_UrbanSound8K.json' + model_save_base = '/local_data/magnolia/experiment_data/date_2017_09_28_time_13_14/aux/model_saves/mask_sce' + + model_location = '/cpu:0' + model_settings = '' + mixes = ['/local_data/magnolia/pipeline_data/date_2017_09_27_time_13_25/settings/mixing_LibriSpeech_UrbanSound8K_test_in_sample.json'] + from_disk = True + mix_number = 1010 + output_path = '/local_data/magnolia/experiment_data/date_2017_09_28_time_13_14/aux/sample_wav_files/mask_sce' + + + os.makedirs(output_path, exist_ok=True) + + mixer = MixIterator(mixes_settings_filenames=mixes, + batch_size=1, + read_waveform=False, + from_disk=from_disk) + + # get frequency dimension + frequency_dim = mixer.sample_dimensions()[0] + + # get number of sources + settings = json.load(open(uid_settings)) + uid_file = settings['output_file'] + uid_csv = pd.read_csv(uid_file) + number_of_sources = uid_csv['uid'].max() + 1 + + model_params['F'] = frequency_dim + model_params['num_training_sources'] = number_of_sources + config = {'model_params': model_params, + 'device': model_location} + model = make_model('RatioMaskSCE', config) + + model.load(model_save_base) + + assert(mix_number <= mixer.epoch_size()) + + mix_settings = json.load(open(mixes[0])) + + signal = mix_settings['signals'][0] + preprocessing_settings = json.load(open(signal['preprocessing_settings'])) + stft_args = preprocessing_settings['processing_parameters']['stft_args'] + istft_args = convert_preprocessing_parameters(stft_args) + preemphasis_coeff = preprocessing_settings['processing_parameters']['preemphasis_coeff'] + n_fft = 2048 + if 'n_fft' in stft_args: + n_fft = stft_args['n_fft'] + + + for i in range(mix_number): + spec, bin_masks, source_specs, uids, snrs = next(mixer) + + model_spec = spec + spec = spec[0] + bin_masks = bin_masks[0] + source_specs = source_specs[0] + uids = uids[0] + snrs = snrs[0] + + print('SNR of this mix: {}'.format(snrs)) + + y_mix = undo_preprocessing(spec, mixer.sample_length_in_bits(), + preemphasis_coeff=preemphasis_coeff, + istft_args=istft_args) + + # NOTE: this is only to make comparisons to the reconstructed waveforms later + y_mix[-n_fft:] = 0.0 + y_mix = standardize_waveform(y_mix) + + # print('Mixed sample') + lr.output.write_wav(os.path.join(output_path, 'mix_{}.wav'.format(mix_number)), y_mix, mixer.sample_rate(), norm=True) + + for i, source_spec in enumerate(source_specs): + y = undo_preprocessing(source_spec, mixer.sample_length_in_bits(), + preemphasis_coeff=preemphasis_coeff, + istft_args=istft_args) + + # NOTE: this is only to make comparisons to the reconstructed waveforms later + y[-n_fft:] = 0.0 + y = standardize_waveform(y) + + # print('Sample for source {}'.format(i + 1)) + lr.output.write_wav(os.path.join(output_path, 'mix_{}_original_source_{}.wav'.format(mix_number, i + 1)), y, mixer.sample_rate(), norm=True) + + source_specs = chimera_clustering_separate(model_spec, model, mixer.number_of_samples_in_mixes()) + + for i, source_spec in enumerate(source_specs): + y = undo_preprocessing(source_spec, mixer.sample_length_in_bits(), + preemphasis_coeff=preemphasis_coeff, + istft_args=istft_args) + + # NOTE: this is only because the masking creates a chirp in the last + # fft frame (likely due to the mask) + y[-n_fft:] = 0.0 + y = standardize_waveform(y) + + # print('Separated sample for source {}'.format(i + 1)) + lr.output.write_wav(os.path.join(output_path, 'mix_{}_dc_separated_{}.wav'.format(mix_number, i + 1)), y, mixer.sample_rate(), norm=True) + + source_specs = chimera_mask(model_spec, model)[0] + + for i in range(source_specs.shape[2]): + source_spec = source_specs[:, :, i] + y = undo_preprocessing(source_spec, mixer.sample_length_in_bits(), + preemphasis_coeff=preemphasis_coeff, + istft_args=istft_args) + + # NOTE: this is only because the masking creates a chirp in the last + # fft frame (likely due to the mask) + y[-n_fft:] = 0.0 + y = standardize_waveform(y) + + # print('Separated sample for source {}'.format(i + 1)) + lr.output.write_wav(os.path.join(output_path, 'mix_{}_mi_separated_{}.wav'.format(mix_number, i + 1)), y, mixer.sample_rate(), norm=True) + + +if __name__ == '__main__': + main() diff --git a/magnolia/python/models/__init__.py b/magnolia/python/models/__init__.py index 52981fc..0a65933 100644 --- a/magnolia/python/models/__init__.py +++ b/magnolia/python/models/__init__.py @@ -2,12 +2,14 @@ from .dnndenoise.chimera import Chimera from .dnndenoise.L41_regression_model import L41RegressionModel from .dnndenoise.sce_mask import RatioMaskSCE +from .dnndenoise.cluster_mask import RatioMaskCluster __all__ = [ "JFLEC", "Chimera", "L41RegressionModel", "RatioMaskSCE", + "RatioMaskCluster", ] def make_model(model_name, config): diff --git a/magnolia/python/models/model_base.py b/magnolia/python/models/model_base.py index 29e3601..7c45e9b 100644 --- a/magnolia/python/models/model_base.py +++ b/magnolia/python/models/model_base.py @@ -140,11 +140,19 @@ def save(self, save_path): self.saver.save(self.sess, save_path) - def load(self, checkpoint_dir): - checkpoint = tf.train.get_checkpoint_state(checkpoint_dir) - if self.debug_flag: - logger.debug('Loading the model from folder: {}'.format(checkpoint_dir)) - self.saver.restore(self.sess, checkpoint.model_checkpoint_path) + def load(self, path): + self.saver.restore(self.sess, path) + # TODO: fix this + #model_name = checkpoint_dir.split(os.sep)[-1] + #dir_name = os.path.join(checkpoint_dir.split(os.sep)[:-1]) + #checkpoint = tf.train.get_checkpoint_state(dir_name, latest_filename=model_name) + #if checkpoint is None: + # raise RuntimeError("Couldn't find checkpoint files at {}".format(checkpoint_dir)) + #path = checkpoint.model_checkpoint_path + #step = int(path.split(os.sep)[-1].split('-')[-1]) + #if self.debug_flag: + # logger.debug('Loading the model (step {}) from folder: {}'.format(step, checkpoint_dir)) + #self.saver.restore(self.sess, path) ########################################################### diff --git a/magnolia/python/utils/clustering_utils.py b/magnolia/python/utils/clustering_utils.py index 8a2512e..5559480 100644 --- a/magnolia/python/utils/clustering_utils.py +++ b/magnolia/python/utils/clustering_utils.py @@ -9,8 +9,8 @@ from sklearn.mixture import BayesianGaussianMixture, GaussianMixture from sklearn.decomposition import PCA -from ..features.spectral_features import istft -from ..features.preprocessing import make_stft_features, \ +from magnolia.preprocessing.spectral_features import istft +from magnolia.preprocessing.preprocessing import make_stft_features, \ undo_preemphasis from magnolia.utils.training import preprocess_l41_batch, preprocess_chimera_batch, preprocess_l41_regression_batch diff --git a/magnolia/python/utils/postprocessing.py b/magnolia/python/utils/postprocessing.py index 3b9367e..99e68a9 100644 --- a/magnolia/python/utils/postprocessing.py +++ b/magnolia/python/utils/postprocessing.py @@ -1,6 +1,6 @@ import numpy as np -from ..features.preprocessing import undo_preemphasis -from ..features.spectral_features import istft +from magnolia.preprocessing.preprocessing import undo_preemphasis +from magnolia.preprocessing.spectral_features import istft def convert_preprocessing_parameters(params): From d24f9099944bc288a02d46ff19adcf6f6765e53a Mon Sep 17 00:00:00 2001 From: Jeffrey Hetherly Date: Fri, 12 Jan 2018 00:26:39 +0000 Subject: [PATCH 04/16] added missing directories --- magnolia/python/analysis/bss_evaluate.py | 54 ++- .../__init__.py | 0 .../separate_mix.py | 8 +- .../separate_sample_from_mix.py | 0 magnolia/python/models/dnnseparate/jflec.py | 351 ++++++++++++++++++ .../training/denoising/JFLEC/__init__.py | 0 .../training/denoising/JFLEC/training.py | 94 +++++ .../denoising/RatioMaskCluster/__init__.py | 0 .../denoising/RatioMaskCluster/training.py | 98 +++++ 9 files changed, 581 insertions(+), 24 deletions(-) rename magnolia/python/inference/denoising/{RatioMaskeSCE => RatioMaskSCE}/__init__.py (100%) rename magnolia/python/inference/denoising/{RatioMaskeSCE => RatioMaskSCE}/separate_mix.py (96%) rename magnolia/python/inference/denoising/{RatioMaskeSCE => RatioMaskSCE}/separate_sample_from_mix.py (100%) create mode 100644 magnolia/python/models/dnnseparate/jflec.py create mode 100644 magnolia/python/training/denoising/JFLEC/__init__.py create mode 100644 magnolia/python/training/denoising/JFLEC/training.py create mode 100644 magnolia/python/training/denoising/RatioMaskCluster/__init__.py create mode 100644 magnolia/python/training/denoising/RatioMaskCluster/training.py diff --git a/magnolia/python/analysis/bss_evaluate.py b/magnolia/python/analysis/bss_evaluate.py index 3bb3925..f782545 100644 --- a/magnolia/python/analysis/bss_evaluate.py +++ b/magnolia/python/analysis/bss_evaluate.py @@ -103,30 +103,44 @@ def evaluate(input_path, output_csv_file, target_stype=None, eval_sr=8000, num_s if __name__ == '__main__': args = [ - ['/local_data/magnolia/experiment_data/date_2017_09_28_time_13_14/aux/evaluations/lab41/in_sample_test', - '/local_data/magnolia/experiment_data/date_2017_09_28_time_13_14/aux/evaluations/bss/lab41/in_sample_test.csv'], - ['/local_data/magnolia/experiment_data/date_2017_09_28_time_13_14/aux/evaluations/lab41/out_of_sample_test', - '/local_data/magnolia/experiment_data/date_2017_09_28_time_13_14/aux/evaluations/bss/lab41/out_of_sample_test.csv'], - ['/local_data/magnolia/experiment_data/date_2017_09_28_time_13_14/aux/evaluations/large_lab41/in_sample_test', - '/local_data/magnolia/experiment_data/date_2017_09_28_time_13_14/aux/evaluations/bss/large_lab41/in_sample_test.csv'], - ['/local_data/magnolia/experiment_data/date_2017_09_28_time_13_14/aux/evaluations/large_lab41/out_of_sample_test', - '/local_data/magnolia/experiment_data/date_2017_09_28_time_13_14/aux/evaluations/bss/large_lab41/out_of_sample_test.csv'], - ['/local_data/magnolia/experiment_data/date_2017_09_28_time_13_14/aux/evaluations/chimera/in_sample_test', - '/local_data/magnolia/experiment_data/date_2017_09_28_time_13_14/aux/evaluations/bss/chimera/mi_in_sample_test.csv', + #['/local_data/magnolia/experiment_data/date_2017_09_28_time_13_14/aux/evaluations/lab41/in_sample_test', + # '/local_data/magnolia/experiment_data/date_2017_09_28_time_13_14/aux/evaluations/bss/lab41/in_sample_test.csv'], + #['/local_data/magnolia/experiment_data/date_2017_09_28_time_13_14/aux/evaluations/lab41/out_of_sample_test', + # '/local_data/magnolia/experiment_data/date_2017_09_28_time_13_14/aux/evaluations/bss/lab41/out_of_sample_test.csv'], + #['/local_data/magnolia/experiment_data/date_2017_09_28_time_13_14/aux/evaluations/large_lab41/in_sample_test', + # '/local_data/magnolia/experiment_data/date_2017_09_28_time_13_14/aux/evaluations/bss/large_lab41/in_sample_test.csv'], + #['/local_data/magnolia/experiment_data/date_2017_09_28_time_13_14/aux/evaluations/large_lab41/out_of_sample_test', + # '/local_data/magnolia/experiment_data/date_2017_09_28_time_13_14/aux/evaluations/bss/large_lab41/out_of_sample_test.csv'], + #['/local_data/magnolia/experiment_data/date_2017_09_28_time_13_14/aux/evaluations/chimera/in_sample_test', + # '/local_data/magnolia/experiment_data/date_2017_09_28_time_13_14/aux/evaluations/bss/chimera/mi_in_sample_test.csv', + # 'mi'], + #['/local_data/magnolia/experiment_data/date_2017_09_28_time_13_14/aux/evaluations/chimera/out_of_sample_test', + # '/local_data/magnolia/experiment_data/date_2017_09_28_time_13_14/aux/evaluations/bss/chimera/mi_out_of_sample_test.csv', + # 'mi'], + #['/local_data/magnolia/experiment_data/date_2017_09_28_time_13_14/aux/evaluations/chimera/in_sample_test', + # '/local_data/magnolia/experiment_data/date_2017_09_28_time_13_14/aux/evaluations/bss/chimera/dc_in_sample_test.csv', + # 'dc'], + #['/local_data/magnolia/experiment_data/date_2017_09_28_time_13_14/aux/evaluations/chimera/out_of_sample_test', + # '/local_data/magnolia/experiment_data/date_2017_09_28_time_13_14/aux/evaluations/bss/chimera/dc_out_of_sample_test.csv', + # 'dc'], + + ['/local_data/magnolia/experiment_data/date_2017_09_28_time_13_14/aux/evaluations/mask_sce/in_sample_test', + '/local_data/magnolia/experiment_data/date_2017_09_28_time_13_14/aux/evaluations/bss/mask_sce/mi_in_sample_test.csv', 'mi'], - ['/local_data/magnolia/experiment_data/date_2017_09_28_time_13_14/aux/evaluations/chimera/out_of_sample_test', - '/local_data/magnolia/experiment_data/date_2017_09_28_time_13_14/aux/evaluations/bss/chimera/mi_out_of_sample_test.csv', + ['/local_data/magnolia/experiment_data/date_2017_09_28_time_13_14/aux/evaluations/mask_sce/out_of_sample_test', + '/local_data/magnolia/experiment_data/date_2017_09_28_time_13_14/aux/evaluations/bss/mask_sce/mi_out_of_sample_test.csv', 'mi'], - ['/local_data/magnolia/experiment_data/date_2017_09_28_time_13_14/aux/evaluations/chimera/in_sample_test', - '/local_data/magnolia/experiment_data/date_2017_09_28_time_13_14/aux/evaluations/bss/chimera/dc_in_sample_test.csv', + ['/local_data/magnolia/experiment_data/date_2017_09_28_time_13_14/aux/evaluations/mask_sce/in_sample_test', + '/local_data/magnolia/experiment_data/date_2017_09_28_time_13_14/aux/evaluations/bss/mask_sce/dc_in_sample_test.csv', 'dc'], - ['/local_data/magnolia/experiment_data/date_2017_09_28_time_13_14/aux/evaluations/chimera/out_of_sample_test', - '/local_data/magnolia/experiment_data/date_2017_09_28_time_13_14/aux/evaluations/bss/chimera/dc_out_of_sample_test.csv', + ['/local_data/magnolia/experiment_data/date_2017_09_28_time_13_14/aux/evaluations/mask_sce/out_of_sample_test', + '/local_data/magnolia/experiment_data/date_2017_09_28_time_13_14/aux/evaluations/bss/mask_sce/dc_out_of_sample_test.csv', 'dc'], - ['/local_data/magnolia/experiment_data/date_2017_09_28_time_13_14/aux/evaluations/snmf/in_sample_test', - '/local_data/magnolia/experiment_data/date_2017_09_28_time_13_14/aux/evaluations/bss/snmf/in_sample_test.csv'], - ['/local_data/magnolia/experiment_data/date_2017_09_28_time_13_14/aux/evaluations/snmf/out_of_sample_test', - '/local_data/magnolia/experiment_data/date_2017_09_28_time_13_14/aux/evaluations/bss/snmf/out_of_sample_test.csv'] + + #['/local_data/magnolia/experiment_data/date_2017_09_28_time_13_14/aux/evaluations/snmf/in_sample_test', + # '/local_data/magnolia/experiment_data/date_2017_09_28_time_13_14/aux/evaluations/bss/snmf/in_sample_test.csv'], + #['/local_data/magnolia/experiment_data/date_2017_09_28_time_13_14/aux/evaluations/snmf/out_of_sample_test', + # '/local_data/magnolia/experiment_data/date_2017_09_28_time_13_14/aux/evaluations/bss/snmf/out_of_sample_test.csv'] ] args = args[8:] diff --git a/magnolia/python/inference/denoising/RatioMaskeSCE/__init__.py b/magnolia/python/inference/denoising/RatioMaskSCE/__init__.py similarity index 100% rename from magnolia/python/inference/denoising/RatioMaskeSCE/__init__.py rename to magnolia/python/inference/denoising/RatioMaskSCE/__init__.py diff --git a/magnolia/python/inference/denoising/RatioMaskeSCE/separate_mix.py b/magnolia/python/inference/denoising/RatioMaskSCE/separate_mix.py similarity index 96% rename from magnolia/python/inference/denoising/RatioMaskeSCE/separate_mix.py rename to magnolia/python/inference/denoising/RatioMaskSCE/separate_mix.py index e4e505c..266836d 100644 --- a/magnolia/python/inference/denoising/RatioMaskeSCE/separate_mix.py +++ b/magnolia/python/inference/denoising/RatioMaskSCE/separate_mix.py @@ -58,11 +58,11 @@ def main(): model_location = '/cpu:0' model_settings = '' - # mixes = ['/local_data/magnolia/pipeline_data/date_2017_09_27_time_13_25/settings/mixing_LibriSpeech_UrbanSound8K_test_in_sample.json'] - mixes = ['/local_data/magnolia/pipeline_data/date_2017_09_27_time_13_25/settings/mixing_LibriSpeech_UrbanSound8K_test_out_of_sample.json'] + mixes = ['/local_data/magnolia/pipeline_data/date_2017_09_27_time_13_25/settings/mixing_LibriSpeech_UrbanSound8K_test_in_sample.json'] + # mixes = ['/local_data/magnolia/pipeline_data/date_2017_09_27_time_13_25/settings/mixing_LibriSpeech_UrbanSound8K_test_out_of_sample.json'] from_disk = True - # output_path = '/local_data/magnolia/experiment_data/date_2017_09_28_time_13_14/aux/evaluations/mask_sce/in_sample_test' - output_path = '/local_data/magnolia/experiment_data/date_2017_09_28_time_13_14/aux/evaluations/mask_sce/out_of_sample_test' + output_path = '/local_data/magnolia/experiment_data/date_2017_09_28_time_13_14/aux/evaluations/mask_sce/in_sample_test' + # output_path = '/local_data/magnolia/experiment_data/date_2017_09_28_time_13_14/aux/evaluations/mask_sce/out_of_sample_test' eval_sr = 8000 mixer = MixIterator(mixes_settings_filenames=mixes, diff --git a/magnolia/python/inference/denoising/RatioMaskeSCE/separate_sample_from_mix.py b/magnolia/python/inference/denoising/RatioMaskSCE/separate_sample_from_mix.py similarity index 100% rename from magnolia/python/inference/denoising/RatioMaskeSCE/separate_sample_from_mix.py rename to magnolia/python/inference/denoising/RatioMaskSCE/separate_sample_from_mix.py diff --git a/magnolia/python/models/dnnseparate/jflec.py b/magnolia/python/models/dnnseparate/jflec.py new file mode 100644 index 0000000..62d4e7c --- /dev/null +++ b/magnolia/python/models/dnnseparate/jflec.py @@ -0,0 +1,351 @@ +import logging.config +import numpy as np +import tensorflow as tf + +from magnolia.models.model_base import ModelBase +from magnolia.utils import tf_utils + + +logger = logging.getLogger('model') + + +class JFLEC(ModelBase): + """ + """ + + def initialize(self): + self.num_sources = self.config['model_params']['num_sources'] + self.num_samples = self.config['model_params']['num_samples'] + self.num_encoding_layers = self.config['model_params']['num_encoding_layers'] + self.embedding_size = self.config['model_params']['embedding_size'] + self.num_decoding_layers = self.config['model_params']['num_decoding_layers'] + self.alpha_init = self.config['model_params']['alpha'] + nl = self.config['model_params']['nonlinearity'] + self.nonlinearity = eval('{}'.format(nl)) + + self.batch_count = 0 + self.nbatches = [] + self.costs = [] + self.t_costs = [] + self.v_costs = [] + self.last_saved = 0 + + + def build_graph(self, graph): + with graph.as_default(): + with tf.device(self.config['device']): + # Placeholder tensor for the input data + self.X = tf.placeholder(tf.float32, [None, self.num_samples]) + + # Placeholder tensor for UIDs for sources + self.X_uids = tf.placeholder(tf.int32, [None, 2]) + + # Placeholder scalar for relative cost factor + self.Alpha = tf.placeholder(tf.float32) + + # Placeholder tensor for the labels/targets + self.Y = tf.placeholder(tf.float32, [None, self.num_samples]) + + # Model methods + self.network + self.cost + self.optimizer + + return graph + + + def learn_from_epoch(self, epoch_id, + validate_every, + stop_threshold, + training_mixer, + validation_mixer, + batch_formatter, + model_save_base): + # FIXME: + # Find the number of batches already elapsed (Useful for resuming training) + start = 0 + if len(self.nbatches) != 0: + start = self.nbatches[-1] + + batch_count = self.batch_count + # Training epoch loop + for batch in iter(training_mixer): + unscaled_spectral_sum_batch, scaled_spectral_sum_batch, spectral_masks_batch, spectral_sources_batch = batch_formatter(batch[0], batch[1], batch[2]) + # should be dimensions of (batch size, source) + uids_batch = batch[3] + + # Train the model on one batch and get the cost + c = self.train_on_batch(scaled_spectral_sum_batch, unscaled_spectral_sum_batch, + spectral_masks_batch, spectral_sources_batch) + + # Store the training cost + self.costs.append(c) + + # Store the current batch_count number + + # Evaluate the model on the validation data + if (batch_count + 1) % validate_every == 0: + # Store the training cost + self.t_costs.append(np.mean(self.costs)) + # Reset the cost over the last 10 batches + self.costs = [] + + # Compute average validation score + all_c_v = [] + for vbatch in iter(validation_mixer): + unscaled_spectral_sum_batch, scaled_spectral_sum_batch, spectral_masks_batch, spectral_sources_batch = batch_formatter(vbatch[0], vbatch[1], vbatch[2]) + # dimensions of (batch size, source) + uids_batch = vbatch[3] + + # Get the cost on the validation batch + c_v = self.get_cost(scaled_spectral_sum_batch, unscaled_spectral_sum_batch, + spectral_masks_batch, spectral_sources_batch) + all_c_v.append(c_v) + + ave_c_v = np.mean(all_c_v) + + # Check if the validation cost is below the minimum validation cost, and if so, save it. + if len(self.v_costs) > 0 and ave_c_v < min(self.v_costs) and len(self.nbatches) > 0: + logger.info("Saving the model because validation score is {} below the old minimum.".format(min(self.v_costs) - ave_c_v)) + + # Save the model to the specified path + self.save(model_save_base) + + # Record the batch that the model was last saved on + self.last_saved = self.nbatches[-1] + + # Store the validation cost + self.v_costs.append(ave_c_v) + + # Store the current batch number + self.nbatches.append(batch_count + 1 + start) + + # Compute scale quantities for plotting + length = len(self.nbatches) + cutoff = int(0.5*length) + lowline = [min(self.v_costs)]*length + + logger.info("Training cost on batch {} is {}.".format(self.nbatches[-1], self.t_costs[-1])) + logger.info("Validation cost on batch {} is {}.".format(self.nbatches[-1], self.v_costs[-1])) + logger.info("Last saved {} batches ago.".format(self.nbatches[-1] - self.last_saved)) + + # Stop training if the number of iterations since the last save point exceeds the threshold + if self.nbatches[-1] - self.last_saved > stop_threshold: + logger.info("Early stopping criteria met!") + break + + batch_count += 1 + + self.batch_count = batch_count + + + def infer(self, **kw_args): + pass + + + @tf_utils.scope_decorator + def network(self): + """ + """ + + # Get the shape of the input + input_shape = tf.shape(self.X) + + # encoder + l = tf.expand_dims(self.X) + for i in range(self.num_encoding_layers): + if i == 0: + nfilters = 2**3 + filter_size = 2**3 + else: + nfilters *= 2 + layer_num = i + 1 + l = self.encoding_layer(l, filter_size, nfilters, layer_num) + + # feature + embeddings + embeddings = self.compute_embeddings(l) + + # clustering + cc = self.make_cluster_centers() + # compute fuzzy assignments + # batch, feature 1, feature 2, nsources + squared_diffs = tf.reduce_sum(tf.square(tf.expand_dims(embeddings, 3) - tf.expand_dims(tf.expand_dims(tf.expand_dims(cc, 0), 0), 0)), -1) + squared_diffs_pow = tf.pow(squared_diffs, 1./(m - 1.)) + W = tf.reciprocal(squared_diffs_pow*tf.expand_dims(tf.reduce_sum(tf.reciprocal(squared_diffs_pow), -1), -1)) + + WT = tf.transpose(W, perm=[0, 3, 1, 2]) + clustering_factors = tf.gather_nd(WT, self.X_uids) + + # NOTE: I'm making an explicit choice to multiply the embedding vectors + # by the fuzzy c-means coefficients + # batch, feature 1, feature 2, nsources, embedding_size + scaled_embeddings = tf.expand_dims(clustering_factors, -1)*embeddings + + # decoder + # collapse embedding dimension with convolution (should revisit later) + with tf.variable_scope('embedding_decoder', reuse=tf.AUTO_REUSE): + filters = tf.truncated_normal([1, 1, 1, self.embedding_size, 1], mean=0.0, stddev=0.1) + bias = tf.truncated_normal([1], mean=0.0, stddev=0.1) + l = tf.nn.convolution(input=scaled_embeddings, + filter=tf.get_variable(name='weights', + initializer=filters), + padding='VALID') + \ + tf.get_variable(name='bias', initializer=bias) + l = self.nonlinearity(tf.squeeze(l)) + + #for i in range(self.num_encoding_layers): + + return embeddings, W + + @tf_utils.scope_decorator + def cost(self): + """ + Constuct the cost function op for the cost function used in the deep + clusetering model and the mask inference head + """ + + # Get the shape of the input + shape = tf.shape(self.y) + + dc_output, mi_output = self.network + + # Reshape the targets to be of shape (batch, T*F, c) and the vectors to + # have shape (batch, T*F, K) + Y = tf.reshape(self.y, [shape[0], shape[1]*shape[2], shape[3]]) + V = tf.reshape(dc_output, + [shape[0], shape[1]*shape[2], self.embedding_size]) + + # Compute the partition size vectors + ones = tf.ones([shape[0], shape[1]*shape[2], 1]) + mul_ones = tf.matmul(tf.transpose(Y, perm=[0,2,1]), ones) + diagonal = tf.matmul(Y, mul_ones) + # D = 1/tf.sqrt(diagonal) + # D = tf.sqrt(1/diagonal) + D = tf.sqrt(tf.where(tf.is_inf(1/diagonal), tf.ones_like(diagonal) * 0, 1/diagonal)) + D = tf.reshape(D, [shape[0], shape[1]*shape[2]]) + + # Compute the matrix products needed for the cost function. Reshapes + # are to allow the diagonal to be multiplied across the correct + # dimensions without explicitly constructing the full diagonal matrix. + DV = D * tf.transpose(V, perm=[2,0,1]) + DV = tf.transpose(DV, perm=[1,2,0]) + VTV = tf.matmul(tf.transpose(V, perm=[0,2,1]), DV) + + DY = D * tf.transpose(Y, perm=[2,0,1]) + DY = tf.transpose(DY, perm=[1,2,0]) + VTY = tf.matmul(tf.transpose(V, perm=[0,2,1]), DY) + + YTY = tf.matmul(tf.transpose(Y, perm=[0,2,1]), DY) + + # Compute the cost by taking the Frobenius norm for each matrix + dc_cost = tf.norm(VTV, axis=[-2,-1]) -2*tf.norm(VTY, axis=[-2,-1]) + \ + tf.norm(YTY, axis=[-2,-1]) + + # broadcast product along source dimension + mi_cost = tf.square(self.y_clean - mi_output*tf.expand_dims(self.X_clean, -1)) + + return self.alpha*tf.reduce_mean(dc_cost) + (1.0 - self.alpha)*tf.reduce_mean(mi_cost) + + @tf_utils.scope_decorator + def optimizer(self): + """ + Constructs the optimizer op used to train the network + """ + opt = tf.train.AdamOptimizer() + return opt.minimize(self.cost) + + # def save(self, path): + # """ + # Saves the model to the specified path. + # """ + # self.saver.save(self.sess, path) + + # def load(self, path): + # """ + # Load the model from the specified path. + # """ + # self.saver.restore(self.sess, path) + + def train_on_batch(self, X_train, X_train_clean, y_train, y_train_clean): + """ + Train the model on a batch with input X and target y. Returns the cost + computed on this batch. + """ + + cost, _ = self.sess.run([self.cost, self.optimizer], + {self.X: X_train, self.y: y_train, + self.X_clean: X_train_clean, + self.y_clean: y_train_clean}) + + return cost + + def get_vectors(self, X_in): + """ + Compute the embedding vectors for the input spectrograms + """ + + vectors = self.sess.run(self.network, {self.X: X_in})[0] + return vectors + + def get_cost(self, X_in, X_clean_in, y_in, y_clean_in): + """ + Computes the cost of a batch, but does not update any model parameters. + """ + cost = self.sess.run(self.cost, {self.X: X_in, self.y: y_in, + self.X_clean: X_clean_in, + self.y_clean: y_clean_in}) + return cost + + def encoding_layer(self, prev_layer, filter_size, nfilters, layer_num): + shape = tf.shape(prev_layer) + prev_nfilters = shape[-1] + + with tf.variable_scope('encoding_layer_{}'.format(layer_num), + reuse=tf.AUTO_REUSE): + filters = tf.truncated_normal([filter_size, prev_nfilters, nfilters], + mean=0.0, stddev=0.1) + bias = tf.truncated_normal([nfilters], + mean=0.0, stddev=0.1) + l = tf.nn.convolution(input=prev_layer, + filter=tf.get_variable(name='weights', + initializer=filters), + padding='VALID', + strides=[1, 1, 2]) + \ + tf.get_variable(name='bias', + initializer=bias) + + l = self.nonlinearity(l) + + return l + + def compute_embeddings(self, prev_layer): + feature_shape = tf.shape(prev_layer) + l = tf.expand_dims(prev_layer) + + with tf.variable_scope('embeddings', reuse=tf.AUTO_REUSE): + # NOTE: This convolution will scan over all "time bins" with + # different weights for each embedding dimension. + # The filter window is divised such that the temporal + # correlations are preserved + filters = tf.truncated_normal([2*feature_shape[1] - 1, 1, + 1, self.embedding_size], mean=0.0, stddev=0.1) + bias = tf.truncated_normal([self.embedding_size], mean=0.0, stddev=0.1) + embeddings = tf.nn.convolution(input=l, + filter=tf.get_variable(name='weights', + initializer=filters), + padding='SAME') + \ + tf.get_variable(name='bias', + initializer=bias) + + embeddings = self.nonlinearity(embeddings) + + return embeddings + + def make_cluster_centers(self): + with tf.variable_scope('cluster_centers', reuse=tf.AUTO_REUSE): + init = tf.truncated_normal([self.num_sources, self.embedding_size], + mean=0.0, stddev=0.1) + + w = tf.get_variable(name='weights', initializer=init) + + return w diff --git a/magnolia/python/training/denoising/JFLEC/__init__.py b/magnolia/python/training/denoising/JFLEC/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/magnolia/python/training/denoising/JFLEC/training.py b/magnolia/python/training/denoising/JFLEC/training.py new file mode 100644 index 0000000..e69e070 --- /dev/null +++ b/magnolia/python/training/denoising/JFLEC/training.py @@ -0,0 +1,94 @@ +# Generic imports +import argparse +import logging.config +import json + +import numpy as np +import pandas as pd +import tensorflow as tf + +# Import the Chimera separation model +from magnolia.models import make_model + +# Import utilities for using the model +from magnolia.training.data_iteration.mix_iterator import MixIterator +# from magnolia.utils.training import preprocess_chimera_batch + + +def main(): + # parse command line arguments + parser = argparse.ArgumentParser(description='Train the JFLEC network.') + # parser.add_argument('--model_settings', '-s', + # default='../../../../data/models_settings/chimera_template.json', + # help='model settings JSON file') + # parser.add_argument('--_settings', '-s', + # default='../../../../data/models_settings/chimera_template.json', + # help='model settings JSON file') + parser.add_argument('--logger_settings', '-l', + default='../../../../data/logging_settings/logging.conf', + help='logging configuration file') + args = parser.parse_args() + + # Load logging configuration + logging.config.fileConfig(args.logger_settings) + logger = logging.getLogger('model') + + # Number of epochs + num_epochs = 2 + # Threshold for stopping if the model hasn't improved for this many consecutive batches + stop_threshold = 10000 + # validate every number of these batches + validate_every = 100 + train_batchsize = 256 + train_mixes = ['/local_data/magnolia/pipeline_data/date_2017_09_27_time_13_25/settings/mixing_LibriSpeech_UrbanSound8K_train.json'] + train_from_disk = False + validate_batchsize = 200 + validate_mixes = ['/local_data/magnolia/pipeline_data/date_2017_09_27_time_13_25/settings/mixing_LibriSpeech_UrbanSound8K_validate.json'] + validate_from_disk = False + model_params = { + 'layer_size': 500, + 'embedding_size': 10, + 'alpha': 0.1, + 'nonlinearity': 'tf.tanh', + } + model_location = '/gpu:0' + uid_settings = '/local_data/magnolia/pipeline_data/date_2017_09_27_time_13_25/settings/assign_uids_LibriSpeech_UrbanSound8K.json' + model_save_base = '/local_data/magnolia/experiment_data/date_2017_09_28_time_13_14/aux/model_saves/jflec' + + + training_mixer = MixIterator(mixes_settings_filenames=train_mixes, + batch_size=train_batchsize, + read_spectrogram=False, + from_disk=train_from_disk) + + validation_mixer = MixIterator(mixes_settings_filenames=validate_mixes, + batch_size=validate_batchsize, + read_spectrogram=False, + from_disk=validate_from_disk) + + # get frequency dimension + frequency_dim = training_mixer.sample_dimensions()[0] + # TODO: throw an exception + assert(frequency_dim == validation_mixer.sample_dimensions()[0]) + + # get number of sources + settings = json.load(open(uid_settings)) + uid_file = settings['output_file'] + uid_csv = pd.read_csv(uid_file) + number_of_sources = uid_csv['uid'].max() + 1 + + model_params['F'] = frequency_dim + config = {'model_params': model_params, + 'device': model_location} + model = make_model('JFLEC', config) + + model.train(validate_every=validate_every, + stop_threshold=stop_threshold, + training_mixer=training_mixer, + validation_mixer=validation_mixer, + batch_formatter=preprocess_chimera_batch, + model_save_base=model_save_base) + + +if __name__ == '__main__': + main() diff --git a/magnolia/python/training/denoising/RatioMaskCluster/__init__.py b/magnolia/python/training/denoising/RatioMaskCluster/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/magnolia/python/training/denoising/RatioMaskCluster/training.py b/magnolia/python/training/denoising/RatioMaskCluster/training.py new file mode 100644 index 0000000..a096a42 --- /dev/null +++ b/magnolia/python/training/denoising/RatioMaskCluster/training.py @@ -0,0 +1,98 @@ +# Generic imports +import argparse +import logging.config +import json + +import numpy as np +import pandas as pd +import tensorflow as tf + +# Import the RatioMaskCluster separation model +from magnolia.models import make_model + +# Import utilities for using the model +from magnolia.training.data_iteration.mix_iterator import MixIterator +from magnolia.utils.training import preprocess_chimera_batch + + +def main(): + # parse command line arguments + parser = argparse.ArgumentParser(description='Train the k-means clustering + ratio mask network.') + # parser.add_argument('--model_settings', '-s', + # default='../../../../data/models_settings/chimera_template.json', + # help='model settings JSON file') + # parser.add_argument('--_settings', '-s', + # default='../../../../data/models_settings/chimera_template.json', + # help='model settings JSON file') + parser.add_argument('--logger_settings', '-l', + default='../../../../data/logging_settings/logging.conf', + help='logging configuration file') + args = parser.parse_args() + + # Load logging configuration + logging.config.fileConfig(args.logger_settings) + logger = logging.getLogger('model') + + # Number of epochs + num_epochs = 20 # try 20 + # Threshold for stopping if the model hasn't improved for this many consecutive batches + stop_threshold = 10000 + # validate every number of these batches + validate_every = 100 + train_batchsize = 256 + train_mixes = ['/local_data/magnolia/pipeline_data/date_2017_09_27_time_13_25/settings/mixing_LibriSpeech_UrbanSound8K_train.json'] + train_from_disk = False + validate_batchsize = 200 + validate_mixes = ['/local_data/magnolia/pipeline_data/date_2017_09_27_time_13_25/settings/mixing_LibriSpeech_UrbanSound8K_validate.json'] + validate_from_disk = False + model_params = { + 'layer_size': 500, + 'embedding_size': 10, + 'alpha': 0.9, # try 0.9 + 'nonlinearity': 'tf.tanh', + 'num_reco_sources': 2, + 'normalize': False, + 'collapse_sources': False, + } + model_location = '/gpu:0' + uid_settings = '/local_data/magnolia/pipeline_data/date_2017_09_27_time_13_25/settings/assign_uids_LibriSpeech_UrbanSound8K.json' + model_save_base = '/local_data/magnolia/experiment_data/date_2017_09_28_time_13_14/aux/model_saves/mask_cluster' + + + training_mixer = MixIterator(mixes_settings_filenames=train_mixes, + batch_size=train_batchsize, + read_waveform=False, + from_disk=train_from_disk) + + validation_mixer = MixIterator(mixes_settings_filenames=validate_mixes, + batch_size=validate_batchsize, + read_waveform=False, + from_disk=validate_from_disk) + + # get frequency dimension + frequency_dim = training_mixer.sample_dimensions()[0] + # TODO: throw an exception + assert(frequency_dim == validation_mixer.sample_dimensions()[0]) + + # get number of sources + settings = json.load(open(uid_settings)) + uid_file = settings['output_file'] + uid_csv = pd.read_csv(uid_file) + number_of_sources = uid_csv['uid'].max() + 1 + + model_params['F'] = frequency_dim + model_params['num_training_sources'] = number_of_sources + config = {'model_params': model_params, + 'device': model_location} + model = make_model('RatioMaskCluster', config) + + model.train(validate_every=validate_every, + stop_threshold=stop_threshold, + training_mixer=training_mixer, + validation_mixer=validation_mixer, + batch_formatter=preprocess_chimera_batch, + model_save_base=model_save_base) + + +if __name__ == '__main__': + main() From 547e8c68c312a1cb28cddbcccc7adc369795ed02 Mon Sep 17 00:00:00 2001 From: Jeffrey Hetherly Date: Tue, 16 Jan 2018 18:21:19 +0000 Subject: [PATCH 05/16] working clustering model --- magnolia/python/analysis/bss_evaluate.py | 60 +++--- magnolia/python/analysis/comparison_plot.py | 33 ++- magnolia/python/analysis/make_sdr_table.py | 15 +- .../out_of_set_sdr_delta_versus_input_snr.pdf | Bin 14312 -> 17732 bytes ...out_of_set_sdr_delta_versus_noise_type.pdf | Bin 16703 -> 18223 bytes .../denoising/RatioMaskCluster/__init__.py | 0 .../RatioMaskCluster/separate_mix.py | 194 ++++++++++++++++++ .../separate_sample_from_mix.py | 170 +++++++++++++++ .../python/models/dnndenoise/cluster_mask.py | 131 ++++++++---- magnolia/python/models/model_base.py | 2 +- .../denoising/RatioMaskCluster/training.py | 9 +- magnolia/python/utils/tf_utils.py | 12 ++ 12 files changed, 542 insertions(+), 84 deletions(-) create mode 100644 magnolia/python/inference/denoising/RatioMaskCluster/__init__.py create mode 100644 magnolia/python/inference/denoising/RatioMaskCluster/separate_mix.py create mode 100644 magnolia/python/inference/denoising/RatioMaskCluster/separate_sample_from_mix.py diff --git a/magnolia/python/analysis/bss_evaluate.py b/magnolia/python/analysis/bss_evaluate.py index f782545..c2cff4f 100644 --- a/magnolia/python/analysis/bss_evaluate.py +++ b/magnolia/python/analysis/bss_evaluate.py @@ -103,26 +103,28 @@ def evaluate(input_path, output_csv_file, target_stype=None, eval_sr=8000, num_s if __name__ == '__main__': args = [ - #['/local_data/magnolia/experiment_data/date_2017_09_28_time_13_14/aux/evaluations/lab41/in_sample_test', - # '/local_data/magnolia/experiment_data/date_2017_09_28_time_13_14/aux/evaluations/bss/lab41/in_sample_test.csv'], - #['/local_data/magnolia/experiment_data/date_2017_09_28_time_13_14/aux/evaluations/lab41/out_of_sample_test', - # '/local_data/magnolia/experiment_data/date_2017_09_28_time_13_14/aux/evaluations/bss/lab41/out_of_sample_test.csv'], - #['/local_data/magnolia/experiment_data/date_2017_09_28_time_13_14/aux/evaluations/large_lab41/in_sample_test', - # '/local_data/magnolia/experiment_data/date_2017_09_28_time_13_14/aux/evaluations/bss/large_lab41/in_sample_test.csv'], - #['/local_data/magnolia/experiment_data/date_2017_09_28_time_13_14/aux/evaluations/large_lab41/out_of_sample_test', - # '/local_data/magnolia/experiment_data/date_2017_09_28_time_13_14/aux/evaluations/bss/large_lab41/out_of_sample_test.csv'], - #['/local_data/magnolia/experiment_data/date_2017_09_28_time_13_14/aux/evaluations/chimera/in_sample_test', - # '/local_data/magnolia/experiment_data/date_2017_09_28_time_13_14/aux/evaluations/bss/chimera/mi_in_sample_test.csv', - # 'mi'], - #['/local_data/magnolia/experiment_data/date_2017_09_28_time_13_14/aux/evaluations/chimera/out_of_sample_test', - # '/local_data/magnolia/experiment_data/date_2017_09_28_time_13_14/aux/evaluations/bss/chimera/mi_out_of_sample_test.csv', - # 'mi'], - #['/local_data/magnolia/experiment_data/date_2017_09_28_time_13_14/aux/evaluations/chimera/in_sample_test', - # '/local_data/magnolia/experiment_data/date_2017_09_28_time_13_14/aux/evaluations/bss/chimera/dc_in_sample_test.csv', - # 'dc'], - #['/local_data/magnolia/experiment_data/date_2017_09_28_time_13_14/aux/evaluations/chimera/out_of_sample_test', - # '/local_data/magnolia/experiment_data/date_2017_09_28_time_13_14/aux/evaluations/bss/chimera/dc_out_of_sample_test.csv', - # 'dc'], + ['/local_data/magnolia/experiment_data/date_2017_09_28_time_13_14/aux/evaluations/lab41/in_sample_test', + '/local_data/magnolia/experiment_data/date_2017_09_28_time_13_14/aux/evaluations/bss/lab41/in_sample_test.csv'], + ['/local_data/magnolia/experiment_data/date_2017_09_28_time_13_14/aux/evaluations/lab41/out_of_sample_test', + '/local_data/magnolia/experiment_data/date_2017_09_28_time_13_14/aux/evaluations/bss/lab41/out_of_sample_test.csv'], + + ['/local_data/magnolia/experiment_data/date_2017_09_28_time_13_14/aux/evaluations/large_lab41/in_sample_test', + '/local_data/magnolia/experiment_data/date_2017_09_28_time_13_14/aux/evaluations/bss/large_lab41/in_sample_test.csv'], + ['/local_data/magnolia/experiment_data/date_2017_09_28_time_13_14/aux/evaluations/large_lab41/out_of_sample_test', + '/local_data/magnolia/experiment_data/date_2017_09_28_time_13_14/aux/evaluations/bss/large_lab41/out_of_sample_test.csv'], + + ['/local_data/magnolia/experiment_data/date_2017_09_28_time_13_14/aux/evaluations/chimera/in_sample_test', + '/local_data/magnolia/experiment_data/date_2017_09_28_time_13_14/aux/evaluations/bss/chimera/mi_in_sample_test.csv', + 'mi'], + ['/local_data/magnolia/experiment_data/date_2017_09_28_time_13_14/aux/evaluations/chimera/out_of_sample_test', + '/local_data/magnolia/experiment_data/date_2017_09_28_time_13_14/aux/evaluations/bss/chimera/mi_out_of_sample_test.csv', + 'mi'], + ['/local_data/magnolia/experiment_data/date_2017_09_28_time_13_14/aux/evaluations/chimera/in_sample_test', + '/local_data/magnolia/experiment_data/date_2017_09_28_time_13_14/aux/evaluations/bss/chimera/dc_in_sample_test.csv', + 'dc'], + ['/local_data/magnolia/experiment_data/date_2017_09_28_time_13_14/aux/evaluations/chimera/out_of_sample_test', + '/local_data/magnolia/experiment_data/date_2017_09_28_time_13_14/aux/evaluations/bss/chimera/dc_out_of_sample_test.csv', + 'dc'], ['/local_data/magnolia/experiment_data/date_2017_09_28_time_13_14/aux/evaluations/mask_sce/in_sample_test', '/local_data/magnolia/experiment_data/date_2017_09_28_time_13_14/aux/evaluations/bss/mask_sce/mi_in_sample_test.csv', @@ -137,13 +139,13 @@ def evaluate(input_path, output_csv_file, target_stype=None, eval_sr=8000, num_s '/local_data/magnolia/experiment_data/date_2017_09_28_time_13_14/aux/evaluations/bss/mask_sce/dc_out_of_sample_test.csv', 'dc'], - #['/local_data/magnolia/experiment_data/date_2017_09_28_time_13_14/aux/evaluations/snmf/in_sample_test', - # '/local_data/magnolia/experiment_data/date_2017_09_28_time_13_14/aux/evaluations/bss/snmf/in_sample_test.csv'], - #['/local_data/magnolia/experiment_data/date_2017_09_28_time_13_14/aux/evaluations/snmf/out_of_sample_test', - # '/local_data/magnolia/experiment_data/date_2017_09_28_time_13_14/aux/evaluations/bss/snmf/out_of_sample_test.csv'] + ['/local_data/magnolia/experiment_data/date_2017_09_28_time_13_14/aux/evaluations/snmf/in_sample_test', + '/local_data/magnolia/experiment_data/date_2017_09_28_time_13_14/aux/evaluations/bss/snmf/in_sample_test.csv'], + ['/local_data/magnolia/experiment_data/date_2017_09_28_time_13_14/aux/evaluations/snmf/out_of_sample_test', + '/local_data/magnolia/experiment_data/date_2017_09_28_time_13_14/aux/evaluations/bss/snmf/out_of_sample_test.csv'] ] - args = args[8:] + args = args[8:12] # Parallel #processes = [] @@ -156,9 +158,9 @@ def evaluate(input_path, output_csv_file, target_stype=None, eval_sr=8000, num_s # Parallel #pool = mp.Pool(processes=min(len(args), os.cpu_count() - 1)) - pool = mp.Pool(processes=2) - pool.starmap(evaluate, args) + #pool = mp.Pool(processes=2) + #pool.starmap(evaluate, args) # Sequential - #for arg in args: - # evaluate(*arg) + for arg in args: + evaluate(*arg) diff --git a/magnolia/python/analysis/comparison_plot.py b/magnolia/python/analysis/comparison_plot.py index 03ddb36..157ceae 100644 --- a/magnolia/python/analysis/comparison_plot.py +++ b/magnolia/python/analysis/comparison_plot.py @@ -76,7 +76,9 @@ def make_sdr_delta_versus_noise_source_plot(models, df_base_name): # create plot fig, ax = plt.subplots(figsize=(8, 6)) index = np.arange(n_groups) - bar_width = 0.25 + plt.xlim(-0.5, n_groups + 0.5) + bar_width = (n_groups + 1)/(1.15*n_groups*len(models)) + #bar_width = 0.15 opacity = 0.8 offset = 0 @@ -153,7 +155,7 @@ def make_sdr_delta_versus_input_snr_plot(models, df_base_name, bins): # create plot fig, ax = plt.subplots(figsize=(8, 6)) index = np.arange(n_groups) - bar_width = 0.25 + bar_width = (bins[-1] - bins[0])/(1.15*n_groups*len(models)) opacity = 0.8 offset = 0 @@ -216,18 +218,30 @@ def main(): # 'out_of_set': '/data/fs4/home/pgamble/Magnolia/Denoising/Autoencoder/Final Results/eval_test_A.csv', # 'color': '#E0FBFC' #}, - #{ - # 'name': 'Chimera MI', - # 'in_set': '/local_data/magnolia/experiment_data/date_2017_09_28_time_13_14/aux/evaluations/bss/chimera/mi_in_sample_test_sdr_summary.csv', - # 'out_of_set': '/local_data/magnolia/experiment_data/date_2017_09_28_time_13_14/aux/evaluations/bss/chimera/mi_out_of_sample_test_sdr_summary.csv', - # 'color': '#3D5A80' - #}, { - 'name': 'DC',#'Chimera DC', + 'name': 'Chimera MI', + 'in_set': '/local_data/magnolia/experiment_data/date_2017_09_28_time_13_14/aux/evaluations/bss/chimera/mi_in_sample_test_sdr_summary.csv', + 'out_of_set': '/local_data/magnolia/experiment_data/date_2017_09_28_time_13_14/aux/evaluations/bss/chimera/mi_out_of_sample_test_sdr_summary.csv', + 'color': '#3D5A80' + }, + { + 'name': 'Chimera DC', 'in_set': '/local_data/magnolia/experiment_data/date_2017_09_28_time_13_14/aux/evaluations/bss/chimera/dc_in_sample_test_sdr_summary.csv', 'out_of_set': '/local_data/magnolia/experiment_data/date_2017_09_28_time_13_14/aux/evaluations/bss/chimera/dc_out_of_sample_test_sdr_summary.csv', 'color': '#0C0A3E' }, + { + 'name': 'SCE + Mask MI', + 'in_set': '/local_data/magnolia/experiment_data/date_2017_09_28_time_13_14/aux/evaluations/bss/mask_sce/mi_in_sample_test_sdr_summary.csv', + 'out_of_set': '/local_data/magnolia/experiment_data/date_2017_09_28_time_13_14/aux/evaluations/bss/mask_sce/mi_out_of_sample_test_sdr_summary.csv', + 'color': '#3D5A80' + }, + { + 'name': 'SCE + Mask Clustering', + 'in_set': '/local_data/magnolia/experiment_data/date_2017_09_28_time_13_14/aux/evaluations/bss/mask_sce/dc_in_sample_test_sdr_summary.csv', + 'out_of_set': '/local_data/magnolia/experiment_data/date_2017_09_28_time_13_14/aux/evaluations/bss/mask_sce/dc_out_of_sample_test_sdr_summary.csv', + 'color': '#0C0A3E' + }, { 'name': 'SCE', 'in_set': '/local_data/magnolia/experiment_data/date_2017_09_28_time_13_14/aux/evaluations/bss/large_lab41/in_sample_test_sdr_summary.csv', @@ -235,6 +249,7 @@ def main(): 'color': '#A4303F' }, ] + # TODO: the input SNR range should be determined automatically bins = np.linspace(-5, 5, 11) bins[-1] = 1.02*bins[-1] diff --git a/magnolia/python/analysis/make_sdr_table.py b/magnolia/python/analysis/make_sdr_table.py index e3ad40f..00d778c 100644 --- a/magnolia/python/analysis/make_sdr_table.py +++ b/magnolia/python/analysis/make_sdr_table.py @@ -53,6 +53,19 @@ def main(): ['/local_data/magnolia/experiment_data/date_2017_09_28_time_13_14/aux/evaluations/bss/large_lab41/out_of_sample_test.csv', '/local_data/magnolia/pipeline_data/date_2017_09_27_time_13_25/aux/out_of_sample_test_mixes.csv', '/local_data/magnolia/experiment_data/date_2017_09_28_time_13_14/aux/evaluations/bss/large_lab41/out_of_sample_test_sdr_summary.csv'], + + ['/local_data/magnolia/experiment_data/date_2017_09_28_time_13_14/aux/evaluations/bss/mask_sce/mi_in_sample_test.csv', + '/local_data/magnolia/pipeline_data/date_2017_09_27_time_13_25/aux/in_sample_test_mixes.csv', + '/local_data/magnolia/experiment_data/date_2017_09_28_time_13_14/aux/evaluations/bss/mask_sce/mi_in_sample_test_sdr_summary.csv'], + ['/local_data/magnolia/experiment_data/date_2017_09_28_time_13_14/aux/evaluations/bss/mask_sce/mi_out_of_sample_test.csv', + '/local_data/magnolia/pipeline_data/date_2017_09_27_time_13_25/aux/out_of_sample_test_mixes.csv', + '/local_data/magnolia/experiment_data/date_2017_09_28_time_13_14/aux/evaluations/bss/mask_sce/mi_out_of_sample_test_sdr_summary.csv'], + ['/local_data/magnolia/experiment_data/date_2017_09_28_time_13_14/aux/evaluations/bss/mask_sce/dc_in_sample_test.csv', + '/local_data/magnolia/pipeline_data/date_2017_09_27_time_13_25/aux/in_sample_test_mixes.csv', + '/local_data/magnolia/experiment_data/date_2017_09_28_time_13_14/aux/evaluations/bss/mask_sce/dc_in_sample_test_sdr_summary.csv'], + ['/local_data/magnolia/experiment_data/date_2017_09_28_time_13_14/aux/evaluations/bss/mask_sce/dc_out_of_sample_test.csv', + '/local_data/magnolia/pipeline_data/date_2017_09_27_time_13_25/aux/out_of_sample_test_mixes.csv', + '/local_data/magnolia/experiment_data/date_2017_09_28_time_13_14/aux/evaluations/bss/mask_sce/dc_out_of_sample_test_sdr_summary.csv'], ['/local_data/magnolia/experiment_data/date_2017_09_28_time_13_14/aux/evaluations/bss/chimera/mi_in_sample_test.csv', '/local_data/magnolia/pipeline_data/date_2017_09_27_time_13_25/aux/in_sample_test_mixes.csv', '/local_data/magnolia/experiment_data/date_2017_09_28_time_13_14/aux/evaluations/bss/chimera/mi_in_sample_test_sdr_summary.csv'], @@ -77,7 +90,7 @@ def main(): # args = args[2:4] #args = args[4:6] #args = args[6:8] - args = args[8:] + #args = args[8:] for arg in args: make_nice_table(*arg) diff --git a/magnolia/python/analysis/out_of_set_sdr_delta_versus_input_snr.pdf b/magnolia/python/analysis/out_of_set_sdr_delta_versus_input_snr.pdf index f84773b3b1337f2d4a852ce21865c47e3ad7ed71..ae14c539398af7ef4371f229999fddb4eae94f35 100644 GIT binary patch delta 7218 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 Vu2kCk14Uub1T<7x*~rou`acjCdWQf2 diff --git a/magnolia/python/analysis/out_of_set_sdr_delta_versus_noise_type.pdf b/magnolia/python/analysis/out_of_set_sdr_delta_versus_noise_type.pdf index 4d1747fc292fe2c35e21ae43ec2fbb6f9fcae233..85c4c861a3f554feaf8ed4b5bb4a53dd26995b2b 100644 GIT binary patch delta 5712 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 delta 4268 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 diff --git a/magnolia/python/inference/denoising/RatioMaskCluster/__init__.py b/magnolia/python/inference/denoising/RatioMaskCluster/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/magnolia/python/inference/denoising/RatioMaskCluster/separate_mix.py b/magnolia/python/inference/denoising/RatioMaskCluster/separate_mix.py new file mode 100644 index 0000000..266836d --- /dev/null +++ b/magnolia/python/inference/denoising/RatioMaskCluster/separate_mix.py @@ -0,0 +1,194 @@ +# Generic imports +# Generic imports +import os +import argparse +import logging.config +import json + +import numpy as np +import pandas as pd +import librosa as lr +import tqdm + +# Import the RatioMaskSCE separation model +from magnolia.models import make_model + +# Import utilities for using the model +from magnolia.utils.postprocessing import convert_preprocessing_parameters +from magnolia.preprocessing.preprocessing import undo_preprocessing +from magnolia.training.data_iteration.mix_iterator import MixIterator +from magnolia.utils.clustering_utils import chimera_clustering_separate, chimera_mask + + + +def standardize_waveform(y): + return (y - y.mean())/y.std() + + +def main(): + # parse command line arguments + parser = argparse.ArgumentParser(description='Denoise mixed samples using the RatioMaskSCE network.') + # parser.add_argument('--model_settings', '-s', + # default='../../../../data/models_settings/chimera_template.json', + # help='model settings JSON file') + # parser.add_argument('--_settings', '-s', + # default='../../../../data/models_settings/chimera_template.json', + # help='model settings JSON file') + parser.add_argument('--logger_settings', '-l', + default='../../../../data/logging_settings/logging.conf', + help='logging configuration file') + args = parser.parse_args() + + # Load logging configuration + logging.config.fileConfig(args.logger_settings) + logger = logging.getLogger('model') + + # from model settings + model_params = { + 'layer_size': 500, + 'embedding_size': 10, + 'alpha': 0.9, # try 0.9 + 'nonlinearity': 'tf.tanh', + 'num_reco_sources': 2, + 'normalize': False, + 'collapse_sources': False, + } + uid_settings = '/local_data/magnolia/pipeline_data/date_2017_09_27_time_13_25/settings/assign_uids_LibriSpeech_UrbanSound8K.json' + model_save_base = '/local_data/magnolia/experiment_data/date_2017_09_28_time_13_14/aux/model_saves/mask_sce' + + model_location = '/cpu:0' + model_settings = '' + mixes = ['/local_data/magnolia/pipeline_data/date_2017_09_27_time_13_25/settings/mixing_LibriSpeech_UrbanSound8K_test_in_sample.json'] + # mixes = ['/local_data/magnolia/pipeline_data/date_2017_09_27_time_13_25/settings/mixing_LibriSpeech_UrbanSound8K_test_out_of_sample.json'] + from_disk = True + output_path = '/local_data/magnolia/experiment_data/date_2017_09_28_time_13_14/aux/evaluations/mask_sce/in_sample_test' + # output_path = '/local_data/magnolia/experiment_data/date_2017_09_28_time_13_14/aux/evaluations/mask_sce/out_of_sample_test' + eval_sr = 8000 + + mixer = MixIterator(mixes_settings_filenames=mixes, + batch_size=1, + read_waveform=False, + from_disk=from_disk) + + # get frequency dimension + frequency_dim = mixer.sample_dimensions()[0] + + # get number of sources + settings = json.load(open(uid_settings)) + uid_file = settings['output_file'] + uid_csv = pd.read_csv(uid_file) + number_of_sources = uid_csv['uid'].max() + 1 + + model_params['F'] = frequency_dim + model_params['num_training_sources'] = number_of_sources + config = {'model_params': model_params, + 'device': model_location} + model = make_model('RatioMaskSCE', config) + + model.load(model_save_base) + + mix_settings = json.load(open(mixes[0])) + + signal = mix_settings['signals'][0] + preprocessing_settings = json.load(open(signal['preprocessing_settings'])) + stft_args = preprocessing_settings['processing_parameters']['stft_args'] + istft_args = convert_preprocessing_parameters(stft_args) + preemphasis_coeff = preprocessing_settings['processing_parameters']['preemphasis_coeff'] + n_fft = 2048 + if 'n_fft' in stft_args: + n_fft = stft_args['n_fft'] + + + os.makedirs(output_path, exist_ok=True) + mix_count = 0 + for _ in tqdm.trange(mixer.epoch_size()): + spec, bin_masks, source_specs, uids, snrs = next(mixer) + model_spec = spec + spec = spec[0] + bin_masks = bin_masks[0] + source_specs = source_specs[0] + uids = uids[0] + snrs = snrs[0] + + # print('SNR of mix {}: {}'.format(mix_count + 1, snrs)) + + y_mix = undo_preprocessing(spec, mixer.sample_length_in_bits(), + preemphasis_coeff=preemphasis_coeff, + istft_args=istft_args) + + + # NOTE: this is only to make comparisons to the reconstructed waveforms later + y_mix[-n_fft:] = 0.0 + y_mix = lr.core.resample(y_mix, mixer.sample_rate(), eval_sr, scale=True) + y_mix = standardize_waveform(y_mix) + + filename = os.path.join(output_path, 'mix_{}_snr_{:.2f}.wav'.format(mix_count + 1, snrs)) + lr.output.write_wav(filename, y_mix, eval_sr, norm=True) + + originals = {} + for i, source_spec in enumerate(source_specs): + y = undo_preprocessing(source_spec, mixer.sample_length_in_bits(), + preemphasis_coeff=preemphasis_coeff, + istft_args=istft_args) + # NOTE: this is only to make comparisons to the reconstructed waveforms later + y[-n_fft:] = 0.0 + y = lr.core.resample(y, mixer.sample_rate(), eval_sr, scale=True) + y = standardize_waveform(y) + + originals[i] = y + + # use dc-head of model + clustering to source-separate the spectrogram + source_specs = chimera_clustering_separate(model_spec, model, mixer.number_of_samples_in_mixes()) + + for i, source_spec in enumerate(source_specs): + y = undo_preprocessing(source_spec, mixer.sample_length_in_bits(), + preemphasis_coeff=preemphasis_coeff, + istft_args=istft_args) + # NOTE: this is only because the masking creates a chirp in the last + # fft frame (likely due to the binary mask) + y[-n_fft:] = 0.0 + y = lr.core.resample(y, mixer.sample_rate(), eval_sr, scale=True) + y = standardize_waveform(y) + + # match this waveform with an original source waveform + min_key = 0 + min_mse = np.inf + for key in originals: + mse = np.mean((y - originals[key])**2) + if mse < min_mse: + min_key = key + min_mse = mse + + # print('Separated sample for source {}'.format(i + 1)) + filename = os.path.join(output_path, 'mix_{}_original_source_{}.wav'.format(mix_count + 1, min_key + 1)) + lr.output.write_wav(filename, originals[min_key], eval_sr, norm=True) + filename = os.path.join(output_path, 'mix_{}_dc_separated_source_{}.wav'.format(mix_count + 1, min_key + 1)) + lr.output.write_wav(filename, y, eval_sr, norm=True) + + y_original = originals.pop(min_key, None) + if y_original is None: + print("something went horribly wrong") + + # use mi-head of model to source-separate the spectrogram + source_specs = chimera_mask(model_spec, model)[0] + + for i in range(source_specs.shape[2]): + source_spec = source_specs[:, :, i] + + y = undo_preprocessing(source_spec, mixer.sample_length_in_bits(), + preemphasis_coeff=preemphasis_coeff, + istft_args=istft_args) + # NOTE: this is only because the masking creates a chirp in the last + # fft frame (likely due to the binary mask) + y[-n_fft:] = 0.0 + y = lr.core.resample(y, mixer.sample_rate(), eval_sr, scale=True) + y = standardize_waveform(y) + + filename = os.path.join(output_path, 'mix_{}_mi_separated_source_{}.wav'.format(mix_count + 1, i + 1)) + lr.output.write_wav(filename, y, eval_sr, norm=True) + + mix_count += 1 + + +if __name__ == '__main__': + main() diff --git a/magnolia/python/inference/denoising/RatioMaskCluster/separate_sample_from_mix.py b/magnolia/python/inference/denoising/RatioMaskCluster/separate_sample_from_mix.py new file mode 100644 index 0000000..bdec6f1 --- /dev/null +++ b/magnolia/python/inference/denoising/RatioMaskCluster/separate_sample_from_mix.py @@ -0,0 +1,170 @@ +# Generic imports +import os +import argparse +import logging.config +import json + +import numpy as np +import pandas as pd +import librosa as lr + +# Import the RatioMaskSCE separation model +from magnolia.models import make_model + +# Import utilities for using the model +from magnolia.utils.postprocessing import convert_preprocessing_parameters +from magnolia.preprocessing.preprocessing import undo_preprocessing +from magnolia.training.data_iteration.mix_iterator import MixIterator +from magnolia.utils.clustering_utils import chimera_clustering_separate, chimera_mask + + +def standardize_waveform(y): + return (y - y.mean())/y.std() + + +def main(): + # parse command line arguments + parser = argparse.ArgumentParser(description='Denoise mixed sample using the RatioMaskSCE network.') + # parser.add_argument('--model_settings', '-s', + # default='../../../../data/models_settings/chimera_template.json', + # help='model settings JSON file') + # parser.add_argument('--_settings', '-s', + # default='../../../../data/models_settings/chimera_template.json', + # help='model settings JSON file') + parser.add_argument('--logger_settings', '-l', + default='../../../../data/logging_settings/logging.conf', + help='logging configuration file') + args = parser.parse_args() + + # Load logging configuration + logging.config.fileConfig(args.logger_settings) + logger = logging.getLogger('model') + + # from model settings + model_params = { + 'layer_size': 500, + 'embedding_size': 10, + 'alpha': 0.9, # try 0.9 + 'nonlinearity': 'tf.tanh', + 'num_reco_sources': 2, + 'normalize': False, + 'collapse_sources': False, + } + uid_settings = '/local_data/magnolia/pipeline_data/date_2017_09_27_time_13_25/settings/assign_uids_LibriSpeech_UrbanSound8K.json' + model_save_base = '/local_data/magnolia/experiment_data/date_2017_09_28_time_13_14/aux/model_saves/mask_sce' + + model_location = '/cpu:0' + model_settings = '' + mixes = ['/local_data/magnolia/pipeline_data/date_2017_09_27_time_13_25/settings/mixing_LibriSpeech_UrbanSound8K_test_in_sample.json'] + from_disk = True + mix_number = 1010 + output_path = '/local_data/magnolia/experiment_data/date_2017_09_28_time_13_14/aux/sample_wav_files/mask_sce' + + + os.makedirs(output_path, exist_ok=True) + + mixer = MixIterator(mixes_settings_filenames=mixes, + batch_size=1, + read_waveform=False, + from_disk=from_disk) + + # get frequency dimension + frequency_dim = mixer.sample_dimensions()[0] + + # get number of sources + settings = json.load(open(uid_settings)) + uid_file = settings['output_file'] + uid_csv = pd.read_csv(uid_file) + number_of_sources = uid_csv['uid'].max() + 1 + + model_params['F'] = frequency_dim + model_params['num_training_sources'] = number_of_sources + config = {'model_params': model_params, + 'device': model_location} + model = make_model('RatioMaskSCE', config) + + model.load(model_save_base) + + assert(mix_number <= mixer.epoch_size()) + + mix_settings = json.load(open(mixes[0])) + + signal = mix_settings['signals'][0] + preprocessing_settings = json.load(open(signal['preprocessing_settings'])) + stft_args = preprocessing_settings['processing_parameters']['stft_args'] + istft_args = convert_preprocessing_parameters(stft_args) + preemphasis_coeff = preprocessing_settings['processing_parameters']['preemphasis_coeff'] + n_fft = 2048 + if 'n_fft' in stft_args: + n_fft = stft_args['n_fft'] + + + for i in range(mix_number): + spec, bin_masks, source_specs, uids, snrs = next(mixer) + + model_spec = spec + spec = spec[0] + bin_masks = bin_masks[0] + source_specs = source_specs[0] + uids = uids[0] + snrs = snrs[0] + + print('SNR of this mix: {}'.format(snrs)) + + y_mix = undo_preprocessing(spec, mixer.sample_length_in_bits(), + preemphasis_coeff=preemphasis_coeff, + istft_args=istft_args) + + # NOTE: this is only to make comparisons to the reconstructed waveforms later + y_mix[-n_fft:] = 0.0 + y_mix = standardize_waveform(y_mix) + + # print('Mixed sample') + lr.output.write_wav(os.path.join(output_path, 'mix_{}.wav'.format(mix_number)), y_mix, mixer.sample_rate(), norm=True) + + for i, source_spec in enumerate(source_specs): + y = undo_preprocessing(source_spec, mixer.sample_length_in_bits(), + preemphasis_coeff=preemphasis_coeff, + istft_args=istft_args) + + # NOTE: this is only to make comparisons to the reconstructed waveforms later + y[-n_fft:] = 0.0 + y = standardize_waveform(y) + + # print('Sample for source {}'.format(i + 1)) + lr.output.write_wav(os.path.join(output_path, 'mix_{}_original_source_{}.wav'.format(mix_number, i + 1)), y, mixer.sample_rate(), norm=True) + + source_specs = chimera_clustering_separate(model_spec, model, mixer.number_of_samples_in_mixes()) + + for i, source_spec in enumerate(source_specs): + y = undo_preprocessing(source_spec, mixer.sample_length_in_bits(), + preemphasis_coeff=preemphasis_coeff, + istft_args=istft_args) + + # NOTE: this is only because the masking creates a chirp in the last + # fft frame (likely due to the mask) + y[-n_fft:] = 0.0 + y = standardize_waveform(y) + + # print('Separated sample for source {}'.format(i + 1)) + lr.output.write_wav(os.path.join(output_path, 'mix_{}_dc_separated_{}.wav'.format(mix_number, i + 1)), y, mixer.sample_rate(), norm=True) + + source_specs = chimera_mask(model_spec, model)[0] + + for i in range(source_specs.shape[2]): + source_spec = source_specs[:, :, i] + y = undo_preprocessing(source_spec, mixer.sample_length_in_bits(), + preemphasis_coeff=preemphasis_coeff, + istft_args=istft_args) + + # NOTE: this is only because the masking creates a chirp in the last + # fft frame (likely due to the mask) + y[-n_fft:] = 0.0 + y = standardize_waveform(y) + + # print('Separated sample for source {}'.format(i + 1)) + lr.output.write_wav(os.path.join(output_path, 'mix_{}_mi_separated_{}.wav'.format(mix_number, i + 1)), y, mixer.sample_rate(), norm=True) + + +if __name__ == '__main__': + main() diff --git a/magnolia/python/models/dnndenoise/cluster_mask.py b/magnolia/python/models/dnndenoise/cluster_mask.py index ea33db2..7c781db 100644 --- a/magnolia/python/models/dnndenoise/cluster_mask.py +++ b/magnolia/python/models/dnndenoise/cluster_mask.py @@ -35,7 +35,9 @@ def initialize(self): self.num_reco_sources = self.config['model_params']['num_reco_sources'] # should always be 2 self.num_training_sources = self.config['model_params']['num_training_sources'] self.layer_size = self.config['model_params']['layer_size'] + self.fuzzifier = self.config['model_params']['fuzzifier'] self.embedding_size = self.config['model_params']['embedding_size'] + self.auxiliary_size = self.config['model_params']['auxiliary_size'] self.normalize = self.config['model_params']['normalize'] self.alpha = self.config['model_params']['alpha'] self.nonlinearity = eval(self.config['model_params']['nonlinearity']) @@ -60,14 +62,21 @@ def build_graph(self, graph): self.y = tf.placeholder("float", [None, None, self.F, None]) # Placeholder tensor for the unscaled labels/targets self.y_clean = tf.placeholder("float", [None, None, self.F, None]) - + # Placeholder for the speaker indicies self.I = tf.placeholder(tf.int32, [None, None]) - + # Define the speaker vectors to use during training self.speaker_vectors = tf_utils.weight_variable( [self.num_training_sources, self.embedding_size], tf.sqrt(2/self.embedding_size)) + if self.auxiliary_size > 0: + # Define the auxiliary vectors to use during training + self.auxiliary_vectors = tf_utils.weight_variable( + [self.auxiliary_size, self.auxiliary_size], + tf.sqrt(2/self.auxiliary_size)) + else: + self.auxiliary_vectors = None # Model methods self.network @@ -91,7 +100,7 @@ def learn_from_epoch(self, epoch_id, unscaled_spectral_sum_batch, scaled_spectral_sum_batch, spectral_masks_batch, spectral_sources_batch = batch_formatter(batch[0], batch[1], batch[2]) # should be dimensions of (batch size, source) uids_batch = batch[3] - + # override ids for simply signal/noise if self.collapse_sources: uids_batch[:, 0] = 0 @@ -118,7 +127,7 @@ def learn_from_epoch(self, epoch_id, unscaled_spectral_sum_batch, scaled_spectral_sum_batch, spectral_masks_batch, spectral_sources_batch = batch_formatter(vbatch[0], vbatch[1], vbatch[2]) # dimensions of (batch size, source) uids_batch = vbatch[3] - + # override ids for simply signal/noise if self.collapse_sources: uids_batch[:, 0] = 0 @@ -173,9 +182,12 @@ def network(self): BLSTM layers followed by a dense layer giving a set of T-F vectors of dimension embedding_size """ + + m = self.fuzzifier # Get the shape of the input shape = tf.shape(self.X) + shapeI = tf.shape(self.I) # BLSTM layer one BLSTM_1 = tf_utils.BLSTM_(self.X, self.layer_size, 'one', @@ -201,10 +213,74 @@ def network(self): z = tf.reshape(feedforward, [shape[0], shape[1], self.F, self.embedding_size]) - # SCE head + # indices helpers for fuzzy c-means + #with tf.device('/cpu:0'): + # known_sources_init = np.zeros(self.num_training_sources) + # known_sources = tf.get_variable('known_sources', + # dtype=tf.bool, trainable=False, + # initializer=tf.constant(known_sources_init, dtype=tf.bool)) + known_sources_init = np.zeros(self.num_training_sources) + known_sources = tf.get_variable('known_sources', + dtype=tf.bool, trainable=False, + initializer=tf.constant(known_sources_init, dtype=tf.bool)) + current_sources_indices, _ = tf.unique(tf.reshape(self.I, shape=[shapeI[0]*shapeI[1]])) + #with tf.device('/cpu:0'): + # known_sources = tf.scatter_update(known_sources, current_sources_indices, + # tf.fill(tf.shape(current_sources_indices), True)) + # + # current_sources = tf.cast(tf.scatter_nd(tf.expand_dims(current_sources_indices, -1), + # tf.ones_like(current_sources_indices, dtype=tf.int32), + # [self.num_training_sources]), + # dtype=tf.bool) + known_sources = tf.scatter_update(known_sources, current_sources_indices, + tf.fill(tf.shape(current_sources_indices), True)) + + current_sources = tf.cast(tf.scatter_nd(tf.expand_dims(current_sources_indices, -1), + tf.ones_like(current_sources_indices, dtype=tf.int32), + [self.num_training_sources]), + dtype=tf.bool) + + batch_sources = tf.reshape(tf.gather(self.speaker_vectors, tf.reshape(self.I, shape=[shapeI[0]*shapeI[1]])), shape=[shapeI[0], shapeI[1]]) + + + # clustering head embedding = self.nonlinearity(z) # Normalize the T-F vectors to get the network output embedding = tf.nn.l2_normalize(embedding, 3) + + # batch, features, embedding + embeddings = tf.reshape(embedding, + [shape[0], shape[1]*self.F, self.embedding_size]) + + # compute fuzzy assignments + # batch, nfeatures, nsources + if self.auxiliary_vectors is None: + squared_diffs_batch = tf.reduce_sum(tf.square(tf.expand_dims(embeddings, 2) - tf.expand_dims(batch_sources, 1)), -1) + squared_diffs_known = tf.reduce_sum(tf.square(tf.expand_dims(embeddings, 2) - tf.expand_dims(tf.expand_dims(tf.boolean_mask(self.speaker_vectors, known_sources), 0), 0)), -1) + squared_diffs_current = tf.reduce_sum(tf.square(tf.expand_dims(embeddings, 2) - tf.expand_dims(tf.expand_dims(tf.boolean_mask(self.speaker_vectors, current_sources), 0), 0)), -1) + diffs_pow_matrix_batch = tf.pow(squared_diffs_batch, 1./(m - 1.)) + diffs_pow_matrix_known = tf.pow(squared_diffs_known, 1./(m - 1.)) + diffs_pow_matrix_current = tf.pow(squared_diffs_current, 1./(m - 1.)) + else: + # NOTE: true/aux refers to both the coordinates and cluster centers + true_embeddings = embeddings[:, :, :-self.auxiliary_size] + aux_embeddings = embeddings[:, :, -self.auxiliary_size:] + true_embeddings_l2 = tf.reduce_sum(tf.square(true_embeddings), axis=-1) + aux_embeddings_l2 = tf.reduce_sum(tf.square(aux_embeddings), axis=-1) + true_squared_diffs_batch = tf.reduce_sum(tf.square(tf.expand_dims(true_embeddings, 2) - tf.expand_dims(batch_sources, 1)), -1) + true_squared_diffs_known = tf.reduce_sum(tf.square(tf.expand_dims(true_embeddings, 2) - tf.expand_dims(tf.expand_dims(tf.boolean_mask(self.speaker_vectors, known_sources), 0), 0)), -1) + true_squared_diffs_current = tf.reduce_sum(tf.square(tf.expand_dims(true_embeddings, 2) - tf.expand_dims(tf.expand_dims(tf.boolean_mask(self.speaker_vectors, current_sources), 0), 0)), -1) + aux_squared_diffs = tf.reduce_sum(tf.square(tf.expand_dims(aux_embeddings, 2) - tf.expand_dims(tf.expand_dims(self.auxiliary_vectors, 0), 0)), -1) + diffs_pow_matrix_batch = tf.pow(true_squared_diffs_batch + tf.expand_dims(aux_embeddings_l2, -1), 1./(m - 1.)) + diffs_pow_matrix_known = tf.concat([tf.pow(true_squared_diffs_known + tf.expand_dims(aux_embeddings_l2, -1), 1./(m - 1.)), + tf.pow(aux_squared_diffs + tf.expand_dims(true_embeddings_l2, -1), 1./(m - 1.))], axis=2) + diffs_pow_matrix_current = tf.concat([tf.pow(true_squared_diffs_current + tf.expand_dims(aux_embeddings_l2, -1), 1./(m - 1.)), + tf.pow(aux_squared_diffs + tf.expand_dims(true_embeddings_l2, -1), 1./(m - 1.))], axis=2) + + + W = tf.reciprocal(diffs_pow_matrix_current*tf.expand_dims(tf.reduce_sum(tf.reciprocal(diffs_pow_matrix_known), -1), -1), name='W') + clustering_factors = tf.squeeze(tf.reciprocal(diffs_pow_matrix_batch*tf.expand_dims(tf.reduce_sum(tf.reciprocal(diffs_pow_matrix_known), -1), -1)), name='clustering_factors', axis=-1) + # MI head # Feedforward layer @@ -212,8 +288,12 @@ def network(self): [1, 1, self.embedding_size, self.num_reco_sources]) # perform a softmax along the source dimension mi_head = tf.nn.softmax(feedforward_fc, dim=3) - - return embedding, mi_head + + if self.auxiliary_vectors is None: + return embedding, mi_head, W, squared_diffs_current + else: + return embedding, mi_head, W[:, :, :-self.auxiliary_size], true_squared_diffs_current + tf.expand_dims(aux_embeddings_l2, -1) + @tf_utils.scope_decorator def cost(self): @@ -225,45 +305,14 @@ def cost(self): # Get the shape of the input shape = tf.shape(self.y) - sce_output, mi_output = self.network - - # Reshape I so that it is of the correct dimension - I = tf.expand_dims( self.I, axis=2 ) - - # Normalize the speaker vectors and collect the speaker vectors - # correspinding to the speakers in batch - if self.normalize: - speaker_vectors = tf.nn.l2_normalize(self.speaker_vectors, 1) - else: - speaker_vectors = self.speaker_vectors - Vspeakers = tf.gather_nd(speaker_vectors, I) - - # Expand the dimensions in preparation for broadcasting - Vspeakers_broad = tf.expand_dims(Vspeakers, 1) - Vspeakers_broad = tf.expand_dims(Vspeakers_broad, 1) - embedding_broad = tf.expand_dims(sce_output, 3) - - # Compute the dot product between the emebedding vectors and speaker - # vectors - dot = tf.reduce_sum(Vspeakers_broad * embedding_broad, 4) - - # Compute the cost for every element - sce_cost = -tf.log(tf.nn.sigmoid(self.y * dot)) - - # Average the cost over all speakers in the input - sce_cost = tf.reduce_mean(sce_cost, 3) - - # Average the cost over all batches - sce_cost = tf.reduce_mean(sce_cost, 0) + cluster_output, mi_output, W, squared_diffs = self.network - # Average the cost over all T-F elements. Here is where weighting to - # account for gradient confidence can occur - sce_cost = tf.reduce_mean(sce_cost) + clustering_loss = tf.reduce_mean(tf.pow(W, self.fuzzifier)*squared_diffs) # broadcast product along source dimension mi_cost = tf.square(self.y_clean - mi_output*tf.expand_dims(self.X_clean, -1)) - return self.alpha*sce_cost + (1.0 - self.alpha)*tf.reduce_mean(mi_cost) + return self.alpha*clustering_loss + (1.0 - self.alpha)*tf.reduce_mean(mi_cost) @tf_utils.scope_decorator def optimizer(self): diff --git a/magnolia/python/models/model_base.py b/magnolia/python/models/model_base.py index 7c45e9b..4fe7b62 100644 --- a/magnolia/python/models/model_base.py +++ b/magnolia/python/models/model_base.py @@ -92,7 +92,7 @@ def __init__(self, config): # Add all the other common code for the initialization here gpu_options = tf.GPUOptions(allow_growth=True) - sessConfig = tf.ConfigProto(gpu_options=gpu_options) + sessConfig = tf.ConfigProto(allow_soft_placement=True, gpu_options=gpu_options) self.sess = tf.Session(config=sessConfig, graph=self.graph) self.make_summaries = 'summary_config' in self.config if self.make_summaries: diff --git a/magnolia/python/training/denoising/RatioMaskCluster/training.py b/magnolia/python/training/denoising/RatioMaskCluster/training.py index a096a42..a577874 100644 --- a/magnolia/python/training/denoising/RatioMaskCluster/training.py +++ b/magnolia/python/training/denoising/RatioMaskCluster/training.py @@ -13,6 +13,7 @@ # Import utilities for using the model from magnolia.training.data_iteration.mix_iterator import MixIterator from magnolia.utils.training import preprocess_chimera_batch +#from magnolia.utils.tf_utils import double_learnable_relu def main(): @@ -39,17 +40,19 @@ def main(): stop_threshold = 10000 # validate every number of these batches validate_every = 100 - train_batchsize = 256 + train_batchsize = 8 train_mixes = ['/local_data/magnolia/pipeline_data/date_2017_09_27_time_13_25/settings/mixing_LibriSpeech_UrbanSound8K_train.json'] train_from_disk = False - validate_batchsize = 200 + validate_batchsize = 5 validate_mixes = ['/local_data/magnolia/pipeline_data/date_2017_09_27_time_13_25/settings/mixing_LibriSpeech_UrbanSound8K_validate.json'] validate_from_disk = False model_params = { 'layer_size': 500, 'embedding_size': 10, - 'alpha': 0.9, # try 0.9 + 'auxiliary_size': 0, + 'alpha': 0.1, # try 0.9 'nonlinearity': 'tf.tanh', + 'fuzzifier': 2, 'num_reco_sources': 2, 'normalize': False, 'collapse_sources': False, diff --git a/magnolia/python/utils/tf_utils.py b/magnolia/python/utils/tf_utils.py index 1228c0c..6bcea7b 100644 --- a/magnolia/python/utils/tf_utils.py +++ b/magnolia/python/utils/tf_utils.py @@ -44,6 +44,18 @@ def bias_variable(shape, value=0.1): initial = tf.constant(value, shape=shape) return tf.Variable(initial) +def double_learnable_relu(preactivations, initial_alphas=[1.0, 0.1], name=None): + """Relu activation with two learnable slopes""" + if name is not None: + name1 = '{}/alpha1'.format(name) + name2 = '{}/alpha2'.format(name) + else: + name1 = 'alpha1' + name2 = 'alpha2' + alpha1 = tf.get_variable(name1, initializer=tf.constant(initial_alphas[0])) + alpha2 = tf.get_variable(name2, initializer=tf.constant(initial_alphas[1])) + return tf.where(preactivations >= 0.0, x=alpha1*preactivations, y=alpha2*preactivations) + def leaky_relu(x, alpha=0.1): """ Leaky rectified linear unit. Returns max(x, alpha*x) From 16d01b6643893e811220cffa5523c02df3626ca4 Mon Sep 17 00:00:00 2001 From: Jeffrey Hetherly Date: Tue, 16 Jan 2018 18:54:35 +0000 Subject: [PATCH 06/16] small touches to plotting --- magnolia/python/analysis/comparison_plot.py | 17 +++++++++++------ .../out_of_set_sdr_delta_versus_input_snr.pdf | Bin 17732 -> 17827 bytes ...out_of_set_sdr_delta_versus_noise_type.pdf | Bin 18223 -> 18116 bytes 3 files changed, 11 insertions(+), 6 deletions(-) diff --git a/magnolia/python/analysis/comparison_plot.py b/magnolia/python/analysis/comparison_plot.py index 157ceae..45e8372 100644 --- a/magnolia/python/analysis/comparison_plot.py +++ b/magnolia/python/analysis/comparison_plot.py @@ -115,11 +115,13 @@ def make_sdr_delta_versus_noise_source_plot(models, df_base_name): ax.xaxis.label.set_size(15) ax.yaxis.label.set_size(15) - ylim = [-0.5, ax.get_ylim()[1]] + ylim = [-0.5, 1.2*ax.get_ylim()[1]] #ylim[0] = -0.5 ax.set_ylim(ylim) #plt.axis([0, 11, -.5, 16]) - plt.legend(fontsize=12, edgecolor='black') + plt.legend(fontsize=12, edgecolor='black', + loc='upper center', ncol=3, mode='expand') + #ax.legend(bbox_to_anchor=(1.5, 1.5)) plt.tight_layout() plt.savefig('{}_sdr_delta_versus_noise_type.pdf'.format(df_base_name), format='pdf') @@ -195,11 +197,12 @@ def make_sdr_delta_versus_input_snr_plot(models, df_base_name, bins): ax.xaxis.label.set_size(15) ax.yaxis.label.set_size(15) - ylim = [-0.5, ax.get_ylim()[1]] + ylim = [-0.5, 1.2*ax.get_ylim()[1]] #ylim[0] = -0.5 ax.set_ylim(ylim) #plt.axis([0, 11, -.5, 16]) - plt.legend(fontsize=12, edgecolor='black') + plt.legend(fontsize=12, edgecolor='black', + loc='upper center', ncol=3, mode='expand') plt.tight_layout() plt.savefig('{}_sdr_delta_versus_input_snr.pdf'.format(df_base_name), format='pdf') @@ -234,13 +237,13 @@ def main(): 'name': 'SCE + Mask MI', 'in_set': '/local_data/magnolia/experiment_data/date_2017_09_28_time_13_14/aux/evaluations/bss/mask_sce/mi_in_sample_test_sdr_summary.csv', 'out_of_set': '/local_data/magnolia/experiment_data/date_2017_09_28_time_13_14/aux/evaluations/bss/mask_sce/mi_out_of_sample_test_sdr_summary.csv', - 'color': '#3D5A80' + 'color': '#CA054D' }, { 'name': 'SCE + Mask Clustering', 'in_set': '/local_data/magnolia/experiment_data/date_2017_09_28_time_13_14/aux/evaluations/bss/mask_sce/dc_in_sample_test_sdr_summary.csv', 'out_of_set': '/local_data/magnolia/experiment_data/date_2017_09_28_time_13_14/aux/evaluations/bss/mask_sce/dc_out_of_sample_test_sdr_summary.csv', - 'color': '#0C0A3E' + 'color': '#393E41' }, { 'name': 'SCE', @@ -258,6 +261,8 @@ def main(): make_sdr_delta_versus_input_snr_plot(models, 'out_of_set', bins) make_sdr_delta_versus_noise_source_plot(models, 'out_of_set') #make_sdr_delta_versus_sex_plot(models, 'out_of_set') + make_sdr_delta_versus_input_snr_plot(models, 'in_set', bins) + make_sdr_delta_versus_noise_source_plot(models, 'in_set') if __name__ == '__main__': diff --git a/magnolia/python/analysis/out_of_set_sdr_delta_versus_input_snr.pdf b/magnolia/python/analysis/out_of_set_sdr_delta_versus_input_snr.pdf index ae14c539398af7ef4371f229999fddb4eae94f35..06bc917121371d577384738b1714e59d48374dd8 100644 GIT binary patch delta 5392 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 delta 5226 zcmZuxcRbbm|G%!4pk{wA z==cbK@L|*M_;^9Be_?-*GVhHikaIS+D!(T1Z)8t}h6Xi<+zZ+Sdxk_`R%+{}=-#x) zr`G?@Bu7YF+w^RkwFa&11a5i1Up^eNf5K*;;Z5}CPR|(1Xp>WxF;W5yW|6ak<&vc+ z!g|tDvywhrm9bUb_n0M%KPnOS*PWVy(zV z#dT_5r2fe8E#Tn0nP|D8TBQ_8lXIva&bM)avv+&5q{5! zB+_fnA0+=rg6L)as3ysUT8j2bQDD|8QIB!2SA~2vCqEeE%KOZj+l-}xdf43l^Gebv z!H&}`4UwV|C|O2htDOSUO8pXTQr6kRao+a6eg<8@K$JmR)1kXxoe%ki-&K}aeOrJFlT?I-_sve68Gk8p``M!J{N2OF=m zjJ~T6(7-SI%?PR*A zTg1hCqL$)Ch4O&fCyP9JqC=#JXoqxZMd<>nHkMwvH+6~?e_l%$w3RpWf(T(vuUFCS zBmx*b>49N8x1K8M5QVH(a-!46kE;DJ#br~=!U+GDO7BK-AWNo5is`iZa_N1#V;EwH zhSZn0swk{WBClg^Me!{DR_2|g&eg)w4G*U#Nflb|I#K?%+cA+OqIZTbPtC-VLb^P3KteA#QqG1p$ptvPe(K)}K2)LyvB}}x9nSa=2GDpsJ zjC&|Ccdvb*qrmdE_o(zq(bB1Bd^%PTt zR{T>nxqci)LD8W^v&v!2)$jb(BjK6nm5iERQQj2q$pYznyg@)#zz_gQon z6J>rh(WM^|q7?dnc5Qs}qUq%f(2k&B*!e35Z%$E5n2!aJ^1_qb8-zbBS}sACTP$nTz0iLHuGH--B1r$d)-;T z0d`(6Q?%mJFZ+j8Uv4K&rj{5Ckn90XlqY+2(zW|eI(dEk7RudDtV(awjbrfO+}<=R+m~FMrT@1* z1E{*<6}A(s&u0s|Z;LhLFazO}qvh2%%@<3*^;X{5RrFxOXNE*^=>-1fJbI$c$^He@ zS(^N`wv=b~x2D5v;r>q-2zG)0v!DJ@yOAbXA7=QN8DO!Hgmq&nlH=X*D|vW06QKPd zJibwDE8k~0c>3paTj?Xjylrv^Nh?GR&jW7B+}wT`mE2LKzNNn$CNB3}Cr3Cbd`AkX z+Ot)h@WXf)Q*GL$KpSR7Q2bK$GREik3Nozr28to8QcC7oS zg&)Pd&5&*N`&AQ5VSeXopg^@NO3gQ$E3nx>LquEpuGy8T{TZc`H_}3MXxTwb$d_013n`SP03%(Iw(@bhtd-;*CNF(BWf|i@6 zOl3@)q^>oQrti>~-x|O{2j9~vG;+E%uDLbe}{YC)QpG)u{U zUMHPfHs?eyRFB4{Qe`~eU3QJRBph3H^wqzeun3+GpmN9gq^DD`71>g)Y+KeshsE;H zyP8zqCi#ec`WUzg1%8`edU2NPA4&) z0gYtQUlv`9FxDtpVcnp=YG&A5Bc}Y_bvlQ^i@T+IQoQv^51tvIv82@KJKVl5W7UM7 z2GL%RG_f?}#<~SM?E%)fUEeoA!FA$0nAvB zcKouMbS$}+m+^U&(jqQb)vs)v@?-*qRujsor!~{>Nq&F~5`mOhMrRpMm^`Ir&C>Mc z?pwP-#glzFt2F(cs|i5zjoe_!b|DbsHPSMv&Vg4Z_%+?I4KqL!uDS+|wm8;so-Bp= z5n-Z$BJ;93-FUUR`eYH!V_TjaFwT7m(XHny)!WXXDbV4|+Gx($i7_Dx#3f6-qmH>8 zpHTM5S~BLl%9P1djTP7W>76>sJrSD^j;6lfRSU!HRRwO}6)tK%#@<|gCP0`{pZ=&_ z)!JY(jIYS%Wb=FVmg7S~ElF=wi$Pobk`3~_nn6$l?ThmI*R^PC)vlfvov3$O?2`>6 z;N>rn);V!v37*oBrsC&P`j4o`mr5Z(LxSS;`=!+@w{4`$W2W7$CY*(|7!RN1c0!*C z#LGwbc~LYNcnlSuT05kaU%N_Z$W4NxwXV%SS4MQ0_u(iF3>sG=aF;#pH!9$*(n0&> zJm82nZz3kMpRp=$^b12DZtyvCfPcYx9ZRV{6~kW1%bsUJ{|;hIMqOfvXE7~sdqV4D z5a(W7gLbc#!b*G$X1o^~Wi|i`faY@4+9aw=t+U%P(Oh9FGX2Bt`J->{_*L z&l?G><*sEh`(+87c&dt_T73zA;qPgyhLQ~%8ko3PE=jKG&bfywe|dEB@C?H|?9waKl!$(!G#j<=~l?%nn|=-U3Qs3WYp{K@cc~vls>q`N1RnYbyv628EwaM#5322ZSJz(4Wcw$wk3W zFAhN}V9xR|@P8|J{imKN*nhxZ7z%+tJq+sqDX0KN{k!1LTm=LftbT!jL7r)khAEuI z5b)D_L(pi%87VLj1UY%hd%oXC-}|3)z22Yuy07>3zTWqJ?j&)4!iS-kiB{z)D= z;;ztI#(T*Gex1L-2^%I~s=g|m@!nl8-S7(E-Mpf4#Ru{t+PpRDaCC*6?yy7*+B>>% zY0Ifn>slwh<)j$R}dji|>~K{fWl%-M;l6im;&X19c6VeNKluIJ8ov-ORppMXt_; z?mi1vYTTl9n;VE}w{|HmvZ8%sp}gN5SM&G{j5zzDAcgBbA zB-mGbOqj(GQa{V}S>`mqT!YZ5e!v{!{@11IiLYQ`WeBO-srssCHK&{S&CqMgZPB#o zCs9h5pD~tYLAGwE7Uf)PCI_WT@ktRLs`IHH z)J_*367DCT@D0b)!pj=mcW)OT3{6~Bu1gERQ12%~9~J|b`eytO)h1g74Q`q0&SRWn z!#6q4dACGfpm^$DASm=s&FFc&rhin^z5H3)8}jHmrK8s+!Ew~D5^@4#8XLS-HYX~q zA~Wj&2Bbl2Q;~3unvNxcY=&GeQOk(=9zD~Xtjm}CqWgtna>t?WF>yy0G500)`;rAM4Lfcr7inFfN0xz1-8G21+9M2>fWRz-mODz|2U(Ab7kJ(&ZK@5$r1ynTW>Aelc ziAnh}6kngyG|9{`C=|dAeO+2Dpv6?qh4c29`}9`k3;=(`t z8pcxYW%d_uw@l?-(X-Psm&IH^ej`BJ?TLZ_@WE7V`CsF9&TQi7Us z(_J8DbidyJ=-*NrW@j5V#M?wc zrI=<2f~>>8)eq+=K5~R2F=cjtqZ-W$(-OEyZrWU6!N z)ydX9C8(a7$#L?|mk353y?!eP_hQSGeRDW1wkncb0Qfdl8xGZXQkBW6z?rY8L4(=_ zW@4Z5k+`-8yHfy)tyFx9p~nN;2)c;I9#;e86Z?Hn3z;GA=YUiTFDiPLuF~h;Eyu3;#)Zh$HC!a_h6SP>^x3@4CFrBUG~Ha()8Z}pvgGQx zFl(W^RX#l<-wY}gUaXixH)jM0wAQDjK*fGw+Filr&XPxWUS@W1={=Hts2N4N6DaxC zvQ_4gMy?%%(fk4}Ub%i(TOSDLydIbeG|A&gDDEb|8=2N{-!_~u3%StX$B|m1%=MsGk=zXo1REboI`!D!(oI-NW-@nFJs|C4=t@SBXFw&w zHykiozgzTo`8w58-Rm7uRk9qGb?yd{lV;B?>9#MTEVIiGI-O`1n=}nn@Q8>~sczR% z3@M$6F#Dy;=lq)$kDtC_qX0PiCa&oY|GzN2z%<`d^2n3n)27FaMV)Dn%gZ0^wOL#-t?6WpG<|yk9d-PMpRrdQptZ;b?#*5!;}Ib;gDtJRfBE9C5d-8k-R=x%7q>MG znM9GU6RYJ8zU`y|{!Z%AX=CGYyVZiTxm%yBQCnl}2H1Njev4USa>O&Z62YuxFy#__ zub)56WXQgmd8oN_5kW@5j}8ITtdD7Qr@lCo2csE8CV%QxHltc z$Doz9WE4n$kIcr9M^YXGrGTXr)H)3HK$-4k-A&1K$oOiX&KpS9u-+~ZJm;}%Pr-gH);=IwA%&7Y5iWahhTJy%vj9qg(esA19?9p!Um9X{s)xMb3Mf_qBzw@WH zJm0SLYrXtHT%0@dC(Jyp-DbLg`*+ zMIF6qS`1GM_fg)@@OxwLvmKI}?4Er8zP3}SUus)!?t=M|?b1oS7TPiDl=0@N6-uh@ zM4A#NzPBMsaJp%HY;?t+{C+3Sm-muxWF;wx719v{yIUeV+t9D(P z{4_G~nLM6rv|0Dgm0h1|vPG+ot&Vvc%-tn_IGL7)rX~<^;ER{p@h?fw%e5Db`5?Q* zBTOT?Vw_?OF_&9ZnOrHG+gJDyUMlai^T@)+dEW2=EAb19YNyiWxJJ@l99&fV`x&+q z?Y@0~*7I(CweNFN_GW`)il!bJWPhF-r|c=gN!D=HlR7QDQT-j?F>$uPgoMtFx!Kkh zCl<%`?rr&ViK9{H_16-OP;2K)FnZ>}`KIA-6L<=VUe{$l_+F>!{6~W5Mq*E|XFn0cVq;e--1kV-PYg9Ja3= zZ`yI%-T2be_sn6yc>eWi{vC&*{HT_gK_ha(99P5)7ras7=hwBR-I&+#XS8<=Ln(z^ zXG9ag-MzY6nyn1vo7+7HT9YEAa?3B8%B;ZDnOH>i&ecxoqK?^G*vLD5$kTxVJv2k> zE?Y-GX)ej0o&$pI)NyL6Vmb*MU9pCU^VWa};AV!SkDanyy5=eX+|%+T{jRcp&J1~Y zf@00eaUA0?g}`g7oK-DkUlZ-P!5=d;5sZs4p?yi+WIuTAuv!>2%bg7`pYEP5ZLArx0DUtYnHyOB zhE-q72J6w&zZzeZM~wB$f`^=mP$#dPbV-%biry6OEO#~m1nQI_Wy;jV4qY{ompZ^c z<)V+r5-PQ-&!zd&l3yc^Npig3v1kHh)Sa%;@F?V4<1QTfA?_gq4n zN4AMv<%CjUOtI~if(W0#WRt2*7~_Q3oyCRIKL^^f{e1b=2EuwR1%@H##~6e+%G|ly z;mf|%22&pFkrUdO@6LM7_FhL<4`=Ed^Qz)Ov{hM)0XVOuQ$$s0b!?zPR#v`oh_t(- zVsj%3{j^Q99C9UArBF_y(pM&d=fb$24^mV~TS{Z2D{JT>r~Ervifsg|e6%BC>QtN1 ziI9;Hzw^E<50AZ?dRbC}eV+zKKScz(Xz=wD9;$ zn&Er=))>Xl2J1_(Rg3wDI9N~Shkt15m&xZ_&iM2=uVv;$j2bg-SUI`0av4y{VDD%k zv)rvk$p|>V>U`Fh^BnK9nQxH#U|HMaK9z4Q+n?O#J;iwHuaEluJ>=Z;A)EQdUKe8rJgj&H4CIgZ9#C`vxrU)FCw0iJ+?m!TjtQ}d(oiqP zF=((uOJhfcWcjgI`Z}6S!{wQlNzZ=51ZieD$Yr0?d>3llE6q zzovs64j@jzwXDc?00)B)Q7>yKj|YX+<>5QJLh^TSHH+-tp@oGYm=4rIwo#lGFZq>p z1hA!!x``?8Vc(fAXRdr%;?TCi`ejwe)-;<6w5x-F41yX3tM_^+&+QK`T&bdc7rKQB zo9D~DrI@#p`a*Pbb>LI)?Azrh*G{ZgXOptp%M-BHr4%P}&!!a5fL@~9c$d%pbb%zY zN>ly!P{obAE*Rs|ZHe2L1`U+|QgAFME_AR5OJCs zS@p0h|DKa;{r-&6Z#Ams_3P56^y9y8MP*cSy`uIC^^3XgHcF?NaCiQH_VCevh-8 zMBolNp07@Ly0z2pjH(+6MC;48>oopc&dL$Vcow!k-7oDwyZ$h#@lpwYyZx|e2G_&G zj_gi1eqP#=-0|mY`SJ2w&x`H8cdHHFpT9IqY`zdV*D4jrGz#(s>_^0gc9TpEY%NWgt07%?o8rupram|ENTM;qr}MeKS`l{VY| zeEeu9^xML2sqKQ*vWe z1!o(YHh5|x4=s-K+f5u;ybzMhVP?PKt1i-~*yIIGRNQeCUHz$g?(;dMW$8v97 zu;9zbXqT3_0&-%jT9$=SkRd;;64;0C{A7i%R-l1Af~EMTIu2+ujR+?9ITl^Jeyp6qKB=JXHOeN0Qz8)-}Mo{+$B|v6G?x5Lcvn3vtsH zCC)NXX%Y}>pi~>xND4xA2D?xdz!2&(IFH&v|9v3sOT8`)p&CI9s8SFptrY@dpk_fJ zR1TRI>NFHW{VKCb?UR8}`C&fPC0Piq9LC8&jf6p{89;7%T#X6B1c5=YQnaE~(^BHp zMm61FaXV}{EZR!2DZ2UnF0cjApAiFk3#KN7lFt9sREw(XLAG;8Tm&t0sB8N!k%USO@@g4 zV{`~27WaqLL;~UuWr)N*^U@djKiwe_$USfWs|XT-BJLjv5`pY_j`s?q=`;C#-r;}Dg~Z`81nRifNe&!= NhyzGToix@0{2zF_nu`Dc delta 6478 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 z zUI6rBw%?q!nks$I|AAVvT(@)7^~sGM41eOZKJfkA`kQ?`RZVul`xvLMr0@s_$=5B4 z9q$dYt`S+PJltXiNK_*hG>yeA) z*9rddZXbtE?16SAj1hI`d4P8PP16toRldmlO8Q1#)o<)tR9HpE`N2s)^JuO1f}Grn z`GN-nBVP zHN_yrEzB&Eh2gHy{->h4>L0?zu*~++?!*^823x~iDW&2g|3pppzRLpGI@LU1Ubgak4+pD&?1*$Q9(S-Vh9gW4_T>6+8(AtdZ&Wm8t zG^HDtqI!J?r`!y(trju^1iwm=G#2uBqzRnsKS*6!X>CexQ@nPad^C8e)L#qR)u!_a zJ-N52X3pbGt-q37=ppO?l;5$X&3!$V2zlDy%f&Ifdr;+wI@p+F7VqfToZff@bkeGa z8I^9prw27m{VJ77FyEz*7D0;TS5DiR@I%J$T!5_H(fR7_X&@aj+OhN4<=UQj*LL z8b4IA`{7!~61RRR7DdQ$R?dTMG8LB-Q#&OyZ1Ycd-P7J(z34D;-~_UCuyA#Au{67X zxN^kXf?zN(6zoVeWv8NqpoxYa)-!y|(x}WGoXg$H;6azJ zczlr#PaCAQ^ad`?U@SvrEl8A|`GnMjA2sPc&p&Htvg-#5gIpRtElvWa8odseT2#8Y zt{5t@n*DsvIrmDpz@D@s3cl5w7nJ6P4`qCDLhOAm|Y&=3mu=>I>H2yq{6|vOCm)!*9#I15VVlkcFLdm;}0rox{Ar=OX(1qJi7n6o7>A&Q>T6M z@#)L{n@HPh(B%p^r=6}?Ty$*RO{fX|@N#Anhv&FLE5`S!V$ zRPOR<;wB3N*Tl_E_XTJrBi{sCm4=&JG!nKOG#>{AH?rv_c*mT|iPehe89gPCsjl^K zsiD{ywZf;G@dF_lUH?H;kRn9eToBY<=*#Mdttos`GeeI(T^DMZ6fene-$tm)BRr_? zOJd1uvzCG6fsF&&%f_A=L_bOPk_ng`4FUs(cv!kPf+3C$mSBjRyCWD02ScnJAGm;# zGGK`7Jpves0z)kCS=+dQQD`tk1w_=tT%iI(Y(XVS7$Obu`x63Az96puTZusY{$UM9 z$oyW}fDy>U6$o+gb3#8hQ10EHm8|K~R(6m<+& z2J=5+2sBx&KWkAi*wMY9GAOcZf964Dut>7JP$=+UB%w&yF+xxz;!xi|Hi05#Fu#ib zs6|1K3Is)>$un`Z77HW0{vSN@{^SP*|0DQ2hJqh;0g6JSjx~eALXR;#bA%g55>X`XZ6oKEb?&Hj>OQI-{gS*0>omEF@`~5 z@c)+U5CsN>qmRl8gCelU^U(04zQdqc@`3%YW)Ag-k@q;N0(p#%9|9N}dTbLI26@yb z7##X9yZ Date: Thu, 18 Jan 2018 00:24:23 +0000 Subject: [PATCH 07/16] unity clustering model --- magnolia/python/models/__init__.py | 2 + .../models/dnndenoise/cluster_mask_unified.py | 361 ++++++++++++++++++ 2 files changed, 363 insertions(+) create mode 100644 magnolia/python/models/dnndenoise/cluster_mask_unified.py diff --git a/magnolia/python/models/__init__.py b/magnolia/python/models/__init__.py index 0a65933..4a4b5d0 100644 --- a/magnolia/python/models/__init__.py +++ b/magnolia/python/models/__init__.py @@ -3,6 +3,7 @@ from .dnndenoise.L41_regression_model import L41RegressionModel from .dnndenoise.sce_mask import RatioMaskSCE from .dnndenoise.cluster_mask import RatioMaskCluster +from .dnndenoise.cluster_mask import RatioMaskClusterUnified __all__ = [ "JFLEC", @@ -10,6 +11,7 @@ "L41RegressionModel", "RatioMaskSCE", "RatioMaskCluster", + "RatioMaskClusterUnified", ] def make_model(model_name, config): diff --git a/magnolia/python/models/dnndenoise/cluster_mask_unified.py b/magnolia/python/models/dnndenoise/cluster_mask_unified.py new file mode 100644 index 0000000..8389319 --- /dev/null +++ b/magnolia/python/models/dnndenoise/cluster_mask_unified.py @@ -0,0 +1,361 @@ +import logging.config +import numpy as np +import tensorflow as tf + +from magnolia.models.model_base import ModelBase +from magnolia.utils import tf_utils + + +logger = logging.getLogger('model') + + +class RatioMaskClusterUnified(ModelBase): + """ + Chimera network from [1] but (implicitly) uses the soft C-means loss. + Defaults correspond to the parameters used by the best + performing model in the paper. + + [1] Luo, Yi., et al. "Deep Clustering and Conventional Networks for Music + Separation: Stronger Together" Published in Acoustics, Speech, and + Signal Processing (ICASSP) 2017; doi:10.1109/ICASSP.2017.7952118 + + Hyperparameters: + F: Number of frequency bins in the input data + num_reco_sources: Number sources to reconstruct + num_training_sources: Number sources in the training set + layer_size: Size of BLSTM layers + embedding_size: Dimension of embedding vector + alpha: Relative mixture of cost terms + nonlinearity: Nonlinearity to use in BLSTM layers + device: Which device to run the model on + """ + + def initialize(self): + self.F = self.config['model_params']['F'] + self.num_reco_sources = self.config['model_params']['num_reco_sources'] # should always be 2 + self.num_training_sources = self.config['model_params']['num_training_sources'] + self.layer_size = self.config['model_params']['layer_size'] + self.fuzzifier = self.config['model_params']['fuzzifier'] + self.embedding_size = self.config['model_params']['embedding_size'] + self.auxiliary_size = self.config['model_params']['auxiliary_size'] + self.normalize = self.config['model_params']['normalize'] + self.alpha = self.config['model_params']['alpha'] + self.nonlinearity = eval(self.config['model_params']['nonlinearity']) + self.collapse_sources = self.config['model_params']['collapse_sources'] + + self.batch_count = 0 + self.costs = [] + self.t_costs = [] + self.v_costs = [] + self.last_saved = 0 + + + def build_graph(self, graph): + with graph.as_default(): + with tf.device(self.config['device']): + # Placeholder tensor for the input data + self.X = tf.placeholder("float", [None, None, self.F]) + # Placeholder tensor for the unscaled input data + self.X_clean = tf.placeholder("float", [None, None, self.F]) + + # Placeholder tensor for the labels/targets + #self.y = tf.placeholder("float", [None, None, self.F, None]) + # Placeholder tensor for the unscaled labels/targets + self.y_clean = tf.placeholder("float", [None, None, self.F, None]) + + # Placeholder for the speaker indicies + self.I = tf.placeholder(tf.int32, [None, None]) + + # Define the speaker vectors to use during training + self.speaker_vectors = tf_utils.weight_variable( + [self.num_training_sources, self.embedding_size], + tf.sqrt(2/self.embedding_size)) + if self.auxiliary_size > 0: + # Define the auxiliary vectors to use during training + self.auxiliary_vectors = tf_utils.weight_variable( + [self.auxiliary_size, self.auxiliary_size], + tf.sqrt(2/self.auxiliary_size)) + else: + self.auxiliary_vectors = None + + # Model methods + self.network + self.cost + self.optimizer + + return graph + + + def learn_from_epoch(self, epoch_id, + validate_every, + stop_threshold, + training_mixer, + validation_mixer, + batch_formatter, + model_save_base): + + batch_count = self.batch_count + # Training epoch loop + for batch in iter(training_mixer): + unscaled_spectral_sum_batch, scaled_spectral_sum_batch, spectral_masks_batch, spectral_sources_batch = batch_formatter(batch[0], batch[1], batch[2]) + # should be dimensions of (batch size, source) + uids_batch = batch[3] + + # override ids for simply signal/noise + if self.collapse_sources: + uids_batch[:, 0] = 0 + uids_batch[:, 1] = 1 + + # Train the model on one batch and get the cost + c = self.train_on_batch(scaled_spectral_sum_batch, unscaled_spectral_sum_batch, + spectral_sources_batch, uids_batch) + + # Store the training cost + self.costs.append(c) + + # Evaluate the model on the validation data + if (batch_count + 1) % validate_every == 0: + # Store the training cost + self.t_costs.append(np.mean(self.costs)) + # Reset the cost over the last 10 batches + self.costs = [] + + # Compute average validation score + all_c_v = [] + for vbatch in iter(validation_mixer): + unscaled_spectral_sum_batch, scaled_spectral_sum_batch, spectral_masks_batch, spectral_sources_batch = batch_formatter(vbatch[0], vbatch[1], vbatch[2]) + # dimensions of (batch size, source) + uids_batch = vbatch[3] + + # override ids for simply signal/noise + if self.collapse_sources: + uids_batch[:, 0] = 0 + uids_batch[:, 1] = 1 + + # Get the cost on the validation batch + c_v = self.get_cost(scaled_spectral_sum_batch, unscaled_spectral_sum_batch, + spectral_sources_batch, uids_batch) + all_c_v.append(c_v) + + ave_c_v = np.mean(all_c_v) + + # Check if the validation cost is below the minimum validation cost, and if so, save it. + if len(self.v_costs) > 0 and ave_c_v < min(self.v_costs):# and len(self.nbatches) > 0: + logger.info("Saving the model because validation score is {} below the old minimum.".format(min(self.v_costs) - ave_c_v)) + + # Save the model to the specified path + self.save(model_save_base) + + # Record the batch that the model was last saved on + self.last_saved = batch_count#self.nbatches[-1] + + # Store the validation cost + self.v_costs.append(ave_c_v) + + # Store the current batch number + #self.nbatches.append(batch_count) + + logger.info("Training cost on batch {} is {}.".format(batch_count, self.t_costs[-1])) + logger.info("Validation cost on batch {} is {}.".format(batch_count, self.v_costs[-1])) + logger.info("Last saved {} batches ago.".format(batch_count - self.last_saved)) + + # Stop training if the number of iterations since the last save point exceeds the threshold + if batch_count - self.last_saved > stop_threshold: + logger.info("Early stopping criteria met!") + break + + batch_count += 1 + + self.batch_count = batch_count + + + def infer(self, **kw_args): + pass + + + @tf_utils.scope_decorator + def network(self): + """ + Construct the op for the network used in [1]. This consists of four + BLSTM layers followed by a dense layer giving a set of T-F vectors of + dimension embedding_size + """ + + m = self.fuzzifier + + # Get the shape of the input + shape = tf.shape(self.X) + shapeI = tf.shape(self.I) + + # BLSTM layer one + BLSTM_1 = tf_utils.BLSTM_(self.X, self.layer_size, 'one', + activation=self.nonlinearity) + + # BLSTM layer two + BLSTM_2 = tf_utils.BLSTM_(BLSTM_1, self.layer_size, 'two', + activation=self.nonlinearity) + + # BLSTM layer three + BLSTM_3 = tf_utils.BLSTM_(BLSTM_2, self.layer_size, 'three', + activation=self.nonlinearity) + + # BLSTM layer four + BLSTM_4 = tf_utils.BLSTM_(BLSTM_3, self.layer_size, 'four', + activation=self.nonlinearity) + + # Feedforward layer + feedforward = tf_utils.conv1d_layer(BLSTM_4, + [1, self.layer_size, self.embedding_size*self.F]) + + # Reshape the feedforward output to have shape (T,F,D) + z = tf.reshape(feedforward, + [shape[0], shape[1], self.F, self.embedding_size]) + + # indices helpers for fuzzy c-means + known_sources_init = np.zeros(self.num_training_sources) + known_sources = tf.get_variable('known_sources', + dtype=tf.bool, trainable=False, + initializer=tf.constant(known_sources_init, dtype=tf.bool)) + current_sources_indices, _ = tf.unique(tf.reshape(self.I, shape=[shapeI[0]*shapeI[1]])) + + known_sources = tf.scatter_update(known_sources, current_sources_indices, + tf.fill(tf.shape(current_sources_indices), True)) + + #current_sources = tf.cast(tf.scatter_nd(tf.expand_dims(current_sources_indices, -1), + # tf.ones_like(current_sources_indices, dtype=tf.int32), + # [self.num_training_sources]), + # dtype=tf.bool) + + batch_sources = tf.reshape(tf.gather(self.speaker_vectors, tf.reshape(self.I, shape=[shapeI[0]*shapeI[1]])), shape=[shapeI[0], shapeI[1]]) + + + # clustering head + embedding = self.nonlinearity(z) + # Normalize the T-F vectors to get the network output + embedding = tf.nn.l2_normalize(embedding, 3) + + # batch, all features, embedding + embeddings = tf.reshape(embedding, + [shape[0], shape[1]*self.F, self.embedding_size]) + + # compute fuzzy assignments + # batch, nfeatures, nsources + if self.auxiliary_vectors is None: + squared_diffs_batch = tf.reduce_sum(tf.square(tf.expand_dims(embeddings, 2) - tf.expand_dims(batch_sources, 1)), -1) + squared_diffs_known = tf.reduce_sum(tf.square(tf.expand_dims(embeddings, 2) - tf.expand_dims(tf.expand_dims(tf.boolean_mask(self.speaker_vectors, known_sources), 0), 0)), -1) + #squared_diffs_current = tf.reduce_sum(tf.square(tf.expand_dims(embeddings, 2) - tf.expand_dims(tf.expand_dims(tf.boolean_mask(self.speaker_vectors, current_sources), 0), 0)), -1) + diffs_pow_matrix_batch = tf.pow(squared_diffs_batch, 1./(m - 1.)) + diffs_pow_matrix_known = tf.pow(squared_diffs_known, 1./(m - 1.)) + #diffs_pow_matrix_current = tf.pow(squared_diffs_current, 1./(m - 1.)) + else: + # NOTE: true/aux refers to both the coordinates and cluster centers + true_embeddings = embeddings[:, :, :-self.auxiliary_size] + aux_embeddings = embeddings[:, :, -self.auxiliary_size:] + true_embeddings_l2 = tf.reduce_sum(tf.square(true_embeddings), axis=-1) + aux_embeddings_l2 = tf.reduce_sum(tf.square(aux_embeddings), axis=-1) + true_squared_diffs_batch = tf.reduce_sum(tf.square(tf.expand_dims(true_embeddings, 2) - tf.expand_dims(batch_sources, 1)), -1) + true_squared_diffs_known = tf.reduce_sum(tf.square(tf.expand_dims(true_embeddings, 2) - tf.expand_dims(tf.expand_dims(tf.boolean_mask(self.speaker_vectors, known_sources), 0), 0)), -1) + #true_squared_diffs_current = tf.reduce_sum(tf.square(tf.expand_dims(true_embeddings, 2) - tf.expand_dims(tf.expand_dims(tf.boolean_mask(self.speaker_vectors, current_sources), 0), 0)), -1) + aux_squared_diffs = tf.reduce_sum(tf.square(tf.expand_dims(aux_embeddings, 2) - tf.expand_dims(tf.expand_dims(self.auxiliary_vectors, 0), 0)), -1) + diffs_pow_matrix_batch = tf.pow(true_squared_diffs_batch + tf.expand_dims(aux_embeddings_l2, -1), 1./(m - 1.)) + diffs_pow_matrix_known = tf.concat([tf.pow(true_squared_diffs_known + tf.expand_dims(aux_embeddings_l2, -1), 1./(m - 1.)), + tf.pow(aux_squared_diffs + tf.expand_dims(true_embeddings_l2, -1), 1./(m - 1.))], axis=2) + #diffs_pow_matrix_current = tf.concat([tf.pow(true_squared_diffs_current + tf.expand_dims(aux_embeddings_l2, -1), 1./(m - 1.)), + # tf.pow(aux_squared_diffs + tf.expand_dims(true_embeddings_l2, -1), 1./(m - 1.))], axis=2) + + + # W = tf.reciprocal(diffs_pow_matrix_current*tf.expand_dims(tf.reduce_sum(tf.reciprocal(diffs_pow_matrix_known), -1), -1), name='W') + clustering_factors = tf.squeeze(tf.reciprocal(diffs_pow_matrix_batch*tf.expand_dims(tf.reduce_sum(tf.reciprocal(diffs_pow_matrix_known), -1), -1)), name='clustering_factors', axis=-1) + + + ## MI head + ## Feedforward layer + #feedforward_fc = tf_utils.conv2d_layer(z, + # [1, 1, self.embedding_size, self.num_reco_sources]) + ## perform a softmax along the source dimension + #mi_head = tf.nn.softmax(feedforward_fc, dim=3) + + # MI head + mi_head = tf.reshape(clustering_factors, + [shape[0], shape[1], self.F, shapeI[1]]) + + return embedding, mi_head + + + @tf_utils.scope_decorator + def cost(self): + """ + Constuct the cost function op for the cost function used in sce + and the mask inference head + """ + + # Get the shape of the target + shape = tf.shape(self.y_clean) + + #cluster_output, mi_output, W, squared_diffs = self.network + cluster_output, mi_output = self.network + + # broadcast product along source dimension + mi_cost = tf.square(self.y_clean - mi_output*tf.expand_dims(self.X_clean, -1)) + + return tf.reduce_mean(mi_cost) + + @tf_utils.scope_decorator + def optimizer(self): + """ + Constructs the optimizer op used to train the network + """ + opt = tf.train.AdamOptimizer() + return opt.minimize(self.cost) + + # def save(self, path): + # """ + # Saves the model to the specified path. + # """ + # self.saver.save(self.sess, path) + + # def load(self, path): + # """ + # Load the model from the specified path. + # """ + # self.saver.restore(self.sess, path) + + def train_on_batch(self, X_train, X_train_clean, y_train_clean, I_train): + """ + Train the model on a batch with input X and target y. Returns the cost + computed on this batch. + """ + + cost, _ = self.sess.run([self.cost, self.optimizer], + {self.X: X_train, + self.X_clean: X_train_clean, + self.y_clean: y_train_clean, + self.I: I_train}) + + return cost + + def get_masks(self, X_in): + """ + Compute the masks for the input spectrograms + """ + + masks = self.sess.run(self.network, {self.X: X_in})[1] + return masks + + def get_vectors(self, X_in): + """ + Compute the embedding vectors for the input spectrograms + """ + + vectors = self.sess.run(self.network, {self.X: X_in})[0] + return vectors + + def get_cost(self, X_in, X_clean_in, y_clean_in, I_in): + """ + Computes the cost of a batch, but does not update any model parameters. + """ + cost = self.sess.run(self.cost, {self.X: X_in, + self.X_clean: X_clean_in, + self.y_clean: y_clean_in, + self.I: I_in}) + return cost From af23f7a27d7176336bf28b86528411ab88db8817 Mon Sep 17 00:00:00 2001 From: Jeffrey Hetherly Date: Thu, 18 Jan 2018 00:29:43 +0000 Subject: [PATCH 08/16] training for unity --- .../RatioMaskClusterUnified/training.py | 101 ++++++++++++++++++ 1 file changed, 101 insertions(+) create mode 100644 magnolia/python/training/denoising/RatioMaskClusterUnified/training.py diff --git a/magnolia/python/training/denoising/RatioMaskClusterUnified/training.py b/magnolia/python/training/denoising/RatioMaskClusterUnified/training.py new file mode 100644 index 0000000..c74d2d2 --- /dev/null +++ b/magnolia/python/training/denoising/RatioMaskClusterUnified/training.py @@ -0,0 +1,101 @@ +# Generic imports +import argparse +import logging.config +import json + +import numpy as np +import pandas as pd +import tensorflow as tf + +# Import the RatioMaskClusterUnified separation model +from magnolia.models import make_model + +# Import utilities for using the model +from magnolia.training.data_iteration.mix_iterator import MixIterator +from magnolia.utils.training import preprocess_chimera_batch +#from magnolia.utils.tf_utils import double_learnable_relu + + +def main(): + # parse command line arguments + parser = argparse.ArgumentParser(description='Train the k-means clustering + ratio mask network.') + # parser.add_argument('--model_settings', '-s', + # default='../../../../data/models_settings/chimera_template.json', + # help='model settings JSON file') + # parser.add_argument('--_settings', '-s', + # default='../../../../data/models_settings/chimera_template.json', + # help='model settings JSON file') + parser.add_argument('--logger_settings', '-l', + default='../../../../data/logging_settings/logging.conf', + help='logging configuration file') + args = parser.parse_args() + + # Load logging configuration + logging.config.fileConfig(args.logger_settings) + logger = logging.getLogger('model') + + # Number of epochs + num_epochs = 20 # try 20 + # Threshold for stopping if the model hasn't improved for this many consecutive batches + stop_threshold = 10000 + # validate every number of these batches + validate_every = 100 + train_batchsize = 8 + train_mixes = ['/local_data/magnolia/pipeline_data/date_2017_09_27_time_13_25/settings/mixing_LibriSpeech_UrbanSound8K_train.json'] + train_from_disk = False + validate_batchsize = 5 + validate_mixes = ['/local_data/magnolia/pipeline_data/date_2017_09_27_time_13_25/settings/mixing_LibriSpeech_UrbanSound8K_validate.json'] + validate_from_disk = False + model_params = { + 'layer_size': 500, + 'embedding_size': 10, + 'auxiliary_size': 0, + 'alpha': 0.1, # try 0.9 + 'nonlinearity': 'tf.tanh', + 'fuzzifier': 2, + 'num_reco_sources': 2, + 'normalize': False, + 'collapse_sources': False, + } + model_location = '/gpu:0' + uid_settings = '/local_data/magnolia/pipeline_data/date_2017_09_27_time_13_25/settings/assign_uids_LibriSpeech_UrbanSound8K.json' + model_save_base = '/local_data/magnolia/experiment_data/date_2017_09_28_time_13_14/aux/model_saves/mask_cluster_unified' + + + training_mixer = MixIterator(mixes_settings_filenames=train_mixes, + batch_size=train_batchsize, + read_waveform=False, + from_disk=train_from_disk) + + validation_mixer = MixIterator(mixes_settings_filenames=validate_mixes, + batch_size=validate_batchsize, + read_waveform=False, + from_disk=validate_from_disk) + + # get frequency dimension + frequency_dim = training_mixer.sample_dimensions()[0] + # TODO: throw an exception + assert(frequency_dim == validation_mixer.sample_dimensions()[0]) + + # get number of sources + settings = json.load(open(uid_settings)) + uid_file = settings['output_file'] + uid_csv = pd.read_csv(uid_file) + number_of_sources = uid_csv['uid'].max() + 1 + + model_params['F'] = frequency_dim + model_params['num_training_sources'] = number_of_sources + config = {'model_params': model_params, + 'device': model_location} + model = make_model('RatioMaskClusterUnified', config) + + model.train(validate_every=validate_every, + stop_threshold=stop_threshold, + training_mixer=training_mixer, + validation_mixer=validation_mixer, + batch_formatter=preprocess_chimera_batch, + model_save_base=model_save_base) + + +if __name__ == '__main__': + main() From 0a23c1b685bc4fea293580c2ff03f10a1fc9fd19 Mon Sep 17 00:00:00 2001 From: Jeffrey Hetherly Date: Wed, 24 Jan 2018 01:24:56 +0000 Subject: [PATCH 09/16] near-final clustering models --- magnolia/python/models/__init__.py | 2 +- .../python/models/dnndenoise/cluster_mask.py | 264 ++++++++++++------ .../models/dnndenoise/cluster_mask_unified.py | 155 +++++----- .../denoising/RatioMaskCluster/training.py | 18 +- .../RatioMaskClusterUnified/training.py | 4 +- 5 files changed, 279 insertions(+), 164 deletions(-) diff --git a/magnolia/python/models/__init__.py b/magnolia/python/models/__init__.py index 4a4b5d0..747799a 100644 --- a/magnolia/python/models/__init__.py +++ b/magnolia/python/models/__init__.py @@ -3,7 +3,7 @@ from .dnndenoise.L41_regression_model import L41RegressionModel from .dnndenoise.sce_mask import RatioMaskSCE from .dnndenoise.cluster_mask import RatioMaskCluster -from .dnndenoise.cluster_mask import RatioMaskClusterUnified +from .dnndenoise.cluster_mask_unified import RatioMaskClusterUnified __all__ = [ "JFLEC", diff --git a/magnolia/python/models/dnndenoise/cluster_mask.py b/magnolia/python/models/dnndenoise/cluster_mask.py index 7c781db..3e1382b 100644 --- a/magnolia/python/models/dnndenoise/cluster_mask.py +++ b/magnolia/python/models/dnndenoise/cluster_mask.py @@ -32,7 +32,8 @@ class RatioMaskCluster(ModelBase): def initialize(self): self.F = self.config['model_params']['F'] - self.num_reco_sources = self.config['model_params']['num_reco_sources'] # should always be 2 + # should always be 2 + self.num_reco_sources = self.config['model_params']['num_reco_sources'] self.num_training_sources = self.config['model_params']['num_training_sources'] self.layer_size = self.config['model_params']['layer_size'] self.fuzzifier = self.config['model_params']['fuzzifier'] @@ -49,7 +50,6 @@ def initialize(self): self.v_costs = [] self.last_saved = 0 - def build_graph(self, graph): with graph.as_default(): with tf.device(self.config['device']): @@ -61,20 +61,21 @@ def build_graph(self, graph): # Placeholder tensor for the labels/targets self.y = tf.placeholder("float", [None, None, self.F, None]) # Placeholder tensor for the unscaled labels/targets - self.y_clean = tf.placeholder("float", [None, None, self.F, None]) + self.y_clean = tf.placeholder( + "float", [None, None, self.F, None]) # Placeholder for the speaker indicies self.I = tf.placeholder(tf.int32, [None, None]) # Define the speaker vectors to use during training self.speaker_vectors = tf_utils.weight_variable( - [self.num_training_sources, self.embedding_size], - tf.sqrt(2/self.embedding_size)) + [self.num_training_sources, self.embedding_size], + tf.sqrt(2 / self.embedding_size)) if self.auxiliary_size > 0: # Define the auxiliary vectors to use during training self.auxiliary_vectors = tf_utils.weight_variable( - [self.auxiliary_size, self.auxiliary_size], - tf.sqrt(2/self.auxiliary_size)) + [self.auxiliary_size, self.auxiliary_size], + tf.sqrt(2 / self.auxiliary_size)) else: self.auxiliary_vectors = None @@ -85,7 +86,6 @@ def build_graph(self, graph): return graph - def learn_from_epoch(self, epoch_id, validate_every, stop_threshold, @@ -97,7 +97,8 @@ def learn_from_epoch(self, epoch_id, batch_count = self.batch_count # Training epoch loop for batch in iter(training_mixer): - unscaled_spectral_sum_batch, scaled_spectral_sum_batch, spectral_masks_batch, spectral_sources_batch = batch_formatter(batch[0], batch[1], batch[2]) + unscaled_spectral_sum_batch, scaled_spectral_sum_batch, spectral_masks_batch, spectral_sources_batch = batch_formatter( + batch[0], batch[1], batch[2]) # should be dimensions of (batch size, source) uids_batch = batch[3] @@ -124,7 +125,8 @@ def learn_from_epoch(self, epoch_id, # Compute average validation score all_c_v = [] for vbatch in iter(validation_mixer): - unscaled_spectral_sum_batch, scaled_spectral_sum_batch, spectral_masks_batch, spectral_sources_batch = batch_formatter(vbatch[0], vbatch[1], vbatch[2]) + unscaled_spectral_sum_batch, scaled_spectral_sum_batch, spectral_masks_batch, spectral_sources_batch = batch_formatter( + vbatch[0], vbatch[1], vbatch[2]) # dimensions of (batch size, source) uids_batch = vbatch[3] @@ -142,24 +144,29 @@ def learn_from_epoch(self, epoch_id, ave_c_v = np.mean(all_c_v) # Check if the validation cost is below the minimum validation cost, and if so, save it. - if len(self.v_costs) > 0 and ave_c_v < min(self.v_costs):# and len(self.nbatches) > 0: - logger.info("Saving the model because validation score is {} below the old minimum.".format(min(self.v_costs) - ave_c_v)) + # and len(self.nbatches) > 0: + if len(self.v_costs) > 0 and ave_c_v < min(self.v_costs): + logger.info("Saving the model because validation score is {} below the old minimum.".format( + min(self.v_costs) - ave_c_v)) # Save the model to the specified path self.save(model_save_base) # Record the batch that the model was last saved on - self.last_saved = batch_count#self.nbatches[-1] + self.last_saved = batch_count # self.nbatches[-1] # Store the validation cost self.v_costs.append(ave_c_v) # Store the current batch number - #self.nbatches.append(batch_count) + # self.nbatches.append(batch_count) - logger.info("Training cost on batch {} is {}.".format(batch_count, self.t_costs[-1])) - logger.info("Validation cost on batch {} is {}.".format(batch_count, self.v_costs[-1])) - logger.info("Last saved {} batches ago.".format(batch_count - self.last_saved)) + logger.info("Training cost on batch {} is {}.".format( + batch_count, self.t_costs[-1])) + logger.info("Validation cost on batch {} is {}.".format( + batch_count, self.v_costs[-1])) + logger.info("Last saved {} batches ago.".format( + batch_count - self.last_saved)) # Stop training if the number of iterations since the last save point exceeds the threshold if batch_count - self.last_saved > stop_threshold: @@ -170,11 +177,9 @@ def learn_from_epoch(self, epoch_id, self.batch_count = batch_count - def infer(self, **kw_args): pass - @tf_utils.scope_decorator def network(self): """ @@ -182,7 +187,8 @@ def network(self): BLSTM layers followed by a dense layer giving a set of T-F vectors of dimension embedding_size """ - + + reduced_contrast = False m = self.fuzzifier # Get the shape of the input @@ -207,93 +213,181 @@ def network(self): # Feedforward layer feedforward = tf_utils.conv1d_layer(BLSTM_4, - [1, self.layer_size, self.embedding_size*self.F]) + [1, self.layer_size, self.embedding_size * self.F]) # Reshape the feedforward output to have shape (T,F,D) z = tf.reshape(feedforward, - [shape[0], shape[1], self.F, self.embedding_size]) + [shape[0], shape[1], self.F, self.embedding_size]) # indices helpers for fuzzy c-means - #with tf.device('/cpu:0'): - # known_sources_init = np.zeros(self.num_training_sources) - # known_sources = tf.get_variable('known_sources', - # dtype=tf.bool, trainable=False, - # initializer=tf.constant(known_sources_init, dtype=tf.bool)) - known_sources_init = np.zeros(self.num_training_sources) - known_sources = tf.get_variable('known_sources', - dtype=tf.bool, trainable=False, - initializer=tf.constant(known_sources_init, dtype=tf.bool)) - current_sources_indices, _ = tf.unique(tf.reshape(self.I, shape=[shapeI[0]*shapeI[1]])) - #with tf.device('/cpu:0'): - # known_sources = tf.scatter_update(known_sources, current_sources_indices, - # tf.fill(tf.shape(current_sources_indices), True)) - # - # current_sources = tf.cast(tf.scatter_nd(tf.expand_dims(current_sources_indices, -1), - # tf.ones_like(current_sources_indices, dtype=tf.int32), + #known_sources_init = np.zeros(self.num_training_sources) + # known_sources = tf.get_variable('known_sources', + # dtype=tf.bool, trainable=False, + # initializer=tf.constant(known_sources_init, dtype=tf.bool)) + #current_sources_indices, _ = tf.unique(tf.reshape(self.I, shape=[shapeI[0]*shapeI[1]])) + # known_sources = tf.scatter_update(known_sources, current_sources_indices, + # tf.fill(tf.shape(current_sources_indices), True)) + + # current_sources = tf.cast(tf.scatter_nd(tf.expand_dims(current_sources_indices, -1), + # tf.ones_like(current_sources_indices, dtype=tf.int32), # [self.num_training_sources]), - # dtype=tf.bool) - known_sources = tf.scatter_update(known_sources, current_sources_indices, - tf.fill(tf.shape(current_sources_indices), True)) - - current_sources = tf.cast(tf.scatter_nd(tf.expand_dims(current_sources_indices, -1), - tf.ones_like(current_sources_indices, dtype=tf.int32), - [self.num_training_sources]), - dtype=tf.bool) - - batch_sources = tf.reshape(tf.gather(self.speaker_vectors, tf.reshape(self.I, shape=[shapeI[0]*shapeI[1]])), shape=[shapeI[0], shapeI[1]]) - - + # dtype=tf.bool) + + # batch_sources = tf.reshape(tf.gather(self.speaker_vectors, tf.reshape(self.I, shape=[shapeI[0]*shapeI[1]])), + # shape=[shapeI[0], shapeI[1], self.embedding_size]) + + flattened_I = tf.reshape(self.I, shape=[shapeI[0] * shapeI[1]]) + batch_range = tf.range(shape[0] * shapeI[1]) + + current_sources_indices, current_sources_indices_batch = tf.unique( + flattened_I) + known_sources_indices = tf.get_variable('known_sources_indices', + initializer=tf.constant( + [], dtype=tf.int32), + dtype=tf.int32, + validate_shape=False, trainable=False) + known_sources_indices = tf.sets.set_union(tf.expand_dims(current_sources_indices, 0), + tf.expand_dims(known_sources_indices, 0)).values + # clustering head embedding = self.nonlinearity(z) # Normalize the T-F vectors to get the network output embedding = tf.nn.l2_normalize(embedding, 3) - + # batch, features, embedding embeddings = tf.reshape(embedding, - [shape[0], shape[1]*self.F, self.embedding_size]) + [shape[0], shape[1] * self.F, self.embedding_size]) # compute fuzzy assignments # batch, nfeatures, nsources if self.auxiliary_vectors is None: - squared_diffs_batch = tf.reduce_sum(tf.square(tf.expand_dims(embeddings, 2) - tf.expand_dims(batch_sources, 1)), -1) - squared_diffs_known = tf.reduce_sum(tf.square(tf.expand_dims(embeddings, 2) - tf.expand_dims(tf.expand_dims(tf.boolean_mask(self.speaker_vectors, known_sources), 0), 0)), -1) - squared_diffs_current = tf.reduce_sum(tf.square(tf.expand_dims(embeddings, 2) - tf.expand_dims(tf.expand_dims(tf.boolean_mask(self.speaker_vectors, current_sources), 0), 0)), -1) - diffs_pow_matrix_batch = tf.pow(squared_diffs_batch, 1./(m - 1.)) - diffs_pow_matrix_known = tf.pow(squared_diffs_known, 1./(m - 1.)) - diffs_pow_matrix_current = tf.pow(squared_diffs_current, 1./(m - 1.)) + ## batch, nfeatures + # batch, nsource in mix, nfeatures + #squared_diffs_batch = tf.reduce_sum(tf.square(embeddings - tf.expand_dims(tf.gather(self.speaker_vectors, flattened_I), 1)), -1) + squared_diffs_batch = tf.reduce_sum(tf.square(tf.expand_dims(embeddings, 1) - + tf.reshape(tf.expand_dims(tf.gather(self.speaker_vectors, flattened_I), 1), + [shape[0], shapeI[1], 1, self.embedding_size])), + -1) + # squared_diffs_batch = tf.reshape( + # squared_diffs_batch, [shape[0] * shapeI[1], shape[1] * self.F]) + diffs_pow_matrix_batch = tf.pow(squared_diffs_batch, 1. / (m - 1.)) + + # W_denom = tf.reduce_sum(tf.reciprocal( + # tf.pow( + # tf.reduce_sum(tf.square(tf.expand_dims(embeddings, 2) - tf.expand_dims( + # tf.expand_dims(tf.gather(self.speaker_vectors, known_sources_indices), 0), 0)), -1), + # 1. / (m - 1.) + # ) + # ), -1) + # W_denom = tf.expand_dims(W_denom, 1) + W_denom = tf.reduce_sum(tf.reciprocal( + diffs_pow_matrix_batch + # tf.pow( + # tf.reduce_sum(tf.square(tf.expand_dims(embeddings, 2) - tf.expand_dims( + # tf.expand_dims(tf.gather(self.speaker_vectors, known_sources_indices), 0), 0)), -1), + # 1. / (m - 1.) + # ) + ), 1) + W_denom = tf.expand_dims(W_denom, 1) + + #squared_diffs_current = tf.reduce_sum(tf.square(tf.expand_dims(embeddings, 2) - tf.expand_dims(tf.expand_dims(tf.gather(self.speaker_vectors, current_sources_indices), 0), 0)), -1) + #diffs_pow_matrix_current = tf.pow(squared_diffs_current, 1./(m - 1.)) + # + # if reduced_contrast: + # W_denom = tf.expand_dims(tf.reduce_sum(tf.reciprocal(diffs_pow_matrix_current), -1), -1) + # else: + # W_denom = tf.expand_dims(tf.reduce_sum(tf.reciprocal( + # tf.pow( + # tf.reduce_sum(tf.square(tf.expand_dims(embeddings, 2) - tf.expand_dims(tf.expand_dims(tf.gather(self.speaker_vectors, known_sources_indices), 0), 0)), -1), + # 1./(m - 1.) + # ) + # ), -1), -1) + + #squared_diffs_batch = tf.reduce_sum(tf.square(tf.expand_dims(embeddings, 2) - tf.expand_dims(batch_sources, 1)), -1) + #squared_diffs_known = tf.reduce_sum(tf.square(tf.expand_dims(embeddings, 2) - tf.expand_dims(tf.expand_dims(tf.boolean_mask(self.speaker_vectors, known_sources), 0), 0)), -1) + #squared_diffs_current = tf.reduce_sum(tf.square(tf.expand_dims(embeddings, 2) - tf.expand_dims(tf.expand_dims(tf.boolean_mask(self.speaker_vectors, current_sources), 0), 0)), -1) + #diffs_pow_matrix_batch = tf.pow(squared_diffs_batch, 1./(m - 1.)) + #diffs_pow_matrix_known = tf.pow(squared_diffs_known, 1./(m - 1.)) + #diffs_pow_matrix_current = tf.pow(squared_diffs_current, 1./(m - 1.)) else: # NOTE: true/aux refers to both the coordinates and cluster centers true_embeddings = embeddings[:, :, :-self.auxiliary_size] aux_embeddings = embeddings[:, :, -self.auxiliary_size:] - true_embeddings_l2 = tf.reduce_sum(tf.square(true_embeddings), axis=-1) - aux_embeddings_l2 = tf.reduce_sum(tf.square(aux_embeddings), axis=-1) - true_squared_diffs_batch = tf.reduce_sum(tf.square(tf.expand_dims(true_embeddings, 2) - tf.expand_dims(batch_sources, 1)), -1) - true_squared_diffs_known = tf.reduce_sum(tf.square(tf.expand_dims(true_embeddings, 2) - tf.expand_dims(tf.expand_dims(tf.boolean_mask(self.speaker_vectors, known_sources), 0), 0)), -1) - true_squared_diffs_current = tf.reduce_sum(tf.square(tf.expand_dims(true_embeddings, 2) - tf.expand_dims(tf.expand_dims(tf.boolean_mask(self.speaker_vectors, current_sources), 0), 0)), -1) - aux_squared_diffs = tf.reduce_sum(tf.square(tf.expand_dims(aux_embeddings, 2) - tf.expand_dims(tf.expand_dims(self.auxiliary_vectors, 0), 0)), -1) - diffs_pow_matrix_batch = tf.pow(true_squared_diffs_batch + tf.expand_dims(aux_embeddings_l2, -1), 1./(m - 1.)) - diffs_pow_matrix_known = tf.concat([tf.pow(true_squared_diffs_known + tf.expand_dims(aux_embeddings_l2, -1), 1./(m - 1.)), - tf.pow(aux_squared_diffs + tf.expand_dims(true_embeddings_l2, -1), 1./(m - 1.))], axis=2) - diffs_pow_matrix_current = tf.concat([tf.pow(true_squared_diffs_current + tf.expand_dims(aux_embeddings_l2, -1), 1./(m - 1.)), - tf.pow(aux_squared_diffs + tf.expand_dims(true_embeddings_l2, -1), 1./(m - 1.))], axis=2) - - - W = tf.reciprocal(diffs_pow_matrix_current*tf.expand_dims(tf.reduce_sum(tf.reciprocal(diffs_pow_matrix_known), -1), -1), name='W') - clustering_factors = tf.squeeze(tf.reciprocal(diffs_pow_matrix_batch*tf.expand_dims(tf.reduce_sum(tf.reciprocal(diffs_pow_matrix_known), -1), -1)), name='clustering_factors', axis=-1) - + true_embeddings_l2 = tf.reduce_sum( + tf.square(true_embeddings), axis=-1) + aux_embeddings_l2 = tf.reduce_sum( + tf.square(aux_embeddings), axis=-1) + #true_squared_diffs_batch = tf.reduce_sum(tf.square(tf.expand_dims(true_embeddings, 2) - tf.expand_dims(batch_sources, 1)), -1) + #true_squared_diffs_known = tf.reduce_sum(tf.square(tf.expand_dims(true_embeddings, 2) - tf.expand_dims(tf.expand_dims(tf.boolean_mask(self.speaker_vectors, known_sources), 0), 0)), -1) + #true_squared_diffs_current = tf.reduce_sum(tf.square(tf.expand_dims(true_embeddings, 2) - tf.expand_dims(tf.expand_dims(tf.gather(self.speaker_vectors, current_sources_indices), 0), 0)), -1) + #aux_squared_diffs = tf.reduce_sum(tf.square(tf.expand_dims(aux_embeddings, 2) - tf.expand_dims(tf.expand_dims(self.auxiliary_vectors, 0), 0)), -1) + ##diffs_pow_matrix_batch = tf.pow(true_squared_diffs_batch + tf.expand_dims(aux_embeddings_l2, -1), 1./(m - 1.)) + # diffs_pow_matrix_known = tf.concat([tf.pow(true_squared_diffs_known + tf.expand_dims(aux_embeddings_l2, -1), 1./(m - 1.)), + # tf.pow(aux_squared_diffs + tf.expand_dims(true_embeddings_l2, -1), 1./(m - 1.))], axis=2) + # diffs_pow_matrix_current = tf.concat([tf.pow(true_squared_diffs_current + tf.expand_dims(aux_embeddings_l2, -1), 1./(m - 1.)), + # tf.pow(aux_squared_diffs + tf.expand_dims(true_embeddings_l2, -1), 1./(m - 1.))], axis=2) + + # if reduced_contrast: + # W_denom = tf.expand_dims(tf.reduce_sum(tf.reciprocal(diffs_pow_matrix_current), -1), -1) + # else: + # W_denom = tf.expand_dims(tf.reduce_sum(tf.reciprocal( + # tf.concat([ + # tf.pow( + # tf.reduce_sum(tf.square(tf.expand_dims(true_embeddings, 2) - tf.expand_dims(tf.expand_dims(tf.gather(self.speaker_vectors, known_sources_indices), 0), 0)), + # -1) + tf.expand_dims(aux_embeddings_l2, -1), + # 1./(m - 1.) + # ), + # tf.pow(aux_squared_diffs + tf.expand_dims(true_embeddings_l2, -1), 1./(m - 1.)) + # ], axis=2) + # ), -1), -1) + + # batch, nsource in mix, nfeatures + true_squared_diffs_batch = tf.reduce_sum(tf.square(tf.expand_dims(true_embeddings, 1) - + tf.reshape(tf.expand_dims(tf.gather(self.speaker_vectors, flattened_I), 1), + [shape[0], shapeI[1], 1, self.embedding_size])), + -1) + # true_squared_diffs_batch = tf.reduce_sum(tf.square( + # true_embeddings - tf.expand_dims(tf.gather(self.speaker_vectors, flattened_I), 1)), -1) + # aux_squared_diffs = tf.reduce_sum(tf.square(tf.expand_dims( + # aux_embeddings, 2) - tf.expand_dims(tf.expand_dims(self.auxiliary_vectors, 0), 0)), -1) + diffs_pow_matrix_batch = tf.pow( + true_squared_diffs_batch + tf.expand_dims(aux_embeddings_l2, 1), 1. / (m - 1.)) + + W_denom = tf.reduce_sum(tf.reciprocal(diffs_pow_matrix_batch + # tf.concat([ + # tf.pow( + # tf.reduce_sum(tf.square(tf.expand_dims(true_embeddings, 2) - tf.expand_dims(tf.expand_dims(tf.gather(self.speaker_vectors, known_sources_indices), 0), 0)), + # -1) + tf.expand_dims(aux_embeddings_l2, -1), + # 1. / (m - 1.) + # ), + # tf.pow(aux_squared_diffs + + # tf.expand_dims(true_embeddings_l2, -1), 1. / (m - 1.)) + # ], axis=2) + ), -1) + W_denom = tf.expand_dims(W_denom, 1) + + #W = tf.reciprocal(diffs_pow_matrix_current*tf.expand_dims(tf.reduce_sum(tf.reciprocal(diffs_pow_matrix_known), -1), -1), name='W') + #clustering_factors = tf.reciprocal(diffs_pow_matrix_batch*tf.expand_dims(tf.reduce_sum(tf.reciprocal(diffs_pow_matrix_known), -1), -1)) + #W = tf.reciprocal(diffs_pow_matrix_current*W_denom, name='W') + #clustering_factors = tf.gather_nd(tf.transpose(W, perm=[0, 2, 1]), tf.stack((batch_range, current_sources_indices_batch), axis=1)) + W = tf.reciprocal(diffs_pow_matrix_batch * W_denom, name='W') + # clustering_factors = W + clustering_factors = tf.transpose(W, perm=[0, 2, 1]) + # MI head + mi_head = tf.reshape(clustering_factors, + [shape[0], shape[1], self.F, shapeI[1]]) # MI head # Feedforward layer - feedforward_fc = tf_utils.conv2d_layer(z, - [1, 1, self.embedding_size, self.num_reco_sources]) + # feedforward_fc = tf_utils.conv2d_layer(z, + # [1, 1, self.embedding_size, self.num_reco_sources]) # perform a softmax along the source dimension - mi_head = tf.nn.softmax(feedforward_fc, dim=3) - + #mi_head = tf.nn.softmax(feedforward_fc, dim=3) + if self.auxiliary_vectors is None: - return embedding, mi_head, W, squared_diffs_current + return embedding, mi_head, W, squared_diffs_batch else: - return embedding, mi_head, W[:, :, :-self.auxiliary_size], true_squared_diffs_current + tf.expand_dims(aux_embeddings_l2, -1) - + return embedding, mi_head, W, true_squared_diffs_batch + aux_embeddings_l2 @tf_utils.scope_decorator def cost(self): @@ -307,12 +401,14 @@ def cost(self): cluster_output, mi_output, W, squared_diffs = self.network - clustering_loss = tf.reduce_mean(tf.pow(W, self.fuzzifier)*squared_diffs) + clustering_loss = tf.reduce_mean( + tf.pow(W, self.fuzzifier) * squared_diffs) # broadcast product along source dimension - mi_cost = tf.square(self.y_clean - mi_output*tf.expand_dims(self.X_clean, -1)) + mi_cost = tf.square(self.y_clean - mi_output * + tf.expand_dims(self.X_clean, -1)) - return self.alpha*clustering_loss + (1.0 - self.alpha)*tf.reduce_mean(mi_cost) + return self.alpha * clustering_loss + (1.0 - self.alpha) * tf.reduce_mean(mi_cost) @tf_utils.scope_decorator def optimizer(self): diff --git a/magnolia/python/models/dnndenoise/cluster_mask_unified.py b/magnolia/python/models/dnndenoise/cluster_mask_unified.py index 8389319..84d9ba9 100644 --- a/magnolia/python/models/dnndenoise/cluster_mask_unified.py +++ b/magnolia/python/models/dnndenoise/cluster_mask_unified.py @@ -32,7 +32,8 @@ class RatioMaskClusterUnified(ModelBase): def initialize(self): self.F = self.config['model_params']['F'] - self.num_reco_sources = self.config['model_params']['num_reco_sources'] # should always be 2 + # should always be 2 + self.num_reco_sources = self.config['model_params']['num_reco_sources'] self.num_training_sources = self.config['model_params']['num_training_sources'] self.layer_size = self.config['model_params']['layer_size'] self.fuzzifier = self.config['model_params']['fuzzifier'] @@ -49,7 +50,6 @@ def initialize(self): self.v_costs = [] self.last_saved = 0 - def build_graph(self, graph): with graph.as_default(): with tf.device(self.config['device']): @@ -61,20 +61,21 @@ def build_graph(self, graph): # Placeholder tensor for the labels/targets #self.y = tf.placeholder("float", [None, None, self.F, None]) # Placeholder tensor for the unscaled labels/targets - self.y_clean = tf.placeholder("float", [None, None, self.F, None]) + self.y_clean = tf.placeholder( + "float", [None, None, self.F, None]) # Placeholder for the speaker indicies self.I = tf.placeholder(tf.int32, [None, None]) # Define the speaker vectors to use during training self.speaker_vectors = tf_utils.weight_variable( - [self.num_training_sources, self.embedding_size], - tf.sqrt(2/self.embedding_size)) + [self.num_training_sources, self.embedding_size], + tf.sqrt(2 / self.embedding_size)) if self.auxiliary_size > 0: # Define the auxiliary vectors to use during training self.auxiliary_vectors = tf_utils.weight_variable( - [self.auxiliary_size, self.auxiliary_size], - tf.sqrt(2/self.auxiliary_size)) + [self.auxiliary_size, self.auxiliary_size], + tf.sqrt(2 / self.auxiliary_size)) else: self.auxiliary_vectors = None @@ -85,7 +86,6 @@ def build_graph(self, graph): return graph - def learn_from_epoch(self, epoch_id, validate_every, stop_threshold, @@ -97,7 +97,8 @@ def learn_from_epoch(self, epoch_id, batch_count = self.batch_count # Training epoch loop for batch in iter(training_mixer): - unscaled_spectral_sum_batch, scaled_spectral_sum_batch, spectral_masks_batch, spectral_sources_batch = batch_formatter(batch[0], batch[1], batch[2]) + unscaled_spectral_sum_batch, scaled_spectral_sum_batch, spectral_masks_batch, spectral_sources_batch = batch_formatter( + batch[0], batch[1], batch[2]) # should be dimensions of (batch size, source) uids_batch = batch[3] @@ -123,7 +124,8 @@ def learn_from_epoch(self, epoch_id, # Compute average validation score all_c_v = [] for vbatch in iter(validation_mixer): - unscaled_spectral_sum_batch, scaled_spectral_sum_batch, spectral_masks_batch, spectral_sources_batch = batch_formatter(vbatch[0], vbatch[1], vbatch[2]) + unscaled_spectral_sum_batch, scaled_spectral_sum_batch, spectral_masks_batch, spectral_sources_batch = batch_formatter( + vbatch[0], vbatch[1], vbatch[2]) # dimensions of (batch size, source) uids_batch = vbatch[3] @@ -140,24 +142,29 @@ def learn_from_epoch(self, epoch_id, ave_c_v = np.mean(all_c_v) # Check if the validation cost is below the minimum validation cost, and if so, save it. - if len(self.v_costs) > 0 and ave_c_v < min(self.v_costs):# and len(self.nbatches) > 0: - logger.info("Saving the model because validation score is {} below the old minimum.".format(min(self.v_costs) - ave_c_v)) + # and len(self.nbatches) > 0: + if len(self.v_costs) > 0 and ave_c_v < min(self.v_costs): + logger.info("Saving the model because validation score is {} below the old minimum.".format( + min(self.v_costs) - ave_c_v)) # Save the model to the specified path self.save(model_save_base) # Record the batch that the model was last saved on - self.last_saved = batch_count#self.nbatches[-1] + self.last_saved = batch_count # self.nbatches[-1] # Store the validation cost self.v_costs.append(ave_c_v) # Store the current batch number - #self.nbatches.append(batch_count) + # self.nbatches.append(batch_count) - logger.info("Training cost on batch {} is {}.".format(batch_count, self.t_costs[-1])) - logger.info("Validation cost on batch {} is {}.".format(batch_count, self.v_costs[-1])) - logger.info("Last saved {} batches ago.".format(batch_count - self.last_saved)) + logger.info("Training cost on batch {} is {}.".format( + batch_count, self.t_costs[-1])) + logger.info("Validation cost on batch {} is {}.".format( + batch_count, self.v_costs[-1])) + logger.info("Last saved {} batches ago.".format( + batch_count - self.last_saved)) # Stop training if the number of iterations since the last save point exceeds the threshold if batch_count - self.last_saved > stop_threshold: @@ -168,11 +175,9 @@ def learn_from_epoch(self, epoch_id, self.batch_count = batch_count - def infer(self, **kw_args): pass - @tf_utils.scope_decorator def network(self): """ @@ -180,8 +185,9 @@ def network(self): BLSTM layers followed by a dense layer giving a set of T-F vectors of dimension embedding_size """ - + m = self.fuzzifier + reduced_contrast = True # Get the shape of the input shape = tf.shape(self.X) @@ -205,82 +211,92 @@ def network(self): # Feedforward layer feedforward = tf_utils.conv1d_layer(BLSTM_4, - [1, self.layer_size, self.embedding_size*self.F]) + [1, self.layer_size, self.embedding_size * self.F]) # Reshape the feedforward output to have shape (T,F,D) z = tf.reshape(feedforward, - [shape[0], shape[1], self.F, self.embedding_size]) + [shape[0], shape[1], self.F, self.embedding_size]) # indices helpers for fuzzy c-means - known_sources_init = np.zeros(self.num_training_sources) - known_sources = tf.get_variable('known_sources', - dtype=tf.bool, trainable=False, - initializer=tf.constant(known_sources_init, dtype=tf.bool)) - current_sources_indices, _ = tf.unique(tf.reshape(self.I, shape=[shapeI[0]*shapeI[1]])) - - known_sources = tf.scatter_update(known_sources, current_sources_indices, - tf.fill(tf.shape(current_sources_indices), True)) - - #current_sources = tf.cast(tf.scatter_nd(tf.expand_dims(current_sources_indices, -1), + #known_sources_init = np.zeros(self.num_training_sources) + # known_sources = tf.get_variable('known_sources', + # dtype=tf.bool, trainable=False, + # initializer=tf.constant(known_sources_init, dtype=tf.bool)) + #current_sources_indices, _ = tf.unique(tf.reshape(self.I, shape=[shapeI[0]*shapeI[1]])) + + # known_sources = tf.scatter_update(known_sources, current_sources_indices, + # tf.fill(tf.shape(current_sources_indices), True)) + + # current_sources = tf.cast(tf.scatter_nd(tf.expand_dims(current_sources_indices, -1), # tf.ones_like(current_sources_indices, dtype=tf.int32), # [self.num_training_sources]), # dtype=tf.bool) - - batch_sources = tf.reshape(tf.gather(self.speaker_vectors, tf.reshape(self.I, shape=[shapeI[0]*shapeI[1]])), shape=[shapeI[0], shapeI[1]]) - - + + #batch_sources = tf.reshape(tf.gather(self.speaker_vectors, tf.reshape(self.I, shape=[shapeI[0]*shapeI[1]])), shape=[shapeI[0], shapeI[1], self.embedding_size]) + + flattened_I = tf.reshape(self.I, shape=[shapeI[0] * shapeI[1]]) + batch_range = tf.range(shape[0] * shapeI[1]) + + current_sources_indices, current_sources_indices_batch = tf.unique( + flattened_I) + known_sources_indices = tf.get_variable('known_sources_indices', + initializer=tf.constant( + [], dtype=tf.int32), + dtype=tf.int32, + validate_shape=False, trainable=False) + known_sources_indices = tf.sets.set_union(tf.expand_dims(current_sources_indices, 0), + tf.expand_dims(known_sources_indices, 0)).values + # clustering head embedding = self.nonlinearity(z) # Normalize the T-F vectors to get the network output embedding = tf.nn.l2_normalize(embedding, 3) - + # batch, all features, embedding embeddings = tf.reshape(embedding, - [shape[0], shape[1]*self.F, self.embedding_size]) + [shape[0], shape[1] * self.F, self.embedding_size]) # compute fuzzy assignments # batch, nfeatures, nsources if self.auxiliary_vectors is None: - squared_diffs_batch = tf.reduce_sum(tf.square(tf.expand_dims(embeddings, 2) - tf.expand_dims(batch_sources, 1)), -1) - squared_diffs_known = tf.reduce_sum(tf.square(tf.expand_dims(embeddings, 2) - tf.expand_dims(tf.expand_dims(tf.boolean_mask(self.speaker_vectors, known_sources), 0), 0)), -1) - #squared_diffs_current = tf.reduce_sum(tf.square(tf.expand_dims(embeddings, 2) - tf.expand_dims(tf.expand_dims(tf.boolean_mask(self.speaker_vectors, current_sources), 0), 0)), -1) - diffs_pow_matrix_batch = tf.pow(squared_diffs_batch, 1./(m - 1.)) - diffs_pow_matrix_known = tf.pow(squared_diffs_known, 1./(m - 1.)) - #diffs_pow_matrix_current = tf.pow(squared_diffs_current, 1./(m - 1.)) + squared_diffs_batch = tf.reduce_sum(tf.square(tf.expand_dims(embeddings, 1) - + tf.reshape(tf.expand_dims(tf.gather(self.speaker_vectors, flattened_I), 1), + [shape[0], shapeI[1], 1, self.embedding_size])), + -1) + diffs_pow_matrix_batch = tf.pow(squared_diffs_batch, 1. / (m - 1.)) + W_denom = tf.expand_dims(tf.reduce_sum(tf.reciprocal(diffs_pow_matrix_batch), 1), 1) else: # NOTE: true/aux refers to both the coordinates and cluster centers true_embeddings = embeddings[:, :, :-self.auxiliary_size] aux_embeddings = embeddings[:, :, -self.auxiliary_size:] - true_embeddings_l2 = tf.reduce_sum(tf.square(true_embeddings), axis=-1) - aux_embeddings_l2 = tf.reduce_sum(tf.square(aux_embeddings), axis=-1) - true_squared_diffs_batch = tf.reduce_sum(tf.square(tf.expand_dims(true_embeddings, 2) - tf.expand_dims(batch_sources, 1)), -1) - true_squared_diffs_known = tf.reduce_sum(tf.square(tf.expand_dims(true_embeddings, 2) - tf.expand_dims(tf.expand_dims(tf.boolean_mask(self.speaker_vectors, known_sources), 0), 0)), -1) - #true_squared_diffs_current = tf.reduce_sum(tf.square(tf.expand_dims(true_embeddings, 2) - tf.expand_dims(tf.expand_dims(tf.boolean_mask(self.speaker_vectors, current_sources), 0), 0)), -1) - aux_squared_diffs = tf.reduce_sum(tf.square(tf.expand_dims(aux_embeddings, 2) - tf.expand_dims(tf.expand_dims(self.auxiliary_vectors, 0), 0)), -1) - diffs_pow_matrix_batch = tf.pow(true_squared_diffs_batch + tf.expand_dims(aux_embeddings_l2, -1), 1./(m - 1.)) - diffs_pow_matrix_known = tf.concat([tf.pow(true_squared_diffs_known + tf.expand_dims(aux_embeddings_l2, -1), 1./(m - 1.)), - tf.pow(aux_squared_diffs + tf.expand_dims(true_embeddings_l2, -1), 1./(m - 1.))], axis=2) - #diffs_pow_matrix_current = tf.concat([tf.pow(true_squared_diffs_current + tf.expand_dims(aux_embeddings_l2, -1), 1./(m - 1.)), - # tf.pow(aux_squared_diffs + tf.expand_dims(true_embeddings_l2, -1), 1./(m - 1.))], axis=2) - - - # W = tf.reciprocal(diffs_pow_matrix_current*tf.expand_dims(tf.reduce_sum(tf.reciprocal(diffs_pow_matrix_known), -1), -1), name='W') - clustering_factors = tf.squeeze(tf.reciprocal(diffs_pow_matrix_batch*tf.expand_dims(tf.reduce_sum(tf.reciprocal(diffs_pow_matrix_known), -1), -1)), name='clustering_factors', axis=-1) - - - ## MI head - ## Feedforward layer - #feedforward_fc = tf_utils.conv2d_layer(z, + true_embeddings_l2 = tf.reduce_sum( + tf.square(true_embeddings), axis=-1) + aux_embeddings_l2 = tf.reduce_sum( + tf.square(aux_embeddings), axis=-1) + true_squared_diffs_batch = tf.reduce_sum(tf.square(tf.expand_dims(true_embeddings, 1) - + tf.reshape(tf.expand_dims(tf.gather(self.speaker_vectors, flattened_I), 1), + [shape[0], shapeI[1], 1, self.embedding_size])), + -1) + diffs_pow_matrix_batch = tf.pow( + true_squared_diffs_batch + tf.expand_dims(aux_embeddings_l2, 1), 1. / (m - 1.)) + + W_denom = tf.expand_dims(tf.reduce_sum(tf.reciprocal(diffs_pow_matrix_batch), -1), 1) + + W = tf.reciprocal(diffs_pow_matrix_batch * W_denom, name='W') + clustering_factors = tf.transpose(W, perm=[0, 2, 1]) + + # MI head + # Feedforward layer + # feedforward_fc = tf_utils.conv2d_layer(z, # [1, 1, self.embedding_size, self.num_reco_sources]) - ## perform a softmax along the source dimension + # perform a softmax along the source dimension #mi_head = tf.nn.softmax(feedforward_fc, dim=3) - + # MI head mi_head = tf.reshape(clustering_factors, [shape[0], shape[1], self.F, shapeI[1]]) - + return embedding, mi_head - @tf_utils.scope_decorator def cost(self): @@ -296,7 +312,8 @@ def cost(self): cluster_output, mi_output = self.network # broadcast product along source dimension - mi_cost = tf.square(self.y_clean - mi_output*tf.expand_dims(self.X_clean, -1)) + mi_cost = tf.square(self.y_clean - mi_output * + tf.expand_dims(self.X_clean, -1)) return tf.reduce_mean(mi_cost) diff --git a/magnolia/python/training/denoising/RatioMaskCluster/training.py b/magnolia/python/training/denoising/RatioMaskCluster/training.py index a577874..95898c1 100644 --- a/magnolia/python/training/denoising/RatioMaskCluster/training.py +++ b/magnolia/python/training/denoising/RatioMaskCluster/training.py @@ -18,7 +18,8 @@ def main(): # parse command line arguments - parser = argparse.ArgumentParser(description='Train the k-means clustering + ratio mask network.') + parser = argparse.ArgumentParser( + description='Train the k-means clustering + ratio mask network.') # parser.add_argument('--model_settings', '-s', # default='../../../../data/models_settings/chimera_template.json', # help='model settings JSON file') @@ -35,22 +36,24 @@ def main(): logger = logging.getLogger('model') # Number of epochs - num_epochs = 20 # try 20 + num_epochs = 20 # try 20 # Threshold for stopping if the model hasn't improved for this many consecutive batches stop_threshold = 10000 # validate every number of these batches validate_every = 100 - train_batchsize = 8 - train_mixes = ['/local_data/magnolia/pipeline_data/date_2017_09_27_time_13_25/settings/mixing_LibriSpeech_UrbanSound8K_train.json'] + train_batchsize = 512 + train_mixes = [ + '/local_data/magnolia/pipeline_data/date_2017_09_27_time_13_25/settings/mixing_LibriSpeech_UrbanSound8K_train.json'] train_from_disk = False - validate_batchsize = 5 - validate_mixes = ['/local_data/magnolia/pipeline_data/date_2017_09_27_time_13_25/settings/mixing_LibriSpeech_UrbanSound8K_validate.json'] + validate_batchsize = 500 + validate_mixes = [ + '/local_data/magnolia/pipeline_data/date_2017_09_27_time_13_25/settings/mixing_LibriSpeech_UrbanSound8K_validate.json'] validate_from_disk = False model_params = { 'layer_size': 500, 'embedding_size': 10, 'auxiliary_size': 0, - 'alpha': 0.1, # try 0.9 + 'alpha': 0.1, # try 0.9 'nonlinearity': 'tf.tanh', 'fuzzifier': 2, 'num_reco_sources': 2, @@ -61,7 +64,6 @@ def main(): uid_settings = '/local_data/magnolia/pipeline_data/date_2017_09_27_time_13_25/settings/assign_uids_LibriSpeech_UrbanSound8K.json' model_save_base = '/local_data/magnolia/experiment_data/date_2017_09_28_time_13_14/aux/model_saves/mask_cluster' - training_mixer = MixIterator(mixes_settings_filenames=train_mixes, batch_size=train_batchsize, read_waveform=False, diff --git a/magnolia/python/training/denoising/RatioMaskClusterUnified/training.py b/magnolia/python/training/denoising/RatioMaskClusterUnified/training.py index c74d2d2..ae1a8b3 100644 --- a/magnolia/python/training/denoising/RatioMaskClusterUnified/training.py +++ b/magnolia/python/training/denoising/RatioMaskClusterUnified/training.py @@ -40,10 +40,10 @@ def main(): stop_threshold = 10000 # validate every number of these batches validate_every = 100 - train_batchsize = 8 + train_batchsize = 512 train_mixes = ['/local_data/magnolia/pipeline_data/date_2017_09_27_time_13_25/settings/mixing_LibriSpeech_UrbanSound8K_train.json'] train_from_disk = False - validate_batchsize = 5 + validate_batchsize = 500 validate_mixes = ['/local_data/magnolia/pipeline_data/date_2017_09_27_time_13_25/settings/mixing_LibriSpeech_UrbanSound8K_validate.json'] validate_from_disk = False model_params = { From 9952723be7dde57b388c62a570b3043d22f65c77 Mon Sep 17 00:00:00 2001 From: Jeffrey Hetherly Date: Wed, 24 Jan 2018 18:23:22 +0000 Subject: [PATCH 10/16] properly include the aux dims --- .../python/models/dnndenoise/cluster_mask.py | 40 +++++++++-------- .../models/dnndenoise/cluster_mask_unified.py | 44 +++++++++---------- 2 files changed, 43 insertions(+), 41 deletions(-) diff --git a/magnolia/python/models/dnndenoise/cluster_mask.py b/magnolia/python/models/dnndenoise/cluster_mask.py index 3e1382b..9a270c3 100644 --- a/magnolia/python/models/dnndenoise/cluster_mask.py +++ b/magnolia/python/models/dnndenoise/cluster_mask.py @@ -348,30 +348,31 @@ def network(self): -1) # true_squared_diffs_batch = tf.reduce_sum(tf.square( # true_embeddings - tf.expand_dims(tf.gather(self.speaker_vectors, flattened_I), 1)), -1) - # aux_squared_diffs = tf.reduce_sum(tf.square(tf.expand_dims( - # aux_embeddings, 2) - tf.expand_dims(tf.expand_dims(self.auxiliary_vectors, 0), 0)), -1) - diffs_pow_matrix_batch = tf.pow( - true_squared_diffs_batch + tf.expand_dims(aux_embeddings_l2, 1), 1. / (m - 1.)) - - W_denom = tf.reduce_sum(tf.reciprocal(diffs_pow_matrix_batch - # tf.concat([ - # tf.pow( - # tf.reduce_sum(tf.square(tf.expand_dims(true_embeddings, 2) - tf.expand_dims(tf.expand_dims(tf.gather(self.speaker_vectors, known_sources_indices), 0), 0)), - # -1) + tf.expand_dims(aux_embeddings_l2, -1), - # 1. / (m - 1.) - # ), - # tf.pow(aux_squared_diffs + - # tf.expand_dims(true_embeddings_l2, -1), 1. / (m - 1.)) - # ], axis=2) + # batch, nfeatures, nsources (aux) + aux_squared_diffs = tf.reduce_sum(tf.square(tf.expand_dims( + aux_embeddings, 2) - tf.expand_dims(tf.expand_dims(self.auxiliary_vectors, 0), 0)), -1) + aux_diffs_pow_matrix = tf.pow(aux_squared_diffs + tf.expand_dims(true_embeddings_l2, -1), 1. / (m - 1.)) + # batch, nsource in mix, nfeatures + squared_diffs_batch = true_squared_diffs_batch + tf.expand_dims(aux_embeddings_l2, 1) + diffs_pow_matrix_batch = tf.pow(squared_diffs_batch, 1. / (m - 1.)) + + W_denom = tf.reduce_sum(tf.reciprocal( + tf.concat([ + tf.transpose(diffs_pow_matrix_batch, perm=[0, 2, 1]), + aux_diffs_pow_matrix + ], axis=2) ), -1) + # batch, 1, nfeatures W_denom = tf.expand_dims(W_denom, 1) #W = tf.reciprocal(diffs_pow_matrix_current*tf.expand_dims(tf.reduce_sum(tf.reciprocal(diffs_pow_matrix_known), -1), -1), name='W') #clustering_factors = tf.reciprocal(diffs_pow_matrix_batch*tf.expand_dims(tf.reduce_sum(tf.reciprocal(diffs_pow_matrix_known), -1), -1)) #W = tf.reciprocal(diffs_pow_matrix_current*W_denom, name='W') #clustering_factors = tf.gather_nd(tf.transpose(W, perm=[0, 2, 1]), tf.stack((batch_range, current_sources_indices_batch), axis=1)) + # batch, nsource in mix, nfeatures W = tf.reciprocal(diffs_pow_matrix_batch * W_denom, name='W') # clustering_factors = W + # batch, nfeatures, nsource in mix clustering_factors = tf.transpose(W, perm=[0, 2, 1]) # MI head @@ -384,10 +385,11 @@ def network(self): # perform a softmax along the source dimension #mi_head = tf.nn.softmax(feedforward_fc, dim=3) - if self.auxiliary_vectors is None: - return embedding, mi_head, W, squared_diffs_batch - else: - return embedding, mi_head, W, true_squared_diffs_batch + aux_embeddings_l2 + return embedding, mi_head, W, squared_diffs_batch + #if self.auxiliary_vectors is None: + # return embedding, mi_head, W, squared_diffs_batch + #else: + # return embedding, mi_head, W, true_squared_diffs_batch + aux_embeddings_l2 @tf_utils.scope_decorator def cost(self): diff --git a/magnolia/python/models/dnndenoise/cluster_mask_unified.py b/magnolia/python/models/dnndenoise/cluster_mask_unified.py index 84d9ba9..51429d2 100644 --- a/magnolia/python/models/dnndenoise/cluster_mask_unified.py +++ b/magnolia/python/models/dnndenoise/cluster_mask_unified.py @@ -218,22 +218,6 @@ def network(self): [shape[0], shape[1], self.F, self.embedding_size]) # indices helpers for fuzzy c-means - #known_sources_init = np.zeros(self.num_training_sources) - # known_sources = tf.get_variable('known_sources', - # dtype=tf.bool, trainable=False, - # initializer=tf.constant(known_sources_init, dtype=tf.bool)) - #current_sources_indices, _ = tf.unique(tf.reshape(self.I, shape=[shapeI[0]*shapeI[1]])) - - # known_sources = tf.scatter_update(known_sources, current_sources_indices, - # tf.fill(tf.shape(current_sources_indices), True)) - - # current_sources = tf.cast(tf.scatter_nd(tf.expand_dims(current_sources_indices, -1), - # tf.ones_like(current_sources_indices, dtype=tf.int32), - # [self.num_training_sources]), - # dtype=tf.bool) - - #batch_sources = tf.reshape(tf.gather(self.speaker_vectors, tf.reshape(self.I, shape=[shapeI[0]*shapeI[1]])), shape=[shapeI[0], shapeI[1], self.embedding_size]) - flattened_I = tf.reshape(self.I, shape=[shapeI[0] * shapeI[1]]) batch_range = tf.range(shape[0] * shapeI[1]) @@ -257,13 +241,15 @@ def network(self): [shape[0], shape[1] * self.F, self.embedding_size]) # compute fuzzy assignments - # batch, nfeatures, nsources + # batch, nsource in mix, nfeatures if self.auxiliary_vectors is None: + # batch, nsource in mix, nfeatures squared_diffs_batch = tf.reduce_sum(tf.square(tf.expand_dims(embeddings, 1) - tf.reshape(tf.expand_dims(tf.gather(self.speaker_vectors, flattened_I), 1), [shape[0], shapeI[1], 1, self.embedding_size])), -1) diffs_pow_matrix_batch = tf.pow(squared_diffs_batch, 1. / (m - 1.)) + # batch, 1, nfeatures W_denom = tf.expand_dims(tf.reduce_sum(tf.reciprocal(diffs_pow_matrix_batch), 1), 1) else: # NOTE: true/aux refers to both the coordinates and cluster centers @@ -273,16 +259,30 @@ def network(self): tf.square(true_embeddings), axis=-1) aux_embeddings_l2 = tf.reduce_sum( tf.square(aux_embeddings), axis=-1) + # batch, nsource in mix, nfeatures true_squared_diffs_batch = tf.reduce_sum(tf.square(tf.expand_dims(true_embeddings, 1) - tf.reshape(tf.expand_dims(tf.gather(self.speaker_vectors, flattened_I), 1), [shape[0], shapeI[1], 1, self.embedding_size])), -1) - diffs_pow_matrix_batch = tf.pow( - true_squared_diffs_batch + tf.expand_dims(aux_embeddings_l2, 1), 1. / (m - 1.)) - - W_denom = tf.expand_dims(tf.reduce_sum(tf.reciprocal(diffs_pow_matrix_batch), -1), 1) - + squared_diffs_batch = true_squared_diffs_batch + tf.expand_dims(aux_embeddings_l2, 1) + diffs_pow_matrix_batch = tf.pow(squared_diffs_batch, 1. / (m - 1.)) + # batch, nfeatures, nsources (aux) + aux_squared_diffs = tf.reduce_sum(tf.square(tf.expand_dims( + aux_embeddings, 2) - tf.expand_dims(tf.expand_dims(self.auxiliary_vectors, 0), 0)), -1) + aux_diffs_pow_matrix = tf.pow(aux_squared_diffs + tf.expand_dims(true_embeddings_l2, -1), 1. / (m - 1.)) + + W_denom = tf.reduce_sum(tf.reciprocal( + tf.concat([ + tf.transpose(diffs_pow_matrix_batch, perm=[0, 2, 1]), + aux_diffs_pow_matrix + ], axis=2) + ), -1) + # batch, 1, nfeatures + W_denom = tf.expand_dims(W_denom, 1) + + # batch, nsource in mix, nfeatures W = tf.reciprocal(diffs_pow_matrix_batch * W_denom, name='W') + # batch, nfeatures, nsource in mix clustering_factors = tf.transpose(W, perm=[0, 2, 1]) # MI head From 588f2a2de6f244d7b5873dfcca8ec4ec12764f26 Mon Sep 17 00:00:00 2001 From: Jeffrey Hetherly Date: Thu, 25 Jan 2018 00:58:30 +0000 Subject: [PATCH 11/16] preliminary mask inference for cluster models --- .../python/models/dnndenoise/cluster_mask.py | 62 ++++++++++++++++--- .../models/dnndenoise/cluster_mask_unified.py | 28 ++++++++- 2 files changed, 77 insertions(+), 13 deletions(-) diff --git a/magnolia/python/models/dnndenoise/cluster_mask.py b/magnolia/python/models/dnndenoise/cluster_mask.py index 9a270c3..bbedcbd 100644 --- a/magnolia/python/models/dnndenoise/cluster_mask.py +++ b/magnolia/python/models/dnndenoise/cluster_mask.py @@ -81,6 +81,8 @@ def build_graph(self, graph): # Model methods self.network + self.clustering_cost + self.mi_cost self.cost self.optimizer @@ -392,25 +394,43 @@ def network(self): # return embedding, mi_head, W, true_squared_diffs_batch + aux_embeddings_l2 @tf_utils.scope_decorator - def cost(self): + def clustering_cost(self): """ - Constuct the cost function op for the cost function used in sce - and the mask inference head + Constuct the cost function op for the cost function used for clustering """ - # Get the shape of the input - shape = tf.shape(self.y) - cluster_output, mi_output, W, squared_diffs = self.network clustering_loss = tf.reduce_mean( tf.pow(W, self.fuzzifier) * squared_diffs) + return clustering_loss + + @tf_utils.scope_decorator + def mi_cost(self): + """ + Constuct the cost function op for the cost function used for mask inference head + """ + + cluster_output, mi_output, W, squared_diffs = self.network + # broadcast product along source dimension mi_cost = tf.square(self.y_clean - mi_output * tf.expand_dims(self.X_clean, -1)) - return self.alpha * clustering_loss + (1.0 - self.alpha) * tf.reduce_mean(mi_cost) + return mi_cost + + @tf_utils.scope_decorator + def cost(self): + """ + Constuct the cost function op for the cost function used for clustering + and the mask inference head + """ + + clustering_loss = self.clustering_cost + mi_loss = self.mi_cost + + return self.alpha * clustering_loss + (1.0 - self.alpha) * tf.reduce_mean(mi_loss) @tf_utils.scope_decorator def optimizer(self): @@ -446,12 +466,34 @@ def train_on_batch(self, X_train, X_train_clean, y_train, y_train_clean, I_train return cost - def get_masks(self, X_in): + def get_masks(self, X_in, nsources=2, nclustering_iterations_max=500, iterations_stop=10): """ Compute the masks for the input spectrograms """ - - masks = self.sess.run(self.network, {self.X: X_in})[1] + + nspectrograms = len(X_in) + #I = np.arange(nspectrograms * nsources, dtype=np.int32).reshape(nspectrograms, nsources) + I = np.tile(np.arange(nsources, dtype=np.int32), reps=(nspectrograms, 1)) + + opt = tf.train.AdamOptimizer() + clustering_minimize = opt.minimize(self.clustering_cost, var_list=[self.speaker_vectors]) + + previous_cost = np.finfo('float').max + iterations_count = 0 + for i in range(nclustering_iterations_max): + cost, _ = self.sess.run([self.clustering_cost, + clustering_minimize], + {self.X: X_in, + self.I: I}) + if cost < previous_cost: + previous_cost = cost + else: + iterations_count += 1 + + if iterations_count >= iterations_stop: + break + + masks = self.sess.run(self.network, {self.X: X_in, self.I: I})[1] return masks def get_vectors(self, X_in): diff --git a/magnolia/python/models/dnndenoise/cluster_mask_unified.py b/magnolia/python/models/dnndenoise/cluster_mask_unified.py index 51429d2..4b5b67e 100644 --- a/magnolia/python/models/dnndenoise/cluster_mask_unified.py +++ b/magnolia/python/models/dnndenoise/cluster_mask_unified.py @@ -351,12 +351,34 @@ def train_on_batch(self, X_train, X_train_clean, y_train_clean, I_train): return cost - def get_masks(self, X_in): + def get_masks(self, X_in, nsources=2, nclustering_iterations_max=500, iterations_stop=10): """ Compute the masks for the input spectrograms """ - - masks = self.sess.run(self.network, {self.X: X_in})[1] + + nspectrograms = len(X_in) + #I = np.arange(nspectrograms * nsources, dtype=np.int32).reshape(nspectrograms, nsources) + I = np.tile(np.arange(nsources, dtype=np.int32), reps=(nspectrograms, 1)) + + opt = tf.train.AdamOptimizer() + clustering_minimize = opt.minimize(self.clustering_cost, var_list=[self.speaker_vectors]) + + previous_cost = np.finfo('float').max + iterations_count = 0 + for i in range(nclustering_iterations_max): + cost, _ = self.sess.run([self.clustering_cost, + clustering_minimize], + {self.X: X_in, + self.I: I}) + if cost < previous_cost: + previous_cost = cost + else: + iterations_count += 1 + + if iterations_count >= iterations_stop: + break + + masks = self.sess.run(self.network, {self.X: X_in, self.I: I})[1] return masks def get_vectors(self, X_in): From 2e97da069e261339c0f39acae2f65faa06de9414 Mon Sep 17 00:00:00 2001 From: Jeffrey Hetherly Date: Thu, 25 Jan 2018 23:15:21 +0000 Subject: [PATCH 12/16] fixed aux dims --- .../RatioMaskCluster/separate_mix.py | 16 ++-- .../separate_sample_from_mix.py | 20 ++--- .../python/models/dnndenoise/cluster_mask.py | 40 +++++++--- .../models/dnndenoise/cluster_mask_unified.py | 43 +++++++++-- magnolia/python/utils/clustering_utils.py | 75 +++++++++++++++++++ 5 files changed, 159 insertions(+), 35 deletions(-) diff --git a/magnolia/python/inference/denoising/RatioMaskCluster/separate_mix.py b/magnolia/python/inference/denoising/RatioMaskCluster/separate_mix.py index 266836d..7c34d07 100644 --- a/magnolia/python/inference/denoising/RatioMaskCluster/separate_mix.py +++ b/magnolia/python/inference/denoising/RatioMaskCluster/separate_mix.py @@ -10,7 +10,7 @@ import librosa as lr import tqdm -# Import the RatioMaskSCE separation model +# Import the RatioMaskCluster separation model from magnolia.models import make_model # Import utilities for using the model @@ -27,7 +27,7 @@ def standardize_waveform(y): def main(): # parse command line arguments - parser = argparse.ArgumentParser(description='Denoise mixed samples using the RatioMaskSCE network.') + parser = argparse.ArgumentParser(description='Denoise mixed samples using the RatioMaskCluster network.') # parser.add_argument('--model_settings', '-s', # default='../../../../data/models_settings/chimera_template.json', # help='model settings JSON file') @@ -47,22 +47,24 @@ def main(): model_params = { 'layer_size': 500, 'embedding_size': 10, - 'alpha': 0.9, # try 0.9 + 'auxiliary_size': 0, + 'alpha': 0.1, # try 0.9 'nonlinearity': 'tf.tanh', + 'fuzzifier': 2, 'num_reco_sources': 2, 'normalize': False, 'collapse_sources': False, } uid_settings = '/local_data/magnolia/pipeline_data/date_2017_09_27_time_13_25/settings/assign_uids_LibriSpeech_UrbanSound8K.json' - model_save_base = '/local_data/magnolia/experiment_data/date_2017_09_28_time_13_14/aux/model_saves/mask_sce' + model_save_base = '/local_data/magnolia/experiment_data/date_2017_09_28_time_13_14/aux/model_saves/mask_cluster' model_location = '/cpu:0' model_settings = '' mixes = ['/local_data/magnolia/pipeline_data/date_2017_09_27_time_13_25/settings/mixing_LibriSpeech_UrbanSound8K_test_in_sample.json'] # mixes = ['/local_data/magnolia/pipeline_data/date_2017_09_27_time_13_25/settings/mixing_LibriSpeech_UrbanSound8K_test_out_of_sample.json'] from_disk = True - output_path = '/local_data/magnolia/experiment_data/date_2017_09_28_time_13_14/aux/evaluations/mask_sce/in_sample_test' - # output_path = '/local_data/magnolia/experiment_data/date_2017_09_28_time_13_14/aux/evaluations/mask_sce/out_of_sample_test' + output_path = '/local_data/magnolia/experiment_data/date_2017_09_28_time_13_14/aux/evaluations/mask_cluster/in_sample_test' + # output_path = '/local_data/magnolia/experiment_data/date_2017_09_28_time_13_14/aux/evaluations/mask_cluster/out_of_sample_test' eval_sr = 8000 mixer = MixIterator(mixes_settings_filenames=mixes, @@ -83,7 +85,7 @@ def main(): model_params['num_training_sources'] = number_of_sources config = {'model_params': model_params, 'device': model_location} - model = make_model('RatioMaskSCE', config) + model = make_model('RatioMaskCluster', config) model.load(model_save_base) diff --git a/magnolia/python/inference/denoising/RatioMaskCluster/separate_sample_from_mix.py b/magnolia/python/inference/denoising/RatioMaskCluster/separate_sample_from_mix.py index bdec6f1..10b7b58 100644 --- a/magnolia/python/inference/denoising/RatioMaskCluster/separate_sample_from_mix.py +++ b/magnolia/python/inference/denoising/RatioMaskCluster/separate_sample_from_mix.py @@ -8,14 +8,14 @@ import pandas as pd import librosa as lr -# Import the RatioMaskSCE separation model +# Import the RatioMaskCluster separation model from magnolia.models import make_model # Import utilities for using the model from magnolia.utils.postprocessing import convert_preprocessing_parameters from magnolia.preprocessing.preprocessing import undo_preprocessing from magnolia.training.data_iteration.mix_iterator import MixIterator -from magnolia.utils.clustering_utils import chimera_clustering_separate, chimera_mask +from magnolia.utils.clustering_utils import mask_cluster_clustering_separate, mask_cluster_mask def standardize_waveform(y): @@ -24,7 +24,7 @@ def standardize_waveform(y): def main(): # parse command line arguments - parser = argparse.ArgumentParser(description='Denoise mixed sample using the RatioMaskSCE network.') + parser = argparse.ArgumentParser(description='Denoise mixed sample using the RatioMaskCluster network.') # parser.add_argument('--model_settings', '-s', # default='../../../../data/models_settings/chimera_template.json', # help='model settings JSON file') @@ -44,21 +44,23 @@ def main(): model_params = { 'layer_size': 500, 'embedding_size': 10, - 'alpha': 0.9, # try 0.9 + 'auxiliary_size': 0, + 'alpha': 0.1, # try 0.9 'nonlinearity': 'tf.tanh', + 'fuzzifier': 2, 'num_reco_sources': 2, 'normalize': False, 'collapse_sources': False, } uid_settings = '/local_data/magnolia/pipeline_data/date_2017_09_27_time_13_25/settings/assign_uids_LibriSpeech_UrbanSound8K.json' - model_save_base = '/local_data/magnolia/experiment_data/date_2017_09_28_time_13_14/aux/model_saves/mask_sce' + model_save_base = '/local_data/magnolia/experiment_data/date_2017_09_28_time_13_14/aux/model_saves/mask_cluster' model_location = '/cpu:0' model_settings = '' mixes = ['/local_data/magnolia/pipeline_data/date_2017_09_27_time_13_25/settings/mixing_LibriSpeech_UrbanSound8K_test_in_sample.json'] from_disk = True mix_number = 1010 - output_path = '/local_data/magnolia/experiment_data/date_2017_09_28_time_13_14/aux/sample_wav_files/mask_sce' + output_path = '/local_data/magnolia/experiment_data/date_2017_09_28_time_13_14/aux/sample_wav_files/mask_cluster' os.makedirs(output_path, exist_ok=True) @@ -81,7 +83,7 @@ def main(): model_params['num_training_sources'] = number_of_sources config = {'model_params': model_params, 'device': model_location} - model = make_model('RatioMaskSCE', config) + model = make_model('RatioMaskCluster', config) model.load(model_save_base) @@ -134,7 +136,7 @@ def main(): # print('Sample for source {}'.format(i + 1)) lr.output.write_wav(os.path.join(output_path, 'mix_{}_original_source_{}.wav'.format(mix_number, i + 1)), y, mixer.sample_rate(), norm=True) - source_specs = chimera_clustering_separate(model_spec, model, mixer.number_of_samples_in_mixes()) + source_specs = mask_cluster_clustering_separate(model_spec, model, mixer.number_of_samples_in_mixes()) for i, source_spec in enumerate(source_specs): y = undo_preprocessing(source_spec, mixer.sample_length_in_bits(), @@ -149,7 +151,7 @@ def main(): # print('Separated sample for source {}'.format(i + 1)) lr.output.write_wav(os.path.join(output_path, 'mix_{}_dc_separated_{}.wav'.format(mix_number, i + 1)), y, mixer.sample_rate(), norm=True) - source_specs = chimera_mask(model_spec, model)[0] + source_specs = mask_cluster_mask(model_spec, model, mixer.number_of_samples_in_mixes())[0] for i in range(source_specs.shape[2]): source_spec = source_specs[:, :, i] diff --git a/magnolia/python/models/dnndenoise/cluster_mask.py b/magnolia/python/models/dnndenoise/cluster_mask.py index bbedcbd..8bc0f3e 100644 --- a/magnolia/python/models/dnndenoise/cluster_mask.py +++ b/magnolia/python/models/dnndenoise/cluster_mask.py @@ -81,6 +81,7 @@ def build_graph(self, graph): # Model methods self.network + # TODO: COMMENT BACK IN!!! self.clustering_cost self.mi_cost self.cost @@ -215,11 +216,11 @@ def network(self): # Feedforward layer feedforward = tf_utils.conv1d_layer(BLSTM_4, - [1, self.layer_size, self.embedding_size * self.F]) + [1, self.layer_size, (self.embedding_size + self.auxiliary_size) * self.F]) # Reshape the feedforward output to have shape (T,F,D) z = tf.reshape(feedforward, - [shape[0], shape[1], self.F, self.embedding_size]) + [shape[0], shape[1], self.F, self.embedding_size + self.auxiliary_size]) # indices helpers for fuzzy c-means #known_sources_init = np.zeros(self.num_training_sources) @@ -258,7 +259,7 @@ def network(self): # batch, features, embedding embeddings = tf.reshape(embedding, - [shape[0], shape[1] * self.F, self.embedding_size]) + [shape[0], shape[1] * self.F, self.embedding_size + self.auxiliary_size]) # compute fuzzy assignments # batch, nfeatures, nsources @@ -405,7 +406,7 @@ def clustering_cost(self): tf.pow(W, self.fuzzifier) * squared_diffs) return clustering_loss - + @tf_utils.scope_decorator def mi_cost(self): """ @@ -419,14 +420,25 @@ def mi_cost(self): tf.expand_dims(self.X_clean, -1)) return mi_cost - + @tf_utils.scope_decorator def cost(self): """ Constuct the cost function op for the cost function used for clustering and the mask inference head """ - + + # # TODO: REMOVE!!! + # cluster_output, mi_output, W, squared_diffs = self.network + + # clustering_loss = tf.reduce_mean( + # tf.pow(W, self.fuzzifier) * squared_diffs) + + # # broadcast product along source dimension + # mi_loss = tf.square(self.y_clean - mi_output * + # tf.expand_dims(self.X_clean, -1)) + + # TODO: COMMENT BACK IN!!! clustering_loss = self.clustering_cost mi_loss = self.mi_cost @@ -470,14 +482,14 @@ def get_masks(self, X_in, nsources=2, nclustering_iterations_max=500, iterations """ Compute the masks for the input spectrograms """ - + nspectrograms = len(X_in) #I = np.arange(nspectrograms * nsources, dtype=np.int32).reshape(nspectrograms, nsources) I = np.tile(np.arange(nsources, dtype=np.int32), reps=(nspectrograms, 1)) - + opt = tf.train.AdamOptimizer() clustering_minimize = opt.minimize(self.clustering_cost, var_list=[self.speaker_vectors]) - + previous_cost = np.finfo('float').max iterations_count = 0 for i in range(nclustering_iterations_max): @@ -489,19 +501,23 @@ def get_masks(self, X_in, nsources=2, nclustering_iterations_max=500, iterations previous_cost = cost else: iterations_count += 1 - + if iterations_count >= iterations_stop: break masks = self.sess.run(self.network, {self.X: X_in, self.I: I})[1] return masks - def get_vectors(self, X_in): + def get_vectors(self, X_in, nsources=2): """ Compute the embedding vectors for the input spectrograms """ - vectors = self.sess.run(self.network, {self.X: X_in})[0] + nspectrograms = len(X_in) + #I = np.arange(nspectrograms * nsources, dtype=np.int32).reshape(nspectrograms, nsources) + I = np.tile(np.arange(nsources, dtype=np.int32), reps=(nspectrograms, 1)) + + vectors = self.sess.run(self.network, {self.X: X_in, self.I: I})[0] return vectors def get_cost(self, X_in, X_clean_in, y_in, y_clean_in, I_in): diff --git a/magnolia/python/models/dnndenoise/cluster_mask_unified.py b/magnolia/python/models/dnndenoise/cluster_mask_unified.py index 4b5b67e..aebf212 100644 --- a/magnolia/python/models/dnndenoise/cluster_mask_unified.py +++ b/magnolia/python/models/dnndenoise/cluster_mask_unified.py @@ -81,6 +81,8 @@ def build_graph(self, graph): # Model methods self.network + self.clustering_cost + self.mi_cost self.cost self.optimizer @@ -211,11 +213,11 @@ def network(self): # Feedforward layer feedforward = tf_utils.conv1d_layer(BLSTM_4, - [1, self.layer_size, self.embedding_size * self.F]) + [1, self.layer_size, (self.embedding_size + self.auxiliary_size) * self.F]) # Reshape the feedforward output to have shape (T,F,D) z = tf.reshape(feedforward, - [shape[0], shape[1], self.F, self.embedding_size]) + [shape[0], shape[1], self.F, self.embedding_size + self.auxiliary_size]) # indices helpers for fuzzy c-means flattened_I = tf.reshape(self.I, shape=[shapeI[0] * shapeI[1]]) @@ -238,7 +240,7 @@ def network(self): # batch, all features, embedding embeddings = tf.reshape(embedding, - [shape[0], shape[1] * self.F, self.embedding_size]) + [shape[0], shape[1] * self.F, self.embedding_size + self.auxiliary_size]) # compute fuzzy assignments # batch, nsource in mix, nfeatures @@ -298,6 +300,33 @@ def network(self): return embedding, mi_head + @tf_utils.scope_decorator + def clustering_cost(self): + """ + Constuct the cost function op for the cost function used for clustering + """ + + cluster_output, mi_output, W, squared_diffs = self.network + + clustering_loss = tf.reduce_mean( + tf.pow(W, self.fuzzifier) * squared_diffs) + + return clustering_loss + + @tf_utils.scope_decorator + def mi_cost(self): + """ + Constuct the cost function op for the cost function used for mask inference head + """ + + cluster_output, mi_output, W, squared_diffs = self.network + + # broadcast product along source dimension + mi_cost = tf.square(self.y_clean - mi_output * + tf.expand_dims(self.X_clean, -1)) + + return mi_cost + @tf_utils.scope_decorator def cost(self): """ @@ -355,14 +384,14 @@ def get_masks(self, X_in, nsources=2, nclustering_iterations_max=500, iterations """ Compute the masks for the input spectrograms """ - + nspectrograms = len(X_in) #I = np.arange(nspectrograms * nsources, dtype=np.int32).reshape(nspectrograms, nsources) I = np.tile(np.arange(nsources, dtype=np.int32), reps=(nspectrograms, 1)) - + opt = tf.train.AdamOptimizer() clustering_minimize = opt.minimize(self.clustering_cost, var_list=[self.speaker_vectors]) - + previous_cost = np.finfo('float').max iterations_count = 0 for i in range(nclustering_iterations_max): @@ -374,7 +403,7 @@ def get_masks(self, X_in, nsources=2, nclustering_iterations_max=500, iterations previous_cost = cost else: iterations_count += 1 - + if iterations_count >= iterations_stop: break diff --git a/magnolia/python/utils/clustering_utils.py b/magnolia/python/utils/clustering_utils.py index 5559480..40e0611 100644 --- a/magnolia/python/utils/clustering_utils.py +++ b/magnolia/python/utils/clustering_utils.py @@ -361,3 +361,78 @@ def l41_regression_signal(spec, model): signal = model.get_signal(model_spec) return signal.transpose(0, 2, 1, 3) + + +def mask_cluster_clustering_separate(spec, model, num_sources, + binary_mask=True): + """ + Takes in a spectrogram and a model which has a get_vectors method and returns + the specified number of output sources. + + Inputs: + spec: Spectrogram (in the format from a MixIterator) to separate. + model: Instance of model to use to separate the signal + num_sources: Integer number of sources to separate into + binary_mask: If true, computes the binary mask. Otherwise + computes a soft mask + + Returns: + sources: Numpy ndarray of shape (num_sources, Spectrogram.shape) + """ + + + model_spec = preprocess_chimera_batch(spec)[1] + # Get the T-F embedding vectors for this signal from the model + vectors = model.get_vectors(model_spec) + + # Clustering algo + clusterer = KMeans(n_clusters=num_sources, random_state=0) + + # Run clustering algorithm on the vectors with k=num_sources to recover the + # signal masks + masks = get_cluster_masks(vectors, num_sources, binary_mask=binary_mask, algo=clusterer) + + # Apply the masks from the clustering to the input signal + masked_specs = apply_masks(spec[0].T, masks) + + sources = np.stack(masked_specs) + + return sources.transpose(0, 2, 1) + + +def mask_cluster_mask(spec, model, num_sources, **kwd_args): + model_spec = preprocess_chimera_batch(spec)[1] + # TODO: COMMENT BACK IN!!! + soft_masks = model.get_masks(model_spec, num_sources, **kwd_args) + + masked_specs = soft_masks*np.expand_dims(spec.transpose(0, 2, 1), axis=-1) + + return masked_specs.transpose(0, 2, 1, 3) + + # # TODO: REMOVE + # # Get the T-F embedding vectors for this signal from the model + # vectors = model.get_vectors(model_spec) + + # # Clustering algo + # clusterer = KMeans(n_clusters=num_sources, random_state=0) + + # # Get the shape of the input + # shape = np.shape(vectors) + + # vectorsr = vectors[0].reshape((shape[1]*shape[2], shape[3])) + + # # Do clustering + # clusterer.fit(vectorsr) + + # cluster_centers = clusterer.cluster_centers_ + + # squared_diffs_pow = np.power(np.sum(np.square(np.expand_dims(vectorsr, 1) - np.expand_dims(cluster_centers, 0)), -1), + # 1. / (model.fuzzifier - 1.)) + + # W_denom = np.expand_dims(np.sum(np.reciprocal(squared_diffs_pow), -1), -1) + + # W = np.reciprocal(squared_diffs_pow * W_denom) + + # clustering_factors = np.reshape(W, (shape[2], shape[1], num_sources)).transpose((1, 0, 2)) + + # return np.expand_dims(model_spec, -1) * np.expand_dims(clustering_factors, 0) From d5def3c49a8619e95cd0835b9d7239322b1d118d Mon Sep 17 00:00:00 2001 From: Jeffrey Hetherly Date: Fri, 26 Jan 2018 16:36:13 +0000 Subject: [PATCH 13/16] removed superfluous unity network --- magnolia/python/analysis/comparison_plot.py | 54 +-- .../out_of_set_sdr_delta_versus_input_snr.pdf | Bin 17827 -> 16720 bytes ...out_of_set_sdr_delta_versus_noise_type.pdf | Bin 18116 -> 18652 bytes .../models/dnndenoise/cluster_mask_unified.py | 429 ------------------ .../RatioMaskClusterUnified/training.py | 101 ----- 5 files changed, 27 insertions(+), 557 deletions(-) delete mode 100644 magnolia/python/models/dnndenoise/cluster_mask_unified.py delete mode 100644 magnolia/python/training/denoising/RatioMaskClusterUnified/training.py diff --git a/magnolia/python/analysis/comparison_plot.py b/magnolia/python/analysis/comparison_plot.py index 45e8372..2d1e9e9 100644 --- a/magnolia/python/analysis/comparison_plot.py +++ b/magnolia/python/analysis/comparison_plot.py @@ -12,7 +12,7 @@ def format_dae_columns(df): cols[5] = 'Input_SNR' cols[6] = 'Input_SDR' cols[7] = 'Output_SDR' - + df.columns = cols @@ -50,7 +50,7 @@ def make_sdr_delta_versus_noise_source_plot(models, df_base_name): df_name = '{}_df'.format(df_base_name) mean_multiindex_name = ('SDR_Improvement', 'mean') eotm_multiindex_name = ('SDR_Improvement', 'error_on_the_mean') - + all_groups = {} all_colors = {} all_names = [] @@ -68,11 +68,11 @@ def make_sdr_delta_versus_noise_source_plot(models, df_base_name): all_label_df = all_groups[model['name']] else: all_label_df = all_label_df.merge(all_groups[model['name']], how='outer') - + labels = all_label_df['Noise_Type'].unique() n_groups = len(labels) del all_label_df - + # create plot fig, ax = plt.subplots(figsize=(8, 6)) index = np.arange(n_groups) @@ -80,7 +80,7 @@ def make_sdr_delta_versus_noise_source_plot(models, df_base_name): bar_width = (n_groups + 1)/(1.15*n_groups*len(models)) #bar_width = 0.15 opacity = 0.8 - + offset = 0 all_rects = [] for entry_name in all_names: @@ -88,7 +88,7 @@ def make_sdr_delta_versus_noise_source_plot(models, df_base_name): groups = all_groups[entry_name] if entry_name in all_colors: color = all_colors[entry_name] - + #male_means = groups[groups['Speaker_Sex'] == 'M'] #male_means = male_means[male_means['Noise_Type'] == labels].fillna(0) male_means = groups[groups['Noise_Type'] == labels].fillna(0) @@ -100,9 +100,9 @@ def make_sdr_delta_versus_noise_source_plot(models, df_base_name): color=color, label=entry_name, yerr=male_errors) - + offset += 1 - + for i in range(len(labels)): labels[i] = labels[i].replace('_', ' ') plt.xticks(index + (len(all_names)/2 - 0.5)*bar_width, labels) @@ -114,7 +114,7 @@ def make_sdr_delta_versus_noise_source_plot(models, df_base_name): plt.title('SDR Improvement Versus Noise Type', fontsize=20) ax.xaxis.label.set_size(15) ax.yaxis.label.set_size(15) - + ylim = [-0.5, 1.2*ax.get_ylim()[1]] #ylim[0] = -0.5 ax.set_ylim(ylim) @@ -123,7 +123,7 @@ def make_sdr_delta_versus_noise_source_plot(models, df_base_name): loc='upper center', ncol=3, mode='expand') #ax.legend(bbox_to_anchor=(1.5, 1.5)) plt.tight_layout() - + plt.savefig('{}_sdr_delta_versus_noise_type.pdf'.format(df_base_name), format='pdf') @@ -131,7 +131,7 @@ def make_sdr_delta_versus_input_snr_plot(models, df_base_name, bins): df_name = '{}_df'.format(df_base_name) mean_multiindex_name = ('SDR_Improvement', 'mean') eotm_multiindex_name = ('SDR_Improvement', 'error_on_the_mean') - + all_groups = {} all_colors = {} all_names = [] @@ -149,17 +149,17 @@ def make_sdr_delta_versus_input_snr_plot(models, df_base_name, bins): all_label_df = all_groups[model['name']] else: all_label_df = all_label_df.merge(all_groups[model['name']], how='outer') - + labels = all_label_df['Input_SNR_Bin'].unique() n_groups = len(labels) del all_label_df - + # create plot fig, ax = plt.subplots(figsize=(8, 6)) index = np.arange(n_groups) bar_width = (bins[-1] - bins[0])/(1.15*n_groups*len(models)) opacity = 0.8 - + offset = 0 all_rects = [] for entry_name in all_names: @@ -167,7 +167,7 @@ def make_sdr_delta_versus_input_snr_plot(models, df_base_name, bins): groups = all_groups[entry_name] if entry_name in all_colors: color = all_colors[entry_name] - + #male_means = groups[groups['Speaker_Sex'] == 'M'] #male_means = male_means[male_means['Input_SNR_Bin'] == labels].fillna(0) male_means = groups[groups['Input_SNR_Bin'] == labels].fillna(0) @@ -179,9 +179,9 @@ def make_sdr_delta_versus_input_snr_plot(models, df_base_name, bins): color=color, label=entry_name, yerr=male_errors) - + offset += 1 - + print_labels = [] for i in range(len(labels)): #print_labels.append('[{}, {})'.format(i - 5, i - 4)) @@ -196,7 +196,7 @@ def make_sdr_delta_versus_input_snr_plot(models, df_base_name, bins): plt.title('SDR Improvement Versus Input SNR', fontsize=20) ax.xaxis.label.set_size(15) ax.yaxis.label.set_size(15) - + ylim = [-0.5, 1.2*ax.get_ylim()[1]] #ylim[0] = -0.5 ax.set_ylim(ylim) @@ -204,7 +204,7 @@ def make_sdr_delta_versus_input_snr_plot(models, df_base_name, bins): plt.legend(fontsize=12, edgecolor='black', loc='upper center', ncol=3, mode='expand') plt.tight_layout() - + plt.savefig('{}_sdr_delta_versus_input_snr.pdf'.format(df_base_name), format='pdf') @@ -222,25 +222,25 @@ def main(): # 'color': '#E0FBFC' #}, { - 'name': 'Chimera MI', + 'name': 'DC + MI (MI)', 'in_set': '/local_data/magnolia/experiment_data/date_2017_09_28_time_13_14/aux/evaluations/bss/chimera/mi_in_sample_test_sdr_summary.csv', 'out_of_set': '/local_data/magnolia/experiment_data/date_2017_09_28_time_13_14/aux/evaluations/bss/chimera/mi_out_of_sample_test_sdr_summary.csv', 'color': '#3D5A80' }, { - 'name': 'Chimera DC', + 'name': 'DC + MI (C)', 'in_set': '/local_data/magnolia/experiment_data/date_2017_09_28_time_13_14/aux/evaluations/bss/chimera/dc_in_sample_test_sdr_summary.csv', 'out_of_set': '/local_data/magnolia/experiment_data/date_2017_09_28_time_13_14/aux/evaluations/bss/chimera/dc_out_of_sample_test_sdr_summary.csv', 'color': '#0C0A3E' }, { - 'name': 'SCE + Mask MI', + 'name': 'SCE + MI (MI)', 'in_set': '/local_data/magnolia/experiment_data/date_2017_09_28_time_13_14/aux/evaluations/bss/mask_sce/mi_in_sample_test_sdr_summary.csv', 'out_of_set': '/local_data/magnolia/experiment_data/date_2017_09_28_time_13_14/aux/evaluations/bss/mask_sce/mi_out_of_sample_test_sdr_summary.csv', 'color': '#CA054D' }, { - 'name': 'SCE + Mask Clustering', + 'name': 'SCE + MI (C)', 'in_set': '/local_data/magnolia/experiment_data/date_2017_09_28_time_13_14/aux/evaluations/bss/mask_sce/dc_in_sample_test_sdr_summary.csv', 'out_of_set': '/local_data/magnolia/experiment_data/date_2017_09_28_time_13_14/aux/evaluations/bss/mask_sce/dc_out_of_sample_test_sdr_summary.csv', 'color': '#393E41' @@ -255,15 +255,15 @@ def main(): # TODO: the input SNR range should be determined automatically bins = np.linspace(-5, 5, 11) bins[-1] = 1.02*bins[-1] - + load_dataframes(models) - + make_sdr_delta_versus_input_snr_plot(models, 'out_of_set', bins) make_sdr_delta_versus_noise_source_plot(models, 'out_of_set') #make_sdr_delta_versus_sex_plot(models, 'out_of_set') make_sdr_delta_versus_input_snr_plot(models, 'in_set', bins) make_sdr_delta_versus_noise_source_plot(models, 'in_set') - + if __name__ == '__main__': - main() \ No newline at end of file + main() diff --git a/magnolia/python/analysis/out_of_set_sdr_delta_versus_input_snr.pdf b/magnolia/python/analysis/out_of_set_sdr_delta_versus_input_snr.pdf index 06bc917121371d577384738b1714e59d48374dd8..85e0beac088cdc6a968c8fbe821d9a9bf4d20871 100644 GIT binary patch delta 6266 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 delta 7156 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 H8qNO$B<$Ny diff --git a/magnolia/python/analysis/out_of_set_sdr_delta_versus_noise_type.pdf b/magnolia/python/analysis/out_of_set_sdr_delta_versus_noise_type.pdf index 999635f2d79d1cc842b63904defaa673aa2308ee..3f8d3797ae9bf4912cb840d184143341e5f5d2a2 100644 GIT binary patch delta 6321 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 delta 5857 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 0: - # Define the auxiliary vectors to use during training - self.auxiliary_vectors = tf_utils.weight_variable( - [self.auxiliary_size, self.auxiliary_size], - tf.sqrt(2 / self.auxiliary_size)) - else: - self.auxiliary_vectors = None - - # Model methods - self.network - self.clustering_cost - self.mi_cost - self.cost - self.optimizer - - return graph - - def learn_from_epoch(self, epoch_id, - validate_every, - stop_threshold, - training_mixer, - validation_mixer, - batch_formatter, - model_save_base): - - batch_count = self.batch_count - # Training epoch loop - for batch in iter(training_mixer): - unscaled_spectral_sum_batch, scaled_spectral_sum_batch, spectral_masks_batch, spectral_sources_batch = batch_formatter( - batch[0], batch[1], batch[2]) - # should be dimensions of (batch size, source) - uids_batch = batch[3] - - # override ids for simply signal/noise - if self.collapse_sources: - uids_batch[:, 0] = 0 - uids_batch[:, 1] = 1 - - # Train the model on one batch and get the cost - c = self.train_on_batch(scaled_spectral_sum_batch, unscaled_spectral_sum_batch, - spectral_sources_batch, uids_batch) - - # Store the training cost - self.costs.append(c) - - # Evaluate the model on the validation data - if (batch_count + 1) % validate_every == 0: - # Store the training cost - self.t_costs.append(np.mean(self.costs)) - # Reset the cost over the last 10 batches - self.costs = [] - - # Compute average validation score - all_c_v = [] - for vbatch in iter(validation_mixer): - unscaled_spectral_sum_batch, scaled_spectral_sum_batch, spectral_masks_batch, spectral_sources_batch = batch_formatter( - vbatch[0], vbatch[1], vbatch[2]) - # dimensions of (batch size, source) - uids_batch = vbatch[3] - - # override ids for simply signal/noise - if self.collapse_sources: - uids_batch[:, 0] = 0 - uids_batch[:, 1] = 1 - - # Get the cost on the validation batch - c_v = self.get_cost(scaled_spectral_sum_batch, unscaled_spectral_sum_batch, - spectral_sources_batch, uids_batch) - all_c_v.append(c_v) - - ave_c_v = np.mean(all_c_v) - - # Check if the validation cost is below the minimum validation cost, and if so, save it. - # and len(self.nbatches) > 0: - if len(self.v_costs) > 0 and ave_c_v < min(self.v_costs): - logger.info("Saving the model because validation score is {} below the old minimum.".format( - min(self.v_costs) - ave_c_v)) - - # Save the model to the specified path - self.save(model_save_base) - - # Record the batch that the model was last saved on - self.last_saved = batch_count # self.nbatches[-1] - - # Store the validation cost - self.v_costs.append(ave_c_v) - - # Store the current batch number - # self.nbatches.append(batch_count) - - logger.info("Training cost on batch {} is {}.".format( - batch_count, self.t_costs[-1])) - logger.info("Validation cost on batch {} is {}.".format( - batch_count, self.v_costs[-1])) - logger.info("Last saved {} batches ago.".format( - batch_count - self.last_saved)) - - # Stop training if the number of iterations since the last save point exceeds the threshold - if batch_count - self.last_saved > stop_threshold: - logger.info("Early stopping criteria met!") - break - - batch_count += 1 - - self.batch_count = batch_count - - def infer(self, **kw_args): - pass - - @tf_utils.scope_decorator - def network(self): - """ - Construct the op for the network used in [1]. This consists of four - BLSTM layers followed by a dense layer giving a set of T-F vectors of - dimension embedding_size - """ - - m = self.fuzzifier - reduced_contrast = True - - # Get the shape of the input - shape = tf.shape(self.X) - shapeI = tf.shape(self.I) - - # BLSTM layer one - BLSTM_1 = tf_utils.BLSTM_(self.X, self.layer_size, 'one', - activation=self.nonlinearity) - - # BLSTM layer two - BLSTM_2 = tf_utils.BLSTM_(BLSTM_1, self.layer_size, 'two', - activation=self.nonlinearity) - - # BLSTM layer three - BLSTM_3 = tf_utils.BLSTM_(BLSTM_2, self.layer_size, 'three', - activation=self.nonlinearity) - - # BLSTM layer four - BLSTM_4 = tf_utils.BLSTM_(BLSTM_3, self.layer_size, 'four', - activation=self.nonlinearity) - - # Feedforward layer - feedforward = tf_utils.conv1d_layer(BLSTM_4, - [1, self.layer_size, (self.embedding_size + self.auxiliary_size) * self.F]) - - # Reshape the feedforward output to have shape (T,F,D) - z = tf.reshape(feedforward, - [shape[0], shape[1], self.F, self.embedding_size + self.auxiliary_size]) - - # indices helpers for fuzzy c-means - flattened_I = tf.reshape(self.I, shape=[shapeI[0] * shapeI[1]]) - batch_range = tf.range(shape[0] * shapeI[1]) - - current_sources_indices, current_sources_indices_batch = tf.unique( - flattened_I) - known_sources_indices = tf.get_variable('known_sources_indices', - initializer=tf.constant( - [], dtype=tf.int32), - dtype=tf.int32, - validate_shape=False, trainable=False) - known_sources_indices = tf.sets.set_union(tf.expand_dims(current_sources_indices, 0), - tf.expand_dims(known_sources_indices, 0)).values - - # clustering head - embedding = self.nonlinearity(z) - # Normalize the T-F vectors to get the network output - embedding = tf.nn.l2_normalize(embedding, 3) - - # batch, all features, embedding - embeddings = tf.reshape(embedding, - [shape[0], shape[1] * self.F, self.embedding_size + self.auxiliary_size]) - - # compute fuzzy assignments - # batch, nsource in mix, nfeatures - if self.auxiliary_vectors is None: - # batch, nsource in mix, nfeatures - squared_diffs_batch = tf.reduce_sum(tf.square(tf.expand_dims(embeddings, 1) - - tf.reshape(tf.expand_dims(tf.gather(self.speaker_vectors, flattened_I), 1), - [shape[0], shapeI[1], 1, self.embedding_size])), - -1) - diffs_pow_matrix_batch = tf.pow(squared_diffs_batch, 1. / (m - 1.)) - # batch, 1, nfeatures - W_denom = tf.expand_dims(tf.reduce_sum(tf.reciprocal(diffs_pow_matrix_batch), 1), 1) - else: - # NOTE: true/aux refers to both the coordinates and cluster centers - true_embeddings = embeddings[:, :, :-self.auxiliary_size] - aux_embeddings = embeddings[:, :, -self.auxiliary_size:] - true_embeddings_l2 = tf.reduce_sum( - tf.square(true_embeddings), axis=-1) - aux_embeddings_l2 = tf.reduce_sum( - tf.square(aux_embeddings), axis=-1) - # batch, nsource in mix, nfeatures - true_squared_diffs_batch = tf.reduce_sum(tf.square(tf.expand_dims(true_embeddings, 1) - - tf.reshape(tf.expand_dims(tf.gather(self.speaker_vectors, flattened_I), 1), - [shape[0], shapeI[1], 1, self.embedding_size])), - -1) - squared_diffs_batch = true_squared_diffs_batch + tf.expand_dims(aux_embeddings_l2, 1) - diffs_pow_matrix_batch = tf.pow(squared_diffs_batch, 1. / (m - 1.)) - # batch, nfeatures, nsources (aux) - aux_squared_diffs = tf.reduce_sum(tf.square(tf.expand_dims( - aux_embeddings, 2) - tf.expand_dims(tf.expand_dims(self.auxiliary_vectors, 0), 0)), -1) - aux_diffs_pow_matrix = tf.pow(aux_squared_diffs + tf.expand_dims(true_embeddings_l2, -1), 1. / (m - 1.)) - - W_denom = tf.reduce_sum(tf.reciprocal( - tf.concat([ - tf.transpose(diffs_pow_matrix_batch, perm=[0, 2, 1]), - aux_diffs_pow_matrix - ], axis=2) - ), -1) - # batch, 1, nfeatures - W_denom = tf.expand_dims(W_denom, 1) - - # batch, nsource in mix, nfeatures - W = tf.reciprocal(diffs_pow_matrix_batch * W_denom, name='W') - # batch, nfeatures, nsource in mix - clustering_factors = tf.transpose(W, perm=[0, 2, 1]) - - # MI head - # Feedforward layer - # feedforward_fc = tf_utils.conv2d_layer(z, - # [1, 1, self.embedding_size, self.num_reco_sources]) - # perform a softmax along the source dimension - #mi_head = tf.nn.softmax(feedforward_fc, dim=3) - - # MI head - mi_head = tf.reshape(clustering_factors, - [shape[0], shape[1], self.F, shapeI[1]]) - - return embedding, mi_head - - @tf_utils.scope_decorator - def clustering_cost(self): - """ - Constuct the cost function op for the cost function used for clustering - """ - - cluster_output, mi_output, W, squared_diffs = self.network - - clustering_loss = tf.reduce_mean( - tf.pow(W, self.fuzzifier) * squared_diffs) - - return clustering_loss - - @tf_utils.scope_decorator - def mi_cost(self): - """ - Constuct the cost function op for the cost function used for mask inference head - """ - - cluster_output, mi_output, W, squared_diffs = self.network - - # broadcast product along source dimension - mi_cost = tf.square(self.y_clean - mi_output * - tf.expand_dims(self.X_clean, -1)) - - return mi_cost - - @tf_utils.scope_decorator - def cost(self): - """ - Constuct the cost function op for the cost function used in sce - and the mask inference head - """ - - # Get the shape of the target - shape = tf.shape(self.y_clean) - - #cluster_output, mi_output, W, squared_diffs = self.network - cluster_output, mi_output = self.network - - # broadcast product along source dimension - mi_cost = tf.square(self.y_clean - mi_output * - tf.expand_dims(self.X_clean, -1)) - - return tf.reduce_mean(mi_cost) - - @tf_utils.scope_decorator - def optimizer(self): - """ - Constructs the optimizer op used to train the network - """ - opt = tf.train.AdamOptimizer() - return opt.minimize(self.cost) - - # def save(self, path): - # """ - # Saves the model to the specified path. - # """ - # self.saver.save(self.sess, path) - - # def load(self, path): - # """ - # Load the model from the specified path. - # """ - # self.saver.restore(self.sess, path) - - def train_on_batch(self, X_train, X_train_clean, y_train_clean, I_train): - """ - Train the model on a batch with input X and target y. Returns the cost - computed on this batch. - """ - - cost, _ = self.sess.run([self.cost, self.optimizer], - {self.X: X_train, - self.X_clean: X_train_clean, - self.y_clean: y_train_clean, - self.I: I_train}) - - return cost - - def get_masks(self, X_in, nsources=2, nclustering_iterations_max=500, iterations_stop=10): - """ - Compute the masks for the input spectrograms - """ - - nspectrograms = len(X_in) - #I = np.arange(nspectrograms * nsources, dtype=np.int32).reshape(nspectrograms, nsources) - I = np.tile(np.arange(nsources, dtype=np.int32), reps=(nspectrograms, 1)) - - opt = tf.train.AdamOptimizer() - clustering_minimize = opt.minimize(self.clustering_cost, var_list=[self.speaker_vectors]) - - previous_cost = np.finfo('float').max - iterations_count = 0 - for i in range(nclustering_iterations_max): - cost, _ = self.sess.run([self.clustering_cost, - clustering_minimize], - {self.X: X_in, - self.I: I}) - if cost < previous_cost: - previous_cost = cost - else: - iterations_count += 1 - - if iterations_count >= iterations_stop: - break - - masks = self.sess.run(self.network, {self.X: X_in, self.I: I})[1] - return masks - - def get_vectors(self, X_in): - """ - Compute the embedding vectors for the input spectrograms - """ - - vectors = self.sess.run(self.network, {self.X: X_in})[0] - return vectors - - def get_cost(self, X_in, X_clean_in, y_clean_in, I_in): - """ - Computes the cost of a batch, but does not update any model parameters. - """ - cost = self.sess.run(self.cost, {self.X: X_in, - self.X_clean: X_clean_in, - self.y_clean: y_clean_in, - self.I: I_in}) - return cost diff --git a/magnolia/python/training/denoising/RatioMaskClusterUnified/training.py b/magnolia/python/training/denoising/RatioMaskClusterUnified/training.py deleted file mode 100644 index ae1a8b3..0000000 --- a/magnolia/python/training/denoising/RatioMaskClusterUnified/training.py +++ /dev/null @@ -1,101 +0,0 @@ -# Generic imports -import argparse -import logging.config -import json - -import numpy as np -import pandas as pd -import tensorflow as tf - -# Import the RatioMaskClusterUnified separation model -from magnolia.models import make_model - -# Import utilities for using the model -from magnolia.training.data_iteration.mix_iterator import MixIterator -from magnolia.utils.training import preprocess_chimera_batch -#from magnolia.utils.tf_utils import double_learnable_relu - - -def main(): - # parse command line arguments - parser = argparse.ArgumentParser(description='Train the k-means clustering + ratio mask network.') - # parser.add_argument('--model_settings', '-s', - # default='../../../../data/models_settings/chimera_template.json', - # help='model settings JSON file') - # parser.add_argument('--_settings', '-s', - # default='../../../../data/models_settings/chimera_template.json', - # help='model settings JSON file') - parser.add_argument('--logger_settings', '-l', - default='../../../../data/logging_settings/logging.conf', - help='logging configuration file') - args = parser.parse_args() - - # Load logging configuration - logging.config.fileConfig(args.logger_settings) - logger = logging.getLogger('model') - - # Number of epochs - num_epochs = 20 # try 20 - # Threshold for stopping if the model hasn't improved for this many consecutive batches - stop_threshold = 10000 - # validate every number of these batches - validate_every = 100 - train_batchsize = 512 - train_mixes = ['/local_data/magnolia/pipeline_data/date_2017_09_27_time_13_25/settings/mixing_LibriSpeech_UrbanSound8K_train.json'] - train_from_disk = False - validate_batchsize = 500 - validate_mixes = ['/local_data/magnolia/pipeline_data/date_2017_09_27_time_13_25/settings/mixing_LibriSpeech_UrbanSound8K_validate.json'] - validate_from_disk = False - model_params = { - 'layer_size': 500, - 'embedding_size': 10, - 'auxiliary_size': 0, - 'alpha': 0.1, # try 0.9 - 'nonlinearity': 'tf.tanh', - 'fuzzifier': 2, - 'num_reco_sources': 2, - 'normalize': False, - 'collapse_sources': False, - } - model_location = '/gpu:0' - uid_settings = '/local_data/magnolia/pipeline_data/date_2017_09_27_time_13_25/settings/assign_uids_LibriSpeech_UrbanSound8K.json' - model_save_base = '/local_data/magnolia/experiment_data/date_2017_09_28_time_13_14/aux/model_saves/mask_cluster_unified' - - - training_mixer = MixIterator(mixes_settings_filenames=train_mixes, - batch_size=train_batchsize, - read_waveform=False, - from_disk=train_from_disk) - - validation_mixer = MixIterator(mixes_settings_filenames=validate_mixes, - batch_size=validate_batchsize, - read_waveform=False, - from_disk=validate_from_disk) - - # get frequency dimension - frequency_dim = training_mixer.sample_dimensions()[0] - # TODO: throw an exception - assert(frequency_dim == validation_mixer.sample_dimensions()[0]) - - # get number of sources - settings = json.load(open(uid_settings)) - uid_file = settings['output_file'] - uid_csv = pd.read_csv(uid_file) - number_of_sources = uid_csv['uid'].max() + 1 - - model_params['F'] = frequency_dim - model_params['num_training_sources'] = number_of_sources - config = {'model_params': model_params, - 'device': model_location} - model = make_model('RatioMaskClusterUnified', config) - - model.train(validate_every=validate_every, - stop_threshold=stop_threshold, - training_mixer=training_mixer, - validation_mixer=validation_mixer, - batch_formatter=preprocess_chimera_batch, - model_save_base=model_save_base) - - -if __name__ == '__main__': - main() From be025dcfa9515ea2d76aada812fdd6aa75eea603 Mon Sep 17 00:00:00 2001 From: Jeffrey Hetherly Date: Fri, 26 Jan 2018 18:46:07 +0000 Subject: [PATCH 14/16] removed superfluous unity network (__init__) --- magnolia/python/models/__init__.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/magnolia/python/models/__init__.py b/magnolia/python/models/__init__.py index 747799a..0a65933 100644 --- a/magnolia/python/models/__init__.py +++ b/magnolia/python/models/__init__.py @@ -3,7 +3,6 @@ from .dnndenoise.L41_regression_model import L41RegressionModel from .dnndenoise.sce_mask import RatioMaskSCE from .dnndenoise.cluster_mask import RatioMaskCluster -from .dnndenoise.cluster_mask_unified import RatioMaskClusterUnified __all__ = [ "JFLEC", @@ -11,7 +10,6 @@ "L41RegressionModel", "RatioMaskSCE", "RatioMaskCluster", - "RatioMaskClusterUnified", ] def make_model(model_name, config): From 70e4db219419aa3d6f0c1422a1d9faecbe01c400 Mon Sep 17 00:00:00 2001 From: Jeffrey Hetherly Date: Tue, 13 Feb 2018 18:33:02 +0000 Subject: [PATCH 15/16] ssp final --- magnolia/python/analysis/comparison_plot.py | 6 +-- .../out_of_set_sdr_delta_versus_input_snr.pdf | Bin 16720 -> 17423 bytes ...out_of_set_sdr_delta_versus_noise_type.pdf | Bin 18652 -> 19026 bytes .../separate_sample_from_mix.py | 2 +- .../python/models/dnndenoise/cluster_mask.py | 37 ++++++-------- .../denoising/RatioMaskCluster/training.py | 6 +-- magnolia/python/utils/clustering_utils.py | 45 ++++++++---------- 7 files changed, 42 insertions(+), 54 deletions(-) diff --git a/magnolia/python/analysis/comparison_plot.py b/magnolia/python/analysis/comparison_plot.py index 2d1e9e9..044d9c0 100644 --- a/magnolia/python/analysis/comparison_plot.py +++ b/magnolia/python/analysis/comparison_plot.py @@ -110,7 +110,7 @@ def make_sdr_delta_versus_noise_source_plot(models, df_base_name): tick.set_rotation(75) tick.set_fontsize(12) plt.xlabel('Noise Type') - plt.ylabel('SDR Improvement') + plt.ylabel('SDR Improvement (dB)') plt.title('SDR Improvement Versus Noise Type', fontsize=20) ax.xaxis.label.set_size(15) ax.yaxis.label.set_size(15) @@ -191,8 +191,8 @@ def make_sdr_delta_versus_input_snr_plot(models, df_base_name, bins): tick.set_rotation(0) tick.set_fontsize(12) #plt.xlabel('Input SNR Range') - plt.xlabel('Input SNR') - plt.ylabel('SDR Improvement') + plt.xlabel('Input SNR (dB)') + plt.ylabel('SDR Improvement (dB)') plt.title('SDR Improvement Versus Input SNR', fontsize=20) ax.xaxis.label.set_size(15) ax.yaxis.label.set_size(15) diff --git a/magnolia/python/analysis/out_of_set_sdr_delta_versus_input_snr.pdf b/magnolia/python/analysis/out_of_set_sdr_delta_versus_input_snr.pdf index 85e0beac088cdc6a968c8fbe821d9a9bf4d20871..afd64fd317dd6ece04dedb21f5f8f523487a9d2b 100644 GIT binary patch delta 6672 zcmZv8cRbba`+rBW;@Eq=>|>tga85=U8QCiuwj(23l5lLNvR^VX5=tc{hs^95LM0R# zky6>pO5bzJ@AH0CzxyB8dEWQ+dOok`b=~*FU=$>o2q|xe5WZWlztC~GDhEBTAaCM( zfS)KKehn*o>ZH?0TvFHMfb_TK`$3Z>vM?P!p;>kBitUt+8EL3;-=vXaIA&){Wq0lE z?)NR>&9xj{-_7Zp)?nAmw)EPmfsr2r9P~fuMvnf(8BH3SsJE9MolYFMV`*C&;Gw`F zZQXtS>&nJAo->31v;54=?_G8y1XtVR(4iFp(nZCeoBn%mcYo~8YAtl0_3j8TLW-;U zDpawQ2uLn@d@I`?rHhSubFO+apV`NR9lYlEoXP(5eQvIC0sNAM=||7lxRbWTSuKtl zZe`Q?jm)i|&*i8?8waxQ-{QVoV~ERi-IT_re;7Sy4^9;kxT5Xj@X`-oPX#=d!__TB z`2%hRh6yEuF2KAJ=$hscDL9wf$l zY}o?gnwsGZ@cQ3pV`{?aww^n*HA&I;#G-14MRltT)W}Wi3o_U$@87ps8hdFSQSMBZj{D2TzZ95F_f=sy%Lhbd!1)f zM!-ZO5hfUvWt9C^H2LQj*GCJn(eLmhd8&RJ7^cuBlD4VVtj}NmV3x}N zvDPay#A7*iCEhq4dfr_bIg{Iou>82b{MzE^nK4JFghm^4`jg}xl-xiilZ|M7SE%0C z@(_%JjGj8elUiL*B+)*0?p{qKU%_nX-T2B&1=LwASIt#mWIXPDb|pkNSdd!Vkk(e& z;njN&m;|9Rn(f7=l}-K$elyi!k+-@_0vrR-B9F|2Y5MBqq$Z4kT~oSEsG6=|F% zffI?<^KEZm-h0ww`RR5#bNpbD!>KP3u`js0btPyT$rWA@D~uPk3gi>i9)KRsnYEa4 z>qu#Vx2xqmgynKo2OsFZ@UB+It z`@sxMow-v&{PhQrUJv6Bud7unSZf>JC^OHl+JfW_TBr|2^bB=* zRQ)pEa6AIPrZ7SbovaWjKW`96)UFk(OzEUt(@EkNf}?eSm%5P40JQhU_4w32T3qo8 zSs5!9kdD9a#K!5U2jw|3j&~|_-S}<()Uof{nsLncybeBylDKQW=AZO@fifn&g-0># zc$r&6BNg8+l;+L-o|D?oExnO#3@pJNh%9A^n-3;kvJKId?o{6sG zM^qvn%xa(C_Q1p`&pPD>$af$xJ(yHALuYklLy{}8mjl<3qxiIQH`N1))npRw>Q(Gxs&Jb7K7t2w#nuX^pbh}oTt`8M3!ADjxkAbRW_q zr_C+-Ic}`Z;N#Yo^2^J@z&!#Xd~ag6XRXZl=YgkJNT*&6<`~i3_Z@B5(?2#gnoxay z@v=;PPvp2`YJj!vmCzBHXQC73cZ0A>AYTZ{sC)ilq>Xk#6|eW0fC{wU=+J~C7-lVA zCMtJ2XO9qPR0Sy?BY)AWbMP!^0sq+Na{iqnF5~@0kfE;4#Zi$VMA%u853D{N^dQNH zi*waAT3Vcz;8p0{!_zRQ?ok#}eYZfxQ1f&^t38WsOI8C$=T+-UxRj-ak5YD6Kz>RF zwNAmXq!n!R#O$IE+j+CFPSQBr)rN+jTi=iFt`gREzw|!(@Mv=9!>5s@o62U%6W_cK zH;lyeuRYqBpr7@vfcvXSsssynDuiU<;}+AF3+AE_48 zS5N2=ta&NqXsW}T&O6yNVHegXbZzkXpkCWdgizHL7F`KqkNMfc84=9X0RxsxePJ%L zSRMD2i91n?B63qC+?e^HHY+~#yLjQNL00|(*TiuKw& z^U>YSC3}1IhTXk+t>9V%)`ye36A865KuLb+?&=8PY4FzYIg=9Zqeg>_;|)@^fk_9l zfVAQtv~pWV<1Kd{M||Q9WD<4l5#^sd5#YhZR$;#_gcXA@*@4(P> zly)-uN7~yW?W4=4HR*`nu*j?K$=jGQLGP)gTc@8;5#JQPe0#R2uixKvU8BfffZ4dY z_j6CLw@t?+S~)WZersne!ptb)@r!3i@Pzi>3>P&cg>8+g*$yP_*^Z{Q*Sx%W(Ywj1 zG*0j2D+Kd*Hd-OvxVoD=oDh!9$s^2EQnbkhO1kUehE)+Sqa*QoBJ<&oo~W3Bd8F}> z$hgszOkqZza>mkUjprY>q58IQVtNR;b=jGVd68nd=3nh9HzD}}+CSk^r>88JGYR2c z2Giqh+E+YN8;78iX*tGriwCA}*=Lyt2MtA=jECwr>I5zsmg%2vD_#2%ypDj~)bnIz z%(QWV+*txT#g{hfRT(mSt{K?Y%ig+tB4$11wm>Znq~w#(tz}7Jx$OSa#lei~2@H#} z2M;aWH*a!9`Rdrnf7ZXbcs#4wqcn_=3JDuw@pHZ?7As~t9Fe=iyi)Xfgj1rQ)*#7^ z;8zr>^oceJFStOLKge0DcWwCUy>h>%B%G;BJW%g)!@NN9ipZ^M=Rlr8wh|$^q;bwI zpLebLj0!!F4t$ujh(PDG zN@g2yf9I`vklpsk?XU(Bo(qO1!~ljz%b2MX-yfxKfjv6NK|SqOrnIP%ThLk-4=0)pk+p57C_X;veLS}!Xa zz9RFHdLi*jHZ<4#OSq!14K1O@lPXHCqq6yh8}VcougL69OHyPnM}&C)qmwB-$6Q2Y zUJLoozOfo%rO|aZ|L~;VJbG{D#BJJK%OZGHeOjw*42M9O_rrFmEj006vTx5hKW1%) z_-5JEr;`F=qHFF|Hz(TyISS(aCz-4fxdU3#UJ@QDpYOj=nEHHBbEu+<(9m`YIxj}& zol5t(-KPr0+3u{AbBU-r-!H5a*x(Qx&UrUg!Eu;&EL7AW?Tp7q+W{WX zR$8WJ!;#w!G@GeQ8T3mPB5;0vp{Du1(1{qlVM%8nKPs7OtY9uu9K_DIcyZ4ET8?Pc zBeAh$uO3s@{4iT3EgFS7E?ylQLC!FR9+KP6qUO|S8IJ&x`JVQuW!3qhD)FWP5#w~N z)$wbvv#UBWGj-cRIycM>JS#+qtSl+iexE(x9n8E(^|(M9|MUn$KY?JE`M4~)&zEH< zz%_nSmc4hI_PJ2__k-v1o{JqNKKc2xs@%J{neV+)gy@9@0*T-0`! z{^qUxg3Z=lr#JbmbY~_B#L;}#Wkm7Fjj3t>li^h))u*HC&X>{)le+5N-Ft7}*tqrf zSnZtOR?NuG`|Yr&X&Wyw1KbrWD}i-8QZCy*A18jSDYBcJArk`8$5;GdmK(bvmme0+ z&Rz;5*G32%_|hE%{@@`M%XncD0=c(VubD7=Pc1#w+^e^TrrSdZ4fX4tclA0Ed=8h! zw|civ@vK*OU36@+VObR>DE@Tcn_1iX{JnAW>?4Mw;fMQ$j@FGaexD>7WvLgN+0<$! z-{A`NYMx*XT)DWG6mDNLlAf5H;JW>>`1+!1a(L18#U`yHH*bZax6W()FMUg^v$(>! znNjkJyC27NZ>lcnT{IQ4t;ggqs*g+6Kj{vZmeq~As}}W$sb(iZVb41$-R6mXLcbO| z5BDPP?YQ#kPkTiNu73ESSEr_=i(;xilIT8t(NBCrvorZy)%#CPlRkJT=*=cbg++pi zY$yT-2dJwv1O)oy9laQWbJfiC{o4?X^*fxQR2TJ^^h>1Xe~wVCzsh8BzIuS}&c$ob z#x~t$PleBRDX18TR)^Ahfjd|4(-wP2^+|L|SQAe@n?I3aQ<sMN*Uq@?7CA=~UYtoYJe0?`c)cinwA}J0->i`0MEc2_ z*+F<(xkP1`+maWyH(tJ{H9*{kvkhqw+i%YL$IBg-rd?nzh4HM1IPEGw#xQw~HJS>?xemzz)9Tpn;^;;xHp zV0+;klIHhwOSnPy-N=&gn>AM#u##Pc)W}8;0n0gH;1YWasLLr0HcLUlc@BGUlpOnT zdVuC!Fi=N|1!U#Y09VLy2-ih0h8qU*a(ja1JTTG&ZfXcjQk^j z&lThrfPoLVSqK;tm5PHRjGu9_3Msq^UJe7KY)klepPMcfOl*64tLxgjl6D`u=^l)& zJmKOc@@{fjtUOZt+7`2*RufM*H=~*5D4vV?T$5AszN5)@4*j~No~h9(gPZyZ0^GRX zk=+~AV#6opXI#PWJfeNx+4l1%j@PV)u!ACbUl9`B0-R3LVHHIQxFg21SLU7WcPKP1 zIfk{|K^C?OSRXPObTQ3PTaqjgd&UU#<7Njx3d)cU@^e5ym@tfVRNyWI z>=BkGu?TTPz~g{4Xet~6CW*knRbfjoSX7$i0|-OFOfeYvM5Gmji%WyIM8AP95-_ky z%ojW+2_spHGeJNhDHxa_Q3+;}|GG(5f$}hEayLvtc4=v_Ny-PjCj$dd!Wu!i9E_wY z?F9jelRt zfF++i2_O*&0PaF+LoXg8XaYzj00EG9wE(2@??@X!Vt+@+0VM8sqywN3zaxDBh5Q{E z0VotDqEMjFzcF(FrT9Ct08p6U(MbTc&*1`~_BrqXio!u46XV^_xdj5KUqlRWSN9-1 zfZpeF^|{~=p!ezAF8R9Qy#e$uP5|x&p!Ye+*gmHZfd0kF0Qd1Ge`TN57eMc``a1gK zz0c!a0|E5!3JLIEkpC|U=>1xK@&4{UE`Z`M5e#tO^A`dD#eEU}fZ{$s8QbR%a1RC) z_Z0;AIy&P4#r?_y0mWY$7~p|!{&+IJuOslH51>deFTL-Vi8C*0)=LLkYUe`6H8|Hd%nvj4!yKa?oEV7;0$fnwSp zyGTVe#gM-+MBbb|I0t z|EKytVibb%g8V54g`zwQe_=|Pf5f10$|%*p528_&$M(-;H2NP4kxEE3CQU;Cf&v9K fc=*x3{~L1j4-EFlyD}h^P*^mBgv4hUJRNiF?S$6p^(yM2zjhzE+?v<%R;lPEob^K zLmI(5;$rvYWh~4@t7qjC!^gU%)>X}lzV&f)iLi5eA0t>?Z?jwG3Kz^BOhVTDy}c^1 zyN%kpzRC3OHVN8ybo=}K#Ii40Cw_U>dJBX8RJXk{tAa>6hKNyaE%I1;1?rtBPLwj^ zElcc}TG^*Jf2Z!GswuJ$7n%#3lDjqcQ; zWk4!P|AI?+MbiSl*Ho*$TR&t;qBMIsq52kai}s zN^XMTGo1F=o`H^3G5vZ4)_D9&g=Mhc1wMwoed&y zZrL)4O;5-hoqAjL#dGP2oLt4H^VF=Ydh4D@-xwC;4_*!yCD9kLBw{^-r?j$568rtg zC1?3u`Yo6~IToVb$=sjiaiWD?wmpo`=*(N27UoA`q}5244Ev+DUlFK4-fU)Gb+2I7 zYRu8w`HeO4hn3m2`Sf5Bm)}L`Sn0==MJdpo81l+52szI!q{v<;{eZpjK|L-1WY*C_ zWAGE9{(+{;gsQ^SZC$O68sZUUnYmW9@S#B$w!ge-Tgg*Rjrl&+E9>P~x4iQmzaqq& zglfiG{*FeImK^9GIAzC#bT=*;_NYRTFZoQKJNgXV)Lo(A~IV_$L4L;d8EUEc~S}-SmrJP4;aT+&fW9Jz*==emKaD*N>4e zjsbI>EMZ`_!?%6n1m`px7fqa=(XIEo!5kFH+3Qat9&e&HoXv%q1@063xz$QPEIee- zeJ!V#&0`-V&Zx}Vs;-lzs||BBiK{YE5Y*Q!H@SDVD46YrK%#ag;|U+%6NkM=%Dx~O z^VIorU{4s)8y4omx`Obu;2}WG?Yt(TKH;#A9$9n6<9=+zSV~Szs6^v~9)mSOmQt}c z0*~%f|HN~nY5pD;IQqMG7%q04JLVskmvuJQSclCjiVMAD!6zIc*cNhUv{k?K-bp%r z(^)O3ti_l6d>dF;OmmcraEQt6(j-M0-MOmHw4>+6?UyK?iV650T9`Haq2a_Snu~Wm zr}W4={n8>gAKjjgD~EKb$pO40nyCg23fHX$>CKdYR7NzB!>5DaT|EGt-n2avW3!+Kb#Y6 zZ|tw@)J%2e#0A~Vu^JbQb^8RJb-?tZ^N;mu!x7uURm<1m)^q%})?b3dKJc_%-M_m!=*c;?pp#WG{3TiuPzhQq3?p8i(Z zfiL56^;L`lIyW}|f#NN;RoKO6_XVWNX287xw$4PWj^`oY8aE~0-a6hR$ zC_2W@uZQ{3ZSrJXsZotddY9<8={I)^p$O*k+OKDO?mVLEdK@yGF=bcamxSdkEuQr3 zDi}zG3M~J)(30tZD&!;O{>?!yH$XH=4=j)LeO>H$T5R+J{FIdD?{z7-K#hxDN7<6! zey`_`;BAvd(c5CxNBx9y@0uS~#I5Z=9*V|z@&n21!LE$MVQG*7T#@CG`>BTx8r4)$ zlaTL#2sAr83D@hLw$HjrZS837UPjm_zqE*er&JQvS`iKwi@na;zwx%~7L%pc9RjwY zwDnVSFU)24=?S(LHF{xj-_9or4Y;+}pGxRB!y2_W4fBHc$x0Kt(*57htY0oIwz$Hl zQwFLZr%n(PEtA-qA-A^a;oJ7%8BNxrB86t0QA-x`!cf?pu!Q|a>@dEbqm30Oo8d1tpK!QVFAvT7Pqs5>LR45g)FZN9=T&`tJw;nkrXwsQ z=^>NBKK5Q|^Wp4Eo08o0fLB)h?oky6sW$9_+6$*kA%E$gTS#gpC*>3)cf77_Ukh6G zdq8`-n_Qg`Y|oR)3i`EN{wTJ(x*gsj`ADQ-*Ei_jpw0PzEGw_qS4L1QVHp7_Y-h(y zjbh`2%bvCmUUt7Kozfot!Pdyw#5(R;pY%=5Dm$*^6YXP z^Ll@oqlX>eLeMm3Z6s$_d@gwp!_%0h9A9-$ffW`7%`>F(M|BlRYn*nwk;&mHsPgmPhDA{ISGap%oe~|0ZTcHWb+!hWQ)ZagZY~ujV}>Z}|nSF9x(e z>1%veGeFylJN$h^{rUn~Dd`JkG*X8nIy@sP`Z1+X$rh`1d>X)G942!*fM-15834Cm0%{>nSNPtSkpxrsmg5zd55rrb+VI?D*N zjXuWtBIYYC#`Wf9JkuOLCCCqn0&OK454quwtsK*mh>E_%^3gHey*&AB?}oX_x}kB` zjQYP#ZMVlq3)Y_rr3<>Ly%h-*aa605Rs2q3Th5=xILQRkkjrg+YO=$xWjiT#e;cU? zl->Q(@hvy+hsy5Cx3LSOb=#i@E6P&r$5$#!B=rxSWM7->NUGaiXzBQtrdV4Y=o4ZX zJFoQlw2wv&vqQG*80qw;cysR?TC23bIsyHeY)hkWs8-7i!I9J7qFKw{jN>A^HOKSY z&I6jc>9ffig9!3S#pmb+f4a!>3K6ahnu?p7H8#RpC+`DBbsgiGv_xZzyHj(zIW+wi z8PCyJ{PV49LD;o>OFn8bM-~&*6CXBZC(%P^6kGx>5-LMaGCJF^0yahRB_^cQ*frm| zojddVioSK7CE6wDZ5hZ0#@2<(;WH#5EnKIS6G`Z(r~^5w>Q3(St#p^(FY!Wh^Fz*y zk+qm^ZFTQ!&S%Y`M`n$fAX2~Oc**#d|`w9>}X{K-w6w7+DbsSm%!!%Iy= zP3bp8Wi^pj@(YD}BlxNOZxyVHbj5v%Q)RyAG=A9M9`0Eatl6T;a~4L8o2r=iCFh)4 z=ex$o-J3{<7BkK8f1W)V8vJ+Cq*B*Cq~CqZ;n%dAu<9v<<&;cEm1o zX5CgQa{U}(-l_k_p*J0d+V5{=p0YUqm`gv@bXce}T2!2Vh*LGI;5#;YSJd)*}J`oUYu|rTX*TafU$j`kHF-#Hcax`JWGoN1462@tfuGcmp8Tl+x zeP%{ou<-Ru$C>`mJ%eF!4?6{q+ISS=8ITkC%r~U(g=O2lKNt15IVc-eScg?o9(QV{ zHRw9lbkny=>)pMl42id5rCrFk*6dDOPR0rgYq^r!U@zzwdxo3G`Px`k9F_z1zodsY z|85Rzu*>r(L2*>UQ? z19zX18~FX?Qa|QRRvB{CW#Z^XYERwWL?7s}<0AgQWPP^i8Tlc^bPp>V(86bXQtY^=|k8Fkw!Ecx(}+SHl3$}^Bt-P?8y zQV*YBN;qF15q4d!>po+(>Zyjeujh3qdZrC$7e6H4 zNGw;yjvoq4ZrSQb@evJV7kh+rvuKnOhIuwOdHZ^07)&NLB>vF>HMJwspFxR>6Xr+V>Mm(BYgb&Ez{pcSYh`Ohm2;*`;VH)F_3;4(msHz66q_ zOYSn)Mq?i>JWLT)+(Lf~_O zPlbog<#6+RcJUp)+c7c1;;h?VF)s&bsj40ktB~~#u*!orhbq7qT8x}FznA&D36bqX zZ@wQxj_jODwiG7!fBE;x1)Qs|@CJAJ;x0m*!*yx%dWXPOrdjR`?S*#zRxF@r)S2ZoK+qMeD~9Uy6Nj(S4;Igt_k3qe~E0tLxQ$e&Hd%Q?VT8S?fF@_Y;o? zN_JaB*xoXK>n#OQ8`Kl$u2|ylrudP3Ez{t+OWd-t0E{KmpOc#Mdond8(U|L_7YZNH zT+fuc|V64(o{XafE}ApQVUR0I%UI~+#g_*_9XJ~(BEj~)W{Qv)dh9|#B$fP>9~ zj^INA4+NBk!70_k`4G@lNQM#va6>>)SPJYC2?8AeIOrjI7R(WWQx-%42&gRz z2QgxeV3Vj6C@j7XvWiiKNO*#4Vp5cT2{s7GB>@Ln;N{?B>USDm0gg*bflX3|U^QHd zs@MZek%EJL(zPI$G@SB6#ti}%QG+wGHV`mQ1`fWHLxB>qa8MSJ4L+8GQ~p6v^)evf zlmrw%1bmBtgZ=0Z&=n;Gwqq`VkI_;T3v49>K>!fy073&mXaWc=fPeuIIskzRo(2$S zpz>${VGJNl0C_xsFauCX6h#aFm4U1dp!Q6rE@=QL!hWO)pyc->EdWK_k8}Vuaz8o^ zp!e>Idg2U#M(;&XYGyQMA2R{a*!{>1K;!l!2LOG*;oyA9$rnH$@HjgAlc{Szxu6I~ z4?k}JeL&}Q#nZ{b9l#uLx&fF2PAc}36F_(Xm;+7^cLxCTlNE~a1TY7zo+NJv_sb5B zz5wQc{U_r6Qv&8d0@>5S+u6e&z#J6pdD+hg!2A>eMR)_)1AZR>d%*AG90*_!6!>_O zE;<0%0~Nji_MmcKCvOKT{!<4O;TzxqU=I}d0oVg#e*n8j38mT?`jK-lc7y=pvqE);gC@@Z%1|F)lDBW5w}T@Tg~OwfP;qfh11;$P0aoYmzW@LL diff --git a/magnolia/python/analysis/out_of_set_sdr_delta_versus_noise_type.pdf b/magnolia/python/analysis/out_of_set_sdr_delta_versus_noise_type.pdf index 3f8d3797ae9bf4912cb840d184143341e5f5d2a2..af88c91dd4885a06d91e83db1d91c928e08a8ec1 100644 GIT binary patch delta 6715 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 UK_qxWkXSW53L+<`f5ZUte^CZHDgXcg diff --git a/magnolia/python/inference/denoising/RatioMaskCluster/separate_sample_from_mix.py b/magnolia/python/inference/denoising/RatioMaskCluster/separate_sample_from_mix.py index 10b7b58..6b09563 100644 --- a/magnolia/python/inference/denoising/RatioMaskCluster/separate_sample_from_mix.py +++ b/magnolia/python/inference/denoising/RatioMaskCluster/separate_sample_from_mix.py @@ -59,7 +59,7 @@ def main(): model_settings = '' mixes = ['/local_data/magnolia/pipeline_data/date_2017_09_27_time_13_25/settings/mixing_LibriSpeech_UrbanSound8K_test_in_sample.json'] from_disk = True - mix_number = 1010 + mix_number = 1 output_path = '/local_data/magnolia/experiment_data/date_2017_09_28_time_13_14/aux/sample_wav_files/mask_cluster' diff --git a/magnolia/python/models/dnndenoise/cluster_mask.py b/magnolia/python/models/dnndenoise/cluster_mask.py index 8bc0f3e..d1dab8c 100644 --- a/magnolia/python/models/dnndenoise/cluster_mask.py +++ b/magnolia/python/models/dnndenoise/cluster_mask.py @@ -478,34 +478,27 @@ def train_on_batch(self, X_train, X_train_clean, y_train, y_train_clean, I_train return cost - def get_masks(self, X_in, nsources=2, nclustering_iterations_max=500, iterations_stop=10): + #def get_masks(self, X_in, nsources=2, nclustering_iterations_max=500, iterations_stop=10): + def get_masks(self, X_in, cluster_centers, nclustering_iterations_max=500, iterations_stop=10): """ Compute the masks for the input spectrograms """ nspectrograms = len(X_in) - #I = np.arange(nspectrograms * nsources, dtype=np.int32).reshape(nspectrograms, nsources) - I = np.tile(np.arange(nsources, dtype=np.int32), reps=(nspectrograms, 1)) - - opt = tf.train.AdamOptimizer() - clustering_minimize = opt.minimize(self.clustering_cost, var_list=[self.speaker_vectors]) - - previous_cost = np.finfo('float').max - iterations_count = 0 - for i in range(nclustering_iterations_max): - cost, _ = self.sess.run([self.clustering_cost, - clustering_minimize], - {self.X: X_in, - self.I: I}) - if cost < previous_cost: - previous_cost = cost - else: - iterations_count += 1 - - if iterations_count >= iterations_stop: - break - + + num_sources = len(cluster_centers) + cluster_centers_init = np.zeros((self.num_training_sources, self.embedding_size)) + cluster_centers_init[:num_sources] = cluster_centers + + #I = np.arange(nspectrograms * num_sources, dtype=np.int32).reshape(nspectrograms, num_sources) + I = np.tile(np.arange(num_sources, dtype=np.int32), reps=(nspectrograms, 1)) + + with self.graph.as_default(): + assign_cc = self.speaker_vectors.assign(cluster_centers_init) + + self.sess.run(assign_cc, {self.X: X_in, self.I: I}) masks = self.sess.run(self.network, {self.X: X_in, self.I: I})[1] + return masks def get_vectors(self, X_in, nsources=2): diff --git a/magnolia/python/training/denoising/RatioMaskCluster/training.py b/magnolia/python/training/denoising/RatioMaskCluster/training.py index 95898c1..af2d0f9 100644 --- a/magnolia/python/training/denoising/RatioMaskCluster/training.py +++ b/magnolia/python/training/denoising/RatioMaskCluster/training.py @@ -53,16 +53,16 @@ def main(): 'layer_size': 500, 'embedding_size': 10, 'auxiliary_size': 0, - 'alpha': 0.1, # try 0.9 + 'alpha': 0.9, # try 0.9 'nonlinearity': 'tf.tanh', - 'fuzzifier': 2, + 'fuzzifier': 1.2, 'num_reco_sources': 2, 'normalize': False, 'collapse_sources': False, } model_location = '/gpu:0' uid_settings = '/local_data/magnolia/pipeline_data/date_2017_09_27_time_13_25/settings/assign_uids_LibriSpeech_UrbanSound8K.json' - model_save_base = '/local_data/magnolia/experiment_data/date_2017_09_28_time_13_14/aux/model_saves/mask_cluster' + model_save_base = '/local_data/magnolia/experiment_data/date_2017_09_28_time_13_14/aux/model_saves/mask_cluster_tight_alpha0.99' training_mixer = MixIterator(mixes_settings_filenames=train_mixes, batch_size=train_batchsize, diff --git a/magnolia/python/utils/clustering_utils.py b/magnolia/python/utils/clustering_utils.py index 40e0611..dd8fe3a 100644 --- a/magnolia/python/utils/clustering_utils.py +++ b/magnolia/python/utils/clustering_utils.py @@ -127,7 +127,7 @@ def get_cluster_masks(vectors, num_sources, binary_mask=True, algo=None): else: # Do clustering - algo.fit(vectors[0].reshape((shape[1]*shape[2],shape[3]))) + algo.fit(vectors[0].reshape((shape[1]*shape[2], shape[3]))) if binary_mask: # Use cluster IDs to construct masks @@ -402,37 +402,32 @@ def mask_cluster_clustering_separate(spec, model, num_sources, def mask_cluster_mask(spec, model, num_sources, **kwd_args): model_spec = preprocess_chimera_batch(spec)[1] - # TODO: COMMENT BACK IN!!! - soft_masks = model.get_masks(model_spec, num_sources, **kwd_args) + + # # TODO: COMMENT BACK IN!!! + # soft_masks = model.get_masks(model_spec, num_sources, **kwd_args) - masked_specs = soft_masks*np.expand_dims(spec.transpose(0, 2, 1), axis=-1) + # masked_specs = soft_masks*np.expand_dims(spec.transpose(0, 2, 1), axis=-1) - return masked_specs.transpose(0, 2, 1, 3) + # return masked_specs.transpose(0, 2, 1, 3) - # # TODO: REMOVE - # # Get the T-F embedding vectors for this signal from the model - # vectors = model.get_vectors(model_spec) + # TODO: REMOVE + # Get the T-F embedding vectors for this signal from the model + vectors = model.get_vectors(model_spec) - # # Clustering algo - # clusterer = KMeans(n_clusters=num_sources, random_state=0) - - # # Get the shape of the input - # shape = np.shape(vectors) - - # vectorsr = vectors[0].reshape((shape[1]*shape[2], shape[3])) - - # # Do clustering - # clusterer.fit(vectorsr) + # Clustering algo + clusterer = KMeans(n_clusters=num_sources, random_state=0) - # cluster_centers = clusterer.cluster_centers_ + # Get the shape of the input + shape = np.shape(vectors) - # squared_diffs_pow = np.power(np.sum(np.square(np.expand_dims(vectorsr, 1) - np.expand_dims(cluster_centers, 0)), -1), - # 1. / (model.fuzzifier - 1.)) + vectorsr = vectors[0].reshape((shape[1]*shape[2], shape[3])) - # W_denom = np.expand_dims(np.sum(np.reciprocal(squared_diffs_pow), -1), -1) + # Do clustering + clusterer.fit(vectorsr) - # W = np.reciprocal(squared_diffs_pow * W_denom) + cluster_centers = clusterer.cluster_centers_ - # clustering_factors = np.reshape(W, (shape[2], shape[1], num_sources)).transpose((1, 0, 2)) + soft_masks = model.get_masks(model_spec, cluster_centers, **kwd_args) + masked_specs = soft_masks*np.expand_dims(spec.transpose(0, 2, 1), axis=-1) - # return np.expand_dims(model_spec, -1) * np.expand_dims(clustering_factors, 0) + return masked_specs.transpose(0, 2, 1, 3) From 1bb8b7688088056cddc7ff17d2afad5393bb5e04 Mon Sep 17 00:00:00 2001 From: jhetherly Date: Mon, 5 Mar 2018 21:03:31 +0000 Subject: [PATCH 16/16] minor plot changes --- magnolia/python/analysis/comparison_plot.py | 23 +++++++++--------- .../out_of_set_sdr_delta_versus_input_snr.pdf | Bin 17423 -> 18167 bytes ...out_of_set_sdr_delta_versus_noise_type.pdf | Bin 19026 -> 19765 bytes 3 files changed, 12 insertions(+), 11 deletions(-) diff --git a/magnolia/python/analysis/comparison_plot.py b/magnolia/python/analysis/comparison_plot.py index 044d9c0..1d43c32 100644 --- a/magnolia/python/analysis/comparison_plot.py +++ b/magnolia/python/analysis/comparison_plot.py @@ -20,12 +20,12 @@ def load_dataframes(models): for model in models: if 'in_set' in model: model['in_set_df'] = pd.read_csv(model['in_set']) - if model['name'] == 'DAE': - format_dae_columns(model['in_set_df']) + #if model['name'] == 'DAE': + # format_dae_columns(model['in_set_df']) if 'out_of_set' in model: model['out_of_set_df'] = pd.read_csv(model['out_of_set']) - if model['name'] == 'DAE': - format_dae_columns(model['out_of_set_df']) + #if model['name'] == 'DAE': + # format_dae_columns(model['out_of_set_df']) def error_on_the_mean(x): @@ -115,7 +115,7 @@ def make_sdr_delta_versus_noise_source_plot(models, df_base_name): ax.xaxis.label.set_size(15) ax.yaxis.label.set_size(15) - ylim = [-0.5, 1.2*ax.get_ylim()[1]] + ylim = [-0.5, 1.3*ax.get_ylim()[1]] #ylim[0] = -0.5 ax.set_ylim(ylim) #plt.axis([0, 11, -.5, 16]) @@ -197,7 +197,7 @@ def make_sdr_delta_versus_input_snr_plot(models, df_base_name, bins): ax.xaxis.label.set_size(15) ax.yaxis.label.set_size(15) - ylim = [-0.5, 1.2*ax.get_ylim()[1]] + ylim = [-0.5, 1.3*ax.get_ylim()[1]] #ylim[0] = -0.5 ax.set_ylim(ylim) #plt.axis([0, 11, -.5, 16]) @@ -216,11 +216,12 @@ def main(): 'out_of_set': '/local_data/magnolia/experiment_data/date_2017_09_28_time_13_14/aux/evaluations/bss/snmf/out_of_sample_test_sdr_summary.csv', 'color': '#98C1D9' }, - #{ - # 'name': 'DAE', - # 'out_of_set': '/data/fs4/home/pgamble/Magnolia/Denoising/Autoencoder/Final Results/eval_test_A.csv', - # 'color': '#E0FBFC' - #}, + { + 'name': 'DAE', + 'in_set': '/local_data/magnolia/experiment_data/date_2017_09_28_time_13_14/aux/evaluations/bss/dae/ae_in_sample_test.csv', + 'out_of_set': '/local_data/magnolia/experiment_data/date_2017_09_28_time_13_14/aux/evaluations/bss/dae/ae_out_of_sample_test.csv', + 'color': '#828C51'#'#E0FBFC' + }, { 'name': 'DC + MI (MI)', 'in_set': '/local_data/magnolia/experiment_data/date_2017_09_28_time_13_14/aux/evaluations/bss/chimera/mi_in_sample_test_sdr_summary.csv', diff --git a/magnolia/python/analysis/out_of_set_sdr_delta_versus_input_snr.pdf b/magnolia/python/analysis/out_of_set_sdr_delta_versus_input_snr.pdf index afd64fd317dd6ece04dedb21f5f8f523487a9d2b..55b43abdcddded3e9e900df34f93d9b12f382983 100644 GIT binary patch delta 6792 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 PM-wodGBSsc>2m%L)ME#S diff --git a/magnolia/python/analysis/out_of_set_sdr_delta_versus_noise_type.pdf b/magnolia/python/analysis/out_of_set_sdr_delta_versus_noise_type.pdf index af88c91dd4885a06d91e83db1d91c928e08a8ec1..ef3f556f556bfa37d0f5c28297aedc578412848d 100644 GIT binary patch delta 7335 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