T.I.T.S. β Terrible Imagegen TranSformer
A 16M-parameter text-to-image diffusion model that draws excellent buses, passable pizza, and no birds whatsoever. Trained from scratch in 7.5 hours on one RTX 4060 Ti, for roughly the electricity cost of a load of laundry.
It is bad on purpose, honestly. The math is textbook DDPM, nothing was sabotaged, and no corners were cut except the one that matters: it has 16 million parameters, which in 2026 is a rounding error. Stable Diffusion has 860M. Flux has 12B. T.I.T.S. has slightly fewer parameters than the number of pixels in the images it is trying to make.
Also, despite the name, it is not a transformer. It's a UNet with a single attention layer bolted to the bottom. The acronym came first. The architecture was never consulted and has no comment.
Final samples. Rows: "a motorcycle parked on the street", "a dog on a couch", "a plate of pizza", "a red bus on a city street". Four samples each, guidance 3.0. Note that row 4 is doing great and row 2 is a war crime.
What it can and cannot do
| Prompt | Result | Grade |
|---|---|---|
| Buses, streets, vehicles | Windows. Wheels. A red front end. An actual bus. | A- |
| Pizza, plates of food | Melted cheese, red toppings, crust. You would eat it. | B |
| Motorcycles | Two wheels, handlebars, roughly the correct number of each | C+ |
| Dogs, cats, people | Fleshy blobs arranged on furniture | F |
| Birds | Immaculate foliage. Zero birds. | F |
| Dragons, castles, anime | Abstract expressionism (unintentional) | N/A |
The pattern is boxes yes, animals no. A bus is a rectangle with windows in it. A dog is a deformable mammal photographed from four hundred angles in four hundred lighting conditions. Sixteen million parameters can afford exactly one of those.
"a blue tit on a branch" at guidance 6. It rendered the branch, the leaves, the dappled light, and the general feeling of being in a garden. Then it clocked off for the day.
Specs
| Parameters | 16.10M (image model) + frozen CLIP text encoder |
| Architecture | TinyUNet β base_ch=64, mults (1,1,2,4), 2 res blocks/stage, one lonely self-attention layer at 16Γ16 |
| Resolution | 128Γ128, pixel space (no VAE, no latents, no shortcuts) |
| Text conditioning | Frozen CLIP ViT-B/32 pooled embedding β FiLM in every ResBlock |
| Diffusion | Linear-beta DDPM, 1000 steps, Ξ΅-prediction, ancestral sampling with xβ clipping |
| Guidance | Classifier-free, 10% caption dropout during training. Default scale 3.0 |
| Training data | COCO Captions β 113,287 images, 566,747 captions, all of it from 2014 |
| Training | 3 epochs / 53,130 steps / batch 32 / AdamW @ 2e-4 / bf16 / EMA 0.9995 |
| Hardware | 1Γ RTX 4060 Ti (8GB), 7.5 hours, ~117W, shared with a browser |
| Final loss | 0.0209 |
| Inference | ~13s per image, which is 13s longer than you'll want to wait |
Usage
pip install -r requirements.txt
python sample.py --checkpoint tits_epoch3.pt --prompt "a red bus on a city street" \
--num_images 4 --guidance 3.0 --upscale 2 --out bus.png
Ask it for a bus. It's really good at buses. Let it have this.
--guidance is the classifier-free guidance scale: 1 = off, 3 = good, 6 = saturated,
10 = radioactive. Raising it does not summon a bird. This was tested thoroughly and at some
personal cost.
The checkpoint ships ema_state_dict (used by default, much cleaner) and model_state_dict.
Optimizer state was stripped, so this is for inference, not for resuming. You do not want to
resume this.
Training your own
python prepare_data.py --out_dir data --image_size 128 # streams COCO, saves at 128px
./run_training.sh # 3 epochs, ~7.5h on a 4060 Ti
prepare_data.py streams from HF and only ever writes the downscaled images, so the 19GB source
never touches your disk. Resumable, because it will be interrupted.
run_training.sh --resume checkpoints/latest.pt picks up from the last save (every 2000 steps).
Things learned the hard way
- Web-scraped alt-text data comes with watermarks. Attempt one used CC3M. Roughly 1 in 10 images had an Alamy or Shutterstock watermark visible even at 64Γ64, and the model was well on its way to concluding that a professional photograph has a grey bar across the middle. Switched to COCO. COCO is clean, if permanently stuck in 2014.
- Vanilla DDPM ancestral sampling drifts. Without clipping predicted xβ each step, a small
model's errors compound over 1000 steps until every image is the same shade of burnt orange.
One
.clamp(-1, 1)fixes it. Attempt one looked like a house fire. - Classifier-free guidance is not optional. Without caption dropout the prompt gently suggests rather than instructs, and every output converges on the platonic average COCO photograph, which is a beige room with something in it.
- EMA goes through a blue phase. Early in training the averaged weights still contain a few percent of the random initialization, and everything comes out smurf-colored. It passes.
- Raising the resolution made it dangerously competent. The README originally warned that bumping past 64Γ64 would quietly turn this into a real model. At 128Γ128 it started producing buses you could identify. Consider this a cautionary tale.
Step 2,000 of 53,130, back when it only made weather.
Limitations and intended use
For entertainment, education, and finding out exactly where 16M parameters runs out of road. Not for production. Inherits every bias in COCO, and its worldview ends in 2014 β it has never seen a modern phone, an e-scooter, or a Tesla, and it is happier that way.
Citation
Please don't.


