Commit c9b5d5dc authored by Davis King's avatar Davis King

Added the vector_normalizer object

--HG--
extra : convert_revision : svn%3Afdd8eb12-d10e-0410-9acb-85c331704f74/trunk%402555
parent 8b487fb7
......@@ -7,6 +7,7 @@
#include <limits>
#include <cmath>
#include "../algs.h"
#include "../matrix.h"
namespace dlib
{
......@@ -153,6 +154,182 @@ namespace dlib
T max_value;
};
// ----------------------------------------------------------------------------------------
template <
typename matrix_type
>
class vector_normalizer
{
public:
typedef typename matrix_type::mem_manager_type mem_manager_type;
typedef typename matrix_type::type scalar_type;
template <typename vector_type>
void train (
const vector_type& samples
)
{
m = mean(vector_to_matrix(samples));
sd = reciprocal(sqrt(variance(vector_to_matrix(samples))));
pca.set_size(0,0);
}
template <typename vector_type>
void train_pca (
const vector_type& samples,
const double eps = 0.99
)
{
train_pca_impl(vector_to_matrix(samples),eps);
}
long in_vector_size (
) const
{
return m.nr();
}
long out_vector_size (
) const
{
if (pca.size() == 0)
return m.nr();
else
return pca.nr();
}
const matrix<scalar_type,0,1,mem_manager_type>& operator() (
const matrix_type& x
) const
{
if (pca.size() == 0)
{
temp_out = pointwise_multiply(x-m, sd);
}
else
{
// If we have a pca transform matrix on hand then
// also apply that.
temp_out = pca*pointwise_multiply(x-m, sd);
}
return temp_out;
}
void swap (
vector_normalizer& item
)
{
m.swap(item.m);
sd.swap(item.sd);
pca.swap(item.pca);
temp_out.swap(item.temp_out);
}
friend void deserialize (
vector_normalizer& item,
std::istream& in
)
{
deserialize(item.m, in);
deserialize(item.sd, in);
deserialize(item.pca, in);
}
friend void serialize (
const vector_normalizer& item,
std::ostream& out
)
{
serialize(item.m, out);
serialize(item.sd, out);
serialize(item.pca, out);
}
private:
template <typename mat_type>
void train_pca_impl (
const mat_type& samples,
const double eps
)
{
m = mean(samples);
sd = reciprocal(sqrt(variance(samples)));
// fill x with the normalized version of the input samples
matrix<typename mat_type::type,0,1,mem_manager_type> x(samples);
for (long r = 0; r < x.size(); ++r)
x(r) = pointwise_multiply(x(r)-m, sd);
matrix<scalar_type,0,0,mem_manager_type> temp, eigen;
matrix<scalar_type,0,1,mem_manager_type> eigenvalues;
// Compute the svd of the covariance matrix of the normalized inputs
svd(covariance(x), temp, eigen, pca);
eigenvalues = diag(eigen);
rsort_columns(pca, eigenvalues);
// figure out how many eigenvectors we want in our pca matrix
const double thresh = sum(eigenvalues)*eps;
long num_vectors = 0;
double total = 0;
for (long r = 0; r < eigenvalues.size() && total < thresh; ++r)
{
++num_vectors;
total += eigenvalues(r);
}
// so now we know we want to use num_vectors of the first eigenvectors.
temp.set_size(num_vectors, eigen.nr());
for (long i = 0; i < num_vectors; ++i)
{
set_rowm(temp,i) = trans(colm(pca,i));
}
temp.swap(pca);
// Apply the pca transform to the data in x. Then we will normalize the
// pca matrix below.
for (long r = 0; r < x.nr(); ++r)
{
x(r) = pca*x(r);
}
// Here we just scale the output features from the pca transform so
// that the variance of each feature is 1. So this doesn't really change
// what the pca is doing, it just makes sure the output features are
// normalized.
pca = trans(scale_columns(trans(pca), reciprocal(sqrt(variance(x)))));
// if the pca transform doesn't reduce the dimensionality
// then just forget about doing pca
if (pca.nr() == pca.nc())
{
pca.set_size(0,0);
}
}
// ------------------- private data members -------------------
matrix<scalar_type,0,1,mem_manager_type> m, sd;
matrix<scalar_type,0,0,mem_manager_type> pca;
// This is just a temporary variable that doesn't contribute to the
// state of this object.
mutable matrix<scalar_type,0,1,mem_manager_type> temp_out;
};
template <
typename matrix_type
>
inline void swap (
vector_normalizer<matrix_type>& a,
vector_normalizer<matrix_type>& b
) { a.swap(b); }
// ----------------------------------------------------------------------------------------
}
......
......@@ -5,10 +5,13 @@
#include <limits>
#include <cmath>
#include "../matrix/matrix_abstract.h"
namespace dlib
{
// ----------------------------------------------------------------------------------------
template <
typename T
>
......@@ -144,6 +147,154 @@ namespace dlib
!*/
};
// ----------------------------------------------------------------------------------------
template <
typename matrix_type
>
class vector_normalizer
{
/*!
REQUIREMENTS ON matrix_type
- must be a dlib::matrix object capable of representing column
vectors
INITIAL VALUE
- in_vector_size() == 0
- out_vector_size() == 0
WHAT THIS OBJECT REPRESENTS
This object represents something that can learn to normalize a set
of vectors. In particular, normalized vectors should have zero
mean and a variance of one.
Also, if desired, this object can also use principal component
analysis for the purposes of reducing the number of elements in a
vector.
!*/
public:
typedef typename matrix_type::mem_manager_type mem_manager_type;
typedef typename matrix_type::type scalar_type;
template <typename vector_type>
void train (
const vector_type& samples
);
/*!
requires
- samples.size() > 0
- samples == a column matrix or something convertible to a column
matrix via vector_to_matrix(). Also, x should contain
matrix_type objects that represent nonempty column vectors.
ensures
- #in_vector_size() == samples(0).nr()
- #out_vector_size() == samples(0).nr()
- This object has learned how to normalize vectors that look like
vectors in the given set of samples.
!*/
template <typename vector_type>
void train_pca (
const vector_type& samples,
const double eps = 0.99
);
/*!
requires
- 0 < eps <= 1
- samples.size() > 0
- samples == a column matrix or something convertible to a column
matrix via vector_to_matrix(). Also, x should contain
matrix_type objects that represent nonempty column vectors.
ensures
- This object has learned how to normalize vectors that look like
vectors in the given set of samples.
- Principal component analysis is performed to find a transform
that might reduce the number of output features.
- #in_vector_size() == samples(0).nr()
- 0 < #out_vector_size() <= samples(0).nr()
- eps is a number that controls how "lossy" the pca transform will be.
Large values of eps result in #out_vector_size() being larger and
smaller values of eps result in #out_vector_size() being smaller.
!*/
long in_vector_size (
) const;
/*!
ensures
- returns the number of rows that input vectors are
required to contain if they are to be normalized by
this object.
!*/
long out_vector_size (
) const;
/*!
ensures
- returns the number of rows in the normalized vectors
that come out of this object.
!*/
const matrix<scalar_type,0,1,mem_manager_type>& operator() (
const matrix_type& x
) const;
/*!
requires
- x.nr() == in_vector_size()
- x.nc() == 1
ensures
- returns a normalized version of x, call it Z, that has the
following properties:
- Z.nr() == out_vector_size()
- Z.nc() == 1
- the expected value of each element of Z is 0
- the expected variance of each element of Z is 1
!*/
void swap (
vector_normalizer& item
);
/*!
ensures
- swaps *this and item
!*/
};
template <
typename matrix_type
>
inline void swap (
vector_normalizer<matrix_type>& a,
vector_normalizer<matrix_type>& b
) { a.swap(b); }
/*!
provides a global swap function
!*/
template <
typename matrix_type,
>
void deserialize (
vector_normalizer<matrix_type>& item,
std::istream& in
);
/*!
provides deserialization support
!*/
template <
typename matrix_type,
>
void serialize (
const vector_normalizer<matrix_type>& item,
std::ostream& out
);
/*!
provides serialization support
!*/
// ----------------------------------------------------------------------------------------
}
#endif // DLIB_STATISTICs_ABSTRACT_
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment