Skip to content

Commit

Permalink
feat: added functionality to obatain minimum and maximum feasible inp…
Browse files Browse the repository at this point in the history
…ut resolution
  • Loading branch information
MLRichter committed Jan 22, 2022
1 parent 2a4d8ee commit dfbc914
Show file tree
Hide file tree
Showing 7 changed files with 160 additions and 1 deletion.
Binary file modified images/alexnet.PNG
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified images/resnet18.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified images/resnet18eff.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified images/resnet18perf.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified images/vgg16.PNG
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
50 changes: 49 additions & 1 deletion rfa_toolbox/utils/graph_utils.py
@@ -1,4 +1,4 @@
from typing import List
from typing import List, Sequence, Tuple, Union

import numpy as np

Expand Down Expand Up @@ -107,3 +107,51 @@ def filters_non_convolutional_node(
and stride size of 1.
"""
return [node for node in nodes if node.layer_info.kernel_size != np.inf]


def input_resolution_range(
graph: EnrichedNetworkNode, cardinality: int = 2
) -> Tuple[Tuple[int, ...], Tuple[int, ...]]:
"""Obtain the smallest and largest feasible input resolution.
The smallest feasible input resolution is defined as the input smallest input
resolution with no unproductive convolutional layers.
The largest feasible input resolution is defined as the input
resolution with at least one convolutional layer with a maximum
receptive field large enough to grasp the entire image.
These can be considered upper and lower bound for potential input resolutions.
Everything smaller than the provided input resolution will result
in unproductive layers, any resolution larger than the large feasible
input resolution will result in potential patterns being undetectable due to
a to small receptive field size.
Args:
graph: The neural network
cardinality: The tensor shape, which is 2D by default.
Returns:
Smallest and largest feasable input resolution.
"""
all_nodes = obtain_all_nodes(graph)
all_nodes = filters_non_convolutional_node(all_nodes)
rf_min = [x.receptive_field_min for x in all_nodes]
rf_max = [x.receptive_field_max for x in all_nodes]

def find_max(rf: List[Union[Tuple[int, int], int]], axis: int = 0) -> int:
"""Find the maximum value of a list of tuples or integers.
Args:
rf: a list of tuples or integers
axis: the axis along which the maximum shall be found
Returns:
The maximum value of the list.
"""
rf_no_tuples = [
x[axis] if isinstance(x, Sequence) or isinstance(x, np.ndarray) else x
for x in rf
]
return max(rf_no_tuples)

r_max = tuple(find_max(rf_max, i) for i in range(cardinality))
r_min = tuple(find_max(rf_min, i) for i in range(cardinality))
return r_min, r_max
111 changes: 111 additions & 0 deletions tests/test_graph/test_utils.py
Expand Up @@ -5,6 +5,7 @@
from rfa_toolbox.architectures.vgg import vgg16
from rfa_toolbox.graphs import EnrichedNetworkNode, LayerDefinition
from rfa_toolbox.utils.graph_utils import (
input_resolution_range,
obtain_all_critical_layers,
obtain_all_nodes,
obtain_border_layers,
Expand All @@ -21,6 +22,46 @@ def single_node():
return node0


@pytest.fixture()
def sequential_network_non_square():
node0 = EnrichedNetworkNode(
name="Layer0",
layer_info=LayerDefinition(name="Conv3x3", kernel_size=(3, 5), stride_size=1),
predecessors=[],
)
node1 = EnrichedNetworkNode(
name="Layer1",
layer_info=LayerDefinition(name="Conv3x3", kernel_size=(3, 5), stride_size=1),
predecessors=[node0],
)
node2 = EnrichedNetworkNode(
name="Layer2",
layer_info=LayerDefinition(name="Conv3x3", kernel_size=(3, 5), stride_size=1),
predecessors=[node1],
)
node3 = EnrichedNetworkNode(
name="Layer3",
layer_info=LayerDefinition(name="Conv3x3", kernel_size=(3, 5), stride_size=1),
predecessors=[node2],
)
node4 = EnrichedNetworkNode(
name="Layer4",
layer_info=LayerDefinition(name="Conv3x3", kernel_size=(3, 5), stride_size=1),
predecessors=[node3],
)
node5 = EnrichedNetworkNode(
name="Layer5",
layer_info=LayerDefinition(name="Conv3x3", kernel_size=(3, 5), stride_size=1),
predecessors=[node4],
)
node6 = EnrichedNetworkNode(
name="Layer6",
layer_info=LayerDefinition(name="Softmax", kernel_size=1, stride_size=1),
predecessors=[node5],
)
return node6


@pytest.fixture()
def sequential_network():
node0 = EnrichedNetworkNode(
Expand Down Expand Up @@ -101,6 +142,46 @@ def nonsequential_network():
return node6


@pytest.fixture()
def nonsequential_network2():
node0 = EnrichedNetworkNode(
name="Layer0",
layer_info=LayerDefinition(name="Conv3x3", kernel_size=3, stride_size=1),
predecessors=[],
)
node1 = EnrichedNetworkNode(
name="Layer1",
layer_info=LayerDefinition(name="Conv3x3", kernel_size=3, stride_size=1),
predecessors=[node0],
)
node2 = EnrichedNetworkNode(
name="Layer2",
layer_info=LayerDefinition(name="Conv3x3", kernel_size=3, stride_size=1),
predecessors=[node1],
)
node3 = EnrichedNetworkNode(
name="Layer3",
layer_info=LayerDefinition(name="Conv3x3", kernel_size=(7, 12), stride_size=1),
predecessors=[node2],
)
node4 = EnrichedNetworkNode(
name="Layer4",
layer_info=LayerDefinition(name="Conv3x3", kernel_size=(21, 4), stride_size=1),
predecessors=[node2],
)
node5 = EnrichedNetworkNode(
name="Layer5",
layer_info=LayerDefinition(name="Conv3x3", kernel_size=3, stride_size=1),
predecessors=[node4, node3],
)
node6 = EnrichedNetworkNode(
name="Layer6",
layer_info=LayerDefinition(name="Softmax", kernel_size=1, stride_size=1),
predecessors=[node5],
)
return node6


class TestObtainAllNodes:
def test_obtain_node_in_single_graph_network(self, single_node):
node = obtain_all_nodes(single_node)
Expand Down Expand Up @@ -237,3 +318,33 @@ def test_obtaining_border_layer_in_very_large_architecture(self, resnet101_model
assert border.receptive_field_min >= input_res
for pred in border.predecessors:
assert pred.receptive_field_min >= input_res


class TestFindInputResolutionRange:
def test_with_higher_degree_tensor(self, sequential_network):
for i in range(10):
cardinality = np.random.randint(1, 1000)
r_min, r_max = input_resolution_range(sequential_network, cardinality)
assert len(r_max) == cardinality
assert len(r_min) == cardinality

def test_with_scalar_receptive_field_sizes(self, sequential_network):
r_min, r_max = input_resolution_range(sequential_network)
assert len(r_max) == 2
assert len(r_min) == 2
assert r_min == (13, 13)
assert r_max == (13, 13)

def test_with_non_sequential(self, nonsequential_network2):
r_min, r_max = input_resolution_range(nonsequential_network2)
assert len(r_max) == 2
assert len(r_min) == 2
assert r_min == (27, 18)
assert r_max == (29, 20)

def test_with_non_square_receptive_field_sizes(self, sequential_network_non_square):
r_min, r_max = input_resolution_range(sequential_network_non_square)
assert len(r_max) == 2
assert len(r_min) == 2
assert r_min == (13, 25)
assert r_max == (13, 25)

0 comments on commit dfbc914

Please sign in to comment.