LZXzju/Qwen2.5-VL-3B-UI-R1-E

Model

Introduction

5

16 commits

2 linked in READMEs

updated May 18, 2025

See the code

README

Introduction

This repository contains the efficient GUI grounding model, UI-R1-E-3B, presented in UI-R1: Enhancing Action Prediction of GUI Agents by Reinforcement Learning.

Project page: https://github.com/lll6gg/UI-R1

Old version: UI-R1-3B

Benchmark 1: ScreenSpotV2

ScreenSpotV2inference modeMobile-TMobile-IDesktop-TDesktop-IWeb-TWeb-IAvg↑ / Len↓
OS-ATLAS-7Bw/o thinking95.275.890.763.690.677.384.1 /
UI-TARS-7Bw/o thinking95.279.190.768.690.678.384.7 /
UI-R1-3B (v1)w/ thinking96.284.392.363.689.275.485.4 / 67
GUI-R1-3Bw/ thinking97.678.294.364.391.072.485.0 / 80
UI-R1-3B (v2)w/ thinking97.679.692.367.988.977.885.8 / 60
UI-R1-E-3Bw/o thinking98.283.994.875.093.283.789.5 / 28

Benchmark 2: ScreenSpot-Pro

ScreenSpot-Proinference modeAverage Length↓Average Accuracy↑
UGround-7Bw/o thinking-16.5
OS-ATLAS-7Bw/o thinking-18.9
UI-R1-3B (v1)w/ thinking10217.8
GUI-R1-3Bw/ thinking11426.6
UI-R1-3B (v2)w/ thinking12929.8
UI-R1-E-3Bw/o thinking2833.5

Leaderboard: UI-I2E-Bench

ModelScreenSpotUI-I2E-Bench AvgScreenSpot-ProAvg
UI-TARS-1.5-7B88.173.242.267.8
Uground-V1-72B89.776.334.366.8
UI-TARS-72B88.473.738.166.7
UI-R1-E-3B89.269.133.563.9
Uground-V1-7B87.170.331.162.8
InfiGUI-R187.569.729.662.3
UI-TARS-7B89.561.435.762.2
Qwen2.5-VL-72B87.151.443.660.7
UI-I2E-VLM-7B82.569.523.658.5
UI-TARS-2B82.36227.757.3
Qwen2.5-VL-7B84.753.82955.8
OmniParser-V27254.839.655.5
Uground-V1-2B78.857.426.654.3
OS-Atlas-7B82.558.618.953.3
UI-R1-3B83.358.517.853.2
UGround-7B74.154.216.548.3
UI-I2E-VLM-4B70.453.412.245.3
OmniParser73.953.18.345.1
ShowUI-2B76.841.57.742
Qwen2.5-VL-3B55.541.723.941.3
Aguvis-7B84.453.222.940.4
OS-Atlas-4B70.144.33.739.4
Qwen2-VL-7B42.648.71.631
Seeclick55.826.41.127.8
InternVL2-4B4.20.90.31.8

