imdb-autofill / docs /model_selection.md
SamuelTheophilus's picture
Add multiple models and pricing for each model.
8a67bec
|
Raw
History Blame Contribute Delete
12.2 kB

Model Selection Journey: IMDB AutoFill VLM Pipeline

Context

The IMDB AutoFill pipeline extracts structured product data (brand, manufacturer, weight, country of origin, etc.) from retail product images. Each product has 3-5 images taken from different angles; images are grouped by a dataset label (tag_text) stuck to the edge of the packaging before being sent to the VLM.

The target dataset is 45 products from a Ghanaian retail context: brands imported from China, Indonesia, Vietnam, Cote d'Ivoire, Nigeria, and Sri Lanka, typically sold and distributed by local importers (e.g. LGD LIMITED, FAGIP VENTURES).

Accuracy is measured field-by-field against a ground-truth Excel sheet (eval/eval_from_org.xlsx).


Phase 1: llama3.2-vision:11b (Local, Ollama)

Why we started here: Zero API cost, runs on the local RTX 5090 32GB GPU via Ollama. Fast iteration without rate limits or spend.

Configuration:

  • Model: llama3.2-vision:11b
  • Backend: Ollama (localhost:11434)
  • Concurrency: 4 parallel workers, 1 image per call
  • Image grouping: by tag_text similarity

Results: 41 matched pairs / 45 GT products: 48.1% overall accuracy

Why we moved on:

  • The 11B model struggled to read small edge labels (tag_text) reliably, causing images to split into separate products instead of grouping correctly.
  • Field extraction quality was low: manufacturer, country, fragrance_flavor all scored poorly.
  • The model's vision capability at 11B was simply insufficient for dense retail label text.

Phase 2: qwen2.5vl:32b (Local, Ollama)

Why we tried this: Qwen2.5-VL is a purpose-built vision-language model with significantly better OCR and label-reading capability than llama3.2-vision. At 32B it fits within the 32GB VRAM of the RTX 5090.

Configuration:

  • Model: qwen2.5vl:32b
  • Backend: Ollama (localhost:11434)
  • Concurrency: reduced to 2 (32B model is slower per call)
  • Image grouping: by tag_text similarity (unchanged)

Results: 55 matched pairs / 45 GT products: 51.9% overall accuracy

Note: 55 matched pairs > 45 GT because the model was over-splitting some products (empty tag_text means each image became its own product). Matched pairs counts how many predictions align to a GT row, not unique products.

What improved:

  • Brand and product name accuracy up significantly
  • tag_text extraction more reliable, leading to better image grouping

Why we moved on:

  • Despite better OCR, the model was still leaving tag_text empty on ~30% of images, causing over-splitting (82+ predictions for 45 actual products).
  • Inference speed was slow (32B model, 2 concurrent workers).
  • The accuracy ceiling for local 32B models seemed to be around 52-55% given the visual complexity of the labels.

Phase 3: Gemini 2.5 Flash and 3.5 Flash (Cloud, Google AI)

Why we tried this: Gemini offered a cost-effective path to a stronger frontier model. Integrated via Google's OpenAI-compatible endpoint (https://generativelanguage.googleapis.com/v1beta/openai/), no new SDK required.

Configuration:

  • Backend: gemini (via VLM_BACKEND=gemini)
  • Models tested: gemini-2.5-flash, gemini-3.5-flash
  • Batch size: 8 images per API call

Results:

Model Matched pairs Overall
Gemini 2.5 Flash 31 47.8%
Gemini 3.5 Flash 32 41.6%

Why we moved on:

  • Gemini models consistently failed to read edge labels (tag_text) even with explicit prompting, causing severe over-splitting (116-122 predictions for 45 products).
  • With only 31-32 matched pairs vs. 45 GT products, many products simply could not be evaluated.
  • The larger Gemini 3.5 model actually scored lower than 2.5, likely a different training emphasis.
  • Overall accuracy was worse than the local qwen model.

Phase 4: GPT-5.5 (OpenAI, Cloud)

