System designTroubleshooting

Open-vocabulary detection class mismatch after fine-tuning

Fix the class mismatch that crashes a fine-tuned open-vocabulary detection model at inference: register the same five names you trained on, then filter.

LumaSpec Vision8 min read Share on LinkedIn

On this page 9 sections
  1. 01The answer: register the trained names, all of them
  2. 02Why the class count breaks the reshape
  3. 03Worked example: 441 600 elements against 65 channels
  4. 04Causes in order, with the test that separates them
  5. 05The fix, step by step
  6. 06When you need a different vocabulary
  7. 07Where the model trains and where it runs
  8. 08Checklist before the next run
  9. 09Questions

Your fine-tuned detector crashes at inference with RuntimeError: shape '[1, 65, -1]' is invalid for input of size 441600, and the traceback ends inside the prediction head's reshape. The weights are fine. Fine-tuning on the five classes in your dataset YAML rebuilt the head for exactly those five names, and your predict script registered only one, so the head and its class bookkeeping disagree about how many class channels every anchor carries. What follows works the arithmetic behind the two numbers in that message, ranks the causes, gives the fix in a few lines, and states the decision for every case where you need a different vocabulary. It is written for engineers deploying trained detectors on lines fed by area scan cameras and smart cameras, with inference on a vision controller.

The answer: register the trained names, all of them

Pass the registration call the complete class list you trained on — here the five names from the dataset YAML: OK, quemian, dirty, toufa and heijietou, in that order, because the list position assigns the class id of every result. If the station only acts on one of them, register all five and filter the output; never trim the input list. A fine-tuned checkpoint also runs with no registration call at all, since the trained names travel inside the weights and a plain predict call uses them.

names = ["OK", "quemian", "dirty", "toufa", "heijietou"]  # the YAML list, verbatim
model.set_classes(names, model.get_text_pe(names))
results = model.predict(source)
# need one class? filter results by class id, never trim the prompt list

Class lists belong with the rest of the task settings, kept in one place between training and serving — vision model task configuration for reliable inference covers that discipline.

Why the class count breaks the reshape

A pretrained open-vocabulary detector scores each candidate region against text embeddings of whatever names you hand it, so any number of names works. Fine-tuning on a fixed dataset ends that. The trainer reshapes the prediction head to the N classes named in the YAML and stores the prompt embeddings for those N names inside the checkpoint; from then on the model is a closed-set detector for exactly that list.

At inference the registration call writes the new name list into the model's bookkeeping, including the per-anchor channel count the post-processing reshape expects. The forward pass still produces the trained channel layout. When the two counts differ, the reshape that flattens each feature level cannot divide the tensor evenly and stops with the shape error — on this stack it fires during warmup, on the 640 px dummy pass, before your first real image.

no = 4 + N + M # channels per anchor: box, classes, mask coefficients anchors = (S/8)^2 + (S/16)^2 + (S/32)^2 # S = inference size in pixels elements per level = no x anchors on that level

N is the class count the bookkeeping holds after the registration call, and M is the segmentation head's mask-coefficient count — zero on box-only heads.

Worked example: 441 600 elements against 65 channels

Inputs, each taken from the failing set-up: inference size S = 640 px, the warmup default; a three-level head at strides 8, 16 and 32, so grids of 80 x 80, 40 x 40 and 20 x 20 — 6 400, 1 600 and 400 anchors; five trained classes; one registered name; and an error that expects 65 channels per anchor over a 441 600-element tensor.

The arithmetic:

  1. The failing tensor is the first level, the stride-8 grid: 441 600 / 6 400 = 69 channels actually produced per anchor.
  2. Those 69 split as 4 box + 5 classes + 60 mask coefficients — the trained layout, five class channels wide.
  3. The reshape expects 65 = 4 + 1 + 60: registering one name rewrote the bookkeeping to a single class, four channels fewer.
  4. 65 x 6 400 = 416 000, not 441 600, and 441 600 / 65 = 6 793.8 — not a whole anchor count — so the view is refused.

The decision: produced 69, expected 65, difference 4 — exactly the four names the script dropped, since 5 - 1 = 4. Register five names and both counts read 69; 69 x 6 400 = 441 600 divides cleanly and inference runs. The tolerance is zero: a mismatch of even one name, in either direction, fails this reshape.

Causes in order, with the test that separates them

Rank Cause Test Fix
1 Fewer names registered than trained — this case Compare the registered list length with the YAML count Re-register the full list; filter results
2 More names, or a new vocabulary, on fine-tuned weights Same count check, other direction Prompt the pretrained checkpoint, or retrain with the expanded YAML
3 Right count, different order Diff the name order against the YAML Restore the YAML order; list position assigns result ids
4 Loader pointing at the pretrained checkpoint Print the class names stored in the checkpoint Point the loader at the fine-tuned best weights — machine vision model file paths: fix deployment errors
5 Checkpoint and runtime from different package versions Redo the worked example with your counts Align the training and serving versions of the stack

