Skip to content
Level 2 · Local OperatorLessonPart 10 · page 5 of 830 minSXMN
30Minutes
1Tools
9Sources
Tools used on this page1

Vision, Speech and Documents: Multimodal Locally

Most of the material you would actually like a model to work with is not plain text. It is a photograph of a whiteboard, an hour of a meeting, a scanned invoice, a screenshot of an error. By the end of this lesson you will be able to send each of those to a model on your own machine, say what each one costs in context and memory before you send it, and state which engine supports which modality on your track rather than finding out by failing.

There is one mechanism under all of this, and knowing it removes most of the surprises.

A multimodal model is a language model with an extra encoder bolted on the front. The encoder turns an image, or a slice of audio, into a sequence of vectors, a projector maps those vectors into the same space the language model’s token embeddings live in, and from that point on they are just tokens in the context. The model does not have a special image input; it has a longer prompt.

What happens to a picture on the way into the model

  1. Your image or audioA file, a base64 data URL, or a path on the machine running the server.you supply this
  2. EncoderA vision or audio tower. Separate weights from the language model, often a separate file.
  3. ProjectorMaps the encoder output into the language model's embedding space. In llama.cpp this is the mmproj file.
  4. ContextThe result occupies context positions exactly like text tokens, and costs key-value cache exactly like text tokens.this is the bill
  5. Language modelPrefill and decode as usual. Nothing downstream knows the tokens came from a picture.
The single most useful consequence: everything you learned about context length and key-value cache in Parts 4 and 6 applies unchanged to images and audio.

Three practical facts fall out of that diagram.

An image has a token cost, and it is not small. A page-sized image at a resolution high enough to read the small print is worth a substantial fraction of a short document. Several images in one conversation add up quickly, and the failure looks like a context overflow, not like an image problem.

The encoder is extra weights. In llama.cpp it is a separate GGUF, the multimodal projector, that you load alongside the model. It has to fit in memory too.

Resolution is a dial you control. Sending a smaller image costs fewer tokens and reads less small print. For “what is on this whiteboard” a small image is fine; for “read this invoice number” it is not, and choosing per task rather than globally is the difference between a usable pipeline and an expensive one.

Qwen3-VL-8B at Q4_K_M with an 8k context, on a machine with 8 GB available to the model

Weights (Q4_K_M)
5.4 GB
KV cache, 8k context
1.2 GB
Free
1.4 GB
Total
8 GB
Weights from the course model table; key-value cache from the per-token figure in the same table multiplied by the context length. The multimodal projector and runtime overhead are not shown because their sizes vary by build, which is exactly why the 8 GB tier is tight here rather than comfortable.

llama.cpp documents multimodal input through a library it calls libmtmd, stating that “llama.cpp supports multimodal input via libmtmd” and naming the tools that use it: llama-cli, llama-server through the OpenAI-compatible chat completions API, and llama-mtmd-cli. The model and its projector can be loaded together: with -hf the projector is downloaded for you, and --mmproj points at a local projector file when you have your own. --no-mmproj-offload keeps the projector on the processor when video memory is scarce, which is a useful lever on an 8 GB card.

RunnableAll tracks

a vision model and its projector, from the hub
llama-server \
--hf-repo ggml-org/Qwen2.5-VL-7B-Instruct-GGUF:Q4_K_M \
--alias vision \
--ctx-size 8192 \
--host 127.0.0.1 \
--port 8082

RunnableAll tracks

one image, one question, from the command line
llama-mtmd-cli \
--model ~/models/vision/model-Q4_K_M.gguf \
--mmproj ~/models/vision/mmproj-f16.gguf \
--image ~/pictures/whiteboard.jpg \
--prompt "Transcribe the text on this whiteboard. If a word is unreadable, write [unclear]."

Qwen3-VL-8B-Instruct is this course’s reference vision model: Apache-2.0, a native context of 256K tokens which its card says is expandable to 1M, optical character recognition that the card describes as covering 32 languages, document structure parsing, video understanding, and operating graphical interfaces well enough to be used as a screen agent, which is where Part 26 picks it up.

vLLM takes multimodal data in two shapes. Offline, a multi_modal_data dictionary alongside the prompt, keyed "image", "audio" or "video". Online, through the OpenAI-compatible chat API, where an image is a content item of type image_url whose URL may be a base64 data URL, audio arrives as input_audio or audio_url, and video as video_url. --limit-mm-per-prompt caps how many items of each modality a request may carry, which is a control worth setting before anybody else can reach the endpoint.

MLX has mlx-vlm, described as “a package for inference and fine-tuning of Vision Language Models (VLMs) and Omni Models (VLMs with audio and video support) on your Mac using MLX”, MIT-licensed, installed with pip install -U mlx-vlm. It provides a generation command, a server and a chat interface, and lists Qwen among more than thirty supported architectures.

