-
Notifications
You must be signed in to change notification settings - Fork 22
TinyViT on non-tiled Siracusa #117
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: devel
Are you sure you want to change the base?
Changes from all commits
63675c6
549a052
b32a357
340d058
00da44e
a7f0fc0
10b22ae
58a01ab
77b055f
3079743
ff9a903
ad13fe2
3a17aa4
8f3f74d
63f122d
f012b1a
de2752b
11d5293
ce7e4c8
4165983
af78d75
bc2119e
4db4978
48b2ff5
7cba0cf
da162b0
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -22,7 +22,8 @@ def __init__(self, | |
| name: str = 'DeeployNetwork', | ||
| default_channels_first: bool = True, | ||
| deeployStateDir: str = "DeeployState", | ||
| inputOffsets: Dict[str, int] = {}): | ||
| inputOffsets: Dict[str, int] = {}, | ||
| n_cores: int = 8): | ||
|
Comment on lines
+25
to
+26
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion | 🟠 Major Avoid mutable default for inputOffsets; initialize from None Using {} as a default shares state across instances. Accept None and init per inputs. Also simplify the empty-check. Apply: - inputOffsets: Dict[str, int] = {},
- n_cores: int = 8):
+ inputOffsets: Optional[Dict[str, int]] = None,
+ n_cores: int = 8):
@@
- if inputOffsets == {}:
- for key in inputTypes.keys():
- inputOffsets[key] = 0
+ if inputOffsets is None:
+ inputOffsets = {key: 0 for key in inputTypes.keys()}As per Ruff B006. Also applies to: 30-35 🧰 Tools🪛 Ruff (0.13.3)25-25: Do not use mutable data structures for argument defaults Replace with (B006) 🤖 Prompt for AI AgentsThere was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why do you add the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The number of cores is needed to dinamically compute the size of the im2col buffer, for the regular and DW Conv2Ds. This was the method I found to pass it on to the network context (PULPDeployer inherits SignPropDeployer - this class -, which in turn inherits NetworkDeployer). Let me know if you think we should proceed differently with this. |
||
| super().__init__(graph, deploymentPlatform, inputTypes, loweringOptimizer, scheduler, name, | ||
| default_channels_first, deeployStateDir) | ||
|
|
||
|
|
@@ -31,6 +32,7 @@ def __init__(self, | |
| inputOffsets[key] = 0 | ||
|
|
||
| self.inputOffsets = inputOffsets | ||
| self.n_cores = n_cores | ||
|
|
||
| def _createIOBindings(self, ctxt, graph): | ||
| ctxt = super()._createIOBindings(ctxt, graph) | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -247,7 +247,7 @@ def _NCHWtoNHWC_fun(graph: gs.Graph, match: Match, name: str, default_channels_f | |
| if node_op in ["RequantizedConv", "Conv"]: | ||
|
|
||
| # Non DW-Type: | ||
| if opNode.attrs['group'] == 1: | ||
| if opNode.attrs.get('group', 1) == 1: | ||
| weightNode = opNode.inputs[1] | ||
| weightTransposeNode, weightTransposeOutput = _appendTransposeNode(weightNode, name + "TransposeWeight", | ||
| inPermute) | ||
|
|
@@ -341,7 +341,7 @@ def _PULPDWNCHWtoNHWC_fun(graph: gs.Graph, match: Match, name: str, default_chan | |
| opNode = matched_nodes[0] | ||
| node_op = opNode.op | ||
|
|
||
| if opNode.attrs['group'] == 1: | ||
| if opNode.attrs.get('group', 1) == 1: | ||
| return graph | ||
|
|
||
| if (("channels_first" in opNode.attrs and opNode.attrs["channels_first"] != default_channels_first) | ||
|
|
@@ -362,30 +362,67 @@ def _PULPDWNCHWtoNHWC_fun(graph: gs.Graph, match: Match, name: str, default_chan | |
| graph.nodes.append(outputTransposeNode) | ||
|
|
||
| if node_op == "RequantizedConv": | ||
|
|
||
| weightNode = opNode.inputs[1] | ||
| weightTransposeNode, weightTransposeOutput = _appendTransposeNode(weightNode, name + "TransposeWeight", | ||
| inPermute) | ||
| opNode.inputs[1] = weightTransposeOutput | ||
| graph.nodes.append(weightTransposeNode) | ||
| else: | ||
| inputTransposeNode, inputTransposeOutput = _appendTransposeNode(inputNode, name + "_TransposeIn", inPermute) | ||
| opNode.inputs[0] = inputTransposeOutput | ||
| graph.nodes.append(inputTransposeNode) | ||
|
Comment on lines
+370
to
+373
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can you clarify why we are transposing the input on non-RequantizedConvs? which cases are that? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I understood it from our private conversation, it's the floating-point implementation of the dw conv. As suggested somewhere else, I would separate it into a dedicated function There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think we can wait for your PR. |
||
|
|
||
| opNode.attrs["channels_first"] = default_channels_first | ||
|
|
||
| return graph | ||
|
|
||
|
|
||
| # Requantized DW Conv | ||
| @contextagnostic | ||
| class PULPDWConvPass(ReplaceSequentialPatternPass): | ||
|
|
||
| def __init__(self, default_channels_first: bool = True): | ||
| # Define pattern graph | ||
| graph = gs.Graph() | ||
|
|
||
| _input = gs.Variable(name = 'input_1') | ||
| output = graph.layer(inputs = [_input], outputs = ['convOut'], op = 'RequantizedConv', name = 'requantizedConv') | ||
|
|
||
| graph.outputs.append(output) | ||
| graph.inputs.append(_input) | ||
|
|
||
| name = "_NCHW_TO_NHWC_CONV_PASS" | ||
| super().__init__(graph, partial(_PULPDWNCHWtoNHWC_fun, default_channels_first = default_channels_first), name) | ||
| # Define name | ||
| name = "_NCHW_TO_NHWC_DW_CONV_PASS" | ||
|
|
||
| # Initialize Pass | ||
| super().__init__(pattern = graph, | ||
| replacement_fn = partial(_PULPDWNCHWtoNHWC_fun, | ||
| default_channels_first = default_channels_first), | ||
| name = name) | ||
|
|
||
|
|
||
| # Float DW Conv | ||
| @contextagnostic | ||
| class PULPFPDWConvPass(ReplaceSequentialPatternPass): | ||
|
|
||
| def __init__(self, default_channels_first: bool = True): | ||
| # Define pattern graph | ||
| graph = gs.Graph() | ||
|
|
||
| _input = gs.Variable(name = 'input_1') | ||
| output = graph.layer(inputs = [_input], outputs = ['convOut'], op = 'Conv', name = 'conv') | ||
|
|
||
| graph.outputs.append(output) | ||
| graph.inputs.append(_input) | ||
|
|
||
| # Define name | ||
| name = "_NCHW_TO_NHWC_FP_DW_CONV_PASS" | ||
|
|
||
| # Initialize Pass | ||
| super().__init__(pattern = graph, | ||
| replacement_fn = partial(_PULPDWNCHWtoNHWC_fun, | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I recommend writing another NCHWtoNHWC_dw function for the PULP conv kernels, and maybe even check that it's an fp kernel, just to make it even clearer that it differs from the integer one. The transposition of the input is quite a big difference that imo deserves a separate function. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. As said in a comment above, if it's ok with you, I'll wait for your PR and make the changes afterwards. |
||
| default_channels_first = default_channels_first), | ||
| name = name) | ||
|
|
||
|
|
||
| def _PULPDenseNCHWtoNHWC_fun(graph: gs.Graph, match: Match, name: str, default_channels_first: bool = True): | ||
|
|
@@ -465,6 +502,7 @@ def __init__(self, default_channels_first: bool = True): | |
| NCHWtoNHWCPadPass(default_channels_first), | ||
| NCHWtoNHWCMaxPoolPass(default_channels_first), | ||
| PULPDWConvPass(default_channels_first), | ||
| PULPFPDWConvPass(default_channels_first), | ||
| PULPNCHWtoNHWCDenseConvPass(default_channels_first), | ||
| PULPNCHWtoNHWCDenseRequantizedConvPass(default_channels_first), | ||
| ] | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -257,7 +257,7 @@ def __init__(self, name: str = '', shape = [1], alias_of: Optional[List[str]] = | |
| self.is_input: bool = False | ||
| self.is_output: bool = False | ||
|
|
||
| self.alias_of: List[str] = alias_of if alias_of is not None else [] | ||
| self.alias_of: List[str] = list(alias_of) if alias_of is not None else [] | ||
|
|
||
| def _bufferRepresentation(self) -> Dict: | ||
| return {"type": self._instance, "name": self.name, "size": int(np.prod(self.shape))} | ||
|
|
@@ -322,7 +322,11 @@ def __getstate__(self): | |
|
|
||
| @classmethod | ||
| def fromNode(cls, node: gs.Node): | ||
| return (cls(name = node.name, shape = node.shape if not isinstance(node, gs.Constant) else node.values.shape)) | ||
| return (cls( | ||
| name = node.name, | ||
| shape = node.shape if not isinstance(node, gs.Constant) else node.values.shape, | ||
| alias_of = [], | ||
| )) | ||
|
|
||
| def add_aliases(self, aliases_to_add: List[str]): | ||
| """ | ||
|
|
@@ -355,7 +359,7 @@ def get_aliases_of(self): | |
| """ | ||
|
|
||
| if hasattr(self, "alias_of"): | ||
| return self.alias_of | ||
| return list(self.alias_of) | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. why? seems unnecessary since we expect alias_of to be a list? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This was the solution I found to the aliasing issue you handle in this PR. Apparently, "casting" like this, a list to a list, actually creates a new instance of the original list. If your PR will get merged first, then I will fix this, otherwise maybe you can remove this in your PR, since it's not going to be needed anymore. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ahhh, now after refactoring that portion of the code, I understand the need for it 😅 but I prefer my solution much more to this. Let's wait a little bit more for my PRs to get merged |
||
| else: | ||
| return list() | ||
|
|
||
|
|
@@ -399,7 +403,7 @@ class TransientBuffer(VariableBuffer): | |
|
|
||
| def __init__(self, name: str = '', size = 0): | ||
| self.name = name | ||
| self.size = size #: int: Total BYTE size of this TransientBuffer | ||
| self.size = size # int: Total BYTE size | ||
|
|
||
| # Do not override - Should be written in the parsing passes | ||
| self._users = [] | ||
|
|
@@ -446,7 +450,9 @@ class ConstantBuffer(VariableBuffer): | |
| """ | ||
|
|
||
| def __init__(self, name: str = '', shape = [1], values = [0]): | ||
| # Pass a copy of alias_of to avoid shared references | ||
| super().__init__(name, shape) | ||
|
|
||
| values = np.asarray(values) | ||
| # intArray = values.astype(int) | ||
| # assert (np.abs(values - intArray)).max() < 0.001, "Constant value {name} is NOT an integer!" | ||
|
|
@@ -481,7 +487,11 @@ def _bufferRepresentation(self) -> Dict: | |
|
|
||
| @classmethod | ||
| def fromVariableBuffer(cls, buffer: VariableBuffer, values): | ||
| ret = cls(name = buffer.name, shape = buffer.shape, values = values) | ||
| ret = cls( | ||
| name = buffer.name, | ||
| shape = buffer.shape, | ||
| values = values, | ||
| ) | ||
|
|
||
| return ret | ||
|
|
||
|
|
@@ -572,14 +582,16 @@ def __init__(self, | |
| transientBuffer: Type[TransientBuffer], | ||
| globalObjects = {}, | ||
| localObjects = {}, | ||
| name: str = 'DeeployNetwork'): | ||
| name: str = 'DeeployNetwork', | ||
| n_cores: int = 8): | ||
| self.globalObjects = OrderedDict() | ||
| self.localObjects = OrderedDict() | ||
| self.VariableBuffer = variableBuffer | ||
| self.ConstantBuffer = constantBuffer | ||
| self.StructBuffer = structBuffer | ||
| self.TransientBuffer = transientBuffer | ||
| self.name = name | ||
| self.n_cores = n_cores | ||
|
|
||
| self._maxDynamicSize = {} #: int: Maximum dynamic memory size occupied by live buffers at any point in time | ||
| self._dynamicSize = {} #: int: Current dynamic memory size occupied by live buffers | ||
|
|
@@ -874,7 +886,7 @@ def is_buffer(self, value: Any) -> bool: | |
| obj = self.lookup(value) | ||
| return isinstance(obj, VariableBuffer) | ||
|
|
||
| def hoistTransientBuffer(self, name: str, size: int) -> str: | ||
| def hoistTransientBuffer(self, name: str, size: Union[int, str]) -> str: | ||
| """Registers a new TransientBuffer in the local context | ||
|
|
||
| Parameters | ||
|
|
@@ -1186,7 +1198,11 @@ def parseOutputs(cls, ctxt: NetworkContext, node: gs.Node) -> NetworkContext: | |
|
|
||
| for node, name in zip(outputNodes, outputNames): | ||
| if not ctxt.is_global(name): | ||
| nb = ctxt.VariableBuffer(name = name, shape = node.shape) | ||
| nb = ctxt.VariableBuffer( | ||
| name = name, | ||
| shape = node.shape, | ||
| alias_of = [], | ||
| ) | ||
| ctxt.add(nb, 'local') | ||
| else: | ||
| nb = ctxt.lookup(name) | ||
|
|
@@ -2487,7 +2503,8 @@ def __init__(self, | |
| inputTypes: Dict[str, Type[Pointer]], | ||
| scheduler: Callable[[gs.Graph], Schedule] = lambda graph: list(graph.nodes), | ||
| name: str = 'DeeployNetwork', | ||
| deeployStateDir: str = "DeeployState"): | ||
| deeployStateDir: str = "DeeployState", | ||
| n_cores: int = 8): | ||
| """Initializes a new NetworkContainer and its NetworkContext | ||
|
|
||
| Parameters | ||
|
|
@@ -2505,6 +2522,8 @@ def __init__(self, | |
| Prefix to use in deployment to uniquify tensor names | ||
| deeployStateDir : str | ||
| Path to a directory to dump intermediate outputs | ||
| n_cores : int | ||
| The number of cores on which the network will be run | ||
|
|
||
|
|
||
| """ | ||
|
|
@@ -2523,7 +2542,8 @@ def __init__(self, | |
| self.ctxt = NetworkContext(variableBuffer = self.Platform.VariableBuffer, | ||
| constantBuffer = self.Platform.ConstantBuffer, | ||
| structBuffer = self.Platform.StructBuffer, | ||
| transientBuffer = self.Platform.TransientBuffer) | ||
| transientBuffer = self.Platform.TransientBuffer, | ||
| n_cores = n_cores) | ||
|
|
||
| self.deeployStateDir = deeployStateDir | ||
|
|
||
|
|
@@ -2683,10 +2703,13 @@ def parse(self, default_channels_first: bool = True) -> bool: | |
|
|
||
| """ | ||
|
|
||
| self.ctxt = NetworkContext(variableBuffer = self.Platform.VariableBuffer, | ||
| constantBuffer = self.Platform.ConstantBuffer, | ||
| structBuffer = self.Platform.StructBuffer, | ||
| transientBuffer = self.Platform.TransientBuffer) | ||
| self.ctxt = NetworkContext( | ||
| variableBuffer = self.Platform.VariableBuffer, | ||
| constantBuffer = self.Platform.ConstantBuffer, | ||
| structBuffer = self.Platform.StructBuffer, | ||
| transientBuffer = self.Platform.TransientBuffer, | ||
| n_cores = self.ctxt.n_cores, | ||
| ) | ||
|
|
||
| log.debug(" - Create IO Bindings") | ||
| self.ctxt = self._createIOBindings(self.ctxt, self.graph) | ||
|
|
@@ -3232,15 +3255,18 @@ class NetworkDeployer(NetworkContainer): | |
| """Deeploy abstraction to contain an entire network and all necessary information to deploy it | ||
| """ | ||
|
|
||
| def __init__(self, | ||
| graph: gs.Graph, | ||
| deploymentPlatform: DeploymentPlatform, | ||
| inputTypes: Dict[str, Type[Pointer]], | ||
| loweringOptimizer: TopologyOptimizer, | ||
| scheduler: Callable[[gs.Graph], Schedule] = lambda graph: list(graph.nodes), | ||
| name: str = 'DeeployNetwork', | ||
| default_channels_first: bool = True, | ||
| deeployStateDir: str = "DeeployState"): | ||
| def __init__( | ||
| self, | ||
| graph: gs.Graph, | ||
| deploymentPlatform: DeploymentPlatform, | ||
| inputTypes: Dict[str, Type[Pointer]], | ||
| loweringOptimizer: TopologyOptimizer, | ||
| scheduler: Callable[[gs.Graph], Schedule] = lambda graph: list(graph.nodes), | ||
| name: str = 'DeeployNetwork', | ||
| default_channels_first: bool = True, | ||
| deeployStateDir: str = "DeeployState", | ||
| n_cores: int = 8, | ||
| ): | ||
| """Initialize a new NetworkDeployer | ||
|
|
||
| Parameters | ||
|
|
@@ -3269,12 +3295,21 @@ def __init__(self, | |
|
|
||
|
|
||
| """ | ||
| super().__init__(graph, deploymentPlatform, inputTypes, scheduler, name, deeployStateDir = deeployStateDir) | ||
| super().__init__( | ||
| graph = graph, | ||
| platform = deploymentPlatform, | ||
| inputTypes = inputTypes, | ||
| scheduler = scheduler, | ||
| name = name, | ||
| deeployStateDir = deeployStateDir, | ||
| n_cores = n_cores, | ||
| ) | ||
|
|
||
| self.loweringOptimizer = loweringOptimizer | ||
| self.default_channels_first = default_channels_first | ||
|
|
||
| self.prepared = False | ||
| self.n_cores = n_cores | ||
|
|
||
| def __repr__(self): | ||
| return super().__repr__( | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.