Kurs
Qwen3.5 Small ist ein praxisnahes Modell für leichtgewichtiges Feintuning. Es ist für Prototyping, aufgabenspezifische Anpassungen und Forschungs-Workflows ausgelegt. In Kombination mit QLoRA können wir es auf einem eingefrorenen, 4‑bit-quantisierten Basismodell feintunen — das macht den Prozess deutlich speichereffizienter und auch auf begrenzter Hardware gut nutzbar.
In diesem Tutorial richten wir die Umgebung ein, laden das Modell und den AG-News-Datensatz, bewerten das Basismodell und feintunen anschließend Qwen3.5 Small für die Nachrichtenklassifikation. Zum Schluss vergleichen wir die Ergebnisse vor und nach dem Training, speichern den LoRA-Adapter und veröffentlichen ihn zur Wiederverwendung bei Hugging Face.
Ich empfehle dir den Lernpfad Hugging Face Fundamentals, falls du mit dem Ökosystem noch nicht vertraut bist.
Was ist Qwen3.5 Small?
Bevor wir in den Code einsteigen, lohnt sich ein Blick auf das Modell. Qwen3.5 ist eine Familie von Foundation-Modellen, und die „Small“-Reihe bezeichnet die leichten Varianten, konkret alle Modelle unter 10 Milliarden Parametern. Für dieses Tutorial verwenden wir die 0.8B-Version.
Trotz nur 800 Millionen Parametern ist Qwen3.5-0.8B sehr leistungsfähig und extrem effizient. In 4‑Bit-Präzision geladen benötigt es weniger als 2 GB VRAM. Damit eignet es sich hervorragend für Consumer-Laptops, freie Cloud-Umgebungen wie Kaggle und schlanke Edge-Deployments.
Außerdem unterstützt die Qwen3.5-Architektur ein riesiges Kontextfenster von 262K Tokens und native Multimodalität. Wir konzentrieren uns hier zwar auf reine Textklassifikation, aber die Grundlagen lassen sich später leicht auf Vision-Aufgaben übertragen. Das Modell bietet auch einen hybriden Reasoning-Modus; wir lassen ihn jedoch deaktiviert, um für unsere einfache Labeling-Aufgabe die Inferenzgeschwindigkeit zu maximieren.
In Summe ist Qwen3.5-0.8B eine sehr praktische Option fürs Prototyping. Es ermöglicht schnelle, kostengünstige Experimente mit Feintuning-Workflows — und liefert trotzdem starke Ergebnisse in Downstream-Aufgaben.
1. Kaggle-Umgebung einrichten
In diesem Tutorial nutzen wir ein Kaggle-Notebook als Trainingsumgebung. Kaggle stellt GPUs bereit und ist damit ideal zum Trainieren, Feintunen und Evaluieren unseres Modells. Starte ein neues Kaggle-Notebook, öffne die Session-Einstellungen und wähle GPU P100 als Beschleuniger.

Als Nächstes richten wir die Umgebung ein, fügen einen Hugging Face-Token in Kaggle hinzu und importieren die Python-Bibliotheken, die wir im Tutorial verwenden.
Hugging Face-Zugriffstoken erstellen
Erstelle vor dem Start des Notebooks einen Hugging Face-Zugriffstoken in deinen Konto-Einstellungen. Gehe zu Settings, öffne Access Tokens und generiere einen neuen Token. Wenn du den feingetunten LoRA-Adapter am Ende bei Hugging Face veröffentlichen möchtest, gib dem Token Schreibrechte.

Token zu Kaggle-Secrets hinzufügen
Öffne nach dem Erstellen des Tokens dein Kaggle-Notebook und füge ihn als Secret hinzu. Gehe im Notebook-Editor zu Add-ons, dann Secrets, und erstelle ein neues Secret namens HUGGINGFACE_TOKEN. Füge dort deinen Tokenwert ein, damit das Notebook sicher darauf zugreifen kann, ohne ihn im Code offenzulegen.

