I wanted to draw the class activation map of YOLOv5 before, but after trying for a long time without success, I accidentally found that someone in the YOLOv5 issue has implemented a gram-cam that supports drawing a certain layer of the network, but it seems that it has not yet been incorporated into the YOLOv5 project: Add GradCAM integration
1. How to use
- Open the github link above :
- Click on
Files changed
:
- Copy explainer.py in the explainer folder here to the file directory of yolov5 :
- Specify parameters :
def parseopt():
parser = argparse.ArgumentParser()
# 指定你训练好的网络权重路径
parser.add_argument('--weights', nargs='+', type=str, default=ROOT / 'runs/train/yolov5l/weights/best.pt', help='model path or triton URL')
# 指定要画类激活图的图像路径(目前只支持一张一张地画)
parser.add_argument('--source', type=str, default='/home/ding/fyw/YOLOv5/data/images/0.jpg', help='file/dir/URL/glob/screen/0(webcam)')
parser.add_argument('--imgsz', '--img', '--img-size', nargs='+', type=int, default=[640], help='inference size h,w')
# 指定gpu
parser.add_argument('--device', default='1', help='cuda device, i.e. 0 or 0,1,2,3 or cpu')
# 选择类激活图类型
parser.add_argument('--method',
type=str,
default='EigenCAM',
help='the method to use for interpreting the feature maps')
parser.add_argument('--verbose', action='store_true', help='verbose log')
- Just run :
python explainer/explainer.py
2. How to draw the class activation map of a specific layer
Here we take YOLOv5l as an example. First, we print(model)
print out the model (too long to show only part of it):
YOLOV5TorchObjectDetector(
(model): DetectionModel(
(model): Sequential(
(0): Conv(
(conv): Conv2d(3, 64, kernel_size=(6, 6), stride=(2, 2), padding=(2, 2), bias=False)
(bn): BatchNorm2d(64, eps=0.001, momentum=0.03, affine=True, track_running_stats=True)
(act): SiLU()
)
(1): Conv(
(conv): Conv2d(64, 128, kernel_size=(3, 3), stride=(2, 2), padding=(1, 1), bias=False)
(bn): BatchNorm2d(128, eps=0.001, momentum=0.03, affine=True, track_running_stats=True)
(act): SiLU() # grad_cam 1
)
(2): C3(
(cv1): Conv(
(conv): Conv2d(128, 64, kernel_size=(1, 1), stride=(1, 1), bias=False)
(bn): BatchNorm2d(64, eps=0.001, momentum=0.03, affine=True, track_running_stats=True)
(act): SiLU()
)
(cv2): Conv(
(conv): Conv2d(128, 64, kernel_size=(1, 1), stride=(1, 1), bias=False)
(bn): BatchNorm2d(64, eps=0.001, momentum=0.03, affine=True, track_running_stats=True)
(act): SiLU()
)
(cv3): Conv(
(conv): Conv2d(128, 128, kernel_size=(1, 1), stride=(1, 1), bias=False)
(bn): BatchNorm2d(128, eps=0.001, momentum=0.03, affine=True, track_running_stats=True)
(act): SiLU()
)
(m): Sequential(
(0): Bottleneck(
(cv1): Conv(
(conv): Conv2d(64, 64, kernel_size=(1, 1), stride=(1, 1), bias=False)
(bn): BatchNorm2d(64, eps=0.001, momentum=0.03, affine=True, track_running_stats=True)
(act): SiLU() # grad_cam 2
)
For example, if you want to draw grad_cam 1
(see the comments of the model file above), then modify the code target_layers
:
modify to:
target_layers = [model.model.model.model[1]._modules['act']]
If you want to draw grad_cam 2
, modify to:
target_layers = [model.model.model.model[2]._modules['m']._modules['0']._modules['cv1']._modules['act']]
For any other layer in the model, follow the above method analogy That's fine.
In addition, errors caused by version issues may occur when running this code:
TypeError: can't convert cuda:0 device type tensor to numpy. Use Tensor.cpu() to copy the tensor to host memory first.
Modify this line of code and add .cpu()
: