Commit 10e890e1 authored by Davis King's avatar Davis King

Merge

parents c9339b79 9427dd23
......@@ -17,17 +17,17 @@ namespace dlib
>
struct frobmetric_training_sample
{
matrix_type anchor;
std::vector<matrix_type> near;
std::vector<matrix_type> far;
matrix_type anchor_vect;
std::vector<matrix_type> near_vects;
std::vector<matrix_type> far_vects;
unsigned long num_triples (
) const { return near.size() * far.size(); }
) const { return near_vects.size() * far_vects.size(); }
void clear()
{
near.clear();
far.clear();
near_vects.clear();
far_vects.clear();
}
};
......@@ -47,8 +47,8 @@ namespace dlib
private:
struct compact_frobmetric_training_sample
{
std::vector<matrix_type> near;
std::vector<matrix_type> far;
std::vector<matrix_type> near_vects;
std::vector<matrix_type> far_vects;
};
struct objective
......@@ -61,7 +61,7 @@ namespace dlib
double operator()(const matrix<double,0,1,mem_manager_type>& u) const
{
long idx = 0;
const long dims = samples[0].far[0].size();
const long dims = samples[0].far_vects[0].size();
// Here we compute \hat A from the paper, which we refer to as just A in
// the code.
matrix<double,0,0,mem_manager_type> A(dims,dims);
......@@ -69,8 +69,8 @@ namespace dlib
std::vector<double> ufar, unear;
for (unsigned long i = 0; i < samples.size(); ++i)
{
ufar.assign(samples[i].far.size(),0);
unear.assign(samples[i].near.size(),0);
ufar.assign(samples[i].far_vects.size(),0);
unear.assign(samples[i].near_vects.size(),0);
for (unsigned long j = 0; j < unear.size(); ++j)
{
for (unsigned long k = 0; k < ufar.size(); ++k)
......@@ -81,9 +81,9 @@ namespace dlib
}
}
for (unsigned long j = 0; j < unear.size(); ++j)
A += unear[j]*samples[i].near[j]*trans(samples[i].near[j]);
A += unear[j]*samples[i].near_vects[j]*trans(samples[i].near_vects[j]);
for (unsigned long j = 0; j < ufar.size(); ++j)
A += ufar[j]*samples[i].far[j]*trans(samples[i].far[j]);
A += ufar[j]*samples[i].far_vects[j]*trans(samples[i].far_vects[j]);
}
eigenvalue_decomposition<matrix<double,0,0,mem_manager_type> > ed(make_symmetric(A));
......@@ -120,17 +120,17 @@ namespace dlib
std::vector<double> ufar, unear;
for (unsigned long i = 0; i < samples.size(); ++i)
{
ufar.resize(samples[i].far.size());
unear.resize(samples[i].near.size());
ufar.resize(samples[i].far_vects.size());
unear.resize(samples[i].near_vects.size());
for (unsigned long j = 0; j < unear.size(); ++j)
unear[j] = sum(pointwise_multiply(Aminus, samples[i].near[j]*trans(samples[i].near[j])));
unear[j] = sum(pointwise_multiply(Aminus, samples[i].near_vects[j]*trans(samples[i].near_vects[j])));
for (unsigned long j = 0; j < ufar.size(); ++j)
ufar[j] = sum(pointwise_multiply(Aminus, samples[i].far[j]*trans(samples[i].far[j])));
ufar[j] = sum(pointwise_multiply(Aminus, samples[i].far_vects[j]*trans(samples[i].far_vects[j])));
for (unsigned long j = 0; j < samples[i].near.size(); ++j)
for (unsigned long j = 0; j < samples[i].near_vects.size(); ++j)
{
for (unsigned long k = 0; k < samples[i].far.size(); ++k)
for (unsigned long k = 0; k < samples[i].far_vects.size(); ++k)
{
grad(idx++) = 1 + ufar[k]-unear[j];
}
......@@ -289,15 +289,6 @@ namespace dlib
void train (
const std::vector<frobmetric_training_sample<matrix_type> >& samples
)
/*!
requires
- samples.size() != 0
- All matrices inside samples (i.e. anchors and elements of near and far)
are column vectors with the same non-zero dimension.
- All the vectors in samples contain finite values.
- All elements of samples contain data, specifically, for all valid i:
- samples[i].num_triples() != 0
!*/
{
// make sure requires clause is not broken
DLIB_ASSERT(samples.size() > 0,
......@@ -306,24 +297,24 @@ namespace dlib
);
#ifdef ENABLE_ASSERTS
{
const long dims = samples[0].anchor.size();
const long dims = samples[0].anchor_vect.size();
DLIB_ASSERT(dims != 0,
"\tvoid vector_normalizer_frobmetric::train()"
<< "\n\t The dimension of the input vectors can't be zero."
);
for (unsigned long i = 0; i < samples.size(); ++i)
{
DLIB_ASSERT(is_col_vector(samples[i].anchor),
DLIB_ASSERT(is_col_vector(samples[i].anchor_vect),
"\tvoid vector_normalizer_frobmetric::train()"
<< "\n\t Invalid inputs were given to this function."
<< "\n\t i: " << i
);
DLIB_ASSERT(samples[i].anchor.size() == dims,
DLIB_ASSERT(samples[i].anchor_vect.size() == dims,
"\tvoid vector_normalizer_frobmetric::train()"
<< "\n\t Invalid inputs were given to this function."
<< "\n\t i: " << i
<< "\n\t dims: " << dims
<< "\n\t samples[i].anchor.size(): " << samples[i].anchor.size()
<< "\n\t samples[i].anchor_vect.size(): " << samples[i].anchor_vect.size()
);
DLIB_ASSERT(samples[i].num_triples() != 0,
......@@ -331,38 +322,38 @@ namespace dlib
<< "\n\t It is illegal for a training sample to have no data in it"
<< "\n\t i: " << i
);
for (unsigned long j = 0; j < samples[i].near.size(); ++j)
for (unsigned long j = 0; j < samples[i].near_vects.size(); ++j)
{
DLIB_ASSERT(is_col_vector(samples[i].near[j]),
DLIB_ASSERT(is_col_vector(samples[i].near_vects[j]),
"\tvoid vector_normalizer_frobmetric::train()"
<< "\n\t Invalid inputs were given to this function."
<< "\n\t i: " << i
<< "\n\t j: " << j
);
DLIB_ASSERT(samples[i].near[j].size() == dims,
DLIB_ASSERT(samples[i].near_vects[j].size() == dims,
"\tvoid vector_normalizer_frobmetric::train()"
<< "\n\t Invalid inputs were given to this function."
<< "\n\t i: " << i
<< "\n\t j: " << j
<< "\n\t dims: " << dims
<< "\n\t samples[i].near[j].size(): " << samples[i].near[j].size()
<< "\n\t samples[i].near_vects[j].size(): " << samples[i].near_vects[j].size()
);
}
for (unsigned long j = 0; j < samples[i].far.size(); ++j)
for (unsigned long j = 0; j < samples[i].far_vects.size(); ++j)
{
DLIB_ASSERT(is_col_vector(samples[i].far[j]),
DLIB_ASSERT(is_col_vector(samples[i].far_vects[j]),
"\tvoid vector_normalizer_frobmetric::train()"
<< "\n\t Invalid inputs were given to this function."
<< "\n\t i: " << i
<< "\n\t j: " << j
);
DLIB_ASSERT(samples[i].far[j].size() == dims,
DLIB_ASSERT(samples[i].far_vects[j].size() == dims,
"\tvoid vector_normalizer_frobmetric::train()"
<< "\n\t Invalid inputs were given to this function."
<< "\n\t i: " << i
<< "\n\t j: " << j
<< "\n\t dims: " << dims
<< "\n\t samples[i].far[j].size(): " << samples[i].far[j].size()
<< "\n\t samples[i].far_vects[j].size(): " << samples[i].far_vects[j].size()
);
}
}
......@@ -373,7 +364,7 @@ namespace dlib
// compute the mean sample
m = 0;
for (unsigned long i = 0; i < samples.size(); ++i)
m += samples[i].anchor;
m += samples[i].anchor_vect;
m /= samples.size();
DLIB_ASSERT(is_finite(m), "Some of the input vectors to vector_normalizer_frobmetric::train() have infinite or NaN values");
......@@ -382,22 +373,22 @@ namespace dlib
// over the next few lines of code.
unsigned long num_triples = 0;
for (unsigned long i = 0; i < samples.size(); ++i)
num_triples += samples[i].near.size()*samples[i].far.size();
num_triples += samples[i].near_vects.size()*samples[i].far_vects.size();
matrix<double,0,1,mem_manager_type> u(num_triples);
u = 0;
// precompute all the anchor to far/near pairs
// precompute all the anchor_vect to far_vects/near_vects pairs
std::vector<compact_frobmetric_training_sample> data(samples.size());
for (unsigned long i = 0; i < data.size(); ++i)
{
data[i].far.reserve(samples[i].far.size());
data[i].near.reserve(samples[i].near.size());
for (unsigned long j = 0; j < samples[i].far.size(); ++j)
data[i].far.push_back(samples[i].anchor - samples[i].far[j]);
for (unsigned long j = 0; j < samples[i].near.size(); ++j)
data[i].near.push_back(samples[i].anchor - samples[i].near[j]);
data[i].far_vects.reserve(samples[i].far_vects.size());
data[i].near_vects.reserve(samples[i].near_vects.size());
for (unsigned long j = 0; j < samples[i].far_vects.size(); ++j)
data[i].far_vects.push_back(samples[i].anchor_vect - samples[i].far_vects[j]);
for (unsigned long j = 0; j < samples[i].near_vects.size(); ++j)
data[i].near_vects.push_back(samples[i].anchor_vect - samples[i].near_vects[j]);
}
// Now run the main part of the algorithm
......
......@@ -19,20 +19,20 @@ namespace dlib
WHAT THIS OBJECT REPRESENTS
This object represents a training data sample for the
vector_normalizer_frobmetric object. It defines a set of training triplets
relative to a single anchor vector. That is, it specifies that the learned
distance metric should satisfy num_triples() constraints which are, for all
valid i and j:
length(T*anchor-T*near[i]) + 1 < length(T*anchor - T*far[j])
relative to a single anchor_vect vector. That is, it specifies that the
learned distance metric should satisfy num_triples() constraints which are,
for all valid i and j:
length(T*anchor_vect-T*near_vects[i]) + 1 < length(T*anchor_vect - T*far_vects[j])
for some appropriate linear transformation T which will be learned by
vector_normalizer_frobmetric.
!*/
matrix_type anchor;
std::vector<matrix_type> near;
std::vector<matrix_type> far;
matrix_type anchor_vect;
std::vector<matrix_type> near_vects;
std::vector<matrix_type> far_vects;
unsigned long num_triples (
) const { return near.size() * far.size(); }
) const { return near_vects.size() * far_vects.size(); }
/*!
ensures
- returns the number of training triplets defined by this object.
......@@ -41,8 +41,8 @@ namespace dlib
void clear()
/*!
ensures
- #near.size() == 0
- #far.size() == 0
- #near_vects.size() == 0
- #far_vects.size() == 0
!*/
};
......@@ -73,13 +73,13 @@ namespace dlib
By Chunhua Shen, Junae Kim, Lei Wang, in CVPR 2011
Therefore, this object is a tool that takes as input training triplets
(anchor, near, far) of vectors and attempts to learn a linear
(anchor_vect, near, far) of vectors and attempts to learn a linear
transformation T such that:
length(T*anchor-T*near) + 1 < length(T*anchor - T*far)
That is, you give a bunch of anchor vectors and for each anchor vector you
specify some vectors which should be near to it and some that should be far
form it. This object then tries to find a transformation matrix that makes
the "near" vectors close to their anchors while the "far" vectors are
length(T*anchor_vect-T*near) + 1 < length(T*anchor_vect - T*far)
That is, you give a bunch of anchor_vect vectors and for each anchor_vect
you specify some vectors which should be near to it and some that should be
far form it. This object then tries to find a transformation matrix that
makes the "near" vectors close to their anchors while the "far" vectors are
farther away.
THREAD SAFETY
......@@ -182,7 +182,7 @@ namespace dlib
/*!
requires
- samples.size() != 0
- All matrices inside samples (i.e. anchors and elements of near and far)
- All matrices inside samples (i.e. anchors and elements of near_vects and far_vects)
are column vectors with the same non-zero dimension.
- All the vectors in samples contain finite values.
- All elements of samples contain data, specifically, for all valid i:
......@@ -193,13 +193,13 @@ namespace dlib
according to the learned distance metric. In particular, we will have:
- #transform() == The linear transformation learned by the FrobMetric
learning procedure.
- #in_vector_size() == samples[0].anchor.size()
- #in_vector_size() == samples[0].anchor_vect.size()
- You can call (*this)(x) to transform a vector according to the learned
distance metric. That is, it should generally be the case that:
- length((*this)(anchor) - (*this)(near)) + 1 < length((*this)(anchor) - (*this)(far))
for the anchor, near, and far vectors in the training data.
- #transformed_means() == the mean of the input anchor vectors after being
transformed by #transform()
- length((*this)(anchor_vect) - (*this)(near)) + 1 < length((*this)(anchor_vect) - (*this)(far))
for the anchor_vect, near, and far vectors in the training data.
- #transformed_means() == the mean of the input anchor_vect vectors
after being transformed by #transform()
!*/
long in_vector_size (
......@@ -230,8 +230,8 @@ namespace dlib
- V.size() == in_vector_size()
- V is a vector such that subtracting it from transformed vectors
results in them having an expected value of 0. Therefore, it is
equal to transform() times the mean of the input anchor vectors given
to train().
equal to transform() times the mean of the input anchor_vect vectors
given to train().
!*/
const matrix<scalar_type,0,0,mem_manager_type>& transform (
......
......@@ -378,7 +378,7 @@ namespace
}
// check the unbiased skewness and excess kurtosis of one million Gaussian
// draws are both near zero.
// draws are both near_vects zero.
DLIB_TEST(abs(rs1.skewness()) < 0.1);
DLIB_TEST(abs(rs1.ex_kurtosis()) < 0.1);
}
......@@ -582,12 +582,12 @@ namespace
running_stats<double> rs;
for (unsigned long i = 0; i < samples.size(); ++i)
{
for (unsigned long j = 0; j < samples[i].near.size(); ++j)
for (unsigned long j = 0; j < samples[i].near_vects.size(); ++j)
{
const double d1 = length_squared(samples[i].anchor - samples[i].near[j]);
for (unsigned long k = 0; k < samples[i].far.size(); ++k)
const double d1 = length_squared(samples[i].anchor_vect - samples[i].near_vects[j]);
for (unsigned long k = 0; k < samples[i].far_vects.size(); ++k)
{
const double d2 = length_squared(samples[i].anchor - samples[i].far[k]);
const double d2 = length_squared(samples[i].anchor_vect - samples[i].far_vects[k]);
rs.add(d2-d1);
}
}
......@@ -620,17 +620,17 @@ namespace
for (int i = 0; i < 50; ++i)
{
samp.clear();
samp.anchor = gaussian_randm(dims,1,k++);
if (samp.anchor(key) > 0)
samp.anchor(key) = rnd.get_random_double() + 5;
samp.anchor_vect = gaussian_randm(dims,1,k++);
if (samp.anchor_vect(key) > 0)
samp.anchor_vect(key) = rnd.get_random_double() + 5;
else
samp.anchor(key) = -(rnd.get_random_double() + 5);
samp.anchor_vect(key) = -(rnd.get_random_double() + 5);
matrix<double,0,1> temp;
for (int j = 0; j < 5; ++j)
{
// Don't always put an equal number of near and far vectors into the
// Don't always put an equal number of near_vects and far_vects vectors into the
// training samples.
const int numa = rnd.get_random_32bit_number()%2 + 1;
const int numb = rnd.get_random_32bit_number()%2 + 1;
......@@ -639,16 +639,16 @@ namespace
{
temp = gaussian_randm(dims,1,k++); temp(key) = 0.1;
//temp = gaussian_randm(dims,1,k++); temp(key) = std::abs(temp(key));
if (samp.anchor(key) > 0) samp.near.push_back(temp);
else samp.far.push_back(temp);
if (samp.anchor_vect(key) > 0) samp.near_vects.push_back(temp);
else samp.far_vects.push_back(temp);
}
for (int num = 0; num < numb; ++num)
{
temp = gaussian_randm(dims,1,k++); temp(key) = -0.1;
//temp = gaussian_randm(dims,1,k++); temp(key) = -std::abs(temp(key));
if (samp.anchor(key) < 0) samp.near.push_back(temp);
else samp.far.push_back(temp);
if (samp.anchor_vect(key) < 0) samp.near_vects.push_back(temp);
else samp.far_vects.push_back(temp);
}
}
samples.push_back(samp);
......@@ -665,12 +665,12 @@ namespace
for (unsigned long i = 0; i < samples.size(); ++i)
{
samples[i].anchor = normalizer(samples[i].anchor);
total += samples[i].anchor;
for (unsigned long j = 0; j < samples[i].near.size(); ++j)
samples[i].near[j] = normalizer(samples[i].near[j]);
for (unsigned long j = 0; j < samples[i].far.size(); ++j)
samples[i].far[j] = normalizer(samples[i].far[j]);
samples[i].anchor_vect = normalizer(samples[i].anchor_vect);
total += samples[i].anchor_vect;
for (unsigned long j = 0; j < samples[i].near_vects.size(); ++j)
samples[i].near_vects[j] = normalizer(samples[i].near_vects[j]);
for (unsigned long j = 0; j < samples[i].far_vects.size(); ++j)
samples[i].far_vects[j] = normalizer(samples[i].far_vects[j]);
}
total /= samples.size();
dlog << LINFO << "sample transformed means: "<< trans(total);
......
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