Skip to content
chiltepin

Generated from: “Explain the shape of the image classifier we ship — what each layer does and how wide it is.

ShelfNet v3 — the shape of the image classifier

Written by an agent from the skill, validated by chiltepin check, rendered by the renderer — shown as generated, 13 September 2026.

DOCUMENTREFERENCE

ShelfNet v3 — the shape of the image classifier

Every layer of the product-photo classifier we ship, what it does, and how wide it is.

ShelfNet v3 is the convolutional network behind the "what product is this?" call in the mobile app. It takes one 224×224 RGB photo and returns one of 12 product categories. The network is small on purpose because it runs on the phone. Every layer is sized for a 30 ms budget on a mid-range CPU, not for leaderboard accuracy.

SECTION 01 · Note

Assumptions

Note
The request names no model, so this doc describes ShelfNet v3. It is a plain VGG-style CNN with batch norm, the shape we ship in app release 4.2. Layer widths, kernel sizes, and the 12-class head are realistic values chosen for this doc. Parameter counts are computed from those widths and include biases and batch-norm scale and shift. Width for a conv layer means the number of filters, which equals the channel count of its output.

Headline numbers

The weight file is under 10 MB in fp16, which is what lets the model ship inside the app bundle instead of behind an API. The last conv block holds 73% of all parameters; it is the first place to prune if the bundle must shrink.

SECTION 02 · Metrics
4.85M
Trainable parameters
9.7 MB
Weights on disk (fp16)
224×224×3
Input image
12
Output classes
27 ms
Median inference on device

Layer by layer

Each conv block halves the spatial size and doubles the channel count. The tensor keeps roughly the same number of values while the features get more abstract. Global average pooling replaces the flatten step, so the network accepts any input size at inference, but the app always sends 224×224.

SECTION 03 · Neural net

ShelfNet v3

NEURAL NET
Neural network: 10 layersInput150528224×224 RGB photoStem conv 7×7 /232ReLUEdges and colour blobsEdges and colourConv block 164ReLU2 × conv 3×3, then max pool2 × conv 3×3, thenConv block 2128ReLU2 × conv 3×3, then max pool2 × conv 3×3, thenConv block 3256ReLU2 × conv 3×3, then max pool2 × conv 3×3, thenConv block 4512ReLU2 × conv 3×3, no pool2 × conv 3×3, noGlobal avg pool5127×7 map to one value per channel7×7 map to oneDense256ReLUDropout 0.4256Training onlyOutput12softmaxOne probability per categoryOne probability
Legendinputconvpooldensedropoutoutput
Params 4.85M

What each layer does and how wide it is

Width in the table is the output tensor: height × width × channels. Every conv layer uses padding that keeps the spatial size; only stride and pooling shrink it. Batch norm follows every conv and is folded into the conv weights at export, so it costs nothing at inference.

SECTION 04 · Comparison
LayerKernel / strideOutput shapeParamsWhat it does
Input224 × 224 × 30RGB photo, scaled to [0, 1] and mean-centred per channel
Stem conv7×7, stride 2, 32 filters112 × 112 × 324.8KDetects edges, corners, and flat colour regions at a coarse scale
Conv block 12 × 3×3, 64 filters, then max pool 2×256 × 56 × 6455.7KCombines edges into textures such as fabric weave and print dots
Conv block 22 × 3×3, 128 filters, then max pool 2×228 × 28 × 128222KFinds parts: a bottle cap, a label corner, a handle
Conv block 32 × 3×3, 256 filters, then max pool 2×214 × 14 × 256886KAssembles parts into object regions and their layout
Conv block 42 × 3×3, 512 filters, no pool7 × 7 × 5123.54MEncodes whole-object identity; holds 73% of all parameters
Global avg pool7×7 mean per channel5120Collapses the map to one 512-wide vector, position-independent
Dense512 → 256256131KMixes channels into a compact category embedding
Dropout 0.42560Zeroes 40% of activations during training; identity at inference
Output256 → 12123.1KSoftmax over the 12 product categories

Params include biases and batch-norm scale and shift. Total: 4.85M.

The output vector is 12 probabilities that sum to 1. The app accepts the top class only when its probability is at least 0.6; below that it asks the user to retake the photo. Changing the class list means retraining only the last two layers, which takes under an hour on one GPU.

View the Markdown
```meta
title: ShelfNet v3 — the shape of the image classifier
subtitle: Every layer of the product-photo classifier we ship, what it does, and how wide it is.
tag: REFERENCE
```

ShelfNet v3 is the convolutional network behind the "what product is this?"
call in the mobile app. It takes one 224×224 RGB photo and returns one of 12
product categories. The network is small on purpose because it runs on the
phone. Every layer is sized for a 30 ms budget on a mid-range CPU, not for
leaderboard accuracy.

