Vision & Audio Fine-Tuning
Vision / Multimodal
Fine-tune vision-language models (LLaMA-3.2-Vision, Qwen2-VL, Pixtral):
pip install "soup-cli[vision]"
soup init --template visionbase: meta-llama/Llama-3.2-11B-Vision-Instruct
task: sft
modality: vision
data:
train: ./data/vision_train.jsonl
format: llava
image_dir: ./data/images
val_split: 0.1
training:
epochs: 3
lr: 1e-5
quantization: 4bit
lora:
r: 64
alpha: 16Vision Data Formats
LLaVA:
{"image": "photo.jpg", "conversations": [{"from": "human", "value": "<image>\nDescribe this image."}, {"from": "gpt", "value": "A cat on a mat."}]}ShareGPT4V:
{"image": "chart.png", "conversations": [{"from": "human", "value": "<image>\nWhat does this show?"}, {"from": "gpt", "value": "Quarterly revenue."}]}A relative image path resolves against data.image_dir, or against the directory of the data file when image_dir is unset. A Hub dataset or a remote URI has no data file directory, so set data.image_dir when its rows hold image paths: since v0.76.0 the load stops with a message naming the setting if you set neither (data.image_dir: . resolves them under the working directory), and a row whose file lies outside the directory is skipped with the usual "rows skipped" warning. Before that, rows from a streaming data.interleave, a remote URI or a Hub dataset kept the path as written, so a relative path was opened against the working directory and image_dir had no effect. An image a Hub dataset has already decoded is passed on as before, and Parquet image structs ({"bytes": ..., "path": ...}) are accepted, their path following the same rules. The same holds for audio paths and data.audio_dir.
soup data inspect automatically shows image statistics (count, formats, missing files) for vision datasets, resolving paths the same way (or with --image-dir). The full path rule, including the remote URI, Hub dataset and replay-file cases, is on Data formats.
Vision and audio SFT supervise every sequence token: the multimodal collators do no assistant-only masking, so data.train_on_responses_only and data.mask_history have no effect there, and soup doctor --config reports both as ignored on modality: vision or audio (v0.76.0, #1156). training.moe_lora, rope_scaling_type, freeze_layers and freeze_ratio are refused at config load on vision and audio SFT.
Audio / Speech
Fine-tune audio-language models (Qwen2-Audio, Whisper):
pip install "soup-cli[audio]"
soup init --template audiobase: Qwen/Qwen2-Audio-7B-Instruct
task: sft
modality: audio
data:
train: ./data/audio_train.jsonl
format: audio
audio_dir: ./data/audio
val_split: 0.1
training:
epochs: 3
lr: 1e-5
quantization: 4bit
lora:
r: 64
alpha: 16Audio Data Format
{"audio": "recording.wav", "messages": [{"role": "user", "content": "Transcribe this audio."}, {"role": "assistant", "content": "Hello world."}]}A relative audio path resolves against data.audio_dir (or the data file's directory) by the same rule as image paths: see Data formats.
Want to fine-tune Whisper itself (speech-to-text, not audio understanding)? That is a dedicated task since v0.71.32: see ASR fine-tuning (
task: asr,format: asr, built-in WER/CER, whisper-tiny/base train on a 4 GB GPU).
Continued Pre-training
Continue training on raw text for domain adaptation:
base: meta-llama/Llama-3.1-8B
task: pretrain
data:
train: ./data/corpus.jsonl # {"text": "..."} or plain .txt files
format: plaintext
max_length: 4096
training:
epochs: 1
lr: 1e-5
quantization: 4bitEmbedding Fine-Tuning
Train sentence embedding models with contrastive learning:
base: sentence-transformers/all-MiniLM-L6-v2
task: embedding
data:
train: ./data/embeddings.jsonl
format: embedding
training:
embedding_loss: contrastive # contrastive, triplet, cosine
embedding_pooling: mean
embedding_margin: 0.5
embedding_temperature: 0.05Embedding Data Format
{"anchor": "What is Python?", "positive": "Python is a programming language."}
{"anchor": "What is Python?", "positive": "A programming language.", "negative": "A type of snake."}embedding_loss: contrastive (the default) needs other rows in the batch as negatives, so since v0.76.0 batch_size: 1 is refused at config load (#1234): with one row the loss and gradient are exactly 0 and the adapter never moves. batch_size: auto resolves to at least 2, the last partial batch of an epoch is dropped, and a batch larger than the training set is lowered to its size. Use triplet (with negative rows) or cosine for a batch of 1.
Soup is free and Apache-2.0. If it saved you a training run, starring the repo costs nothing and helps most. You can also fund the GPU time behind the work a 4 GB laptop cannot reach.