# How to Cache Face Encodings for Faster Lookups in Python

> Speed up face recognition lookups in Python by caching face encodings. Compute vectors once, serialize with pickle, and load at startup to avoid recomputation.

- Repository: [Adam Geitgey/face_recognition](https://github.com/ageitgey/face_recognition)
- Tags: performance
- Published: 2026-03-06

---

**Cache face encodings by computing 128-dimensional vectors once, serializing them with pickle, and loading them at startup to avoid expensive neural network recomputation.**

When building face recognition systems with the `ageitgey/face_recognition` library, the biggest performance bottleneck is computing **face encodings**—the 128-dimensional vectors that represent facial features. This step runs a deep neural network via `dlib` and becomes prohibitively slow when processing large known-face databases. By caching these encodings to disk, you transform a computationally expensive operation into a fast NumPy array comparison.

## Why Cache Face Encodings?

The `face_recognition` library generates encodings through the `face_encodings()` function in [`face_recognition/api.py`](https://github.com/ageitgey/face_recognition/blob/main/face_recognition/api.py) (lines 203–215). This function invokes a `dlib` face recognition model that performs forward propagation through a deep neural network to produce a 128-dimensional vector for each detected face.

When you need to identify faces against a database of known individuals, recomputing these vectors for every lookup creates O(n) neural network invocations. By computing each known person's encoding once and persisting it, you reduce subsequent lookups to vectorized NumPy operations using `face_distance()` (lines 63–75 in [`api.py`](https://github.com/ageitgey/face_recognition/blob/main/api.py)), which calculates Euclidean distance via `np.linalg.norm`.

## Step-by-Step: How to Cache Face Encodings

### Generate Encodings Once

Process your training images through `face_encodings()` to extract 128-dimensional vectors. This function accepts an image array and optional face locations, returning a list of encodings.

```python
import face_recognition

image = face_recognition.load_image_file("person.jpg")
face_locations = face_recognition.face_locations(image)
encodings = face_recognition.face_encodings(image, known_face_locations=face_locations)

```

### Serialize with Pickle

Persist the encodings using Python's standard `pickle` module or `numpy.save`. The KNN example in [`examples/face_recognition_knn.py`](https://github.com/ageitgey/face_recognition/blob/main/examples/face_recognition_knn.py) (lines 104–107) demonstrates this pattern by saving a trained classifier to disk.

```python
import pickle

# Save encodings dictionary: {name: [encoding, ...]}

with open("known_faces_cache.pkl", "wb") as f:
    pickle.dump(known_encodings_dict, f)

```

### Load at Startup

At application initialization, deserialize the cache into memory. This makes the encoding database available for instant comparison without neural network overhead. The KNN example shows the loading pattern at lines 130–132.

```python
def load_cache(cache_path="known_faces_cache.pkl"):
    if os.path.exists(cache_path):
        with open(cache_path, "rb") as f:
            return pickle.load(f)
    return {}

```

### Perform Fast Comparisons

With cached encodings loaded, use `face_distance()` to compute Euclidean distances between the unknown encoding and all known vectors. This function (lines 63–75 in [`api.py`](https://github.com/ageitgey/face_recognition/blob/main/api.py)) performs a vectorized NumPy operation that is orders of magnitude faster than neural network inference.

```python
import face_recognition

# known_encodings: list of 128-dim vectors from cache

# unknown_encoding: single 128-dim vector from new image

distances = face_recognition.face_distance(known_encodings, unknown_encoding)
best_match_index = distances.argmin()
if distances[best_match_index] < 0.6:
    name = known_names[best_match_index]

```

## Complete Working Example

This script implements a complete caching workflow, mirroring the patterns found in [`examples/face_recognition_knn.py`](https://github.com/ageitgey/face_recognition/blob/main/examples/face_recognition_knn.py) and the core API implementation in [`face_recognition/api.py`](https://github.com/ageitgey/face_recognition/blob/main/face_recognition/api.py).

```python
import os
import pickle
import pathlib
import face_recognition

CACHE_PATH = pathlib.Path("known_faces_cache.pkl")

def build_cache(known_dir):
    """
    Scan directory where each subfolder is a person.
    Compute one encoding per image and store dict:
    {person_name: [encoding, ...]}
    """
    cache = {}
    for person in os.listdir(known_dir):
        person_path = os.path.join(known_dir, person)
        if not os.path.isdir(person_path):
            continue
        
        encodings = []
        for img_file in os.listdir(person_path):
            if img_file.startswith("."):
                continue
            img_path = os.path.join(person_path, img_file)
            img = face_recognition.load_image_file(img_path)
            faces = face_recognition.face_locations(img)
            
            if len(faces) == 1:
                enc = face_recognition.face_encodings(img, known_face_locations=faces)[0]
                encodings.append(enc)
        
        if encodings:
            cache[person] = encodings
    
    with open(CACHE_PATH, "wb") as f:
        pickle.dump(cache, f)
    print(f"Cache built with {len(cache)} people")

def load_cache():
    """Return dict {person: [encodings]} from disk, or empty dict."""
    if CACHE_PATH.is_file():
        with open(CACHE_PATH, "rb") as f:
            return pickle.load(f)
    return {}

def find_match(unknown_img_path, cache, tolerance=0.6):
    """
    Encode unknown image and compare against cached encodings.
    Returns (person_name, distance) or ('unknown', None).
    """
    img = face_recognition.load_image_file(unknown_img_path)
    faces = face_recognition.face_locations(img)
    if not faces:
        return None
    
    unknown_enc = face_recognition.face_encodings(img, known_face_locations=faces)[0]
    best_match = ("unknown", None)
    
    for name, enc_list in cache.items():
        distances = face_recognition.face_distance(enc_list, unknown_enc)
        min_dist = distances.min()
        if min_dist <= tolerance and (best_match[1] is None or min_dist < best_match[1]):
            best_match = (name, float(min_dist))
    
    return best_match

# Example usage

if __name__ == "__main__":
    known_dir = "knn_examples/train"
    if not CACHE_PATH.is_file():
        build_cache(known_dir)
    
    cache = load_cache()
    result = find_match("test_image.jpg", cache)
    print(f"Match: {result}")

```

## Key Source Files and Implementation Details

Understanding the underlying implementation helps optimize your caching strategy:

- **[`face_recognition/api.py`](https://github.com/ageitgey/face_recognition/blob/main/face_recognition/api.py)** (lines 203–215): Contains `face_encodings()`, which converts face images into 128-dimensional vectors using the `dlib` face recognition model. This is the expensive operation to cache.

- **[`face_recognition/api.py`](https://github.com/ageitgey/face_recognition/blob/main/face_recognition/api.py)** (lines 63–75): Implements `face_distance()`, which computes Euclidean distance between encodings using `np.linalg.norm`. This is the fast operation you want to perform on cached data.

- **[`face_recognition/api.py`](https://github.com/ageitgey/face_recognition/blob/main/face_recognition/api.py)** (lines 217–226): Contains `compare_faces()`, a convenience wrapper around `face_distance()` that applies a tolerance threshold.

- **[`examples/face_recognition_knn.py`](https://github.com/ageitgey/face_recognition/blob/main/examples/face_recognition_knn.py)** (lines 104–107 and 130–132): Demonstrates the pickle serialization pattern for persisting trained models, which applies directly to encoding caches.

- **[`examples/web_service_example.py`](https://github.com/ageitgey/face_recognition/blob/main/examples/web_service_example.py)**: Shows real-time usage where pre-computed encodings enable instant comparison against uploaded images.

## Summary

- **Face encodings** are 128-dimensional vectors generated by expensive neural network inference in `dlib` via `face_recognition.face_encodings()`.
- **Cache these vectors** to disk using `pickle` or `numpy.save` after the initial computation to avoid redundant processing.
- **Load encodings at startup** to enable fast, vectorized distance calculations using `face_recognition.face_distance()`, which performs simple NumPy operations rather than neural network inference.
- **Invalidate caches** by regenerating the pickle file when adding or removing known individuals from your database.
- **Reference implementation** patterns exist in [`examples/face_recognition_knn.py`](https://github.com/ageitgey/face_recognition/blob/main/examples/face_recognition_knn.py) and the core API in [`face_recognition/api.py`](https://github.com/ageitgey/face_recognition/blob/main/face_recognition/api.py).

## Frequently Asked Questions

### How much faster is cached face recognition compared to real-time encoding?

Cached face recognition is orders of magnitude faster. Computing a face encoding requires running a deep neural network through `dlib`, which takes hundreds of milliseconds per face. Once cached, `face_recognition.face_distance()` performs a vectorized NumPy Euclidean distance calculation across thousands of encodings in microseconds.

### What is the best file format for caching face encodings?

Python's standard `pickle` module is the most common approach, as demonstrated in [`examples/face_recognition_knn.py`](https://github.com/ageitgey/face_recognition/blob/main/examples/face_recognition_knn.py) (lines 104–107). Alternatively, use `numpy.save()` or `numpy.savez()` to store the 128-dimensional arrays if you need cross-language compatibility. Both methods preserve the exact floating-point precision required for accurate face matching.

### How do I handle cache invalidation when adding new people?

When your known-face database changes, regenerate the cache by re-running your encoding script against the updated directory structure. Delete the old pickle file and rebuild it using `face_recognition.face_encodings()` for the new images. This follows the same pattern as the KNN example's training flow, where the model is retrained and resaved when the training directory changes.

### Can I cache unknown face encodings during runtime?

Yes, for long-running services processing many images, cache unknown face encodings using `functools.lru_cache` or a custom dictionary keyed by image hash. This prevents recomputing encodings for identical images submitted multiple times. However, ensure the cache has size limits to prevent memory exhaustion in high-throughput applications.