12 KiB
Qwen2-VLQwen2-VL
OverviewOverview
Qwen2-VL ๋ชจ๋ธ์ ์๋ฆฌ๋ฐ๋ฐ ๋ฆฌ์์น์ Qwenํ์์ ๊ฐ๋ฐํ Qwen-VL ๋ชจ๋ธ์ ์ฃผ์ ์ ๋ฐ์ดํธ ๋ฒ์ ์ ๋๋ค.
๋ธ๋ก๊ทธ์ ์์ฝ์ ๋ค์๊ณผ ๊ฐ์ต๋๋ค:
์ด ๋ธ๋ก๊ทธ๋ ์ง๋ ๋ช ๋ ๊ฐ Qwen-VL์์ ์ค๋ํ ๊ฐ์ ์ ๊ฑฐ์ณ ๋ฐ์ ๋ Qwen2-VL ๋ชจ๋ธ์ ์๊ฐํฉ๋๋ค. ์ค์ ๊ฐ์ ์ฌํญ์ ํฅ์๋ ์ด๋ฏธ์ง ์ดํด, ๊ณ ๊ธ ๋น๋์ค ์ดํด, ํตํฉ ์๊ฐ ์์ด์ ํธ ๊ธฐ๋ฅ, ํ์ฅ๋ ๋ค์ธ์ด ์ง์์ ํฌํจํ๊ณ ์์ต๋๋ค.๋ชจ๋ธ ์ํคํ ์ฒ๋ Naive Dynamic Resolution ์ง์์ ํตํด ์์์ ์ด๋ฏธ์ง ํด์๋๋ฅผ ์ฒ๋ฆฌํ ์ ์๋๋ก ์ต์ ํ๋์์ผ๋ฉฐ, ๋ฉํฐ๋ชจ๋ฌ ํ์ ์์น ์๋ฒ ๋ฉ(M-ROPE)์ ํ์ฉํ์ฌ 1D ํ ์คํธ์ ๋ค์ฐจ์ ์๊ฐ ๋ฐ์ดํฐ๋ฅผ ํจ๊ณผ์ ์ผ๋ก ์ฒ๋ฆฌํฉ๋๋ค. ์ด ์ ๋ฐ์ดํธ๋ ๋ชจ๋ธ์ ์๊ฐ ๊ด๋ จ ์์ ์์ GPT-4o์ Claude 3.5 Sonnet ๊ฐ์ ์ ๋์ ์ธ AI ์์คํ ๊ณผ ๊ฒฝ์๋ ฅ ์๋ ์ฑ๋ฅ์ ๋ณด์ฌ์ฃผ๋ฉฐ, ํ ์คํธ ๋ฅ๋ ฅ์์๋ ์คํ์์ค ๋ชจ๋ธ ์ค ์์๊ถ์ ๋ญํฌ๋์ด ์์ต๋๋ค. ์ด๋ฌํ ๋ฐ์ ์ Qwen2-VL์ ๊ฐ๋ ฅํ ๋ฉํฐ๋ชจ๋ฌ ์ฒ๋ฆฌ ๋ฐ ์ถ๋ก ๋ฅ๋ ฅ์ด ํ์ํ ๋ค์ํ ์์ฉ ๋ถ์ผ์์ ํ์ฉํ ์ ์๋ ๋ค์ฌ๋ค๋ฅํ ๋๊ตฌ๋ก ๋ง๋ค์ด์ค๋๋ค.
Qwen2-VL ๊ตฌ์กฐ. ์ถ์ฒ: ๋ธ๋ก๊ทธ ๊ฒ์๊ธ
์ด ๋ชจ๋ธ์ simonJJJ์ ์ํด ๊ธฐ์ฌ๋์์ต๋๋ค.
์ฌ์ฉ ์์Usage example
๋จ์ผ ๋ฏธ๋์ด ์ถ๋ก Single Media inference
์ด ๋ชจ๋ธ์ ์ด๋ฏธ์ง์ ๋น๋์ค๋ฅผ ๋ชจ๋ ์ธํ์ผ๋ก ๋ฐ์ ์ ์์ต๋๋ค. ๋ค์์ ์ถ๋ก ์ ์ํ ์์ ์ฝ๋์ ๋๋ค.
import torch
from transformers import Qwen2VLForConditionalGeneration, AutoTokenizer, AutoProcessor
# ์ฌ์ฉ ๊ฐ๋ฅํ ์ฅ์น์์ ๋ชจ๋ธ์ ๋ฐ ์ ๋ฐ๋(half-precision)๋ก ๋ก๋
model = Qwen2VLForConditionalGeneration.from_pretrained("Qwen/Qwen2-VL-7B-Instruct", device_map="auto")
processor = AutoProcessor.from_pretrained("Qwen/Qwen2-VL-7B-Instruct")
conversation = [
{
"role":"user",
"content":[
{
"type":"image",
"url": "https://qianwen-res.oss-cn-beijing.aliyuncs.com/Qwen-VL/assets/demo.jpeg"
},
{
"type":"text",
"text":"Describe this image."
}
]
}
]
inputs = processor.apply_chat_template(
conversation,
add_generation_prompt=True,
tokenize=True,
return_dict=True,
return_tensors="pt"
).to(model.device)
# ์ถ๋ก : ์์ํ ์์ฑ
output_ids = model.generate(**inputs, max_new_tokens=128)
generated_ids = [output_ids[len(input_ids):] for input_ids, output_ids in zip(inputs.input_ids, output_ids)]
output_text = processor.batch_decode(generated_ids, skip_special_tokens=True, clean_up_tokenization_spaces=True)
print(output_text)
# ๋น๋์ค
conversation = [
{
"role": "user",
"content": [
{"type": "video", "path": "/path/to/video.mp4"},
{"type": "text", "text": "What happened in the video?"},
],
}
]
inputs = processor.apply_chat_template(
conversation,
fps=1,
add_generation_prompt=True,
tokenize=True,
return_dict=True,
return_tensors="pt"
).to(model.device)
# ์ถ๋ก : ์์ํ ์์ฑ
output_ids = model.generate(**inputs, max_new_tokens=128)
generated_ids = [output_ids[len(input_ids):] for input_ids, output_ids in zip(inputs.input_ids, output_ids)]
output_text = processor.batch_decode(generated_ids, skip_special_tokens=True, clean_up_tokenization_spaces=True)
print(output_text)
๋ฐฐ์น ํผํฉ ๋ฏธ๋์ด ์ถ๋ก Batch Mixed Media Inference
์ด ๋ชจ๋ธ์ ์ด๋ฏธ์ง, ๋น๋์ค, ํ ์คํธ ๋ฑ ๋ค์ํ ์ ํ์ ๋ฐ์ดํฐ๋ฅผ ํผํฉํ์ฌ ๋ฐฐ์น ์ ๋ ฅ์ผ๋ก ์ฒ๋ฆฌํ ์ ์์ต๋๋ค. ๋ค์์ ์์ ์ ๋๋ค.
# ์ฒซ๋ฒ์งธ ์ด๋ฏธ์ง์ ๋ํ ๋ํ
conversation1 = [
{
"role": "user",
"content": [
{"type": "image", "path": "/path/to/image1.jpg"},
{"type": "text", "text": "Describe this image."}
]
}
]
# ๋ ๊ฐ์ ์ด๋ฏธ์ง์ ๋ํ ๋ํ
conversation2 = [
{
"role": "user",
"content": [
{"type": "image", "path": "/path/to/image2.jpg"},
{"type": "image", "path": "/path/to/image3.jpg"},
{"type": "text", "text": "What is written in the pictures?"}
]
}
]
# ์์ ํ
์คํธ๋ก๋ง ์ด๋ฃจ์ด์ง ๋ํ
conversation3 = [
{
"role": "user",
"content": "who are you?"
}
]
# ํผํฉ๋ ๋ฏธ๋์ด๋ก ์ด๋ฃจ์ด์ง ๋ํ
conversation4 = [
{
"role": "user",
"content": [
{"type": "image", "path": "/path/to/image3.jpg"},
{"type": "image", "path": "/path/to/image4.jpg"},
{"type": "video", "path": "/path/to/video.jpg"},
{"type": "text", "text": "What are the common elements in these medias?"},
],
}
]
conversations = [conversation1, conversation2, conversation3, conversation4]
# ๋ฐฐ์น ์ถ๋ก ์ ์ํ ์ค๋น
ipnuts = processor.apply_chat_template(
conversations,
fps=1,
add_generation_prompt=True,
tokenize=True,
return_dict=True,
return_tensors="pt"
).to(model.device)
# ๋ฐฐ์น ์ถ๋ก
output_ids = model.generate(**inputs, max_new_tokens=128)
generated_ids = [output_ids[len(input_ids):] for input_ids, output_ids in zip(inputs.input_ids, output_ids)]
output_text = processor.batch_decode(generated_ids, skip_special_tokens=True, clean_up_tokenization_spaces=True)
print(output_text)
์ฌ์ฉ ํUsage Tips
์ด๋ฏธ์ง ํด์๋ ํธ๋ ์ด๋์คํImage Resolution trade-off
์ด ๋ชจ๋ธ์ ๋ค์ํ ํด์๋์ ์ ๋ ฅ์ ์ง์ํฉ๋๋ค. ๋ํดํธ๋ก ์ ๋ ฅ์ ๋ํด ๋ค์ดํฐ๋ธ(native) ํด์๋๋ฅผ ์ฌ์ฉํ์ง๋ง, ๋ ๋์ ํด์๋๋ฅผ ์ ์ฉํ๋ฉด ์ฑ๋ฅ์ด ํฅ์๋ ์ ์์ต๋๋ค. ๋ค๋ง, ์ด๋ ๋ ๋ง์ ์ฐ์ฐ ๋น์ฉ์ ์ด๋ํฉ๋๋ค. ์ฌ์ฉ์๋ ์ต์ ์ ์ค์ ์ ์ํด ์ต์ ๋ฐ ์ต๋ ํฝ์ ์๋ฅผ ์กฐ์ ํ ์ ์์ต๋๋ค.
min_pixels = 224*224
max_pixels = 2048*2048
processor = AutoProcessor.from_pretrained("Qwen/Qwen2-VL-7B-Instruct", min_pixels=min_pixels, max_pixels=max_pixels)
์ ํ๋ GPU RAM์ ๊ฒฝ์ฐ, ๋ค์๊ณผ ๊ฐ์ด ํด์๋๋ฅผ ์ค์ผ ์ ์์ต๋๋ค:
min_pixels = 256*28*28
max_pixels = 1024*28*28
processor = AutoProcessor.from_pretrained("Qwen/Qwen2-VL-7B-Instruct", min_pixels=min_pixels, max_pixels=max_pixels)
์ด๋ ๊ฒ ํ๋ฉด ๊ฐ ์ด๋ฏธ์ง๊ฐ 256~1024๊ฐ์ ํ ํฐ์ผ๋ก ์ธ์ฝ๋ฉ๋ฉ๋๋ค. ์ฌ๊ธฐ์ 28์ ๋ชจ๋ธ์ด 14 ํฌ๊ธฐ์ ํจ์น(patch)์ 2์ ์๊ฐ ํจ์น(temporal patch size)๋ฅผ ์ฌ์ฉํ๊ธฐ ๋๋ฌธ์ ๋์จ ๊ฐ์ ๋๋ค (14 ร 2 = 28).
๋ค์ค ์ด๋ฏธ์ง ์ธํMultiple Image Inputs
๊ธฐ๋ณธ์ ์ผ๋ก ์ด๋ฏธ์ง์ ๋น๋์ค ์ฝํ ์ธ ๋ ๋ํ์ ์ง์ ํฌํจ๋ฉ๋๋ค. ์ฌ๋ฌ ๊ฐ์ ์ด๋ฏธ์ง๋ฅผ ์ฒ๋ฆฌํ ๋๋ ์ด๋ฏธ์ง ๋ฐ ๋น๋์ค์ ๋ผ๋ฒจ์ ์ถ๊ฐํ๋ฉด ์ฐธ์กฐํ๊ธฐ๊ฐ ๋ ์ฌ์์ง๋๋ค. ์ฌ์ฉ์๋ ๋ค์ ์ค์ ์ ํตํด ์ด ๋์์ ์ ์ดํ ์ ์์ต๋๋ค:
conversation = [
{
"role": "user",
"content": [
{"type": "image"},
{"type": "text", "text": "Hello, how are you?"}
]
},
{
"role": "assistant",
"content": "I'm doing well, thank you for asking. How can I assist you today?"
},
{
"role": "user",
"content": [
{"type": "text", "text": "Can you describe these images and video?"},
{"type": "image"},
{"type": "image"},
{"type": "video"},
{"type": "text", "text": "These are from my vacation."}
]
},
{
"role": "assistant",
"content": "I'd be happy to describe the images and video for you. Could you please provide more context about your vacation?"
},
{
"role": "user",
"content": "It was a trip to the mountains. Can you see the details in the images and video?"
}
]
# ๋ํดํธ:
prompt_without_id = processor.apply_chat_template(conversation, add_generation_prompt=True)
# ์์ ์์ํ: '<|im_start|>system\nYou are a helpful assistant.<|im_end|>\n<|im_start|>user\n<|vision_start|><|image_pad|><|vision_end|>Hello, how are you?<|im_end|>\n<|im_start|>assistant\nI'm doing well, thank you for asking. How can I assist you today?<|im_end|>\n<|im_start|>user\nCan you describe these images and video?<|vision_start|><|image_pad|><|vision_end|><|vision_start|><|image_pad|><|vision_end|><|vision_start|><|video_pad|><|vision_end|>These are from my vacation.<|im_end|>\n<|im_start|>assistant\nI'd be happy to describe the images and video for you. Could you please provide more context about your vacation?<|im_end|>\n<|im_start|>user\nIt was a trip to the mountains. Can you see the details in the images and video?<|im_end|>\n<|im_start|>assistant\n'
# id ์ถ๊ฐ
prompt_with_id = processor.apply_chat_template(conversation, add_generation_prompt=True, add_vision_id=True)
# ์์ ์์ํ: '<|im_start|>system\nYou are a helpful assistant.<|im_end|>\n<|im_start|>user\nPicture 1: <|vision_start|><|image_pad|><|vision_end|>Hello, how are you?<|im_end|>\n<|im_start|>assistant\nI'm doing well, thank you for asking. How can I assist you today?<|im_end|>\n<|im_start|>user\nCan you describe these images and video?Picture 2: <|vision_start|><|image_pad|><|vision_end|>Picture 3: <|vision_start|><|image_pad|><|vision_end|>Video 1: <|vision_start|><|video_pad|><|vision_end|>These are from my vacation.<|im_end|>\n<|im_start|>assistant\nI'd be happy to describe the images and video for you. Could you please provide more context about your vacation?<|im_end|>\n<|im_start|>user\nIt was a trip to the mountains. Can you see the details in the images and video?<|im_end|>\n<|im_start|>assistant\n'
๋น ๋ฅธ ์์ฑ์ ์ํ Flash-Attention 2Flash-Attention 2 to speed up generation
์ฒซ๋ฒ์งธ๋ก, Flash Attention 2์ ์ต์ ๋ฒ์ ์ ์ค์นํฉ๋๋ค:
pip install -U flash-attn --no-build-isolation
๋ํ, Flash-Attention 2๋ฅผ ์ง์ํ๋ ํ๋์จ์ด๊ฐ ํ์ํฉ๋๋ค. ์์ธํ ๋ด์ฉ์ ๊ณต์ ๋ฌธ์์ธ flash attention repository์์ ํ์ธํ ์ ์์ต๋๋ค. FlashAttention-2๋ ๋ชจ๋ธ์ด torch.float16 ๋๋ torch.bfloat16 ํ์์ผ๋ก ๋ก๋๋ ๊ฒฝ์ฐ์๋ง ์ฌ์ฉํ ์ ์์ต๋๋ค.
Flash Attention-2๋ฅผ ์ฌ์ฉํ์ฌ ๋ชจ๋ธ์ ๋ก๋ํ๊ณ ์คํํ๋ ค๋ฉด, ๋ค์๊ณผ ๊ฐ์ด ๋ชจ๋ธ์ ๋ก๋ํ ๋ attn_implementation="flash_attention_2" ์ต์
์ ์ถ๊ฐํ๋ฉด ๋ฉ๋๋ค:
from transformers import Qwen2VLForConditionalGeneration
model = Qwen2VLForConditionalGeneration.from_pretrained(
"Qwen/Qwen2-VL-7B-Instruct",
dtype=torch.bfloat16,
attn_implementation="flash_attention_2",
)
Qwen2VLConfig
autodoc Qwen2VLConfig
Qwen2VLImageProcessor
autodoc Qwen2VLImageProcessor - preprocess
Qwen2VLImageProcessorFast
autodoc Qwen2VLImageProcessorFast - preprocess
Qwen2VLProcessor
autodoc Qwen2VLProcessor
Qwen2VLModel
autodoc Qwen2VLModel - forward
Qwen2VLForConditionalGeneration
autodoc Qwen2VLForConditionalGeneration - forward
