--- base_model: Qwen/Qwen3.5-4B-Base library_name: transformers language: - id license: apache-2.0 pipeline_tag: image-text-to-text tags: - image-captioning - qwen3.5 - bahasa-indonesia - lora - lora-merged - vlm - multimodal - json-output datasets: - custom --- # Qwen3.5-4B ImCap — Indonesian Image Captioning (LoRA merged) Model ini merupakan hasil fine-tuning (LoRA, sudah di-**merge** ke base weights) dari [`Qwen/Qwen3.5-4B-Base`](https://huggingface.co/Qwen/Qwen3.5-4B-Base) untuk tugas **image captioning Bahasa Indonesia**. --- ## Deskripsi Singkat | Atribut | Nilai | |---|---| | Base model | `Qwen/Qwen3.5-4B-Base` | | Metode fine-tune | LoRA (rank/alpha sesuai config SFT) | | Status adapter | **Merged** ke base weights | | Bahasa output | Bahasa Indonesia 🇮🇩 | | Format output | JSON — `{"caption": "..."}` | | `enable_thinking` saat training | `False` | | EOS token | `<\|im_end\|>` (+ `<\|endoftext\|>`) | | Precision | bfloat16 | --- ## Cara Pakai (Inference) ### Minimal (bfloat16, GPU) ```python import torch from PIL import Image from transformers import AutoProcessor, AutoModelForImageTextToText REPO = "Adicandra/Qwen3.5-4B-ImageCaptioning-LoRA" processor = AutoProcessor.from_pretrained(REPO) model = AutoModelForImageTextToText.from_pretrained( REPO, torch_dtype=torch.bfloat16, device_map="auto", ) model.eval() ``` ### System Prompt (WAJIB sama dengan training) ```python SYSTEM_PROMPT = ( "Annotator dataset image captioning. Tulis caption Bahasa Indonesia yang deskriptif.\n\n" "Aturan:\n" "- Deskripsikan subjek utama, detail visual (warna, posisi, atribut), dan latar belakang.\n" "- Jika gambar mengandung teks penting (meme, infografis, berita, poster), sertakan isi teksnya dalam caption.\n" "- KHUSUS UNTUK GAMBAR MEME: Analisis dan jelaskan makna sarkasme, ironi, atau humor yang terkandung di dalamnya jika ada.\n" "- Panjang caption fleksibel: 2-3 kalimat untuk gambar biasa, lebih panjang jika ada teks/informasi penting atau sarkasme.\n" "- Hanya deskripsikan yang terlihat (serta konteks humor/sarkasme jika itu meme). Jangan tebak identitas/nama. Jangan awali dengan \"gambar ini menunjukkan\".\n" '- Output: Harus berupa JSON valid dengan format: {"caption": "isi caption disini"}' ) ``` ### Render Chat + Generate ```python import json, re USER_PROMPT = "Buatkan caption deskriptif untuk gambar ini." MAX_IMAGE_SIZE = (560, 560) # ── Load & resize gambar ────────────────────────────────────────────────────── img = Image.open("path/to/image.jpg").convert("RGB") img.thumbnail(MAX_IMAGE_SIZE, Image.Resampling.LANCZOS) # ── Susun messages ──────────────────────────────────────────────────────────── messages = [ {"role": "system", "content": SYSTEM_PROMPT}, {"role": "user", "content": [ {"type": "image"}, {"type": "text", "text": USER_PROMPT}, ]}, ] # ── Render template (enable_thinking=False wajib) ──────────────────────────── text = processor.apply_chat_template( messages, tokenize=False, add_generation_prompt=True, enable_thinking=False, ) # ── Tokenize ────────────────────────────────────────────────────────────────── inputs = processor(text=[text], images=[[img]], return_tensors="pt").to(model.device) input_len = inputs.input_ids.shape[1] # ── EOS tokens ─────────────────────────────────────────────────────────────── im_end_id = processor.tokenizer.convert_tokens_to_ids("<|im_end|>") eot_id = processor.tokenizer.convert_tokens_to_ids("<|endoftext|>") eos_ids = list({im_end_id, eot_id} - {-1}) # ── Generate — greedy (deterministik) ──────────────────────────────────────── with torch.no_grad(): out = model.generate( **inputs, max_new_tokens=256, do_sample=False, use_cache=True, eos_token_id=eos_ids, pad_token_id=processor.tokenizer.pad_token_id, ) raw = processor.tokenizer.decode(out[0, input_len:], skip_special_tokens=True) # ── Parse JSON output ───────────────────────────────────────────────────────── def extract_caption(raw: str): cleaned = re.sub(r".*?", "", raw, flags=re.DOTALL).strip() try: obj = json.loads(cleaned) if isinstance(obj, dict) and "caption" in obj: return obj["caption"], "valid" except Exception: pass m = re.search(r"\{.*\}", cleaned, flags=re.DOTALL) if m: try: obj = json.loads(m.group(0)) if isinstance(obj, dict) and "caption" in obj: return obj["caption"], "recovered" except Exception: pass return cleaned, "invalid" caption, status = extract_caption(raw) print(f"[{status}] {caption}") ``` ### Generate dengan Sampling ```python # Tambahkan argumen berikut ke model.generate() untuk sampling: with torch.no_grad(): out = model.generate( **inputs, max_new_tokens=256, do_sample=True, temperature=0.7, top_p=0.8, top_k=20, min_p=0.0, repetition_penalty=1.2, use_cache=True, eos_token_id=eos_ids, pad_token_id=processor.tokenizer.pad_token_id, ) ``` --- ## Format Output Model selalu menghasilkan JSON valid: ```json {"caption": "Seekor kucing oranye sedang duduk di atas meja kayu berwarna cokelat, menatap ke arah kanan frame dengan mata setengah terpejam. Latar belakang berupa dinding putih yang sedikit buram."} ``` Jika gambar mengandung teks (meme, infografis, poster), teks tersebut akan disertakan dalam caption beserta konteks humor/sarkasme bila relevan. --- ## Diagnostik: Cek Apakah `` Muncul Model di-train dengan `enable_thinking=False`. Pastikan argumen tersebut selalu diteruskan ke `apply_chat_template`. Jika token `` masih muncul di output: 1. Pastikan `enable_thinking=False` di `apply_chat_template`. 2. Gunakan `suppress_tokens=[think_id]` saat `generate()` sebagai fallback. 3. Fungsi `extract_caption()` di atas sudah mem-strip blok `...` secara otomatis. --- ## Keterbatasan & Catatan - Model hanya menghasilkan caption **Bahasa Indonesia**; tidak dirancang untuk bahasa lain. - Untuk input resolusi tinggi (> 560×560), model tetap berfungsi tetapi performa optimal pada thumbnail 560×560. - **Jangan tebak identitas/nama orang** dari gambar — sesuai aturan system prompt. - Evaluasi formal (CIDEr, BLEU, METEOR) belum tersedia; performa diukur secara kualitatif. --- ## Lisensi Mengikuti lisensi base model: [Apache 2.0](https://www.apache.org/licenses/LICENSE-2.0). --- ## Citation Jika menggunakan model ini dalam penelitian, silakan sitasi base model Qwen3.5 dan sebutkan repo ini sebagai fine-tune untuk image captioning Bahasa Indonesia.