Spaces:
Sleeping
Sleeping
File size: 7,971 Bytes
b42db06 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 | # train_model.py (UPDATED TO USE USER DATA)
import pandas as pd
import numpy as np
from sklearn.model_selection import train_test_split
import xgboost as xgb
from sklearn.metrics import mean_absolute_error, mean_squared_error, r2_score
import joblib
import sys
import sqlite3
import json
import os
from datetime import datetime
print("--- Starting model training process with user data ---")
# --- Step 1: Load User Data from Database ---
try:
os.makedirs('db', exist_ok=True)
conn = sqlite3.connect('db/training_data.db')
cursor = conn.cursor()
# Get user training data
cursor.execute('''
SELECT route_id, addresses, coordinates, predicted_eta_minutes,
actual_eta_minutes, start_time, sensor_data, route_metadata
FROM training_data
WHERE actual_eta_minutes IS NOT NULL
''')
user_data = cursor.fetchall()
conn.close()
print(f"β
Step 1a: Found {len(user_data)} user training records")
if len(user_data) < 10:
print("β οΈ Warning: Less than 10 user records found. Using fallback dataset.")
# Fallback to original dataset
try:
dataset_path = os.path.join(os.path.dirname(__file__), 'dataset', 'delhivery_data.csv')
df = pd.read_csv(dataset_path)
print(f"β
Step 1b: Successfully loaded fallback 'delhivery_data.csv'. Shape: {df.shape}")
except FileNotFoundError:
print("β FATAL ERROR: No user data and fallback dataset not found.")
sys.exit()
else:
# Convert user data to DataFrame
user_records = []
for record in user_data:
route_id, addresses, coordinates, predicted_eta, actual_eta, start_time, sensor_data, route_metadata = record
try:
# Parse JSON data
addresses_list = json.loads(addresses)
coordinates_list = json.loads(coordinates)
route_meta = json.loads(route_metadata)
# Calculate features
num_stops = len(addresses_list)
total_distance = route_meta.get('total_distance_km', 0)
ors_duration = route_meta.get('ors_duration_minutes', predicted_eta)
user_records.append({
'actual_duration_minutes': actual_eta,
'ors_duration_minutes': ors_duration,
'total_distance_km': total_distance,
'num_stops': num_stops,
'start_time': start_time,
'route_id': route_id
})
except (json.JSONDecodeError, KeyError) as e:
print(f"β οΈ Skipping invalid record {route_id}: {e}")
continue
df = pd.DataFrame(user_records)
print(f"β
Step 1b: Created user data DataFrame. Shape: {df.shape}")
# If we have user data, also try to load fallback for more training data
try:
fallback_df = pd.read_csv('/home/tejas/Projects/Delivery_route_optimize/delivery-route-app/backend/dataset/delhivery_data.csv')
print(f"β
Step 1c: Also loaded fallback dataset. Shape: {fallback_df.shape}")
# Combine datasets
df = pd.concat([df, fallback_df], ignore_index=True)
print(f"β
Step 1d: Combined datasets. Final shape: {df.shape}")
except FileNotFoundError:
print("βΉοΈ No fallback dataset found, using only user data")
except Exception as e:
print(f"β Error loading user data: {e}")
print("Falling back to original dataset...")
try:
df = pd.read_csv('/home/tejas/Projects/Delivery_route_optimize/delivery-route-app/backend/dataset/delhivery_data.csv')
print(f"β
Step 1a: Successfully loaded fallback 'delhivery_data.csv'. Shape: {df.shape}")
except FileNotFoundError:
print("β FATAL ERROR: No data available for training.")
sys.exit()
# --- Step 2: Clean and Map Columns ---
# Check if we have user data format or fallback format
if 'actual_duration_minutes' in df.columns:
# User data format
required_columns = ['actual_duration_minutes', 'ors_duration_minutes', 'total_distance_km', 'num_stops', 'start_time']
print("β
Step 2a: Using user data format")
else:
# Fallback format
required_columns = ['actual_time', 'osrm_time', 'osrm_distance', 'od_start_time']
print("β
Step 2a: Using fallback data format")
if not all(col in df.columns for col in required_columns):
print(f"β FATAL ERROR: One or more required columns are missing. Found: {df.columns.tolist()}")
sys.exit()
initial_rows = len(df)
df = df[required_columns].dropna()
print(f"β
Step 2b: Dropped rows with missing values. Rows before: {initial_rows}, Rows after: {len(df)}")
if len(df) == 0:
print("β FATAL ERROR: No valid data remaining after removing missing values.")
sys.exit()
# Rename columns if using fallback format
if 'actual_time' in df.columns:
df = df.rename(columns={
'actual_time': 'actual_duration_minutes',
'osrm_time': 'ors_duration_minutes',
'osrm_distance': 'total_distance_km',
'od_start_time': 'start_time'
})
df['num_stops'] = np.random.randint(2, 8, df.shape[0])
initial_rows = len(df)
df = df[(df['actual_duration_minutes'] > 0) & (df['ors_duration_minutes'] > 0) & (df['total_distance_km'] > 0)]
print(f"β
Step 2c: Performed final data cleaning. Rows before: {initial_rows}, Rows after: {len(df)}")
if len(df) == 0:
print("β FATAL ERROR: No valid data remaining after final cleaning.")
sys.exit()
# --- Step 3: Feature Engineering ---
# --- THIS IS THE CRUCIAL FIX ---
# Add errors='coerce' to turn any bad dates into NaT (Not a Time)
initial_rows = len(df)
df['start_time'] = pd.to_datetime(df['start_time'], errors='coerce')
# Now, drop any rows that had a bad date
df = df.dropna(subset=['start_time'])
print(f"β
Step 3a: Converted dates, ignoring errors. Rows before: {initial_rows}, Rows after: {len(df)}")
# -----------------------------
df['hour'] = df['start_time'].dt.hour
def get_time_of_day(hour):
if 6 <= hour < 11: return 'Morning_Rush'
elif 11 <= hour < 17: return 'Midday'
elif 17 <= hour < 21: return 'Evening_Rush'
else: return 'Night'
df['time_of_day'] = df['hour'].apply(get_time_of_day)
df['day_of_week'] = df['start_time'].dt.dayofweek.apply(lambda x: 'Weekday' if x < 5 else 'Weekend')
print("β
Step 3b: Feature engineering complete.")
# --- Step 4: Prepare Data for XGBoost ---
features = ['ors_duration_minutes', 'total_distance_km', 'num_stops', 'time_of_day', 'day_of_week']
target = 'actual_duration_minutes'
X = df[features]
y = df[target]
X = pd.get_dummies(X, columns=['time_of_day', 'day_of_week'])
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)
print(f"β
Step 4: Data split into training and testing sets. Training samples: {len(X_train)}")
# --- Step 5: Train the Model ---
model = xgb.XGBRegressor(objective='reg:squarederror', n_estimators=100, learning_rate=0.1, max_depth=5, random_state=42, n_jobs=-1)
print("\nβ³ Step 5: Training the XGBoost model...")
model.fit(X_train, y_train)
print("β
Model training complete!")
# --- Step 6: Evaluate and Save ---
predictions = model.predict(X_test)
mae = mean_absolute_error(y_test, predictions)
mse = mean_squared_error(y_test, predictions)
rmse = np.sqrt(mse)
r2 = r2_score(y_test, predictions)
print(f"\nπ Step 6: Model Evaluation Metrics:")
print(f" - Mean Absolute Error (MAE): {mae:.2f} minutes")
print(f" - Root Mean Sq. Error (RMSE): {rmse:.2f} minutes")
print(f" - R-Squared Score (RΒ²): {r2:.4f}")
joblib.dump(model, 'eta_prediction_model.pkl')
joblib.dump(list(X_train.columns), 'model_columns.pkl')
print("β
Model and columns saved successfully.")
print("\n--- Script finished successfully! ---") |