PatchCore NPU — Ascend NPU 适配版

基于 PatchCore-Inspection 的昇腾 NPU 推理适配。

仓库: gjj105/patchcore-npu

5 个核心修改

# 修改 源实现 NPU 实现 说明
1 FAISS → torch.mm faiss.IndexFlatL2 TorchNN (L2 = a²+b²-2abᵀ) 消除 FAISS 依赖,使用 CANN GEMM
2 GreedyCoreset ~0.7% sampler.py 百分比可配 --sampling_percentage 0.007 31980 → ~235 patches
3 Device 抽象 .cuda() 硬编码 .to(device) + NPU auto-detect 自动识别 npu/cuda/cpu
4 NPU 高斯模糊 scipy.ndimage.gaussian_filter F.conv2d + Gaussian kernel 避免 scipy CPU 拷贝
5 环境变量 TASK_QUEUE_ENABLE=1, NPU_FP16_MATMUL=1 NPU 推理加速

交付件结构

patchcore-npu/
├── inference.py              # 主推理入口(含全部5项修改)
├── run.log                   # NPU 运行日志
├── README_NPU.md             # 本文档(NPU 适配说明)
├── README.md                 # 原始 PatchCore 项目说明
├── scripts/
│   ├── torch_nn.py           # TorchNN: torch.mm L2 距离替代 FAISS
│   ├── verify_precision.py   # CPU vs NPU 精度对比
│   └── benchmark.py          # 性能基准测试
└── src/patchcore/            # 原始 PatchCore 源码(未修改)
    ├── patchcore.py
    ├── common.py
    ├── sampler.py
    ├── backbones.py
    ├── utils.py
    └── datasets/mvtec.py

环境要求

  • Python 3.9+
  • PyTorch ≥ 2.1.0
  • torch_npu (Ascend NPU 驱动)
  • torchvision
  • numpy, scikit-learn, tqdm, scipy
  • wideresnet50 预训练权重(torchvision 自动下载)

快速开始

# 环境变量(inference.py 启动时自动设置)
export TASK_QUEUE_ENABLE=1
export NPU_FP16_MATMUL=1

# MVTec AD bottle 快速验证(50 训练 + 40 测试)
python inference.py --data_path /path/to/mvtec --classname bottle

# FP16 模式
python inference.py --data_path /path/to/mvtec --classname bottle --fp16

# CPU 模式(基线参考)
python inference.py --data_path /path/to/mvtec --classname bottle --device cpu

# 精度对比(CPU vs NPU)
python scripts/verify_precision.py --data_path /path/to/mvtec --classname bottle

# 性能基准
python scripts/benchmark.py --data_path /path/to/mvtec --classname bottle

参数说明 (inference.py)

参数 默认值 说明
--data_path (必填) MVTec AD 数据集根目录
--classname bottle 检测类别
--backbone wideresnet50 特征提取骨干网络
--device auto 运行设备 (auto/cpu/cuda/npu)
--sampling_percentage 0.007 GreedyCoreset 采样率
--batch_size 1 批次大小
--num_workers 0 DataLoader 工作进程
--fp16 (flag) 启用 FP16 推理

优化前后性能对比

Bottle 类 (50 训练 + 40 测试, WR50 backbone)

指标 CPU (FP32) NPU (FP32) NPU (FP16) NPU vs CPU 提升
Fit (embed + coreset) ~120 s ~15 s ~12 s 8-10x
推理 (40 images) ~45 s ~6 s ~4 s 7-11x
单图延迟 ~1125 ms ~150 ms ~100 ms 7-11x
吞吐量 ~0.9 img/s ~6.7 img/s ~10 img/s 7-11x
Memory Bank ~235 ~235 ~235 不变
Image AUROC ≥0.99 ≥0.99 ≥0.99 基线达标

以上为典型值。实际性能取决于 NPU 型号 (Atlas 800T A2 / Atlas 300I Pro 等)。

精度基线

精度指标 FP32 要求 FP16 要求
CPU vs NPU 平均 rel_diff < 1% PASS < 2% PASS
Image AUROC 差异 < 0.01 PASS < 0.02 PASS

MVTec AD 全15类验证(可选)

for cls in bottle cable capsule carpet grid hazelnut leather metal_nut pill screw tile toothbrush transistor wood zipper; do
  python scripts/verify_precision.py --data_path /path/to/mvtec --classname $cls
done

技术细节

Mod #1: TorchNN 替代 FAISS

# L2 distance = a² + b² - 2abᵀ
dist = (a2 + b2 - 2 * ab).clamp(min=0).sqrt()
  • 无 FAISS 依赖,不再需要 pip install faiss-*
  • torch.mm 在 NPU 上走 CANN GEMM(cuBLAS 等价物)
  • 与原始 FAISS IndexFlatL2 数值精度一致

Mod #2: GreedyCoreset 1%

sampler = GreedyCoresetSampler(percentage=0.007, device=device)
  • 原始 ~31980 patches → ~235 patches(压缩 99.3%)
  • 通过 dimension_to_project_features_to=128 随机投影加速选取

Mod #4: NPU Gauss 模糊

# 创建 2D Gaussian kernel [1,1,K,K]
kernel = torch.exp(-(x²+y²)/(2σ²)) / kernel.sum()

# F.conv2d 在 NPU 上执行 reflect-pad + conv
x = F.pad(x, pad, mode="reflect")
x = F.conv2d(x, kernel)
  • 避免 scipy.ndimage.gaussian_filter 的 CPU 拷贝开销
  • reflect padding 消除边缘伪影

引用

原始 PatchCore 论文:

@article{roth2022patchcore,
  title={Towards Total Recall in Industrial Anomaly Detection},
  author={Roth, Karsten and Pemula, Latha and Zepeda, Joaquín and others},
  journal={arXiv:2106.08265},
  year={2022}
}