Two families matter for local work, and the course pins one of them.

Parakeet is the course’s reference. parakeet-tdt-0.6b-v3 is a 0.6B model that its card describes as a multilingual automatic speech recognition model for high-throughput transcription, with a FastConformer encoder and a TDT decoder, automatic punctuation and capitalisation, and word-level and segment-level timestamps. It covers 25 European languages and detects the language itself without prompting. It expects 16 kHz mono audio in .wav or .flac. The card reports handling audio up to 24 minutes with full attention on an A100 80GB, or up to three hours with local attention; treat those as the publisher’s figures for their hardware, not yours.

It runs through NVIDIA’s NeMo toolkit, whose ASR documentation shows the shape of the call:

Fragment — not complete on its own

import nemo.collections.asr as nemo_asr
asr_model = nemo_asr.models.ASRModel.from_pretrained("nvidia/parakeet-tdt-0.6b-v3")
transcript = asr_model.transcribe(["meeting.wav"])[0].text

Whisper-class models are the other family, and they are not in this course’s model table because the course pins Parakeet. They are worth naming because you will meet them everywhere: whisper.cpp describes itself as “high-performance inference of OpenAI’s Whisper automatic speech recognition (ASR) model”, is MIT-licensed, runs on everything from a Raspberry Pi to WebAssembly, and supports integer quantisation of the Whisper weights so a transcription model fits in very little memory. On a machine with no NVIDIA hardware it is often the path of least resistance, and its GGML model files work the same way GGUF files do in Part 6. Check the Whisper weights’ own licence on their model card before shipping anything.

A PDF is either text with a layout or a picture of text, and the two need entirely different treatment. The mistake is to reach for the vision model first.

Born-digital PDFs carry their text. Extracting it is exact, instant and free of model error. PyMuPDF describes itself as “a high-performance Python library for data extraction, analysis, conversion & manipulation of PDF (and other) documents”, handling PDF, XPS, EPUB, MOBI, FB2, CBZ, SVG, TXT, MD and image formats, with page rendering, text extraction, table extraction, image extraction and OCR integration. Its companion project converts PDFs to Markdown, which is a convenient input for the chunking strategy in the previous lesson because it preserves headings.

Scans need pixels. Render each page to an image and send it to a vision model, or to a dedicated optical character recognition engine. This is one to two orders of magnitude more expensive per page than reading a text layer, so the sensible pipeline decides per page.

The rule that keeps this cheap:

  1. Extract the text layer for every page.
  2. Count pages that produced little or no text.
  3. Send only those pages through the vision path.
  4. Record which pages came from which path, because a reader of the answer deserves to know whether their citation came from a text layer or from a model reading a photograph.

That last point is not bureaucracy. Optical character recognition errors are not random noise; they are plausible substitutions, and a transposed digit in an invoice total is exactly the kind of error that survives every downstream check.

Track S — NVIDIA DGX Spark

Vision and speech both work. llama.cpp with a projector is the simplest path and runs the same way as on any Linux machine. vLLM’s multimodal support is available here, and Part 9 covers getting vLLM running on this platform. NeMo follows the NVIDIA container path that Part 8 uses for aarch64, which is the smoothest route to Parakeet on this track. 128 GB of unified memory means the resolution and context limits above are advisory rather than binding.

Track X — AMD Ryzen AI Max+ 395Partial

llama.cpp with Vulkan or ROCm covers vision and whisper.cpp covers speech; NeMo and vLLM are the parts to check for your driver stack before relying on them.

llama.cpp’s multimodal path works through the same backend you already built in Part 6, so a vision model is a projector file away. For speech, whisper.cpp is the low-friction option on this track. Parakeet through NeMo depends on your ROCm PyTorch stack, which Part 8 covers with its status dated; treat it as something to verify rather than assume.

Track M — Apple silicon

Two good paths. llama.cpp with Metal handles vision models with a projector, and mlx-vlm is the MLX-native option with a command-line generator, a server and a chat interface. For speech, whisper.cpp has first-class Metal support and is the usual choice here. Unified memory means the image resolution you choose competes directly with everything else open on the machine.

Track N — NVIDIA desktop or laptop

Everything in this lesson runs, and video memory is the binding constraint rather than system memory. On an 8 GB card, keeping the projector off the graphics processor is the lever that makes a vision model fit; on 24 GB and above, the constraint moves to how many images you put in one context. NeMo and vLLM are both at home on this track.

Preserve evidence through modality conversion

Section titled “Preserve evidence through modality conversion”

A scanned document passes through several transformations before a final answer: page rendering, OCR or vision encoding, extraction and possibly retrieval. Keep page numbers and source identifiers through the pipeline. Otherwise a plausible answer cannot be traced back to the region or timestamp that supports it.

