# PyTorch Metric Learning for Bird Search, Part 2: Index Embeddings with Qdrant

Author: AlgoFly Team
Published: 2022-10-26T18:46:22Z
Canonical: https://algofly.ai/blog/pytorch-metric-learning-bird-search-part-2

A trained embedding model can turn a bird photograph into a vector, but a useful application needs somewhere to store the reference vectors and a way to retrieve them. In [part two of our PyTorch Metric Learning series](https://www.youtube.com/watch?v=cfhF1mEewbY), we use Qdrant to index labeled images and search them with a new image.


This follows [the training walkthrough in part one](/blog/pytorch-metric-learning-bird-classification-part-1). The model remains responsible for the representation; Qdrant is responsible for storing vectors, associating metadata, and retrieving nearby points. A database cannot repair an embedding that ranks the wrong visual relationships.


## Keep the model and the index separate


The workflow has two paths. An indexing path reads known images, applies the training\-compatible transform, runs the model, and stores vectors with labels. A query path applies the same transform and model to an incoming image, then searches for nearby gallery entries.


The video demonstrates a local Qdrant service, a Python client, and a notebook\-driven indexing loop. The public [repository](https://github.com/imneonizer/pytorch-triplet-loss) includes a Docker Compose definition and a command\-line indexing script as well as the notebook. This makes it possible to build the gallery independently of the visual exploration interface.


## Define what belongs in one collection


![Visual search diagram showing gallery embeddings and a query embedding entering the same Qdrant collection and returning ranked labeled references](/content/videos/pytorch-metric-learning-bird-search-part-2/workflow.svg)

The reference and query paths must use a compatible model and transform.


The example collection is named `bird_species_alexnet_64d`, uses 64\-dimensional vectors, and selects cosine distance. The vector dimension must match the model output. The distance measure should also match how the representation was trained and evaluated.


Dimension alone is not a sufficient compatibility check. Two different checkpoints may both produce 64 numbers but place images in different spaces. Use collection names or metadata that identify the model and preprocessing version. Rebuild a separate gallery when those change, validate it, and then switch the application's query path to the matching version.


For example, a deployment record could contain the following fields. This is an illustrative application manifest, not an additional configuration required by the original notebook:



```
{
  "collection": "birds-alexnet64-v1",
  "embedding_dimensions": 64,
  "distance": "Cosine",
  "model_revision": "reviewed-checkpoint-v1",
  "transform_revision": "rgb-resize128-normalize-v1"
}

```

## Batch the work and retain useful metadata


The repository indexes images in batches and stores the human\-readable class label as point payload. For a gallery that users can inspect, also retain a stable image identifier and the information needed to display or fetch the reference image. A label alone is enough for a small classification demonstration, but it makes error analysis harder.


The original code generates point IDs when inserting embeddings. For repeatable synchronization, prefer an application\-level stable ID derived from the image record, and update that point when its content changes. Otherwise rerunning an append job can add duplicate reference entries rather than replacing the old embedding.


Keep the gallery's provenance clear. Record which dataset split each image came from and which model produced its vector. That makes it possible to build an evaluation filter without guessing from filenames or accidentally searching the same image that was used as a query.


## A nearest neighbor is a candidate answer


![Example bird query image preserved in the project's indexing notebook](/content/videos/pytorch-metric-learning-bird-search-part-2/query-example.png)

A query image from the public indexing notebook. The image is input to search, rather than a measured result by itself.


A query produces one embedding and asks for the closest gallery points. For a simple classifier, the label of the first point becomes the proposed label. For a search application, return several reference images so that the user can inspect the match. Those are different user experiences built on the same representation.


A similarity score is not automatically a calibrated probability that the label is correct. Its distribution changes with the model, gallery, and query population. If the application must reject unrelated images, choose a decision rule using held\-out in\-domain and out\-of\-domain examples. Do not select a universal threshold because one successful query had a high score.


Several neighbors can also reveal ambiguity. A close cluster of different species is useful evidence that the application should be cautious. A confident single\-label interface can hide that uncertainty and make the system appear more precise than the gallery supports.


## Do not let the query find itself


The notebook includes examples of indexing training images and then adding validation images. That can be useful for browsing all data, but it changes how evaluation must work. If a validation query is also in the indexed gallery, exclude its own image ID before calculating retrieval quality.


Exact and near\-duplicate photographs can inflate results even when the filename differs. Keep a reviewed evaluation gallery and query set separate from the convenient all\-images browsing index. Document whether classes overlap between training and evaluation, because known\-class recognition and unseen\-class retrieval answer different questions.


Use a small exact\-search baseline when checking an approximate index. If the exact comparison also gives the wrong neighbors, investigate the representation or preprocessing. If exact comparison is good but indexed results differ, inspect index settings and filtering before retraining the model.


## Make rebuilding deliberate


The original demonstration contains collection recreation steps and an `--overwrite` option in its indexing workflow. Those are convenient in a disposable experiment. In a persistent application, use a new collection for a rebuild and switch only after validating point counts, query behavior, and metadata. A failed reindex should not leave the active gallery empty.


The video dates from 2022, and Qdrant's client interfaces have evolved. Use the current official documentation when adapting its search calls. The enduring pieces are the collection contract, point payload, stable identifiers, and model\-compatible queries; old method names are not the important part of the architecture.


## A reusable pattern for visual search


The same design can support product matching, a visual parts catalog, or an image collection search tool. The hard part is deciding what similarity should mean and curating a gallery that represents it. The database makes the lookup operational; the data and representation make the result useful.


For a newer fine\-tuning example using a vision transformer and Open Metric Learning, see our [image embeddings article](/blog/image-embeddings-similarity-search). It revisits the same separation between model training, reference\-gallery construction, and search evaluation.


## Watch and explore


* [AlgoFly: PyTorch Metric Learning, part two](https://www.youtube.com/watch?v=cfhF1mEewbY)
* [Notebook and indexing scripts](https://github.com/imneonizer/pytorch-triplet-loss)
* [Qdrant local quickstart](https://qdrant.tech/documentation/quickstart/)
* [Qdrant collection concepts](https://qdrant.tech/documentation/manage-data/collections/)


