--- license: other license_name: lfm-open-license-v1.0 license_link: https://huggingface.co/LiquidAI/LFM2.5-VL-450M/blob/main/LICENSE base_model: LiquidAI/LFM2.5-VL-450M pipeline_tag: image-text-to-text library_name: transformers.js tags: - ad-detection - ad-blocking - onnx - webgpu - transformers-js - lfm2.5 - vision-language-model - browser --- # Minus-v0.1 — a vision model that blocks ads by *looking* at them ![Minus covering real ads on theverge.com with Spanish flashcards](assets/minus_theverge.png) *Minus-v0.1 running in the Chrome extension on theverge.com: the banner and sidebar ad slots are covered by language flashcards, each tagged with the model's confidence. The article itself is untouched.* **Minus-v0.1** is a 450M-parameter vision–language model fine-tuned to answer one question about an image: **"Is this an advertisement?"** It powers the [Minus Chrome extension](https://github.com/garagehq/Minus-chrome-extension), where it runs **entirely in the browser** (transformers.js + ONNX Runtime Web on WebGPU) and covers detected ads with language flashcards — no filter lists, no servers, no telemetry. It is the browser sibling of the [minus](https://github.com/garagehq/minus) HDMI ad-blocking device. Instead of matching URLs or DOM patterns like a classic ad blocker, Minus classifies **pixels**: screenshots of page elements, video frames, and iframes. That means it generalizes to first-party ads, sponsored tiles, native placements and streaming-TV commercials that filter lists can't see — and it keeps working when ad-tech rotates domains. - **Base model:** [LiquidAI/LFM2.5-VL-450M](https://huggingface.co/LiquidAI/LFM2.5-VL-450M) - **Task:** binary ad / not-ad classification via a single-token answer (`Yes` / `No`), scored as `p(ad) = P(Yes)` - **Prompt:** `Is this an advertisement? Answer Yes or No.` - **Format in this repo:** ONNX, quantized for the browser (~430 MB total): q4 decoder, q8 token embeddings, q8 vision encoder. **WebGPU required** (the q4 decoder uses `GatherBlockQuantized`, which onnxruntime-web's WASM backend doesn't implement). ## How it was trained Minus-v0.1 is **iteration 28** of a months-long training campaign (the first 27 iterations were exploration: base-model selection between FastVLM / SmolVLM / CLIP-style classifiers / LFM2.5-VL, then successive rounds of hard-negative and hard-positive mining, each gated by frozen benchmarks and *live* in-browser soak tests). **Recipe (frozen across late iterations):** LoRA (r=16, α=32) on the language-model blocks only — vision tower and projector stay frozen — lr 2e-4, 3 epochs, effective batch 32. Trained on a single NVIDIA Jetson AGX Thor (128 GB unified memory). The LoRA is merged before export. **Data (not released):** ~81,600 training samples across two domains: - **Streaming TV** — frames sampled by the minus HDMI device during real viewing sessions: commercials vs. program content, label-audited before training. - **Web pages** — display/banner ad creatives vs. hard negatives mined from real browsing: editorial content, product photography (e-commerce tiles that *look* like ads), site self-promo and UI elements, cookie/consent banners, chat widgets, site headers, and scale-jittered variants of all of these to survive browser resampling. Roughly **45% of the web-ad positives are native/chum-box style** (Taboola/Outbrain-like), which classic blockers struggle with. ## Evaluation All gates are held-out sets that were **frozen before** this iteration trained; the live number comes from headed-browser soaks on real sites. | benchmark | result | |---|---| | Streaming holdout (1,956 frames, hand-verified) | **99.90%** ad recall / **98.06%** non-ad recall | | Static-web bench (999 images) @ shipping gate | **98.0%** ad recall, **11** false positives | | Product-image FP holdout (199 ad-look-alike product shots) | **1/199** false positives | | Live in-browser precision (month of soak tests, real sites) | **~90–94%** of covered elements are actually ads | The static-web PR curve dominates or ties the previous production model at **every** operating point with non-ad recall ≥ 95%. Every word shown over a blocked ad feeds the extension's built-in spaced-repetition review — the ads you don't watch become vocabulary you keep: ![The extension's spaced-repetition review page](assets/minus_review.png) ## Using the model ### transformers.js (what the extension does) ```js import { AutoProcessor, AutoModelForVision2Seq, RawImage } from "@huggingface/transformers"; const processor = await AutoProcessor.from_pretrained("TheGarageDev/Minus-v0.1"); const model = await AutoModelForVision2Seq.from_pretrained("TheGarageDev/Minus-v0.1", { device: "webgpu", // REQUIRED — the q4 decoder is WebGPU-only dtype: { embed_tokens: "q8", vision_encoder: "q8", decoder_model_merged: "q4" }, }); const image = await RawImage.read(imageUrlOrCanvas); const messages = [{ role: "user", content: [ { type: "image" }, { type: "text", text: "Is this an advertisement? Answer Yes or No." }, ]}]; const prompt = processor.apply_chat_template(messages, { add_generation_prompt: true }); const inputs = await processor(prompt, image); // One decode step; compare the logits of "Yes" vs "No" for a calibrated p(ad). const { logits } = await model({ ...inputs }); // p_ad = softmax over {logit("Yes"), logit("No")} — see the extension's engine // (offscreen.js) for the exact token ids + scoring code. ``` **Thresholding matters.** The extension does not block at p ≥ 0.5 — it uses per-context gates chosen from the PR curve: **0.60** for elements with ad context (iframes / ad-slot containers) and **0.88** for bare images. If you deploy this model, pick your own operating point for your precision target. ### Python (reference / server-side) The ONNX graphs run under `onnxruntime` too (CPU/CUDA execution providers support the quantized ops). For full-precision experiments, start from the base model and the recipe above. ## Limitations & honest notes - **Binary classifier, not a chat model.** The fine-tune deliberately collapses the model onto Yes/No answers for one prompt; don't expect general VLM behavior. - **Domain:** English-centric web pages and US/EU streaming TV. Ads in other scripts/markets will work worse. - **Preprocessing sensitivity:** browser-side image resampling can shift scores on borderline UI-like inputs (we train with scale-jitter to mitigate; a handful of known residuals remain, e.g. certain consent banners). - **Screenshots of ads vs. ads:** the model sees pixels. Editorial *about* an ad, or a screenshot of an ad inside an article, can legitimately score high. - The training dataset is **not** released. ## License Inherits the **[LFM Open License v1.0](https://huggingface.co/LiquidAI/LFM2.5-VL-450M/blob/main/LICENSE)** from the base model ([LiquidAI/LFM2.5-VL-450M](https://huggingface.co/LiquidAI/LFM2.5-VL-450M)). The Minus extension source is at [garagehq/Minus-chrome-extension](https://github.com/garagehq/Minus-chrome-extension). ## Support Minus is free and open-source — the model above, the [Chrome extension](https://github.com/garagehq/Minus-chrome-extension), and the device it grew out of. If it saves you from a few autoplay pre-rolls, you can buy me a coffee ☕: Buy Me A Coffee > **[buymeacoffee.com/cyrilengmann](https://buymeacoffee.com/cyrilengmann)**