DL: First Neural Network with TensorFlow/Keras

Writing a neural network in raw PyTorch means a training loop, gradient zeroing, and a pile of boilerplate before your model learns a thing. TensorFlow Keras throws all of that out: you stack a few layers, call compile() and fit(), and training just runs. In this tutorial you build your first Keras network, add callbacks, and even run it on the PyTorch backend, since TensorFlow itself will not install on the newest Python yet.

“The purpose of Keras is to give an unfair advantage to any developer looking to ship ML-powered apps.”

François Chollet, Deep Learning with Python

Last Updated: July 2026 | Tested on: Python 3.14.6, Keras 3.14.1 (PyTorch backend) | Difficulty: Intermediate | Reading Time: 12 minutes

If PyTorch is “NumPy with gradients,” then Keras is “deep learning for humans.” Keras was built from day one for fast experimentation. You define your model in a few lines, compile it with an optimizer and a loss function, and call fit() to train. No hand-written training loop, no gradient zeroing, no boilerplate. Think of it like ordering a meal at a restaurant instead of cooking from scratch: you say what you want, the kitchen handles the steps. Keras is the menu, and the backend (PyTorch, TensorFlow, or JAX) is the kitchen that actually does the work.

Here is the part that surprises people. Since Keras 3 (released late 2023), Keras is no longer tied to TensorFlow. The same Keras code can run on PyTorch, TensorFlow, or JAX, and you pick the backend with one environment variable. That matters a lot right now, because at the time of writing TensorFlow still has no wheel for Python 3.14.6, so a plain import tensorflow fails on the newest Python. The fix is simple: we run Keras 3.14.1 on the PyTorch backend by setting KERAS_BACKEND=torch. Same Keras API, no TensorFlow needed. If you must use TensorFlow itself today, stay on Python 3.13 for now.

TensorFlow led production ML (Machine Learning) from 2015 to 2022 thanks to TensorFlow Serving, TensorFlow Lite, and TFX (TensorFlow Extended) pipelines. PyTorch has since closed most of the gap, but TensorFlow is still a strong pick for mobile and edge deployment and for teams already invested in the Google Cloud ML ecosystem. The good news is that you learn the Keras API once and carry it to any backend.

Here is what we cover:

  • The Keras Sequential API for building networks
  • model.compile(): optimizer, loss, metrics
  • model.fit(): training with callbacks
  • Model evaluation and prediction
  • Keras 3.x multi-backend architecture

Prerequisites

1. Build Modeltf.keras.Sequentialor Functional API2. CompileOptimizer + Loss + Metrics3. Fit / Trainmodel.fit(X, y, epochs)4. Evaluatemodel.evaluate(X_test)5. Predictmodel.predict(new_data)6. Save / Exportmodel.save(‘model.keras’)Python TensorFlow Keras: Model Lifecycle from Build and Compile to Predict and Save

The diagram shows the Keras workflow as a straight line: build the model, compile it, fit it, evaluate it, predict, and save. Keras is the high-level API on top, and a backend engine (PyTorch in this tutorial, since TensorFlow has no Python 3.14.6 wheel yet) handles the real number crunching on Central Processing Unit (CPU) or Graphics Processing Unit (GPU). This split is why Keras code stays so short. You describe what the network looks like, and the backend figures out how to run it. The examples below walk through this exact path with the Iris dataset.

📋 Prerequisites:

The Sequential API: Model in 5 Lines

The Sequential API is the quickest way to build a TensorFlow Keras model. You hand Keras a list of layers, and it wires them up in order, top to bottom. Picture a stack of pancakes: each layer sits on the one below it, and the data flows down through the stack. Each Dense layer is the same idea as PyTorch’s nn.Linear plus an activation. No class definitions, no forward method, just a list.

One thing to notice in the code: the first line sets KERAS_BACKEND to torch before importing keras. That order matters. Keras reads the backend choice at import time, so if you set it afterwards it has no effect. We import keras directly (not tensorflow.keras), because in Keras 3 the top-level keras package is the real API on every backend.

📄 keras_sequential.py: building a model with Keras Sequential

import os
# Pick the backend BEFORE importing keras (TensorFlow has no Python 3.14.6 wheel yet)
os.environ["KERAS_BACKEND"] = "torch"

import numpy as np
from sklearn.datasets import load_iris
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
import keras

