Views
No views yet
Qwen/Qwen2-VL-7BQwen/Qwen2-VL-7BQwen/Qwen2-VL-7BQwen/Qwen2-VL-7B using PEFT without modifying the base model weights.1pip install torch>=2.0.0 torchvision>=0.15.0
2pip install transformers>=4.56.0 peft>=0.17.0 accelerate>=1.10.0
3pip install pillow>=10.0.0 qwen-vl-utils>=0.0.8pip install torch>=2.0.0 torchvision>=0.15.0 transformers>=4.56.0 peft>=0.17.0 accelerate>=1.10.0 pillow>=10.0.0 qwen-vl-utils>=0.0.81import torch
2from transformers import AutoProcessor, Qwen2VLForConditionalGeneration
3from peft import PeftModel
4
5DEFAULT_EOS_TOKEN = "</s>"
6DEFAULT_BOS_TOKEN = "<s>"
7DEFAULT_UNK_TOKEN = "<unk>"
8
9def align_tokenizer_and_model(tokenizer, model):
10 """
11 Ensure required special tokens exist and resize embeddings to match tokenizer vocab.
12 This is necessary because the adapter was trained with this alignment.
13 """
14 special_tokens = {}
15 if tokenizer.pad_token is None:
16 tokenizer.pad_token = tokenizer.eos_token
17 if tokenizer.eos_token is None:
18 special_tokens["eos_token"] = DEFAULT_EOS_TOKEN
19 if tokenizer.bos_token is None:
20 special_tokens["bos_token"] = DEFAULT_BOS_TOKEN
21 if tokenizer.unk_token is None:
22 special_tokens["unk_token"] = DEFAULT_UNK_TOKEN
23
24 num_new_tokens = tokenizer.add_special_tokens(special_tokens)
25 if num_new_tokens > 0 or model.get_input_embeddings().weight.shape[0] != len(tokenizer):
26 model.resize_token_embeddings(len(tokenizer))
27 if num_new_tokens > 0:
28 input_embeds = model.get_input_embeddings().weight.data
29 output_embeds = model.get_output_embeddings().weight.data
30
31 if tokenizer.unk_token_id is not None:
32 input_init = input_embeds[tokenizer.unk_token_id].unsqueeze(0)
33 output_init = output_embeds[tokenizer.unk_token_id].unsqueeze(0)
34 else:
35 input_init = input_embeds[:-num_new_tokens].mean(dim=0, keepdim=True)
36 output_init = output_embeds[:-num_new_tokens].mean(dim=0, keepdim=True)
37
38 input_embeds[-num_new_tokens:] = input_init
39 output_embeds[-num_new_tokens:] = output_init
40
41# Model IDs
42base_model_id = "Qwen/Qwen2-VL-7B"
43adapter_id = "radical-ai/MATRIX-PT"
44
45# Load processor from base model
46processor = AutoProcessor.from_pretrained(base_model_id, trust_remote_code=True)
47tokenizer = processor.tokenizer
48tokenizer.padding_side = "left"
49if tokenizer.pad_token_id is None:
50 tokenizer.pad_token_id = tokenizer.eos_token_id
51
52# Use Instruct processor for chat template (base model template has issues)
53instruct_processor = AutoProcessor.from_pretrained(
54 "Qwen/Qwen2-VL-7B-Instruct",
55 trust_remote_code=True
56)
57processor.chat_template = instruct_processor.chat_template
58tokenizer.chat_template = instruct_processor.tokenizer.chat_template
59
60# Load base model
61model = Qwen2VLForConditionalGeneration.from_pretrained(
62 base_model_id,
63 device_map="auto",
64 torch_dtype=torch.bfloat16,
65 trust_remote_code=True,
66)
67
68# IMPORTANT: Align tokenizer and model before loading adapter
69align_tokenizer_and_model(tokenizer, model)
70
71# Load adapter
72model = PeftModel.from_pretrained(model, adapter_id)
73model.eval()1# Text-only inference
2question = "What is a phase diagram?"
3messages = [{"role": "user", "content": question}]
4
5rendered = processor.apply_chat_template(
6 messages,
7 tokenize=False,
8 add_generation_prompt=True,
9)
10inputs = tokenizer([rendered], return_tensors="pt")
11inputs = {k: v.to(model.device) for k, v in inputs.items()}
12
13with torch.no_grad():
14 outputs = model.generate(
15 **inputs,
16 max_new_tokens=256,
17 do_sample=False,
18 pad_token_id=tokenizer.pad_token_id
19 )
20
21# Decode only the new tokens
22input_len = inputs["input_ids"].shape[1]
23generated_ids = outputs[:, input_len:]
24response = processor.batch_decode(
25 generated_ids,
26 skip_special_tokens=True,
27 clean_up_tokenization_spaces=True,
28)[0].strip()
29
30print(response)1from PIL import Image
2
3# Load image
4image = Image.open("path/to/image.png").convert("RGB")
5
6# Create message with image
7messages = [
8 {
9 "role": "user",
10 "content": [
11 {"type": "image"},
12 {"type": "text", "text": "Describe this experimental image."}
13 ]
14 }
15]
16
17# Process with image
18prompt = processor.apply_chat_template(messages, add_generation_prompt=True)
19inputs = processor(text=prompt, images=[image], return_tensors="pt")
20
21# Convert pixel_values to bfloat16 if present
22if "pixel_values" in inputs:
23 inputs["pixel_values"] = inputs["pixel_values"].to(torch.bfloat16)
24
25inputs = {k: v.to(model.device) for k, v in inputs.items()}
26
27with torch.no_grad():
28 outputs = model.generate(
29 **inputs,
30 max_new_tokens=256,
31 do_sample=False,
32 )
33
34input_len = inputs["input_ids"].shape[1]
35generated_ids = outputs[:, input_len:]
36response = processor.batch_decode(
37 generated_ids,
38 skip_special_tokens=True,
39 clean_up_tokenization_spaces=True,
40)[0].strip()
41
42print(response)Qwen/Qwen2-VL-7B model under identical prompting and decoding settings.@article{mcgrath2026matrix,
title = {MATRIX: A Multimodal Benchmark and Post-Training Framework for Materials Science},
author = {McGrath, Delia and Chong, Curtis and Kulkarni, Rohil and Ceder, Gerbrand and Kolluru, Adeesh},
journal = {arXiv preprint arXiv:2602.00376},
year = {2026}
}