-
Notifications
You must be signed in to change notification settings - Fork 478
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[SYSTEMDS-3694] Python NN Sequence and layer interface
This commit: - Adds a Layer interface for the Python API. - Affine and ReLU classes are changed to extend this interface. - Fixes fixes some small formatting issues in the modified classes. - Adds a Sequential primitive to the nn Python API. It is able to combine multiple nn layers into one sequential module. - fix in the python MultiReturn so outputs of the instance can be properly accessed. - Adds the backwards pass to the Sequential primitives. - Variations to Sequential testing involving MultiReturns. - Test if the input gradient is set correctly on the backwards pass and Fixes a bug where this was not the case on the affine layer. - Testing to verify that the layer gets updated correctly during forward and backward pass. AMLS project SoSe'24 Closes #2025
- Loading branch information
1 parent
8f2a18a
commit 8e1e53b
Showing
8 changed files
with
572 additions
and
44 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,69 @@ | ||
# ------------------------------------------------------------- | ||
# | ||
# Licensed to the Apache Software Foundation (ASF) under one | ||
# or more contributor license agreements. See the NOTICE file | ||
# distributed with this work for additional information | ||
# regarding copyright ownership. The ASF licenses this file | ||
# to you under the Apache License, Version 2.0 (the | ||
# "License"); you may not use this file except in compliance | ||
# with the License. You may obtain a copy of the License at | ||
# | ||
# http://www.apache.org/licenses/LICENSE-2.0 | ||
# | ||
# Unless required by applicable law or agreed to in writing, | ||
# software distributed under the License is distributed on an | ||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY | ||
# KIND, either express or implied. See the License for the | ||
# specific language governing permissions and limitations | ||
# under the License. | ||
# | ||
# ------------------------------------------------------------- | ||
import os | ||
|
||
from systemds.context import SystemDSContext | ||
from systemds.operator import Source | ||
from systemds.utils.helpers import get_path_to_script_layers | ||
|
||
|
||
class Layer: | ||
""" | ||
Interface for neural network layers | ||
""" | ||
|
||
_source: Source = None | ||
|
||
def __init__(self, sds_context: SystemDSContext = None, dml_script: str = None): | ||
if sds_context is not None and dml_script is not None: | ||
self.__class__._create_source(sds_context, dml_script) | ||
|
||
# bypassing overload limitation in python | ||
self.forward = self._instance_forward | ||
self.backward = self._instance_backward | ||
|
||
@classmethod | ||
def _create_source(cls, sds_context: SystemDSContext, dml_script: str): | ||
""" | ||
Create SystemDS source | ||
:param sds_context: SystemDS context | ||
:param dml_script: DML script inside /scripts/nn/layers/ | ||
:return: | ||
""" | ||
if cls._source is None or cls._source.sds_context != sds_context: | ||
script_path = get_path_to_script_layers() | ||
path = os.path.join(script_path, dml_script) | ||
name = dml_script.split(".")[0] | ||
cls._source = sds_context.source(path, name) | ||
|
||
def _instance_forward(self, *args): | ||
raise NotImplementedError | ||
|
||
def _instance_backward(self, *args): | ||
raise NotImplementedError | ||
|
||
@staticmethod | ||
def forward(*args): | ||
raise NotImplementedError | ||
|
||
@staticmethod | ||
def backward(*args): | ||
raise NotImplementedError |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,97 @@ | ||
# ------------------------------------------------------------- | ||
# | ||
# Licensed to the Apache Software Foundation (ASF) under one | ||
# or more contributor license agreements. See the NOTICE file | ||
# distributed with this work for additional information | ||
# regarding copyright ownership. The ASF licenses this file | ||
# to you under the Apache License, Version 2.0 (the | ||
# "License"); you may not use this file except in compliance | ||
# with the License. You may obtain a copy of the License at | ||
# | ||
# http://www.apache.org/licenses/LICENSE-2.0 | ||
# | ||
# Unless required by applicable law or agreed to in writing, | ||
# software distributed under the License is distributed on an | ||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY | ||
# KIND, either express or implied. See the License for the | ||
# specific language governing permissions and limitations | ||
# under the License. | ||
# | ||
# ------------------------------------------------------------- | ||
from systemds.operator import MultiReturn | ||
from systemds.operator.nn.layer import Layer | ||
|
||
|
||
class Sequential(Layer): | ||
def __init__(self, *args): | ||
super().__init__() | ||
|
||
self.layers = [] | ||
if len(args) == 1 and isinstance(args[0], list): | ||
self.layers = args[0] | ||
else: | ||
self.layers = list(args) | ||
|
||
def __len__(self): | ||
return len(self.layers) | ||
|
||
def __getitem__(self, idx): | ||
return self.layers[idx] | ||
|
||
def __setitem__(self, idx, value): | ||
self.layers[idx] = value | ||
|
||
def __delitem__(self, idx): | ||
del self.layers[idx] | ||
|
||
def __iter__(self): | ||
return iter(self.layers) | ||
|
||
def __reversed__(self): | ||
return reversed(self.layers) | ||
|
||
def push(self, layer: Layer): | ||
""" | ||
Add layer | ||
:param layer: Layer | ||
:return: | ||
""" | ||
self.layers.append(layer) | ||
|
||
def pop(self): | ||
""" | ||
Remove last layer | ||
:return: Layer | ||
""" | ||
return self.layers.pop() | ||
|
||
def _instance_forward(self, X): | ||
""" | ||
Forward pass | ||
:param X: Input matrix | ||
:return: output matrix | ||
""" | ||
out = X | ||
for layer in self: | ||
out = layer.forward(out) | ||
|
||
# if MultiReturn, take only output matrix | ||
if isinstance(out, MultiReturn): | ||
out = out[0] | ||
return out | ||
|
||
def _instance_backward(self, dout, X): | ||
""" | ||
Backward pass | ||
:param dout: gradient of output, passed from the upstream | ||
:param X: input matrix | ||
:return: output matrix | ||
""" | ||
dx = dout | ||
for layer in reversed(self): | ||
dx = layer.backward(dx, X) | ||
|
||
# if MultiReturn, take only gradient of input | ||
if isinstance(dx, MultiReturn): | ||
dx = dx[0] | ||
return dx |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.