# Rahul builds the same Iris classifier as the PyTorch tutorial
iris = load_iris()
X = StandardScaler().fit_transform(iris.data.astype("float32"))
y = iris.target.astype("int64")
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)

model = keras.Sequential([
    keras.Input(shape=(4,)),
    keras.layers.Dense(32, activation="relu"),
    keras.layers.Dense(16, activation="relu"),
    keras.layers.Dense(3, activation="softmax"),
])

model.summary()

▶ Output

Model: "sequential"
┌─────────────────────────────────┬────────────────────────┬───────────────┐
│ Layer (type)                    │ Output Shape           │       Param # │
├─────────────────────────────────┼────────────────────────┼───────────────┤
│ dense (Dense)                   │ (None, 32)             │           160 │
├─────────────────────────────────┼────────────────────────┼───────────────┤
│ dense_1 (Dense)                 │ (None, 16)             │           528 │
├─────────────────────────────────┼────────────────────────┼───────────────┤
│ dense_2 (Dense)                 │ (None, 3)              │            51 │
└─────────────────────────────────┴────────────────────────┴───────────────┘
 Total params: 739 (2.89 KB)
 Trainable params: 739 (2.89 KB)
 Non-trainable params: 0 (0.00 B)

What happened here: Three Dense layers define the same architecture as the PyTorch tutorial. The keras.Input(shape=(4,)) line just tells Keras the input has four features, so it can build the model right away and print the summary. The parameter math is straightforward: each Dense layer has (inputs * units) weights plus one bias per unit. Dense(4 to 32) = 4*32 + 32 = 160, Dense(32 to 16) = 32*16 + 16 = 528, Dense(16 to 3) = 16*3 + 3 = 51. That adds up to 739 trainable parameters.

Activations like relu and softmax add zero parameters, so a PyTorch model with this exact architecture reports 739 too. One real difference: Keras puts softmax inside the model here, while PyTorch usually leaves the last layer as raw logits and lets CrossEntropyLoss apply softmax internally.

Compile and Fit: Training in Two Lines

Two calls do almost everything. compile() picks the training recipe (the optimizer, the loss, the metrics to watch), and fit() runs the whole training loop for you. Picture it like baking: compile() is setting the oven temperature and choosing the recipe, and fit() is sliding the tray in and letting it bake while you wait. We add keras.utils.set_random_seed(42) at the top so the weights start from the same place and your run lands close to the output below.

On the PyTorch backend the last digits still drift a bit between runs, so treat the exact loss values as representative, not as numbers to match to the decimal. The shape is what matters: loss falls steadily, test accuracy hits 100%, and validation accuracy settles around 95.8%.

📄 keras_train.py: compile, fit, evaluate

keras.utils.set_random_seed(42)   # makes the run reproducible

model.compile(
    optimizer="adam",
    loss="sparse_categorical_crossentropy",
    metrics=["accuracy"],
)

# Aditi trains with one function call
history = model.fit(
    X_train, y_train,
    epochs=50,
    batch_size=16,
    validation_split=0.2,
    verbose=0,
)

# Evaluate on test set
test_loss, test_acc = model.evaluate(X_test, y_test, verbose=0)
print(f"Test Loss: {test_loss:.4f}")
print(f"Test Accuracy: {test_acc:.1%}")

# Training history
print(f"\nTraining history (last 5 epochs):")
for i in range(-5, 0):
    print(f"  Epoch {50+i+1}: loss={history.history['loss'][i]:.4f}, "
          f"val_acc={history.history['val_accuracy'][i]:.1%}")

▶ Output

Test Loss: 0.1228
Test Accuracy: 100.0%

Training history (last 5 epochs):
  Epoch 46: loss=0.1812, val_acc=95.8%
  Epoch 47: loss=0.1752, val_acc=95.8%
  Epoch 48: loss=0.1687, val_acc=95.8%
  Epoch 49: loss=0.1635, val_acc=95.8%
  Epoch 50: loss=0.1583, val_acc=95.8%

What happened here: compile() picks the training algorithm. fit() then runs the whole loop in one call: batching, shuffling, the forward pass, the backward pass, the weight updates, and the validation check after each epoch. Compare that to PyTorch, where you write the loop by hand. The val_acc of 95.8% is measured on a small slice of the training data that fit() holds back (validation_split=0.2), so it bounces around more than the final test score. The test set here is tiny (30 Iris samples), which is why test accuracy lands at a clean 100%. Keras trades some control for a lot of convenience. When you do need custom training logic, you write your own loop or subclass keras.Model.

