Downloads · 30 days
69
6% of all-time downloads
hungvtm/qwen3vl-food-lora
qwen3vl-food-lora is a image-text-to-text model from hungvtm. Use it for the image-text-to-text task on the model card, and read the license before you ship it in a product. It is set up for peft. The card lists the license as apache-2.0.
A LoRA adapter fine-tuning Qwen3-VL-4B-Instruct (4-bit, via Unsloth) to analyze a food photo and return a structured description: dish name, food type, ingredients, cooking method, portion size, and nutrition (calorie…
Downloads · 30 days
69
6% of all-time downloads
All-time downloads
1.2K
Public
Repo size
144 MB
Likes
2
Public
Click a slice to open those files.
.safetensors132 MB · 92%
From the Hugging Face model README
A LoRA adapter fine-tuning Qwen3-VL-4B-Instruct (4-bit, via Unsloth) to analyze a food photo and return a structured description: dish name, food type, ingredients, cooking method, portion size, and nutrition (calories, protein, fat, carbohydrates).
Trained on Codatta/MM-Food-100K, 100K real user-submitted food photos spanning homemade, restaurant, raw, and packaged foods.
Full write-up: Building a Food Nutrition Estimation AI
The model was trained on a single fixed instruction, so use it verbatim for best results:
Analyze this food image and provide a complete description including:
dish name, food type, main ingredients, cooking method, portion sizes,
and nutritional information (calories, protein, fat, carbohydrates).
Example output:
**Dish Name:** Vegetable Rice
**Food Type:** Homemade food
**Ingredients:** rice, green onions, mushrooms, potatoes
**Cooking Method:** boiled and mixed
**Portion Size:** rice:300g, vegetables:100g
**Nutritional Information:** Calories: 350.0 kcal, Protein: 10.0 g, Fat: 5.0 g, Carbohydrates: 60.0 g
from unsloth import FastVisionModel
from transformers import TextStreamer
from PIL import Image
model, tokenizer = FastVisionModel.from_pretrained(
model_name="unsloth/Qwen3-VL-4B-Instruct-bnb-4bit",
load_in_4bit=True,
)
model.load_adapter("hungvtm/qwen3vl-food-lora")
FastVisionModel.for_inference(model)
FOOD_PROMPT = (
"Analyze this food image and provide a complete description including: "
"dish name, food type, main ingredients, cooking method, portion sizes, "
"and nutritional information (calories, protein, fat, carbohydrates)."
)
image = Image.open("food.jpg").convert("RGB")
messages = [{"role": "user", "content": [{"type": "image"}, {"type": "text", "text": FOOD_PROMPT}]}]
input_text = tokenizer.apply_chat_template(messages, add_generation_prompt=True)
inputs = tokenizer(image, input_text, add_special_tokens=False, return_tensors="pt").to("cuda")
streamer = TextStreamer(tokenizer, skip_prompt=True)
_ = model.generate(**inputs, streamer=streamer, max_new_tokens=512, temperature=0.3, min_p=0.1)
from transformers import AutoProcessor, Qwen3VLForConditionalGeneration
from peft import PeftModel
from PIL import Image
import torch
base_id = "unsloth/Qwen3-VL-4B-Instruct-bnb-4bit"
model = Qwen3VLForConditionalGeneration.from_pretrained(base_id, torch_dtype=torch.float16, device_map="cuda")
model = PeftModel.from_pretrained(model, "hungvtm/qwen3vl-food-lora")
processor = AutoProcessor.from_pretrained("hungvtm/qwen3vl-food-lora")
image = Image.open("food.jpg").convert("RGB")
prompt = (
"Analyze this food image and provide a complete description including: "
"dish name, food type, main ingredients, cooking method, portion sizes, "
"and nutritional information (calories, protein, fat, carbohydrates)."
)
messages = [{"role": "user", "content": [{"type": "image"}, {"type": "text", "text": prompt}]}]
text = processor.apply_chat_template(messages, add_generation_prompt=True)
inputs = processor(images=image, text=text, return_tensors="pt").to("cuda")
out = model.generate(**inputs, max_new_tokens=512, temperature=0.3)
print(processor.decode(out[0], skip_special_tokens=True))
| Setting | Value |
|---|---|
| Base model | unsloth/Qwen3-VL-4B-Instruct-bnb-4bit (4-bit) |
| Method | LoRA via Unsloth FastVisionModel.get_peft_model |
| LoRA rank / alpha / dropout | 16 / 16 / 0 |
| Trainable modules | Language layers, attention (Q/K/V/O), MLP (gate/up/down) |
| Vision encoder | Frozen (finetune_vision_layers=False) |
| Dataset | MM-Food-100K, 5,000-sample subset, 95/5 train/val split |
| Epochs | 2 |
| Batch size × grad. accumulation | 2 × 4 (effective batch 8) |
| Learning rate | 2e-4, cosine schedule, 3% warmup |
| Precision / optimizer | FP16, AdamW 8-bit |
| Max sequence length | 2048 |
| Image pixel budget | 256×28×28 – 1280×28×28 |
| Seed | 3407 |
Training loss dropped from ~2.3 to under 0.1 within the first ~200 steps and stayed flat over ~1,200 steps; validation loss decreased from 0.119 to 0.110 across the run, indicating the model wasn't just memorizing the training set.
Exact-match dish-name accuracy on 20 held-out validation samples: 50%. However, manually reviewing the "misses" shows the metric understates real quality — 30% of predictions are the same dish under different wording (e.g. "coconut milk" → "coconut milk drink", "rice bowl with egg and meat" → "rice with meat and egg"), and only 20% name a genuinely different dish (e.g. "cold noodle salad" → "noodle soup").
Ingredient lists, cooking method, and portion-size estimates are consistently close to ground truth on packaged and homemade foods with a visible reference point; exact macro values (calories/protein/fat/carbs) are closer to plausible ranges than exact matches, since identical dishes can vary by hundreds of calories depending on how much oil went into cooking them.