Why we chose this: After Gemini's failure on tag_text reading, GPT-5.5 was evaluated as the strongest available vision model. It demonstrated noticeably better spatial awareness and OCR of small printed text, including the edge dataset labels.

Configuration:

  • Model: gpt-5.5-2026-04-23 via OpenAI API
  • Backend: openai (via VLM_BACKEND=openai)
  • Batch size: 8 images per API call (all session images in one request)
  • API params: max_completion_tokens=4096, no temperature (GPT-5.5 only supports default)
  • Response format: {"type": "json_object"}

Key integration notes:

  • GPT-5.5 does not support max_tokens; use max_completion_tokens
  • GPT-5.5 does not support custom temperature; only the default (1) is supported
  • Images are sent as base64 data URLs in the message content array

v1: Initial GPT-5.5 run

Results: 45 matched pairs / 45 GT products: 59.5% overall accuracy

All 45 GT products matched; tag_text reading was reliable enough to group images correctly. This was the first run where we achieved a 1:1 mapping between predictions and GT.

v2: Prompt improvements

Three targeted changes based on failure analysis of v1 predictions:

  1. Manufacturer prompt: Updated to emphasise local African importer/distributor over foreign factory. Added examples of the expected company name format (LGD LIMITED, FAGIP VENTURES, SISTER SARDINE & MACKEREL VENTURES). Directed model to look for "Manufactured by", "Manufactured for", "Imported by", "Distributed by", "Packed by" phrasing.

  2. Country normalisation: Added P.R.C., PRC, P.R.C, PEOPLES REPUBLIC OF CHINA, PEOPLE'S REPUBLIC OF CHINA -> CHINA mappings to backend/normalizer.py. Many products from Chinese factories use the PRC abbreviation on pack. Country accuracy: 38.5% -> 50%.

  3. Fragrance/flavor broadening: Expanded the field definition to cover food flavors (BEEF, STEW RAGOUT, SOUP, NOODLES, CHICKEN), beverage flavors (COLA, LEMON, MALT, MULTI FRUIT), cosmetic fragrances (ROSE, CITRUS, JAPANESE CAMELLIA), and detergent scents (POWDER, FRESH). The GT definition is broader than traditional "fragrance"; any taste/scent descriptor on pack counts.

Results: 41 matched pairs / 45 GT products: 63.4% overall accuracy

Note: matched pairs dropped from 45 to 41 in v2 (minor grouping regression to investigate). Overall accuracy still improved because field-level extraction quality went up.

v2 Field breakdown

Field Accuracy Notes
category_type 90.0% Strong
brand 85.4% Strong
packaging_type 82.9% Strong
product_name 78.0% Good
weight 63.4% Good
barcode 60.0% Good
country_of_origin 50.0% PRC->CHINA fix helped
fragrance_flavor 42.3% Broadened definition helped
manufacturer 39.0% Still reading foreign factory
tagline 0.0% Regressed: definition too strict
promotional_messages 0.0% Not yet extracted
addons 0.0% Not yet extracted

Why GPT-5.5 was promising:

  1. tag_text reliability: reads small edge labels that other models miss, enabling correct image grouping
  2. Structured JSON compliance: consistently returns valid JSON arrays without retry failures
  3. OCR quality: correctly reads dense small-print text (manufacturer, country, barcode) on diverse packaging
  4. Spatial awareness: correctly identifies which text is on the front vs. back vs. edge of a product

Why we moved on:

  • 6 sessions (8-image batches) consistently returned empty content across all 3 retries, likely OpenAI's content filter silently refusing certain product images
  • Each failed session took 1300s before giving up (3x retry x ~430s/attempt)
  • 34/45 products matched due to the 6 total failures

Architecture

The backend is designed to be model-agnostic via VLM_BACKEND env var:

VLM_BACKEND=anthropic  -> Claude Sonnet 4.6 (current default)
VLM_BACKEND=openai     -> GPT-5.5
VLM_BACKEND=gemini     -> Gemini 2.5 Flash via OpenAI-compat endpoint

Batch size is set to 8 images per call for all cloud backends.


API Pricing (as of June 2026)

