├── .gitignore ├── Featuremaps&Filters ├── featuremap10.png ├── featuremap20.png ├── featuremap30.png ├── featuremap40.png └── img.png ├── LICENSE ├── README.md ├── Visualiser.py ├── __pycache__ └── extractor.cpython-37.pyc └── extractor.py /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | wheels/ 23 | pip-wheel-metadata/ 24 | share/python-wheels/ 25 | *.egg-info/ 26 | .installed.cfg 27 | *.egg 28 | MANIFEST 29 | 30 | # PyInstaller 31 | # Usually these files are written by a python script from a template 32 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 33 | *.manifest 34 | *.spec 35 | 36 | # Installer logs 37 | pip-log.txt 38 | pip-delete-this-directory.txt 39 | 40 | # Unit test / coverage reports 41 | htmlcov/ 42 | .tox/ 43 | .nox/ 44 | .coverage 45 | .coverage.* 46 | .cache 47 | nosetests.xml 48 | coverage.xml 49 | *.cover 50 | *.py,cover 51 | .hypothesis/ 52 | .pytest_cache/ 53 | 54 | # Translations 55 | *.mo 56 | *.pot 57 | 58 | # Django stuff: 59 | *.log 60 | local_settings.py 61 | db.sqlite3 62 | db.sqlite3-journal 63 | 64 | # Flask stuff: 65 | instance/ 66 | .webassets-cache 67 | 68 | # Scrapy stuff: 69 | .scrapy 70 | 71 | # Sphinx documentation 72 | docs/_build/ 73 | 74 | # PyBuilder 75 | target/ 76 | 77 | # Jupyter Notebook 78 | .ipynb_checkpoints 79 | 80 | # IPython 81 | profile_default/ 82 | ipython_config.py 83 | 84 | # pyenv 85 | .python-version 86 | 87 | # pipenv 88 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 89 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 90 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 91 | # install all needed dependencies. 92 | #Pipfile.lock 93 | 94 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow 95 | __pypackages__/ 96 | 97 | # Celery stuff 98 | celerybeat-schedule 99 | celerybeat.pid 100 | 101 | # SageMath parsed files 102 | *.sage.py 103 | 104 | # Environments 105 | .env 106 | .venv 107 | env/ 108 | venv/ 109 | ENV/ 110 | env.bak/ 111 | venv.bak/ 112 | 113 | # Spyder project settings 114 | .spyderproject 115 | .spyproject 116 | 117 | # Rope project settings 118 | .ropeproject 119 | 120 | # mkdocs documentation 121 | /site 122 | 123 | # mypy 124 | .mypy_cache/ 125 | .dmypy.json 126 | dmypy.json 127 | 128 | # Pyre type checker 129 | .pyre/ 130 | -------------------------------------------------------------------------------- /Featuremaps&Filters/featuremap10.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ashborn-SM/Visualizing-Filters-and-Feature-Maps-in-Convolutional-Neural-Networks-using-PyTorch/d497fe9c71458617dcf9c864569673b601dd8f11/Featuremaps&Filters/featuremap10.png -------------------------------------------------------------------------------- /Featuremaps&Filters/featuremap20.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ashborn-SM/Visualizing-Filters-and-Feature-Maps-in-Convolutional-Neural-Networks-using-PyTorch/d497fe9c71458617dcf9c864569673b601dd8f11/Featuremaps&Filters/featuremap20.png -------------------------------------------------------------------------------- /Featuremaps&Filters/featuremap30.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ashborn-SM/Visualizing-Filters-and-Feature-Maps-in-Convolutional-Neural-Networks-using-PyTorch/d497fe9c71458617dcf9c864569673b601dd8f11/Featuremaps&Filters/featuremap30.png -------------------------------------------------------------------------------- /Featuremaps&Filters/featuremap40.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ashborn-SM/Visualizing-Filters-and-Feature-Maps-in-Convolutional-Neural-Networks-using-PyTorch/d497fe9c71458617dcf9c864569673b601dd8f11/Featuremaps&Filters/featuremap40.png -------------------------------------------------------------------------------- /Featuremaps&Filters/img.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ashborn-SM/Visualizing-Filters-and-Feature-Maps-in-Convolutional-Neural-Networks-using-PyTorch/d497fe9c71458617dcf9c864569673b601dd8f11/Featuremaps&Filters/img.png -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Rahul0128 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Visualizing-Filters-and-Feature-Maps-in-Convolutional-Neural-Networks-using-PyTorch 2 | 3 | ## About 4 | 5 | This repository is about the code to extract cnn layers and visualise the filters and featuremaps 6 | 7 | ## Filters 8 | 9 | Filters are set of weights which are learned using the backpropagation algorithm. If you do alot of practical deep learning coding, you may know filters in the name of kernels. Filter size can be of 3×3 or maybe 5×5 or maybe even 7×7.  10 | Filters in a CNN layer learn to detect abstract concepts like boundary of a face, edges of a buildings etc. By stacking more and more CNN layers on top of each other, we can get more abstract and in-depth information from a CNN. 11 | ![Filter](https://cdn-images-1.medium.com/max/1200/1*obE_Fc8k2LEcSIr057eubQ.png) 12 | 13 | ## FeatureMaps 14 | 15 | Feature Maps are the results we get after applying the filter through the pixel value of the image.This is what the model see's in a image and the process is called convolution operation. The reason for visualising the feature maps is to gain deeper understandings about CNN. 16 | 17 | 18 | ![Feature maps](https://cdn-images-1.medium.com/max/800/1*dMsu9z5eP-aXZXXHkXP4vg.png) 19 | 20 | ## Extracting the CNN layers 21 | 22 | ``` 23 | conv_layers = [] 24 | model_weights = [] 25 | model_children = list(models.resnet50().children()) 26 | counter = 0 27 | 28 | for i in range(len(model_children)): 29 | if type(model_children[i]) == nn.Conv2d: 30 | counter += 1 31 | model_weights.append(model_children[i].weight) 32 | conv_layers.append(model_children[i]) 33 | elif type(model_children[i]) == nn.Sequential: 34 | for j in range(len(model_children[i])): 35 | for child in model_children[i][j].children(): 36 | if type(child) == nn.Conv2d: 37 | counter += 1 38 | model_weights.append(child.weight) 39 | conv_layers.append(child) 40 | ``` 41 | The above code is simple and self-explanatory but it is limited to pre-existing models like other resnet model resnet-18, 34, 101, 152. For a custom model ,things will be different ,lets say there is a Sequential layer inside another Sequential layer and if there is a CNN layer it will be unchecked. 42 | This is where the extractor.py module comes in. 43 | 44 | ### Extractor class 45 | The Extractor class can find every CNN layer(except down-sample layers) including their weights in any resnet model and almost in any custom resnet and vgg model. Its not limited to CNN layers it can find Linear layers and if the name of the Down-sampling layer is mentioned, it 46 | can find that too. It can also give some useful information like the number of CNN, Linear and Sequential layers in a model. 47 | 48 | ### How to use 49 | In the Extractor class the model parameter takes in a model and the DS_layer_name parameter is optional. The DS_layer_name parameter is to find the down-sampling layer normally in resnet layer the name will be 'downsample' so it is kept as default. 50 | 51 | ``` 52 | extractor = Extractor(model = resnet, DS_layer_name = 'downsample') 53 | ``` 54 | The below code is to activate the program 55 | ``` 56 | extractor.activate() 57 | ``` 58 | You can get relevant details in a dictionary by calling ```extractor.info()``` 59 | ``` 60 | {'Down-sample layers name': 'downsample', 'Total CNN Layers': 49, 'Total Sequential Layers': 4, 'Total Downsampling Layers': 4, 'Total Linear Layers': 1, 'Total number of Bottleneck and Basicblock': 16, 'Total Execution time': '0.00137 sec'} 61 | ``` 62 | #### Accessing the weights and the layers 63 | ``` 64 | extractor.CNN_layers -----> Gives all the CNN layers in a model 65 | extractor.Linear_layers --> Gives all the Linear layers in a model 66 | extractor.DS_layers ------> Gives all the Down-sample layers in a model if there are any 67 | extractor.CNN_weights ----> Gives all the CNN layer's weights in a model 68 | extractor.Linear_weights -> Gives all the Linear layer's weights in a model 69 | 70 | ``` 71 | -------------------------------------------------------------------------------- /Visualiser.py: -------------------------------------------------------------------------------- 1 | # Importing libraries 2 | import torch 3 | import matplotlib.pyplot as plt 4 | import torchvision.transforms as t 5 | import cv2 as cv 6 | import torchvision.models as models 7 | # Importing the module 8 | from extractor import Extractor 9 | 10 | 11 | device = torch.device('cuda' if torch.cuda.is_available() else 'cpu') 12 | # Loading the model 13 | resnet = models.resnet50() 14 | 15 | extractor = Extractor(list(resnet.children())) 16 | extractor.activate() 17 | 18 | 19 | # Visualising the filters 20 | plt.figure(figsize=(35, 35)) 21 | for index, filter in enumerate(extractor.CNN_weights[0]): 22 | plt.subplot(8, 8, index + 1) 23 | plt.imshow(filter[0, :, :].detach(), cmap='gray') 24 | plt.axis('off') 25 | 26 | plt.show() 27 | 28 | 29 | # Filter Map 30 | img = cv.cvtColor(cv.imread('Featuremaps&Filters/img.png'), cv.COLOR_BGR2RGB) 31 | img = t.Compose([ 32 | t.ToPILImage(), 33 | t.Resize((128, 128)), 34 | # t.Grayscale(), 35 | t.ToTensor(), 36 | t.Normalize(0.5, 0.5)])(img).unsqueeze(0) 37 | 38 | featuremaps = [extractor.CNN_layers[0](img)] 39 | for x in range(1, len(extractor.CNN_layers)): 40 | featuremaps.append(extractor.CNN_layers[x](featuremaps[-1])) 41 | 42 | # Visualising the featuremaps 43 | for x in range(len(featuremaps)): 44 | plt.figure(figsize=(30, 30)) 45 | layers = featuremaps[x][0, :, :, :].detach() 46 | for i, filter in enumerate(layers): 47 | if i == 64: 48 | break 49 | plt.subplot(8, 8, i + 1) 50 | plt.imshow(filter, cmap='gray') 51 | plt.axis('off') 52 | 53 | # plt.savefig('featuremap%s.png'%(x)) 54 | 55 | plt.show() 56 | -------------------------------------------------------------------------------- /__pycache__/extractor.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ashborn-SM/Visualizing-Filters-and-Feature-Maps-in-Convolutional-Neural-Networks-using-PyTorch/d497fe9c71458617dcf9c864569673b601dd8f11/__pycache__/extractor.cpython-37.pyc -------------------------------------------------------------------------------- /extractor.py: -------------------------------------------------------------------------------- 1 | # Importing libraries 2 | import torchvision.models as models 3 | from torch import nn 4 | import time 5 | 6 | class Extractor(): 7 | 8 | def __init__(self, model_children, DS_layer_name='downsample'): 9 | self.model_children = model_children 10 | self.DS_layer_name = DS_layer_name 11 | 12 | self.CNN_layers = [] 13 | self.Linear_layers = [] 14 | self.DS_layers = [] 15 | 16 | self.CNN_weights = [] 17 | self.Linear_weights = [] 18 | 19 | self.__no_sq_layers = 0 # number of sequential layers 20 | self.__no_containers = 0 # number of containers 21 | 22 | self.__verbose = [] 23 | 24 | self.__bottleneck = models.resnet.Bottleneck 25 | self.__basicblock = models.resnet.BasicBlock 26 | 27 | def __Append(self, layer, Linear=False): 28 | """ 29 | This function will append the layers weights and 30 | the layer itself to the appropriate variables 31 | 32 | params: layer: takes in CNN or Linear layer 33 | returns: None 34 | """ 35 | 36 | if Linear: 37 | self.Linear_weights.append(layer.weight) 38 | self.Linear_layers.append(layer) 39 | 40 | else: 41 | self.CNN_weights.append(layer.weight) 42 | self.CNN_layers.append(layer) 43 | 44 | def __Layer_Extractor(self, layers): 45 | """ 46 | This function(algorithm) finds CNN and linear layer in a Sequential layer 47 | 48 | params: layers: takes in either CNN or Sequential or linear layer 49 | return: None 50 | """ 51 | 52 | for x in range(len(layers)): 53 | 54 | if type(layers[x]) == nn.Sequential: 55 | # Calling the fn to loop through the layer to get CNN layer 56 | self.__Layer_Extractor(layers[x]) 57 | self.__no_sq_layers += 1 58 | 59 | if type(layers[x]) == nn.Conv2d: 60 | self.__Append(layers[x]) 61 | 62 | if type(layers[x]) == nn.Linear: 63 | self.__Append(layers[x], True) 64 | 65 | # This statement makes sure to get the down-sampling layer in the model 66 | if self.DS_layer_name in layers[x]._modules.keys(): 67 | self.DS_layers.append(layers[x]._modules[self.DS_layer_name]) 68 | 69 | # The below statement will loop throgh the containers and append it 70 | if isinstance(layers[x], (self.__bottleneck, self.__basicblock)): 71 | self.__no_containers += 1 72 | for child in layers[x].children(): 73 | if type(child) == nn.Conv2d: 74 | self.__Append(child) 75 | 76 | def __Verbose(self): 77 | 78 | for cnn_l, cnn_wts in zip(self.CNN_layers, self.CNN_weights): 79 | self.__verbose.append(f"CNN Layer : {cnn_l} ---> Weights shape :\ 80 | {cnn_wts.shape}") 81 | 82 | for linear_l, linear_wts in zip(self.Linear_layers, self.Linear_weights): 83 | self.__verbose.append(f"Linear Layer : {linear_l} --->\ 84 | Weights shape : {linear_wts.shape}") 85 | 86 | def activate(self): 87 | """Activates the algorithm""" 88 | 89 | start = time.time() 90 | self.__Layer_Extractor(self.model_children) 91 | self.__Verbose() 92 | self.__ex_time = str(round(time.time() - start, 5)) + ' sec' 93 | 94 | def info(self): 95 | """Information""" 96 | 97 | return { 98 | 'Down-sample layers name': self.DS_layer_name, 99 | 'Total CNN Layers': len(self.CNN_layers), 100 | 'Total Sequential Layers': self.__no_sq_layers, 101 | 'Total Downsampling Layers': len(self.DS_layers), 102 | 'Total Linear Layers': len(self.Linear_layers), 103 | 'Total number of Bottleneck and Basicblock': self.__no_containers, 104 | 'Total Execution time': self.__ex_time 105 | } 106 | 107 | def __repr__(self): 108 | return '\n'.join(self.__verbose) 109 | 110 | def __str__(self): 111 | return '\n'.join(self.__verbose) 112 | --------------------------------------------------------------------------------