Praktische Hinweise: Vom Feintunen bis zur Präzision – eine erhebliche Reduzierung
Schritt-für-Schritt-Anleitung zu den Praktischen Hinweisen: Vom Feintunen bis zur Präzision – signifikante Reduzierung der Verträge, Überprüfungen sowie Code-Blöcke für Teams, die dieses Muster einsetzen.
Dieser Leitfaden zeigt den Weg von Rohstoffen bis zu einem funktionsfähigen System für: Vom Feintunen zur Präzision – signifikante Reduzierung von Halluzinationen in Ihrem RAG-Pipeline. Der Schwerpunkt liegt auf umsetzbaren Schritten, expliziten Überprüfungen sowie Code, den Sie ohne Rätseln über die Absicht direkt in ein Repository einfügen können.
1. Einführung
In der Einführungsphase sollten Sie die Eingaben, den Verantwortlichen für den Schritt sowie die Abbruchkriterien definieren, bevor Sie Code ändern. Die Betreiber sollten in der Lage sein, den Schritt von einem bekannten Checkpoint aus erneut auszuführen, ohne auf versteckte Zustände schließen zu müssen. Betrachten Sie diese Phase als Vertrag zwischen den Eingaben und den validierten Ausgaben. Benennen Sie die Erzeugnisse, definieren Sie Erfolgskontrollen und lehnen Sie stille, unvollständige Abschlüsse ab. Zitieren Sie die Passagen, die tatsächlich die Antwort begründen. Ohne Zitate können die Betreiber keine Halluzinationen von Lücken in der Indizierung unterscheiden.
You: What was the revenue from contracts with customers in 2024?
Assistant: The revenue was €179,058,000. ← Wrong! Correct answer is €159,088,000.
2. Warum Feintunen?
Zur Phase des Feintunings mit „2 Why“ sollten vor der Codeänderung die Eingaben, der Verantwortliche für den Schritt sowie die Abbruchkriterien definiert werden. Die Operator sollten in der Lage sein, den Schritt von einem bekannten Checkpoint aus erneut auszuführen, ohne auf versteckte Zustände schließen zu müssen. Zeiten sowie Kosten für Token oder Abfragen sollten neben den funktionalen Ergebnissen aufgezeichnet werden. Eine frühzeitige Sichtbarkeit der Kosten verhindert überraschende Rechnungen, wenn der Prozess von Demonstrumgebungen in gemeinsam genutzte Umgebungen übergeht. Zitieren Sie die Passagen, die tatsächlich die Antwort untermauern. Ohne Zitate können die Operator nicht zwischen Halluzinationen und Lücken in der Indizierung unterscheiden.
Die Lösung: Feintuning mit MLX
Für die Feinabstimmung der Lösung in Phasen sollten Eingaben, der Verantwortliche für den jeweiligen Schritt sowie die Abbruchkriterien vor dem Ändern des Codes definiert werden. Die Operator sollten in der Lage sein, den Schritt von einem bekannten Checkpoint aus erneut auszuführen, ohne auf versteckte Zustände schließen zu müssen. Die Konfiguration sollte außerhalb des Anwendungscode gespeichert werden. Umgebungsdateien, Geheimdatenspeicher sowie Feature-Flags sollten an einem Ort zusammengefasst sein, den die Operator überprüfen können, ohne den gesamten Ablaufverlauf durchlesen zu müssen. Zitieren Sie die Passagen, die tatsächlich die Antwort untermauern. Ohne Zitate können die Operator nicht zwischen Halluzinationen und Lücken in der Indizierung unterscheiden.
3. Architekturdiagramm
In der Phase des 3. Architekturdiagramms sollten Eingaben, Verantwortliche für die jeweiligen Schritte sowie Abbruchkriterien definiert werden, bevor Code geändert wird. Die Operator sollten in der Lage sein, den Schritt von einem bekannten Checkpoint aus erneut auszuführen, ohne auf versteckte Zustände schließen zu müssen. Dokumentieren Sie sowohl den erfolgreichen Ablauf als auch den Notfallweg gemeinsam. Wiederholungsversuche, menschliche Überprüfungen sowie die Handhabung von Fehlern gehören zum Produkt selbst und nicht zu späteren Optimierungen. Zitieren Sie die Passagen, die tatsächlich die Antwort untermauern – ohne Zitate können die Operator nicht zwischen Halluzinationen und Lücken in der Indizierung unterscheiden.
┌─────────────────────────────────────────────────────────────────────────────┐
│ PART 3: FINE-TUNING WORKFLOW (M1) │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ 1. Dataset 2. MLX Fine-Tuning │
│ ┌──────────────────┐ ┌────────────────────────────────────────────┐ │
│ │ Chart Images │─────▶│ Base Model (Qwen2-VL-2B) │ │
│ │ Q&A Pairs │ │ + LoRA Adapters (mlx_vlm.lora) │ │
│ │ (train.jsonl) │ │ + Unified Memory Training on Apple Silicon│ │
│ └──────────────────┘ └────────────────┬───────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌──────────────────────────────────────────┐ │
│ │ Fine-Tuned LoRA Adapters │ │
│ │ (./fine_tuned_adapters/) │ │
│ └────────────────┬─────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌──────────────────────────────────────────┐ │
│ │ 3. Merge & Export to GGUF │ │
│ │ (mlx_vlm.fuse + convert_hf_to_gguf.py) │ │
│ └────────────────┬─────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌──────────────────────────────────────────┐ │
│ │ 4. Deploy with Ollama │ │
│ │ ollama create my-chart-model │ │
│ │ (text.gguf + mmproj.gguf) │ │
│ └────────────────┬─────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌──────────────────────────────────────────────────────────────────────┐ │
│ │ 5. Update rag_engine.py │ │
│ │ │ │
│ │ VISION_MODEL = "my-chart-model" # ← Change ONE line │ │
│ │ TEXT_MODEL = "llama3.2:3b" # Unchanged │ │
│ │ │ │
│ │ ✔ Existing app.py (from Part 2) automatically uses the new model! │ │
│ └──────────────────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────────────────┘
4. Vorbereitung des Datensatzes
Für die Phase „Vorbereitung des Datensatzes“ sollten vor dem Ändern des Codes die Eingaben, der Verantwortliche für den Schritt sowie die Abbruchkriterien definiert werden. Die Operator sollten in der Lage sein, den Schritt von einem bekannten Checkpoint aus erneut auszuführen, ohne auf versteckte Zustände schließen zu müssen. Es sind kleinere, testbare Einheiten vorzuziehen statt umfangreicher Skripte. Wenn ein Schritt fehlschlägt, sollte der Fehler auf eine einzige Verantwortung verweisen und nicht auf ein verworrenes Pipeline-System. Zitieren Sie die Passagen, die tatsächlich die Antwort untermauern. Ohne Zitate können die Operator nicht zwischen Halluzinationen und Lücken in der Indizierung unterscheiden. Für die Phase „Vorbereitung des Datensatzes“ sollten vor dem Ändern des Codes die Eingaben, der Verantwortliche für den Schritt sowie die Abbruchkriterien definiert werden. Die Operator sollten in der Lage sein, den Schritt von einem bekannten Checkpoint aus erneut auszuführen, ohne auf versteckte Zustände schließen zu müssen. Erhalten Sie neben den funktionalen Ergebnissen auch Aufzeichnungen über die Laufzeiten sowie die Kosten pro Token oder Abfrage. Eine frühzeitige Sichtbarkeit der Kosten verhindert überraschende Rechnungen, wenn der Ablauf von einer Demo-Umgebung in eine gemeinsame Umgebung wechselt.
Model sees: (Chart image)
Model reads: (Question)
Model learns: (Correct answer)
Quellen der Datensätze
Während der Phase „Quellen der Datensätze“ sollten Sie zunächst den Vertrag festhalten: erforderliche Eingaben, Erfolgsindikator sowie das Vorgehen bei teilweisen Fehlern. Diese Checkliste sorgt dafür, dass spätere Codeänderungen transparent bleiben. Bewahren Sie die Konfiguration außerhalb des Anwendungscode auf. Umgebungsdateien, Geheimdatenspeicher und Feature-Flags sollten an einem Ort gesammelt sein, den Betreiber ohne das Durchlesen des gesamten Systems überprüfen können. Messen Sie die Trefferquote anhand eines festgelegten Fragekatalogs, bevor Sie die Anfragen anpassen. Eine häufige Änderung der Anfragen behebt selten ein schwaches Suchsystem.
Schritt für Schritt: Datenvorbereitung
Während der schrittweisen Datenvorbereitung sollten Sie zunächst den Vertrag aufschreiben: erforderliche Eingaben, Erfolgsindikator sowie das Vorgehen bei teilweisen Fehlern. Diese Checkliste sorgt dafür, dass spätere Codeänderungen transparent bleiben. Dokumentieren Sie sowohl den erfolgreichen Ablauf als auch den Notfallplan gemeinsam. Wiederholte Versuche, menschliche Überprüfungen sowie die Handhabung von Fehlern gehören zum Produkt selbst und nicht zu späteren Optimierungen. Messen Sie die Trefferquote anhand eines festgelegten Fragekatalogs, bevor Sie die Anfragenanweisungen anpassen – eine häufige Änderung dieser Anweisungen behebt in der Regel nicht ein schwaches Suchsystem.
Step 1: Create Raw Dataset → chart_dataset/raw_train.jsonl
Step 2: Convert to MLX Format → chart_dataset/train.jsonl
Step 3: Verify Dataset → Check that train.jsonl exists
Schritt 1: Rohdatensatz erstellen
Beim Bearbeiten von Schritt 1 „Phase erstellen“ sollten Sie zunächst den Vertrag aufschreiben: erforderliche Eingaben, Erfolgszeichen sowie das Vorgehen bei teilweisen Fehlern. Diese Checkliste sorgt dafür, dass spätere Codeänderungen transparent bleiben. Ziehen Sie kleine, testbare Einheiten vor großen Skripten vor. Wenn ein Schritt fehlschlägt, sollte der Fehler auf eine einzige Verantwortung verweisen und nicht auf ein verworrenes Ablaufschema. Messen Sie die Trefferquote anhand eines festgelegten Fragekatalogs, bevor Sie die Anfragen anpassen. Eine häufige Änderung der Anfragen behebt selten ein schwaches Suchsystem. Beim Bearbeiten von Schritt 1 „Phase erstellen“ sollten Sie zunächst den Vertrag aufschreiben: erforderliche Eingaben, Erfolgszeichen sowie das Vorgehen bei teilweisen Fehlern. Diese Checkliste sorgt dafür, dass spätere Codeänderungen transparent bleiben. Notieren Sie die Laufzeiten sowie die Kosten pro Token oder Abfrage neben den funktionalen Ergebnissen. Eine frühzeitige Sichtbarkeit der Kosten verhindert überraschende Rechnungen, wenn der Ablauf von einer Demo in gemeinsame Umgebungen wechselt.
# download_chartqa.py
from datasets import load_dataset
import json
import os
os.makedirs("chart_dataset", exist_ok=True)
# Download ChartQA dataset
dataset = load_dataset("ahmed-masry/ChartQA", split="train")
# Convert to flat format with standard keys
converted = []
for item in dataset:
converted.append({
"image": item["imgname"], # Path to chart image
"question": item["query"],
"answer": item["label"]
})
# Save as raw dataset
with open("chart_dataset/raw_train.jsonl", "w") as f:
for entry in converted:
f.write(json.dumps(entry) + "\n")
print(f"✅ Converted {len(converted)} ChartQA samples to chart_dataset/raw_train.jsonl")
README.md: 100%|█████████████████████████████████████████████| 2.12k/2.12k [00:00<00:00, 3.72MB/s]
Warning: You are sending unauthenticated requests to the HF Hub. Please set a HF_TOKEN to enable higher rate limits and faster downloads.
data/train-00000-of-00003.parquet: downloading bytes: ████████████████████████| 213MB, 17.3MB/s
......
Generating test split: 100%|████████████████████████| 2500/2500 [00:00<00:00, 20517.87 examples/s]
✅ Converted 28299 ChartQA samples to raw_train.jsonl
Option B: Synthetische Daten erzeugen
Die Phase „Synthetische Daten erzeugen“ von Option B funktioniert am besten, wenn sie als messbare Struktur betrachtet wird. Erfassen Sie ein optimales Transkript, einen Fehlerfall sowie die Notizen zum Rollback, bevor Sie den Umfang erweitern. Bewahren Sie die Konfiguration außerhalb des Anwendungscode auf. Umgebungsdateien, Geheimdatenspeicher sowie Feature-Flags sollten an einem Ort gesammelt sein, den Betreiber ohne Durchsicht des gesamten Systems prüfen können. Trennen Sie die Strategie zur Aufteilung in Blöcke von der Strategie zum Abrufen. Ein Änderungs an einer sollte nicht dazu führen, dass die andere neu geschrieben werden muss, wenn sich die Qualitätsmetriken ändern.
# generate_synthetic_data.py
import json
import matplotlib.pyplot as plt
import numpy as np
import os
os.makedirs("chart_dataset/images", exist_ok=True)
dataset = []
for i in range(100):
categories = ['Q1', 'Q2', 'Q3', 'Q4']
values = np.random.randint(100, 500, 4)
plt.figure()
plt.bar(categories, values)
plt.title(f"Quarterly Revenue {i}")
plt.savefig(f"chart_dataset/images/chart_{i:03d}.png")
plt.close()
dataset.append({
"image": f"images/chart_{i:03d}.png",
"question": "Which quarter had the highest revenue?",
"answer": f"Q{np.argmax(values) + 1} with ${max(values)} million"
})
with open("chart_dataset/raw_train.jsonl", "w") as f:
for entry in dataset:
f.write(json.dumps(entry) + "\n")
print("✅ Generated 100 synthetic samples in chart_dataset/raw_train.jsonl")
Schritt 2: In das MLX-Format konvertieren
Der Schritt 2 „In eine Phase umwandeln“ funktioniert am besten, wenn er als messbare Oberfläche betrachtet wird. Erfassen Sie vor Erweiterung des Umfangs ein erfolgreiches Beispiel, einen Fehlerfall sowie die Notizen zur Rücksetzung. Dokumentieren Sie gleichzeitig den erfolgreichen Ablauf und den Wiederherstellungsprozess. Versuche, menschliche Überprüfungen sowie die Handhabung von Fehlern gehören zum Produkt selbst und nicht zu späteren Optimierungen. Trennen Sie die Strategie zur Aufteilung in Blöcke von der Strategie zum Abrufen. Ein Änderungsbedarf bei einer dieser Strategien sollte nicht dazu führen, dass die andere neu geschrieben werden muss, wenn sich die Qualitätsmetriken ändern.
# convert_to_mlx_format.py
import json
import os
def convert_raw_to_mlx_format(data_dir="chart_dataset"):
"""
Convert raw dataset to MLX format.
Input: chart_dataset/raw_train.jsonl
Output: chart_dataset/train.jsonl (MLX-compatible)
"""
raw_file = os.path.join(data_dir, "raw_train.jsonl")
out_file = os.path.join(data_dir, "train.jsonl")
if not os.path.exists(raw_file):
print(f"❌ {raw_file} not found. Run download_chartqa.py or generate_synthetic_data.py first.")
return None
dataset = []
with open(raw_file, "r") as f:
for line in f:
item = json.loads(line)
# Build full image path
img_str = item["image"]
image_path = img_str if img_str.startswith(data_dir) else os.path.join(data_dir, img_str)
dataset.append({
"images": [image_path], # MUST be a list
"messages": [
{"role": "user", "content": item["question"]},
{"role": "assistant", "content": item["answer"]}
]
})
with open(out_file, "w") as f:
for entry in dataset:
f.write(json.dumps(entry) + "\n")
print(f"✅ Converted {len(dataset)} samples to {out_file}")
return dataset
if __name__ == "__main__":
convert_raw_to_mlx_format()
python convert_to_mlx_format.py
Schritt 3: Das Datensatz überprüfen
Schritt 3: Die Überprüfung der Funktionsfähigkeit funktioniert am besten, wenn sie als messbare Oberfläche betrachtet wird. Erfassen Sie vor der Erweiterung des Umfangs ein erfolgreiches Beispiel, einen Fehlerfall sowie eine Notiz zur Rücksetzung. Ziehen Sie kleine, testbare Einheiten vor umfangreichen Skripten vor. Wenn ein Schritt fehlschlägt, sollte der Fehler auf eine einzige Verantwortung verweisen und nicht auf einen verworrenen Ablauf. Trennen Sie die Aufteilungspolitik von der Abrufpolitik. Ein Änderungs an einer sollte nicht dazu führen, dass die andere neu geschrieben werden muss, wenn sich die Qualitätsmetriken ändern.
ls -la chart_dataset/
chart_dataset/
├── raw_train.jsonl # Raw Q&A pairs (flat keys)
├── train.jsonl # MLX-formatted (overwritten by prepare_mlx_dataset)
└── images/ # Chart images
📌 Important: Before running the fine-tuning command, ensure train.jsonl exists in your dataset directory.
If you see a FileNotFoundError, you haven't run the conversion step yet.
5. Feinabstimmung mit MLX
Die Phase „5 Fine-Tuning with MLX“ funktioniert am besten, wenn sie als messbare Oberfläche betrachtet wird. Erfassen Sie ein gelungenes Beispiel, einen Fehlerfall sowie die Notizen zum Rollback, bevor Sie den Umfang erweitern. Betrachten Sie diese Phase als Vertrag zwischen Eingaben und validierten Ausgaben. Benennen Sie die Erzeugnisse, definieren Sie Erfolgskontrollen und lehnen Sie stille, unvollständige Abschlüsse ab. Trennen Sie die Strategie zur Aufteilung in Blöcke von der Strategie zur Abrufung. Ein Änderungs an einer sollte nicht dazu führen, dass die andere neu geschrieben werden muss, wenn sich die Qualitätsmetriken ändern.
Voraussetzungen
Die Voraussetzungsphase funktioniert am besten, wenn sie als messbarer Bereich betrachtet wird. Erfassen Sie vor Erweiterung des Umfangs ein gelungenes Beispiel, einen Fehlerfall sowie eine Notiz zur Rücksetzung. Erfassen Sie außerdem die Laufzeiten sowie die Kosten für Tokens oder Abfragen neben den funktionalen Ergebnissen. Eine frühzeitige Sichtbarkeit der Kosten verhindert überraschende Rechnungen, wenn der Prozess von einer Demo in gemeinsame Umgebungen übergeht. Trennen Sie die Aufteilungspolitik von der Abrufpolitik – ein Änderungsbedarf bei einer sollte nicht dazu führen, dass die andere neu geschrieben werden muss, wenn sich die Qualitätsmetriken ändern.
# For fine-tuning vision models
pip install mlx-vlm
# For merging adapters (needed after training)
pip install mlx-lm
Fine-Tuning ausführen
Die Phase der Feinabstimmung funktioniert am besten, wenn sie als messbare Struktur betrachtet wird. Erfassen Sie ein optimales Transkript, einen Fehlerfall sowie die Notizen zum Rollback, bevor Sie den Umfang erweitern. Bewahren Sie die Konfiguration außerhalb des Anwendungscode auf. Umgebungsdateien, Geheimdatenspeicher und Feature-Flags sollten an einem Ort gesammelt sein, den Betreuer ohne das Durchlesen des gesamten Systems überprüfen können. Trennen Sie die Strategie zur Aufteilung in Blöcke von der Strategie zum Abrufen. Ein Änderungs an einer sollte nicht dazu führen, dass die andere neu geschrieben werden muss, wenn sich die Qualitätsmetriken ändern.
python -m mlx_vlm.lora \
--model Qwen/Qwen2-VL-2B-Instruct \
--dataset ./chart_dataset/train.jsonl \
--iters 1000 \
--batch-size 1 \
--lora-rank 8 \
--gradient-accumulation-steps 4 \
--max-seq-length 512
Verständnis der Trainingsausgabe
Die Phase der Verständnistrainingsausgabe funktioniert am besten, wenn sie als messbare Größe betrachtet wird. Erfassen Sie ein optimales Transkript, einen Fehlerfall sowie die Notiz zur Rücksetzung, bevor Sie den Umfang erweitern. Dokumentieren Sie gleichzeitig den erfolgreichen Ablauf und den Wiederherstellungsprozess. Wiederholversuche, menschliche Überprüfungen sowie die Handhabung von fehlerhaften Nachrichten gehören zum Produkt selbst und nicht zu späteren Optimierungen. Trennen Sie die Chunking-Strategie von der Abrufstrategie – ein Änderung in einer sollte nicht dazu führen, dass die andere neu geschrieben werden muss, wenn sich die Qualitätsmetriken ändern.
INFO:__main__:Loading model from Qwen/Qwen2-VL-2B-Instruct
Fetching 11 files: 100%|████████████████████████████████████████| 11/11 [00:00<00:00, 1465.47it/s]
Download complete: : | 0.00B
Reconstruction complete: | | 0.00B / 0.00B
INFO:__main__:Loading dataset from ./chart_dataset/train.jsonl
INFO:__main__:Setting up LoRA
#trainable params: 9.232384 M || all params: 2208.9856 M || trainable%: 0.418%
INFO:__main__:Setting up optimizer
INFO:__main__:Training model (sft)
Starting training..., iterations: 1000
No validation dataset provided — training will run without validation.
......
Iter 120: Train loss 7.77592850, Learning Rate 2.000e-05,
It/sec 0.242, Tokens/sec 103.862, Trained Tokens 51480, Peak mem 8.069 GB
.....
Saved final adapter weights to adapters.safetensors.
INFO:__main__:Training completed! Model saved to adapters.safetensors
6. Fehlerbehebung
Die 6 Fehlersuchphasen funktionieren am besten, wenn sie als messbarer Bereich betrachtet werden. Erfassen Sie vor der Erweiterung des Umfangs ein „goldenes“ Transkript, einen Fehlerfall sowie eine Notiz zur Rücksetzung. Ziehen Sie kleine, testbare Einheiten vor umfangreichen Skripten vor. Wenn ein Schritt fehlschlägt, sollte der Fehler auf eine einzige Verantwortung verweisen und nicht auf einen verworrenen Ablauf. Trennen Sie die Aufteilungspolitik von der Abrufpolitik. Ein Änderungs an einer sollte nicht dazu führen, dass die andere neu geschrieben werden muss, wenn sich die Qualitätsmetriken ändern.
Fehler: Datensatz nicht gefunden
Die Phase „Fehler: Datensatz nicht gefunden“ funktioniert am besten, wenn sie als messbarer Bereich betrachtet wird. Erfassen Sie vor der Erweiterung des Umfangs ein „goldenes“ Transkript, einen Fehlerfall sowie eine Notiz zur Rücksetzung.
FileNotFoundError: Couldn't find any data file at .../chart_dataset/train.jsonl
python convert_to_mlx_format.py
Fehler: Keine Speichermöglichkeit mehr (OOM)
RuntimeError: [METAL] Command buffer execution failed: Insufficient Memory
python -m mlx_vlm.lora \
--model Qwen/Qwen2-VL-2B-Instruct \
--dataset ./chart_dataset/train.jsonl \
--iters 1000 \
--batch-size 1 \ # ← Reduced from 4 to 1
--lora-rank 4 \ # ← Reduced from 8 to 4
--gradient-accumulation-steps 8 \ # ← Added
--max-seq-length 256 # ← Added
Fehler: Adapterpfad nicht gefunden
FileNotFoundError: The adapter path does not exist: fine_tuned_adapters
ls -la adapters.safetensors adapter_config.json
python -m mlx_lm fuse \
--model Qwen/Qwen2-VL-2B-Instruct \
--adapter-path . \
--save-path ./fine_tuned_model_merged
Fehler: Unvollständiger Cache
IncompleteSnapshotError: The cached snapshot for 'Qwen/Qwen2-VL-2B-Instruct' is incomplete
rm -rf ~/.cache/huggingface/hub/models--Qwen--Qwen2-VL-2B-Instruct
7. Wie lange dauert das Training?
Total Time (seconds) = Total Iterations ÷ It/sec
Total Time (minutes) = Total Time (seconds) ÷ 60
1000 ÷ 0.242 = 4,132 seconds
4,132 ÷ 60 = ~69 minutes
8. Zusammenführen von LoRA-Adaptern
python -m mlx_lm fuse \
--model Qwen/Qwen2-VL-2B-Instruct \
--adapter-path . \
--save-path ./fine_tuned_model_merged
9. Exportieren in GGUF für Ollama
Schritt 1: MLX in das Hugging Face-Format exportieren
#!/usr/bin/env python3
import mlx_lm
from mlx_lm import load, save
model, tokenizer, config = load("./fine_tuned_model_merged")
save(model, tokenizer, config, "./hf_export")
print("✅ Exported to ./hf_export")
python export_to_hf.py
Schritt 2: In GGUF umwandeln
# Clone llama.cpp (if not already done)
git clone https://github.com/ggerganov/llama.cpp
cd llama.cpp
# Convert text model
python convert_hf_to_gguf.py ../hf_export \
--outfile chart_model-text.gguf \
--outtype f16
Schritt 3: Vision Projector umwandeln
python convert_hf_to_gguf.py ../hf_export \
--outfile chart_model-mmproj.gguf \
--outtype f16 \
--mmproj
ls ~/.cache/huggingface/hub/models--Qwen--Qwen2-VL-2B-Instruct/snapshots/
# sample output . You'll see a hash directory (e.g., 895c3a49...)
# 895c3a49bc3fa70a340399125c650a463535e71c
cd llama.cpp
# replace <hash> with the output above (e.g., 895c3a49...)
python convert_hf_to_gguf.py ~/.cache/huggingface/hub/models--Qwen--Qwen2-VL-2B-Instruct/snapshots/<hash>/ \
--outfile base-mmproj.gguf \
--outtype f16 \
--mmproj
10. Einbetten mit Ollama
FROM ./chart_model-text.gguf
FROM ./chart_model-mmproj.gguf
PARAMETER temperature 0.2
Das Modell erstellen
ollama create my-chart-model -f Modelfile
Das Modell überprüfen
ollama list
# output :
# NAME ID SIZE MODIFIED
# my-chart-model:latest b3fd6d8fd742 4.4 GB 7 hours ago
# qwen2.5vl:3b fb90415cde1e 3.2 GB 9 days ago
# llama3.2:3b a80c4f17acd5 2.0 GB 10 days ago
# llama3:latest 365c0bd3c000 4.7 GB 3 months ago
# You should see my-chart-model in the list.
Das Modell testen
ollama run my-chart-model "What is 2+2?"
11. In Ihre bestehende RAG-App integrieren
# In rag_engine.py (from Parts 1 & 2)
# Before:
VISION_MODEL = "qwen2.5vl:3b"
# After:
VISION_MODEL = "my-chart-model" # Your fine-tuned model
TEXT_MODEL = "llama3.2:3b" # Unchanged
python app.py
12. Bewertung: Basismodell vs. feinabgestimmtes Modell
Vergleich
13. Fazit
Den vollständigen Code erhalten
git clone https://github.com/froilan-sia/m1_multimodal_rag.git
cd m1_multimodal_rag