Menu

© 2026 Furkanul Islam

}
{
</>

Getting Started with TensorFlow for Deep Learning: A Practical Guide

A comprehensive introduction to building neural networks with TensorFlow 2 and Keras. Learn deep learning fundamentals, model architecture, training best practices, and deployment strategies with real-world examples.

TensorFlow 2 revolutionized deep learning by making it more accessible while maintaining the power needed for production systems. In this guide, I’ll walk you through everything you need to start building neural networks, from basic concepts to deployment strategies.

Why TensorFlow 2 Changed the Game

When TensorFlow 2.0 was released in 2019, it addressed the biggest criticisms of the original:

TensorFlow 1.xTensorFlow 2.x
Complex graph sessionsEager execution by default
Verbose Keras integrationKeras as official API
Steep learning curvePythonic and intuitive
Hard to debugStandard Python debugging

Key Advantages for Deep Learning

  1. Production-ready deployment: TensorFlow Serving, TFLite, TensorFlow.js
  2. Cross-platform support: Mobile, web, edge devices, cloud
  3. Strong ecosystem: TensorBoard, TensorFlow Hub, TensorFlow Extended (TFX)
  4. Google backing: Continuous development and enterprise support
  5. Keras integration: High-level API for rapid prototyping

Setting Up Your Environment

# Install TensorFlow with GPU support
pip install tensorflow[and-cuda]

# Or CPU-only version
pip install tensorflow

# Additional useful packages
pip install tensorflow-hub tensorflow-datasets matplotlib scikit-learn

Verify installation:

import tensorflow as tf

print(f"TensorFlow version: {tf.__version__}")
print(f"GPU Available: {tf.config.list_physical_devices('GPU')}")
print(f"Eager execution: {tf.executing_eagerly()}")

Building Your First Neural Network

Let’s build a complete image classifier step by step.

Step 1: Load and Prepare Data

import tensorflow as tf
from tensorflow import keras
import numpy as np
import matplotlib.pyplot as plt

# Load Fashion MNIST dataset
fashion_mnist = keras.datasets.fashion_mnist
(train_images, train_labels), (test_images, test_labels) = fashion_mnist.load_data()

# Class names
class_names = ['T-shirt/top', 'Trouser', 'Pullover', 'Dress', 'Coat',
               'Sandal', 'Shirt', 'Sneaker', 'Bag', 'Ankle boot']

# Explore data
print(f"Training data shape: {train_images.shape}")  # (60000, 28, 28)
print(f"Test data shape: {test_images.shape}")  # (10000, 28, 28)

# Preprocess - normalize pixel values to [0, 1]
train_images = train_images.astype('float32') / 255.0
test_images = test_images.astype('float32') / 255.0

# Reshape for CNN input (batch_size, height, width, channels)
train_images = train_images.reshape(-1, 28, 28, 1)
test_images = test_images.reshape(-1, 28, 28, 1)

Step 2: Build Model Architecture

from tensorflow.keras import layers, models

model = models.Sequential([
    # First Convolutional Block
    layers.Conv2D(32, (3, 3), activation='relu', padding='same', input_shape=(28, 28, 1)),
    layers.BatchNormalization(),
    layers.Conv2D(32, (3, 3), activation='relu', padding='same'),
    layers.BatchNormalization(),
    layers.MaxPooling2D((2, 2)),
    layers.Dropout(0.25),

    # Second Convolutional Block
    layers.Conv2D(64, (3, 3), activation='relu', padding='same'),
    layers.BatchNormalization(),
    layers.Conv2D(64, (3, 3), activation='relu', padding='same'),
    layers.BatchNormalization(),
    layers.MaxPooling2D((2, 2)),
    layers.Dropout(0.25),

    # Fully Connected Layers
    layers.Flatten(),
    layers.Dense(128, activation='relu'),
    layers.BatchNormalization(),
    layers.Dropout(0.5),

    # Output Layer
    layers.Dense(10, activation='softmax')
])

model.summary()

Step 3: Compile the Model

model.compile(
    optimizer=keras.optimizers.Adam(learning_rate=0.001),
    loss='sparse_categorical_crossentropy',
    metrics=['accuracy', keras.metrics.Precision(), keras.metrics.Recall()]
)

# Learning rate scheduling
lr_scheduler = keras.callbacks.ReduceLROnPlateau(
    monitor='val_loss',
    factor=0.5,
    patience=3,
    min_lr=1e-7,
    verbose=1
)

Step 4: Train with Validation

# Data augmentation for better generalization
data_augmentation = keras.Sequential([
    layers.RandomRotation(0.1),
    layers.RandomZoom(0.1),
    layers.RandomTranslation(0.1, 0.1),
])

# Early stopping to prevent overfitting
early_stopping = keras.callbacks.EarlyStopping(
    monitor='val_loss',
    patience=10,
    restore_best_weights=True,
    verbose=1
)

# Train the model
history = model.fit(
    data_augmentation(train_images, training=True),
    train_labels,
    epochs=50,
    batch_size=64,
    validation_split=0.2,
    callbacks=[lr_scheduler, early_stopping],
    verbose=1
)

Step 5: Evaluate and Visualize

# Evaluate on test set
test_loss, test_acc, test_prec, test_rec = model.evaluate(test_images, test_labels, verbose=0)
print(f"Test accuracy: {test_acc:.4f}")
print(f"Test precision: {test_prec:.4f}")
print(f"Test recall: {test_rec:.4f}")

# Plot training history
plt.figure(figsize=(12, 4))

plt.subplot(1, 2, 1)
plt.plot(history.history['accuracy'], label='Train Acc')
plt.plot(history.history['val_accuracy'], label='Val Acc')
plt.xlabel('Epoch')
plt.ylabel('Accuracy')
plt.legend()
plt.title('Accuracy Over Epochs')

plt.subplot(1, 2, 2)
plt.plot(history.history['loss'], label='Train Loss')
plt.plot(history.history['val_loss'], label='Val Loss')
plt.xlabel('Epoch')
plt.ylabel('Loss')
plt.legend()
plt.title('Loss Over Epochs')

plt.tight_layout()
plt.show()

# Confusion matrix
from sklearn.metrics import confusion_matrix, classification_report
import seaborn as sns

predictions = model.predict(test_images)
predicted_labels = np.argmax(predictions, axis=1)

cm = confusion_matrix(test_labels, predicted_labels)
plt.figure(figsize=(10, 8))
sns.heatmap(cm, annot=True, fmt='d', cmap='Blues',
            xticklabels=class_names, yticklabels=class_names)
plt.xlabel('Predicted')
plt.ylabel('True')
plt.title('Confusion Matrix')
plt.show()

print(classification_report(test_labels, predicted_labels, target_names=class_names))

Advanced Model Architectures

Functional API for Complex Models

from tensorflow.keras import layers, models, Input

# Multi-input model
input_image = Input(shape=(28, 28, 1), name='image_input')
input_metadata = Input(shape=(5,), name='metadata_input')

# Image branch
x = layers.Conv2D(32, (3, 3), activation='relu')(input_image)
x = layers.MaxPooling2D((2, 2))(x)
x = layers.Conv2D(64, (3, 3), activation='relu')(x)
x = layers.GlobalAveragePooling2D()(x)
image_features = layers.Dense(64, activation='relu')(x)

# Metadata branch
y = layers.Dense(32, activation='relu')(input_metadata)
y = layers.Dropout(0.3)(y)

# Combine branches
combined = layers.concatenate([image_features, y])
z = layers.Dense(128, activation='relu')(combined)
z = layers.Dropout(0.5)(z)
output = layers.Dense(10, activation='softmax')(z)

model = models.Model(inputs=[input_image, input_metadata], outputs=output)
model.compile(optimizer='adam', loss='sparse_categorical_crossentropy', metrics=['accuracy'])

Transfer Learning with Pre-trained Models

# Load pre-trained base model (exclude top classification layers)
base_model = keras.applications.MobileNetV2(
    input_shape=(224, 224, 3),
    include_top=False,
    weights='imagenet'
)

# Freeze base model layers
base_model.trainable = False

# Build new model on top
inputs = keras.Input(shape=(224, 224, 3))
x = keras.applications.mobilenet_v2.preprocess_input(inputs)
x = base_model(x, training=False)
x = layers.GlobalAveragePooling2D()(x)
x = layers.BatchNormalization()(x)
x = layers.Dense(128, activation='relu')(x)
x = layers.Dropout(0.5)(x)
outputs = layers.Dense(num_classes, activation='softmax')(x)

model = keras.Model(inputs, outputs)

# Compile with higher learning rate for new layers
model.compile(
    optimizer=keras.optimizers.Adam(0.001),
    loss='sparse_categorical_crossentropy',
    metrics=['accuracy']
)

# Train top layers first
history = model.fit(train_data, train_labels, epochs=10, validation_data=val_data)

# Fine-tune: unfreeze some base layers and train with lower learning rate
base_model.trainable = True
for layer in base_model.layers[:100]:
    layer.trainable = False

model.compile(
    optimizer=keras.optimizers.Adam(1e-5),
    loss='sparse_categorical_crossentropy',
    metrics=['accuracy']
)

# Fine-tune
history_fine = model.fit(train_data, train_labels, epochs=20, validation_data=val_data)

Custom Model with Subclassing

class ResidualBlock(layers.Layer):
    """Custom residual block for deeper networks."""

    def __init__(self, filters, kernel_size=3, stride=1, **kwargs):
        super().__init__(**kwargs)
        self.conv1 = layers.Conv2D(filters, kernel_size, strides=stride,
                                   padding='same', use_bias=False)
        self.bn1 = layers.BatchNormalization()
        self.conv2 = layers.Conv2D(filters, kernel_size, strides=1,
                                   padding='same', use_bias=False)
        self.bn2 = layers.BatchNormalization()

        # Shortcut connection
        if stride != 1:
            self.shortcut = layers.Conv2D(filters, 1, strides=stride,
                                          padding='same', use_bias=False)
            self.shortcut_bn = layers.BatchNormalization()
        else:
            self.shortcut = None

    def call(self, inputs, training=False):
        x = self.conv1(inputs)
        x = self.bn1(x, training=training)
        x = tf.nn.relu(x)

        x = self.conv2(x)
        x = self.bn2(x, training=training)

        if self.shortcut is not None:
            shortcut = self.shortcut(inputs)
            shortcut = self.shortcut_bn(shortcut, training=training)
        else:
            shortcut = inputs

        x = x + shortcut
        return tf.nn.relu(x)

class CustomResNet(keras.Model):
    def __init__(self, num_classes=10):
        super().__init__()
        self.conv_initial = layers.Conv2D(64, 7, strides=2, padding='same')
        self.bn_initial = layers.BatchNormalization()
        self.maxpool = layers.MaxPooling2D(3, strides=2, padding='same')

        self.res_block1 = ResidualBlock(64)
        self.res_block2 = ResidualBlock(128, stride=2)
        self.res_block3 = ResidualBlock(256, stride=2)

        self.global_avg_pool = layers.GlobalAveragePooling2D()
        self.dense = layers.Dense(num_classes, activation='softmax')

    def call(self, inputs, training=False):
        x = self.conv_initial(inputs)
        x = self.bn_initial(x, training=training)
        x = tf.nn.relu(x)
        x = self.maxpool(x)

        x = self.res_block1(x, training=training)
        x = self.res_block2(x, training=training)
        x = self.res_block3(x, training=training)

        x = self.global_avg_pool(x)
        return self.dense(x)

# Usage
model = CustomResNet(num_classes=10)
model.compile(optimizer='adam', loss='sparse_categorical_crossentropy', metrics=['accuracy'])

Training Best Practices

1. Learning Rate Strategies

# Learning rate warmup and decay
def lr_warmup_decay(initial_lr, warmup_epochs, decay_rate, decay_steps):
    def schedule(epoch):
        if epoch < warmup_epochs:
            # Linear warmup
            return initial_lr * (epoch + 1) / warmup_epochs
        else:
            # Exponential decay
            return initial_lr * decay_rate ** ((epoch - warmup_epochs) / decay_steps)
    return schedule

lr_callback = keras.callbacks.LearningRateScheduler(
    lr_warmup_decay(initial_lr=0.01, warmup_epochs=5, decay_rate=0.95, decay_steps=10)
)

2. Callbacks for Better Training

callbacks = [
    # Early stopping
    keras.callbacks.EarlyStopping(
        monitor='val_loss',
        patience=15,
        restore_best_weights=True,
        verbose=1
    ),

    # Model checkpointing
    keras.callbacks.ModelCheckpoint(
        filepath='best_model.h5',
        monitor='val_accuracy',
        save_best_only=True,
        save_weights_only=False,
        mode='max',
        verbose=1
    ),

    # TensorBoard logging
    keras.callbacks.TensorBoard(
        log_dir='./logs',
        histogram_freq=1,
        write_graph=True,
        write_images=True,
        embeddings_freq=0
    ),

    # CSV logging
    keras.callbacks.CSVLogger('training_history.csv'),
]

3. Handling Class Imbalance

from sklearn.utils import class_weight

# Calculate class weights
class_weights = class_weight.compute_class_weight(
    'balanced',
    classes=np.unique(train_labels),
    y=train_labels
)
class_weight_dict = dict(enumerate(class_weights))

# Train with class weights
model.fit(
    train_images, train_labels,
    class_weight=class_weight_dict,
    epochs=50,
    validation_data=(val_images, val_labels)
)

# Or use sample weights
sample_weights = np.ones(len(train_labels))
for i, label in enumerate(train_labels):
    sample_weights[i] = class_weight_dict[label]

model.fit(train_images, train_labels, sample_weight=sample_weights)

4. Mixed Precision Training (GPU)

from tensorflow.keras import mixed_precision

# Enable mixed precision
mixed_precision.set_global_policy('mixed_float16')

# Build model with proper dtype
inputs = keras.Input(shape=(224, 224, 3))
x = layers.Conv2D(64, 3, activation='relu')(inputs)
# ... rest of model

# Use float32 for output layer
outputs = layers.Dense(num_classes, activation='softmax', dtype='float32')(x)

model = keras.Model(inputs, outputs)

Monitoring with TensorBoard

# Launch TensorBoard
# Run in terminal: tensorboard --logdir=./logs

# View in notebook
%load_ext tensorboard
%tensorboard --logdir=./logs

# Custom scalar logging
class CustomCallback(keras.callbacks.Callback):
    def on_epoch_end(self, epoch, logs=None):
        tf.summary.scalar('custom_metric', logs['accuracy'], step=epoch)
        tf.summary.scalar('learning_rate', self.model.optimizer.lr, step=epoch)

with tf.summary.create_file_writer('./logs/custom').as_default():
    model.fit(train_data, train_labels, callbacks=[CustomCallback()])

Model Deployment Options

1. TensorFlow Serving

# Save model in SavedModel format
model.save('my_model/1/')  # Versioned for TF Serving

# Docker command to run TF Serving
# docker run -p 8501:8501 \
#   --mount type=bind,source=$(pwd)/my_model,target=/models/my_model \
#   -e MODEL_NAME=my_model tensorflow/serving

2. TensorFlow Lite (Mobile/Edge)

# Convert to TFLite
converter = tf.lite.TFLiteConverter.from_keras_model(model)
converter.optimizations = [tf.lite.Optimize.DEFAULT]
converter.target_spec.supported_types = [tf.float16]  # FP16 quantization

tflite_model = converter.convert()

# Save
with open('model.tflite', 'wb') as f:
    f.write(tflite_model)

# Inference with TFLite
interpreter = tf.lite.Interpreter(model_path='model.tflite')
interpreter.allocate_tensors()

input_details = interpreter.get_input_details()
output_details = interpreter.get_output_details()

interpreter.set_tensor(input_details[0]['index'], test_image)
interpreter.invoke()
output = interpreter.get_tensor(output_details[0]['index'])

3. TensorFlow.js (Browser)

# Install converter
pip install tensorflowjs
# Convert to TF.js format
import tensorflowjs as tfjs

tfjs.converters.save_keras_model(model, 'tfjs_model/')
// In browser
import * as tf from '@tensorflow/tfjs';

const model = await tf.loadLayersModel('tfjs_model/model.json');
const prediction = model.predict(inputTensor);

Complete End-to-End Example

import tensorflow as tf
from tensorflow import keras
import numpy as np

class ImageClassifier:
    """Production-ready image classifier with training and inference."""

    def __init__(self, input_shape=(224, 224, 3), num_classes=10):
        self.input_shape = input_shape
        self.num_classes = num_classes
        self.model = self._build_model()

    def _build_model(self):
        """Build EfficientNet-based classifier."""
        base_model = keras.applications.EfficientNetB0(
            input_shape=self.input_shape,
            include_top=False,
            weights='imagenet'
        )
        base_model.trainable = False

        inputs = keras.Input(shape=self.input_shape)
        x = keras.applications.efficientnet.preprocess_input(inputs)
        x = base_model(x, training=False)
        x = layers.GlobalAveragePooling2D()(x)
        x = layers.Dropout(0.2)(x)
        outputs = layers.Dense(self.num_classes, activation='softmax')(x)

        model = keras.Model(inputs, outputs)
        model.compile(
            optimizer=keras.optimizers.Adam(0.001),
            loss='sparse_categorical_crossentropy',
            metrics=['accuracy', keras.metrics.TopKCategoricalAccuracy(k=3)]
        )
        return model

    def train(self, train_data, val_data, epochs=30):
        """Train the model with callbacks."""
        callbacks = [
            keras.callbacks.EarlyStopping(
                monitor='val_loss',
                patience=10,
                restore_best_weights=True
            ),
            keras.callbacks.ReduceLROnPlateau(
                monitor='val_loss',
                factor=0.5,
                patience=5,
                min_lr=1e-7
            ),
            keras.callbacks.ModelCheckpoint(
                'best_model.h5',
                monitor='val_accuracy',
                save_best_only=True,
                mode='max'
            )
        ]

        history = self.model.fit(
            train_data,
            epochs=epochs,
            validation_data=val_data,
            callbacks=callbacks
        )
        return history

    def fine_tune(self, train_data, val_data, epochs=20):
        """Fine-tune base model."""
        # Unfreeze base model
        self.model.layers[1].trainable = True

        # Recompile with lower learning rate
        self.model.compile(
            optimizer=keras.optimizers.Adam(1e-5),
            loss='sparse_categorical_crossentropy',
            metrics=['accuracy']
        )

        history = self.model.fit(
            train_data,
            epochs=epochs,
            validation_data=val_data,
            callbacks=[
                keras.callbacks.EarlyStopping(
                    monitor='val_loss',
                    patience=10,
                    restore_best_weights=True
                )
            ]
        )
        return history

    def predict(self, images):
        """Run inference."""
        predictions = self.model.predict(images)
        return {
            'predicted_class': np.argmax(predictions, axis=1),
            'confidence': np.max(predictions, axis=1),
            'all_probs': predictions
        }

    def save(self, path):
        """Save model."""
        self.model.save(path)

    def load(self, path):
        """Load model."""
        self.model = keras.models.load_model(path)

# Usage
classifier = ImageClassifier(input_shape=(224, 224, 3), num_classes=10)

# Prepare data
train_data = tf.keras.preprocessing.image_dataset_from_directory(
    'data/train',
    image_size=(224, 224),
    batch_size=32
)

val_data = tf.keras.preprocessing.image_dataset_from_directory(
    'data/val',
    image_size=(224, 224),
    batch_size=32
)

# Train
history = classifier.train(train_data, val_data, epochs=30)

# Fine-tune
classifier.fine_tune(train_data, val_data, epochs=20)

# Save
classifier.save('final_classifier.h5')

Key Takeaways

Deep learning with TensorFlow requires understanding:

  1. Model architecture: Choose the right layers for your problem
  2. Training strategies: Learning rate scheduling, callbacks, regularization
  3. Transfer learning: Leverage pre-trained models for better performance
  4. Deployment options: TF Serving, TFLite, TF.js for different platforms
  5. Monitoring: TensorBoard for visualization and debugging

TensorFlow’s ecosystem provides everything you need to go from prototype to production.


Questions about TensorFlow or deep learning? Reach out through the contact page or connect on LinkedIn.

MD Furkanul Islam

MD Furkanul Islam

Data Engineer & AI/ML Specialist

9+ years building intelligent data systems at scale. Passionate about bridging the gap between data engineering, AI, and robotics.