Siriuslala/vlm_interp

Interpretability for VLMs (pure hand-written, no vibe-coding)

Python

4

15 commits

updated Jan 6, 2026

See the code

README

Reading Images Like Texts: Sequential Image Understanding in Vision-Language Models

πŸ“ƒ Paper

πŸ‘‹ We encourage everyone to explore deeper on VLMs based on our methods!

Introduction

Inspired by the dual-stream hypothesis of human vision, which distinguishes the "what" and "where" pathways, we deconstruct the visual processing in VLMs into two parts:

  • object recognition -- the "what" way
  • spatial perception -- the "where" way

For object recognition, we convert images into text token maps and find that the model's perception of image content unfolds as a two-stage process from shallow to deep layers, beginning with attribute recognition and culminating in semantic disambiguation.

For spatial perception, we theoretically derive and empirically verify the geometric structure underlying the positional representation in VLMs.

Based on these findings, we introduce an instruction-agnostic token compression algorithm based on a plug-and-play visual decoder to improve decoding efficiency, and a RoPE scaling technique to enhance spatial reasoning. Through rigorous experiments, our work validates these analyses, offering a deeper understanding of VLM internals and providing clear principles for designing more capable future architectures.

Quick Start

Environment Setup

We recommend you to create a conda environment first as follows:

conda create -n mm python=3.12
conda activate mm

Then clone our repository and install the required packages:

git clone https://github.com/Siriuslala/vlm_interp.git
cd vlm_interp
pip install -r requirements.txt

Before starting, please create a .env file, and finish the configuration of the environment variables following the example in .env_example:

ROOT_DIR=./  # where your code is located
DATA_DIR=/path/to/your/data  # where your data is located
WORK_DIR=/path/to/your/work  # intermediate files (e.g., model checkpoints, log files, etc.)

Directory Structure

[vlm_interp/]
β”œβ”€β”€ .env_example
β”œβ”€β”€ eval  # evaluation scripts
β”‚   β”œβ”€β”€ CHAIR
β”‚   β”œβ”€β”€ COCO
β”‚   β”œβ”€β”€ GQA
β”‚   β”œβ”€β”€ MMBench
β”‚   β”œβ”€β”€ MME
β”‚   β”œβ”€β”€ POPE
β”‚   β”œβ”€β”€ SQA
β”‚   β”œβ”€β”€ TextVQA
β”‚   β”œβ”€β”€ VQA
β”‚   β”œβ”€β”€ VSR
β”‚   β”œβ”€β”€ WhatsUp
β”‚   β”‚   β”œβ”€β”€ bboxes  # our annotations for WhatsUp
β”‚   β”‚   └── dataset_zoo
β”‚   β”œβ”€β”€ custom_data.py
β”‚   └── data_utils.py
β”œβ”€β”€ font
β”‚   └── SimHei.ttf
β”œβ”€β”€ model  # include the visual decoder and RoPE scaling model
β”‚   β”œβ”€β”€ qwen2_vl_rope_scaling.py
β”‚   └── unembedding.py
β”œβ”€β”€ patch  # monkey patch files
β”‚   β”œβ”€β”€ intern_2_5_hijack.py
β”‚   β”œβ”€β”€ internvl2_5_utils
β”‚   β”œβ”€β”€ llava1_5_vl_hijack.py
β”‚   β”œβ”€β”€ monkey_patch.py
β”‚   └── qwen2_5_vl_hijack.py
β”œβ”€β”€ requirements.txt
β”œβ”€β”€ test  # simple tests
β”‚   β”œβ”€β”€ test_intern.py
β”‚   β”œβ”€β”€ test_llava.py
β”‚   β”œβ”€β”€ test_pred.py
β”‚   β”œβ”€β”€ test_pred_batch.py
β”‚   └── test_qwen.py
β”œβ”€β”€ test_direction.py  # test scripts for the exploration of spatial perception
β”œβ”€β”€ test_pos_embed.py  # test scripts for the exploration of positional embedding
β”œβ”€β”€ test_seg_map.py  # test scripts for the exploration of object recognition
β”œβ”€β”€ train_rope_scaling  # scripts for training the RoPE scaling model
β”‚   β”œβ”€β”€ qwen2_vl_sft_parallel.py
β”‚   β”œβ”€β”€ qwen2_vl_sft_single_gpu.py
β”‚   β”œβ”€β”€ qwen_vl_finetune
β”‚   └── scripts
β”‚       β”œβ”€β”€ sft.sh
β”‚       β”œβ”€β”€ sft_official.sh
β”‚       └── sft_parellel.sh
β”œβ”€β”€ train_unembedding  # scripts for training the visual decoder for token compression
β”‚   β”œβ”€β”€ dataset.py
β”‚   β”œβ”€β”€ draw.py
β”‚   β”œβ”€β”€ scripts
β”‚   β”‚   └── train.sh
β”‚   └── train.py
└── utils
    β”œβ”€β”€ __init__.py
    β”œβ”€β”€ data_process.py  # scripts for processing the dataset
    β”œβ”€β”€ download.py
    β”œβ”€β”€ draw_file_tree.py
    β”œβ”€β”€ env.sh
    β”œβ”€β”€ font.sh
    └── intern_vl.py

