Part 5: Neo-ML: Two Ways to Recognise a Flower – An Introduction to Convolutional Neural Networks (CNNs)
A ruler and two rules, or four million numbers and a camera
I have now built a flower classifier twice.
The first time was while I was studying machine learning at Imperial College London. The model was a handful of lines of code, trained in a blink, and you could read its entire reasoning aloud in a few seconds.
The second time was this week. A convolutional neural network, nearly four million adjustable numbers, a few thousand photos. For fun, I showed it a picture of my dog and it told me, with 50% confidence, she was a tulip. I don’t see the likeness. For fun, an impression of what my CNN sees is shown below. I find it a bit creepy.
Both models do the same job on paper: look at a flower, classify it. But they go about it in completely different ways, and the difference tells you most of what you need to know about choosing a model for real AI work. So let’s put them side by side, code and all.
Model one: measure first, then decide
The Iris dataset is the “hello world” of machine learning. It holds 150 iris flowers, 50 from each of three species: setosa, versicolor and virginica. For each flower, a botanist took four measurements with a ruler: sepal length, sepal width, petal length and petal width. The statistician Ronald Fisher made the dataset famous in 1936.
Notice what the model is given. Not a photo. Just four numbers per flower.
Here is the whole model, using a decision tree from the scikit-learn library.
from sklearn.datasets import load_iris
from sklearn.model_selection import train_test_split
from sklearn.tree import DecisionTreeClassifier, export_text
iris = load_iris()
X_train, X_test, y_train, y_test = train_test_split(
iris.data, iris.target, test_size=0.2, random_state=42, stratify=iris.target)
model = DecisionTreeClassifier(max_depth=2, random_state=42)
model.fit(X_train, y_train)
print(export_text(model, feature_names=iris.feature_names))
print(f”Test accuracy: {model.score(X_test, y_test):.1%}”)
Line by line:
- load_iris() loads the 150 flowers and their species labels.
- train_test_split holds back 20% of the flowers (30 of them) as test data the model never sees during training.
- DecisionTreeClassifier(max_depth=2) asks for a tree that may ask at most two questions in a row.
- fit does the learning. It searches for the measurement and the cut-off value that best separate the species.
- export_text prints what the model learned, in plain text.
And this is what it prints:
|— petal length (cm) <= 2.45
| |— class: 0
|— petal length (cm) > 2.45
| |— petal width (cm) <= 1.65
| | |— class: 1
| |— petal width (cm) > 1.65
| | |— class: 2
Test accuracy: 93.3%
That is the entire model. In English:
- If the petal is 2.45 cm long or less, it’s a setosa (class 0).
- Otherwise, if the petal is 1.65 cm wide or less, it’s a versicolor (class 1).
- Otherwise it’s a virginica (class 2).
Two rules, and it gets 28 of the 30 unseen flowers right. The first rule never misses, because setosa petals are much smaller than the others. The mistakes all come from versicolor and virginica, whose petal sizes overlap a little. The model also ignored the sepals completely. It worked out for itself that two of the four measurements were enough.
One honest footnote: the exact cut-off moves slightly depending on which flowers land in the training set. Train on all 150 and the second rule becomes 1.75 cm. The shape of the answer stays the same.
Model two: look, and learn what matters
Now take away the botanist and the ruler. All we have is photographs: 3,670 of them, sorted into five folders for daisies, dandelions, roses, sunflowers and tulips. And it’s the folder names that act as the image labels.
A photo, to a computer, is a grid of numbers. Each image here is resized to 180 by 180 pixels with three colour values per pixel. That is 97,200 numbers per flower, against the Iris model’s four. And no single one of those numbers means anything. Pixel 4,012 being a bit red tells you nothing about roses.

