gurumurthy3's picture
Update README.md
9c29d5d verified
|
Raw
History Blame Contribute Delete
5.51 kB
---
license: mit
datasets:
- jxie/flickr8k
language:
- en
base_model:
- openai-community/gpt2
- facebook/dinov3-vitb16-pretrain-lvd1689m
pipeline_tag: image-to-text
library_name: stackformer
tags:
- stackformer
- gpt2
- vision-language
- image-captioning
- cross-attention
- perceiver-resampler
- pytorch
---
# gpt2-stackformer-vision_V2
A GPT-2 backbone rebuilt on the [`stackformer`](https://pypi.org/project/stackformer/)
library, extended with a frozen ViT-B/16 vision encoder and sparse cross-attention layers,
fine-tuned on [Flickr8k](https://huggingface.co/datasets/jxie/flickr8k) for image
captioning. Also supports plain text-to-text continuation (GPT-2-style) when no image is
given.
## Model summary
| | |
|---|---|
| text backbone | GPT-2 small (12 layers, 768 dim, 12 heads) β€” frozen during fine-tuning |
| vision encoder | torchvision `vit_b_16`, ImageNet-pretrained, frozen |
| visual tokens | 128 (compressed from ViT patch tokens via a Perceiver-style resampler) |
| cross-attention | sparse β€” inserted before GPT-2 blocks 3, 6, and 9 |
| trainable params | ~16.6M (vision projector + resampler + 3 cross-attention blocks) |
| total params | ~265M |
| training data | Flickr8k (~8K images Γ— 5 captions) |
| training budget | single T4 GPU, ~5 hours |
| context length | 128 tokens |
GPT-2's pretrained weights were loaded directly into `stackformer`'s `GPT_2` class and kept
frozen throughout training; only the vision-side modules were fine-tuned. Two bugs in
`stackformer`'s attention implementation (inverted causal masking, and attention dropout
not respecting `.eval()`) were identified and patched for both training and inference β€” see
**Known issues** below.
## Usage
This model is built from custom `stackformer`-based classes (`GPT2VL`,
`SparseCrossAttnBlock`, `PerceiverResamplerSF`, `TorchvisionViTEncoder`) rather than a
standard `transformers` architecture, so it cannot be loaded with `AutoModel`. Use the
reference implementation in [this Space](https://huggingface.co/spaces/gurumurthy3/gpt2-stackformer-vision-v2-demo)
(or copy `app.py` from it) to load and run the model β€” it reconstructs the exact
architecture, applies the required `stackformer` bug patches, and loads this checkpoint.
Minimal loading sketch (see the Space's `app.py` for the full, working version including
the model classes and bug patches):
```python
import torch
from huggingface_hub import snapshot_download
from transformers import GPT2TokenizerFast
local_dir = snapshot_download("gurumurthy3/gpt2-stackformer-vision_V2")
ckpt = torch.load(f"{local_dir}/model_checkpoint.pth", map_location="cpu")
cfg = ckpt["config"]
# model = GPT2VL(cfg, device="cpu", dtype=torch.float32) # see app.py for the class
# model.load_state_dict(ckpt["model_state_dict"])
# model.eval()
tokenizer = GPT2TokenizerFast.from_pretrained(f"{local_dir}/tokenizer")
```
### Text-to-text
```python
# images=None -> behaves like a (lightly fine-tuned) GPT-2
logits = model(input_ids, images=None)
```
### Image-to-text (captioning)
```python
visual_context = model.encode_image(image_tensor) # run the vision encoder once
logits = model(input_ids, visual_context=visual_context) # reuse it for every decoding step
```
## Known issues
- **Causal masking bug**: `stackformer`'s boolean causal mask is inverted relative to what
`torch.nn.functional.scaled_dot_product_attention` expects. Uncorrected, this lets
attention see future tokens instead of past ones.
- **Attention dropout ignores `.eval()`**: `stackformer` passes `dropout_p` directly into
`scaled_dot_product_attention`, which (unlike `nn.Dropout`) applies it unconditionally
regardless of `model.train()`/`model.eval()`.
Both are patched at import time in the reference `app.py` β€” if you load this checkpoint
yourself, apply the same patches or generation quality will be degraded.
## Limitations
- The text backbone was frozen during fine-tuning, so language quality is exactly GPT-2
small's β€” fluent but not state-of-the-art.
- Trained on Flickr8k only (~8K natural images, mostly people/animals/everyday scenes), for
a short, single-GPU budget. Expect short, simple, sometimes generic or repetitive
captions, and weaker performance on image domains far from Flickr8k's distribution (e.g.
diagrams, text-heavy images, illustrations).
- 128-token context length β€” long prompts or captions will be truncated.
- Vision context is a soft conditioning signal via cross-attention, not a hard constraint,
so generated text can occasionally ignore or misdescribe image content.
## Training details
![Training loss vs step](loss_vs_step.png)
Loss drops sharply over the first ~1,000 steps (14.7 β†’ ~4) as the randomly-initialized
vision projector, resampler, and cross-attention blocks start aligning with the frozen
GPT-2/ViT representations, then decreases gradually over the remaining ~5 epochs, ending
around 2.7.
- Base text weights: [`openai-community/gpt2`](https://huggingface.co/openai-community/gpt2)
(small, 124M), loaded into `stackformer`'s `GPT_2` and frozen.
- Vision encoder: torchvision `vit_b_16`, `ViT_B_16_Weights.IMAGENET1K_V1`, frozen.
- Optimizer: AdamW, lr 2e-4, weight decay 0.01, effective batch size 32 (batch 16 Γ— grad
accumulation 2).
- Mixed precision (bf16/fp16 depending on GPU support), `pin_memory=True` +
`non_blocking=True` data transfer.
- Stopped via a wall-clock time budget (~4.5 hours) rather than a fixed epoch count, to fit
a single T4 session.