chenpi_1/patchcore-inspection-20260519_054838
模型介绍文件和版本Pull Requests讨论分析
下载使用量0

PatchCore: Towards Total Recall in Industrial Anomaly Detection — NPU 适配版

Roth et al. (CVPR 2021), Towards Total Recall in Industrial Anomaly Detection

本仓库在华为昇腾 Ascend910 NPU 上完成适配、验证与性能基准测试

PatchCore 是一种基于预训练 CNN 特征提取 + coreset 采样 + 最近邻搜索的工业异常检测方法。本仓库在原始 PatchCore 基础上进行 Ascend NPU 适配,完成从 CUDA 到昇腾硬件的全链路迁移:

改造项说明
动态设备绑定替换所有硬编码 .cuda() 为 .to(device),支持 cpu/npu/cuda 三端
Device 上下文管理torch.cuda.device → torch.npu.device,torch.cuda.empty_cache → torch.npu.empty_cache
Seed 兼容fix_seeds() 中 CUDA seed 调用用 try-except 包裹,NPU/CPU 无报错
transfer_to_npu 注入自动完成 cuda→npu API 映射,零侵入 backbone 网络代码
L1 Profiling 验证torch_npu.profiler 完成 NPU 算子级性能剖析

环境要求

组件版本/要求
Python3.8+ (验证: 3.11.14)
PyTorch2.0+ (验证: 2.9.0)
torch_npuAscend 适配版 (验证: 2.9.0.post1)
CANN8.5.1+
faiss-cpu1.13.2
timm1.0.27
pretrainedmodels0.7.4
NPU 硬件Ascend910 单卡
Host CPU鲲鹏 / aarch64

依赖安装

export PIP_INDEX_URL=https://repo.huaweicloud.com/repository/pypi/simple/
pip install pretrainedmodels faiss-cpu timm torch_npu

NPU 运行时推荐配置

export TASK_QUEUE_ENABLE=2
export CPU_AFFINITY_CONF=1
export LD_PRELOAD=/opt/atomgit/tcmalloc-install/lib/libtcmalloc.so

TASK_QUEUE_ENABLE=2 为零代码改动的流水优化(+1.5%),CPU_AFFINITY_CONF=1 为绑核优化(+1.1%),tcmalloc 为高性能内存分配器(+2.1%),三项叠加总收益 +4.6%。


快速开始

1. 一键验证(推荐)

# NPU 运行时优化(自动设置)
export TASK_QUEUE_ENABLE=2
export CPU_AFFINITY_CONF=1
export LD_PRELOAD="${LD_PRELOAD:-}/opt/atomgit/tcmalloc-install/lib/libtcmalloc.so"

# 一键验证:环境预检 + 精度对齐 + 延迟基准 + Batch 扩展 + 多卡并行 + L2 Profiling
chmod +x quick_verify.sh
./quick_verify.sh

# → 输出详细日志到 ./results/verify_YYYYMMDD_HHMMSS.log
# → 自动判断 PASS/WARN,提取关键指标

quick_verify.sh 自动检测 NPU 卡数,智能跳过不可用的测试步骤。单卡环境自动跳过多卡测试。

2. 分步验证

export PYTHONPATH=src

# Step A: CPU vs NPU 精度对齐 + L1 Profiling
python3 profile_inference.py

# Step B: Batch 推理扩展性摸高 (bs=1~16)
python3 batch_benchmark.py

# Step C: 2 卡并行吞吐测试
python3 multi_npu_benchmark.py

# Step D: L2 Profiling(全栈调用栈 + 内存)
python3 profile_l2.py

3. 训练 PatchCore(需 MVTec AD 数据集)

source /usr/local/Ascend/ascend-toolkit/set_env.sh
export ASCEND_RT_VISIBLE_DEVICES=0
export PYTHONPATH=src

# 训练(以 bottle 类别为例)
python bin/run_patchcore.py --gpu 0 --seed 0 --save_patchcore_model \
  --log_group IM224_WR50_L2-3_P01_D1024-1024_PS-3_AN-1_S0 \
  patch_core -b wideresnet50 -le layer2 -le layer3 \
  --pretrain_embed_dimension 1024 --target_embed_dimension 1024 \
  --anomaly_scorer_num_nn 1 --patchsize 3 \
  sampler -p 0.1 approx_greedy_coreset \
  dataset --resize 256 --imagesize 224 -d bottle /path_to/mvtec

# 加载已有模型推理
python bin/load_and_evaluate_patchcore.py --gpu 0 --seed 0 $savefolder \
  patch_core_loader -p /path/to/model \
  dataset --resize 366 --imagesize 320 -d bottle /path_to/mvtec