So the model has to do two jobs. First work out what to look for, then decide. The tool for that is a convolutional neural network, or CNN.
A short detour: what is a CNN?
In 2012, a small team from the University of Toronto entered the ImageNet challenge, an annual competition in which software had to recognise objects in photographs across 1,000 categories, learning from about 1.2 million labelled images. Alex Krizhevsky, Ilya Sutskever and their supervisor Geoffrey Hinton submitted a CNN that became known as AlexNet.
It didn’t just win. Its error rate was about 15%, against about 26% for the runner-up. In a contest where teams usually fought over a point or two, that was a different league, and within a couple of years almost everyone in the field had switched to the same approach. The current AI boom is usually dated from that result.
The idea itself wasn’t new. Yann LeCun had CNNs reading handwritten digits on bank cheques in the 1990s. What changed by 2012 was a huge labelled dataset and GPUs fast enough to train on it. The breakthrough was as much about data and hardware as about the model.
So what is it? A CNN is a neural network built for images, and it rests on three ideas.
- Filters. A filter is a tiny window, often 3 by 3 pixels, that slides across the whole image looking for one simple pattern, such as a vertical edge. Wherever it finds the pattern, it lights up. The sliding is the “convolution” in the name.
- Layers. Filters are stacked in layers, and each layer looks at the output of the one before. The first finds edges and colours. The next combines edges into textures and curves. The next combines those into parts, such as a petal. It is a little like reading: strokes make letters, letters make words, words make meaning.
- Learning. Nobody designs the filters. They start as random numbers, and training nudges them, photo by photo, until they detect patterns that help tell the classes apart.
That third point is the big one. In the Iris model a botanist chose what to measure. In a CNN, the filters are the measurements, and the network works them out for itself.
Here is mine, written with Keras.
model = Sequential([
layers.Input(shape=(180, 180, 3)),
layers.Rescaling(1./255),
layers.Conv2D(16, 3, padding=’same’, activation=’relu’),
layers.MaxPooling2D(),
layers.Conv2D(32, 3, padding=’same’, activation=’relu’),
layers.MaxPooling2D(),
layers.Conv2D(64, 3, padding=’same’, activation=’relu’),
layers.MaxPooling2D(),
layers.Flatten(),
layers.Dense(128, activation=’relu’),
layers.Dense(5)
])
Read it top to bottom, because that is the route a photo takes.
- Input and Rescaling take the photo in and shrink the pixel values from 0 to 255 down to 0 to 1, which networks learn from more easily.
- Each Conv2D layer slides a set of small 3 by 3 windows, called filters, over the image. Each filter hunts for one pattern. The first layer finds edges and patches of colour, the second combines those into textures and curves, the third into things like petals and flower centres.
- Each MaxPooling2D halves the image size, keeping only the strongest signals.
- Flatten and the two Dense layers weigh up everything that was found and produce five scores, one per flower. Highest score wins.
Here is the part that matters for this comparison. Nowhere in that code is there a word about petals, colours or stems. The code only describes the shape of the model. Everything it knows about flowers is learned during training and stored as 3,989,285 numbers.
The convolution layers are doing the botanist’s job. They are deciding what is worth measuring.
For the curious: counting the parameters
Keras will count the numbers for you. Run model.summary() and the last lines read:
Total params: 3,989,285 (15.22 MB)
Trainable params: 3,989,285 (15.22 MB)
Non-trainable params: 0 (0.00 B)
- Total params is every adjustable number in the model.
- Trainable params are the ones training is allowed to change. Here that is all of them.
- Non-trainable params are numbers that are locked. There are none, because this model was trained from scratch.
- 15.22 MB is the memory they take up. Each parameter is stored in four bytes.
Where do they all live? Not where you might expect.
| Layer | Parameters |
| Conv2D, 16 filters | 448 |
| Conv2D, 32 filters | 4,640 |
| Conv2D, 64 filters | 18,496 |
| Dense, 128 units | 3,965,056 |
| Dense, 5 outputs | 645 |
The three convolution layers, which do all the seeing, account for well under 1% of the total. Over 99% sit in one dense layer that weighs up the evidence. Filters are cheap because the same small window is reused across the whole image.
And for all that, four million parameters is small by today’s standards. No GPU necessary: this model trained on my laptop.
It works, up to a point. The first version of this model typically reaches around 60% on photos it hasn’t seen, and it improves once you add a couple of standard fixes for overfitting. But ask it why it chose “rose” and all it can give you is a confidence score. And because it only knows five answers, everything in the world is one of five flowers. Including my dog now nicknamed Tulip.
Side by side
| Iris decision tree | Flower CNN | |
| What goes in | 4 measurements taken by a person | 97,200 raw pixel values |
| Who chose what to measure | A botanist | The network, during training |
| Training examples | 120 flowers | 2,936 photos |
| What the model stores | 2 rules | 3,989,285 numbers |
| Hardware | Any laptop, instantly | A laptop, more slowly; no GPU needed at this size |
| Explanation for an answer | “The petal is under 2.45 cm” | A confidence score |
| Works on a raw photo | No | Yes |
This is not a fair fight, and it isn’t meant to be. They are different datasets and different problems: three species against five, measurements against photos. The accuracy figures can’t be compared.
What can be compared is where the hard work happens. With Iris, a human had already done the difficult part by deciding that petal size mattered and measuring it. The model only had to draw two lines. With the CNN, nobody told it where to look, so it had to learn that too. That is why it needs twenty-four times the examples and millions of times the parameters.
Pros and cons
The rule-based model
For
- You can read it. A rejected item comes with a reason a person can check.
- It is easy to verify. It’s just measurements and maths, so anyone with a ruler and the two rules can confirm an answer by hand.
- It needs very little data. 120 examples were plenty.
- It trains in an instant and runs on anything.
- There is very little to go wrong, and when it does go wrong you can see where.
Against
- Somebody has to measure the right things first. On a production line that means a sensor, a calibrated camera or a person with a ruler.
- It only works when the decision can be written as rules on those measurements.
- It can’t cope with raw, messy input such as a photo taken at an odd angle in poor light.
The CNN
For
- It works directly on images. No one has to decide in advance what to measure.
- It handles variety that no rulebook could cover: angle, lighting, background, shape.
- The same approach transfers to other visual problems, from cracked bottles to medical scans.
Against
- It is hungry for labelled examples, and labelling is slow, costly work.
- It needs far more compute to train. This small one ran on a laptop, but larger models need a GPU.
- It can’t explain itself beyond a score, which matters when someone asks “why was this rejected?”
- It fails in strange ways, and confidently. See: dog, tulip.
- It needs watching after it goes live, because the world it was trained on keeps changing.
So which one is better?
Neither. It’s the wrong question.
The right question is: can you measure the thing that matters?
If you can, and the decision can be written as a rule, use the rule. It is cheaper to build, cheaper to run, and it can explain itself. A fill level on a bottling line is a height against a mark. You don’t need four million parameters to read a ruler.
If you can’t, because what matters is buried in pixels and varies every time, that is where a network earns its keep. Nobody can write a rule for “looks like a rose” or “looks cracked”.
Plenty of real systems use both. A camera and some image processing take the measurement, and a simple rule makes the call. Deep learning comes in only for the part that won’t reduce to a measurement.
AI wasn’t born from deep learning, and sledgehammers don’t crack nuts. The Iris dataset is ninety years old and a two-rule tree is still the right answer to its own problem. The best model isn’t the newest or the biggest. It’s the one that works for your use case.

