MLX-Trainingsdaten auf dem Cloud-Mac vor dem Start prüfen

MLX-Trainingsdaten auf dem Cloud-Mac vor dem Start prüfen

MLX-Trainingsdaten auf dem Cloud-Mac vor dem Start prüfen

Wenn du auf einem Cloud-Mac ein MLX-Fine-Tuning vorbereitest, starte nicht sofort das Training. Nach dem Hochladen der Trainings- und Validierungsdaten lohnt sich zunächst eine Datenprüfung: Eine Zeile mit unvollständigem JSON, eine leere Antwort oder dieselbe Unterhaltung in beiden Datensätzen können die Vergleichbarkeit eines Experiments zunichtemachen. Im Folgenden bauen wir mit der Python-Standardbibliothek eine wiederholbare Prüfung auf — ohne Modelldownload und unabhängig von der Trainingsumgebung.

Das Eingabeformat festlegen, statt das Skript raten zu lassen

Dieser Artikel behandelt reine Textdialoge: Jede Zeile enthält genau ein Objekt mit ausschließlich dem Feld messages; jede Nachricht enthält ausschließlich role und content. Am Anfang ist eine einzelne system-Nachricht erlaubt. Danach müssen sich user und assistant abwechseln, und die Unterhaltung muss mit einer Antwort enden. Multimodale Inhalte, Tool-Aufrufe und aufeinanderfolgende Nachrichten derselben Rolle sind in diesem Eingabeformat nicht vorgesehen.

Das sind Abnahmekriterien für dieses Projekt, keine Aussage darüber, dass alle MLX-Trainingstools nur diese Struktur akzeptieren. Prüfe vor der Ausführung, welches Datenformat das verwendete Tool tatsächlich einliest. Verwendet die Quelldatei andere Felder, ergänze einen ausdrücklichen Konvertierungsschritt, statt den Prüfer Felder stillschweigend verwerfen zu lassen.

{"messages":[{"role":"user","content":"解释训练集与验证集的区别。"},{"role":"assistant","content":"训练集用于更新参数,验证集用于观察未参与更新的数据表现。"}]}

Speichere die beiden Datensätze als train.jsonl und valid.jsonl, jeweils UTF-8-kodiert und ohne BOM. Leerzeilen gelten als Fehler; ein abschließender Zeilenumbruch am Dateiende zählt nicht als zusätzliche Leerzeile. Bewahre die Originaldateien schreibgeschützt auf und speichere Korrekturen als separate Kopie, damit Datenänderungen nachvollziehbar bleiben.

Formatfehler zeilenweise mit der Standardbibliothek abfangen

Prüfe zunächst mit python3 --version, ob der Interpreter verfügbar ist, und speichere den folgenden Code als check_dataset.py. Die Zeilenlänge ist auf 256 KiB begrenzt. Das ist eine Schutzgrenze für Eingaben in diesem Projekt, nicht die Kontextkapazität des Modells. Das Skript liest binär und mit einer Größenbegrenzung, damit überlange Zeilen nicht erst vollständig in den Arbeitsspeicher geladen werden.

import hashlib
import json
import sys

LIMIT = 256 * 1024
seen = {}


def unique_object(pairs):
    result = {}
    for key, value in pairs:
        if key in result:
            raise ValueError("duplicate object key")
        result[key] = value
    return result


def reject_constant(value):
    raise ValueError("nonstandard JSON constant")