Benötigte Bibliotheken installieren
Jetzt installieren wir die zentralen Bibliotheken für diesen Workflow. Sie decken Modellladen, Quantisierung, parametereffizientes Feintuning, Datensatzhandling und Evaluation ab.
!pip install -q -U transformers accelerate
!pip install -q peft trl bitsandbytes datasets scikit-learn
Bei Hugging Face authentifizieren
Als Nächstes laden wir den Hugging Face-Token aus den Kaggle-Secrets und authentifizieren uns. So erhält das Notebook Zugriff auf das Modell und wir können später den trainierten Adapter pushen.
from huggingface_hub import login
from kaggle_secrets import UserSecretsClient
user_secrets = UserSecretsClient()
login(token=user_secrets.get_secret("HUGGINGFACE_TOKEN"))
Python-Bibliotheken importieren
Zum Schluss importieren wir die Python-Bibliotheken, die wir für Laden, Training, Datenverarbeitung und Evaluation benötigen.
import torch
from datasets import load_dataset
from transformers import AutoModelForCausalLM, AutoTokenizer, BitsAndBytesConfig
from peft import LoraConfig, get_peft_model
from trl import SFTTrainer, SFTConfig
from transformers import TrainingArguments
from sklearn.metrics import accuracy_score, f1_score
from tqdm.notebook import tqdm
2. Qwen3.5-Modell und AG-News-Datensatz laden
Jetzt, da die Umgebung steht, laden wir das Basismodell und bereiten den Datensatz fürs Feintuning vor.
In diesem Abschnitt laden wir Qwen3.5-0.8B in 4‑Bit-Präzision, richten den Tokenizer ein und laden einen kleinen Ausschnitt des AG-News-Datensatzes für Training und Evaluation.
Qwen3.5-0.8B mit 4‑Bit-Quantisierung laden
Wir beginnen damit, Qwen3.5-0.8B per BitsAndBytes in 4 Bit zu laden. Das reduziert den Speicherbedarf und macht das Arbeiten im Kaggle-Notebook deutlich praktischer.
model_id = "Qwen/Qwen3.5-0.8B"
bnb_config = BitsAndBytesConfig(
load_in_4bit=True,
bnb_4bit_quant_type="nf4",
bnb_4bit_compute_dtype=torch.bfloat16
)
model = AutoModelForCausalLM.from_pretrained(
model_id,
quantization_config=bnb_config,
device_map="auto",
dtype=torch.bfloat16
)
Tokenizer laden
Als Nächstes laden wir den Tokenizer für dasselbe Modell. Wir setzen außerdem das Padding-Token auf das End-of-Sequence-Token, damit das Modell während Training und Generierung gepaddete Eingaben korrekt verarbeitet.
tokenizer = AutoTokenizer.from_pretrained(model_id)
tokenizer.pad_token = tokenizer.eos_token
model.config.pad_token_id = tokenizer.eos_token_id
model.generation_config.pad_token_id = tokenizer.eos_token_id
AG-News-Datensatz laden
Zum Schluss laden wir den AG-News-Datensatz und erstellen kleinere Trainings- und Test-Splits für dieses Tutorial. Ein Subset hält den Workflow schnell, liefert aber genug Daten, um den Effekt des Feintunings zu bewerten.
dataset = load_dataset("ag_news")
train_dataset = dataset["train"].select(range(2500))
test_dataset = dataset["test"].select(range(200))
3. Prompt-Formatierung und Baseline-Evaluation
Bevor wir feintunen, definieren wir, wie das Modell die Aufgabe präsentiert bekommt, und messen die Leistung im Ursprungszustand.
Wir erstellen einen instruktionalen Prompt für die Nachrichtenklassifikation, extrahieren Vorhersagen aus der Modellausgabe, führen eine Beispielinferenz durch und evaluieren das Basismodell vor dem Training auf dem Testset.
Den Instruktions-Prompt erstellen
Wir definieren zunächst die Labelnamen und bauen einen Prompt, der das Modell bittet, einen Nachrichtenartikel zu klassifizieren. Der Prompt ist so formuliert, dass das Modell nur das numerische Label zurückgibt — das erleichtert das Parsen in der Evaluation.
labels = ['World','Sports','Business','Sci/Tech']
def build_prompt(text):
return f"""
Classify the news article.
Article:
{text}
Return ONLY the number of the correct label.
0 = World
1 = Sports
2 = Business
3 = Sci/Tech
Answer:
"""
Vorhersagen aus der Modellausgabe extrahieren
Als Nächstes schreiben wir eine kleine Hilfsfunktion, die das vorhergesagte Label aus dem generierten Text zieht. Sie entfernt etwaige Reasoning-Tokens und gibt das finale Klassenlabel als Integer zurück.
import re
def extract_label(text):
text = re.sub(r"<think>.*?</think>", "", text, flags=re.DOTALL)
matches = re.findall(r"[0-3]", text)
if matches:
return int(matches[-1])
return -1
Beispielinferenz ausführen
Bevor wir das Modell auf dem gesamten Testset evaluieren, testen wir es an einem einzelnen Beispiel. So können wir Artikel, korrektes Label und die Vorhersage vor dem Feintuning prüfen.
sample = train_dataset[10]
sample_text = sample["text"]
sample_label = sample["label"]
print("Ground truth label:", sample_label)
print("Sample text:")
print(sample_text[:300])
Die Ausgabe zeigt, dass das wahre Label 2 ist, was Business entspricht.
Ground truth label: 2
Sample text:
Oil and Economy Cloud Stocks' Outlook NEW YORK (Reuters) - Soaring crude prices plus worries about the economy
and the outlook for earnings are expected to hang over the stock market next week during the depth of the summer doldrums.
Als Nächstes formatieren wir den Prompt, übergeben ihn ans Modell und generieren eine Vorhersage.
prompt = build_prompt(sample_text)
inputs = tokenizer(prompt, return_tensors="pt").to("cuda")
with torch.inference_mode():
outputs = model.generate(
**inputs,
max_new_tokens=5,
do_sample=False,
pad_token_id=tokenizer.eos_token_id
)
prediction = tokenizer.decode(outputs[0], skip_special_tokens=True)
print("Model prediction (before training):")
print(extract_label(prediction))
Das Modell sagt ebenfalls 2 vorher und klassifiziert das Beispiel korrekt. Damit ist klar, dass Prompt-Format und Vorhersage-Pipeline funktionieren.
Model prediction (before training):
2
Basismodell vor dem Feintuning evaluieren
Nun evaluieren wir das Basismodell auf dem Testset, um eine Baseline zu etablieren. Das dient als Referenz für den Vergleich vor und nach dem Feintuning.
def evaluate_model(dataset):
preds = []
refs = []
model.eval()
for example in tqdm(dataset):
text = example["text"]
label = example["label"]
prompt = build_prompt(text)
inputs = tokenizer(prompt, return_tensors="pt", truncation=True)
inputs = {k: v.to("cuda") for k, v in inputs.items()}
with torch.inference_mode():
outputs = model.generate(
**inputs,
max_new_tokens=5,
do_sample=False,
pad_token_id=tokenizer.eos_token_id
)
prediction = tokenizer.decode(outputs[0], skip_special_tokens=True)
pred_label = extract_label(prediction)
preds.append(pred_label)
refs.append(label)
acc = accuracy_score(refs, preds)
f1 = f1_score(refs, preds, average="weighted")
print("Accuracy:", acc)
print("F1:", f1)
print('Pre-fine-tuning evaluation')
evaluate_model(test_dataset)
Die Ausgabe meldet eine Accuracy von 0.52 und einen gewichteten F1-Score von 0.4589. Die Baseline zeigt, dass das Modell die Aufgabe bereits ansatzweise beherrscht — mit klarer Luft nach oben durch Feintuning.
Pre-fine-tuning evaluation
Accuracy: 0.52
F1: 0.45889163289429247
4. Qwen3.5 mit QLoRA feintunen
Mit der Baseline im Gepäck feintunen wir das Modell nun auf die Nachrichtenklassifikation. In diesem Abschnitt formatieren wir die Trainingsdaten, konfigurieren die LoRA-Adapter, definieren die Trainingseinstellungen, trainieren das Modell und speichern den Adapter für später.
Trainingsdaten vorbereiten
Wir bringen jedes Trainingsexemplar in dasselbe Prompt-Format wie bei der Inferenz. Am Ende des Prompts hängen wir das korrekte Label an, damit das Modell während des Feintunings die richtige Antwort generieren lernt.
def format_train(example):
prompt = build_prompt(example['text'])
answer = str(example['label'])
return {'text': prompt + ' ' + answer}
train_dataset = train_dataset.map(format_train)
LoRA-Adapter konfigurieren
Als Nächstes definieren wir die LoRA-Konfiguration. Sie steuert, welche Schichten beim Feintuning angepasst werden. So trainieren wir nur einen kleinen Satz zusätzlicher Parameter statt des gesamten Modells.
lora_config = LoraConfig(
r=16,
lora_alpha=16,
lora_dropout=0,
bias='none',
task_type='CAUSAL_LM',
target_modules=[
'q_proj','k_proj','v_proj','o_proj',
'gate_proj','up_proj','down_proj'
]
)
Training konfigurieren
Dann setzen wir die Trainingsargumente fürs überwachte Feintuning. Diese Einstellungen steuern Batch-Größe, Gradientenakkumulation, Trainingsdauer und den Speicherort der Ausgaben.
training_args = SFTConfig(
output_dir="./qwen35-lora",
per_device_train_batch_size=8,
gradient_accumulation_steps=2,
learning_rate=2e-4,
num_train_epochs=1,
logging_steps=10,
bf16=True,
dataset_text_field="text",
packing=False,
)
Modell trainieren
Wir initialisieren den Trainer mit Modell, Datensatz, LoRA-Konfiguration, Trainingseinstellungen und Tokenizer und starten das Feintuning auf dem formatierten AG-News-Trainingsset.
trainer = SFTTrainer(
model=model,
train_dataset=train_dataset,
peft_config=lora_config,
args=training_args,
processing_class=tokenizer
)
trainer.train()
Ausgabe:
TrainOutput(global_step=157, training_loss=1.9094241804378047, metrics={'train_runtime': 616.477, 'train_samples_per_second': 4.055, 'train_steps_per_second': 0.255, 'total_flos': 1041693973114368.0, 'train_loss': 1.9094241804378047})
Trainierten Adapter speichern
Nach dem Training speichern wir den LoRA-Adapter und den Tokenizer. So können wir den feingetunten Adapter später zum Evaluieren, Teilen oder Veröffentlichen bei Hugging Face wieder laden.
trainer.model.save_pretrained("qwen35-small-news-class")
tokenizer.save_pretrained("qwen35-small-news-class")
5. Das feingetunte Modell evaluieren
Nach dem Training laden wir das Basismodell erneut und hängen den gespeicherten LoRA-Adapter an. So evaluieren wir mit den feingetunten Gewichten und vergleichen mit der früheren Baseline.
Basismodell neu laden und LoRA-Adapter anhängen
Zuerst laden wir das Qwen3.5-Basismodell erneut, dann binden wir den feingetunten LoRA-Adapter aus dem Kaggle-Working-Verzeichnis ein. Anschließend schalten wir auf Evaluationsmodus und werfen einen Blick auf die trainierbaren Parameter.
from transformers import AutoModelForCausalLM
base_model = AutoModelForCausalLM.from_pretrained(
model_id,
device_map="auto",
dtype=torch.bfloat16
)
from peft import PeftModel
model = PeftModel.from_pretrained(
base_model,
"/kaggle/working/qwen35-small-news-class"
)
model.eval()
model.print_trainable_parameters()
Die Ausgabe zeigt, dass es nun keine trainierbaren Parameter mehr gibt: Das Training ist abgeschlossen, und wir verwenden den gespeicherten Adapter nur noch für Inferenz und Evaluation.
trainable params: 0 || all params: 758,782,784 || trainable%: 0.0000
Das feingetunte Modell evaluieren
Jetzt lassen wir erneut dieselbe Evaluationsfunktion über das Testset laufen. So messen wir, wie stark sich das Modell durch das Feintuning verbessert hat.
print('Post-fine-tuning evaluation')
evaluate_model(test_dataset)
Die Ausgabe meldet eine Accuracy von 0.865 und einen gewichteten F1-Score von 0.8661. Im Vergleich zur Baseline ist das ein deutlicher Sprung nach vorn.
Post-fine-tuning evaluation
Accuracy: 0.865
F1: 0.8660926258753361
Ergebnisse vergleichen
Nun vergleichen wir die Leistung vor und nach dem Feintuning. Sowohl Accuracy als auch F1-Score steigen deutlich — QLoRA hilft dem Modell, sich sehr gut an die Nachrichtenklassifikation anzupassen.
|
Metrik |
Vor dem Training |
Nach dem Training |
|
Accuracy |
0.52 |
0.865 |
|
F1-Score |
0.4589 |
0.866 |
Wie man sieht, steigert das Feintuning von Qwen3.5 Small mit QLoRA die Klassifikationsleistung deutlich.
6. Modell bei Hugging Face veröffentlichen
Nach der Evaluation können wir den feingetunten Adapter und den Tokenizer bei Hugging Face hochladen. So lässt sich das Modell bequem wiederverwenden, teilen und in zukünftigen Projekten laden — ohne das komplette Training zu wiederholen.
model.push_to_hub("kingabzpro/qwen35-small-news-class")
tokenizer.push_to_hub("kingabzpro/qwen35-small-news-class")
Nach Abschluss des Uploads ist das Modell hier verfügbar:
Model:
https://huggingface.co/kingabzpro/qwen35-small-news-class

