{ "cells": [ { "cell_type": "markdown", "id": "afdd07a1", "metadata": {}, "source": [ "# Convolutional networks in Keras 3 and TensorFlow\n", "\n", "The same ideas the TensorFlow way: Keras 3 layers, `compile`/`fit`, callbacks, and `tf.GradientTape` when you want your own training loop. TensorFlow 2.21 is stable and mostly receives fixes now; Google recommends Keras 3, JAX or PyTorch for new generative-AI work, and Keras 3 code like this also runs on JAX or PyTorch by changing one line.\n", "\n", "[Open in Google Colab](https://colab.research.google.com/github/mahmoudyosrimahmoud13/inpace-library/blob/main/courses/dl-4-cnns/files/dl4_convolutional_networks_keras.ipynb) · part of the inPace course library. Slides © DeepLearning.AI (CC BY-SA 2.0); this notebook: inPace (CC BY-SA 4.0).\n", "\n", "**Run it top to bottom** (Runtime → Run all in Colab). Each part says what to look for." ] }, { "cell_type": "code", "execution_count": null, "id": "7a22c818", "metadata": {}, "outputs": [], "source": [ "# Works in Google Colab as it is (TensorFlow and Keras are already installed there).\n", "# On your own computer: pip install tensorflow keras scikit-learn scipy matplotlib\n", "# One framework per notebook: PyTorch and TensorFlow can clash when loaded into the same Python process.\n", "import os\n", "os.environ[\"KERAS_BACKEND\"] = \"tensorflow\" # Keras 3 also runs on \"jax\" or \"torch\": change it here\n", "os.environ.setdefault(\"TF_CPP_MIN_LOG_LEVEL\", \"3\")\n", "import numpy as np, matplotlib.pyplot as plt\n", "import tensorflow as tf, keras\n", "keras.utils.set_random_seed(0)\n", "print(\"TensorFlow\", tf.__version__, \"| Keras\", keras.__version__, \"| backend:\", keras.backend.backend(),\n", " \"| GPU:\", bool(tf.config.list_physical_devices(\"GPU\")))" ] }, { "cell_type": "markdown", "id": "db0850bf", "metadata": {}, "source": [ "## 1. A CNN on digits\n", "Keras images are channels-last: `(batch, height, width, channels)`." ] }, { "cell_type": "code", "execution_count": null, "id": "7ea77fe4", "metadata": {}, "outputs": [], "source": [ "from sklearn.datasets import load_digits\n", "from sklearn.model_selection import train_test_split\n", "d = load_digits()\n", "Xa, Xb, ya, yb = train_test_split((d.images / 16.0).astype(\"float32\")[..., None], d.target, test_size=0.3, random_state=0)\n", "cnn = keras.Sequential([keras.Input((8, 8, 1)),\n", " keras.layers.Conv2D(16, 3, padding=\"same\", activation=\"relu\"), keras.layers.MaxPooling2D(),\n", " keras.layers.Conv2D(32, 3, padding=\"same\", activation=\"relu\"), keras.layers.MaxPooling2D(),\n", " keras.layers.Flatten(), keras.layers.Dense(10)])\n", "cnn.compile(\"adamw\", keras.losses.SparseCategoricalCrossentropy(from_logits=True), metrics=[\"accuracy\"])\n", "cnn.fit(Xa, ya, epochs=60, batch_size=64, verbose=0)\n", "print(f\"parameters {cnn.count_params():,} | test accuracy {cnn.evaluate(Xb, yb, verbose=0)[1]:.3f}\")" ] }, { "cell_type": "markdown", "id": "62ac53b8", "metadata": {}, "source": [ "## 2. A residual block with the functional API" ] }, { "cell_type": "code", "execution_count": null, "id": "aef843a6", "metadata": {}, "outputs": [], "source": [ "def residual_block(x, filters):\n", " shortcut = x\n", " x = keras.layers.Conv2D(filters, 3, padding=\"same\", use_bias=False)(x)\n", " x = keras.layers.ReLU()(keras.layers.BatchNormalization()(x))\n", " x = keras.layers.BatchNormalization()(keras.layers.Conv2D(filters, 3, padding=\"same\", use_bias=False)(x))\n", " return keras.layers.ReLU()(keras.layers.Add()([x, shortcut])) # the skip connection\n", "inp = keras.Input((8, 8, 16)); print(\"shape kept:\", keras.Model(inp, residual_block(inp, 16)).output_shape)" ] }, { "cell_type": "markdown", "id": "8bc431a3", "metadata": {}, "source": [ "## 3. Depthwise separable convolution" ] }, { "cell_type": "code", "execution_count": null, "id": "9213c57e", "metadata": {}, "outputs": [], "source": [ "x = keras.Input((32, 32, 64))\n", "normal = keras.Model(x, keras.layers.Conv2D(128, 3, padding=\"same\")(x))\n", "separable = keras.Model(x, keras.layers.SeparableConv2D(128, 3, padding=\"same\")(x))\n", "print(f\"weights: normal {normal.count_params():,} vs separable {separable.count_params():,}\")" ] }, { "cell_type": "markdown", "id": "790688f9", "metadata": {}, "source": [ "## 4. Augmentation layers" ] }, { "cell_type": "code", "execution_count": null, "id": "70c481f1", "metadata": {}, "outputs": [], "source": [ "aug = keras.Sequential([keras.layers.RandomRotation(0.05), keras.layers.RandomZoom(0.15), keras.layers.RandomContrast(0.3)])\n", "fig, ax = plt.subplots(1, 8, figsize=(10, 1.5))\n", "for i, a in enumerate(ax):\n", " a.imshow((Xa[0] if i == 0 else aug(Xa[:1], training=True)[0])[..., 0], cmap=\"gray_r\"); a.axis(\"off\")\n", "plt.show()" ] }, { "cell_type": "markdown", "id": "8df42c1f", "metadata": {}, "source": [ "## 5. Non-max suppression with TensorFlow\n", "TensorFlow's boxes are `[y1, x1, y2, x2]`." ] }, { "cell_type": "code", "execution_count": null, "id": "8a2b9d2b", "metadata": {}, "outputs": [], "source": [ "boxes = tf.constant([[30, 30, 120, 130], [38, 40, 128, 140], [25, 35, 125, 125], [60, 200, 160, 290], [70, 210, 165, 300]], tf.float32)\n", "scores = tf.constant([0.9, 0.75, 0.6, 0.85, 0.7])\n", "print(\"kept:\", tf.image.non_max_suppression(boxes, scores, max_output_size=10, iou_threshold=0.5).numpy().tolist())" ] }, { "cell_type": "markdown", "id": "d18e4ed6", "metadata": {}, "source": [ "## 6. Transfer learning with keras.applications\n", "Needs internet to download the ImageNet weights (runs in Colab)." ] }, { "cell_type": "code", "execution_count": null, "id": "363ca96f", "metadata": {}, "outputs": [], "source": [ "try:\n", " base = keras.applications.MobileNetV3Small(input_shape=(224, 224, 3), include_top=False, weights=\"imagenet\")\n", " base.trainable = False\n", " inp = keras.Input((224, 224, 3))\n", " out = keras.layers.Dense(3)(keras.layers.GlobalAveragePooling2D()(base(inp, training=False)))\n", " model = keras.Model(inp, out)\n", " print(\"trainable weights:\", sum(int(np.prod(w.shape)) for w in model.trainable_weights))\n", "except Exception as e:\n", " print(\"Needs internet to download the weights (run this in Colab):\", type(e).__name__)" ] } ], "metadata": { "colab": { "provenance": [], "toc_visible": true }, "kernelspec": { "display_name": "Python 3", "language": "python", "name": "python3" } }, "nbformat": 4, "nbformat_minor": 5 }