def validate(obj):
    if not isinstance(obj, dict) or set(obj) != {"messages"}:
        raise ValueError("expected messages-only object")
    messages = obj["messages"]
    if not isinstance(messages, list):
        raise ValueError("messages must be an array")
    roles = []
    for message in messages:
        if not isinstance(message, dict):
            raise ValueError("message must be an object")
        if set(message) != {"role", "content"}:
            raise ValueError("unexpected message fields")
        role = message["role"]
        content = message["content"]
        if not isinstance(role, str):
            raise ValueError("role must be text")
        if not isinstance(content, str) or not content.strip():
            raise ValueError("content must be nonempty text")
        roles.append(role)
    body = roles[1:] if roles[:1] == ["system"] else roles
    if len(body) < 2 or len(body) % 2:
        raise ValueError("expected complete conversation pairs")
    if body != ["user", "assistant"] * (len(body) // 2):
        raise ValueError("invalid role order")
    return messages


def inspect(path):
    count = 0
    with open(path, "rb") as stream:
        while True:
            raw = stream.readline(LIMIT + 1)
            if not raw:
                break
            count += 1
            try:
                if len(raw) > LIMIT:
                    raise ValueError("line exceeds byte limit")
                obj = json.loads(
                    raw.decode("utf-8"),
                    object_pairs_hook=unique_object,
                    parse_constant=reject_constant,
                )
                messages = validate(obj)
                canonical = json.dumps(
                    messages, ensure_ascii=False,
                    sort_keys=True, separators=(",", ":"),
                ).encode("utf-8")
                digest = hashlib.sha256(canonical).hexdigest()
                if digest in seen:
                    raise ValueError(
                        "duplicate conversation; first at " + seen[digest]
                    )
                seen[digest] = f"{path}:{count}"
            except (ValueError, RecursionError) as error:
                raise ValueError(f"{path}:{count}: {error}") from error
    if count == 0:
        raise ValueError(f"{path}: empty dataset")
    return count


if __name__ == "__main__":
    if len(sys.argv) != 3:
        raise SystemExit("usage: check_dataset.py TRAIN VALID")
    try:
        counts = [inspect(path) for path in sys.argv[1:]]
    except (OSError, ValueError) as error:
        print(str(error), file=sys.stderr)
        raise SystemExit(1)
    print(json.dumps({"train": counts[0], "valid": counts[1]}))

Duplikate auch zwischen den Datensätzen erkennen

Übergib zuerst den Trainings- und danach den Validierungsdatensatz. Das Skript verwendet für beide denselben Hash-Index. Dadurch führen sowohl Duplikate innerhalb eines Datensatzes als auch Überschneidungen zwischen den Datensätzen zum Fehlschlagen der Prüfung; die Fehlermeldung nennt die erste Fundstelle. Das Skript stoppt beim ersten Fehler. Behebe ihn und führe die Prüfung erneut aus. Der vollständige Dialoginhalt wird dabei nicht ausgegeben.

python3 check_dataset.py train.jsonl valid.jsonl

Vor der Hash-Berechnung werden die Objektschlüssel sortiert und Unterschiede in der JSON-Formatierung entfernt. Die Reihenfolge der Nachrichten und die Zeichen des Nachrichtentextes bleiben jedoch erhalten. Eine andere Einrückung oder Schlüsselreihenfolge umgeht die Erkennung daher nicht; zusätzliche Leerzeichen im Nachrichtentext können dagegen einen anderen Hash ergeben. Das Skript vereinheitlicht weder Groß- und Kleinschreibung noch die Unicode-Darstellung eigenmächtig, damit Code, Formeln und die Bedeutung des Originaltextes unverändert bleiben.

Exakte Deduplizierung verhindert nicht jede Form von Datenleckage. Unterschiedliche Frage-Antwort-Paare aus demselben Dokument oder Beispiele mit identischer Frage und umformulierter Antwort können weiterhin über beide Datensätze verteilt sein.

Sind Kennungen der Quelldokumente vorhanden, teile die Daten zunächst nach Dokumenten gruppiert auf und erzeuge erst danach die Dialoge. Ohne solche Kennungen solltest du zumindest zusätzlich nach doppelten Fragen suchen und Stichproben manuell prüfen. Eine bestandene Hash-Prüfung ist kein Beleg für die Unabhängigkeit des Validierungsdatensatzes. Der Hash-Index wächst mit der Anzahl der Beispiele; für sehr große Datensätze sollte ein festplattenbasierter Index verwendet werden.

Die Tokenlänge separat prüfen

Die Bytezahl dient nur dazu, problematische Eingaben zu begrenzen. Sie lässt sich nicht in eine feste Tokenzahl umrechnen. Chinesischer Text, Code und Emojis unterscheiden sich im Verhältnis von Kodierung zu Tokenisierung. Außerdem fügt das Chat-Template Rollenmarkierungen und weitere Spezialtokens hinzu.

Verwende für die Längenprüfung den zum Zielmodell gehörenden Tokenizer. Verarbeite den vollständigen Dialog mit dem Chat-Template, das das Trainingstool tatsächlich verwendet, und zähle anschließend die Tokens. Halte die Modellrevision, das Template, die maximale Sequenzlänge und die Anzahl der Beispiele mit Überschreitung fest. Es reicht nicht, die einzelnen content-Felder getrennt zu tokenisieren und die Ergebnisse zu addieren.

Für zu lange Beispiele muss ausdrücklich festgelegt werden, ob sie verworfen, aufgeteilt oder gekürzt werden. Prüfe beim Kürzen, ob die Antwort vollständig abgeschnitten wird und ob die Loss-Maske weiterhin gültige Antworttokens abdeckt. Beispiele ohne ein einziges wirksames Trainingstoken müssen verworfen werden. Einen universellen Befehl zum Kürzen gibt es hier nicht, da sich Templates und Maskenverhalten je nach Trainingsschnittstelle unterscheiden.

Den Prüfer mit Negativbeispielen testen, dann ins Training einbinden

Erzeuge zunächst aus Kopien eines Beispiels vier fehlerhafte Varianten: eine leere Antwort, eine falsche Rollenreihenfolge, doppelte Objektschlüssel und einen dateiübergreifend duplizierten Dialog. Führe jede Prüfung einzeln aus und bestätige, dass sie jeweils mit Exitcode 1 endet und Datei sowie Zeilennummer angibt. Teste außerdem den Erfolgsfall mit zwei unterschiedlichen, gültigen Dialogen, damit nicht unbemerkt bleibt, wenn der Prüfer grundsätzlich jeden Eingang ablehnt.

Verknüpfe die Prüfung im Experiment-Skript mit den nachfolgenden Trainingsschritten über && oder eine ausdrückliche Abfrage des Exitcodes. Nach einem Fehlschlag darf das Training nicht weiterlaufen. Sobald die Daten feststehen, speichere die SHA-256-Hashes beider Dateien, die Anzahl der Beispiele, die Ergebnisse der Längenprüfung und die Version des Prüfers. Der aktuelle Trainingslauf darf ausschließlich diese geprüfte Kopie einlesen.

Das Ziel der Abnahme ist nicht die bloße Aussage „Die Daten sind in Ordnung“, sondern ein reproduzierbarer Nachweis: Gleiche Eingaben liefern dieselben Ergebnisse, Fehler lassen sich lokalisieren, die Aufteilung der Datensätze ist begründet und die Behandlung der Sequenzlängen ist dokumentiert. Erst dann sollte Trainingszeit investiert werden.

Häufig gestellte Fragen

Reicht eine erfolgreiche JSONL-Prüfung für den Trainingsstart?

Nein. Prüfen Sie zusätzlich das Eingabeformat des Trainingswerkzeugs und die Länge mit dem Tokenizer sowie der Chat-Vorlage des gewählten Modells.

Verhindert eine andere Schlüsselreihenfolge die Duplikaterkennung?

Nein. Vor der SHA-256-Berechnung werden die Objektschlüssel sortiert. Umformulierungen und semantisch ähnliche Dialoge erkennt dieses Verfahren jedoch nicht.

Mac passend zur Versuchsdauer mieten

Exklusiver Mac mini M4 mit 16 GB gemeinsamem Arbeitsspeicher und 256 GB SSD. Vorauszahlung für Tage, Wochen, Monate oder Quartale; ohne Verlängerung wird das Gerät nach Ablauf gestoppt. Modelle und Laufzeitumgebung richten Sie selbst ein. Maßgeblich ist der Echtzeitstatus in der Konsole.

Mac tageweise mieten

Erst Konfiguration prüfen, dann loslegen

Prüfen Sie Modellgewichte, Spitzenauslastung und Mietdauer, bevor Sie einen Zeitraum auswählen. Eine Anleitung zur Verbindung finden Sie im Hilfe-Center.