Linear Machines and Trainers¶
Machines are one of the core components of Bob. They represent
statistical models or other functions defined by parameters that can be learnt
or manually set. The simplest of Bob’s machines is a
bob.learn.linear.Machine
. This package contains the definition of
this class as well as trainers that can learn linear machine parameters from
data.
Linear machines¶
Linear machines execute the simple operation \(y = \mathbf{W} x\), where
\(y\) is the output vector, \(x\) is the input vector and \(W\) is
a matrix (2D array) stored in the machine. The input vector \(x\) should be
composed of double-precision floating-point elements. The output will also be
in double-precision. Here is how to use a
bob.learn.linear.Machine
:
>>> W = numpy.array([[0.5, 0.5], [1.0, 1.0]], 'float64')
>>> numpy.allclose(W, [[ 0.5, 0.5], [ 1. , 1. ]])
True
>>> machine = bob.learn.linear.Machine(W)
>>> machine.shape
(2, 2)
>>> x = numpy.array([0.3, 0.4], 'float64')
>>> y = machine(x)
>>> numpy.allclose(y, [ 0.55, 0.55])
True
As was shown in the above example, the way to pass data through a machine is to
call its bob.learn.linear.Machine.forward()
method, for which the
__call__
method is an alias.
The first thing to notice about machines is that they can be stored and
retrieved in bob.io.base.HDF5File
. To save the before
metioned machine to a file, just use the machine’s
bob.learn.linear.Machine.save()
command. Because several machines
can be stored on the same bob.io.base.HDF5File
, we let the user
open the file and set it up before the machine can write to it:
>>> myh5_file = bob.io.base.HDF5File('linear.hdf5', 'w')
>>> #do other operations on myh5_file to set it up, optionally
>>> machine.save(myh5_file)
>>> del myh5_file #close
You can load the machine again in a similar way:
>>> myh5_file = bob.io.base.HDF5File('linear.hdf5')
>>> reloaded = bob.learn.linear.Machine(myh5_file)
>>> numpy.array_equal(machine.weights, reloaded.weights)
True
The shape of a bob.learn.linear.Machine
(see
bob.learn.linear.Machine.shape
) indicates the size of the input
vector that is expected by this machine and the size of the output vector it
produces, in a tuple format like (input_size, output_size)
:
>>> machine.shape
(2, 2)
A bob.learn.linear.Machine
also supports pre-setting
normalization vectors that are applied to every input \(x\). You can set a
subtraction factor and a division factor, so that the actual input \(x'\)
that is fed to the matrix \(W\) is \(x' = (x - s) ./ d\). The variables
\(s\) and \(d\) are vectors that have to have the same size as the
input vector \(x\). The operator \(./\) indicates an element-wise
division. By default, \(s := 0.0\) and \(d := 1.0\).
>>> numpy.allclose(machine.input_subtract, [ 0., 0.])
True
>>> numpy.allclose(machine.input_divide, [ 1., 1.])
True
To set a new value for \(s\) or \(d\) just assign the desired machine property:
>>> machine.input_subtract = numpy.array([0.5, 0.8])
>>> machine.input_divide = numpy.array([2.0, 4.0])
>>> y = machine(x)
>>> y
array([-0.15, -0.15])
Note
In the event you save a machine that has the subtraction and/or a division factor set, the vectors are saved and restored automatically w/o user intervention.
Linear machine trainers¶
Next, we examine available ways to train a bob.learn.linear.Machine
so they can do something useful for you.
Principal component analysis¶
PCA 1 is one way to train a bob.learn.linear.Machine
. The
associated Bob class is bob.learn.linear.PCATrainer
as the
training procedure mainly relies on a singular value decomposition.
PCA belongs to the category of unsupervised learning algorithms, which
means that the training data is not labelled. Therefore, the training set can
be represented by a set of features stored in a container. Using Bob,
this container is a 2D numpy.ndarray
.
>>> data = numpy.array([[3,-3,100], [4,-4,50], [3.5,-3.5,-50], [3.8,-3.7,-100]], dtype='float64')
>>> print(data)
[[ 3. -3. 100. ]
[ 4. -4. 50. ]
[ 3.5 -3.5 -50. ]
[ 3.8 -3.7 -100. ]]
Once the training set has been defined, the overall procedure to train a
bob.learn.linear.Machine
with a
bob.learn.linear.PCATrainer
is simple and shown below. Please note
that the concepts remains very similar for most of the other trainers and
machines.
>>> trainer = bob.learn.linear.PCATrainer() # Creates a PCA trainer
>>> [machine, eig_vals] = trainer.train(data) # Trains the machine with the given data
>>> print(machine.weights) # The weights of the returned (linear) Machine after the training procedure
[[ 0.002 -0.706 -0.708]
[-0.002 0.708 -0.706]
[-1. -0.003 -0. ]]
Next, input data can be projected using this learned projection matrix \(W\).
>>> e = numpy.array([3.2,-3.3,-10], 'float64')
>>> print(machine(e))
[9.999 0.47 0.092]
Linear discriminant analysis¶
LDA 2 is another way to train a bob.learn.linear.Machine
.
The associated Bob class is
bob.learn.linear.FisherLDATrainer
.
In contrast to PCA 1, LDA 2 is a supervised technique.
Furthermore, the training data should be organized differently. It is indeed
required to be a list of 2D numpy.ndarray
’s, one for each class.
>>> data1 = numpy.array([[3,-3,100], [4,-4,50], [40,-40,150]], dtype='float64')
>>> data2 = numpy.array([[3,6,-50], [4,8,-100], [40,79,-800]], dtype='float64')
>>> data = [data1,data2]
Once the training set has been defined, the procedure to train the
bob.learn.linear.Machine
with LDA is very similar to the one
for PCA. This is shown below.
>>> trainer = bob.learn.linear.FisherLDATrainer()
>>> [machine,eig_vals] = trainer.train(data) # Trains the machine with the given data
>>> print(eig_vals)
[ 13.10097786 0. ]
>>> machine.resize(3,1) # Make the output space of dimension 1
>>> print(machine.weights) # The new weights after the training procedure
[[0.609]
[0.785]
[0.111]]
Whitening¶
This is generally used for i-vector preprocessing.
Let’s consider a 2D array of data used to train the withening, and a sample to be whitened:
>>> data = numpy.array([[ 1.2622, -1.6443, 0.1889], [ 0.4286, -0.8922, 1.3020], [-0.6613, 0.0430, 0.6377], [-0.8718, -0.4788, 0.3988], [-0.0098, -0.3121,-0.1807], [ 0.4301, 0.4886, -0.1456]])
>>> sample = numpy.array([1, 2, 3.])
The initialisation of the trainer and the machine:
>>> t = bob.learn.linear.WhiteningTrainer()
Then, the training and projection are done as follows:
>>> m = t.train(data)
>>> withened_sample = m.forward(sample)
Within-Class Covariance Normalisation¶
This can also be used for i-vector preprocessing. Let’s first put the training data into list of numpy arrays.
>>> data = [numpy.array([[ 1.2622, -1.6443, 0.1889], [ 0.4286, -0.8922, 1.3020]]), numpy.array([[-0.6613, 0.0430, 0.6377], [-0.8718, -0.4788, 0.3988]]), numpy.array([[-0.0098, -0.3121,-0.1807], [ 0.4301, 0.4886, -0.1456]])]
The initialisation of the trainer is done as follows:
>>> t = bob.learn.linear.WCCNTrainer()
Then, the training and projection are done as follows:
>>> m = t.train(data)
>>> wccn_sample = m.forward(sample)