Skip to content
Snippets Groups Projects
Commit 16693a2d authored by Olegs NIKISINS's avatar Olegs NIKISINS
Browse files

Added one-class SVM option in VideoSvmPadAlgorith, created a couple config files for tests

parent e88da213
No related branches found
No related tags found
1 merge request!12Added anomaly detection algos and unseen attack protocols for aggregated database
...@@ -453,7 +453,7 @@ class VideoSvmPadAlgorithm(Algorithm): ...@@ -453,7 +453,7 @@ class VideoSvmPadAlgorithm(Algorithm):
#========================================================================== #==========================================================================
def norm_train_cv_data(self, real_train, real_cv, attack_train, attack_cv): def norm_train_cv_data(self, real_train, real_cv, attack_train, attack_cv, one_class_flag = False):
""" """
Mean-std normalization of train and cross-validation data arrays. Mean-std normalization of train and cross-validation data arrays.
...@@ -471,6 +471,11 @@ class VideoSvmPadAlgorithm(Algorithm): ...@@ -471,6 +471,11 @@ class VideoSvmPadAlgorithm(Algorithm):
``attack_cv`` : 2D :py:class:`numpy.ndarray` ``attack_cv`` : 2D :py:class:`numpy.ndarray`
Subset of cross-validation features for the attack class. Subset of cross-validation features for the attack class.
``one_class_flag`` : :py:class:`bool`
If set to ``True``, only positive/real samples will be used to
compute the mean and std normalization vectors. Set to ``True`` if
using one-class SVM. Default: False.
**Returns:** **Returns:**
``real_train_norm`` : 2D :py:class:`numpy.ndarray` ``real_train_norm`` : 2D :py:class:`numpy.ndarray`
...@@ -485,18 +490,30 @@ class VideoSvmPadAlgorithm(Algorithm): ...@@ -485,18 +490,30 @@ class VideoSvmPadAlgorithm(Algorithm):
``attack_cv_norm`` : 2D :py:class:`numpy.ndarray` ``attack_cv_norm`` : 2D :py:class:`numpy.ndarray`
Normalized subset of cross-validation features for the attack class. Normalized subset of cross-validation features for the attack class.
""" """
if not(one_class_flag):
features_train = np.vstack([real_train, attack_train])
features_train_norm, features_mean, features_std = self.mean_std_normalize(features_train)
real_train_norm = features_train_norm[0:real_train.shape[0], :]
features_train = np.vstack([real_train, attack_train]) attack_train_norm = features_train_norm[real_train.shape[0]:, :]
features_train_norm, features_mean, features_std = self.mean_std_normalize(features_train) real_cv_norm, _, _ = self.mean_std_normalize(real_cv, features_mean, features_std)
real_train_norm = features_train_norm[0:real_train.shape[0], :] attack_cv_norm, _, _ = self.mean_std_normalize(attack_cv, features_mean, features_std)
attack_train_norm = features_train_norm[real_train.shape[0]:, :] else: # one-class SVM case
real_cv_norm, _, _ = self.mean_std_normalize(real_cv, features_mean, features_std) #only real class used for training in one class SVM:
real_train_norm, features_mean, features_std = self.mean_std_normalize(real_train)
attack_cv_norm, _, _ = self.mean_std_normalize(attack_cv, features_mean, features_std) attack_train_norm, _, _ = self.mean_std_normalize(attack_train, features_mean, features_std)
real_cv_norm, _, _ = self.mean_std_normalize(real_cv, features_mean, features_std)
attack_cv_norm, _, _ = self.mean_std_normalize(attack_cv, features_mean, features_std)
return real_train_norm, real_cv_norm, attack_train_norm, attack_cv_norm return real_train_norm, real_cv_norm, attack_train_norm, attack_cv_norm
...@@ -569,12 +586,15 @@ class VideoSvmPadAlgorithm(Algorithm): ...@@ -569,12 +586,15 @@ class VideoSvmPadAlgorithm(Algorithm):
A trained SVM machine. A trained SVM machine.
""" """
one_class_flag = (machine_type == 'ONE_CLASS') # True if one-class SVM is used
# get the data for the hyper-parameter grid-search: # get the data for the hyper-parameter grid-search:
real_train, real_cv, attack_train, attack_cv = self.prepare_data_for_hyper_param_grid_search(training_features, n_samples) real_train, real_cv, attack_train, attack_cv = self.prepare_data_for_hyper_param_grid_search(training_features, n_samples)
if mean_std_norm_flag: if mean_std_norm_flag:
# normalize the data: # normalize the data:
real_train, real_cv, attack_train, attack_cv = self.norm_train_cv_data(real_train, real_cv, attack_train, attack_cv) real_train, real_cv, attack_train, attack_cv = self.norm_train_cv_data(real_train, real_cv, attack_train, attack_cv,
one_class_flag)
precisions_cv = [] # for saving the precision on the cross-validation set precisions_cv = [] # for saving the precision on the cross-validation set
...@@ -593,7 +613,13 @@ class VideoSvmPadAlgorithm(Algorithm): ...@@ -593,7 +613,13 @@ class VideoSvmPadAlgorithm(Algorithm):
setattr(trainer, key, trainer_grid_search_param[key]) # set the params of trainer setattr(trainer, key, trainer_grid_search_param[key]) # set the params of trainer
data = [np.copy(real_train), np.copy(attack_train)] # data used for training the machine in the grid-search if not( one_class_flag ): # two-class SVM case
data = [np.copy(real_train), np.copy(attack_train)] # data used for training the machine in the grid-search
else: # one class SVM case
data = [np.copy(real_train)] # only real class is used for training
machine = trainer.train(data) # train the machine machine = trainer.train(data) # train the machine
...@@ -626,8 +652,10 @@ class VideoSvmPadAlgorithm(Algorithm): ...@@ -626,8 +652,10 @@ class VideoSvmPadAlgorithm(Algorithm):
debug_dict = {} debug_dict = {}
debug_dict['precisions_train'] = precisions_train debug_dict['precisions_train'] = precisions_train
debug_dict['precisions_cv'] = precisions_cv debug_dict['precisions_cv'] = precisions_cv
debug_dict['cost'] = selected_params['cost']
debug_dict['gamma'] = selected_params['gamma'] for key in selected_params.keys():
debug_dict[key] = selected_params[key]
f = bob.io.base.HDF5File(debug_file, 'w') # open hdf5 file to save the debug data f = bob.io.base.HDF5File(debug_file, 'w') # open hdf5 file to save the debug data
for key in debug_dict.keys(): for key in debug_dict.keys():
f.set(key, debug_dict[key]) f.set(key, debug_dict[key])
...@@ -640,10 +668,18 @@ class VideoSvmPadAlgorithm(Algorithm): ...@@ -640,10 +668,18 @@ class VideoSvmPadAlgorithm(Algorithm):
if mean_std_norm_flag: if mean_std_norm_flag:
# Normalize the data: # Normalize the data:
features = np.vstack([real, attack]) if not( one_class_flag ): # two-class SVM case
features_norm, features_mean, features_std = self.mean_std_normalize(features)
real = features_norm[0:real.shape[0], :] # The array is now normalized features = np.vstack([real, attack])
attack = features_norm[real.shape[0]:, :] # The array is now normalized features_norm, features_mean, features_std = self.mean_std_normalize(features)
real = features_norm[0:real.shape[0], :] # The array is now normalized
attack = features_norm[real.shape[0]:, :] # The array is now normalized
else: # one-class SVM case
real, features_mean, features_std = self.mean_std_normalize(real) # use only real class to compute normalizers
attack = self.mean_std_normalize(attack, features_mean, features_std)
# ``real`` and ``attack`` arrays are now normalizaed
if reduced_train_data_flag: if reduced_train_data_flag:
...@@ -651,7 +687,13 @@ class VideoSvmPadAlgorithm(Algorithm): ...@@ -651,7 +687,13 @@ class VideoSvmPadAlgorithm(Algorithm):
real = self.select_quasi_uniform_data_subset(real, n_train_samples) real = self.select_quasi_uniform_data_subset(real, n_train_samples)
attack = self.select_quasi_uniform_data_subset(attack, n_train_samples) attack = self.select_quasi_uniform_data_subset(attack, n_train_samples)
data = [np.copy(real), np.copy(attack)] # data for final training if not( one_class_flag ): # two-class SVM case
data = [np.copy(real), np.copy(attack)] # data for final training
else: # one-class SVM case
data = [np.copy(real)] # only real class used for training
machine = trainer.train(data) # train the machine machine = trainer.train(data) # train the machine
...@@ -743,17 +785,26 @@ class VideoSvmPadAlgorithm(Algorithm): ...@@ -743,17 +785,26 @@ class VideoSvmPadAlgorithm(Algorithm):
**Returns:** **Returns:**
``probabilities`` : 2D :py:class:`numpy.ndarray` ``probabilities`` : 1D or 2D :py:class:`numpy.ndarray`
2D in the case of two-class SVM.
An array containing class probabilities for each frame. An array containing class probabilities for each frame.
First column contains probabilities for each frame being a real class. First column contains probabilities for each frame being a real class.
Second column contains probabilities for each frame being an attack class. Second column contains probabilities for each frame being an attack class.
1D in the case of one-class SVM.
Vector with scores for each frame defining belonging to the real class.
Must be writable with the ``write_feature`` function and Must be writable with the ``write_feature`` function and
readable with the ``read_feature`` function. readable with the ``read_feature`` function.
""" """
features_array = self.convert_frame_cont_to_array(feature) features_array = self.convert_frame_cont_to_array(feature)
probabilities = self.machine.predict_class_and_probabilities(features_array)[1] if not( self.machine_type == 'ONE_CLASS' ): # two-class SVM case
probabilities = self.machine.predict_class_and_probabilities(features_array)[1]
else:
probabilities = self.machine.predict_class_and_scores(features_array)[1]
return probabilities return probabilities
...@@ -765,22 +816,28 @@ class VideoSvmPadAlgorithm(Algorithm): ...@@ -765,22 +816,28 @@ class VideoSvmPadAlgorithm(Algorithm):
**Parameters:** **Parameters:**
``toscore`` : 2D :py:class:`numpy.ndarray` ``toscore`` : 1D or 2D :py:class:`numpy.ndarray`
2D in the case of two-class SVM.
An array containing class probabilities for each frame. An array containing class probabilities for each frame.
First column contains probabilities for each frame being a real class. First column contains probabilities for each frame being a real class.
Second column contains probabilities for each frame being an attack class. Second column contains probabilities for each frame being an attack class.
1D in the case of one-class SVM.
Vector with scores for each frame defining belonging to the real class.
**Returns:** **Returns:**
``score`` : :py:class:`float` ``score`` : :py:class:`float` or a 1D :py:class:`numpy.ndarray`
or a list of scores containing individual score for each frame. If ``frame_level_scores_flag = False`` a single score is returned.
A score value for the object ``toscore``. One score per video.
A probability of a sample being a real class. Score is a probability of a sample being a real class.
If ``frame_level_scores_flag = True`` a 1D array of scores is returned.
One score per frame.
Score is a probability of a sample being a real class.
""" """
if self.frame_level_scores_flag: if self.frame_level_scores_flag:
score = toscore[:,0] # here score is a list containing scores for each frame score = toscore[:,0] # here score is a 1D array containing scores for each frame
else: else:
...@@ -796,8 +853,13 @@ class VideoSvmPadAlgorithm(Algorithm): ...@@ -796,8 +853,13 @@ class VideoSvmPadAlgorithm(Algorithm):
**Parameters:** **Parameters:**
``toscore`` : 2D :py:class:`numpy.ndarray` ``toscore`` : 1D or 2D :py:class:`numpy.ndarray`
An array containing scores computed by score() method of this class. 2D in the case of two-class SVM.
An array containing class probabilities for each frame.
First column contains probabilities for each frame being a real class.
Second column contains probabilities for each frame being an attack class.
1D in the case of one-class SVM.
Vector with scores for each frame defining belonging to the real class.
**Returns:** **Returns:**
...@@ -805,13 +867,15 @@ class VideoSvmPadAlgorithm(Algorithm): ...@@ -805,13 +867,15 @@ class VideoSvmPadAlgorithm(Algorithm):
A list containing the scores. A list containing the scores.
""" """
if self.frame_level_scores_flag: scores = self.score(toscore) # returns float score or 1D array of scores
if isinstance(scores, np.float): # if a single score
list_of_scores = self.score(toscore) list_of_scores = [scores]
else: else:
list_of_scores = [self.score(toscore)] list_of_scores = list(scores)
return list_of_scores return list_of_scores
......
#!/usr/bin/env python2
# -*- coding: utf-8 -*-
"""
This file contains configurations to run Frame Differences and SVM based face PAD baseline.
The settings are tuned for the Replay-attack database.
The idea of the algorithms is inherited from the following paper: [AM11]_.
"""
#=======================================================================================
sub_directory = 'frame_diff_svm'
"""
Sub-directory where results will be placed.
You may change this setting using the ``--sub-directory`` command-line option
or the attribute ``sub_directory`` in a configuration file loaded **after**
this resource.
"""
#=======================================================================================
# define preprocessor:
from ..preprocessor import FrameDifference
NUMBER_OF_FRAMES = None # process all frames
CHECK_FACE_SIZE_FLAG = True # Check size of the face
MIN_FACE_SIZE = 50 # Minimal size of the face to consider
preprocessor = FrameDifference(number_of_frames = NUMBER_OF_FRAMES,
check_face_size_flag = CHECK_FACE_SIZE_FLAG,
min_face_size = MIN_FACE_SIZE)
"""
In the preprocessing stage the frame differences are computed for both facial and non-facial/background
regions. In this case all frames of the input video are considered, which is defined by
``number_of_frames = None``. The frames containing faces of the size below ``min_face_size = 50`` threshold
are discarded. Both RGB and gray-scale videos are acceptable by the preprocessor.
The preprocessing idea is introduced in [AM11]_.
"""
#=======================================================================================
# define extractor:
from ..extractor import FrameDiffFeatures
WINDOW_SIZE=20
OVERLAP=0
extractor = FrameDiffFeatures(window_size=WINDOW_SIZE,
overlap=OVERLAP)
"""
In the feature extraction stage 5 features are extracted for all non-overlapping windows in
the Frame Difference input signals. Five features are computed for each of windows in the
facial face regions, the same is done for non-facial regions. The non-overlapping option
is controlled by ``overlap = 0``. The length of the window is defined by ``window_size``
argument.
The features are introduced in the following paper: [AM11]_.
"""
#=======================================================================================
# define algorithm:
from ..algorithm import VideoSvmPadAlgorithm
MACHINE_TYPE = 'ONE_CLASS'
KERNEL_TYPE = 'RBF'
N_SAMPLES = 10000
TRAINER_GRID_SEARCH_PARAMS = {'nu': [0.001, 0.01, 0.05, 0.1], 'gamma': [2**P for P in range(-15, 0, 2)]}
MEAN_STD_NORM_FLAG = True # enable mean-std normalization
FRAME_LEVEL_SCORES_FLAG = True # one score per frame(!) in this case
algorithm = VideoSvmPadAlgorithm(machine_type = MACHINE_TYPE,
kernel_type = KERNEL_TYPE,
n_samples = N_SAMPLES,
trainer_grid_search_params = TRAINER_GRID_SEARCH_PARAMS,
mean_std_norm_flag = MEAN_STD_NORM_FLAG,
frame_level_scores_flag = FRAME_LEVEL_SCORES_FLAG)
"""
The one-class SVM algorithm with RBF kernel is used to classify the data into *real* and *attack* classes.
One score is produced for each frame of the input video, ``frame_level_scores_flag = True``.
The grid search of SVM parameters is used to select the successful settings.
The grid search is done on the subset of training data.
The size of this subset is defined by ``n_samples`` parameter.
The data is also mean-std normalized, ``mean_std_norm_flag = True``.
"""
#!/usr/bin/env python2
# -*- coding: utf-8 -*-
"""
This file contains configurations to run Image Quality Measures (IQM) and SVM based face PAD baseline.
The settings of the preprocessor and extractor are tuned for the Replay-attack database.
In the SVM algorithm the amount of training data is reduced speeding-up the training for
large data sets, such as Aggregated PAD database.
The IQM features used in this algorithm/resource are introduced in the following papers: [WHJ15]_ and [CBVM16]_.
"""
#=======================================================================================
sub_directory = 'qm_svm_aggregated_db'
"""
Sub-directory where results will be placed.
You may change this setting using the ``--sub-directory`` command-line option
or the attribute ``sub_directory`` in a configuration file loaded **after**
this resource.
"""
#=======================================================================================
# define preprocessor:
from ..preprocessor import VideoFaceCrop
CROPPED_IMAGE_SIZE = (64, 64) # The size of the resulting face
CROPPED_POSITIONS = {'topleft' : (0,0) , 'bottomright' : CROPPED_IMAGE_SIZE}
FIXED_POSITIONS = None
MASK_SIGMA = None # The sigma for random values areas outside image
MASK_NEIGHBORS = 5 # The number of neighbors to consider while extrapolating
MASK_SEED = None # The seed for generating random values during extrapolation
CHECK_FACE_SIZE_FLAG = True # Check the size of the face
MIN_FACE_SIZE = 50
USE_LOCAL_CROPPER_FLAG = True # Use the local face cropping class (identical to Ivana's paper)
RGB_OUTPUT_FLAG = True # Return RGB cropped face using local cropper
preprocessor = VideoFaceCrop(cropped_image_size = CROPPED_IMAGE_SIZE,
cropped_positions = CROPPED_POSITIONS,
fixed_positions = FIXED_POSITIONS,
mask_sigma = MASK_SIGMA,
mask_neighbors = MASK_NEIGHBORS,
mask_seed = None,
check_face_size_flag = CHECK_FACE_SIZE_FLAG,
min_face_size = MIN_FACE_SIZE,
use_local_cropper_flag = USE_LOCAL_CROPPER_FLAG,
rgb_output_flag = RGB_OUTPUT_FLAG)
"""
In the preprocessing stage the face is cropped in each frame of the input video given facial annotations.
The size of the face is normalized to ``cropped_image_size`` dimensions. The faces of the size
below ``min_face_size`` threshold are discarded. The preprocessor is similar to the one introduced in
[CAM12]_, which is defined by ``use_local_cropper_flag = True``. The preprocessed frame is the RGB
facial image, which is defined by ``RGB_OUTPUT_FLAG = True``.
"""
#=======================================================================================
# define extractor:
from ..extractor import VideoQualityMeasure
GALBALLY=True
MSU=True
DTYPE=None
extractor = VideoQualityMeasure(galbally=GALBALLY,
msu=MSU,
dtype=DTYPE)
"""
In the feature extraction stage the Image Quality Measures are extracted from each frame of the preprocessed RGB video.
The features to be computed are introduced in the following papers: [WHJ15]_ and [CBVM16]_.
"""
#=======================================================================================
# define algorithm:
from ..algorithm import VideoSvmPadAlgorithm
MACHINE_TYPE = 'ONE_CLASS'
KERNEL_TYPE = 'RBF'
N_SAMPLES = 50000
TRAINER_GRID_SEARCH_PARAMS = {'nu': [0.001, 0.01, 0.05, 0.1], 'gamma': [0.01, 0.1, 1, 10]}
MEAN_STD_NORM_FLAG = True # enable mean-std normalization
FRAME_LEVEL_SCORES_FLAG = True # one score per frame(!) in this case
SAVE_DEBUG_DATA_FLAG = True # save the data, which might be useful for debugging
REDUCED_TRAIN_DATA_FLAG = False # DO NOT reduce the amount of training data in the final training stage
N_TRAIN_SAMPLES = 50000 # number of training samples per class in the final SVM training stage (NOT considered, because REDUCED_TRAIN_DATA_FLAG = False)
algorithm = VideoSvmPadAlgorithm(machine_type = MACHINE_TYPE,
kernel_type = KERNEL_TYPE,
n_samples = N_SAMPLES,
trainer_grid_search_params = TRAINER_GRID_SEARCH_PARAMS,
mean_std_norm_flag = MEAN_STD_NORM_FLAG,
frame_level_scores_flag = FRAME_LEVEL_SCORES_FLAG,
save_debug_data_flag = SAVE_DEBUG_DATA_FLAG,
reduced_train_data_flag = REDUCED_TRAIN_DATA_FLAG,
n_train_samples = N_TRAIN_SAMPLES)
"""
The one-class SVM algorithm with RBF kernel is used to classify the data into *real* and *attack* classes.
One score is produced for each frame of the input video, ``frame_level_scores_flag = True``.
The grid search of SVM parameters is used to select the successful settings.
The grid search is done on the subset of training data.
The size of this subset is defined by ``n_samples`` parameter.
The final training of the SVM is done on all training data ``reduced_train_data_flag = False``.
The data is also mean-std normalized, ``mean_std_norm_flag = True``.
"""
...@@ -82,8 +82,10 @@ setup( ...@@ -82,8 +82,10 @@ setup(
'lbp-svm-aggregated-db = bob.pad.face.config.lbp_svm_aggregated_db', 'lbp-svm-aggregated-db = bob.pad.face.config.lbp_svm_aggregated_db',
'qm-svm = bob.pad.face.config.qm_svm', 'qm-svm = bob.pad.face.config.qm_svm',
'qm-svm-aggregated-db = bob.pad.face.config.qm_svm_aggregated_db', 'qm-svm-aggregated-db = bob.pad.face.config.qm_svm_aggregated_db',
'qm-one-class-svm-aggregated-db = bob.pad.face.config.qm_one_class_svm_aggregated_db',
'frame-diff-svm = bob.pad.face.config.frame_diff_svm', 'frame-diff-svm = bob.pad.face.config.frame_diff_svm',
'frame-diff-svm-aggregated-db = bob.pad.face.config.frame_diff_svm_aggregated_db', 'frame-diff-svm-aggregated-db = bob.pad.face.config.frame_diff_svm_aggregated_db',
'frame-diff-one-class-svm = bob.pad.face.config.frame_diff_one_class_svm',
], ],
# registered preprocessors: # registered preprocessors:
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment