Transfer Learning with MobileNetV2 in Keras: A Practical Guide 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. Here’s a number: 3,670 . That’s how many flower photos I had to teach a neural network the difference between roses, daisies, dandelions, sunflowers and tulips. For 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. So I didn’t train from scratch. I used transfer learning . By 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. Don’t train a brain from scratch. Borrow one that has already learned how to see. Transfer learning is a machine learning technique where a model trained on one task is reused as the starting point for a different task. Think 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. Neural networks work the same way: Early 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 The 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. Pretrained base frozen ──► Global average pooling ──► New Dense 5 "how to see" "summarise" "which flower?" People often mix these up, so here’s the difference: Start with feature extraction. Fine-tune only if you need more. Before 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: python python import numpy as npimport PIL.Image as Imageimport tensorflow hub as hubimport tf keras img 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 Then I matched the predicted index against the ImageNet label file. The model recognised the goldfish without a single line of training from me. 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. But 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 . I used the flower photos dataset from TensorFlow: 3,670 images in five folders. python import pathlib, cv2, numpy as np, tensorflow as tffrom sklearn.model selection import train test split url = "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 Two details matter here: base = 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 model = tf.keras.Sequential base, tf.keras.layers.GlobalAveragePooling2D , tf.keras.layers.Dense 5 one output per flower class model.summary Let’s break this down: Now 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 . I’m training roughly 6,000 parameters instead of 2,000,000. That’s why a small dataset is enough. MobileNetV2 in Keras expects pixel values in the range -1, 1 , which preprocess input handles: python from tensorflow.keras.applications.mobilenet v2 import preprocess input X 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 from logits=True is there because the last layer has no softmax. It outputs raw scores, and the loss function handles the conversion. Even 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 . After 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 . For a network that had never seen these flower classes before, that’s the power of borrowed knowledge. If you want more accuracy, unfreeze the last few layers and train with a much smaller learning rate : base.trainable = Truefor layer in base.layers :-30 : keep early layers frozen layer.trainable = False model.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 You 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. 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. 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. 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. 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. 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 . Transfer learning isn’t magic. Think twice when: Even then, it’s worth a quick experiment. A frozen-base baseline takes minutes. What is transfer learning in deep learning? 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. What’s the difference between feature extraction and fine-tuning? Feature extraction freezes the pretrained layers and trains only a new head. Fine-tuning also unfreezes some pretrained layers and trains them gently. Why freeze the pretrained layers? To protect what they learned from ImageNet while your new head is still random. Updating them early can damage them. Which pretrained model should I choose? 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. The biggest lesson from this project is that you don’t always need more data or a bigger model. Sometimes you need better starting knowledge. Transfer 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. 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? The full notebook is here: GITHUB https://github.com/FahadUrRehman07/FloraVision-Flower-Species-Classification-Using-MobileNetV2-Transfer-Learning 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.