Transformers documentation
Teks-gambar-ke-teks
Teks-gambar-ke-teks
Model image-text-to-text, yang juga dikenal sebagai vision language models (VLMs), adalah model bahasa yang menerima input gambar. Model-model ini dapat menangani berbagai tugas, mulai dari visual question answering hingga image segmentation. Tugas ini memiliki banyak kesamaan dengan image-to-text, tetapi dengan beberapa kasus penggunaan yang tumpang tindih, seperti image captioning. Model image-to-text hanya menerima input gambar dan sering kali menyelesaikan tugas tertentu, sedangkan VLMs menerima input teks dan gambar yang terbuka serta merupakan model yang lebih generalis.
Dalam panduan ini, kami memberikan gambaran singkat tentang VLM dan menunjukkan cara menggunakannya dengan Transformers untuk inferensi.
Sebagai permulaan, terdapat beberapa jenis VLM:
- model dasar yang digunakan untuk fine-tuning
- model chat yang telah dihaluskan (fine-tuned) untuk percakapan
- model yang telah dihaluskan instruksi
Panduan ini berfokus pada inferensi dengan model yang telah di-tune instruksi.
Mari kita mulai dengan menginstal dependensi.
pip install -q transformers accelerate pip install flash-attn --no-build-isolation
Mari kita inisialisasi model dan prosesor.
from transformers import AutoProcessor, AutoModelForImageTextToText
from accelerate import Accelerator
import torch
device = Accelerator().device
model = AutoModelForImageTextToText.from_pretrained(
"Qwen/Qwen3-VL-4B-Instruct",
dtype=torch.bfloat16,
attn_implementation="flash_attention_2",
).to(device)Model beresolusi dinamis seperti Qwen3-VL dan Qwen3.5 secara default menggunakan
longest_edgesekitar 16MP, yang dapat menghasilkan lebih dari 10.000 token visual per gambar dan menyebabkan inferensi macet atau OOM pada GPU konsumen. Aturmin_pixelsdanmax_pixelssaat memuat processor untuk membatasi hal ini.
processor = AutoProcessor.from_pretrained(
"Qwen/Qwen3-VL-4B-Instruct",
min_pixels=224 * 224,
max_pixels=1024 * 1024,
)Model ini memiliki chat template yang membantu pengguna mengurai keluaran chat. Selain itu, model ini juga dapat menerima beberapa gambar sebagai input dalam satu percakapan atau pesan. Sekarang kita akan menyiapkan input.
Input gambar terlihat seperti berikut.