Callbacks: Early Stopping and Model Checkpointing

A callback is a small helper that Keras runs at set points during training, like after every epoch. Think of it as a smoke alarm wired into your training run: it watches a number you care about and acts on its own. Two callbacks show up in almost every real TensorFlow Keras project. EarlyStopping halts training when the model stops improving, and ModelCheckpoint saves the best version to disk as you go.

📄 keras_callbacks.py: using callbacks for better training

# In Keras 3, callbacks live under keras.callbacks (not tensorflow.keras)
from keras.callbacks import EarlyStopping, ModelCheckpoint

keras.utils.set_random_seed(42)

# Aviraj sets up production-ready training
callbacks = [
    EarlyStopping(
        monitor="val_loss",
        patience=10,
        restore_best_weights=True,
    ),
    ModelCheckpoint(
        "best_model.keras",
        monitor="val_accuracy",
        save_best_only=True,
    ),
]

model2 = keras.Sequential([
    keras.Input(shape=(4,)),
    keras.layers.Dense(32, activation="relu"),
    keras.layers.Dense(16, activation="relu"),
    keras.layers.Dense(3, activation="softmax"),
])
model2.compile(optimizer="adam", loss="sparse_categorical_crossentropy", metrics=["accuracy"])

history = model2.fit(
    X_train, y_train,
    epochs=200,  # Set high, early stopping will halt
    batch_size=16,
    validation_split=0.2,
    callbacks=callbacks,
    verbose=0,
)

stopped_epoch = len(history.history["loss"])
print(f"Early stopping triggered at epoch {stopped_epoch}")
print(f"Best val_accuracy: {max(history.history['val_accuracy']):.1%}")

▶ Output

Early stopping triggered at epoch 115
Best val_accuracy: 95.8%

What happened here: EarlyStopping watches the validation loss and stops once it goes 10 epochs (patience=10) with no improvement, which guards against overfitting and wasted time. Here the loss kept creeping down for a while, so training ran to epoch 115 out of the 200 we allowed before the stop kicked in. restore_best_weights=True then rewinds the model to its best epoch. ModelCheckpoint writes the best model to best_model.keras on disk whenever validation accuracy improves. This is the standard production setup: set a high epoch ceiling and let the callbacks decide when to quit, so you always keep the best model even if later epochs get worse.

Common Mistakes

⚠️ Common Mistakes:
  • Using categorical_crossentropy with integer labels: Use sparse_categorical_crossentropy for integer labels (0, 1, 2). Use categorical_crossentropy only with one-hot encoded labels.
  • Forgetting to declare the input shape: Start the model with a keras.Input(shape=(…)) layer so Keras knows the input size up front and can build and summarize the model. The remaining layers infer their shapes automatically.
  • Using softmax with from_logits=True: If your last layer has softmax activation, set from_logits=False in loss. If no activation, set from_logits=True.

Interview Corner

Q: What is Keras 3.x and how does it differ from TF-Keras?

Keras 3.x (released late 2023) is a multi-backend framework that runs on TensorFlow, PyTorch, or JAX. Before that, Keras was glued to TensorFlow as tf.keras. Now you write Keras code once and run it on any backend, so you get Keras simplicity with PyTorch performance or JAX JIT (Just-In-Time) compilation. This is exactly what lets the tutorial above run on Python 3.14.6: we point Keras at the PyTorch backend with KERAS_BACKEND=torch, and the same code that would run on TensorFlow runs unchanged.

Practice Exercises

  1. Build the same Iris classifier in Keras and PyTorch. Compare lines of code and training curves.
  2. Use the Functional API to build a model with two inputs merged together.
  3. Add a LearningRateScheduler callback that reduces the learning rate (LR) by half every 20 epochs.
  4. Train on MNIST with a CNN (Convolutional Neural Network) using Keras Sequential API and reach 99% accuracy.

More in this series:

Frequently Asked Questions

How to install Keras and TensorFlow on Python 3.14.6?

At the time of writing, TensorFlow has no Python 3.14.6 wheel, so pip install tensorflow fails on 3.14. Install Keras with the PyTorch backend instead: pip install keras torch, then set the environment variable KERAS_BACKEND=torch before importing keras. The Keras API is identical on every backend. If you specifically need TensorFlow itself, use Python 3.13 for now.