Object Recognition

The scripts for object recognition are located in test_seg_map.py. This script contains the functions corresponding to the experiments in Section 3 of our paper. Specific usage details can be found in the comments of the script (after if __name__ == '__main__':).

Some of the important functions in test_seg_map.py are:

  • seg_with_unembedding_tokens: draw the text maps and segmentation maps for LLaVA;
  • seg_with_unembedding_tokens_qwen: draw the text maps and segmentation maps for QwenVL;

Spatial Perception

The scripts for spatial perception are located in test_direction.py. This script contains the functions corresponding to the experiments in Section 4 of our paper. Specific usage details can be found in the comments of the script (after if __name__ == '__main__':).

Some of the important functions in test_direction.py are:

  • explore_1d_pos_embed_visual_geometry: plot the 1D positional embedding in visual geometry (Section 4.1);
  • get_relation_representations_layerwise: plot the relation representations layerwise for left of, right of, behind, in front of (Section 4.2);
  • intervene_in_spatial_reasoning: intervene in the spatial reasoning process (Section 4.2);
  • erase_object_in_llm: the erase test in Section 4.2;

Citation

@article{li2025reading,
  title={Reading Images Like Texts: Sequential Image Understanding in Vision-Language Models},
  author={Li, Yueyan and Zhao, Chenggong and Zang, Zeyuan and Yuan, Caixia and Wang, Xiaojie},
  journal={arXiv preprint arXiv:2509.19191},
  year={2025}
}

Siriuslala/vlm_interp

Interpretability for VLMs (pure hand-written, no vibe-coding)

Python

4

15 commits

updated Jan 6, 2026

See the code

README

Reading Images Like Texts: Sequential Image Understanding in Vision-Language Models

πŸ“ƒ Paper

πŸ‘‹ We encourage everyone to explore deeper on VLMs based on our methods!

Introduction

Inspired by the dual-stream hypothesis of human vision, which distinguishes the "what" and "where" pathways, we deconstruct the visual processing in VLMs into two parts:

  • object recognition -- the "what" way
  • spatial perception -- the "where" way

For object recognition, we convert images into text token maps and find that the model's perception of image content unfolds as a two-stage process from shallow to deep layers, beginning with attribute recognition and culminating in semantic disambiguation.

For spatial perception, we theoretically derive and empirically verify the geometric structure underlying the positional representation in VLMs.

Based on these findings, we introduce an instruction-agnostic token compression algorithm based on a plug-and-play visual decoder to improve decoding efficiency, and a RoPE scaling technique to enhance spatial reasoning. Through rigorous experiments, our work validates these analyses, offering a deeper understanding of VLM internals and providing clear principles for designing more capable future architectures.

Quick Start

Environment Setup

We recommend you to create a conda environment first as follows:

conda create -n mm python=3.12
conda activate mm

Then clone our repository and install the required packages:

git clone https://github.com/Siriuslala/vlm_interp.git
cd vlm_interp
pip install -r requirements.txt

Before starting, please create a .env file, and finish the configuration of the environment variables following the example in .env_example:

ROOT_DIR=./  # where your code is located
DATA_DIR=/path/to/your/data  # where your data is located
WORK_DIR=/path/to/your/work  # intermediate files (e.g., model checkpoints, log files, etc.)

Directory Structure

