Driver FixRecommendedSound, Wi-Fi or graphics acting up? Check drivers firstFind missing or outdated drivers fast.Check DriversOctober DealsAmazon USOctober deal check: compare before you payAmazon US: current deals, useful picks and tech finds.Check DealsPC HealthRecommendedCrashes, freezes, slowdowns? Check your PC nowSpot repairable issues before they interrupt work.Check PC×
Skip to content
World desk7 min

Build a Siamese Network for Image Similarity in Keras

A practical Keras Siamese-network walkthrough: create labeled MNIST pairs, reuse one encoder across both inputs, train with contrastive loss, and choose evaluation methods that match verification or image retrieval.
Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

A Siamese image model learns to place related images near one another in an embedding space. In Keras, the essential pattern is to build one image encoder, call that same encoder for both inputs, and train the resulting pair model with labels that define what “similar” means. The official Keras contrastive-loss example demonstrates the pattern on MNIST digits; below is a runnable adaptation and guidance for using it beyond the demo.

What the Siamese model learns

A Siamese network has two input branches that share weights. Each branch passes an image through the same encoder and produces an embedding vector. A distance function compares those vectors: same-relation pairs are trained to have a small distance, while different-relation pairs are trained to be farther apart.

As an Amazon Associate I earn from qualifying purchases.

As the Keras example puts it, “Siamese Networks are neural networks which share weights between two or more sister networks, each producing embedding vectors of its respective inputs.” This shared encoder is the defining detail: calling two separately initialized encoders would not implement the same design.

What’s actually slowing this PC down?

Pick the symptom - the matching free tool is one click away.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Before writing code, define the positive relation for your application. It might mean the same object, person, product, class, or near-duplicate image. The MNIST example defines a positive pair as two images of the same digit class, not necessarily two pictures of the same physical handwritten digit.

Choose the learning objective and data unit

The Keras examples illustrate three distinct approaches. Choose based on the supervision you have and how you intend to use the embeddings; their code snippets and labels are not interchangeable.

Approach Training data Objective and distinction
Contrastive loss Labeled image pairs Pull positive pairs close and push negative pairs beyond a margin. A direct fit when pair labels are available. See the Keras contrastive example.
Triplet loss Anchor, positive, and negative images Make the anchor closer to the positive than to the negative by a margin. Requires selecting meaningful positives and negatives. See the Keras triplet example.
Batch metric learning Anchor-positive pairs sampled across classes in a batch Uses other examples in the batch within the embedding objective. The Keras example normalizes embeddings and uses dot products for neighbor comparisons. See the Keras metric-learning example.

The contrastive-loss implementation below follows the first approach. It uses the Keras example’s label convention: 0 means a same-class pair, and 1 means a different-class pair. Keep that convention consistent when building labels, defining the loss, and interpreting predictions.

Rank #2
Sale
Hands-On Machine Learning with Scikit-Learn, Keras, and TensorFlow: Concepts, Tools, and Techniques to Build Intelligent Systems
  • Use scikit-learn to track an example ML project end to end
  • Explore several models, including support vector machines, decision trees, random forests, and ensemble methods
  • Exploit unsupervised learning techniques such as dimensionality reduction, clustering, and anomaly detection
  • Dive into neural net architectures, including convolutional nets, recurrent nets, generative adversarial networks, autoencoders, diffusion models, and transformers
  • Use TensorFlow and Keras to build and train neural nets for computer vision, natural language processing, generative models, and deep reinforcement learning

Build and train a contrastive model

Load MNIST and construct pairs

The reference walkthrough uses 28×28 grayscale MNIST images. Its pair builder creates a matching pair and a different-class pair from each source image, and constructs pairs separately from the training, validation, and test partitions. This example uses the training and validation pairs for fitting; hold the test pairs back for final evaluation.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
import numpy as np
import keras
from keras import layers

(x_train, y_train), (x_test, y_test) = keras.datasets.mnist.load_data()

# Add the channel dimension and scale pixels to [0, 1].
x_train = x_train.astype("float32")[..., None] / 255.0
x_test = x_test.astype("float32")[..., None] / 255.0