Strukturkan percakapan Anda seperti yang ditunjukkan di bawah ini untuk satu prompt dengan input gambar dan teks.
messages = [
{
"role": "user",
"content": [
{"type": "image", "image": "https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/cats.png"},
{"type": "text", "text": "What do we see in this image?"},
]
}
]Gantilah peran user dan assistant secara bergantian untuk memberikan konteks sebelumnya kepada model sehingga menghasilkan respons yang lebih baik.
messages = [
{
"role": "user",
"content": [
{"type": "image", "image": "https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/cats.png"},
{"type": "text", "text": "What do we see in this image?"},
]
},
{
"role": "assistant",
"content": [
{"type": "text", "text": "In this image we can see two cats on the nets."},
]
},
{
"role": "user",
"content": [
{"type": "image", "image": "https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/bee.jpg"},
{"type": "text", "text": "And how about this image?"},
]
},
]Sekarang kita akan memanggil metode apply_chat_template() dari processor untuk memproses pra-olaha outputnya bersama dengan input gambar.
inputs = processor.apply_chat_template(messages, add_generation_prompt=True, tokenize=True, return_dict=True, return_tensors="pt").to(device)Sekarang kita dapat meneruskan input yang telah dipreprocess ke model.
input_len = len(inputs.input_ids[0])
with torch.no_grad():
generated_ids = model.generate(**inputs, max_new_tokens=200)
generated_texts = processor.batch_decode(generated_ids[:, input_len:], skip_special_tokens=True)
print(generated_texts)
## ['In this image we can see flowers, plants and insect.']Pipeline
Cara tercepat untuk memulai adalah dengan menggunakan API Pipeline. Tentukan tugas "image-text-to-text" dan model yang ingin Anda gunakan.
from transformers import pipeline
pipe = pipeline("image-text-to-text", model="llava-hf/llava-interleave-qwen-0.5b-hf")Contoh di bawah ini menggunakan template chat untuk memformat input teks.
messages = [
{
"role": "user",
"content": [
{
"type": "image",
"image": "https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/bee.jpg",
},
{"type": "text", "text": "Describe this image."},
],
},
{
"role": "assistant",
"content": [
{"type": "text", "text": "There's a pink flower"},
],
},
]Lewatkan teks dan gambar yang sudah diformat dengan chat template ke Pipeline dan atur return_full_text=False untuk menghapus input dari output yang dihasilkan.
outputs = pipe(text=messages, max_new_tokens=20, return_full_text=False)
outputs[0]["generated_text"]
# with a yellow center in the foreground. The flower is surrounded by red and white flowers with green stemsJika Anda lebih memilih, Anda juga dapat memuat gambar secara terpisah dan meneruskannya ke pipeline seperti ini:
import requests
from PIL import Image
from transformers import pipeline
pipe = pipeline("image-text-to-text", model="HuggingFaceTB/SmolVLM-256M-Instruct")
img_urls = [
"https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/cats.png",
"https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/bee.jpg",
]
images = [
Image.open(requests.get(img_urls[0], stream=True).raw),
Image.open(requests.get(img_urls[1], stream=True).raw),
]
messages = [
{
"role": "user",
"content": [
{"type": "image"},
{"type": "image"},
{"type": "text", "text": "What do you see in these images?"},
],
}
]
outputs = pipe(text=messages, images=images, max_new_tokens=50, return_full_text=False)
outputs[0]["generated_text"]
" In the first image, there are two cats sitting on a plant. In the second image, there are flowers with a pinkish hue."Gambar masih akan disertakan di dalam field "input_text" dari output:
outputs[0]['input_text']
"""
[{'role': 'user',
'content': [{'type': 'image',
'image': <PIL.PngImagePlugin.PngImageFile image mode=RGBA size=622x412>},
{'type': 'image',
'image': <PIL.JpegImagePlugin.JpegImageFile image mode=RGB size=5184x3456>},
{'type': 'text', 'text': 'What do you see in these images?'}]}]
"""Streaming
Kita dapat menggunakan streaming teks untuk pengalaman generasi yang lebih baik. Transformers mendukung streaming dengan kelas TextStreamer atau TextIteratorStreamer. Kita akan menggunakan TextIteratorStreamer dengan IDEFICS-8B.
Asumsikan kita memiliki aplikasi yang menyimpan riwayat chat dan menerima input pengguna baru. Kita akan memproses input secara biasa dan menginisialisasi TextIteratorStreamer untuk menangani generasi di thread terpisah. Hal ini memungkinkan Anda untuk men-streaming token teks yang dihasilkan secara real-time. Argumen generasi apa pun dapat diteruskan ke TextIteratorStreamer.
import time
from transformers import TextIteratorStreamer
from threading import Thread
def model_inference(
user_prompt,
chat_history,
max_new_tokens,
images
):
user_prompt = {
"role": "user",
"content": [
{"type": "image"},
{"type": "text", "text": user_prompt},
]
}
chat_history.append(user_prompt)
streamer = TextIteratorStreamer(
processor.tokenizer,
skip_prompt=True,
timeout=5.0,
)
generation_args = {
"max_new_tokens": max_new_tokens,
"streamer": streamer,
"do_sample": False
}
# add_generation_prompt=True makes model generate bot response
prompt = processor.apply_chat_template(chat_history, add_generation_prompt=True)
inputs = processor(
text=prompt,
images=images,
return_tensors="pt",
).to(device)
generation_args.update(inputs)
thread = Thread(
target=model.generate,
kwargs=generation_args,
)
thread.start()
acc_text = ""
for text_token in streamer:
time.sleep(0.04)
acc_text += text_token
if acc_text.endswith("<end_of_utterance>"):
acc_text = acc_text[:-18]
yield acc_text
thread.join()Sekarang, mari kita panggil fungsi model_inference yang telah kita buat dan alirkan nilainya.
generator = model_inference(
user_prompt="And what is in this image?",
chat_history=messages[:2],
max_new_tokens=100,
images=images
)
for value in generator:
print(value)
# In
# In this
# In this image ...Sesuaikan model di perangkat yang lebih kecil
VLMs sering kali berukuran besar dan perlu dioptimalkan agar muat di perangkat yang lebih kecil. Transformers mendukung banyak pustaka kuantisasi model, dan di sini kami hanya akan menunjukkan kuantisasi int8 dengan Quanto. Kuantisasi int8 menawarkan peningkatan memori hingga 75 persen (jika semua bobot dikuantisasi). Namun, ini bukan tanpa biaya, karena 8-bit bukanlah presisi native CUDA, sehingga bobot dikuantisasi bolak-balik secara on-the-fly, yang menambah latensi.
Pertama, instal dependensi.
pip install -U optimum-quanto bitsandbytes
Untuk mengkuantisasi model saat dimuat, kita perlu terlebih dahulu membuat QuantoConfig. Kemudian muat model seperti biasa, tetapi lewati quantization_config saat inisialisasi model.
from transformers import AutoModelForImageTextToText, QuantoConfig
model_id = "Qwen/Qwen3-VL-4B-Instruct"
quantization_config = QuantoConfig(weights="int8")
quantized_model = AutoModelForImageTextToText.from_pretrained(
model_id, device_map="auto", quantization_config=quantization_config
)
messages = [
{
"role": "user",
"content": [
{"type": "image", "image": "https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/cats.png"},
{"type": "text", "text": "What do we see in this image?"},
]
},
]
inputs = processor.apply_chat_template(messages, add_generation_prompt=True, tokenize=True, return_dict=True, return_tensors="pt").to(quantized_model.device)
input_len = len(inputs.input_ids[0])
with torch.no_grad():
generated_ids = quantized_model.generate(**inputs, cache_implementation="static", max_new_tokens=100)
generated_texts = processor.batch_decode(generated_ids[:, input_len:], skip_special_tokens=True)
print(generated_texts[0])
## ['In this image, we see two tabby cats resting on a large, tangled pile of fishing nets. The nets are a mix of brown, orange, and red colors, with some blue and green ropes visible in the background. The cats appear relaxed and comfortable, nestled into the fibers of the nets. One cat is in the foreground, looking slightly to the side, while the other is positioned further back, looking directly at the camera. The scene suggests a coastal or fishing-related setting, possibly near']Dan itu saja, kita dapat menggunakan model dengan cara yang sama tanpa perubahan.
Mengobrol secara iteratif dengan cache
Jika Anda sedang membangun agen chat multimodal, kemungkinan besar Anda meneruskan gambar atau audio yang sama di setiap giliran. Caching memungkinkan Anda untuk menggunakan kembali representasi terenkodnya alih-alih memproses ulang setiap kali, sehingga mengurangi komputasi yang redundan.
Seperti model yang hanya berbasis teks, model multimodal melacak riwayat percakapan dengan template chat. Perbedaannya adalah template chat hanya diterapkan dalam format "text" dan konten multimodal baru diproses secara terpisah. Jika Anda menerapkan template dengan pemrosesan, seluruh percakapan akan diproses ulang setiap giliran karena Jinja tidak dapat membedakan input yang sudah dikodekan sebelumnya dari input baru.
Contoh di bawah ini mendemonstrasikan pola ini. Untuk model teks saja, lihat panduan iterative generation.
Berikut adalah contoh Python sederhana yang mendemonstrasikan pola ini. Untuk contoh dengan model teks saja, lihat panduan ini.
import torch
from transformers import AutoProcessor, Gemma4ForConditionalGeneration, TextStreamer
from transformers.cache_utils import DynamicCache
from transformers.image_utils import load_image
from transformers.audio_utils import load_audio
model_id = 'google/gemma-4-E2B-it'
processor = AutoProcessor.from_pretrained(model_id)
model = Gemma4ForConditionalGeneration.from_pretrained(model_id, dtype=torch.float32, device_map='cpu')
past_key_values = DynamicCache()
streamer = TextStreamer(processor.tokenizer, skip_prompt=True, skip_special_tokens=True)
## Turn 1: multimodal input (text + image + audio)
audio = load_audio("https://huggingface.co/datasets/Xenova/transformers.js-docs/resolve/main/jfk.wav")
image = load_image("https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/bee.jpg")
messages = [
{
"role": "user",
"content": [
{"type": "text", "text": "In detail, describe the following audio and image."},
{"type": "audio"},
{"type": "image"},
],
},
]
input_string = processor.apply_chat_template(messages, add_generation_prompt=True, tokenize=False)
inputs = processor(text=input_string, images=[image], audio=[audio], return_tensors='pt')
output = model.generate(**inputs, past_key_values=past_key_values, max_new_tokens=1024, do_sample=False, streamer=streamer)
first_response = processor.decode(output[0][inputs['input_ids'].shape[1]:], skip_special_tokens=True)
## Turn 2: text-only follow-up (reuses KV cache, no new media)
messages.append({"role": "assistant", "content": [{"type": "text", "text": first_response}]})
cached_string = processor.apply_chat_template(messages, tokenize=False)
messages.append({"role": "user", "content": [{"type": "text", "text": "Summarize the previous descriptions in one sentence."}]})
full_string = processor.apply_chat_template(messages, add_generation_prompt=True, tokenize=False)
new_string = full_string[len(cached_string):]
new_input_ids = processor.tokenizer(new_string, add_special_tokens=False, return_tensors='pt')['input_ids']
# Build and pass an attention mask only if batched input or there is padding
# attention_mask = torch.ones(1, past_key_values.get_seq_length() + new_input_ids.shape[1], dtype=torch.long)
output2 = model.generate(input_ids=new_input_ids, past_key_values=past_key_values, max_new_tokens=1024, do_sample=False, streamer=streamer)
second_response = processor.decode(output2[0][new_input_ids.shape[1]:], skip_special_tokens=True)
## Turn 3: new image (must go through processor for image token expansion + encoding)
image2 = load_image("https://huggingface.co/datasets/Xenova/transformers.js-docs/resolve/main/artemis.jpeg")
messages.append({"role": "assistant", "content": [{"type": "text", "text": second_response}]})
cached_string2 = processor.apply_chat_template(messages, tokenize=False)
messages.append({"role": "user", "content": [{"type": "text", "text": "Describe this image."}, {"type": "image"}]})
full_string2 = processor.apply_chat_template(messages, add_generation_prompt=True, tokenize=False)
new_string2 = full_string2[len(cached_string2):]
new_inputs2 = processor(text=new_string2, images=[image2], add_special_tokens=False, return_tensors='pt')
# new_inputs2['attention_mask'] = torch.ones(1, past_key_values.get_seq_length() + new_inputs2['input_ids'].shape[1], dtype=torch.long)
new_inputs2['past_key_values'] = past_key_values
output3 = model.generate(**new_inputs2, max_new_tokens=1024, do_sample=False, streamer=streamer)Bacaan Lanjutan
Berikut adalah beberapa sumber tambahan untuk tugas gambar-teks-ke-teks.
- Halaman tugas gambar-teks-ke-teks mencakup jenis model, kasus penggunaan, dataset, dan lainnya.
- Vision Language Models Explained adalah artikel blog yang membahas segala hal tentang model bahasa visual dan fine-tuning berlabel menggunakan TRL.
- Belajar cara fine-tune model vision language menggunakan TRL