|
Download docs/USAGE.md from Duoia/duovlm-40m-v1: direct link, hf CLI and curl.
- Browser
- Download file 5.85 kB
-
https://huggingface.co/Duoia/duovlm-40m-v1/resolve/main/docs/USAGE.md
- Command line
-
hf download hf://Duoia/duovlm-40m-v1/docs/USAGE.md
-
curl -L -o USAGE.md https://huggingface.co/Duoia/duovlm-40m-v1/resolve/main/docs/USAGE.md
5.85 kB
| # USAGE (English) / 使用说明 | |
| DuoVLM-40M is **not a transformers model**. There is no `AutoModel.from_pretrained` path: the | |
| vision encoder is a stock CLIP, but the language model is a 12-layer GPT written against | |
| `litgpt==0.5.13`, and image features are injected by overwriting 196 placeholder-token embeddings. | |
| All of that lives in `code/`. | |
| ## 1. Install | |
| ```bash | |
| python -m venv .venv && . .venv/bin/activate | |
| pip install -r requirements.txt | |
| # torch with your CUDA build, e.g.: | |
| # pip install torch --index-url https://download.pytorch.org/whl/cu124 | |
| ``` | |
| CPU-only machines work: set `DUOVLM_DEVICE=cpu` (runs in fp32, ~1–3 s per question). | |
| ## 2. Environment variables (nothing is hard-coded to the author's machine) | |
| | var | default | meaning | | |
| |---|---|---| | |
| | `DUOVLM_WEIGHTS` | `weights/duovlm-s3-final.pth` | checkpoint path | | |
| | `DUOVLM_CFG` | `<weights dir>/model_config.yaml` | architecture spec | | |
| | `DUOVLM_TOKENIZER` | `tokenizer/` | our 8k BPE tokenizer | | |
| | `DUOVLM_CLIP` | `openai/clip-vit-base-patch16` | vision tower (HuggingFace id) | | |
| | `DUOVLM_CLIP_DIR` | *(unset)* | local CLIP directory; **wins over `DUOVLM_CLIP`** (offline use) | | |
| | `DUOVLM_DEVICE` | auto | `cuda` or `cpu` | | |
| ## 3. Ask a question | |
| ```bash | |
| python code/ask_cli.py --image cat.jpg --question "What is the cat doing?" | |
| python code/ask_cli.py --image cat.jpg # no question = describe the image | |
| python code/ask_cli.py --image cat.jpg --question "..." --blind # also run the "no image" control | |
| python code/ask_cli.py --dir ./photos --question "What is happening?" | |
| python code/ask_cli.py --text "The history of the city begins in" --continue-n 60 # text-only continuation | |
| python code/ask_cli.py --image cat.jpg --question "..." --json # machine-readable output | |
| ``` | |
| **Questions must be English.** The model never saw another language. | |
| ## 4. Python API | |
| ```python | |
| import sys; sys.path.insert(0, "code") | |
| from duovlm_infer import DuoVLMInfer | |
| vlm = DuoVLMInfer() # loads once (~1 s on GPU, ~10 s CPU) | |
| r = vlm.ask("photo.jpg", "How many people are in the image?") | |
| print(r["answer"], r["ms"], "ms", r["stats"]) | |
| # {'answer': 'two', 'tokens': 1, 'ms': 45, 'emb_ms': 30, 'source': 'image', 'stats': {'eos_rows': 1}} | |
| ``` | |
| `ask()` accepts a path **or a `PIL.Image`** (handy for HTTP uploads). `blind=True` keeps the template | |
| but zeroes the 196 image positions — the honest "no image" control. `no_image=True` removes the image | |
| positions entirely (out of distribution; only good for demonstrating that the language model itself | |
| knows nothing). | |
| `dict` you get back: `answer`, `tokens`, `ms` (generation only), `emb_ms` (CLIP), `source` | |
| (`image` / `blind(zeros)` / `no-image` / `text`), `stats` (`eos_rows` self-stopped, `loop_rows` | |
| loop cut, `deg_rows` degeneracy cut). | |
| ## 5. Decoding rules (not optional) | |
| Sequence template, fixed: | |
| ``` | |
| <|bos|> [IMAGE]×196 <|user|> question <|eot|> <|assistant|> answer <|eos|> | |
| ``` | |
| - **`no_repeat_ngram=3` is required.** Without it this 39M model repeats itself until `max_new` | |
| with certainty (measured: greedy never makes `<|eos|>` the argmax past the first sentence). | |
| - `max_new`: **12** for Q&A, **30–40** for captions. Do not ask it for paragraphs. | |
| - `code/decoding.py` adds a repetition penalty, a **loop cut** (stops and trims when the tail becomes | |
| periodic, period ≤ 12), and a **degeneracy guard** (stops when the local 2-gram distinct ratio | |
| collapses — this catches the "reworded loop" that an n-gram ban alone lets through: | |
| `a very small, small, and very small area. The ocean is a small, large area ...`). | |
| - `temperature > 0` + `top_k` for caption variety; keep the n-gram ban on. | |
| ## 6. Interactive page | |
| ```bash | |
| python code/webapp.py # http://localhost:8000 (add --port 8080, --no-warmup) | |
| ``` | |
| Plain HTML, no CSS, English UI, nothing stored. Pick an image (file dialog, or `Ctrl+V` to paste a | |
| screenshot), or click one of the bundled samples; type an English question or click a preset button; | |
| optionally tick "blind" to see both answers side by side. The answer block reports token count, | |
| latency, whether the loop cut fired, and which source was used. Requires `fastapi` + `uvicorn`. | |
| ## 7. Verify the package | |
| ```bash | |
| python code/verify_package.py # full: checksums, params, structure, inference, determinism, mini-protocol | |
| python code/verify_package.py --quick # skips the CLIP download | |
| ``` | |
| Objective checks decide PASS/FAIL; model accuracy is printed only (a 39M model legitimately misses | |
| two out of three questions). `protocol/mini_vqa_100.jsonl` + `protocol/images/` let you recompute an | |
| accuracy figure on 100 items without the 18 GB feature cache used for the full run. | |
| ## 8. Troubleshooting | |
| | symptom | cause / fix | | |
| |---|---| | |
| | `ModuleNotFoundError: No module named 'torch'` | you ran a different interpreter. Use `python -m venv` from step 1, or the absolute path of that env's python. | | |
| | `ModuleNotFoundError: No module named 'litgpt'` / attribute errors inside litgpt | version drift: pin `litgpt==0.5.13` exactly (other versions change GPT internals). | | |
| | first run hangs on "loading CLIP" | it is downloading ~600 MB. Set `DUOVLM_CLIP_DIR=/path/to/clip-vit-base-patch16` for offline. | | |
| | `OSError: Can't load ... clip` / 401 | no network and no `DUOVLM_CLIP_DIR`. | | |
| | answers differ by a word from `README.md` | expected: bf16 kernels differ across GPUs/CUDA, and a different CLIP `revision` shifts features in the last bits. Greedy decoding is deterministic per machine, not across machines. | | |
| | it answers nonsense to a Chinese question | the model is English-only; this is documented behavior, not a bug. | | |
| | long outputs turn into loops | you bypassed `decoding.py` (`ngram=0`, `loop_break=False`). Use the defaults. | | |
| | CPU run is slow / dtype complaints | set `DUOVLM_DEVICE=cpu`; the wrapper then uses fp32. | | |