MZQMohd Zamin Quadri
All work

Frameworks compared

A Translation, Not a Race

One model, built in two frameworks.

Role
Project author
Classification
Reproducible experiment
Inspect repository
The five differences between the frameworks and how each was handled. Four were corrected for; the reduction order of the kernels is the floor.Five places where Keras and PyTorch disagree, listed with the reason each difference exists. Four of them cross the frame one at a time into a handled column: weight decay halved to 5e-5 because the two frameworks apply the penalty at different points, OneCycle reimplemented per batch because Keras has none, channel dropout swapped for SpatialDropout2D, and augmentation matched by definition rather than by draw because two random streams cannot be made to produce the same crops. The fifth stays where it is and is underlined in the warning colour: kernel reduction order differs between frameworks and between devices, and is accepted as the floor on any comparison of this kind. The parameter count both models are asserted to have follows, 815,018, checked on every push with no data and no fit, together with what that count does not catch - two dropout rates rather than one, which changes the regularisation and leaves the count unmoved because dropout carries no weights. The sequence closes on the split the comparison runs over and on the two results: PyTorch at 64.26, and an empty dashed slot marked pending where the Keras number would go.
  1. Problem

    Comparisons fail when the models quietly differ.

  2. How it was solved

    I forced identical images and asserted identical parameters.

    1. Same images
    2. Same model
    3. Same run
  3. Result

    815,018

    parameters in both, checked on every push

  4. Built with

    • TensorFlow
    • Keras
    • Docker
    • GitHub Actions
    See the evidence on GitHub
Five places where Keras and PyTorch disagree, listed with the reason each difference exists. Four of them cross the frame one at a time into a handled column: weight decay halved to 5e-5 because the two frameworks apply the penalty at different points, OneCycle reimplemented per batch because Keras has none, channel dropout swapped for SpatialDropout2D, and augmentation matched by definition rather than by draw because two random streams cannot be made to produce the same crops. The fifth stays where it is and is underlined in the warning colour: kernel reduction order differs between frameworks and between devices, and is accepted as the floor on any comparison of this kind. The parameter count both models are asserted to have follows, 815,018, checked on every push with no data and no fit, together with what that count does not catch - two dropout rates rather than one, which changes the regularisation and leaves the count unmoved because dropout carries no weights. The sequence closes on the split the comparison runs over and on the two results: PyTorch at 64.26, and an empty dashed slot marked pending where the Keras number would go.

Reproducible experiment / Frameworks

Rebuilding a model in a second framework is easy to do loosely. The useful question is which differences between the two cannot be removed at all, and what each one costs.

Both models, asserted815,018 parameters

One split, two frameworks, and one difference that stays

read off the designexpected countcannot be corrected forSplit exportedProduced by running torch. Theother side reads it rather…Architecture translatedRebuilt layer for layer in thesecond framework.Count derived by handFrom the architecture, notfrom either framework's…Differences correctedInitialisers, epsilon, paddingand ordering, each handled…Asserted every pushA silent change in eitherframework fails the build…The irreducible oneKernel reduction order. Not adefect, and not removable.
The split is exported from the PyTorch run rather than rebuilt, so the two sides are provably reading the same data. Four framework differences are corrected for; the fifth is the floor.

This diagram in words

  1. Split exported source — Produced by running torch. The other side reads it rather than rebuilding it.
    NumPy
  2. Architecture translated process — Rebuilt layer for layer in the second framework.
    Keras
  3. Count derived by hand artifact — From the architecture, not from either framework's summary.
  4. Differences corrected process — Initialisers, epsilon, padding and ordering, each handled explicitly.
    TensorFlow
  5. Asserted every push gate — A silent change in either framework fails the build rather than the comparison.
    GitHub Actions
  6. The irreducible one artifact — Kernel reduction order. Not a defect, and not removable.

01 / 7Two frameworks, one architectureThe same network written twice, to find out what writing it twice actually costs.