```callout
tone: note
title: Assumptions
body: "The request names no model, so this doc describes ShelfNet v3. It is a plain VGG-style CNN with batch norm, the shape we ship in app release 4.2. Layer widths, kernel sizes, and the 12-class head are realistic values chosen for this doc. Parameter counts are computed from those widths and include biases and batch-norm scale and shift. Width for a conv layer means the number of filters, which equals the channel count of its output."
```

## Headline numbers

The weight file is under 10 MB in fp16, which is what lets the model ship
inside the app bundle instead of behind an API. The last conv block holds
73% of all parameters; it is the first place to prune if the bundle must
shrink.

```stats
stats:
  - { value: 4.85M, label: Trainable parameters }
  - { value: 9.7 MB, label: Weights on disk (fp16) }
  - { value: "224×224×3", label: Input image }
  - { value: 12, label: Output classes }
  - { value: 27 ms, label: Median inference on device }
```

## Layer by layer

Each conv block halves the spatial size and doubles the channel count. The
tensor keeps roughly the same number of values while the features get more
abstract. Global average pooling replaces the flatten step, so the network
accepts any input size at inference, but the app always sends 224×224.

```neuralnet
id: shelfnet-shape
title: ShelfNet v3
params: 4.85M
maxUnits: 12
layers:
  - { label: Input, units: 150528, kind: input, note: "224×224 RGB photo" }
  - { label: Stem conv 7×7 /2, units: 32, kind: conv, activation: ReLU, note: "Edges and colour blobs" }
  - { label: Conv block 1, units: 64, kind: conv, activation: ReLU, note: "2 × conv 3×3, then max pool" }
  - { label: Conv block 2, units: 128, kind: conv, activation: ReLU, note: "2 × conv 3×3, then max pool" }
  - { label: Conv block 3, units: 256, kind: conv, activation: ReLU, note: "2 × conv 3×3, then max pool" }
  - { label: Conv block 4, units: 512, kind: conv, activation: ReLU, note: "2 × conv 3×3, no pool" }
  - { label: Global avg pool, units: 512, kind: pool, note: "7×7 map to one value per channel" }
  - { label: Dense, units: 256, kind: dense, activation: ReLU }
  - { label: Dropout 0.4, units: 256, kind: dropout, note: "Training only" }
  - { label: Output, units: 12, kind: output, activation: softmax, note: "One probability per category" }
```

## What each layer does and how wide it is

Width in the table is the output tensor: height × width × channels. Every
conv layer uses padding that keeps the spatial size; only stride and pooling
shrink it. Batch norm follows every conv and is folded into the conv weights
at export, so it costs nothing at inference.

```table
columns:
  - Layer
  - Kernel / stride
  - Output shape
  - Params
  - What it does
rows:
  - [Input, "—", "224 × 224 × 3", "0", "RGB photo, scaled to [0, 1] and mean-centred per channel"]
  - [Stem conv, "7×7, stride 2, 32 filters", "112 × 112 × 32", "4.8K", "Detects edges, corners, and flat colour regions at a coarse scale"]
  - [Conv block 1, "2 × 3×3, 64 filters, then max pool 2×2", "56 × 56 × 64", "55.7K", "Combines edges into textures such as fabric weave and print dots"]
  - [Conv block 2, "2 × 3×3, 128 filters, then max pool 2×2", "28 × 28 × 128", "222K", "Finds parts: a bottle cap, a label corner, a handle"]
  - [Conv block 3, "2 × 3×3, 256 filters, then max pool 2×2", "14 × 14 × 256", "886K", "Assembles parts into object regions and their layout"]
  - [Conv block 4, "2 × 3×3, 512 filters, no pool", "7 × 7 × 512", { v: "3.54M", highlight: true }, "Encodes whole-object identity; holds 73% of all parameters"]
  - [Global avg pool, "7×7 mean per channel", "512", "0", "Collapses the map to one 512-wide vector, position-independent"]
  - [Dense, "512 → 256", "256", "131K", "Mixes channels into a compact category embedding"]
  - [Dropout 0.4, "—", "256", "0", "Zeroes 40% of activations during training; identity at inference"]
  - [Output, "256 → 12", "12", "3.1K", "Softmax over the 12 product categories"]
note: "Params include biases and batch-norm scale and shift. Total: 4.85M."
```

The output vector is 12 probabilities that sum to 1. The app accepts the top
class only when its probability is at least 0.6; below that it asks the user
to retake the photo. Changing the class list means retraining only the last
two layers, which takes under an hour on one GPU.