def make_pairs(images, labels, rng=None):
    """Return image pairs and labels: 0=same class, 1=different class."""
    rng = np.random.default_rng() if rng is None else rng
    by_class = [np.flatnonzero(labels == c) for c in np.unique(labels)]
    pairs, pair_labels = [], []

    for i, image in enumerate(images):
        same_class = by_class[labels[i]]
        positive_index = i
        while positive_index == i:
            positive_index = rng.choice(same_class)
        negative_class = rng.choice([c for c in range(len(by_class)) if c != labels[i]])
        negative_index = rng.choice(by_class[negative_class])

        pairs.append((image, images[positive_index]))
        pair_labels.append(0)
        pairs.append((image, images[negative_index]))
        pair_labels.append(1)

    left = np.asarray([pair[0] for pair in pairs], dtype="float32")
    right = np.asarray([pair[1] for pair in pairs], dtype="float32")
    return (left, right), np.asarray(pair_labels, dtype="float32")


(x_train_left, x_train_right), y_train_pairs = make_pairs(
    x_train, y_train, np.random.default_rng(1)
)
(x_test_left, x_test_right), y_test_pairs = make_pairs(
    x_test, y_test, np.random.default_rng(2)
)

This code assumes the labels index contiguous classes from zero, as MNIST does. For another dataset, adapt class lookup to its labels. If the intended claim is performance on unseen people, products, or other entities, split by entity before generating pairs; otherwise related images can leak across partitions and make evaluation misleading.

Create one shared image encoder

The encoder below follows the small-network spirit of the Keras tutorial: convolution, average pooling, batch normalization, flattening, and a compact embedding. The tutorial’s own embedding is a 10-unit tanh output. This is a teaching baseline for small grayscale digits, not a fixed architecture recommendation for every image domain.

input_shape = (28, 28, 1)
image = keras.Input(shape=input_shape)

x = layers.Conv2D(32, 3, activation="relu")(image)
x = layers.BatchNormalization()(x)
x = layers.AveragePooling2D()(x)
x = layers.Conv2D(64, 3, activation="relu")(x)
x = layers.BatchNormalization()(x)
x = layers.AveragePooling2D()(x)
x = layers.Flatten()(x)
x = layers.Dense(128, activation="relu")(x)
embedding = layers.Dense(10, activation="tanh")(x)
embedding_network = keras.Model(image, embedding, name="embedding_network")

left_input = keras.Input(shape=input_shape, name="left_image")
right_input = keras.Input(shape=input_shape, name="right_image")
left_embedding = embedding_network(left_input)
right_embedding = embedding_network(right_input)

def euclidean_distance(inputs):
    left, right = inputs
    squared_distance = keras.ops.sum(keras.ops.square(left - right), axis=1, keepdims=True)
    return keras.ops.sqrt(keras.ops.maximum(squared_distance, keras.backend.epsilon()))

distance = layers.Lambda(euclidean_distance, name="distance")(
    [left_embedding, right_embedding]
)
pair_model = keras.Model([left_input, right_input], distance, name="siamese_contrastive")

Both branches call embedding_network, so their weights are shared and updated together. The example’s Keras source file shows the corresponding implementation. The tutorial page was created May 6, 2021 and last modified January 28, 2026; it does not pin an installed Keras version or guarantee compatibility with every backend and configuration. Check the current example and your local environment if an API differs.

Apply contrastive loss with the same label convention

With same-class labels set to zero and different-class labels set to one, contrastive loss pulls zero-labeled pairs toward distance zero. It penalizes one-labeled pairs only while their distance remains inside the margin.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
def contrastive_loss(y_true, distance):
    margin = 1.0
    y_true = keras.ops.cast(y_true, distance.dtype)
    similar_cost = (1.0 - y_true) * keras.ops.square(distance)
    dissimilar_cost = y_true * keras.ops.square(
        keras.ops.maximum(margin - distance, 0.0)
    )
    return keras.ops.mean(similar_cost + dissimilar_cost)

pair_model.compile(
    optimizer=keras.optimizers.RMSprop(),
    loss=contrastive_loss,
)

# Reserve validation pairs from the training partition in a real workflow.
pair_model.fit(
    [x_train_left, x_train_right],
    y_train_pairs,
    batch_size=16,
    epochs=10,
    validation_split=0.1,
)

The Keras walkthrough uses margin 1, RMSprop, batch size 16, and 10 epochs as example settings. They are not universal hyperparameter recommendations. For a careful experiment, construct validation pairs explicitly and ensure the validation split reflects the intended deployment task.

Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

Evaluate distances for the actual task

