FAISS with C++ indexing 512D vectors

Viewed 54

I have a collection of 512D std::vector to store face embeddings. I create my index and perform training on a subset of the data.

int d = 512;

size_t nb = this->templates.size()  // 95000

size_t nt = 50000; // training data size

std::vector<float> training_set(nt * d);

faiss::IndexFlatIP coarse_quantizer(d);

int ncentroids = int(4 * sqrt(nb)));

faiss::IndexIVFPQ index(&coarse_quantizer,d,ncentroids,4,8);

std::vector<float> training_set(nt*d);

The this->templates has an index value in [0] and the 512D vectors in [1]. My question is about the training and indexing. I have this currently:

int v=0;
for (auto const& element : this->templates)
{
   std::vector<double> enrollment_template = element.second;
   for (int i=0;i<d;i++){
     training_set[(v*d)+i] = (float)enrollment_template.at(i);

     v++;
}

index.train(nt,training_set.data());

FAISS Index.Train function

virtual void train(idx_t n, const float *x)
Perform training on a representative set of vectors

Parameters:
n – nb of training vectors

x – training vecors, size n * d

Is that the proper way of adding the 512D vector data into Faiss for training? It seems to me that if I have 2 face embeddings that are 512D in size, the training_set would be like this:

training_set[0-511] - Face #1's 512D vectors training_set[512-1024] - Face #2's 512D vectors

and since Faiss knows we are working with 512D vectors, it will intelligently parse them out of the array.

1 Answers

Here's a more efficient way to write it:

int v = 0;
for (auto const& element : this->templates)
{
    auto& enrollment_template = element.second; // not copy
    if (v + d > training_set.size()) {
        break; // prevent overflow, "nt" is smaller than templates.size()
    }
    for (int i = 0; i < d; i++) {
        training_set[v] = enrollment_template[i]; // not at()
        v++;
    }
}

We avoid a copy with auto& enrollment_template, avoid extra branching with enrollment_template[i] (you know you won't be out of bounds), and simplify the address computation with training_set[v] by making v a count of elements rather than rows.

Further efficiency could be gained if templates can be changed to store floats rather than doubles--then you'd just be bitwise-copying 512 floats rather than converting doubles to floats.

Also, be sure to declare d as constexpr to give the compiler the best chance of optimizing the loop.

Related