Blog / Technical / SmallVLM — Minimal Vision-Language Model Tutorial

SmallVLM — Minimal Vision-Language Model Tutorial

A from-scratch, explainable implementation of a small Vision-Language Model (VLM) for **image captioning**, built entirely with PyTorch. The goal is to understand every design decision rather than to achieve...

model-architectures

A from-scratch, explainable implementation of a small Vision-Language Model (VLM) for image captioning, built entirely with PyTorch. The goal is to understand every design decision rather than to achieve state-of-the-art results.


What you'll learn

  • How patch embeddings turn a 224×224 image into 196 discrete tokens
  • How a ViT encoder contextualises those tokens with self-attention
  • How a two-layer MLP bridges vision and language representation spaces
  • How a GPT-style decoder generates captions word-by-word using cross-attention to image features
  • How to pre-train, fine-tune, and align a VLM with RL (REINFORCE)
  • Three decoding strategies: greedy, beam search, nucleus sampling
  • Hyperparameter search with Optuna

Architecture at a glance

Image (B,3,224,224)
  → ViT Vision Encoder (6 layers, 384 dim)  →  patch_tokens (B,196,384)
  → MLP Projection (384→768→384)            →  proj_tokens  (B,196,384)
                                                     ↓ cross-attention
Text tokens (B,T)
  → GPT Decoder (6 layers, 384 dim)
  → LM Head (384→50257)                     →  logits (B,T,50257)

Total parameters: ~22M
Full architecture explainer: docs/architecture.md


Project structure

model_architectures/
├── src/
│   ├── model/
│   │   ├── config.py           # ModelConfig dataclass
│   │   ├── vision_encoder.py   # ViT encoder
│   │   ├── projection.py       # MLP connector
│   │   ├── language_decoder.py # GPT decoder with cross-attention
│   │   └── vlm.py              # SmallVLM top-level module
│   ├── data/
│   │   ├── dataset.py          # HuggingFace-backed image-caption dataset
│   │   ├── transforms.py       # Train / val image augmentations
│   │   └── collator.py         # Batch collation
│   ├── training/
│   │   ├── trainer.py          # BaseTrainer (DDP, BF16, checkpointing)
│   │   ├── pretrain.py         # Stage 1: pre-training entry point
│   │   ├── finetune.py         # Stage 2: fine-tuning entry point
│   │   └── rl_sft_trainer.py   # Stage 3: SFT / REINFORCE entry point
│   ├── inference/
│   │   └── generator.py        # Greedy / beam search / nucleus sampling
│   ├── tuning/
│   │   └── hyperparameter_tuning.py  # Optuna HPT
│   └── utils/
│       ├── logging_utils.py    # Structured logger factory
│       └── config_utils.py     # YAML loading and merging
├── configs/
│   ├── model.yaml              # Architecture hyperparameters
│   ├── pretrain.yaml           # Pre-training settings
│   ├── finetune.yaml           # Fine-tuning settings
│   ├── rl_sft.yaml             # RL / SFT settings
│   ├── inference.yaml          # Inference settings
│   └── tuning.yaml             # Optuna search space
├── docs/
│   ├── architecture.md         # Detailed architecture walkthrough
│   ├── training_pipeline.md    # Training stages explained
│   └── diagrams/
│       ├── vlm_architecture.mmd   # Mermaid architecture diagram
│       └── training_flow.mmd      # Mermaid training pipeline diagram
├── docker/
│   ├── Dockerfile              # nvidia/cuda:13.0.1-devel-ubuntu22.04 base
│   ├── .env                    # Environment variable template
│   └── docker-compose.yml      # Services: pretrain, finetune, rl_sft, inference, tuning
└── pyproject.toml

Quick start

1. Build and start the container

bash
cd docker
cp .env .env.local          # fill in DATA_DIR, OUTPUT_DIR, WANDB_API_KEY
docker compose build

2. Pre-train

bash
docker compose run pretrain

Equivalent bare-metal command (from project root):

bash
torchrun --nproc_per_node=2 -m src.training.pretrain

3. Fine-tune

bash
docker compose run finetune

4. RL or SFT alignment

Edit configs/rl_sft.yaml → set mode: rl or mode: sft, then:

bash
docker compose run rl_sft

5. Generate captions

bash
docker compose run inference

6. Tune hyperparameters

bash
docker compose run tuning

Configuration

All constants and hyperparameters are in configs/. No command-line arguments — edit the YAML files instead.


GPU requirements

Stage GPUs Approx. VRAM
Pre-training 2× H200 ~40 GB/GPU
Fine-tuning 2× H200 ~30 GB/GPU
RL / SFT 2× H200 ~35 GB/GPU
Inference 1× H200 ~5 GB
HPT (per trial) 1× H200 ~10 GB

Documentation

Document Description
docs/architecture.md Detailed component walkthrough with tensor shapes
docs/training_pipeline.md Training stage descriptions and algorithms
docs/diagrams/vlm_architecture.mmd Mermaid data-flow diagram
docs/diagrams/training_flow.mmd Mermaid training pipeline diagram