{"slug": "transfer-learning-with-mobilenetv2-in-keras-a-practical-guide", "title": "Transfer Learning with MobileNetV2 in Keras: A Practical Guide", "summary": "A practical guide demonstrates transfer learning with MobileNetV2 in Keras, using a frozen pretrained base plus a new Dense(5) output layer to classify 3,670 flower photos across five categories (roses, daisy, dandelion, sunflowers, tulips) rather than training from scratch on the roughly 1.28 million-image ImageNet dataset. The author loads a MobileNetV2 classifier from TensorFlow Hub at 224×224 input resolution, notes that TensorFlow 2.16+ with Keras 3 requires the legacy tf_keras package for hub.KerasLayer, and recommends starting with feature extraction and fine-tuning only if more accuracy is needed.", "body_md": "Here’s a number: **3,670**.\n\nThat’s how many flower photos I had to teach a neural network the difference between roses, daisies, dandelions, sunflowers and tulips.\n\nFor deep learning, that’s tiny. The famous ImageNet dataset has about **1.28 million** training images. Train a deep convolutional network from scratch on 3,670 photos and it will do what small datasets make networks do: **memorize** the training set and fall apart on new images.\n\nSo I didn’t train from scratch. I used **transfer learning**.\n\nBy the end of this article you’ll know how it works, how to build it in about 20 lines of Keras, and the mistakes that silently wreck most transfer learning projects.\n\n*Don’t train a brain from scratch. Borrow one that has already learned how to see.*\n\nTransfer learning is a machine learning technique where a model trained on one task is reused as the starting point for a different task.\n\nThink of a radiologist who has studied thousands of scans. If you asked them to start reading a new type of scan, they wouldn’t begin from zero. They already know what edges, shapes, textures and abnormalities look like. They only need to learn what’s specific to the new scan.\n\nNeural networks work the same way:\n\n```\nEarly layers   →  edges, corners, coloursMiddle layers  →  textures, patterns, simple shapesLater layers   →  task-specific parts (e.g. \"dog ear\", \"car wheel\")Final layer    →  the actual decision (1,000 ImageNet classes)\n```\n\nThe early and middle layers learn things that are useful for almost any image task. The final layer is the part that’s specific to the original task. So we keep the first part and replace the last.\n\n```\nPretrained base (frozen)  ──►  Global average pooling  ──►  New Dense(5)     \"how to see\"                  \"summarise\"              \"which flower?\"\n```\n\nPeople often mix these up, so here’s the difference:\n\nStart with feature extraction. Fine-tune only if you need more.\n\nBefore building anything, I wanted to see what the pretrained model could already do. I loaded a MobileNetV2 classifier from TensorFlow Hub, resized a goldfish photo to 224×224, scaled it to the 0–1 range, and asked for a prediction:\n\npython\n\n``` python\nimport numpy as npimport PIL.Image as Imageimport tensorflow_hub as hubimport tf_keras\nimg_size = (224, 224)classifier = tf_keras.Sequential([    hub.KerasLayer(        \"https://tfhub.dev/google/tf2-preview/mobilenet_v2/classification/4\",        input_shape=img_size + (3,)    )])img = Image.open(\"goldfish.jpg\").resize(img_size)x = np.array(img) / 255.0result = classifier.predict(x[np.newaxis, ...])label_index = np.argmax(result[0], axis=-1)\n```\n\nThen I matched the predicted index against the ImageNet label file. The model recognised the goldfish without a single line of training from me.\n\n**Why did I use** **tf_keras?** With TensorFlow 2.16+ and Keras 3, hub.KerasLayer needs the legacy Keras package. That's a very common error in Colab, and importing tf_keras is the fix.\n\nBut this classifier can only choose among **ImageNet’s 1,000 categories**. The label list has “daisy”, but no rose, tulip, sunflower or dandelion class. To classify my flowers I needed the model’s *knowledge*, not its *final answer*.\n\nI used the flower photos dataset from TensorFlow: 3,670 images in five folders.\n\n``` python\nimport pathlib, cv2, numpy as np, tensorflow as tffrom sklearn.model_selection import train_test_split\nurl = \"https://storage.googleapis.com/download.tensorflow.org/example_images/flower_photos.tgz\"data_dir = pathlib.Path(    tf.keras.utils.get_file(\"flower_photos\", origin=url, cache_dir=\".\", untar=True))classes = [\"roses\", \"daisy\", \"dandelion\", \"sunflowers\", \"tulips\"]X, y = [], []for label, name in enumerate(classes):    for path in data_dir.glob(f\"**/{name}/*\"):        img = cv2.imread(str(path))        img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)   # OpenCV loads BGR        X.append(cv2.resize(img, (224, 224)))        y.append(label)X, y = np.array(X), np.array(y)X_train, X_test, y_train, y_test = train_test_split(X, y, random_state=0)\n```\n\nTwo details matter here:\n\n```\nbase = tf.keras.applications.MobileNetV2(    input_shape=(224, 224, 3),    include_top=False,        # remove the ImageNet classifier    weights=\"imagenet\"        # keep the pretrained knowledge)base.trainable = False        # feature extraction: freeze everything\nmodel = tf.keras.Sequential([    base,    tf.keras.layers.GlobalAveragePooling2D(),    tf.keras.layers.Dense(5)   # one output per flower class])model.summary()\n```\n\nLet’s break this down:\n\nNow look at model.summary(). The base has about **2.26 million** parameters, all frozen. The new head has only **6,405** trainable parameters (1,280 × 5 weights plus 5 biases).\n\n*I’m training roughly 6,000 parameters instead of 2,000,000. That’s why a small dataset is enough.*\n\nMobileNetV2 in Keras expects pixel values in the range **[-1, 1]**, which preprocess_input handles:\n\n``` python\nfrom tensorflow.keras.applications.mobilenet_v2 import preprocess_input\nX_train_p = preprocess_input(X_train.astype(\"float32\"))X_test_p  = preprocess_input(X_test.astype(\"float32\"))model.compile(    optimizer=\"adam\",    loss=tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True),    metrics=[\"accuracy\"])model.fit(X_train_p, y_train, epochs=5)model.evaluate(X_test_p, y_test)\n```\n\nfrom_logits=True is there because the last layer has no softmax. It outputs raw scores, and the loss function handles the conversion.\n\nEven in the middle of the very first epoch (batch 45 of 86), the running training accuracy was already around **70%** [UPDATE with your final numbers].\n\nAfter training, my accuracy on the **untouched test set** was **[YOUR TEST ACCURACY]%**, after only **[N] epochs** [ADD your own numbers, and a training curve screenshot if you can].\n\nFor a network that had never seen these flower classes before, that’s the power of borrowed knowledge.\n\nIf you want more accuracy, unfreeze the last few layers and train with a **much smaller learning rate**:\n\n```\nbase.trainable = Truefor layer in base.layers[:-30]:      # keep early layers frozen    layer.trainable = False\nmodel.compile(    optimizer=tf.keras.optimizers.Adam(1e-5),   # tiny learning rate    loss=tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True),    metrics=[\"accuracy\"])model.fit(X_train_p, y_train, epochs=5)\n```\n\nYou must **recompile** after changing trainable, or the change won't take effect. Also read the Keras fine-tuning guide's advice about BatchNorm layers before unfreezing a large part of the network.\n\n**1. Forgetting to freeze the base.** Without base.trainable = False, you're training all 2.26 million parameters with a large learning rate. That can wipe out the pretrained knowledge you came for.\n\n**2. Wrong preprocessing.** The TF Hub MobileNetV2 model expects inputs in **[0, 1]**. The Keras applications version expects **[-1, 1]**. Same architecture, different scaling. Use the right one for your source.\n\n**3. RGB vs BGR.** OpenCV loads images as BGR, but pretrained models were trained on RGB. Convert with cv2.cvtColor or the colours are wrong.\n\n**4. Off-by-one labels.** The TF Hub classifier outputs 1,001 scores, and index 0 is a “background” class. Match it against a label file with the same 1,001 entries.\n\n**5. Leaky evaluation.** Split into train and test *before* any augmentation or tuning, and touch the test set once. I covered this in detail in my article on [data augmentation](https://medium.com/@fahadrehmann07/data-augmentation-the-simple-trick-that-can-make-your-machine-learning-model-more-robust-098a1399c1aa).\n\nTransfer learning isn’t magic. Think twice when:\n\nEven then, it’s worth a quick experiment. A frozen-base baseline takes minutes.\n\n**What is transfer learning in deep learning?**\n\n It’s reusing a model trained on a large dataset as the starting point for a new task, so you need far less data and training time.\n\n**What’s the difference between feature extraction and fine-tuning?**\n\n Feature extraction freezes the pretrained layers and trains only a new head. Fine-tuning also unfreezes some pretrained layers and trains them gently.\n\n**Why freeze the pretrained layers?**\n\n To protect what they learned from ImageNet while your new head is still random. Updating them early can damage them.\n\n**Which pretrained model should I choose?**\n\n MobileNetV2 is small and fast, so it’s a great starting point. Larger models such as ResNet, EfficientNet or ConvNeXt can be more accurate but cost more to run.\n\nThe biggest lesson from this project is that **you don’t always need more data or a bigger model. Sometimes you need better starting knowledge.**\n\nTransfer learning turns a 3,670-photo dataset into a working image classifier, and the same idea powers modern NLP and computer vision systems far beyond this example.\n\n**If this helped you, clap 👏 (you can clap up to 50 times), follow for more practical deep learning, and tell me in the comments: which pretrained model would you try next, and on what data?**\n\nThe full notebook is here: [GITHUB](https://github.com/FahadUrRehman07/FloraVision-Flower-Species-Classification-Using-MobileNetV2-Transfer-Learning)\n\n[Transfer Learning with MobileNetV2 in Keras: A Practical Guide](https://pub.towardsai.net/transfer-learning-with-mobilenetv2-in-keras-a-practical-guide-f5cc4935108a) was originally published in [Towards AI](https://pub.towardsai.net) on Medium, where people are continuing the conversation by highlighting and responding to this story.", "url": "https://wpnews.pro/news/transfer-learning-with-mobilenetv2-in-keras-a-practical-guide", "canonical_source": "https://pub.towardsai.net/transfer-learning-with-mobilenetv2-in-keras-a-practical-guide-f5cc4935108a?source=rss----98111c9905da---4", "published_at": "2026-09-22 19:01:01+00:00", "updated_at": "2026-09-22 19:23:51.836248+00:00", "lang": "en", "topics": ["machine-learning", "neural-networks", "computer-vision", "ai-research", "developer-tools"], "entities": ["MobileNetV2", "Keras", "TensorFlow", "TensorFlow Hub", "ImageNet", "tf_keras", "Colab", "OpenCV"], "alternates": {"html": "https://wpnews.pro/news/transfer-learning-with-mobilenetv2-in-keras-a-practical-guide", "markdown": "https://wpnews.pro/news/transfer-learning-with-mobilenetv2-in-keras-a-practical-guide.md", "text": "https://wpnews.pro/news/transfer-learning-with-mobilenetv2-in-keras-a-practical-guide.txt", "jsonld": "https://wpnews.pro/news/transfer-learning-with-mobilenetv2-in-keras-a-practical-guide.jsonld"}}