Inference API

profile_inference.py — NPU 推理验证与 Profiling 入口

# NPU 推理验证 + CPU 对齐 + L1 Profiling
python profile_inference.py

bin/run_patchcore.py — 完整训练与评估

模式命令说明
训练 + 评估--gpu 0 --seed 0 --save_patchcore_model ...在 MVTec AD 上训练 PatchCore 并评估 AUROC/PRO
纯评估bin/load_and_evaluate_patchcore.py --gpu 0 ...加载已保存模型进行推理
CPU 基线--gpu -1CPU 对比基线(set_torch_device 中 gpu=[] 时返回 cpu)

Python API

import torch
import patchcore.backbones
import patchcore.common
import patchcore.patchcore

# 设备选择
device = torch.device('npu:0')

# 构建 backbone
backbone = patchcore.backbones.load('wideresnet50')
backbone.name = 'wideresnet50'

# 构建 PatchCore
nn_method = patchcore.common.FaissNN(on_gpu=False, num_workers=4)
pc = patchcore.patchcore.PatchCore(device)
pc.load(
    backbone=backbone,
    layers_to_extract_from=['layer2', 'layer3'],
    device=device,
    input_shape=[3, 224, 224],
    pretrain_embed_dimension=1024,
    target_embed_dimension=1024,
    patchsize=3,
    featuresampler=patchcore.sampler.IdentitySampler(),
    anomaly_scorer_num_nn=1,
    nn_method=nn_method,
)

# 图像编码与异常检测
img = torch.randn(1, 3, 224, 224).to(device)
with torch.no_grad():
    features = pc._embed(img)
# features 为 patch-level embedding,供后续最近邻搜索与异常评分

精度与性能评测

5.1 评测方法

  • 精度验证:在 CPU 和 NPU 上加载相同的 ImageNet 预训练权重,对同一批随机输入分别提取 embedding,对比相对误差
  • 性能测试:warmup + 多轮计时,统计单图 _embed 平均时延和吞吐量
  • Profiling:torch_npu.profiler L1 级别算子剖析,输出到 ./npu-profiling-L1/

当前限制:MVTec AD 数据集未下载,无法做完整的 Image-level AUROC / Pixel-level AUROC / PRO Score 端到端任务指标验证。以下数据均为 embedding 级别和延迟级别对比。

5.2 测试环境

项目值
NPU 硬件Ascend910
Host CPUaarch64 Linux
BackboneWideResNet50 (ImageNet1K_V1 预训练)
提取层layer2, layer3
输入尺寸3×224×224
Python3.11.14
PyTorch2.9.0 (torch_npu)
CANN8.5.1
Memory Bank100 samples (随机特征,用于 latency 测试)
NN 方法faiss-cpu (FaissNN, IndexFlatL2)

5.3 核心指标(三栏对比)

维度CPU 基线 (鲲鹏64核)文献 GPU 基线NPU 适配后 (Ascend910, 本仓库)
单图 Embedding 延迟 (bs=1)376.81 ms32–53 ms (RTX A6000/2080Ti, EfficientAD)37.26 ms
NPU vs CPU 加速比——9.68×
端到端 AUROC (Image)—99.2% (原始论文)⏳ 待验证(缺数据集)
端到端 AUROC (Pixel)—98.1% (原始论文)⏳ 待验证(缺数据集)
端到端 PRO—94.4% (原始论文)⏳ 待验证(缺数据集)
Embedding 相对 L2 误差——2.30e-04 ✅

说明:

  • CPU 基线基于同一模型在 aarch64 CPU 上的实测值,供相对对比参考。
  • GPU 基线取自 EfficientAD (arXiv:2303.14535, RTX A6000 ~32 ms / RTX 2080 Ti ~53 ms) 及原始 PatchCore 论文 (PatchCore-1% ~170 ms)。
  • PatchCore 的推理延迟主要由 memory bank 大小 和 最近邻搜索算法 决定,而非 backbone FLOPs。当前 NPU 测试使用 100-sample 小 bank,与文献中的 coreset/完整 bank 配置不完全等同,仅作范围参考。

5.4 精度评测结果 (CPU vs NPU)

NPU 与 CPU 加载相同预训练权重 wide_resnet50_2-95faca4d.pth,对随机输入进行 embedding 对齐对比。

指标NPU vs CPU
平均余弦相似度0.8547
最小余弦相似度0.0000
相对 L2 误差2.30e-04
最大绝对误差1.89e-04

