From 7e3100f796753472919878bb6a1524eec75c0b2a Mon Sep 17 00:00:00 2001 From: lucasdelimanogueira Date: Tue, 7 May 2024 17:44:59 -0300 Subject: [PATCH] unit test nn module mse loss and sigmoid --- norch/__init__.py | 1 + norch/__pycache__/__init__.cpython-38.pyc | Bin 220 -> 234 bytes norch/__pycache__/tensor.cpython-38.pyc | Bin 12633 -> 13063 bytes norch/nn/__pycache__/loss.cpython-38.pyc | Bin 1403 -> 1416 bytes norch/nn/loss.py | 2 +- norch/tensor.py | 45 +++++++++--- tests/test_nn.py | 84 ++++++++++++++++++++++ 7 files changed, 123 insertions(+), 9 deletions(-) create mode 100644 tests/test_nn.py 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 60e1eea56ed04b091e3eea4e2a40899c12ea57d4..21fa1621cf3c36c01212baf3f69bf4d4787ed8e5 100644 GIT binary patch delta 93 zcmcb^_==G?l$V!_0SG>IS*5v6av7r-85vTTQkZj?LLfBrL**~S#z delta 79 zcmaFGc!!ZUl$V!_0SF33EYnOT^2&-y068fPDU3M`xr|Yaj0`DEDa<)c6FrngnWK2} c@{5u)^h#3mit~$#n1Cv8O>Bu{12K3Q0gVF?{Qv*} diff --git a/norch/__pycache__/tensor.cpython-38.pyc b/norch/__pycache__/tensor.cpython-38.pyc index 1471e6def2579338c40de0f043ce466a8eba19c5..b9d5e9baaf7339d2cafbc1895c1cd7bb4b48def3 100644 GIT binary patch delta 4650 zcmbtYYit}>6`nh@vpf6nzP&Ho>ql&_*YPWfOCB_-6Pz|FO=#mJG@FLW+OvL4?A^`X zStluzT8gF6f<#Sjic|?9TLKBAC@3WG15l+R)E_{lO8kK>6e0Bo5>nM)1@#BwoIC4X zuk(NeukJVZea=1S+;h+IpC-RC?LF1hW9{i+nVLleuFpp?3aAeclG zE=cq<_L?-iq=}}WHkpF%8W+w5uga!i6Z9ri-YbaEf6+80824%dppT>9Agcj`=;9iw z)?}>dGL+zq%?RmXy;+U$u_%mK?HGPnn}LoEiXDmrigWEKG}U;%Uqdo+jxLUF6B+Yx zk`3fFVM0jTj(;wfjp@NXRD@HlljG?Gd&4$sm-FR?+&uf#mQC6#wo-0>n#wSNqD*KK z!2wVqCH9>}h%L!(@-+LF9G6_ge|Js3M&w}_KEV1MFZK1{>;Z&91YAc$2tx>AggC%{ zw&vJFD62ShW)3;C69SDvciKz6IK_uxLuf|m1jzUc`CLhzpSzG#ja$_co?l)x>iteXBHUg%wOrRY!qix`qQh~w-7pM%`F`$wOX^m5*nnVg}*-U}A% zew1R7BQQ*Uc6WE+A4Eo5s<2dOk9-Ddzh+wK#t_N}=i$Or2&WOaUB8SqwCjpP zRZ9j{)puEU_=No|_FL?{8FrEn+1uf#$UE%Q=-Y#)1UeSOJqKD`&jIaq_ zW?6)uW>cCF*2Uwm4zk;^o)pM!&X_CY*CQ0AS&`;YZqxIHtU*VS5Wi=K+21;2DnGFc zRy^dIY_(8aj_1p`4{BP2$*kKflaUL?Y>v{J(S(^Aao9hCHKi`L7HwtMBfI#~CRuBA z_vj;zN5I5u$D`+v^w$uws5YOXs-RL$RsUJv`j^pBOKRPZ*tRA$_QTk|R!c?{ZyY_3 zuth>ISQ09gCm&lv6XnSU330~5EFX8+{iG(2x8hGEjvVqjjZhZ{GO4EPTmLpO8q}a^ zz>y7Nh|}V-NN+NKOV<)ulwyjiVmMb_h8vy`r|y{%4M}g(y{fO)YqkB@zv_iG0T}U^ zWR0weuo`@j9)vFxJxEuKP}L$uHEfC#7FlUoTp{!b%(Q|1+TcyRXEbZa$TGPGL^p_A z-zo!YKsnTc~$t;2uw_6=d<)!dA?Eo zhuKSQU-5IdFo}xWg|HKhIUHBjh4Q?r9zxxr8xx2ZoT>+0>W6K8j3mF@G!LEVJaNq= z_gnKxCz)r9$>-e9W9I?_x{xs3jC^iJRSWFnj^W zUAiExkW!vg=XJVhcr_Uf48~oWEe_@nUoj358Sr)!M(y1E9?YX}6OT>Eqz|qMGU;PV z=l*9baoo?4co8Em|B6`b19E;49TaTSF?_76t~$tOI~Pfa{k1az7W8>%YH5oFaf7P` zBbnyOtT8p4)6|8mfhJN7W)6b_Gfjm@3;%> z&)Q!tqY0jXA%TRY1o4a3`%UJO~P za-iSdECqb9tQamt&%1}-m8r;!?Umb#pHHP3SJ-eBu zx&#~E)T(yX)MIMbKR$;sULjvsF}stB5-vyf=ak1@K@sj@pQK_jqKIgnUNJ`MHjC~v z(YN>i)Vwyg_(Yn&+^G4K2jUfjmUFnDDuUlFwZaSmIFGw< zc1U*=Faj^Ua!_6=JfQLbE3(CeciMYhgM(T$oN?{)j?k3l(YLBpUL#YO}U)!Sg0&?&Owfk$t zA0U2MYo!1W^Q;-?$wOIXBOOH5C&x1o{O3kJ&3^591c8vABVmL z_SW#rOK1=@g@7r&6=pw$wcQAN5HRwghY)ZXJ%Ye9qgSvtg`gqKAj~3QR7CR#MTCn8 z2Ero3C4?&ouL_Xb)30OIM7Ro2QHr!s%u(YCeT}^_GH|qC3GiP;ak-L;q}UXXl7Mf# k`s;@}{`MNNG0loog8)`^|OH;HYI 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 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