Image-Text-to-Text
Transformers.js
ONNX
lfm2_vl
ad-detection
ad-blocking
webgpu
transformers-js
lfm2.5
vision-language-model
browser
conversational
Instructions to use TheGarageDev/Minus-v0.1 with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- Transformers.js
How to use TheGarageDev/Minus-v0.1 with Transformers.js:
// npm i @huggingface/transformers import { pipeline } from '@huggingface/transformers'; // Allocate pipeline const pipe = await pipeline('image-text-to-text', 'TheGarageDev/Minus-v0.1');
| 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-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: | |
|  | |
| ## 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 ☕: | |
| <a href="https://buymeacoffee.com/cyrilengmann" target="_blank"><img src="https://cdn.buymeacoffee.com/buttons/v2/default-yellow.png" alt="Buy Me A Coffee" height="50" width="210"></a> | |
| > **[buymeacoffee.com/cyrilengmann](https://buymeacoffee.com/cyrilengmann)** | |