Spaces:
Running
Running
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 +306 -0
- regnet_x_800mf_evaluate.py +454 -0
- regnet_x_800mf_quantize_all.py +213 -0
- remote_eval.py +563 -0
- requirements.txt +181 -0
- run.sh +11 -0
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回传并进行分析。 仍有问题在改进
|