Instructions to use aacudad/AnomalyThink-Qwen3-VL-8B-SFT-GRPO with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- Transformers
How to use aacudad/AnomalyThink-Qwen3-VL-8B-SFT-GRPO with Transformers:
# Use a pipeline as a high-level helper from transformers import pipeline pipe = pipeline("image-text-to-text", model="aacudad/AnomalyThink-Qwen3-VL-8B-SFT-GRPO") messages = [ { "role": "user", "content": [ {"type": "image", "url": "https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/p-blog/candy.JPG"}, {"type": "text", "text": "What animal is on the candy?"} ] }, ] pipe(text=messages)# pip install -U transformers accelerate # Load model directly from transformers import AutoProcessor, AutoModelForMultimodalLM processor = AutoProcessor.from_pretrained("aacudad/AnomalyThink-Qwen3-VL-8B-SFT-GRPO") model = AutoModelForMultimodalLM.from_pretrained("aacudad/AnomalyThink-Qwen3-VL-8B-SFT-GRPO", device_map="auto") messages = [ { "role": "user", "content": [ {"type": "image", "url": "https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/p-blog/candy.JPG"}, {"type": "text", "text": "What animal is on the candy?"} ] }, ] inputs = processor.apply_chat_template( messages, add_generation_prompt=True, tokenize=True, return_dict=True, return_tensors="pt", ).to(model.device) outputs = model.generate(**inputs, max_new_tokens=256) print(processor.decode(outputs[0][inputs["input_ids"].shape[-1]:])) - Notebooks
- Google Colab
- Kaggle
- Local Apps Settings
- vLLM
How to use aacudad/AnomalyThink-Qwen3-VL-8B-SFT-GRPO with vLLM:
Install from pip and serve model
# Install vLLM from pip: pip install vllm # Start the vLLM server: vllm serve "aacudad/AnomalyThink-Qwen3-VL-8B-SFT-GRPO" # Call the server using curl (OpenAI-compatible API): curl -X POST "http://localhost:8000/v1/chat/completions" \ -H "Content-Type: application/json" \ --data '{ "model": "aacudad/AnomalyThink-Qwen3-VL-8B-SFT-GRPO", "messages": [ { "role": "user", "content": [ { "type": "text", "text": "Describe this image in one sentence." }, { "type": "image_url", "image_url": { "url": "https://cdn.britannica.com/61/93061-050-99147DCE/Statue-of-Liberty-Island-New-York-Bay.jpg" } } ] } ] }'Use Docker
docker model run hf.co/aacudad/AnomalyThink-Qwen3-VL-8B-SFT-GRPO
- SGLang
How to use aacudad/AnomalyThink-Qwen3-VL-8B-SFT-GRPO with SGLang:
Install from pip and serve model
# Install SGLang from pip: pip install sglang # Start the SGLang server: python3 -m sglang.launch_server \ --model-path "aacudad/AnomalyThink-Qwen3-VL-8B-SFT-GRPO" \ --host 0.0.0.0 \ --port 30000 # Call the server using curl (OpenAI-compatible API): curl -X POST "http://localhost:30000/v1/chat/completions" \ -H "Content-Type: application/json" \ --data '{ "model": "aacudad/AnomalyThink-Qwen3-VL-8B-SFT-GRPO", "messages": [ { "role": "user", "content": [ { "type": "text", "text": "Describe this image in one sentence." }, { "type": "image_url", "image_url": { "url": "https://cdn.britannica.com/61/93061-050-99147DCE/Statue-of-Liberty-Island-New-York-Bay.jpg" } } ] } ] }'Use Docker images
docker run --gpus all \ --shm-size 32g \ -p 30000:30000 \ -v ~/.cache/huggingface:/root/.cache/huggingface \ --env "HF_TOKEN=<secret>" \ --ipc=host \ lmsysorg/sglang:latest \ python3 -m sglang.launch_server \ --model-path "aacudad/AnomalyThink-Qwen3-VL-8B-SFT-GRPO" \ --host 0.0.0.0 \ --port 30000 # Call the server using curl (OpenAI-compatible API): curl -X POST "http://localhost:30000/v1/chat/completions" \ -H "Content-Type: application/json" \ --data '{ "model": "aacudad/AnomalyThink-Qwen3-VL-8B-SFT-GRPO", "messages": [ { "role": "user", "content": [ { "type": "text", "text": "Describe this image in one sentence." }, { "type": "image_url", "image_url": { "url": "https://cdn.britannica.com/61/93061-050-99147DCE/Statue-of-Liberty-Island-New-York-Bay.jpg" } } ] } ] }' - Docker Model Runner
How to use aacudad/AnomalyThink-Qwen3-VL-8B-SFT-GRPO with Docker Model Runner:
docker model run hf.co/aacudad/AnomalyThink-Qwen3-VL-8B-SFT-GRPO
AnomalyThink-Qwen3-VL-8B-SFT-GRPO
The Qwen3-VL-8B SFT model refined with GRPO. DS-MVTec 87.14 / VisA 72.39 under the training prompt, the best DS-MVTec score of any Qwen model in the project.
Post-submission research artefact. The thesis reports Qwen3-VL-8B only as a corpus-transfer check (base 78.68 / 64.45, trained on the Qwen2.5-VL KCR corpus 85.82 / 76.45, Table 6.13). The three Qwen3-VL-8B models in this family (AnomalyThink-Qwen3-VL-8B-SFT, AnomalyThink-Qwen3-VL-8B-SFT-GRPO, AnomalyThink-Qwen3-VL-8B-KCR) were trained after the thesis was submitted, in September 2026, by running the thesis recipe end to end on this backbone. They are not thesis results and are released for reproducibility.
Results (MMAD subsets, balanced accuracy, strict scoring)
| Model | DS-MVTec | VisA |
|---|---|---|
| Qwen3-VL-8B-Instruct base, zero-shot | 78.68 | 64.45 |
| AnomalyThink-Qwen3-VL-8B-SFT (6K Gemini traces, epoch 1) | 80.31 | 67.32 |
| AnomalyThink-Qwen3-VL-8B-SFT-GRPO (epoch 1, training prompt) | 87.14 | 72.39 |
| AnomalyThink-Qwen3-VL-8B-KCR (own corrected rollouts, epoch 2) | 84.39 | 76.63 |
| This model | 87.14 | 72.39 |
Evaluation protocol. One harness for every row: the DS-MVTec (1,670 images) and VisA (2,141 images) subsets of MMAD, one image per prompt, the training prompt with the one-line system message "Please answer by yes or no", greedy decoding, at most 1,024 new tokens, images capped at 262,144 pixels, vLLM generation. Balanced accuracy is the mean of sensitivity and specificity. Strict scoring: a generation with no parsable <answer> counts as wrong. Per-sample evaluation files are next to the weights (eval_*.json).
The pattern of the thesis reproduces on this backbone: GRPO carries DS-MVTec, and the corrected corpus built from the GRPO policy's rollouts carries VisA, where 76.63 is the highest VisA score of any model trained in the project.
Training
- Initialisation: AnomalyThink-Qwen3-VL-8B-SFT (epoch 1 of the 6K SFT), which is also the fixed reference policy.
- Recipe: the thesis GRPO configuration unchanged. Two reward functions summed with unit weight, a binary full-schema format reward and an accuracy reward (verdict plus the mean of a Nomic-embedding type similarity and a 3x3 location cell match), maximum 3.0. Group size 4, clipping 0.2, KL coefficient 0 (monitored), learning rate 1e-6, effective batch 8, prompt set of 4,236 class-balanced Real-IAD images, images capped at 480,000 pixels, 2 epochs of 530 steps, all parameters trainable. This is checkpoint-530, the end of epoch 1; epoch 2 (checkpoint-1060) scored 84.89 / 69.90.
- Prompt: GRPO trains on the fixed question "Are there any defects in the query image?" with no system message. Under that prompt this checkpoint scores 86.52 / 73.90; the table reports the training-prompt score so that it is comparable with the SFT and KCR rows.
What Keep-Correct-Revise (KCR) means
An SFT plus GRPO policy is sampled eight times on every training image. A trace is kept when the verdict is right and a Gemini-3-Flash judge finds the reasoning grounded, corrected by the teacher when every rollout was wrong, and revised by the teacher when the verdict was right but the reasoning was weakly grounded. A fresh model is then fine-tuned from the base weights on the curated corpus, so the final model is supervised and the reinforcement-learned policy is the source of its training data.
Usage
The checkpoint was saved with transformers 5.0.0 and loads directly with transformers>=5.0:
from transformers import AutoProcessor, AutoModelForImageTextToText
import torch
repo = "aacudad/AnomalyThink-Qwen3-VL-8B-SFT-GRPO"
processor = AutoProcessor.from_pretrained(repo)
model = AutoModelForImageTextToText.from_pretrained(repo, torch_dtype=torch.bfloat16, device_map="auto")
msgs = [{"role": "system", "content": "Please answer by yes or no"},
{"role": "user", "content": [{"type": "image", "image": "part.jpg"},
{"type": "text", "text": "Analyze the provided image of the pcb. Determine if there are any anomalies present. If an anomaly is detected, specify its type and location, and provide a detailed reasoning for your conclusion."}]}]
inputs = processor.apply_chat_template(msgs, add_generation_prompt=True, tokenize=True, return_dict=True, return_tensors="pt").to(model.device)
out = model.generate(**inputs, max_new_tokens=1024, do_sample=False)
print(processor.decode(out[0][inputs["input_ids"].shape[1]:], skip_special_tokens=True))
The output is <think>...</think> followed, for anomalies, by <location>, <type> and <answer>Yes</answer>, or <answer>No</answer> for a normal part. For transformers 4.57 or vLLM 0.10.x use the files in transformers4_vllm/ (the stock Qwen3-VL-8B-Instruct tokenizer and processor files and a config with rope_theta written into text_config) next to model.safetensors; this is how every number above was produced.
Intended use and limitations
Research artefact for explainable industrial anomaly detection on top-down product images. Single seed, no human evaluation of the explanations, selection on DS-MVTec with VisA reported at the same checkpoint. The public benchmark images may have been seen during backbone pretraining, which cannot be ruled out for any model in this comparison.
Citation
@mastersthesis{acudad2026reasoning,
author = {Acudad, A.},
title = {Reasoning-Enhanced Vision-Language Models for Explainable Industrial Anomaly Detection},
school = {Delft University of Technology},
year = {2026},
type = {Master's thesis},
url = {https://resolver.tudelft.nl/uuid:65c62420-79c0-447f-b095-7fb11d4474fc}
}
Thesis: https://resolver.tudelft.nl/uuid:65c62420-79c0-447f-b095-7fb11d4474fc. Code and evaluation files: https://github.com/aacudad/IAD-VLMs.
License
Apache-2.0, derived from Qwen/Qwen3-VL-8B-Instruct.
- Downloads last month
- 37
Model tree for aacudad/AnomalyThink-Qwen3-VL-8B-SFT-GRPO
Base model
Qwen/Qwen3-VL-8B-Instruct