Construct a small evaluation set with a rotated page, a multi-column layout, a table, an illegible field and a clean control. For audio, include silence, overlapping speakers and a proper noun whose spelling matters. Score extraction separately from the final task. A model cannot reliably reason from a total whose digits were transcribed incorrectly.

Define what happens when evidence is unclear: return an uncertainty marker or request review rather than converting unreadable text into a confident value. Record preprocessing resolution, audio segmentation and model identity alongside the answer. These are part of the experiment, since changing them changes both information available to the model and resource use. Text-only token counts do not fully describe the multimodal request’s memory and compute requirements.

Multimodal input is an encoder and a projector that turn pixels or audio into tokens in the ordinary context, which is why images cost context length, key-value cache and memory in exactly the ways Parts 4 and 6 taught. llama.cpp reaches vision models through libmtmd and a separate projector file, with llama-server exposing them on the OpenAI-compatible chat API; vLLM takes multimodal content through multi_modal_data offline and typed content items online, with a per-prompt limit worth setting; mlx-vlm is the MLX path on a Mac. Qwen3-VL-8B is the course’s reference vision model under Apache-2.0. Parakeet is the pinned speech model, CC-BY-4.0 rather than Apache-2.0, run through NeMo; whisper.cpp is the portable alternative and the easier one on tracks where the PyTorch stack is awkward. For documents, extract the text layer first, count the pages that failed, and send only those through the expensive path, recording which page came from which so that a citation means something.

Check your understanding

Question 1. You send four high-resolution page images in one conversation and the server reports a context overflow. What is the underlying cause?
Show the answer and why

Answer: Images are turned into tokens that occupy ordinary context positions, so four large images can exceed the context length you allocated

Nothing downstream of the projector knows the tokens came from pictures. The context budget, the key-value cache arithmetic and the overflow behaviour are all the same as for text, which is why the memory lessons in Parts 4 and 6 transfer directly.

Question 2. A colleague sends you a 200-page PDF to make searchable. What should you do first?
Show the answer and why

Answer: Extract the text layer, count how many pages produced little or no text, and send only those through the vision path

Born-digital pages carry exact text at no model cost. Reserving the expensive, error-prone path for the pages that actually need it is usually one or two orders of magnitude cheaper, and it lets you record which pages were read by a model rather than extracted.

Question 3. Why does the course record that Parakeet is CC-BY-4.0 rather than treating all its models as interchangeable?
Show the answer and why

Answer: Because it is a permissive licence with an attribution condition, which is a real obligation once transcription goes into something you ship

Attribution is a condition, not a formality, and it differs from the Apache-2.0 terms most of the course's models carry. The model reference records the licence for every model precisely so that this is checkable rather than assumed.

Question 4. On an 8 GB NVIDIA card, which lever most directly helps a vision model fit?
Show the answer and why

Answer: Keeping the multimodal projector off the graphics processor, and lowering the context length

The projector is extra weights and llama.cpp documents an option to leave it unoffloaded, and the key-value cache scales with context length. Higher resolution moves the wrong way: it produces more visual tokens, which costs both context and cache.

Sources for this lesson

9 verified · checked 2026-09-08

  1. 01llama.cpp — Multimodal support§ libmtmd; --mmproj and -hf; supported model familiesgithub.com/ggml-org/llama.cpp/blob/master/docs/multimodal.md2026-09-08
  2. 02Qwen3-VL-8B-Instruct model card§ Capabilities; context length; deploymenthuggingface.co/Qwen/Qwen3-VL-8B-Instruct2026-09-08
  3. 03vLLM — Multimodal Inputs§ Offline inference; online serving; --limit-mm-per-promptdocs.vllm.ai/en/latest/features/multimodal_inputs.html2026-09-08
  4. 04mlx-vlm§ README; installation; CLI and servergithub.com/Blaizzy/mlx-vlm2026-09-08
  5. 05Parakeet TDT 0.6B v3 model card§ Model overview; licence; input format; long-form audiohuggingface.co/nvidia/parakeet-tdt-0.6b-v32026-09-08
  6. 06NVIDIA NeMo — Automatic Speech Recognition§ Transcribing with a pretrained modeldocs.nvidia.com/nemo-framework/user-guide/latest/nemotoolkit/asr/intro.html2026-09-08
  7. 07whisper.cpp§ README; licence; supported platformsgithub.com/ggml-org/whisper.cpp2026-09-08
  8. 08ggml-org/Qwen2.5-VL-7B-Instruct-GGUF on Hugging Face§ Quantised files; usage with the llama.cpp serverhuggingface.co/ggml-org/Qwen2.5-VL-7B-Instruct-GGUF2026-09-08
  9. 09PyMuPDF — About§ Licensing; capabilitiespymupdf.readthedocs.io/en/latest/about.html2026-09-08

Every technical claim on this page was checked against the official documentation of the tool, vendor or model publisher on the date shown, at the version pinned for the course. Where the course disagrees with folklore, the source is how you can tell which one to trust.