Views
No views yet

Size Wu, Wenwei Zhang, Lumin Xu, Sheng Jin, Zhonghua Wu, Qingyi Tao, Wentao Liu, Wei Li, Chen Change Loy
| Model Variant | LLM | MAR | Hugging Face Hub |
|---|---|---|---|
| Harmon-0.5B | Qwen2.5-0.5B-Instruct | MAR-Base | |
| Harmon-1.5B | Qwen2.5-1.5B-Instruct | MAR-Huge |
1import torch
2import numpy as np
3from transformers import AutoTokenizer, AutoModel
4from einops import rearrange
5from PIL import Image
6import requests
7
8
9PROMPT_TEMPLATE = dict(
10 SYSTEM='<|im_start|>system\n{system}<|im_end|>\n',
11 INSTRUCTION='<|im_start|>user\n{input}<|im_end|>\n<|im_start|>assistant\n',
12 SUFFIX='<|im_end|>',
13 SUFFIX_AS_EOS=True,
14 SEP='\n',
15 STOP_WORDS=['<|im_end|>', '<|endoftext|>'])
16
17
18def expand2square(pil_img, background_color):
19 width, height = pil_img.size
20 if width == height:
21 return pil_img
22 elif width > height:
23 result = Image.new(pil_img.mode, (width, width), background_color)
24 result.paste(pil_img, (0, (width - height) // 2))
25 return result
26 else:
27 result = Image.new(pil_img.mode, (height, height), background_color)
28 result.paste(pil_img, ((height - width) // 2, 0))
29 return result
30
31
32@torch.no_grad()
33def question_answer(question,
34 image,
35 model,
36 tokenizer,
37 max_new_tokens=512,
38 image_size=512
39 ):
40 assert image_size == 512
41 image = expand2square(
42 image, (127, 127, 127))
43 image = image.resize(size=(image_size, image_size))
44 image = torch.from_numpy(np.array(image)).to(dtype=model.dtype, device=model.device)
45 image = rearrange(image, 'h w c -> c h w')[None]
46 image = 2 * (image / 255) - 1
47
48 prompt = PROMPT_TEMPLATE['INSTRUCTION'].format(input="<image>\n" + question)
49 assert '<image>' in prompt
50 image_length = (image_size // 16) ** 2 + model.mar.buffer_size
51 prompt = prompt.replace('<image>', '<image>'*image_length)
52 input_ids = tokenizer.encode(
53 prompt, add_special_tokens=True, return_tensors='pt').cuda()
54 _, z_enc = model.extract_visual_feature(model.encode(image))
55 inputs_embeds = z_enc.new_zeros(*input_ids.shape, model.llm.config.hidden_size)
56 inputs_embeds[input_ids == image_token_idx] = z_enc.flatten(0, 1)
57 inputs_embeds[input_ids != image_token_idx] = model.llm.get_input_embeddings()(
58 input_ids[input_ids != image_token_idx]
59 )
60 output = model.llm.generate(inputs_embeds=inputs_embeds,
61 use_cache=True,
62 do_sample=False,
63 max_new_tokens=max_new_tokens,
64 eos_token_id=tokenizer.eos_token_id,
65 pad_token_id=tokenizer.pad_token_id
66 if tokenizer.pad_token_id is not None else
67 tokenizer.eos_token_id
68 )
69 return tokenizer.decode(output[0])
70
71
72harmon_tokenizer = AutoTokenizer.from_pretrained("wusize/Harmon-1_5B",
73 trust_remote_code=True)
74harmon_model = AutoModel.from_pretrained("wusize/Harmon-1_5B",
75 trust_remote_code=True).eval().cuda().bfloat16()
76
77special_tokens_dict = {'additional_special_tokens': ["<image>", ]}
78num_added_toks = harmon_tokenizer.add_special_tokens(special_tokens_dict)
79assert num_added_toks == 1
80
81image_token_idx = harmon_tokenizer.encode("<image>", add_special_tokens=False)[-1]
82print(f"Image token: {harmon_tokenizer.decode(image_token_idx)}")
83
84image_file = "http://images.cocodataset.org/val2017/000000039769.jpg"
85raw_image = Image.open(requests.get(image_file, stream=True).raw).convert('RGB')
86
87output_text = question_answer(question='Describe the image in detail.',
88 image=raw_image,
89 model=harmon_model,
90 tokenizer=harmon_tokenizer,
91 )
92
93print(output_text)
941import os
2import torch
3from transformers import AutoTokenizer, AutoModel
4from einops import rearrange
5from PIL import Image
6
7
8PROMPT_TEMPLATE = dict(
9 SYSTEM='<|im_start|>system\n{system}<|im_end|>\n',
10 INSTRUCTION='<|im_start|>user\n{input}<|im_end|>\n<|im_start|>assistant\n',
11 SUFFIX='<|im_end|>',
12 SUFFIX_AS_EOS=True,
13 SEP='\n',
14 STOP_WORDS=['<|im_end|>', '<|endoftext|>'])
15
16GENERATION_TEMPLATE = "Generate an image: {text}"
17
18
19@torch.no_grad()
20def generate_images(prompts,
21 negative_prompt,
22 tokenizer,
23 model,
24 output,
25 grid_size=2, # will produce 2 x 2 images per prompt
26 num_steps=64, cfg_scale=3.0, temperature=1.0, image_size=512):
27 assert image_size == 512
28 m = n = image_size // 16
29
30 prompts = [
31 PROMPT_TEMPLATE['INSTRUCTION'].format(input=prompt)
32 for prompt in prompts
33 ] * (grid_size ** 2)
34
35 if cfg_scale != 1.0:
36 prompts += [PROMPT_TEMPLATE['INSTRUCTION'].format(input=negative_prompt)] * len(prompts)
37
38 inputs = tokenizer(
39 prompts, add_special_tokens=True, return_tensors='pt', padding=True).to(model.device)
40
41 images = model.sample(**inputs, num_iter=num_steps, cfg=cfg_scale, cfg_schedule="constant",
42 temperature=temperature, progress=True, image_shape=(m, n))
43 images = rearrange(images, '(m n b) c h w -> b (m h) (n w) c', m=grid_size, n=grid_size)
44
45 images = torch.clamp(
46 127.5 * images + 128.0, 0, 255).to("cpu", dtype=torch.uint8).numpy()
47
48 os.makedirs(output, exist_ok=True)
49 for idx, image in enumerate(images):
50 Image.fromarray(image).save(f"{output}/{idx:08d}.jpg")
51
52
53harmon_tokenizer = AutoTokenizer.from_pretrained("wusize/Harmon-1_5B",
54 trust_remote_code=True)
55harmon_model = AutoModel.from_pretrained("wusize/Harmon-1_5B",
56 trust_remote_code=True).cuda().bfloat16().eval()
57
58
59texts = ['a dog on the left and a cat on the right.',
60 'a photo of a pink stop sign.']
61pos_prompts = [GENERATION_TEMPLATE.format(text=text) for text in texts]
62neg_prompt = 'Generate an image.' # for classifier-free guidance
63
64
65generate_images(prompts=pos_prompts,
66 negative_prompt=neg_prompt,
67 tokenizer=harmon_tokenizer,
68 model=harmon_model,
69 output='output',)
701@misc{wu2025harmon,
2 title={Harmonizing Visual Representations for Unified Multimodal Understanding and Generation},
3 author={Size Wu and Wenwei Zhang and Lumin Xu and Sheng Jin and Zhonghua Wu and Qingyi Tao and Wentao Liu and Wei Li and Chen Change Loy},
4 year={2025},
5 eprint={2503.21979},
6 archivePrefix={arXiv},
7 primaryClass={cs.CV},
8 url={https://arxiv.org/abs/2503.21979},
9}