# -*- coding: utf-8 -*-
from setuptools import setup

packages = \
['rfa_toolbox',
 'rfa_toolbox.architectures',
 'rfa_toolbox.encodings',
 'rfa_toolbox.encodings.nasbench101',
 'rfa_toolbox.encodings.pytorch',
 'rfa_toolbox.encodings.tensorflow_keras',
 'rfa_toolbox.utils']

package_data = \
{'': ['*']}

install_requires = \
['attrs>=21.2.0,<22.0.0',
 'graphviz>=0.19.1,<0.20.0',
 'numpy>=1.21.4,<2.0.0',
 'tqdm>=4.62.3,<5.0.0']

extras_require = \
{'docs': ['Sphinx>=4.0.0,<5.0.0',
          'sphinx-rtd-theme>=1.0.0,<2.0.0',
          'myst-parser>=0.16.0,<0.17.0']}

setup_kwargs = {
    'name': 'rfa-toolbox',
    'version': '1.3.0',
    'description': 'A toolbox for analyzing the receptive field expansion in neural networks.',
    'long_description': '# ReceptiveFieldAnalysisToolbox\n\n<p align="center">\n  <a href="https://github.com/MLRichter/receptive_field_analysis_toolbox/actions?query=workflow%3ACI">\n    <img src="https://img.shields.io/github/workflow/status/MLRichter/receptive_field_analysis_toolbox/CI/main?label=CI&logo=github&style=flat-square" alt="CI Status" >\n  </a>\n  <a href="https://receptive-field-analysis-toolbox.readthedocs.io/en/latest/index.html">\n    <img src="https://img.shields.io/readthedocs/receptive-field-analysis-toolbox.svg?logo=read-the-docs&logoColor=fff&style=flat-square" alt="Documentation Status">\n  </a>\n    <a href="https://codecov.io/gh/MLRichter/receptive_field_analysis_toolbox">\n        <img src="https://codecov.io/gh/MLRichter/receptive_field_analysis_toolbox/branch/main/graph/badge.svg?token=3K52NPEAEU"/>\n    </a>\n</p>\n<p align="center">\n  <a href="https://python-poetry.org/">\n    <img src="https://img.shields.io/badge/packaging-poetry-299bd7?style=flat-square&logo=data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAASCAYAAABrXO8xAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAJJSURBVHgBfZLPa1NBEMe/s7tNXoxW1KJQKaUHkXhQvHgW6UHQQ09CBS/6V3hKc/AP8CqCrUcpmop3Cx48eDB4yEECjVQrlZb80CRN8t6OM/teagVxYZi38+Yz853dJbzoMV3MM8cJUcLMSUKIE8AzQ2PieZzFxEJOHMOgMQQ+dUgSAckNXhapU/NMhDSWLs1B24A8sO1xrN4NECkcAC9ASkiIJc6k5TRiUDPhnyMMdhKc+Zx19l6SgyeW76BEONY9exVQMzKExGKwwPsCzza7KGSSWRWEQhyEaDXp6ZHEr416ygbiKYOd7TEWvvcQIeusHYMJGhTwF9y7sGnSwaWyFAiyoxzqW0PM/RjghPxF2pWReAowTEXnDh0xgcLs8l2YQmOrj3N7ByiqEoH0cARs4u78WgAVkoEDIDoOi3AkcLOHU60RIg5wC4ZuTC7FaHKQm8Hq1fQuSOBvX/sodmNJSB5geaF5CPIkUeecdMxieoRO5jz9bheL6/tXjrwCyX/UYBUcjCaWHljx1xiX6z9xEjkYAzbGVnB8pvLmyXm9ep+W8CmsSHQQY77Zx1zboxAV0w7ybMhQmfqdmmw3nEp1I0Z+FGO6M8LZdoyZnuzzBdjISicKRnpxzI9fPb+0oYXsNdyi+d3h9bm9MWYHFtPeIZfLwzmFDKy1ai3p+PDls1Llz4yyFpferxjnyjJDSEy9CaCx5m2cJPerq6Xm34eTrZt3PqxYO1XOwDYZrFlH1fWnpU38Y9HRze3lj0vOujZcXKuuXm3jP+s3KbZVra7y2EAAAAAASUVORK5CYII=" alt="Poetry">\n  </a>\n  <a href="https://github.com/ambv/black">\n    <img src="https://img.shields.io/badge/code%20style-black-000000.svg?style=flat-square" alt="black">\n  </a>\n  <a href="https://github.com/pre-commit/pre-commit">\n    <img src="https://img.shields.io/badge/pre--commit-enabled-brightgreen?logo=pre-commit&logoColor=white&style=flat-square" alt="pre-commit">\n  </a>\n</p>\n<p align="center">\n  <a href="https://pypi.org/project/rfa_toolbox/">\n    <img src="https://img.shields.io/pypi/v/rfa_toolbox.svg?logo=python&logoColor=fff&style=flat-square" alt="PyPI Version">\n  </a>\n  <img src="https://img.shields.io/pypi/pyversions/rfa_toolbox.svg?style=flat-square&logo=python&amp;logoColor=fff" alt="Supported Python versions">\n  <img src="https://img.shields.io/pypi/l/rfa_toolbox.svg?style=flat-square" alt="License">\n</p>\n\nThis is RFA-Toolbox, a simple and easy-to-use library that allows you to optimize your neural network architectures\nusing receptive field analysis (RFA) and create graph visualizations of your architecture.\n\n## Installation\n\nInstall this via pip:\n\n`pip install rfa_toolbox`\n\n## What is Receptive Field Analysis?\n\nReceptive Field Analysis (RFA) is a simple yet effective way to optimize the efficiency of any neural architecture without\ntraining it.\n\n## Usage\n\nThis library allows you to look for certain inefficiencies withing your convolutional neural network setup without\never training the model.\nYou can do this simply by importing your architecture into the format of RFA-Toolbox and then use the in-build functions\nto visualize your architecture using GraphViz.\nThe visualization will automatically mark layers predicted to be unproductive red and critical layers, that are potentially unproductive orange.\nIn edge case scenarios, where the receptive field expands of the boundaries of the image on some but not all tensor-axis, the layer will be marked yellow,\nsince such a layer is probably not operating and maximum efficiency.\nBeing able to detect these types of inefficiencies is especially useful if you plan to train your model on resolutions that are substantially lower than the\ndesign-resolution of most models.\nAs an alternative, you can also use the graph from RFA-Toolbox to hook RFA-toolbox more directly into your program.\n\n### Examples\n\nThere are multiple ways to import your model into RFA-Toolbox for analysis, with additional ways being added in future\nreleases.\n\n#### PyTorch\n\nThe simplest way of importing a model is by directly extracting the compute-graph from the PyTorch-implementation of your model.\nHere is a simple example:\n\n```python\nimport torchvision\nfrom rfa_toolbox import create_graph_from_pytorch_model, visualize_architecture\nmodel = torchvision.models.alexnet()\ngraph = create_graph_from_pytorch_model(model)\nvisualize_architecture(\n    graph, f"alexnet_32_pixel", input_res=32\n).view()\n```\n\nThis will create a graph of your model and visualize it using GraphViz and color all layers that are predicted to be\nunproductive for an input resolution of 32x32 pixels:\n![rf_stides.PNG](https://github.com/MLRichter/receptive_field_analysis_toolbox/blob/main/images/alexnet.PNG?raw=true)\n\nKeep in mind that the Graph is reverse-engineerd from the PyTorch JIT-compiler, therefore no looping-logic is\nallowed within the forward pass of the model.\nAlso, the use of the functional-library for stateful-operations like pooling or convolutional layers is discouraged,\nsince it can cause malformed graphs during the reverse engineering of the compute graph.\n\n#### Tensorflow / Keras\n\nSimilar to the PyTorch-import, models can be imported from TensorFlow as well.\n\nThe code below will create a graph of VGG16 and visualize it using GraphViz and color all layers that are predicted to be\nunproductive for an input resolution of 32x32 pixels. This is analog to the PyTorch-variant depicted above:\n\n```python\n\nfrom keras.applications.vgg19 import VGG19\nfrom rfa_toolbox import create_graph_from_tensorflow_model\n\nmodel = VGG19(include_top=True, weights=None)\ngraph: EnrichedNetworkNode = create_graph_from_tensorflow_model(model)\nvisualize_architecture(graph, "VGG16", input_res=32).view()\n\n```\n\nThis will create the following visualization:\n![vgg16.PNG](https://github.com/MLRichter/receptive_field_analysis_toolbox/blob/main/images/vgg16.PNG?raw=true)\n\nCurrently, only the use of the keras.Model object is supported.\n\n#### Custom\n\nIf you are not able to automatically import your model from PyTorch or you just want some visualization, you can\nalso directly implement the model with the propriatary-Graph-format of RFA-Toolbox.\nThis is similar to coding a compute-graph in a declarative style like in TensorFlow 1.x.\n\n```python\nfrom rfa_toolbox import visualize_architecture\nfrom rfa_toolbox.graphs import EnrichedNetworkNode, LayerDefinition\n\n\nconv1 = EnrichedNetworkNode(\n    name="Conv1",\n    layer_info=LayerDefinition(\n        name="Conv3x3",\n        kernel_size=3, stride_size=1,\n        filters=64\n    ),\n    predecessors=[]\n)\nconv2 = EnrichedNetworkNode(\n    name="Conv2",\n    layer_info=LayerDefinition(\n        name="Conv3x3",\n        kernel_size=3, stride_size=1,\n        filters=128\n    ),\n    predecessors=[conv1]\n)\n\nconv3 = EnrichedNetworkNode(\n    name="Conv3",\n    layer_info=LayerDefinition(\n        name="Conv3x3",\n        kernel_size=3, stride_size=1,\n        filters=256\n    ),\n    predecessors=[conv1]\n)\n\nconv4 = EnrichedNetworkNode(\n    name="Conv4",\n    layer_info=LayerDefinition(\n        name="Conv3x3",\n        kernel_size=3, stride_size=1,\n        filters=256\n    ),\n    predecessors=[conv2, conv3]\n)\n\nout = EnrichedNetworkNode(\n    name="Softmax",\n    layer_info=LayerDefinition(\n        name="Fully Connected",\n        units=1000\n    ),\n    predecessors=[conv4]\n)\nvisualize_architecture(\n    out, f"example_model", input_res=32\n).view()\n\n```\n\nThis will produce the following graph:\n\n![simple_conv.png](https://github.com/MLRichter/receptive_field_analysis_toolbox/blob/main/images/simple_conv.png?raw=true)\n\n### A quick primer on the Receptive Field\n\nTo understand how RFA works, we first need to understand what a receptive field is, and it\'s effect on what the network is learning to detect.\nEvery layer in a (convolutional) neural network has a receptive field. It can be considered the "field of view" of this layer.\nIn more precise terms, we define a receptive field as the area influencing the output of a single position of the\nconvolutional kernel.\nHere is a simple, 1-dimensional example:\n![rf.PNG](https://github.com/MLRichter/receptive_field_analysis_toolbox/blob/main/images/rf.PNG?raw=true)\nThe first layer of this simple architecture can only ever "see" the information the input pixels directly\nunder it\'s kernel, in this scenario 3 pixels.\nAnother observation we can make from this example is that the receptive field size is expanding from layer to layer.\nThis is happening, because the consecutive layers also have kernel sizes greater than 1 pixel, which means that\nthey combine multiple adjacent positions on the feature map into a single position in their output.\nIn other words, every consecutive layer adds additional context to each feature map position by expanding the receptive field.\nThis ultimately allows networks to go from detecting small and simple patterns to big and very complicated ones.\n\nThe effective size of the kernel is not the only factor influence the growth of the receptive field size.\nAnother important factor is the stride size:\n![rf_stides.PNG](https://github.com/MLRichter/receptive_field_analysis_toolbox/blob/main/images/rf_strides.PNG?raw=true)\nThe stride size is the size of the step between the individual kernel positions. Commonly, every possible\nposition is evaluated, which is not affecting the receptive field size in any way.\nWhen the stride size is greater than one however valid positions of the kernel are skipped, which reduces\nthe size of the feature map. Since now information on the feature map is now condensed on fewer feature map positions,\nthe growth of the receptive field is multiplied for future layers.\nIn real-world architectures, this is typically the case when downsampling layers like convolutions with a stride size of 2\nare used.\n\n### Why does the Receptive Field Matter?\n\nAt this point you may be wondering why the receptive field of all things is useful for optimizing an\narchitecture.\nThe short answer to this is: because it influences where the network can process patterns of a certain size.\nSimply speaking each convolutional layer is only able to detect patterns of a certain size because of its receptive field.\nInterestingly this also means that there is an upper limit to the usefulness of expanding the receptive field.\nAt the latest, this is the case when the receptive field of a layer is BIGGER than the input image, since no novel\ncontext can be added at this point.\nFor convolutional layers this is a problem, because layers past this "Border Layer" now lack the primary mechenism convolutional layers\nuse to improve the intermediate representation of the data, making these layers unproductive.\nIf you are interested in the details of this phenomenon I recommend that you read these:\n\n- [(Input) Size Matters for Convolutional Neural Network Classifier](https://link.springer.com/chapter/10.1007/978-3-030-86340-1_11)\n- [Should You Go Deeper? Optimizing Convolutional Neural Network Architectures without Training](https://arxiv.org/abs/2106.12307)\n  (published at the 20th IEEE International Conference for Machine Learning Application - ICMLA)\n\n### Optimizing Architectures using Receptive Field Analysis\n\nSo far, we learned that the expansion of the receptive field is the primary mechanism for improving\nthe intermediate solution utilized by convolutional layers.\nAt the point where this is no longer possible, layers are not able to contribute to the quality of the output of the model\nand become unproductive.\nWe refer to these layers as unproductive layers. Layers who advance the receptive field sizes beyond the input resolution\nare referred to as critical layers.\nCritical layers are not necessarily unproductive, since they are still able to incorporate some novel context into the data,\ndepending on how large the receptive field size of the input is.\n\nOf course, being able to predict why and which layer will become dead weight during training is highly useful, since\nwe can now adjust the design of the architecture to fit our input resolution better without spending time on training models.\nDepending on the requirements, we may choose to emphasize efficiency by primarily removing unproductive layers. Another\noption is to focus on predictive performance by making the unproductive layers productive again.\n\nWe now illustrate how you may choose to optimize an architecture on a simple example:\n\nLet\'s take the ResNet architecture, which is a very popular CNN-model.\nWe want to train ResNet18 on ResizedImageNet16, which has a 16 pixel input resolution.\nWhen we apply Receptive Field Analysis, we can see that most convolutional layers will in fact not contribute\nto the inference process (unproductive layers marked red, probable unproductive layers marked orange):\n\n![resnet18.PNG](https://github.com/MLRichter/receptive_field_analysis_toolbox/blob/main/images/resnet18.png?raw=true)\n\nWe can clearly see that most of the network\'s layers will not contribute anything useful to the quality of the output, since\ntheir receptive field sizes are too large.\n\nFrom here on we have multiple ways of optimizing the setup.\nOf course, we can simply increase the resolution, to involve more layers in the inference process, but that is usually\nvery expensive from a computational point of view.\nIn the first scenario, we are not interested in increasing the predictive performance of the model, we simply want to save\ncomputational resources. We reduce the kernel size of the\nfirst layer to 3x3 from 7x7.\nThis change allows the first three building blocks to contribute more to\nthe quality of the prediction, since no layer is predicted to be unproductive.\nWe then simply replace the remaining building blocks with a simple output head.\nThis new architecture then looks like this:\n\n![resnet18eff.PNG](https://github.com/MLRichter/receptive_field_analysis_toolbox/blob/main/images/resnet18eff.png?raw=true)\n\nNote that all previously unproductive layers are now either removed or only marked as "critical", which\nis generally not a big problem, since the receptive field size is "reset" by the receptive field size\nafter each building block.\nAlso note that fully connected layers are always marked as critical or unproductive, since they\ntechnically have an infinite receptive field size.\n\nThe resulting architecture achieves slightly better predictive performance as\nthe original architecture, but with substantially lower computational cost.\nIn this case we save approx. 80% of the computational cost and improve the predictive performance slightly\nfrom 17% to 18%.\n\nIn another scenario we may not be satisfied with the predictive performance.\nIn other words, we want to make use of the underutilized parameters of the network\nby turning all unproductive layers into productive layers.\nWe achieve this by changing their receptive field sizes.\nThe biggest lever when it comes to changing the receptive field size is always the quantity of downsampling layers.\nDownsampling layers have a multiplicative effect on the growth of the receptive field for all consecutive layers.\nWe can exploit this by simply removing the MaxPooling layer, which is the second layer of the original\narchitecture.\nWe also reduce the kernel size of the first layer to 3x3 from 7x7, and it\'s stride size to 1.\nThis drastically reduces the receptive field sizes of the entire architecture, making most layers productive again.\nWe address the remaining unproductive layers to by removing the final downsampling layer and distributing the building\nblocks as evenly as possible among the three stages between the remaining downsampling layers.\n\nThe resulting architecture now looks like this:\n\n![resnet18perf.PNG](https://github.com/MLRichter/receptive_field_analysis_toolbox/blob/main/images/resnet18perf.png?raw=true)\n\nThe architecture now no longer has unproductive layers in their building blocks and only 2 critical layers.\nThis improved architecture also achieves 34% Top1-Accuracy in ResizedImageNet16 instead of the 17% of the original architecture.\nHowever, this improvement comes at a price, since the removed downsampling layers have a negative impact on the computations\nrequired to process an image, which increases by roughly a factor of 8.\n\nIn any way, RFAToolbox allows you to optimize your convolutional neural network architectures\nfor efficiency, performance or a sweetspot between the two without the need for long-running trial-and-error sessions.\n\n## Credits\n\nThis package was created with\n[Cookiecutter](https://github.com/audreyr/cookiecutter) and the\n[browniebroke/cookiecutter-pypackage](https://github.com/browniebroke/cookiecutter-pypackage)\nproject template.\n',
    'author': 'Mats L. Richter',
    'author_email': 'matrichter@uni-osnabrueck.de',
    'maintainer': None,
    'maintainer_email': None,
    'url': 'https://github.com/MLRichter/receptive_field_analysis_toolbox',
    'packages': packages,
    'package_data': package_data,
    'install_requires': install_requires,
    'extras_require': extras_require,
    'python_requires': '>=3.7,<3.11',
}


setup(**setup_kwargs)
