使用SHAP解释DeepLabV3+语义分割模型时遇max_evals过低错误求助
解决SHAP测试DeepLabV3+语义分割模型时的
max_evals=500 is too low错误 问题描述
想用SHAP测试多类别语义分割模型deeplab_v3plus的可解释性,分析哪些特征对语义分类贡献最大,但运行代码时触发如下错误:
ValueError: max_evals=500 is too low for the Permutation explainer, it must be at least 2 * num_features + 1 = 1854721!
当前test_img/*下只有3张图片,怀疑是图片数量不足导致的问题?
运行代码
import glob from PIL import Image import torch from torchvision import transforms from torchvision.utils import make_grid import torchvision.transforms.functional as tf from deeplab import deeplab_v3plus import shap def test(args): # make a video prez model = deeplab_v3plus('resnet101', num_classes=args.nclass, output_stride=16, pretrained_backbone=True) model.load_state_dict(torch.load(args.seg_file,map_location=torch.device('cpu'))) # because no gpu available on sandbox environnement model = model.to(args.device) model.eval() explainer = shap.Explainer(model) with torch.no_grad(): for i, file in enumerate(args.img_folder): img = img2tensor(file, args) pred = model(img) print(explainer(img)) if __name__ == '__main__': class Arguments: def __init__(self): self.device = torch.device("cuda:1" if torch.cuda.is_available() else "cpu") self.seg_file = "Model_Woodscape.pth" self.img_folder = glob.glob("test_img/*.png") self.mean = [0.485, 0.456, 0.406] self.std = [0.229, 0.224, 0.225] self.h, self.w = 483, 640 self.nclass = 10 self.cmap = { 1: [128, 64, 128], # "road", 2: [69, 76, 11], # "lanemarks", 3: [0, 255, 0], # "curb", 4: [220, 20, 60], # "person", 5: [255, 0, 0], # "rider", 6: [0, 0, 142], # "vehicles", 7: [119, 11, 32], # "bicycle", 8: [0, 0, 230], # "motorcycle", 9: [220, 220, 0], # "traffic_sign", 0: [0, 0, 0] # "void" } args = Arguments() test(args)
报错信息
(dee_env) jovyan@jupyter:~/use-cases/Scene_understanding/Code_Woodscape/deeplab_v3+$ python test_shap.py BILINEAR is deprecated and will be removed in Pillow 10 (2023-07-01). Use Resampling.BILINEAR instead. Traceback (most recent call last): File "/home/jovyan/use-cases/Scene_understanding/Code_Woodscape/deeplab_v3+/test_shap.py", line 85, in <module> test(args) File "/home/jovyan/use-cases/Scene_understanding/Code_Woodscape/deeplab_v3+/test_shap.py", line 37, in test print(explainer(img)) File "/home/jovyan/use-cases/Scene_understanding/Code_Woodscape/deeplab_v3+/dee_env/lib/python3.9/site-packages/shap/explainers/_permutation.py", line 82, in __call__ return super().__call__( File "/home/jovyan/use-cases/Scene_understanding/Code_Woodscape/deeplab_v3+/dee_env/lib/python3.9/site-packages/shap/explainers/_explainer.py", line 266, in __call__ row_result = self.explain_row( File "/home/jovyan/use-cases/Scene_understanding/Code_Woodscape/deeplab_v3+/dee_env/lib/python3.9/site-packages/shap/explainers/_permutation.py", line 164, in explain_row raise ValueError(f"max_evals={max_evals} is too low for the Permutation explainer, it must be at least 2 * num_features + 1 = {2 * len(inds) + 1}!") ValueError: max_evals=500 is too low for the Permutation explainer, it must be at least 2 * num_features + 1 = 1854721!
原因分析
这个错误和测试图片数量无关,核心问题在于:
- 调用
shap.Explainer(model)时,SHAP自动选择了Permutation Explainer,但该解释器是为表格数据设计的,完全不适合处理图像这种高维度输入。 - 你的输入是3通道483×640的图像,总特征数为
3×483×640=927360,Permutation Explainer要求的最小max_evals为2×特征数+1=1854721,远大于默认值500,因此触发报错。
解决方法
使用SHAP专为深度学习图像模型设计的DeepExplainer或GradientExplainer,这类基于梯度的解释器计算效率高,适合图像任务。
1. 修改解释器初始化代码
替换原有的解释器初始化逻辑,加入背景数据集(用少量测试图即可,3-5张足够):
def test(args): model = deeplab_v3plus('resnet101', num_classes=args.nclass, output_stride=16, pretrained_backbone=True) model.load_state_dict(torch.load(args.seg_file,map_location=torch.device('cpu'))) model = model.to(args.device) model.eval() # 准备背景数据集:用3张测试图作为背景,用于估算模型期望输出 background_imgs = [] for file in args.img_folder[:3]: img = img2tensor(file, args) background_imgs.append(img) background = torch.cat(background_imgs, dim=0) # 使用DeepExplainer替代默认的Permutation Explainer explainer = shap.DeepExplainer(model, background) with torch.no_grad(): for i, file in enumerate(args.img_folder): img = img2tensor(file, args) # 生成所有类别的SHAP值 shap_values = explainer.shap_values(img) # shap_values是列表,每个元素对应一个类别的SHAP值,形状与输入img一致 print(f"处理第{i+1}张图,生成了{len(shap_values)}个类别的SHAP值")
2. 补充缺失的img2tensor函数
你的代码中未实现img2tensor,补充如下:
def img2tensor(file, args): img = Image.open(file).convert('RGB') # 替换废弃的BILINEAR为Resampling.BILINEAR transform = transforms.Compose([ transforms.Resize((args.h, args.w), interpolation=transforms.Resampling.BILINEAR), transforms.ToTensor(), transforms.Normalize(mean=args.mean, std=args.std) ]) img_tensor = transform(img).unsqueeze(0).to(args.device) return img_tensor
3. 可选:可视化SHAP结果
添加可视化代码,直观展示每个类别的特征贡献:
import shap.plots def denormalize(img, mean, std): """将标准化后的图像转回原始范围,用于可视化""" img = img.clone() for channel in range(img.size(0)): img[channel] = img[channel] * std[channel] + mean[channel] return img.permute(1,2,0).cpu().numpy() # 在循环中添加可视化 with torch.no_grad(): for i, file in enumerate(args.img_folder): img = img2tensor(file, args) shap_values = explainer.shap_values(img) # 可视化类别1(道路)的SHAP值 original_img = denormalize(img.squeeze(0), args.mean, args.std) shap.plots.image(shap_values[1], original_img)
注意事项
- 背景数据集无需大量样本,3-5张即可满足需求。
- 确保背景数据和输入数据在同一设备(CPU/GPU)上。
- 如果使用GPU,注意控制单批次数据大小,避免显存溢出。
内容的提问来源于stack exchange,提问作者Revolucion for Monica
相关产品推荐
相关产品推荐

