JOVANA
Explore Library Glossary Getting Started Three Levels Fields How it works Mission
Join the mission
All guides

From Pixels to a Label: The Classification Task

Meet the simplest-sounding yet deepest task in computer vision — turning a grid of pixels into one confident word.

What a classifier actually does

Let's start with the cleanest possible job description. An image classifier takes in one image and gives back one word. That's it. The input is an image — and recall from earlier tracks that to a computer an image is just an H×W×3 tensor: a grid of H rows by W columns, with 3 numbers at each cell (the red, green, and blue brightness of that pixel). The output is a single label drawn from a fixed, pre-agreed list of K classes that someone chose before the model was ever built — for example {cat, dog, bird, car} when K = 4.

The classification task in one picture: a grid of pixels enters, and exactly one label from a fixed list comes out.

An image of an animal feeds into a box labelled 'classifier', which outputs a single chosen label from a short list of candidate class names.

A friendly way to picture it: think of a postal sorter standing in front of K labelled bins. Every letter (image) that arrives must be dropped into exactly one bin (class). The sorter never invents a new bin on the spot and never refuses to choose — every letter lands somewhere. That 'exactly one bin, always' rule is the whole personality of classification, and we will keep coming back to this image.

Finally, let's fence off the scope so there's no confusion later. Classification answers "what is this image?" with one label for the whole picture. Its neighbours answer different questions: detection answers "where are the objects?" by drawing boxes around each one, and segmentation answers "which pixels belong to what?" by colouring every pixel with a class. Same raw pixels, three very different output shapes. This whole track is about the first one — turning a grid of pixels into one confident word.

Naming the answer: one-hot labels

Before a machine can learn to answer, we have to write down the correct answer in a form it can compute with. A human is happy hearing "the answer is dog", but a network deals in numbers and, crucially, needs to measure how wrong it currently is. So we encode the ground-truth answer as a vector of numbers rather than as the word 'dog' or even the bare index 1.

The standard recipe is one-hot encoding — think of it as the perfect answer sheet. You make a vector with one slot per class, fill every slot with 0, and place a single 1 in the slot of the true class. 'One-hot' literally means exactly one entry is 'hot' (equal to 1) while all the rest are cold (equal to 0).

y_c = \begin{cases} 1 & \text{if } c \text{ is the true class} \\ 0 & \text{otherwise} \end{cases}, \qquad \sum_{c=1}^{K} y_c = 1

The one-hot target vector y.

Reading this symbol by symbol: y is the label vector (the whole answer sheet), and c is the index that walks over the classes from 1 to K. The rule says entry y_c is 1 only when class c is the genuinely correct one, and 0 for every other class. The right-hand part, ∑ y_c = 1, just says all the entries add up to 1. Let's make it concrete with our K = 4 classes (cat, dog, bird, car) listed in that order. If the true answer is dog, the one-hot vector is y = [0, 1, 0, 0]: a 0 for cat, a 1 for dog, a 0 for bird, a 0 for car. Change the truth to 'car' and it becomes [0, 0, 0, 1]. Simple, but exact.

From raw scores to probabilities: softmax

Now to the model's side of the table. The deep network you met in earlier tracks ends in a final layer that emits K raw real numbers, one per class. These numbers are called logits. A logit is just an uncalibrated score: bigger means 'the network leans more toward this class', but the values can be any real number — negative, zero, 5.3, anything. They are emphatically not probabilities: they don't sit between 0 and 1, and they don't add up to anything in particular.

Where logits come from: pixels flow through the convolutional pipeline and the final layer emits K raw scores, one per class.

A diagram of a convolutional neural network: an input image passes through stacked convolution and pooling layers and ends in a layer producing a vector of K logits.

To turn those raw scores into an honest probability distribution we apply softmax. The job of softmax is to take any K real numbers and return K numbers that are all non-negative and add up to exactly 1 — a valid probability distribution we can interpret as the model's confidence in each class.

p_i = \frac{e^{z_i}}{\sum_{j=1}^{K} e^{z_j}}

Softmax turns logits into probabilities.

Symbol by symbol: z_i is the logit (raw score) for class i. The exponential e^{z_i} does two things at once — it makes every value strictly positive (no negative probabilities allowed), and it exaggerates differences, so a slightly bigger logit becomes a noticeably bigger number. The denominator sums e^{z_j} over every class j = 1…K, which is the normaliser that forces the outputs to total 1. The result p_i is the probability the model assigns to class i. Worked example with logits [2.0, 1.0, 0.1]: exponentiate to get e^2.0 ≈ 7.39, e^1.0 ≈ 2.72, e^0.1 ≈ 1.11; their sum is ≈ 11.21; dividing each by the sum gives [0.66, 0.24, 0.10] (which indeed adds to 1). Notice the gap between logits controls how peaked the result is: the 1.0-point lead of the first logit became a confident 66%.

import numpy as np

def softmax(z):
    z = np.array(z, dtype=float)
    z = z - z.max()          # subtract the max first for numerical stability
    e = np.exp(z)            # exponentiate: every value becomes positive
    return e / e.sum()       # normalise so the outputs sum to 1

logits = [2.0, 1.0, 0.1]
probs = softmax(logits)
print(probs)                 # ~ [0.659, 0.242, 0.099]
print(probs.sum())           # 1.0
Softmax in a few lines. Subtracting the max before exp avoids overflow and changes nothing about the result.

Making the call: the argmax decision rule

Softmax gives us a rich probability vector like [0.66, 0.24, 0.10] — but the task demanded one answer. How do we commit? With the argmax decision rule: simply predict the class with the highest probability. Back to the postal sorter — they listen to all K confidences and drop the letter into the loudest bin. Here the loudest is class 1 at 0.66, so we predict class 1.

