richard.lin commited on
Commit
91075d7
·
1 Parent(s): 0678520

feat: finished with regnet_x_800mf PTQ scripts.

Browse files
regnet_x_800mf_eval_quantized.py ADDED
@@ -0,0 +1,306 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Evaluate quantized ONNX models on ImageNet-1k validation set.
3
+
4
+ Uses ONNX Runtime for inference. Loads the cached ImageNet dataset
5
+ directly from arrow shard files.
6
+ """
7
+
8
+ import argparse
9
+ import os
10
+ import time
11
+ import io
12
+ import numpy as np
13
+ import onnxruntime as ort
14
+ import torch
15
+ from torch.utils.data import Dataset, DataLoader
16
+ from PIL import Image
17
+ import pyarrow.ipc as ipc
18
+ from sklearn.metrics import average_precision_score, precision_recall_fscore_support
19
+ import torchvision
20
+ from torchvision.models import RegNet_X_800MF_Weights
21
+
22
+
23
+ # ---------------------------------------------------------------------------
24
+ # Dataset that reads directly from arrow shards
25
+ # ---------------------------------------------------------------------------
26
+
27
+ class ArrowImageNetDataset(Dataset):
28
+ """Load ImageNet validation data from cached arrow shard files."""
29
+
30
+ def __init__(self, arrow_dir, transform=None):
31
+ self.transform = transform
32
+ self.shards = []
33
+ self.offsets = [0]
34
+
35
+ # Load all valid arrow shards
36
+ shard_files = sorted(
37
+ f for f in os.listdir(arrow_dir)
38
+ if f.startswith("imagenet-1k_validation-validation-") and f.endswith(".arrow")
39
+ )
40
+
41
+ for fname in shard_files:
42
+ path = os.path.join(arrow_dir, fname)
43
+ try:
44
+ with open(path, "rb") as f:
45
+ reader = ipc.RecordBatchStreamReader(f)
46
+ table = reader.read_all()
47
+ self.shards.append(table)
48
+ self.offsets.append(self.offsets[-1] + len(table))
49
+ print(f" Loaded shard {fname}: {len(table)} rows")
50
+ except Exception as e:
51
+ print(f" SKIP shard {fname}: {e}")
52
+
53
+ self.total = self.offsets[-1]
54
+ print(f" Total images: {self.total}")
55
+
56
+ def __len__(self):
57
+ return self.total
58
+
59
+ def __getitem__(self, idx):
60
+ # Binary search for the correct shard
61
+ lo, hi = 0, len(self.shards) - 1
62
+ while lo < hi:
63
+ mid = (lo + hi) // 2
64
+ if self.offsets[mid + 1] <= idx:
65
+ lo = mid + 1
66
+ else:
67
+ hi = mid
68
+ shard_idx = lo
69
+ local_idx = idx - self.offsets[shard_idx]
70
+
71
+ table = self.shards[shard_idx]
72
+ img_bytes = table.column("image")[local_idx].as_py()
73
+
74
+ if isinstance(img_bytes, dict):
75
+ img_bytes = img_bytes.get("bytes", img_bytes.get("path", b""))
76
+ if isinstance(img_bytes, bytes):
77
+ img = Image.open(io.BytesIO(img_bytes)).convert("RGB")
78
+ else:
79
+ img = Image.new("RGB", (224, 224))
80
+
81
+ label = table.column("label")[local_idx].as_py()
82
+
83
+ if self.transform:
84
+ img = self.transform(img)
85
+
86
+ return img, label
87
+
88
+
89
+ # ---------------------------------------------------------------------------
90
+ # Metrics (same as model_eval_test.py)
91
+ # ---------------------------------------------------------------------------
92
+
93
+ def compute_metrics(logits, labels, num_classes):
94
+ probs = torch.softmax(torch.from_numpy(logits), dim=1).numpy()
95
+ preds = probs.argmax(axis=1)
96
+ N = len(labels)
97
+
98
+ top1 = (preds == labels).sum() / N
99
+ topk_vals = np.argsort(probs, axis=1)[:, ::-1]
100
+ top5 = sum(labels[i] in topk_vals[i, :5] for i in range(N)) / N
101
+
102
+ one_hot = np.zeros((N, num_classes), dtype=np.int32)
103
+ one_hot[np.arange(N), labels] = 1
104
+ aps = []
105
+ for c in range(num_classes):
106
+ if one_hot[:, c].sum() == 0:
107
+ continue
108
+ try:
109
+ ap = average_precision_score(one_hot[:, c], probs[:, c])
110
+ except ValueError:
111
+ ap = 0.0
112
+ aps.append(ap)
113
+ mAP = np.mean(aps) if aps else 0.0
114
+
115
+ prec_mac, rec_mac, f1_mac, _ = precision_recall_fscore_support(
116
+ labels, preds, average="macro", zero_division=0
117
+ )
118
+ prec_wt, rec_wt, f1_wt, _ = precision_recall_fscore_support(
119
+ labels, preds, average="weighted", zero_division=0
120
+ )
121
+
122
+ return {
123
+ "top1": top1,
124
+ "top5": top5,
125
+ "mAP": mAP,
126
+ "precision_macro": prec_mac,
127
+ "recall_macro": rec_mac,
128
+ "f1_macro": f1_mac,
129
+ "precision_weighted": prec_wt,
130
+ "recall_weighted": rec_wt,
131
+ "f1_weighted": f1_wt,
132
+ }
133
+
134
+
135
+ # ---------------------------------------------------------------------------
136
+ # ONNX model evaluation
137
+ # ---------------------------------------------------------------------------
138
+
139
+ def evaluate_onnx(onnx_path, loader, num_classes, print_every=500):
140
+ """Evaluate an ONNX model using ONNX Runtime with batch=1 (models have static shapes)."""
141
+ providers = []
142
+ if "CUDAExecutionProvider" in ort.get_available_providers():
143
+ providers.append(("CUDAExecutionProvider", {"device_id": 0}))
144
+ providers.append("CPUExecutionProvider")
145
+
146
+ session_options = ort.SessionOptions()
147
+ session_options.graph_optimization_level = ort.GraphOptimizationLevel.ORT_DISABLE_ALL
148
+
149
+ session = ort.InferenceSession(onnx_path, sess_options=session_options, providers=providers)
150
+ input_name = session.get_inputs()[0].name
151
+
152
+ all_logits = []
153
+ all_labels = []
154
+ total = 0
155
+
156
+ start = time.time()
157
+ for batch_idx, (imgs, labels) in enumerate(loader):
158
+ # Run one image at a time (model has static batch=1 in internal Reshape nodes)
159
+ for i in range(imgs.size(0)):
160
+ single_img = imgs[i:i+1].numpy() # shape (1, C, H, W)
161
+ outputs = session.run(None, {input_name: single_img})
162
+ all_logits.append(outputs[0])
163
+ all_labels.append(np.array([labels[i].item()] if torch.is_tensor(labels[i]) else [labels[i]]))
164
+ total += imgs.size(0)
165
+
166
+ if print_every and (batch_idx + 1) % print_every == 0:
167
+ elapsed = time.time() - start
168
+ speed = total / elapsed
169
+ print(f" [{total:>6d} images] {speed:.1f} img/s")
170
+
171
+ all_logits = np.concatenate(all_logits, axis=0)
172
+ all_labels = np.concatenate(all_labels, axis=0)
173
+
174
+ metrics = compute_metrics(all_logits, all_labels, num_classes)
175
+ metrics["total_images"] = total
176
+ elapsed = time.time() - start
177
+ metrics["elapsed"] = elapsed
178
+ return metrics
179
+
180
+
181
+ # ---------------------------------------------------------------------------
182
+ # Main
183
+ # ---------------------------------------------------------------------------
184
+
185
+ def main():
186
+ parser = argparse.ArgumentParser(description="Evaluate quantized ONNX models")
187
+ parser.add_argument("--batch_size", type=int, default=32)
188
+ parser.add_argument("--num_workers", type=int, default=4)
189
+ parser.add_argument("--subset", type=int, default=0, help="Evaluate on first N images (0=all)")
190
+ args = parser.parse_args()
191
+
192
+ # Load model config for transforms
193
+ print("Loading model config for transforms...")
194
+ weights = RegNet_X_800MF_Weights.IMAGENET1K_V2
195
+ # model = torchvision.models.regnet_x_800mf(weights=weights)
196
+ # model.eval()
197
+
198
+ transform = weights.transforms()
199
+ input_size = (3, 224, 224) # (C, H, W)
200
+
201
+ num_classes = 1000
202
+ # del model
203
+
204
+ # Load dataset from cached arrow shards
205
+ arrow_dir = os.path.expanduser(
206
+ "~/.cache/huggingface/datasets/Tsomaros___imagenet-1k_validation/"
207
+ "default/0.0.0/55405c49dece42420e68ddd5f80174f19b29ebaf/"
208
+ )
209
+ print(f"Loading dataset from arrow shards: {arrow_dir}")
210
+ dataset = ArrowImageNetDataset(arrow_dir, transform=transform)
211
+
212
+ if args.subset > 0:
213
+ from torch.utils.data import Subset
214
+ dataset = Subset(dataset, range(min(args.subset, len(dataset))))
215
+ print(f" Using subset: {args.subset} images")
216
+
217
+ loader = DataLoader(
218
+ dataset,
219
+ batch_size=args.batch_size,
220
+ shuffle=False,
221
+ num_workers=args.num_workers,
222
+ pin_memory=True,
223
+ )
224
+
225
+ # Define models to evaluate
226
+ models = {
227
+ "FP32 (baseline)": "regnet_x_800mf_fp32.onnx",
228
+ "FP16": "fp16/regnet_x_800mf_fp16.onnx",
229
+ "INT8 entropy": "int8/regnet_x_800mf_int8_entropy.onnx",
230
+ "INT8 max": "int8/regnet_x_800mf_int8_max.onnx",
231
+ "FP8 entropy": "fp8/regnet_x_800mf_fp8_entropy.onnx",
232
+ "FP8 max": "fp8/regnet_x_800mf_fp8_max.onnx",
233
+ "INT4 awq_clip": "int4/regnet_x_800mf_int4_awq_clip.onnx",
234
+ "INT4 awq_lite (asym)": "int4/regnet_x_800mf_int4_awq_lite_asym.onnx",
235
+ "INT4 awq_lite (sym)": "int4/regnet_x_800mf_int4_awq_lite.onnx",
236
+ "INT4 awq_full": "int4/regnet_x_800mf_int4_awq_full.onnx",
237
+ "INT4 rtn_dq": "int4/regnet_x_800mf_int4_rtn_dq.onnx",
238
+ }
239
+
240
+ # Filter to only existing files
241
+ existing_models = {}
242
+ for name, path in models.items():
243
+ if os.path.exists(path):
244
+ existing_models[name] = path
245
+ else:
246
+ print(f" SKIP: {name} — file not found: {path}")
247
+
248
+ results = {}
249
+
250
+ for name, onnx_path in existing_models.items():
251
+ print(f"\n{'='*60}")
252
+ print(f"Evaluating: {name}")
253
+ print(f" Model: {onnx_path}")
254
+ print(f"{'='*60}")
255
+
256
+ try:
257
+ metrics = evaluate_onnx(onnx_path, loader, num_classes)
258
+ results[name] = metrics
259
+ print(f"\n Top-1 Accuracy: {metrics['top1']*100:.3f}%")
260
+ print(f" Top-5 Accuracy: {metrics['top5']*100:.3f}%")
261
+ print(f" mAP: {metrics['mAP']:.4f}")
262
+ print(f" F1 (macro): {metrics['f1_macro']:.4f}")
263
+ print(f" F1 (weighted): {metrics['f1_weighted']:.4f}")
264
+ print(f" Time: {metrics['elapsed']:.1f}s")
265
+ except Exception as e:
266
+ print(f" FAILED: {e}")
267
+ import traceback
268
+ traceback.print_exc()
269
+ results[name] = {"error": str(e)}
270
+
271
+ # Print comparison table
272
+ print(f"\n\n{'='*90}")
273
+ print("Evaluation Comparison Table")
274
+ print(f"{'='*90}")
275
+ print(f" {'Model':<25s} {'Top-1%':>8s} {'Top-5%':>8s} {'mAP':>8s} {'F1_mac':>8s} {'F1_wt':>8s} {'Time':>8s}")
276
+ print(f" {'-'*25} {'-'*8} {'-'*8} {'-'*8} {'-'*8} {'-'*8} {'-'*8}")
277
+
278
+ for name, m in results.items():
279
+ if "error" in m:
280
+ print(f" {name:<25s} FAILED: {m['error']}")
281
+ else:
282
+ print(
283
+ f" {name:<25s} "
284
+ f"{m['top1']*100:>8.3f} "
285
+ f"{m['top5']*100:>8.3f} "
286
+ f"{m['mAP']:>8.4f} "
287
+ f"{m['f1_macro']:>8.4f} "
288
+ f"{m['f1_weighted']:>8.4f} "
289
+ f"{m['elapsed']:>7.1f}s"
290
+ )
291
+
292
+ print(f"\n Reference (timm model card): Top-1: 82.346% | Top-5: 96.394%")
293
+ print(f"{'='*90}")
294
+
295
+ # Find best INT8 model and copy as the canonical output
296
+ int8_results = {k: v for k, v in results.items() if k.startswith("INT8") and "error" not in v}
297
+ if int8_results:
298
+ best_int8 = max(int8_results, key=lambda k: int8_results[k]["top1"])
299
+ best_path = existing_models[best_int8]
300
+ print(f"\n Best INT8 model: {best_int8} ({best_path})")
301
+ print(f" Top-1: {int8_results[best_int8]['top1']*100:.3f}%")
302
+ print(f" Top-5: {int8_results[best_int8]['top5']*100:.3f}%")
303
+
304
+
305
+ if __name__ == "__main__":
306
+ main()
regnet_x_800mf_evaluate.py ADDED
@@ -0,0 +1,454 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Evaluate mlx-vision/regnet_x_800mf-mlxim on ImageNet-1k validation set.
3
+
4
+ The mlx-vision model is a port of torchvision RegNet_X_800MF (IMAGENET1K_V2)
5
+ weights to Apple MLX. Since this evaluation runs on a standard PyTorch
6
+ environment, we load the equivalent torchvision model with identical weights.
7
+
8
+ We compute Top-1 / Top-5 accuracy, per-class accuracy, and mAP over the
9
+ full 50K ImageNet validation images.
10
+
11
+ Dataset: Tsomaros/Imagenet-1k_validation (validation split, 50K images)
12
+ label indices 0-999 align with torchvision model output indices.
13
+
14
+ Usage:
15
+ python regnet_x_800mf_evaluate.py [--batch_size 64] [--num_workers 4] [--streaming]
16
+ """
17
+
18
+ import argparse
19
+ import os
20
+ import time
21
+ import numpy as np
22
+ import torch
23
+ import torch.onnx
24
+ from torch.utils.data import Dataset, DataLoader
25
+ from datasets import load_dataset
26
+ import torchvision
27
+ from torchvision.models import RegNet_X_800MF_Weights
28
+ from sklearn.metrics import average_precision_score, precision_recall_fscore_support
29
+
30
+
31
+ # ---------------------------------------------------------------------------
32
+ # Dataset wrapper
33
+ # ---------------------------------------------------------------------------
34
+
35
+ class ImageNetValDataset(Dataset):
36
+ """Wrap the HF ImageNet-1k validation split for evaluation."""
37
+
38
+ def __init__(self, hf_dataset, transform=None):
39
+ self.hf_dataset = hf_dataset
40
+ self.transform = transform
41
+
42
+ def __len__(self):
43
+ return len(self.hf_dataset)
44
+
45
+ def __getitem__(self, idx):
46
+ row = self.hf_dataset[idx]
47
+ img = row["image"]
48
+ if img.mode != "RGB":
49
+ img = img.convert("RGB")
50
+ label = row["label"]
51
+ if self.transform:
52
+ img = self.transform(img)
53
+ return img, label
54
+
55
+
56
+ # ---------------------------------------------------------------------------
57
+ # Metrics
58
+ # ---------------------------------------------------------------------------
59
+
60
+ def compute_metrics(logits: np.ndarray, labels: np.ndarray, num_classes: int):
61
+ """
62
+ Compute comprehensive single-label classification metrics.
63
+
64
+ Args:
65
+ logits: (N, C) raw model outputs
66
+ labels: (N,) integer ground-truth class indices
67
+ num_classes: total number of classes (1000)
68
+
69
+ Returns:
70
+ dict with top1, top5, mAP, precision, recall, f1, per_class_ap
71
+ """
72
+ probs = torch.softmax(torch.from_numpy(logits), dim=1).numpy() # (N, C)
73
+ preds = probs.argmax(axis=1)
74
+
75
+ N = len(labels)
76
+
77
+ # --- Top-1 / Top-5 Accuracy ---
78
+ top1_correct = (preds == labels).sum()
79
+ top1 = top1_correct / N
80
+
81
+ topk_vals = np.argsort(probs, axis=1)[:, ::-1] # (N, C) sorted descending
82
+ top5_correct = sum(labels[i] in topk_vals[i, :5] for i in range(N))
83
+ top5 = top5_correct / N
84
+
85
+ # --- mAP (per-class AP averaged) ---
86
+ one_hot = np.zeros((N, num_classes), dtype=np.int32)
87
+ one_hot[np.arange(N), labels] = 1
88
+
89
+ aps = []
90
+ per_class_ap = np.zeros(num_classes, dtype=np.float64)
91
+ for c in range(num_classes):
92
+ if one_hot[:, c].sum() == 0:
93
+ continue
94
+ try:
95
+ ap = average_precision_score(one_hot[:, c], probs[:, c])
96
+ except ValueError:
97
+ ap = 0.0
98
+ per_class_ap[c] = ap
99
+ aps.append(ap)
100
+
101
+ mAP = np.mean(aps) if aps else 0.0
102
+
103
+ # --- Precision / Recall / F1 ---
104
+ prec_mac, rec_mac, f1_mac, _ = precision_recall_fscore_support(
105
+ labels, preds, average="macro", zero_division=0
106
+ )
107
+ prec_wt, rec_wt, f1_wt, _ = precision_recall_fscore_support(
108
+ labels, preds, average="weighted", zero_division=0
109
+ )
110
+
111
+ return {
112
+ "top1": top1,
113
+ "top5": top5,
114
+ "mAP": mAP,
115
+ "precision_macro": prec_mac,
116
+ "recall_macro": rec_mac,
117
+ "f1_macro": f1_mac,
118
+ "precision_weighted": prec_wt,
119
+ "recall_weighted": rec_wt,
120
+ "f1_weighted": f1_wt,
121
+ "per_class_ap": per_class_ap,
122
+ }
123
+
124
+
125
+ # ---------------------------------------------------------------------------
126
+ # Evaluation
127
+ # ---------------------------------------------------------------------------
128
+
129
+ @torch.no_grad()
130
+ def evaluate(model, loader, device, num_classes, print_every=500):
131
+ """Run full evaluation and return metrics dict."""
132
+ model.eval()
133
+ all_logits = []
134
+ all_labels = []
135
+ total = 0
136
+
137
+ start = time.time()
138
+ for batch_idx, (imgs, labels) in enumerate(loader):
139
+ imgs = imgs.to(device)
140
+ outputs = model(imgs)
141
+ all_logits.append(outputs.cpu().numpy())
142
+ all_labels.append(np.array(labels))
143
+ total += imgs.size(0)
144
+
145
+ if print_every and (batch_idx + 1) % print_every == 0:
146
+ elapsed = time.time() - start
147
+ speed = total / elapsed
148
+ print(f" [{total:>6d} images] {speed:.1f} img/s")
149
+
150
+ all_logits = np.concatenate(all_logits, axis=0)
151
+ all_labels = np.concatenate(all_labels, axis=0)
152
+
153
+ metrics = compute_metrics(all_logits, all_labels, num_classes)
154
+ metrics["total_images"] = total
155
+ return metrics
156
+
157
+
158
+ # ---------------------------------------------------------------------------
159
+ # Calibration data
160
+ # ---------------------------------------------------------------------------
161
+
162
+ def save_calibration_data(k=20, save_path="regnet_x_800mf_calibration.npy"):
163
+ """
164
+ Randomly sample k images from Tsomaros/Imagenet-1k_validation validation split as
165
+ calibration data for model quantization, and save as a .npy file.
166
+
167
+ Args:
168
+ k: number of calibration images to sample
169
+ save_path: output .npy file path
170
+ """
171
+ # Load model & transforms (same as main)
172
+ print("Loading torchvision RegNet_X_800MF (IMAGENET1K_V2) ...")
173
+ weights = RegNet_X_800MF_Weights.IMAGENET1K_V2
174
+ model = torchvision.models.regnet_x_800mf(weights=weights)
175
+ model.eval()
176
+
177
+ transform = weights.transforms()
178
+ input_size = (3, 224, 224) # (C, H, W)
179
+ print(f" Input size: {input_size}")
180
+
181
+ # Load dataset
182
+ print("Loading Tsomaros/Imagenet-1k_validation validation ...")
183
+ ds = load_dataset("Tsomaros/Imagenet-1k_validation", split="validation")
184
+ total = len(ds)
185
+ print(f" Total images: {total}")
186
+
187
+ # Random sample k indices
188
+ rng = np.random.default_rng(42)
189
+ indices = rng.choice(total, size=min(k, total), replace=False)
190
+ indices.sort() # sorted for sequential HF dataset access
191
+
192
+ # Collect calibration images
193
+ calibration = np.empty((len(indices), *input_size), dtype=np.float32)
194
+ for i, idx in enumerate(indices):
195
+ row = ds[int(idx)]
196
+ img = row["image"]
197
+ if img.mode != "RGB":
198
+ img = img.convert("RGB")
199
+ tensor = transform(img) # (C, H, W) float32
200
+ calibration[i] = tensor.numpy()
201
+ if (i + 1) % 20 == 0 or (i + 1) == len(indices):
202
+ print(f" [{i + 1:>4d}/{len(indices)}] images collected")
203
+
204
+ np.save(save_path, calibration)
205
+ print(f"Saved calibration data: {calibration.shape} -> {save_path}")
206
+
207
+
208
+ # ---------------------------------------------------------------------------
209
+ # Main
210
+ # ---------------------------------------------------------------------------
211
+
212
+ def main():
213
+ parser = argparse.ArgumentParser(
214
+ description="Evaluate mlx-vision/regnet_x_800mf-mlxim (via torchvision) on ImageNet-1k val"
215
+ )
216
+ parser.add_argument("--batch_size", type=int, default=64, help="Batch size")
217
+ parser.add_argument("--num_workers", type=int, default=4, help="DataLoader workers")
218
+ parser.add_argument("--streaming", action="store_true", help="Stream dataset instead of downloading")
219
+ parser.add_argument("--subset", type=int, default=0, help="Evaluate on first N images only (0 = all 50K)")
220
+ parser.add_argument("--save_model", action="store_true", help="Save pth, onnx, and calibration data")
221
+ args = parser.parse_args()
222
+
223
+ device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
224
+ print(f"Device: {device}")
225
+
226
+ # ------------------------------------------------------------------
227
+ # Load model & transforms
228
+ # ------------------------------------------------------------------
229
+ print("Loading torchvision RegNet_X_800MF (IMAGENET1K_V2) ...")
230
+ print(" (equivalent to mlx-vision/regnet_x_800mf-mlxim weights)")
231
+ weights = RegNet_X_800MF_Weights.IMAGENET1K_V2
232
+ model = torchvision.models.regnet_x_800mf(weights=weights)
233
+ model = model.to(device)
234
+ model.eval()
235
+
236
+ transform = weights.transforms()
237
+ num_classes = 1000
238
+ input_size = (1, 3, 224, 224) # (B, C, H, W)
239
+ print(f" Classes: {num_classes}, Input size: {input_size}")
240
+
241
+ if args.save_model:
242
+ inputs = torch.randn(input_size).to(device)
243
+ torch.save(model.state_dict(), 'regnet_x_800mf_fp32.pth')
244
+
245
+ torch.onnx.export(model, inputs, "regnet_x_800mf_fp32.onnx")
246
+
247
+ save_calibration_data()
248
+
249
+ # ------------------------------------------------------------------
250
+ # Load dataset
251
+ # ------------------------------------------------------------------
252
+ print("Loading Tsomaros/Imagenet-1k_validation validation split ...")
253
+ ds = None
254
+ try:
255
+ ds = load_dataset(
256
+ "Tsomaros/Imagenet-1k_validation",
257
+ split="validation",
258
+ streaming=args.streaming,
259
+ )
260
+ except Exception as e:
261
+ print(f" load_dataset failed: {type(e).__name__}: {str(e)[:150]}")
262
+ print(" Falling back to loading from cached arrow shard files ...")
263
+ ds = None
264
+
265
+ if ds is None:
266
+ # Fallback: load directly from arrow shard files
267
+ import io as _io
268
+ import pyarrow.ipc as _ipc
269
+ from PIL import Image as _PILImage
270
+
271
+ # Try to locate the cached arrow directory for Tsomaros/Imagenet-1k_validation
272
+ _cache_base = os.path.expanduser("~/.cache/huggingface/datasets/")
273
+ _arrow_dir = None
274
+ # Search for the ILSVRC imagenet-1k dataset cache
275
+ for root, dirs, files in os.walk(_cache_base):
276
+ if "imagenet" in root.lower() and any(f.endswith(".arrow") for f in files):
277
+ _arrow_dir = root
278
+ break
279
+
280
+ if _arrow_dir is None or not os.path.isdir(_arrow_dir):
281
+ print(f" ERROR: Arrow cache directory not found under {_cache_base}")
282
+ print(" Please run with network access first to download the dataset.")
283
+ return
284
+
285
+ _shard_files = sorted(
286
+ f for f in os.listdir(_arrow_dir)
287
+ if f.endswith(".arrow")
288
+ )
289
+ print(f" Found {len(_shard_files)} arrow shard files in {_arrow_dir}")
290
+
291
+ class _ArrowShardDataset(Dataset):
292
+ """Load ImageNet validation data from cached arrow shard files."""
293
+
294
+ def __init__(self, arrow_dir, shard_files, transform=None):
295
+ self.transform = transform
296
+ self.shards = []
297
+ self.offsets = [0]
298
+ for fname in shard_files:
299
+ path = os.path.join(arrow_dir, fname)
300
+ try:
301
+ with open(path, "rb") as f:
302
+ reader = _ipc.RecordBatchStreamReader(f)
303
+ table = reader.read_all()
304
+ self.shards.append(table)
305
+ self.offsets.append(self.offsets[-1] + len(table))
306
+ except Exception as ex:
307
+ print(f" SKIP shard {fname}: {ex}")
308
+ self.total = self.offsets[-1]
309
+ print(f" Total images loaded from shards: {self.total}")
310
+
311
+ def __len__(self):
312
+ return self.total
313
+
314
+ def __getitem__(self, idx):
315
+ lo, hi = 0, len(self.shards) - 1
316
+ while lo < hi:
317
+ mid = (lo + hi) // 2
318
+ if self.offsets[mid + 1] <= idx:
319
+ lo = mid + 1
320
+ else:
321
+ hi = mid
322
+ shard_idx = lo
323
+ local_idx = idx - self.offsets[shard_idx]
324
+ table = self.shards[shard_idx]
325
+ img_bytes = table.column("image")[local_idx].as_py()
326
+ if isinstance(img_bytes, dict):
327
+ img_bytes = img_bytes.get("bytes", img_bytes.get("path", b""))
328
+ if isinstance(img_bytes, bytes):
329
+ img = _PILImage.open(_io.BytesIO(img_bytes)).convert("RGB")
330
+ else:
331
+ img = _PILImage.new("RGB", (224, 224))
332
+ label = table.column("label")[local_idx].as_py()
333
+ if self.transform:
334
+ img = self.transform(img)
335
+ return img, label
336
+
337
+ dataset = _ArrowShardDataset(_arrow_dir, _shard_files, transform=transform)
338
+ total_images = len(dataset)
339
+ if args.subset > 0:
340
+ from torch.utils.data import Subset
341
+ dataset = Subset(dataset, range(min(args.subset, total_images)))
342
+ total_images = min(args.subset, total_images)
343
+ print(f" Using subset: {total_images} images")
344
+ elif args.streaming:
345
+ if args.subset > 0:
346
+ from itertools import islice
347
+ ds = list(islice(ds, args.subset))
348
+ print(f" Using subset: {args.subset} images (streamed)")
349
+
350
+ # Materialize for DataLoader compatibility
351
+ if not isinstance(ds, list):
352
+ ds = list(ds)
353
+ from torch.utils.data import Dataset as _DS
354
+
355
+ class _StreamDS(_DS):
356
+ def __init__(self, items, tfm):
357
+ self.items = items
358
+ self.tfm = tfm
359
+
360
+ def __len__(self):
361
+ return len(self.items)
362
+
363
+ def __getitem__(self, idx):
364
+ row = self.items[idx]
365
+ img = row["image"]
366
+ if img.mode != "RGB":
367
+ img = img.convert("RGB")
368
+ return self.tfm(img), row["label"]
369
+
370
+ dataset = _StreamDS(ds, transform)
371
+ total_images = len(ds)
372
+ else:
373
+ if args.subset > 0:
374
+ ds = ds.select(range(args.subset))
375
+ print(f" Using subset: {args.subset} images")
376
+ dataset = ImageNetValDataset(ds, transform=transform)
377
+ total_images = len(dataset)
378
+
379
+ print(f" Total images: {total_images}")
380
+
381
+ loader = DataLoader(
382
+ dataset,
383
+ batch_size=args.batch_size,
384
+ shuffle=False,
385
+ num_workers=args.num_workers,
386
+ pin_memory=True,
387
+ )
388
+
389
+ # ------------------------------------------------------------------
390
+ # Evaluate
391
+ # ------------------------------------------------------------------
392
+ print(f"\n{'='*60}")
393
+ print("Running ImageNet-1k validation evaluation ...")
394
+ print(f"{'='*60}\n")
395
+
396
+ metrics = evaluate(model, loader, device, num_classes=num_classes)
397
+
398
+ # ------------------------------------------------------------------
399
+ # Print results
400
+ # ------------------------------------------------------------------
401
+ print(f"\n{'='*60}")
402
+ print("Evaluation Results — regnet_x_800mf (mlx-vision/regnet_x_800mf-mlxim)")
403
+ print(f"{'='*60}")
404
+ print(f"\n Dataset: ImageNet-1k validation ({metrics['total_images']} images)")
405
+ print(f" Top-1 Accuracy: {metrics['top1']*100:.3f}%")
406
+ print(f" Top-5 Accuracy: {metrics['top5']*100:.3f}%")
407
+ print(f" mAP: {metrics['mAP']:.4f}")
408
+ print(f"\n Precision (macro): {metrics['precision_macro']:.4f}")
409
+ print(f" Recall (macro): {metrics['recall_macro']:.4f}")
410
+ print(f" F1 (macro): {metrics['f1_macro']:.4f}")
411
+ print(f" Precision (weighted): {metrics['precision_weighted']:.4f}")
412
+ print(f" Recall (weighted): {metrics['recall_weighted']:.4f}")
413
+ print(f" F1 (weighted): {metrics['f1_weighted']:.4f}")
414
+
415
+ # Per-class AP summary
416
+ per_class_ap = metrics["per_class_ap"]
417
+ valid_mask = per_class_ap > 0
418
+ valid_aps = per_class_ap[valid_mask]
419
+ if len(valid_aps) > 0:
420
+ print(f"\n Per-class AP (over {len(valid_aps)} classes present in val):")
421
+ print(f" Mean: {valid_aps.mean():.4f}")
422
+ print(f" Median: {np.median(valid_aps):.4f}")
423
+ print(f" Min: {valid_aps.min():.4f}")
424
+ print(f" Max: {valid_aps.max():.4f}")
425
+ print(f" Std: {valid_aps.std():.4f}")
426
+
427
+ # Top-10 / Bottom-10 classes by AP
428
+ present_indices = np.where(valid_mask)[0]
429
+ sorted_by_ap = present_indices[np.argsort(per_class_ap[present_indices])]
430
+
431
+ # Load class names from torchvision weights meta
432
+ class_names = weights.meta.get("categories", None)
433
+
434
+ def _class_label(idx):
435
+ if class_names:
436
+ return class_names[idx][:40]
437
+ return f"class_{idx}"
438
+
439
+ print(f"\n Top-10 classes by AP:")
440
+ for idx in sorted_by_ap[-10:][::-1]:
441
+ print(f" [{idx:>3d}] {_class_label(idx):42s} AP = {per_class_ap[idx]:.4f}")
442
+ print(f"\n Bottom-10 classes by AP:")
443
+ for idx in sorted_by_ap[:10]:
444
+ print(f" [{idx:>3d}] {_class_label(idx):42s} AP = {per_class_ap[idx]:.4f}")
445
+
446
+ # Reference accuracy from torchvision model card
447
+ print(f"\n{'='*60}")
448
+ print("Reference (torchvision RegNet_X_800MF IMAGENET1K_V2):")
449
+ print(" Top-1: 77.054% | Top-5: 93.530% (224px val crop)")
450
+ print(f"{'='*60}")
451
+
452
+
453
+ if __name__ == "__main__":
454
+ main()
regnet_x_800mf_quantize_all.py ADDED
@@ -0,0 +1,213 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Quantize regnet_x_800mf_fp32.onnx using Nvidia Model Optimizer.
3
+
4
+ Quantization modes attempted:
5
+ - FP16: convert_to_f16
6
+ - INT8: entropy / max calibration
7
+ - FP8: entropy / max calibration
8
+ - INT4: awq_clip / awq_lite (symmetric + asymmetric) / awq_full / rtn_dq
9
+
10
+ Calibration data: regnet_x_800mf_calibration.npy (20 images, shape 20x3x224x224)
11
+ """
12
+
13
+ import os
14
+ import sys
15
+ import time
16
+ import numpy as np
17
+ import onnx
18
+ import onnxruntime as ort
19
+
20
+ # ---------------------------------------------------------------------------
21
+ # Configuration
22
+ # ---------------------------------------------------------------------------
23
+ ONNX_PATH = "regnet_x_800mf_fp32.onnx"
24
+ CALIB_PATH = "regnet_x_800mf_calibration.npy"
25
+ MODEL_NAME = "regnet_x_800mf"
26
+
27
+ # Load calibration data
28
+ calib_data = np.load(CALIB_PATH)
29
+ print(f"Calibration data: shape={calib_data.shape}, dtype={calib_data.dtype}")
30
+
31
+ # ---------------------------------------------------------------------------
32
+ # FP16 Quantization
33
+ # ---------------------------------------------------------------------------
34
+ def run_fp16():
35
+ print("\n" + "=" * 70)
36
+ print("FP16 Quantization")
37
+ print("=" * 70)
38
+
39
+ from modelopt.onnx.quantization.int8 import convert_to_f16
40
+
41
+ onnx_model = onnx.load(ONNX_PATH, load_external_data=True)
42
+ fp16_model = convert_to_f16(onnx_model, keep_io_types=True)
43
+
44
+ output_path = "fp16/regnet_x_800mf_fp16.onnx"
45
+ if not os.path.exists("fp16"):
46
+ os.mkdir("fp16")
47
+
48
+ onnx.save(fp16_model, output_path)
49
+ size_mb = os.path.getsize(output_path) / 1e6
50
+ print(f" Saved: {output_path} ({size_mb:.1f} MB)")
51
+ return output_path
52
+
53
+
54
+ # ---------------------------------------------------------------------------
55
+ # INT8 Quantization
56
+ # ---------------------------------------------------------------------------
57
+ def run_int8(method, label=None):
58
+ """Run INT8 quantization with a given calibration method."""
59
+ if label is None:
60
+ label = f"int8_{method}{zp_tag}"
61
+
62
+ print("\n" + "=" * 70)
63
+ print(f"INT8 Quantization — method={method}")
64
+ print("=" * 70)
65
+
66
+ from modelopt.onnx.quantization import quantize
67
+
68
+ output_path = f"int8/regnet_x_800mf_{label}.onnx"
69
+ if not os.path.exists("int8"):
70
+ os.mkdir("int8")
71
+ t0 = time.time()
72
+ quantize(
73
+ onnx_path=ONNX_PATH,
74
+ quantize_mode="int8",
75
+ calibration_data=calib_data,
76
+ calibration_method=method,
77
+ output_path=output_path,
78
+ use_external_data_format=True,
79
+ calibration_eps=["cuda:0", "cpu"],
80
+ )
81
+ elapsed = time.time() - t0
82
+
83
+ # Calculate total size (onnx + .data)
84
+ size_mb = os.path.getsize(output_path) / 1e6
85
+ data_path = output_path + ".data"
86
+ if os.path.exists(data_path):
87
+ size_mb += os.path.getsize(data_path) / 1e6
88
+ print(f" Saved: {output_path} ({size_mb:.1f} MB) in {elapsed:.1f}s")
89
+ return output_path
90
+
91
+
92
+ # ---------------------------------------------------------------------------
93
+ # FP8 Quantization
94
+ # ---------------------------------------------------------------------------
95
+ def run_fp8(method):
96
+ print("\n" + "=" * 70)
97
+ print(f"FP8 Quantization — method={method}")
98
+ print("=" * 70)
99
+
100
+ from modelopt.onnx.quantization import quantize
101
+
102
+ output_path = f"fp8/regnet_x_800mf_fp8_{method}.onnx"
103
+ if not os.path.exists("fp8"):
104
+ os.mkdir("fp8")
105
+ t0 = time.time()
106
+ quantize(
107
+ onnx_path=ONNX_PATH,
108
+ quantize_mode="fp8",
109
+ calibration_data=calib_data,
110
+ calibration_method=method,
111
+ output_path=output_path,
112
+ use_external_data_format=True,
113
+ calibration_eps=["cuda:0", "cpu"],
114
+ )
115
+ elapsed = time.time() - t0
116
+
117
+ size_mb = os.path.getsize(output_path) / 1e6
118
+ data_path = output_path + ".data"
119
+ if os.path.exists(data_path):
120
+ size_mb += os.path.getsize(data_path) / 1e6
121
+ print(f" Saved: {output_path} ({size_mb:.1f} MB) in {elapsed:.1f}s")
122
+ return output_path
123
+
124
+
125
+ # ---------------------------------------------------------------------------
126
+ # INT4 Quantization
127
+ # ---------------------------------------------------------------------------
128
+ def run_int4(method, use_zero_point=False):
129
+ print("\n" + "=" * 70)
130
+ print(f"INT4 Quantization — method={method}, zero_point={use_zero_point}")
131
+ print("=" * 70)
132
+
133
+ from modelopt.onnx.quantization import quantize
134
+
135
+ zp_tag = "_asym" if use_zero_point else ""
136
+ output_path = f"int4/regnet_x_800mf_int4_{method}{zp_tag}.onnx"
137
+ if not os.path.exists("int4"):
138
+ os.mkdir("int4")
139
+ t0 = time.time()
140
+ quantize(
141
+ onnx_path=ONNX_PATH,
142
+ quantize_mode="int4",
143
+ calibration_data=calib_data,
144
+ calibration_method=method,
145
+ use_zero_point=use_zero_point,
146
+ output_path=output_path,
147
+ use_external_data_format=True,
148
+ calibration_eps=["cuda:0", "cpu"],
149
+ )
150
+ elapsed = time.time() - t0
151
+
152
+ size_mb = os.path.getsize(output_path) / 1e6
153
+ data_path = output_path + ".data"
154
+ if os.path.exists(data_path):
155
+ size_mb += os.path.getsize(data_path) / 1e6
156
+ print(f" Saved: {output_path} ({size_mb:.1f} MB) in {elapsed:.1f}s")
157
+ return output_path
158
+
159
+
160
+ # ---------------------------------------------------------------------------
161
+ # Main
162
+ # ---------------------------------------------------------------------------
163
+ if __name__ == "__main__":
164
+ results = {}
165
+
166
+ # --- FP16 ---
167
+ try:
168
+ results["fp16"] = run_fp16()
169
+ except Exception as e:
170
+ print(f" FP16 FAILED: {e}")
171
+
172
+ # --- INT8 variants ---
173
+ for method in ["entropy", "max"]:
174
+ label = f"int8_{method}"
175
+ try:
176
+ results[label] = run_int8(method, label=label)
177
+ except Exception as e:
178
+ print(f" {label} FAILED: {e}")
179
+
180
+ # --- FP8 variants ---
181
+ for method in ["entropy", "max"]:
182
+ label = f"fp8_{method}"
183
+ try:
184
+ results[label] = run_fp8(method)
185
+ except Exception as e:
186
+ print(f" {label} FAILED: {e}")
187
+
188
+ # --- INT4 variants ---
189
+ for method in ["awq_clip", "awq_full", "rtn_dq"]:
190
+ label = f"int4_{method}"
191
+ try:
192
+ results[label] = run_int4(method, use_zero_point=False)
193
+ except Exception as e:
194
+ print(f" {label} FAILED: {e}")
195
+ # special treat for awq_lite
196
+ method = "awq_lite"
197
+ for zp in [False, True]:
198
+ label = f"int4_{method}" + ("_asym" if zp else "")
199
+ try:
200
+ results[label] = run_int4(method, use_zero_point=zp)
201
+ except Exception as e:
202
+ print(f" {label} FAILED: {e}")
203
+
204
+ # --- Summary ---
205
+ print("\n" + "=" * 70)
206
+ print("Quantization Summary")
207
+ print("=" * 70)
208
+ for label, path in results.items():
209
+ size_mb = os.path.getsize(path) / 1e6
210
+ data_path = path + ".data"
211
+ if os.path.exists(data_path):
212
+ size_mb += os.path.getsize(data_path) / 1e6
213
+ print(f" {label:30s} → {path:50s} ({size_mb:.1f} MB)")
remote_eval.py ADDED
@@ -0,0 +1,563 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ """
3
+ Remote TensorRT evaluation script for HGNetV2-B2 quantized ONNX models.
4
+
5
+ Steps:
6
+ 1. Upload ONNX models + calibration data to remote
7
+ 2. Build TensorRT engines with various precision flags
8
+ 3. Run inference on ImageNet validation set via ONNX Runtime (on remote)
9
+ 4. Collect and compare accuracy metrics
10
+ 5. Log everything to /data/rilin/hgnetv2/evaluate.log
11
+ """
12
+
13
+ import paramiko
14
+ import os
15
+ import time
16
+ import sys
17
+ import json
18
+ import scp
19
+ import io
20
+ import numpy as np
21
+ import torch
22
+ import timm
23
+ from timm.data import resolve_model_data_config, create_transform
24
+ from PIL import Image
25
+ import pyarrow.ipc as ipc
26
+ from sklearn.metrics import average_precision_score, precision_recall_fscore_support
27
+
28
+ # ===================== CONFIG =====================
29
+ REMOTE_HOST = "192.168.8.108"
30
+ REMOTE_USER = "nvidia"
31
+ REMOTE_PASS = "nvidia"
32
+ REMOTE_BASE = "/data/rilin/hgnetv2"
33
+ TRTEXEC = "/data/rilin/tlr/aicompiler"
34
+ LOCAL_MODELS = {
35
+ "FP32 (baseline)": "hgnetv2_b2_fp32.onnx",
36
+ "FP16": "fp16/hgnetv2_b2_fp16.onnx",
37
+ "INT8 entropy": "int8/hgnetv2_b2_int8_entropy.onnx",
38
+ "INT8 max": "int8/hgnetv2_b2_int8_max.onnx",
39
+ "FP8 entropy": "fp8/hgnetv2_b2_fp8_entropy.onnx",
40
+ "FP8 max": "fp8/hgnetv2_b2_fp8_max.onnx",
41
+ "INT4 awq_clip": "int4/hgnetv2_b2_int4_awq_clip.onnx",
42
+ "INT4 awq_lite (asym)": "int4/hgnetv2_b2_int4_awq_lite_asym.onnx",
43
+ "INT4 awq_lite (sym)": "int4/hgnetv2_b2_int4_awq_lite.onnx",
44
+ "INT4 awq_full": "int4/hgnetv2_b2_int4_awq_full.onnx",
45
+ "INT4 rtn_dq": "int4/hgnetv2_b2_int4_rtn_dq.onnx"
46
+ }
47
+ # ===================== END CONFIG =====================
48
+
49
+
50
+ def ssh_exec(ssh, cmd, timeout=600, log_output=True):
51
+ """Execute command on remote and return stdout, stderr."""
52
+ stdin, stdout, stderr = ssh.exec_command(cmd, timeout=timeout)
53
+ out = stdout.read().decode()
54
+ err = stderr.read().decode()
55
+ if log_output and out.strip():
56
+ print(f" [OUT] {out.strip()[:500]}")
57
+ if log_output and err.strip():
58
+ print(f" [ERR] {err.strip()[:500]}")
59
+ return out.strip(), err.strip()
60
+
61
+
62
+ def main():
63
+ # Connect
64
+ print(f"Connecting to {REMOTE_HOST}...")
65
+ ssh = paramiko.SSHClient()
66
+ ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
67
+ ssh.connect(REMOTE_HOST, username=REMOTE_USER, password=REMOTE_PASS, timeout=30)
68
+ print("Connected!")
69
+
70
+ # Create remote dirs
71
+ ssh_exec(ssh, f"mkdir -p {REMOTE_BASE}/models {REMOTE_BASE}/results", log_output=False)
72
+
73
+ # =====================================================================
74
+ # PHASE 1: Upload models and calibration data
75
+ # =====================================================================
76
+ print("\n" + "=" * 70)
77
+ print("PHASE 1: Uploading models to remote")
78
+ print("=" * 70)
79
+
80
+ scp_client = scp.SCPClient(ssh.get_transport())
81
+
82
+ # Upload calibration data
83
+ calib_local = "hgnetv2_b2_calibration.npy"
84
+ calib_remote = f"{REMOTE_BASE}/models/hgnetv2_b2_calibration.npy"
85
+ print(f" Uploading {calib_local}...")
86
+ scp_client.put(calib_local, calib_remote)
87
+
88
+ # Upload ONNX models + external data
89
+ uploaded = []
90
+ for name, local_path in LOCAL_MODELS.items():
91
+ if not os.path.exists(local_path):
92
+ print(f" SKIP {name}: {local_path} not found")
93
+ continue
94
+
95
+ remote_path = f"{REMOTE_BASE}/models/{os.path.basename(local_path)}"
96
+ print(f" Uploading {name}: {local_path} -> {remote_path}")
97
+ scp_client.put(local_path, remote_path)
98
+ uploaded.append((name, remote_path))
99
+
100
+ # Also upload external data files (.data or _data)
101
+ for ext in [".data", "_data"]:
102
+ data_local = local_path + ext if ext == ".data" else local_path.replace(".onnx", ".onnx" + ext)
103
+ # Check various naming patterns
104
+ for candidate in [local_path + ".data", local_path.replace(".onnx", ".onnx.data"),
105
+ local_path + "_data"]:
106
+ if os.path.exists(candidate):
107
+ data_remote = f"{REMOTE_BASE}/models/{os.path.basename(candidate)}"
108
+ print(f" + external data: {candidate}")
109
+ scp_client.put(candidate, data_remote)
110
+ break
111
+
112
+ scp_client.close()
113
+ print(f" Uploaded {len(uploaded)} models.")
114
+
115
+ # Verify uploads
116
+ out, _ = ssh_exec(ssh, f"ls -lh {REMOTE_BASE}/models/", log_output=False)
117
+ print(f" Remote files:\n{out}")
118
+
119
+ # =====================================================================
120
+ # PHASE 2: Build TensorRT engines
121
+ # =====================================================================
122
+ print("\n" + "=" * 70)
123
+ print("PHASE 2: Building TensorRT engines")
124
+ print("=" * 70)
125
+
126
+ # FP32 baseline: just build engine
127
+ # FP16: --fp16
128
+ # INT8 QDQ models: --int8 (they already have QDQ nodes)
129
+ # FP8 QDQ models: --fp8
130
+ # INT4 QDQ models: --int4
131
+
132
+ engine_configs = []
133
+
134
+ # FP32 baseline
135
+ engine_configs.append({
136
+ "name": "fp32",
137
+ "onnx": f"{REMOTE_BASE}/models/hgnetv2_b2_fp32.onnx",
138
+ "engine": f"{REMOTE_BASE}/results/hgnetv2_b2_fp32.plan",
139
+ "flags": "",
140
+ })
141
+
142
+ # FP16
143
+ engine_configs.append({
144
+ "name": "fp16",
145
+ "onnx": f"{REMOTE_BASE}/models/hgnetv2_b2_fp16.onnx",
146
+ "engine": f"{REMOTE_BASE}/results/hgnetv2_b2_fp16.plan",
147
+ "flags": "--fp16",
148
+ })
149
+
150
+ # INT8 QDQ models (already quantized, just need --int8 flag for TRT to recognize QDQ)
151
+ for name in ["int8_entropy", "int8_entropy_asym", "int8_max", "int8_max_asym"]:
152
+ onnx_file = f"{REMOTE_BASE}/models/hgnetv2_b2_{name}.onnx"
153
+ engine_configs.append({
154
+ "name": name,
155
+ "onnx": onnx_file,
156
+ "engine": f"{REMOTE_BASE}/results/hgnetv2_b2_{name}.plan",
157
+ "flags": "--int8 --fp16",
158
+ })
159
+
160
+ # FP8 QDQ models
161
+ for name in ["fp8_entropy", "fp8_max"]:
162
+ onnx_file = f"{REMOTE_BASE}/models/hgnetv2_b2_{name}.onnx"
163
+ engine_configs.append({
164
+ "name": name,
165
+ "onnx": onnx_file,
166
+ "engine": f"{REMOTE_BASE}/results/hgnetv2_b2_{name}.plan",
167
+ "flags": "--fp8 --fp16",
168
+ })
169
+
170
+ # INT4 QDQ models
171
+ for name in ["int4_rtn_dq", "int4_awq_clip", "int4_awq_lite", "int4_awq_full"]:
172
+ onnx_file = f"{REMOTE_BASE}/models/hgnetv2_b2_{name}.onnx"
173
+ engine_configs.append({
174
+ "name": name,
175
+ "onnx": onnx_file,
176
+ "engine": f"{REMOTE_BASE}/results/hgnetv2_b2_{name}.plan",
177
+ "flags": "--int4 --fp16",
178
+ })
179
+
180
+ build_results = {}
181
+ for cfg in engine_configs:
182
+ name = cfg["name"]
183
+ onnx = cfg["onnx"]
184
+ engine = cfg["engine"]
185
+ flags = cfg["flags"]
186
+
187
+ print(f"\n--- Building: {name} (flags: {flags or 'none'}) ---")
188
+
189
+ # Check if ONNX file exists on remote
190
+ check_out, _ = ssh_exec(ssh, f"test -f {onnx} && echo EXISTS || echo MISSING", log_output=False)
191
+ if "MISSING" in check_out:
192
+ print(f" SKIP: ONNX file not found on remote: {onnx}")
193
+ build_results[name] = {"status": "skipped", "reason": "onnx missing"}
194
+ continue
195
+
196
+ cmd = f"{TRTEXEC} --onnx={onnx} --saveEngine={engine} {flags} --tacticSources=+CUBLAS,+CUBLASLT 2>&1"
197
+ print(f" CMD: {cmd}")
198
+ t0 = time.time()
199
+ out, err = ssh_exec(ssh, cmd, timeout=600)
200
+ elapsed = time.time() - t0
201
+
202
+ # Check if engine was built
203
+ check_out, _ = ssh_exec(ssh, f"test -f {engine} && echo EXISTS || echo MISSING", log_output=False)
204
+ if "EXISTS" in check_out:
205
+ size_out, _ = ssh_exec(ssh, f"ls -lh {engine}", log_output=False)
206
+ print(f" SUCCESS: {size_out} ({elapsed:.1f}s)")
207
+ build_results[name] = {"status": "success", "engine": engine, "time": elapsed}
208
+ else:
209
+ print(f" FAILED ({elapsed:.1f}s)")
210
+ build_results[name] = {"status": "failed", "time": elapsed, "output": out[-500:] if out else ""}
211
+
212
+ # =====================================================================
213
+ # PHASE 3: Install onnxruntime on remote and evaluate
214
+ # =====================================================================
215
+ print("\n" + "=" * 70)
216
+ print("PHASE 3: Installing dependencies and evaluating on ImageNet")
217
+ print("=" * 70)
218
+
219
+ # Check if pip packages are available
220
+ pip_check, _ = ssh_exec(ssh, "pip3 list 2>/dev/null | grep -iE 'onnx|numpy|pillow|sklearn'", log_output=False)
221
+ if not pip_check.strip():
222
+ print(" Installing Python packages on remote...")
223
+ ssh_exec(ssh, "pip3 install onnxruntime numpy pillow scikit-learn 2>&1 | tail -5", timeout=300)
224
+
225
+ # Prepare calibration data as numpy on remote for evaluation
226
+ # We need to generate evaluation data on the remote side
227
+ # Since the remote may not have ImageNet, we'll upload a pre-built evaluation batch
228
+
229
+ print("\n Preparing evaluation data locally and uploading...")
230
+
231
+ # Build evaluation dataset from cached arrow shards
232
+ model = timm.create_model("hgnetv2_b2.ssld_stage2_ft_in1k", pretrained=True)
233
+ data_config = resolve_model_data_config(model)
234
+ transform = create_transform(**data_config, is_training=False)
235
+ del model
236
+
237
+ # Load from arrow shards
238
+ arrow_dir = os.path.expanduser(
239
+ "~/.cache/huggingface/datasets/Tsomaros___imagenet-1k_validation/"
240
+ "default/0.0.0/55405c49dece42420e68ddd5f80174f19b29ebaf/"
241
+ )
242
+
243
+ # Build a manageable subset for remote eval - use 2000 images for speed
244
+ # (full 41K would take very long over SSH)
245
+ N_EVAL = 2000
246
+ print(f" Loading {N_EVAL} evaluation images from arrow shards...")
247
+
248
+ all_images = []
249
+ all_labels = []
250
+
251
+ shard_files = sorted(
252
+ f for f in os.listdir(arrow_dir)
253
+ if f.startswith("imagenet-1k_validation-validation-") and f.endswith(".arrow")
254
+ )
255
+
256
+ count = 0
257
+ for fname in shard_files:
258
+ if count >= N_EVAL:
259
+ break
260
+ path = os.path.join(arrow_dir, fname)
261
+ try:
262
+ with open(path, "rb") as f:
263
+ reader = ipc.RecordBatchStreamReader(f)
264
+ table = reader.read_all()
265
+ except Exception:
266
+ continue
267
+
268
+ for i in range(len(table)):
269
+ if count >= N_EVAL:
270
+ break
271
+ img_bytes = table.column("image")[i].as_py()
272
+ if isinstance(img_bytes, dict):
273
+ img_bytes = img_bytes.get("bytes", img_bytes.get("path", b""))
274
+ if isinstance(img_bytes, bytes):
275
+ img = Image.open(io.BytesIO(img_bytes)).convert("RGB")
276
+ else:
277
+ continue
278
+ label = table.column("label")[i].as_py()
279
+ tensor = transform(img)
280
+ all_images.append(tensor.numpy())
281
+ all_labels.append(label)
282
+ count += 1
283
+ if count % 500 == 0:
284
+ print(f" [{count}/{N_EVAL}] images loaded")
285
+
286
+ images_np = np.stack(all_images) # (N, C, H, W)
287
+ labels_np = np.array(all_labels) # (N,)
288
+ print(f" Evaluation data: images={images_np.shape}, labels={labels_np.shape}")
289
+
290
+ # Save and upload
291
+ eval_images_path = "/tmp/hgnetv2_eval_images.npy"
292
+ eval_labels_path = "/tmp/hgnetv2_eval_labels.npy"
293
+ np.save(eval_images_path, images_np)
294
+ np.save(eval_labels_path, labels_np)
295
+
296
+ scp_client = scp.SCPClient(ssh.get_transport())
297
+ scp_client.put(eval_images_path, f"{REMOTE_BASE}/models/eval_images.npy")
298
+ scp_client.put(eval_labels_path, f"{REMOTE_BASE}/models/eval_labels.npy")
299
+ scp_client.close()
300
+ print(" Uploaded evaluation data.")
301
+
302
+ # =====================================================================
303
+ # PHASE 4: Run evaluation on remote via ONNX Runtime + TensorRT EP
304
+ # =====================================================================
305
+ print("\n" + "=" * 70)
306
+ print("PHASE 4: Running evaluation on remote platform")
307
+ print("=" * 70)
308
+
309
+ # Write evaluation script and upload
310
+ eval_script = r'''
311
+ import sys
312
+ import os
313
+ import time
314
+ import numpy as np
315
+
316
+ # Try ONNX Runtime with TensorRT EP first, fall back to CPU
317
+ try:
318
+ import onnxruntime as ort
319
+ print(f"ONNX Runtime version: {ort.__version__}")
320
+ print(f"Available providers: {ort.get_available_providers()}")
321
+ except ImportError:
322
+ print("ERROR: onnxruntime not installed")
323
+ sys.exit(1)
324
+
325
+ from sklearn.metrics import average_precision_score, precision_recall_fscore_support
326
+ import torch
327
+
328
+ BASE = "/data/rilin/hgnetv2"
329
+
330
+ # Load eval data
331
+ print("Loading evaluation data...")
332
+ images = np.load(f"{BASE}/models/eval_images.npy")
333
+ labels = np.load(f"{BASE}/models/eval_labels.npy")
334
+ print(f" Images: {images.shape}, Labels: {labels.shape}")
335
+ N = len(labels)
336
+ num_classes = 1000
337
+
338
+ # Compute metrics
339
+ def compute_metrics(logits, labels, num_classes):
340
+ probs = torch.softmax(torch.from_numpy(logits), dim=1).numpy()
341
+ preds = probs.argmax(axis=1)
342
+ N = len(labels)
343
+ top1 = (preds == labels).sum() / N
344
+ topk_vals = np.argsort(probs, axis=1)[:, ::-1]
345
+ top5 = sum(labels[i] in topk_vals[i, :5] for i in range(N)) / N
346
+ one_hot = np.zeros((N, num_classes), dtype=np.int32)
347
+ one_hot[np.arange(N), labels] = 1
348
+ aps = []
349
+ for c in range(num_classes):
350
+ if one_hot[:, c].sum() == 0: continue
351
+ try: ap = average_precision_score(one_hot[:, c], probs[:, c])
352
+ except: ap = 0.0
353
+ aps.append(ap)
354
+ mAP = np.mean(aps) if aps else 0.0
355
+ prec_mac, rec_mac, f1_mac, _ = precision_recall_fscore_support(labels, preds, average="macro", zero_division=0)
356
+ prec_wt, rec_wt, f1_wt, _ = precision_recall_fscore_support(labels, preds, average="weighted", zero_division=0)
357
+ return {"top1": float(top1), "top5": float(top5), "mAP": float(mAP),
358
+ "f1_macro": float(f1_mac), "f1_weighted": float(f1_wt)}
359
+
360
+ # Models to evaluate
361
+ models = {
362
+ "fp32": f"{BASE}/models/hgnetv2_b2_fp32.onnx",
363
+ "fp16": f"{BASE}/models/hgnetv2_b2_fp16.onnx",
364
+ "int8_entropy": f"{BASE}/models/hgnetv2_b2_int8_entropy.onnx",
365
+ "int8_entropy_asym": f"{BASE}/models/hgnetv2_b2_int8_entropy_asym.onnx",
366
+ "int8_max": f"{BASE}/models/hgnetv2_b2_int8_max.onnx",
367
+ "int8_max_asym": f"{BASE}/models/hgnetv2_b2_int8_max_asym.onnx",
368
+ "fp8_entropy": f"{BASE}/models/hgnetv2_b2_fp8_entropy.onnx",
369
+ "fp8_max": f"{BASE}/models/hgnetv2_b2_fp8_max.onnx",
370
+ "int4_rtn_dq": f"{BASE}/models/hgnetv2_b2_int4_rtn_dq.onnx",
371
+ "int4_awq_clip": f"{BASE}/models/hgnetv2_b2_int4_awq_clip.onnx",
372
+ "int4_awq_lite": f"{BASE}/models/hgnetv2_b2_int4_awq_lite.onnx",
373
+ "int4_awq_full": f"{BASE}/models/hgnetv2_b2_int4_awq_full.onnx",
374
+ }
375
+
376
+ # Evaluate each model
377
+ results = {}
378
+ for name, onnx_path in models.items():
379
+ if not os.path.exists(onnx_path):
380
+ print(f"\nSKIP {name}: file not found")
381
+ results[name] = {"error": "file not found"}
382
+ continue
383
+
384
+ print(f"\n{'='*50}")
385
+ print(f"Evaluating: {name}")
386
+ print(f" ONNX: {onnx_path}")
387
+
388
+ # Try TRT EP first, then CPU
389
+ sess = None
390
+ for providers in [
391
+ [("TensorrtExecutionProvider", {"trt_max_workspace_size": 2147483648}), "CPUExecutionProvider"],
392
+ ["CPUExecutionProvider"],
393
+ ]:
394
+ try:
395
+ opts = ort.SessionOptions()
396
+ opts.graph_optimization_level = ort.GraphOptimizationLevel.ORT_DISABLE_ALL
397
+ sess = ort.InferenceSession(onnx_path, sess_options=opts, providers=providers)
398
+ print(f" Session created with providers: {sess.get_providers()}")
399
+ break
400
+ except Exception as e:
401
+ print(f" Provider {providers} failed: {e}")
402
+ sess = None
403
+ continue
404
+
405
+ if sess is None:
406
+ print(f" FAILED: could not create session")
407
+ results[name] = {"error": "session creation failed"}
408
+ continue
409
+
410
+ input_name = sess.get_inputs()[0].name
411
+ all_logits = []
412
+ batch_size = 1 # batch=1 for safety with static shapes
413
+
414
+ t0 = time.time()
415
+ for i in range(N):
416
+ single = images[i:i+1]
417
+ out = sess.run(None, {input_name: single})
418
+ all_logits.append(out[0])
419
+ if (i+1) % 500 == 0:
420
+ elapsed = time.time() - t0
421
+ speed = (i+1) / elapsed
422
+ print(f" [{i+1}/{N}] {speed:.1f} img/s")
423
+
424
+ all_logits = np.concatenate(all_logits, axis=0)
425
+ elapsed = time.time() - t0
426
+
427
+ metrics = compute_metrics(all_logits, labels, num_classes)
428
+ metrics["elapsed"] = elapsed
429
+ metrics["speed"] = N / elapsed
430
+ results[name] = metrics
431
+
432
+ print(f" Top-1: {metrics['top1']*100:.3f}%")
433
+ print(f" Top-5: {metrics['top5']*100:.3f}%")
434
+ print(f" mAP: {metrics['mAP']:.4f}")
435
+ print(f" Time: {elapsed:.1f}s ({metrics['speed']:.1f} img/s)")
436
+
437
+ # Print comparison table
438
+ print(f"\n\n{'='*80}")
439
+ print("Evaluation Comparison Table (TensorRT Platform)")
440
+ print(f"{'='*80}")
441
+ print(f" {'Model':<25s} {'Top-1%':>8s} {'Top-5%':>8s} {'mAP':>8s} {'F1_mac':>8s} {'Speed':>10s}")
442
+ print(f" {'-'*25} {'-'*8} {'-'*8} {'-'*8} {'-'*8} {'-'*10}")
443
+
444
+ for name, m in results.items():
445
+ if "error" in m:
446
+ print(f" {name:<25s} FAILED: {m['error']}")
447
+ else:
448
+ print(f" {name:<25s} {m['top1']*100:>8.3f} {m['top5']*100:>8.3f} {m['mAP']:>8.4f} {m['f1_macro']:>8.4f} {m['speed']:>9.1f}/s")
449
+
450
+ print(f"\n Reference (timm): Top-1: 82.346% | Top-5: 96.394%")
451
+ print(f"{'='*80}")
452
+
453
+ # Save results as JSON
454
+ import json
455
+ with open(f"{BASE}/results/eval_results.json", "w") as f:
456
+ json.dump(results, f, indent=2)
457
+ print(f"\nResults saved to {BASE}/results/eval_results.json")
458
+ '''
459
+
460
+ # Upload eval script
461
+ script_remote = f"{REMOTE_BASE}/eval_trt.py"
462
+ scp_client = scp.SCPClient(ssh.get_transport())
463
+ scp_client.put(io.BytesIO(eval_script.encode()), script_remote)
464
+ scp_client.close()
465
+
466
+ # Run evaluation on remote
467
+ print("\n Running remote evaluation script...")
468
+ print(" (This will take a while - evaluating 12 models x 2000 images)")
469
+
470
+ # Run in background and poll
471
+ cmd = f"cd {REMOTE_BASE} && python3 {script_remote} 2>&1 | tee {REMOTE_BASE}/evaluate.log"
472
+ t0 = time.time()
473
+
474
+ stdin, stdout, stderr = ssh.exec_command(cmd, timeout=3600)
475
+
476
+ # Stream output
477
+ full_output = ""
478
+ while True:
479
+ line = stdout.readline()
480
+ if not line:
481
+ break
482
+ line = line.strip()
483
+ if line:
484
+ print(f" {line}")
485
+ full_output += line + "\n"
486
+
487
+ exit_code = stdout.channel.recv_exit_status()
488
+ elapsed = time.time() - t0
489
+ print(f"\n Remote evaluation completed in {elapsed:.1f}s (exit code: {exit_code})")
490
+
491
+ # =====================================================================
492
+ # PHASE 5: Also test with trtexec for performance benchmarking
493
+ # =====================================================================
494
+ print("\n" + "=" * 70)
495
+ print("PHASE 5: TensorRT engine build + trtexec benchmarking")
496
+ print("=" * 70)
497
+
498
+ # For each successfully built engine, run trtexec inference benchmark
499
+ for cfg in engine_configs:
500
+ name = cfg["name"]
501
+ engine = cfg["engine"]
502
+
503
+ check_out, _ = ssh_exec(ssh, f"test -f {engine} && echo EXISTS || echo MISSING", log_output=False)
504
+ if "EXISTS" not in check_out:
505
+ print(f" SKIP {name}: engine not built")
506
+ continue
507
+
508
+ print(f"\n--- Benchmarking: {name} ---")
509
+ cmd = f"{TRTEXEC} --loadEngine={engine} --iterations=100 --warmUp=10 2>&1 | tail -30"
510
+ out, err = ssh_exec(ssh, cmd, timeout=120)
511
+
512
+ # =====================================================================
513
+ # SUMMARY
514
+ # =====================================================================
515
+ print("\n" + "=" * 70)
516
+ print("FINAL SUMMARY")
517
+ print("=" * 70)
518
+
519
+ # Fetch results JSON from remote
520
+ try:
521
+ scp_client = scp.SCPClient(ssh.get_transport())
522
+ local_results_path = "/tmp/hgnetv2_remote_eval_results.json"
523
+ scp_client.get(f"{REMOTE_BASE}/results/eval_results.json", local_results_path)
524
+ scp_client.close()
525
+
526
+ with open(local_results_path) as f:
527
+ remote_results = json.load(f)
528
+
529
+ print("\nRemote Evaluation Results (ORT + TensorRT EP):")
530
+ print(f" {'Model':<25s} {'Top-1%':>8s} {'Top-5%':>8s} {'mAP':>8s}")
531
+ print(f" {'-'*25} {'-'*8} {'-'*8} {'-'*8}")
532
+ for name, m in remote_results.items():
533
+ if "error" in m:
534
+ print(f" {name:<25s} FAILED: {m['error']}")
535
+ else:
536
+ print(f" {name:<25s} {m['top1']*100:>8.3f} {m['top5']*100:>8.3f} {m['mAP']:>8.4f}")
537
+ except Exception as e:
538
+ print(f" Could not fetch results: {e}")
539
+
540
+ # Fetch the remote log
541
+ try:
542
+ scp_client = scp.SCPClient(ssh.get_transport())
543
+ scp_client.get(f"{REMOTE_BASE}/evaluate.log", "/tmp/hgnetv2_evaluate.log")
544
+ scp_client.close()
545
+ print(f"\nRemote log saved to /tmp/hgnetv2_evaluate.log")
546
+ except Exception as e:
547
+ print(f" Could not fetch log: {e}")
548
+
549
+ # Print build results
550
+ print("\nTensorRT Engine Build Results:")
551
+ print(f" {'Model':<25s} {'Status':>10s} {'Time':>8s}")
552
+ print(f" {'-'*25} {'-'*10} {'-'*8}")
553
+ for name, r in build_results.items():
554
+ status = r["status"]
555
+ t = f"{r.get('time', 0):.1f}s" if "time" in r else "-"
556
+ print(f" {name:<25s} {status:>10s} {t:>8s}")
557
+
558
+ ssh.close()
559
+ print("\nDone!")
560
+
561
+
562
+ if __name__ == "__main__":
563
+ main()
requirements.txt ADDED
@@ -0,0 +1,181 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ accelerate==1.13.0
2
+ aiohappyeyeballs==2.6.2
3
+ aiohttp==3.14.0
4
+ aiosignal==1.4.0
5
+ anaconda-anon-usage @ file:///home/task_177490767889375/croot/anaconda-anon-usage_1774907925443/work
6
+ anaconda-auth @ file:///home/task_177681132732760/croot/anaconda-cloud-auth-split_1776812130933/work
7
+ anaconda-cli-base @ file:///home/task_177546922169020/croot/anaconda-cli-base_1775469250358/work
8
+ annotated-doc==0.0.4
9
+ annotated-types @ file:///home/task_176174487847081/conda-bld/annotated-types_1761744928471/work
10
+ antlr4-python3-runtime==4.9.3
11
+ anyio @ file:///home/task_177313440431767/croot/anyio_1773134428538/work
12
+ archspec @ file:///home/task_175812491784513/conda-bld/archspec_1758124989039/work
13
+ attrs==26.1.0
14
+ audioop-lts==0.2.2
15
+ bcrypt==5.0.0
16
+ boltons @ file:///croot/boltons_1751383775419/work
17
+ brotli==1.2.0
18
+ brotlicffi @ file:///home/task_176496126124857/conda-bld/brotlicffi_1764961326114/work
19
+ certifi @ file:///home/task_177697098542695/croot/certifi_1776971111806/work/certifi
20
+ cffi==2.0.0
21
+ charset-normalizer @ file:///home/task_176174481524083/conda-bld/charset-normalizer_1761744828711/work
22
+ click==8.4.1
23
+ colorama==0.4.6
24
+ conda @ file:///home/task_177585116263960/croot/conda_1775852100438/work/conda-src
25
+ conda-anaconda-telemetry @ file:///croot/conda-anaconda-telemetry_1755883788794/work
26
+ conda-anaconda-tos @ file:///croot/conda-anaconda-tos_1755123313635/work
27
+ conda-content-trust @ file:///home/task_177526675282743/croot/conda-content-trust_1775266795331/work
28
+ conda-libmamba-solver @ file:///home/task_177680598199370/croot/conda-libmamba-solver_1776806057595/work/src
29
+ conda-package-handling @ file:///home/task_176236637997838/conda-bld/conda-package-handling_1762366405159/work
30
+ conda_package_streaming @ file:///home/task_176236152622180/conda-bld/conda-package-streaming_1762361633858/work
31
+ cppimport==26.4.17
32
+ cryptography @ file:///home/task_176009247093364/conda-bld/cryptography-split_1760092601830/work
33
+ cuda-bindings==13.3.1
34
+ cuda-pathfinder==1.5.5
35
+ cuda-toolkit==13.0.2
36
+ cupy-cuda12x==14.1.1
37
+ datasets==4.8.5
38
+ dill==0.4.1
39
+ distro @ file:///work/perseverance-python-buildout/croot/distro_1728396110052/work
40
+ evaluate==0.4.6
41
+ fastapi==0.136.3
42
+ filelock==3.29.1
43
+ flatbuffers==25.12.19
44
+ frozendict @ file:///home/task_176175049677026/conda-bld/frozendict_1761750665451/work
45
+ frozenlist==1.8.0
46
+ fsspec==2026.2.0
47
+ gradio==6.16.0
48
+ gradio_client==2.5.0
49
+ groovy==0.1.2
50
+ h11 @ file:///home/task_176193111687894/conda-bld/h11_1761931259172/work
51
+ hf-gradio==0.4.1
52
+ hf-xet==1.5.0
53
+ httpcore @ file:///croot/httpcore_1748526048470/work
54
+ httpx @ file:///home/task_176044729083783/conda-bld/httpx_1760447344657/work
55
+ huggingface_hub==1.17.0
56
+ idna @ file:///home/task_176191175488988/conda-bld/idna_1761911981595/work
57
+ invoke==3.0.3
58
+ jaraco.classes @ file:///croot/jaraco.classes_1755516327832/work
59
+ jaraco.context @ file:///home/task_176947759528748/croot/jaraco.context_1769477842447/work
60
+ jaraco.functools @ file:///home/task_176838874890348/croot/jaraco.functools_1768389366336/work
61
+ jeepney @ file:///tmp/build/80754af9/jeepney_1627537048313/work
62
+ Jinja2==3.1.6
63
+ joblib==1.5.3
64
+ jsonpatch @ file:///work/perseverance-python-buildout/croot/jsonpatch_1728399595941/work
65
+ jsonpointer @ file:///home/task_177443285493046/croot/jsonpointer_1774432880631/work
66
+ keyring @ file:///home/task_176363713576885/conda-bld/keyring_1763637184857/work
67
+ libmambapy @ file:///home/task_177669999816871/croot/mamba-split_1776700366233/work/libmambapy
68
+ lief==0.17.6
69
+ Mako==1.3.12
70
+ markdown-it-py @ file:///home/task_176700162971740/croot/markdown-it-py_1767001650253/work
71
+ MarkupSafe==3.0.3
72
+ mdurl @ file:///home/task_175855216352428/conda-bld/mdurl_1758552176751/work
73
+ menuinst @ file:///home/task_176538230483207/conda-bld/menuinst_1765382366331/work
74
+ ml_dtypes==0.5.4
75
+ mlx==0.31.2
76
+ more-itertools @ file:///home/task_177581903763312/croot/more-itertools_1775819395840/work
77
+ mpmath==1.3.0
78
+ msgpack @ file:///croot/msgpack-python_1750958623279/work
79
+ multidict==6.7.1
80
+ multiprocess==0.70.19
81
+ narwhals==2.22.0
82
+ networkx==3.6.1
83
+ ninja==1.13.0
84
+ numpy==2.4.6
85
+ nvidia-cublas==13.1.1.3
86
+ nvidia-cuda-cupti==13.0.85
87
+ nvidia-cuda-nvrtc==13.0.88
88
+ nvidia-cuda-runtime==13.0.96
89
+ nvidia-cudnn-cu13==9.20.0.48
90
+ nvidia-cufft==12.0.0.61
91
+ nvidia-cufile==1.15.1.6
92
+ nvidia-curand==10.4.0.35
93
+ nvidia-cusolver==12.0.4.66
94
+ nvidia-cusparse==12.6.3.3
95
+ nvidia-cusparselt-cu13==0.8.1
96
+ nvidia-ml-py==13.610.43
97
+ nvidia-modelopt==0.44.0
98
+ nvidia-nccl-cu13==2.29.7
99
+ nvidia-nvjitlink==13.0.88
100
+ nvidia-nvshmem-cu13==3.4.5
101
+ nvidia-nvtx==13.0.85
102
+ omegaconf==2.3.0
103
+ onnx==1.21.0
104
+ onnx-ir==0.2.1
105
+ onnx_graphsurgeon==0.6.1
106
+ onnxconverter-common==1.16.0
107
+ onnxruntime-gpu==1.24.4
108
+ onnxscript==0.7.0
109
+ onnxslim==0.1.94
110
+ opencv-python==4.13.0.92
111
+ orjson==3.11.9
112
+ packaging==26.0
113
+ pandas==3.0.3
114
+ paramiko==5.0.0
115
+ pillow==12.2.0
116
+ pkce @ file:///work/perseverance-python-buildout/croot/pkce_1728388093137/work
117
+ platformdirs @ file:///home/task_177365214898855/croot/platformdirs_1773652963129/work
118
+ pluggy @ file:///home/task_177697287633370/croot/pluggy_1776972917169/work
119
+ polygraphy==0.50.3
120
+ propcache==0.5.2
121
+ protobuf==7.35.0
122
+ psutil==7.2.2
123
+ PuLP==3.3.2
124
+ pyarrow==24.0.0
125
+ pybind11==3.0.4
126
+ pycosat @ file:///croot/pycosat_1736868416091/work
127
+ pycparser @ file:///home/task_177495969264897/croot/pycparser_1774959744365/work
128
+ pydantic @ file:///home/task_177676175720061/croot/pydantic_1776761799833/work
129
+ pydantic-settings @ file:///home/task_176416496069032/conda-bld/pydantic-settings_1764165165402/work
130
+ pydantic_core @ file:///home/task_177669498737047/croot/pydantic-core_1776695032910/work
131
+ pydub==0.25.1
132
+ Pygments @ file:///home/task_177512698095713/croot/pygments_1775127017410/work
133
+ PyJWT @ file:///home/task_177377321285166/croot/pyjwt_1773773790894/work
134
+ PyNaCl==1.6.2
135
+ PySocks @ file:///home/task_176175235884818/conda-bld/pysocks_1761753009495/work
136
+ python-dateutil==2.9.0.post0
137
+ python-dotenv @ file:///home/task_176855885656798/croot/python-dotenv_1768559688221/work
138
+ python-multipart==0.0.30
139
+ pytz==2026.2
140
+ PyYAML==6.0.3
141
+ readchar @ file:///home/task_176061336965958/conda-bld/readchar_1760613437376/work
142
+ regex==2026.5.9
143
+ requests @ file:///home/task_177506603358355/croot/requests_1775066075144/work
144
+ rich @ file:///home/task_176037557612320/conda-bld/rich_1760375632000/work
145
+ ruamel.yaml @ file:///home/task_176253595903435/conda-bld/ruamel.yaml_1762535972567/work
146
+ ruamel.yaml.clib @ file:///home/task_176253026438192/conda-bld/ruamel.yaml.clib_1762530278005/work
147
+ safehttpx==0.1.7
148
+ safetensors==0.7.0
149
+ scikit-learn==1.9.0
150
+ scipy==1.17.1
151
+ scp==0.15.0
152
+ SecretStorage @ file:///home/task_177512694364126/croot/secretstorage_1775127010526/work
153
+ semantic-version==2.10.0
154
+ semver @ file:///home/task_176190313959014/conda-bld/semver_1761903196627/work
155
+ setuptools==81.0.0
156
+ shellingham @ file:///home/task_176191203629823/conda-bld/shellingham_1761912204430/work
157
+ six==1.17.0
158
+ socksio==1.0.0
159
+ starlette==1.2.1
160
+ sympy==1.14.0
161
+ threadpoolctl==3.6.0
162
+ timm==1.0.27
163
+ tokenizers==0.22.2
164
+ tomli @ file:///home/task_176829646049141/croot/tomli_1768296482268/work
165
+ tomlkit @ file:///home/task_176289666312502/conda-bld/tomlkit_1762896676945/work
166
+ torch==2.12.0
167
+ torchvision==0.27.0
168
+ tqdm @ file:///home/task_177139827237424/croot/tqdm_1771398701478/work
169
+ transformers==5.10.2
170
+ triton==3.7.0
171
+ truststore @ file:///home/task_176252074050774/conda-bld/truststore_1762520903993/work
172
+ typer==0.20.0
173
+ typer-slim==0.20.0
174
+ typing-inspection @ file:///home/task_176061348700415/conda-bld/typing-inspection_1760614166875/work
175
+ typing_extensions @ file:///croot/typing_extensions_1756280817316/work
176
+ urllib3 @ file:///home/task_176781010304586/croot/urllib3_1767810267320/work
177
+ uvicorn==0.49.0
178
+ wheel==0.46.3
179
+ xxhash==3.7.0
180
+ yarl==1.24.2
181
+ zstandard @ file:///home/task_177696025748869/croot/zstandard_1776960539790/work
run.sh ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ python3 hgnetv2_b2_evaluate.py --save_model
2
+ python3 hgnetv2_b2_quantize_all.py
3
+ python3 hgnetv2_b2_eval_quantized.py
4
+
5
+ python remote_eval.py #ssh nvidia@192.168.8.108 (nvidia) 导出tensorrt,运行,把logits回传并进行分析。 仍有问题在改进
6
+
7
+ python3 regnet_x_800mf_evaluate.py --save_model
8
+ python3 regnet_x_800mf_quantize_all.py
9
+ python3 regnet_x_800mf_eval_quantized.py
10
+
11
+ python remote_eval.py #ssh nvidia@192.168.8.108 (nvidia) 导出tensorrt,运行,把logits回传并进行分析。 仍有问题在改进