예제 #1
0
def do_feature_extraction(db, param, log, overwrite=False):
    """Feature extraction stage

    Parameters
    ----------
    db : dcase_util.dataset.Dataset
        Dataset

    param : dcase_util.containers.DCASEAppParameterContainer
        Application parameters

    log : dcase_util.ui.FancyLogger
        Logging interface

    overwrite : bool
        Default value False

    Returns
    -------
    nothing

    """

    # Define processing chain (Multichannel audio reading + feature extraction for each channel)
    feature_processing_chain = get_processing_chain(param, chain_type='feature_processing_chain')
    # Loop over all audio files in the current dataset and extract acoustic features for each of them.
    for audio_filename in db.audio_files:
        # Get filename for feature data from audio filename
        feature_filename = dcase_util.utils.Path(
            path=audio_filename
        ).modify(
            path_base=param.get_path('path.application.feature_extractor'),
            filename_extension='.cpickle'
        )

        if not os.path.isfile(feature_filename) or overwrite:
            log.line(
                data=os.path.split(audio_filename)[1],
                indent=2
            )
            # Extract features and store them into FeatureContainer, and save it to the disk
            feature_processing_chain.process(filename=audio_filename).save(filename=feature_filename)
예제 #2
0
def do_testing(db, folds, param, log, overwrite=False):
    """Testing stage

    Parameters
    ----------
    db : dcase_util.dataset.Dataset
        Dataset

    folds : list of int
        List of active folds

    param : dcase_util.containers.DCASEAppParameterContainer
        Application parameters

    log : dcase_util.ui.FancyLogger
        Logging interface

    overwrite : bool
        Default value False

    Returns
    -------
    nothing

    """

    # Loop over all cross-validation folds and test
    for fold in folds:
        log.line(
            data='Fold [{fold}]'.format(fold=fold),
            indent=2
        )

        # Setup keras, run only once
        dcase_util.keras.setup_keras(
            seed=param.get_path('learner.parameters.random_seed'),
            profile=param.get_path('learner.parameters.keras_profile'),
            backend=param.get_path('learner.parameters.backend')
        )
        import keras

        # Get results filename
        fold_results_filename = os.path.join(
            param.get_path('path.application.recognizer'),
            'res_fold_{fold}.txt'.format(fold=fold)
        )

        # check if results already processed
        if not os.path.isfile(fold_results_filename) or overwrite:
            # -- prepare learner -- #
            # Get model filename
            fold_model_filename = os.path.join(
                param.get_path('path.application.learner'),
                'model_fold_{fold}.h5'.format(fold=fold)
            )

            # load model
            keras_model = keras.models.load_model(fold_model_filename)

            # -- prepare data -- #
            # Get normalization factor filename
            fold_stats_filename = os.path.join(
                param.get_path('path.application.feature_normalizer'),
                'norm_fold_{fold}.cpickle'.format(fold=fold)
            )

            # Create processing chain for features
            data_processing_chain = get_processing_chain(param, fold_stats_filename=fold_stats_filename, chain_type='data_processing_chain')

            # Get label encoder
            label2num_enc = LabelEncoder()
			scene_labels_num = label2num_enc.fit_transform(db.scene_labels())

            # Initialize results container
            res = dcase_util.containers.MetaDataContainer(
                filename=fold_results_filename
            )

            # Loop through all test files from the current cross-validation fold
            for item in db.test(fold=fold):
                # Get feature filename
                feature_filename = dcase_util.utils.Path(
                    path=item.filename
                ).modify(
                    path_base=param.get_path('path.application.feature_extractor'),
                    filename_extension='.cpickle'
                )

                # do processing chain
                features = data_processing_chain.process(filename=feature_filename)

                # Get network output
                posteriors_all = keras_model.predict(x=features.data)

                # combine estimates/posteriors from different channels in a particular sensor node
                if param.get_path('recognizer.fusion.method') == 'mean': # mean rule
                    posteriors = numpy.mean(posteriors_all, axis=0)
                elif param.get_path('recognizer.fusion.method') == 'none': # none (if channels are 'fed directly to/combined before the' classifier)
                    posteriors = posteriors_all
                else:
                    raise ValueError("Fusion not supported")

                estimated_scene_label = label2num_enc.inverse_transform(numpy.argmax(posteriors, axis=0))

                # Store result into results container
                res.append(
                    {
                        'filename': item.filename,
                        'scene_label': estimated_scene_label
                    }
                )

            # Save results container
            res.save()
예제 #3
0
def do_learning(db, folds, param, log, overwrite=False):
    """Learning stage

    Parameters
    ----------
    db : dcase_util.dataset.Dataset
        Dataset

    folds : list of int
        List of active folds

    param : dcase_util.containers.DCASEAppParameterContainer
        Application parameters

    log : dcase_util.ui.FancyLogger
        Logging interface

    overwrite : bool
        Default value False

    Returns
    -------
    nothing

    """

    # Loop over all cross-validation folds and learn acoustic models
    for fold in folds:
        log.line(data='Fold [{fold}]'.format(fold=fold), indent=2)

        # Get model filename
        fold_model_filename = os.path.join(
            param.get_path('path.application.learner'),
            'model_fold_{fold}.h5'.format(fold=fold)
        )

        if not os.path.isfile(fold_model_filename) or overwrite:
            # -- prepare learner -- #
            # Setup keras, run only once
            dcase_util.keras.setup_keras(
                seed=param.get_path('learner.parameters.random_seed'),
                profile=param.get_path('learner.parameters.keras_profile'),
                backend=param.get_path('learner.parameters.backend')
            )
            import keras

            # Create model
            keras_model = dcase_util.keras.create_sequential_model(
                model_parameter_list=param.get_path('learner.parameters.model.config'),
                constants=param.get_path('learner.parameters.model.constants')
            )

            # Show model topology
            log.line(
                dcase_util.keras.model_summary_string(keras_model)
            )

            # Create optimizer object
            param.set_path(
                path='learner.parameters.compile.optimizer',
                new_value=dcase_util.keras.create_optimizer(
                    class_name=param.get_path('learner.parameters.optimizer.class_name'),
                    config=param.get_path('learner.parameters.optimizer.config')
                )
            )

            # Compile model
            keras_model.compile(**param.get_path('learner.parameters.compile'))

            # -- data related -- #
            # Get validation files
            validationsplit_fold_filename = os.path.join(param.get_path('path.application.learner'),'validationsplit_fold_{fold}.pickle'.format(fold=fold))
            if not os.path.isfile(validationsplit_fold_filename):
                training_files, validation_files = db.validation_split(
                    fold=fold,
                    split_type='balanced',
                    validation_amount=param.get_path('learner.parameters.validation_amount'),
                    verbose=True
                )
                with open(validationsplit_fold_filename, 'wb') as f: pickle.dump([training_files,validation_files], f)
            else:
                with open(validationsplit_fold_filename, "rb") as f: [training_files,validation_files] = pickle.load(f)  # load

            # get matching labels
            training_files, training_labels, validation_files, val_labels = get_label_from_filename(db.train(fold=fold), training_files, validation_files, param)

            # Get label encoder
            label2num_enc = LabelEncoder()
            scene_labels_num = label2num_enc.fit_transform(db.scene_labels())

            # convert labels to numeric format
            training_labels_num = label2num_enc.transform(training_labels)
            val_labels_num = label2num_enc.transform(val_labels)

            # get amount of batches for training and validation
            if param.get_path('learner.parameters.fit.undersampling'):
                class_weight = skutils.compute_class_weight('balanced', numpy.unique(training_labels_num),training_labels_num)  # get class weights
                train_batches = (sum(training_labels_num == numpy.argmax(class_weight)) * len(numpy.unique(training_labels_num))) // param.get_path('learner.parameters.fit.batch_size')
            else:
                train_batches = len(training_files) // param.get_path('learner.parameters.fit.batch_size')  # get amount of batches
            val_batches = int(numpy.ceil(len(validation_files)/param.get_path('learner.parameters.fit.batch_size')))  # get amount of batches

            # get normalizer filename
            fold_stats_filename = os.path.join(
                param.get_path('path.application.feature_normalizer'),
                'norm_fold_{fold}.cpickle'.format(fold=fold)
            )

            # Create data processing chain for features
            data_processing_chain = get_processing_chain(param, fold_stats_filename=fold_stats_filename, chain_type = 'data_processing_chain')

            # Init data generators
            from task5_datagenerator import DataGenerator
            TrainDataGenerator = DataGenerator(training_files, training_labels_num,
                                               data_processing_chain=data_processing_chain,
                                               batches=train_batches,
                                               batch_size=param.get_path('learner.parameters.fit.batch_size'),
                                               undersampling=param.get_path('learner.parameters.fit.undersampling'),
                                               shuffle=True)
            ValidationDataGenerator = DataGenerator(validation_files, val_labels_num,
                                               data_processing_chain=data_processing_chain,
                                               batches=val_batches,
                                               batch_size=param.get_path('learner.parameters.fit.batch_size'),
                                               undersampling='None',
                                               shuffle=False)

            # -- train/epoch loop -- #
            prevmodelload = False
            val_scores = []
            epoch_list = []
            for epoch_start in range(-1, param.get_path('learner.parameters.fit.epochs')-1,param.get_path('learner.parameters.fit.processing_interval')):  # for every epoch
                # update epoch information
                epoch_end = epoch_start + param.get_path('learner.parameters.fit.processing_interval')  # specifiy epoch range
                if epoch_end > param.get_path('learner.parameters.fit.epochs'):  # Make sure we have only specified amount of epochs
                    epoch_end = param.get_path('learner.parameters.fit.epochs') - 1

                model_fold_epoch_filename = os.path.join(param.get_path('path.application.learner'),'model_fold_{fold}_epoch_{epoch:d}.h5'.format(fold=fold,epoch=epoch_end))
                val_fold_epoch_filename = os.path.join(param.get_path('path.application.learner'),'val_fold_{fold}_epoch_{epoch:d}.pickle'.format(fold=fold,epoch=epoch_end))
                if not (os.path.isfile(model_fold_epoch_filename) & os.path.isfile(val_fold_epoch_filename)):  # if model does not exist
                    if prevmodelload:  # if epoch already performed before
                        log.line('Loaded model of fold {fold} epoch {epoch_start:d}'.format(fold=fold,epoch_start=epoch_start), indent=2)
                        keras_model = keras.models.load_model(prev_model_fold_epoch_filename)  # get model
                        prevmodelload = False

                    # train model
                    keras_model.fit_generator(
                        generator=TrainDataGenerator,
                        initial_epoch=epoch_start,
                        epochs=epoch_end,
                        steps_per_epoch=train_batches,
                        verbose=0
                    )

                    # evaluate model on validation set for each mic in each example, output is 4 channels * len(validation_files) long
                    posteriors_all = keras_model.predict_generator(
                        generator=ValidationDataGenerator,
                        steps=val_batches
                    )

                    # combine estimates/posteriors from different channels in a particular sensor node
                    posteriors = numpy.empty((len(validation_files),len(db.scene_labels())))
                    for i in range(len(validation_files)): # for each validation file
                        # mean rule
                        if param.get_path('recognizer.fusion.method')=='mean':
                            posteriors[i,:] = numpy.mean(posteriors_all[i*param.get_path('feature_extractor.channels'):(i+1)*param.get_path('feature_extractor.channels'),:],axis=0)
                        # none
                        elif param.get_path('recognizer.fusion.method')=='none':
                            posteriors = posteriors_all
                        else:
                            raise ValueError("Fusion not supported")

                    # get estimated labels
                    val_labels_est_num = numpy.argmax(posteriors, axis=1)

                    # get score
                    F1_score = f1_score(val_labels_num, val_labels_est_num, labels = scene_labels_num, average='macro')
                    log.line('Fold {fold} - Epoch {epoch:d}/{epochs:d} - validation set F1-score: {Fscore:f}'.format(fold=fold,epoch=epoch_end,epochs=param.get_path('learner.parameters.fit.epochs') - 1,Fscore=F1_score),indent=2)

                    # save intermediate results
                    keras_model.save(model_fold_epoch_filename)
                    with open(val_fold_epoch_filename, 'wb') as f: pickle.dump([F1_score], f)
                else: # if already performed
                    # update model loading and load performance prev model
                    prevmodelload = True
                    prev_model_fold_epoch_filename = model_fold_epoch_filename
                    with open(val_fold_epoch_filename, "rb") as f: [F1_score] = pickle.load(f)  # load

                # append scores
                val_scores.append(F1_score)
                epoch_list.append(epoch_end)

            # get best model
            epoch_best = epoch_list[numpy.argmax(val_scores)]
            log.line('Best performing model on epoch {epoch_best:d} with an F1-score of {Fscore:f}%'.format(epoch_best=epoch_best,Fscore=numpy.max(val_scores)*100), indent=2)
            # load best model
            model_fold_epoch_filename = os.path.join(param.get_path('path.application.learner'),'model_fold_{fold}_epoch_{epoch:d}.h5'.format(fold=fold,epoch=epoch_best))
            keras_model = keras.models.load_model(model_fold_epoch_filename)
            # save best model
            keras_model.save(fold_model_filename)