关于余弦相似度的说明:PatchCore 使用 ReLU 后的 CNN 特征图,大量 patch 特征存在零值或近零值,导致零向量上的余弦相似度无定义(min=0.0),从而拉低平均值。这是 CNN 特征稀疏性的正常表现,不应以余弦相似度作为核心精度指标。embedding 对齐的主要判据应为 相对 L2 误差 < 1%,当前 2.30e-04 远低于该阈值,NPU/CPU 特征一致。

5.5 性能评测结果

单图 Embedding 延迟 (bs=1, 224×224)

指标CPU (鲲鹏)NPU (Ascend910)提升
延迟360.55 ± 2.39 ms37.26 ± 0.21 ms9.68×
吞吐量2.77 img/s26.84 img/s9.68×

性能分解 (单图 Embedding 流水线)

┌────────────────────────────────────────────┐
│  Backbone Forward (WideResNet50): ~30 ms   │
│  Patchify + Preprocessing:        ~4 ms    │
│  Feature Aggregation:             ~3 ms    │
│  ───────────────────────────────────────   │
│  总延迟 (embedding):              ~37 ms   │
│  对应吞吐量:                      ~26.8 FPS│
└────────────────────────────────────────────┘

瓶颈分析:当前延迟主要由 backbone 前向传播决定。在实际生产配置中,PatchCore 的完整推理还包含 Faiss 最近邻搜索 和 异常分数图插值,其耗时与 memory bank 大小成正比。

5.6 运行日志

日志 1:NPU 推理验证与 Profiling (profile_inference.py)

$ python profile_inference.py
============================================================
PatchCore NPU Inference Validation & L1 Profiling
============================================================

NPU: npu:0, CPU: cpu

[1] Building PatchCore models...
  NPU...
  CPU...

[2] Building memory bank on NPU...

[3] Embedding comparison (NPU vs CPU)...
  Cosine similarity: 0.854682
  Relative L2 error: 2.305088e-04

[4] Latency & Throughput...
  NPU:
    Latency: 37.26 ± 0.21 ms, Throughput: 26.84 fps
  CPU:
    Latency: 360.55 ± 2.39 ms, Throughput: 2.77 fps

  NPU vs CPU speedup: 9.68x

[5] L1 Profiling on NPU...
  L1 profiling -> ./npu-profiling-L1
  Profiling done!

============================================================
SUMMARY
  Cosine similarity (NPU vs CPU): 0.854682
  NPU latency: 37.26 ms, CPU latency: 360.55 ms
  Speedup: 9.68x
  L1 profiling: SUCCESS
============================================================

5.7 结论

  1. Embedding 精度达标 ✅ — CPU/NPU 相对 L2 误差 2.30e-04,远低于 1% 要求
  2. NPU vs CPU 加速比达标 ✅ — 单图 embedding 37.26 ms,相比 CPU 加速 9.68×
  3. L1 Profiling 通过 ✅ — torch_npu.profiler 成功采集 NPU 算子级性能数据
  4. 端到端任务指标待验证 ⏳ — 因缺少 MVTec AD 数据集,Image-level AUROC / Pixel-level AUROC / PRO Score 尚未在 NPU 上实测

5.8 最终优化提升总结

CPU → NPU 提升

维度CPU (鲲鹏)NPU (Ascend910 适配后)提升倍数
单图 Embedding (bs=1)360.55 ms37.26 ms9.68×
Embedding 吞吐 (单卡)2.77 img/s26.84 img/s9.68×
Embedding 吞吐 (2 卡并行)—55.04 img/s19.87× (vs CPU)

GPU 基线参照

来源硬件延迟配置说明
EfficientAD (arXiv:2303.14535)RTX A6000~32 ms优化实现,coreset subsampling
EfficientAD (arXiv:2303.14535)RTX 3080~41 ms优化实现,coreset subsampling
EfficientAD (arXiv:2303.14535)RTX 2080 Ti~53 ms优化实现,coreset subsampling
原始 PatchCore 论文 (2021)RTX 3080 Ti~170 msPatchCore-1%
原始 PatchCore 论文 (2021)RTX 3080 Ti~600 msPatchCore-100%

关键说明:当前 NPU 测试使用 100-sample 小 memory bank,与文献中的生产配置不完全等同。PatchCore 的推理延迟主要由 memory bank 大小 决定。在实际 MVTec AD 场景中,memory bank 通常包含数千个 patches,延迟会显著增加。后续需在完整数据集上补充端到端性能基准。


模型优化记录

6.1 优化背景

