made changes to bounds

This commit is contained in:
Manish
2018-07-12 17:40:38 +05:30
parent cb36f2d702
commit 0a77354803
2 changed files with 138 additions and 74 deletions
+4 -2
View File
@@ -221,12 +221,14 @@ class LMNNFunction
//! Holds the norm of each data point.
arma::vec norm;
//! Hold previous eval values for each datapoint.
std::vector<arma::mat> evalOld;
arma::cube evalOld;
//! Hold previous maximum norm of impostor.
arma::mat maxImpNorm;
//! Holds previous transformation matrix. Used for L-BFGS like optimizer.
arma::mat transformationOld;
//! Holds previous transformation matrix for each point. Used for
//! optimizers which operates over batches.
std::vector<arma::mat> transformationOldPoint;
arma::cube transformationOldPoint;
/**
* Precalculate the gradient part due to target neighbors and stores
* the result as a matrix. Used for L-BFGS like optimizers which does not
+134 -72
View File
@@ -34,15 +34,19 @@ LMNNFunction<MetricType>::LMNNFunction(const arma::mat& dataset,
regularization(regularization),
iteration(0),
range(range),
constraint(dataset, labels, k),
evalOld(dataset.n_cols),
transformationOldPoint(dataset.n_cols)
constraint(dataset, labels, k)
{
// Initialize the initial learning point.
initialPoint.eye(dataset.n_rows, dataset.n_rows);
// Initialize transformed dataset to base dataset.
transformedDataset = dataset;
evalOld.set_size(k, k, dataset.n_cols);
evalOld.fill(arma::datum::nan);
maxImpNorm.set_size(k, dataset.n_cols);
maxImpNorm.fill(arma::datum::nan);
// Initialize target neighbors & impostors.
targetNeighbors = arma::Mat<size_t>(k, dataset.n_cols, arma::fill::zeros);
impostors = arma::Mat<size_t>(k, dataset.n_cols, arma::fill::zeros);
@@ -61,8 +65,9 @@ void LMNNFunction<MetricType>::Shuffle()
{
arma::mat newDataset = dataset;
arma::Mat<size_t> newLabels = labels;
std::vector<arma::mat> newEvalOld = evalOld;
std::vector<arma::mat> newTransformationOldPoint = transformationOldPoint;
arma::cube newEvalOld = evalOld;
arma::cube newTransformationOldPoint = transformationOldPoint;
arma::mat newMaxImpNorm = maxImpNorm;
// Generate ordering.
arma::uvec ordering = arma::shuffle(arma::linspace<arma::uvec>(0,
@@ -73,11 +78,12 @@ void LMNNFunction<MetricType>::Shuffle()
dataset = newDataset.cols(ordering);
labels = newLabels.cols(ordering);
maxImpNorm = newMaxImpNorm.cols(ordering);
for (size_t i = 0; i < ordering.n_cols; i++)
for (size_t i = 0; i < ordering.n_elem; i++)
{
evalOld[i] = newEvalOld[ordering[i]];
transformationOldPoint[i] = newTransformationOldPoint[ordering[i]];
evalOld.slice(i) = newEvalOld.slice(ordering(i));
transformationOldPoint.slice(i) = newTransformationOldPoint.slice(ordering(i));
}
// Re-calculate target neighbors as indices changed.
@@ -117,12 +123,6 @@ double LMNNFunction<MetricType>::Evaluate(const arma::mat& transformation)
cost += (1 - regularization) * eval;
}
// Ensure that evalOld has proper size.
if (evalOld[i].n_elem == 0)
{
evalOld[i].set_size(k, k);
}
for (int j = k - 1; j >= 0; j--)
{
// Bound constraints to avoid uneccesary computation. Here bp stands for
@@ -137,15 +137,32 @@ double LMNNFunction<MetricType>::Evaluate(const arma::mat& transformation)
bool exactEval = true;
// Bounds for eval.
if (transformationOld.n_elem != 0 && !std::isnan(evalOld[i](j, l)))
if (transformationOld.n_elem != 0 && !std::isnan(evalOld(j, l, i)))
{
eval = evalOld[i](j, l) + transformationDiff *
(norm(targetNeighbors(j, i)) + norm(impostors(l, i)) +
double maxNorm;
if (!std::isnan(maxImpNorm(l, i)))
maxNorm = std::max(norm(impostors(l, i)), maxImpNorm(l, i));
else
maxNorm = norm(impostors(l, i));
// Update cache max impostor norm.
maxImpNorm(l, i) = maxNorm;
eval = evalOld(j, l, i) + transformationDiff *
(norm(targetNeighbors(j, i)) + maxNorm +
2 * norm(i));
// Check if there is need to calculate exact eval value.
if (eval <= -1)
{
exactEval = false;
}
else
{
// Reset cacche max impostor norm.
maxImpNorm(l, i) = arma::datum::nan;
evalOld(j, l, i) = arma::datum::nan;
}
}
// Calculate exact eval value.
@@ -167,7 +184,7 @@ double LMNNFunction<MetricType>::Evaluate(const arma::mat& transformation)
}
// Update cache eval value.
evalOld[i](j, l) = eval;
evalOld(j, l, i) = eval;
// Check bounding condition.
if (eval <= -1)
@@ -196,6 +213,14 @@ double LMNNFunction<MetricType>::Evaluate(const arma::mat& transformation,
{
double cost = 0;
// Ensure cache transformation cube has correct size.
if (transformationOldPoint.n_elem == 0)
{
transformationOldPoint.set_size(transformation.n_rows,
transformation.n_cols, dataset.n_cols);
transformationOldPoint.fill(arma::datum::nan);
}
// Apply metric over dataset.
transformedDataset = transformation * dataset;
@@ -218,16 +243,10 @@ double LMNNFunction<MetricType>::Evaluate(const arma::mat& transformation,
// Calculate norm of change in transformation.
double transformationDiff = 0;
if (transformationOldPoint[i].n_elem != 0)
if (!transformationOldPoint.slice(i).has_nan())
{
transformationDiff = arma::norm(transformation -
transformationOldPoint[i]);
}
// Ensure that evalOld has proper size.
if (evalOld[i].n_elem == 0)
{
evalOld[i].set_size(k, k);
transformationOldPoint.slice(i));
}
for (int j = k - 1; j >= 0; j--)
@@ -244,15 +263,32 @@ double LMNNFunction<MetricType>::Evaluate(const arma::mat& transformation,
bool exactEval = true;
// Bounds for eval.
if (transformationOldPoint[i].n_elem != 0 && !std::isnan(evalOld[i](j, l)))
if (!transformationOldPoint.slice(i).has_nan() && !std::isnan(evalOld(j, l, i)))
{
eval = evalOld[i](j, l) + transformationDiff *
(norm(targetNeighbors(j, i)) + norm(impostors(l, i)) +
2 * norm(i));
double maxNorm;
if (!std::isnan(maxImpNorm(l, i)))
maxNorm = std::max(norm(impostors(l, i)), maxImpNorm(l, i));
else
maxNorm = norm(impostors(l, i));
// Update cache max impostor norm.
maxImpNorm(l, i) = maxNorm;
eval = evalOld(j, l, i) + transformationDiff *
(norm(targetNeighbors(j, i)) + maxNorm +
2 * norm(i));
// Check if there is need to calculate exact eval value.
if (eval <= -1)
{
exactEval = false;
}
else
{
// Reset cacche max impostor norm.
maxImpNorm(l, i) = arma::datum::nan;
evalOld(j, l, i) = arma::datum::nan;
}
}
// Calculate exact eval value.
@@ -274,10 +310,10 @@ double LMNNFunction<MetricType>::Evaluate(const arma::mat& transformation,
}
// Update cache eval value.
evalOld[i](j, l) = eval;
evalOld(j, l, i) = eval;
// Update cache transformation matrix.
transformationOldPoint[i] = transformation;
transformationOldPoint.slice(i) = transformation;
// Check bounding condition.
if (eval <= -1)
@@ -322,14 +358,11 @@ void LMNNFunction<MetricType>::Gradient(const arma::mat& transformation,
// Flag to trigger exact eval calculation.
bool exactEval = true;
if (evalOld[i].n_elem != 0)
// Use eval calualated during Evaluate.
if (!std::isnan(evalOld(j, l, i)))
{
// Use eval calualated during Evaluate.
if (!std::isnan(evalOld[i](j, l)))
{
eval = evalOld[i](j, l);
exactEval = false;
}
eval = evalOld(j, l, i);
exactEval = false;
}
if (exactEval)
@@ -395,14 +428,11 @@ void LMNNFunction<MetricType>::Gradient(const arma::mat& transformation,
// Flag to trigger exact eval calculation.
bool exactEval = true;
if (evalOld[i].n_elem != 0)
// Use eval calualated during Evaluate.
if (!std::isnan(evalOld(j, l, i)))
{
// Use eval calualated during Evaluate.
if (!std::isnan(evalOld[i](j, l)))
{
eval = evalOld[i](j, l);
exactEval = false;
}
eval = evalOld(j, l, i);
exactEval = false;
}
if (exactEval)
@@ -478,12 +508,6 @@ double LMNNFunction<MetricType>::EvaluateWithGradient(
cost += (1 - regularization) * eval;
}
// Ensure that evalOld has proper size.
if (evalOld[i].n_elem == 0)
{
evalOld[i].set_size(k, k);
}
for (int j = k - 1; j >= 0; j--)
{
// Bound constraints to avoid uneccesary computation.
@@ -497,15 +521,32 @@ double LMNNFunction<MetricType>::EvaluateWithGradient(
bool exactEval = true;
// Bounds for eval.
if (transformationOld.n_elem != 0 && !std::isnan(evalOld[i](j, l)))
if (transformationOld.n_elem != 0 && !std::isnan(evalOld(j, l, i)))
{
eval = evalOld[i](j, l) + transformationDiff *
(norm(targetNeighbors(j, i)) + norm(impostors(l, i)) +
double maxNorm;
if (!std::isnan(maxImpNorm(l, i)))
maxNorm = std::max(norm(impostors(l, i)), maxImpNorm(l, i));
else
maxNorm = norm(impostors(l, i));
// Update cache max impostor norm.
maxImpNorm(l, i) = maxNorm;
eval = evalOld(j, l, i) + transformationDiff *
(norm(targetNeighbors(j, i)) + maxNorm +
2 * norm(i));
// Check if there is need to calculate exact eval value.
if (eval <= -1)
{
exactEval = false;
}
else
{
// Reset cacche max impostor norm.
maxImpNorm(l, i) = arma::datum::nan;
evalOld(j, l, i) = arma::datum::nan;
}
}
// Calculate exact eval value.
@@ -527,7 +568,7 @@ double LMNNFunction<MetricType>::EvaluateWithGradient(
}
// Update cache eval value.
evalOld[i](j, l) = eval;
evalOld(j, l, i) = eval;
// Check bounding condition.
if (eval <= -1)
@@ -569,16 +610,17 @@ double LMNNFunction<MetricType>::EvaluateWithGradient(
{
double cost = 0;
// Ensure cache transformation cube has correct size.
if (transformationOldPoint.n_elem == 0)
{
transformationOldPoint.set_size(transformation.n_rows,
transformation.n_cols, dataset.n_cols);
transformationOldPoint.fill(arma::datum::nan);
}
// Apply metric over dataset.
transformedDataset = transformation * dataset;
// Calculate norm of change in transformation.
double transformationDiff = 0;
if (transformationOld.n_elem != 0)
{
transformationDiff = arma::norm(transformation - transformationOld);
}
if (iteration++ % range == 0)
{
// Re-calculate impostors on transformed dataset.
@@ -605,10 +647,12 @@ double LMNNFunction<MetricType>::EvaluateWithGradient(
cij += diff * arma::trans(diff);
}
// Ensure that evalOld has proper size.
if (evalOld[i].n_elem == 0)
// Calculate norm of change in transformation.
double transformationDiff = 0;
if (!transformationOldPoint.slice(i).has_nan())
{
evalOld[i].set_size(k, k);
transformationDiff = arma::norm(transformation -
transformationOldPoint.slice(i));
}
for (int j = k - 1; j >= 0; j--)
@@ -624,15 +668,32 @@ double LMNNFunction<MetricType>::EvaluateWithGradient(
bool exactEval = true;
// Bounds for eval.
if (transformationOld.n_elem != 0 && !std::isnan(evalOld[i](j, l)))
if (!transformationOldPoint.slice(i).has_nan() && !std::isnan(evalOld(j, l, i)))
{
eval = evalOld[i](j, l) + transformationDiff *
(norm(targetNeighbors(j, i)) + norm(impostors(l, i)) +
double maxNorm;
if (!std::isnan(maxImpNorm(l, i)))
maxNorm = std::max(norm(impostors(l, i)), maxImpNorm(l, i));
else
maxNorm = norm(impostors(l, i));
// Update cache max impostor norm.
maxImpNorm(l, i) = maxNorm;
eval = evalOld(j, l, i) + transformationDiff *
(norm(targetNeighbors(j, i)) + maxNorm +
2 * norm(i));
// Check if there is need to calculate exact eval value.
if (eval <= -1)
{
exactEval = false;
}
else
{
// Reset cacche max impostor norm.
maxImpNorm(l, i) = arma::datum::nan;
evalOld(j, l, i) = arma::datum::nan;
}
}
// Calculate exact eval value.
@@ -654,7 +715,10 @@ double LMNNFunction<MetricType>::EvaluateWithGradient(
}
// Update cache eval value.
evalOld[i](j, l) = eval;
evalOld(j, l, i) = eval;
// Update cache transformation matrix.
transformationOldPoint.slice(i) = transformation;
// Check bounding condition.
if (eval <= -1)
@@ -679,8 +743,6 @@ double LMNNFunction<MetricType>::EvaluateWithGradient(
gradient = 2 * transformation * ((1 - regularization) * cij +
regularization * cil);
// Update cache transformation matrix.
transformationOld = transformation;
return cost;
}