Views
No views yet
Qwen/Qwen3.5-4B-Base untuk tugas image captioning Bahasa Indonesia.| 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 |
1import torch
2from PIL import Image
3from transformers import AutoProcessor, AutoModelForImageTextToText
4
5REPO = "Adicandra/Qwen3.5-4B-ImageCaptioning-LoRA"
6
7processor = AutoProcessor.from_pretrained(REPO)
8model = AutoModelForImageTextToText.from_pretrained(
9 REPO,
10 torch_dtype=torch.bfloat16,
11 device_map="auto",
12)
13model.eval()1SYSTEM_PROMPT = (
2 "Annotator dataset image captioning. Tulis caption Bahasa Indonesia yang deskriptif.\n\n"
3 "Aturan:\n"
4 "- Deskripsikan subjek utama, detail visual (warna, posisi, atribut), dan latar belakang.\n"
5 "- Jika gambar mengandung teks penting (meme, infografis, berita, poster), sertakan isi teksnya dalam caption.\n"
6 "- KHUSUS UNTUK GAMBAR MEME: Analisis dan jelaskan makna sarkasme, ironi, atau humor yang terkandung di dalamnya jika ada.\n"
7 "- Panjang caption fleksibel: 2-3 kalimat untuk gambar biasa, lebih panjang jika ada teks/informasi penting atau sarkasme.\n"
8 "- Hanya deskripsikan yang terlihat (serta konteks humor/sarkasme jika itu meme). Jangan tebak identitas/nama. Jangan awali dengan \"gambar ini menunjukkan\".\n"
9 '- Output: Harus berupa JSON valid dengan format: {"caption": "isi caption disini"}'
10)1import json, re
2
3USER_PROMPT = "Buatkan caption deskriptif untuk gambar ini."
4MAX_IMAGE_SIZE = (560, 560)
5
6# ── Load & resize gambar ──────────────────────────────────────────────────────
7img = Image.open("path/to/image.jpg").convert("RGB")
8img.thumbnail(MAX_IMAGE_SIZE, Image.Resampling.LANCZOS)
9
10# ── Susun messages ────────────────────────────────────────────────────────────
11messages = [
12 {"role": "system", "content": SYSTEM_PROMPT},
13 {"role": "user", "content": [
14 {"type": "image"},
15 {"type": "text", "text": USER_PROMPT},
16 ]},
17]
18
19# ── Render template (enable_thinking=False wajib) ────────────────────────────
20text = processor.apply_chat_template(
21 messages,
22 tokenize=False,
23 add_generation_prompt=True,
24 enable_thinking=False,
25)
26
27# ── Tokenize ──────────────────────────────────────────────────────────────────
28inputs = processor(text=[text], images=[[img]], return_tensors="pt").to(model.device)
29input_len = inputs.input_ids.shape[1]
30
31# ── EOS tokens ───────────────────────────────────────────────────────────────
32im_end_id = processor.tokenizer.convert_tokens_to_ids("<|im_end|>")
33eot_id = processor.tokenizer.convert_tokens_to_ids("<|endoftext|>")
34eos_ids = list({im_end_id, eot_id} - {-1})
35
36# ── Generate — greedy (deterministik) ────────────────────────────────────────
37with torch.no_grad():
38 out = model.generate(
39 **inputs,
40 max_new_tokens=256,
41 do_sample=False,
42 use_cache=True,
43 eos_token_id=eos_ids,
44 pad_token_id=processor.tokenizer.pad_token_id,
45 )
46
47raw = processor.tokenizer.decode(out[0, input_len:], skip_special_tokens=True)
48
49# ── Parse JSON output ─────────────────────────────────────────────────────────
50def extract_caption(raw: str):
51 cleaned = re.sub(r"<think>.*?</think>", "", raw, flags=re.DOTALL).strip()
52 try:
53 obj = json.loads(cleaned)
54 if isinstance(obj, dict) and "caption" in obj:
55 return obj["caption"], "valid"
56 except Exception:
57 pass
58 m = re.search(r"\{.*\}", cleaned, flags=re.DOTALL)
59 if m:
60 try:
61 obj = json.loads(m.group(0))
62 if isinstance(obj, dict) and "caption" in obj:
63 return obj["caption"], "recovered"
64 except Exception:
65 pass
66 return cleaned, "invalid"
67
68caption, status = extract_caption(raw)
69print(f"[{status}] {caption}")1# Tambahkan argumen berikut ke model.generate() untuk sampling:
2with torch.no_grad():
3 out = model.generate(
4 **inputs,
5 max_new_tokens=256,
6 do_sample=True,
7 temperature=0.7,
8 top_p=0.8,
9 top_k=20,
10 min_p=0.0,
11 repetition_penalty=1.2,
12 use_cache=True,
13 eos_token_id=eos_ids,
14 pad_token_id=processor.tokenizer.pad_token_id,
15 ){"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."}<think> Munculenable_thinking=False. Pastikan argumen tersebut selalu diteruskan ke apply_chat_template. Jika token <think> masih muncul di output:enable_thinking=False di apply_chat_template.suppress_tokens=[think_id] saat generate() sebagai fallback.extract_caption() di atas sudah mem-strip blok <think>...</think> secara otomatis.