\hat{y} = \arg\max_{i}\; p_i \;=\; \arg\max_{i}\; z_i

Pick the index of the largest probability — equivalently, the largest logit.

Symbol by symbol: p_i is the probability for class i. The operator arg max over i does not return the largest value; it returns the index i at which that value is largest. So ŷ (read 'y-hat', the model's single predicted class) is whichever class index won. The second equality, arg max p_i = arg max z_i, follows from softmax being monotonic (from the last section): since softmax never reorders, the class with the biggest probability is always the class with the biggest logit. Concretely, for [0.66, 0.24, 0.10] the arg max is index 1; for the logits [2.0, 1.0, 0.1] the arg max is also index 1 — same winner, no softmax needed.

Two practical footnotes. First, ties: if two classes share the exact top probability, the rule is ambiguous, and libraries break ties by a fixed convention (typically picking the lowest index) — rare with real-valued logits but worth knowing. Second, a free speed-up: because arg max over probabilities equals arg max over logits, at inference (when you only need the predicted label, not the probabilities) you can skip the softmax entirely and just read off the largest logit. You only need softmax when you actually want the probability numbers — for training, calibration, or reporting confidence.

One label or many? Single-label vs multi-label

Time to challenge the assumption we've leaned on the whole way: that every image has exactly one honest answer. Often it doesn't. A single photo might genuinely contain both a dog and a frisbee; a street scene contains a car and a person and a traffic light. Forcing such an image into one bin throws away true information — the picture really is several things at once.

This splits the world into two settings. Single-label classification — everything we've built so far — assumes exactly one winner, which is why softmax (a single competition where probabilities sum to 1) plus argmax fits so naturally: more dog necessarily means less cat. Multi-label classification, by contrast, lets several classes be true at once, so we must drop the all-or-nothing competition. Note this is still image classification — same pixels-to-labels spirit — just with the 'exactly one' rule relaxed.

The conceptual fix is intuitive: instead of one shared softmax across all classes, multi-label asks K independent yes/no questions — "is there a dog? (yes/no)", "is there a frisbee? (yes/no)", and so on, each judged on its own. Each question is squeezed into a 0–1 answer by a sigmoid (a one-class probability, free to be high for several classes simultaneously), and each gets its own threshold — say, call it present if its probability clears 0.5. No competition, no forced single winner.

How loss teaches the network: cross-entropy in one minute

We now have two vectors of the same shape: the one-hot target y (the truth, e.g. [0,1,0,0]) and the softmax output p (the model's guess, e.g. [0.10,0.70,0.15,0.05]). Training needs a single number that says how bad this guess is, so we can nudge the network to do better. That number is the cross-entropy loss. The cleanest intuition: cross-entropy measures the model's surprise at hearing the true answer. If the model already put high probability on the correct class, the truth is no surprise and the loss is tiny; if it put low probability there, the truth is a shock and the loss is large.

\mathcal{L} = -\sum_{c=1}^{K} y_c \log p_c \;=\; -\log p_{\text{true}}

Cross-entropy, which collapses to the negative log probability of the correct class.

Symbol by symbol: the sum runs over all classes c = 1…K. y_c is the one-hot target — remember it is 1 for the single true class and 0 everywhere else. p_c is the model's predicted probability for class c. The log is what makes the penalty bite: log of a probability near 1 is near 0 (no penalty), but log of a small probability is a big negative number (huge penalty), and the leading minus sign flips it positive so loss is something we minimise. Here's the elegant part: because y is one-hot, every term where y_c = 0 simply vanishes, and only the single term at the true class survives. So the whole sum collapses to L = −log(p_true) — the negative log of the probability the model assigned to the correct class. Nothing else in the distribution affects the loss directly.

Let's feel the numbers, using natural log. Suppose the model gives the true class (dog) a probability of 0.70. Then the loss is L = −log(0.70) ≈ 0.357. Now imagine training improves it: at p_true = 0.90 the loss drops to −log(0.90) ≈ 0.105; at 0.99 it's ≈ 0.010; and as p_true → 1 the loss → 0 (no surprise, perfect). Go the other way and it bites hard: at p_true = 0.10 the loss is −log(0.10) ≈ 2.303. So pushing the correct-class probability up is exactly what shrinks the loss — that is the lever training pulls.

import numpy as np

# one-hot target for class "dog" (index 1) out of 4 classes
y = np.array([0, 1, 0, 0])

# model's predicted probabilities (output of softmax)
p = np.array([0.10, 0.70, 0.15, 0.05])

# cross-entropy collapses to -log(probability of the true class)
loss = -np.sum(y * np.log(p))
print(loss)                 # -log(0.70) = 0.357

prediction = np.argmax(p)   # 1  ->  "dog"  (the decision rule from earlier)
One-hot truth meets softmax guess: the loss is just the negative log of the true-class probability.
Why a smooth loss matters: a continuous loss surface lets the network roll downhill toward better weights, one small step at a time.

A curved loss surface with a ball rolling downhill toward the minimum, illustrating gradient descent reducing the loss step by step.

And that completes the whole journey of this guide: pixels → logits → softmax → probabilities → argmax, with cross-entropy as the teacher that scores each guess so the network can improve. Everything downstream builds on this skeleton. We've only previewed how the network actually learns; Guide 3 derives the gradient of cross-entropy and lays out the modern training recipe. Next, though, Guide 2 asks a sharper question we've been dancing around: once we have a classifier, how do we measure whether it's any good?