CoolFace
Modelpublic

Vebbern/Qwen2.5-VL-3B-R2R-panoramic

sourceHugging Faceapache-2.0updated 1y agoView on Hugging Face
0likes8downloads
README.md273 linesDownload Raw Back to root
1---2license: apache-2.03language:4- en5base_model:6- Qwen/Qwen2.5-VL-3B-Instruct7tags:8- R2R9- VLN10- Room-to-Room11- LVLM12---13 14# Qwen2.5-VL-3B-R2R-panoramic15 16**Qwen2.5-VL-3B-R2R-panoramic** is a Vision-and-Language Navigation (VLN) model fine-tuned from [Qwen2.5-VL-3B-Instruct](https://huggingface.co/Qwen/Qwen2.5-VL-3B-Instruct) on the [Room-to-Room (R2R)](https://bringmeaspoon.org/) dataset using the Matterport3D (MP3D) simulator. The model is trained using a panoramic action space, where the model recives a preprocessed panoramic image and a set of candidate views which each point towards a node in a Matterport3D simualtor environment.  17 18Only the LLM component is fine-tuned โ€” the vision encoder and cross-modal projector are kept frozen.19 20 21## ๐Ÿง  Model Summary22 23- **Base Model**: Qwen2.5-VL-3B-Instruct24- **Dataset**: Room-to-Room (R2R) via the Matterport3D simulator.25- **Image Resolution**: 320x240 for candidate images and 960ร—240 for panoramic images.26- **Action Space**: Panoramic.27 28## ๐Ÿงช Training Setup29 30- **Frozen Modules**: Vision encoder and cross-modal projector  31- **Fine-Tuned Module**: LLM decoder (Qwen2.5)  32- **Optimizer**: AdamW  33- **Batch Size**: `1` (with gradient accumulation over each episode)  34- **Learning Rate**: `1e-5`  35- **Weight Decay**: `0.1`  36- **Precision**: `bfloat16`  37- **LR Scheduler**:  Linear scheduler with warmup (first 10% of steps)  38- **Hardware**: Trained on a single NVIDIA A100 80GB GPU  39 40Training was done using supervised learning for next-action prediction. The model was conditioned at each step with a system prompt, panoramic RGB image observations (960ร—240) of current view, as well as variable amount of candidate RGB iamges (320x240), and cumulative episode history including previosu panoramas. The model was trained offline (not in the MP3D simulator) using teacher-forcing on a preprocessed R2R dataset.41 42 43## ๐Ÿ“ฆ Usage 44```python45import torch46from torch.utils.data import Dataset, DataLoader47from datasets import Dataset as DT48from transformers import Qwen2_5_VLForConditionalGeneration, AutoTokenizer, AutoProcessor49from PIL import Image50 51lass CustomDataset(Dataset):52    def __init__(self, data):53        self.text = data["text"]54        self.panoramas = data["panoramas"]55        self.candidates = data["candidates"]56        57    def __len__(self):58        return len(self.text)59    60    def __getitem__(self, index):61        return self.text[index], self.panoramas[index], self.candidates[index]62 63# TODO: make the collatefunctor work with batches64class CollateFunctor:65    # No batch, therefore no max length66    def __init__(self, processor, width, height):67        self.processor = processor68        self.width = width69        self.height = height70 71    def __call__(self, batch):72        text, panoramas, candidates = batch[0]73        label_start = processor.tokenizer("<|im_start|>assistant\nCandidate: ", return_tensors="pt").input_ids74 75        images = [Image.open(img) for img in panoramas]76        candidate_images = [Image.open(img) for img in candidates]77        #candidate_images = [Image.open(img).resize((self.width, self.height), Image.Resampling.LANCZOS) for img in candidates]78        images.extend(candidate_images)79        80        processed = processor(text=text, images=[images], return_tensors="pt")81 82        prompt_input_ids = processed["input_ids"]83        input_ids = torch.cat([prompt_input_ids, label_start], dim=1)84        85        attention_mask = torch.ones(1, input_ids.shape[1])86        processed["input_ids"] = input_ids87        processed["attention_mask"] = attention_mask88        89        return processed90 91 92def format_prompt(images_path, path_id, route_instruction, step_id, distance_traveled, candidates, processor, system_prompt):93    # should be in the order: panorama_history, current_panorama, candidates views from left to right94    images = os.listdir(images_path)95    panoramas = [os.path.join(images_path, img) for img in images if img.startswith("pano")]96    panoramas = sorted(panoramas, key=lambda x: int(x.split("_")[-1].split(".")[-2]))97 98    # these are probably sorted by default, however you might need to check99    candidate_images = [os.path.join(images_path, img) for img in images if img.startswith("pano") == False]100    candidate_images = sorted(candidate_images, key=lambda x: int(x.split("_")[-1].split(".")[0]))101    102    current_panorama = panoramas.pop(-1)103 104    # route instruction, current step, cumulative distance105    content = [106        {107            "type" : "text",108            "text" : f"Route instruction: {route_instruction}\nCurrent step: {step_id}\nCumulative Distance Traveled: {distance_traveled} meters\n\nPanorama Images from Previous Steps:"109        }110    ]111 112    # panorama from previous steps113    for i, img in enumerate(panoramas):114        content.append({115            "type" : "text",116            "text" : f"\n\tPanorama at step: {i}: "117        })118        content.append({119            "type" : "image",120            "image" : img121        })122 123    if len(panoramas) == 0:124        content[0]["text"] += f"[]"125 126    # current panorama127    content.append({128        "type" : "text",129        "text" : f"\n\nCurrent Panorama Image:\n\t"130    })131 132    content.append({133        "type" : "image",134        "image" : current_panorama135    })136 137    # candidate directions138    content.append({139        "type" : "text",140        "text" : "\n\nCandidate Directions:"141    })142 143    for i, candidate in enumerate(candidates):144        relative_angle = round(candidate["relative_angle"], 0)145        distance = round(candidate["distance"], 2)146        direction = "Left" if relative_angle < 0 else "Right"147        148        content.append({149            "type" : "text",150            "text" : f"\n\tCandidate: {i}:\n\t\tRelative angle: {abs(relative_angle)} degrees to the {direction}\n\t\tDistance: {distance} meters\n\t\tview: "151        })152 153        content.append({154            "type" : "image",155            "image" : candidate_images[i]156        })157 158 159    # adds candidate STOP and the select cnadidate view 160    content.append({161        "type" : "text",162        "text" : "\n\tCandidate: Stop\n\nNow, analyze the route instruction, your current position, and the available candidate directions. Select the candidate that best matches the instruction and helps you continue along the correct path. Answer on the format: Candidate: (and then the number)"163    })164 165    messages = [166        {"role" : "system", "content" : [{"type" : "text", "text" : system_prompt}]},167        {"role" : "user", "content" : content},168    ]169 170    text = processor.apply_chat_template(messages, tokenize=False, add_generation_prompt=False)171 172    panoramas.extend([current_panorama])173 174    formatted_sample = {}175    formatted_sample["text"] = text176    formatted_sample["candidates"] = candidate_images177    formatted_sample["panoramas"] = panoramas178 179    formatted_data = [formatted_sample] 180    formatted_data = DT.from_list(formatted_data)181    return formatted_data182 183 184processor = AutoProcessor.from_pretrained("Vebbern/Qwen2.5-VL-3B-R2R-panoramic")185model = Qwen2_5_VLForConditionalGeneration.from_pretrained(186    "Vebbern/Qwen2.5-VL-3B-R2R-panoramic",187    torch_dtype=torch.bfloat16,188    attn_implementation="flash_attention_2",189    device_map="cuda"190)191 192# remember to set the correct image resolution (however a higher might still work as the vision encoder is not trained)193collate_fn = CollateFunctor(processor, 320, 240)194 195# Load mandatory system prompt196with open("system_prompt.txt", "r") as f:197    system_prompt = f.read()198 199path_id = 4332 # id for the R2R path200route_instruction = "Walk to the other end of the lobby and wait near the exit. "201images_path = f"./images/{path_id}"202step_id = 0203cumulative_distance = 0204candidates = {205    "0" : {206        "relative_angle" -60.62797609213225,207        "relative_direction": "Left",208        "distance": 2.3325929641723633209    },210    "1": {211            "relative_angle": -0.00397697185949581,212            "relative_direction": "Front",213            "distance": 4.637096405029297214        },215    "2": {216        "relative_angle": 25.24592108757226,217        "relative_direction": "Front",218        "distance": 3.3661904335021973219        }220}221 222prompt = format_prompt(images_path, path_id, route_instruction, step_id, cumulative_distance, candidates, processor, system_prompt)223 224dataset = CustomDataset(prompt)225data_loader = DataLoader(226    dataset,227    batch_size=1,228    collate_fn=collate_fn229)230 231# Run inference232for batch in data_loader:233    batch.to("cuda")234            235    outputs = model(**batch)236    argmax = torch.argmax(outputs.logits, dim=2)[0]237    model_prediction = processor.decode(argmax[-1]) # is -1 because it does not predict one more238    print(f"Predicted action: {model_prediction}")239 240```241 242> โš ๏ธ Sorry for the rough code โ€” the goal here is to show how the system prompt and inputs should be structured for inference. The system prompt is included in the repo.243 244 245## ๐Ÿ“Š Evaluation Results246 247The model was evaluated on the standard Room-to-Room (R2R) validation sets using the Matterport3D simulator. Performance is measured using the standard VLN (Vision-and-Language Navigation) metrics.248 249| Metric                  | Val Seen | Val Unseen | Test  |250|-------------------------|----------|------------|-------|251| Path Length (โ†“)         | 9.98     | 9.83      | 9.96 |252| Navigation Error (โ†“)    | 5.69     | 6.65       | 6.53  |253| Oracle Success Rate (โ†‘) | 56%      | 46%        | 50%   |254| Success Rate (โ†‘)        | 50%      | 38%        | 41%   |255| SPL (โ†‘)                 | 47%      | 35%        | 38%   |256 257### ๐Ÿงพ Metric Definitions258- **Navigation Error**: Mean distance from the goal when the agent stops.259- **Success Rate**: Percentage of episodes where the agent ends within 3 meters of the goal.260- **SPL (Success weighted by Path Length)**: Penalizes long or inefficient paths.261- **Oracle Success**: If the agent had stopped at its closest point to the goal.262 263### ๐Ÿ“ Remarks264 265This model performs far behind R2R State-of-the-art models, likely due to a combination of factors such as underlying model archtiecture, training strategy, and panoramic representation. 266 267## ๐Ÿ” Related Models268There also exists a low-level action space eqivalent of this model.269- **Low-Level Action Space Version**: [Qwen2.5-VL-3B-R2R-low-level](https://huggingface.co/Vebbern/Qwen2.5-VL-3B-R2R-low-level)270 271## ๐Ÿชช License272 273This model is licensed under the [Apache License 2.0](https://www.apache.org/licenses/LICENSE-2.0).