mlpack
constraints_impl.hpp
Go to the documentation of this file.
1 
12 #ifndef MLPACK_METHODS_LMNN_CONSTRAINTS_IMPL_HPP
13 #define MLPACK_METHODS_LMNN_CONSTRAINTS_IMPL_HPP
14 
15 // In case it hasn't been included already.
16 #include "constraints.hpp"
17 
18 namespace mlpack {
19 namespace lmnn {
20 
21 template<typename MetricType>
23  const arma::mat& /* dataset */,
24  const arma::Row<size_t>& labels,
25  const size_t k) :
26  k(k),
27  precalculated(false)
28 {
29  // Ensure a valid k is passed.
30  size_t minCount = arma::min(arma::histc(labels, arma::unique(labels)));
31 
32  if (minCount < k + 1)
33  {
34  Log::Fatal << "Constraints::Constraints(): One of the class contains only "
35  << minCount << " instances, but value of k is " << k << " "
36  << "(k should be < " << minCount << ")!" << std::endl;
37  }
38 }
39 
40 template<typename MetricType>
42  const arma::mat& distances,
43  arma::Mat<size_t>& neighbors,
44  const arma::vec& norms)
45 {
46  // Shortcut...
47  if (neighbors.n_rows == 1)
48  return;
49 
50  // Just a simple loop over the results---we want to make sure that the
51  // largest-norm point with identical distance has the last location.
52  for (size_t i = 0; i < neighbors.n_cols; ++i)
53  {
54  for (size_t start = 0; start < neighbors.n_rows - 1; start++)
55  {
56  size_t end = start + 1;
57  while (distances(start, i) == distances(end, i) &&
58  end < neighbors.n_rows)
59  {
60  end++;
61  if (end == neighbors.n_rows)
62  break;
63  }
64 
65  if (start != end)
66  {
67  // We must sort these elements by norm.
68  arma::Col<size_t> newNeighbors =
69  neighbors.col(i).subvec(start, end - 1);
70  arma::uvec indices = arma::conv_to<arma::uvec>::from(newNeighbors);
71 
72  arma::uvec order = arma::sort_index(norms.elem(indices));
73  neighbors.col(i).subvec(start, end - 1) =
74  newNeighbors.elem(order);
75  }
76  }
77  }
78 }
79 
80 // Calculates k similar labeled nearest neighbors.
81 template<typename MetricType>
82 void Constraints<MetricType>::TargetNeighbors(arma::Mat<size_t>& outputMatrix,
83  const arma::mat& dataset,
84  const arma::Row<size_t>& labels,
85  const arma::vec& norms)
86 {
87  // Perform pre-calculation. If neccesary.
88  Precalculate(labels);
89 
90  // KNN instance.
91  KNN knn;
92 
93  arma::Mat<size_t> neighbors;
94  arma::mat distances;
95 
96  for (size_t i = 0; i < uniqueLabels.n_cols; ++i)
97  {
98  // Perform KNN search with same class points as both reference
99  // set and query set.
100  knn.Train(dataset.cols(indexSame[i]));
101  knn.Search(k, neighbors, distances);
102 
103  // Re-order neighbors on the basis of increasing norm in case
104  // of ties among distances.
105  ReorderResults(distances, neighbors, norms);
106 
107  // Re-map neighbors to their index.
108  for (size_t j = 0; j < neighbors.n_elem; ++j)
109  neighbors(j) = indexSame[i].at(neighbors(j));
110 
111  // Store target neihbors.
112  outputMatrix.cols(indexSame[i]) = neighbors;
113  }
114 }
115 
116 // Calculates k similar labeled nearest neighbors on a
117 // batch of data points.
118 template<typename MetricType>
119 void Constraints<MetricType>::TargetNeighbors(arma::Mat<size_t>& outputMatrix,
120  const arma::mat& dataset,
121  const arma::Row<size_t>& labels,
122  const arma::vec& norms,
123  const size_t begin,
124  const size_t batchSize)
125 {
126  // Perform pre-calculation. If neccesary.
127  Precalculate(labels);
128 
129  arma::mat subDataset = dataset.cols(begin, begin + batchSize - 1);
130  arma::Row<size_t> sublabels = labels.cols(begin, begin + batchSize - 1);
131 
132  // KNN instance.
133  KNN knn;
134 
135  arma::Mat<size_t> neighbors;
136  arma::mat distances;
137 
138  // Vectors to store indices.
139  arma::uvec subIndexSame;
140 
141  for (size_t i = 0; i < uniqueLabels.n_cols; ++i)
142  {
143  // Calculate Target Neighbors.
144  subIndexSame = arma::find(sublabels == uniqueLabels[i]);
145 
146  // Perform KNN search with same class points as both reference
147  // set and query set.
148  knn.Train(dataset.cols(indexSame[i]));
149  knn.Search(subDataset.cols(subIndexSame), k, neighbors, distances);
150 
151  // Re-order neighbors on the basis of increasing norm in case
152  // of ties among distances.
153  ReorderResults(distances, neighbors, norms);
154 
155  // Re-map neighbors to their index.
156  for (size_t j = 0; j < neighbors.n_elem; ++j)
157  neighbors(j) = indexSame[i].at(neighbors(j));
158 
159  // Store target neighbors.
160  outputMatrix.cols(begin + subIndexSame) = neighbors;
161  }
162 }
163 
164 // Calculates k differently labeled nearest neighbors.
165 template<typename MetricType>
166 void Constraints<MetricType>::Impostors(arma::Mat<size_t>& outputMatrix,
167  const arma::mat& dataset,
168  const arma::Row<size_t>& labels,
169  const arma::vec& norms)
170 {
171  // Perform pre-calculation. If neccesary.
172  Precalculate(labels);
173 
174  // KNN instance.
175  KNN knn;
176 
177  arma::Mat<size_t> neighbors;
178  arma::mat distances;
179 
180  for (size_t i = 0; i < uniqueLabels.n_cols; ++i)
181  {
182  // Perform KNN search with differently labeled points as reference
183  // set and same class points as query set.
184  knn.Train(dataset.cols(indexDiff[i]));
185  knn.Search(dataset.cols(indexSame[i]), k, neighbors, distances);
186 
187  // Re-order neighbors on the basis of increasing norm in case
188  // of ties among distances.
189  ReorderResults(distances, neighbors, norms);
190 
191  // Re-map neighbors to their index.
192  for (size_t j = 0; j < neighbors.n_elem; ++j)
193  neighbors(j) = indexDiff[i].at(neighbors(j));
194 
195  // Store impostors.
196  outputMatrix.cols(indexSame[i]) = neighbors;
197  }
198 }
199 
200 // Calculates k differently labeled nearest neighbors. The function
201 // writes back calculated neighbors & distances to passed matrices.
202 template<typename MetricType>
203 void Constraints<MetricType>::Impostors(arma::Mat<size_t>& outputNeighbors,
204  arma::mat& outputDistance,
205  const arma::mat& dataset,
206  const arma::Row<size_t>& labels,
207  const arma::vec& norms)
208 {
209  // Perform pre-calculation. If neccesary.
210  Precalculate(labels);
211 
212  // KNN instance.
213  KNN knn;
214 
215  arma::Mat<size_t> neighbors;
216  arma::mat distances;
217 
218  for (size_t i = 0; i < uniqueLabels.n_cols; ++i)
219  {
220  // Perform KNN search with differently labeled points as reference
221  // set and same class points as query set.
222  knn.Train(dataset.cols(indexDiff[i]));
223  knn.Search(dataset.cols(indexSame[i]), k, neighbors, distances);
224 
225  // Re-order neighbors on the basis of increasing norm in case
226  // of ties among distances.
227  ReorderResults(distances, neighbors, norms);
228 
229  // Re-map neighbors to their index.
230  for (size_t j = 0; j < neighbors.n_elem; ++j)
231  neighbors(j) = indexDiff[i].at(neighbors(j));
232 
233  // Store impostors.
234  outputNeighbors.cols(indexSame[i]) = neighbors;
235  outputDistance.cols(indexSame[i]) = distances;
236  }
237 }
238 
239 // Calculates k differently labeled nearest neighbors on a
240 // batch of data points.
241 template<typename MetricType>
242 void Constraints<MetricType>::Impostors(arma::Mat<size_t>& outputMatrix,
243  const arma::mat& dataset,
244  const arma::Row<size_t>& labels,
245  const arma::vec& norms,
246  const size_t begin,
247  const size_t batchSize)
248 {
249  // Perform pre-calculation. If neccesary.
250  Precalculate(labels);
251 
252  arma::mat subDataset = dataset.cols(begin, begin + batchSize - 1);
253  arma::Row<size_t> sublabels = labels.cols(begin, begin + batchSize - 1);
254 
255  // KNN instance.
256  KNN knn;
257 
258  arma::Mat<size_t> neighbors;
259  arma::mat distances;
260 
261  // Vectors to store indices.
262  arma::uvec subIndexSame;
263 
264  for (size_t i = 0; i < uniqueLabels.n_cols; ++i)
265  {
266  // Calculate impostors.
267  subIndexSame = arma::find(sublabels == uniqueLabels[i]);
268 
269  // Perform KNN search with differently labeled points as reference
270  // set and same class points as query set.
271  knn.Train(dataset.cols(indexDiff[i]));
272  knn.Search(subDataset.cols(subIndexSame), k, neighbors, distances);
273 
274  // Re-order neighbors on the basis of increasing norm in case
275  // of ties among distances.
276  ReorderResults(distances, neighbors, norms);
277 
278  // Re-map neighbors to their index.
279  for (size_t j = 0; j < neighbors.n_elem; ++j)
280  neighbors(j) = indexDiff[i].at(neighbors(j));
281 
282  // Store impostors.
283  outputMatrix.cols(begin + subIndexSame) = neighbors;
284  }
285 }
286 
287 // Calculates k differently labeled nearest neighbors & distances on a
288 // batch of data points.
289 template<typename MetricType>
290 void Constraints<MetricType>::Impostors(arma::Mat<size_t>& outputNeighbors,
291  arma::mat& outputDistance,
292  const arma::mat& dataset,
293  const arma::Row<size_t>& labels,
294  const arma::vec& norms,
295  const size_t begin,
296  const size_t batchSize)
297 {
298  // Perform pre-calculation. If neccesary.
299  Precalculate(labels);
300 
301  arma::mat subDataset = dataset.cols(begin, begin + batchSize - 1);
302  arma::Row<size_t> sublabels = labels.cols(begin, begin + batchSize - 1);
303 
304  // KNN instance.
305  KNN knn;
306 
307  arma::Mat<size_t> neighbors;
308  arma::mat distances;
309 
310  // Vectors to store indices.
311  arma::uvec subIndexSame;
312 
313  for (size_t i = 0; i < uniqueLabels.n_cols; ++i)
314  {
315  // Calculate impostors.
316  subIndexSame = arma::find(sublabels == uniqueLabels[i]);
317 
318  // Perform KNN search with differently labeled points as reference
319  // set and same class points as query set.
320  knn.Train(dataset.cols(indexDiff[i]));
321  knn.Search(subDataset.cols(subIndexSame), k, neighbors, distances);
322 
323  // Re-order neighbors on the basis of increasing norm in case
324  // of ties among distances.
325  ReorderResults(distances, neighbors, norms);
326 
327  // Re-map neighbors to their index.
328  for (size_t j = 0; j < neighbors.n_elem; ++j)
329  neighbors(j) = indexDiff[i].at(neighbors(j));
330 
331  // Store impostors.
332  outputNeighbors.cols(begin + subIndexSame) = neighbors;
333  outputDistance.cols(begin + subIndexSame) = distances;
334  }
335 }
336 
337 // Calculates k differently labeled nearest neighbors & distances over some
338 // data points.
339 template<typename MetricType>
340 void Constraints<MetricType>::Impostors(arma::Mat<size_t>& outputNeighbors,
341  arma::mat& outputDistance,
342  const arma::mat& dataset,
343  const arma::Row<size_t>& labels,
344  const arma::vec& norms,
345  const arma::uvec& points,
346  const size_t numPoints)
347 {
348  // Perform pre-calculation. If neccesary.
349  Precalculate(labels);
350 
351  // KNN instance.
352  KNN knn;
353 
354  arma::Mat<size_t> neighbors;
355  arma::mat distances;
356 
357  // Vectors to store indices.
358  arma::uvec subIndexSame;
359 
360  for (size_t i = 0; i < uniqueLabels.n_cols; ++i)
361  {
362  // Calculate impostors.
363  subIndexSame = arma::find(labels.cols(points.head(numPoints)) ==
364  uniqueLabels[i]);
365 
366  // Perform KNN search with differently labeled points as reference
367  // set and same class points as query set.
368  knn.Train(dataset.cols(indexDiff[i]));
369  knn.Search(dataset.cols(points.elem(subIndexSame)),
370  k, neighbors, distances);
371 
372  // Re-order neighbors on the basis of increasing norm in case
373  // of ties among distances.
374  ReorderResults(distances, neighbors, norms);
375 
376  // Re-map neighbors to their index.
377  for (size_t j = 0; j < neighbors.n_elem; ++j)
378  neighbors(j) = indexDiff[i].at(neighbors(j));
379 
380  // Store impostors.
381  outputNeighbors.cols(points.elem(subIndexSame)) = neighbors;
382  outputDistance.cols(points.elem(subIndexSame)) = distances;
383  }
384 }
385 
386 // Generates {data point, target neighbors, impostors} triplets using
387 // TargetNeighbors() and Impostors().
388 template<typename MetricType>
389 void Constraints<MetricType>::Triplets(arma::Mat<size_t>& outputMatrix,
390  const arma::mat& dataset,
391  const arma::Row<size_t>& labels,
392  const arma::vec& norms)
393 {
394  // Perform pre-calculation. If neccesary.
395  Precalculate(labels);
396 
397  size_t N = dataset.n_cols;
398 
399  arma::Mat<size_t> impostors(k, dataset.n_cols);
400  Impostors(impostors, dataset, labels, norms);
401 
402  arma::Mat<size_t> targetNeighbors(k, dataset.n_cols);;
403  TargetNeighbors(targetNeighbors, dataset, labels, norms);
404 
405  outputMatrix = arma::Mat<size_t>(3, k * k * N , arma::fill::zeros);
406 
407  for (size_t i = 0, r = 0; i < N; ++i)
408  {
409  for (size_t j = 0; j < k; ++j)
410  {
411  for (size_t l = 0; l < k; l++, r++)
412  {
413  // Generate triplets.
414  outputMatrix(0, r) = i;
415  outputMatrix(1, r) = targetNeighbors(j, i);
416  outputMatrix(2, r) = impostors(l, i);
417  }
418  }
419  }
420 }
421 
422 template<typename MetricType>
424  const arma::Row<size_t>& labels)
425 {
426  // Make sure the calculation is necessary.
427  if (precalculated)
428  return;
429 
430  uniqueLabels = arma::unique(labels);
431 
432  indexSame.resize(uniqueLabels.n_elem);
433  indexDiff.resize(uniqueLabels.n_elem);
434 
435  for (size_t i = 0; i < uniqueLabels.n_elem; ++i)
436  {
437  // Store same and diff indices.
438  indexSame[i] = arma::find(labels == uniqueLabels[i]);
439  indexDiff[i] = arma::find(labels != uniqueLabels[i]);
440  }
441 
442  precalculated = true;
443 }
444 
445 } // namespace lmnn
446 } // namespace mlpack
447 
448 #endif
static MLPACK_EXPORT util::PrefixedOutStream Fatal
Prints fatal messages prefixed with [FATAL], then terminates the program.
Definition: log.hpp:90
Linear algebra utility functions, generally performed on matrices or vectors.
Definition: cv.hpp:1
void Train(MatType referenceSet)
Set the reference set to a new reference set, and build a tree if necessary.
Definition: neighbor_search_impl.hpp:320
The NeighborSearch class is a template class for performing distance-based neighbor searches...
Definition: neighbor_search.hpp:88
Interface for generating distance based constraints on a given dataset, provided corresponding true l...
Definition: constraints.hpp:31
void Impostors(arma::Mat< size_t > &outputMatrix, const arma::mat &dataset, const arma::Row< size_t > &labels, const arma::vec &norms)
Calculates k differently labeled nearest neighbors for each datapoint and writes them back to passed ...
Definition: constraints_impl.hpp:166
void Triplets(arma::Mat< size_t > &outputMatrix, const arma::mat &dataset, const arma::Row< size_t > &labels, const arma::vec &norms)
Generate triplets {i, j, l} for each datapoint i and writes back generated triplets to matrix passed...
Definition: constraints_impl.hpp:389
Constraints(const arma::mat &dataset, const arma::Row< size_t > &labels, const size_t k)
Constructor for creating a Constraints instance.
Definition: constraints_impl.hpp:22
void TargetNeighbors(arma::Mat< size_t > &outputMatrix, const arma::mat &dataset, const arma::Row< size_t > &labels, const arma::vec &norms)
Calculates k similar labeled nearest neighbors and stores them into the passed matrix.
Definition: constraints_impl.hpp:82
void Search(const MatType &querySet, const size_t k, arma::Mat< size_t > &neighbors, arma::mat &distances)
For each point in the query set, compute the nearest neighbors and store the output in the given matr...
Definition: neighbor_search_impl.hpp:389