Should I learn Keras or PyTorch first?

Learn PyTorch if you want to do research or need full control over the training loop. Learn Keras if you want to prototype quickly or deploy to mobile and edge. Everything in this TensorFlow Keras tutorial carries over to either path. As of 2026, PyTorch leads in research and has strong production adoption, while TensorFlow still leads for mobile (TFLite) and the Google Cloud ML ecosystem. With Keras 3 you can get the best of both: write Keras code and run it on a PyTorch backend.

When should I use Sequential vs Functional API in Keras?

Use Sequential for a simple stack of layers with one input and one output. Use the Functional API for models with multiple inputs or outputs, shared layers, or residual connections. Reach for the Functional API for anything more complex than a straight-line stack.

Interview Questions on TensorFlow Keras

These come from real screens and onsites. Practice answering before you read each answer.

Q: What does model.compile() actually do, and why must you call it before fit()?

compile() configures the training process. It attaches the optimizer, the loss function, and the metrics to the model and builds the internal training step. Without it, fit() has no loss to minimize and no optimizer to update the weights, so it raises an error. Think of compile() as loading the recipe before you start cooking.

Q: When do you use sparse_categorical_crossentropy versus categorical_crossentropy?

Use sparse_categorical_crossentropy when your labels are plain integer class indices like 0, 1, 2. Use categorical_crossentropy when your labels are one-hot vectors like [0, 1, 0]. Picking the wrong one throws a shape mismatch error. The Iris example above uses integer labels, so sparse is the correct choice.

Q: Your model trains for 200 epochs, but validation loss starts climbing after epoch 40 while training loss keeps falling. What do you add and how?

That widening gap is classic overfitting. Add an EarlyStopping callback that monitors val_loss with a patience of about 10 and restore_best_weights=True, so training halts after 10 epochs without improvement and rewinds to the best weights. Pair it with ModelCheckpoint using save_best_only=True to keep the best model on disk. You keep the high epoch ceiling and let the callbacks decide when to stop.

Q: A teammate runs your Keras script on Python 3.14.6 and import tensorflow fails, even though the training code is fine. What is the fix?

TensorFlow has no Python 3.14.6 wheel yet, so importing it fails on the newest Python. Because Keras 3 is multi-backend, install keras and torch, then set KERAS_BACKEND=torch before importing keras. The same Keras code runs unchanged on the PyTorch backend. If you specifically need TensorFlow itself, run the script on Python 3.13 for now.

Q: Why must you set the KERAS_BACKEND environment variable before importing keras, not after?

Keras reads the backend choice once, at import time. If you set the variable after import keras has already run, Keras has locked in its default backend and your change is silently ignored. Always put os.environ[“KERAS_BACKEND”] = “torch” on the first lines of the file, above import keras.

Q: What is a callback in Keras, and name two you would use in a real project?

A callback is a small helper that Keras runs at set points during training, such as after each epoch. EarlyStopping stops training when a monitored metric stops improving, and ModelCheckpoint saves the model to disk whenever it gets better. Together they let you automate training decisions without writing a custom loop.

What’s Next?

You have now built and trained a neural network with TensorFlow Keras: the Sequential API to stack layers, compile() to set the training recipe, fit() to run the whole loop, and callbacks like EarlyStopping and ModelCheckpoint to keep the best model. You also saw how Keras 3 lets all of this run on the PyTorch backend even where TensorFlow cannot yet. Next, in Dropout and Batch Normalization, we cover the regularization techniques that keep deep networks from overfitting so you can pick the right one for your projects.

For the full roadmap from Python basics to deep learning, head back to the Python + AI/ML tutorial series home.

Further reading: for the full reference, see TensorFlow documentation.

Previous: Python: PyTorch vs TensorFlow vs JAX, Deep Learning Frameworks Compared

Next: Dropout and Batch Normalization: DL Regularization Explained

Series Home: Python + AI/ML Tutorial Series

RahulAuthor posts

Avatar for Rahul

Rahul is a passionate IT professional who loves to sharing his knowledge with others and inspiring them to expand their technical knowledge. Rahul's current objective is to write informative and easy-to-understand articles to help people avoid day-to-day technical issues altogether. Follow Rahul's blog to stay informed on the latest trends in IT and gain insights into how to tackle complex technical issues. Whether you're a beginner or an expert in the field, Rahul's articles are sure to leave you feeling inspired and informed.

No comment

Leave a Reply

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