Trainable parameters
815,018
Asserted
On every push, with no data and no fit
Subset
13,500 train, 1,500 validation
Epochs
12
Five places where Keras and PyTorch disagree, listed with the reason each difference exists. Four of them cross the frame one at a time into a handled column: weight decay halved to 5e-5 because the two frameworks apply the penalty at different points, OneCycle reimplemented per batch because Keras has none, channel dropout swapped for SpatialDropout2D, and augmentation matched by definition rather than by draw because two random streams cannot be made to produce the same crops. The fifth stays where it is and is underlined in the warning colour: kernel reduction order differs between frameworks and between devices, and is accepted as the floor on any comparison of this kind. The parameter count both models are asserted to have follows, 815,018, checked on every push with no data and no fit, together with what that count does not catch - two dropout rates rather than one, which changes the regularisation and leaves the count unmoved because dropout carries no weights. The sequence closes on the split the comparison runs over and on the two results: PyTorch at 64.26, and an empty dashed slot marked pending where the Keras number would go.
Read the full background

Problem

Why this work exists

A framework comparison is worth nothing if the two models see different images or are not the same model. Both are easy to get wrong quietly: two random number streams cannot be made to draw the same augmentations, and two block diagrams can agree while the models underneath them do not.

Contribution

What I can claim

Exported the exact training indices by running torch rather than reproducing them in NumPy, translated the architecture with the parameter count derived by hand and asserted in CI, and enumerated the five differences between the frameworks with the correction each one needed.

Method

Making two frameworks answerable to the same number

Comparing two deep-learning frameworks is usually a comparison of two different experiments wearing the same name.

  1. Fix

    The data split is exported by running one framework and then reused by the other.

  2. Translate

    The architecture is rebuilt rather than ported.

  3. Derive

    The parameter count is worked out by hand from the layer definitions, so there is a number to be wrong about that does not come from either framework.

  4. Correct

    Each known difference between the two is applied deliberately.

  5. Assert

    The count is then asserted on every push, so the two models cannot silently diverge later.

  6. What this does not show

    No accuracy comparison is published, because the second run has not been made: the results directory is empty and the repository's own table records it as pending. What is established is that the two models are the same model, which is the precondition for a comparison rather than the comparison itself.

How it was built

System

Workflow and decisions

The architecture, the split and the parameter count are three separate artefacts that have to agree. The count is checked without training, the split is checked before training, and the training script refuses to run if either disagrees.

  1. 1Export the split by running torch
  2. 2Translate the architecture
  3. 3Derive the parameter count by hand
  4. 4Correct for each framework difference
  5. 5Assert the count on every push
  • TensorFlow
  • Keras
  • NumPy
  • Docker
  • GitHub Actions

Evidence

What is actually versioned

Both models, asserted815,018 parameters

Derived by hand per layer and asserted on every push by a check that needs no data, no compile and no fit, because the training runs elsewhere and the number was otherwise only as true as somebody's memory of the last run.

The same images15,000 indices

13,500 train and 1,500 validation, exported by running torch rather than reproduced in NumPy, because torch's random stream cannot be replicated there and guessing would have made the comparison meaningless without saying so.

Differences enumerated4 of 5 corrected

Weight decay, the learning-rate schedule, channel dropout and the augmentation definitions were each corrected for. The reduction order of the kernels is accepted as the irreducible floor on any comparison of this kind.

How the work is checked

Quality controls

How the work is checked

  • The parameter count is asserted rather than stated, by a check that runs on every push in seconds
  • The split travels as exported indices, so the two frameworks cannot quietly see different images
  • Weight decay is halved for Keras, because the penalty enters the loss there rather than the gradient and its derivative carries a factor of two
  • The two dropout rates are kept as separate constants, because the count cannot catch them being confused
  • CIFAR-10 itself is never committed; only the index file travels

Limitations

Where the evidence stops

  • The Keras run has not been made. The repository's results directory is empty and its own table records the Keras accuracy as pending, so no comparison figure is published here
  • Even with every correction applied, kernel reduction orders differ between frameworks and devices, so an exact match was never available
  • The parameter count is a floor, not a proof: it catches a wrong channel width or a missing bias, and it cannot catch the two dropout rates being confused, which is the worked example in the repository
  • One architecture on one subset of one dataset, which says nothing general about either framework
What this changed in my practice

What this changed in my practice

The assertion that protects the comparison also shows its limit. The reference uses 0.25 in the conv blocks and 0.5 in the classifier head. Reusing the conv rate halves the regularisation in the head, and because dropout carries no weights the count still lands on 815,018. So the count is where the checking starts rather than where it ends.

Next evidence milestone: Run it. Everything needed for the comparison is in place and the one thing missing is the Keras number, which has to come from a GPU rather than from this repository's CI.