Rank 3 is the dangerous one:

The fix, step by step

  1. Open the training YAML and copy the names block verbatim — five entries here, 0: OK through 4: heijietou.
  2. Load the fine-tuned checkpoint: the best weights from the training run, not the pretrained file you started from.
  3. Call the registration once per model load with the full list, in YAML order.
  4. Predict, and confirm the results carry ids 0 through 4.
  5. Filter to the class the station needs by id, in post-processing.
  6. Validate on a hand-labeled batch: each id must resolve to the name you expect before the station gates product.

Success looks like this: the warmup pass completes, the first image returns boxes with ids 0-4, and filtering to one id leaves the other detections untouched.

When you need a different vocabulary

Fine-tuning closes the vocabulary — that is its job. Pick your case:

  • Fixed line, fixed defect set — this case. Treat the model as closed-set: register the five names or skip the call, filter at the output, and re-check the mapping after every retrain.
  • Occasional new names, no retraining. Keep the pretrained checkpoint for those runs — its head was never reshaped, so text prompts of any length still work. Run it beside the fine-tuned model, not in place of it.
  • The five classes plus new ones. Retrain with the expanded YAML: six names rebuild the head at 70 channels per anchor (4 + 6 + 60), one channel more, so training time and memory barely move. Training anomaly detection across multiple product classes covers the dataset work that adding classes drags along.

One dataset decision belongs here. The YAML above carries an explicit OK class for good parts; good parts can instead enter as unlabeled background frames, and background images in object detection training: why no labels explains when negatives need no labels at all.

Where the model trains and where it runs

The training run behind the error is modest: a 10.1 million parameter segmentation model, batch 4, AdamW at a 1e-3 learning rate, 150 epochs, on a 48 GB workstation GPU. Parameter count does not size that machine — activations and optimizer state do, which is why small models still fill large cards; why small neural network models use large GPU memory works that math. For the machine itself, GPU workstation sizing for fine-tuning and inference budgets the memory for segmentation models of this size.

Serving is lighter. A 10 million parameter head runs comfortably on fanless vision controllers at line rates. The camera side does not change: the area scan cameras that shoot the line today keep feeding the model, and the camera finder is the place to start when the station is new. When the class set never changes at all, on-device inspection in smart cameras sidesteps the whole vocabulary question.

A controller such as the MV-VC3301H-128G66 carries the retrained head with capacity to spare for the camera links and the reject I/O:

Vision controllersHikrobot MV-VC3301H-128G66 I3-8100, 8GB+128G SSD, No PoE, PCIe expansion supported, Incl. VM6200Pro licenseMV-VC3301H-128G66
LAN ports
5+ LAN ports
USB 3 ports
3-4 USB 3 ports
Light outputs
4 built-in channels
Memory
8 GB
$2,419.00View partBuild a system with it

To compare the range directly:

ComputersVision controllers

Vision computers and fanless industrial PCs

190 models · from $1,119.00

Checklist before the next run

  • Registration list copied from the training YAML — same count, same order.
  • Checkpoint path points at the fine-tuned best weights.
  • One registration call per model load, never per frame.
  • Id-to-name mapping confirmed on a hand-labeled image.
  • Output filtered by class id, not by trimming the vocabulary.
  • Training and serving package versions aligned.
  • Good parts handled deliberately: a labelled OK class or unlabeled background frames, never an accident.

Questions

Can I register only the one class my station needs at inference?

No. The registered count must equal the trained count — five names in this case — or the reshape inside the head fails before the first image. Register all five and filter the results by class id in post-processing. The filter is one lookup per frame, and the model's accuracy on the kept class is unchanged.

What happens if I register six names on the five-class model?

The same crash in the other direction. Bookkeeping expects 4 + 6 + 60 = 70 channels per anchor while the head produces the trained 69, so at 640 px the first level still holds 441 600 elements and 441 600 divided by 70 leaves a remainder. Six names require retraining with a six-entry YAML.

Does the order of the registered names matter?

It does not change whether the model runs, but it changes what every result means. The list assigns class ids by position, so a reordered list re-labels every box without any error message. Keep the training YAML order and confirm the id-to-name mapping on one hand-labeled image before the station acts on detections.

Do box-only detectors fail the same way as segmentation models?

Yes. A box-only head has no mask coefficients, so channels per anchor are 4 + N. A five-class detector produces 9 channels per anchor; registering one name makes the reshape expect 5, and the same view error fires. The fix is identical: register the trained list and filter outputs.

Should the text-prompt registration run every frame?

No. Register once per model load: the call encodes the name list into prompt embeddings and stores them with the model, so repeating it per frame re-runs the text encoder for identical output. Frame code should only predict and filter the results.