Quelle: kingabzpro/qwen35-small-news-class
Auch das Kaggle-Notebook zu diesem Tutorial ist verfügbar. Nutze es am besten nur, wenn du bei der Reproduktion der obigen Schritte auf Probleme stößt. Du kannst das Notebook auch klonen und lokal ausführen — denke daran, Hugging Face-Nutzername und Token auf dein Konto anzupassen.
7. Fazit
In diesem Tutorial haben wir Qwen3.5-0.8B mit QLoRA für die Nachrichtenklassifikation feingetunt — mit einem sehr schlanken Setup und starken Ergebnissen. Nach nur einer Epoche auf einem kleinen Datenausschnitt stieg die Accuracy von 0.52 auf 0.865, der gewichtete F1 von 0.4589 auf 0.8661.
Für ein so kleines Modell ist das tatsächlich das Überraschende: Wir brauchen weder ein riesiges Modell noch einen riesigen Datensatz oder lange Trainingsläufe, um starke Downstream-Ergebnisse zu erzielen.
Besonders nützlich ist zudem das praxisnahe Setup. Qwen beschreibt das 0.8B-Modell als passend für Prototyping und aufgabenspezifisches Feintuning. QLoRA hält den Speicherbedarf niedrig, indem Adapter auf einem eingefrorenen 4‑Bit-Basismodell trainiert werden. Das ist nicht nur ein Cloud-Notebook-Workflow — es eignet sich auch realistisch für Tests auf kleineren Consumer- und Laptop-GPUs.
Spannend finde ich außerdem, dass die Qwen3.5-Familie sowohl Text- als auch Vision-Feintuning unterstützt. Wir haben es hier für Textklassifikation genutzt, doch dasselbe Prinzip lässt sich auf Bildklassifikation, Image-to-Text und Text-to-Text übertragen. Damit ist ein kleines Modell wie dieses ideal, um Ideen schnell zu testen, günstig zu feintunen und das Deployment schlank zu halten.
Sieh dir außerdem unsere weiteren Ressourcen an, darunter unseren Leitfaden zu Qwen3.5 und unser Tutorial zum lokalen Ausführen von Qwen3.5 auf einer einzelnen GPU.
FAQs
Warum QLoRA (4‑Bit-Quantisierung) statt vollem Feintuning für ein 0.8B-Modell?
Auch wenn 0.8B nach LLM-Maßstäben „klein“ ist, erfordert das vollständige Aktualisieren aller 800 Millionen Parameter viel VRAM und Rechenleistung. QLoRA friert das Basismodell in 4‑Bit-Präzision ein und trainiert nur einen sehr kleinen Satz neuer Adapter-Gewichte obendrauf. Das reduziert den Speicherbedarf drastisch — so kannst du bequem auf einer kostenlosen Kaggle-P100-GPU oder einem normalen Consumer-Laptop trainieren, ohne in Speicherprobleme zu laufen.
Qwen3.5 bietet einen neuen „Thinking“-Modus. Sollte ich ihn für Klassifikation nutzen?
Qwen3.5-0.8B unterstützt nativ hybrides Reasoning, läuft aber standardmäßig im „Non-Thinking“-Modus. Du kannst das Reasoning aktivieren, aber einfache Klassifikationsaufgaben wie AG News benötigen keine komplexe, schrittweise Logik. Das Thinking-Feature deaktiviert zu lassen, ist für einfache Labeling-Aufgaben deutlich schneller und token-effizienter.
Wie beeinflusst Qwen3.5s riesiges 262K-Kontextfenster dieses Feintuning-Skript?
Auch wenn das Basismodell bis zu 262.144 Tokens unterstützt, sind sehr lange Dokumente beim Feintuning schnell zu viel für den GPU-Speicher in Kaggle. Für Nachrichtenklassifikation ist der Text kurz — also kein Thema. Wenn du das Skript für Langdokument-Aufgaben (z. B. ganze Bücher zusammenfassen) anpassen willst, aktiviere Gradient Checkpointing und reduziere die Batch-Größe.
Als zertifizierter Data Scientist ist es meine Leidenschaft, modernste Technologien zu nutzen, um innovative Machine Learning-Anwendungen zu entwickeln. Mit meinem fundierten Hintergrund in den Bereichen Spracherkennung, Datenanalyse und Reporting, MLOps, KI und NLP habe ich meine Fähigkeiten bei der Entwicklung intelligenter Systeme verfeinert, die wirklich etwas bewirken können. Neben meinem technischen Fachwissen bin ich auch ein geschickter Kommunikator mit dem Talent, komplexe Konzepte in eine klare und prägnante Sprache zu fassen. Das hat dazu geführt, dass ich ein gefragter Blogger zum Thema Datenwissenschaft geworden bin und meine Erkenntnisse und Erfahrungen mit einer wachsenden Gemeinschaft von Datenexperten teile. Zurzeit konzentriere ich mich auf die Erstellung und Bearbeitung von Inhalten und arbeite mit großen Sprachmodellen, um aussagekräftige und ansprechende Inhalte zu entwickeln, die sowohl Unternehmen als auch Privatpersonen helfen, das Beste aus ihren Daten zu machen.