Token rates per 1M tokens

Model Input Output Batch Input Batch Output
Claude Sonnet 4.6 $3.00 $15.00 $1.50 $7.50
GPT-5.5 $5.00 $30.00 $2.50 $15.00
Gemini 2.5 Flash $0.30 $2.50 $0.15 $1.25

Batch pricing (50% off standard) applies when using the provider's async Batch API, available for all three models in the Bulk Batch tab.

Estimated cost per image

Each extraction call sends approximately 1,560 input tokens (image tiles + shared prompt overhead across the batch of 8) and receives approximately 300 output tokens (JSON for one product item).

Model Quick Upload Bulk Batch
Claude Sonnet 4.6 ~$0.0090 ~$0.0045
GPT-5.5 ~$0.0170 ~$0.0085
Gemini 2.5 Flash ~$0.0012 ~$0.0006

Derivation (Claude example):

input:  1,560 tokens * ($3.00 / 1,000,000) = $0.00468
output:   300 tokens * ($15.00 / 1,000,000) = $0.00450
total per image                              = $0.00918  (~$0.009)
batch (50% off)                              = $0.00459  (~$0.0045)

Actual costs will vary with image resolution and extraction complexity. The per-row cost shown in the results table reflects real token usage from each API call.


Phase 5: Claude Sonnet 4.6 (Anthropic, Cloud) [Current]

Why we chose this: After diagnosing the GPT-5.5 empty content failures, Claude Sonnet 4.6 was tested on the 6 failed sessions. All 6 completed successfully in 15-75s each, with no empty responses. The decision was made to switch the full pipeline to Claude.

Configuration:

  • Model: claude-sonnet-4-6 via Anthropic SDK
  • Backend: anthropic (via VLM_BACKEND=anthropic)
  • Batch size: 8 images per API call (unchanged)
  • API params: max_tokens=4096, system prompt as top-level system parameter
  • Images: base64-encoded JPEG blocks in user content array

Key integration notes:

  • Anthropic's native SDK uses a different image format than OpenAI (source.type: "base64" blocks)
  • System prompt is passed as the system parameter, not as a role: "system" message
  • No response_format equivalent; prompt instructs JSON output directly
  • Message Batches API (50% off): eval/run_eval_batch.py submits all 42 VLM requests as one batch

Eval progression (Claude Sonnet 4.6):

Version Key changes Matched Overall
v1 (baseline) Switch from GPT-5.5 41 76.3%
v2 Country: company address > "Made in" + Example 6 42 76.7%
v3 Tagline: non-strict + shortest descriptor; brand accent norm 42 77.3%
v4 fragrance_flavor: non-strict + detergent POWDER; manufacturer normalizer 42 79.0%
v5 Weight normalizer (GMS->G, G->KG, ML->L); addons: non-strict 42 79.6%
v6 Brand: trade name vs manufacturer distinction 44 79.9%

v6 Field Breakdown (final, 44/45 matched)

Field Accuracy
variant 100.0%
brand 97.7%
category_type 90.9%
country_of_origin 88.9%
packaging_type 88.6%
product_name 86.4%
weight 81.8%
addons 75.0%
tagline 72.7%
fragrance_flavor 73.1%
manufacturer 63.6%
barcode 58.1%
promotional_messages 33.3%
OVERALL 79.9%

Why Claude Sonnet 4.6 Wins

  1. Zero empty-content failures: processed all 41 sessions (including 8-10 image batches) with no silent refusals
  2. Speed: 15-75s per session vs 300-1300s for GPT-5.5
  3. Instruction following: responds well to field priority rules (tagline shortest-first, country company address, brand trade name)
  4. Cost: Anthropic Batch API at 50% discount makes full evals roughly 2x cheaper than GPT-5.5 standard

Remaining hard limits (not model-dependent):

  • Manufacturer (63.6%): GT is inconsistent; sometimes wants the manufacturer, sometimes the local distributor
  • Barcode (58.1%): 16 sessions have no barcode visible in any image
  • Promotional messages (33.3%): only 3 GT products have promos, high variance