A fine-tuned Qwen3-VL-2B-Instruct model for visual affordance detection from egocentric (first-person view) images. The model identifies what actions can be performed on visible objects in a scene.
1import os
2os.environ["TORCHDYNAMO_DISABLE"]="1"# Required on Windows34from unsloth import FastVisionModel
5from qwen_vl_utils import process_vision_info
67# Load model with LoRA adapter8model, tokenizer = FastVisionModel.from_pretrained(9"Kavin60606/qwen3-vl-2b-affordance-finetuned",10 load_in_4bit=True,11 max_seq_length=2048,12)13FastVisionModel.for_inference(model)1415# Prepare input16messages =[17{18"role":"system",19"content":[{"type":"text","text":"You are a visual affordance detection assistant. Given a first-person view image, identify what actions can be performed on the visible objects."}],20},21{22"role":"user",23"content":[24{"type":"image","image":"path/to/your/image.jpg"},25{"type":"text","text":"What affordances are present in this scene?"},26],27},28]2930# Generate31input_text = tokenizer.apply_chat_template(32 messages, tokenize=False, add_generation_prompt=True33)34image_inputs, _ = process_vision_info(messages)35inputs = tokenizer(36 text=[input_text],37 images=image_inputs,38 return_tensors="pt",39 padding=True,40).to(model.device)4142output_ids = model.generate(**inputs, max_new_tokens=256)43new_tokens = output_ids[0][inputs["input_ids"].shape[1]:]44response = tokenizer.decode(new_tokens, skip_special_tokens=True)45print(response)
Using with Transformers + PEFT (without Unsloth)
python
1import os
2os.environ["TORCHDYNAMO_DISABLE"]="1"34import torch
5from transformers import AutoProcessor, Qwen3VLForConditionalGeneration
6from peft import PeftModel
7from qwen_vl_utils import process_vision_info
89# Load base model in 4-bit10from transformers import BitsAndBytesConfig
1112bnb_config = BitsAndBytesConfig(13 load_in_4bit=True,14 bnb_4bit_quant_type="nf4",15 bnb_4bit_compute_dtype=torch.bfloat16,16)1718base_model = Qwen3VLForConditionalGeneration.from_pretrained(19"Qwen/Qwen3-VL-2B-Instruct",20 quantization_config=bnb_config,21 device_map="auto",22)2324# Load LoRA adapter25model = PeftModel.from_pretrained(base_model,"Kavin60606/qwen3-vl-2b-affordance-finetuned")26processor = AutoProcessor.from_pretrained("Qwen/Qwen3-VL-2B-Instruct")2728# Prepare and run inference29messages =[30{31"role":"user",32"content":[33{"type":"image","image":"path/to/image.jpg"},34{"type":"text","text":"What affordances are present in this scene?"},35],36},37]3839text = processor.apply_chat_template(messages, tokenize=False, add_generation_prompt=True)40image_inputs, _ = process_vision_info(messages)41inputs = processor(text=[text], images=image_inputs, return_tensors="pt", padding=True).to(model.device)4243output = model.generate(**inputs, max_new_tokens=256)44print(processor.decode(output[0], skip_special_tokens=True))
Running on Mac (Apple Silicon M1/M2/M3/M4)
Note: Unsloth and bitsandbytes (4-bit quantization) do not support macOS. Use the standard transformers + peft approach with MPS backend instead. The model loads in float16 (~4GB), which fits on any Mac with 16GB+ unified memory.
Requirements (Mac):
bash
1pip install torch torchvision
2pip install transformers>=4.57.1 peft>=0.14.0 accelerate
3pip install qwen-vl-utils Pillow
4# Do NOT install bitsandbytes or unsloth on Mac
Inference (Mac):
python
1import os
2os.environ["TORCHDYNAMO_DISABLE"]="1"34import torch
5from transformers import AutoProcessor, Qwen3VLForConditionalGeneration
6from peft import PeftModel
7from qwen_vl_utils import process_vision_info
89# Load base model in float16 on MPS (Apple GPU)10base_model = Qwen3VLForConditionalGeneration.from_pretrained(11"Qwen/Qwen3-VL-2B-Instruct",12 torch_dtype=torch.float16,13 device_map="mps",14)1516# Load LoRA adapter17model = PeftModel.from_pretrained(18 base_model,19"Kavin60606/qwen3-vl-2b-affordance-finetuned",20)21processor = AutoProcessor.from_pretrained("Qwen/Qwen3-VL-2B-Instruct")2223# Prepare input24messages =[25{26"role":"user",27"content":[28{"type":"image","image":"path/to/your/image.jpg"},29{"type":"text","text":"What affordances are present in this scene?"},30],31},32]3334text = processor.apply_chat_template(messages, tokenize=False, add_generation_prompt=True)35image_inputs, _ = process_vision_info(messages)36inputs = processor(37 text=[text], images=image_inputs, return_tensors="pt", padding=True38).to("mps")3940output = model.generate(**inputs, max_new_tokens=256)41print(processor.decode(output[0], skip_special_tokens=True))
Platform Compatibility Summary
Platform
Inference
Fine-Tuning
Notes
NVIDIA GPU (Linux/Windows)
Full support
Full support (QLoRA 4-bit)
Recommended. 8GB+ VRAM.
Apple Silicon Mac (M1-M4)
Works (MPS, float16)
LoRA only (no QLoRA)
16GB+ unified memory recommended. No bitsandbytes/Unsloth.
Intel Mac
CPU only, very slow
Not practical
Not recommended.
CPU only (any OS)
Works but slow
Not practical
For testing only. Use device_map="cpu".
Repository Structure
.
├── adapter_config.json # LoRA adapter configuration
├── adapter_model.safetensors # Fine-tuned LoRA weights (~35MB)
├── tokenizer.json # Tokenizer files
├── tokenizer_config.json
├── preprocessor_config.json
├── chat_template.jinja
├── trainer_state.json # Full training log history
│
├── checkpoints/
│ ├── checkpoint-2000/ # Checkpoint at step 2000
│ └── checkpoint-2145/ # Final checkpoint (step 2145)
│
├── dataset/
│ ├── annotations.jsonl # Original annotations (3,177 entries)
│ ├── train_msswift.jsonl # Training data - MS-SWIFT format (2,859)
│ ├── val_msswift.jsonl # Validation data - MS-SWIFT format (318)
│ ├── train_llamafactory.json # Training data - LLaMA-Factory format
│ ├── val_llamafactory.json # Validation data - LLaMA-Factory format
│ └── dataset_info.json # LLaMA-Factory dataset registry
│
├── results/
│ ├── training_curves.png # Training loss, grad norm, LR curves
│ ├── loss_by_epoch.png # Loss over epochs
│ ├── inference_comparison.png # Visual GT vs prediction comparison
│ └── inference_results.json # Full inference results (20 samples)
│
├── train.py # Training script (Unsloth + TRL)
├── inference.py # Inference script
├── analyze_and_infer.py # Analysis + inference + visualization
└── prepare_finetune_data.py # Dataset preparation script
Training Configuration
python
1# Key hyperparameters2MODEL ="unsloth/Qwen3-VL-2B-Instruct"3QUANTIZATION ="4-bit NF4"4LORA_RANK =165LORA_ALPHA =326BATCH_SIZE =17GRADIENT_ACCUMULATION =4# effective batch = 48LEARNING_RATE =2e-49LR_SCHEDULER ="cosine"10WARMUP_RATIO =0.111EPOCHS =312MAX_SEQ_LENGTH =204813OPTIMIZER ="adamw_8bit"14PRECISION ="bfloat16"15GRADIENT_CHECKPOINTING =True16VISION_ENCODER ="frozen"# ViT frozen to save VRAM