File size: 6,331 Bytes
12631d6
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
#!/usr/bin/env python3
"""KrishokChat ML Models Automated Verification Suite.

Validates:
1. Every YOLO26 PyTorch (.pt) vision classification model loads and performs inference.
2. Every ONNX model loads and runs a test session via onnxruntime.
3. Output probability dimensions strictly match class_names.json.
4. All 5 folds of the EfficientNet-B0 soil moisture regression model load and predict.
5. Prints a comprehensive diagnostic table for academic reviewers.
"""

import sys
import time
import json
from pathlib import Path
import numpy as np
from PIL import Image

try:
    import torch
    from ultralytics import YOLO
except ImportError as e:
    print(f"Error: Missing core dependencies. Run: pip install -r requirements.txt\n{e}")
    sys.exit(1)

try:
    import onnxruntime as ort
except ImportError:
    ort = None

BASE_DIR = Path(__file__).resolve().parent

VISION_MODELS = [
    ("01_crop_classifier", "crop_classifier.pt", "crop_classifier.onnx", 10),
    ("02_potato_disease", "potato_disease.pt", "potato_disease.onnx", 3),
    ("03_rice_disease", "rice_disease.pt", "rice_disease.onnx", 10),
    ("04_wheat_disease", "wheat_disease.pt", "wheat_disease.onnx", 11),
    ("05_corn_disease", "corn_disease.pt", "corn_disease.onnx", 4),
    ("06_chilli_disease", "chilli_disease.pt", "chilli_disease.onnx", 8),
    ("07_brassica_disease", "brassica_disease.pt", "brassica_disease.onnx", 11),
]


def run_vision_verification():
    print("=" * 80)
    print(" KRISHOKCHAT ML SUITE — VISION MODELS (YOLO26-CLS) VERIFICATION")
    print("=" * 80)
    header = f"{'Folder / Task':<25} | {'PT File':<20} | {'Classes':<7} | {'PT Status':<10} | {'ONNX Status':<11} | {'Latency':<8}"
    print(header)
    print("-" * 80)

    # Base test image
    dummy_img = Image.fromarray(np.uint8(np.random.rand(224, 224, 3) * 255))

    all_passed = True

    for folder, pt_name, onnx_name, expected_classes in VISION_MODELS:
        model_dir = BASE_DIR / folder
        pt_path = model_dir / pt_name
        onnx_path = model_dir / onnx_name
        classes_path = model_dir / "class_names.json"

        # Check files exist
        if not pt_path.exists():
            print(f"{folder:<25} | {pt_name:<20} | {expected_classes:<7} | FAILED (missing)")
            all_passed = False
            continue

        # Check class names
        with open(classes_path, encoding="utf-8") as f:
            classes_data = json.load(f)
            num_classes = len(classes_data)
            assert num_classes == expected_classes, f"Class count mismatch: {num_classes} != {expected_classes}"

        # 1. PyTorch YOLO26-cls test
        pt_status = "FAIL"
        elapsed = 0.0
        try:
            t0 = time.time()
            model = YOLO(str(pt_path))
            res = model.predict(dummy_img, imgsz=224, verbose=False)[0]
            elapsed = (time.time() - t0) * 1000.0
            probs = res.probs.data.cpu().numpy()
            if len(probs) == expected_classes and np.isclose(np.sum(probs), 1.0, atol=1e-2):
                pt_status = "PASSED"
            else:
                pt_status = f"ERR(shape {len(probs)})"
                all_passed = False
        except Exception as ex:
            pt_status = f"EXC: {type(ex).__name__}"
            all_passed = False

        # 2. ONNX test
        onnx_status = "SKIP"
        if ort is not None and onnx_path.exists():
            try:
                session = ort.InferenceSession(str(onnx_path))
                input_tensor = session.get_inputs()[0]
                shape = input_tensor.shape
                # Adapt resolution if static (e.g. 224 vs 256)
                h = shape[2] if len(shape) > 2 and isinstance(shape[2], int) else 224
                w = shape[3] if len(shape) > 3 and isinstance(shape[3], int) else 224
                onnx_np = np.array(dummy_img.resize((w, h)), dtype=np.float32).transpose(2, 0, 1)[None, ...] / 255.0

                outputs = session.run(None, {input_tensor.name: onnx_np})[0]
                if outputs.shape[-1] == expected_classes:
                    onnx_status = "PASSED"
                else:
                    onnx_status = f"ERR({outputs.shape})"
                    all_passed = False
            except Exception as ex:
                onnx_status = f"EXC: {type(ex).__name__}"
                all_passed = False
        elif not onnx_path.exists():
            onnx_status = "MISSING"

        print(f"{folder:<25} | {pt_name:<20} | {num_classes:<7} | {pt_status:<10} | {onnx_status:<11} | {elapsed:.1f} ms")

    return all_passed


def run_soil_verification():
    print("\n" + "=" * 80)
    print(" KRISHOKCHAT ML SUITE — SOIL MOISTURE REGRESSION (EFFICIENTNET-B0) VERIFICATION")
    print("=" * 80)

    soil_dir = BASE_DIR / "08_soil_moisture"
    sys.path.insert(0, str(soil_dir))

    try:
        from model_def import load_soil_model
    except ImportError as e:
        print(f"Soil model def import failed: {e}")
        return False

    all_passed = True
    dummy_tensor = torch.randn(1, 3, 224, 224)

    for fold in range(5):
        fold_file = soil_dir / f"effnetb0_fold{fold}.pt"
        if not fold_file.exists():
            print(f"Fold {fold}: FAILED (file not found: {fold_file.name})")
            all_passed = False
            continue

        try:
            t0 = time.time()
            model = load_soil_model(fold_file, device="cpu")
            with torch.no_grad():
                pred = model(dummy_tensor).item()
            latency = (time.time() - t0) * 1000.0
            print(f"Fold {fold:<2} ({fold_file.name:<18}) | Output: {pred:7.2f} kPa | Status: PASSED | Latency: {latency:.1f} ms")
        except Exception as e:
            print(f"Fold {fold:<2} ({fold_file.name:<18}) | Status: FAILED ({e})")
            all_passed = False

    return all_passed


if __name__ == "__main__":
    v_ok = run_vision_verification()
    s_ok = run_soil_verification()

    print("\n" + "=" * 80)
    if v_ok and s_ok:
        print(" SUCCESS: ALL 8 MODEL CATEGORIES AND WEIGHTS VERIFIED CLEANLY!")
        print(" The 'backend_models/' folder is 100% ready for reviewers and Hugging Face release.")
        print("=" * 80)
        sys.exit(0)
    else:
        print(" WARNING: Some model checks failed or were skipped.")
        print("=" * 80)
        sys.exit(1)