add support regnety#3424
Merged
zhangyubo0722 merged 2 commits intoJul 14, 2026
Merged
Conversation
learncat163
force-pushed
the
feature/add-regenety
branch
from
June 15, 2026 11:32
cbc5dd5 to
de2f4da
Compare
Author
RegNetY 精度对齐验证1. 随机输入 logit 对比:回归原版regenetx模型和新加入的regnety单张随机输入 (seed=42, [1,3,224,224]),对比 timm 与 Paddle 最后一层输出。 RegNetX(12/12 通过,最大差异范围: 4.77e-06 ~ 2.19e-05)
RegNetY(12/12 通过,最大差异范围: 2.62e-06 ~ 6.68e-06)
验证代码import os
import sys
import re
import argparse
import subprocess
import json
import numpy as np
from pathlib import Path
from datetime import datetime
SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))
PROJ_ROOT = os.path.join(SCRIPT_DIR, "..", "..", "..", "..")
CONVERTED_DIR = os.path.join(SCRIPT_DIR, "paddle")
DOWNLOAD_DIR = os.path.join(SCRIPT_DIR, "timm")
REPORT_PATH = os.path.join(SCRIPT_DIR, "..", "docs", "diff_report.md")
LOG_PATH = os.path.join(SCRIPT_DIR, "..", "logs",
f"diff_{datetime.now().strftime('%Y%m%d_%H%M%S')}.log")
REGNETX_MODELS = {
"regnetx_002": {
"paddle_name": "RegNetX_200MF",
"cfg": {"w_a": 36.44, "w_0": 24, "w_m": 2.49, "d": 13, "group_w": 8},
},
"regnetx_004": {
"paddle_name": "RegNetX_400MF",
"cfg": {"w_a": 24.48, "w_0": 24, "w_m": 2.54, "d": 22, "group_w": 16},
},
"regnetx_006": {
"paddle_name": "RegNetX_600MF",
"cfg": {"w_a": 36.97, "w_0": 48, "w_m": 2.24, "d": 16, "group_w": 24},
},
"regnetx_008": {
"paddle_name": "RegNetX_800MF",
"cfg": {"w_a": 35.73, "w_0": 56, "w_m": 2.28, "d": 16, "group_w": 16},
},
"regnetx_016": {
"paddle_name": "RegNetX_1600MF",
"cfg": {"w_a": 34.01, "w_0": 80, "w_m": 2.25, "d": 18, "group_w": 24},
},
"regnetx_032": {
"paddle_name": "RegNetX_3200MF",
"cfg": {"w_a": 26.31, "w_0": 88, "w_m": 2.25, "d": 25, "group_w": 48},
},
"regnetx_040": {
"paddle_name": "RegNetX_4GF",
"cfg": {"w_a": 38.65, "w_0": 96, "w_m": 2.43, "d": 23, "group_w": 40},
},
"regnetx_064": {
"paddle_name": "RegNetX_6400MF",
"cfg": {"w_a": 60.83, "w_0": 184, "w_m": 2.07, "d": 17, "group_w": 56},
},
"regnetx_080": {
"paddle_name": "RegNetX_8GF",
"cfg": {"w_a": 49.56, "w_0": 80, "w_m": 2.88, "d": 23, "group_w": 120},
},
"regnetx_120": {
"paddle_name": "RegNetX_12GF",
"cfg": {"w_a": 73.36, "w_0": 168, "w_m": 2.37, "d": 19, "group_w": 112},
},
"regnetx_160": {
"paddle_name": "RegNetX_16GF",
"cfg": {"w_a": 55.59, "w_0": 216, "w_m": 2.1, "d": 22, "group_w": 128},
},
"regnetx_320": {
"paddle_name": "RegNetX_32GF",
"cfg": {"w_a": 69.86, "w_0": 320, "w_m": 2.0, "d": 23, "group_w": 168},
},
}
REGNETY_MODELS = {
"regnety_002": {"w_a": 36.44, "w_0": 24, "w_m": 2.49, "d": 13, "group_w": 8},
"regnety_004": {"w_a": 27.89, "w_0": 48, "w_m": 2.09, "d": 16, "group_w": 8},
"regnety_006": {"w_a": 32.54, "w_0": 48, "w_m": 2.32, "d": 15, "group_w": 16},
"regnety_008": {"w_a": 38.84, "w_0": 56, "w_m": 2.4, "d": 14, "group_w": 16},
"regnety_016": {"w_a": 20.71, "w_0": 48, "w_m": 2.65, "d": 27, "group_w": 24},
"regnety_032": {"w_a": 42.63, "w_0": 80, "w_m": 2.66, "d": 21, "group_w": 24},
"regnety_040": {"w_a": 31.41, "w_0": 96, "w_m": 2.24, "d": 22, "group_w": 64},
"regnety_064": {"w_a": 33.22, "w_0": 112, "w_m": 2.27, "d": 25, "group_w": 72},
"regnety_080": {"w_a": 76.82, "w_0": 192, "w_m": 2.19, "d": 17, "group_w": 56},
"regnety_120": {"w_a": 73.36, "w_0": 168, "w_m": 2.37, "d": 19, "group_w": 112},
"regnety_160": {"w_a": 106.23, "w_0": 200, "w_m": 2.48, "d": 18, "group_w": 112},
"regnety_320": {"w_a": 115.89, "w_0": 232, "w_m": 2.53, "d": 20, "group_w": 232},
}
MODEL_URLS = {
"RegNetX_200MF": "https://paddle-imagenet-models-name.bj.bcebos.com/dygraph/RegNetX_200MF_pretrained.pdparams",
"RegNetX_400MF": "https://paddle-imagenet-models-name.bj.bcebos.com/dygraph/RegNetX_400MF_pretrained.pdparams",
"RegNetX_600MF": "https://paddle-imagenet-models-name.bj.bcebos.com/dygraph/RegNetX_600MF_pretrained.pdparams",
"RegNetX_800MF": "https://paddle-imagenet-models-name.bj.bcebos.com/dygraph/RegNetX_800MF_pretrained.pdparams",
"RegNetX_1600MF": "https://paddle-imagenet-models-name.bj.bcebos.com/dygraph/RegNetX_1600MF_pretrained.pdparams",
"RegNetX_3200MF": "https://paddle-imagenet-models-name.bj.bcebos.com/dygraph/RegNetX_3200MF_pretrained.pdparams",
"RegNetX_4GF": "https://paddle-imagenet-models-name.bj.bcebos.com/dygraph/RegNetX_4GF_pretrained.pdparams",
"RegNetX_6400MF": "https://paddle-imagenet-models-name.bj.bcebos.com/dygraph/RegNetX_6400MF_pretrained.pdparams",
"RegNetX_8GF": "https://paddle-imagenet-models-name.bj.bcebos.com/dygraph/RegNetX_8GF_pretrained.pdparams",
"RegNetX_12GF": "https://paddle-imagenet-models-name.bj.bcebos.com/dygraph/RegNetX_12GF_pretrained.pdparams",
"RegNetX_16GF": "https://paddle-imagenet-models-name.bj.bcebos.com/dygraph/RegNetX_16GF_pretrained.pdparams",
"RegNetX_32GF": "https://paddle-imagenet-models-name.bj.bcebos.com/dygraph/RegNetX_32GF_pretrained.pdparams",
}
def log(msg):
print(msg)
os.makedirs(os.path.dirname(LOG_PATH), exist_ok=True)
with open(LOG_PATH, "a") as f:
f.write(msg + "\n")
def find_timm_weight(model_name):
"""Find timm weight in HuggingFace cache."""
hf_id = f"timm/{model_name}.pycls_in1k"
cache_dir = os.path.expanduser("~/.cache/huggingface/hub")
model_dir = os.path.join(cache_dir, f"models--{hf_id.replace('/', '--')}")
if not os.path.exists(model_dir):
return None
snapshots_dir = os.path.join(model_dir, "snapshots")
if not os.path.exists(snapshots_dir):
return None
for snap in os.listdir(snapshots_dir):
cand = os.path.join(snapshots_dir, snap, "model.safetensors")
if os.path.exists(cand):
return cand
return None
def download_timm_weight(model_name):
"""Download timm weight to HF cache."""
log(f" Downloading timm weight: {model_name}.pycls_in1k ...")
os.environ.setdefault("HF_ENDPOINT", "https://hf-mirror.com")
from huggingface_hub import hf_hub_download
hf_hub_download(f"timm/{model_name}.pycls_in1k", "model.safetensors")
log(f" Downloaded.")
return find_timm_weight(model_name)
def download_regnetx_pdparams(paddle_name):
"""Download RegNetX pdparams from PaddleClas MODEL_URLS."""
url = MODEL_URLS.get(paddle_name)
if not url:
log(f" [ERROR] No URL for {paddle_name}")
return None
fname = f"{paddle_name}_pretrained.pdparams"
out_path = os.path.join(DOWNLOAD_DIR, fname)
if os.path.exists(out_path):
return out_path
log(f" Downloading {paddle_name} from MODEL_URLS ...")
import urllib.request
os.makedirs(DOWNLOAD_DIR, exist_ok=True)
urllib.request.urlretrieve(url, out_path)
log(f" Downloaded ({os.path.getsize(out_path) // 1024 // 1024} MB)")
return out_path
def convert_regnety_if_needed(model_name):
"""Convert RegNetY from timm to pdparams if not in converted/."""
out_path = os.path.join(CONVERTED_DIR, f"{model_name}.pdparams")
if os.path.exists(out_path):
return out_path
weight_path = find_timm_weight(model_name)
if weight_path is None:
weight_path = download_timm_weight(model_name)
if weight_path is None:
return None
log(f" Converting {model_name} timm -> paddle ...")
from safetensors.torch import load_file
import torch
import paddle
from importlib.util import spec_from_file_location, module_from_spec
conv_script = os.path.join(SCRIPT_DIR, "..", "..", "weight", "scripts",
"20260608-convert-weight.py")
spec = spec_from_file_location("conv", conv_script)
conv_mod = module_from_spec(spec)
spec.loader.exec_module(conv_mod)
timm_sd = load_file(weight_path)
paddle_sd = {}
for timm_key, tensor in timm_sd.items():
if "num_batches_tracked" in timm_key:
continue
paddle_key = conv_mod.convert_key(timm_key)
if paddle_key is None:
continue
value = tensor.numpy()
value = conv_mod.convert_weight_value(timm_key, value)
paddle_sd[paddle_key] = paddle.to_tensor(value)
os.makedirs(CONVERTED_DIR, exist_ok=True)
paddle.save(paddle_sd, out_path)
log(f" Converted and saved to {out_path}")
return out_path
def prepare_weights(to_test):
"""Ensure all required weights are available. Returns skip list."""
skip = []
log("\n--- Preparing weights ---")
for name, info in to_test.items():
is_regnetx = name.startswith("regnetx")
log(f"\n [{name}]")
timm_path = find_timm_weight(name)
if timm_path is None:
log(f" timm weight not in cache, downloading...")
timm_path = download_timm_weight(name)
if timm_path is None:
log(f" [SKIP] Cannot get timm weight for {name}")
skip.append(name)
continue
else:
log(f" timm: OK")
if is_regnetx:
paddle_name = info["paddle_name"]
pdparams_path = os.path.join(DOWNLOAD_DIR,
f"{paddle_name}_pretrained.pdparams")
if not os.path.exists(pdparams_path):
pdparams_path = download_regnetx_pdparams(paddle_name)
else:
pdparams_path = convert_regnety_if_needed(name)
if pdparams_path is None or not os.path.exists(pdparams_path):
log(f" [SKIP] Cannot get pdparams for {name}")
skip.append(name)
else:
log(f" pdparams: OK ({pdparams_path})")
return skip
def test_model_subprocess(model_name, cfg, se_on, pdparams_path):
"""Run diff test in subprocess to avoid ParamAttr naming conflicts."""
is_regnetx = model_name.startswith("regnetx")
test_code = f'''
import sys, os, gc
sys.path.insert(0, {repr(PROJ_ROOT)})
os.environ["CUDA_VISIBLE_DEVICES"] = ""
import numpy as np
import paddle
paddle.set_device("cpu")
import torch
import timm
cfg = {cfg!r}
se_on = {se_on!r}
model_name = {model_name!r}
pdparams_path = {pdparams_path!r}
timm_model = timm.create_model(f"{{model_name}}.pycls_in1k", pretrained=True)
timm_model.eval()
np.random.seed(42)
img_np = np.random.randn(1, 3, 224, 224).astype(np.float32)
with torch.no_grad():
timm_out = timm_model(torch.from_numpy(img_np)).cpu().numpy()
del timm_model
gc.collect()
from ppcls.arch.backbone.model_zoo.regnet import RegNet
paddle_model = RegNet(
w_a=cfg["w_a"], w_0=cfg["w_0"], w_m=cfg["w_m"],
d=cfg["d"], group_w=cfg["group_w"],
bot_mul=1.0, q=8, se_on=se_on, class_num=1000)
param_dict = paddle.load(pdparams_path)
paddle_model.set_state_dict(param_dict)
paddle_model.eval()
with paddle.no_grad():
paddle_out = paddle_model(paddle.to_tensor(img_np)).numpy()
del paddle_model
gc.collect()
diff = np.abs(timm_out - paddle_out)
top1_match = np.argmax(timm_out) == np.argmax(paddle_out)
import json
print("JSON_RESULT:" + json.dumps({{
"max_diff": float(diff.max()),
"mean_diff": float(diff.mean()),
"top1_match": bool(top1_match)
}}))
'''
result = subprocess.run(
[sys.executable, "-c", test_code],
capture_output=True, text=True, timeout=600,
env={**os.environ, "CUDA_VISIBLE_DEVICES": ""})
if result.returncode != 0:
err_lines = result.stderr.strip().split('\n')
return None, None, None, err_lines[-1] if err_lines else "unknown error"
for line in result.stdout.strip().split('\n'):
if line.startswith("JSON_RESULT:"):
data = json.loads(line[len("JSON_RESULT:"):])
return data["max_diff"], data["mean_diff"], data["top1_match"], None
return None, None, None, "No JSON_RESULT in output"
def generate_report(results, report_path):
"""Generate markdown diff report."""
now = datetime.now().strftime("%Y-%m-%d %H:%M")
lines = [
"
f"> Generated: {now}\n\n",
"
"- Random input (seed=42): `[1, 3, 224, 224]` from N(0,1)\n",
"- RegNetX: paddle weight from `download/` (PaddleClas official)\n",
"- RegNetY: paddle weight from `converted/` (timm converted)\n",
"- timm model loaded from HuggingFace cache\n",
"- Target: max abs diff < 1e-4\n\n",
]
for variant, prefix in [("RegNetX", "regnetx"), ("RegNetY", "regnety")]:
variant_results = {k: v for k, v in results.items() if k.startswith(prefix)}
if not variant_results:
continue
lines.append(f"
lines.append("| Model | Max Diff | Mean Diff | Top1 Match | Result |\n")
lines.append("|-------|----------|-----------|------------|--------|\n")
for name, r in variant_results.items():
if r.get("error"):
lines.append(f"| {name} | ERROR | ERROR | - | FAIL: {r['error'][:50]} |\n")
else:
status = "PASS" if r["max_diff"] < 1e-4 else "FAIL"
top1 = "Yes" if r["top1_match"] else "No"
lines.append(
f"| {name} | {r['max_diff']:.2e} | {r['mean_diff']:.2e} "
f"| {top1} | {status} |\n"
)
lines.append("\n")
total = len(results)
passed = sum(1 for r in results.values()
if not r.get("error") and r["max_diff"] < 1e-4)
skipped = sum(1 for r in results.values() if r.get("skipped"))
lines.append(f"
f" (max diff < 1e-4), {skipped} skipped.\n")
os.makedirs(os.path.dirname(report_path), exist_ok=True)
with open(report_path, "w", encoding="utf-8") as f:
f.writelines(lines)
log(f"\nReport saved to: {report_path}")
def main():
parser = argparse.ArgumentParser()
parser.add_argument("--model", nargs="*", default=None)
parser.add_argument("--regnetx", action="store_true")
parser.add_argument("--regnety", action="store_true")
parser.add_argument("--all", action="store_true", default=True)
args = parser.parse_args()
to_test = {}
for name, info in REGNETX_MODELS.items():
to_test[name] = {"cfg": info["cfg"], "se_on": False,
"paddle_name": info["paddle_name"]}
for name, cfg in REGNETY_MODELS.items():
to_test[name] = {"cfg": cfg, "se_on": True}
if args.model:
to_test = {k: v for k, v in to_test.items() if k in args.model}
elif args.regnetx and not args.regnety:
to_test = {k: v for k, v in to_test.items() if k.startswith("regnetx")}
elif args.regnety and not args.regnetx:
to_test = {k: v for k, v in to_test.items() if k.startswith("regnety")}
log("=" * 70)
log("RegNet Diff Test: timm vs paddle (new weight loading)")
log("=" * 70)
log(f"Models to test: {len(to_test)}")
log(f" RegNetX: {sum(1 for k in to_test if k.startswith('regnetx'))}")
log(f" RegNetY: {sum(1 for k in to_test if k.startswith('regnety'))}")
skip = prepare_weights(to_test)
log("\n--- Running diff tests ---")
header = f"{'Model':<16} {'Max Diff':<12} {'Mean Diff':<12} {'Top1':<6} {'Result'}"
log(header)
log("-" * len(header))
results = {}
for name, info in to_test.items():
if name in skip:
log(f"{name:<16} SKIPPED (missing weights)")
results[name] = {"error": "skipped", "skipped": True}
continue
is_regnetx = name.startswith("regnetx")
if is_regnetx:
pdparams_path = os.path.join(
DOWNLOAD_DIR, f"{info['paddle_name']}_pretrained.pdparams")
else:
pdparams_path = os.path.join(CONVERTED_DIR, f"{name}.pdparams")
try:
max_diff, mean_diff, top1_match, error = test_model_subprocess(
name, info["cfg"], info["se_on"], pdparams_path)
if error:
log(f"{name:<16} ERROR: {error[:60]}")
results[name] = {"error": error}
else:
status = "PASS" if max_diff < 1e-4 else "FAIL"
top1_str = "Yes" if top1_match else "No"
log(f"{name:<16} {max_diff:<12.2e} {mean_diff:<12.2e} "
f"{top1_str:<6} {status}")
results[name] = {
"max_diff": float(max_diff),
"mean_diff": float(mean_diff),
"top1_match": bool(top1_match),
}
except Exception as e:
log(f"{name:<16} EXCEPTION: {e}")
results[name] = {"error": str(e)}
generate_report(results, REPORT_PATH)
if __name__ == "__main__":
main()2. ImageNet 验证集 RegNetY logit 逐样本对比在完整 ImageNet val (50000 张) 上,逐样本对比 HG cache (timm) 权重与转换后的 Paddle pdparams 的最后一层 logit 差异。
关键指标:
验证代码import os
import sys
import json
import argparse
import subprocess
import time
from pathlib import Path
from datetime import datetime
import numpy as np
SCRIPT_DIR = Path(__file__).resolve().parent
PROJ_ROOT = SCRIPT_DIR.parents[3]
HIS = SCRIPT_DIR.parents[2]
CONVERTED_DIR = HIS / "regnety" / "weight" / "converted"
IMAGENET_DIR = PROJ_ROOT / "dataset" / "imagenet"
DATA_DIR = SCRIPT_DIR.parent / "data"
LOG_DIR = SCRIPT_DIR.parent / "logs"
DOCS_DIR = SCRIPT_DIR.parent / "docs"
REGNETY_MODELS = {
"regnety_002": {"w_a": 36.44, "w_0": 24, "w_m": 2.49, "d": 13, "group_w": 8},
"regnety_004": {"w_a": 27.89, "w_0": 48, "w_m": 2.09, "d": 16, "group_w": 8},
"regnety_006": {"w_a": 32.54, "w_0": 48, "w_m": 2.32, "d": 15, "group_w": 16},
"regnety_008": {"w_a": 38.84, "w_0": 56, "w_m": 2.4, "d": 14, "group_w": 16},
"regnety_016": {"w_a": 20.71, "w_0": 48, "w_m": 2.65, "d": 27, "group_w": 24},
"regnety_032": {"w_a": 42.63, "w_0": 80, "w_m": 2.66, "d": 21, "group_w": 24},
"regnety_040": {"w_a": 31.41, "w_0": 96, "w_m": 2.24, "d": 22, "group_w": 64},
"regnety_064": {"w_a": 33.22, "w_0": 112, "w_m": 2.27, "d": 25, "group_w": 72},
"regnety_080": {"w_a": 76.82, "w_0": 192, "w_m": 2.19, "d": 17, "group_w": 56},
"regnety_120": {"w_a": 73.36, "w_0": 168, "w_m": 2.37, "d": 19, "group_w": 112},
"regnety_160": {"w_a": 106.23, "w_0": 200, "w_m": 2.48, "d": 18, "group_w": 112},
"regnety_320": {"w_a": 115.89, "w_0": 232, "w_m": 2.53, "d": 20, "group_w": 232},
}
LOGIT_THRESHOLD = 1e-4
ACC_THRESHOLD = 0.2
def log(msg):
print(msg)
LOG_DIR.mkdir(parents=True, exist_ok=True)
ts = datetime.now().strftime("%Y%m%d_%H%M%S")
log_file = LOG_DIR / f"regnety_logit_diff_{ts}.log"
with open(log_file, "a") as f:
f.write(msg + "\n")
def build_image_list(num_samples=None):
"""Build sorted image list and ground truth from ImageNet val."""
synset_dirs = sorted([d for d in IMAGENET_DIR.iterdir()
if d.is_dir() and d.name.startswith('n')])
if not synset_dirs:
raise FileNotFoundError(f"No synset dirs in {IMAGENET_DIR}")
synset_to_id = {d.name: idx for idx, d in enumerate(synset_dirs)}
image_paths = []
gt_labels = []
for d in synset_dirs:
cid = synset_to_id[d.name]
for img in sorted(d.glob('*.JPEG')):
image_paths.append(str(img))
gt_labels.append(cid)
if num_samples and num_samples < len(image_paths):
image_paths = image_paths[:num_samples]
gt_labels = gt_labels[:num_samples]
return image_paths, gt_labels
def run_diff_subprocess(model_name, cfg, pdparams_path, image_paths, gt_labels, batch_size):
"""Run timm + Paddle logit diff in an isolated subprocess.
Steps inside subprocess:
1. timm inference -> save all logits to temp file
2. Paddle inference -> load timm logits, compare per-batch
3. Output JSON stats
"""
tmp_logits = DATA_DIR / "tmp" / f"_timm_logits_{model_name}.npy"
tmp_logits.parent.mkdir(parents=True, exist_ok=True)
paths_file = DATA_DIR / "tmp" / f"_paths_{model_name}.txt"
gt_file = DATA_DIR / "tmp" / f"_gt_{model_name}.npy"
with open(paths_file, 'w') as f:
for p in image_paths:
f.write(p + '\n')
np.save(gt_file, np.array(gt_labels, dtype=np.int32))
test_code = f'''
import sys, os, gc, json, time
sys.path.insert(0, {repr(str(PROJ_ROOT))})
os.environ["CUDA_VISIBLE_DEVICES"] = "0"
os.environ["HF_ENDPOINT"] = "https://hf-mirror.com"
import numpy as np
import torch
import paddle
paddle.set_device("gpu")
model_name = {model_name!r}
cfg = {cfg!r}
pdparams_path = {repr(str(pdparams_path))}
paths_file = {repr(str(paths_file))}
gt_file = {repr(str(gt_file))}
logits_file = {repr(str(tmp_logits))}
batch_size = {batch_size}
IMAGENET_MEAN = [0.485, 0.456, 0.406]
IMAGENET_STD = [0.229, 0.224, 0.225]
with open(paths_file) as f:
image_paths = [l.strip() for l in f if l.strip()]
gt = np.load(gt_file)
n = len(image_paths)
import timm
from torch.utils.data import Dataset as TD, DataLoader as TDL
from PIL import Image as PILImage
from torchvision import transforms as tv_t
timm_name = f"{{model_name}}.pycls_in1k"
timm_model = timm.create_model(timm_name, pretrained=True)
timm_model.eval()
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
timm_model = timm_model.to(device)
crop_pct = 0.9
scale_size = int(224 / crop_pct)
transform = tv_t.Compose([
tv_t.Resize(scale_size, interpolation=tv_t.InterpolationMode.BICUBIC),
tv_t.CenterCrop(224),
tv_t.ToTensor(),
tv_t.Normalize(mean=IMAGENET_MEAN, std=IMAGENET_STD),
])
class _TDataset(TD):
def __init__(self, paths):
self.paths = paths
def __len__(self):
return len(self.paths)
def __getitem__(self, idx):
try:
img = PILImage.open(self.paths[idx]).convert('RGB')
return transform(img), idx
except:
return torch.zeros(3, 224, 224), idx
ds = _TDataset(image_paths)
loader = TDL(ds, batch_size=batch_size, shuffle=False, num_workers=4, drop_last=False)
timm_logits = np.zeros((n, 1000), dtype=np.float32)
timm_preds = np.zeros(n, dtype=np.int32)
t0 = time.time()
with torch.no_grad():
for imgs, idxs in loader:
imgs = imgs.to(device)
logits = timm_model(imgs).cpu().numpy()
idxs = idxs.numpy()
timm_logits[idxs] = logits
timm_preds[idxs] = logits.argmax(axis=1)
timm_time = time.time() - t0
timm_correct = int((timm_preds == gt).sum())
timm_acc = timm_correct / n * 100
np.save(logits_file, timm_logits)
del timm_model
gc.collect()
torch.cuda.empty_cache()
from ppcls.arch.backbone.model_zoo.regnet import RegNet
from paddle.io import Dataset as PD, DataLoader as PDL
from paddle.vision import transforms as paddle_t
paddle_model = RegNet(
w_a=cfg["w_a"], w_0=cfg["w_0"], w_m=cfg["w_m"],
d=cfg["d"], group_w=cfg["group_w"],
bot_mul=1.0, q=8, se_on=True, class_num=1000)
param_dict = paddle.load(pdparams_path)
paddle_model.set_dict(param_dict)
paddle_model.eval()
ss = int(224 / 0.9)
p_transform = paddle_t.Compose([
paddle_t.Resize(ss, interpolation='bicubic'),
paddle_t.CenterCrop(224),
paddle_t.ToTensor(),
paddle_t.Normalize(mean=IMAGENET_MEAN, std=IMAGENET_STD),
])
class _PDataset(PD):
def __init__(self, paths):
self.paths = paths
def __len__(self):
return len(self.paths)
def __getitem__(self, idx):
try:
img = PILImage.open(self.paths[idx]).convert('RGB')
return p_transform(img), idx
except:
return paddle.zeros([3, 224, 224], dtype='float32'), idx
p_ds = _PDataset(image_paths)
p_loader = PDL(p_ds, batch_size=batch_size, shuffle=False, num_workers=4, drop_last=False)
paddle_preds = np.zeros(n, dtype=np.int32)
global_max_diff = 0.0
global_mean_diff = 0.0
global_max_diff_sample = -1
sample_max_diffs = np.zeros(n, dtype=np.float32)
batch_count = 0
t0 = time.time()
for batch_imgs, batch_idxs in p_loader:
with paddle.no_grad():
p_logits = paddle_model(batch_imgs).numpy()
idxs = batch_idxs.numpy()
paddle_preds[idxs] = p_logits.argmax(axis=1)
t_logits_batch = timm_logits[idxs]
diff = np.abs(t_logits_batch - p_logits)
per_sample_max = diff.max(axis=1)
sample_max_diffs[idxs] = per_sample_max
batch_max = float(diff.max())
batch_mean = float(diff.mean())
if batch_max > global_max_diff:
global_max_diff = batch_max
global_max_diff_sample = int(idxs[per_sample_max.argmax()])
global_mean_diff += batch_mean
batch_count += 1
paddle_time = time.time() - t0
global_mean_diff /= batch_count
paddle_correct = int((paddle_preds == gt).sum())
paddle_acc = paddle_correct / n * 100
agree = int((timm_preds == paddle_preds).sum())
over_threshold = int((sample_max_diffs > 1e-4).sum())
import os
if os.path.exists(logits_file):
os.remove(logits_file)
result = {{
"timm_acc": round(timm_acc, 4),
"timm_correct": timm_correct,
"paddle_acc": round(paddle_acc, 4),
"paddle_correct": paddle_correct,
"acc_diff": round(abs(timm_acc - paddle_acc), 4),
"max_logit_diff": float(global_max_diff),
"mean_logit_diff": float(global_mean_diff),
"worst_sample_idx": global_max_diff_sample,
"agreement": agree,
"total": n,
"over_threshold": over_threshold,
"timm_time": round(timm_time, 2),
"paddle_time": round(paddle_time, 2),
}}
print("JSON_RESULT:" + json.dumps(result))
'''
env = {**os.environ, "CUDA_VISIBLE_DEVICES": "0"}
result = subprocess.run(
[sys.executable, "-c", test_code],
capture_output=True, text=True, timeout=1800, env=env)
for f in [paths_file, gt_file, tmp_logits]:
if f.exists():
f.unlink()
if result.returncode != 0:
err = result.stderr.strip().split('\n')[-1] if result.stderr.strip() else "unknown"
return {"error": err}
for line in result.stdout.strip().split('\n'):
if line.startswith("JSON_RESULT:"):
return json.loads(line[len("JSON_RESULT:"):])
return {"error": "No JSON_RESULT in output"}
def generate_report(results, start_time, end_time):
"""Generate markdown comparison report."""
DOCS_DIR.mkdir(parents=True, exist_ok=True)
report_path = DOCS_DIR / "regnety_logit_diff_report.md"
now = end_time.strftime("%Y-%m-%d %H:%M:%S")
duration = (end_time - start_time).total_seconds()
lines = [
"
f"> Generated: {now}\n",
f"> Duration: {duration:.1f}s\n\n",
"
"- **Dataset**: ImageNet val (50000 images, synset sorted)\n",
"- **HG Cache**: timm weights from HuggingFace cache\n",
"- **Paddle Params**: converted pdparams from `weight/converted/`\n",
"- **Thresholds**:\n",
f" - Last-layer logit max abs diff <= {LOGIT_THRESHOLD:.0e}\n",
f" - Top-1 Acc diff < {ACC_THRESHOLD}%\n",
"- **Preprocessing**: Resize(248, BICUBIC) -> CenterCrop(224) -> Normalize(ImageNet)\n\n",
"
]
all_pass = all(
r.get("max_logit_diff", 1) <= LOGIT_THRESHOLD and r.get("acc_diff", 100) < ACC_THRESHOLD
for r in results.values() if "error" not in r
)
pass_count = sum(
1 for r in results.values()
if "error" not in r and r["max_logit_diff"] <= LOGIT_THRESHOLD and r["acc_diff"] < ACC_THRESHOLD
)
fail_count = sum(1 for r in results.values() if "error" in r)
error_count = fail_count
lines.append(f"**{pass_count}/{len(results)} models passed** all thresholds.")
if error_count:
lines.append(f" {error_count} models had errors.")
lines.append("\n\n")
lines.append("
lines.append("| Model | timm Acc | Paddle Acc | Acc Diff | Max Logit Diff | Mean Logit Diff | Agree | Logit | Acc |\n")
lines.append("|-------|----------|-----------|---------|---------------|----------------|-------|-------|-----|\n")
for name, r in results.items():
if "error" in r:
lines.append(f"| {name} | ERROR | ERROR | - | - | - | - | FAIL | FAIL |\n")
continue
acc_sign = "+" if r["paddle_acc"] >= r["timm_acc"] else "-"
logit_ok = "PASS" if r["max_logit_diff"] <= LOGIT_THRESHOLD else "FAIL"
acc_ok = "PASS" if r["acc_diff"] < ACC_THRESHOLD else "FAIL"
lines.append(
f"| {name} | {r['timm_acc']:.2f}% | {r['paddle_acc']:.2f}% "
f"| {acc_sign}{r['acc_diff']:.2f}% "
f"| {r['max_logit_diff']:.2e} | {r['mean_logit_diff']:.2e} "
f"| {r['agreement']}/{r['total']} "
f"| {logit_ok} | {acc_ok} |\n"
)
lines.append("\n")
lines.append("
for name, r in results.items():
lines.append(f"
if "error" in r:
lines.append(f"**Error**: {r['error']}\n\n")
continue
lines.append("| Metric | Value |\n")
lines.append("|--------|-------|\n")
lines.append(f"| timm Top-1 Acc | {r['timm_acc']:.2f}% ({r['timm_correct']}/{r['total']}) |\n")
lines.append(f"| Paddle Top-1 Acc | {r['paddle_acc']:.2f}% ({r['paddle_correct']}/{r['total']}) |\n")
lines.append(f"| Acc Diff | {r['acc_diff']:.4f}% |\n")
lines.append(f"| Max Logit Diff | {r['max_logit_diff']:.6e} |\n")
lines.append(f"| Mean Logit Diff | {r['mean_logit_diff']:.6e} |\n")
lines.append(f"| Worst Sample Idx | {r['worst_sample_idx']} |\n")
lines.append(f"| Agreement | {r['agreement']}/{r['total']} ({r['agreement']/r['total']*100:.2f}%) |\n")
lines.append(f"| Samples > 1e-4 | {r['over_threshold']}/{r['total']} |\n")
lines.append(f"| timm Time | {r['timm_time']:.1f}s ({r['total']/r['timm_time']:.0f} img/s) |\n")
lines.append(f"| Paddle Time | {r['paddle_time']:.1f}s ({r['total']/r['paddle_time']:.0f} img/s) |\n\n")
logit_ok = "PASS" if r["max_logit_diff"] <= LOGIT_THRESHOLD else "FAIL"
acc_ok = "PASS" if r["acc_diff"] < ACC_THRESHOLD else "FAIL"
lines.append(f"**Verdict**: Logit={logit_ok}, Acc={acc_ok}\n\n")
lines.append("---\n\n")
lines.append(f"*Report generated: {now}*\n")
with open(report_path, "w", encoding="utf-8") as f:
f.writelines(lines)
log(f"Report saved: {report_path}")
return report_path
def main():
parser = argparse.ArgumentParser(description="RegNetY HG vs Paddle Logit Diff")
parser.add_argument("--model", nargs="*", default=None,
help="Specific model(s) to test")
parser.add_argument("--all", action="store_true",
help="Test all RegNetY models")
parser.add_argument("--batch-size", type=int, default=32)
parser.add_argument("--num-samples", type=int, default=0,
help="Limit samples (0=all)")
args = parser.parse_args()
if args.model:
models = {k: v for k, v in REGNETY_MODELS.items() if k in args.model}
else:
models = REGNETY_MODELS
if not models:
log("No models selected")
return 1
log("=" * 70)
log("RegNetY HG Cache vs Paddle Params - Logit Diff Validation")
log("=" * 70)
log(f"Models: {len(models)} ({', '.join(models.keys())})")
num_samples = args.num_samples if args.num_samples > 0 else None
image_paths, gt_labels = build_image_list(num_samples)
log(f"Images: {len(image_paths)}")
start_time = datetime.now()
results = {}
for i, (name, cfg) in enumerate(models.items(), 1):
pdparams = CONVERTED_DIR / f"{name}.pdparams"
if not pdparams.exists():
log(f"\n[{i}/{len(models)}] {name}: SKIP (no pdparams)")
results[name] = {"error": f"pdparams not found: {pdparams}"}
continue
log(f"\n[{i}/{len(models)}] {name}")
log(f" pdparams: {pdparams} ({pdparams.stat().st_size // 1024 // 1024} MB)")
t0 = time.time()
r = run_diff_subprocess(name, cfg, str(pdparams), image_paths, gt_labels, args.batch_size)
elapsed = time.time() - t0
if "error" in r:
log(f" ERROR: {r['error']}")
else:
logit_ok = "PASS" if r["max_logit_diff"] <= LOGIT_THRESHOLD else "FAIL"
acc_ok = "PASS" if r["acc_diff"] < ACC_THRESHOLD else "FAIL"
log(f" timm Acc: {r['timm_acc']:.2f}% Paddle Acc: {r['paddle_acc']:.2f}% "
f"Diff: {r['acc_diff']:.4f}% [{acc_ok}]")
log(f" Max Logit Diff: {r['max_logit_diff']:.2e} "
f"Mean: {r['mean_logit_diff']:.2e} [{logit_ok}]")
log(f" Agreement: {r['agreement']}/{r['total']} "
f"Over threshold: {r['over_threshold']}")
log(f" Time: {elapsed:.1f}s (timm {r['timm_time']:.1f}s + paddle {r['paddle_time']:.1f}s)")
results[name] = r
end_time = datetime.now()
report_path = generate_report(results, start_time, end_time)
log("\n" + "=" * 70)
log("Final Summary")
log("=" * 70)
header = f"{'Model':<16} {'timm Acc':>10} {'Paddle Acc':>10} {'Acc Diff':>10} {'Max Diff':>12} {'Logit':>6} {'Acc':>6}"
log(header)
log("-" * len(header))
for name, r in results.items():
if "error" in r:
log(f"{name:<16} {'ERROR':>10} {'ERROR':>10} {'-':>10} {'-':>12} {'FAIL':>6} {'FAIL':>6}")
continue
logit_ok = "PASS" if r["max_logit_diff"] <= LOGIT_THRESHOLD else "FAIL"
acc_ok = "PASS" if r["acc_diff"] < ACC_THRESHOLD else "FAIL"
log(f"{name:<16} {r['timm_acc']:>9.2f}% {r['paddle_acc']:>9.2f}% "
f"{r['acc_diff']:>9.4f}% {r['max_logit_diff']:>12.2e} {logit_ok:>6} {acc_ok:>6}")
log(f"\nReport: {report_path}")
return 0
if __name__ == "__main__":
sys.exit(main()) |
Author
3. RegNetX 代码回归测试新增 RegNetY 入口函数后,原有 RegNetX 12 个模型加载相同官方权重,logit 输出完全相同 (diff=0)。 验证代码import os
import sys
import subprocess
import json
import numpy as np
from datetime import datetime
SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))
PROJ_ROOT = os.path.join(SCRIPT_DIR, "..", "..", "..", "..")
DOWNLOAD_DIR = os.path.join(SCRIPT_DIR, "..", "..", "weight", "download")
ORIGINAL_CODE = os.path.join(SCRIPT_DIR, "..", "..", "0-code", "raw-code",
"regnet_original.py")
CURRENT_CODE = os.path.join(PROJ_ROOT, "ppcls", "arch", "backbone", "model_zoo",
"regnet.py")
DOCS_DIR = os.path.join(SCRIPT_DIR, "..", "docs")
LOG_PATH = os.path.join(SCRIPT_DIR, "..", "logs",
f"code_diff_{datetime.now().strftime('%Y%m%d_%H%M%S')}.log")
REGNETX_MODELS = {
"regnetx_002": {"paddle_name": "RegNetX_200MF",
"cfg": {"w_a": 36.44, "w_0": 24, "w_m": 2.49, "d": 13, "group_w": 8}},
"regnetx_004": {"paddle_name": "RegNetX_400MF",
"cfg": {"w_a": 24.48, "w_0": 24, "w_m": 2.54, "d": 22, "group_w": 16}},
"regnetx_006": {"paddle_name": "RegNetX_600MF",
"cfg": {"w_a": 36.97, "w_0": 48, "w_m": 2.24, "d": 16, "group_w": 24}},
"regnetx_008": {"paddle_name": "RegNetX_800MF",
"cfg": {"w_a": 35.73, "w_0": 56, "w_m": 2.28, "d": 16, "group_w": 16}},
"regnetx_016": {"paddle_name": "RegNetX_1600MF",
"cfg": {"w_a": 34.01, "w_0": 80, "w_m": 2.25, "d": 18, "group_w": 24}},
"regnetx_032": {"paddle_name": "RegNetX_3200MF",
"cfg": {"w_a": 26.31, "w_0": 88, "w_m": 2.25, "d": 25, "group_w": 48}},
"regnetx_040": {"paddle_name": "RegNetX_4GF",
"cfg": {"w_a": 38.65, "w_0": 96, "w_m": 2.43, "d": 23, "group_w": 40}},
"regnetx_064": {"paddle_name": "RegNetX_6400MF",
"cfg": {"w_a": 60.83, "w_0": 184, "w_m": 2.07, "d": 17, "group_w": 56}},
"regnetx_080": {"paddle_name": "RegNetX_8GF",
"cfg": {"w_a": 49.56, "w_0": 80, "w_m": 2.88, "d": 23, "group_w": 120}},
"regnetx_120": {"paddle_name": "RegNetX_12GF",
"cfg": {"w_a": 73.36, "w_0": 168, "w_m": 2.37, "d": 19, "group_w": 112}},
"regnetx_160": {"paddle_name": "RegNetX_16GF",
"cfg": {"w_a": 55.59, "w_0": 216, "w_m": 2.1, "d": 22, "group_w": 128}},
"regnetx_320": {"paddle_name": "RegNetX_32GF",
"cfg": {"w_a": 69.86, "w_0": 320, "w_m": 2.0, "d": 23, "group_w": 168}},
}
# 使用不同代码版本推理的子进程代码模板
INFERENCE_CODE = '''
import sys, os, gc, json
os.environ["CUDA_VISIBLE_DEVICES"] = ""
import numpy as np
import paddle
paddle.set_device("cpu")
# 读取指定版本的 regnet.py 源码,替换掉跨包导入后 exec
code_path = {code_path!r}
with open(code_path) as f:
source = f.read()
# 替换跨包导入为空实现,避免 ImportError
source = source.replace(
"from ....utils.save_load import load_dygraph_pretrain",
"def load_dygraph_pretrain(*a, **k): pass"
)
# 创建一个临时命名空间执行源码
_mod_ns = {{
"__name__": "regnet_version",
"__file__": code_path,
}}
exec(source, _mod_ns)
RegNet = _mod_ns["RegNet"]
cfg = {cfg!r}
pdparams_path = {pdparams_path!r}
np.random.seed(42)
img_np = np.random.randn(1, 3, 224, 224).astype("float32")
model = RegNet(
w_a=cfg["w_a"], w_0=cfg["w_0"], w_m=cfg["w_m"],
d=cfg["d"], group_w=cfg["group_w"],
bot_mul=1.0, q=8, se_on=False, class_num=1000)
param_dict = paddle.load(pdparams_path)
res = model.set_state_dict(param_dict)
model.eval()
with paddle.no_grad():
out = model(paddle.to_tensor(img_np)).numpy()[0]
# 保存完整张量到 npy 文件用于后续逐元素对比
out_path = {out_path!r}
os.makedirs(os.path.dirname(out_path), exist_ok=True)
np.save(out_path, out)
del model
gc.collect()
print("JSON_RESULT:" + json.dumps({{
"max": float(out.max()),
"min": float(out.min()),
"mean": float(out.mean()),
"top1": int(np.argmax(out)),
"top5": [int(x) for x in np.argsort(out)[-5:][::-1]],
"missing_keys": len(res[0]) if res else 0,
"unexpected_keys": len(res[1]) if res else 0,
}}))
'''
def log(msg):
print(msg, flush=True)
os.makedirs(os.path.dirname(LOG_PATH), exist_ok=True)
with open(LOG_PATH, "a") as f:
f.write(msg + "\n")
def run_one(model_name, cfg, pdparams_path, code_path, version_label, out_path):
"""Run inference in subprocess with a specific code version."""
code = INFERENCE_CODE.format(
proj_root=PROJ_ROOT, code_path=code_path,
cfg=cfg, pdparams_path=pdparams_path, out_path=out_path)
result = subprocess.run(
[sys.executable, "-c", code],
capture_output=True, text=True, timeout=300,
env={**os.environ, "CUDA_VISIBLE_DEVICES": ""})
if result.returncode != 0:
err = result.stderr.strip().split('\n')[-1] if result.stderr else "unknown"
return {"error": err[:200]}
for line in result.stdout.strip().split('\n'):
if line.startswith("JSON_RESULT:"):
return json.loads(line[len("JSON_RESULT:"):])
return {"error": "No JSON_RESULT"}
def generate_report(results, report_path):
"""Generate markdown report."""
now = datetime.now().strftime("%Y-%m-%d %H:%M")
lines = [
"# RegNetX 代码对比报告:原始代码 vs 当前代码\n\n",
f"> 生成时间: {now}\n\n",
"## 说明\n\n",
"从 git `aae1e95` 导出原始 regnet.py,与当前修改后的 regnet.py 对比。\n",
"两者加载相同的 PaddleClas 官方权重,使用相同的随机输入 (seed=42),记录推理输出。\n\n",
"## 结果\n\n",
"| 模型 | 原始 Top-1 | 当前 Top-1 | Top-1 一致 | 最大张量误差 | 平均张量误差 | 完全相同 |\n",
"|------|-----------|-----------|-----------|------------|------------|----------|\n",
]
for name, r in results.items():
if r.get("error"):
lines.append(f"| {name} | ERROR | - | - | - | - | - | - | - |\n")
continue
orig = r["original"]
curr = r["current"]
if orig.get("error") or curr.get("error"):
o_err = orig.get("error", "OK")[:30]
c_err = curr.get("error", "OK")[:30]
lines.append(f"| {name} | {o_err} | {c_err} | - | - | - | - |\n")
continue
top1_match = "Yes" if orig["top1"] == curr["top1"] else "No"
td = r.get("tensor_diff")
if td:
eq = "Yes" if td["exact_equal"] else "No"
lines.append(
f"| {name} | {orig['top1']} | {curr['top1']} "
f"| {top1_match} | {td['max_diff']:.2e} | {td['mean_diff']:.2e} | {eq} |\n"
)
else:
lines.append(
f"| {name} | {orig['top1']} | {curr['top1']} "
f"| {top1_match} | - | - | - |\n"
)
lines.append("\n## 结论\n\n")
all_match = all(
r.get("original", {}).get("top1") == r.get("current", {}).get("top1")
for r in results.values() if not r.get("error")
)
if all_match:
lines.append("所有模型的推理结果完全一致,**代码修改未破坏 RegNetX 的推理行为**。\n")
else:
mismatches = [n for n, r in results.items()
if not r.get("error") and r.get("original", {}).get("top1") != r.get("current", {}).get("top1")]
lines.append(f"有 {len(mismatches)} 个模型的 Top-1 预测不一致:{', '.join(mismatches)}。\n")
os.makedirs(os.path.dirname(report_path), exist_ok=True)
with open(report_path, "w", encoding="utf-8") as f:
f.writelines(lines)
log(f"\nReport saved to: {report_path}")
def main():
log("=" * 70)
log("RegNetX Code Diff: Original vs Current regnet.py")
log("=" * 70)
log(f"Original: {ORIGINAL_CODE}")
log(f"Current: {CURRENT_CODE}")
log(f"Models: {len(REGNETX_MODELS)}")
# 临时目录保存张量
tensor_dir = os.path.join(SCRIPT_DIR, "..", "data", "tensors")
os.makedirs(tensor_dir, exist_ok=True)
results = {}
for name, info in REGNETX_MODELS.items():
cfg = info["cfg"]
paddle_name = info["paddle_name"]
pdparams_path = os.path.join(DOWNLOAD_DIR, f"{paddle_name}_pretrained.pdparams")
if not os.path.exists(pdparams_path):
log(f"\n{name}: SKIP (pdparams not found)")
results[name] = {"error": "pdparams not found"}
continue
orig_npy = os.path.join(tensor_dir, f"{name}_original.npy")
curr_npy = os.path.join(tensor_dir, f"{name}_current.npy")
log(f"\n{'='*50}")
log(f"Model: {name} ({paddle_name})")
# 串行执行:先原始,再当前
log(f" [1/2] Original code...")
orig = run_one(name, cfg, pdparams_path, ORIGINAL_CODE, "original", orig_npy)
log(f" [2/2] Current code...")
curr = run_one(name, cfg, pdparams_path, CURRENT_CODE, "current", curr_npy)
if orig.get("error"):
log(f" Original ERROR: {orig['error'][:80]}")
else:
log(f" Original: top1={orig['top1']} top5={orig['top5']} "
f"range=[{orig['min']:.3f},{orig['max']:.3f}]")
if curr.get("error"):
log(f" Current ERROR: {curr['error'][:80]}")
else:
log(f" Current: top1={curr['top1']} top5={curr['top5']} "
f"range=[{curr['min']:.3f},{curr['max']:.3f}]")
# 逐元素张量对比
tensor_diff = None
if not orig.get("error") and not curr.get("error"):
if os.path.exists(orig_npy) and os.path.exists(curr_npy):
orig_tensor = np.load(orig_npy)
curr_tensor = np.load(curr_npy)
diff = np.abs(orig_tensor - curr_tensor)
tensor_diff = {
"max_diff": float(diff.max()),
"mean_diff": float(diff.mean()),
"exact_equal": bool(np.array_equal(orig_tensor, curr_tensor)),
}
eq_str = "IDENTICAL" if tensor_diff["exact_equal"] else f"max_diff={diff.max():.2e}"
match = "MATCH" if orig["top1"] == curr["top1"] else "DIFF"
log(f" Top-1: {match} | Tensor: {eq_str}")
else:
match = "MATCH" if orig["top1"] == curr["top1"] else "DIFF"
log(f" Top-1: {match} | Tensor: npy files not found")
results[name] = {"original": orig, "current": curr, "tensor_diff": tensor_diff}
report_path = os.path.join(DOCS_DIR, "code_diff_report.md")
generate_report(results, report_path)
if __name__ == "__main__":
main()4. ImageNet 验证集 Top-1 Acc 全量对比12 RegNetX + 12 RegNetY 共 24 个模型,Paddle 与 timm 的 Top-1 Acc 完全一致,Agreement >= 99.998%。
验证代码import os
import sys
import logging
import time
import argparse
from pathlib import Path
from datetime import datetime
from typing import Dict, List, Optional, Tuple
import numpy as np
import cv2
try:
from tqdm import tqdm
except ImportError:
tqdm = None
os.environ['HF_ENDPOINT'] = 'https://hf-mirror.com'
os.environ['CUDA_VISIBLE_DEVICES'] = '0'
PROJECT_ROOT = Path(__file__).resolve().parents[4]
sys.path.insert(0, str(PROJECT_ROOT))
IMAGENET_DEFAULT_MEAN = (0.485, 0.456, 0.406)
IMAGENET_DEFAULT_STD = (0.229, 0.224, 0.225)
REGNETX_CONFIGS = {
"regnetx_002": {"w_a": 36.44, "w_0": 24, "w_m": 2.49, "d": 13, "group_w": 8},
"regnetx_004": {"w_a": 24.48, "w_0": 24, "w_m": 2.54, "d": 22, "group_w": 16},
"regnetx_006": {"w_a": 36.97, "w_0": 48, "w_m": 2.24, "d": 16, "group_w": 24},
"regnetx_008": {"w_a": 35.73, "w_0": 56, "w_m": 2.28, "d": 16, "group_w": 16},
"regnetx_016": {"w_a": 34.01, "w_0": 80, "w_m": 2.25, "d": 18, "group_w": 24},
"regnetx_032": {"w_a": 26.31, "w_0": 88, "w_m": 2.25, "d": 25, "group_w": 48},
"regnetx_040": {"w_a": 38.65, "w_0": 96, "w_m": 2.43, "d": 23, "group_w": 40},
"regnetx_064": {"w_a": 60.83, "w_0": 184, "w_m": 2.07, "d": 17, "group_w": 56},
"regnetx_080": {"w_a": 49.56, "w_0": 80, "w_m": 2.88, "d": 23, "group_w": 120},
"regnetx_120": {"w_a": 73.36, "w_0": 168, "w_m": 2.37, "d": 19, "group_w": 112},
"regnetx_160": {"w_a": 55.59, "w_0": 216, "w_m": 2.1, "d": 22, "group_w": 128},
"regnetx_320": {"w_a": 69.86, "w_0": 320, "w_m": 2.0, "d": 23, "group_w": 168},
}
REGNETY_CONFIGS = {
"regnety_002": {"w_a": 36.44, "w_0": 24, "w_m": 2.49, "d": 13, "group_w": 8},
"regnety_004": {"w_a": 27.89, "w_0": 48, "w_m": 2.09, "d": 16, "group_w": 8},
"regnety_006": {"w_a": 32.54, "w_0": 48, "w_m": 2.32, "d": 15, "group_w": 16},
"regnety_008": {"w_a": 38.84, "w_0": 56, "w_m": 2.4, "d": 14, "group_w": 16},
"regnety_016": {"w_a": 20.71, "w_0": 48, "w_m": 2.65, "d": 27, "group_w": 24},
"regnety_032": {"w_a": 42.63, "w_0": 80, "w_m": 2.66, "d": 21, "group_w": 24},
"regnety_040": {"w_a": 31.41, "w_0": 96, "w_m": 2.24, "d": 22, "group_w": 64},
"regnety_064": {"w_a": 33.22, "w_0": 112, "w_m": 2.27, "d": 25, "group_w": 72},
"regnety_080": {"w_a": 76.82, "w_0": 192, "w_m": 2.19, "d": 17, "group_w": 56},
"regnety_120": {"w_a": 73.36, "w_0": 168, "w_m": 2.37, "d": 19, "group_w": 112},
"regnety_160": {"w_a": 106.23, "w_0": 200, "w_m": 2.48, "d": 18, "group_w": 112},
"regnety_320": {"w_a": 115.89, "w_0": 232, "w_m": 2.53, "d": 20, "group_w": 232},
}
ALL_MODEL_NAMES = list(REGNETX_CONFIGS.keys()) + list(REGNETY_CONFIGS.keys())
def setup_logging(log_file: str) -> logging.Logger:
log_dir = Path(log_file).parent
log_dir.mkdir(parents=True, exist_ok=True)
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s - %(levelname)s - %(message)s',
handlers=[
logging.FileHandler(log_file, encoding='utf-8'),
logging.StreamHandler()
]
)
return logging.getLogger(__name__)
logger = None
def get_model_cfg(model_name: str) -> Tuple[dict, bool]:
"""Return (config_dict, se_on) for a given model name."""
if model_name in REGNETX_CONFIGS:
return REGNETX_CONFIGS[model_name], False
if model_name in REGNETY_CONFIGS:
return REGNETY_CONFIGS[model_name], True
raise ValueError(f"Unknown model: {model_name}")
def paddle_inference_subprocess(
model_name: str,
weight_path: str,
image_paths: List[Path],
ground_truth: List[List[int]],
batch_size: int = 32,
predictions_dir: Optional[Path] = None
) -> dict:
"""Run Paddle inference in a subprocess to avoid ParamAttr name conflicts.
PaddlePaddle does not allow two models with the same ParamAttr(name=...)
in one process. Each model runs in an isolated subprocess.
"""
import subprocess
cfg, se_on = get_model_cfg(model_name)
num_images = len(image_paths)
gt_flat = [gt[0] for gt in ground_truth]
tmp_dir = Path(__file__).parent.parent / 'data' / 'tmp'
tmp_dir.mkdir(parents=True, exist_ok=True)
paths_file = tmp_dir / f"_topacc_{model_name}_paths.txt"
gt_file = tmp_dir / f"_topacc_{model_name}_gt.npy"
pred_out = tmp_dir / f"_topacc_{model_name}_pred.npy"
with open(paths_file, 'w') as f:
for p in image_paths:
f.write(str(p) + '\n')
np.save(gt_file, np.array(gt_flat, dtype=np.int32))
test_code = f'''
import sys, os, json
sys.path.insert(0, {repr(str(PROJECT_ROOT))})
os.environ["CUDA_VISIBLE_DEVICES"] = "0"
import numpy as np
import paddle
paddle.set_device("gpu")
from ppcls.arch.backbone.model_zoo.regnet import RegNet
from paddle.io import Dataset, DataLoader
from PIL import Image as PILImage
from paddle.vision import transforms as paddle_transforms
cfg = {cfg!r}
se_on = {se_on!r}
model_name = {model_name!r}
weight_path = {repr(str(weight_path))}
batch_size = {batch_size}
paths_file = {repr(str(paths_file))}
gt_file = {repr(str(gt_file))}
pred_out = {repr(str(pred_out))}
IMAGENET_MEAN = {list(IMAGENET_DEFAULT_MEAN)!r}
IMAGENET_STD = {list(IMAGENET_DEFAULT_STD)!r}
with open(paths_file) as f:
image_paths = [line.strip() for line in f if line.strip()]
gt = np.load(gt_file)
model = RegNet(
w_a=cfg["w_a"], w_0=cfg["w_0"], w_m=cfg["w_m"],
d=cfg["d"], group_w=cfg["group_w"],
bot_mul=1.0, q=8, se_on=se_on, class_num=1000)
param_dict = paddle.load(weight_path)
model.set_dict(param_dict)
model.eval()
class _ValDS(Dataset):
def __init__(self, paths, size=224, crop_pct=0.9):
self.paths = paths
ss = int(size / crop_pct)
self.transform = paddle_transforms.Compose([
paddle_transforms.Resize(ss, interpolation='bicubic'),
paddle_transforms.CenterCrop(size),
paddle_transforms.ToTensor(),
paddle_transforms.Normalize(mean=IMAGENET_MEAN, std=IMAGENET_STD),
])
def __len__(self):
return len(self.paths)
def __getitem__(self, idx):
try:
img = PILImage.open(self.paths[idx]).convert('RGB')
return self.transform(img), idx
except Exception:
return paddle.zeros([3, 224, 224], dtype='float32'), idx
predictions = np.zeros(len(image_paths), dtype=np.int32)
ds = _ValDS(image_paths)
loader = DataLoader(ds, batch_size=batch_size, shuffle=False,
num_workers=4, drop_last=False)
for batch_imgs, batch_idxs in loader:
with paddle.no_grad():
logits = model(batch_imgs)
preds = logits.argmax(axis=1).numpy()
idxs = batch_idxs.numpy()
predictions[idxs] = preds.astype(np.int32)
correct = int(np.sum(predictions == gt))
accuracy = correct / len(gt) * 100 if len(gt) > 0 else 0.0
np.save(pred_out, predictions)
print("JSON_RESULT:" + json.dumps({{
"correct": correct, "num_images": len(image_paths),
"top1_acc": float(accuracy)
}}))
'''
result = subprocess.run(
[sys.executable, "-c", test_code],
capture_output=True, text=True, timeout=600,
env={**os.environ, "CUDA_VISIBLE_DEVICES": "0"})
for f in [paths_file, gt_file]:
if f.exists():
f.unlink()
if result.returncode != 0:
err = result.stderr.strip().split('\n')[-1] if result.stderr.strip() else "unknown"
raise RuntimeError(f"Paddle subprocess failed: {err}")
for line in result.stdout.strip().split('\n'):
if line.startswith("JSON_RESULT:"):
import json
data = json.loads(line[len("JSON_RESULT:"):])
predictions = None
if pred_out.exists():
predictions = np.load(pred_out)
pred_out.unlink()
result_dict = {
'model_variant': model_name,
'framework': 'paddle',
'num_images': data['num_images'],
'valid_images': data['num_images'],
'correct': data['correct'],
'top1_acc': data['top1_acc'],
'predictions': predictions,
}
if predictions is not None and predictions_dir:
predictions_dir.mkdir(parents=True, exist_ok=True)
np.save(predictions_dir / f"{model_name}_predictions.npy", predictions)
logger.info(f"Predictions saved")
return result_dict
raise RuntimeError("No JSON_RESULT found in Paddle subprocess output")
def batch_inference_timm(
model_name: str,
image_paths: List[Path],
ground_truth: List[List[int]],
batch_size: int = 32,
predictions_dir: Optional[Path] = None
) -> dict:
"""timm batch inference (PyTorch)."""
import torch
import timm as _timm
from torch.utils.data import Dataset as TorchDataset, DataLoader as TorchDataLoader
from PIL import Image as PILImage
from torchvision import transforms as tv_transforms
timm_name = f"{model_name}.pycls_in1k"
logger.info(f"Loading timm model: {timm_name}")
model = _timm.create_model(timm_name, pretrained=True)
model.eval()
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
model = model.to(device)
logger.info(f"timm model loaded, device={device}")
IMAGENET_MEAN = list(IMAGENET_DEFAULT_MEAN)
IMAGENET_STD = list(IMAGENET_DEFAULT_STD)
crop_pct = 0.9
scale_size = int(224 / crop_pct)
transform = tv_transforms.Compose([
tv_transforms.Resize(scale_size, interpolation=tv_transforms.InterpolationMode.BICUBIC),
tv_transforms.CenterCrop(224),
tv_transforms.ToTensor(),
tv_transforms.Normalize(mean=IMAGENET_MEAN, std=IMAGENET_STD),
])
class _TimmValDataset(TorchDataset):
def __init__(self, paths):
self.paths = paths
def __len__(self):
return len(self.paths)
def __getitem__(self, idx):
try:
img = PILImage.open(str(self.paths[idx])).convert('RGB')
return transform(img), idx
except Exception:
return torch.zeros(3, 224, 224), idx
num_images = len(image_paths)
predictions = np.zeros(num_images, dtype=np.int32)
logger.info(f"timm inference: {num_images} images, batch_size={batch_size}")
dataset = _TimmValDataset(image_paths)
loader = TorchDataLoader(dataset, batch_size=batch_size, shuffle=False,
num_workers=4, drop_last=False)
iter_obj = tqdm(loader, desc=f"timm {model_name}") if tqdm else loader
with torch.no_grad():
for batch_imgs, batch_idxs in iter_obj:
batch_imgs = batch_imgs.to(device)
logits = model(batch_imgs)
preds = logits.argmax(dim=1).cpu().numpy()
idxs = batch_idxs.numpy()
predictions[idxs] = preds.astype(np.int32)
correct = sum(1 for i, pred in enumerate(predictions)
if ground_truth[i] and int(pred) in ground_truth[i])
valid = sum(1 for gt in ground_truth if gt)
accuracy = correct / valid * 100 if valid > 0 else 0.0
result = {
'model_variant': model_name,
'framework': 'timm',
'num_images': num_images,
'valid_images': valid,
'correct': correct,
'top1_acc': float(accuracy),
'predictions': predictions,
}
if predictions_dir:
predictions_dir.mkdir(parents=True, exist_ok=True)
np.save(predictions_dir / f"{model_name}_timm_predictions.npy", predictions)
logger.info("timm predictions saved")
return result
def generate_report(results: List[Dict], start_time: datetime, end_time: datetime, args):
"""Generate markdown validation report."""
output_dir = Path(__file__).parent.parent / 'docs'
output_dir.mkdir(parents=True, exist_ok=True)
report_file = output_dir / 'topacc_validation_report.md'
paddle_results = {r['model_variant']: r for r in results if r.get('framework') == 'paddle'}
timm_results = {r['model_variant']: r for r in results if r.get('framework') == 'timm'}
all_variants = list(dict.fromkeys(r['model_variant'] for r in results))
def sort_key(name):
family = 0 if name.startswith('regnetx') else 1
size = int(name.split('_')[1])
return (family, size)
all_variants.sort(key=sort_key)
with open(report_file, 'w', encoding='utf-8') as f:
f.write("
f.write(f"**Generated**: {end_time.strftime('%Y-%m-%d %H:%M:%S')}\n")
f.write(f"**Duration**: {(end_time - start_time).total_seconds():.2f}s\n\n")
f.write("---\n\n")
f.write("
f.write(f"- **Dataset**: ImageNet val (synset sorted, single-label)\n")
f.write(f"- **Images**: {results[0]['num_images'] if results else 'N/A'}\n")
f.write(f"- **Batch size**: {args.batch_size}\n")
f.write(f"- **Models**: {len(all_variants)}\n")
f.write(f"- **Threshold**: |Paddle - timm| < 0.2%\n\n")
f.write("
f.write("| Model | Paddle Top-1 | timm Top-1 | Diff | Pass |\n")
f.write("|-------|-------------|-----------|------|------|\n")
for variant in all_variants:
p = paddle_results.get(variant)
t = timm_results.get(variant)
if p and t:
diff = abs(p['top1_acc'] - t['top1_acc'])
passed = "PASS" if diff < 0.2 else "FAIL"
sign = "+" if p['top1_acc'] >= t['top1_acc'] else "-"
f.write(f"| {variant} | {p['top1_acc']:.2f}% | {t['top1_acc']:.2f}% "
f"| {sign}{diff:.2f}% | {passed} |\n")
elif p:
f.write(f"| {variant} | {p['top1_acc']:.2f}% | N/A | N/A | N/A |\n")
elif t:
f.write(f"| {variant} | N/A | {t['top1_acc']:.2f}% | N/A | N/A |\n")
f.write("\n")
f.write("
for variant in all_variants:
f.write(f"
for tag, res in [('Paddle', paddle_results.get(variant)),
('timm', timm_results.get(variant))]:
if not res:
continue
f.write(f"**{tag}**:\n")
f.write(f"- Correct / Total: {res['correct']} / {res['num_images']}\n")
f.write(f"- Top-1 Acc: {res['top1_acc']:.2f}%\n")
if 'inference_time' in res:
f.write(f"- Time: {res['inference_time']:.2f}s "
f"({res['num_images']/res['inference_time']:.0f} img/s)\n")
if variant in paddle_results and variant in timm_results:
p_preds = paddle_results[variant].get('predictions')
t_preds = timm_results[variant].get('predictions')
if p_preds is not None and t_preds is not None:
agree = int(np.sum(p_preds == t_preds))
total = len(p_preds)
f.write(f"\n**Agreement**: {agree}/{total} ({agree/total*100:.2f}%)\n")
f.write("\n")
f.write("
f.write("```\n")
f.write("crop_pct=0.9, scale_size=int(224/0.9)=248\n")
f.write("1. Resize(short edge to 248, BICUBIC)\n")
f.write("2. CenterCrop(224)\n")
f.write("3. Normalize(mean=(0.485,0.456,0.406), std=(0.229,0.224,0.225))\n")
f.write("```\n\n")
f.write("---\n\n")
f.write(f"*Report generated: {end_time.strftime('%Y-%m-%d %H:%M:%S')}*\n")
logger.info(f"Report saved: {report_file}")
return report_file
def main():
parser = argparse.ArgumentParser(
description='RegNet ImageNet Top-1 Accuracy Validation',
formatter_class=argparse.RawDescriptionHelpFormatter,
epilog="""
Examples:
python topacc.py --model all
python topacc.py --model regnety_002
python topacc.py --model regnety_002 --num-samples 100
python topacc.py --model all --paddle-only
"""
)
parser.add_argument(
'--model', type=str, default='all',
choices=['all'] + ALL_MODEL_NAMES,
help='Model variant (default: all)')
parser.add_argument(
'--batch-size', type=int, default=32,
help='Batch size (default: 32)')
parser.add_argument(
'--num-samples', type=int, default=50000,
help='Limit validation images (default: 50000)')
parser.add_argument(
'--paddle-only', action='store_true',
help='Only run Paddle inference')
parser.add_argument(
'--timm-only', action='store_true',
help='Only run timm inference')
args = parser.parse_args()
HIS_dir = Path(__file__).parents[3]
base_dir = HIS_dir / 'regnety' / '3-diff'
weight_dir = HIS_dir / 'regnety' / 'weight' / 'converted'
output_dir = base_dir / 'docs'
log_dir = base_dir / 'logs'
imagenet_base = PROJECT_ROOT / 'dataset' / 'imagenet'
log_dir.mkdir(parents=True, exist_ok=True)
output_dir.mkdir(parents=True, exist_ok=True)
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
log_file = log_dir / f'topacc_{timestamp}.log'
global logger
logger = setup_logging(str(log_file))
start_time = datetime.now()
logger.info("=" * 80)
logger.info("RegNet ImageNet Top-1 Accuracy Validation")
logger.info("=" * 80)
logger.info(f"Start: {start_time.strftime('%Y-%m-%d %H:%M:%S')}")
logger.info(f"Model: {args.model}")
logger.info(f"Batch size: {args.batch_size}")
logger.info(f"Samples: {args.num_samples}")
logger.info("\n" + "=" * 60)
logger.info("Step 1: Prepare validation data")
logger.info("=" * 60)
synset_dirs = sorted([d for d in imagenet_base.iterdir()
if d.is_dir() and d.name.startswith('n')])
if not synset_dirs:
logger.error(f"No synset dirs found: {imagenet_base}")
return 1
logger.info(f"Found {len(synset_dirs)} synset dirs")
synset_to_classid = {d.name: idx for idx, d in enumerate(synset_dirs)}
all_image_paths = []
all_gt_labels = []
for d in synset_dirs:
class_id = synset_to_classid[d.name]
for img_path in sorted(d.glob('*.JPEG')):
all_image_paths.append(img_path)
all_gt_labels.append([class_id])
logger.info(f"Found {len(all_image_paths)} images (all with GT labels)")
num_samples = min(args.num_samples, len(all_image_paths))
image_files = all_image_paths[:num_samples]
ground_truth = all_gt_labels[:num_samples]
logger.info(f"Validation samples: {num_samples}")
if args.model == 'all':
model_names = ALL_MODEL_NAMES
else:
model_names = [args.model]
logger.info(f"\nModels to validate: {', '.join(model_names)}")
logger.info("\n" + "=" * 60)
logger.info("Step 2: CUDA warmup")
logger.info("=" * 60)
import paddle
_dummy = paddle.zeros([1], dtype='float32')
_dummy = paddle.nn.functional.relu(_dummy)
del _dummy
logger.info("CUDA warmup done")
logger.info("\n" + "=" * 60)
logger.info("Step 3: Batch inference")
logger.info("=" * 60)
predictions_dir = base_dir / 'data' / 'predictions'
predictions_dir.mkdir(parents=True, exist_ok=True)
all_results = []
for model_name in model_names:
logger.info(f"\n{'=' * 50}")
logger.info(f"Model: {model_name}")
logger.info(f"{'=' * 50}")
if not args.paddle_only:
try:
t_start = time.time()
timm_result = batch_inference_timm(
model_name, image_files, ground_truth,
args.batch_size, predictions_dir)
timm_result['inference_time'] = time.time() - t_start
all_results.append(timm_result)
logger.info(f" [timm] Top-1: {timm_result['top1_acc']:.2f}% "
f"({timm_result['correct']}/{timm_result['num_images']})")
except Exception as e:
logger.error(f"timm inference failed for {model_name}: {e}")
import traceback; traceback.print_exc()
if not args.timm_only:
weight_path = weight_dir / f"{model_name}.pdparams"
try:
p_start = time.time()
result = paddle_inference_subprocess(
model_name, str(weight_path), image_files, ground_truth,
args.batch_size, predictions_dir)
result['inference_time'] = time.time() - p_start
all_results.append(result)
logger.info(f" [Paddle] Top-1: {result['top1_acc']:.2f}% "
f"({result['correct']}/{result['num_images']})")
timm_acc = next((r['top1_acc'] for r in all_results
if r['model_variant'] == model_name
and r.get('framework') == 'timm'), None)
if timm_acc is not None:
diff = abs(result['top1_acc'] - timm_acc)
status = "PASS" if diff < 0.2 else "FAIL"
logger.info(f" [Diff] |Paddle - timm| = {diff:.2f}% -> {status} (<0.2%)")
except Exception as e:
logger.error(f"Paddle inference failed for {model_name}: {e}")
import traceback; traceback.print_exc()
if all_results:
logger.info("\n" + "=" * 60)
logger.info("Step 4: Generate report")
logger.info("=" * 60)
end_time = datetime.now()
generate_report(all_results, start_time, end_time, args)
logger.info("\n" + "=" * 80)
logger.info("Validation complete")
logger.info("=" * 80)
if all_results:
paddle_map = {r['model_variant']: r for r in all_results
if r.get('framework') == 'paddle'}
timm_map = {r['model_variant']: r for r in all_results
if r.get('framework') == 'timm'}
logger.info(f"\n{'Model':<20} {'Paddle':>10} {'timm':>10} {'Diff':>8} {'Pass':>6}")
logger.info(" " + "-" * 58)
for model_name in model_names:
p_acc = paddle_map[model_name]['top1_acc'] if model_name in paddle_map else float('nan')
t_acc = timm_map[model_name]['top1_acc'] if model_name in timm_map else float('nan')
if not (p_acc != p_acc or t_acc != t_acc):
diff = abs(p_acc - t_acc)
ok = "OK" if diff < 0.2 else "FAIL"
logger.info(f" {model_name:<20} {p_acc:>9.2f}% {t_acc:>9.2f}% {diff:>7.2f}% {ok:>6}")
elif p_acc == p_acc:
logger.info(f" {model_name:<20} {p_acc:>9.2f}% {'N/A':>10} {'N/A':>8} {'N/A':>6}")
else:
logger.info(f" {model_name:<20} {'N/A':>10} {t_acc:>9.2f}% {'N/A':>8} {'N/A':>6}")
end_time = datetime.now()
logger.info(f"\nTotal: {(end_time - start_time).total_seconds():.2f}s")
logger.info(f"Report: {output_dir / 'topacc_validation_report.md'}")
return 0
if __name__ == "__main__":
sys.exit(main()) |
zhangyubo0722
approved these changes
Jul 14, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
RegNetY模型权重
paddle-aistudio在线演示demo
1. 模型描述
RegNet中的RegNetX已经在paddle中实现。而RegNetY 在 RegNetX 基础上引入 Squeeze-and-Excitation (SE) 注意力模块,但是PaddleClas还没有实现。本提交在原有的代码基础上,增加了RegNetY的支持。
2. 预训练权重
2.1 整体权重概览
权重按来源分为两类:
ppcls/arch/backbone/model_zoo/regnet.py这块不再处理,已经内置,可http自动下载RegNetY(timm 权重转换)
timm/regnety_002.pycls_in1kregnety_002.pdparamstimm/regnety_004.pycls_in1kregnety_004.pdparamstimm/regnety_006.pycls_in1kregnety_006.pdparamstimm/regnety_008.pycls_in1kregnety_008.pdparamstimm/regnety_016.pycls_in1kregnety_016.pdparamstimm/regnety_032.pycls_in1kregnety_032.pdparamstimm/regnety_040.pycls_in1kregnety_040.pdparamstimm/regnety_064.pycls_in1kregnety_064.pdparamstimm/regnety_080.pycls_in1kregnety_080.pdparamstimm/regnety_120.pycls_in1kregnety_120.pdparamstimm/regnety_160.pycls_in1kregnety_160.pdparamstimm/regnety_320.pycls_in1kregnety_320.pdparams2.2 权重获取说明
RegNetX - 从 PaddleClas 官方 URL 直接下载(定义在
ppcls/arch/backbone/model_zoo/regnet.py的MODEL_URLS):RegNetY - 先下载 timm 权重,再转换为 Paddle 格式。
转换过程中的关键处理:转换的过程,尽可能遵循了原来在处理RegNetX的原有风格,尽可能少的改动原有代码的基础上,实现了对RegNetY的支持。
3. 精度对齐
3.1 原版的RegNetX的精度对齐
因为本提交是在原版的RegNetX的基础上,迭代增强出的支持RegNetY,所以在新增了RegNetY之后,必须能和原版的RegNetX保持严格对齐。
经过分析,PaddleClas内置的RegNetX上的权重和Timm上类似名称的权重值不能一一对应,可能是来自其他的权重数据。
所以原版RegNetX的对齐要求,这里使用原始的git代码仓库中的老版本代码和本次提交的新代码,分别对同一个pdparams的权重进行加载推理,并对比输出的logit的差异。
3.1.1 输入随机向量的top1 acc对比
所有模型的top1相同。
3.1.2 使用相同的输入进行推理,对比logit张量值
已经经过验证,虽然支持升级了RegNetY,但是原来的RegNetX模型输出的张量,在新旧代码上的值完全一致。
3.2 前向推理精度对齐
通过加载转换后的 Paddle 权重,与 PyTorch timm 原版模型进行前向推理对比:
RegNetY
3.3 ImageNet 验证集精度验证
在 ImageNet 验证集(5万张)上验证模型的 Top-1 准确率:
RegNetY
所有模型的 Top-1 准确率与 timm 版本完全一致,误差为 0.00%,满足精度要求。