diff --git a/norch/__init__.py b/norch/__init__.py index e013fa7..98792f5 100644 --- a/norch/__init__.py +++ b/norch/__init__.py @@ -1,2 +1,3 @@ from norch.tensor import Tensor +from .nn import * from .optim import * \ No newline at end of file diff --git a/norch/__pycache__/__init__.cpython-38.pyc b/norch/__pycache__/__init__.cpython-38.pyc index 60e1eea..21fa162 100644 Binary files a/norch/__pycache__/__init__.cpython-38.pyc and b/norch/__pycache__/__init__.cpython-38.pyc differ diff --git a/norch/__pycache__/tensor.cpython-38.pyc b/norch/__pycache__/tensor.cpython-38.pyc index 1471e6d..b9d5e9b 100644 Binary files a/norch/__pycache__/tensor.cpython-38.pyc and b/norch/__pycache__/tensor.cpython-38.pyc differ diff --git a/norch/nn/__pycache__/loss.cpython-38.pyc b/norch/nn/__pycache__/loss.cpython-38.pyc index 9f14563..170a29b 100644 Binary files a/norch/nn/__pycache__/loss.cpython-38.pyc and b/norch/nn/__pycache__/loss.cpython-38.pyc differ diff --git a/norch/nn/loss.py b/norch/nn/loss.py index 9b55af9..789fab6 100644 --- a/norch/nn/loss.py +++ b/norch/nn/loss.py @@ -22,4 +22,4 @@ class MSELoss(Loss): assert labels.shape == predictions.shape, \ "Labels and predictions shape does not match: {} and {}".format(labels.shape, predictions.shape) - return ((predictions - labels) **2).sum() \ No newline at end of file + return ((predictions - labels) ** 2).sum() / predictions.numel \ No newline at end of file diff --git a/norch/tensor.py b/norch/tensor.py index bf4e4d4..bd5bf18 100644 --- a/norch/tensor.py +++ b/norch/tensor.py @@ -29,6 +29,10 @@ class Tensor: self.ndim = len(shape) self.device = device + self.numel = 1 + for s in self.shape: + self.numel *= s + self.requires_grad = requires_grad self.grad = None self.grad_fn = None @@ -84,6 +88,7 @@ class Tensor: result_data.shape = self.shape.copy() result_data.ndim = self.ndim result_data.device = self.device + result_data.numel = self.numel return result_data @@ -100,7 +105,8 @@ class Tensor: result_data.shape = self.shape.copy() result_data.ndim = self.ndim result_data.device = self.device - + result_data.numel = self.numel + return result_data def reshape(self, new_shape): @@ -117,6 +123,7 @@ class Tensor: result_data.shape = new_shape.copy() result_data.ndim = len(new_shape) result_data.device = self.device + result_data.numel = self.numel result_data.requires_grad = self.requires_grad if result_data.requires_grad: @@ -229,7 +236,8 @@ class Tensor: result_data.shape = self.shape.copy() result_data.ndim = self.ndim result_data.device = self.device - + result_data.numel = self.numel + result_data.requires_grad = self.requires_grad or other.requires_grad if result_data.requires_grad: result_data.grad_fn = AddBackward(self, other) @@ -253,7 +261,8 @@ class Tensor: result_data.shape = self.shape.copy() result_data.ndim = self.ndim result_data.device = self.device - + result_data.numel = self.numel + result_data.requires_grad = self.requires_grad or other.requires_grad if result_data.requires_grad: result_data.grad_fn = AddBackward(other, self) @@ -277,6 +286,7 @@ class Tensor: result_data.shape = self.shape.copy() result_data.ndim = self.ndim result_data.device = self.device + result_data.numel = self.numel result_data.requires_grad = self.requires_grad or other.requires_grad if result_data.requires_grad: @@ -301,6 +311,7 @@ class Tensor: result_data.shape = self.shape.copy() result_data.ndim = self.ndim result_data.device = self.device + result_data.numel = self.numel result_data.requires_grad = self.requires_grad or other.requires_grad if result_data.requires_grad: @@ -314,7 +325,8 @@ class Tensor: result_data.shape = self.shape.copy() result_data.ndim = self.ndim result_data.device = self.device - + result_data.numel = self.numel + Tensor._C.scalar_mul_tensor.argtypes = [ctypes.POINTER(CTensor), ctypes.c_float] Tensor._C.scalar_mul_tensor.restype = ctypes.POINTER(CTensor) @@ -339,6 +351,7 @@ class Tensor: result_data.shape = self.shape.copy() result_data.ndim = self.ndim result_data.device = self.device + result_data.numel = self.numel result_data.requires_grad = self.requires_grad or other.requires_grad if result_data.requires_grad: @@ -371,6 +384,10 @@ class Tensor: result_data.shape = [other.shape[0], self.shape[0], other.shape[2]] result_data.ndim = 3 result_data.device = self.device + result_data.numel = 1 + for s in result_data.shape: + result_data.numel *= s + elif self.ndim == 3 and other.ndim == 3: #broadcasted 3D x 3D matmul @@ -385,7 +402,8 @@ class Tensor: result_data.shape = [other.shape[0], self.shape[1], other.shape[2]] result_data.ndim = 3 result_data.device = self.device - + for s in result_data.shape: + result_data.numel *= s else: #2D matmul if self.ndim != 2 or other.ndim != 2: @@ -404,6 +422,8 @@ class Tensor: result_data.shape = [self.shape[0], other.shape[1]] result_data.ndim = 2 result_data.device = self.device + for s in result_data.shape: + result_data.numel *= s result_data.requires_grad = self.requires_grad or other.requires_grad if result_data.requires_grad: @@ -423,7 +443,8 @@ class Tensor: result_data.shape = self.shape.copy() result_data.ndim = self.ndim result_data.device = self.device - + result_data.numel = self.numel + result_data.requires_grad = self.requires_grad if result_data.requires_grad: result_data.grad_fn = PowBackward(self, other) @@ -442,7 +463,8 @@ class Tensor: result_data.shape = self.shape.copy() result_data.ndim = self.ndim result_data.device = self.device - + result_data.numel = self.numel + result_data.requires_grad = self.requires_grad if result_data.requires_grad: result_data.grad_fn = PowBackward(other, self) @@ -462,7 +484,8 @@ class Tensor: result_data.shape = self.shape.copy() result_data.ndim = self.ndim result_data.device = self.device - + result_data.numel = self.numel + result_data.requires_grad = self.requires_grad if result_data.requires_grad: result_data.grad_fn = DivisionBackward(self, other) @@ -478,6 +501,7 @@ class Tensor: result_data.shape = self.shape.copy() result_data.ndim = self.ndim result_data.device = self.device + result_data.numel = self.numel result_data.requires_grad = self.requires_grad or other.requires_grad if result_data.requires_grad: @@ -500,6 +524,7 @@ class Tensor: result_data.shape = self.shape.copy() result_data.ndim = self.ndim result_data.device = self.device + result_data.numel = self.numel result_data.requires_grad = self.requires_grad if result_data.requires_grad: @@ -519,6 +544,7 @@ class Tensor: result_data.shape = self.shape.copy() result_data.ndim = self.ndim result_data.device = self.device + result_data.numel = self.numel result_data.requires_grad = self.requires_grad if result_data.requires_grad: @@ -537,6 +563,7 @@ class Tensor: result_data.shape = [1] result_data.ndim = 1 result_data.device = self.device + result_data.numel = 1 result_data.requires_grad = self.requires_grad if result_data.requires_grad: @@ -562,6 +589,7 @@ class Tensor: result_data.shape[axis2] = self.shape[axis1] result_data.ndim = self.ndim result_data.device = self.device + result_data.numel = self.numel result_data.requires_grad = self.requires_grad if result_data.requires_grad: @@ -582,6 +610,7 @@ class Tensor: result_data.shape = self.shape.copy()[::-1] result_data.ndim = self.ndim result_data.device = self.device + result_data.numel = self.numel result_data.requires_grad = self.requires_grad if result_data.requires_grad: diff --git a/tests/test_nn.py b/tests/test_nn.py new file mode 100644 index 0000000..483eac3 --- /dev/null +++ b/tests/test_nn.py @@ -0,0 +1,84 @@ +import unittest +import norch +from norch import utils +import torch +import os + +class TestNNModule(unittest.TestCase): + + def setUp(self): + self.device = os.environ.get('device') + if self.device is None or self.device != 'cuda': + self.device = 'cpu' + + def test_mse_loss(self): + """ + Test the MSELoss + """ + loss_fn_norch = norch.nn.MSELoss() + loss_fn_torch = torch.nn.MSELoss() + + # Test case 1: Predictions and labels are equal + predictions_norch = norch.Tensor([1.1, 2, 3, 4]) + labels_norch = norch.Tensor([1.1, 2, 3, 4]) + loss_norch = loss_fn_norch.forward(predictions_norch, labels_norch) + loss_torch_result = utils.to_torch(loss_norch) + + predictions_torch = torch.tensor([1.1, 2, 3, 4]) + labels_torch = torch.tensor([1.1, 2, 3, 4]) + loss_torch_expected = loss_fn_torch(predictions_torch, labels_torch) + + self.assertTrue(utils.compare_torch(loss_torch_result, loss_torch_expected)) + + # Test case 2: Predictions and labels are different + predictions_norch = norch.Tensor([1.1, 2, 3, 4]) + labels_norch = norch.Tensor([4, 3, 2.1, 1]) + loss_norch = loss_fn_norch.forward(predictions_norch, labels_norch) + loss_torch_result = utils.to_torch(loss_norch) + + predictions_torch = torch.tensor([1.1, 2, 3, 4]) + labels_torch = torch.tensor([4, 3, 2.1, 1]) + loss_torch_expected = loss_fn_torch(predictions_torch, labels_torch) + + print(loss_torch_result, loss_torch_expected) + self.assertTrue(utils.compare_torch(loss_torch_result, loss_torch_expected)) + + def test_sigmoid_activation(self): + """ + Test Sigmoid activation function + """ + sigmoid_fn_norch = norch.nn.Sigmoid() + sigmoid_fn_torch = torch.nn.Sigmoid() + + # Test case 1: Positive input + x = norch.Tensor([1, 2, 3]) + sigmoid_norch = sigmoid_fn_norch.forward(x) + sigmoid_torch_result = utils.to_torch(sigmoid_norch) + + x = torch.tensor([1, 2, 3]) + sigmoid_torch_expected = sigmoid_fn_torch.forward(x) + + self.assertTrue(utils.compare_torch(sigmoid_torch_result, sigmoid_torch_expected)) + + # Test case 1: Negative input + x = norch.Tensor([-1, 2, -3]) + sigmoid_norch = sigmoid_fn_norch.forward(x) + sigmoid_torch_result = utils.to_torch(sigmoid_norch) + + x = torch.tensor([-1, 2, -3]) + sigmoid_torch_expected = sigmoid_fn_torch.forward(x) + + self.assertTrue(utils.compare_torch(sigmoid_torch_result, sigmoid_torch_expected)) + + # Test case 1: Zero input + x = norch.Tensor([0, 0, 0]) + sigmoid_norch = sigmoid_fn_norch.forward(x) + sigmoid_torch_result = utils.to_torch(sigmoid_norch) + + x = torch.tensor([0, 0, 0]) + sigmoid_torch_expected = sigmoid_fn_torch.forward(x) + + self.assertTrue(utils.compare_torch(sigmoid_torch_result, sigmoid_torch_expected)) + + + \ No newline at end of file