Cache distance to avoid recomputation

This commit is contained in:
Manish
2018-06-21 20:39:09 +05:30
parent 8a6709f089
commit d05cfd31cc
3 changed files with 140 additions and 14 deletions
+24
View File
@@ -68,6 +68,16 @@ class Constraints
*/
void Impostors(arma::Mat<size_t>& outputMatrix);
/**
* Calculates k differently labeled nearest neighbors & distances to
* impostors for each datapoint and writes them back to passed matrices.
*
* @param outputNeighbors Coordinates matrix to store impostors.
* @param outputDistance matrix to store distance.
*/
void Impostors(arma::Mat<size_t>& outputNeighbors,
arma::mat& outputDistance);
/**
* Calculates k differently labeled nearest neighbors for a batch of dataset
* and writes them back to passed matrix.
@@ -80,6 +90,20 @@ class Constraints
const size_t begin,
const size_t batchSize);
/**
* Calculates k differently labeled nearest neighbors & distances to
* impostors for a batch of dataset and writes them back to passed matrices.
*
* @param outputNeighbors Coordinates matrix to store impostors.
* @param outputDistance matrix to store distance.
* @param begin Index of the initial point of dataset.
* @param batchSize Number of data points to use.
*/
void Impostors(arma::Mat<size_t>& outputNeighbors,
arma::mat& outputDistance,
const size_t begin,
const size_t batchSize);
/**
* Generate triplets {i, j, l} for each datapoint i and writes back generated
* triplets to matrix passed.
@@ -143,6 +143,42 @@ void Constraints::Impostors(arma::Mat<size_t>& outputMatrix)
}
}
// Calculates k differently labeled nearest neighbors. The function
// writes back calculated neighbors & distances to passed matrices.
void Constraints::Impostors(arma::Mat<size_t>& outputNeighbors,
arma::mat& outputDistance)
{
size_t N = dataset.n_cols;
// Perform pre-calculation, if necessary.
Precalculate();
outputNeighbors = arma::Mat<size_t>(k, N, arma::fill::zeros);
outputDistance = arma::mat(k, N, arma::fill::zeros);
// KNN instance.
neighbor::KNN knn;
arma::Mat<size_t> neighbors;
arma::mat distances;
for (size_t i = 0; i < uniqueLabels.n_cols; i++)
{
// Perform KNN search with differently labeled points as reference
// set and same class points as query set.
knn.Train(dataset.cols(indexDiff[i]));
knn.Search(dataset.cols(indexSame[i]), k, neighbors, distances);
// Re-map neighbors to their index.
for (size_t j = 0; j < neighbors.n_elem; j++)
neighbors(j) = indexDiff[i].at(neighbors(j));
// Store impostors.
outputNeighbors.cols(indexSame[i]) = neighbors;
outputDistance.cols(indexSame[i]) = distances;
}
}
// Calculates k differently labeled nearest neighbors on a
// batch of data points.
void Constraints::Impostors(arma::Mat<size_t>& outputMatrix,
@@ -183,6 +219,48 @@ void Constraints::Impostors(arma::Mat<size_t>& outputMatrix,
}
}
// Calculates k differently labeled nearest neighbors & distances on a
// batch of data points.
void Constraints::Impostors(arma::Mat<size_t>& outputNeighbors,
arma::mat& outputDistance,
const size_t begin,
const size_t batchSize)
{
arma::mat subDataset = dataset.cols(begin, begin + batchSize - 1);
arma::Row<size_t> sublabels = labels.cols(begin, begin + batchSize - 1);
// Perform pre-calculation, if necessary.
Precalculate();
// KNN instance.
neighbor::KNN knn;
arma::Mat<size_t> neighbors;
arma::mat distances;
// Vectors to store indices.
arma::uvec subIndexSame;
for (size_t i = 0; i < uniqueLabels.n_cols; i++)
{
// Calculate impostors.
subIndexSame = arma::find(sublabels == uniqueLabels[i]);
// Perform KNN search with differently labeled points as reference
// set and same class points as query set.
knn.Train(dataset.cols(indexDiff[i]));
knn.Search(subDataset.cols(subIndexSame), k, neighbors, distances);
// Re-map neighbors to their index.
for (size_t j = 0; j < neighbors.n_elem; j++)
neighbors(j) = indexDiff[i].at(neighbors(j));
// Store impostors.
outputNeighbors.cols(begin + subIndexSame) = neighbors;
outputDistance.cols(begin + subIndexSame) = distances;
}
}
// Generates {data point, target neighbors, impostors} triplets using
// TargetNeighbors() and Impostors().
void Constraints::Triplets(arma::Mat<size_t>& outputMatrix)
+38 -14
View File
@@ -76,11 +76,12 @@ double LMNNFunction<MetricType>::Evaluate(const arma::mat& coordinates)
// Apply metric over dataset.
transformedDataset = coordinates * dataset;
arma::mat distance = arma::mat(k, dataset.n_cols, arma::fill::zeros);
if (iteration++ % range == 0)
{
// Re-calculate impostors on transformed dataset.
Constraints constraint(transformedDataset, labels, k);
constraint.Impostors(impostors);
constraint.Impostors(impostors, distance);
}
for (size_t i = 0; i < dataset.n_cols; i++)
@@ -99,12 +100,17 @@ double LMNNFunction<MetricType>::Evaluate(const arma::mat& coordinates)
// breaking point.
for (size_t l = 0, bp = k; l < bp ; l++)
{
// Calculate cost due to data point, target neighbors, impostors
// Check if we already have distance between impostor & data point
// stored.
double distImp = distance(l, i) > 0 ? distance(l, i) :
metric.Evaluate(transformedDataset.col(i),
transformedDataset.col(impostors(l, i)));
// Calculate cost due to {data point, target neighbors, impostors}
// triplets.
double eval = metric.Evaluate(transformedDataset.col(i),
transformedDataset.col(targetNeighbors(j, i))) -
metric.Evaluate(transformedDataset.col(i),
transformedDataset.col(impostors(l, i)));
distImp;
// Check bounding condition.
if (eval < -1)
@@ -133,11 +139,12 @@ double LMNNFunction<MetricType>::Evaluate(const arma::mat& coordinates,
// Apply metric over dataset.
transformedDataset = coordinates * dataset;
arma::mat distance = arma::mat(k, dataset.n_cols, arma::fill::zeros);
if (iteration++ % range == 0)
{
// Re-calculate impostors on transformed dataset.
Constraints constraint(transformedDataset, labels, k);
constraint.Impostors(impostors, begin, batchSize);
constraint.Impostors(impostors, distance, begin, batchSize);
}
for (size_t i = begin; i < begin + batchSize; i++)
@@ -155,12 +162,17 @@ double LMNNFunction<MetricType>::Evaluate(const arma::mat& coordinates,
// Bound constraints to avoid uneccesary computation.
for (size_t l = 0, bp = k; l < bp ; l++)
{
// Calculate cost due to data point, target neighbors, impostors
// Check if we already have distance between impostor & data point
// stored.
double distImp = distance(l, i) > 0 ? distance(l, i) :
metric.Evaluate(transformedDataset.col(i),
transformedDataset.col(impostors(l, i)));
// Calculate cost due to {data point, target neighbors, impostors}
// triplets.
double eval = metric.Evaluate(transformedDataset.col(i),
transformedDataset.col(targetNeighbors(j, i))) -
metric.Evaluate(transformedDataset.col(i),
transformedDataset.col(impostors(l, i)));
distImp;
// Check bounding condition.
if (eval < -1)
@@ -295,11 +307,12 @@ double LMNNFunction<MetricType>::EvaluateWithGradient(
// Apply metric over dataset.
transformedDataset = coordinates * dataset;
arma::mat distance = arma::mat(k, dataset.n_cols, arma::fill::zeros);
if (iteration++ % range == 0)
{
// Re-calculate impostors on transformed dataset.
Constraints constraint(transformedDataset, labels, k);
constraint.Impostors(impostors);
constraint.Impostors(impostors, distance);
}
gradient.zeros(coordinates.n_rows, coordinates.n_cols);
@@ -326,12 +339,17 @@ double LMNNFunction<MetricType>::EvaluateWithGradient(
// Bound constraints to avoid uneccesary computation.
for (size_t l = 0, bp = k; l < bp ; l++)
{
// Check if we already have distance between impostor & data point
// stored.
double distImp = distance(l, i) > 0 ? distance(l, i) :
metric.Evaluate(transformedDataset.col(i),
transformedDataset.col(impostors(l, i)));
// Calculate cost due to {data point, target neighbors, impostors}
// triplets.
double eval = metric.Evaluate(transformedDataset.col(i),
transformedDataset.col(targetNeighbors(j, i))) -
metric.Evaluate(transformedDataset.col(i),
transformedDataset.col(impostors(l, i)));
distImp;
// Check bounding condition.
if (eval < -1)
@@ -373,11 +391,12 @@ double LMNNFunction<MetricType>::EvaluateWithGradient(
// Apply metric over dataset.
transformedDataset = coordinates * dataset;
arma::mat distance = arma::mat(k, dataset.n_cols, arma::fill::zeros);
if (iteration++ % range == 0)
{
// Re-calculate impostors on transformed dataset.
Constraints constraint(transformedDataset, labels, k);
constraint.Impostors(impostors, begin, batchSize);
constraint.Impostors(impostors, distance, begin, batchSize);
}
gradient.zeros(coordinates.n_rows, coordinates.n_cols);
@@ -404,12 +423,17 @@ double LMNNFunction<MetricType>::EvaluateWithGradient(
// Bound constraints to avoid uneccesary computation.
for (size_t l = 0, bp = k; l < bp ; l++)
{
// Check if we already have distance between impostor & data point
// stored.
double distImp = distance(l, i) > 0 ? distance(l, i) :
metric.Evaluate(transformedDataset.col(i),
transformedDataset.col(impostors(l, i)));
// Calculate cost due to {data point, target neighbors, impostors}
// triplets.
double eval = metric.Evaluate(transformedDataset.col(i),
transformedDataset.col(targetNeighbors(j, i))) -
metric.Evaluate(transformedDataset.col(i),
transformedDataset.col(impostors(l, i)));
distImp;
// Check bounding condition.
if (eval < -1)