[vlm_interp/]
β”œβ”€β”€ .env_example
β”œβ”€β”€ eval  # evaluation scripts
β”‚   β”œβ”€β”€ CHAIR
β”‚   β”œβ”€β”€ COCO
β”‚   β”œβ”€β”€ GQA
β”‚   β”œβ”€β”€ MMBench
β”‚   β”œβ”€β”€ MME
β”‚   β”œβ”€β”€ POPE
β”‚   β”œβ”€β”€ SQA
β”‚   β”œβ”€β”€ TextVQA
β”‚   β”œβ”€β”€ VQA
β”‚   β”œβ”€β”€ VSR
β”‚   β”œβ”€β”€ WhatsUp
β”‚   β”‚   β”œβ”€β”€ bboxes  # our annotations for WhatsUp
β”‚   β”‚   └── dataset_zoo
β”‚   β”œβ”€β”€ custom_data.py
β”‚   └── data_utils.py
β”œβ”€β”€ font
β”‚   └── SimHei.ttf
β”œβ”€β”€ model  # include the visual decoder and RoPE scaling model
β”‚   β”œβ”€β”€ qwen2_vl_rope_scaling.py
β”‚   └── unembedding.py
β”œβ”€β”€ patch  # monkey patch files
β”‚   β”œβ”€β”€ intern_2_5_hijack.py
β”‚   β”œβ”€β”€ internvl2_5_utils
β”‚   β”œβ”€β”€ llava1_5_vl_hijack.py
β”‚   β”œβ”€β”€ monkey_patch.py
β”‚   └── qwen2_5_vl_hijack.py
β”œβ”€β”€ requirements.txt
β”œβ”€β”€ test  # simple tests
β”‚   β”œβ”€β”€ test_intern.py
β”‚   β”œβ”€β”€ test_llava.py
β”‚   β”œβ”€β”€ test_pred.py
β”‚   β”œβ”€β”€ test_pred_batch.py
β”‚   └── test_qwen.py
β”œβ”€β”€ test_direction.py  # test scripts for the exploration of spatial perception
β”œβ”€β”€ test_pos_embed.py  # test scripts for the exploration of positional embedding
β”œβ”€β”€ test_seg_map.py  # test scripts for the exploration of object recognition
β”œβ”€β”€ train_rope_scaling  # scripts for training the RoPE scaling model
β”‚   β”œβ”€β”€ qwen2_vl_sft_parallel.py
β”‚   β”œβ”€β”€ qwen2_vl_sft_single_gpu.py
β”‚   β”œβ”€β”€ qwen_vl_finetune
β”‚   └── scripts
β”‚       β”œβ”€β”€ sft.sh
β”‚       β”œβ”€β”€ sft_official.sh
β”‚       └── sft_parellel.sh
β”œβ”€β”€ train_unembedding  # scripts for training the visual decoder for token compression
β”‚   β”œβ”€β”€ dataset.py
β”‚   β”œβ”€β”€ draw.py
β”‚   β”œβ”€β”€ scripts
β”‚   β”‚   └── train.sh
β”‚   └── train.py
└── utils
    β”œβ”€β”€ __init__.py
    β”œβ”€β”€ data_process.py  # scripts for processing the dataset
    β”œβ”€β”€ download.py
    β”œβ”€β”€ draw_file_tree.py
    β”œβ”€β”€ env.sh
    β”œβ”€β”€ font.sh
    └── intern_vl.py

Object Recognition

The scripts for object recognition are located in test_seg_map.py. This script contains the functions corresponding to the experiments in Section 3 of our paper. Specific usage details can be found in the comments of the script (after if __name__ == '__main__':).

Some of the important functions in test_seg_map.py are:

  • seg_with_unembedding_tokens: draw the text maps and segmentation maps for LLaVA;
  • seg_with_unembedding_tokens_qwen: draw the text maps and segmentation maps for QwenVL;

Spatial Perception

The scripts for spatial perception are located in test_direction.py. This script contains the functions corresponding to the experiments in Section 4 of our paper. Specific usage details can be found in the comments of the script (after if __name__ == '__main__':).

Some of the important functions in test_direction.py are:

  • explore_1d_pos_embed_visual_geometry: plot the 1D positional embedding in visual geometry (Section 4.1);
  • get_relation_representations_layerwise: plot the relation representations layerwise for left of, right of, behind, in front of (Section 4.2);
  • intervene_in_spatial_reasoning: intervene in the spatial reasoning process (Section 4.2);
  • erase_object_in_llm: the erase test in Section 4.2;

Citation

@article{li2025reading,
  title={Reading Images Like Texts: Sequential Image Understanding in Vision-Language Models},
  author={Li, Yueyan and Zhao, Chenggong and Zang, Zeyuan and Yuan, Caixia and Wang, Xiaojie},
  journal={arXiv preprint arXiv:2509.19191},
  year={2025}
}