v2.0.3: fix: Stabilize DataProcessor core logic
- Korrektur der Spaltenzugriffe in `prepare_data_for_modeling` zur Behebung von `TypeError`. - Umbenennung von `reclassify_all_branches` zu `process_reclassify_branches` zur Behebung des Dispatcher-Fehlers. - Korrektur des `alignment_demo` Aufrufs, um auf die zentrale Helper-Funktion zu verweisen. - Härtung verschiedener Batch-Prozesse gegen fehlerhafte Daten und `None`-Werte.
This commit is contained in:
@@ -7,7 +7,7 @@ Enthält die Logik für die Verarbeitung einzelner Zeilen sowie die Steuerung
|
|||||||
verschiedener Batch-Modi und Dienstprogramme.
|
verschiedener Batch-Modi und Dienstprogramme.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
__version__ = "v2.0.1"
|
__version__ = "v2.0.3"
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
import time
|
import time
|
||||||
@@ -5527,7 +5527,7 @@ class DataProcessor:
|
|||||||
f"FEHLER: Spalte '{branche_col_internal}' (aus 'Chat Vorschlag Branche') nicht im DataFrame gefunden.")
|
f"FEHLER: Spalte '{branche_col_internal}' (aus 'Chat Vorschlag Branche') nicht im DataFrame gefunden.")
|
||||||
return None
|
return None
|
||||||
|
|
||||||
# Verwende die CRM Branche als Basis für die Gruppierung
|
# Verwende die CRM Branche als Basis für die Gruppierung
|
||||||
branche_col_internal = "branche_crm" # NEU: Wir verwenden die CRM-Branche als Feature
|
branche_col_internal = "branche_crm" # NEU: Wir verwenden die CRM-Branche als Feature
|
||||||
self.logger.info(
|
self.logger.info(
|
||||||
f"Verarbeite kategoriales Feature '{branche_col_internal}' und mappe es zu 'Branchen_Gruppe'...")
|
f"Verarbeite kategoriales Feature '{branche_col_internal}' und mappe es zu 'Branchen_Gruppe'...")
|
||||||
@@ -5659,72 +5659,26 @@ class DataProcessor:
|
|||||||
self.logger.info(
|
self.logger.info(
|
||||||
f"Daten gesplittet. Train Set: {len(X_train)} Zeilen, Test Set: {len(X_test)} Zeilen.")
|
f"Daten gesplittet. Train Set: {len(X_train)} Zeilen, Test Set: {len(X_test)} Zeilen.")
|
||||||
|
|
||||||
# 3. Imputation (Fehlende Werte ersetzen)
|
# 3. Pipeline-Definition (Imputation wird Teil der Pipeline)
|
||||||
imputer = SimpleImputer(strategy='median')
|
# Schritt 1: Imputer für fehlende Werte
|
||||||
self.logger.info(
|
# Schritt 2: SMOTE für Klassen-Balancierung
|
||||||
f"Fitte Imputer mit Strategie '{imputer.strategy}' auf Trainingsdaten...")
|
# Schritt 3: RandomForestClassifier als Modell
|
||||||
imputer.fit(X_train)
|
|
||||||
|
|
||||||
# Speichern Sie den Imputer (wird fuer Vorhersagen benoetigt).
|
|
||||||
self.imputer = imputer # Speichern Sie ihn in der Instanz
|
|
||||||
try:
|
|
||||||
imputer_dir = os.path.dirname(imputer_out)
|
|
||||||
if imputer_dir and not os.path.exists(imputer_dir):
|
|
||||||
os.makedirs(imputer_dir, exist_ok=True)
|
|
||||||
with open(imputer_out, 'wb') as f:
|
|
||||||
pickle.dump(imputer, f)
|
|
||||||
self.logger.info(
|
|
||||||
f"Imputer erfolgreich gespeichert in '{imputer_out}'.")
|
|
||||||
except Exception as e:
|
|
||||||
self.logger.error(
|
|
||||||
f"FEHLER beim Speichern des Imputers in '{imputer_out}': {e}")
|
|
||||||
self.logger.debug(traceback.format_exc())
|
|
||||||
# Training sollte hier nicht unbedingt abbrechen, aber ein Hinweis
|
|
||||||
# ist wichtig
|
|
||||||
|
|
||||||
X_train_imputed = imputer.transform(X_train)
|
|
||||||
X_test_imputed = imputer.transform(X_test)
|
|
||||||
X_train_imputed = pd.DataFrame(
|
|
||||||
X_train_imputed,
|
|
||||||
columns=feature_columns_ml) # feature_columns_ml verwenden
|
|
||||||
X_test_imputed = pd.DataFrame(
|
|
||||||
X_test_imputed,
|
|
||||||
columns=feature_columns_ml) # feature_columns_ml verwenden
|
|
||||||
|
|
||||||
# +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
|
|
||||||
# +++ ANPASSUNG HIER: GridSearchCV mit Pipeline für SMOTE & RandomForest +
|
|
||||||
# +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
|
|
||||||
# 4. Erstellen einer Pipeline und Definieren des Parameter-Grids
|
|
||||||
|
|
||||||
# Schritt 1: SMOTE für Klassen-Balancierung
|
|
||||||
# Schritt 2: RandomForestClassifier als Modell
|
|
||||||
pipeline = ImbPipeline([
|
pipeline = ImbPipeline([
|
||||||
|
('imputer', SimpleImputer(strategy='median')),
|
||||||
('smote', SMOTE(random_state=42)),
|
('smote', SMOTE(random_state=42)),
|
||||||
# class_weight nicht nötig bei SMOTE
|
|
||||||
('classifier', RandomForestClassifier(random_state=42, n_jobs=-1))
|
('classifier', RandomForestClassifier(random_state=42, n_jobs=-1))
|
||||||
])
|
])
|
||||||
|
|
||||||
# Definieren der Hyperparameter, die getestet werden sollen.
|
# 4. Hyperparameter-Grid für GridSearchCV definieren
|
||||||
# WICHTIG: Die Parameternamen müssen mit dem Namen des Schritts in der
|
# Die Parameternamen müssen mit dem Namen des Schritts in der Pipeline beginnen.
|
||||||
# Pipeline beginnen (z.B. 'classifier__...')
|
|
||||||
param_grid = {
|
param_grid = {
|
||||||
'classifier__n_estimators': [200, 300], # Anzahl der Bäume
|
'classifier__n_estimators': [200, 300],
|
||||||
# Maximale Tiefe der Bäume (None = unbegrenzt)
|
|
||||||
'classifier__max_depth': [10, 20, None],
|
'classifier__max_depth': [10, 20, None],
|
||||||
# Mindestanzahl Samples für einen Split
|
|
||||||
'classifier__min_samples_split': [2, 5],
|
'classifier__min_samples_split': [2, 5],
|
||||||
# Mindestanzahl Samples in einem Blatt
|
|
||||||
'classifier__min_samples_leaf': [1, 2]
|
'classifier__min_samples_leaf': [1, 2]
|
||||||
}
|
}
|
||||||
# HINWEIS: Dies sind 2 * 3 * 2 * 2 = 24 Kombinationen.
|
|
||||||
# Mit cv=3 (siehe unten) werden 24 * 3 = 72 Modelle trainiert. Dies kann dauern!
|
|
||||||
# Für einen schnellen Test können Sie die Anzahl der Optionen
|
|
||||||
# reduzieren.
|
|
||||||
|
|
||||||
# Initialisieren von GridSearchCV
|
# 5. GridSearchCV initialisieren
|
||||||
# cv=3 bedeutet 3-fache Kreuzvalidierung.
|
|
||||||
# scoring='accuracy' bedeutet, dass die beste Kombination anhand der Genauigkeit ausgewählt wird.
|
|
||||||
# verbose=2 gibt detaillierte Log-Ausgaben während der Suche.
|
|
||||||
grid_search = GridSearchCV(
|
grid_search = GridSearchCV(
|
||||||
estimator=pipeline,
|
estimator=pipeline,
|
||||||
param_grid=param_grid,
|
param_grid=param_grid,
|
||||||
@@ -5735,10 +5689,10 @@ class DataProcessor:
|
|||||||
|
|
||||||
self.logger.info("Starte Hyperparameter-Tuning mit GridSearchCV...")
|
self.logger.info("Starte Hyperparameter-Tuning mit GridSearchCV...")
|
||||||
start_fit_time = time.time()
|
start_fit_time = time.time()
|
||||||
# Fitte das Grid auf den (noch nicht resampleten) Trainingsdaten.
|
|
||||||
# Die Pipeline kümmert sich intern darum, dass SMOTE nur auf die
|
# 6. Grid auf den Original-Trainingsdaten fitten (ohne vorherige Imputation)
|
||||||
# Trainings-Folds angewendet wird.
|
# Die Pipeline kümmert sich intern um die korrekte Imputation und SMOTE für jeden Fold.
|
||||||
grid_search.fit(X_train_imputed, y_train)
|
grid_search.fit(X_train, y_train)
|
||||||
end_fit_time = time.time()
|
end_fit_time = time.time()
|
||||||
self.logger.info(
|
self.logger.info(
|
||||||
f"GridSearchCV-Suche abgeschlossen. Dauer: {end_fit_time - start_fit_time:.2f} Sekunden.")
|
f"GridSearchCV-Suche abgeschlossen. Dauer: {end_fit_time - start_fit_time:.2f} Sekunden.")
|
||||||
|
|||||||
Reference in New Issue
Block a user