Fine-Tuning Qwen3-Embedding with SWIFT and Docker: A Complete, Practical Guide
A practical end-to-end guide to fine-tuning Qwen3-Embedding using ModelScope SWIFT
Fine-Tuning Qwen3-Embedding with SWIFT and Docker: A Complete, Practical Guide

Image by author — Generated with ChatGPT
This story was written with the assistance of an AI writing program.
Large-scale retrieval systems often require more than generic, off-the-shelf embedding models. When accuracy, domain specificity, or search precision becomes critical, the solution is often straightforward: fine-tune your embedding model on your own data.
The Qwen3-Embedding family supports further training, and the **ModelScope SWIFT** framework makes this process both scalable and production-ready.
This guide provides a full end-to-end pipeline — from Docker environment setup, data preparation, InfoNCE configuration, LoRA vs Full FT options, multi-GPU training, and practical troubleshooting for OOM issues — all tailored for Qwen3-Embedding.
1. Why SWIFT for Qwen3-Embedding
ModelScope SWIFT is a large-model training and deployment framework built for real-world tasks:
Key Features
- Model Variety: 500+ text models, 200+ multimodal models
- Hardware Support: CPU, RTX GPUs, T4/V100/A10/A100/H100, Ascend, MPS
- Training Methods: Full fine-tuning, LoRA, QLoRA, DoRA, etc.
- Distributed Training: DDP, FSDP, DeepSpeed ZeRO-2/3, Megatron tensor/pipeline/sequence parallelism
- RLHF Training: DPO, PPO, GRPO, KTO, DAPO, reward models, etc.
For embedding models like Qwen3-Embedding, SWIFT is ideal because it supports InfoNCE loss, multi-GPU scaling, and parameter-efficient tuning.
Why SWIFT for embedding models?
For embedding models like Qwen3-Embedding, SWIFT is an ideal choice because it:
- provides native support for InfoNCE loss, the same loss used in the original Qwen3-Embedding paper,
- scales seamlessly across multiple GPUs,
- supports both LoRA and full-parameter fine-tuning,
- and integrats cleanly with PyTorch + DeepSpeed.
Most importantly, 👉 this training pipeline follows the official guidance from the Qwen3-Embedding GitHub repository, specifically:
This ensures the setup aligns with how Qwen developers themselves recommend training the model.
2. Environment Setup
Before starting any fine-tuning work, we assume that you are setting up a dedicated development environment — either on a local workstation with GPUs, a remote server, or inside a Docker container. The following Python packages form the core runtime environment for training Qwen3-Embedding using SWIFT.
pip install ms-swift -U
# Or install from source:
pip install git+https://github.com/modelscope/ms-swift.git
pip install transformers -U
pip install deepspeed
pip install liger-kernel
pip install flash-attn --no-build-isolation
3. Docker Image for SWIFT + Qwen3-Embedding
Training large embedding models — especially with LoRA, DeepSpeed, FlashAttention, or multi-GPU configurations — requires a clean, reproducible, and GPU-optimized runtime environment. To avoid dependency conflicts or version mismatches, it is best practice to encapsulate the entire stack inside a Docker image.
The following Dockerfile builds a fully functional environment for SWIFT-based fine-tuning of Qwen3-Embedding, including CUDA, DeepSpeed, MPI, FlashAttention, and SWIFT itself.
docker/Dockerfile
FROM pytorch/pytorch:2.7.1-cuda12.8-cudnn9-devel
SHELL ["/bin/bash", "-c"]
RUN apt-get update && \
apt-get install -y --no-install-recommends git build-essential && \
rm -rf /var/lib/apt/lists/*
RUN conda update -n base -c defaults conda -y && \
conda install -y -c conda-forge openmpi mpi4py && \
pip install --no-cache-dir -U deepspeed
WORKDIR /opt
RUN git clone https://github.com/modelscope/ms-swift.git
WORKDIR /opt/ms-swift
RUN pip install --no-cache-dir -e '.[all]'
RUN pip install --no-cache-dir ninja packaging wheel && \
pip install --no-cache-dir --no-build-isolation flash-attn && \
pip install --no-cache-dir msgspec
WORKDIR /workspace
4. Project Layout
A clean directory structure helps keep the training pipeline reproducible and easy to maintain. Each top-level folder has a clear responsibility:
project/
├── docker/
│ ├── Dockerfile
│ └── docker-compose.yml
├── scripts/
│ ├── train.sh
│ └── config/
│ └── swift.yaml
├── data/ (mounted)
├── models/ (mounted)
└── output/ (mounted)
docker/
Contains everything related to the runtime environment.
- Dockerfile → builds the GPU-ready SWIFT training image
- docker-compose.yml → mounts folders, sets GPU IDs, and launches training
scripts/
Holds the training logic and configuration.
- train.sh → entrypoint script (detects GPUs, launches SWIFT)
- config/swift.yaml → SWIFT training configuration (model, loss, batch size, etc.)
data/
Mounted read-only at runtime.
Contains the training dataset (e.g., train.jsonl).
models/
Mounted read-only. Contains the base Qwen3-Embedding model you want to fine-tune.
output/
Mounted read-write. Stores all checkpoints, LoRA adapters, logs, and evaluation plots generated during training.
5. docker-compose: GPU-Aware
docker/docker-compose.yml
version: "3.8"
services:
swift-train:
image: ${IMAGE}
container_name: swift-train
ipc: host
shm_size: "32g"
restart: "no"
deploy:
resources:
reservations:
devices:
- driver: nvidia
device_ids: ["${GPU_IDS}"]
capabilities: [gpu]
volumes:
- ${DATA_DIR}:/workspace/data:ro
- ${MODEL_DIR}:/workspace/models:ro
- ${OUTPUT_DIR}:/workspace/output
- ${SCRIPTS_DIR}:/workspace/scripts
working_dir: /workspace
entrypoint: ["/bin/bash", "-lc", "/workspace/scripts/train.sh"]
6. .env Configuration
.env
IMAGE=pytorch-swift-custom:latest
GPU_IDS=0,1
DATA_DIR=/path/to/dataset
MODEL_DIR=/path/to/model
OUTPUT_DIR=/path/to/output
SCRIPTS_DIR=./scripts
7. SWIFT YAML Config (LoRA Fine-Tuning)
scripts/config/swift.yaml
model: /workspace/models
task_type: embedding
model_type: qwen3_emb
train_type: lora
attn_impl: flash_attn
dataset: /workspace/data/train.jsonl
split_dataset_ratio: 0.3
output_dir: /workspace/output
eval_strategy: steps
eval_steps: 200
num_train_epochs: 4
save_steps: 200
per_device_train_batch_size: 8
per_device_eval_batch_size: 8
gradient_accumulation_steps: 1
learning_rate: 1e-4
loss_type: infonce
dataloader_drop_last: true
max_length: 1024
torch_dtype: float16
deepspeed: zero3
save_only_model: true
save_total_limit: 1
To switch from LoRA to Full Fine-Tuning:
train_type: full
⚠️ Full FT significantly increases GPU memory usage.
8. Training Script with GPU Auto-Detection
scripts/train.sh
#!/usr/bin/env bash
set -euo pipefail
echo "[swift] Training job started."
CONFIG_FILE="/workspace/scripts/config/swift.yaml"
if [[ ! -f "$CONFIG_FILE" ]]; then
echo "[error] Config file not found: $CONFIG_FILE"
exit 1
fi
if [[ -z "${GPU_IDS:-}" ]]; then
echo "[warning] GPU_IDS not set. Defaulting NPROC_PER_NODE=1."
nproc_per_node=1
else
gpu_ids_clean=$(echo "$GPU_IDS" | tr -d ' ')
IFS=',' read -ra gpu_array <<< "$gpu_ids_clean"
nproc_per_node=${#gpu_array[@]}
fi
echo "[swift] Using GPU IDs: $GPU_IDS"
echo "[swift] Detected GPU count: $nproc_per_node"
export NPROC_PER_NODE=$nproc_per_node
NPROC_PER_NODE=$nproc_per_node swift sft --config "$CONFIG_FILE"
echo "[swift] Training completed."
💡 Important:
Distributed training requires the correct NPROC_PER_NODE.
9. Data Preparation for InfoNCE
Qwen3-Embedding uses a conversational message format for training samples. Each example contains an anchor (messages), one positive (positive_messages), and optionally hard negatives (negative_messages).
Positive-only sample
{"messages":[{"role":"user","content":"sentence1"}], "positive_messages":[[{"role":"user","content":"sentence2"}]]}
With multiple hard negatives
{"messages":[{"role":"user","content":"sentence1"}], "positive_messages":[[{"role":"user","content":"sentence2"}]], "negative_messages":[[{"role":"user","content":"sentence3"}],[{"role":"user","content":"sentence4"}]]}
Constraints:
messages→ anchor (single list)positive_messages→ list-of-list, outer length must be 1negative_messagesoptional → used as hard negatives
If omitted, in-batch negatives are used.
Ref: https://swift.readthedocs.io/en/latest/BestPractices/Embedding.html
✅ Only one dataset file is needed
For training, you only need a single JSONL file, typically:
train.jsonl
Every sample (anchor, positive, negative) is stored line-by-line inside this file.
✅ Validation dataset is created automatically
You do not need to prepare a separate validation file.
SWIFT automatically handles validation when you set:
split_dataset_ratio: 0.3
For example:
split_dataset_ratio: 0.3→ 70% training, 30% validation- SWIFT internally generates the validation dataset from
train.jsonl - Outputs like
val_dataset.jsonlare automatically saved in the checkpoint folder
10. Adding Custom Instructions
Default embedding prompt:
{Query}<|endoftext|>
With system instruction:
{Instruction} {Query}<|endoftext|>
Example:
{"messages":[{"role":"system","content":"Given a web search query, retrieve relevant passages that answer the query"},{"role":"user","content":"Who are you?"}],"positive_messages": [[{"role": "user", "content": "sentence2"}]], "negative_messages": [[{"role": "user", "content": "sentence3"}], [{"role": "user", "content": "sentence4"}]]}
System content is automatically prepended to the first user message.
11. InfoNCE Loss Settings
Qwen3-Embedding is trained with InfoNCE, so continuing with that loss is recommended.
Environment variables:
✔ INFONCE_TEMPERATURE
Default: 0.01
✔ INFONCE_USE_BATCH
Use in-batch negatives (default: True)
✔ INFONCE_HARD_NEGATIVES
Limit number of hard negatives per sample
- If unset → use all
- If set → truncate or pad to fixed count
✔ INFONCE_MASK_FAKE_NEGATIVE
Avoid “false negatives” that behave like positives.
12. Output Artifacts for LoRA vs Full Fine-Tuning
LoRA Output Structure
After training, the LoRA fine-tuning outputs include:
output/
└── checkpoint-XXXX/
├── adapter_config.json
├── adapter_model.safetensors
├── trainer_state.json
├── logging.jsonl
├── val_dataset.jsonl
└── images/
├── eval_loss.png
├── train_loss.png
└── ...
These files represent:
adapter_model.safetensors→ LoRA weight matricesadapter_config.json→ LoRA hyperparameters (rank, alpha, modules)trainer_state.json→ global training metadatalogging.jsonl→ step-by-step metrics- PNG files → visualized training/evaluation curves
Full Fine-Tuning Output
If you switch:
train_type: full
SWIFT produces full model weights, not LoRA adapters. However:
- GPU memory usage increases sharply (8B model → large VRAM requirement)
- Checkpoints include full updated model parameters
Official CLI parameter documentation: https://swift.readthedocs.io/en/latest/Instruction/Command-line-parameters.html
13. Running the Training
docker compose --env-file ../.env up
14. Practical OOM Troubleshooting
1. Reduce effective batch size
- Lower
per_device_train_batch_size - Increase
gradient_accumulation_stepsif needed
2. Ensure sequence lengths are controlled
- Sometimes
max_lengthdoes not truncate inputs - Manually pre-truncate long texts
3. Limit number of hard negatives
- More hard negatives = larger effective batch
- Control via
INFONCE_HARD_NEGATIVES
4. Verify GPU visibility
GPU_IDSmust match actual GPUsNPROC_PER_NODEmust match the GPU count
15. Final Thoughts
Fine-tuning Qwen3-Embedding is one of the most impactful ways to upgrade a retrieval system — especially when working with specialized domains like finance, education, legal documents, or enterprise knowledge systems.
This guide provided:
- A fully production-ready Docker environment
- GPU-aware distributed training with SWIFT
- Data preparation strategies for InfoNCE
- Clear distinction between LoRA and Full Fine-Tuning
- Practical OOM guidance from real-world experience
With this setup, you can confidently iterate on model versions, experiment with hard negative strategies, customize embedding behaviors, and ultimately achieve significantly better retrieval results.
If you have questions, feel free to reach out or leave a comment. Don’t forget to hit the like button and subscribe for more content! 😊
메타데이터
- post_id
- e4107c2781c9
- slug
- fine-tuning-qwen3-embedding-with-swift-and-docker-a-complete-practical-guide-e4107c2781c9
- url
- https://medium.com/@kimdoil1211/fine-tuning-qwen3-embedding-with-swift-and-docker-a-complete-practical-guide-e4107c2781c9
- canonical_url
- https://medium.com/@kimdoil1211/fine-tuning-qwen3-embedding-with-swift-and-docker-a-complete-practical-guide-e4107c2781c9
- author_url
- https://medium.com/@kimdoil1211
- status
- ok
- fetched_at
- 2026-07-13 06:23:13