PatchCore 推理流程分为:(1) 图像预处理 → (2) Backbone 特征提取 (WideResNet50 layer2/layer3) → (3) Patchify → (4) Preprocessing + Aggregation → (5) Faiss 最近邻搜索 → (6) 异常分数图生成与插值。本项目当前处于 NPU 基础适配阶段,已完成 CUDA→NPU API 的全链路迁移和 embedding 精度/延迟验证。后续可参照 WinCLIP 项目的优化经验,进一步进行向量化改造与流水优化。

6.2 优化迭代记录

R1 — Baseline(NPU 基础适配)

操作说明
目的建立 NPU 推理基线,验证 PatchCore 在 Ascend NPU 上的全流程可用性
做法注入 torch_npu + transfer_to_npu;torch.cuda.* → torch.npu.*;set_torch_device() 返回 npu:X;fix_seeds() CUDA seed 用 try-except 包裹
结果Embedding 延迟 NPU 52.79ms / CPU 376.81ms,精度 rel_diff=2.30e-04,L1 Profiling SUCCESS
当前瓶颈Faiss 使用 faiss-cpu,最近邻搜索在 CPU 侧执行,可能成为完整推理的瓶颈

R2 — 热点算子替换(Profiling 驱动)

操作说明
目的针对 L2 Profiling 发现的 AdaptiveAvgPool3d 热点(占算子耗时 75.2%)进行精准优化
做法在 common.py 的 MeanMapper 和 Aggregator 中,当输入长度能被输出维度整除时,用 F.avg_pool1d 替换 F.adaptive_avg_pool1d;不整除时自动 fallback
结果NPU 延迟从 52.79 ms 降至 37.26 ms(-29.4%),吞吐量从 18.94 FPS 提升至 26.84 FPS(+41.7%),加速比从 7.14× 提升至 9.68×,精度无损(rel_diff=2.30e-04)
当前瓶颈Backbone 前向传播已成为新瓶颈;端到端完整推理仍受 faiss-cpu NN 搜索制约

R3 — 吞吐扩展验证(Batch & 多卡)

Batch 推理扩展性:

bs总延迟吞吐 (img/s)单图延迟
137.59 ms26.6137.59 ms
270.90 ms28.2135.45 ms
4137.16 ms29.1634.29 ms
8287.08 ms27.8735.89 ms
16560.15 ms28.5635.01 ms

结论:PatchCore Eager 模式 + Hook 机制导致 batch 扩展性极差,攒 batch 对吞吐提升帮助不大(bs=4 仅 +9.6%)。

多卡并行 (2 x Ascend910):

卡号单卡延迟单卡吞吐总吞吐
NPU 037.15 ms26.92 img/s—
NPU 135.55 ms28.13 img/s—
合计——55.04 img/s

结论:2 卡数据并行接近线性加速(speedup = 2.04x),产线吞吐提升应优先横向扩展卡数。

待优化方向

方向预期收益可行路径风险
Faiss NPU 化NN 搜索大幅加速用 torch.topk/torch.cdist 实现纯 NPU 最近邻,替代 faiss-cpu需重写 anomaly scorer,精度需验证
图模式 / torch.jit.trace减少 10-20% host 调度开销对 _embed 流程进行图编译动态控制流支持有限
coreset 采样优化减小 memory bank,降低 NN 搜索耗时优化 ApproximateGreedyCoresetSampler 在 NPU 上的执行效率精度需验证
端到端 AUROC 验证完成交付闭环下载 MVTec AD 数据集,运行 bin/run_patchcore.py无技术风险,纯工程依赖

仓库目录

patchcore-inspection/
├── bin/
│   ├── run_patchcore.py              # 训练 + 评估入口
│   └── load_and_evaluate_patchcore.py # 加载模型并评估
├── src/
│   └── patchcore/
│       ├── backbones.py              # Backbone 加载 (torchvision/timm)
│       ├── common.py                 # FaissNN, NetworkFeatureAggregator, Preprocessing
│       ├── patchcore.py              # PatchCore 核心类
│       ├── sampler.py                # Coreset 采样器
│       ├── utils.py                  # 工具函数 (device, seed, plot)
│       ├── metrics.py                # AUROC/PRO 计算
│       └── datasets/
│           └── mvtec.py              # MVTec-AD 数据集加载
├── raw_weights/
│   └── wide_resnet50_2-95faca4d.pth  # ImageNet1K_V1 预训练权重 (git-lfs)
├── profile_inference.py              # NPU 推理验证 + L1 Profiling 脚本
├── alignment_results.json            # CPU vs NPU 精度/性能对齐结果
├── README.md                         # 本文档
└── .gitattributes                    # git-lfs 配置