Evaluation Code for GUI Grounding

  1. Generation for UI-R1-E-3B:

    model = Qwen2_5_VLForConditionalGeneration.from_pretrained(
        args.model_path,
        torch_dtype=torch.bfloat16,
        attn_implementation="flash_attention_2",
        device_map="cpu",
    )
    model = model.to(torch.device(rank))
    model = model.eval()
    processor = AutoProcessor.from_pretrained(ori_processor_path)
    question_template = (
        f"In this UI screenshot, I want to perform the command '{task_prompt}'.\n"
        "Please provide the action to perform (enumerate in ['click'])"
        "and the coordinate where the cursor is moved to(integer) if click is performed.\n"
        "Output the final answer in <answer> </answer> tags directly."
        "The output answer format should be as follows:\n"
        "<answer>[{'action': 'click', 'coordinate': [x, y]}]</answer>\n"
        "Please strictly follow the format."
    )
    query = '<image>\n' + question_template
    messages = [
        {
            "role": "user",
            "content": [
                {"type": "image", "image": image_path}
            ] + [{"type": "text", "text": query}],
        }
    ]
    text = processor.apply_chat_template(
        messages, tokenize=False, add_generation_prompt=True
    )
    image_inputs, video_inputs = process_vision_info(messages)
    inputs = processor(
        text=[text],
        images=image_inputs,
        videos=video_inputs,
        padding=True,
        return_tensors="pt",
    )
    generated_ids = model.generate(**inputs, max_new_tokens=1024)
    generated_ids_trimmed = [
        out_ids[len(in_ids) :] for in_ids, out_ids in zip(inputs.input_ids, generated_ids)
    ]
    response = processor.batch_decode(
        generated_ids_trimmed, skip_special_tokens=True, clean_up_tokenization_spaces=False
    )
    response = response[0]
    pred_coord, _ = extract_coord(response)
    
  2. Rescale the predicted coordinate according to the image resize

    image = Image.open(image_path)
    origin_width, origin_height = image.size
    resized_height,resized_width = smart_resize(origin_height,origin_width,max_pixels=12845056)
    scale_x = origin_width / resized_width
    scale_y = origin_height / resized_height
    pred_coord[0] = int(pred_coord[0] * scale_x)
    pred_coord[1] = int(pred_coord[1] * scale_y)
    

    Function smart_resize is from Qwen2VL:

    import math
    def smart_resize(
        height: int, width: int, factor: int = 28, min_pixels: int = 56 * 56, max_pixels: int = 14 * 14 * 4 * 1280
    ):
        """Rescales the image so that the following conditions are met:
    
        1. Both dimensions (height and width) are divisible by 'factor'.
    
        2. The total number of pixels is within the range ['min_pixels', 'max_pixels'].
    
        3. The aspect ratio of the image is maintained as closely as possible.
    
        """
        if height < factor or width < factor:
            raise ValueError(f"height:{height} or width:{width} must be larger than factor:{factor}")
        elif max(height, width) / min(height, width) > 200:
            raise ValueError(
                f"absolute aspect ratio must be smaller than 200, got {max(height, width) / min(height, width)}"
            )
        h_bar = round(height / factor) * factor
        w_bar = round(width / factor) * factor
        if h_bar * w_bar > max_pixels:
            beta = math.sqrt((height * width) / max_pixels)
            h_bar = math.floor(height / beta / factor) * factor
            w_bar = math.floor(width / beta / factor) * factor
        elif h_bar * w_bar < min_pixels:
            beta = math.sqrt(min_pixels / (height * width))
            h_bar = math.ceil(height * beta / factor) * factor
            w_bar = math.ceil(width * beta / factor) * factor
        return h_bar, w_bar
    
qwen2_5_vl
safetensors
visual-question-answering

Contributors

LZXzju

16 commits

LZXzju/Qwen2.5-VL-3B-UI-R1-E

Model

Introduction

5

16 commits

2 linked in READMEs

updated May 18, 2025

See the code

README

Introduction

This repository contains the efficient GUI grounding model, UI-R1-E-3B, presented in UI-R1: Enhancing Action Prediction of GUI Agents by Reinforcement Learning.

Project page: https://github.com/lll6gg/UI-R1

Old version: UI-R1-3B

Benchmark 1: ScreenSpotV2

ScreenSpotV2inference modeMobile-TMobile-IDesktop-TDesktop-IWeb-TWeb-IAvg↑ / Len↓
OS-ATLAS-7Bw/o thinking95.275.890.763.690.677.384.1 /
UI-TARS-7Bw/o thinking95.279.190.768.690.678.384.7 /
UI-R1-3B (v1)w/ thinking96.284.392.363.689.275.485.4 / 67
GUI-R1-3Bw/ thinking97.678.294.364.391.072.485.0 / 80
UI-R1-3B (v2)w/ thinking97.679.692.367.988.977.885.8 / 60
UI-R1-E-3Bw/o thinking98.283.994.875.093.283.789.5 / 28

Benchmark 2: ScreenSpot-Pro

ScreenSpot-Proinference modeAverage Length↓Average Accuracy↑
UGround-7Bw/o thinking-16.5
OS-ATLAS-7Bw/o thinking-18.9
UI-R1-3B (v1)w/ thinking10217.8
GUI-R1-3Bw/ thinking11426.6
UI-R1-3B (v2)w/ thinking12929.8
UI-R1-E-3Bw/o thinking2833.5

Leaderboard: UI-I2E-Bench

