agrisense-backend / Modules /Crop_Rec /evaluate_models.py
akashg7's picture
Intelligence: Ecological Crop Recommendation Pipeline
8d6422d
Raw
History Blame Contribute Delete
4.25 kB
# CropRecommendationSystem/evaluate_models.py
# ============================================================================
# Post-Training Evaluation & Comparison Suite
# ============================================================================
# Mirrors the price forecaster's evaluate_lightgbm.py
# Loads saved models and runs comprehensive evaluation on held-out data.
# ============================================================================
import os
import numpy as np
import pandas as pd
import joblib
from pathlib import Path
from sklearn.metrics import (
accuracy_score, f1_score, classification_report,
confusion_matrix
)
BASE_DIR = Path(__file__).resolve().parent
DATA_DIR = BASE_DIR / "data" / "processed"
MODEL_DIR = BASE_DIR.parent / "models"
EVAL_DIR = BASE_DIR / "outputs" / "eval"
def load_all():
"""Load models, scaler, encoders, and test data."""
df = pd.read_parquet(DATA_DIR / "crop_features_engineered.parquet")
encoders = joblib.load(DATA_DIR / "label_encoders.pkl")
crop_model = joblib.load(MODEL_DIR / "crop_recommendation_lgbm.pkl")
fert_model = joblib.load(MODEL_DIR / "fert_recommendation_lgbm.pkl")
scaler = joblib.load(MODEL_DIR / "crop_feature_scaler.pkl")
return df, encoders, crop_model, fert_model, scaler
def main():
print("=" * 70)
print("๐Ÿ“Š CROP RECOMMENDATION: EVALUATION SUITE")
print("=" * 70)
df, encoders, crop_model, fert_model, scaler = load_all()
crop_names = list(encoders['crop_type'].classes_)
fert_names = list(encoders['fertilizer'].classes_)
# Recreate features and test split (same seed as training)
drop_cols = [
'crop_type', 'fertilizer', 'soil_type',
'crop_type_encoded', 'fertilizer_encoded',
]
feature_cols = [c for c in df.columns if c not in drop_cols]
X = df[feature_cols].values
y_crop = df['crop_type_encoded'].values
y_fert = df['fertilizer_encoded'].values
from sklearn.model_selection import train_test_split
_, X_test, _, yc_test, _, yf_test = train_test_split(
X, y_crop, y_fert, test_size=0.20, random_state=42, stratify=y_crop
)
X_test_s = scaler.transform(X_test)
# --- Crop Evaluation ---
yc_pred = crop_model.predict(X_test_s)
print(f"\n{'='*60}")
print(" CROP TYPE MODEL โ€” Test Set Evaluation")
print(f"{'='*60}")
print(f" Accuracy: {accuracy_score(yc_test, yc_pred)*100:.2f}%")
print(f" F1 Macro: {f1_score(yc_test, yc_pred, average='macro'):.4f}")
print(classification_report(yc_test, yc_pred, target_names=crop_names, digits=3))
# --- Fertilizer Evaluation ---
yf_pred = fert_model.predict(X_test_s)
print(f"\n{'='*60}")
print(" FERTILIZER MODEL โ€” Test Set Evaluation")
print(f"{'='*60}")
print(f" Accuracy: {accuracy_score(yf_test, yf_pred)*100:.2f}%")
print(f" F1 Macro: {f1_score(yf_test, yf_pred, average='macro'):.4f}")
print(classification_report(yf_test, yf_pred, target_names=fert_names, digits=3))
# --- Confusion Matrix Export ---
EVAL_DIR.mkdir(parents=True, exist_ok=True)
cm_crop = confusion_matrix(yc_test, yc_pred)
cm_fert = confusion_matrix(yf_test, yf_pred)
pd.DataFrame(cm_crop, index=crop_names, columns=crop_names).to_csv(
EVAL_DIR / "confusion_matrix_crop.csv"
)
pd.DataFrame(cm_fert, index=fert_names, columns=fert_names).to_csv(
EVAL_DIR / "confusion_matrix_fertilizer.csv"
)
print(f"\n๐Ÿ’พ Confusion matrices saved to {EVAL_DIR}/")
# --- Per-Soil-Type Breakdown ---
print(f"\n{'='*60}")
print(" PER-SOIL-TYPE ACCURACY BREAKDOWN")
print(f"{'='*60}")
soil_encoded = X_test[:, feature_cols.index('soil_type_encoded')]
soil_le = encoders['soil_type']
for soil_idx in sorted(np.unique(soil_encoded)):
mask = soil_encoded == soil_idx
if mask.sum() > 0:
soil_name = soil_le.inverse_transform([int(soil_idx)])[0]
crop_acc = accuracy_score(yc_test[mask], yc_pred[mask]) * 100
fert_acc = accuracy_score(yf_test[mask], yf_pred[mask]) * 100
print(f" {soil_name:10s} โ†’ Crop: {crop_acc:5.1f}% | Fert: {fert_acc:5.1f}% (n={mask.sum()})")
if __name__ == "__main__":
main()