File size: 4,246 Bytes
8d6422d
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
# 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()