The problem in brief
Each image is a 168 x 168 black canvas on which between 2 and 5 small garments from Fashion-MNIST (T-shirt, trouser, pullover, dress, coat, sandal, shirt, sneaker, bag, ankle boot) have been pasted at random places. You must say, for each of the 10 garment types, whether it appears at least once. This is multi-label classification: several answers can be true at once, unlike ordinary classification where exactly one class wins. You get 6 318 training images and 702 validation images; the 780 test images are hidden on the contest platform, which allows pretrained ResNets from torchvision and limits the evaluation to 2.5 minutes on a GPU.
The test set is private, so all scores below are on the official validation set, which the organisers say was built exactly like the test set. We never tuned anything on it.
How you are scored
For each class , the F1-score compares the predicted and true presence over all images; the task metric is their average (macro F1):
Points follow a linear scale between two thresholds published in the statement: 0 points at F1 , 100 points at F1 , and in between.
The lower threshold is not arbitrary. Each garment type is present in about 37 % of the images, so the naive solution that answers "every garment is present" has recall 1 and precision 0.37 for every class, hence F1 . Anything at or below 0.57 is no better than that.
First look at the data

- Every class appears in 35 to 39 % of the images: the classes are balanced, so there is no imbalance trick to play.
- Most images hold 3 or 4 garments, each about 20 to 40 pixels wide, anywhere on the canvas, sometimes touching the border. The same garment type can appear twice.
- The hard part is inherited from Fashion-MNIST: shirt, T-shirt, pullover and coat look alike at this resolution. Footwear (sandal, sneaker, ankle boot), trousers and bags are easy.
A first baseline
The "everything is present" answer scores 0.545 (0 points). A pixel-based model does even worse: a one-vs-rest logistic regression on the image downsampled to 42 x 42 gets 0.423. That is a useful lesson. A linear model on pixels learns "a bright blob at this location means a trouser", but a trouser can be anywhere, so the weights for every location get averaged into mush. What we need is a detector that recognises a garment wherever it is.
The key idea
A convolutional network followed by global pooling is a "find it anywhere" detector, and one sigmoid per class turns it into ten independent yes/no questions.
- Convolutions are translation-equivariant. The same filters slide over the whole image, so a trouser produces the same activations at the top-left as at the bottom-right, just shifted.
- Global pooling removes the position. After four blocks of convolutions and pooling, the network has a 10 x 10 map of 256 features. Taking the maximum (or the mean) of each feature over the whole map gives a 256-number summary that no longer depends on where the garments were. Max pooling matches the question exactly ("is there a trouser somewhere?"), while average pooling counts how much of it there is; in our runs both work equally well (0.924 and 0.926), because the final linear layer and the threshold can absorb the difference.
- Sigmoid + binary cross-entropy, not softmax. A softmax makes the classes compete for a probability mass of 1, which is wrong when 4 garments are present. With 10 sigmoids, each output answers its own question:
We predict "present" when .
The full solution
- Scale pixels to . Hold out 600 training images (only to test per-class thresholds, see below).
- Network trained from scratch: four blocks of two 3 x 3 convolutions with batch normalisation and ReLU, each followed by 2 x 2 max-pooling (32, 64, 128, 256 channels; 168 to 10 pixels), global max pooling, one linear layer to 10 logits. About 1.2 M parameters.
- Augmentation: random horizontal flip and random shift of up to 12 pixels (with wrap-around). Both preserve the labels.
- 30 epochs of AdamW (learning rate with a one-cycle schedule, weight decay , batch 64): about 10 minutes on an Apple GPU, around a minute or two on a contest GPU. Prediction averages the image and its mirror.
f = self.features(x) # (B, 256, 10, 10)
z = self.head(f.amax((2, 3))) # "is feature k present anywhere?" -> 10 logits
loss = F.binary_cross_entropy_with_logits(z, y)
The pretrained ResNets allowed by the statement were not needed: the garments are tiny grey-scale icons, very different from ImageNet photos, and a small network trained from scratch already clears the 100-point threshold by a wide margin.
Thresholds. Tuning one threshold per class on the 600 held-out training images changed almost nothing (0.923 against 0.924 at 0.5): when classes are balanced and the model is well calibrated, 0.5 is already close to optimal for F1.

The per-class scores tell the expected story: trousers, sandals and bags are near 0.99, while shirt (0.78), T-shirt and coat (0.86) are the weak spots, because at 28 x 28 pixels a shirt, a pullover, a coat and a T-shirt differ by a collar, a zip or the sleeve length. The same four classes are the classic confusions of single-label Fashion-MNIST.
Results
Official validation set (702 images); the test labels are private.
| approach | val macro F1 | points |
|---|---|---|
| answer "all 10 present" (statement's naive solution) | 0.545 | 0 |
| logistic regression on 42 x 42 pixels | 0.423 | 0 |
| CNN + global average pooling | 0.926 | 100 |
| CNN + global max pooling, threshold 0.5 (final) | 0.924 | 100 |
| same, per-class thresholds tuned on held-out training images | 0.923 | 100 |
The final model reaches 0.924 macro F1 on validation, well above the 0.87 needed for full marks; since the test set is built the same way, the margin of 0.05 should be comfortable.
What to remember
- Multi-label means one sigmoid per class, trained with binary cross-entropy; softmax would force the classes to compete.
- "Is it anywhere in the image?" calls for convolutions plus global pooling, which make the answer independent of position. A linear model on pixels cannot do this and scores below the trivial baseline.
- Compute the trivial baseline of your metric. "Everything present" gives F1 0.54 here, which explains the statement's 0-point threshold of 0.57.
- Pretrained is not always necessary. For small synthetic icons, a compact CNN trained from scratch for 10 minutes beats the threshold easily.
- Look at per-class scores. The average hides that shirt and T-shirt carry most of the remaining errors.