ModelScreenSpotUI-I2E-Bench AvgScreenSpot-ProAvg
UI-TARS-1.5-7B88.173.242.267.8
Uground-V1-72B89.776.334.366.8
UI-TARS-72B88.473.738.166.7
UI-R1-E-3B89.269.133.563.9
Uground-V1-7B87.170.331.162.8
InfiGUI-R187.569.729.662.3
UI-TARS-7B89.561.435.762.2
Qwen2.5-VL-72B87.151.443.660.7
UI-I2E-VLM-7B82.569.523.658.5
UI-TARS-2B82.36227.757.3
Qwen2.5-VL-7B84.753.82955.8
OmniParser-V27254.839.655.5
Uground-V1-2B78.857.426.654.3
OS-Atlas-7B82.558.618.953.3
UI-R1-3B83.358.517.853.2
UGround-7B74.154.216.548.3
UI-I2E-VLM-4B70.453.412.245.3
OmniParser73.953.18.345.1
ShowUI-2B76.841.57.742
Qwen2.5-VL-3B55.541.723.941.3
Aguvis-7B84.453.222.940.4
OS-Atlas-4B70.144.33.739.4
Qwen2-VL-7B42.648.71.631
Seeclick55.826.41.127.8
InternVL2-4B4.20.90.31.8

Evaluation Code for GUI Grounding

  1. Generation for UI-R1-E-3B:

    model = Qwen2_5_VLForConditionalGeneration.from_pretrained(
        args.model_path,
        torch_dtype=torch.bfloat16,
        attn_implementation="flash_attention_2",
        device_map="cpu",
    )
    model = model.to(torch.device(rank))
    model = model.eval()
    processor = AutoProcessor.from_pretrained(ori_processor_path)
    question_template = (
        f"In this UI screenshot, I want to perform the command '{task_prompt}'.\n"
        "Please provide the action to perform (enumerate in ['click'])"
        "and the coordinate where the cursor is moved to(integer) if click is performed.\n"
        "Output the final answer in <answer> </answer> tags directly."
        "The output answer format should be as follows:\n"
        "<answer>[{'action': 'click', 'coordinate': [x, y]}]</answer>\n"
        "Please strictly follow the format."
    )
    query = '<image>\n' + question_template
    messages = [
        {
            "role": "user",
            "content": [
                {"type": "image", "image": image_path}
            ] + [{"type": "text", "text": query}],
        }
    ]
    text = processor.apply_chat_template(
        messages, tokenize=False, add_generation_prompt=True
    )
    image_inputs, video_inputs = process_vision_info(messages)
    inputs = processor(
        text=[text],
        images=image_inputs,
        videos=video_inputs,
        padding=True,
        return_tensors="pt",
    )
    generated_ids = model.generate(**inputs, max_new_tokens=1024)
    generated_ids_trimmed = [
        out_ids[len(in_ids) :] for in_ids, out_ids in zip(inputs.input_ids, generated_ids)
    ]
    response = processor.batch_decode(
        generated_ids_trimmed, skip_special_tokens=True, clean_up_tokenization_spaces=False
    )
    response = response[0]
    pred_coord, _ = extract_coord(response)
    
  2. Rescale the predicted coordinate according to the image resize

    image = Image.open(image_path)
    origin_width, origin_height = image.size
    resized_height,resized_width = smart_resize(origin_height,origin_width,max_pixels=12845056)
    scale_x = origin_width / resized_width
    scale_y = origin_height / resized_height
    pred_coord[0] = int(pred_coord[0] * scale_x)
    pred_coord[1] = int(pred_coord[1] * scale_y)
    

    Function smart_resize is from Qwen2VL:

    import math
    def smart_resize(
        height: int, width: int, factor: int = 28, min_pixels: int = 56 * 56, max_pixels: int = 14 * 14 * 4 * 1280
    ):
        """Rescales the image so that the following conditions are met:
    
        1. Both dimensions (height and width) are divisible by 'factor'.
    
        2. The total number of pixels is within the range ['min_pixels', 'max_pixels'].
    
        3. The aspect ratio of the image is maintained as closely as possible.
    
        """
        if height < factor or width < factor:
            raise ValueError(f"height:{height} or width:{width} must be larger than factor:{factor}")
        elif max(height, width) / min(height, width) > 200:
            raise ValueError(
                f"absolute aspect ratio must be smaller than 200, got {max(height, width) / min(height, width)}"
            )
        h_bar = round(height / factor) * factor
        w_bar = round(width / factor) * factor
        if h_bar * w_bar > max_pixels:
            beta = math.sqrt((height * width) / max_pixels)
            h_bar = math.floor(height / beta / factor) * factor
            w_bar = math.floor(width / beta / factor) * factor
        elif h_bar * w_bar < min_pixels:
            beta = math.sqrt(min_pixels / (height * width))
            h_bar = math.ceil(height * beta / factor) * factor
            w_bar = math.ceil(width * beta / factor) * factor
        return h_bar, w_bar
    
qwen2_5_vl
safetensors
visual-question-answering

Contributors

LZXzju

16 commits