Pair verification needs a validation-selected threshold

The model outputs a distance, not a class probability. For verification—answering whether two images should count as a match—choose a distance threshold using validation data, then apply that fixed rule to held-out test pairs. A distance below the threshold is a match under that rule.

The Keras example’s helper uses 0.5 as an instructional cutoff, with distances above 0.5 treated as dissimilar. That cutoff is not calibrated for other datasets, encoders, or applications. Report test performance with the validation-selected threshold rather than tuning it on the test set.

Retrieval needs ranked-neighbor evaluation

For search or retrieval, compare a query embedding with a gallery of embeddings, rank candidates by distance, and assess whether relevant items appear near the top. Pair accuracy alone does not describe this ranking task. With unit-normalized embeddings, dot products can be used as cosine-similarity scores, as in the Keras metric-learning example.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Keep dataset results in context

The contrastive walkthrough uses MNIST digits; the triplet walkthrough uses the Totally Looks Like dataset, with anchor and visually similar positive image files; the metric-learning example uses CIFAR-10. They are different data setups, not comparable evidence that one method or architecture wins. Evaluate on held-out examples representative of the images, labels, and task you expect in deployment.

For historical context only, FaceNet reported 99.63% on Labeled Faces in the Wild and 95.12% on YouTube Faces DB in its 2015 paper, along with 128-byte face representations. The paper also reported a 30% error-rate reduction against the best published result on both named datasets. Those are results for that paper’s system and protocols, not results from this Keras MNIST tutorial or current records. See the FaceNet paper.

Adapt the pipeline without changing what its labels mean

  • Image shape and preprocessing: MNIST uses one grayscale channel. The triplet example instead decodes three-channel JPEGs, converts values to floating point, resizes to 200×200, and applies ResNet preprocessing. Change the model input shape and preprocessing together when changing datasets; the triplet example illustrates that different path.
  • Pair labels: Keep the documented zero-for-similar and one-for-dissimilar convention if retaining this loss. If you invert labels, rewrite the loss and prediction interpretation accordingly.
  • Sampling: Random pairs can be easy to distinguish. In an applied problem, inspect the examples the model confuses and ensure your pair or triplet construction represents meaningful cases. Triplet learning needs anchor-positive-negative construction; batch metric learning uses examples in a batch differently.
  • Split strategy: Split at the underlying identity or entity level when generalization to unseen entities is the goal, then construct pairs within each partition.
  • Output use: Set a validation threshold for verification or evaluate ranked neighbors for retrieval. Do not treat either metric as a substitute for the other.

Common implementation errors

  • Separate encoders by accident: Two separately constructed models do not share weights. Define one encoder model and invoke it for both inputs.
  • Reversed labels: Under the code above, zero denotes a positive pair and one a negative pair. A common label inversion makes the loss push examples in the wrong direction.
  • Preprocessing mismatch: Training, validation, test, and live inference images must have compatible shape, channel order, value scaling, and resizing.
  • Calling distance a probability: A Euclidean distance has no inherent probability calibration. Establish an operating threshold from validation examples if making binary decisions.
  • Using pair accuracy for search: Retrieval depends on the ordering of a gallery, so evaluate ranked neighbors using held-out data.

Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.

Leave a Reply

Your email address will not be published. Required fields are marked *

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

More from the Wire

  1. World desk4 min
    How to Spot an AI Voice Scam Before Sending MoneyDon’t rely on how a caller sounds. Pause, call back through a known number, and verify the emergency with another trusted person before sending money.
  2. Mountain View desk4 min
    Google’s SynthID Detector: How to Check AI-Generated Images, Video and AudioGoogle’s SynthID Detector looks for an embedded watermark in supported images, video and audio. Here is what its results do—and do not—show.
  3. Redmond desk20 min
    How to create a link to File or Folder in Windows 11Windows 11 gives you several ways to point to a file or folder without moving or duplicating it. You can create a desktop shortcut,…
Recommended PC Tool
Recommended PC Tool
Crashes, No Sound, or Screen Glitches?Free driver scan
Windows Errors? Fix Them Before They SpreadFree repair scan

Two free Windows tools

One Free Minute Could Fix That PC

Before you go - each of these free tools takes about a minute and tackles what quietly slows a Windows PC down.

Special offer. View Outbyte info, uninstall instructions, EULA, and Privacy Policy.