MLP.py 4.51 KB
Newer Older
1 2 3 4 5 6 7 8 9
#!/usr/bin/env python
# vim: set fileencoding=utf-8 :

import numpy

from bob.pad.base.algorithm import Algorithm
import bob.learn.mlp
import bob.io.base

Amir MOHAMMADI's avatar
Amir MOHAMMADI committed
10 11
import logging
logger = logging.getLogger(__name__)
12 13 14 15 16 17 18


class MLP(Algorithm):
    """Interfaces an MLP classifier used for PAD

    Attributes
    ----------
19
    hidden_units : :py:obj:`tuple` of :any:`int`
20
      The number of hidden units in each hidden layer
21
    max_iter : :any:`int`
22
      The maximum number of training iterations
23 24 25 26
    precision : :any:`float`
      criterion to stop the training: if the difference
      between current and last loss is smaller than
      this number, then stop training.
27 28 29 30 31 32 33 34 35 36 37 38 39 40 41
    """

    def __init__(self, hidden_units=(10, 10), max_iter=1000, precision=0.001, **kwargs):
        """Init function

        Parameters
        ----------
        hidden_units : :py:obj:`tuple` of int
          The number of hidden units in each hidden layer
        max_iter : int
          The maximum number of training iterations
        precision : float
          criterion to stop the training: if the difference
          between current and last loss is smaller than
          this number, then stop training.
Amir MOHAMMADI's avatar
Amir MOHAMMADI committed
42

43
        """
Amir MOHAMMADI's avatar
Amir MOHAMMADI committed
44
        Algorithm.__init__(self,
45
                           performs_projection=True,
Amir MOHAMMADI's avatar
Amir MOHAMMADI committed
46
                           requires_projector_training=True,
47 48 49 50 51 52 53 54 55 56
                           **kwargs)

        self.hidden_units = hidden_units
        self.max_iter = max_iter
        self.precision = precision
        self.mlp = None

    def train_projector(self, training_features, projector_file):
        """Trains the MLP

57 58
        Parameters
        ----------
Amir MOHAMMADI's avatar
Amir MOHAMMADI committed
59
        training_features : :any:`list` of :py:class:`numpy.ndarray`
60
          Data used to train the MLP. The real attempts are in training_features[0] and the attacks are in training_features[1]
61 62
        projector_file : str
          Filename where to save the trained model.
Amir MOHAMMADI's avatar
Amir MOHAMMADI committed
63

64 65 66
        """
        # training is done in batch (i.e. using all training data)
        batch_size = len(training_features[0]) + len(training_features[1])
Amir MOHAMMADI's avatar
Amir MOHAMMADI committed
67

68 69 70 71 72
        # The labels
        label_real = numpy.zeros((len(training_features[0]), 2), dtype='float64')
        label_real[:, 0] = 1
        label_attack = numpy.zeros((len(training_features[1]), 2), dtype='float64')
        label_attack[:, 1] = 0
Amir MOHAMMADI's avatar
Amir MOHAMMADI committed
73

74 75 76 77
        real = numpy.array(training_features[0])
        attack = numpy.array(training_features[1])
        X = numpy.vstack([real, attack])
        Y = numpy.vstack([label_real, label_attack])
Amir MOHAMMADI's avatar
Amir MOHAMMADI committed
78

79 80 81 82 83 84 85 86 87
        # Building MLP architecture
        input_dim = real.shape[1]
        shape = []
        shape.append(input_dim)
        for i in range(len(self.hidden_units)):
            shape.append(self.hidden_units[i])
        # last layer contains two units: one for each class (i.e. real and attack)
        shape.append(2)
        shape = tuple(shape)
Amir MOHAMMADI's avatar
Amir MOHAMMADI committed
88

89 90 91 92
        self.mlp = bob.learn.mlp.Machine(shape)
        self.mlp.output_activation = bob.learn.activation.Logistic()
        self.mlp.randomize()
        trainer = bob.learn.mlp.BackProp(batch_size, bob.learn.mlp.CrossEntropyLoss(self.mlp.output_activation), self.mlp, train_biases=True)
Amir MOHAMMADI's avatar
Amir MOHAMMADI committed
93

94 95 96
        n_iter = 0
        previous_cost = 0
        current_cost = 1
Amir MOHAMMADI's avatar
Amir MOHAMMADI committed
97
        while (n_iter < self.max_iter) and (abs(previous_cost - current_cost) > self.precision):
98 99 100 101
            previous_cost = current_cost
            trainer.train(self.mlp, X, Y)
            current_cost = trainer.cost(self.mlp, X, Y)
            n_iter += 1
102
            logger.debug("Iteration {} -> cost = {} (previous = {}, max_iter = {})".format(n_iter, trainer.cost(self.mlp, X, Y), previous_cost, self.max_iter))
103 104 105 106 107 108

        f = bob.io.base.HDF5File(projector_file, 'w')
        self.mlp.save(f)

    def project(self, feature):
        """Project the given feature
Amir MOHAMMADI's avatar
Amir MOHAMMADI committed
109

110 111
        Parameters
        ----------
Amir MOHAMMADI's avatar
Amir MOHAMMADI committed
112
        feature : :py:class:`numpy.ndarray`
113
          The feature to classify
114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129

        Returns
        -------
        numpy.ndarray
          The value of the two units in the last layer of the MLP.
        """
       # if isinstance(feature, FrameContainer):
       #     feature = convert_frame_cont_to_array(feature)
        return self.mlp(feature)

    def score(self, toscore):
        """Returns the probability of the real class.

        Parameters
        ----------
        toscore : :py:class:`numpy.ndarray`
Amir MOHAMMADI's avatar
Amir MOHAMMADI committed
130

131 132 133
        Returns
        -------
        float
Amir MOHAMMADI's avatar
Amir MOHAMMADI committed
134
         probability of the authentication attempt to be real.
135 136 137 138 139
        """
        if toscore.ndim == 1:
            return [toscore[0]]
        else:
            return numpy.mean([toscore[:, 0]])