Source code for bob.ip.pytorch_extractor.MLPAlgorithm

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
@author: Olegs Nikisins
"""

# =============================================================================
# Import what is needed here:

from bob.pad.base.algorithm import Algorithm

from bob.bio.video.utils import FrameContainer

import numpy as np

from bob.ip.pytorch_extractor.utils import transform_and_net_forward

from bob.ip.pytorch_extractor.utils import load_pretrained_model

from bob.pad.base.utils import mean_std_normalize

from bob.pad.base.utils import convert_list_of_frame_cont_to_array

import bob.io.base

import torch


# =============================================================================
# Main body :

class MLPAlgorithm(Algorithm):
    """
    This class is an MLP-based PAD algorithm, allowing to pass input feature
    vectors (1D numpy arrays) via a pretrained MLP.

    Attributes
    -----------

    network : object
        An instance of an MLP Network to be used for score computation.
        Note: in current algorith the ``forward()`` method of the Network
        is used for score computation. For example, if you don't want to use
        a sigmoid in the output of an MLP, set the kwargs accodingly.

    model_file : str
        A paths to the model file to be used for ``network`` initialization.

    url : str
        URL to download the pretrained models from.
        If models are not available in the locations specified in the
        ``model_file`` sting, the system will try to download them from
        ``url``. The downloaded models **will be placed to the locations**
        specified in ``model_file`` argument.
        Default: None

    archive_extension : str
        Extension of the archived file to download from above ``url``.
        Default: '.tar.gz'

    frame_level_scores_flag : bool
        Return scores for each frame individually if True. Otherwise, return a
        single score per video. Default: ``True``.

    mean_std_norm_flag : bool
        Perform mean-std normalization of data if set to ``True``.
        Note: make sure an MLP was trained on mean-std normalizaed
        features, if this flag is set to ``True``. The tutorial on MLP
        training in ``bob.learn.pytorch`` provides an example on how to train
        network on normalized features.
        Default: ``True``.
    """

[docs] def __init__(self, network, model_file = None, url = None, archive_extension = '.tar.gz', frame_level_scores_flag = True, mean_std_norm_flag = True): super(MLPAlgorithm, self).__init__(network = network, model_file = model_file, url = url, archive_extension = archive_extension, frame_level_scores_flag = frame_level_scores_flag, mean_std_norm_flag = mean_std_norm_flag, performs_projection=True, requires_projector_training=True) self.transform = lambda x : torch.Tensor(x).unsqueeze(0) self.network = network self.model_file = model_file self.url = url self.archive_extension = archive_extension self.frame_level_scores_flag = frame_level_scores_flag self.mean_std_norm_flag = mean_std_norm_flag self.features_mean = None # this argument will be updated with features mean self.features_std = None # this argument will be updated with features std
# ========================================================================= def _save_mean_std(self, projector_file, features_mean, features_std): """ Saves mean and std normalization to the hdf5 file. The absolute name of the file is specified in ``projector_file`` string. Parameters ---------- projector_file : str Absolute name of the file to save the data to, as returned by ``bob.pad.base`` framework. features_mean : 1D :py:class:`numpy.ndarray` Mean of the features. features_std : 1D :py:class:`numpy.ndarray` Standart deviation of the features. """ f = bob.io.base.HDF5File(projector_file,'w') # open hdf5 file to save f.set("features_mean", features_mean) f.set("features_std", features_std) del f # =========================================================================
[docs] def train_projector(self, training_features, projector_file): """ Compute mean-std normalizers using samples of the real class only. Parameters ---------- training_features : [[object], [object]] A list containing two elements: [0] - a list of Frame Containers with feature vectors for the real class; [1] - a list of Frame Containers with feature vectors for the attack class. projector_file : str The file to save the trained projector to, as returned by the ``bob.pad.base`` framework. """ # training_features[0] - training features for the REAL class. # training_features[1] - training features for the ATTACK class. real = convert_list_of_frame_cont_to_array(training_features[0]) # compute normalization params: _, features_mean, features_std = mean_std_normalize(real) # Save the normalizers: self._save_mean_std(projector_file, features_mean, features_std)
# =========================================================================
[docs] def load_projector(self, projector_file): """ Loads features mean and std from the hdf5 file. The absolute name of the file is specified in ``projector_file`` string. Parameters ---------- projector_file : str Absolute name of the file to load the trained projector from, as returned by ``bob.pad.base`` framework. """ f = bob.io.base.HDF5File(projector_file, 'r') # file to read features_mean = f.read("features_mean") features_std = f.read("features_std") del f self.features_mean = features_mean self.features_std = features_std
# =========================================================================
[docs] def project(self, feature): """ This function computes a vector of scores, one score for each sample in the input array of features. Set ``performs_projection = True`` in the constructor to enable this function. Parameters ---------- feature : FrameContainer or :py:class:`numpy.ndarray` Two types of inputs are accepted. A Frame Container containing the features of an individual frmaes, see ``bob.bio.video.utils.FrameContainer``. Or a ND feature array of the size (n_samples x n_features). Returns ------- scores : 1D :py:class:`numpy.ndarray` Vector of scores. Scores for the bona-fide class are expected to be higher, than the scores of the negative / attack class. """ # try to load the model if not available, do nothing if available: load_pretrained_model(model_path = self.model_file, url = self.url, archive_extension = self.archive_extension) # 1. Convert input array to numpy array if necessary. if isinstance( feature, FrameContainer): # if FrameContainer convert to 3D numpy array feature = feature.as_array() if self.mean_std_norm_flag: feature, _, _ = mean_std_normalize(feature, self.features_mean, self.features_std) scores = transform_and_net_forward(feature = feature, transform = self.transform, network = self.network, model_file = self.model_file, color_input_flag = False) return scores
# =========================================================================
[docs] def score(self, toscore): """ Returns a probability of a sample being a real class. Parameters ---------- toscore : 1D :py:class:`numpy.ndarray` Vector with scores for each frame/sample defining the probability of the frame being a sample of the real class. Returns ------- score : [:py:class:`float`] If ``frame_level_scores_flag = False`` a single score is returned. One score per video. This score is placed into a list, because the ``score`` must be an iterable. Score is a probability of a sample being a real class. If ``frame_level_scores_flag = True`` a list of scores is returned. One score per frame/sample. """ if self.frame_level_scores_flag: score = list(toscore) else: score = [np.mean(toscore)] # compute a single score per video return score
# =========================================================================
[docs] def score_for_multiple_projections(self, toscore): """ Returns a list of scores computed by the score method of this class. Parameters ---------- toscore : 1D :py:class:`numpy.ndarray` Vector with scores for each frame/sample defining the probability of the frame being a sample of the real class. Returns ------- list_of_scores : [:py:class:`float`] A list containing the scores. """ scores = self.score( toscore) # returns float score or 1D array of scores if isinstance(scores, np.float): # if a single score list_of_scores = [scores] else: list_of_scores = list(scores) return list_of_scores