Volleyball mit KI-Agenten spielen, um den Umsatz von Restaurants vorherzusagen.
Ein Hackathon für den Umsatz von Restaurants, der auf menschlich gesteuerten KI-Agenten basiert: Datenaudit, validierung berücksichtigend individuelle Besonderheiten, Feature-Engineering, Ensembling sowie der Abstand zu den Platzierungen in den Leaderboards.
Stellen Sie sich einen Hackathon vor, bei dem das Modell selbst nur die Hälfte der Arbeit darstellt. Die andere Hälfte besteht darin, eine Handvoll KI-Agenten mit konkurrierenden Ideen beschäftigt zu halten, ohne dass einer von ihnen eine Entscheidung trifft, die eigentlich einer Person zusteht. Dieser Artikel beschreibt einen solchen Ansatz aus einem Hackathon zur Vorhersage des Umsatzes in Restaurants, der von Great Learning in Zusammenarbeit mit UT Austin und deren McCombs Business School veranstaltet wurde. Er behandelt einen effizienten ML-Arbeitsablauf, strenge Validierungen sowie ein Ensemble, das laut Angaben des Projekts im Wettbewerb gut abschnitt.
Die Orchestrierungsschicht bestand aus einem Framework namens DontPanic. Seine Arbeitsabläufe folgen der Logik eines Volleyball-Spiels: Ein Agent erhält eine Aufgabe, plant den nächsten Schritt, leitet die Arbeit an spezialisierte Rollen weiter und übergibt schließlich das Ergebnis – doch der „Ball“ landet stets wieder bei einem Menschen an der Entscheidungslinie. Die Agenten beschleunigen die Ausführung, tragen aber nicht die Verantwortung für die Entscheidungen.
1. Die Aufgabe
Die Konkurrenten mussten den jährlichen Umsatz von Restaurants in ganz Indien anhand von 3.493 beschrifteten Trainingsdatensätzen sowie 500 unbeschrifteten Testdatensätzen vorhersagen. Es gab etwa 32 Rohprädiktoren, die Standort, Küche, Restaurantformat, Annehmlichkeiten, Popularität in sozialen Medien sowie Bewertungen der Kundenerfahrung berücksichtigten. Unter „Umsatz“ ist hier der jährliche Verkaufsumsatz zu verstehen, nicht die Fluktuation des Personals. Als Messgröße diente der RMSE, bei dem die Fehler quadriert und anschließend durchschnitten werden; dadurch können einige besonders große Fehlprognosen den Gesamtwert stark beeinflussen.
Ziel war es nicht, das ausgefallenste Modell zu entwickeln. Es sollte vielmehr ein vorhersagendes System entstehen, das verteidigbar ist: Die Daten sollten überprüft werden, die wirtschaftlichen Aspekte des Ziels verstanden, sinnvolle Signale konstruiert sowie Kandidatmodelle mithilfe stabiler Kreuzvalidierung geprüft werden. Verbesserungen, die sich nicht reproduzieren ließen, sollten verworfen und schließlich die tatsächliche Allgemeingültigkeit des Modells von an konkretes Wettbewerbsszenario angepassten Einstellungen getrennt gehalten werden.
2. Die Werkzeuge
Alles befand sich in VS Code als modulares Python-Paket unter src/, anstelle einer Ansammlung unzusammenhängender Notizbuch-Experimente. Diese Entscheidung hat sich ausgezahlt, da Reinigung, Erstellung neuer Funktionen, Validierung und Inferenz alle über denselben wiederverwendbaren Code abgewickelt wurden. Dadurch konnte eine Funktion beim Training nicht unauffällig auf eine Weise funktionieren und bei der Abgabe auf eine andere.
3. Agentenbasierte Orchestrierung
DontPanic war das Framework, das das Projekt umschloss. Vollständige Autonomie war nie das Ziel. Es ging darum, mehreren Agenten die Möglichkeit zu geben, schnell voranzukommen, während gleichzeitig die Entscheidungsstruktur festgelegt wurde: Wer erkundet, wer entwickelt, wer in Frage stellt, welches Ergebnis als autoritativ gilt und wann die Arbeit an einen Menschen zurückgegeben werden muss, um eine Entscheidung zu treffen.
Das ist wichtig, weil Experimente mit KI nacheinander plausible nächste Schritte aufwerfen. Ein frei agierender Agent wird weiterhin das optimieren, was er zuletzt wahrgenommen hat, und ein Mensch kann den Überblick verlieren, ob sich die Hypothese, das Validierungsprotokoll oder das Förderkriterium im Hintergrund geändert haben. Betrachten Sie DontPanic als das Regelbuch, unter dem das Experiment abläuft: Die Agenten durften den Ablauf vorantreiben, durften die Regeln jedoch niemals ohne dass jemand es bemerkt, umschreiben.
Jeder Laufzeitmodus eines Agenten wurde durch ein Manifest beschrieben. Ein Beispiel für einen Claude CLI Laufzeitmodus nennt die erforderlichen Befehle und Dateien, wie die Bereitschaft überprüft wird, welche Teile vom Framework übernommen werden, was der Operator tun muss und welche Aktionen verboten sind:
{
"schema_version": "1.0.0",
"id": "agent-claude-cli",
"title": "Claude CLI agent runtime",
"kind": "agent_cli",
"category": "agent-cli",
"summary": "External agent CLI used by DontPanic as an implementer, auditor, or goal-auditor runtime.",
"setup_required": true,
"setup_doc": "docs/GETTING_STARTED.md#setup-tracks--pick-yours",
"default_in_profiles": ["core"],
"use_cases": ["U1", "U3", "U4", "U5", "U6", "U7"],
"requires": {
"commands": ["claude"],
"env": [],
"files": ["~/.dontpanic/config.json"],
"services": ["Claude account / Claude Code or CLI runtime"],
"auth": ["Claude CLI authenticated by operator"],
"config": [
"roles.implementer",
"roles.auditor",
"roles.goal_auditor"
]
},
"verify": {
"doctor_profiles": ["core"],
"probes": ["cli:claude", "agent_role_config"],
"readiness": "Ready when the claude command is present and the selected DontPanic role points at claude."
},
"owner_boundary": {
"dontpanic_core": [
"agent registry entry",
"dispatch envelope",
"audit/signoff capture",
"quota and breaker handling",
"role configuration"
],
"adapter": [
"subprocess executor for claude",
"stream/output parsing",
"timeout and envelope normalization"
],
"operator": [
"Claude CLI installation",
"account authentication",
"model/quota management",
"local CLI updates"
]
},
"mutation_boundary": {
"writes_dontpanic_state": true,
"external_mutation": "agent_subprocess_execution",
"allowed_path": "DontPanic supervisor invokes claude through the governed executor and captures artifacts through audit/signoff writers.",
"forbidden": [
"Bypassing supervisor gates",
"Untracked agent writes outside declared plan scope",
"Treating Claude account configuration as DontPanic-owned state"
]
},
"notes": [
"Claude is an external runtime, not bundled by DontPanic.",
"Core profile may also support other agent CLIs; this manifest records the Claude-specific path."
],
"setup_steps": [
{
"id": "install_claude_cli",
"what": "Install the Claude CLI so DontPanic can spawn agent runs through the governed executor.",
"automatable": true,
"command_template": "npm install -g @anthropic-ai/claude-cli",
"verify_probe": "cli:claude",
"human_required_reason": null
},
{
"id": "authenticate_claude",
"what": "Authenticate the Claude CLI against the operator's Claude account or API key.",
"automatable": false,
"command_template": "claude auth login --token <YOUR_CLAUDE_API_TOKEN>",
"verify_probe": "agent_role_config",
"human_required_reason": "Operator must approve scopes for their Claude account and treat the API token as a secret; DontPanic does not provision Claude credentials on the operator's behalf."
}
]
}
3.1 Der Rally in fünf Schritten
- Einreichen: Bevor jedes Experiment begann, formulierte der Mensch die Geschäftsfrage, die Einschränkungen, die Metrik, die Akzeptanzkriterien sowie den aktuellen „Champion“.
- Empfangen: Ein Entdeckungsagent überprüfte die Belege, markierte Anomalien und schlug testbare Hypothesen vor, anstatt das Modell sofort neu zu schreiben.
- Einstellen: Sobald eine Hypothese genehmigt wurde, erstellte ein Implementierungsagent sie als modularen Code, deterministische Artefakte sowie eine Experimentdefinition, die erneut ausgeführt werden konnte.
- Schlag: Ein Prüfungsagent suchte nach Leckagen, fehlerhafter Kreuzvalidierung, instabilen „Seeds“ sowie unklarer Datenherkunft und prüfte, ob der scheinbare Vorteil einer Vergleich mit dem „gefrorenen Champion“ standhielt.
Der eigentliche Vorteil lag im kontrollierten Parallelismus und nicht in der reinen Codiergeschwindigkeit. Verschiedene Agenten konnten sich auf unterschiedliche Fehlerquellen konzentrieren, während der menschliche Kontrollmechanismus die Produktziele, die Geschäftstauglichkeit sowie die Rechenschaftspflicht gewährleistete. Das Ergebnis erinnerte eher an ein kleines Produktteam als an eine einzelne Chatbot-Sitzung.
3.2 Lean ML-Delivery
Der Agent-Arbeitsablauf wurde ebenfalls auf einen effizienten Produktlebenszyklus abgestimmt, mit dem Ziel, Experimente in der DEV-Phase kostengünstig durchzuführen, Beweise in der UAT-Phase schwer zu fälschen und den Produktzustand versehentlich nicht verändern zu lassen. Die Trennung in DEV/UAT/PROD wurde mit zunehmender Anzahl der Experimente immer wertvoller. Ein neuer Agent konnte ohne Abhängigkeit von konversationaler Memorie hinzukommen, da die autoritativen Aufzeichnungen in Form von Dateien, Manifesten, Vorhersagen außerhalb des Kontexts, einem Register der „Champions“ sowie schriftlich festgehaltenen Entscheidungen zur Optimierung aufbewahrt wurden. Das Muster, das beibehalten werden sollte: Lassen Sie die Agenten für Geschwindigkeit sorgen, während Artefakte und Kontrollmechanismen für die Genauigkeit verantwortlich sind.
4. Überprüfung der Daten, bevor man ihnen Glauben schenkt
Vor dem Modellieren wurden die Trainings- und Testdateien mit SHA-256 geprüft und Spalte für Spalte überprüft. Das war keine Bürokratie – mehrere Besonderheiten hätten zum Zeitpunkt der Vorhersage den Merkmalsraum verändern und die Ergebnisse beeinträchtigen können, ohne dass ein sichtbarer Fehler auftrat.
- Der Trainingsdatensatz enthielt 3.493 Zeilen und 34 Spalten, während der Testdatensatz 500 Zeilen und 33 Spalten hatte, da er kein Zielkriterium enthielt.
- Die Spalten
AmbienceundRestaurant Tierhatten im Trainings- und Testdatensatz unterschiedliche Datentypen, weshalb eine explizite Umwandlung erforderlich war, damit die Modelle identische Merkmale erhielten. - In der Trainingsdatenbank hieß eine Spalte
Endorsed By, im Testdatensatz hingegenEndoresed By. Ohne eine Zuordnung hätte der Prozess ein im Test völlig fehlendes Merkmal erzeugen können.
Registration Number fungierte als Identifikator und nicht als Geschäftsprädiktor (Pearson etwa -0,017, Spearman etwa -0,022). Er wurde aus den Modellen ausgeklammert und lediglich zum Zuordnen der Zeilen am Ende beibehalten.5. Erkundung der Daten
5.1 Ein stark rechtsschiefes Zielmerkmal
Der jährliche Umsatz wies einen starken rechten Schwanz auf. Das 90. Perzentil lag bei etwa ₹48 Mio., das 95. bei etwa ₹57 Mio., das 99. bei etwa ₹100 Mio. und der maximale Wert bei ₹400 Mio., wobei die Roh-Abweichung etwa 6,65 betrug. Eine log1p-Transformation machte die Verteilung nahezu symmetrisch, und es wurden die Rohwerte sowie die Werte nach den Yeo-Johnson-Verfahren verglichen; später wurde auch eine Variante pro Quadratfuß getestet. Aus geschäftlicher Sicht fallen die meisten Restaurants in einen recht engen Einnahmebereich, während eine kleine Gruppe sehr großer Betriebe bei Fehlern erhebliche finanzielle Auswirkungen verursacht.
Der rechte Schwanz war nicht nur optisch auffällig – er dominierte auch die Verluste. Die Fehleranalyse zeigte, dass eine kleine Anzahl der am schlechtesten vorhergesagten Restaurants einen großen Anteil am gesamten quadrierten Fehler ausmachte, wodurch die Diagnose des rechten Schwanzes zu einem zentralen Bestandteil der Bewertung wurde.
5.2 Für fehlende Daten sind mehr als eine Regel notwendig
Fehlende Werte wurden nicht als einheitliches Phänomen betrachtet. Einige spiegelten gewöhnliche Mängel in der Datenqualität wider, während andere die Geschäftsstruktur widerspiegelten – beispielsweise ein leeres Unterhaltungsrating, weil der Ort keine Unterhaltung anbietet. Eine einzige Ersatzregel für beide Fälle würde nützliche Informationen zerstören.
Gewöhnliches Fehlen von Werten, wie beispielsweise die Beliebtheit in sozialen Medien, die Gesamtbewertung, die Atmosphäre oder die Kategorie, wurde durch Ersatzwerte auf Basis von Trainingsdaten sowie explizite Indikatoren für fehlende Werte behandelt. Dadurch kann ein Modell zwischen einem geschätzten Wert und dem Faktor, dass nichts beobachtet wurde, unterscheiden. Strukturelles Fehlen von Werten, das Live-Musik, Comedy-Auftritte, günstige Angebote und Live-Sportveranstaltungen umfasst, wurde durch Verfügbarkeitsflaggen wie has_live_music und has_comedy dargestellt. Ein leeres Rating kann bedeuten, dass der Service nicht existiert – was einen anderen Geschäftsstatus darstellt als ein unbekanntes Rating.
ORDINARY_MISSINGNESS_FLAGS = {
"facebook_popularity_quotient": "facebook_missing",
"instagram_popularity_quotient": "instagram_missing",
"overall_restaurant_rating": "overall_rating_missing",
"ambience": "ambience_missing",
"restaurant_tier": "tier_missing",
}
STRUCTURAL_AVAILABILITY_FLAGS = {
"live_music_rating": "has_live_music",
"comedy_gigs_rating": "has_comedy",
"value_deals_rating": "has_value_deals",
"live_sports_rating": "has_live_sports",
}
def add_missingness_features(df: pd.DataFrame) -> pd.DataFrame:
"""Add ordinary-null and structural-availability indicators before imputation."""
required_columns = set(ORDINARY_MISSINGNESS_FLAGS) | set(
STRUCTURAL_AVAILABILITY_FLAGS
)
missing_columns = sorted(required_columns - set(df.columns))
if missing_columns:
raise KeyError(f"Missing required columns for missingness features: {missing_columns}")
df = df.copy()
for column, flag in ORDINARY_MISSINGNESS_FLAGS.items():
df[flag] = df[column].isna().astype("int8")
for column, flag in STRUCTURAL_AVAILABILITY_FLAGS.items():
df[flag] = df[column].notna().astype("int8")
return df
5.3 Kategorische Unordnung: 297 Schreibweisen von Städten
In der Spalte „Stadt“ befanden sich 297 rohe Schreibweisen für eine weitaus kleinere Anzahl tatsächlicher Märkte. Groß-/Kleinschreibung sowie Leerzeichen variierten, es traten Tippfehler und Alias-Namen auf, und einige Einträge waren zusammengesetzte Zeichenfolgen, die mehrere Städte bezeichneten. Eine reproduzierbare Aufbereitung kombinierte eine Überprüfung mittels Fuzzy-Matching mit Rapidfuzz, ein sorgfältig zusammengestelltes Alias-Wörterbuch sowie eine explizite Analyse der zusammengesetzten Einträge. Flaggen für seltene Städte sowie Merkmale zur Häufigkeit der Städte blieben erhalten, da sowohl die Marktgröße als auch die Datenqualität die Schätzungen des Umsatzes beeinflussen können. Der Abgleichsschritt schlägt lediglich Kandidaten für die menschliche Überprüfung vor und ändert niemals die Werte selbst:
def generate_city_candidate_matches(
city: pd.Series,
minimum_similarity: float = 80.0,
) -> pd.DataFrame:
"""Return nearest fuzzy matches for review; this never changes city values."""
normalized = normalize_city_values(city, aliases={})
counts = normalized.value_counts()
names = [name for name in counts.index if name != "unknown"]
rows = []
for raw_city in names:
candidates = [candidate for candidate in names if candidate != raw_city]
match = process.extractOne(raw_city, candidates, scorer=fuzz.ratio)
if match is not None and match[1] >= minimum_similarity:
rows.append(
{
"Raw City": raw_city,
"Candidate": match[0],
"Similarity": float(match[1]),
"Count": int(counts[raw_city]),
}
)
return pd.DataFrame(
rows,
columns=["Raw City", "Candidate", "Similarity", "Count"],
).sort_values(["Similarity", "Count", "Raw City"], ascending=[False, False, True])
5.4 Paare von Variablen
Sowohl der Restauranttyp als auch die Stadtklasse trennten den Umsatz auf sinnvolle Weise voneinander. Der Formattyp bestimmt durchschnittliche Ticketpreise, Durchsatz und Betriebsmodell, während die Stadtklasse eine grobe Indikation für Nachfragedichte, Kaufkraft und Marktgröße darstellt. Die einzelnen Bewertungen waren nützlich, aber stark von Störungen beeinflusst. Essen, Hygiene, Preis-Leistungs-Verhältnis, Service und Atmosphäre sind keine unabhängigen Faktoren. Sie wirken sich auf Format, Alter, Markt, soziale Sichtbarkeit und Positionierung aus, was sowohl nicht-lineare Lernalgorithmen als auch sorgfältig kontrollierte Interaktionsmerkmale erforderlich machte.
5.5 Das größere Bild
Die Pearson- und Spearman-Korrelationen zeigten unterschiedliche Ergebnisse. Mehrere Prädiktoren wiesen eine viel stärkere monotone als lineare Beziehung zum Personalwechsel auf, sodass auch dort nützliche Signale vorhanden waren, wo eine Geradlinenanpassung unzureichend war. Es wurden Paarweise-Unterstützungsprüfungen hinzugefügt, damit extreme Korrelationen, die auf sehr wenigen überschneidenden Beobachtungen beruhten, nicht fälschlicherweise als stabile Struktur angesehen wurden.
5.6 zeitbasierte Merkmale
Die Eröffnungsdaten wurden in kalendrische Komponenten sowie Altersmerkmale aufgeteilt. Ältere Restaurants neigten dazu, mehr Umsatz zu erzielen, doch dies wurde eher als Zusammenhang denn als kausale Behauptung betrachtet, da das Alter möglicherweise ein Indikator für Markenbekanntheit, überlebende Standorte, wiederkehrende Nachfrage oder angesammelte Größe sein kann. Ein fester Referenzdatum sorgte dafür, dass die Merkmale reproduzierbar blieben.
6. Ein Ungleichgewichtsproblem in einer Regressionsaufgabe
Bei der Regression gibt es im Sinne der Klassifizierung kein Ungleichgewicht der Klassen, doch dieses Ziel hatte ein Äquivalent: Der obere Wertebereich war selten und kostspielig. Unter Berücksichtigung des RMSE können einige besonders große Residuen den Gesamtwert der quadrierten Fehler dominieren. In den Diagnosen machten die schlechtesten Beobachtungen einen unverhältnismäßig großen Anteil aus, sodass ein Modell bei den meisten Restaurants gut abschneiden konnte und dennoch schlechte Ergebnisse erzielte, wenn es einige besonders hohe Umsätze übersah.
Deshalb wurde für die Validierung eine auf das Ziel strukturierte 5-fache Kreuzvalidierung verwendet. Das kontinuierliche Ziel wurde lediglich in 10 Quantilintervalle unterteilt, um die Validierungsgruppen zuzuweisen, während die Modelle weiterhin anhand des Rohumsatzes trainiert und bewertet wurden. Die abschließenden Prüfungen zur Auswahl des Gewinners wurden mit unabhängigen Startwerten (17, 42 und 101) wiederholt. Dadurch wurde die Wahrscheinlichkeit verringert, dass ein Kandidat einfach nur deshalb Champion wird, weil eine glückliche Aufteilung die extremen Werte in leichtere Validierungsgruppen brachte.
7. Feature-Engineering
Die Features wurden in stateless Transformationen und angepasste Statistiken unterteilt. Die stateless Logik funktionierte auf Train- und Testdaten identisch. Alles, was angepasst wurde – sei es Medianwerte, Häufigkeitskarten oder Kodierungen – wurde ausschließlich aus den Trainingsdaten gelernt, versioniert und bei der Inferenz wiederverwendet. Dadurch blieb der Prozess überprüfbar und es wurde vor subtilen Train/Test-Drifts durch die Vorkonvertierung geschützt.
- Fehler- und Verfügbarkeitsflaggen, wie oben beschrieben.
- Kalenderfeatures aus dem Startdatum (Jahr, Monat, Quartal, Wochentag), zusätzlich Alter in Monaten und Jahren sowie eine Kategorie „Startzeitraum“ mit vier Unterkategorien.
- Eine Anzahl der Küchen, die durch das Parsen der mit Kommas getrennten Liste der Küchen ermittelt wird.
- Eine Stadthierarchie von roher Stadt über gereinigte Stadt bis hin zur Hauptstadt und zum Bundesstaat, inklusive Flaggen für zusammengesetzte Einträge, Regionen und Aliasnamen sowie Indikatoren für Häufigkeit und seltene Städte bei drei Schwellenwerten.
Der Hauptpipeline verwendete 71 Prädiktoren. Zusätzlich entwickelte Modelle wurden durch Ablationstests getestet, anstatt automatisch beibehalten zu werden. Ein Merkmal erhielt seinen Platz durch eine verbesserte Validierung, nicht aufgrund intuitiver Attraktivität.
8. Einbeziehung externer Referenzdaten
Eine weitere Forschungsgruppe kombinierte die bereinigten Daten zu Städten und Bundesstaaten mit öffentlichen geografischen und wirtschaftlichen Referenzdaten. Es ging darum zu klären, ob die lokale Marktkapazität, Urbanisierungsgrad, Tourismusintensität sowie die Bevölkerungszahl zusätzliche Informationen liefern, die über die Eigenschaften des Restaurants selbst hinausgehen.
- Pro Stadt: Bevölkerungszahl, Anzahl und Größe der Haushalte, logarithmische Bevölkerungszahl, Koordinaten sowie Ja/Nein-Indikatoren für Metropolen, die Hauptstadtregion, Küstenstädte, Landeshauptstädte und Städte mit mehr als einer Million Einwohnern.
- Bundesstaatsebene: Bevölkerungszahl, städtische Bevölkerung und ihr Anteil, Bevölkerungsdichte, Pro-Kopf-NSDP-, GSDP- und NSDP-Werte sowie Inlands- und Auslandstouristenbesuche, ergänzt durch einen berechneten Wert für den Tourismus pro Kopf.
Das Ergebnis war absichtlich konservativ. Mehrere externe Merkmale zeigten in isolierten Tests Signale, doch die Familie verbesserte sich im Rahmen des vollständigen Substitutions- und Promotionsprotokolls nicht im Endensemble, weshalb sie niemals zu den „clean champions“ gehörte. Das ist eine nützliche Lektion für das Geschäftsmodellieren: Eine Variable kann für sich genommen plausibel und vorhersagend sein, doch ihre Bedeutung nimmt ab, sobald stärkere, korrelierte Signale vorhanden sind.
9. Modelle und warum
Die Daten enthielten kontinuierliche Bewertungen, spärliche sowie strukturelle Lücken sowie kategoriale Merkmale mit hoher Kardinalität, weshalb mehrere Modellfamilien als Hypothesen miteinander konkurrierten, anstatt davon auszugehen, dass ein Algorithmus dominieren würde. Ridge und ElasticNet erwiesen sich überraschenderweise als konkurrenzfähig mit CatBoost. Das war kein Rückschlag für die Boosting-Methoden – es zeigte vielmehr, wie viel Struktur in die künstlich erzeugten Merkmale eingeflossen war und wie nützlich Regularisierung ist, wenn viele miteinander verbundene Prädiktoren vorliegen. Baummodelle blieben weiterhin wichtig, da sie nicht-lineare Wechselwirkungen erfassen konnten, die die linearen Modelle nicht direkt darstellen können.
10. Anpassung
Während der Entwicklung erhielt jede Familie ihre eigene Optuna-Studie, die anhand des RMSE außerhalb der Trainingsdaten aus wiederholten, nach dem Zielwert stratifizierten Teilgruppen bewertet wurde. Die Identifizierung von Kandidaten sowie ihre Förderung beruhten auf interner Validierung und nicht auf der Leaderboard-Liste – ein Unterschied, der wichtig ist, da die spätere Phase der Wettbewerbsoptimierung nicht mit der Generalisierbarkeit verwechselt werden sollte.
Die stärkste Kombination setzte CatBoost, Ridge, ElasticNet sowie eine varianzstabilierte Variante von Ridge mit Quadratwurzel (WR01) in Anteilen von etwa 30 %, 10 %, 6 % und 54 % ein. Der wiederholte RMSE außerhalb der Trainingsdaten lag bei rund 20,10 M. Der hohe Anteil von WR01 zeigte deutlich: Das transformierte lineare Modell war zwar nicht das beste eigenständige Lernalgorithmus, doch seine andere Fehlergeometrie machte es innerhalb des Ensembles äußerst wertvoll.
11. Was half und was nicht
Jedes Experiment wurde mit seiner Funktionsfamilie, Modellkonfiguration, Validierungsdesign, OOF-Vorhersagen sowie Entscheidung zur Verwendung erfasst. Der Fortschritt entstand dadurch, dass schwache Ideen genauso entschieden abgelehnt wurden wie starke Ideen gefördert.
- Zunächst wurde eine Basislinie festgelegt. Die mittleren und medianen Prädiktoren legten einen RMSE-Bodenwert von etwa 21,6 bis 21,7 M fest, wodurch Modelle, die lediglich kompliziert waren, leicht erkannt werden konnten.
- Die Regularisierung war wichtig. Ridge- und ElasticNet-Methoden übertrafen das einfache OLS deutlich, da der konstruierte Raum korrelierte Signale und Wechselwirkungen enthielt.
- CatBoost brachte nichtlineare sowie kategoriale Vorteile mit sich, doch es wurde nicht angenommen, dass die Feinabstimmung hilfreich sei. Ein vielversprechendes CatBoost-Ergebnis verbesserte die Optimierungsergebnisse um etwa 258 K, scheiterte jedoch bei der Bestätigung mit unabhängigen Datensätzen und wurde aufgrund von Überanpassung verworfen.
12. Ergebnisse für die verschiedenen Champions
Das Projekt erzeugte zwei Arten von Champions: ein sauberes Modell, das durch wiederholte Kreuzvalidierung ausgewählt wurde, sowie einen Wettbewerbs-Champion, der an die Bewertungsumgebung angepasst war. Ein dritter Checkpoint, S08, zeigt, welche Wirkung eine absichtlich geringe Anpassung der Fehlerverteilung hat, noch vor den intensiveren Schritten speziell für den Wettbewerb. Diese Ergebnisse beantworten unterschiedliche Fragen und sind keine austauschbaren Maßstäbe für die Generalisierbarkeit.
Auf der Seite des Wettbewerbs verringerte die Einreichung LB05 den öffentlichen RMSE um ₹9.195.461,72 im Vergleich zu S08, das entspricht etwa 76,1 %. Das ist ein Ergebnis aus der Rangliste, kein Beweis dafür, dass das einsatzfähige Modell plötzlich bei einem RMSE von ₹2,89 Mio. generalisiert. Hier ist das Skript, das die gefrorenen Komponenten verwendet und die erste gemischte Einreichung erstellt:
from __future__ import annotations
from datetime import datetime, timezone
from pathlib import Path
import time
import json
import joblib
import numpy as np
import pandas as pd
from src import config, data, models, submission
from src.pipeline import FeaturePipeline
from main import _prepare_catboost_matrices, _prepare_model_matrices
S01_ID = "S01"
S01_WEIGHTS = {"catboost": 0.50, "ridge": 0.45, "elasticnet": 0.05}
def run(output_dir: Path | None = None) -> tuple[Path, Path]:
output_dir = output_dir or config.SUBMISSIONS_DIR
output_dir.mkdir(parents=True, exist_ok=True)
manifest_path = config.EXPERIMENTS_DIR / "frozen_v1_manifest.json"
if not manifest_path.exists():
raise FileNotFoundError(f"Frozen V1 manifest is required: {manifest_path}")
manifest = json.loads(manifest_path.read_text(encoding="utf-8"))
if manifest.get("feature_pipeline") != "processed_v1" or manifest.get("predictor_count") != 71:
raise ValueError("Frozen V1 manifest does not describe the processed_v1 71-predictor contract")
train_raw = data.load_train()
test_raw = data.load_test()
pipeline = FeaturePipeline().fit(train_raw)
train = pipeline.transform(train_raw)
test = pipeline.transform(test_raw)
target = config.INTERNAL_TARGET
X_train = train.drop(columns=[target, config.INTERNAL_ID_COL], errors="ignore")
X_test = test.drop(columns=[config.INTERNAL_ID_COL], errors="ignore")
y = train[target].astype(float).to_numpy()
X_numeric, X_test_numeric = _prepare_model_matrices(X_train, X_test)
X_cat, X_test_cat = _prepare_catboost_matrices(X_train, X_test)
predictions = {}
timings = {}
start = time.perf_counter()
catboost = models.get_catboost(**{
key: value for key, value in manifest["catboost_config"].items()
if key != "native_categorical"
})
cat_features = [
index for index, dtype in enumerate(X_cat.dtypes)
if str(dtype) in {"object", "string", "category"}
]
catboost.fit(X_cat, y, cat_features=cat_features, verbose=False)
predictions["catboost"] = catboost.predict(X_test_cat)
timings["catboost_seconds"] = time.perf_counter() - start
start = time.perf_counter()
ridge = models.get_ridge(**manifest["ridge_config"])
ridge.fit(X_numeric, y)
predictions["ridge"] = ridge.predict(X_test_numeric)
timings["ridge_seconds"] = time.perf_counter() - start
start = time.perf_counter()
elasticnet = models.get_elasticnet(**manifest["elasticnet_config"])
elasticnet.fit(X_numeric, y)
predictions["elasticnet"] = elasticnet.predict(X_test_numeric)
timings["elasticnet_seconds"] = time.perf_counter() - start
weights = manifest["ensemble_weights"]
blend_prediction = sum(weights[name] * predictions[name] for name in weights)
submission_df = submission.build_submission(test_raw, blend_prediction)
submission_path = output_dir / "S01_frozen_v1_blend.csv"
submission.write_submission(submission_df, submission_path, expected_rows=len(test_raw))
component_paths = {}
for name, prediction in predictions.items():
component_paths[name] = output_dir / f"S01_{name}_predictions.csv"
pd.DataFrame({config.ID_COL: test_raw[config.ID_COL].to_numpy(), config.TARGET: prediction}).to_csv(
component_paths[name], index=False
)
metadata = {
"submission_id": S01_ID,
"model": "Frozen V1 Blend",
"feature_version": "frozen production",
"oof_rmse": 20097389.205713525,
"catboost_weight": S01_WEIGHTS["catboost"],
"ridge_weight": S01_WEIGHTS["ridge"],
"elasticnet_weight": S01_WEIGHTS["elasticnet"],
"public_leaderboard_rmse": None,
"leaderboard_rank": None,
"timestamp_utc": datetime.now(timezone.utc).isoformat(timespec="seconds"),
"submission_file": str(submission_path),
"component_files": {name: str(path) for name, path in component_paths.items()},
"fit_method": "fit_each_frozen_component_on_all_3493_processed_v1_training_rows",
"catboost_production_iterations_rule": "fixed_manifest_value_no_early_stopping",
"manifest_path": str(manifest_path),
**timings,
}
metadata_path = output_dir / "S01_metadata.json"
metadata_path.write_text(json.dumps(metadata, indent=2), encoding="utf-8")
return submission_path, metadata_path
if __name__ == "__main__":
for path in run():
print(path)
13. Leistung im Wettbewerb versus Generalisierung
Sobald das saubere Modell sowie sein Validierungsprotokoll festgelegt waren, verbesserte eine separate Optimierungsschicht für den Wettbewerb die Vorhersagen für den spezifischen, ausgeschlossenen Bewertungssatz. Die dahinterstehenden Mechanismen fallen hier nicht in den Betrachtungsbereich. Methodisch wichtig ist, dass diese Schicht Informationen aus der Wettbewerbsumgebung nutzte und nicht aus neu beschrifteten Trainingsdaten.
Daher sollte der endgültige RMSE-Wert auf der Leaderboard-Liste von etwa ₹2,893 Mio. als spezifisch für den Wettbewerb und potenziell überangepasst an diesen Bewertungssatz angesehen werden. Eine faire Schätzung dafür, wie gut das zugrunde liegende Modell bei neuen Restaurants abschneiden würde, bleibt weiterhin das Ergebnis der wiederholten CV-Testung von rund ₹20,10 Mio. Es ist sowohl für wissenschaftliche als auch für geschäftliche Interpretationen unerlässlich, diese beiden Werte voneinander zu trennen.
In einer echten Vorhersagemigration würde die für Wettbewerbe optimierte Schicht weggelassen werden, es sei denn, ein äquivalenter Kalibrierungssignal wäre im Voraus verfügbar. Das einsetzbare System würde den festgelegten Feature-Pipeline- und Ensemble-Ablauf verwenden, Residuen sowie Veränderungen in der Verteilung überwachen und bei tatsächlichem Umsatzwechsel neu trainieren.
14. Annahmen
- Es wurde angenommen, dass die Trainings- und Testdaten aus ausreichend ähnlichen Geschäftsprozessen stammen, damit die Kreuzvalidierung sinnvoll ist. Es fand keine formelle Korrektur aufgrund von Kovariatenverschiebungen statt, außer bei Überprüfungen des Schemas und der Verteilung.
- Die Registrierungsnummer diente ausschließlich als Identifikator und wurde aus dem sauberen Modell ausgeklammert, da sie keinen nachvollziehbaren Zusammenhang mit dem Umsatz aufwies.
- Fehlende Bewertungen von Einrichtungen wurden als Träger struktureller Informationen angesehen, wenn der entsprechende Service nicht vorhanden war – dies prägte die Gestaltung der Verfügbarkeitsflagge.
15. Empfehlungen
- Betrachten Sie die Datenqualität als Bestandteil der Modellleistung. Schema-Unterschiede, fragmentierte Stadtnamen sowie inkonsistente kategoriale Werte können das Signal bereits vor dem Erkennen durch den Algorithmus zerstören.
- Trennen Sie „fehlend wegen Unbekanntheit“ von „fehlend, weil die entsprechende Eigenschaft nicht vorhanden ist“. Letzteres ist oft ein Vorteil und kein Problem.
- Bei verzerrten Umsatzzielen sollten der RMSE der Randwerte sowie die Konzentration der quadrierten Fehler und die Gesamtsumme überwacht werden. Einige große Kunden können darüber entscheiden, ob eine Prognose finanziell nützlich ist.
- Verwenden Sie explizite Promotionsmechanismen: Suche nach Kandidaten, Bestätigung durch unabhängige Beispiele sowie Ersetzung durch Ensemble-Lösungen. Dadurch verhindert man, dass ein Vorteil, der bei einer bestimmten Aufteilung entsteht, fälschlicherweise als bester Ergebnis angesehen wird.
- Betrachten Sie agierende KI als Betriebsmodell und nicht als Lizenz für autonome Promotionen. Lassen Sie die Agenten Vorschläge machen, Modelle entwickeln und diese in Frage stellen, während wichtige Entscheidungen bezüglich Modelle und Produkte weiterhin von Menschen getroffen werden.
- Speichern Sie die echten Daten an einem Ort außerhalb des Chat-Fensters. Datensätze, Manifeste, OOF-Prognosen, Experimentprotokolle, festgelegte „Champions“ sowie die Entscheidungen zur Promotion sollten alle gespeichert werden, damit ein neuer Agent auf den vorhandenen Beweisen aufbauen kann und nicht auf einer Liste früherer Nachrichten.
- Unterscheiden Sie bei Experimenten mit ML zwischen DEV, UAT und PROD. Freie Kreativität findet in DEV statt, während Replikationen und kritische Überprüfungen in UAT durchgeführt werden. PROD hingegen sollte deterministisch, unveränderlich und rückgängig machbar sein.
16. Lektionen
Die wichtigste Lektion ist nicht, dass ein Algorithmus gewonnen hat. Eine disziplinierte Problemanalyse, Datenaudits, validierungssensible Prüfverfahren, Feature-Engineering, vielfältige Residuen sowie ein kontrollierter Arbeitsablauf führten zu einem besseren Prognosesystem als allein die Modellkomplexität. Mit DontPanic arbeiteten die Agenten als Team zusammen, was Entdeckung und Implementierung beschleunigte, während explizite menschliche Kontrollpunkte verhinderten, dass Geschwindigkeit das Urteilsvermögen ersetzt.
Der letztendliche Wettbewerbschampion erreichte nach einer auf die Bewertungsumgebung abgestimmten Optimierungsphase einen RMSE von etwa 2,893 Mio. ₹. Dieser Wert entschied den Wettbewerb, wird jedoch getrennt aufgeführt, da er möglicherweise überangepasst ist und daher nicht als erwarteter Fehler für zukünftige Restaurants angesehen werden sollte.
Die wichtigere Lektion ist eine praktische: Führen Sie vor jeder Anpassung eine Validierung durch, behalten Sie fehlgeschlagene Experimente bei, fördern Sie Erträge nur dann, wenn sie sich wiederholen, und verwechseln Sie niemals ein Modell, das einen Benchmark anführt, mit einem, dem man in der Produktion vertrauen könnte. Wenn mehrere KI-Agenten beteiligt sind, lassen Sie sie Volleyball spielen: Erlauben Sie ihnen, aggressiv zu arbeiten, stellen Sie aber sicher, dass wichtige Entscheidungen vor dem Erzielen eines Punktes an einen Menschen weitergeleitet werden.