Who picks the tool
-->
Direkt zum Hauptbereich
Die klassische ML-Antwort auf „kategorisiere diese Texte" lautet: sammle tausende gelabelte Beispiele, trainiere ein Modell, wiederhole. Ich habe nichts davon gemacht. Ich habe meine Kategorien in je einem Satz beschrieben und ein vortrainiertes Modell die Ähnlichkeit ausrechnen lassen.
Im ersten Artikel ging es darum, dass mein Transaktions-Classifier komplett in Elixir lebt. Jetzt zum Wie. Das ist die Entscheidung, auf die ich am stolzesten bin, weil sie so viel Komplexität spart.
Der teure Weg, den ich nicht gegangen bin: Ein klassischer Klassifikator will einen Trainingsdatensatz, also tausende Banktexte, jeder von Hand mit der richtigen Kategorie versehen. Dann trainiert man ein Modell, evaluiert, justiert und trainiert neu. Für ein Privatprojekt ist das utopisch, und selbst im Unternehmen vergehen Wochen, bevor das erste Feature läuft.
Der Weg, den ich gegangen bin, heißt Embeddings. Ein Embedding-Modell verwandelt einen Text in einen Vektor aus Zahlen, und zwar so, dass Texte mit ähnlicher Bedeutung nahe beieinander liegen. „EDEKA Köln" und „REWE Markt" landen dicht zusammen, weit weg von „Allianz Versicherung". Das Modell versteht den Inhalt also schon, weil es auf riesigen Textmengen vortrainiert wurde. Ich muss es nicht trainieren, ich muss es nur benutzen.
Die Idee: Ich beschreibe jede Kategorie einmal mit typischen Stichworten, und diese Beschreibung wird zu einem Vektor. Eine eingehende Transaktion wird ebenfalls zu einem Vektor, und dann nehme ich die Kategorie, deren Vektor am ähnlichsten ist.
Das Modell selbst heißt multilingual-e5-base. „Multilingual" ist hier entscheidend, denn die Texte sind deutsch. Es ist rund 278 MB groß, läuft lokal auf der CPU, und ich lade es mit einer Zeile über Bumblebee:
Beim Start rechne ich für jede Kategorie-Beschreibung einmal den Vektor aus und cache ihn. Kommt eine neue Transaktion herein, vektorisiere ich nur ihren Text und vergleiche ihn mit den gecachten Kategorie-Vektoren. Dieser Vergleich ist schlicht ein Skalarprodukt (die Cosine-Similarity), also eine Zahl zwischen 0 und 1, die angibt, wie ähnlich sich zwei Vektoren sind:
Die wichtigste Designentscheidung steckt im nächsten Detail: Ich lasse das Modell auch „weiß nicht" sagen. Wenn die beste Übereinstimmung unter einem Schwellwert liegt, rate ich nicht, sondern sortiere die Transaktion in „Sonstiges" und überlasse sie einem Menschen:
Den Schwellwert (bei mir 0.75) habe ich nicht geraten. Ich logge bei jeder Klassifikation die Top-3-Scores und sehe so, wo „richtige" Treffer liegen (meist um 0.80 bis 0.92) und wo „plausibel, aber falsch" anfängt (eher 0.65 bis 0.75). Das ist die Art von Tuning, die man im Betrieb macht, nicht in einem Trainingslauf.
Bleibt der ehrliche Trade-off: Ist das so genau wie ein sorgfältig fine-getuntes Modell auf zehntausenden Beispielen? Nein. Für einen großen E-Commerce-Konzern mit Millionen Transaktionen und einem eigenen ML-Team würde ich anders entscheiden. Aber für ein Feature, das sofort nützlich sein soll, ohne Datensammlung, ohne Trainings-Infrastruktur und mit einem Tuning-Knopf statt einem Trainings-Zyklus, ist das Verhältnis aus Aufwand und Nutzen kaum zu schlagen. Und es wird mit jeder Korrektur besser.
Im nächsten Teil: Wie eine Korrektur im UI das Verhalten sofort verbessert, ganz ohne Retraining.
Kommentare