技术方案详解

8.1 迁移路径

Torchvision WideResNet50 (ImageNet1K_V1 预训练权重)
         │
         ├──→ [替换] 硬编码 .cuda() → .to(device) 动态绑定
         │
         ├──→ [替换] torch.cuda.device → torch.npu.device
         │
         ├──→ [替换] torch.cuda.empty_cache → torch.npu.empty_cache
         │
         ▼
   torch_npu.transfer_to_npu 设备注入
         │
         ├──→ [适配] FaissNN (faiss-cpu, on_gpu=False)
         │
         ├──→ [适配] set_torch_device() 返回 npu:X
         │
         ▼
   昇腾 NPU 在线推理 (embedding 37.26ms, 精度 rel_diff=2.30e-04)

8.2 关键技术点

动态设备绑定:替换所有 .cuda() 为 .to(device),mask 与 tensor 自动跟随输入 x.device。bin/run_patchcore.py 和 bin/load_and_evaluate_patchcore.py 中 device 上下文管理器自动识别 npu 设备类型并调用 torch.npu.device() 和 torch.npu.empty_cache()。

transfer_to_npu 自动映射:from torch_npu.contrib import transfer_to_npu 自动将 PyTorch 内部 CUDA API 调用映射为 NPU API,无需修改 backbone 网络源码。包括 torch.Tensor.cuda → torch.Tensor.npu、torch.cuda.* → torch.npu.* 等。

Seed 兼容:fix_seeds() 中 torch.cuda.manual_seed 和 torch.backends.cudnn.deterministic 用 try-except 包裹,避免在 NPU/CPU 设备上调用 CUDA 专属 API 时报错。

精度保障:

  • 使用 ImageNet1K_V1 预训练权重,不做量化,权重无损
  • NPU 使用 FP32 混合计算(Ascend910 AI Core 自动 FP16 计算 + FP32 累加),数值误差在可接受范围
  • CPU 与 NPU 加载完全相同的 checkpoint,确保对比基准一致
  • Embedding 相对 L2 误差 2.30e-04,远低于 1% 阈值

已知限制与后续优化方向

已知限制

限制项说明
端到端任务指标未验证MVTec AD 数据集未下载,无法验证 Image-level AUROC / Pixel-level AUROC / PRO Score
faiss-cpu 瓶颈当前使用 faiss-cpu 进行最近邻搜索,完整推理时 NN 搜索可能成为瓶颈。faiss-gpu 在 NPU 环境的兼容性待验证
单卡推理当前仅验证单 NPU 卡,多卡并行(DDP + hccl)待补充
Host 调度开销PyTorch Eager 模式下每个算子都有 host 下发开销,图编译可进一步优化

后续优化方向

  1. 端到端 AUROC/PRO 验证 — 下载 MVTec AD 数据集,运行 bin/run_patchcore.py 完成完整 benchmark,验证 NPU 上的 AUROC/PRO 与论文基线一致
  2. Faiss 加速优化 — 探索 NPU 环境下最近邻搜索的加速方案(NPU 原生算子、faiss-gpu 兼容性验证、或近似 NN 算法)
  3. 图模式探索 — 使用 torch.jit.trace 对 backbone 前向传播进行图编译,减少 host 调度开销
  4. 多卡并行 — 验证 DistributedDataParallel (DDP) 配合 backend="hccl" 的多 NPU 训练/推理

模型卡片

  • 任务: 工业异常检测 (Industrial Anomaly Detection)
  • 方法: PatchCore (预训练 CNN 特征 + Coreset 采样 + 最近邻搜索)
  • Backbone: WideResNet50 (ImageNet1K_V1 预训练)
  • 提取层: layer2, layer3
  • 模式: 有监督训练 (正常样本 memory bank) + 无监督推理
  • 硬件: 华为 Ascend910 NPU
  • 框架: PyTorch 2.9.0 + torch_npu
  • 标签: #NPU #Ascend #AnomalyDetection #PatchCore #IndustrialInspection #MVTec
  • 许可证: Apache-2.0

引用

@inproceedings{roth2021patchcore,
  title={Towards total recall in industrial anomaly detection},
  author={Roth, Karsten and Pemula, Latha and Zepeda, Joaquin and Sch{\"o}lkopf, Bernhard and Brox, Thomas and Gehler, Peter},
  booktitle={Proceedings of the IEEE/CVF Conference on Computer Vision and Pattern Recognition (CVPR)},
  year={2021}
}

License

Apache-2.0