From 156ec9e3e30b1e993fbf74edb53bf055208555bc Mon Sep 17 00:00:00 2001 From: lucasdelimanogueira Date: Thu, 16 May 2024 20:34:06 -0300 Subject: [PATCH 01/28] add new test matmul cases broadcasted and batched --- norch/__pycache__/tensor.cpython-38.pyc | Bin 15407 -> 15407 bytes tests/test_autograd.py | 66 +++++++++++++++++++++++- tests/test_operations.py | 66 ++++++++++++++++++++++++ 3 files changed, 131 insertions(+), 1 deletion(-) diff --git a/norch/__pycache__/tensor.cpython-38.pyc b/norch/__pycache__/tensor.cpython-38.pyc index 5a33b4a3b38d8766ef05b6c52cb3729e188a2a2f..eeeaf809dd0adb885c280d76e140a26090808205 100644 GIT binary patch delta 19 ZcmZ2qvA%*Ul$V!_0SKyFHgc)j001~_1nmF- delta 19 ZcmZ2qvA%*Ul$V!_0SGdZH*%@k001{t1i1hJ diff --git a/tests/test_autograd.py b/tests/test_autograd.py index 42431a8..b22eba6 100644 --- a/tests/test_autograd.py +++ b/tests/test_autograd.py @@ -211,7 +211,71 @@ class TestTensorAutograd(unittest.TestCase): self.assertTrue(utils.compare_torch(norch_tensor1_grad_matmul, torch_tensor1_grad_matmul)) self.assertTrue(utils.compare_torch(norch_tensor2_grad_matmul, torch_tensor2_grad_matmul)) - + def test_batched_matmul(self): + """ + Test autograd from batched matrix multiplication: BxMxP = BxNxM @ BxMxP + """ + B = 3 # Batch size + + norch_tensor1_matmul = norch.Tensor([[[1., 2], [3, -4], [5, 6], [7, 8]] for _ in range(B)], requires_grad=True).to(self.device) + norch_tensor2_matmul = norch.Tensor([[[2., 3, 1, 0, 4], [5, -1, 2, 3, 0]] for _ in range(B)], requires_grad=True).to(self.device) + + norch_result_matmul = norch_tensor1_matmul @ norch_tensor2_matmul + norch_result_matmul_sum = norch_result_matmul.sum() # Sum over all elements + norch_result_matmul_sum.backward() + + # Convert gradients to torch tensors + norch_tensor1_grad_matmul = utils.to_torch(norch_tensor1_matmul.grad).to(self.device) + norch_tensor2_grad_matmul = utils.to_torch(norch_tensor2_matmul.grad).to(self.device) + + # Repeat the same process with torch tensors + torch_tensor1_matmul = torch.tensor([[[1., 2], [3, -4], [5, 6], [7, 8]] for _ in range(B)], requires_grad=True).to(self.device) + torch_tensor2_matmul = torch.tensor([[[2., 3, 1, 0, 4], [5, -1, 2, 3, 0]] for _ in range(B)], requires_grad=True).to(self.device) + torch_result_matmul = torch.matmul(torch_tensor1_matmul, torch_tensor2_matmul) + torch_result_matmul_sum = torch_result_matmul.sum() + torch_result_matmul_sum.backward() + + # Extract gradients from torch tensors + torch_tensor1_grad_matmul = torch_tensor1_matmul.grad + torch_tensor2_grad_matmul = torch_tensor2_matmul.grad + + # Assertions to compare the gradients + self.assertTrue(utils.compare_torch(norch_tensor1_grad_matmul, torch_tensor1_grad_matmul)) + self.assertTrue(utils.compare_torch(norch_tensor2_grad_matmul, torch_tensor2_grad_matmul)) + + def test_broadcasted_batched_matmul(self): + """ + Test autograd from batched matrix multiplication: BxMxP = NxM @ BxMxP + """ + B = 3 # Batch size + + norch_tensor1_matmul = norch.Tensor([[1., 2], [3, -4], [5, 6], [7, 8]], requires_grad=True).to(self.device) + norch_tensor2_matmul = norch.Tensor([[[2., 3, 1, 0, 4], [5, -1, 2, 3, 0]] for _ in range(B)], requires_grad=True).to(self.device) + + norch_result_matmul = norch_tensor1_matmul @ norch_tensor2_matmul + norch_result_matmul_sum = norch_result_matmul.sum() # Sum over all elements + norch_result_matmul_sum.backward() + + # Convert gradients to torch tensors + norch_tensor1_grad_matmul = utils.to_torch(norch_tensor1_matmul.grad).to(self.device) + norch_tensor2_grad_matmul = utils.to_torch(norch_tensor2_matmul.grad).to(self.device) + + # Repeat the same process with torch tensors + torch_tensor1_matmul = torch.tensor([[1., 2], [3, -4], [5, 6], [7, 8]], requires_grad=True).to(self.device) + torch_tensor2_matmul = torch.tensor([[[2., 3, 1, 0, 4], [5, -1, 2, 3, 0]] for _ in range(B)], requires_grad=True).to(self.device) + torch_result_matmul = torch.matmul(torch_tensor1_matmul, torch_tensor2_matmul) + torch_result_matmul_sum = torch_result_matmul.sum() + torch_result_matmul_sum.backward() + + # Extract gradients from torch tensors + torch_tensor1_grad_matmul = torch_tensor1_matmul.grad + torch_tensor2_grad_matmul = torch_tensor2_matmul.grad + + # Assertions to compare the gradients + self.assertTrue(utils.compare_torch(norch_tensor1_grad_matmul, torch_tensor1_grad_matmul)) + self.assertTrue(utils.compare_torch(norch_tensor2_grad_matmul, torch_tensor2_grad_matmul)) + + def test_elementwise_mul_scalar(self): """ Test autograd from elementwise multiplication with scalar: scalar * tensor diff --git a/tests/test_operations.py b/tests/test_operations.py index 449c531..3a6f414 100644 --- a/tests/test_operations.py +++ b/tests/test_operations.py @@ -242,6 +242,26 @@ class TestTensorOperations(unittest.TestCase): self.assertTrue(utils.compare_torch(torch_result, torch_expected)) + def test_matmul(self): + """ + Test batched matrix multiplication: MxP = NxM @ MxP + """ + # Creating batched tensors for Norch + norch_tensor1 = norch.Tensor([[1, 2], [3, -4], [5, 6], [7, 8]]).to(self.device) + norch_tensor2 = norch.Tensor([[2, 3, 1, 0, 4], [5, -1, 2, 3, 0]]).to(self.device) + + norch_result = norch_tensor1 @ norch_tensor2 + torch_result = utils.to_torch(norch_result).to(self.device) + + # Converting to PyTorch tensors for comparison + torch_tensor1 = torch.tensor([[1, 2], [3, -4], [5, 6], [7, 8]]).to(self.device) + torch_tensor2 = torch.tensor([[2, 3, 1, 0, 4], [5, -1, 2, 3, 0]]).to(self.device) + + torch_expected = torch.matmul(torch_tensor1, torch_tensor2) + + # Comparing results + self.assertTrue(utils.compare_torch(torch_result, torch_expected)) + def test_reshape_then_matmul(self): """ Test reshaping a tensor followed by matrix multiplication: (tensor.reshape(shape) @ other_tensor) @@ -258,6 +278,52 @@ class TestTensorOperations(unittest.TestCase): self.assertTrue(utils.compare_torch(torch_result, torch_expected)) + def test_batched_matmul(self): + """ + Test batched matrix multiplication: BxMxP = BxNxM @ BxMxP + """ + B = 3 # Batch size + + # Creating batched tensors for Norch + norch_tensor1 = norch.Tensor([[[1, 2], [3, -4], [5, 6], [7, 8]] for _ in range(B)]).to(self.device) + norch_tensor2 = norch.Tensor([[[2, 3, 1, 0, 4], [5, -1, 2, 3, 0]] for _ in range(B)]).to(self.device) + + norch_result = norch_tensor1 @ norch_tensor2 + torch_result = utils.to_torch(norch_result).to(self.device) + + # Converting to PyTorch tensors for comparison + torch_tensor1 = torch.tensor([[[1, 2], [3, -4], [5, 6], [7, 8]] for _ in range(B)]).to(self.device) + torch_tensor2 = torch.tensor([[[2, 3, 1, 0, 4], [5, -1, 2, 3, 0]] for _ in range(B)]).to(self.device) + + torch_expected = torch.matmul(torch_tensor1, torch_tensor2) + + # Comparing results + self.assertTrue(utils.compare_torch(torch_result, torch_expected)) + + + def test_broadcasted_batched_matmul(self): + """ + Test broadcasted batched matrix multiplication: BxMxP = NxM @ BxMxP + """ + B = 3 # Batch size + + # Creating batched tensors for Norch + norch_tensor1 = norch.Tensor([[1, 2], [3, -4], [5, 6], [7, 8]]).to(self.device) + norch_tensor2 = norch.Tensor([[[2, 3, 1, 0, 4], [5, -1, 2, 3, 0]] for _ in range(B)]).to(self.device) + + norch_result = norch_tensor1 @ norch_tensor2 + torch_result = utils.to_torch(norch_result).to(self.device) + + # Converting to PyTorch tensors for comparison + torch_tensor1 = torch.tensor([[1, 2], [3, -4], [5, 6], [7, 8]]).to(self.device) + torch_tensor2 = torch.tensor([[[2, 3, 1, 0, 4], [5, -1, 2, 3, 0]] for _ in range(B)]).to(self.device) + + torch_expected = torch.matmul(torch_tensor1, torch_tensor2) + + # Comparing results + self.assertTrue(utils.compare_torch(torch_result, torch_expected)) + + def test_transpose_then_matmul(self): """ From 3627e424691b8da84b609e2d821f4fb00647032e Mon Sep 17 00:00:00 2001 From: lucasdelimanogueira Date: Thu, 16 May 2024 21:05:30 -0300 Subject: [PATCH 02/28] sum axis autograd --- .../__pycache__/functions.cpython-38.pyc | Bin 7539 -> 7975 bytes norch/autograd/functions.py | 23 ++++++++++++++---- tests/test_autograd.py | 22 +++++++++++++++++ 3 files changed, 40 insertions(+), 5 deletions(-) diff --git a/norch/autograd/__pycache__/functions.cpython-38.pyc b/norch/autograd/__pycache__/functions.cpython-38.pyc index d48733e29dfa77cff51a2321223754822c1d80b7..2c88c4fb3673489be608f82942d81dc1a1b73f4f 100644 GIT binary patch delta 1591 zcmah}&2Jl35Z`&b-mL9pYkM6hvD2!}M~Mka(kjpaA+Zs_=*Gvh{S=S-uNdhAr*1zi34XSGaCmIF0AzC&CdLO@4cBf zZ~hqlYTSIer6r`$_wBuzYYVO4na2aLvpX30S?EW}iYKHUwPcGi4c+qB=JwVeGM=E} z>Ld&>R71y}-F4%6uzJaIhFx02g}rj!n2?E37M`GG+T>>F3TdAuI%P0CWt}A0_yN3z zCw=^<;a{Lrdd*d)1xjuXpXB6x7}KH) zd`N?Y0wK{1$A!FN4qN^7e~CT+B&45?KPi7RSHO`k9=Q)`*&G?whX{kGBZ`oRR@Y$p}Yxgrjai zMs+kNu%To$2Pn{{dU&C-I%Q`IZ`weM zeowhZH%A9G%7!Oy!g)efH4nCep0*8i)9QaYp1K4zibR`7ppCOE_CG!jXWydXc9B@3 zqIoSzMMKB({Ot=mCLN_HJh3U>)My>RP6A?*L|UzjfgxmKU3HdMcVI6c{9-W7*3h|? zWw)HIl~&irZ0zwcI{c_(8U&#;iHQe}9NGN1RFdWon|qxi255UWxfR5q>?? zRd>0F^dS8f0}tf8onv`!CqEuqhb`$SK1~w}7I79mEP7dRmql)*$n6NZwIKHm%f`b^A6sakMKMH)CeVjG9em&@@Oh{sy&+S9JgY delta 1048 zcmY*YOH31C5Z>8sODRy=ZlRPG!&59ONF=;MYb&8jqF~Y(f}(&>kjl#`yYt*yc+jH+v@LrJSU_cS<^aW$p{w4SYh{B3Qs z2&iag@6Fsf9G*_j&Lra);PgC2v7J~7*kImsc6e?oHA!E*M|6qSyJMBgu}o}#?vtlsIv7Q}Q0qKo%2~ret1@&gOU(doc3%M3c zP0^*YAhcw9XG;r6WuZ{7Ufm%#jZY_^WF?C^B@GEks0nQpyvibQe@t>)HLoddWgKD( zwlXYW!Q=Ga?Q>+>Ff$!b4o*&7i_>qL*Kt$Wj3DZ~TT+nv+gN!+47;OkGbZt)sB1Sp zxJ8lTzG5SSwwZ(R9{+ZQ!=G4e6xd3xx(FS1AYoI3n{&`QhxCU{Ao{Xt!J^jQ{dlxZV$ zp)9=T_L?@z`dvnMGW(|`C#LbVY#yvr^MPQZn>k)(@rWki#3uehnF+c@REdz+hVq6` xo(al^qHG(=I-v{ Date: Thu, 16 May 2024 21:25:03 -0300 Subject: [PATCH 03/28] batched broadcasted autograd --- .../__pycache__/functions.cpython-38.pyc | Bin 7975 -> 8063 bytes norch/autograd/functions.py | 6 ++++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/norch/autograd/__pycache__/functions.cpython-38.pyc b/norch/autograd/__pycache__/functions.cpython-38.pyc index 2c88c4fb3673489be608f82942d81dc1a1b73f4f..b0f6eab62515c59bd55b03e38aae8bd4d31ecff8 100644 GIT binary patch delta 892 zcmZ8fUr1A76u;-*JDYR=?{sauTWC+5A&YqM{(e z7f%F9kY*GV1ZGhWMLqP=W3PJ)A@HHM=rKFrYUuhd=f`({=XZXb?|k30=vLHr-e$8% zydJK`N1v?OX038qrKIXvU$VyFnu#+aPI6>t81hnD8j|Pbc_lBWA+Mz2l7i=QvH_+4 zn+iyI&MYg2Y$z+TQ<_)vYFaf^!?eJv5}yaBG$!}`r!CJXg}{*dq;SRA!OGL6wYkM> z^EU$m_k@0iun9$@1vO)&CJLr0Ol5L~${Y@7vQv}U5!_SM@X4xGWq5jyYD1o2A9GnN zg9H}Y3QqoX`HHt^I5#mpPG5ZowR+C=3YE%W2D5_oK zQuNlD&>EN+DJyBuqWtt||c<8Zv1#YaL(jov0G~?-nWAxE; z*%4(aRta!0+@?fzm)6R=>m2_&v#8k07VWfA-2u%M322K}6^LRr{pJ>yg%#|e<(hqP zpDCRRzP(zU>BVgaK3lx#FyNi6=wu$dN;XUM-Fva>aFG+xNk1$)Jff-HF)hKw1CG*! zOqeLTsJ;{1eXP=uZw#K&XJ50{&Em&93KZ-v79{-_;03Mur{MtY57fd->I%e7XIS~1 zs)D_=5oq5wd_%|eerTb4`cZgCU-UMuhlQ`pYm$O$2Z|t{h;+g?Z8a(wUwlq`?lrfSWC##$#(1fxuDK3Z$k|vfxlge;SVn;Oc zR>UF{#hU08G_mPx;1o(?LHJ+3C=`UKG_kWsYv(sXRAT&EH&Y3>%(LvmROQBWa1$-=MVQBj?xfOBoVgZWKhCHg{N=8}?fO$C4zP%uo>AyU zwSC@^Br3CV;LYB}XYDye}MATB9! z0)KiBz!If~lnjOL|1CiSW~jvLwh@Z4QN6}vOm$qe_UHi)Blz7Cgk}8Hk#=P%@QAFb zj}fQUV>sy>YTRGLx4sfQ$Dlu<vuq;I^asJ5veW Date: Fri, 17 May 2024 11:39:26 -0300 Subject: [PATCH 04/28] fix sum axis autograd and reversed add broadcasted order --- build/cpu.o | Bin 11272 -> 11192 bytes build/cuda.cu.o | Bin 120000 -> 120000 bytes build/tensor.o | Bin 42568 -> 42624 bytes norch/__pycache__/tensor.cpython-38.pyc | Bin 15407 -> 15409 bytes .../__pycache__/functions.cpython-38.pyc | Bin 8063 -> 7881 bytes norch/autograd/functions.py | 11 ++++----- norch/csrc/cpu.cpp | 14 +++++++----- norch/csrc/cpu.h | 2 +- norch/csrc/tensor.cpp | 11 ++++++--- norch/libtensor.so | Bin 160224 -> 160224 bytes norch/tensor.py | 2 +- tests/test_autograd.py | 21 ++++++++++++++++-- tests/test_operations.py | 16 +++++++++++-- 13 files changed, 58 insertions(+), 19 deletions(-) diff --git a/build/cpu.o b/build/cpu.o index 6228ac23853271c276dd0881ed9552fffc806dde..054ce2e8e0f1664781c7f4b704e9d4f88cb3fc9e 100644 GIT binary patch delta 2015 zcmZuwe`s4(6u$SRYg3jr>AEFtv;JN&ZR40An_bbh>cxj_T4li^g4SPXQCXptB8?e5 zDt$a7X2(A!Y%-C-29ah4v53s3W28bE*$mif(Aag5t_3SCbkn+c?z{JOZPcMeUAB?Gb;Jm6IJ^Q52PRB16-K50^o9U{O z0x9*i>uO?7QE6($=^{9fPOmt75~icZQgm?&wK2pQ;^kzaPay>3lOczQ=WA!thlky2 z_)(r?oSPgs3y-5vO0*nRPpOHlqUuSLRCRV6_R#~OG)1YnOsCxlMDN|kAUa!3vKbyL z$ufM|NSFz}YOqWPiw+!v3$c0m%(Pj-Bb_EVA}Am5$SwerIQBLm3uy+4JVr7 z)kr(XCUut9ScWd1G+26zd+d@KM}9(*Wo1|_WGCj|a`$$Yb%tH|aY;7MWd@t?U}y z=)kQuVU?PHk#j6TS?<2^_Vak5wYxf7(%xOBiz9fSEm>otCF~*}mNe@eW2<_?U^zO4 zO8%UpY3}c7_7Kt7JY699d|qQ&x{$h?&;N9J#aa7LK-F)mI<&eg<5w$g*=$&s_;;RW zrFjqT?ASVd+~6xKbA6;dv7^}btKulJ6VCf!TXh{(p&9L4z|fAu8)(nN2-+z)S6!#% z6u4AXTf7(L4jbQs-CGIW-lBND*G_2`qb~uK` zLcWG;$o_^W&VExOV2dpQTH~qmr0;X^5VG$}_Vv=OzGt8p*$K%ywhQ(G zp7~gV2(pb>6dpNJCeWZ*$WxF&_K0N5oC5w(viYCvlKlNz(ksJqBnI?iqA zN2$r~6akLNICB#BR|r6rcrn(9-)cbOFH77n@t-ArM&i#){P->bn12awhXlTl9NR63 zm!!if=@656zr?>q+y!0Uikk1GqQNaFOYw`Ti1>Cve_bGcrLIqjcQ)2jdKG$`JoJWX z*CEp6sj85L#&0CsaIRzD@=*y1R5r$Eo1Udrx=Vz&L^2xz=xX+aVv;RF(T+_aCMDjA z7=Jh`^lU*i3;%sn9R5rep4)`;t%ZM8jt4@XAv|#tPK)r$!dutnKLj78^4?tEhSSo2 zunB)Kex=!2uVRp%R%M38zMe8)O(9yxMlC=;}3XDyJ4>7F-oAJ z^)U5AFWUX^CE9NI6YU9TXggebqFMy(hPT@emZoaODFYYqH1U8q4M4fK0@i(w{{aF@ B6E*+< delta 2065 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 LWH#+J)!qLOft1J6 diff --git a/build/tensor.o b/build/tensor.o index d330a483d7b16c0a921697663d057ddf521cc8fc..5df78192dbfd694f7baea9d517a107fdcdcef5fc 100644 GIT binary patch literal 42624 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 diff --git a/norch/__pycache__/tensor.cpython-38.pyc b/norch/__pycache__/tensor.cpython-38.pyc index eeeaf809dd0adb885c280d76e140a26090808205..d3ad7c7337ee09c4c77f1477e10593e6a27e86dd 100644 GIT binary patch delta 49 zcmZ2qv9W?Tl$V!_0SM&M-P1m9n C4GyUQ diff --git a/norch/autograd/__pycache__/functions.cpython-38.pyc b/norch/autograd/__pycache__/functions.cpython-38.pyc index b0f6eab62515c59bd55b03e38aae8bd4d31ecff8..b9738ebc3466df126c4c54d79857ec53db6d7209 100644 GIT binary patch delta 922 zcmZWnT}TvB6rOwU%>K-})4H?nxVkF&kS&2082+WMza@zU5>W_d&1O5RtKM1ZhgoD1 z>d9V?Ak@n)1w{oWR?tI1Z`FgZ(LxVB^bpy5^it=pS}hIS!ynex``Eb0E0v- zh)Nrohp6zhs$K6yC@07rPgI$A;@WZO3J;NX$b}8E3JpdS-bW&^6>ju&pf<*M9Dalk zkTs-w^?rolZws`wA6u-u1`*>!r5<;or~0C=xdezX2;bEx$-~>~K3$l52hZMLQTRY< zf{94)?gaPYS!9|#fM`vJGK|W5aLE{hhcy?-6Y$oqk##844(lh7dHhcw#!ra93tiFp zZl4#h8a+!oU_07MUc&ymZhZvx&&z5?uqL;we+|C+bHUQabmtl}JDKJuaV1^Zhh=Zz zc71ovC}yw9ng}FT$&bR9`lgt;bzIyoF1+T#EiQKEVihi?=3)#!3ZE+s7;QM{3u8vx2(WH?YHe$FY#}Qp>iOQqrE{g7;-`_(%!%e`zrMRJpe&Lxf z-d|wfKt5~`ABrw%^)!u<87hGT931P#XP;kGde+a}EnWv&6ZtRX5t@iFJHn+eMR1ce z(;>(bkWpx^=xo4&O=)gILQU8bb{QK5i#1*_!Tc=4lMOdmA4m%D`tqz&TaPj^%Sqy=fCVxpKNu{OUf0&axRLH19!j;|qIF?TFgvre%QXT@ z{MEHv2Zwv3gGMV&EMml!>l&8IUCJeF5=Z^jK4&a>ZYYM)bSztdG2=`$CGPvTSGG!q zJPcP6TqiTU1Pt8Nz$zXPfBa2aGdZrv&|xpOT&|OXRqJDe-sk6g(F_p?mN` z{0y~e?ZkVQ$8BHYUM=78|B%<>Tlo>#A{xUR;f)vww`d)d_9`#6V<~kmye##dIK2AU zqDIQ%BjZLqi3dr9Qg|=Eu5Jl+(LQhU_L81RF*b_2it31bLnz+~%I2U{6s0^UuZr>x bD67FP@!ef2#wuzGi@gP2r`O?ic^!WNxNHQW diff --git a/norch/autograd/functions.py b/norch/autograd/functions.py index e4f91f5..0023139 100644 --- a/norch/autograd/functions.py +++ b/norch/autograd/functions.py @@ -84,9 +84,9 @@ class MatmulBackward: x, y = self.input if x.ndim != y.ndim: # broadcasted case - print("KKKKKKKKK", x.ndim, y.ndim) aux = (gradient @ y.transpose(-1,-2)) - return [aux.sum(axis=0), x.transpose(-1,-2) @ gradient] + aux_sum = aux.sum(axis=0) + return [aux_sum, x.transpose(-1,-2) @ gradient] else: return [gradient @ y.transpose(-1,-2), x.transpose(-1,-2) @ gradient] @@ -133,14 +133,15 @@ class SumBackward: def backward(self, gradient): input_shape = self.input[0].shape - if self.axis is None: + if self.axis == -1: # If axis is None, sum reduces the tensor to a scalar. grad_output = float(gradient.tensor.contents.data[0]) * self.input[0].ones_like() else: # Broadcast the gradient to the input shape along the specified axis. - grad_output_shape = [1 if i in self.axis else dim for i, dim in enumerate(input_shape)] + grad_output_shape = list(input_shape) + grad_output_shape[self.axis] = 1 grad_output = gradient.reshape(grad_output_shape) - grad_output = grad_output * self.input[0].ones_like() + grad_output = grad_output + self.input[0].zeros_like() return [grad_output] class ReshapeBackward: diff --git a/norch/csrc/cpu.cpp b/norch/csrc/cpu.cpp index bde7b0c..a2f107b 100644 --- a/norch/csrc/cpu.cpp +++ b/norch/csrc/cpu.cpp @@ -11,7 +11,7 @@ void add_tensor_cpu(Tensor* tensor1, Tensor* tensor2, float* result_data) { } } -void add_broadcasted_tensor_cpu(Tensor* tensor1, Tensor* tensor2, float* result_data, int* broadcasted_shape) { +void add_broadcasted_tensor_cpu(Tensor* tensor1, Tensor* tensor2, float* result_data, int* broadcasted_shape, int broadcasted_size) { int max_ndim = tensor1->ndim > tensor2->ndim ? tensor1->ndim : tensor2->ndim; // Calculate strides for broadcasting @@ -28,12 +28,12 @@ void add_broadcasted_tensor_cpu(Tensor* tensor1, Tensor* tensor2, float* result_ int dim2 = i < tensor2->ndim ? tensor2->shape[tensor2->ndim - max_ndim + i] : 1; strides1[i] = dim1 == broadcasted_shape[i] ? stride1 : 0; strides2[i] = dim2 == broadcasted_shape[i] ? stride2 : 0; - stride1 *= broadcasted_shape[i]; - stride2 *= broadcasted_shape[i]; + stride1 *= (dim1 == broadcasted_shape[i]) ? dim1 : 1; + stride2 *= (dim2 == broadcasted_shape[i]) ? dim2 : 1; } // Perform element-wise addition with broadcasting - for (int i = 0; i < tensor1->size; i++) { + for (int i = 0; i < broadcasted_size; i++) { int index1 = 0, index2 = 0; int linear_index = i; for (int j = max_ndim - 1; j >= 0; j--) { @@ -219,7 +219,11 @@ void sum_tensor_cpu(Tensor* tensor, float* result_data, int axis) { return; } - int result_shape[tensor->ndim - 1]; + int* result_shape = (int*)malloc((tensor->ndim - 1) * sizeof(int)); + if (result_shape == NULL) { + fprintf(stderr, "Memory allocation failed\n"); + exit(1); + } int result_size = 1; int axis_stride = tensor->strides[axis]; diff --git a/norch/csrc/cpu.h b/norch/csrc/cpu.h index fad467c..2a97fd2 100644 --- a/norch/csrc/cpu.h +++ b/norch/csrc/cpu.h @@ -4,7 +4,7 @@ #include "tensor.h" void add_tensor_cpu(Tensor* tensor1, Tensor* tensor2, float* result_data); -void add_broadcasted_tensor_cpu(Tensor* tensor1, Tensor* tensor2, float* result_data, int* broadcasted_shape); +void add_broadcasted_tensor_cpu(Tensor* tensor1, Tensor* tensor2, float* result_data, int* broadcasted_shape, int broadcasted_size); void sum_tensor_cpu(Tensor* tensor, float* result_data, int axis); void sub_tensor_cpu(Tensor* tensor1, Tensor* tensor2, float* result_data); void sub_broadcasted_tensor_cpu(Tensor* tensor1, Tensor* tensor2, float* result_data, int* broadcasted_shape); diff --git a/norch/csrc/tensor.cpp b/norch/csrc/tensor.cpp index 104994e..730d204 100644 --- a/norch/csrc/tensor.cpp +++ b/norch/csrc/tensor.cpp @@ -149,20 +149,25 @@ extern "C" { broadcasted_shape[max_ndim - 1 - i] = dim1 > dim2 ? dim1 : dim2; } + int broadcasted_size = 1; + for (int i = 0; i < max_ndim; i++) { + broadcasted_size *= broadcasted_shape[i]; + } + if (strcmp(tensor1->device, "cuda") == 0) { float* result_data; - cudaMalloc((void **)&result_data, tensor1->size * sizeof(float)); + cudaMalloc((void **)&result_data, broadcasted_size * sizeof(float)); add_broadcasted_tensor_cuda(tensor1, tensor2, result_data, broadcasted_shape); return create_tensor(result_data, broadcasted_shape, max_ndim, tensor1->device); } else { - float* result_data = (float*)malloc(tensor1->size * sizeof(float)); + float* result_data = (float*)malloc(broadcasted_size * sizeof(float)); if (result_data == NULL) { fprintf(stderr, "Memory allocation failed\n"); exit(1); } - add_broadcasted_tensor_cpu(tensor1, tensor2, result_data, broadcasted_shape); + add_broadcasted_tensor_cpu(tensor1, tensor2, result_data, broadcasted_shape, broadcasted_size); return create_tensor(result_data, broadcasted_shape, max_ndim, tensor1->device); } } diff --git a/norch/libtensor.so b/norch/libtensor.so index 06f1149ad1e7f471af0f85187b24903bc3f88fb5..9f15e450bdefc73779a4026e40a6c89dcbd07b43 100755 GIT binary patch delta 22521 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 delta 22515 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 Date: Fri, 17 May 2024 13:15:33 -0300 Subject: [PATCH 05/28] sub broadcasted cuda --- build/cpu.o | Bin 11192 -> 11248 bytes build/cuda.cu.o | Bin 120000 -> 130552 bytes build/tensor.o | Bin 42624 -> 43056 bytes norch/__pycache__/tensor.cpython-38.pyc | Bin 15409 -> 15409 bytes .../__pycache__/functions.cpython-38.pyc | Bin 7881 -> 7881 bytes norch/autograd/functions.py | 14 ++-- norch/csrc/cpu.cpp | 8 +- norch/csrc/cpu.h | 2 +- norch/csrc/cuda.cu | 71 ++++++++++++++++-- norch/csrc/cuda.h | 5 +- norch/csrc/tensor.cpp | 27 +++++-- norch/libtensor.so | Bin 160224 -> 172912 bytes tests/test_autograd.py | 21 +++++- tests/test_operations.py | 10 ++- 14 files changed, 128 insertions(+), 30 deletions(-) diff --git a/build/cpu.o b/build/cpu.o index 054ce2e8e0f1664781c7f4b704e9d4f88cb3fc9e..0b75e023751f2236eacd5535a5a69885d1d11141 100644 GIT binary patch delta 1565 zcmbW0Ur19?9LLYun%gY5nc8XQQgga3HMAE~0+ASKMx%lVlY%s&!b&8F!iOBc zUiMH;B1kZrjZCqJ3?*MI_tqc7!akr zAID!B7HRnHtN+fnQizxv;FmEJud%G#poX2ccN+;Iew*W#;XqE7u?rrtT0M&|uop(! z4UruWdsc;=w`;N^PkFzUP;zmT1exXda1QyBS#ssK@N)`(heh%;vLn^VXOVXxKa9M= zngKmTC0s@!Dus;=JwRTREmx@GbI~S{H*c2nLF8-5SGGt#hrG-nH#%dJ{CcM2r*DQ{ z7prYW`Ji3OH)Kb;kWVA;M1EIfIq#ub?~r(b&qaGKR5~VN&ujD)Spx2h^O}A*ATfbjVJaGUW(sG_)9eYDdcKzcwX`z!fxhFYqD}Q zS-)Yz%~}d_IpR&_s(ltB*PqL+;o5V}Luom42JvgF)!cG%;)g&vqa$#(JV;kzf;&}Z zM$sWM#Ipn|Tvc`WRj1BEX$5m))sYe`3Y8m$09RFsoz$tkLb$35DymawV3@0_Zc;jR z8opJq7F8cr;)T4a_iogLj;oqDr{ghb@-U~WfFYea1f5(}zk6DzreVOtXb8qU^Yk4= zymd4M32!5{LGEr3?DK8M(G@;&K*Go1iO);-^HC#h=N=IQJl=*dkE4*t#N5h8tEE_; P>I<}0*3mTxS9<;e>Dnk_ delta 1640 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 diff --git a/build/tensor.o b/build/tensor.o index 5df78192dbfd694f7baea9d517a107fdcdcef5fc..b8560d82b24a628ce8cc4ff6e0ead48af48ba712 100644 GIT binary patch literal 43056 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 AyZ`_I literal 42624 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 delta 405 zcmX|&Pbhw?gw`5`P*Tdl39a2YP+U0p6D}U5a3Ist z{Amp(5$&X0+~nZu;Oydn-%7sqeV(W9_wziqKelVbnx+(p$t1cbHZqzivC}ks;e;X{wIThKE&Jm@#P-tvrwt zXcOvVgR|Bh5@1WdQS{fC3jOnzGKWrXDl>>uamLWWLFWa!S*#wThv#ZPy7;Z0U@)83 zyuc81dLIVpD!IW#_Nk-^i1W+Ui*a_9o?(`srE8euf)T+Cb4Ct}*=6?_u*7%I02bI{ zrm@Ai737Z@$F@jOkf+uwcDdqR#Xb#R3_Be09U;kY-y~8(EzXzKFdB6ndim > tensor2->ndim ? tensor1->ndim : tensor2->ndim; // Calculate strides for broadcasting @@ -75,12 +75,12 @@ void sub_broadcasted_tensor_cpu(Tensor* tensor1, Tensor* tensor2, float* result_ int dim2 = i < tensor2->ndim ? tensor2->shape[tensor2->ndim - max_ndim + i] : 1; strides1[i] = dim1 == broadcasted_shape[i] ? stride1 : 0; strides2[i] = dim2 == broadcasted_shape[i] ? stride2 : 0; - stride1 *= broadcasted_shape[i]; - stride2 *= broadcasted_shape[i]; + stride1 *= (dim1 == broadcasted_shape[i]) ? dim1 : 1; + stride2 *= (dim2 == broadcasted_shape[i]) ? dim2 : 1; } // Perform element-wise addition with broadcasting - for (int i = 0; i < tensor1->size; i++) { + for (int i = 0; i < broadcasted_size; i++) { int index1 = 0, index2 = 0; int linear_index = i; for (int j = max_ndim - 1; j >= 0; j--) { diff --git a/norch/csrc/cpu.h b/norch/csrc/cpu.h index 2a97fd2..be875ed 100644 --- a/norch/csrc/cpu.h +++ b/norch/csrc/cpu.h @@ -7,7 +7,7 @@ void add_tensor_cpu(Tensor* tensor1, Tensor* tensor2, float* result_data); void add_broadcasted_tensor_cpu(Tensor* tensor1, Tensor* tensor2, float* result_data, int* broadcasted_shape, int broadcasted_size); void sum_tensor_cpu(Tensor* tensor, float* result_data, int axis); void sub_tensor_cpu(Tensor* tensor1, Tensor* tensor2, float* result_data); -void sub_broadcasted_tensor_cpu(Tensor* tensor1, Tensor* tensor2, float* result_data, int* broadcasted_shape); +void sub_broadcasted_tensor_cpu(Tensor* tensor1, Tensor* tensor2, float* result_data, int* broadcasted_shape, int broadcasted_size); void elementwise_mul_tensor_cpu(Tensor* tensor1, Tensor* tensor2, float* result_data); void scalar_div_tensor_cpu(float scalar, Tensor* tensor, float* result_data); void tensor_div_scalar_cpu(Tensor* tensor, float scalar, float* result_data); diff --git a/norch/csrc/cuda.cu b/norch/csrc/cuda.cu index 9ebe6d3..467b8cc 100644 --- a/norch/csrc/cuda.cu +++ b/norch/csrc/cuda.cu @@ -71,7 +71,7 @@ __global__ void add_broadcasted_tensor_cuda_kernel(float* data1, float* data2, f result_data[i] = data1[index1] + data2[index2]; } -__host__ void add_broadcasted_tensor_cuda(Tensor* tensor1, Tensor* tensor2, float* result_data, int* broadcasted_shape) { +__host__ void add_broadcasted_tensor_cuda(Tensor* tensor1, Tensor* tensor2, float* result_data, int* broadcasted_shape, int broadcasted_size) { int max_ndim = tensor1->ndim > tensor2->ndim ? tensor1->ndim : tensor2->ndim; int* strides1 = (int*)malloc(max_ndim * sizeof(int)); @@ -87,8 +87,8 @@ __host__ void add_broadcasted_tensor_cuda(Tensor* tensor1, Tensor* tensor2, floa int dim2 = i < tensor2->ndim ? tensor2->shape[tensor2->ndim - max_ndim + i] : 1; strides1[i] = dim1 == broadcasted_shape[i] ? stride1 : 0; strides2[i] = dim2 == broadcasted_shape[i] ? stride2 : 0; - stride1 *= broadcasted_shape[i]; - stride2 *= broadcasted_shape[i]; + stride1 *= (dim1 == broadcasted_shape[i]) ? dim1 : 1; + stride2 *= (dim2 == broadcasted_shape[i]) ? dim2 : 1; } int* d_broadcasted_shape; @@ -104,8 +104,8 @@ __host__ void add_broadcasted_tensor_cuda(Tensor* tensor1, Tensor* tensor2, floa cudaMalloc((void**)&d_strides2, max_ndim * sizeof(int)); cudaMemcpy(d_strides2, strides2, max_ndim * sizeof(int), cudaMemcpyHostToDevice); - int number_of_blocks = (tensor1->size + THREADS_PER_BLOCK - 1) / THREADS_PER_BLOCK; - add_broadcasted_tensor_cuda_kernel<<>>(tensor1->data, tensor2->data, result_data, d_broadcasted_shape, d_strides1, d_strides2, max_ndim, tensor1->size); + int number_of_blocks = (broadcasted_size + THREADS_PER_BLOCK - 1) / THREADS_PER_BLOCK; + add_broadcasted_tensor_cuda_kernel<<>>(tensor1->data, tensor2->data, result_data, d_broadcasted_shape, d_strides1, d_strides2, max_ndim, broadcasted_size); cudaError_t error = cudaGetLastError(); if (error != cudaSuccess) { @@ -187,6 +187,67 @@ __host__ void sub_tensor_cuda(Tensor* tensor1, Tensor* tensor2, float* result_da cudaDeviceSynchronize(); } +__global__ void sub_broadcasted_tensor_cuda_kernel(float* data1, float* data2, float* result_data, int* broadcasted_shape, int* strides1, int*strides2, int max_ndim, int size) { + int i = blockIdx.x * blockDim.x + threadIdx.x; + if (i >= size) return; + + int index1 = 0, index2 = 0; + int linear_index = i; + for (int j = max_ndim - 1; j >= 0; j--) { + int pos = linear_index % broadcasted_shape[j]; + linear_index /= broadcasted_shape[j]; + if (strides1[j] != 0) index1 += pos * strides1[j]; + if (strides2[j] != 0) index2 += pos * strides2[j]; + } + result_data[i] = data1[index1] - data2[index2]; +} + +__host__ void sub_broadcasted_tensor_cuda(Tensor* tensor1, Tensor* tensor2, float* result_data, int* broadcasted_shape, int broadcasted_size) { + int max_ndim = tensor1->ndim > tensor2->ndim ? tensor1->ndim : tensor2->ndim; + + int* strides1 = (int*)malloc(max_ndim * sizeof(int)); + int* strides2 = (int*)malloc(max_ndim * sizeof(int)); + if (strides1 == NULL || strides2 == NULL) { + fprintf(stderr, "Memory allocation failed\n"); + exit(1); + } + + int stride1 = 1, stride2 = 1; + for (int i = max_ndim - 1; i >= 0; i--) { + int dim1 = i < tensor1->ndim ? tensor1->shape[tensor1->ndim - max_ndim + i] : 1; + int dim2 = i < tensor2->ndim ? tensor2->shape[tensor2->ndim - max_ndim + i] : 1; + strides1[i] = dim1 == broadcasted_shape[i] ? stride1 : 0; + strides2[i] = dim2 == broadcasted_shape[i] ? stride2 : 0; + stride1 *= (dim1 == broadcasted_shape[i]) ? dim1 : 1; + stride2 *= (dim2 == broadcasted_shape[i]) ? dim2 : 1; + } + + int* d_broadcasted_shape; + int* d_strides1; + int* d_strides2; + + cudaMalloc((void**)&d_broadcasted_shape, max_ndim * sizeof(int)); + cudaMemcpy(d_broadcasted_shape, broadcasted_shape, max_ndim * sizeof(int), cudaMemcpyHostToDevice); + + cudaMalloc((void**)&d_strides1, max_ndim * sizeof(int)); + cudaMemcpy(d_strides1, strides1, max_ndim * sizeof(int), cudaMemcpyHostToDevice); + + cudaMalloc((void**)&d_strides2, max_ndim * sizeof(int)); + cudaMemcpy(d_strides2, strides2, max_ndim * sizeof(int), cudaMemcpyHostToDevice); + + int number_of_blocks = (broadcasted_size + THREADS_PER_BLOCK - 1) / THREADS_PER_BLOCK; + sub_broadcasted_tensor_cuda_kernel<<>>(tensor1->data, tensor2->data, result_data, d_broadcasted_shape, d_strides1, d_strides2, max_ndim, broadcasted_size); + + cudaError_t error = cudaGetLastError(); + if (error != cudaSuccess) { + printf("CUDA error: %s\n", cudaGetErrorString(error)); + exit(-1); + } + + cudaDeviceSynchronize(); + cudaFree(d_broadcasted_shape); +} + __global__ void elementwise_mul_tensor_cuda_kernel(float* data1, float* data2, float* result_data, int size) { int i = blockIdx.x * blockDim.x + threadIdx.x; diff --git a/norch/csrc/cuda.h b/norch/csrc/cuda.h index 5d5e353..f7a883b 100644 --- a/norch/csrc/cuda.h +++ b/norch/csrc/cuda.h @@ -8,7 +8,10 @@ __host__ void add_tensor_cuda(Tensor* tensor1, Tensor* tensor2, float* result_data); __global__ void add_broadcasted_tensor_cuda_kernel(float* data1, float* data2, float* result_data, int* broadcasted_shape, int* strides1, int*strides2, int max_ndim, int size); - __host__ void add_broadcasted_tensor_cuda(Tensor* tensor1, Tensor* tensor2, float* result_data, int* broadcasted_shape); + __host__ void add_broadcasted_tensor_cuda(Tensor* tensor1, Tensor* tensor2, float* result_data, int* broadcasted_shape, int broadcasted_size); + + __global__ void sub_broadcasted_tensor_cuda_kernel(float* data1, float* data2, float* result_data, int* broadcasted_shape, int* strides1, int*strides2, int max_ndim, int size); + __host__ void sub_broadcasted_tensor_cuda(Tensor* tensor1, Tensor* tensor2, float* result_data, int* broadcasted_shape, int broadcasted_size); __global__ void sum_tensor_cuda_kernel(float* data, float* result_data); __host__ void sum_tensor_cuda(Tensor* tensor, float* result_data); diff --git a/norch/csrc/tensor.cpp b/norch/csrc/tensor.cpp index 730d204..824737d 100644 --- a/norch/csrc/tensor.cpp +++ b/norch/csrc/tensor.cpp @@ -157,7 +157,7 @@ extern "C" { if (strcmp(tensor1->device, "cuda") == 0) { float* result_data; cudaMalloc((void **)&result_data, broadcasted_size * sizeof(float)); - add_broadcasted_tensor_cuda(tensor1, tensor2, result_data, broadcasted_shape); + add_broadcasted_tensor_cuda(tensor1, tensor2, result_data, broadcasted_shape, broadcasted_size); return create_tensor(result_data, broadcasted_shape, max_ndim, tensor1->device); } else { @@ -295,16 +295,27 @@ extern "C" { broadcasted_shape[max_ndim - 1 - i] = dim1 > dim2 ? dim1 : dim2; } - // Allocate memory for result tensor - float* result_data = (float*)malloc(tensor1->size * sizeof(float)); - if (result_data == NULL) { - fprintf(stderr, "Memory allocation failed\n"); - exit(1); + int broadcasted_size = 1; + for (int i = 0; i < max_ndim; i++) { + broadcasted_size *= broadcasted_shape[i]; } - sub_broadcasted_tensor_cpu(tensor1, tensor2, result_data, broadcasted_shape); + if (strcmp(tensor1->device, "cuda") == 0) { + float* result_data; + cudaMalloc((void **)&result_data, broadcasted_size * sizeof(float)); + sub_broadcasted_tensor_cuda(tensor1, tensor2, result_data, broadcasted_shape, broadcasted_size); + return create_tensor(result_data, broadcasted_shape, max_ndim, tensor1->device); + } + else { + float* result_data = (float*)malloc(broadcasted_size * sizeof(float)); + if (result_data == NULL) { + fprintf(stderr, "Memory allocation failed\n"); + exit(1); + } - return create_tensor(result_data, broadcasted_shape, max_ndim, tensor1->device); + sub_broadcasted_tensor_cpu(tensor1, tensor2, result_data, broadcasted_shape, broadcasted_size); + return create_tensor(result_data, broadcasted_shape, max_ndim, tensor1->device); + } } Tensor* elementwise_mul_tensor(Tensor* tensor1, Tensor* tensor2) { diff --git a/norch/libtensor.so b/norch/libtensor.so index 9f15e450bdefc73779a4026e40a6c89dcbd07b43..a125f3cf42d7df3c5d394600b7016cafea233f0f 100755 GIT binary patch delta 42610 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 diff --git a/tests/test_autograd.py b/tests/test_autograd.py index 7635e58..8cd105e 100644 --- a/tests/test_autograd.py +++ b/tests/test_autograd.py @@ -58,7 +58,7 @@ class TestTensorAutograd(unittest.TestCase): self.assertTrue(utils.compare_torch(norch_tensor2_grad, torch_tensor2_grad)) - def test_broadcasting_addition_autograd(self): + def test_broadcasted_addition_autograd(self): """ Test autograd for broadcasting addition: tensor1 + tensor2 """ @@ -115,7 +115,7 @@ class TestTensorAutograd(unittest.TestCase): self.assertTrue(utils.compare_torch(norch_tensor1_grad_sub, torch_tensor1_grad_sub)) self.assertTrue(utils.compare_torch(norch_tensor2_grad_sub, torch_tensor2_grad_sub)) - def test_broadcasting_subtraction_autograd(self): + def test_broadcasted_subtraction_autograd(self): """ Test autograd for broadcasting subtraction: tensor1 - tensor2 """ @@ -135,6 +135,23 @@ class TestTensorAutograd(unittest.TestCase): self.assertTrue(utils.compare_torch(norch_tensor1_grad, torch_tensor1_grad)) self.assertTrue(utils.compare_torch(norch_tensor2_grad, torch_tensor2_grad)) + + # reversed order broadcasting + + norch_result = (norch_tensor2 - norch_tensor1).sum() + norch_result.backward() + norch_tensor1_grad = utils.to_torch(norch_tensor1.grad).to(self.device) + norch_tensor2_grad = utils.to_torch(norch_tensor2.grad).to(self.device) + + torch_tensor1 = torch.tensor([[[1., 2, 3], [4, 5, 6]]], requires_grad=True).to(self.device) # Shape (1, 2, 3) + torch_tensor2 = torch.tensor([1.5, -1, 0], requires_grad=True).to(self.device) # Shape (3) + torch_result = (torch_tensor2 - torch_tensor1).sum() + torch_result.backward() + torch_tensor1_grad = torch_tensor1.grad + torch_tensor2_grad = torch_tensor2.grad + + self.assertTrue(utils.compare_torch(norch_tensor1_grad, torch_tensor1_grad)) + self.assertTrue(utils.compare_torch(norch_tensor2_grad, torch_tensor2_grad)) def test_division(self): diff --git a/tests/test_operations.py b/tests/test_operations.py index e7dd833..d6552a6 100644 --- a/tests/test_operations.py +++ b/tests/test_operations.py @@ -52,6 +52,7 @@ class TestTensorOperations(unittest.TestCase): self.assertTrue(utils.compare_torch(torch_result, torch_expected)) + # reversed order broadcasting norch_tensor1 = norch.Tensor([[0, 2]]).to(self.device) norch_tensor2 = norch.Tensor([[3, 4], [5, -1]]).to(self.device) norch_result = norch_tensor1 + norch_tensor2 @@ -61,7 +62,6 @@ class TestTensorOperations(unittest.TestCase): torch_tensor2 = torch.tensor([[3, 4], [5, -1]]).to(self.device) torch_expected = torch_tensor1 + torch_tensor2 - print(torch_result, torch_expected) self.assertTrue(utils.compare_torch(torch_result, torch_expected)) norch_result = norch_tensor2 + norch_tensor1 @@ -101,6 +101,14 @@ class TestTensorOperations(unittest.TestCase): self.assertTrue(utils.compare_torch(torch_result, torch_expected)) + # reversed order broadcasting + norch_result = norch_tensor2 - norch_tensor1 + torch_result = utils.to_torch(norch_result).to(self.device) + + torch_expected = torch_tensor2 - torch_tensor1 + + self.assertTrue(utils.compare_torch(torch_result, torch_expected)) + def test_division_by_scalar(self): """ From abcd17e6797071c021a8ef795104304763e96602 Mon Sep 17 00:00:00 2001 From: lucasdelimanogueira Date: Fri, 17 May 2024 13:49:32 -0300 Subject: [PATCH 06/28] fix test autograd sub broadcasted --- norch/__pycache__/tensor.cpython-38.pyc | Bin 15409 -> 15409 bytes .../__pycache__/functions.cpython-38.pyc | Bin 7881 -> 7881 bytes norch/autograd/functions.py | 1 + tests/test_autograd.py | 8 ++++++++ 4 files changed, 9 insertions(+) diff --git a/norch/__pycache__/tensor.cpython-38.pyc b/norch/__pycache__/tensor.cpython-38.pyc index 939d3712e44c90682fa532429141b3c9dc46906e..72d3bebe7c6e0b2310c3fc5635d02abacb3002da 100644 GIT binary patch delta 763 zcmaLV&rcIk5C`y1DaCYCA(W;?0$tPA*t%+$!ip`5lt#nBs1biHDzvsJ{s?s~rhp16 z+%%d@^q|HA@nT|3Hfz)n6L0zY0_XuvAkX*J zPAc+CRck$i4OQ^mW5p4doxzTW?${{eli)Ovtw`Fi92fKxtKrQY?b_t6U&05vmZ>B^ zsxfl)e^*c9YUMmCso<y z7qvcR26a?W@A9qoivE1?N;1h$wiWAnPV4q|6w*>f<=Q_$U1+3d#dEOJ zcN*ym-_s*}YUhUygUA>Fa>^1p>KyONY!q2znT&WQk21!*fm1s`7VH5zun)+WVj`LV z=fDMU5ny1J1*%{g)W9s51J}U~Fb@{MO>hg`cCB~0KCP=*Q)><0cXsb1GO>0`w z`lI+;6b3z1@ld=7A}lK^MeydI;9(EqU2js*lkWGWB6u)|5A$B$%$psaQ8Q}p10Ijt zVgID_hWm?5mibic^h@;C2t%RxIyKLots2tR8Z zmyRQ>Cv^CpIHi893;f=nY_eSAlhc~TKm9W_$mPIWX##&1O||_w`34b}Krd(l1%9Y> z(n)@$sI8~4q1e1|=@GR5x6+s)8xFJid1U7M~t6QpH!h5@xX;OSt zV{rC=SD(ezav7Bra7HBkPzp}@Z@)g)lAT-%9i{?*2pqo-V@okHf$quvB564CEDvkIewD}nv2msq7tvfswDd0{N`1At;S^Ul~7FGh=j@ IKV^>n26O4QV*mgE diff --git a/norch/autograd/__pycache__/functions.cpython-38.pyc b/norch/autograd/__pycache__/functions.cpython-38.pyc index 53f8f0f128e7c447aba89ed800191f02ca1cd362..a7d3c51799e12ba048365fea0230b6d120e52637 100644 GIT binary patch delta 346 zcmWNJODKZ@0LT5Y+3RDQZ`-$r_q(__aGDy8yf)EFNiOqhH_1v4CeJu%e-SBpF25p6Dr8Hn)|M&CiNlzp}>#Q0_Fz!@9t5!~>@zJ^-{94=h* Z!V$qeeFbxP;9G$U3EB!bVBuw+24oFXJ&uMvK0<4TT7I^r ztsJ(ZR%&tJCI?psXBUU@J%^{~^km&xx8M~7O`c3^xN~ynPS`3?VzE;4`M9hW`Kp>> zt-=eDA;nt0a$4k@L1dLsPmgjG)jU%M;gD^jOIxFG$eSwm6{g{$Ms-y2oobDF=i5sCD7-FWV7kw-#PQjOWDQ*D# z{4MFh1UvLG%<)IRg#|7fB+N2n$RM0pFC7O~`6UiuiOpp<*kizG&f zj=533iBlR(9vpGRbcu83OnzL-HZoM<#2GItVz}n8`4CZlo4awvCQAfKepCP(X?krdc;LHDLW*YlK8%dp&HsaKa;N|R diff --git a/norch/autograd/functions.py b/norch/autograd/functions.py index d0aed64..30e452f 100644 --- a/norch/autograd/functions.py +++ b/norch/autograd/functions.py @@ -15,6 +15,7 @@ class AddBroadcastedBackward: x, y = self.input grad_x = self._reshape_gradient(gradient, x.shape) grad_y = self._reshape_gradient(gradient, y.shape) + return [grad_x, grad_y] def _reshape_gradient(self, gradient, shape): diff --git a/tests/test_autograd.py b/tests/test_autograd.py index 8cd105e..1db0569 100644 --- a/tests/test_autograd.py +++ b/tests/test_autograd.py @@ -80,12 +80,17 @@ class TestTensorAutograd(unittest.TestCase): self.assertTrue(utils.compare_torch(norch_tensor2_grad, torch_tensor2_grad)) ## reversed order broadcasting + norch_tensor1 = norch.Tensor([[[1., 2, 3], [4, 5, 6]]], requires_grad=True).to(self.device) # Shape (1, 2, 3) + norch_tensor2 = norch.Tensor([1.5, -1, 0], requires_grad=True).to(self.device) # Shape (3) norch_result = (norch_tensor2 + norch_tensor1).sum() norch_result.backward() norch_tensor1_grad = utils.to_torch(norch_tensor1.grad).to(self.device) norch_tensor2_grad = utils.to_torch(norch_tensor2.grad).to(self.device) + torch_tensor1 = torch.tensor([[[1., 2, 3], [4, 5, 6]]], requires_grad=True).to(self.device) # Shape (1, 2, 3) + torch_tensor2 = torch.tensor([1.5, -1, 0], requires_grad=True).to(self.device) # Shape (3) + torch_result = (torch_tensor2 + torch_tensor1).sum() torch_result.backward() torch_tensor1_grad = torch_tensor1.grad @@ -137,6 +142,8 @@ class TestTensorAutograd(unittest.TestCase): self.assertTrue(utils.compare_torch(norch_tensor2_grad, torch_tensor2_grad)) # reversed order broadcasting + norch_tensor1 = norch.Tensor([[[1., 2, 3], [4, 5, 6]]], requires_grad=True).to(self.device) # Shape (1, 2, 3) + norch_tensor2 = norch.Tensor([1.5, -1, 0], requires_grad=True).to(self.device) # Shape (3) norch_result = (norch_tensor2 - norch_tensor1).sum() norch_result.backward() @@ -145,6 +152,7 @@ class TestTensorAutograd(unittest.TestCase): torch_tensor1 = torch.tensor([[[1., 2, 3], [4, 5, 6]]], requires_grad=True).to(self.device) # Shape (1, 2, 3) torch_tensor2 = torch.tensor([1.5, -1, 0], requires_grad=True).to(self.device) # Shape (3) + torch_result = (torch_tensor2 - torch_tensor1).sum() torch_result.backward() torch_tensor1_grad = torch_tensor1.grad From 5dd3f3d2026e2ea8a84c85c8f6a242ec9d77a987 Mon Sep 17 00:00:00 2001 From: lucasdelimanogueira Date: Fri, 17 May 2024 19:08:09 -0300 Subject: [PATCH 07/28] implementing keepdim sum --- build/cpu.o | Bin 11248 -> 10976 bytes build/tensor.o | Bin 43056 -> 43312 bytes examples/train.ipynb | 33 ++++++++++------- norch/__pycache__/tensor.cpython-38.pyc | Bin 15409 -> 15530 bytes .../__pycache__/functions.cpython-38.pyc | Bin 7881 -> 8163 bytes norch/autograd/functions.py | 12 +++++-- norch/csrc/cpu.cpp | 25 ++++--------- norch/csrc/cpu.h | 2 +- norch/csrc/tensor.cpp | 34 +++++++++++++----- norch/csrc/tensor.h | 2 +- norch/libtensor.so | Bin 172912 -> 172864 bytes .../modules/__pycache__/linear.cpython-38.pyc | Bin 1036 -> 1040 bytes norch/tensor.py | 22 ++++++++---- test.py | 24 +++++++++++++ tests/test_operations.py | 18 ++++++++++ 15 files changed, 119 insertions(+), 53 deletions(-) create mode 100644 test.py diff --git a/build/cpu.o b/build/cpu.o index 0b75e023751f2236eacd5535a5a69885d1d11141..adc39e5238ee388ad81ef0ef6924b00d82d01d9e 100644 GIT binary patch delta 1324 zcmZWnQAkr!7(VCD%&B$n)NN>F=``mwu!3|*BISV#CFp}B5+y~3kfxy_#M;Bf=ynsz z15rfaLr@Qf+CT;wY!9&&Qd;y979)I^C}=szKwSUXIWxTd@N@t3{onun=X~4R*;w0s zx71lN&c8fl7Kow zRs^JzHMRg&YH>*?3C)*4LxToMsE^bUf59-L0bvr}Vq_U)?eXtF`aHi<3!D@6BgJ2{|`KXZ%y!`eZ{b+XuZsm_!sO)rG%DWIf?mTJ%Qs9d7h$Nqspd@>h+tR5$ znHF#0d7(GMngRizrv3Y!{x?9$Lm1u60|?>_|Dc$_BLY_io&oBNFXwaMg1|ij?-lq{ zfwu@er+@>gKMRyMIPO8KqH#_PJQM?`?HuSAcz}kBorOWsoiTO41b!ArrJR=T?u)4J zdUx+YS0GYSf-_+F1H~@qE7v5(pXpSY)BjE3imX=(xd}fCi_7Ar%(GpPE=U&F1a9U} zoA_={N6*C77~cb#*0(!SgiOR5)q$7gPq2U6b+#0OeORVlziPtMuHQ4^Y1hY1c-r+@ z6D~}zLMx_5+I0ndX2IGlXYBM>#dmy|&bw=^b)~F@P#?8b)=E7NI#uMLot5r%IacYm z_VL%E8afZ0F$c}|G)S|8>TyeR4tf`mH3xkUpBH5`yQ;x@u!sxS(blS3YkM(2eWefJ N6hFjI1GKgBz+Y#@V0Zuk delta 1535 zcmZva@k>)t6vyv2>&3KfkJkD|wG zAl+Yz=sy^^8pJ9HSHujYR_q6fRig|h4jL#_60URi?z1^MaNoW6bI$pmd+&K^U#@pl zm4?(u?h}eTsu?3;!xz&1q5R0v0r|)HoLmi)aYzs}&4j4g-R6OWzm2_Ewf@MQtQ?q} zlaIs-Q92~(>;wjdkpuE-GILfU1jWdeN-J6QKX+Tnk6rH_mLULJp_(eg- zh!<`YhqRF8XUF=>&hCM<98~afahT>|CQNriQV!1JIt~Q^L`DFz;!=7>G9ah>bL7g1 z3Ats3@!Scs!i4PopTCXw*6F>t&*2OBS6k7sv@~0ZOMRSTF766XD38S`Wd5ty&h-L6 z7Y)ejgE=~T6VJ1(Y8Vx;&lOfZeA2W$2J=SDgbbZns(}&wa{tLJ{4_*|X*xsl`MeHk zI+Iw;=ij`Vlj|3Jn(`jmeI;}}>&DA~9c9ccTliSSs?PYbRIzN*aPVZr+Z-!8bk&H@hw zKO}fa@Io)qch%x0x9nh(E8=YmQ~H|GW>?^yV#%z)sR_p)d}eOh zBEF`GFADBPR#n7nt8s(IU%ecMty3pB*O!ZUv&9DpcW_NR;Y(4%<|5uJp4%?aj6QB4 z-hu0E!(r<@DdO8B51Sv*z*2{~1{$CEuseLNgxejyQ^M^Ihf28J;mHy%8oZ5UOB{BG z73?WD?&jmTlYQO#8JiVr>2PG4xQmbkqiTm#PUclrdpWAA4#j1~w6Pe*>~^sQ zwM#lJLR(e7yw=(!-FC5OI7PRxh1M=dw9ZQMvL1JbqoTpuePTCo_p8O)4YMA#;V<+^ Bv|#`M diff --git a/build/tensor.o b/build/tensor.o index b8560d82b24a628ce8cc4ff6e0ead48af48ba712..0071d08d1c19ea6783ac31468df999592c89e296 100644 GIT binary patch literal 43312 zcmeI53wTu3wa3o{l!!=zS|1g4v{ex?CgEj7F@S@kfYM58MQs?8iA3{iW};|~cpF8> zF+Q)gUbS+$wc3_es;IG|2E+!HYFepMMVl(N&|t+%Z>iU-cddQan)#nS=MX^c?IZj9 z!p!{kKL5S;+H0N1J~M{}k?B+OLLo;>$eHM5KPh#brT1pvmz%fc&QNDB=lz01KB$PM z-j23yE{~>K*F{q;-KmN-(bSC2r;ev~8@X*X71mnpsT! zb~?#p-0jPYNshL7In$72yUIs#k6n??Y(no2nfNgk(`!1ic|v4OD{U0nmYz9^K1RPC zZ69(qOA+gihE}JYf(N>}J~eYxTV!*n_xW^W4YgX)lb10gV{g6~njgKob0@vk71_e$ zAn~@1p;TmRTkBS5{>iGBuGku_`ek&*n|aaD%5-E)DlZ+`%C_zcwsxnTbj6l}2Ueyc zTiWWkhI$|8&7XZ5X1Q2rxjLF2c*F=ECe3b1C-u%_?uu;X7usxHx_m@gWNT}gZ$CVV z>!RuM{PYa6>)Ggv-;!O%RVzc(cSQQ;CdJYAu|`uS+I0+X&Bp4W+Q$?|+asGF%cG`0 z9mpm~^WKBquCQ^a`~8AKirrz6&Gd%mj7C`<9qo=Mxtp0;&!`J&)Y~Yww@}M1x%M2* z0{*5BHC1ZMME~uN3f_JQZhw=x`x;g=jM~xNP2B-I+X|MWgAcRCBW(mK8a_#EtY&#`ZW#4qR$xdhhXO z>}ncSDzY{mS!-ueWFu|7(P=qvC$kt#?|ZM*73pSYQF2d+tSxANhTe;Z3ey$c8jqpw zbfN2^^1}2C!r2(7896Ug<=G-XP)J>yZt0$sf(Q(lH+HaPjoVTCklWaJ+6``uhMr41 zsl2vGcWBdbv_F%+ebQ|vSEb8G!S14+@-G_>W+$DVLE%7imU=-`k3LeC≥3tc|8y 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 AyZ`_I diff --git a/examples/train.ipynb b/examples/train.ipynb index 3a37c54..9c92203 100644 --- a/examples/train.ipynb +++ b/examples/train.ipynb @@ -26,23 +26,28 @@ }, { "cell_type": "code", - "execution_count": 2, + "execution_count": 7, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "Epoch [1/10], Loss: 1.7035\n", - "Epoch [2/10], Loss: 0.7193\n", - "Epoch [3/10], Loss: 0.3068\n", - "Epoch [4/10], Loss: 0.1742\n", - "Epoch [5/10], Loss: 0.1342\n", - "Epoch [6/10], Loss: 0.1232\n", - "Epoch [7/10], Loss: 0.1220\n", - "Epoch [8/10], Loss: 0.1241\n", - "Epoch [9/10], Loss: 0.1270\n", - "Epoch [10/10], Loss: 0.1297\n" + "Invalid axis" + ] + }, + { + "ename": "ValueError", + "evalue": "Matrix multiplication requires 2D tensors", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mValueError\u001b[0m Traceback (most recent call last)", + "Cell \u001b[0;32mIn[7], line 56\u001b[0m\n\u001b[1;32m 53\u001b[0m loss \u001b[38;5;241m=\u001b[39m criterion(outputs, target)\n\u001b[1;32m 55\u001b[0m optimizer\u001b[38;5;241m.\u001b[39mzero_grad()\n\u001b[0;32m---> 56\u001b[0m \u001b[43mloss\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mbackward\u001b[49m\u001b[43m(\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 57\u001b[0m optimizer\u001b[38;5;241m.\u001b[39mstep()\n\u001b[1;32m 59\u001b[0m \u001b[38;5;28mprint\u001b[39m(\u001b[38;5;124mf\u001b[39m\u001b[38;5;124m'\u001b[39m\u001b[38;5;124mEpoch [\u001b[39m\u001b[38;5;132;01m{\u001b[39;00mepoch\u001b[38;5;250m \u001b[39m\u001b[38;5;241m+\u001b[39m\u001b[38;5;250m \u001b[39m\u001b[38;5;241m1\u001b[39m\u001b[38;5;132;01m}\u001b[39;00m\u001b[38;5;124m/\u001b[39m\u001b[38;5;132;01m{\u001b[39;00mepochs\u001b[38;5;132;01m}\u001b[39;00m\u001b[38;5;124m], Loss: \u001b[39m\u001b[38;5;132;01m{\u001b[39;00mloss[\u001b[38;5;241m0\u001b[39m]\u001b[38;5;132;01m:\u001b[39;00m\u001b[38;5;124m.4f\u001b[39m\u001b[38;5;132;01m}\u001b[39;00m\u001b[38;5;124m'\u001b[39m)\n", + "File \u001b[0;32m~/Documentos/recreate_pytorch/PyNorch/norch/tensor.py:167\u001b[0m, in \u001b[0;36mTensor.backward\u001b[0;34m(self, gradient)\u001b[0m\n\u001b[1;32m 165\u001b[0m \u001b[38;5;66;03m# Propagate gradients to inputs if not a leaf tensor\u001b[39;00m\n\u001b[1;32m 166\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m tensor\u001b[38;5;241m.\u001b[39mgrad_fn \u001b[38;5;129;01mis\u001b[39;00m \u001b[38;5;129;01mnot\u001b[39;00m \u001b[38;5;28;01mNone\u001b[39;00m:\n\u001b[0;32m--> 167\u001b[0m grads \u001b[38;5;241m=\u001b[39m \u001b[43mtensor\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mgrad_fn\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mbackward\u001b[49m\u001b[43m(\u001b[49m\u001b[43mgrad\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 168\u001b[0m \u001b[38;5;28;01mfor\u001b[39;00m tensor, grad \u001b[38;5;129;01min\u001b[39;00m \u001b[38;5;28mzip\u001b[39m(tensor\u001b[38;5;241m.\u001b[39mgrad_fn\u001b[38;5;241m.\u001b[39minput, grads):\n\u001b[1;32m 169\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;28misinstance\u001b[39m(tensor, Tensor) \u001b[38;5;129;01mand\u001b[39;00m tensor \u001b[38;5;129;01mnot\u001b[39;00m \u001b[38;5;129;01min\u001b[39;00m visited:\n", + "File \u001b[0;32m~/Documentos/recreate_pytorch/PyNorch/norch/autograd/functions.py:90\u001b[0m, in \u001b[0;36mMatmulBackward.backward\u001b[0;34m(self, gradient)\u001b[0m\n\u001b[1;32m 88\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m [aux_sum, x\u001b[38;5;241m.\u001b[39mtranspose(\u001b[38;5;241m-\u001b[39m\u001b[38;5;241m1\u001b[39m,\u001b[38;5;241m-\u001b[39m\u001b[38;5;241m2\u001b[39m) \u001b[38;5;241m@\u001b[39m gradient]\n\u001b[1;32m 89\u001b[0m \u001b[38;5;28;01melse\u001b[39;00m:\n\u001b[0;32m---> 90\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m [\u001b[43mgradient\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m@\u001b[39;49m\u001b[43m \u001b[49m\u001b[43my\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mtranspose\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;241;43m-\u001b[39;49m\u001b[38;5;241;43m1\u001b[39;49m\u001b[43m,\u001b[49m\u001b[38;5;241;43m-\u001b[39;49m\u001b[38;5;241;43m2\u001b[39;49m\u001b[43m)\u001b[49m, x\u001b[38;5;241m.\u001b[39mtranspose(\u001b[38;5;241m-\u001b[39m\u001b[38;5;241m1\u001b[39m,\u001b[38;5;241m-\u001b[39m\u001b[38;5;241m2\u001b[39m) \u001b[38;5;241m@\u001b[39m gradient]\n", + "File \u001b[0;32m~/Documentos/recreate_pytorch/PyNorch/norch/tensor.py:487\u001b[0m, in \u001b[0;36mTensor.__matmul__\u001b[0;34m(self, other)\u001b[0m\n\u001b[1;32m 484\u001b[0m \u001b[38;5;28;01melse\u001b[39;00m:\n\u001b[1;32m 485\u001b[0m \u001b[38;5;66;03m#2D matmul\u001b[39;00m\n\u001b[1;32m 486\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mndim \u001b[38;5;241m!=\u001b[39m \u001b[38;5;241m2\u001b[39m \u001b[38;5;129;01mor\u001b[39;00m other\u001b[38;5;241m.\u001b[39mndim \u001b[38;5;241m!=\u001b[39m \u001b[38;5;241m2\u001b[39m:\n\u001b[0;32m--> 487\u001b[0m \u001b[38;5;28;01mraise\u001b[39;00m \u001b[38;5;167;01mValueError\u001b[39;00m(\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mMatrix multiplication requires 2D tensors\u001b[39m\u001b[38;5;124m\"\u001b[39m)\n\u001b[1;32m 489\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mshape[\u001b[38;5;241m1\u001b[39m] \u001b[38;5;241m!=\u001b[39m other\u001b[38;5;241m.\u001b[39mshape[\u001b[38;5;241m0\u001b[39m]:\n\u001b[1;32m 490\u001b[0m \u001b[38;5;28;01mraise\u001b[39;00m \u001b[38;5;167;01mValueError\u001b[39;00m(\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mIncompatible shapes for matrix multiplication\u001b[39m\u001b[38;5;124m\"\u001b[39m)\n", + "\u001b[0;31mValueError\u001b[0m: Matrix multiplication requires 2D tensors" ] } ], @@ -87,11 +92,13 @@ "for x in x_values:\n", " y_true.append(math.pow(math.sin(x), 2))\n", "\n", + "batch_size = 5\n", + "\n", "\n", "for epoch in range(epochs):\n", " for x, target in zip(x_values, y_true):\n", - " x = norch.Tensor([[x]]).T\n", - " target = norch.Tensor([[target]]).T\n", + " x = norch.Tensor([[x] for _ in range(batch_size)]).T\n", + " target = norch.Tensor([[target] for _ in range(batch_size)]).T\n", "\n", " x = x.to(device)\n", " target = target.to(device)\n", diff --git a/norch/__pycache__/tensor.cpython-38.pyc b/norch/__pycache__/tensor.cpython-38.pyc index 72d3bebe7c6e0b2310c3fc5635d02abacb3002da..92de5802f744c677d973b17b28e9a0a9761ffd8d 100644 GIT binary patch delta 1207 zcmaKr-)|IE6vywmJ3G7SbbqkhW?LKD4c!uUyF%CP?-rBtV@=}^G@>F>7na%9PD{5P z2WWM${0xc`Nlx@Z(J0{sVhGI;HN@xx@Gls%G5X|_ycrUG()*o-NHB4e`Ruvpe9w=$ zckbHq)s^T=kw}wJzgxGTn%-Jli~ej--TU2W3s9Z4SRak^Kk@yL8VuDnbM{c7&VQv2(@w6neWO-hd;ma7D9wU|Zfq$Hy}BI_xrV0Psig(Sjv9 zj1#BZHgR|7M!nI5cXeK(S^uSiT}S?}^#WQOHB?Fi^M2Bw>5?@5owt<>gX?*^dk>ZP zhwj~m1!WO<0&*^7&PNWbWmeuF-1q*HdBJ!Zq9cI6WBhE-^Y=G^84D))G{Uqdy!p>ntp;pk9yl}wtG zw(ClJ-W8VNYGuQeGX%Rus8#dQPySk2rbKjMpVD4c@n%!FbVjxd#8UzumrR)wrhr#@ zRL;VNhn4@;tU}~OT+M1eMJTT%QS~47o5kSYA*K??g07|sE9M4AgcRl2(p{yCE1i0* z_yw}l&N^3(<4QAdKA`*wS9?)pwdZXAsHEDMEY(AE)#{@9ma|Daiv5eJ1DY%v;l297 za-~+A=iSAPdKi%qA1~(C>HfLbwFAquZ`x;+UmfSB!e(U%dW_;SA$8Jam^2DgJP@X^ z)F>x6P_y#s)T9*3M{zIM-%IqFR~UGP=sFh$dc7-y*NE=$p5fnUnLjLa@zh9_e&t&u z`#ZOz{{-OQ1|R;F*r9xX@Wg0GNcwM8;>zfdy4atL4z3PCKMafm+W_e8Nx;9`Q?TZM zL%<@i1mF|5j{+;eTfi}36*vx@1Wo~`fwRE7zNvl7Z1#`Eo#f9JRl=KAyG9k(TMAU>V#_T$%!3I#d@$uh4((BT zhJUlKq$X9NcM`CHr}@LX9pgEKje^ef2htcrKEYqK4L4PEXMS<8!hf}`&{00qe%&~Y zi&J5~<0DERMO+1jfF@vq-_7iyX}*@px6VMwC~WIqAL3egr1O1gokILP@FH)O?5O7$ zai;w~?%BKFXtd=Md#{iaZ6nt8>i;b^kHst({W8E@RP<-&VQKw4VVd*i9-i%amL~YC zo|9G$&V#@K*m-U-buEyAHl-AY7Nl2k4<@nimBw;OUl=xeCqGEc;EP`K&`NWC3gkd zq{wQId(nfyrnA(_-J@On^}$Xqm@y-+G;cRk$k?B~#KML&iz{eIoE%Q5rUHtu_8vVfK(rM!e);f$AJT|VvoBVmH)1YV<{8MQ} zeV2B5czqbI5ugk_1i|1V5k+O3{sonii{6TP4-TB8q|`6HAhQ^i4!56RNao z7gebb{D5GyP%OAmsvAWYDuPQFf@C3xD-l6lDya93ZLkCP&g8k3?G}M>SJLgu z6Id?IxJOpiCCFKLLe{_LXXbhIYz={c4jPP^SVz>usd4#whNhs|(+E?fOe~0!GSn!Z zsYOwSEmZ}DU}d$e$-44f`K{-V?jq5{7-4V9w#eEnHN7fubexGW~UGai9?Qsj^dJP z)N$vlD4aQThMr~Wvtgtxhqygb_9Dca0hLnrLOXxX3Kk#Log?PM+V^mVn{gAHNkzzz5gFy z;rp8WAJOCRNaSKS;Ia4`>kej-_iza}+vHBh55qI@F+L41L?w|4_95@-5^i6U`zvu6 zUWtKZ84loU!th3XNM?fl$bPvbqhGY8GBGIvm(SvIRxXF&vO4b^T4c delta 754 zcmY*V&ubG=5Z>AMHrb@jTDnQL$*w6>EDbFM4}yxpHf^KD5R_7+^#`ObElpbsFBPS1 zP-%NmdQb=ah1jHmPz6z9!Gnd~#EXAGPJ$;7J?KF^Sa4po!R|7%voqg(GxL2t@hN2t zheAPxp6$hx<98!(jSY^!_4AQ6o)bXnHK_6Y^~vHl`GYxa$Zx3g9lfrLl|Xy-vv!-Q zB32y_9A@BDpEhm*#H)9Vd;Fl!Rb93Sqs-;5wy2D97lxFizt%8xbtav^JasKE+#Kdv z9LIufrm%@V$%LOiLYL0Qvgw34g$n2}AEq|EWhZWElwf3iWW{N6o>EX;h$#gqsT8ZO zI;DyubIgMRFK7jytS!JUs_d^QVWq^bbkFIYx&S5aGLOr?FxG-W6L>-fuA zhWEI%KV=S5<=q~J!TQ8VVgxpDHc^1nxMf?gi9Zr4GfU_Xdpfe1ZcWAI^+FyX{{ik%uqprm diff --git a/norch/autograd/functions.py b/norch/autograd/functions.py index 30e452f..fe31be9 100644 --- a/norch/autograd/functions.py +++ b/norch/autograd/functions.py @@ -26,7 +26,7 @@ class AddBroadcastedBackward: # Sum along axes where the target shape dimension is 1 for i in range(len(shape)): if shape[i] == 1: - gradient = gradient.sum(axis=i) + gradient = gradient.sum(axis=i, keepdim=True) return gradient @@ -126,9 +126,10 @@ class LogBackward: return [grad_input] class SumBackward: - def __init__(self, x, axis=None): + def __init__(self, x, axis=None, keepdim=False): self.input = [x] self.axis = axis + self.keepdim = keepdim def backward(self, gradient): input_shape = self.input[0].shape @@ -136,13 +137,18 @@ class SumBackward: # If axis is None, sum reduces the tensor to a scalar. grad_output = float(gradient.tensor.contents.data[0]) * self.input[0].ones_like() else: + if not self.keepdim: + # Remove dimensions of size 1 from the gradient tensor. + input_shape = [s for i, s in enumerate(input_shape) if i != self.axis] + # Broadcast the gradient to the input shape along the specified axis. grad_output_shape = list(input_shape) - grad_output_shape[self.axis] = 1 + grad_output_shape.insert(self.axis, 1) grad_output = gradient.reshape(grad_output_shape) grad_output = grad_output + self.input[0].zeros_like() return [grad_output] + class ReshapeBackward: def __init__(self, x): self.input = [x] diff --git a/norch/csrc/cpu.cpp b/norch/csrc/cpu.cpp index 3b4e303..ffeadbc 100644 --- a/norch/csrc/cpu.cpp +++ b/norch/csrc/cpu.cpp @@ -205,7 +205,7 @@ void log_tensor_cpu(Tensor* tensor, float* result_data) { } } -void sum_tensor_cpu(Tensor* tensor, float* result_data, int axis) { +void sum_tensor_cpu(Tensor* tensor, float* result_data, int size, int* result_shape, int axis, bool keepdim) { if (axis == -1) { // Sum over all elements float sum = 0.0; @@ -219,35 +219,22 @@ void sum_tensor_cpu(Tensor* tensor, float* result_data, int axis) { return; } - int* result_shape = (int*)malloc((tensor->ndim - 1) * sizeof(int)); - if (result_shape == NULL) { - fprintf(stderr, "Memory allocation failed\n"); - exit(1); - } - int result_size = 1; int axis_stride = tensor->strides[axis]; - int idx = 0; - for (int i = 0; i < tensor->ndim; i++) { - if (i != axis) { - result_shape[idx++] = tensor->shape[i]; - result_size *= tensor->shape[i]; - } - } - - memset(result_data, 0, result_size * sizeof(float)); - for (int i = 0; i < tensor->shape[axis]; i++) { - for (int j = 0; j < result_size; j++) { + for (int j = 0; j < size; j++) { int index = 0; int remainder = j; for (int k = tensor->ndim - 2; k >= 0; k--) { - index += (remainder % result_shape[k]) * tensor->strides[k < axis ? k : k + 1]; + index += (remainder % result_shape[k]) * tensor->strides[k < axis ? k : k + 1]; remainder /= result_shape[k]; } result_data[j] += tensor->data[index + i * axis_stride]; } } + for (int j = 0; j < size; j++) { + printf("%f", result_data[j]); + } } } diff --git a/norch/csrc/cpu.h b/norch/csrc/cpu.h index be875ed..5fe358c 100644 --- a/norch/csrc/cpu.h +++ b/norch/csrc/cpu.h @@ -5,7 +5,7 @@ void add_tensor_cpu(Tensor* tensor1, Tensor* tensor2, float* result_data); void add_broadcasted_tensor_cpu(Tensor* tensor1, Tensor* tensor2, float* result_data, int* broadcasted_shape, int broadcasted_size); -void sum_tensor_cpu(Tensor* tensor, float* result_data, int axis); +void sum_tensor_cpu(Tensor* tensor, float* result_data, int size, int* shape, int axis, bool keepdim); void sub_tensor_cpu(Tensor* tensor1, Tensor* tensor2, float* result_data); void sub_broadcasted_tensor_cpu(Tensor* tensor1, Tensor* tensor2, float* result_data, int* broadcasted_shape, int broadcasted_size); void elementwise_mul_tensor_cpu(Tensor* tensor1, Tensor* tensor2, float* result_data); diff --git a/norch/csrc/tensor.cpp b/norch/csrc/tensor.cpp index 824737d..c945689 100644 --- a/norch/csrc/tensor.cpp +++ b/norch/csrc/tensor.cpp @@ -172,7 +172,7 @@ extern "C" { } } - Tensor* sum_tensor(Tensor* tensor, int axis) { + Tensor* sum_tensor(Tensor* tensor, int axis, bool keepdim) { char* device = (char*)malloc(strlen(tensor->device) + 1); if (device != NULL) { @@ -192,29 +192,45 @@ extern "C" { shape[0] = 1; ndim = 1; } else { - shape = (int*) malloc((tensor->ndim - 1) * sizeof(int)); - for (int i = 0, j = 0; i < tensor->ndim; ++i) { - if (i != axis) { - shape[j++] = tensor->shape[i]; + + if (keepdim) { + shape = (int*) malloc((tensor->ndim) * sizeof(int)); + for (int i = 0; i < tensor->ndim; i++) { + shape[i] = tensor->shape[i]; } + shape[axis] = 1; + ndim = tensor->ndim; + + } else { + shape = (int*) malloc((tensor->ndim - 1) * sizeof(int)); + for (int i = 0, j = 0; i < tensor->ndim; ++i) { + if (i != axis) { + shape[j++] = tensor->shape[i]; + } + } + ndim = tensor->ndim - 1; } - ndim = tensor->ndim - 1; + } + + int size = 1; + for (int i = 0; i < ndim; i++) { + size *= shape[i]; } if (strcmp(tensor->device, "cuda") == 0) { float* result_data; - cudaMalloc((void**)&result_data, tensor->size * sizeof(float)); + cudaMalloc((void**)&result_data, size * sizeof(float)); sum_tensor_cuda(tensor, result_data); return create_tensor(result_data, shape, ndim, device); } else { - float* result_data = (float*)malloc(1 * sizeof(float)); + float* result_data = (float*)malloc(size * sizeof(float)); if (result_data == NULL) { fprintf(stderr, "Memory allocation failed\n"); exit(1); } - sum_tensor_cpu(tensor, result_data, axis); + sum_tensor_cpu(tensor, result_data, size, shape, axis, keepdim); return create_tensor(result_data, shape, ndim, device); } } diff --git a/norch/csrc/tensor.h b/norch/csrc/tensor.h index 5ff62d0..a32e74c 100644 --- a/norch/csrc/tensor.h +++ b/norch/csrc/tensor.h @@ -16,7 +16,7 @@ extern "C" { Tensor* create_tensor(float* data, int* shape, int ndim, char* device); float get_item(Tensor* tensor, int* indices); Tensor* add_tensor(Tensor* tensor1, Tensor* tensor2); - Tensor* sum_tensor(Tensor* tensor, int axis); + Tensor* sum_tensor(Tensor* tensor, int axis, bool keepdims); Tensor* sub_tensor(Tensor* tensor1, Tensor* tensor2); Tensor* elementwise_mul_tensor(Tensor* tensor1, Tensor* tensor2); Tensor* scalar_mul_tensor(Tensor* tensor, float scalar); diff --git a/norch/libtensor.so b/norch/libtensor.so index a125f3cf42d7df3c5d394600b7016cafea233f0f..09af449b259d5e11ee27f21d0f339c1a2e53fcef 100755 GIT binary patch delta 24561 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 Q=KGb}L>Z&C{z|R=e-l?R3jhEB delta 25007 zcmZ`>31AG@|DPE`5(x=nv(6;qK9UsC78TVc)R7?WBJP{GuZV~%A|xKhU9@_jMN!9( zl2Sn(ao@yMOB_+O^vM1{-}m0E&7}Wn*LUCdzUIxFne01s)HC;}XPUonR6B(q5dtW= z=`|X9Y-=b++tP=Gc!cc|gRu1VgFHqT5reKfeAP^~!m>8$pI)ea>d5(7olAc;wC~k> zK@FD?Lo7a3*=wzB#bQc8SRr2}V1}kCN)cP(WKUI%RD6P!nTi%sg!L&D(5xz$Yv5xX zHH|NTwebm)x!!|-J|J|poNg-;wlWAGV=&jftvn266L{!LLPD@s{qDh3G>F#|ABexJ$K6t;NN zU>-j6=?lv!T#9?2hQVS&;IjmurTDlWluR9;6%x6Usc!8HPyF*`E7sMmtNUrMjZ?1v zw(iZ2oo?e)^*+mRx3Gw!k!oGGzNkh0kpKH7|F;4EH=LaXemn6kb`Mo6u`u@#^&=MR z-dkNsNcA&fW4Z4BYA_er#s7WE{~gAnJuK=m{=E`Q^axRJ@$UoqzsI?J4f@V}JR|j} zk(zr##9iU{htmCSo{CaZDXx5eRv?TD&PHz#K5K`-mn$sbhN1#L^eZur6hQev>Un)e zAVMX+l$+pxOU0sU3@#)n)d9&s?n*J9CV+BQ8Ypp0ApBJUg8_*@d9xS?NPGhyfnR@1 z;Ll6^QZIq;|5)IuO%xu{Ynb_L6%s|lHd_7sDB8c1j&4imrdM7 zhH+8G7jZ~121#v^GK`8cPi>^0nI6K5<)lCu`R-aw;A3xae1c*Sw#z(?8zcm3!Vx;2 zO9Rio5o0cZ3XbI`i2k8QToa1rUyeuIEmI;rP3@I>G zR{H39A<$6(#a%}5=88ZV9Tq7as`gOe4Ts{S{`am5{8Xv`5{4xcP@u=(3WDq?<-EIa z==-k){=NW81Bve~`Hf8MGD=3LD~_qMWU8;w6CxG#mxgype2!ht8j~eq3m|pObe3Py zQCWiKa|PZaOY@D)#JFsMj}|})lM%;X76>EamqmpB#j=z=jajRdG~kyc1PlYeNr#SX z6Zj|6(Q2|8D~%HPYck@(G82uy5cqr2(E(CVPK>};ll%h- zjb@xD@w0vw{KiT9p2YXf$ggLl?1}#&)pQs=@dyr(-E#@61YxKE$`7*X*hPUb&W>jo zlEB_g0-q)Mzmq-TD|xw4zd~{kc8qIrBP73J<1U{jiOlh<{Z7!S@gx@884lW;L zxQ#rI4g7f7L|q;Vextb@GUCe51>T4_KpJlUKY^E-PEcM*!^dRAMzbhV(d&Ccz&OoX zNJWiT3;aL9Y1lY0k+^{CQL`QuC61<#$<2{jnez0t;l94wp?j zP>>Zb8CmyafiOl zRv#94$Sq6 zZ)E`YWsP6P3cQg~i!50+_M(&}p>lgcrHP%6+Hx&Y>xmc}c{(huRG1?0MqUrg@aD}B z_*8jLEtmW+KNt8LQhymaxl|GJWrAYlHCxV)&E+M^2s>UDdgl?L$S8E8k0{(P*#d9O zQgvk5+kOyuqw_K(|CGC~{FsF+Nl#-Z2*Q5Z!oy^NjtvudBY$0G#AntEe7pe4ZCRg1 zJG%LWYCBRb-bIUy7%*b&fN?ukRJ3_|Dw( zsJy$<95^~u{O9IEKfiStJT0nx>ZEH*X-N+S|J?Lt@T`c^S>nc*9fFoDti(F`Rpm9R zYuF3A{xTeAVsF(7$GW-uHr{crq;4V zA8IE?kz?~yjd33nKD(&skHIGVayg#FhSX~0&7=(i5$sMa3p-XTSgppM*0QJ#nOp4; z>-#Poqeltb$ci$LwXGeZc46_gE!Jcgo-`Mpn(VvU7PUY71$yKah;wdp;VH$u>sYLE z_T|(=F4Uf^38fnmQKTY3-^LVvTj-iIN$h{FG(?*0FeZ zcA*BaJ9RDW{kn~<#zl^Z5u-)qICYfCEVXWkI-4D+Yq1V-k?iBbGmSm1Yf;~0ZXqG+ zeAXZ~oAoIlNlGo8hUSkwV*DR{zNv~6?YX~1qmPYm-8wW!NjmCz7tZ5PgL7tSSY zM5x6|UK&TP3w0b@6KY|rL%qC}xOeePXQv>u*bo5paiR8O|3apAn3s2e3$`Vz6K3(o z+2A}v+4e9C8yx0ko$JIQZ!R)jSt?`(x=^Fo9mpJmOkEdj9~YUG>}gmCYxABLtNxys zwTBDW3>U7MtnGUiD^4xvk>*0}$l^gY)efOS)ZF$ejdz%Y3gxv)82C#=* zu+5owJqz=y=jEN{f-S+C)U&9gSWLYTE3S#oBiDteI$HzD)sR%iyvuw^b_&>&!1}mg z!`Q#T{!`D31=R0kwK(-5n=V}KS!#Xw67J<4<$_(v4%D|;6J2EbxKNYW)A|;50ds2* z;w{%HoO1??YhYnr8+cjAxJahCNDgK34J@jaEp5s^EYG3v*sK&L9 zm;zj=eOR4_7Hb<9*-#hiSoRsHa=pZPB3!6db|g0J;r>I!tRh~62Y~wfqx829M;cq# z;vv&p2C$-$q0AELgHOPYqLDRgnc_Au5Z`AX8t382E>UR)d39Jq@Si2d)CMOA)X0m>$SV|?oEw>8 zZyKo{WeM%v)h29SyNWdqfUbRWwzA*bkI}00ks8aU zMLk1v4U1l+{qr6BC)%q0_8qfysH|qNMjgtk>sjXx{fnI|58Wx`a|HXPLz`k#o#QZ8 zzGGGO1Z&>0r}nfQ8St;VMN#are>ofn;hn?&^`8z05^{(WwYQtugN}231IlrQDdV%6 zA@bA)3XAX56FqvZQ!TC9OP1BkkCl(HXx-kh=uv*GbxcVuv@8#32pbpESiQlv$BZgA z+-i6g#AKQ`BTF$(E(&{P50jvhAeQ6N-u^ZKQ_Hw8y?+|IQ3* zo%)nKkGaA*M4pC^XXEoaUatG$EhXaS zEw2rHjJcs^57>Q_gdRvihda-%n8)U9)GN-t8IBuJ``1qApFU#ipz8TU_ET4@*wpH}?^5zVc4?b9lh!Ox?!C)J_-{NH!4G&Y-aaUpi;EsS7sXe?S}O_w z7dMA}5pkxteZz^qcUFK{-Wgrr2kh*eD9@F^Qgo9Zu)t|S<-fc`B5kjv#2n$3Nj^QW z#K@nCq3nGMxYl_@nv+1Zl!)5z8t&wM?v%^vx5=Gcs&#KUtX6zz4MQSsI?XDC~y{@7fU=j1+alkAnCRpev< zai+LU=S1J#MEo6T5*2^{9(xfVZOUynYP!FcmCY;u?R~axx?kxH zcezEEfM*op72k{JjrLavQ>gs#E_*J7X}+lee1Yit-{BSSb^Bd(ckl3u!z6Y8MvB`M zU7{P0ZXa$b?XMA&h^`SYpIawR^i3t=F(UH0xA+}i@fN^RbisFc#iMRgLjJ4bNAFU= zH4_l&e^h)@wmAWvS>UiTwHD#EsQmkte#vEk4aWQS(nTOI#gl4+VmI z<4r!zJW3a3$-K#@nU`1jG_&9;Z@MbC`7~3JYL#!A`A6YR*8sxy(asouyuqiLrq_9f zzk#U&8NPR&-5WLGlX438v03t4vDQ6|NSsNVj?@SrFI?wITPIhO--zV`Us2Y$!Pbnn zHvP$=%2oKdG#3@soGRjl3Mrat_rCfr@9Eds>(K#P!Zl`zt5PPCII%Po3r;cF2RqpM zseYEv79q~%$bzWCt!w0#+n+2tu8eOe(S{L?7h(G~wlOZJ^eZysJa_22eC}UTRlOLV zhTB^>-CIC>DK_C=YJ;Mn(n<2*D!VhLqULvrsbl@LcGuX934YaD!oi$LDYhf;=D$yb z8JFoGNSNt?drtc@2nerkTp=?txi}?|OqfgNt3ki3EPbq>BB>KM7^^O`MUw)wqNT-_*C;GM`;w&QaN}agGLMK;dTgUe)J)ZdTRjcD)yjlz9LnPDbqc_(l zeN1gI4^*nwz)P&pgo@g-bG%Hq;hw2XHz<~TC(ypPT>ncvaJ1KT>K{jZqDd|SHZDoF_snllP#PSs69Bx3wVGSO$A)>CokYR z+#lL0&7y!Exk|U;oanoeh}q6?s-8yyXO+%6$F+&OPky8L{U1EJ59feA$jLsK_zd?_ z8{7kxikESYPBs4*r-_Pty_B}Dsc+2^CX}gG88Mnl8X&B?oOyY#xz&bbb&NA9dngG7 zOZwt8FX<-4hmz*M5qOZrFU+^eBbmN_XZaN(I*VVJtCCJ$*Jm6kmxc2>d^;%Wuzc zpIq{B`6cC}mJjsXKZTI6nofNGd0}pNnqM|%Ah-f4`QK^265B=2$&`F>hOfk`o#rdC ze<)d|lwA3px0Ek#Q|xV^RitDwFSOfcPV~J$6U0hHj7sX3w_)6@|DRaQzy&!wOza>ItAQCTB4m!nGVogr+=sF|(0@0rj8${hc!gGD7H_4 zknm~0C_yeaEiyOxDDRmc5K4i}JXu zBxn}tI-W^kx!uhnqHj4Ob|xY}D^_Rnp1CkpWa5WR-ZQrjlL*g*QR>a2XG$OEJ>yL} z^F^NXm`5HAMfTpvi%`GuC|5+08;VLE;gPR`{{=Rg7p|^7IClQ<#XG~iM~%Kf>@o1JnoT)dE8$C zOT9nqFt7MFNSZ1>B7@Hoza8O$-~QPg_yLwNuT1bCczgDz@#?v);Y8m{<3aQ#A`iSv z1`m7@uoO7Lqv>(Q0q(GC!HBc8 zg#kG!5PAAREbdCPRtw7YP9qm&&PsYO8YH&m(Y6Mr}Hir{^4-)Y&4^wUznNY)Ed1NZ*V-xagLegzm-W{<>#uL!`ux`8qc}{IGhV(md*!O_l z)nWOVJOfmhBGKP9;5qp}`+1daEetIF8V(s%ntXtHFAC5G?Pnp2tbUD2qiN0S&t9aK z@sGv}UHbxP#{V#VZaW&x!au0!m`ceioNjvD555BtmOf zXV!n`T2Obsl*4m&@gm`W@ZwPobb7wWw!5fA&X%AovD;+5(aBM;UKBMES#K+D$V_;8Z*vH?44w@1--k6!heOoY!|B3tk*#{fM%dY^j_RZEjgcu%b(x6Qx>M|VO!V}?a6MIzPyxnc{kg-#G-wFo#*c| zMGypiB7(uYcRX8iNGm>JC%Fv^5Ffge?bpw%uUXpi(pvToF1`CQm(Csr=~_P;(s4T= zJw;=^SG6dfuM(6>XjL-*yA1}*jjwE%sNq0|)*WcmcLZH!r!S~!7L@;sU?8(jeM#0A3|g>CFVkjtv_Rhm}I-5 z<}4QL#CNacw*ADNK!Gd?t;j?sWPs+|EjIPq95&@#ov*v%>i(jB}BJ_+o+^%h?<+ ze5DCpEN&^H5?eD=o1Jqc7>R&9@{pAh$`$6pg`^R=YR>Ew+x>i2w}A=m;V$gqJ!N)@ zt+rRm88{);V@@03Kyd#h$$L#nvfWo7DVT;llWf`QqpUoK18cdjDziafS=ImTNc`%l zrunT$KUZW6zm3-Fufx|Gcr9LRQ!}mFTGnn;wa~ij>BNYkN#aUmvJd_SV1GB7t!Ney z=kflv;ETx%Nln!2o$^gZ=Q)*^C>Yx=E=p=t9foH9tD!|n@v z_66%$-2U=Ko`KWejs3c1Oz2k>J-;Y_Da!&)=>mBLGhUW5t|-72@osnU&wL`%px=H2 zO2jl4w06h7oy+k9DOP)Hu-cJDZ!M=4UBO0f4ZsWh=&hwZjAiL@ws~uydYPTt8iv2~ za%}b2hA(GG|QedyJPL65iSF?!2mDJ7b`1bd;naijc+K^>LN=Ssw*@woL!HJJc&LLPnB%fni zmeSqg70d;XVkz~RFk-6J5;!5k$%!_?*#u1YYvu)vR zH!gyA)I|#x7wDpHup{E#hQC!3C)@ZmBi`$jrOw16yQupT9{U!c>=wq;TP}^KS4-n2 z%P!^Vy|@qzebNi0w+>P%%+31LFxLw1i7Ib}fA*T(M3DvHwExNK?Cq)jwUAr5vyfYO z%b1!G+~oWM&_9BwrameLmxYDCtqlv+;Ek}53YYBejGyyEDY%QQ{l1>sl~iuwU@BR_ z>G*VCl_qz{KzaCPGVlm0gn@`wh5@%!!@w|jWdALl8;GQEcCfhr_0)b`zzwWfz(d%T zP9ZdQ%m@81h?*>9LWi(WG19PbFU7Dx>9QYTqxV-RT9u+nUr1$8m$K*k{k3T+Jc1=G z^gwWvGsz%dLl`FgUsFHuX)&*bq5m*^5hw= zA@w1)piD{Snde20RiK)V&2l`pPRi9D{qX7IR5 z&f#%oBtW_+95&gk0ndfa8I28_wi$-a&2Z9QbvDISWFE!%l7$`XqWzt~E!<7u7XHF= z0!2DL9rRD&uE~Ng{1z4lH!>{Lfj1&5Mx`x2i)U^$MR+~YH5e6-ACCKLC#G{tcayj! zl`PeUlO{`Vp-Whb`p~fC2VP-mDrNnTnLL)R6xU%Ed&0k3cl<0S?X$ylZI8jfnh)k49e-&TaKK>}*zqwla|gUW@en<_p1iqB{HO zN*y(h6+3Ayvj`^hFDcyYIn!CzwbHEJ$%%rR%uL(_gZTClT8Icot%h z^wc;q)BYrwCEDwR{`$GlzAM`w8 zqArGq+Exk!mP%#=rASM@1)`gM)Oe$saP!f{B#-CIx$EoJaLpqpaLqjzUk)!VpOt9iMKtJyn_tLe>lT?tpKvnN-qWxgL*&>7D$ ztlZUx+MRLi@x_@Q2bOXbEmm?Bg~xIgHQCT>;VRzEU9i%$#=kvMxv_*VIs5qEr z1hprNdJrC3g_APvmJ(*Ir3M$&I%)u~BPIehf@?06%rzHY$TiO&$mg2T^Mt4SylJjc zKVan^TFWF1EU4~!KSP~XegKQUKg{FcT&{1@0@8;+h^=dHrPgMX?BRGwx7BVflhePT zmX>{;TKor~cV~D^n8USPp3l?SwjZz3Jr?*lJao%!;ady*urU9$`f zkNvdOeYlgK&*JJMlX=iT#`2)OI5N}zn>!EsB72@=Eweecpw6Pbo$kJgC4KIdleoV9 z?9h{DT2LQeoix_wX?RGtnZnz0Ma|w?`V`cR%g58Y+Lyh!lV=jS($G2F$%VbR*2XOR zS$H`&PRg{;bu(*CVqwoqX}5bA9#)FzhBNH=y9;E}6Q9WBc=@Qs?Ju_Yc}@R^bV8;2 zWfMqg5IsY<9NUw<$PM&AEr^+dcqKs)s|(^TLHu42=St%99$fNEL0lt<3nlSKL1cos zKoDO~7i!}Lai$Lv_70xN61wbw1<*bjcESm1 z?Tnyh+PN_tzqa+)l!&#F*pOa z9QbiEP0MdzzSva9D@PJRe(I;ssP9X{$3gKfaR+~91BzjHCBQy_u|x74 zGXTSe<~a@lMi0w#+y@*!D$n8Nr6^tF@*JUnlg7XfAe)fqm1oEx9#hyt`vM~?t{&VYk}wG;CkdjLO~ndi6x*fS~5fxQHj zakKIq!GNF7&U1VMxO)yV19)a`o?`*v!+Cj*ZGdWWUVj2j*L`eWo0onmura=zNi)nzNfFl<}4lo075@4exkOLeI zcnC0ODdYgFEK5Kweet7I9fE+@0HXk7RzMJN5#Vw_k5v!^{0J}`(6JhFSTD6&gB}1Z z|2gCUM*xlnw6B94V9At!GI07Kn`#a;5NWtzk?j$-+*rb&;5|+@b^=cnmdqL z!0CVy=n-{~KES4hsH63zHm!2l6$LFtkGJIkNu?(~0UGc)`HZ7HzKp{sP=938Drx0B zbk7!AMfHweqlH#U-L1DGP+K2Dpr4)$5Hz%8o`Y|JVk_=5M{Dj~q)>OwtEjs|sye~s zcct|c(4pP8=#L5Jl+`_3LT8X(qa|0?ia@wN1VD=j*5`w$O{}JGBBn6?IH4!k)gRN) zPxpufb0xh7ff{-%0s;CE04?=HeSRcFXEoM05uEyweuChb2>mg^As_3WtpIO*qSqw& zqNU!N;L29|P=bH7(&rP5ZmVx1_+4B51fc4!KWYW_{iAiy)_+bZq zD8XeN^!WrgbksKisF)9<#?YLu~6Rg`k&P^dK2C;&bL<1<7LY^zmP2k5r8 zkngIGpizLHPN2NLi;yMsn=~q>tL*?h^eP03>a7U4=_3fJdU`t;4AHj}7_FZrJLCfe<~KKpj0U3ce)h zD`>Pt-%F!j`W+gj>uNMcr}P@p2>0f6y>+x!6@qaXs-NiTL@ciFrBO5eDvg+~cEIQx zJ+OmTP5XP6-ns+twILX)gY|SG&eXTl=%RiVBklQI{jUzt+Ds4Z2x4cwSw~1dpRW(? z2uZ7+jG=l&-wvYodVzkfBX}eAx5V35w{`+?m)@)sMrZU`8Wq!%X;e=CmPTFlvoso{ zzo5|?X2}K5^HRbQS%k*kHgp!!;3CeCTzxW8L-lV#Rp0DB z-b6>FQ9;vTP_wxhB#sJq#p0lxWI0nm}ns8gVq!V~N%WG^>w^b{6YYa@Jo@1dnfk zi9AHlAkjl48fv0F!E{Wb@9TF+G{Qt4s|OB)XuvRtMww`D(AOl|Rc{6!YabJN3_K_D zU_Fk=9iNLhE@Mqj#|eihPj@0T}%{xmym&{Z2xRFSidF!X{SSXXO=K6Y&TbcI*-6YB8 zZRE~K{J3o4TC@O)v9I0dx;hF8s;GyJf>HDCmg!h!lhFu07G&+%Fn!7>t%Vw^XArk} z*T4wOEX2J{e?esPW>;e+d5Io48WFhcn#g@xE6L`KMNde)dAG)k62DVVggQU-hF?Kg zrm=U1XYFB$H}3>{UEoV9 z=B;2={mwYd7^QHhNcPP;L)H`I_L6Mg_B4qqmoQLL%{w12lX&w6x;G`>ywBxBi8pUq z>Q_cMXWk_;fa4YY>+zbewZ3FBZ#mmfKRaHt#7>lC^B%@4CEmPe?+uAxBrhzXBLB)i z5^vtmwK%^dT7xXYRr7AC-8flI6krtx3EL#|k!?b`u_^6oiRb%zf;3IqekSqeJr1j2 z?M1~iZ`Bwo@#eiFM+kf`#k@!4H&US5abeM4o@z%W-q`Ea*y{DM#GAKzZEl4_bOmVJ ziN3)#*PTL1_jrjn?>M?z;>}y59yc^rc#3cTwU6aL28UWfZ#D@neO;cR=B<5OaWd{g ziEF@OETqXn^LDtKB;LFM@G*%$A*anaS(^tE|LqomFt%LvE2k&{l!~x0d?QQd{cq<> zym^1{@Aa_B2;-C7*3|eG8zp(o`_%T3c=Hb4{UzSC z)2=dK;>{arf2{{jK}vs=RnML%OjA!Q95^rP1V9mloA_ZMntr1)9T~qz2B?*Ufl+}W9Ms= zi?PubEAuU1areumTJ=J^o2<~j(A0JMomEFSFDFaUFE%2SN$pt=gHwb-MT?* ms?O890eD>D5B#uSrM`XxnEFf=>-lE-9Sqg=y7ddK+W!HRAh>k^ diff --git a/norch/nn/modules/__pycache__/linear.cpython-38.pyc b/norch/nn/modules/__pycache__/linear.cpython-38.pyc index d4a4c2cdca0a08a5e80a29ad666355d6ae35f6aa..15ee46288c31ddb6189927e914a6e86122f654aa 100644 GIT binary patch delta 45 zcmeC-n83ju%FD~e00j3JyKm&~XJqu4JeyICJ)qJrzbH9l^ASc4CdR1AXPNB*2@DLa delta 41 vcmbQh(Zj(V%FD~e00ieVEjM!aGcvkNp3SJnoR*)z`2-^e6XUJP7n$t=&vgq{ diff --git a/norch/tensor.py b/norch/tensor.py index 18192c3..a4a84f6 100644 --- a/norch/tensor.py +++ b/norch/tensor.py @@ -630,20 +630,28 @@ class Tensor: return result_data - def sum(self, axis=-1): - Tensor._C.sum_tensor.argtypes = [ctypes.POINTER(CTensor), ctypes.c_int] + def sum(self, axis=-1, keepdim=False): + Tensor._C.sum_tensor.argtypes = [ctypes.POINTER(CTensor), ctypes.c_int, ctypes.c_bool] Tensor._C.sum_tensor.restype = ctypes.POINTER(CTensor) - result_tensor_ptr = Tensor._C.sum_tensor(self.tensor, axis) + result_tensor_ptr = Tensor._C.sum_tensor(self.tensor, axis, keepdim) result_data = Tensor() result_data.tensor = result_tensor_ptr if axis == -1: - result_data.shape = [1] - result_data.ndim = 1 + if keepdim: + result_data.ndim = self.ndim + result_data.shape = [1] * self.ndim + + else: + result_data.shape = [1] + result_data.ndim = 1 else: - result_data.shape = self.shape[:axis] + self.shape[axis+1:] + if keepdim: + result_data.shape = self.shape[:axis] + [1] + self.shape[axis+1:] + else: + result_data.shape = self.shape[:axis] + self.shape[axis+1:] result_data.ndim = len(result_data.shape) result_data.device = self.device @@ -653,7 +661,7 @@ class Tensor: result_data.requires_grad = self.requires_grad if result_data.requires_grad: - result_data.grad_fn = SumBackward(self, axis) + result_data.grad_fn = SumBackward(self, axis, keepdim=keepdim) return result_data diff --git a/test.py b/test.py new file mode 100644 index 0000000..89801e0 --- /dev/null +++ b/test.py @@ -0,0 +1,24 @@ +import norch + +W1 = norch.Tensor([[1], [2], [3], [4], [5], [6], [7], [8], [9], [10]], requires_grad=True) # A has shape 10x1 +X = norch.Tensor([[1, 2, 3, 4, 5]]) # B has shape 1x5 +B1 = norch.Tensor([[1, 2, 3, 4, 5], [2, 1, 2, 3, 4], [3, 1,2,3, 4], [4, 1, 1, 1, 1,], [5,1,1,1,1], [6,1,1,1,1], [7,1,1,1,1], [8,1,1,1,1], [9,1,1,1,1], [10,1,1,1,1]], requires_grad=True) + + +W2 = norch.Tensor([[1], [2], [3], [4], [5], [6], [7], [8], [9], [10]], requires_grad=True).T +B2 = norch.Tensor([[1, 2, 3, 4, 5]], requires_grad=True) + + +Z1 = W1 @ X + B1 + +# Perform matrix multiplication + +Z2 = W2 @ Z1 + B2 +print(Z2.shape) + +l = Z2.sum() +l.backward() +#print(l) + +print("Resulting matrix shape:", W1.grad) + diff --git a/tests/test_operations.py b/tests/test_operations.py index d6552a6..a09d351 100644 --- a/tests/test_operations.py +++ b/tests/test_operations.py @@ -249,6 +249,24 @@ class TestTensorOperations(unittest.TestCase): self.assertTrue(utils.compare_torch(torch_result, torch_expected)) + + def test_sum_axis_keepdim(self): + """ + Test summation of a tensor along a specific axis with keepdim=True + """ + norch_tensor = norch.Tensor([[[1, 2], [3, -4]], [[5, 6], [7, 8]]]).to(self.device) + norch_result = norch_tensor.sum(axis=1, keepdim=True) + torch_result = utils.to_torch(norch_result).to(self.device) + + torch_tensor = torch.tensor([[[1, 2], [3, -4]], [[5, 6], [7, 8]]]).to(self.device) + torch_expected = torch.sum(torch_tensor, dim=1, keepdim=True) + + print(torch_result, '\n', torch_expected) + + + self.assertTrue(utils.compare_torch(torch_result, torch_expected)) + + def test_transpose_T(self): """ Test transposition of a tensor: tensor.T From 2df6c36d99e4283924c0e5cf598aaae6642a4fde Mon Sep 17 00:00:00 2001 From: lucasdelimanogueira Date: Fri, 17 May 2024 19:29:16 -0300 Subject: [PATCH 08/28] fixed sum axis with keepdim --- build/cpu.o | Bin 10976 -> 10848 bytes build/tensor.o | Bin 43312 -> 43304 bytes norch/csrc/cpu.cpp | 5 +---- norch/csrc/cpu.h | 2 +- norch/csrc/tensor.cpp | 35 ++++++++++++++++++----------------- norch/libtensor.so | Bin 172864 -> 172864 bytes 6 files changed, 20 insertions(+), 22 deletions(-) diff --git a/build/cpu.o b/build/cpu.o index adc39e5238ee388ad81ef0ef6924b00d82d01d9e..6372b93e473468016bdccce213ac77d7ba4524e3 100644 GIT binary patch delta 1139 zcmZuvUr19?7(eIEVk7O|soPr6Vq&+u<%7|~GAk!_;1Wp+4nl+;g6O3naG=nbaxu8v z2GSuYBIqd}#YXgp9yWZ~B!v}0_7JX^u%VE#K~u!_oqNxWh97+A&+q&FzVCeBz4=If zct#QO@_cYaiicG_wxa6MoE}+H^?}r7on*CGwiX?^CH_j4MJLH;NCAPV23aw}T|J}_ z>tbisKt`mX-u$ele+nwjl|>1G59G_@^|UF}NCB^Bfy-h9a3?BFV1yt)8gDl9#luPu z$=-LXi3_a8TecM0QW^rHRVHjVBH4d+45X8t3^7@pAT zcJ#DCHgR^Z2Alc*O7jySjcni&s?};5DdJLYw^mzRD2u-Bh^nu_hcBx1yvvpR-um7~ zpSe#bbM9_I+J?Cya#(sGTxvOJ`;C*5_iU5HL?Q5+`j2_y9|VM25k_ZP*^m&rYHeth z<8h9w9Jkpleum>WI3D1*+-3pKIev}f3mi9pK!`;xJcwr7E#@c}JmvzM%L49k{1tWj zJl;1vI@S>VlI-_=6p}6eQ#cvwSj2dl;!e+%uac#1f~^C52L{OOE`L2Vi4K5wf-@WC zX&FBVUgs~nl8-uHV(;FRm=KI@%;R)O_RvAu?tIt~k?EA|@z%RvYKYF#d5G3qm7qCh z^{iv`hrHUd#@}Rys6Ws~^CyouihlEs!W1hZ;e?y|kGkogqSTKwisDe*7N?7*l@K1L yTS}iW$(aEKPtu7%AAUjK!K%=$K%bEF(sRL!4!h6NeWO#skmI&&-NtCYa{Mm^l0$d^ delta 1260 zcmZuvUr19?7(eG~;ZWQ4-{vCAG~IO>Xg%nV(#j7ml;J~yNRSozrYU47HAY;FE;k`K z5cLow^w2|~GLTUVwujgtAGGKttVZ}SnV{jI3F7+B&Y98hgYTa2{J!7!edqknjbDxT zJ=K&!_q=C7^$+T%FQ%L0VY4r!n>};gCIq|9*kgEW_=1`7fg$@wc*|Y}D^Z9OG~I%j z)z$2UxUZSVx<#zMxPI%i9{Hu{bZN`367-&xEqi|?TWCO>CXol&M+L;o$l7~K2=*rv z{u0qp^Fr`}L!Um&X9W2t!9QyZiDFDwk<}OcH^(9->_lOevm`;yBY0ueKCNPdC}f*i zkm!JreL96DQCR1dmLC!(Y|1kj3B`nz;Y*Q;aSKxVbA7sUiqkMZhj+7zEZBVUXWLvB z6MJ!=>VTlHSg<2;C0T-@5vEv+J6=QVvk?mG`}S+W`Pgt5k6;X^XmSdV_}|p9P_#(Q zQ!TvFEbN#O3)X3DZwA+V|H|wW5Cal)jbt(z16JwU(rzXbg>?4%;7y|>zB3l2_nV6^ zIS|f03JNJ6#eg(TehiG2|0pPE7$WS7<8%S$0)9uUqFzuighT2hI-qHL*GH^x!soc;u0M|uSe&0N8%jC$H^iAJ%z%OdV$e*s-EU@HIs diff --git a/build/tensor.o b/build/tensor.o index 0071d08d1c19ea6783ac31468df999592c89e296..893104690408c2422c28c7d106ccbeac4d8628d0 100644 GIT binary patch literal 43304 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≥3tc|8y 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 jA`!<~S7Bb99^JtGnB!|c*YVr__data[index + i * axis_stride]; } } - for (int j = 0; j < size; j++) { - printf("%f", result_data[j]); - } } } diff --git a/norch/csrc/cpu.h b/norch/csrc/cpu.h index 5fe358c..28404e5 100644 --- a/norch/csrc/cpu.h +++ b/norch/csrc/cpu.h @@ -5,7 +5,7 @@ void add_tensor_cpu(Tensor* tensor1, Tensor* tensor2, float* result_data); void add_broadcasted_tensor_cpu(Tensor* tensor1, Tensor* tensor2, float* result_data, int* broadcasted_shape, int broadcasted_size); -void sum_tensor_cpu(Tensor* tensor, float* result_data, int size, int* shape, int axis, bool keepdim); +void sum_tensor_cpu(Tensor* tensor, float* result_data, int size, int* shape, int axis); void sub_tensor_cpu(Tensor* tensor1, Tensor* tensor2, float* result_data); void sub_broadcasted_tensor_cpu(Tensor* tensor1, Tensor* tensor2, float* result_data, int* broadcasted_shape, int broadcasted_size); void elementwise_mul_tensor_cpu(Tensor* tensor1, Tensor* tensor2, float* result_data); diff --git a/norch/csrc/tensor.cpp b/norch/csrc/tensor.cpp index c945689..b717145 100644 --- a/norch/csrc/tensor.cpp +++ b/norch/csrc/tensor.cpp @@ -192,24 +192,14 @@ extern "C" { shape[0] = 1; ndim = 1; } else { - - if (keepdim) { - shape = (int*) malloc((tensor->ndim) * sizeof(int)); - for (int i = 0; i < tensor->ndim; i++) { - shape[i] = tensor->shape[i]; + shape = (int*) malloc((tensor->ndim - 1) * sizeof(int)); + for (int i = 0, j = 0; i < tensor->ndim; ++i) { + if (i != axis) { + shape[j++] = tensor->shape[i]; } - shape[axis] = 1; - ndim = tensor->ndim; - - } else { - shape = (int*) malloc((tensor->ndim - 1) * sizeof(int)); - for (int i = 0, j = 0; i < tensor->ndim; ++i) { - if (i != axis) { - shape[j++] = tensor->shape[i]; - } - } - ndim = tensor->ndim - 1; } + ndim = tensor->ndim - 1; + } int size = 1; @@ -230,7 +220,18 @@ extern "C" { fprintf(stderr, "Memory allocation failed\n"); exit(1); } - sum_tensor_cpu(tensor, result_data, size, shape, axis, keepdim); + sum_tensor_cpu(tensor, result_data, size, shape, axis); + + if (keepdim) { + shape = (int*) malloc((tensor->ndim) * sizeof(int)); + for (int i = 0; i < tensor->ndim; i++) { + shape[i] = tensor->shape[i]; + } + shape[axis] = 1; + ndim = tensor->ndim; + + } + return create_tensor(result_data, shape, ndim, device); } } diff --git a/norch/libtensor.so b/norch/libtensor.so index 09af449b259d5e11ee27f21d0f339c1a2e53fcef..9212825f2e434aeeaf8c3bd47dd6fe72c899a152 100755 GIT binary patch delta 22187 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

Y)eBGW397Q<|ob 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 delta 22180 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 VX;&I|X1Bgpn#q7tl From eb49d8cae3e0537602f011e85dcf58f1823dfeab Mon Sep 17 00:00:00 2001 From: lucasdelimanogueira Date: Fri, 17 May 2024 20:12:57 -0300 Subject: [PATCH 09/28] fixed sum axis with keepdim --- examples/train.ipynb | 561 ++++++++++++++++++++++- norch/__pycache__/tensor.cpython-38.pyc | Bin 15530 -> 15534 bytes norch/nn/__pycache__/loss.cpython-38.pyc | Bin 1416 -> 1420 bytes test.py | 76 ++- 4 files changed, 614 insertions(+), 23 deletions(-) diff --git a/examples/train.ipynb b/examples/train.ipynb index 9c92203..05e4e42 100644 --- a/examples/train.ipynb +++ b/examples/train.ipynb @@ -26,28 +26,523 @@ }, { "cell_type": "code", - "execution_count": 7, + "execution_count": 16, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "Invalid axis" - ] - }, - { - "ename": "ValueError", - "evalue": "Matrix multiplication requires 2D tensors", - "output_type": "error", - "traceback": [ - "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", - "\u001b[0;31mValueError\u001b[0m Traceback (most recent call last)", - "Cell \u001b[0;32mIn[7], line 56\u001b[0m\n\u001b[1;32m 53\u001b[0m loss \u001b[38;5;241m=\u001b[39m criterion(outputs, target)\n\u001b[1;32m 55\u001b[0m optimizer\u001b[38;5;241m.\u001b[39mzero_grad()\n\u001b[0;32m---> 56\u001b[0m \u001b[43mloss\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mbackward\u001b[49m\u001b[43m(\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 57\u001b[0m optimizer\u001b[38;5;241m.\u001b[39mstep()\n\u001b[1;32m 59\u001b[0m \u001b[38;5;28mprint\u001b[39m(\u001b[38;5;124mf\u001b[39m\u001b[38;5;124m'\u001b[39m\u001b[38;5;124mEpoch [\u001b[39m\u001b[38;5;132;01m{\u001b[39;00mepoch\u001b[38;5;250m \u001b[39m\u001b[38;5;241m+\u001b[39m\u001b[38;5;250m \u001b[39m\u001b[38;5;241m1\u001b[39m\u001b[38;5;132;01m}\u001b[39;00m\u001b[38;5;124m/\u001b[39m\u001b[38;5;132;01m{\u001b[39;00mepochs\u001b[38;5;132;01m}\u001b[39;00m\u001b[38;5;124m], Loss: \u001b[39m\u001b[38;5;132;01m{\u001b[39;00mloss[\u001b[38;5;241m0\u001b[39m]\u001b[38;5;132;01m:\u001b[39;00m\u001b[38;5;124m.4f\u001b[39m\u001b[38;5;132;01m}\u001b[39;00m\u001b[38;5;124m'\u001b[39m)\n", - "File \u001b[0;32m~/Documentos/recreate_pytorch/PyNorch/norch/tensor.py:167\u001b[0m, in \u001b[0;36mTensor.backward\u001b[0;34m(self, gradient)\u001b[0m\n\u001b[1;32m 165\u001b[0m \u001b[38;5;66;03m# Propagate gradients to inputs if not a leaf tensor\u001b[39;00m\n\u001b[1;32m 166\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m tensor\u001b[38;5;241m.\u001b[39mgrad_fn \u001b[38;5;129;01mis\u001b[39;00m \u001b[38;5;129;01mnot\u001b[39;00m \u001b[38;5;28;01mNone\u001b[39;00m:\n\u001b[0;32m--> 167\u001b[0m grads \u001b[38;5;241m=\u001b[39m \u001b[43mtensor\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mgrad_fn\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mbackward\u001b[49m\u001b[43m(\u001b[49m\u001b[43mgrad\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 168\u001b[0m \u001b[38;5;28;01mfor\u001b[39;00m tensor, grad \u001b[38;5;129;01min\u001b[39;00m \u001b[38;5;28mzip\u001b[39m(tensor\u001b[38;5;241m.\u001b[39mgrad_fn\u001b[38;5;241m.\u001b[39minput, grads):\n\u001b[1;32m 169\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;28misinstance\u001b[39m(tensor, Tensor) \u001b[38;5;129;01mand\u001b[39;00m tensor \u001b[38;5;129;01mnot\u001b[39;00m \u001b[38;5;129;01min\u001b[39;00m visited:\n", - "File \u001b[0;32m~/Documentos/recreate_pytorch/PyNorch/norch/autograd/functions.py:90\u001b[0m, in \u001b[0;36mMatmulBackward.backward\u001b[0;34m(self, gradient)\u001b[0m\n\u001b[1;32m 88\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m [aux_sum, x\u001b[38;5;241m.\u001b[39mtranspose(\u001b[38;5;241m-\u001b[39m\u001b[38;5;241m1\u001b[39m,\u001b[38;5;241m-\u001b[39m\u001b[38;5;241m2\u001b[39m) \u001b[38;5;241m@\u001b[39m gradient]\n\u001b[1;32m 89\u001b[0m \u001b[38;5;28;01melse\u001b[39;00m:\n\u001b[0;32m---> 90\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m [\u001b[43mgradient\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m@\u001b[39;49m\u001b[43m \u001b[49m\u001b[43my\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mtranspose\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;241;43m-\u001b[39;49m\u001b[38;5;241;43m1\u001b[39;49m\u001b[43m,\u001b[49m\u001b[38;5;241;43m-\u001b[39;49m\u001b[38;5;241;43m2\u001b[39;49m\u001b[43m)\u001b[49m, x\u001b[38;5;241m.\u001b[39mtranspose(\u001b[38;5;241m-\u001b[39m\u001b[38;5;241m1\u001b[39m,\u001b[38;5;241m-\u001b[39m\u001b[38;5;241m2\u001b[39m) \u001b[38;5;241m@\u001b[39m gradient]\n", - "File \u001b[0;32m~/Documentos/recreate_pytorch/PyNorch/norch/tensor.py:487\u001b[0m, in \u001b[0;36mTensor.__matmul__\u001b[0;34m(self, other)\u001b[0m\n\u001b[1;32m 484\u001b[0m \u001b[38;5;28;01melse\u001b[39;00m:\n\u001b[1;32m 485\u001b[0m \u001b[38;5;66;03m#2D matmul\u001b[39;00m\n\u001b[1;32m 486\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mndim \u001b[38;5;241m!=\u001b[39m \u001b[38;5;241m2\u001b[39m \u001b[38;5;129;01mor\u001b[39;00m other\u001b[38;5;241m.\u001b[39mndim \u001b[38;5;241m!=\u001b[39m \u001b[38;5;241m2\u001b[39m:\n\u001b[0;32m--> 487\u001b[0m \u001b[38;5;28;01mraise\u001b[39;00m \u001b[38;5;167;01mValueError\u001b[39;00m(\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mMatrix multiplication requires 2D tensors\u001b[39m\u001b[38;5;124m\"\u001b[39m)\n\u001b[1;32m 489\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mshape[\u001b[38;5;241m1\u001b[39m] \u001b[38;5;241m!=\u001b[39m other\u001b[38;5;241m.\u001b[39mshape[\u001b[38;5;241m0\u001b[39m]:\n\u001b[1;32m 490\u001b[0m \u001b[38;5;28;01mraise\u001b[39;00m \u001b[38;5;167;01mValueError\u001b[39;00m(\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mIncompatible shapes for matrix multiplication\u001b[39m\u001b[38;5;124m\"\u001b[39m)\n", - "\u001b[0;31mValueError\u001b[0m: Matrix multiplication requires 2D tensors" + "tensor([3.3399136066436768,], device=\"cpu\", requires_grad=True)\n", + "tensor([inf,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n", + "tensor([nan,], device=\"cpu\", requires_grad=True)\n" ] } ], @@ -92,7 +587,7 @@ "for x in x_values:\n", " y_true.append(math.pow(math.sin(x), 2))\n", "\n", - "batch_size = 5\n", + "batch_size = 2\n", "\n", "\n", "for epoch in range(epochs):\n", @@ -104,6 +599,7 @@ " target = target.to(device)\n", "\n", " outputs = model(x)\n", + "\n", " loss = criterion(outputs, target)\n", " \n", " optimizer.zero_grad()\n", @@ -114,6 +610,26 @@ " loss_list.append(loss[0])" ] }, + { + "cell_type": "code", + "execution_count": 15, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "[nan, nan, nan, nan, nan, nan, nan, nan, nan, nan]" + ] + }, + "execution_count": 15, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "loss_list" + ] + }, { "cell_type": "code", "execution_count": 3, @@ -147,12 +663,12 @@ }, { "cell_type": "code", - "execution_count": 4, + "execution_count": 14, "metadata": {}, "outputs": [ { "data": { - "image/png": "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evVqdejQQTfccINzv0qVKun+++/XoUOH9OOPP0qSPvnkE9WoUUOPPPJItjx/HXJ5Jb9TAOCqKE4A4Cbq1q0rX1/fbNt/+OEH9e3bV4GBgQoICFDNmjWdb8CTkpLy/b5XXXVVls8z3/DmVi7yOjbz+Mxjjx8/rj///FONGzfOtl9O24ri119/lSQ1bdo029eaNWvm/Lqfn5+mTZumNWvWKCgoSDfeeKNeeeUVZ4GQpJtuukn9+/fX888/rxo1aqh379768MMPlZaWVqAsjRs3zlYimjRpIknO+6iqVKmiXr16acGCBc59oqKiVLduXd1yyy0F+jlXX321IiMjsy0BAQGFzvPrr7/meO4yZ3HMPH+//PKLmjZtWqBJQq7kdwoAXBXFCQDcxOVXljKdPn1aN910k3bu3KkpU6bo008/VUxMjKZNmyZJBZp+3NvbO8ftlmWV6LEmjB07Vj///LOmTp0qf39/TZw4Uc2bN9eOHTsk2VdOlixZok2bNunhhx/WkSNHNGrUKLVr105nzpwpthzDhw/XgQMH9N133yklJUUrV67UkCFD5OXlGf8su9vvBQAUhGf8FxoAyqgNGzbo999/19y5czVmzBj17NlTkZGRWYZJmVSrVi35+/tr//792b6W07aiqF+/viRp79692b62d+9e59czNWrUSP/4xz+0fv167d69W+fPn9drr72WZZ/rrrtOL774orZu3aqoqCj98MMPWrhwYb5Z9u/fn60c/Pzzz5KUZUbDO+64QzVr1lRUVJSWLVums2fP6p577inQ6y2MguSpX79+jucuc5hn5vlr1KiR9u7dW6QhjADgCShOAODGMv+yf/mb4/Pnz+vdd981FSkLb29vRUZGavny5Tp69Khz+/79+7VmzZpi+Rnt27dXrVq1NGvWrCxD6tasWaM9e/aoR48ekuyZ6M6dO5fl2EaNGqly5crO4/74449sRaNNmzaSVKDhekePHtWyZcucnycnJ+ujjz5SmzZtVLt2bed2Hx8fDRkyRIsXL9bcuXN1zTXXqHXr1oV74QVQkDzdu3fX5s2btWnTJud+qampmj17tkJDQ533TfXv318nT57U9OnTs/0criQBKAuYjhwA3Nj111+vqlWrasSIEXr00UflcDg0f/58l3oj+9xzz2n9+vXq1KmTHnzwQaWnp2v69Olq1aqV4uLiCvQ9Lly4oH/+85/ZtlerVk0PPfSQpk2bppEjR+qmm27SkCFDnNORh4aGOqf9/vnnn3Xrrbdq4MCBatGihXx8fLRs2TIlJiZq8ODBkqR58+bp3XffVd++fdWoUSOlpKRozpw5CggIUPfu3fPN2aRJE/3tb3/Tli1bFBQUpA8++ECJiYn68MMPs+07fPhwvf322/ryyy+dQysLavv27fr444+zbW/UqJEiIiIKlWf8+PGKjo5Wt27d9Oijj6patWqaN2+eDh48qE8++cQ5fHD48OH66KOPNG7cOG3evFmdO3dWamqqPv/8cz300EMFmkYdANwZxQkA3Fj16tX12Wef6R//+IeeffZZVa1aVXfffbduvfVWde3a1XQ8SVK7du20Zs0aPf7445o4caJCQkI0ZcoU7dmzp0Cz/kn2VbSJEydm296oUSM99NBDuvfee1WhQgW9/PLLeuqpp1SxYkX17dtX06ZNc86UFxISoiFDhig2Nlbz58+Xj4+PmjVrpsWLF6t///6S7MkhNm/erIULFyoxMVGBgYHq0KGDoqKi1KBBg3xzXn311XrnnXf0xBNPaO/evWrQoIEWLVqU4/8W7dq1U8uWLbVnzx4NGzasQOchU3R0dI4Pyh0xYkSW4lSQPEFBQfruu+/01FNP6Z133tG5c+fUunVrffrpp86rdZJ99XD16tV68cUXtWDBAn3yySeqXr26brjhBl1zzTWFyg8A7shhudKfJQEAZUafPn30ww8/aN++faajGBMeHq5q1aopNja22L93aGioWrVqpc8++6zYvzcAlEXc4wQAKHF//vlnls/37dun1atX6+abbzYTyAVs3bpVcXFxGj58uOkoAIACYKgeAKDENWzYUPfee68aNmyoX3/9VTNnzpSvr6+efPJJ09FK3e7du7Vt2za99tprCg4O1qBBg0xHAgAUAMUJAFDi7rjjDkVHRyshIUF+fn6KiIjQSy+9pKuvvtp0tFK3ZMkSTZkyRU2bNlV0dLT8/f1NRwIAFAD3OAEAAABAPrjHCQAAAADyQXECAAAAgHyUuXucMjIydPToUVWuXFkOh8N0HAAAAACGWJallJQU1alTx/nA79yUueJ09OhRhYSEmI4BAAAAwEXEx8erXr16ee5T5opT5cqVJdknJyAgwHAaAAAAAKYkJycrJCTE2RHyUuaKU+bwvICAAIoTAAAAgALdwsPk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iT/H6c8//1RaWpqzNP3666968803tXfvXpcuTTAvMlKqVUs6eVJav950GgAAAKBgilScevfurY8++kiSdPr0aXXs2FGvvfaa+vTpo5kzZxZrQHgWHx9pyBB7neF6AAAAcBdFKk7bt29X586dJUlLlixRUFCQfv31V3300Ud6++23izUgPE/m7HrLl0vJyUajAAAAAAVSpOJ09uxZVa5cWZK0fv169evXT15eXrruuuv066+/FmtAeJ527aSmTaVz56Rly0ynAQAAAPJXpOLUuHFjLV++XPHx8Vq3bp1uv/12SdLx48eZcAH5cjguXXViuB4AAADcQZGK06RJk/T4448rNDRUHTp0UEREhCT76lN4eHixBoRnGjrU/hgba8+uBwAAALiyIhWnAQMG6PDhw9q6davWrVvn3H7rrbfqjTfeKLZw8FwNG0qdOkmWJUVHm04DAAAA5K1IxUmSateurfDwcB09elS//fabJKlDhw5q1qxZsYWDZ2O4HgAAANxFkYpTRkaGpkyZosDAQNWvX1/169dXlSpV9MILLygjI6O4M8JD3XWX/QDcuDhp927TaQAAAIDcFak4PfPMM5o+fbpefvll7dixQzt27NBLL72kd955RxMnTizujPBQ1atL3bvb61FRZrMAAAAAeXFYlmUV9qA6depo1qxZuvPOO7NsX7FihR566CEdOXKk2AIWt+TkZAUGBiopKYkZAF3AkiX2laeQEOnQIcmryINHAQAAgMIpTDco0tvUU6dO5XgvU7NmzXTq1KmifEuUUT17SgEBUny89PXXptMAAAAAOStScQoLC9P06dOzbZ8+fbpat259xaFQdvj721ecJCaJAAAAgOsq0lC9jRs3qkePHrrqqqucz3DatGmT4uPjtXr1anXu3LnYgxYXhuq5ng0bpC5dpMBAKSHBLlMAAABASSvxoXo33XSTfv75Z/Xt21enT5/W6dOn1a9fP/3www+aP39+kUKj7LrxRqlePSkpSVq1ynQaAAAAILsiXXHKzc6dO9W2bVulp6cX17csdlxxck3jx0vTpkl9+kjLlplOAwAAgLKgxK84AcUt82G4q1ZJzC8CAAAAV0Nxgkto1UoKC5MuXJD+8x/TaQAAAICsKE5wGZlXnZhdDwAAAK7GpzA79+vXL8+vnz59+kqyoIwbMkR68knpm2+kgwelBg1MJwIAAABs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5F4+/PBD1apVSz169DAdxS2cPXtWXl5Z3wp6e3srIyPDUCL3U7FiRQUHB+uPP/7QunXr1Lt3b9OR3EaDBg1Uu3ZtxcbGOrclJyfr+++/d9l7sBmqZ8iZM2ey/PX14MGDiouLU7Vq1XTVVVcZTObaRo8erQULFmjFihWqXLmycwxsYGCgypcvbzid65owYYK6deumq666SikpKVqwYIE2bNigdevWmY7m0ipXrpzt/rmKFSuqevXq3FeXh8cff1y9evVS/fr1dfToUU2ePFne3t4aMmSI6Wgu77HHHtP111+vl156SQMHDtTmzZs1e/ZszZ4923Q0l5eRkaEPP/xQI0aMkI8Pb28KolevXnrxxRd11VVXqWXLltqxY4def/11jRo1ynQ0l7du3TpZlqWmTZtq//79euKJJ9SsWTONHDnSdDSXkt/73bFjx+qf//ynrr76ajVo0EATJ05UnTp11KdPH3Oh82J6Wr+y6ssvv7QkZVtGjBhhOppLy+mcSbI+/PBD09Fc2qhRo6z69etbvr6+Vs2aNa1bb73VWr9+velYbonpyPM3aNAgKzg42PL19bXq1q1rDRo0yNq/f7/pWG7j008/tVq1amX5+flZzZo1s2bPnm06kltYt26dJcnau3ev6ShuIzk52RozZox11VVXWf7+/lbDhg2tZ555xkpLSzMdzeUtWrTIatiwoeXr62vVrl3bGj16tHX69GnTsVxOfu93MzIyrIkTJ1pBQUGWn5+fdeutt7r0/4cdlsXjoQEAAAAgL9zjBAAAAAD5oDgBAAAAQD4oTgAAAACQD4oTAAAAAOSD4gQAAAAA+aA4AQAAAEA+KE4AAAAAkA+KEwAAAADkg+IEAEAhOBwOLV++3HQMAEApozgBANzGvffeK4fDkW254447TEcDAHg4H9MBAAAojDvuuEMffvhhlm1+fn6G0gAAygquOAEA3Iqfn59q166dZalataokexjdzJkz1a1bN5UvX14NGzbUkiVLshy/a9cu3XLLLSpfvryqV6+u+++/X2fOnMmyzwcffKCWLVvKz89PwcHBevjhh7N8/eTJk+rbt68qVKigq6++WitXrizZFw0AMI7iBADwKBMnTlT//v21c+dODRs2TIMHD9aePXskSampqeratauqVq2qLVu26D//+Y8+//zzLMVo5syZGj16tO6//37t2rVLK1euVOPGjbP8jOeff14DBw7U//73P3Xv3l3Dhg3TqVOnSvV1AgBKl8OyLMt0CAAACuLee+/Vxx9/LH9//yzbn376aT399NNyOBx64IEHNHPmTOfXrrvuOrVt21bvvvuu5syZo6eeekrx8fGqWLGiJGn16tXq1auXjh49qqCgINWtW1cjR47UP//5zxwzOBwOPfvss3rhhRck2WWsUqVKWrNmDfdaAYAH4x4nAIBb6dKlS5ZiJEnVqlVzrkdERGT5WkREhOLi4iRJe/bsUVhYmLM0SVKnTp2UkZGhvXv3yuFw6OjRo7r11lvzzNC6dWvnesWKFRUQEKDjx48X9SUBANwAxQkA4FYqVqyYbehccSlfvnyB9itXrlyWzx0OhzIyMkoiEgDARXCPEwDAo/z3v//N9nnz5s0lSc2bN9fOnTuVmprq/Pq3334rLy8vNW3aVJUrV1ZoaKhiY2NLNTMAwPVxxQkA4FbS0tKUkJCQZZuPj49q1KghSfrPf/6j9u3b64YbblBUVJQ2b96s999/X5I0bNgwTZ48WSNGjNBzzz2nEydO6JFHHtE999yjoKAgSdJzzz2nBx54QLVq1VK3bt2UkpKib7/9Vo888kjpvlAAgEuhOAEA3MratWsVHBycZVvTpk31008/SbJnvFu4cKEeeughBQcHKzo6Wi1atJAkVahQQevWrdOYMWN07bXXqkKFCurfv79ef/115/caMWKEzp07pzfeeEOPP/64atSooQEDBpTeCwQAuCRm1QMAeAyHw6Fly5apT58+pqMAADwM9zgBAAAAQD4oTgAAAACQD+5xAgB4DEafAwBKClecAAAAACAfFCcAAAAAyAfFCQAAAADyQXECAAAAgHxQnAAAAAAgHxQnAAAAAMgHxQkAAAAA8kFxAgAAAIB8/D9+bdOrEm6qZgAAAABJRU5ErkJggg==", + "image/png": "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", "text/plain": [ "

" ] @@ -175,6 +691,13 @@ "plt.xticks(epochs_list)\n", "plt.show()" ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] } ], "metadata": { diff --git a/norch/__pycache__/tensor.cpython-38.pyc b/norch/__pycache__/tensor.cpython-38.pyc index 92de5802f744c677d973b17b28e9a0a9761ffd8d..45abd37a6e25c4ec09757980a929bbc399ecda78 100644 GIT binary patch delta 953 zcmaKq&rcIk5XU?Hv8`PzrAvRM1r`AVb z5=llq5e04902U=h@2t3qK_XoL&+Ls+$*XLWK9K_K+u z^P&;%BDuK&^w?Wi{(Dadc2IOU(dw;>EFW3XWVyIrDah*PfO*!eCRNC6A+t7SZOjVk zwJf9rwqkEekhhhhtR@R{S{AY6tk>;!#CgTA%qhkdP)w;qDM$P;*<}ZIIf{*YJ+Rwb zRJAX;2bipiu8F3P;yY)9F{U3GyAg=tZ|5Ym;kc_8*LeeOyIRrg z_Hu)i7)Qz74`!?>^{bn157^r%Iz$K(jD#pIip7w?HBqt+Q$S>Rsh2CsPpYk+XIg%M z{7Zz(xDpa_>P>*6s|w3Yj&XTe>?~P@k=#)jN^kwI*%&qBQ=}&nuH=e-iD$L^!Gv~{ z%*8lb-VagyP~O8QDYK4HOX(P>Sg@qovRb=0YA*V>Bfgd1!2aKE`Fyx^B9X6C zZ71C%B>5A3_Mf8H{-?mvOv5@{GP~4xtpw14kLngoG5yfg*o1sfEek#|zyv-Hl;X$J zJ6tPCw9r;V1UExvEd5g`%r2`CZkTDHkEaM>!Wlw@&`!wR?-+TbgsX&WgzE&_Fj*!j zgh|2_;WlBKaEEZ0FhiIn+$YQt<~4#w^VyV^$J0q!$;b~VDA1(%Gdu_nv9oF553Qo< AtN;K2 delta 822 zcmaLV%TE(g6bInmK8DAnfN5)=v{h(KYok-GSZN`t&=SK2g9tuQDYV)OQilR91Q8$c zotT`sGX~ws!bc`9SZFjD{|Cpoa^XU^Y)nYJzbO*q!dZN|bLX5hC+FT5m6eKlz-+G9 z@n`L0Pp;#&`K!oO<%j4nFqMpr5=+p5#&wpYh}oiaHkGigSYqrX{j@x5I?nUtL0|zV z=!tbqIE`hJmJ(L-*gBODZl|7R?Oj3#_S5v<-d3X;$MO>ql{W0tEJag}0eb3n(Rar# zlPvKlYH)glEW$G+IfqE{MU+*io7qy>%Yi7U0cjfcdQ9?pzNTf-Mn1RIb^#y!oaaV4 zfOUaGFJmqx{^LI%KQ7#-4ub_Y*R2zC5R+vz2 zi06c8c~7RAr)=NrL?KDah)aJZ;TpV>9x(y^j2h^1y+aw@7iO%N9`1ios~Shj`6>A( z<$^nvo~UdR$~-Q(jn47&{e0+?s|&S~;Gd~?(`x$;Ugb-BoL@0}ylwFyqGBKc4ufuR z1ZcNx7^{n50u;ezfEmiOpaSN=0$2oB!8LFl+yJ-0ZEy$N)ivmJYHd-TEN10WMZQOB Lyv*)Xf8x|{PBO^g diff --git a/norch/nn/__pycache__/loss.cpython-38.pyc b/norch/nn/__pycache__/loss.cpython-38.pyc index 170a29b4b95611e278c5932f02719b7679e906db..a39202c7182c730dfc171bf4b535752507f08ad9 100644 GIT binary patch delta 39 tcmeC+?&0PR<>lpK00QGX?i;xS85!*+$1lpK0D|~FtBu@&jEq*3V;R+$)AI8-cQG=t004o?2f+XU diff --git a/test.py b/test.py index 89801e0..92d779d 100644 --- a/test.py +++ b/test.py @@ -3,7 +3,7 @@ import norch W1 = norch.Tensor([[1], [2], [3], [4], [5], [6], [7], [8], [9], [10]], requires_grad=True) # A has shape 10x1 X = norch.Tensor([[1, 2, 3, 4, 5]]) # B has shape 1x5 B1 = norch.Tensor([[1, 2, 3, 4, 5], [2, 1, 2, 3, 4], [3, 1,2,3, 4], [4, 1, 1, 1, 1,], [5,1,1,1,1], [6,1,1,1,1], [7,1,1,1,1], [8,1,1,1,1], [9,1,1,1,1], [10,1,1,1,1]], requires_grad=True) - +B1 = norch.Tensor([[1], [2], [3], [4], [5], [6], [7], [8], [9], [10]], requires_grad=True) # A has shape 10x1 W2 = norch.Tensor([[1], [2], [3], [4], [5], [6], [7], [8], [9], [10]], requires_grad=True).T B2 = norch.Tensor([[1, 2, 3, 4, 5]], requires_grad=True) @@ -13,12 +13,80 @@ Z1 = W1 @ X + B1 # Perform matrix multiplication -Z2 = W2 @ Z1 + B2 +Z2 = W2 @ Z1 * B2 print(Z2.shape) l = Z2.sum() l.backward() -#print(l) +print(l) -print("Resulting matrix shape:", W1.grad) +print("Resulting matrix shape:", B1.grad) + +"""import norch +import norch.nn as nn +import norch.optim as optim +import random +import math + +random.seed(1) + +class MyModel(nn.Module): + def __init__(self): + super(MyModel, self).__init__() + self.fc1 = nn.Linear(1, 10) + self.sigmoid = nn.Sigmoid() + self.fc2 = nn.Linear(10, 1) + + def forward(self, x): + out = self.fc1(x) + out = self.sigmoid(out) + out = self.fc2(out) + + return out + +device = "cpu" +epochs = 1 + +model = MyModel().to(device) +criterion = nn.MSELoss() +optimizer = optim.SGD(model.parameters(), lr=0.001) +loss_list = [] + +x_values = [[0., 1], [0., 1], [0., 1], [0., 1], [0., 1], [0., 1], [0., 1], [0., 1], [0., 1], [0., 1]] + +y_true = [] +for x in x_values: + y_true.append([math.pow(math.sin(x[0]), 2), math.pow(math.sin(x[1]), 2)]) + +batch_size = 5 + + +for epoch in range(epochs): + for x, target in zip(x_values, y_true): + x = norch.Tensor([x]) + target = norch.Tensor([target]) + + x = x.to(device) + target = target.to(device) + + outputs = model(x) + + loss = criterion(outputs, target) + + optimizer.zero_grad() + loss.backward() + optimizer.step() + + print('loss', loss, '\n\n') + print('weight', model.fc1.weight, '\n\n') + print('bias', model.fc1.bias, '\n\n') + + + if math.isnan(loss[0]): + print("Kkkkkkkkk") + exit() + + #print(f'Epoch [{epoch + 1}/{epochs}], Loss: {loss[0]:.4f}') + loss_list.append(loss[0]) +""" \ No newline at end of file From 13da54ca3967b4181c8e01683d34e893e5615b29 Mon Sep 17 00:00:00 2001 From: lucasdelimanogueira Date: Sat, 18 May 2024 08:54:03 -0300 Subject: [PATCH 10/28] fix add broadcasted access memory wrong values --- norch/__pycache__/tensor.cpython-38.pyc | Bin 15534 -> 15623 bytes norch/tensor.py | 8 ++++++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/norch/__pycache__/tensor.cpython-38.pyc b/norch/__pycache__/tensor.cpython-38.pyc index 45abd37a6e25c4ec09757980a929bbc399ecda78..6c93f168b96f7a34206c20121ae3073bdf07cbca 100644 GIT binary patch delta 1363 zcmY+COH5p46vywmk6~as1%}rE@~|>(hK@K;Q3^z*Hd-5^CdJgWwI2ib4loQe!#9_w zccks0KwU^Y#)S(58%-0bn(M-a#l*ygZrr$WHX0iriQT)Z@tiw|dXxM69_M`L|3BZo zw^ue+gD(YxRSG>Hyn8Nr{?p*O2A@W&{32d%XgfZoY$+Q`5==1Fb$>y1R7Xka4y5#= zdPUt*3Yw$IpYeX{(3Ecbi%P+8bjJ|EO@*l2M>mxs(5#wH)zDaDYQ$Q{Na<&lS3(7! zW9Uk%*72QIR7KRze?)nq+_zLeM0xb4Lh>iO&QTmFsats=W=lOaHCD4N}@o|JHYm3rbXfep2NdId}{jee~ARn}3hM>9aV} z_S`czT?bpEW(KaP1+AbvnxoHZ@|vTWsyFK8(bi0@2sdC;+0ku)EmL?$BwLg9gi`fk zupIi8O>;y6Kls%UG(w{mf-tQ@uzF(5%H*@MM+h7Zw;Ph%##Hz!?zXq!Z{ZVbGEO_C zz=&y{NleZzC8QaxkP+T9vi7tkrN#45#t&pewILL^L6 zh#zSue}$2J(^~tN8EEwYqlaXLdFg%YQ*rV6lQ}|cg zn4Y917QzvHwSA@a-~v1yFQ;54#W^fXt>qUcgct&xZEkbp9b^Ca&Tz_s??9F%IvHua zK3tFYJN>YN>DI^G;Rge%k0oUT=HhLbdT0-3Fx(Y^xABRt_22@9qUu4je72Grom)r? z@de)N8ihsN8EnEABbD$aUW!b@SMGk~Lybx#qYuN^?mN*a8`|%-A&tGkx=J#f%Pt9_ z;|nM6Q6wJk#$;?D@ETj_WRxA=iTNQ+IT81d*v)c@ZrtgaD3=Kbgs|m;WzH-L5u+a| zKR`$6v*LHLW_X({D`&=;kpElLScsi^m6Vsrm<5&{NGQVj zEatOVz+wT5`siu`!g{u1Z&*)1NQQ(W0kcZBW5i~Y#bP2lDKn{-G|M1mg|%T5$>A|+ z!gDr@P7zk6eGrNbVP`d7mSMD-RKKwhJxc{ zP#oUM@1l`ls5=;w#1Kgp1yrH_A#aL4s%Kx0wnD4&mjWB@DrnOPwfp_wsiV8l_=HW_gb)SVD`z3c4DA3_?;TQL3c6z_M5;v+Eb#X507$I!z#c>^c?*SG5lKZKwpyxKk#wWB>Y9wvGwU5Zqmmwz%j_d ze-))162~aVIL8FXO^!*9DUNB5TO7AJW;x~ Date: Sat, 18 May 2024 08:54:43 -0300 Subject: [PATCH 11/28] fix subtraction broadcasted access memory wrong values --- norch/tensor.py | 3 ++- test.py | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/norch/tensor.py b/norch/tensor.py index 4a896b3..6548e9a 100644 --- a/norch/tensor.py +++ b/norch/tensor.py @@ -316,6 +316,8 @@ class Tensor: if isinstance(other, (int, float)): other = other * self.ones_like() + broadcasted_shape = [] + # Function to determine if broadcasting is needed and get the broadcasted shape def broadcast_shape(shape1, shape2): if shape1 == shape2: @@ -325,7 +327,6 @@ class Tensor: shape1 = [1] * (max_len - len(shape1)) + shape1 shape2 = [1] * (max_len - len(shape2)) + shape2 - broadcasted_shape = [] for dim1, dim2 in zip(shape1, shape2): if dim1 != dim2 and dim1 != 1 and dim2 != 1: raise ValueError("Shapes are not compatible for broadcasting") diff --git a/test.py b/test.py index 92d779d..3e45c78 100644 --- a/test.py +++ b/test.py @@ -9,7 +9,7 @@ W2 = norch.Tensor([[1], [2], [3], [4], [5], [6], [7], [8], [9], [10]], requires_ B2 = norch.Tensor([[1, 2, 3, 4, 5]], requires_grad=True) -Z1 = W1 @ X + B1 +Z1 = W1 @ X - B1 # Perform matrix multiplication From acb6c17ccae627ed981f1a31451ac1d97ce4aaba Mon Sep 17 00:00:00 2001 From: lucasdelimanogueira Date: Sat, 18 May 2024 09:01:42 -0300 Subject: [PATCH 12/28] fix subtraction broadcasted access memory wrong values --- norch/__pycache__/tensor.cpython-38.pyc | Bin 15623 -> 15627 bytes norch/tensor.py | 27 ++++++++++++------------ test.py | 2 +- 3 files changed, 15 insertions(+), 14 deletions(-) diff --git a/norch/__pycache__/tensor.cpython-38.pyc b/norch/__pycache__/tensor.cpython-38.pyc index 6c93f168b96f7a34206c20121ae3073bdf07cbca..cc2bfbb01905a7a4280bde1fc7a2266214c3f8a8 100644 GIT binary patch delta 1281 zcmb7COKcNY6rDSsnS3S=#*Qa(LgMf-mE#1kgMg3}l0Yk{N*9fwl7YO~8IxcVJb9k* z2@_~VTiTRGq+1DgbyQVVBt)r3Ai60+LSoVKS+$ZEhy}1?SE*vheU6h_6_v_J@4Wlo z+{L&)n60$L4BQ72o15Q=Irqd=Oef$)Cml&P(rVT zTDclDs0AO+hdbOqL+t=5eA3>jq^Sf8dJx}jX;?E#2i{JwsFb+4DJ~KMx-sqz;HNDg z*K2fCXnAPm(UmZ6B45X~w_W-WemPxMQyU`iEneC1xp#^xqH?*>@_QQ=p7 zFY=+r1>PccNWz-+dX4ijE#_>cD~62e2gnU5x%f z40lEFLF_)g{d!9W-s#+-&%B{cD|ec)W;fjR(-P0+GDmK69c*$P!V*DRpZ!;HP23BI z+{XAP026L5Q7^-1)YCdFY*pZb`@_}|fbTJvh`I4?YXNSR!t>j&1N??vJ9Foj6-3P94X&7wU*@+%yg-Z4;|d;31S=R1z+tfm7S-v~h0E@j5rH zX=ws2t%{>O8D3-j>c4b*A;02$O}4Bu9|GM zr4ZIQT26gddM#Aa9Zi*THI9B*k|kC%b%WwU`ClE#g(!|zF2w(^YaPjf1)09=o>-#f zk!Y9&Y;9GyeQRL*a}~KTs}goKjK7ELPf`+fwQfzK3o7|RvfBmFp6ICrB3gp{tM^ka z0q1sPhs@CiBvZvx&EFtKPA5&RtQXFw^4Y|u?JoPqQ?qO=J5g4xk@19w-Rn@UN%P5+ zl}Q!sti`-5xVQdCXuzY55!9ll#=A&6?2I-+7N3vCA%nBgZqVINqk{m$%Z+h3hrvDZ z(0K~uZ3OEoj|qPSM;j;cMA+|phPH*5gy=06Sn{G&k{wz6IsK8kAP>nUrKCD4zeR%e zoziUof+Xa2bY?gTAvMQQs5$zkHae^Jw9z-T$$#Q0qf--fYQhiOP0+WRaN(g%pgacM zHrC|QgS=aPA$E!hK8&0C(tAHU_2q@_z7W5oQ(u&(lNp&b*m`tw#2TPgSi#Ze(-+Q3 zCB-I9#PHC#9U@KClIEz6HmdMNN3~?nY8%#8EYjl=ILfMOOCoAPUIUP(h8)FGhvfl@ zpP}4*h``kO>%wOO&I+&uqy(e|)DpCN$ajlo1a(j+tAK+oN7P~Z ziXYa;@Y|O5%{lq6#oXu3NjWf3Oh28#qt74RKC7dsBqc&|5t zH);Ppeih5WEw{1#V+BU=wz(gEa_^cZ)rEK4YO%8;1zs^Cdwz0;F%@rg{7t9VyY}Jj z_@TfXqJTC5J4Jhe^%NL$`}ben$rZtSo#~ByMu0J!7qiyLG-Gl1;(;m=cD*Ydc=N%8 zXe106bQ=>d1LUx>r^8+8z60KLhv$6NmqT zS^Ou_iaotqxPu?`mf&A(==<6n6(r*V@&cv=2*vn}fH?v40!mn!Y;Jv9xR(W75%3N{ d*`Mc=`7F2R_*FcZ?1v+GF?j&4;pfRS{{tB|a$^7h diff --git a/norch/tensor.py b/norch/tensor.py index 6548e9a..807c290 100644 --- a/norch/tensor.py +++ b/norch/tensor.py @@ -223,8 +223,8 @@ class Tensor: def __add__(self, other): if isinstance(other, (int, float)): other = other * self.ones_like() - - broadcasted_shape = [] + + broadcasted_shape_add = [] # Function to determine if broadcasting is needed and get the broadcasted shape def broadcast_shape(shape1, shape2): @@ -239,10 +239,10 @@ class Tensor: for dim1, dim2 in zip(shape1, shape2): if dim1 != dim2 and dim1 != 1 and dim2 != 1: raise ValueError("Shapes are not compatible for broadcasting") - broadcasted_shape.append(max(dim1, dim2)) - return broadcasted_shape, True + broadcasted_shape_add.append(max(dim1, dim2)) + return broadcasted_shape_add, True - broadcasted_shape, needs_broadcasting = broadcast_shape(self.shape, other.shape) + broadcasted_shape_add, needs_broadcasting = broadcast_shape(self.shape, other.shape) if needs_broadcasting: # Call add_broadcasted_tensor if broadcasting is needed @@ -253,8 +253,8 @@ class Tensor: result_data = Tensor() result_data.tensor = result_tensor_ptr - result_data.shape = broadcasted_shape - result_data.ndim = len(broadcasted_shape) + result_data.shape = broadcasted_shape_add.copy() + result_data.ndim = len(broadcasted_shape_add) result_data.device = self.device result_data.numel = 1 @@ -316,7 +316,7 @@ class Tensor: if isinstance(other, (int, float)): other = other * self.ones_like() - broadcasted_shape = [] + broadcasted_shape_sub = [] # Function to determine if broadcasting is needed and get the broadcasted shape def broadcast_shape(shape1, shape2): @@ -327,13 +327,14 @@ class Tensor: shape1 = [1] * (max_len - len(shape1)) + shape1 shape2 = [1] * (max_len - len(shape2)) + shape2 + for dim1, dim2 in zip(shape1, shape2): if dim1 != dim2 and dim1 != 1 and dim2 != 1: raise ValueError("Shapes are not compatible for broadcasting") - broadcasted_shape.append(max(dim1, dim2)) - return broadcasted_shape, True + broadcasted_shape_sub.append(max(dim1, dim2)) + return broadcasted_shape_sub, True - broadcasted_shape, needs_broadcasting = broadcast_shape(self.shape, other.shape) + broadcasted_shape_sub, needs_broadcasting = broadcast_shape(self.shape, other.shape) if needs_broadcasting: # Call add_broadcasted_tensor if broadcasting is needed @@ -344,8 +345,8 @@ class Tensor: result_data = Tensor() result_data.tensor = result_tensor_ptr - result_data.shape = broadcasted_shape - result_data.ndim = len(broadcasted_shape) + result_data.shape = broadcasted_shape_sub.copy() + result_data.ndim = len(broadcasted_shape_sub) result_data.device = self.device result_data.numel = self.numel # Update this to calculate the correct number of elements if broadcasting diff --git a/test.py b/test.py index 3e45c78..92d779d 100644 --- a/test.py +++ b/test.py @@ -9,7 +9,7 @@ W2 = norch.Tensor([[1], [2], [3], [4], [5], [6], [7], [8], [9], [10]], requires_ B2 = norch.Tensor([[1, 2, 3, 4, 5]], requires_grad=True) -Z1 = W1 @ X - B1 +Z1 = W1 @ X + B1 # Perform matrix multiplication From 418bb20b2966d37396112318550311a44e92847d Mon Sep 17 00:00:00 2001 From: lucasdelimanogueira Date: Sat, 18 May 2024 15:08:31 -0300 Subject: [PATCH 13/28] fix add broadcasted backward grad wrong values --- examples/train.ipynb | 2574 +++++++++++++---- .../__pycache__/functions.cpython-38.pyc | Bin 8163 -> 8173 bytes norch/autograd/functions.py | 2 +- .../modules/__pycache__/linear.cpython-38.pyc | Bin 1040 -> 1089 bytes norch/nn/modules/linear.py | 14 +- 5 files changed, 2054 insertions(+), 536 deletions(-) diff --git a/examples/train.ipynb b/examples/train.ipynb index 05e4e42..67d7c2d 100644 --- a/examples/train.ipynb +++ b/examples/train.ipynb @@ -26,523 +26,2003 @@ }, { "cell_type": "code", - "execution_count": 16, + "execution_count": 11, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "tensor([3.3399136066436768,], device=\"cpu\", requires_grad=True)\n", - "tensor([inf,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n", - "tensor([nan,], device=\"cpu\", requires_grad=True)\n" + "loss_antes tensor([3.3399136066436768,], device=\"cpu\", requires_grad=True)\n", + "f1 antes tensor([[0.6715301871299744,],\n", + "[-0.13446585834026337,],\n", + "[0.5245601534843445,],\n", + "[-0.9957879185676575,],\n", + "[-0.10922560840845108,],\n", + "[0.4430800676345825,],\n", + "[-0.5424755811691284,],\n", + "[0.8905413746833801,],\n", + "[0.8028548955917358,],\n", + "[-0.9388200044631958,]], device=\"cpu\", requires_grad=True)\n", + "f1 grad_antes tensor([[0.7763990759849548,],\n", + "[-0.07534197717905045,],\n", + "[-0.7497915625572205,],\n", + "[0.1710733026266098,],\n", + "[-0.05041608214378357,],\n", + "[-0.020194964483380318,],\n", + "[-0.14156067371368408,],\n", + "[-0.1368660032749176,],\n", + "[-0.027204060927033424,],\n", + "[-0.0021885898895561695,]], device=\"cpu\", requires_grad=True)\n", + "f2 antes tensor([[-0.5338311195373535,]], device=\"cpu\", requires_grad=True)\n", + "f2 grad_antes tensor([[-3.655086040496826,]], device=\"cpu\", requires_grad=True)\n", + "\n", + "\n", + "f1 depois tensor([[0.6707537770271301,],\n", + "[-0.13439051806926727,],\n", + "[0.5253099203109741,],\n", + "[-0.9959589838981628,],\n", + "[-0.1091751903295517,],\n", + "[0.4431002736091614,],\n", + "[-0.5423340201377869,],\n", + "[0.8906782269477844,],\n", + "[0.8028820753097534,],\n", + "[-0.9388177990913391,]], device=\"cpu\", requires_grad=True)\n", + "f2 depois tensor([[-0.530176043510437,]], device=\"cpu\", requires_grad=True)\n", + "\n", + "\n", + "\n", + "loss_antes tensor([3.549180507659912,], device=\"cpu\", requires_grad=True)\n", + "f1 antes tensor([[0.6707537770271301,],\n", + "[-0.13439051806926727,],\n", + "[0.5253099203109741,],\n", + "[-0.9959589838981628,],\n", + "[-0.1091751903295517,],\n", + "[0.4431002736091614,],\n", + "[-0.5423340201377869,],\n", + "[0.8906782269477844,],\n", + "[0.8028820753097534,],\n", + "[-0.9388177990913391,]], device=\"cpu\", requires_grad=True)\n", + "f1 grad_antes tensor([[0.8605965375900269,],\n", + "[-0.0792151615023613,],\n", + "[-0.7264857888221741,],\n", + "[0.1592811942100525,],\n", + "[-0.034485332667827606,],\n", + "[-0.014619439840316772,],\n", + "[-0.09272521734237671,],\n", + "[-0.16714394092559814,],\n", + "[-0.01336900144815445,],\n", + "[-0.0027314303442835808,]], device=\"cpu\", requires_grad=True)\n", + "f2 antes tensor([[-0.530176043510437,]], device=\"cpu\", requires_grad=True)\n", + "f2 grad_antes tensor([[-3.7678537368774414,]], device=\"cpu\", requires_grad=True)\n", + "\n", + "\n", + "f1 depois tensor([[0.669893205165863,],\n", + "[-0.13431130349636078,],\n", + "[0.5260363817214966,],\n", + "[-0.9961182475090027,],\n", + "[-0.10914070159196854,],\n", + "[0.44311490654945374,],\n", + "[-0.5422412753105164,],\n", + "[0.8908453583717346,],\n", + "[0.8028954267501831,],\n", + "[-0.9388150572776794,]], device=\"cpu\", requires_grad=True)\n", + "f2 depois tensor([[-0.5264081954956055,]], device=\"cpu\", requires_grad=True)\n", + "\n", + "\n", + "\n", + "loss_antes tensor([4.6171770095825195,], device=\"cpu\", requires_grad=True)\n", + "f1 antes tensor([[0.669893205165863,],\n", + "[-0.13431130349636078,],\n", + "[0.5260363817214966,],\n", + "[-0.9961182475090027,],\n", + "[-0.10914070159196854,],\n", + "[0.44311490654945374,],\n", + "[-0.5422412753105164,],\n", + "[0.8908453583717346,],\n", + "[0.8028954267501831,],\n", + "[-0.9388150572776794,]], device=\"cpu\", requires_grad=True)\n", + "f1 grad_antes tensor([[1.01288902759552,],\n", + "[-0.08897353708744049,],\n", + "[-0.7638412714004517,],\n", + "[0.16191577911376953,],\n", + "[0.039845600724220276,],\n", + "[0.006034668534994125,],\n", + "[0.047490328550338745,],\n", + "[-0.1645362675189972,],\n", + "[0.008131217211484909,],\n", + "[-0.002857489977031946,]], device=\"cpu\", requires_grad=True)\n", + "f2 antes tensor([[-0.5264081954956055,]], device=\"cpu\", requires_grad=True)\n", + "f2 grad_antes tensor([[-4.297523498535156,]], device=\"cpu\", requires_grad=True)\n", + "\n", + "\n", + "f1 depois tensor([[0.6688803434371948,],\n", + "[-0.13422232866287231,],\n", + "[0.5268002152442932,],\n", + "[-0.9962801337242126,],\n", + "[-0.10918054729700089,],\n", + "[0.4431088864803314,],\n", + "[-0.5422887802124023,],\n", + "[0.8910098671913147,],\n", + "[0.8028873205184937,],\n", + "[-0.9388121962547302,]], device=\"cpu\", requires_grad=True)\n", + "f2 depois tensor([[-0.5221107006072998,]], device=\"cpu\", requires_grad=True)\n", + "\n", + "\n", + "\n", + "loss_antes tensor([5.7759575843811035,], device=\"cpu\", requires_grad=True)\n", + "f1 antes tensor([[0.6688803434371948,],\n", + "[-0.13422232866287231,],\n", + "[0.5268002152442932,],\n", + "[-0.9962801337242126,],\n", + "[-0.10918054729700089,],\n", + "[0.4431088864803314,],\n", + "[-0.5422887802124023,],\n", + "[0.8910098671913147,],\n", + "[0.8028873205184937,],\n", + "[-0.9388121962547302,]], device=\"cpu\", requires_grad=True)\n", + "f1 grad_antes tensor([[1.119826316833496,],\n", + "[-0.09500840306282043,],\n", + "[-0.7736542820930481,],\n", + "[0.15942737460136414,],\n", + "[0.11072525382041931,],\n", + "[0.025451697409152985,],\n", + "[0.17995193600654602,],\n", + "[-0.17571325600147247,],\n", + "[0.022571314126253128,],\n", + "[-0.003071666695177555,]], device=\"cpu\", requires_grad=True)\n", + "f2 antes tensor([[-0.5221107006072998,]], device=\"cpu\", requires_grad=True)\n", + "f2 grad_antes tensor([[-4.806644439697266,]], device=\"cpu\", requires_grad=True)\n", + "\n", + "\n", + "f1 depois tensor([[0.6677604913711548,],\n", + "[-0.13412731885910034,],\n", + "[0.5275738835334778,],\n", + "[-0.9964395761489868,],\n", + "[-0.10929127037525177,],\n", + "[0.44308343529701233,],\n", + "[-0.5424687266349792,],\n", + "[0.8911855816841125,],\n", + "[0.8028647303581238,],\n", + "[-0.9388090968132019,]], device=\"cpu\", requires_grad=True)\n", + "f2 depois tensor([[-0.5173040628433228,]], device=\"cpu\", requires_grad=True)\n", + "\n", + "\n", + "\n", + "loss_antes tensor([5.934185981750488,], device=\"cpu\", requires_grad=True)\n", + "f1 antes tensor([[0.6677604913711548,],\n", + "[-0.13412731885910034,],\n", + "[0.5275738835334778,],\n", + "[-0.9964395761489868,],\n", + "[-0.10929127037525177,],\n", + "[0.44308343529701233,],\n", + "[-0.5424687266349792,],\n", + "[0.8911855816841125,],\n", + "[0.8028647303581238,],\n", + "[-0.9388090968132019,]], device=\"cpu\", requires_grad=True)\n", + "f1 grad_antes tensor([[1.074658751487732,],\n", + "[-0.08936237543821335,],\n", + "[-0.6985772252082825,],\n", + "[0.140721395611763,],\n", + "[0.11916199326515198,],\n", + "[0.02836146578192711,],\n", + "[0.20278266072273254,],\n", + "[-0.22442427277565002,],\n", + "[0.016736526042222977,],\n", + "[-0.0034379507414996624,]], device=\"cpu\", requires_grad=True)\n", + "f2 antes tensor([[-0.5173040628433228,]], device=\"cpu\", requires_grad=True)\n", + "f2 grad_antes tensor([[-4.872036933898926,]], device=\"cpu\", requires_grad=True)\n", + "\n", + "\n", + "f1 depois tensor([[0.6666858196258545,],\n", + "[-0.13403795659542084,],\n", + "[0.5282724499702454,],\n", + "[-0.9965803027153015,],\n", + "[-0.10941043496131897,],\n", + "[0.44305506348609924,],\n", + "[-0.5426715016365051,],\n", + "[0.8914099931716919,],\n", + "[0.8028479814529419,],\n", + "[-0.9388056397438049,]], device=\"cpu\", requires_grad=True)\n", + "f2 depois tensor([[-0.5124320387840271,]], device=\"cpu\", requires_grad=True)\n", + "\n", + "\n", + "\n", + "loss_antes tensor([4.719274044036865,], device=\"cpu\", requires_grad=True)\n", + "f1 antes tensor([[0.6666858196258545,],\n", + "[-0.13403795659542084,],\n", + "[0.5282724499702454,],\n", + "[-0.9965803027153015,],\n", + "[-0.10941043496131897,],\n", + "[0.44305506348609924,],\n", + "[-0.5426715016365051,],\n", + "[0.8914099931716919,],\n", + "[0.8028479814529419,],\n", + "[-0.9388056397438049,]], device=\"cpu\", requires_grad=True)\n", + "f1 grad_antes tensor([[0.8705567717552185,],\n", + "[-0.07186676561832428,],\n", + "[-0.5466199517250061,],\n", + "[0.10827980190515518,],\n", + "[0.04532705247402191,],\n", + "[0.010129459202289581,],\n", + "[0.07962468266487122,],\n", + "[-0.30150455236434937,],\n", + "[-0.008896466344594955,],\n", + "[-0.0038197385147213936,]], device=\"cpu\", requires_grad=True)\n", + "f2 antes tensor([[-0.5124320387840271,]], device=\"cpu\", requires_grad=True)\n", + "f2 grad_antes tensor([[-4.344778060913086,]], device=\"cpu\", requires_grad=True)\n", + "\n", + "\n", + "f1 depois tensor([[0.6658152341842651,],\n", + "[-0.1339660882949829,],\n", + "[0.5288190841674805,],\n", + "[-0.9966886043548584,],\n", + "[-0.10945576429367065,],\n", + "[0.4430449306964874,],\n", + "[-0.542751133441925,],\n", + "[0.8917114734649658,],\n", + "[0.8028568625450134,],\n", + "[-0.9388018250465393,]], device=\"cpu\", requires_grad=True)\n", + "f2 depois tensor([[-0.5080872774124146,]], device=\"cpu\", requires_grad=True)\n", + "\n", + "\n", + "\n", + "loss_antes tensor([2.9647133350372314,], device=\"cpu\", requires_grad=True)\n", + "f1 antes tensor([[0.6658152341842651,],\n", + "[-0.1339660882949829,],\n", + "[0.5288190841674805,],\n", + "[-0.9966886043548584,],\n", + "[-0.10945576429367065,],\n", + "[0.4430449306964874,],\n", + "[-0.542751133441925,],\n", + "[0.8917114734649658,],\n", + "[0.8028568625450134,],\n", + "[-0.9388018250465393,]], device=\"cpu\", requires_grad=True)\n", + "f1 grad_antes tensor([[0.6034455895423889,],\n", + "[-0.0498625822365284,],\n", + "[-0.3749368190765381,],\n", + "[0.07348791509866714,],\n", + "[-0.07949754595756531,],\n", + "[-0.020759649574756622,],\n", + "[-0.1351551115512848,],\n", + "[-0.3795509338378906,],\n", + "[-0.04138007014989853,],\n", + "[-0.004105208907276392,]], device=\"cpu\", requires_grad=True)\n", + "f2 antes tensor([[-0.5080872774124146,]], device=\"cpu\", requires_grad=True)\n", + "f2 grad_antes tensor([[-3.443668842315674,]], device=\"cpu\", requires_grad=True)\n", + "\n", + "\n", + "f1 depois tensor([[0.6652117967605591,],\n", + "[-0.1339162290096283,],\n", + "[0.5291939973831177,],\n", + "[-0.9967620968818665,],\n", + "[-0.10937626659870148,],\n", + "[0.44306570291519165,],\n", + "[-0.5426159501075745,],\n", + "[0.8920910358428955,],\n", + "[0.8028982281684875,],\n", + "[-0.9387977123260498,]], device=\"cpu\", requires_grad=True)\n", + "f2 depois tensor([[-0.5046436190605164,]], device=\"cpu\", requires_grad=True)\n", + "\n", + "\n", + "\n", + "loss_antes tensor([1.7221571207046509,], device=\"cpu\", requires_grad=True)\n", + "f1 antes tensor([[0.6652117967605591,],\n", + "[-0.1339162290096283,],\n", + "[0.5291939973831177,],\n", + "[-0.9967620968818665,],\n", + "[-0.10937626659870148,],\n", + "[0.44306570291519165,],\n", + "[-0.5426159501075745,],\n", + "[0.8920910358428955,],\n", + "[0.8028982281684875,],\n", + "[-0.9387977123260498,]], device=\"cpu\", requires_grad=True)\n", + "f1 grad_antes tensor([[0.38911673426628113,],\n", + "[-0.0323101170361042,],\n", + "[-0.24431711435317993,],\n", + "[0.047654714435338974,],\n", + "[-0.19218149781227112,],\n", + "[-0.048304274678230286,],\n", + "[-0.33135807514190674,],\n", + "[-0.4348369240760803,],\n", + "[-0.06675009429454803,],\n", + "[-0.0042629726231098175,]], device=\"cpu\", requires_grad=True)\n", + "f2 antes tensor([[-0.5046436190605164,]], device=\"cpu\", requires_grad=True)\n", + "f2 grad_antes tensor([[-2.624619960784912,]], device=\"cpu\", requires_grad=True)\n", + "\n", + "\n", + "f1 depois tensor([[0.6648226976394653,],\n", + "[-0.13388392329216003,],\n", + "[0.529438316822052,],\n", + "[-0.9968097805976868,],\n", + "[-0.10918408632278442,],\n", + "[0.4431140124797821,],\n", + "[-0.5422846078872681,],\n", + "[0.892525851726532,],\n", + "[0.802964985370636,],\n", + "[-0.938793420791626,]], device=\"cpu\", requires_grad=True)\n", + "f2 depois tensor([[-0.5020189881324768,]], device=\"cpu\", requires_grad=True)\n", + "\n", + "\n", + "\n", + "loss_antes tensor([1.3282623291015625,], device=\"cpu\", requires_grad=True)\n", + "f1 antes tensor([[0.6648226976394653,],\n", + "[-0.13388392329216003,],\n", + "[0.529438316822052,],\n", + "[-0.9968097805976868,],\n", + "[-0.10918408632278442,],\n", + "[0.4431140124797821,],\n", + "[-0.5422846078872681,],\n", + "[0.892525851726532,],\n", + "[0.802964985370636,],\n", + "[-0.938793420791626,]], device=\"cpu\", requires_grad=True)\n", + "f1 grad_antes tensor([[0.2811588644981384,],\n", + "[-0.02352893352508545,],\n", + "[-0.18157480657100677,],\n", + "[0.03541348874568939,],\n", + "[-0.23582345247268677,],\n", + "[-0.05850478261709213,],\n", + "[-0.41329389810562134,],\n", + "[-0.46008923649787903,],\n", + "[-0.07939890772104263,],\n", + "[-0.004317682236433029,]], device=\"cpu\", requires_grad=True)\n", + "f2 antes tensor([[-0.5020189881324768,]], device=\"cpu\", requires_grad=True)\n", + "f2 grad_antes tensor([[-2.3050053119659424,]], device=\"cpu\", requires_grad=True)\n", + "\n", + "\n", + "f1 depois tensor([[0.6645415425300598,],\n", + "[-0.13386039435863495,],\n", + "[0.5296198725700378,],\n", + "[-0.9968451857566833,],\n", + "[-0.10894826054573059,],\n", + "[0.44317251443862915,],\n", + "[-0.5418713092803955,],\n", + "[0.8929859399795532,],\n", + "[0.8030443787574768,],\n", + "[-0.9387891292572021,]], device=\"cpu\", requires_grad=True)\n", + "f2 depois tensor([[-0.4997139871120453,]], device=\"cpu\", requires_grad=True)\n", + "\n", + "\n", + "\n", + "loss_antes tensor([1.7036319971084595,], device=\"cpu\", requires_grad=True)\n", + "f1 antes tensor([[0.6645415425300598,],\n", + "[-0.13386039435863495,],\n", + "[0.5296198725700378,],\n", + "[-0.9968451857566833,],\n", + "[-0.10894826054573059,],\n", + "[0.44317251443862915,],\n", + "[-0.5418713092803955,],\n", + "[0.8929859399795532,],\n", + "[0.8030443787574768,],\n", + "[-0.9387891292572021,]], device=\"cpu\", requires_grad=True)\n", + "f1 grad_antes tensor([[0.2559458911418915,],\n", + "[-0.021709883585572243,],\n", + "[-0.17255055904388428,],\n", + "[0.033771101385354996,],\n", + "[-0.19383782148361206,],\n", + "[-0.04744270443916321,],\n", + "[-0.35895103216171265,],\n", + "[-0.46357548236846924,],\n", + "[-0.08267522603273392,],\n", + "[-0.00431686220690608,]], device=\"cpu\", requires_grad=True)\n", + "f2 antes tensor([[-0.4997139871120453,]], device=\"cpu\", requires_grad=True)\n", + "f2 grad_antes tensor([[-2.6104650497436523,]], device=\"cpu\", requires_grad=True)\n", + "\n", + "\n", + "f1 depois tensor([[0.6642856001853943,],\n", + "[-0.1338386833667755,],\n", + "[0.5297924280166626,],\n", + "[-0.996878981590271,],\n", + "[-0.10875442624092102,],\n", + "[0.44321995973587036,],\n", + "[-0.5415123701095581,],\n", + "[0.8934495449066162,],\n", + "[0.8031270503997803,],\n", + "[-0.9387848377227783,]], device=\"cpu\", requires_grad=True)\n", + "f2 depois tensor([[-0.4971035122871399,]], device=\"cpu\", requires_grad=True)\n", + "\n", + "\n", + "\n", + "loss_antes tensor([2.7193281650543213,], device=\"cpu\", requires_grad=True)\n", + "f1 antes tensor([[0.6642856001853943,],\n", + "[-0.1338386833667755,],\n", + "[0.5297924280166626,],\n", + "[-0.996878981590271,],\n", + "[-0.10875442624092102,],\n", + "[0.44321995973587036,],\n", + "[-0.5415123701095581,],\n", + "[0.8934495449066162,],\n", + "[0.8031270503997803,],\n", + "[-0.9387848377227783,]], device=\"cpu\", requires_grad=True)\n", + "f1 grad_antes tensor([[0.2549161911010742,],\n", + "[-0.022135019302368164,],\n", + "[-0.1816510409116745,],\n", + "[0.03576912730932236,],\n", + "[-0.10002899914979935,],\n", + "[-0.023694587871432304,],\n", + "[-0.230293869972229,],\n", + "[-0.45979392528533936,],\n", + "[-0.08331713825464249,],\n", + "[-0.004302922170609236,]], device=\"cpu\", requires_grad=True)\n", + "f2 antes tensor([[-0.4971035122871399,]], device=\"cpu\", requires_grad=True)\n", + "f2 grad_antes tensor([[-3.298077344894409,]], device=\"cpu\", requires_grad=True)\n", + "\n", + "\n", + "f1 depois tensor([[0.6640306711196899,],\n", + "[-0.13381655514240265,],\n", + "[0.529974102973938,],\n", + "[-0.9969147443771362,],\n", + "[-0.10865439474582672,],\n", + "[0.4432436525821686,],\n", + "[-0.541282057762146,],\n", + "[0.8939093351364136,],\n", + "[0.8032103776931763,],\n", + "[-0.9387805461883545,]], device=\"cpu\", requires_grad=True)\n", + "f2 depois tensor([[-0.4938054382801056,]], device=\"cpu\", requires_grad=True)\n", + "\n", + "\n", + "\n", + "loss_antes tensor([3.807875871658325,], device=\"cpu\", requires_grad=True)\n", + "f1 antes tensor([[0.6640306711196899,],\n", + "[-0.13381655514240265,],\n", + "[0.529974102973938,],\n", + "[-0.9969147443771362,],\n", + "[-0.10865439474582672,],\n", + "[0.4432436525821686,],\n", + "[-0.541282057762146,],\n", + "[0.8939093351364136,],\n", + "[0.8032103776931763,],\n", + "[-0.9387805461883545,]], device=\"cpu\", requires_grad=True)\n", + "f1 grad_antes tensor([[0.23403728008270264,],\n", + "[-0.021054290235042572,],\n", + "[-0.1780746877193451,],\n", + "[0.03534826263785362,],\n", + "[-0.018274232745170593,],\n", + "[-0.0032432936131954193,],\n", + "[-0.12982642650604248,],\n", + "[-0.45986711978912354,],\n", + "[-0.08593392372131348,],\n", + "[-0.004297991748899221,]], device=\"cpu\", requires_grad=True)\n", + "f2 antes tensor([[-0.4938054382801056,]], device=\"cpu\", requires_grad=True)\n", + "f2 grad_antes tensor([[-3.9027557373046875,]], device=\"cpu\", requires_grad=True)\n", + "\n", + "\n", + "f1 depois tensor([[0.6637966632843018,],\n", + "[-0.13379549980163574,],\n", + "[0.5301522016525269,],\n", + "[-0.996950089931488,],\n", + "[-0.10863611847162247,],\n", + "[0.44324690103530884,],\n", + "[-0.5411522388458252,],\n", + "[0.8943691849708557,],\n", + "[0.8032963275909424,],\n", + "[-0.9387762546539307,]], device=\"cpu\", requires_grad=True)\n", + "f2 depois tensor([[-0.48990267515182495,]], device=\"cpu\", requires_grad=True)\n", + "\n", + "\n", + "\n", + "loss_antes tensor([4.036541938781738,], device=\"cpu\", requires_grad=True)\n", + "f1 antes tensor([[0.6637966632843018,],\n", + "[-0.13379549980163574,],\n", + "[0.5301522016525269,],\n", + "[-0.996950089931488,],\n", + "[-0.10863611847162247,],\n", + "[0.44324690103530884,],\n", + "[-0.5411522388458252,],\n", + "[0.8943691849708557,],\n", + "[0.8032963275909424,],\n", + "[-0.9387762546539307,]], device=\"cpu\", requires_grad=True)\n", + "f1 grad_antes tensor([[0.18463999032974243,],\n", + "[-0.017372921109199524,],\n", + "[-0.1511504054069519,],\n", + "[0.030296552926301956,],\n", + "[-0.0036210864782333374,],\n", + "[0.0004928670823574066,],\n", + "[-0.1348465234041214,],\n", + "[-0.46710795164108276,],\n", + "[-0.09121057391166687,],\n", + "[-0.004304955247789621,]], device=\"cpu\", requires_grad=True)\n", + "f2 antes tensor([[-0.48990267515182495,]], device=\"cpu\", requires_grad=True)\n", + "f2 grad_antes tensor([[-4.0182294845581055,]], device=\"cpu\", requires_grad=True)\n", + "\n", + "\n", + "f1 depois tensor([[0.6636120080947876,],\n", + "[-0.13377812504768372,],\n", + "[0.5303033590316772,],\n", + "[-0.9969803690910339,],\n", + "[-0.10863249748945236,],\n", + "[0.44324639439582825,],\n", + "[-0.5410174131393433,],\n", + "[0.8948363065719604,],\n", + "[0.8033875226974487,],\n", + "[-0.9387719631195068,]], device=\"cpu\", requires_grad=True)\n", + "f2 depois tensor([[-0.4858844578266144,]], device=\"cpu\", requires_grad=True)\n", + "\n", + "\n", + "\n", + "loss_antes tensor([3.136807918548584,], device=\"cpu\", requires_grad=True)\n", + "f1 antes tensor([[0.6636120080947876,],\n", + "[-0.13377812504768372,],\n", + "[0.5303033590316772,],\n", + "[-0.9969803690910339,],\n", + "[-0.10863249748945236,],\n", + "[0.44324639439582825,],\n", + "[-0.5410174131393433,],\n", + "[0.8948363065719604,],\n", + "[0.8033875226974487,],\n", + "[-0.9387719631195068,]], device=\"cpu\", requires_grad=True)\n", + "f1 grad_antes tensor([[0.12360509485006332,],\n", + "[-0.012205632403492928,],\n", + "[-0.10940354317426682,],\n", + "[0.022177211940288544,],\n", + "[-0.06886429339647293,],\n", + "[-0.015093699097633362,],\n", + "[-0.25298362970352173,],\n", + "[-0.47847220301628113,],\n", + "[-0.09734726697206497,],\n", + "[-0.004317260347306728,]], device=\"cpu\", requires_grad=True)\n", + "f2 antes tensor([[-0.4858844578266144,]], device=\"cpu\", requires_grad=True)\n", + "f2 grad_antes tensor([[-3.5422072410583496,]], device=\"cpu\", requires_grad=True)\n", + "\n", + "\n", + "f1 depois tensor([[0.6634883880615234,],\n", + "[-0.13376592099666595,],\n", + "[0.5304127335548401,],\n", + "[-0.9970025420188904,],\n", + "[-0.108563631772995,],\n", + "[0.4432614743709564,],\n", + "[-0.5407644510269165,],\n", + "[0.8953147530555725,],\n", + "[0.8034848570823669,],\n", + "[-0.938767671585083,]], device=\"cpu\", requires_grad=True)\n", + "f2 depois tensor([[-0.4823422431945801,]], device=\"cpu\", requires_grad=True)\n", + "\n", + "\n", + "\n", + "loss_antes tensor([1.8728151321411133,], device=\"cpu\", requires_grad=True)\n", + "f1 antes tensor([[0.6634883880615234,],\n", + "[-0.13376592099666595,],\n", + "[0.5304127335548401,],\n", + "[-0.9970025420188904,],\n", + "[-0.108563631772995,],\n", + "[0.4432614743709564,],\n", + "[-0.5407644510269165,],\n", + "[0.8953147530555725,],\n", + "[0.8034848570823669,],\n", + "[-0.938767671585083,]], device=\"cpu\", requires_grad=True)\n", + "f1 grad_antes tensor([[0.07208667695522308,],\n", + "[-0.007449393626302481,],\n", + "[-0.06917522847652435,],\n", + "[0.014202376827597618,],\n", + "[-0.1775367110967636,],\n", + "[-0.04076655954122543,],\n", + "[-0.42342108488082886,],\n", + "[-0.48892438411712646,],\n", + "[-0.1023271381855011,],\n", + "[-0.004327966365963221,]], device=\"cpu\", requires_grad=True)\n", + "f2 antes tensor([[-0.4823422431945801,]], device=\"cpu\", requires_grad=True)\n", + "f2 grad_antes tensor([[-2.7370166778564453,]], device=\"cpu\", requires_grad=True)\n", + "\n", + "\n", + "f1 depois tensor([[0.66341632604599,],\n", + "[-0.13375847041606903,],\n", + "[0.5304819345474243,],\n", + "[-0.9970167279243469,],\n", + "[-0.1083860918879509,],\n", + "[0.4433022439479828,],\n", + "[-0.5403410196304321,],\n", + "[0.895803689956665,],\n", + "[0.8035871982574463,],\n", + "[-0.9387633204460144,]], device=\"cpu\", requires_grad=True)\n", + "f2 depois tensor([[-0.47960522770881653,]], device=\"cpu\", requires_grad=True)\n", + "\n", + "\n", + "\n", + "loss_antes tensor([1.0701744556427002,], device=\"cpu\", requires_grad=True)\n", + "f1 antes tensor([[0.66341632604599,],\n", + "[-0.13375847041606903,],\n", + "[0.5304819345474243,],\n", + "[-0.9970167279243469,],\n", + "[-0.1083860918879509,],\n", + "[0.4433022439479828,],\n", + "[-0.5403410196304321,],\n", + "[0.895803689956665,],\n", + "[0.8035871982574463,],\n", + "[-0.9387633204460144,]], device=\"cpu\", requires_grad=True)\n", + "f1 grad_antes tensor([[0.04095809906721115,],\n", + "[-0.004402738530188799,],\n", + "[-0.04267047345638275,],\n", + "[0.008884895592927933,],\n", + "[-0.26698610186576843,],\n", + "[-0.061598122119903564,],\n", + "[-0.5584473609924316,],\n", + "[-0.4951053261756897,],\n", + "[-0.10524972528219223,],\n", + "[-0.00433389050886035,]], device=\"cpu\", requires_grad=True)\n", + "f2 antes tensor([[-0.47960522770881653,]], device=\"cpu\", requires_grad=True)\n", + "f2 grad_antes tensor([[-2.0689847469329834,]], device=\"cpu\", requires_grad=True)\n", + "\n", + "\n", + "f1 depois tensor([[0.6633753776550293,],\n", + "[-0.13375407457351685,],\n", + "[0.5305246114730835,],\n", + "[-0.9970256090164185,],\n", + "[-0.10811910778284073,],\n", + "[0.44336384534835815,],\n", + "[-0.5397825837135315,],\n", + "[0.8962987661361694,],\n", + "[0.8036924600601196,],\n", + "[-0.9387589693069458,]], device=\"cpu\", requires_grad=True)\n", + "f2 depois tensor([[-0.47753623127937317,]], device=\"cpu\", requires_grad=True)\n", + "\n", + "\n", + "\n", + "loss_antes tensor([0.9255756139755249,], device=\"cpu\", requires_grad=True)\n", + "f1 antes tensor([[0.6633753776550293,],\n", + "[-0.13375407457351685,],\n", + "[0.5305246114730835,],\n", + "[-0.9970256090164185,],\n", + "[-0.10811910778284073,],\n", + "[0.44336384534835815,],\n", + "[-0.5397825837135315,],\n", + "[0.8962987661361694,],\n", + "[0.8036924600601196,],\n", + "[-0.9387589693069458,]], device=\"cpu\", requires_grad=True)\n", + "f1 grad_antes tensor([[0.028540857136249542,],\n", + "[-0.0031756071839481592,],\n", + "[-0.03230834752321243,],\n", + "[0.006829733494669199,],\n", + "[-0.28655558824539185,],\n", + "[-0.06592711061239243,],\n", + "[-0.598193347454071,],\n", + "[-0.49654722213745117,],\n", + "[-0.10640368610620499,],\n", + "[-0.004335499834269285,]], device=\"cpu\", requires_grad=True)\n", + "f2 antes tensor([[-0.47753623127937317,]], device=\"cpu\", requires_grad=True)\n", + "f2 grad_antes tensor([[-1.9241368770599365,]], device=\"cpu\", requires_grad=True)\n", + "\n", + "\n", + "f1 depois tensor([[0.6633468270301819,],\n", + "[-0.13375090062618256,],\n", + "[0.5305569171905518,],\n", + "[-0.9970324635505676,],\n", + "[-0.10783255100250244,],\n", + "[0.44342976808547974,],\n", + "[-0.5391843914985657,],\n", + "[0.8967953324317932,],\n", + "[0.8037988543510437,],\n", + "[-0.9387546181678772,]], device=\"cpu\", requires_grad=True)\n", + "f2 depois tensor([[-0.4756121039390564,]], device=\"cpu\", requires_grad=True)\n", + "\n", + "\n", + "\n", + "loss_antes tensor([1.4076058864593506,], device=\"cpu\", requires_grad=True)\n", + "f1 antes tensor([[0.6633468270301819,],\n", + "[-0.13375090062618256,],\n", + "[0.5305569171905518,],\n", + "[-0.9970324635505676,],\n", + "[-0.10783255100250244,],\n", + "[0.44342976808547974,],\n", + "[-0.5391843914985657,],\n", + "[0.8967953324317932,],\n", + "[0.8037988543510437,],\n", + "[-0.9387546181678772,]], device=\"cpu\", requires_grad=True)\n", + "f1 grad_antes tensor([[0.026303380727767944,],\n", + "[-0.003027313854545355,],\n", + "[-0.032382525503635406,],\n", + "[0.006953757721930742,],\n", + "[-0.22800634801387787,],\n", + "[-0.05212089419364929,],\n", + "[-0.544280469417572,],\n", + "[-0.4948328137397766,],\n", + "[-0.10663937777280807,],\n", + "[-0.0043349056504666805,]], device=\"cpu\", requires_grad=True)\n", + "f2 antes tensor([[-0.4756121039390564,]], device=\"cpu\", requires_grad=True)\n", + "f2 grad_antes tensor([[-2.3728513717651367,]], device=\"cpu\", requires_grad=True)\n", + "\n", + "\n", + "f1 depois tensor([[0.6633205413818359,],\n", + "[-0.1337478756904602,],\n", + "[0.5305892825126648,],\n", + "[-0.9970394372940063,],\n", + "[-0.10760454833507538,],\n", + "[0.4434818923473358,],\n", + "[-0.5386400818824768,],\n", + "[0.8972901701927185,],\n", + "[0.8039054870605469,],\n", + "[-0.9387502670288086,]], device=\"cpu\", requires_grad=True)\n", + "f2 depois tensor([[-0.4732392430305481,]], device=\"cpu\", requires_grad=True)\n", + "\n", + "\n", + "\n", + "loss_antes tensor([2.4435479640960693,], device=\"cpu\", requires_grad=True)\n", + "f1 antes tensor([[0.6633205413818359,],\n", + "[-0.1337478756904602,],\n", + "[0.5305892825126648,],\n", + "[-0.9970394372940063,],\n", + "[-0.10760454833507538,],\n", + "[0.4434818923473358,],\n", + "[-0.5386400818824768,],\n", + "[0.8972901701927185,],\n", + "[0.8039054870605469,],\n", + "[-0.9387502670288086,]], device=\"cpu\", requires_grad=True)\n", + "f1 grad_antes tensor([[0.02584111876785755,],\n", + "[-0.0030884500592947006,],\n", + "[-0.03463677689433098,],\n", + "[0.007556402590125799,],\n", + "[-0.13057145476341248,],\n", + "[-0.02953137457370758,],\n", + "[-0.452243834733963,],\n", + "[-0.49202901124954224,],\n", + "[-0.10671739280223846,],\n", + "[-0.004334022756665945,]], device=\"cpu\", requires_grad=True)\n", + "f2 antes tensor([[-0.4732392430305481,]], device=\"cpu\", requires_grad=True)\n", + "f2 grad_antes tensor([[-3.126370668411255,]], device=\"cpu\", requires_grad=True)\n", + "\n", + "\n", + "f1 depois tensor([[0.6632946729660034,],\n", + "[-0.13374479115009308,],\n", + "[0.5306239128112793,],\n", + "[-0.9970470070838928,],\n", + "[-0.10747397691011429,],\n", + "[0.443511426448822,],\n", + "[-0.5381878614425659,],\n", + "[0.8977822065353394,],\n", + "[0.8040121793746948,],\n", + "[-0.93874591588974,]], device=\"cpu\", requires_grad=True)\n", + "f2 depois tensor([[-0.4701128602027893,]], device=\"cpu\", requires_grad=True)\n", + "\n", + "\n", + "\n", + "loss_antes tensor([3.4438509941101074,], device=\"cpu\", requires_grad=True)\n", + "f1 antes tensor([[0.6632946729660034,],\n", + "[-0.13374479115009308,],\n", + "[0.5306239128112793,],\n", + "[-0.9970470070838928,],\n", + "[-0.10747397691011429,],\n", + "[0.443511426448822,],\n", + "[-0.5381878614425659,],\n", + "[0.8977822065353394,],\n", + "[0.8040121793746948,],\n", + "[-0.93874591588974,]], device=\"cpu\", requires_grad=True)\n", + "f1 grad_antes tensor([[0.022833500057458878,],\n", + "[-0.0028506112284958363,],\n", + "[-0.03335754573345184,],\n", + "[0.007391867693513632,],\n", + "[-0.057143040001392365,],\n", + "[-0.012724379077553749,],\n", + "[-0.39252525568008423,],\n", + "[-0.4895278811454773,],\n", + "[-0.10699693858623505,],\n", + "[-0.0043337587267160416,]], device=\"cpu\", requires_grad=True)\n", + "f2 antes tensor([[-0.4701128602027893,]], device=\"cpu\", requires_grad=True)\n", + "f2 grad_antes tensor([[-3.7115235328674316,]], device=\"cpu\", requires_grad=True)\n", + "\n", + "\n", + "f1 depois tensor([[0.6632718443870544,],\n", + "[-0.13374194502830505,],\n", + "[0.5306572914123535,],\n", + "[-0.997054398059845,],\n", + "[-0.10741683095693588,],\n", + "[0.44352415204048157,],\n", + "[-0.53779536485672,],\n", + "[0.8982717394828796,],\n", + "[0.8041191697120667,],\n", + "[-0.9387415647506714,]], device=\"cpu\", requires_grad=True)\n", + "f2 depois tensor([[-0.4664013385772705,]], device=\"cpu\", requires_grad=True)\n", + "\n", + "\n", + "\n", + "loss_antes tensor([3.5297656059265137,], device=\"cpu\", requires_grad=True)\n", + "f1 antes tensor([[0.6632718443870544,],\n", + "[-0.13374194502830505,],\n", + "[0.5306572914123535,],\n", + "[-0.997054398059845,],\n", + "[-0.10741683095693588,],\n", + "[0.44352415204048157,],\n", + "[-0.53779536485672,],\n", + "[0.8982717394828796,],\n", + "[0.8041191697120667,],\n", + "[-0.9387415647506714,]], device=\"cpu\", requires_grad=True)\n", + "f1 grad_antes tensor([[0.017184482887387276,],\n", + "[-0.0022500779014080763,],\n", + "[-0.027381807565689087,],\n", + "[0.0061619337648153305,],\n", + "[-0.05533846467733383,],\n", + "[-0.012297499924898148,],\n", + "[-0.4088391959667206,],\n", + "[-0.4877457022666931,],\n", + "[-0.10747168958187103,],\n", + "[-0.004334122408181429,]], device=\"cpu\", requires_grad=True)\n", + "f2 antes tensor([[-0.4664013385772705,]], device=\"cpu\", requires_grad=True)\n", + "f2 grad_antes tensor([[-3.7575340270996094,]], device=\"cpu\", requires_grad=True)\n", + "\n", + "\n", + "f1 depois tensor([[0.6632546782493591,],\n", + "[-0.13373969495296478,],\n", + "[0.5306846499443054,],\n", + "[-0.9970605373382568,],\n", + "[-0.10736149549484253,],\n", + "[0.4435364603996277,],\n", + "[-0.5373865365982056,],\n", + "[0.8987594842910767,],\n", + "[0.8042266368865967,],\n", + "[-0.9387372136116028,]], device=\"cpu\", requires_grad=True)\n", + "f2 depois tensor([[-0.4626438021659851,]], device=\"cpu\", requires_grad=True)\n", + "\n", + "\n", + "\n", + "loss_antes tensor([2.593369245529175,], device=\"cpu\", requires_grad=True)\n", + "f1 antes tensor([[0.6632546782493591,],\n", + "[-0.13373969495296478,],\n", + "[0.5306846499443054,],\n", + "[-0.9970605373382568,],\n", + "[-0.10736149549484253,],\n", + "[0.4435364603996277,],\n", + "[-0.5373865365982056,],\n", + "[0.8987594842910767,],\n", + "[0.8042266368865967,],\n", + "[-0.9387372136116028,]], device=\"cpu\", requires_grad=True)\n", + "f1 grad_antes tensor([[0.010942069813609123,],\n", + "[-0.0015021947911009192,],\n", + "[-0.019018465653061867,],\n", + "[0.004346743691712618,],\n", + "[-0.1281527876853943,],\n", + "[-0.028471527621150017,],\n", + "[-0.4968017339706421,],\n", + "[-0.4864799976348877,],\n", + "[-0.10797170549631119,],\n", + "[-0.004334734287112951,]], device=\"cpu\", requires_grad=True)\n", + "f2 antes tensor([[-0.4626438021659851,]], device=\"cpu\", requires_grad=True)\n", + "f2 grad_antes tensor([[-3.2207882404327393,]], device=\"cpu\", requires_grad=True)\n", + "\n", + "\n", + "f1 depois tensor([[0.6632437109947205,],\n", + "[-0.1337381899356842,],\n", + "[0.5307036638259888,],\n", + "[-0.9970648884773254,],\n", + "[-0.10723334550857544,],\n", + "[0.44356492161750793,],\n", + "[-0.5368897318840027,],\n", + "[0.8992459774017334,],\n", + "[0.8043345808982849,],\n", + "[-0.9387328624725342,]], device=\"cpu\", requires_grad=True)\n", + "f2 depois tensor([[-0.4594230055809021,]], device=\"cpu\", requires_grad=True)\n", + "\n", + "\n", + "\n", + "loss_antes tensor([1.4533612728118896,], device=\"cpu\", requires_grad=True)\n", + "f1 antes tensor([[0.6632437109947205,],\n", + "[-0.1337381899356842,],\n", + "[0.5307036638259888,],\n", + "[-0.9970648884773254,],\n", + "[-0.10723334550857544,],\n", + "[0.44356492161750793,],\n", + "[-0.5368897318840027,],\n", + "[0.8992459774017334,],\n", + "[0.8043345808982849,],\n", + "[-0.9387328624725342,]], device=\"cpu\", requires_grad=True)\n", + "f1 grad_antes tensor([[0.006082791369408369,],\n", + "[-0.0008716879528947175,],\n", + "[-0.011527320370078087,],\n", + "[0.0026770427357405424,],\n", + "[-0.2334199994802475,],\n", + "[-0.05158990994095802,],\n", + "[-0.6104713082313538,],\n", + "[-0.4853925108909607,],\n", + "[-0.10834759473800659,],\n", + "[-0.0043352399952709675,]], device=\"cpu\", requires_grad=True)\n", + "f2 antes tensor([[-0.4594230055809021,]], device=\"cpu\", requires_grad=True)\n", + "f2 grad_antes tensor([[-2.4111087322235107,]], device=\"cpu\", requires_grad=True)\n", + "\n", + "\n", + "f1 depois tensor([[0.6632376313209534,],\n", + "[-0.13373732566833496,],\n", + "[0.5307151675224304,],\n", + "[-0.9970675706863403,],\n", + "[-0.10699992626905441,],\n", + "[0.44361650943756104,],\n", + "[-0.5362792611122131,],\n", + "[0.8997313976287842,],\n", + "[0.8044429421424866,],\n", + "[-0.9387285113334656,]], device=\"cpu\", requires_grad=True)\n", + "f2 depois tensor([[-0.4570119082927704,]], device=\"cpu\", requires_grad=True)\n", + "\n", + "\n", + "\n", + "loss_antes tensor([0.8132335543632507,], device=\"cpu\", requires_grad=True)\n", + "f1 antes tensor([[0.6632376313209534,],\n", + "[-0.13373732566833496,],\n", + "[0.5307151675224304,],\n", + "[-0.9970675706863403,],\n", + "[-0.10699992626905441,],\n", + "[0.44361650943756104,],\n", + "[-0.5362792611122131,],\n", + "[0.8997313976287842,],\n", + "[0.8044429421424866,],\n", + "[-0.9387285113334656,]], device=\"cpu\", requires_grad=True)\n", + "f1 grad_antes tensor([[0.003378207329660654,],\n", + "[-0.0005022759432904422,],\n", + "[-0.00697502912953496,],\n", + "[0.0016470388509333134,],\n", + "[-0.31112340092658997,],\n", + "[-0.06845957040786743,],\n", + "[-0.6921502351760864,],\n", + "[-0.4842437505722046,],\n", + "[-0.10855133831501007,],\n", + "[-0.004335499368607998,]], device=\"cpu\", requires_grad=True)\n", + "f2 antes tensor([[-0.4570119082927704,]], device=\"cpu\", requires_grad=True)\n", + "f2 grad_antes tensor([[-1.8035893440246582,]], device=\"cpu\", requires_grad=True)\n", + "\n", + "\n", + "f1 depois tensor([[0.6632342338562012,],\n", + "[-0.13373681902885437,],\n", + "[0.5307221412658691,],\n", + "[-0.997069239616394,],\n", + "[-0.10668880492448807,],\n", + "[0.44368496537208557,],\n", + "[-0.5355871319770813,],\n", + "[0.9002156257629395,],\n", + "[0.8045514822006226,],\n", + "[-0.938724160194397,]], device=\"cpu\", requires_grad=True)\n", + "f2 depois tensor([[-0.45520833134651184,]], device=\"cpu\", requires_grad=True)\n", + "\n", + "\n", + "\n", + "loss_antes tensor([0.7657988667488098,], device=\"cpu\", requires_grad=True)\n", + "f1 antes tensor([[0.6632342338562012,],\n", + "[-0.13373681902885437,],\n", + "[0.5307221412658691,],\n", + "[-0.997069239616394,],\n", + "[-0.10668880492448807,],\n", + "[0.44368496537208557,],\n", + "[-0.5355871319770813,],\n", + "[0.9002156257629395,],\n", + "[0.8045514822006226,],\n", + "[-0.938724160194397,]], device=\"cpu\", requires_grad=True)\n", + "f1 grad_antes tensor([[0.002433427842333913,],\n", + "[-0.00037373980740085244,],\n", + "[-0.0054710120894014835,],\n", + "[0.0013142635580152273,],\n", + "[-0.3197174668312073,],\n", + "[-0.07017246633768082,],\n", + "[-0.7081764936447144,],\n", + "[-0.4828653335571289,],\n", + "[-0.10862217843532562,],\n", + "[-0.004335553850978613,]], device=\"cpu\", requires_grad=True)\n", + "f2 antes tensor([[-0.45520833134651184,]], device=\"cpu\", requires_grad=True)\n", + "f2 grad_antes tensor([[-1.7501987218856812,]], device=\"cpu\", requires_grad=True)\n", + "\n", + "\n", + "f1 depois tensor([[0.6632317900657654,],\n", + "[-0.13373644649982452,],\n", + "[0.5307276248931885,],\n", + "[-0.9970705509185791,],\n", + "[-0.10636908560991287,],\n", + "[0.4437551498413086,],\n", + "[-0.5348789691925049,],\n", + "[0.9006984829902649,],\n", + "[0.8046600818634033,],\n", + "[-0.9387198090553284,]], device=\"cpu\", requires_grad=True)\n", + "f2 depois tensor([[-0.45345813035964966,]], device=\"cpu\", requires_grad=True)\n", + "\n", + "\n", + "\n", + "loss_antes tensor([1.2838889360427856,], device=\"cpu\", requires_grad=True)\n", + "f1 antes tensor([[0.6632317900657654,],\n", + "[-0.13373644649982452,],\n", + "[0.5307276248931885,],\n", + "[-0.9970705509185791,],\n", + "[-0.10636908560991287,],\n", + "[0.4437551498413086,],\n", + "[-0.5348789691925049,],\n", + "[0.9006984829902649,],\n", + "[0.8046600818634033,],\n", + "[-0.9387198090553284,]], device=\"cpu\", requires_grad=True)\n", + "f1 grad_antes tensor([[0.00233837659470737,],\n", + "[-0.00037080622860230505,],\n", + "[-0.0057242088951170444,],\n", + "[0.0013989957515150309,],\n", + "[-0.2602483332157135,],\n", + "[-0.05713380128145218,],\n", + "[-0.6667792797088623,],\n", + "[-0.4810810983181,],\n", + "[-0.1086316630244255,],\n", + "[-0.004335513338446617,]], device=\"cpu\", requires_grad=True)\n", + "f2 antes tensor([[-0.45345813035964966,]], device=\"cpu\", requires_grad=True)\n", + "f2 grad_antes tensor([[-2.266176462173462,]], device=\"cpu\", requires_grad=True)\n", + "\n", + "\n", + "f1 depois tensor([[0.6632294654846191,],\n", + "[-0.13373607397079468,],\n", + "[0.5307333469390869,],\n", + "[-0.9970719218254089,],\n", + "[-0.10610883682966232,],\n", + "[0.4438122808933258,],\n", + "[-0.5342121720314026,],\n", + "[0.9011795520782471,],\n", + "[0.8047687411308289,],\n", + "[-0.9387154579162598,]], device=\"cpu\", requires_grad=True)\n", + "f2 depois tensor([[-0.4511919617652893,]], device=\"cpu\", requires_grad=True)\n", + "\n", + "\n", + "\n", + "loss_antes tensor([2.281325340270996,], device=\"cpu\", requires_grad=True)\n", + "f1 antes tensor([[0.6632294654846191,],\n", + "[-0.13373607397079468,],\n", + "[0.5307333469390869,],\n", + "[-0.9970719218254089,],\n", + "[-0.10610883682966232,],\n", + "[0.4438122808933258,],\n", + "[-0.5342121720314026,],\n", + "[0.9011795520782471,],\n", + "[0.8047687411308289,],\n", + "[-0.9387154579162598,]], device=\"cpu\", requires_grad=True)\n", + "f1 grad_antes tensor([[0.002312807599082589,],\n", + "[-0.00037990478449501097,],\n", + "[-0.006166975945234299,],\n", + "[0.0015327550936490297,],\n", + "[-0.17550796270370483,],\n", + "[-0.03864316642284393,],\n", + "[-0.6074409484863281,],\n", + "[-0.4787381887435913,],\n", + "[-0.10863614827394485,],\n", + "[-0.004335468169301748,]], device=\"cpu\", requires_grad=True)\n", + "f2 antes tensor([[-0.4511919617652893,]], device=\"cpu\", requires_grad=True)\n", + "f2 grad_antes tensor([[-3.0208113193511963,]], device=\"cpu\", requires_grad=True)\n", + "\n", + "\n", + "f1 depois tensor([[0.6632271409034729,],\n", + "[-0.13373570144176483,],\n", + "[0.5307394862174988,],\n", + "[-0.9970734715461731,],\n", + "[-0.10593333095312119,],\n", + "[0.44385093450546265,],\n", + "[-0.5336047410964966,],\n", + "[0.901658296585083,],\n", + "[0.8048774003982544,],\n", + "[-0.9387111067771912,]], device=\"cpu\", requires_grad=True)\n", + "f2 depois tensor([[-0.44817113876342773,]], device=\"cpu\", requires_grad=True)\n", + "\n", + "\n", + "\n", + "loss_antes tensor([3.129655122756958,], device=\"cpu\", requires_grad=True)\n", + "f1 antes tensor([[0.6632271409034729,],\n", + "[-0.13373570144176483,],\n", + "[0.5307394862174988,],\n", + "[-0.9970734715461731,],\n", + "[-0.10593333095312119,],\n", + "[0.44385093450546265,],\n", + "[-0.5336047410964966,],\n", + "[0.901658296585083,],\n", + "[0.8048774003982544,],\n", + "[-0.9387111067771912,]], device=\"cpu\", requires_grad=True)\n", + "f1 grad_antes tensor([[0.002009616931900382,],\n", + "[-0.000343533989507705,],\n", + "[-0.00584056880325079,],\n", + "[0.001475243829190731,],\n", + "[-0.12349772453308105,],\n", + "[-0.027017254382371902,],\n", + "[-0.5739820003509521,],\n", + "[-0.4758336544036865,],\n", + "[-0.10865852981805801,],\n", + "[-0.004335459321737289,]], device=\"cpu\", requires_grad=True)\n", + "f2 antes tensor([[-0.44817113876342773,]], device=\"cpu\", requires_grad=True)\n", + "f2 grad_antes tensor([[-3.5381662845611572,]], device=\"cpu\", requires_grad=True)\n", + "\n", + "\n", + "f1 depois tensor([[0.6632251143455505,],\n", + "[-0.13373535871505737,],\n", + "[0.5307453274726868,],\n", + "[-0.9970749616622925,],\n", + "[-0.10580983012914658,],\n", + "[0.4438779652118683,],\n", + "[-0.5330307483673096,],\n", + "[0.902134120464325,],\n", + "[0.8049860596656799,],\n", + "[-0.9387067556381226,]], device=\"cpu\", requires_grad=True)\n", + "f2 depois tensor([[-0.44463297724723816,]], device=\"cpu\", requires_grad=True)\n", + "\n", + "\n", + "\n", + "loss_antes tensor([3.037909984588623,], device=\"cpu\", requires_grad=True)\n", + "f1 antes tensor([[0.6632251143455505,],\n", + "[-0.13373535871505737,],\n", + "[0.5307453274726868,],\n", + "[-0.9970749616622925,],\n", + "[-0.10580983012914658,],\n", + "[0.4438779652118683,],\n", + "[-0.5330307483673096,],\n", + "[0.902134120464325,],\n", + "[0.8049860596656799,],\n", + "[-0.9387067556381226,]], device=\"cpu\", requires_grad=True)\n", + "f1 grad_antes tensor([[0.0014686842914670706,],\n", + "[-0.0002620296145323664,],\n", + "[-0.004654414486140013,],\n", + "[0.001194190583191812,],\n", + "[-0.14022156596183777,],\n", + "[-0.029818542301654816,],\n", + "[-0.590398907661438,],\n", + "[-0.47261136770248413,],\n", + "[-0.10869503021240234,],\n", + "[-0.00433548167347908,]], device=\"cpu\", requires_grad=True)\n", + "f2 antes tensor([[-0.44463297724723816,]], device=\"cpu\", requires_grad=True)\n", + "f2 grad_antes tensor([[-3.4859201908111572,]], device=\"cpu\", requires_grad=True)\n", + "\n", + "\n", + "f1 depois tensor([[0.6632236242294312,],\n", + "[-0.13373509049415588,],\n", + "[0.5307499766349792,],\n", + "[-0.997076153755188,],\n", + "[-0.10566961020231247,],\n", + "[0.44390779733657837,],\n", + "[-0.5324403643608093,],\n", + "[0.902606725692749,],\n", + "[0.8050947785377502,],\n", + "[-0.938702404499054,]], device=\"cpu\", requires_grad=True)\n", + "f2 depois tensor([[-0.4411470592021942,]], device=\"cpu\", requires_grad=True)\n", + "\n", + "\n", + "\n", + "loss_antes tensor([2.069284200668335,], device=\"cpu\", requires_grad=True)\n", + "f1 antes tensor([[0.6632236242294312,],\n", + "[-0.13373509049415588,],\n", + "[0.5307499766349792,],\n", + "[-0.997076153755188,],\n", + "[-0.10566961020231247,],\n", + "[0.44390779733657837,],\n", + "[-0.5324403643608093,],\n", + "[0.902606725692749,],\n", + "[0.8050947785377502,],\n", + "[-0.938702404499054,]], device=\"cpu\", requires_grad=True)\n", + "f1 grad_antes tensor([[0.0008991045760922134,],\n", + "[-0.0001672774087637663,],\n", + "[-0.0031067486852407455,],\n", + "[0.0008097040699794888,],\n", + "[-0.21756145358085632,],\n", + "[-0.04547252506017685,],\n", + "[-0.6499848961830139,],\n", + "[-0.46950939297676086,],\n", + "[-0.10873179882764816,],\n", + "[-0.004335511475801468,]], device=\"cpu\", requires_grad=True)\n", + "f2 antes tensor([[-0.4411470592021942,]], device=\"cpu\", requires_grad=True)\n", + "f2 grad_antes tensor([[-2.8770015239715576,]], device=\"cpu\", requires_grad=True)\n", + "\n", + "\n", + "f1 depois tensor([[0.6632227301597595,],\n", + "[-0.13373492658138275,],\n", + "[0.5307530760765076,],\n", + "[-0.9970769882202148,],\n", + "[-0.1054520457983017,],\n", + "[0.443953275680542,],\n", + "[-0.5317903757095337,],\n", + "[0.9030762314796448,],\n", + "[0.8052034974098206,],\n", + "[-0.9386980533599854,]], device=\"cpu\", requires_grad=True)\n", + "f2 depois tensor([[-0.43827006220817566,]], device=\"cpu\", requires_grad=True)\n", + "\n", + "\n", + "\n", + "loss_antes tensor([1.0626496076583862,], device=\"cpu\", requires_grad=True)\n", + "f1 antes tensor([[0.6632227301597595,],\n", + "[-0.13373492658138275,],\n", + "[0.5307530760765076,],\n", + "[-0.9970769882202148,],\n", + "[-0.1054520457983017,],\n", + "[0.443953275680542,],\n", + "[-0.5317903757095337,],\n", + "[0.9030762314796448,],\n", + "[0.8052034974098206,],\n", + "[-0.9386980533599854,]], device=\"cpu\", requires_grad=True)\n", + "f1 grad_antes tensor([[0.0004779190931003541,],\n", + "[-9.233965101884678e-05,],\n", + "[-0.0017994967056438327,],\n", + "[0.0004766720230691135,],\n", + "[-0.3117518723011017,],\n", + "[-0.06493335962295532,],\n", + "[-0.7210543751716614,],\n", + "[-0.46694326400756836,],\n", + "[-0.10875803977251053,],\n", + "[-0.004335535690188408,]], device=\"cpu\", requires_grad=True)\n", + "f2 antes tensor([[-0.43827006220817566,]], device=\"cpu\", requires_grad=True)\n", + "f2 grad_antes tensor([[-2.0616979598999023,]], device=\"cpu\", requires_grad=True)\n", + "\n", + "\n", + "f1 depois tensor([[0.6632222533226013,],\n", + "[-0.1337348371744156,],\n", + "[0.5307548642158508,],\n", + "[-0.997077465057373,],\n", + "[-0.10514029115438461,],\n", + "[0.4440182149410248,],\n", + "[-0.5310693383216858,],\n", + "[0.9035431742668152,],\n", + "[0.8053122758865356,],\n", + "[-0.9386937022209167,]], device=\"cpu\", requires_grad=True)\n", + "f2 depois tensor([[-0.4362083673477173,]], device=\"cpu\", requires_grad=True)\n", + "\n", + "\n", + "\n", + "loss_antes tensor([0.5739572048187256,], device=\"cpu\", requires_grad=True)\n", + "f1 antes tensor([[0.6632222533226013,],\n", + "[-0.1337348371744156,],\n", + "[0.5307548642158508,],\n", + "[-0.997077465057373,],\n", + "[-0.10514029115438461,],\n", + "[0.4440182149410248,],\n", + "[-0.5310693383216858,],\n", + "[0.9035431742668152,],\n", + "[0.8053122758865356,],\n", + "[-0.9386937022209167,]], device=\"cpu\", requires_grad=True)\n", + "f1 grad_antes tensor([[0.000260532513493672,],\n", + "[-5.200725354370661e-05,],\n", + "[-0.001068109180778265,],\n", + "[0.0002877784427255392,],\n", + "[-0.37277156114578247,],\n", + "[-0.07756895571947098,],\n", + "[-0.767205536365509,],\n", + "[-0.46505141258239746,],\n", + "[-0.10877126455307007,],\n", + "[-0.004335546866059303,]], device=\"cpu\", requires_grad=True)\n", + "f2 antes tensor([[-0.4362083673477173,]], device=\"cpu\", requires_grad=True)\n", + "f2 grad_antes tensor([[-1.5151994228363037,]], device=\"cpu\", requires_grad=True)\n", + "\n", + "\n", + "f1 depois tensor([[0.6632220149040222,],\n", + "[-0.133734792470932,],\n", + "[0.5307559370994568,],\n", + "[-0.9970777630805969,],\n", + "[-0.10476751625537872,],\n", + "[0.44409579038619995,],\n", + "[-0.530302107334137,],\n", + "[0.9040082097053528,],\n", + "[0.8054210543632507,],\n", + "[-0.9386893510818481,]], device=\"cpu\", requires_grad=True)\n", + "f2 depois tensor([[-0.4346931576728821,]], device=\"cpu\", requires_grad=True)\n", + "\n", + "\n", + "\n", + "loss_antes tensor([0.6002265810966492,], device=\"cpu\", requires_grad=True)\n", + "f1 antes tensor([[0.6632220149040222,],\n", + "[-0.133734792470932,],\n", + "[0.5307559370994568,],\n", + "[-0.9970777630805969,],\n", + "[-0.10476751625537872,],\n", + "[0.44409579038619995,],\n", + "[-0.530302107334137,],\n", + "[0.9040082097053528,],\n", + "[0.8054210543632507,],\n", + "[-0.9386893510818481,]], device=\"cpu\", requires_grad=True)\n", + "f1 grad_antes tensor([[0.00019762390002142638,],\n", + "[-4.062050356878899e-05,],\n", + "[-0.000881690182723105,],\n", + "[0.00024174268764909357,],\n", + "[-0.3731463551521301,],\n", + "[-0.07734758406877518,],\n", + "[-0.7702389359474182,],\n", + "[-0.46358126401901245,],\n", + "[-0.10877510905265808,],\n", + "[-0.0043355487287044525,]], device=\"cpu\", requires_grad=True)\n", + "f2 antes tensor([[-0.4346931576728821,]], device=\"cpu\", requires_grad=True)\n", + "f2 grad_antes tensor([[-1.5494858026504517,]], device=\"cpu\", requires_grad=True)\n", + "\n", + "\n", + "f1 depois tensor([[0.6632218360900879,],\n", + "[-0.13373474776744843,],\n", + "[0.5307568311691284,],\n", + "[-0.997078001499176,],\n", + "[-0.10439436882734299,],\n", + "[0.444173127412796,],\n", + "[-0.5295318961143494,],\n", + "[0.9044718146324158,],\n", + "[0.8055298328399658,],\n", + "[-0.9386849999427795,]], device=\"cpu\", requires_grad=True)\n", + "f2 depois tensor([[-0.433143675327301,]], device=\"cpu\", requires_grad=True)\n", + "\n", + "\n", + "\n", + "loss_antes tensor([1.1280887126922607,], device=\"cpu\", requires_grad=True)\n", + "f1 antes tensor([[0.6632218360900879,],\n", + "[-0.13373474776744843,],\n", + "[0.5307568311691284,],\n", + "[-0.997078001499176,],\n", + "[-0.10439436882734299,],\n", + "[0.444173127412796,],\n", + "[-0.5295318961143494,],\n", + "[0.9044718146324158,],\n", + "[0.8055298328399658,],\n", + "[-0.9386849999427795,]], device=\"cpu\", requires_grad=True)\n", + "f1 grad_antes tensor([[0.00020095807849429548,],\n", + "[-4.25368853029795e-05,],\n", + "[-0.0009756854269653559,],\n", + "[0.0002722226199693978,],\n", + "[-0.32227733731269836,],\n", + "[-0.0660870224237442,],\n", + "[-0.7391701936721802,],\n", + "[-0.4620158076286316,],\n", + "[-0.10877511650323868,],\n", + "[-0.004335545469075441,]], device=\"cpu\", requires_grad=True)\n", + "f2 antes tensor([[-0.433143675327301,]], device=\"cpu\", requires_grad=True)\n", + "f2 grad_antes tensor([[-2.12423038482666,]], device=\"cpu\", requires_grad=True)\n", + "\n", + "\n", + "f1 depois tensor([[0.6632216572761536,],\n", + "[-0.13373470306396484,],\n", + "[0.5307577848434448,],\n", + "[-0.9970782995223999,],\n", + "[-0.10407209396362305,],\n", + "[0.44423922896385193,],\n", + "[-0.5287927389144897,],\n", + "[0.9049338102340698,],\n", + "[0.8056386113166809,],\n", + "[-0.9386806488037109,]], device=\"cpu\", requires_grad=True)\n", + "f2 depois tensor([[-0.43101945519447327,]], device=\"cpu\", requires_grad=True)\n", + "\n", + "\n", + "\n", + "loss_antes tensor([2.0611908435821533,], device=\"cpu\", requires_grad=True)\n", + "f1 antes tensor([[0.6632216572761536,],\n", + "[-0.13373470306396484,],\n", + "[0.5307577848434448,],\n", + "[-0.9970782995223999,],\n", + "[-0.10407209396362305,],\n", + "[0.44423922896385193,],\n", + "[-0.5287927389144897,],\n", + "[0.9049338102340698,],\n", + "[0.8056386113166809,],\n", + "[-0.9386806488037109,]], device=\"cpu\", requires_grad=True)\n", + "f1 grad_antes tensor([[0.0002014815981965512,],\n", + "[-4.4060874643037096e-05,],\n", + "[-0.0010651235934346914,],\n", + "[0.000302235217532143,],\n", + "[-0.2603999376296997,],\n", + "[-0.05211338773369789,],\n", + "[-0.7007260322570801,],\n", + "[-0.4598775804042816,],\n", + "[-0.10877527296543121,],\n", + "[-0.00433554220944643,]], device=\"cpu\", requires_grad=True)\n", + "f2 antes tensor([[-0.43101945519447327,]], device=\"cpu\", requires_grad=True)\n", + "f2 grad_antes tensor([[-2.8713698387145996,]], device=\"cpu\", requires_grad=True)\n", + "\n", + "\n", + "f1 depois tensor([[0.6632214784622192,],\n", + "[-0.13373465836048126,],\n", + "[0.5307588577270508,],\n", + "[-0.9970785975456238,],\n", + "[-0.10381169617176056,],\n", + "[0.444291353225708,],\n", + "[-0.5280920267105103,],\n", + "[0.905393660068512,],\n", + "[0.805747389793396,],\n", + "[-0.9386762976646423,]], device=\"cpu\", requires_grad=True)\n", + "f2 depois tensor([[-0.428148090839386,]], device=\"cpu\", requires_grad=True)\n", + "\n", + "\n", + "\n", + "loss_antes tensor([2.7530932426452637,], device=\"cpu\", requires_grad=True)\n", + "f1 antes tensor([[0.6632214784622192,],\n", + "[-0.13373465836048126,],\n", + "[0.5307588577270508,],\n", + "[-0.9970785975456238,],\n", + "[-0.10381169617176056,],\n", + "[0.444291353225708,],\n", + "[-0.5280920267105103,],\n", + "[0.905393660068512,],\n", + "[0.805747389793396,],\n", + "[-0.9386762976646423,]], device=\"cpu\", requires_grad=True)\n", + "f1 grad_antes tensor([[0.00017271195247303694,],\n", + "[-3.9180675230454654e-05,],\n", + "[-0.0009948447113856673,],\n", + "[0.00028688638121820986,],\n", + "[-0.23283472657203674,],\n", + "[-0.044941551983356476,],\n", + "[-0.6817824840545654,],\n", + "[-0.4570259153842926,],\n", + "[-0.10877696424722672,],\n", + "[-0.0043355440720915794,]], device=\"cpu\", requires_grad=True)\n", + "f2 antes tensor([[-0.428148090839386,]], device=\"cpu\", requires_grad=True)\n", + "f2 grad_antes tensor([[-3.3184895515441895,]], device=\"cpu\", requires_grad=True)\n", + "\n", + "\n", + "f1 depois tensor([[0.6632212996482849,],\n", + "[-0.13373461365699768,],\n", + "[0.530759871006012,],\n", + "[-0.9970788955688477,],\n", + "[-0.10357885807752609,],\n", + "[0.44433629512786865,],\n", + "[-0.5274102687835693,],\n", + "[0.9058507084846497,],\n", + "[0.8058561682701111,],\n", + "[-0.9386719465255737,]], device=\"cpu\", requires_grad=True)\n", + "f2 depois tensor([[-0.4248296022415161,]], device=\"cpu\", requires_grad=True)\n", + "\n", + "\n", + "\n", + "loss_antes tensor([2.5207862854003906,], device=\"cpu\", requires_grad=True)\n", + "f1 antes tensor([[0.6632212996482849,],\n", + "[-0.13373461365699768,],\n", + "[0.530759871006012,],\n", + "[-0.9970788955688477,],\n", + "[-0.10357885807752609,],\n", + "[0.44433629512786865,],\n", + "[-0.5274102687835693,],\n", + "[0.9058507084846497,],\n", + "[0.8058561682701111,],\n", + "[-0.9386719465255737,]], device=\"cpu\", requires_grad=True)\n", + "f1 grad_antes tensor([[0.00012257778143975884,],\n", + "[-2.8910602850373834e-05,],\n", + "[-0.0007696571992710233,],\n", + "[0.00022545919637195766,],\n", + "[-0.259912371635437,],\n", + "[-0.049322932958602905,],\n", + "[-0.695215106010437,],\n", + "[-0.4537573456764221,],\n", + "[-0.10877971351146698,],\n", + "[-0.0043355440720915794,]], device=\"cpu\", requires_grad=True)\n", + "f2 antes tensor([[-0.4248296022415161,]], device=\"cpu\", requires_grad=True)\n", + "f2 grad_antes tensor([[-3.1753969192504883,]], device=\"cpu\", requires_grad=True)\n", + "\n", + "\n", + "f1 depois tensor([[0.6632211804389954,],\n", + "[-0.1337345838546753,],\n", + "[0.530760645866394,],\n", + "[-0.9970791339874268,],\n", + "[-0.1033189445734024,],\n", + "[0.4443856179714203,],\n", + "[-0.5267150402069092,],\n", + "[0.9063044786453247,],\n", + "[0.8059649467468262,],\n", + "[-0.9386675953865051,]], device=\"cpu\", requires_grad=True)\n", + "f2 depois tensor([[-0.42165419459342957,]], device=\"cpu\", requires_grad=True)\n", + "\n", + "\n", + "\n", + "loss_antes tensor([1.5749989748001099,], device=\"cpu\", requires_grad=True)\n", + "f1 antes tensor([[0.6632211804389954,],\n", + "[-0.1337345838546753,],\n", + "[0.530760645866394,],\n", + "[-0.9970791339874268,],\n", + "[-0.1033189445734024,],\n", + "[0.4443856179714203,],\n", + "[-0.5267150402069092,],\n", + "[0.9063044786453247,],\n", + "[0.8059649467468262,],\n", + "[-0.9386675953865051,]], device=\"cpu\", requires_grad=True)\n", + "f1 grad_antes tensor([[7.186472066678107e-05,],\n", + "[-1.760224040481262e-05,],\n", + "[-0.0004918009508401155,],\n", + "[0.0001463637308916077,],\n", + "[-0.32627710700035095,],\n", + "[-0.0627223402261734,],\n", + "[-0.7342636585235596,],\n", + "[-0.45063966512680054,],\n", + "[-0.1087823361158371,],\n", + "[-0.004335545003414154,]], device=\"cpu\", requires_grad=True)\n", + "f2 antes tensor([[-0.42165419459342957,]], device=\"cpu\", requires_grad=True)\n", + "f2 grad_antes tensor([[-2.509979248046875,]], device=\"cpu\", requires_grad=True)\n", + "\n", + "\n", + "f1 depois tensor([[0.6632211208343506,],\n", + "[-0.1337345689535141,],\n", + "[0.5307611227035522,],\n", + "[-0.9970792531967163,],\n", + "[-0.10299266874790192,],\n", + "[0.4444483518600464,],\n", + "[-0.5259807705879211,],\n", + "[0.9067550897598267,],\n", + "[0.8060737252235413,],\n", + "[-0.9386632442474365,]], device=\"cpu\", requires_grad=True)\n", + "f2 depois tensor([[-0.4191442131996155,]], device=\"cpu\", requires_grad=True)\n", + "\n", + "\n", + "\n", + "loss_antes tensor([0.7283971309661865,], device=\"cpu\", requires_grad=True)\n", + "f1 antes tensor([[0.6632211208343506,],\n", + "[-0.1337345689535141,],\n", + "[0.5307611227035522,],\n", + "[-0.9970792531967163,],\n", + "[-0.10299266874790192,],\n", + "[0.4444483518600464,],\n", + "[-0.5259807705879211,],\n", + "[0.9067550897598267,],\n", + "[0.8060737252235413,],\n", + "[-0.9386632442474365,]], device=\"cpu\", requires_grad=True)\n", + "f1 grad_antes tensor([[3.624881719588302e-05,],\n", + "[-9.184748705592938e-06,],\n", + "[-0.0002701894845813513,],\n", + "[8.174658432835713e-05,],\n", + "[-0.39565861225128174,],\n", + "[-0.07754066586494446,],\n", + "[-0.7773778438568115,],\n", + "[-0.44817399978637695,],\n", + "[-0.10878413170576096,],\n", + "[-0.004335546400398016,]], device=\"cpu\", requires_grad=True)\n", + "f2 antes tensor([[-0.4191442131996155,]], device=\"cpu\", requires_grad=True)\n", + "f2 grad_antes tensor([[-1.7069237232208252,]], device=\"cpu\", requires_grad=True)\n", + "\n", + "\n", + "f1 depois tensor([[0.6632210612297058,],\n", + "[-0.1337345540523529,],\n", + "[0.5307614207267761,],\n", + "[-0.9970793128013611,],\n", + "[-0.10259701311588287,],\n", + "[0.44452589750289917,],\n", + "[-0.5252034068107605,],\n", + "[0.9072032570838928,],\n", + "[0.8061825037002563,],\n", + "[-0.9386588931083679,]], device=\"cpu\", requires_grad=True)\n", + "f2 depois tensor([[-0.41743728518486023,]], device=\"cpu\", requires_grad=True)\n", + "\n", + "\n", + "\n", + "loss_antes tensor([0.38103076815605164,], device=\"cpu\", requires_grad=True)\n", + "f1 antes tensor([[0.6632210612297058,],\n", + "[-0.1337345540523529,],\n", + "[0.5307614207267761,],\n", + "[-0.9970793128013611,],\n", + "[-0.10259701311588287,],\n", + "[0.44452589750289917,],\n", + "[-0.5252034068107605,],\n", + "[0.9072032570838928,],\n", + "[0.8061825037002563,],\n", + "[-0.9386588931083679,]], device=\"cpu\", requires_grad=True)\n", + "f1 grad_antes tensor([[1.9445760699454695e-05,],\n", + "[-5.074367436463945e-06,],\n", + "[-0.0001577447255840525,],\n", + "[4.855751831200905e-05,],\n", + "[-0.4345645308494568,],\n", + "[-0.08597981184720993,],\n", + "[-0.8020811676979065,],\n", + "[-0.4464889466762543,],\n", + "[-0.10878493636846542,],\n", + "[-0.004335548263043165,]], device=\"cpu\", requires_grad=True)\n", + "f2 antes tensor([[-0.41743728518486023,]], device=\"cpu\", requires_grad=True)\n", + "f2 grad_antes tensor([[-1.2345538139343262,]], device=\"cpu\", requires_grad=True)\n", + "\n", + "\n", + "f1 depois tensor([[0.6632210612297058,],\n", + "[-0.1337345540523529,],\n", + "[0.5307615995407104,],\n", + "[-0.9970793724060059,],\n", + "[-0.1021624505519867,],\n", + "[0.44461187720298767,],\n", + "[-0.5244013071060181,],\n", + "[0.9076497554779053,],\n", + "[0.8062912821769714,],\n", + "[-0.9386545419692993,]], device=\"cpu\", requires_grad=True)\n", + "f2 depois tensor([[-0.41620272397994995,]], device=\"cpu\", requires_grad=True)\n", + "\n", + "\n", + "\n", + "loss_antes tensor([0.46308961510658264,], device=\"cpu\", requires_grad=True)\n", + "f1 antes tensor([[0.6632210612297058,],\n", + "[-0.1337345540523529,],\n", + "[0.5307615995407104,],\n", + "[-0.9970793724060059,],\n", + "[-0.1021624505519867,],\n", + "[0.44461187720298767,],\n", + "[-0.5244013071060181,],\n", + "[0.9076497554779053,],\n", + "[0.8062912821769714,],\n", + "[-0.9386545419692993,]], device=\"cpu\", requires_grad=True)\n", + "f1 grad_antes tensor([[1.5900583093753085e-05,],\n", + "[-4.262175025360193e-06,],\n", + "[-0.00014031199680175632,],\n", + "[4.396402800921351e-05,],\n", + "[-0.4293730854988098,],\n", + "[-0.08445803821086884,],\n", + "[-0.7991432547569275,],\n", + "[-0.44525817036628723,],\n", + "[-0.10878511518239975,],\n", + "[-0.004335548263043165,]], device=\"cpu\", requires_grad=True)\n", + "f2 antes tensor([[-0.41620272397994995,]], device=\"cpu\", requires_grad=True)\n", + "f2 grad_antes tensor([[-1.3610137701034546,]], device=\"cpu\", requires_grad=True)\n", + "\n", + "\n", + "f1 depois tensor([[0.6632210612297058,],\n", + "[-0.1337345540523529,],\n", + "[0.53076171875,],\n", + "[-0.9970794320106506,],\n", + "[-0.10173308104276657,],\n", + "[0.4446963369846344,],\n", + "[-0.5236021876335144,],\n", + "[0.9080950021743774,],\n", + "[0.8064000606536865,],\n", + "[-0.9386501908302307,]], device=\"cpu\", requires_grad=True)\n", + "f2 depois tensor([[-0.4148417115211487,]], device=\"cpu\", requires_grad=True)\n", + "\n", + "\n", + "\n", + "loss_antes tensor([0.9917470216751099,], device=\"cpu\", requires_grad=True)\n", + "f1 antes tensor([[0.6632210612297058,],\n", + "[-0.1337345540523529,],\n", + "[0.53076171875,],\n", + "[-0.9970794320106506,],\n", + "[-0.10173308104276657,],\n", + "[0.4446963369846344,],\n", + "[-0.5236021876335144,],\n", + "[0.9080950021743774,],\n", + "[0.8064000606536865,],\n", + "[-0.9386501908302307,]], device=\"cpu\", requires_grad=True)\n", + "f1 grad_antes tensor([[1.725900983728934e-05,],\n", + "[-4.755161626235349e-06,],\n", + "[-0.0001656918175285682,],\n", + "[5.283833161229268e-05,],\n", + "[-0.39227035641670227,],\n", + "[-0.07540077716112137,],\n", + "[-0.7764900326728821,],\n", + "[-0.4438931941986084,],\n", + "[-0.10878507047891617,],\n", + "[-0.004335548263043165,]], device=\"cpu\", requires_grad=True)\n", + "f2 antes tensor([[-0.4148417115211487,]], device=\"cpu\", requires_grad=True)\n", + "f2 grad_antes tensor([[-1.9917298555374146,]], device=\"cpu\", requires_grad=True)\n", + "\n", + "\n", + "f1 depois tensor([[0.6632210612297058,],\n", + "[-0.1337345540523529,],\n", + "[0.5307618975639343,],\n", + "[-0.9970794916152954,],\n", + "[-0.10134080797433853,],\n", + "[0.44477173686027527,],\n", + "[-0.5228257179260254,],\n", + "[0.9085388779640198,],\n", + "[0.8065088391304016,],\n", + "[-0.9386458396911621,]], device=\"cpu\", requires_grad=True)\n", + "f2 depois tensor([[-0.4128499925136566,]], device=\"cpu\", requires_grad=True)\n", + "\n", + "\n", + "\n", + "loss_antes tensor([1.8588377237319946,], device=\"cpu\", requires_grad=True)\n", + "f1 antes tensor([[0.6632210612297058,],\n", + "[-0.1337345540523529,],\n", + "[0.5307618975639343,],\n", + "[-0.9970794916152954,],\n", + "[-0.10134080797433853,],\n", + "[0.44477173686027527,],\n", + "[-0.5228257179260254,],\n", + "[0.9085388779640198,],\n", + "[0.8065088391304016,],\n", + "[-0.9386458396911621,]], device=\"cpu\", requires_grad=True)\n", + "f1 grad_antes tensor([[1.7525486327940598e-05,],\n", + "[-4.979164714313811e-06,],\n", + "[-0.0001831587724154815,],\n", + "[5.9408426750451326e-05,],\n", + "[-0.3537100553512573,],\n", + "[-0.0655350610613823,],\n", + "[-0.7517706751823425,],\n", + "[-0.44189900159835815,],\n", + "[-0.10878506302833557,],\n", + "[-0.004335548263043165,]], device=\"cpu\", requires_grad=True)\n", + "f2 antes tensor([[-0.4128499925136566,]], device=\"cpu\", requires_grad=True)\n", + "f2 grad_antes tensor([[-2.7267839908599854,]], device=\"cpu\", requires_grad=True)\n", + "\n", + "\n", + "f1 depois tensor([[0.6632210612297058,],\n", + "[-0.1337345540523529,],\n", + "[0.5307620763778687,],\n", + "[-0.9970795512199402,],\n", + "[-0.10098709911108017,],\n", + "[0.4448372721672058,],\n", + "[-0.5220739245414734,],\n", + "[0.9089807868003845,],\n", + "[0.8066176176071167,],\n", + "[-0.9386414885520935,]], device=\"cpu\", requires_grad=True)\n", + "f2 depois tensor([[-0.41012319922447205,]], device=\"cpu\", requires_grad=True)\n", + "\n", + "\n", + "\n", + "loss_antes tensor([2.4078986644744873,], device=\"cpu\", requires_grad=True)\n", + "f1 antes tensor([[0.6632210612297058,],\n", + "[-0.1337345540523529,],\n", + "[0.5307620763778687,],\n", + "[-0.9970795512199402,],\n", + "[-0.10098709911108017,],\n", + "[0.4448372721672058,],\n", + "[-0.5220739245414734,],\n", + "[0.9089807868003845,],\n", + "[0.8066176176071167,],\n", + "[-0.9386414885520935,]], device=\"cpu\", requires_grad=True)\n", + "f1 grad_antes tensor([[1.4794508388149552e-05,],\n", + "[-4.3502986954990774e-06,],\n", + "[-0.0001684400049271062,],\n", + "[5.552908987738192e-05,],\n", + "[-0.34249699115753174,],\n", + "[-0.06158159673213959,],\n", + "[-0.741202175617218,],\n", + "[-0.4391755759716034,],\n", + "[-0.10878520458936691,],\n", + "[-0.004335548263043165,]], device=\"cpu\", requires_grad=True)\n", + "f2 antes tensor([[-0.41012319922447205,]], device=\"cpu\", requires_grad=True)\n", + "f2 grad_antes tensor([[-3.1034812927246094,]], device=\"cpu\", requires_grad=True)\n", + "\n", + "\n", + "f1 depois tensor([[0.6632210612297058,],\n", + "[-0.1337345540523529,],\n", + "[0.530762255191803,],\n", + "[-0.997079610824585,],\n", + "[-0.10064460337162018,],\n", + "[0.4448988437652588,],\n", + "[-0.5213327407836914,],\n", + "[0.9094199538230896,],\n", + "[0.8067263960838318,],\n", + "[-0.9386371374130249,]], device=\"cpu\", requires_grad=True)\n", + "f2 depois tensor([[-0.407019704580307,]], device=\"cpu\", requires_grad=True)\n", + "\n", + "\n", + "\n", + "loss_antes tensor([2.069084405899048,], device=\"cpu\", requires_grad=True)\n", + "f1 antes tensor([[0.6632210612297058,],\n", + "[-0.1337345540523529,],\n", + "[0.530762255191803,],\n", + "[-0.997079610824585,],\n", + "[-0.10064460337162018,],\n", + "[0.4448988437652588,],\n", + "[-0.5213327407836914,],\n", + "[0.9094199538230896,],\n", + "[0.8067263960838318,],\n", + "[-0.9386371374130249,]], device=\"cpu\", requires_grad=True)\n", + "f1 grad_antes tensor([[1.0171942449233029e-05,],\n", + "[-3.1009558369987644e-06,],\n", + "[-0.00012620999768842012,],\n", + "[4.227289537084289e-05,],\n", + "[-0.36690032482147217,],\n", + "[-0.06612859666347504,],\n", + "[-0.7512801885604858,],\n", + "[-0.4360799193382263,],\n", + "[-0.10878542065620422,],\n", + "[-0.004335548263043165,]], device=\"cpu\", requires_grad=True)\n", + "f2 antes tensor([[-0.407019704580307,]], device=\"cpu\", requires_grad=True)\n", + "f2 grad_antes tensor([[-2.8768625259399414,]], device=\"cpu\", requires_grad=True)\n", + "\n", + "\n", + "f1 depois tensor([[0.6632210612297058,],\n", + "[-0.1337345540523529,],\n", + "[0.5307623744010925,],\n", + "[-0.9970796704292297,],\n", + "[-0.10027769953012466,],\n", + "[0.4449649751186371,],\n", + "[-0.5205814838409424,],\n", + "[0.9098560214042664,],\n", + "[0.8068351745605469,],\n", + "[-0.9386327862739563,]], device=\"cpu\", requires_grad=True)\n", + "f2 depois tensor([[-0.4041428565979004,]], device=\"cpu\", requires_grad=True)\n", + "\n", + "\n", + "\n", + "loss_antes tensor([1.1751409769058228,], device=\"cpu\", requires_grad=True)\n", + "f1 antes tensor([[0.6632210612297058,],\n", + "[-0.1337345540523529,],\n", + "[0.5307623744010925,],\n", + "[-0.9970796704292297,],\n", + "[-0.10027769953012466,],\n", + "[0.4449649751186371,],\n", + "[-0.5205814838409424,],\n", + "[0.9098560214042664,],\n", + "[0.8068351745605469,],\n", + "[-0.9386327862739563,]], device=\"cpu\", requires_grad=True)\n", + "f1 grad_antes tensor([[5.6858157222450245e-06,],\n", + "[-1.794572881408385e-06,],\n", + "[-7.686503522563726e-05,],\n", + "[2.6163063012063503e-05,],\n", + "[-0.41251328587532043,],\n", + "[-0.07647360116243362,],\n", + "[-0.776080846786499,],\n", + "[-0.4332115948200226,],\n", + "[-0.10878559947013855,],\n", + "[-0.004335548263043165,]], device=\"cpu\", requires_grad=True)\n", + "f2 antes tensor([[-0.4041428565979004,]], device=\"cpu\", requires_grad=True)\n", + "f2 grad_antes tensor([[-2.1680784225463867,]], device=\"cpu\", requires_grad=True)\n", + "\n", + "\n", + "f1 depois tensor([[0.6632210612297058,],\n", + "[-0.1337345540523529,],\n", + "[0.5307624340057373,],\n", + "[-0.9970796704292297,],\n", + "[-0.09986518323421478,],\n", + "[0.4450414478778839,],\n", + "[-0.5198054313659668,],\n", + "[0.9102892279624939,],\n", + "[0.806943953037262,],\n", + "[-0.9386284351348877,]], device=\"cpu\", requires_grad=True)\n", + "f2 depois tensor([[-0.40197476744651794,]], device=\"cpu\", requires_grad=True)\n", + "\n", + "\n", + "\n", + "loss_antes tensor([0.483631432056427,], device=\"cpu\", requires_grad=True)\n", + "f1 antes tensor([[0.6632210612297058,],\n", + "[-0.1337345540523529,],\n", + "[0.5307624340057373,],\n", + "[-0.9970796704292297,],\n", + "[-0.09986518323421478,],\n", + "[0.4450414478778839,],\n", + "[-0.5198054313659668,],\n", + "[0.9102892279624939,],\n", + "[0.806943953037262,],\n", + "[-0.9386284351348877,]], device=\"cpu\", requires_grad=True)\n", + "f1 grad_antes tensor([[2.705442284423043e-06,],\n", + "[-8.808051461528521e-07,],\n", + "[-3.982111957157031e-05,],\n", + "[1.3783894246444106e-05,],\n", + "[-0.45520567893981934,],\n", + "[-0.0868092030286789,],\n", + "[-0.8013222813606262,],\n", + "[-0.43104979395866394,],\n", + "[-0.1087857112288475,],\n", + "[-0.004335548263043165,]], device=\"cpu\", requires_grad=True)\n", + "f2 antes tensor([[-0.40197476744651794,]], device=\"cpu\", requires_grad=True)\n", + "f2 grad_antes tensor([[-1.3908722400665283,]], device=\"cpu\", requires_grad=True)\n", + "\n", + "\n", + "f1 depois tensor([[0.6632210612297058,],\n", + "[-0.1337345540523529,],\n", + "[0.5307624936103821,],\n", + "[-0.9970796704292297,],\n", + "[-0.09940997511148453,],\n", + "[0.44512826204299927,],\n", + "[-0.5190041065216064,],\n", + "[0.9107202887535095,],\n", + "[0.807052731513977,],\n", + "[-0.9386240839958191,]], device=\"cpu\", requires_grad=True)\n", + "f2 depois tensor([[-0.4005838930606842,]], device=\"cpu\", requires_grad=True)\n", + "\n", + "\n", + "\n", + "loss_antes tensor([0.25040537118911743,], device=\"cpu\", requires_grad=True)\n", + "f1 antes tensor([[0.6632210612297058,],\n", + "[-0.1337345540523529,],\n", + "[0.5307624936103821,],\n", + "[-0.9970796704292297,],\n", + "[-0.09940997511148453,],\n", + "[0.44512826204299927,],\n", + "[-0.5190041065216064,],\n", + "[0.9107202887535095,],\n", + "[0.807052731513977,],\n", + "[-0.9386240839958191,]], device=\"cpu\", requires_grad=True)\n", + "f1 grad_antes tensor([[1.443897417630069e-06,],\n", + "[-4.830071702599525e-07,],\n", + "[-2.312150172656402e-05,],\n", + "[8.1453090388095e-06,],\n", + "[-0.47574329376220703,],\n", + "[-0.09185812622308731,],\n", + "[-0.8136283159255981,],\n", + "[-0.4296617805957794,],\n", + "[-0.10878578573465347,],\n", + "[-0.004335548263043165,]], device=\"cpu\", requires_grad=True)\n", + "f2 antes tensor([[-0.4005838930606842,]], device=\"cpu\", requires_grad=True)\n", + "f2 grad_antes tensor([[-1.0008103847503662,]], device=\"cpu\", requires_grad=True)\n", + "\n", + "\n", + "f1 depois tensor([[0.6632210612297058,],\n", + "[-0.1337345540523529,],\n", + "[0.5307624936103821,],\n", + "[-0.9970796704292297,],\n", + "[-0.09893423318862915,],\n", + "[0.44522011280059814,],\n", + "[-0.5181905031204224,],\n", + "[0.9111499786376953,],\n", + "[0.8071615099906921,],\n", + "[-0.9386197328567505,]], device=\"cpu\", requires_grad=True)\n", + "f2 depois tensor([[-0.3995830714702606,]], device=\"cpu\", requires_grad=True)\n", + "\n", + "\n", + "\n", + "loss_antes tensor([0.3715560734272003,], device=\"cpu\", requires_grad=True)\n", + "f1 antes tensor([[0.6632210612297058,],\n", + "[-0.1337345540523529,],\n", + "[0.5307624936103821,],\n", + "[-0.9970796704292297,],\n", + "[-0.09893423318862915,],\n", + "[0.44522011280059814,],\n", + "[-0.5181905031204224,],\n", + "[0.9111499786376953,],\n", + "[0.8071615099906921,],\n", + "[-0.9386197328567505,]], device=\"cpu\", requires_grad=True)\n", + "f1 grad_antes tensor([[1.304548277403228e-06,],\n", + "[-4.4751604377779586e-07,],\n", + "[-2.2718259060638957e-05,],\n", + "[8.14833038020879e-06,],\n", + "[-0.4686702489852905,],\n", + "[-0.08977428823709488,],\n", + "[-0.8087867498397827,],\n", + "[-0.4286609888076782,],\n", + "[-0.10878578573465347,],\n", + "[-0.004335548263043165,]], device=\"cpu\", requires_grad=True)\n", + "f2 antes tensor([[-0.3995830714702606,]], device=\"cpu\", requires_grad=True)\n", + "f2 grad_antes tensor([[-1.219107985496521,]], device=\"cpu\", requires_grad=True)\n", + "\n", + "\n", + "f1 depois tensor([[0.6632210612297058,],\n", + "[-0.1337345540523529,],\n", + "[0.5307624936103821,],\n", + "[-0.9970796704292297,],\n", + "[-0.09846556186676025,],\n", + "[0.4453098773956299,],\n", + "[-0.5173817276954651,],\n", + "[0.9115786552429199,],\n", + "[0.8072702884674072,],\n", + "[-0.9386153817176819,]], device=\"cpu\", requires_grad=True)\n", + "f2 depois tensor([[-0.39836397767066956,]], device=\"cpu\", requires_grad=True)\n", + "\n", + "\n", + "\n", + "loss_antes tensor([0.9010469317436218,], device=\"cpu\", requires_grad=True)\n", + "f1 antes tensor([[0.6632210612297058,],\n", + "[-0.1337345540523529,],\n", + "[0.5307624936103821,],\n", + "[-0.9970796704292297,],\n", + "[-0.09846556186676025,],\n", + "[0.4453098773956299,],\n", + "[-0.5173817276954651,],\n", + "[0.9115786552429199,],\n", + "[0.8072702884674072,],\n", + "[-0.9386153817176819,]], device=\"cpu\", requires_grad=True)\n", + "f1 grad_antes tensor([[1.5068005723151146e-06,],\n", + "[-5.306197863319539e-07,],\n", + "[-2.8542974177980796e-05,],\n", + "[1.0420772923680488e-05,],\n", + "[-0.44426774978637695,],\n", + "[-0.08295752108097076,],\n", + "[-0.792965829372406,],\n", + "[-0.42744114995002747,],\n", + "[-0.10878578573465347,],\n", + "[-0.004335548263043165,]], device=\"cpu\", requires_grad=True)\n", + "f2 antes tensor([[-0.39836397767066956,]], device=\"cpu\", requires_grad=True)\n", + "f2 grad_antes tensor([[-1.8984699249267578,]], device=\"cpu\", requires_grad=True)\n", + "\n", + "\n", + "f1 depois tensor([[0.6632210612297058,],\n", + "[-0.1337345540523529,],\n", + "[0.5307624936103821,],\n", + "[-0.9970796704292297,],\n", + "[-0.09802129119634628,],\n", + "[0.4453928470611572,],\n", + "[-0.5165887475013733,],\n", + "[0.9120060801506042,],\n", + "[0.8073790669441223,],\n", + "[-0.9386110305786133,]], device=\"cpu\", requires_grad=True)\n", + "f2 depois tensor([[-0.3964655101299286,]], device=\"cpu\", requires_grad=True)\n", + "\n", + "\n", + "\n", + "loss_antes tensor([1.7061519622802734,], device=\"cpu\", requires_grad=True)\n", + "f1 antes tensor([[0.6632210612297058,],\n", + "[-0.1337345540523529,],\n", + "[0.5307624936103821,],\n", + "[-0.9970796704292297,],\n", + "[-0.09802129119634628,],\n", + "[0.4453928470611572,],\n", + "[-0.5165887475013733,],\n", + "[0.9120060801506042,],\n", + "[0.8073790669441223,],\n", + "[-0.9386110305786133,]], device=\"cpu\", requires_grad=True)\n", + "f1 grad_antes tensor([[1.5378873285953887e-06,],\n", + "[-5.577430783887394e-07,],\n", + "[-3.1709354516351596e-05,],\n", + "[1.1776156497944612e-05,],\n", + "[-0.4221835136413574,],\n", + "[-0.07640326768159866,],\n", + "[-0.7774246335029602,],\n", + "[-0.4255422353744507,],\n", + "[-0.10878578573465347,],\n", + "[-0.004335548263043165,]], device=\"cpu\", requires_grad=True)\n", + "f2 antes tensor([[-0.3964655101299286,]], device=\"cpu\", requires_grad=True)\n", + "f2 grad_antes tensor([[-2.6123950481414795,]], device=\"cpu\", requires_grad=True)\n", + "\n", + "\n", + "f1 depois tensor([[0.6632210612297058,],\n", + "[-0.1337345540523529,],\n", + "[0.5307625532150269,],\n", + "[-0.9970796704292297,],\n", + "[-0.0975991040468216,],\n", + "[0.4454692602157593,],\n", + "[-0.5158113241195679,],\n", + "[0.9124315977096558,],\n", + "[0.8074878454208374,],\n", + "[-0.9386066794395447,]], device=\"cpu\", requires_grad=True)\n", + "f2 depois tensor([[-0.3938531279563904,]], device=\"cpu\", requires_grad=True)\n", + "\n", + "\n", + "\n", + "Epoch [1/1], Loss: 1.7062\n" ] } ], @@ -570,7 +2050,7 @@ " return out\n", "\n", "device = \"cpu\"\n", - "epochs = 10\n", + "epochs = 1\n", "\n", "model = MyModel().to(device)\n", "criterion = nn.MSELoss()\n", @@ -587,11 +2067,11 @@ "for x in x_values:\n", " y_true.append(math.pow(math.sin(x), 2))\n", "\n", - "batch_size = 2\n", + "batch_size = 1000\n", "\n", "\n", "for epoch in range(epochs):\n", - " for x, target in zip(x_values, y_true):\n", + " for i, (x, target) in enumerate(zip(x_values, y_true)):\n", " x = norch.Tensor([[x] for _ in range(batch_size)]).T\n", " target = norch.Tensor([[target] for _ in range(batch_size)]).T\n", "\n", @@ -604,7 +2084,19 @@ " \n", " optimizer.zero_grad()\n", " loss.backward()\n", + " print('loss_antes', loss)\n", + "\n", + " print('f1 antes', model.fc1.bias)\n", + " print('f1 grad_antes', model.fc1.bias.grad)\n", + " print('f2 antes', model.fc2.bias)\n", + " print('f2 grad_antes', model.fc2.bias.grad)\n", + "\n", " optimizer.step()\n", + " print('\\n')\n", + "\n", + " print('f1 depois', model.fc1.bias)\n", + " print('f2 depois', model.fc2.bias)\n", + " print('\\n\\n')\n", "\n", " print(f'Epoch [{epoch + 1}/{epochs}], Loss: {loss[0]:.4f}')\n", " loss_list.append(loss[0])" @@ -612,16 +2104,16 @@ }, { "cell_type": "code", - "execution_count": 15, + "execution_count": 9, "metadata": {}, "outputs": [ { "data": { "text/plain": [ - "[nan, nan, nan, nan, nan, nan, nan, nan, nan, nan]" + "[1.7061519622802734]" ] }, - "execution_count": 15, + "execution_count": 9, "metadata": {}, "output_type": "execute_result" } @@ -632,28 +2124,46 @@ }, { "cell_type": "code", - "execution_count": 3, + "execution_count": 2, "metadata": {}, "outputs": [ { - "data": { - "text/plain": [ - "MyModel(\n", - " (fc1): Linear(input_dim=1, output_dim=10, bias=True)\n", - " (sigmoid): Sigmoid()\n", - " (fc2): Linear(input_dim=10, output_dim=1, bias=True)\n", - ")" - ] - }, - "execution_count": 3, - "metadata": {}, - "output_type": "execute_result" + "ename": "NameError", + "evalue": "name 'model' is not defined", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mNameError\u001b[0m Traceback (most recent call last)", + "Cell \u001b[0;32mIn[2], line 1\u001b[0m\n\u001b[0;32m----> 1\u001b[0m \u001b[43mmodel\u001b[49m\n", + "\u001b[0;31mNameError\u001b[0m: name 'model' is not defined" + ] } ], "source": [ "model" ] }, + { + "cell_type": "code", + "execution_count": 14, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "tensor([[-1.4443700313568115,]], device=\"cpu\", requires_grad=True)\n", + "0.1516466453264173\n" + ] + } + ], + "source": [ + "x = 0.4\n", + "input = norch.Tensor([[x]]).T\n", + "print(model(input))\n", + "print(math.pow(math.sin(x), 2))" + ] + }, { "cell_type": "markdown", "metadata": {}, @@ -663,12 +2173,12 @@ }, { "cell_type": "code", - "execution_count": 14, + "execution_count": 5, "metadata": {}, "outputs": [ { "data": { - "image/png": "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", + "image/png": "iVBORw0KGgoAAAANSUhEUgAAA0kAAAHWCAYAAACi1sL/AAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjcuNSwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy/xnp5ZAAAACXBIWXMAAA9hAAAPYQGoP6dpAABLhUlEQVR4nO3deVxV1cLG8ecAMohATiAqOZA5pmlqOV0rveWQqWiZOVvXHNO6dbNBG7TMeu2amjhUTjlkpqbmkFmaluWQetUcc4gcsxRwQoP9/rEDIVAQgbXP4ff9fPbnbPYZeNgv743HtfbaLsuyLAEAAAAAJElepgMAAAAAgJNQkgAAAAAgFUoSAAAAAKRCSQIAAACAVChJAAAAAJAKJQkAAAAAUqEkAQAAAEAqlCQAAAAASIWSBAAAAACpUJIAwEN1795dZcuWzdZ7X3nlFblcrpwN5GHKli2rBx54wHQMow4dOiSXy6X/+7//Mx0FAHIUJQkA8pjL5crStnr1atNRjejevbsKFSpkOoYjJJeQq21vvvmm6YgA4JF8TAcAgPxmxowZab6ePn26Vq5cme545cqVb+j7TJ48WUlJSdl670svvaTBgwff0PdHzunYsaNatGiR7njNmjUNpAEAz0dJAoA81rlz5zRff//991q5cmW64393/vx5FSxYMMvfp0CBAtnKJ0k+Pj7y8eE/EU5Rq1atTH8/AAA5h+l2AOBAd999t6pVq6bNmzfrH//4hwoWLKgXXnhBkvTZZ5+pZcuWKlmypPz8/BQZGalhw4YpMTExzWf8/Zqk1NePTJo0SZGRkfLz81OdOnW0cePGNO/N6Jokl8ul/v37a+HChapWrZr8/PxUtWpVLV++PF3+1atXq3bt2vL391dkZKQmTpyY49c5ffLJJ7rjjjsUEBCgYsWKqXPnzjpy5Eia1xw/flw9evRQ6dKl5efnp/DwcLVu3VqHDh1Kec2mTZt0//33q1ixYgoICFC5cuXUs2fPLOf44osvdPvtt8vf319VqlTR/PnzU547cOCAXC6X/vvf/6Z733fffSeXy6XZs2df/w+fgeRrpK6VJ3Wuhx56SEWKFFHBggV111136fPPP0/3uosXL+qVV17RrbfeKn9/f4WHhysqKko///xzutdm9jsFAO6EfyYEAIf6/fff1bx5cz3yyCPq3LmzwsLCJElTp05VoUKF9PTTT6tQoUL66quvNHToUMXFxentt9/O9HNnzZql+Ph4PfHEE3K5XHrrrbcUFRWlAwcOZDr6tG7dOs2fP199+/ZVUFCQxowZo3bt2umXX35R0aJFJUlbtmxRs2bNFB4erldffVWJiYl67bXXVLx48Rs/KX+ZOnWqevTooTp16mjEiBE6ceKE3n33XX377bfasmWLbrrpJklSu3bttHPnTg0YMEBly5bVyZMntXLlSv3yyy8pX993330qXry4Bg8erJtuukmHDh3KsFhkZN++ferQoYN69+6tbt26acqUKXrooYe0fPly/fOf/1T58uXVoEEDzZw5U0899VSa986cOVNBQUFq3bp1pt/n/PnzOnXqVLrjN910U5oRv8zySNKJEydUv359nT9/Xk8++aSKFi2qadOm6cEHH9S8efPUtm1bSVJiYqIeeOABrVq1So888ogGDhyo+Ph4rVy5Ujt27FBkZGTK972R3ykAcCQLAGBUv379rL//z3Hjxo0tSdaECRPSvf78+fPpjj3xxBNWwYIFrYsXL6Yc69atm1WmTJmUrw8ePGhJsooWLWr98ccfKcc/++wzS5K1ePHilGMvv/xyukySLF9fX2v//v0px7Zt22ZJssaOHZtyrFWrVlbBggWtI0eOpBzbt2+f5ePjk+4zM9KtWzcrMDDwqs9funTJCg0NtapVq2ZduHAh5fiSJUssSdbQoUMty7Ks06dPW5Kst99++6qftWDBAkuStXHjxkxz/V2ZMmUsSdann36aciw2NtYKDw+3atasmXJs4sSJliRr165daX6GYsWKWd26dbvm90j+v9nVtvXr1193nkGDBlmSrLVr16Yci4+Pt8qVK2eVLVvWSkxMtCzLsj788ENLkvXOO++ky5WUlJQmX1Z+pwDAnTDdDgAcys/PTz169Eh3PCAgIGU/Pj5ep06dUqNGjXT+/Hnt3r0708/t0KGDChcunPJ1o0aNJNlTsDLTtGnTNCMI1atXV3BwcMp7ExMT9eWXX6pNmzYqWbJkyutuueUWNW/ePNPPz4pNmzbp5MmT6tu3r/z9/VOOt2zZUpUqVUqZNhYQECBfX1+tXr1ap0+fzvCzkkeclixZosuXL193lpIlS6aMvEhScHCwunbtqi1btuj48eOSpIcfflj+/v6aOXNmyutWrFihU6dOZfk6o169emnlypXptipVqlx3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", "text/plain": [ "
" ] diff --git a/norch/autograd/__pycache__/functions.cpython-38.pyc b/norch/autograd/__pycache__/functions.cpython-38.pyc index 7a8db4bd40e3c40b4b366baa87af8349bdd1c6b8..d56a7979b8bb61f3a906fe1b7edf92f9d2705f87 100644 GIT binary patch delta 434 zcmaEC|JI&2l$V!_0SM&Yd8Do1$oo>begcr!!H~s}!YIj*#khcJAww+_kk6dLl+9FR zQ^Hcin8ljKmcA&XC5K!kog=!coFi!_>^^0#sYe zRKg8n*Rs^GEZ_l~$-0nyMV^yC3W-d1;MJKdDv{6T z4HPc&nY=()eDXqx3^pGS*LyO*WSp=UkU1ZSVLDlhyx=+yNd6S|1JOCIF!{Kw|K|BJ%8bGRK<+LSwE=LmMCFb!22Fk|Ek609oSkqmP}Lz6mBEvp hpF delta 432 zcmaEB|Ja^4l$V!_0SK-haZg*Zk@ux=eGib=!H~s}!YIj*#aPP(WHY5OWiu67lrYyY zX0c?kX0d_fni*lL7O*d55Miig&hrC`a1@8MGo&%5FsHDzaFlS?Ff}u}0M*tqm2iRB zwJbF(3%J2%vMyv|WT;_Wz*EDrkg=Azh8b)gE70f#yp!uhPIB`Vu>##vBn~7dM~LoV z7J7Q)ErHaSf^P1pk{Is=G{S%3rwBL{Pl$K;PfB9k3>btcP8_N;zsorZ2LZ(op=b=6>?3b01@cc3 a$XP`oyNf{P7lF(u3YyHwD?WLbyfFY`j9~)+ diff --git a/norch/autograd/functions.py b/norch/autograd/functions.py index fe31be9..74594fe 100644 --- a/norch/autograd/functions.py +++ b/norch/autograd/functions.py @@ -132,7 +132,7 @@ class SumBackward: self.keepdim = keepdim def backward(self, gradient): - input_shape = self.input[0].shape + input_shape = self.input[0].shape.copy() if self.axis == -1: # If axis is None, sum reduces the tensor to a scalar. grad_output = float(gradient.tensor.contents.data[0]) * self.input[0].ones_like() diff --git a/norch/nn/modules/__pycache__/linear.cpython-38.pyc b/norch/nn/modules/__pycache__/linear.cpython-38.pyc index 15ee46288c31ddb6189927e914a6e86122f654aa..583d8b658a4b0af8fdc5714cb9e07d976ae544c7 100644 GIT binary patch delta 350 zcmXv}!Ab)`3{5iAal7r7whETs^dK%`k6r~&BBcseguRsF>}sX1GHWlg#Y>?l!7_i~ zC-?_`g4g{4KSB^Q)h6WSz{R)?tVtLJbjVZw?7+ z(qdMQgqFHAgGg$n*SMGFAR$uc5S9zo9i{Qm^siPn&dPD|Us-qYwcpN#IeL^i+zL(C zr11rF7^qZfu-kaKE(@8(F~|CH z+Jq^k{z{orPJAKuLNZn7Sg|Lx+o&_mJO*IPCi!Ha>3yr^R9dP}jlYjNqcn}pz{C$G f#J)WhH|<)*^Njng7nTZjcdsYo*to62eu~3i4YNbu delta 276 zcmX@eF@b|Ol$V!_0SN9dc2CQk$XgS~4B`Pnu?~<(Wr$)-VTfXCXGmjAVN79a;fP{R zVNPLbVTfW$VNGEJ(yZ+aEDTX>!3>)0x7d6#^HLLwl3`|m%mJ}sxY%LhD_y=4mKugE z<`kxMrWD3r#>s|^;e5=uSc+3~(uz2Nii@}=&t^>H1G|cmi;)coMHnXwGij@c0A+z% z8EO~>Ks2K`LoHJc6VPTxKTW10ZjfG{$t6r0ygWc=F$<7jU=(1Syq0Mbqrl_@X1U3Y i%;{1*>>MmbAUQux)>~}3`6;D2sYOgce$-?p7JC4A2r^m# diff --git a/norch/nn/modules/linear.py b/norch/nn/modules/linear.py index 2d61023..b90d845 100644 --- a/norch/nn/modules/linear.py +++ b/norch/nn/modules/linear.py @@ -2,15 +2,23 @@ from ..module import Module from ..parameter import Parameter class Linear(Module): - def __init__(self, input_dim, output_dim): + def __init__(self, input_dim, output_dim, bias=True): super().__init__() self.input_dim = input_dim self.output_dim = output_dim self.weight = Parameter(shape=[self.output_dim, self.input_dim]) - self.bias = Parameter(shape=[self.output_dim, 1]) + + if bias: + self.bias = Parameter(shape=[self.output_dim, 1]) + else: + self.bias = None def forward(self, x): - z = self.weight @ x + self.bias + if self.bias: + z = self.weight @ x + self.bias + else: + z = self.weight @ x + return z def inner_repr(self): From 8e8780d2a6f3f4506e353a254a42fdd38b1c16af Mon Sep 17 00:00:00 2001 From: lucasdelimanogueira Date: Sat, 18 May 2024 15:25:39 -0300 Subject: [PATCH 14/28] fix add broadcasted backward grad wrong values copy pointer data --- test.py | 92 --------------------------------------------------------- 1 file changed, 92 deletions(-) delete mode 100644 test.py diff --git a/test.py b/test.py deleted file mode 100644 index 92d779d..0000000 --- a/test.py +++ /dev/null @@ -1,92 +0,0 @@ -import norch - -W1 = norch.Tensor([[1], [2], [3], [4], [5], [6], [7], [8], [9], [10]], requires_grad=True) # A has shape 10x1 -X = norch.Tensor([[1, 2, 3, 4, 5]]) # B has shape 1x5 -B1 = norch.Tensor([[1, 2, 3, 4, 5], [2, 1, 2, 3, 4], [3, 1,2,3, 4], [4, 1, 1, 1, 1,], [5,1,1,1,1], [6,1,1,1,1], [7,1,1,1,1], [8,1,1,1,1], [9,1,1,1,1], [10,1,1,1,1]], requires_grad=True) -B1 = norch.Tensor([[1], [2], [3], [4], [5], [6], [7], [8], [9], [10]], requires_grad=True) # A has shape 10x1 - -W2 = norch.Tensor([[1], [2], [3], [4], [5], [6], [7], [8], [9], [10]], requires_grad=True).T -B2 = norch.Tensor([[1, 2, 3, 4, 5]], requires_grad=True) - - -Z1 = W1 @ X + B1 - -# Perform matrix multiplication - -Z2 = W2 @ Z1 * B2 -print(Z2.shape) - -l = Z2.sum() -l.backward() -print(l) - -print("Resulting matrix shape:", B1.grad) - - -"""import norch -import norch.nn as nn -import norch.optim as optim -import random -import math - -random.seed(1) - -class MyModel(nn.Module): - def __init__(self): - super(MyModel, self).__init__() - self.fc1 = nn.Linear(1, 10) - self.sigmoid = nn.Sigmoid() - self.fc2 = nn.Linear(10, 1) - - def forward(self, x): - out = self.fc1(x) - out = self.sigmoid(out) - out = self.fc2(out) - - return out - -device = "cpu" -epochs = 1 - -model = MyModel().to(device) -criterion = nn.MSELoss() -optimizer = optim.SGD(model.parameters(), lr=0.001) -loss_list = [] - -x_values = [[0., 1], [0., 1], [0., 1], [0., 1], [0., 1], [0., 1], [0., 1], [0., 1], [0., 1], [0., 1]] - -y_true = [] -for x in x_values: - y_true.append([math.pow(math.sin(x[0]), 2), math.pow(math.sin(x[1]), 2)]) - -batch_size = 5 - - -for epoch in range(epochs): - for x, target in zip(x_values, y_true): - x = norch.Tensor([x]) - target = norch.Tensor([target]) - - x = x.to(device) - target = target.to(device) - - outputs = model(x) - - loss = criterion(outputs, target) - - optimizer.zero_grad() - loss.backward() - optimizer.step() - - print('loss', loss, '\n\n') - print('weight', model.fc1.weight, '\n\n') - print('bias', model.fc1.bias, '\n\n') - - - if math.isnan(loss[0]): - print("Kkkkkkkkk") - exit() - - #print(f'Epoch [{epoch + 1}/{epochs}], Loss: {loss[0]:.4f}') - loss_list.append(loss[0]) -""" \ No newline at end of file From c808a49a3010a8eae1905e8ac6e03951ff02f922 Mon Sep 17 00:00:00 2001 From: lucasdelimanogueira Date: Sat, 18 May 2024 17:59:14 -0300 Subject: [PATCH 15/28] dataset, dataloader and mnist --- examples/train.ipynb | 19589 +++++++++++++++- norch/__init__.py | 1 + norch/__pycache__/__init__.cpython-38.pyc | Bin 342 -> 362 bytes norch/__pycache__/tensor.cpython-38.pyc | Bin 15627 -> 16110 bytes .../__pycache__/functions.cpython-38.pyc | Bin 8173 -> 8173 bytes norch/datasets/__init__.py | 1 + norch/datasets/mnist.py | 90 + norch/tensor.py | 30 +- norch/utils/__init__.py | 3 +- norch/utils/__pycache__/utils.cpython-38.pyc | Bin 759 -> 3901 bytes norch/utils/data/__init__.py | 4 + norch/utils/data/batch.py | 13 + norch/utils/data/dataloader.py | 20 + norch/utils/data/dataset.py | 74 + norch/utils/data/example.py | 65 + norch/utils/utils.py | 108 +- test.py | 75 + 17 files changed, 19257 insertions(+), 816 deletions(-) create mode 100644 norch/datasets/__init__.py create mode 100644 norch/datasets/mnist.py create mode 100644 norch/utils/data/__init__.py create mode 100644 norch/utils/data/batch.py create mode 100644 norch/utils/data/dataloader.py create mode 100644 norch/utils/data/dataset.py create mode 100644 norch/utils/data/example.py create mode 100644 test.py diff --git a/examples/train.ipynb b/examples/train.ipynb index 67d7c2d..b29a29d 100644 --- a/examples/train.ipynb +++ b/examples/train.ipynb @@ -26,7 +26,64 @@ }, { "cell_type": "code", - "execution_count": 11, + "execution_count": 2, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Download train-images-idx3-ubyte.gz from http://yann.lecun.com/exdb/mnist/train-images-idx3-ubyte.gz to .data/mnist/train-images-idx3-ubyte.gz\n", + "Downloading... 100% | [==================================================] | Done !\n", + ".data/mnist/mnist-data-py\n", + "Extracting... | NOTE: gzip files are not extracted, and moved to .data/mnist/mnist-data-py | Done !\n", + "Download train-labels-idx1-ubyte.gz from http://yann.lecun.com/exdb/mnist/train-labels-idx1-ubyte.gz to .data/mnist/train-labels-idx1-ubyte.gz\n", + "Downloading... 100% | [==================================================] | Done !\n", + ".data/mnist/mnist-data-py\n", + "Extracting... | NOTE: gzip files are not extracted, and moved to .data/mnist/mnist-data-py | Done !\n", + "Download t10k-labels-idx1-ubyte.gz from http://yann.lecun.com/exdb/mnist/t10k-labels-idx1-ubyte.gz to .data/mnist/t10k-labels-idx1-ubyte.gz\n", + "Downloading... 100% | [==================================================] | Done !\n", + ".data/mnist/mnist-data-py\n", + "Extracting... | NOTE: gzip files are not extracted, and moved to .data/mnist/mnist-data-py | Done !\n", + "Download t10k-images-idx3-ubyte.gz from http://yann.lecun.com/exdb/mnist/t10k-images-idx3-ubyte.gz to .data/mnist/t10k-images-idx3-ubyte.gz\n", + "Downloading... 100% | [==================================================] | Done !\n", + ".data/mnist/mnist-data-py\n", + "Extracting... | NOTE: gzip files are not extracted, and moved to .data/mnist/mnist-data-py | Done !\n" + ] + }, + { + "ename": "BadGzipFile", + "evalue": "Not a gzipped file (b' 6\u001b[0m train_data, test_data \u001b[38;5;241m=\u001b[39m \u001b[43mnorch\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mdatasets\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mMNIST\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43msplits\u001b[49m\u001b[43m(\u001b[49m\u001b[43mtransform\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43m \u001b[49m\u001b[43mreshape\u001b[49m\u001b[43m)\u001b[49m\n", + "File \u001b[0;32m~/Documentos/recreate_pytorch/PyNorch/norch/datasets/mnist.py:75\u001b[0m, in \u001b[0;36mMNIST.splits\u001b[0;34m(cls, root, train_data, train_label, test_data, test_label, **kwargs)\u001b[0m\n\u001b[1;32m 73\u001b[0m test_data \u001b[38;5;241m=\u001b[39m os\u001b[38;5;241m.\u001b[39mpath\u001b[38;5;241m.\u001b[39mjoin(path, test_data)\n\u001b[1;32m 74\u001b[0m test_label \u001b[38;5;241m=\u001b[39m os\u001b[38;5;241m.\u001b[39mpath\u001b[38;5;241m.\u001b[39mjoin(path, test_label)\n\u001b[0;32m---> 75\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[43mMNIST\u001b[49m\u001b[43m(\u001b[49m\u001b[43mtrain_data\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mtrain_label\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43mkwargs\u001b[49m\u001b[43m)\u001b[49m, MNIST(test_data, test_label, \u001b[38;5;241m*\u001b[39m\u001b[38;5;241m*\u001b[39mkwargs)\n", + "File \u001b[0;32m~/Documentos/recreate_pytorch/PyNorch/norch/datasets/mnist.py:39\u001b[0m, in \u001b[0;36mMNIST.__init__\u001b[0;34m(self, path_data, path_label, transform)\u001b[0m\n\u001b[1;32m 38\u001b[0m \u001b[38;5;28;01mdef\u001b[39;00m \u001b[38;5;21m__init__\u001b[39m(\u001b[38;5;28mself\u001b[39m, path_data, path_label, transform\u001b[38;5;241m=\u001b[39m\u001b[38;5;28;01mNone\u001b[39;00m):\n\u001b[0;32m---> 39\u001b[0m data \u001b[38;5;241m=\u001b[39m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43m_load_mnist\u001b[49m\u001b[43m(\u001b[49m\u001b[43mpath_data\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mheader_size\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[38;5;241;43m16\u001b[39;49m\u001b[43m)\u001b[49m\u001b[38;5;241m.\u001b[39mreshape((\u001b[38;5;241m-\u001b[39m\u001b[38;5;241m1\u001b[39m, \u001b[38;5;241m28\u001b[39m, \u001b[38;5;241m28\u001b[39m))\n\u001b[1;32m 40\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m transform \u001b[38;5;129;01mis\u001b[39;00m \u001b[38;5;129;01mnot\u001b[39;00m \u001b[38;5;28;01mNone\u001b[39;00m:\n\u001b[1;32m 41\u001b[0m data \u001b[38;5;241m=\u001b[39m transform(data)\n", + "File \u001b[0;32m~/Documentos/recreate_pytorch/PyNorch/norch/datasets/mnist.py:47\u001b[0m, in \u001b[0;36mMNIST._load_mnist\u001b[0;34m(self, path, header_size)\u001b[0m\n\u001b[1;32m 45\u001b[0m \u001b[38;5;28;01mdef\u001b[39;00m \u001b[38;5;21m_load_mnist\u001b[39m(\u001b[38;5;28mself\u001b[39m, path, header_size):\n\u001b[1;32m 46\u001b[0m \u001b[38;5;28;01mwith\u001b[39;00m gzip\u001b[38;5;241m.\u001b[39mopen(path, \u001b[38;5;124m'\u001b[39m\u001b[38;5;124mrb\u001b[39m\u001b[38;5;124m'\u001b[39m) \u001b[38;5;28;01mas\u001b[39;00m f:\n\u001b[0;32m---> 47\u001b[0m \u001b[43mf\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mseek\u001b[49m\u001b[43m(\u001b[49m\u001b[43mheader_size\u001b[49m\u001b[43m)\u001b[49m \u001b[38;5;66;03m# Skip the header\u001b[39;00m\n\u001b[1;32m 48\u001b[0m data \u001b[38;5;241m=\u001b[39m f\u001b[38;5;241m.\u001b[39mread()\n\u001b[1;32m 50\u001b[0m data_list \u001b[38;5;241m=\u001b[39m \u001b[38;5;28mlist\u001b[39m(data)\n", + "File \u001b[0;32m/usr/lib/python3.8/gzip.py:384\u001b[0m, in \u001b[0;36mGzipFile.seek\u001b[0;34m(self, offset, whence)\u001b[0m\n\u001b[1;32m 382\u001b[0m \u001b[38;5;28;01melif\u001b[39;00m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mmode \u001b[38;5;241m==\u001b[39m READ:\n\u001b[1;32m 383\u001b[0m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_check_not_closed()\n\u001b[0;32m--> 384\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43m_buffer\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mseek\u001b[49m\u001b[43m(\u001b[49m\u001b[43moffset\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mwhence\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 386\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39moffset\n", + "File \u001b[0;32m/usr/lib/python3.8/_compression.py:143\u001b[0m, in \u001b[0;36mDecompressReader.seek\u001b[0;34m(self, offset, whence)\u001b[0m\n\u001b[1;32m 141\u001b[0m \u001b[38;5;66;03m# Read and discard data until we reach the desired position.\u001b[39;00m\n\u001b[1;32m 142\u001b[0m \u001b[38;5;28;01mwhile\u001b[39;00m offset \u001b[38;5;241m>\u001b[39m \u001b[38;5;241m0\u001b[39m:\n\u001b[0;32m--> 143\u001b[0m data \u001b[38;5;241m=\u001b[39m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mread\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;28;43mmin\u001b[39;49m\u001b[43m(\u001b[49m\u001b[43mio\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mDEFAULT_BUFFER_SIZE\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43moffset\u001b[49m\u001b[43m)\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 144\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;129;01mnot\u001b[39;00m data:\n\u001b[1;32m 145\u001b[0m \u001b[38;5;28;01mbreak\u001b[39;00m\n", + "File \u001b[0;32m/usr/lib/python3.8/gzip.py:479\u001b[0m, in \u001b[0;36m_GzipReader.read\u001b[0;34m(self, size)\u001b[0m\n\u001b[1;32m 475\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_new_member:\n\u001b[1;32m 476\u001b[0m \u001b[38;5;66;03m# If the _new_member flag is set, we have to\u001b[39;00m\n\u001b[1;32m 477\u001b[0m \u001b[38;5;66;03m# jump to the next member, if there is one.\u001b[39;00m\n\u001b[1;32m 478\u001b[0m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_init_read()\n\u001b[0;32m--> 479\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;129;01mnot\u001b[39;00m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43m_read_gzip_header\u001b[49m\u001b[43m(\u001b[49m\u001b[43m)\u001b[49m:\n\u001b[1;32m 480\u001b[0m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_size \u001b[38;5;241m=\u001b[39m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_pos\n\u001b[1;32m 481\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;124mb\u001b[39m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124m\"\u001b[39m\n", + "File \u001b[0;32m/usr/lib/python3.8/gzip.py:427\u001b[0m, in \u001b[0;36m_GzipReader._read_gzip_header\u001b[0;34m(self)\u001b[0m\n\u001b[1;32m 424\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28;01mFalse\u001b[39;00m\n\u001b[1;32m 426\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m magic \u001b[38;5;241m!=\u001b[39m \u001b[38;5;124mb\u001b[39m\u001b[38;5;124m'\u001b[39m\u001b[38;5;130;01m\\037\u001b[39;00m\u001b[38;5;130;01m\\213\u001b[39;00m\u001b[38;5;124m'\u001b[39m:\n\u001b[0;32m--> 427\u001b[0m \u001b[38;5;28;01mraise\u001b[39;00m BadGzipFile(\u001b[38;5;124m'\u001b[39m\u001b[38;5;124mNot a gzipped file (\u001b[39m\u001b[38;5;132;01m%r\u001b[39;00m\u001b[38;5;124m)\u001b[39m\u001b[38;5;124m'\u001b[39m \u001b[38;5;241m%\u001b[39m magic)\n\u001b[1;32m 429\u001b[0m (method, flag,\n\u001b[1;32m 430\u001b[0m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_last_mtime) \u001b[38;5;241m=\u001b[39m struct\u001b[38;5;241m.\u001b[39munpack(\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124m" ] diff --git a/norch/__init__.py b/norch/__init__.py index ca7be0b..3eb0b7f 100644 --- a/norch/__init__.py +++ b/norch/__init__.py @@ -2,6 +2,7 @@ from norch.tensor import Tensor from .nn import * from .optim import * from .utils import * +from .datasets import * __version__ = "0.0.1" __author__ = 'Lucas de Lima Nogueira' diff --git a/norch/__pycache__/__init__.cpython-38.pyc b/norch/__pycache__/__init__.cpython-38.pyc index d1722766eae308a42b9c2c3e679b5faf1b625dbc..8202d1d36b6b390a36dae101fa23ac1e0b542235 100644 GIT binary patch delta 96 zcmcb{^oof$l$V!_0SJ@@JkzdEI5&Oi?!9sjM77rr_1TwKOvM>SwF~SrS delta 77 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 diff --git a/norch/autograd/__pycache__/functions.cpython-38.pyc b/norch/autograd/__pycache__/functions.cpython-38.pyc index d56a7979b8bb61f3a906fe1b7edf92f9d2705f87..0a7fe5b867d4774623f8e809fbb694066446922d 100644 GIT binary patch delta 19 ZcmaEB|JI%>l$V!_0SMwgZsd9_4*)!51?T_( delta 19 ZcmaEB|JI%>l$V!_0SM&YZRC0^4*)vV1*HH0 diff --git a/norch/datasets/__init__.py b/norch/datasets/__init__.py new file mode 100644 index 0000000..fe34b11 --- /dev/null +++ b/norch/datasets/__init__.py @@ -0,0 +1 @@ +from .mnist import * \ No newline at end of file diff --git a/norch/datasets/mnist.py b/norch/datasets/mnist.py new file mode 100644 index 0000000..ff11edf --- /dev/null +++ b/norch/datasets/mnist.py @@ -0,0 +1,90 @@ +import gzip +import os +import norch +from norch.utils.data import Dataset +import numpy as np + +class MNIST(Dataset): + """ + Loads training, validation, and test partitions of the mnist dataset + (http://yann.lecun.com/exdb/mnist/). If the data is not already contained in data_dir, it will + try to download it. + + This dataset contains 60000 training examples, and 10000 test examples of handwritten digits + in {0, ..., 9} and corresponding labels. Each handwritten image has an "original" dimension of + 28x28x1, and is stored row-wise as a string of 784x1 bytes. Pixel values are in range 0 to 255 + (inclusive). + + Args: + data_dir: String. Relative or absolute path of the dataset. + devel_size: Integer. Size of the development (validation) dataset partition. + + Returns: + X_train: float64 numpy array with shape [784, 60000-devel_size] with values in [0, 1]. + Y_train: uint8 numpy array with shape [60000-devel_size]. Labels. + X_devel: float64 numpy array with shape [784, devel_size] with values in [0, 1]. + Y_devel: uint8 numpy array with shape [devel_size]. Labels. + X_test: float64 numpy array with shape [784, 10000] with values in [0, 1]. + Y_test: uint8 numpy array with shape [10000]. Labels. + """ + + urls = ['https://ossci-datasets.s3.amazonaws.com/mnist/train-images-idx3-ubyte.gz', + 'https://ossci-datasets.s3.amazonaws.com/mnist/train-labels-idx1-ubyte.gz', + 'https://ossci-datasets.s3.amazonaws.com/mnist/t10k-images-idx3-ubyte.gz', + 'https://ossci-datasets.s3.amazonaws.com/mnist/t10k-labels-idx1-ubyte.gz',] + name = 'mnist-data-py' + dirname = 'mnist' + + def __init__(self, path_data, path_label, transform=None, target_transform=None): + self.data = self._load_mnist(path_data, header_size=16).reshape((-1, 28, 28)) + self.labels = self._load_mnist(path_label, header_size=8) + self.transform = transform + self.target_transform = target_transform + + def _load_mnist(self, path, header_size): + with gzip.open(path, 'rb') as f: + data = np.frombuffer(f.read(), np.uint8, offset=header_size) + return np.asarray(data, dtype=np.uint8) + + + @classmethod + def splits(cls, root='.data', train_data='train-images-idx3-ubyte.gz', train_label='train-labels-idx1-ubyte.gz', + test_data='t10k-images-idx3-ubyte.gz', test_label='t10k-labels-idx1-ubyte.gz', **kwargs): + r""" + Loads training and test partitions of the [mnist dataset](https://www.cs.toronto.edu/~kriz/cifar.html). If + the data is not already contained in the ``root`` folder, it will download it. + + Args: + root (str): relative or absolute path of the dataset. + + Returns: + tuple(Dataset): training and testing datasets + """ + path = os.path.join(root, cls.dirname, cls.name) + if not os.path.isdir(path): + path = cls.download(root) + train_data = os.path.join(path, train_data) + train_label = os.path.join(path, train_label) + test_data = os.path.join(path, test_data) + test_label = os.path.join(path, test_label) + return MNIST(train_data, train_label, **kwargs), MNIST(test_data, test_label, **kwargs) + + def __getitem__(self, item): + data = self.data[item].tolist() + label = self.labels[item].tolist() + + if self.transform is not None: + data = self.transform(data) + + if self.target_transform is not None: + label = self.target_transform(label) + + + + return data, label + + def __setitem__(self, key, value): + self.data[key], self.labels[key] = value + + def __len__(self): + return len(self.data) diff --git a/norch/tensor.py b/norch/tensor.py index 807c290..8f3ae12 100644 --- a/norch/tensor.py +++ b/norch/tensor.py @@ -19,13 +19,18 @@ class Tensor: def __init__(self, data=None, device="cpu", requires_grad=False): if data != None: + if isinstance(data, (float, int)): + data = [data] + data, shape = self.flatten(data) - self.data_ctype = (ctypes.c_float * len(data))(*data) - self.shape_ctype = (ctypes.c_int * len(shape))(*shape) + + self.shape = shape.copy() + + self.data_ctype = (ctypes.c_float * len(data))(*data.copy()) + self.shape_ctype = (ctypes.c_int * len(shape))(*shape.copy()) self.ndim_ctype = ctypes.c_int(len(shape)) self.device_ctype = device.encode('utf-8') - self.shape = shape self.ndim = len(shape) self.device = device @@ -109,6 +114,25 @@ class Tensor: return result_data def reshape(self, new_shape): + # Calculate the total number of elements in the tensor + total_elements = self.numel + + # Check for the presence of -1 in new_shape + if new_shape.count(-1) > 1: + raise ValueError("Only one dimension can be inferred (set to -1).") + + inferred_dim = None + known_dims_product = 1 + for dim in new_shape: + if dim == -1: + inferred_dim = dim + else: + known_dims_product *= dim + + # Calculate the inferred dimension if -1 is present + if inferred_dim == -1: + inferred_dim_size = total_elements // known_dims_product + new_shape = [inferred_dim_size if dim == -1 else dim for dim in new_shape] new_shape_ctype = (ctypes.c_int * len(new_shape))(*new_shape) new_ndim_ctype = ctypes.c_int(len(new_shape)) diff --git a/norch/utils/__init__.py b/norch/utils/__init__.py index 90f60fd..c6adfd1 100644 --- a/norch/utils/__init__.py +++ b/norch/utils/__init__.py @@ -1 +1,2 @@ -from .utils import * \ No newline at end of file +from .utils import * +from .data import * \ No newline at end of file diff --git a/norch/utils/__pycache__/utils.cpython-38.pyc b/norch/utils/__pycache__/utils.cpython-38.pyc index 95d3b4582f73d44896121e8f25e8606d2085c37f..d78c80a01f3b3ae50ea607c841e71b1083517427 100644 GIT binary patch literal 3901 zcmbVP&5s*N6|d@U+wC6DWHQWdlHFC*h}}($GIkPn7wsqnSh62T;ANAnKxQM;<0^YP z)9!Yv%R7nF2BZlB4iVzE5<(gubK%5az@O1qF3dl`As6_)>h^pwaG@hrB|NLa|t5wGSPMzbAjn4P*rduck6Fg$wqUK$0`c~I6wcWK%?Q|VeSGpBp z3HurAR>g{_i0U)mt%-BO6?L>+u_`>VgjQWN#4@Bj!SAu=`2*Ntoj#|ke5mcZ8@#x>hFv7B3fv>rL|!8-UC9z_e9R$eWv=Cw&24iYtbVdYlq zdd_<TvFya2k?W-5You!( zh56P7eJkxx@twx|CePQ;GNC9`FL=Hb#@L9SiLY6p2(fAjg;Bn66oIlM8Rr$aIh4>^ zIkse(mY8)7v|PL?HJH=jj@94|{;H*D;NaIN`UJGaL$Va)AK*>DLZR7111MoVw`O)` zKeVuU_MFcgZD(9Kd)6Nw&MFz8x8tf?S!LgWOl8hFi#wt^tLkdz>_Hl%HSJPQ)LutO zgu7>n`g4Hv!P~Q1R@L>aw#UECW>ur3MjEu2k-WEObM|QKr|ez!8N17V#zY06Sqk0* z=I$iBag+oCupNYv^al!f4PyWEyFY4q2A-d)a2Uoxgwb?YszPF0>v6i-Zl^lv?<8MJ zHHebk7U0l+G?A$elQ?a^|IUr~Z@hQ2ElLv`O6rM~}Uk%eu zW0MjJ!~WG&tLCOZ4v_Nz#O;`F!tdT++K+{lf=hp64$E0-l z4*(-%Y?VRUJF+=lSp*&KwOXy_Qtm2g;+E$2Q0lz4Edv3<$emDu`@6>E3vyobs`irr%L~GJtf~n{_$(@Hu z80YR*kjgk10gJ&_O0G1W+$EL8Al4cG9MiSQw1LkNhE_^h0v&EvpIb8z8a$HLPF54k7>6%AO#K!5EU_}@;vAvd z++uBlW$zl#AaJDXvNt)l1I6h-zq&UG&Y|DR%hf#Ru`P1?j z7&#qU?63(p&V)3H%Aj`C+sKcX+&>VgbN=&}{5uE4#|Jk}|H1V9^3&_Hr#CjgC1!7d zZl2zpO>NZEH5B*pZJR#kNB+|rvvg`bolU(%PV&?h=xA1Q8~dME>8=^*+{FoI0-ie* z8nr<(OZoB%h<59TXv?bwTEIAF^K;MaN}<9VOVvj>gQ4u!=$?W&c2^gG0)?1;=(=R7 zHwxpjmCAm%Va7^{Q#_Nz(n!6@DchPR}H~EVFhVAk-I>#I%apK@20;0pqkj^zU zGNkiGkPH!y;B+U$;e|Gl#yVz-TU+J~EV`lx72LKymLU5m3dSiBJbb1-gWD4=%YO$F zyC>2!bNnbQC6tk92>dYwAHonP&vDE{5F-nm7niO70GBM>q9~?E2FlUX&K_ARS0Up6 zV-;qL&!YS|xP{;~xgy?gR@9GC`__. + + """ + @classmethod + def fromlist(cls, values, fields): + """ + Add an example from a list of data with respect to the fields. + + Args: + values: raw data + fields (tuple(string, Field)): fields to preprocess the data on + + Returns: + None + """ + example = cls() + for (value, field) in zip(values, fields): + assert len(field) == 2, f"expected a field template similar to \ + ('name_field', Field()) but got {format(field)}" + assert isinstance(field[1], Field), f"expected a field template similar to \ + ('name_field', Field()) but got {format(field)}" + name = field[0] + value = field[1].process(value) + setattr(example, name, value) + + return example diff --git a/norch/utils/utils.py b/norch/utils/utils.py index 1652e16..f8b9b30 100644 --- a/norch/utils/utils.py +++ b/norch/utils/utils.py @@ -1,4 +1,9 @@ import random +import requests +import tarfile +import zipfile +import shutil +import os def generate_random_list(shape): """ @@ -11,4 +16,105 @@ def generate_random_list(shape): if len(inner_shape) == 0: return [random.uniform(-1, 1) for _ in range(shape[0])] else: - return [generate_random_list(inner_shape) for _ in range(shape[0])] \ No newline at end of file + return [generate_random_list(inner_shape) for _ in range(shape[0])] + + +def download_from_url(url, save_path, chunk_size=128): + """Download a file from an URL. + + Original answer from https://stackoverflow.com/questions/9419162/download-returned-zip-file-from-url + + Args: + url (str): path to the URL. + save_path (str): path to the saving directory. + chunk_size (int): download chunk. + + Returns: + None + """ + response = requests.get(url, stream=True) + total = response.headers.get('content-length') + with open(save_path, 'wb') as f: + if total is None: + f.write(response.content) + else: + downloaded = 0 + total = int(total) + for data in response.iter_content(chunk_size=max(int(total / 1000), 1024 * 1024)): + downloaded += len(data) + f.write(data) + progress_bar(downloaded, total, "Downloading...") + + +def extract_to_dir(filename, dirpath='.'): + # Does not create folder twice with the same name + name, ext = os.path.splitext(filename) + # if os.path.basename(name) == os.path.basename(dirpath): + # dirpath = '.' + # Extract + print(dirpath) + print("Extracting...", end="") + if tarfile.is_tarfile(filename): + tarfile.open(filename, 'r').extractall(dirpath) + elif zipfile.is_zipfile(filename): + zipfile.ZipFile(filename, 'r').extractall(dirpath) + elif ext == '.gz': + if not os.path.exists(dirpath): + os.mkdir(dirpath) + shutil.move(filename, os.path.join(dirpath, os.path.basename(filename))) + print(f" | NOTE: gzip files are not extracted, and moved to {dirpath}", end="") + # Return the path where the file was extracted + print(" | Done !") + return os.path.abspath(dirpath) + +def progress_bar(current_index, max_index, prefix=None, suffix=None, start_time=None): + """Display a progress bar and duration. + + Args: + current_index (int): current state index (or epoch number). + max_index (int): maximal numbers of state. + prefix (str, optional): prefix of the progress bar. The default is None. + suffix (str, optional): suffix of the progress bar. The default is None. + start_time (float, optional): starting time of the progress bar. If not None, it will display the time + spent from the beginning to the current state. The default is None. + + Returns: + None. Display the progress bar in the console. + """ + # Add a prefix to the progress bar + prefix = "" if prefix is None else str(prefix) + " " + + # Get the percentage + percentage = current_index * 100 // max_index + loading = "[" + "=" * (percentage // 2) + " " * (50 - percentage // 2) + "]" + progress_display = "\r{0}{1:3d}% | {2}".format(prefix, percentage, loading) + + # Add a suffix to the progress bar + progress_display += "" if suffix is None else " | " + str(suffix) + + # Add a timer + if start_time is not None: + time_min, time_sec = get_time(start_time, time.time()) + time_display = " | Time: {0}m {1}s".format(time_min, time_sec) + progress_display += time_display + + # Print the progress bar + # TODO: return a string instead + print(progress_display, end="{}".format("" if current_index < max_index else " | Done !\n")) + +def get_time(start_time, end_time): + """Get ellapsed time in minutes and seconds. + + Args: + start_time (float): strarting time + end_time (float): ending time + + Returns: + elapsed_mins (float): elapsed time in minutes + elapsed_secs (float): elapsed time in seconds. + """ + elapsed_time = end_time - start_time + elapsed_mins = int(elapsed_time / 60) + elapsed_secs = int(elapsed_time - (elapsed_mins * 60)) + + return elapsed_mins, elapsed_secs diff --git a/test.py b/test.py new file mode 100644 index 0000000..dfe3aae --- /dev/null +++ b/test.py @@ -0,0 +1,75 @@ +import norch +from norch.utils.data.dataloader import Dataloader +import norch +import norch.nn as nn +import norch.optim as optim +import random +random.seed(1) + +to_tensor = lambda x: norch.Tensor(x) +reshape = lambda x: x.reshape([-1, 784]) +transform = lambda x: reshape(to_tensor(x)) +target_transform = lambda x: to_tensor(x) + +train_data, test_data = norch.datasets.MNIST.splits(transform=transform, target_transform=target_transform) +sample, _ = train_data[0] + +BATCH_SIZE = 100 + +train_loader = Dataloader(train_data, batch_size = BATCH_SIZE) + +class MyModel(nn.Module): + def __init__(self): + super(MyModel, self).__init__() + self.fc1 = nn.Linear(784, 10) + self.sigmoid = nn.Sigmoid() + self.fc2 = nn.Linear(10, 1) + + def forward(self, x): + out = self.fc1(x) + out = self.sigmoid(out) + out = self.fc2(out) + + return out + +device = "cpu" +epochs = 10 + +model = MyModel().to(device) +criterion = nn.MSELoss() +optimizer = optim.SGD(model.parameters(), lr=0.001) +loss_list = [] + + +for epoch in range(epochs): + for idx, batch in enumerate(train_loader): + x, target = batch + + x = x.T + target = target.T + + x = x.to(device) + target = target.to(device) + + outputs = model(x) + + loss = criterion(outputs, target) + + optimizer.zero_grad() + loss.backward() + print('loss_antes', loss) + + print('f1 antes', model.fc1.bias) + print('f1 grad_antes', model.fc1.bias.grad) + print('f2 antes', model.fc2.bias) + print('f2 grad_antes', model.fc2.bias.grad) + + optimizer.step() + print('\n') + + print('f1 depois', model.fc1.bias) + print('f2 depois', model.fc2.bias) + print('\n\n') + + print(f'Epoch [{epoch + 1}/{epochs}], Loss: {loss[0]:.4f}') + loss_list.append(loss[0]) \ No newline at end of file From cc483417dc2c3a09a95786e537010f454c745993 Mon Sep 17 00:00:00 2001 From: lucasdelimanogueira Date: Mon, 20 May 2024 12:50:45 -0300 Subject: [PATCH 16/28] max min and equal operations --- build/cpu.o | Bin 10848 -> 13776 bytes build/cuda.cu.o | Bin 130552 -> 134232 bytes build/tensor.o | Bin 43304 -> 48712 bytes norch/__pycache__/tensor.cpython-38.pyc | Bin 16110 -> 17577 bytes .../__pycache__/functions.cpython-38.pyc | Bin 8173 -> 8929 bytes norch/autograd/functions.py | 18 ++ norch/csrc/cpu.cpp | 68 +++++++ norch/csrc/cpu.h | 3 + norch/csrc/cuda.cu | 22 +++ norch/csrc/cuda.h | 3 + norch/csrc/tensor.cpp | 184 +++++++++++++++++- norch/csrc/tensor.h | 3 + norch/libtensor.so | Bin 172864 -> 181968 bytes norch/nn/__init__.py | 3 +- norch/nn/__pycache__/__init__.cpython-38.pyc | Bin 215 -> 241 bytes .../nn/__pycache__/activation.cpython-38.pyc | Bin 1170 -> 1201 bytes norch/nn/activation.py | 3 +- norch/nn/functional.py | 5 + norch/tensor.py | 93 +++++++++ test.py | 9 +- tests/test_autograd.py | 38 +++- tests/test_operations.py | 99 +++++++++- 22 files changed, 543 insertions(+), 8 deletions(-) create mode 100644 norch/nn/functional.py diff --git a/build/cpu.o b/build/cpu.o index 6372b93e473468016bdccce213ac77d7ba4524e3..3299b62bd1e95cbf0cb55b237c31bd7fe046c036 100644 GIT binary patch literal 13776 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 O*fYX^AI2fyY5fmBLsPu~ diff --git a/build/cuda.cu.o b/build/cuda.cu.o index 7f95837f611145b07fe527f2322a17c89d8456d2..cb40e3902554ad0a536216247e5433cfc59de8e9 100644 GIT binary patch delta 25163 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 F{{y-pCP@GQ diff --git a/build/tensor.o b/build/tensor.o index 893104690408c2422c28c7d106ccbeac4d8628d0..53ef71001ad06f64757a2dae640d22bbfe9bacc7 100644 GIT binary patch literal 48712 zcmeI54}4VBmB-%%tbwA$R$COb<9|eqNdN&sjmU!$0jZLzENvK)38dzqnMpyHsNEoS z9O=5!?pBL`ck8NUSGrQgiW>gdgq3yEN-eACX1m%#gOw`v-)h-&?mK7Z_wKxx0M>rC z+uYA5%*^-R_q*qud+vL0?tAmN!JRp+AQEx3M4a=T+$W`u)A^&^`*QQP+&R)Y!g+h> z2_IC%(yzzb_LRrctvh1rmacTgwpe;r`!f@$-7an$OS|vI+FIXpTF#1P4l^V}aO01T zrT0&Bcd?b{F*dEAVc>0-q3tpiBw7^n-gd>(bIkip#kTaEkwn{L=?<@PTYDt?KE26o zke*XQ{kA))GrjH0OGu8kcrn|MWIM`7bB`VF9yX!pfK1#^#q^qS_e^rPwbDlJdzm?- z>0|6Wv6Vw^Vku%>u}Eje8Tv>U*Qe)$@B58MDTkAe&=>@8nsn{2*`e|(AFA8FjO&ND@x*+53V_S~~Te~t®2V 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 MNa=?1YRCV70W9R`W&i*H literal 43304 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 delta 343 zcmaFp`qrK=l$V!_0SMwgdZeuvo5&}_^oC)g&MTf2#$X0brI#QnzsZWsQj_($ls8v1 z$_fc-G8eG|MT?|Bgba|-+`LLSlZnxGvxrzOBey$97_2~ivOK%Y5Gf>h=*bB&=55zE?tVLd6 zozEnH3i|@ZS0KqT7x{wZHXBLXGqU;vB{occBIURFh>QxOZ~&0I3q@@JP_5%+8@a=b zL6awni%%Apx0474syKwA3M8T_IN4muLJH)GB9MEEKo%E)EGPmQTNFI`0#N@dC4NSQ I$-9)40eKck8UO$Q diff --git a/norch/autograd/functions.py b/norch/autograd/functions.py index 74594fe..b862b73 100644 --- a/norch/autograd/functions.py +++ b/norch/autograd/functions.py @@ -198,5 +198,23 @@ class CosBackward: def backward(self, gradient): x = self.input[0] return [-gradient * x.sin()] + +class MaxBackward: + def __init__(self, x, axis=None, keepdim=False): + self.input = [x] + self.axis = axis + self.keepdim = keepdim + + def backward(self, gradient): + pass + +class MinBackward: + def __init__(self, x, axis=None, keepdim=False): + self.input = [x] + self.axis = axis + self.keepdim = keepdim + + def backward(self, gradient): + pass diff --git a/norch/csrc/cpu.cpp b/norch/csrc/cpu.cpp index c308d58..6d65273 100644 --- a/norch/csrc/cpu.cpp +++ b/norch/csrc/cpu.cpp @@ -235,8 +235,76 @@ void sum_tensor_cpu(Tensor* tensor, float* result_data, int size, int* result_sh } } +void max_tensor_cpu(Tensor* tensor, float* result_data, int size, int* result_shape, int axis) { + if (axis == -1) { + float max_value = -INFINITY; + for (int i = 0; i < tensor->size; i++) { + max_value = fmax(max_value, tensor->data[i]); + } + *result_data = max_value; + } else { + for (int i = 0; i < size; i++) { + result_data[i] = -INFINITY; + } + if (axis < 0 || axis >= tensor->ndim) { + printf("Invalid axis"); + return; + } + + int axis_stride = tensor->strides[axis]; + for (int i = 0; i < tensor->shape[axis]; i++) { + for (int j = 0; j < size; j++) { + int index = 0; + int remainder = j; + for (int k = tensor->ndim - 2; k >= 0; k--) { + index += (remainder % result_shape[k]) * tensor->strides[k < axis ? k : k + 1]; + remainder /= result_shape[k]; + } + result_data[j] = fmax(result_data[j], tensor->data[index + i * axis_stride]); + } + } + } +} +void min_tensor_cpu(Tensor* tensor, float* result_data, int size, int* result_shape, int axis) { + if (axis == -1) { + float min_value = INFINITY; + for (int i = 0; i < tensor->size; i++) { + min_value = fmin(min_value, tensor->data[i]); + } + *result_data = min_value; + } else { + for (int i = 0; i < size; i++) { + result_data[i] = INFINITY; + } + if (axis < 0 || axis >= tensor->ndim) { + printf("Invalid axis"); + return; + } + + int axis_stride = tensor->strides[axis]; + + for (int i = 0; i < tensor->shape[axis]; i++) { + for (int j = 0; j < size; j++) { + int index = 0; + int remainder = j; + for (int k = tensor->ndim - 2; k >= 0; k--) { + index += (remainder % result_shape[k]) * tensor->strides[k < axis ? k : k + 1]; + remainder /= result_shape[k]; + } + result_data[j] = fmin(result_data[j], tensor->data[index + i * axis_stride]); + } + } + } +} + +void equal_tensor_cpu(Tensor* tensor1, Tensor* tensor2, float* result_data) { + + for (int i = 0; i < tensor1->size; i++) { + result_data[i] = (tensor1->data[i] == tensor2->data[i]) ? 1.0f : 0.0f; + } +} void ones_like_tensor_cpu(Tensor* tensor, float* result_data) { diff --git a/norch/csrc/cpu.h b/norch/csrc/cpu.h index 28404e5..b006ca7 100644 --- a/norch/csrc/cpu.h +++ b/norch/csrc/cpu.h @@ -6,6 +6,8 @@ void add_tensor_cpu(Tensor* tensor1, Tensor* tensor2, float* result_data); void add_broadcasted_tensor_cpu(Tensor* tensor1, Tensor* tensor2, float* result_data, int* broadcasted_shape, int broadcasted_size); void sum_tensor_cpu(Tensor* tensor, float* result_data, int size, int* shape, int axis); +void max_tensor_cpu(Tensor* tensor, float* result_data, int size, int* result_shape, int axis); +void min_tensor_cpu(Tensor* tensor, float* result_data, int size, int* result_shape, int axis); void sub_tensor_cpu(Tensor* tensor1, Tensor* tensor2, float* result_data); void sub_broadcasted_tensor_cpu(Tensor* tensor1, Tensor* tensor2, float* result_data, int* broadcasted_shape, int broadcasted_size); void elementwise_mul_tensor_cpu(Tensor* tensor1, Tensor* tensor2, float* result_data); @@ -19,6 +21,7 @@ void scalar_pow_tensor_cpu(float base, Tensor* tensor, float* result_data); void tensor_pow_scalar_cpu(Tensor* tensor, float exponent, float* result_data); void log_tensor_cpu(Tensor* tensor, float* result_data); void scalar_mul_tensor_cpu(Tensor* tensor, float scalar, float* result_data); +void equal_tensor_cpu(Tensor* tensor1, Tensor* tensor2, float* result_data); void ones_like_tensor_cpu(Tensor* tensor, float* result_data); void zeros_like_tensor_cpu(Tensor* tensor, float* result_data); void transpose_1D_tensor_cpu(Tensor* tensor, float* result_data); diff --git a/norch/csrc/cuda.cu b/norch/csrc/cuda.cu index 467b8cc..5726c69 100644 --- a/norch/csrc/cuda.cu +++ b/norch/csrc/cuda.cu @@ -542,6 +542,28 @@ __host__ void log_tensor_cuda(Tensor* tensor, float* result_data) { cudaDeviceSynchronize(); } +__global__ void equal_tensor_cuda_kernel(float* data1, float* data2, float* result_data, int size) { + + int i = blockIdx.x * blockDim.x + threadIdx.x; + if (i < size) { + result_data[i] = (data1[i] == data2[i]) ? 1.0f : 0.0f; + } +} + +__host__ void equal_tensor_cuda(Tensor* tensor1, Tensor* tensor2, float* result_data) { + + int number_of_blocks = (tensor1->size + THREADS_PER_BLOCK - 1) / THREADS_PER_BLOCK; + equal_tensor_cuda_kernel<<>>(tensor1->data, tensor2->data, result_data, tensor1->size); + + cudaError_t error = cudaGetLastError(); + if (error != cudaSuccess) { + printf("CUDA error: %s\n", cudaGetErrorString(error)); + exit(-1); + } + + cudaDeviceSynchronize(); +} + __global__ void ones_like_tensor_cuda_kernel(float* data, float* result_data, int size) { diff --git a/norch/csrc/cuda.h b/norch/csrc/cuda.h index f7a883b..712e600 100644 --- a/norch/csrc/cuda.h +++ b/norch/csrc/cuda.h @@ -49,6 +49,9 @@ __global__ void log_tensor_cuda_kernel(float* data, float* result_data, int size); __host__ void log_tensor_cuda(Tensor* tensor, float* result_data); + __global__ void equal_tensor_cuda_kernel(float* data1, float* data2, float* result_data, int size); + __host__ void equal_tensor_cuda(Tensor* tensor1, Tensor* tensor2, float* result_data); + __global__ void ones_like_tensor_cuda_kernel(float* data, float* result_data, int size); __host__ void ones_like_tensor_cuda(Tensor* tensor, float* result_data); diff --git a/norch/csrc/tensor.cpp b/norch/csrc/tensor.cpp index b717145..8564b2a 100644 --- a/norch/csrc/tensor.cpp +++ b/norch/csrc/tensor.cpp @@ -173,7 +173,6 @@ extern "C" { } Tensor* sum_tensor(Tensor* tensor, int axis, bool keepdim) { - char* device = (char*)malloc(strlen(tensor->device) + 1); if (device != NULL) { strcpy(device, tensor->device); @@ -211,16 +210,145 @@ extern "C" { float* result_data; cudaMalloc((void**)&result_data, size * sizeof(float)); + cudaMemset(result_data, 0, size * sizeof(float)); sum_tensor_cuda(tensor, result_data); return create_tensor(result_data, shape, ndim, device); } + else { + float* result_data = (float*)calloc(size, sizeof(float)); + if (result_data == NULL) { + fprintf(stderr, "Memory allocation failed\n"); + exit(1); + } + sum_tensor_cpu(tensor, result_data, size, shape, axis); + + if (keepdim) { + shape = (int*) malloc((tensor->ndim) * sizeof(int)); + for (int i = 0; i < tensor->ndim; i++) { + shape[i] = tensor->shape[i]; + } + shape[axis] = 1; + ndim = tensor->ndim; + + } + + return create_tensor(result_data, shape, ndim, device); + } + } + + Tensor* max_tensor(Tensor* tensor, int axis, bool keepdim) { + char* device = (char*)malloc(strlen(tensor->device) + 1); + if (device != NULL) { + strcpy(device, tensor->device); + } else { + fprintf(stderr, "Memory allocation failed\n"); + exit(-1); + } + int ndim; + int* shape; + if (axis == -1) { + shape = (int*) malloc(sizeof(int)); + if (shape == NULL) { + fprintf(stderr, "Memory allocation failed\n"); + exit(1); + } + shape[0] = 1; + ndim = 1; + } else { + shape = (int*) malloc((tensor->ndim - 1) * sizeof(int)); + for (int i = 0, j = 0; i < tensor->ndim; ++i) { + if (i != axis) { + shape[j++] = tensor->shape[i]; + } + } + ndim = tensor->ndim - 1; + + } + + int size = 1; + for (int i = 0; i < ndim; i++) { + size *= shape[i]; + } + + if (strcmp(tensor->device, "cuda") == 0) { + + float* result_data; + cudaMalloc((void**)&result_data, size * sizeof(float)); + //cudaMemset(result_data, -INFINITY, size * sizeof(float)); + //max_tensor_cuda(tensor, result_data); + return create_tensor(result_data, shape, ndim, device); + } else { float* result_data = (float*)malloc(size * sizeof(float)); if (result_data == NULL) { fprintf(stderr, "Memory allocation failed\n"); exit(1); } - sum_tensor_cpu(tensor, result_data, size, shape, axis); + max_tensor_cpu(tensor, result_data, size, shape, axis); + + if (keepdim) { + shape = (int*) malloc((tensor->ndim) * sizeof(int)); + for (int i = 0; i < tensor->ndim; i++) { + shape[i] = tensor->shape[i]; + } + shape[axis] = 1; + ndim = tensor->ndim; + + } + + return create_tensor(result_data, shape, ndim, device); + } + } + + Tensor* min_tensor(Tensor* tensor, int axis, bool keepdim) { + char* device = (char*)malloc(strlen(tensor->device) + 1); + if (device != NULL) { + strcpy(device, tensor->device); + } else { + fprintf(stderr, "Memory allocation failed\n"); + exit(-1); + } + int ndim; + int* shape; + if (axis == -1) { + shape = (int*) malloc(sizeof(int)); + if (shape == NULL) { + fprintf(stderr, "Memory allocation failed\n"); + exit(1); + } + shape[0] = 1; + ndim = 1; + } else { + shape = (int*) malloc((tensor->ndim - 1) * sizeof(int)); + for (int i = 0, j = 0; i < tensor->ndim; ++i) { + if (i != axis) { + shape[j++] = tensor->shape[i]; + } + } + ndim = tensor->ndim - 1; + + } + + int size = 1; + for (int i = 0; i < ndim; i++) { + size *= shape[i]; + } + + if (strcmp(tensor->device, "cuda") == 0) { + + float* result_data; + cudaMalloc((void**)&result_data, size * sizeof(float)); + //cudaMemset(result_data, INFINITY, size * sizeof(float)); + //min_tensor_cuda(tensor, result_data); + return create_tensor(result_data, shape, ndim, device); + } + else { + float* result_data = (float*)malloc(size * sizeof(float)); + if (result_data == NULL) { + fprintf(stderr, "Memory allocation failed\n"); + exit(1); + } + min_tensor_cpu(tensor, result_data, size, shape, axis); if (keepdim) { shape = (int*) malloc((tensor->ndim) * sizeof(int)); @@ -905,6 +1033,58 @@ extern "C" { } } + Tensor* equal_tensor(Tensor* tensor1, Tensor* tensor2) { + if (tensor1->ndim != tensor2->ndim) { + fprintf(stderr, "Tensors must have the same number of dimensions %d and %d for equal\n", tensor1->ndim, tensor2->ndim); + exit(1); + } + + if (strcmp(tensor1->device, tensor2->device) != 0) { + fprintf(stderr, "Tensors must be on the same device: %s and %s\n", tensor1->device, tensor2->device); + exit(1); + } + + char* device = (char*)malloc(strlen(tensor1->device) + 1); + if (device != NULL) { + strcpy(device, tensor1->device); + } else { + fprintf(stderr, "Memory allocation failed\n"); + exit(-1); + } + int ndim = tensor1->ndim; + int* shape = (int*)malloc(ndim * sizeof(int)); + if (shape == NULL) { + fprintf(stderr, "Memory allocation failed\n"); + exit(1); + } + + for (int i = 0; i < ndim; i++) { + if (tensor1->shape[i] != tensor2->shape[i]) { + fprintf(stderr, "Tensors must have the same shape %d and %d at index %d for equal\n", tensor1->shape[i], tensor2->shape[i], i); + exit(1); + } + shape[i] = tensor1->shape[i]; + } + + if (strcmp(tensor1->device, "cuda") == 0) { + + float* result_data; + cudaMalloc((void **)&result_data, tensor1->size * sizeof(float)); + equal_tensor_cuda(tensor1, tensor2, result_data); + return create_tensor(result_data, shape, ndim, device); + } + else { + float* result_data = (float*)malloc(tensor1->size * sizeof(float)); + if (result_data == NULL) { + fprintf(stderr, "Memory allocation failed\n"); + exit(1); + } + equal_tensor_cpu(tensor1, tensor2, result_data); + return create_tensor(result_data, shape, ndim, device); + } + } + + Tensor* ones_like_tensor(Tensor* tensor) { char* device = (char*)malloc(strlen(tensor->device) + 1); if (device != NULL) { diff --git a/norch/csrc/tensor.h b/norch/csrc/tensor.h index a32e74c..bdcf25d 100644 --- a/norch/csrc/tensor.h +++ b/norch/csrc/tensor.h @@ -17,6 +17,8 @@ extern "C" { float get_item(Tensor* tensor, int* indices); Tensor* add_tensor(Tensor* tensor1, Tensor* tensor2); Tensor* sum_tensor(Tensor* tensor, int axis, bool keepdims); + Tensor* max_tensor(Tensor* tensor, int axis, bool keepdim); + Tensor* min_tensor(Tensor* tensor, int axis, bool keepdim); Tensor* sub_tensor(Tensor* tensor1, Tensor* tensor2); Tensor* elementwise_mul_tensor(Tensor* tensor1, Tensor* tensor2); Tensor* scalar_mul_tensor(Tensor* tensor, float scalar); @@ -28,6 +30,7 @@ extern "C" { Tensor* tensor_pow_scalar(Tensor* tensor, float exponent); Tensor* scalar_pow_tensor(float base, Tensor* tensor); Tensor* log_tensor(Tensor* tensor); + Tensor* equal_tensor(Tensor* tensor1, Tensor* tensor2); void to_device(Tensor* tensor, char* device); Tensor* ones_like_tensor(Tensor* tensor); Tensor* zeros_like_tensor(Tensor* tensor); diff --git a/norch/libtensor.so b/norch/libtensor.so index 9212825f2e434aeeaf8c3bd47dd6fe72c899a152..90f234627e5a99a18bef507126d80646332a50f1 100755 GIT binary patch delta 45489 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

k?Cy=dcch|KubnVuSk0fguT{B&)1UE8Q@zraVc zT77@&+7EQ?TNWMggRcFpu6;(={!-UIsB3?$Yrn5+Tej$c^}6;-UAt7*p08`q(zU1Q z+7oKFEe-N&0u6@h+S$5xcU?O}*KVzAC+XVFYP2o31`TQewgw@(wx6!;uC_BvTL+?^ zS!y&9lJ=y~FSEqBAc9@?U2NAq!-~j7|0t)BzY|5|lYSy&TYLT9M#O?TZL|!C9P)zw+243*{maNHrsLI`%!XwR+LK_QBr)u`cR&b zl_tFsE_-MDdF|dK2-dHvY_=~%Shj}CXR=~MS^g(0QT$bo?G+?l2%~atM6MB$gG!rq zmOQD~NGX%?=IjP<-3wL6@A9)=KS?`x$=7-(iJ!}+J}Hvnsob?sm=wHA9^WUhk>f5h z))UrqPi?mO71KO2OER92_HP*Sy_e8>2$yfXFYoLVC>?%ZKGA1HGd-5@sS{(jYGWTeeP{ z-ZZ@)9dpUF%C^PCqi{&U1RJ>w1x)(RUpAW@H#J^9_==a*`aQX8zb1hxe-QQAmorPc zSJ06bpCyAc|0(A#@RQ%}*I3F5qSI2m1D4;hd-jj~N56KG|DW=U*-hl-qkZLR{p&SY z5~#+m9i;6exCO7-2wI{hFL^skcFp&YZ}&Hek@Debb-jjchwfMH<2)mW4|p?rL;zD( zeE74bn1MsYVdUvNBD)WsFpchy;w4mce(A@U^!-xS$rrD9h55mEw38X%x~Z$n{Do_k8Gav7sATDW(au4y3}pUR`g%< z80Kg;K9pY!swX)xUTeY|KzMXAcFAo9-*g?Zg?es{dn9jKTUXvR%sD3afg0nC{h7tz zk@&s@g9tYXyR{@6k)L**zzctu4-fP8Eq@4ZF_LrctcSuhG8&67^=AJ&uz2Bj)nem^ zvS&^`@%i$hp;E|0Ieh3m>D5Q_`5YhV!(V0ZkzFE_ebfnAfu8mWS@xT%;nuGtnWYxL z$@dEb+x%ow#aF(q5l^*?Fa4Dc+n&#%_tED0y!ls}&qWVNKG)47lEoQTxL_5g4WS|J zWg<=lEZz(!w!lRe(q}X}#wg149!i+g36sY3>;szM^StT6>J!FpW9>MeNT=UusZr(52jqVicS*YXLbv{ z?vhpHI&K3CS=wEB&6rSW%>T%0%2-QdhSV*|G$S_GOR_H_U#%oEMHtMTxmYqK8KQ!~Cj$hb%_P_qIH2Y@qb?F3DUmQT7_yHFCe38k1dYOl&Xn zhGy1xw3R0A0V_$|mvZgm{xD$=;b&G=+zU0jz448B(=D`l;zr32M@C4}ZJM{YZ)?NL z^7cXblar?THVZtYhuLsUX3qowvom z(7fGzo968gH?gDm$~~rpd)y|2;lUesi>~_4H)-^m6HU5RALK%#*9XsvtrLmMGc&NML^5@uU_Jd~Sq_|z=dpH{O-ynj=EeQJH_%5{0e)Bvf?FT`i*$u0TZR6k#l z=&6xwzd2Er(dcY#j|i|3zxj(CS{NW%Zcv}5-=aS4{aN;!l<0BrdbK}oe~}x^5049@ z@y@6yNslhhxLA^Lu{ygQeyQ!_Lr3akV+A%lAO_6GHB^c5C}ny-Uj?Q=VbYy#!q4#0 zSN>ssE7y;~Au~SrXE|$f{ovg{lF)R=wIRELtSfRwzBI~nVukh3pNQ*C&SC`jEJ$Yz zPqR&3x}gunm-3C`x=pL_(U3I|abkuGh*(K?>{9=to@`V-exboBzHXmznT&`}xc?10 z;TQ_p=C6{_=_dTR0}X}^pXXSEFjN+dNkp>5_$Fm~e?yoL5Fa+C_SflzmjES!7Hn*Bh-Wg?Qga8-#kTq*e2%Gw)2V&U5M0}a=C%Jg<6%m#!>b7#g6ba#mpYwj*r zAY~ukJSbq&SFdSym*(Xx#LofRcbDiZ#8|D}z|7CaS0}b#+GZAeC;z{)D zWf<&AdvxxRXw=?aMqZ^W@yri2*_^+ZKY#TVOO8z>Gt;*CDLcqIYdzWvMa&}?sxo6! zW=R&gB5h;GYkIJH87w^6Oa%-!y?}X{})78bXF1TDPklxwF>~&H=YAQ*huaoELCE{T zaeK%QUxMMe$Gn(+pG$Jq%s?ss5;+aEap%2DlaAt9yuHgPbYbELzN10FafIDdb-PHE zv3sh@@8r=%;mvm;5+^cF8}tX0RivwK*53Gs&f4erOvd^hJY};MLyZ`-Dbsr?VQwc( zI%~FXW#>7O^1@j;zB@?FYs}p0r$k@#^Tp_L-#+MebZ*EQ3WF|9Is-15xwU|?3X|k7 zUJsDIK2Ij9kpnvECd!*x(JVC^ZbFeCsl-YjyOr%E6l|&OK?dMvgWk-2c%IHw$#?YD z;P?g1RKLiVp0HUeO`4~TvjP>&%Lf8N6Vw1Rt zNR}8spiJ+(3&1oICY{8=-(V8w`5KuvexE{?-XDL`(8dt)PImv+pVthyG$|TfGJ$`c zr%SKlS;D1DPaF7xN;;#Na8@^?*+gB9rPrRBor%x#t%^NP4@g{U&uGK5bVkp8L)V^b zrTt3nO9sN@xVn;FdR{q4FFk{aCVQr`gKekBG;BrlU~?@3!NT_F9IH=jfH%@ie_sA2>~~)D6y)E45E^qNj$B!Ls+dy58BiVX#g@0GP(( zXX%A{$QgP)*iuPdx_*Y9%n?Op3LHi^Ewwpey1+>djmEnBLvLF z6I6-uA#RqJ`*}Ae%npP}U0j3b5By~I^Bm^po-_1hcI_0g{ck6;;iu@O@)<_(FDEkx zVy;Fcet$?TH*E_2){cl1v%Ky!)&CGPt~>b+|C&Z;2Id|2ke_@_Cp>srusazMpYP76 z>3lc(n$EWy(WMKD(HPcrf*| zi5$&uyP|rhsjDMT)7@(0DLJY*+2dVeko(#6D>=V7yqODJFUi=cKc4?iBBJyA#|PAJ z|Jkq|kGSydfzGD!1IqNiiw{n%M#7|i4?ZP3FJ9XD8UD=(IV$4zc|5Kr>O-iRBuG6G zHzvI0B=N$&yPKhk1gi=#cK2?CCEs`@z^}=d)LRlZ^+Axr5+@6BxHp~PHIDQHiQ*cG z)9F49W6?|uU@bba0X$;!fyA*Z-i5=% za_)Q|sqZ}+tkM~u`TxmtOIjNAD1e}w5BPng{a zle+r$5$fu_SD34x9--5E>k#Qu!`0;#bnVwcpy8`8(OJV+iK>rk2}Ra^hy+u=^DtGU z_4N*$*5ya3uV*UkzFs3nxvx2gsjn|pP+ud77M-u2xV?w3nbTlz1Y*H_{glp;@#mY= z*LsAROqkTyB^A`yi-pYB%@x$wvj^x9wfo#N=Ii6b)YlhKUDMZlxO>LDMnRFa4I;tR zuOxCc_c`eR+tLqHUq3k{k5pnkz9a^@pPj#u-%`Tk8qs)@jUl=?qp~EUvibn;#;n(P z$ok}G>U-Q&*zbneKm?znY^F+#UsI;H17U{QnbQu*8LM+#-=D&KpK(auy*jk-d!N~T zS2wFg6rxY9@f5I9eF}IUHtyWU_E_UN$@d5Kp*eMfMqYv)YX~NTg=XqOxzn3XEe)aj zUVk1V)nHqM<6b)*c$$FZq}Kk1eREFKY|e>fW)+MH|HJhMbPl`Q2TVTn!NUC?B2tEn z`IC{E32qIdMFO^B;}B6b~+TdfI~mV7S1ye6>jWTH=Z6EXBU z4f-m4@yhxE6f?_C)Uwe`nciavbDo`f^>g|9nonF0;G2nLIEz2W#|5E%Kifxxj?mgS z@syMZ-PBw9;KZ5O+PF{te#3qL2m)gR!Lw#f zuJ4n7+~_a;ah2v!Fmb07v@>_sf3i1ZQ-x$&yO)mg2~oBT^|Dj);g1{1Uu^3n4}Ck= zzX@UM75nX_il2W+6*m|M#aSP#ino3U#ji{9(ybj$@9eHtz8ZC|JZxj9y?x)k!%U&>?|+2`|j7=W)8!*qY*NExbgPhZ*~tCB~m%nTpZ39CtCW)He9mMPq*swX$? z$MHGd&jZwe>|clHQ>Ft0rM2tiho}6dH`d8>jx>}Oza<~O7}@&cHCV%y>ut8;jB9ge zT@wnUOERuudf%rui6&pWrc-Oxr56i()*YK<@nW#kb4Xf8Z;;~-PWC@WMoqWTcdAos z_Xhd;F@I^*5t;$!{+Oh@=)+PgeZ$_cFIS7ws5j+Lk4H+m>*ODg2T2Xyl>a#%E=8`B zgHHrWw^z&F72(pYwep}7=6Y|h#=&&jp3KsW>z|Tq%V*?`uJP5>vE}RKGbfU}k5w=f zJOr^Gev>%&5k7yU&V2;uHXtM{Ovb)ym_CC%+gHNjrgbe5p}Pdu&06Z{4M5gzYp@_= z)w+9) z>~b{NM}!&j1uGJnKglHLtd^gi9wZg6q=Ol}k`8pl8kz;OWe99vRmX|W=?cw)^(6HAELfz_EGYP#W`PUTS&yO*%Ys0KWkH+eGz(g+ zq*+i^PP5?o3YrBKDxgWc! z&dFt}b5BV~3W*5Mg6S)$qn`n?Hjrs7)5>Tp!^^12=#?~*N*MYVpk1OnYKRGo>&7&9|b z)^)Wogj@}h5|THA<;CK#U1gcHWe zOib*onush_O%!8HR)^)(L_gxu;bqi|i%Y19&zDdWN6V=fAtexS#_)6|Zb1n%(JfOo zA!0bp3lcKx&t=p^BJpVBQaTrVOQ?x8CDg>`GCCIziXmV{U!93BNm|f-GNzMi;>Kdt zL?<$XN0w0&zQiL-sT_EAtJG<+eE4ibspm5J&e@pWZHewB@Ij}040u2YoTiE6%5Y$ zsDuWrJ#lbXG0m*Q3y2AEn_P6Hk+i3TCe|Qg;ZJncS@;Gzn1!@SJ3+-l>%%f(e&ZA~#6jL*U-hj}T7^cq5W+-B2T+&rDhvuqgqTs%D<05M2 z5ivPrf!$2uTxw?UTxw>-B5LO6IS_gu!_=8s3`NY$qnA`OTQMZ&jT_vzE?h{>Tp=bq z%!iq#^2SSJq|i4=niVtzjEhkqlJO+YCiHwtzJ7p9(g|xCv08{x>#X)7Nl91F{?@A1 z36N!0FObOhfJm*x3!G>7GHwp_GGGq%GJFB`@+S*=j>YhG_BtWH%-*h6s=X0Ks=ZSf zAn}Y`g;tT|66e~@1Qt;9NJfE7mJsU#3Fy_^(#QE-`JqN#5fjpGJ~6;xNNBMz9jlP zp0K_R7v%E$vt7o#L)gY6+vMGM(xv@#)6QDcgPvmKWbLJ3oeRWMJ~{f5x~R-9#-ten*aYmM&eL zC7*km>1w7R?uVk}h<}=ypKfH9K915``eIgXOZ8@OkAkFYugh(p2g*aQ<-1(jK<)H? zn?}w&lSZy11@U=iB#oRWZ`O&_+wq%O+s@?IYDCU`I*r`bb<|G6TQqVmuhYnNqTmT@ zy$BjPFW$TvuDA2+Yqjlkc~y;^bYQxC;MRPX)@!Mai5saet)^37cC2AR>JdhLnaZ2~ zVR{=8(`(z9HLZp(F0UYNe)6w(C%A;XNo^fpPi-xEh1y!Yn%UYALT&Zo&CwxxTa#a@ zZR^ZbYOBf`B^6GQBObru5>ERY1J_X-mkOzkQia*LQ;*u{$D1G4)7v;uSldRx6nWF# z6zR7@yC3&fQa{G8p*HGIr8Wjo@Psunh}wwY%@=`s8%|Se+ZZv)?#I;$_B@MSLG4tm zrgkPyrgqlJ%$K47YA263I|u0P^qE}S&hCjd=BCbgx%ICnUEW+y?R>LJ_V_JbnqDAh z{g&zCNI^U_kq{7*j8r{j@Wouo+lg~2uukxmzj)Nl9M7A{zIv_c6KW5B-B{RYENA>4Ax#>KzFtC2bStMOu8yJekV--9 zD_(RSMBe<^OK;f8%=ZS$VhpPZJbL;3Dy6}GP-ZI%BTC*;>FB`9-egH>cgA< zo_hTe`L*@W%H#7KB=ul6;)ntkGk<-AvLjK2{qxHN8K1i!4uYoZq$uP-u$hO-p13BwQXb%x7%n4kDeE~yt9DX zc(IsH#kdi4D!eIp!aB>9PQ}&v%&U&BdK+0IYTMYE!)-Xr-@WjYcH~fB&dj5BCKgj) zF6L5ST2K&=HJzz12HxE5the)NZf!e$!|c8|4yQJH&80SeUPx`E=TIAm-e808?L=)X z+BO~x zw%d4lpt4;OgT2B@!1=fS`+a7!DRYcsy#PNys4MmJAfzP8QpEhS3{lz|#E5|BWXYHM z{Ygkkk|XDK$DWH!|*O#{HCY8*}ag#dA2p5JHtUgq>K8^V(90u)~xB!cMBi zo@OBIaAhN5C)Z-@uN-cILY*+^TO*VZYe{Rfxkm-w`Bf)VX*OhS;Pt&1- z@PgvFQ6L51sj!#9*o_VLzhrBNg$ERGPz~@}A6y{}0`4bugh+4Q=6g4mS|a4Lk;J5SnOIvubagr^wRMmzxxCS`DlAGjYH(2yLqwymp`BF}3hv z_!}Mupw;#ktULbfE6SG@9D=Ndaq5f&3ER?Mu) zD^UsSQez9jiGh|K*g{NR$x2VKwR;3-&%j<_@~Ykoig;eM$J#b|Rj>IXo+k;gYVyKE z8MR==krD~M$xE|@ksqXxzY1yc;!Bm}h^lA>mY&J0dg&FRO-eXchRLgX#i;leWV*?V zWI8J<-UNnBUg;`K{*oRoLC80gSKAsm&CzHioXJa*aF`}iMj)|FUYf+>ijt5gCa>!J zQ6rRyd@y-w@`39q05y4Ork(4-L#KVux{41ZWk63cN6GAoOVFvF;xJ`@Pti+B%>ovk z1#Ej3u+>?>0(t>+?uFW-Ug9J%U3uC|94&1#D#Lq={Uk3(#XCr>t5o(D^Igav(zG8o=%vouXElyNYvtchR2$#!; zJ;VXZHqwmlhk*q2tHD@An$G>fctUDaB)`91q`w%ftQ#PXgdTCA7%RFdafC!F+gU4O z5L&Lvn;@M@syHBl_?1L(Flp-EnmT0Es}qs2*C9je$|s6*6Vhq^Eo zhm5@r8CqAK4iZz^s+WAC#$Jbry$&^aP(lllvDYC(>q_In@U@9DkPx#{Oo*rQDIs3U z9YWlcz#-x=(NTGAh!{)%LL&)>y7-7vdmT#c^;lTVq?M@CN@>dM~EB6W~^ma%14Sl#droLDs%G0aTYSh{dkc=wl(5p zv`AX4O#*|*VYCoF39K{U{|F(w+;mNw>~2;~o9t=>40uB!FxkvbR<|wmx}VB7bRy6O zFY?!INg?fYyaKQRlS0~U@mj5G)4vv};Sb$0w$&Myn}PdyQV)pkG-+M^x7 zcXFbDwABq+ZC}>#$*z~lcX+}lyWM_GyCd4seB&nkPI#?W+hjRs;^mA(6DdTXGhWj* z?JoP)j}~Wy?c26UT<){4#d`7V4Cx~;#Whe$c72Q;oRrCRl`d#E%WuTm&Kif30P*qK z|A5py^b^UiKe$ffr-4m%l&_${MZIpFR9u3j6u-;-Dl>rT#YGvv_)*^0N68P80{qNm zwotU(1j5}0uFE3M%~CoCNnwhWV3#?TqZAV?x}FpurKT%Kf~1B@J{m5I(y2xvOL3_u zg#~@cQehGUtKToDEA8q@4I_rLRH(&|CV7IAuM|T~L=%<)wZzKoVyB4MKmQ^2go~|E zT!JBXg+;fPwk7SD(ITZY#DX@msMiv6Yp=W!jIobpSyGE%NKy_ZUpYmr#j|9oC3ej& z<`P22{vTo;NP?o|D4mJeNtRi)w7p{&D<)ze;D~`zOU!|!H_9gE6vToy;lzPbOYEHt zMGA$BJ#b|HH@*W|6(~rJP$^7u_f*D)NTJH2P$@j`_jINhvPQWaXd)D31=2+zLmAhI6b?WIXcD@|SQ|bYZBL%kmdoT$bC0ON|sO z>fSTEGs)?y9;G};@>ilGAoo}~Oyu%W_s-AKX;i%H;b&AFk*10S>w2}OIb=F>0UvhR?WS901hIC6L8`oFapQ*vK zMXs+GkuW0Nn1t9@nNuHQIBQ3|D+e*r>M@)reO4iGO+D1Za zDpH3d9h5l)NkEoxEfqOTi+W8Fl=DQ(6T1Y?Q(_eQY)0nrJ{L!@KK5BsdWE9TGUN%j zlc~%heflCZIC5P%3`E)10JB__&-$x(0|qpcm2VqJP5so&)v{6){LP-GenqdcUg}|) z^h#Cm_k_Tj&K2?i6IPnClCXr=YxioQd<&L~FoUy{;)WzW8%Y65dPB*%ei7OAQHUB` zFWrhrLsJrsN@zWt2P&vA+f=A9E~Z{Z8bMNhPKEs&FXT9;m=vNLTe)WuX=utZN21$* zOc}@6+N0vwsm(_Jw zY&0ipi(~t%H9v)JO#BoV=QlZI@Yv=ei#V*3>F!i~%#R$<45rh+Xpbff0iv}+{uAr3 z&7MqL>5I5pl~j7rSV=`U6sd{SP`c>J+kYwT&^Edtp`4XRfufJM`n*sG=baV0alDEV z=q1pJ7P=Jckjp0aGXW1Cu`&B8l|f>-D2Toy051br5_6wdLJ4JX@2%euim3fXUa0!e5+{x(KeyYJaWV*}B*+I!p z^`fU1cAh4DBSj_-ZQV&YFL@rDs9fzKHLH_+N$L}3J}Y^6E>TQ9r6v~lNHI9^CwKRH zhS<1-AKmMDb`S$RZ@G(}8A6C>E}l&$bU?*1QV2^##nl)YmSpJW;JL*hg{5pUxI`t# zj9KmyImGjPH$zz3hK|r;-q6t@Dn6!}yYW^lPiF^XSYl#Fr?7;Cjwa8bi9RlZ=(uo& zbkW1}zK7BCL4-$$e_Y!sE<mo&~004+qinwo^S1H_`JKr&7<)CiHh&WboWH;Uz;igXdclJbK1Y9W$}}+JIQ{ z9?ssw#-tm3hm0>6kvsmiy`rB`K5S&(q=Le@w870fI3*dH_Q~=TV!E|#(bN5JztBWw zZK%0rPp|kr217tM!(bseW|_BOcp!?tfRH}M@J(pvYjSNXNG_Sq<9vM*m3rCn@g37rJ)0zsX+HI}P@gHk z_m7Q_bEzvxb-IWyK^DivsMz>~8NSA42_4&~nLBo9JHyvGDn2n>bf4i{kkB?hJ}!BN zuR~Bm=T2@je3!+iw2x0nY@d{vBSpt2G#c)b7UQ~0OdX#$Ejq?KewR2SF{OP*VoIvN zuPJR|u;T~*hH}w)VN-_|o=vQ7*HBg)+%MV%oqSLXtzE*mjMIirBStizHfwa< z#L7CRw8E~PMl{bKvqNx-6uiwJJ0`kJEsQZnCB(NY@rj8XcOp4mmhAb#z?NQ|hw76-ziSY2QfWP|-2UB{M!IJlPoJuwa?P&&%9hjK0Y`mw7&p z6&>aZ(w3o)c%NSC@K>x`J;AWPt0}p3vD0(GCm}VQ^8)9T~%u+#Gu*}g?YVc2Lxil!QZ*koOaapsZl<$QGNvYopjZ=q? z7o0r0x{enN9hQbAju(WwiLZ3^OFnCCl-i=d5S1L)T4mZWBSa>|qzJB`O^8ZP%nJw*<3`;TKXDCX9fTYfm4bqY)HF2I{ zaN1hmFU|J@IGEONg`uSo+H&j)-!weDw02WY^^t-tBZda2K9rnyER9Y}`&ew0Vt#d5 zU|L3KgSeP*p-FPePxb4j&JZF~;#xO$`p{b#8a+2HJ}$+alA5y2+3~osL269;q?nZW zdxCFTfoSOD>}(lYFMeBOL$f(AQmijDo0O(BlztS9_4_x9Gp}?CPKfMpC@u&}JtR2A zh<-_1EW=#VCN@e*9wj*Sll)UVEOT+pG29XT(zf;%mI}_xR|(^rAstp24X!zLQ;v#8 z3oN~u(WLg%J8rNJf#x8TYCWyH)h7ch-C2dY(gQS=+Auuhf zvGlW3gVdzog{FxuVq;w&#W}q_z|h6eBh2i!RgmxlNA(4_6T`Zs4U0=&79vdyd#!D| zWOH&{+n`YCR_yqc_U$_+weQ`mVPewdank6xV6*7pmvT{Pp4?(`XlZ+MYHC7SM$4d3 z#hfigTGoe!B^Ba!ATeo>;Bq-EHX(U!n6rQ4)B7JNOJ$pLFzWJZt5Y)shew)_{5x2AvEr*fsV_Pod15w^IbsAp;9RO*$+PKycdU**GaV*A$ler?;DLibswc>S^d`*ghdN4eK<@ z6qZERY0|y%Sf29@uDK?^cz@9Z>YrdXxs7?-uww!ltRSpSQC{jV`F9S6IcmrdwBCiN z&NbB|<2N=+jw#(l5H#F`AC1O^Vp!U~t%B8pNIz{-q6gp-*66L+w3K|4U`%)R%r|*BmW(#!n8JeohD3nH_mVaIG(m6=zBaoM&E;y=WL>3Hj$le>Q3K#it|19 zv~Zo0SJ=|xI?L77RTws`N55g+dv?g~mNBd|&aGlSLre0LvvY80I$8C?W=1{QSY;ak zy2!E0HVL%cy~?%-^gU4WSgqW%%61ZTp;r~YU@r*4-c>ed9Nnh+R@tIK*Z5c2GC?l~ zRoU`EV?)v3f=a&#C;)vw3JO3kHG~3Cu`v|j{MWQ86o9_b911{(#6tn-u_Pz}ElNXw z&>bz&AM|2t^as7y2K}Az#H$_pgD&fU{-9esqCe=t%qp8@5h@qEKmq8O?oa@_E(;1k z^Lj%84wW}Rqd|xCg96aYp!uME24Mi8=RmiCCJjb^(2by0P~#Bv$6eAK&}h*2hoV1d zzhUSP8lPKbu`NPncW#w!Cup9Roe}8axEO_d5FF2D{Eo^aC9@ ztIC!I`q^yc2a#vMmAqbWxRU59kHZO3lv;y?tN^}JM8`K$}T}@HY5p*YL z7HFe4(GN5ibT#PrYtRqWYi*V7I_UXz=!eay-g@K!=&u{l546ojM975CjX&FjjsPBm z?guT{jE`iag8&}Yt7Hu@WHD&O#fNF+%AkMKI|T4nnSl=ru|J4z3z1|o0~FQrbE zZDq9p_)F~k$H3j(s%-Qp((DF$7*qqkaHKspGWx@Vc7CXxp9p>zQ+JkYv+E?)JV{@jzU2NwMv-1nVPmHay(ch`F z^Y^NJi?9L$mGS>Q){nryn)u)Ri{Rf(uCmeJF0>o)u*c#t_}`~i*~m}*+0)H1kMk|~ zoi{v5$!z)G3KGF@gUiW(8R!dsa-09=7lPlc?SJrLUswTwsP@R$8Y5g{_uwP&-^{48 z(I2q1d)Pz`p>PrWotag(Q<&9i4=d~o<}vu$U9gFRZRbl<>3Xn`1w*d#A3$IR1nmNb zDRu*i;JfwscLk(>U-0w6|F8aq;2-Vvul+4-$wMHlca?1vl-UO$4YjY>kH9Av;;}XO zxpw|V@OS?|{U3w>C-}7^fcVo0;Hc57Pn9jEMhGMAAxs2+Xx}Ou{=G`|01cb${J!A- zFB{jj8vF~CAC4}C;BW3~vS5T1>UaS4>;cLSQol|* z4^YmIkm}OuBA-Xj#BrmWR+-e^9*uDDw}GFj$@1lNyJ&ZitI; zJ+R6!Ph=!fc(r^P7fG}oHo7QvMoEFLDZ{F4Y_$EXm$A8bvQu(4`Zur^@*KW#bdNo zchzXg!S(xb919{?kn5H4yltd1mPwGmG=b}Wh)bw_d`vfqP%unhm{?__zus);XV{as z75L($DqAjeRreoY9~-%vw8Np0j%}^hMxat}wB({xl98rQ;Svj!A2HJ2FHOY}f_O7o z+r!hYH66QMd?A+^^6xSuA=7Xgm+7QT%$Mpqdf`kP^op`LUkd8K4g3Z;mQp+TN<{VO ztL>bIOdyV|ROSVe!5)EXnTL>Z$8na*Se=xDe96zU4u-_n|2q*6fd34gkWj+#+r z`<--X>7;BND+M}^hk$vOa%ij+v;4m3Nd!@&C4B;{OhZidoQSZM5 z{CkV5Y!?W>4z$_xVKfQ*2r@xr)wIGL#;PS%wqDxllQQZ2MFG1AJtx4YBfa3jT$zVX zK}*?4gE6IvxaFV%Yq`K_2rOL21V%#Otz~?=MmQ**WIlaygYrWeo3EV^cz~M}I-gCs zzz_&DUd{v_Lm+KA7x=@#G`s_WX)+b?XoWQ~U#_yfMx6azSvnEHxDWn!E0kTRd(H#@ z4(@WOg}GcoB5rjWtYQ{ULm+7t7wF0bWU>B&X{^ayM1g+*{Jvx^+VWcl&WCED1NhOn zslqLjz1VDGomKeGSJ))!J0*Rxd zw2xBzo5or`xo;x{tALesMv#)EBnXem{Rm1RBZB(7!s@r!h=Zii z3nFOr1m(yqsktSee-MZ~q~$uVVB4W(DmRSvO-lf$>ZCtFq5SCGJ=Qw ztoU>4+?-W7ZW1mxVE|Hu7PB&EwiIR#;4r>zSwL_sF%7{}{nf zl*|*9Tr~Fi6-iAE@#^m&^ZvpI?T2?HgH?YAS^eE(f|J=hVW@{+oP&VAkLb)J_JZy}97uIw0MZwo4?RWCOm#sZRyc9seRk$l^b3r>bLk zh)^_Dxw}B>Xwe55yD#$HL&(tuKZ4`x;#c*hpdt!H9~+`N1!QtXp|-CRxE{W#@D&K~ zHQIp$O*ygwawdEcXe<_xcoMqyp_v)e^pc75kVBIQxiX} z#fP&Qlo;NlQ-mM1p@y4sMGd)pD~|W#I6Y#4r$$Bn<#R4Bs8Px1`dL)42=eHNLWKP~ z!&|w5zI<<{7anN)AD6Gt$!B0wB@Ud_;csxfQimTUxCI?9>lpQMg(Qlu>+k}OTXp!y z9Dk_8pK<(&4sVOynCKS@`45q*302G)&R~$D;^#T;rNbj}wIPZEbodyKhwJd&9FMNX z*(P+KGtAWtI)GSQXNZAR9ZpWl1W(uD`#GMe!=G?GONS>p_h*U*=onMrIx&!=!#4vb ztDwPebbJCm%%J)e$JGPG0L5jY6c%`gs__+$>96n9l_FOj@09jVfqTQeC&Pl{LXBI@`CQyj~m zA01SDGH??Hk$9LPkKDH zXi|b*nVk(^vUXFBr^`2-{4GPkt&dS`*@a(VCHx^$J@_g0DVG;>L!lQ z+sg1^T>d!6KfTHTatlHV`F1tDX(yCFU;sG_lhTGU1cxiEt-4|1xN#%H=~E?CKT&d* zA~h~^_O9WKO`lVvO8!MUiE_7}8Lp~}#nF=ByEZYLK7>Mb5Xb$4M$uuu7_I~yBqID{v{p3 z?|faeTRF!~6%1FiXdlPtj%Ty{f;o4+~$*# z&ha-sWNkG)b2)zbb%xXD?5IxVcwujbs{@(MaEmZ<8)FP%MA*PPytkGCGdcbh$Ey}H zfIeDA^;?cFIL2^wp#N~(!ZS~uvHG}x6313wAoAEx`>=k(K+ZV4lmYb7F{%X|Z={>y z6&yFOX7cn&E~-a4Zu^ws>Xh8!c&U})bV=o-PCi#7Bl`OsW2kH73m=9Dzshj>aR91a zxxAT&Obuy2j<5Na$*Z}(ir{3IpGPat<@f`nQKf~3e8Nl&@JW%Sg z8q4w9^K0sv$?*|q8LrOcUWS{5kESvP{p<#~vgEoOi^&&?<#AmV;b))gC5A9`lp(J;x8qtgX(~GmgLDDWRrhJnkrn_c7RmNl`=8 zn&T_^R-?+l&hSA(!B?z+m|o(+?drOa84 zuRZ1Bp%NL_spAY*)xE*-#(XJsVQgVL#}DpffVwMvN7aS=1NOh2uv~&*hHf@Z)LO_1 zA7=V$0aHakKt{EnDq)JW)@Dy z5f_dc(HMG~NvLsojpMEO7MRI3u2|y1JuNOu0*Vr{OfpyvVr{htFW)mdg&!miW{-7>jvq7abK5rq|FA8DS1=# zb0_5uE6kfXwP1?eI=u0|f^E{zqQkI3@o~xrEkz&s)esM5&JJmlQnpcwPwq-1aPNvcZC#ID)9Ig6e8;B~r- z@?nKETf&!a;txyBz12sID$P+QAC_WG*E_LpD$R0KK7_bQeHNzDBU_cc=QH)-Ftf~;^pamChbQ4GKuS1XIZEr}Qd8IT8LWpnl+AwGas12ENpC7Uj!O;y56d^gZ~y=R diff --git a/norch/nn/__init__.py b/norch/nn/__init__.py index dadface..8f7997b 100644 --- a/norch/nn/__init__.py +++ b/norch/nn/__init__.py @@ -1,3 +1,4 @@ from .modules import * from .activation import * -from .loss import * \ No newline at end of file +from .loss import * +from .functional import * \ No newline at end of file diff --git a/norch/nn/__pycache__/__init__.cpython-38.pyc b/norch/nn/__pycache__/__init__.cpython-38.pyc index 1f19ab62795fbb5ec30fbec1f63af9cd477f3d17..5b7fd1c7b09471efcfce61414f7fe60ec9c05c2f 100644 GIT binary patch delta 131 zcmcc4_>qw}l$V!_0SI_~ywl<)^2#!*Ow^X~XGmepVGdzPVG3r@WPZsAl-Fdu#i-?{ z$#RQ5H$SB`C$%_=D>1nwvn;VBGe0kiB`3caD3Vs12NcWCOUzlxP{ab%1txwuO>9zQ W52*CZFG|jsxLSpa4JgCG2!a5R3nAtJ delta 88 zcmey!c%6|ql$V!_0SF?wEYn;j^2##GOw^VUVhU!^WPZsA6xU?D#i-?{$viPpROA** kPJVImN`@jjPT{fB7k)HKRhy#CsJ~$!{ z&d&aX^V}R={03FM3EN43T~%H6RejwL{<|L>1VInOHMO(dc)AZJZYtR@X*LXxwHh?Y(+l|TYt zzriNB`UfVaNKXM5D!{V?eH8H;nL3Y@Ea?TSx=uxEkm5nD7AH}j$Mqzs)w-&BDTPXv zgFriX5d_%0#Fe8ztm}CSlr81qVdhXiS*FsoH|5l8Kp*nO3P)=LcTO|5*)v?h3+yx1 zp($W9^z!CWnl+{B$BeFF3=z#ctiSl=%@ExV6O0i2sZpck{wx32FoqYzncD~gPyt2C xqHt65J<3A2V2Vam<7C@u)IK)aO==fwFilD3RmEzje2qRkr#VVc0%7&{{sM&UZ)gAj delta 548 zcmYjO%}T>S5Z>8Mn$#v(ZLxw>dQpg=z4jm~3W|sJp!Cv93E9*tNh6#3hl+x}K#ecr z8wlbol7-}u zw|2>-=18}@T`zFs*z<#++nsVzcZ#e^L!ce!5KPd7C6tzY@%uF$1&1rrvpSSXf0;A8 zKC{zZ#pF?}A5oG$+;JH}f`lPEgF7g&605K!hI8Pp6Aa<{*>-rUh3>GQg0E&#nJO(y zOVyW>U$Oi!Ptg(q Date: Mon, 20 May 2024 15:24:55 -0300 Subject: [PATCH 17/28] min max autograd --- norch/__pycache__/tensor.cpython-38.pyc | Bin 17577 -> 17692 bytes .../__pycache__/functions.cpython-38.pyc | Bin 8929 -> 9732 bytes norch/autograd/functions.py | 42 ++++++- norch/tensor.py | 13 ++- test.py | 7 +- tests/test_autograd.py | 105 +++++++++++++++++- tests/test_operations.py | 2 +- 7 files changed, 158 insertions(+), 11 deletions(-) diff --git a/norch/__pycache__/tensor.cpython-38.pyc b/norch/__pycache__/tensor.cpython-38.pyc index 983cfe6016a3d31faae8b3a71fef51ec2b6b18f5..4d65981655e17c69dd8e981cc0fa199a12daf864 100644 GIT binary patch delta 1116 zcmZvb%TH556o>DbTPXHPDV0Y>K!{Ml2a%T`4{Id|2_YdGs3;UmvE@Bf0WAUoDkut> zuv08(Od!!~(R z^+vanyO(e*;WLBF;%id2)~)gSd-C0iHrWZ_6Sej{=un328vvccl#v&Ao3b(ys~YYg z->(ExmYCi{emlV{tZ7-`dsCJMseTfCgaDy{P)JbM(W|)A6@We}tnVk83iEDt6HOU* zS+C}+2?ZpIg_Co}yBOnOk>;vek##M4h%(jPi1%3yk%O>guge;$Av|`Q*e%v6-*eXh z_8P35Y6ATjrTx=2@k}*~%=7#Z=pX(+FAV2AV>_q?r4R&RlJT4n1eh1P$dkhIGU^G^ zD4?$i!7?=NM-H~AIZ?rAAH%$+!=t^dQ3^=`EmqZb$-JA!!IO`b0d!2qOkhAN69q0{@0i%C$WeY4?ttE{@eKP1`4fzdd@>V!66VrMrL3eHq93E~ zu!;t&4Q9lv^UX=KC>(5g3$-1^TsnOqe!Z+2X6b*4l6(BUf3Rwm(taTgAVX|cU)&4&s12=@sO2;+na!X#meKqt!|5uOrg*Zi53f}A7wIpGCj dJEoua^>^^#2%i^UF5QF^;4 z3IvYmLW30DU?R~BJ2&ztkhm~i>(U+Wy=PhyHBK_W``z!J@7}q0?(0R^UI61&gF(x& zXFt(9T9y7}TsJ^gerFgHAdC9qCg{eC#lN5j_Zv*IuOv$LuGtME_|v>qbb|%On*pu!Angjh7Gmf~m^7n@K z0J#OWP7i=?mSTUbT7JgI=f}ln33N~X9!LFIe|njDDNceE%51d|rSMqt6hSz0P{(GUMxWtC?M`eT^IqseV^)u!3qerdDfaZeD2aNaXgbBUJe`OngqnHNKKSBTyCM3*bwK{cI(ZbC1CYQ#Q*a_R4o zN)SZCUBVQBu9i4WNE03rW(aeH$Ao#p0%4Kxl(0&mBM{fPYDOgaoUqQ2Elh~8JT+g1tLrU!_6 zL|(*Nb!zi!mS`j;Te2l-#F8?uEcvBNHvkpC3*vHf z^B$R@5iufdI1?3?uugkmo0O1nqn%PLB~uAHF(PLLN-UbGvJ`kRY;3>ElO;OrXZwFm zv?P&AJ7Y}5lKE5ZS{v(xVz3ZG{@`6`bpd>UWhwv)+$vc+Hejss(??p<)y0NDVK+iO zKu%mTR&#RnR?=8x9T>fU(1Xy6a1p_e;6u2Ca2eqWLcY)XzzRATo)U8-^aD(l!N%d+ z#$qy>la>r~p`Q&PH;7PZRkZSHSAz8(me~YAXcbkdn^p-;f=^V08qo=!N~;Bo)eCDH ze$dyzaql#U-@z(2sVcMSV#}Q1oVLFLba&&LY|XE*Wje7RNrNUzqv zfp6l`loVIydw~vb7^0t%FN``=G0fYWJCEY;g5iIF;lMK-Y=)!Ea8eo89)#~RQDRDb zerX9%C`u5C1CcKRnNeiFSwt+Ck=ql*W(5)AljYfECa(~85cURgX8>_A3y|Po?P*qzM nP`DL=f}jZG*CLP)i$Fdp0y(q@WNlHz(R`mosbo diff --git a/norch/autograd/functions.py b/norch/autograd/functions.py index b862b73..e5b3c20 100644 --- a/norch/autograd/functions.py +++ b/norch/autograd/functions.py @@ -206,8 +206,34 @@ class MaxBackward: self.keepdim = keepdim def backward(self, gradient): - pass + input_shape = self.input[0].shape.copy() + if self.axis == -1: + max_value = self.input[0].max() + mask = self.input[0].equal(max_value) + grad_output = float(gradient.tensor.contents.data[0]) * self.input[0].ones_like() + + grad_output = (grad_output * mask) / mask.sum().tensor.contents.data[0] + + else: + if not self.keepdim: + # Remove dimensions of size 1 from the gradient tensor. + input_shape = [s for i, s in enumerate(input_shape) if i != self.axis] + + # Broadcast the gradient to the input shape along the specified axis. + grad_output_shape = list(input_shape) + grad_output_shape.insert(self.axis, 1) + grad_output = gradient.reshape(grad_output_shape) + grad_output = grad_output + self.input[0].zeros_like() + + max_value = self.input[0].max() + mask = self.input[0].equal(max_value) + + grad_output = (grad_output * mask) / mask.sum().tensor.contents.data[0] + + return [grad_output] + + class MinBackward: def __init__(self, x, axis=None, keepdim=False): self.input = [x] @@ -215,6 +241,18 @@ class MinBackward: self.keepdim = keepdim def backward(self, gradient): - pass + input_shape = self.input[0].shape.copy() + if self.axis == -1: + min_value = self.input[0].min() + mask = self.input[0].equal(min_value) + + grad_output = float(gradient.tensor.contents.data[0]) * self.input[0].ones_like() + + grad_output = (grad_output * mask) / mask.sum().tensor.contents.data[0] + + else: + pass + + return [grad_output] diff --git a/norch/tensor.py b/norch/tensor.py index 677231b..e8963e8 100644 --- a/norch/tensor.py +++ b/norch/tensor.py @@ -643,8 +643,19 @@ class Tensor: def equal(self, other): + if isinstance(other, Tensor) and other.numel == 1: + # other is a single value tensor + other = self.zeros_like() + other + return self.equal(other) + if not isinstance(other, Tensor): - return False + # other is a single value + if isinstance(other, (int, float)): + other = self.zeros_like() + other + + return self.equal(other) + else: + return False if self.shape != other.shape: return False diff --git a/test.py b/test.py index 9cd3b38..8dae5e0 100644 --- a/test.py +++ b/test.py @@ -7,8 +7,11 @@ import random random.seed(1) a = norch.Tensor([1, 2, 3]) -b = norch.Tensor([1, 2, 4]) -print(a == b) +b = 3 +for i in range(a.numel): + print(a.tensor.contents.data[i]) + +print(a) """ diff --git a/tests/test_autograd.py b/tests/test_autograd.py index aa31ca8..16e09bd 100644 --- a/tests/test_autograd.py +++ b/tests/test_autograd.py @@ -56,39 +56,134 @@ class TestTensorAutograd(unittest.TestCase): self.assertTrue(utils.compare_torch(norch_tensor1_grad, torch_tensor1_grad)) self.assertTrue(utils.compare_torch(norch_tensor2_grad, torch_tensor2_grad)) + + norch_tensor1 = norch.Tensor([[[1, 2.5], [3, -4]], [[5, 6], [7, 8]]], requires_grad=True).to(self.device) + norch_tensor2 = norch.Tensor([[[1, 1.], [1, 1.9]], [[1, 1], [1, 1]]], requires_grad=True).to(self.device) + norch_result = (norch_tensor1 + norch_tensor2).sum(axis=1).sum() + + norch_result.backward() + norch_tensor1_grad = utils.to_torch(norch_tensor1.grad).to(self.device) + norch_tensor2_grad = utils.to_torch(norch_tensor2.grad).to(self.device) + + torch_tensor1 = torch.tensor([[[1, 2.5], [3, -4]], [[5, 6], [7, 8]]], requires_grad=True).to(self.device) + torch_tensor2 = torch.tensor([[[1, 1.], [1, 1.9]], [[1, 1], [1, 1]]], requires_grad=True).to(self.device) + + torch_result = (torch_tensor1 + torch_tensor2).sum(axis=1).sum() + torch_result.backward() + torch_tensor1_grad = torch_tensor1.grad + torch_tensor2_grad = torch_tensor2.grad + + self.assertTrue(utils.compare_torch(norch_tensor1_grad, torch_tensor1_grad)) + self.assertTrue(utils.compare_torch(norch_tensor2_grad, torch_tensor2_grad)) + + + def test_max(self): + """ + Test autograd from max + """ + norch_tensor = norch.Tensor([[[10, 10], [-4, -4]], [[5., 6], [7, 8]]], requires_grad=True).to(self.device) + norch_result = norch_tensor.max() + + norch_result.backward() + norch_tensor_grad = utils.to_torch(norch_tensor.grad).to(self.device) + + torch_tensor = torch.tensor([[[10, 10], [-4, -4]], [[5., 6], [7, 8]]], requires_grad=True).to(self.device) + + torch_result = torch_tensor.max() + torch_result.backward() + torch_tensor_grad = torch_tensor.grad + + self.assertTrue(utils.compare_torch(norch_tensor_grad, torch_tensor_grad)) def test_max_axis(self): """ Test autograd from max specifying axis """ norch_tensor = norch.Tensor([[[10, 10], [-4, -4]], [[5., 6], [7, 8]]], requires_grad=True).to(self.device) - norch_result = norch_tensor.max(axis=1).sum() + norch_max_axis = norch_tensor.max(axis=1) + norch_result = norch_max_axis.sum() + + norch_result.backward() + norch_tensor_grad = utils.to_torch(norch_tensor.grad).to(self.device) + + torch_tensor = torch.tensor([[[10, 10], [-4, -4]], [[5., 6], [7, 8]]], requires_grad=True).to(self.device) + + torch_max_axis, _ = torch_tensor.max(axis=1) + torch_result = torch_max_axis.sum() + torch_result.backward() + torch_tensor_grad = torch_tensor.grad + + self.assertTrue(utils.compare_torch(norch_tensor_grad, torch_tensor_grad)) + + + norch_tensor = norch.Tensor([[[10, 10], [-4, -4]], [[5., 6], [7, 8]]], requires_grad=True).to(self.device) + norch_max_axis = norch_tensor.max(axis=2) + norch_result = norch_max_axis.sum() + + norch_result.backward() + norch_tensor_grad = utils.to_torch(norch_tensor.grad).to(self.device) + + torch_tensor = torch.tensor([[[10, 10], [-4, -4]], [[5., 6], [7, 8]]], requires_grad=True).to(self.device) + + torch_max_axis, _ = torch_tensor.max(axis=2) + torch_result = torch_max_axis.sum() + torch_result.backward() + torch_tensor_grad = torch_tensor.grad + + self.assertTrue(utils.compare_torch(norch_tensor_grad, torch_tensor_grad)) + + print(norch_tensor_grad, '\n\n', torch_tensor_grad) + + + def test_min(self): + """ + Test autograd from min + """ + norch_tensor = norch.Tensor([[[10, 10], [-4, -4]], [[5., 6], [7, 8]]], requires_grad=True).to(self.device) + norch_result = norch_tensor.min() norch_result.backward() norch_tensor_grad = utils.to_torch(norch_tensor.grad).to(self.device) torch_tensor = torch.tensor([[[10, 10], [-4, -4]], [[5., 6], [7, 8]]], requires_grad=True).to(self.device) - torch_result = torch_tensor.max(axis=1).sum() + torch_result = torch_tensor.min() torch_result.backward() torch_tensor_grad = torch_tensor.grad self.assertTrue(utils.compare_torch(norch_tensor_grad, torch_tensor_grad)) - def test_min_axis(self): """ Test autograd from min specifying axis """ norch_tensor = norch.Tensor([[[10, 10], [-4, -4]], [[5., 6], [7, 8]]], requires_grad=True).to(self.device) - norch_result = norch_tensor.min(axis=1).sum() + norch_min = norch_tensor.min(axis=1) + norch_result = norch_min.sum() norch_result.backward() norch_tensor_grad = utils.to_torch(norch_tensor.grad).to(self.device) torch_tensor = torch.tensor([[[10, 10], [-4, -4]], [[5., 6], [7, 8]]], requires_grad=True).to(self.device) - torch_result = torch_tensor.min(axis=1).sum() + torch_min = torch_tensor.min(axis=1) + torch_result = torch_min.sum() + torch_result.backward() + torch_tensor_grad = torch_tensor.grad + + self.assertTrue(utils.compare_torch(norch_tensor_grad, torch_tensor_grad)) + + norch_tensor = norch.Tensor([[[10, 10], [-4, -4]], [[5., 6], [7, 8]]], requires_grad=True).to(self.device) + norch_min = norch_tensor.min(axis=2) + norch_result = norch_min.sum() + + norch_result.backward() + norch_tensor_grad = utils.to_torch(norch_tensor.grad).to(self.device) + + torch_tensor = torch.tensor([[[10, 10], [-4, -4]], [[5., 6], [7, 8]]], requires_grad=True).to(self.device) + + torch_min = torch_tensor.min(axis=2) + torch_result = torch_min.sum() torch_result.backward() torch_tensor_grad = torch_tensor.grad diff --git a/tests/test_operations.py b/tests/test_operations.py index ab65a64..008136d 100644 --- a/tests/test_operations.py +++ b/tests/test_operations.py @@ -533,7 +533,7 @@ class TestTensorOperations(unittest.TestCase): def test_equal(self): """ - Test equal two tensors: tensor1 == tensor2 + Test equal two tensors: tensor1.equal(tensor2) """ norch_tensor1 = norch.Tensor([[[1, 2], [3, -4]], [[5, 1], [7, 8]]]).to(self.device) norch_tensor2 = norch.Tensor([[[1, 1], [1, 1]], [[1, 1], [1, 1]]]).to(self.device) From b324b7e1fa60c1a1e68eccdd913a400399f71838 Mon Sep 17 00:00:00 2001 From: lucasdelimanogueira Date: Mon, 20 May 2024 16:42:45 -0300 Subject: [PATCH 18/28] broadcasted equal operation --- build/cpu.o | Bin 13776 -> 15032 bytes build/cuda.cu.o | Bin 134232 -> 144760 bytes build/tensor.o | Bin 48712 -> 50416 bytes norch/__pycache__/tensor.cpython-38.pyc | Bin 17692 -> 18194 bytes .../__pycache__/functions.cpython-38.pyc | Bin 9732 -> 9830 bytes norch/autograd/functions.py | 7 +- norch/csrc/cpu.cpp | 39 +++++++++++ norch/csrc/cpu.h | 1 + norch/csrc/cuda.cu | 61 ++++++++++++++++++ norch/csrc/cuda.h | 3 + norch/csrc/tensor.cpp | 48 ++++++++++++++ norch/csrc/tensor.h | 1 + norch/libtensor.so | Bin 181968 -> 194792 bytes norch/tensor.py | 60 +++++++++++++---- tests/test_operations.py | 15 +++++ 15 files changed, 221 insertions(+), 14 deletions(-) diff --git a/build/cpu.o b/build/cpu.o index 3299b62bd1e95cbf0cb55b237c31bd7fe046c036..64295093a522141da1cc4b87261cfd18857efdb7 100644 GIT binary patch delta 1050 zcmZ{hO=uHQ5Xaws&?I7{TVpp#H7^<}Noq=V-LyFcMFJj*RPZ2J6+thphXxGvAZ$_S zO^h885k&B26>L}!0X_8S!9zp^MLZ}TY(c06#e-1i<;{bz(1ADe<~Q^HZ}&}iuG4Jg z^-iT-Y}xHEhjgvJZ2W~$k}Ida}e-8L9w z^*w`2`ib~}v1IIyMA?2sn_zK%a8OjyP70^Ge9^;tU1d1Uxi$?A^z(2BeGA&?SK%$s zY46eVAd$Un`n_xLP!k1*n8ALeg(729GO+u^y%7m|7vlC6@$1AdQvWyNpNT&t9yO)@ zg7^ot?=rY-oC-4sB$<(cog&^L-XJ~))~KDDr)nW1v8vt*j*nh22C7#Y%}ZCIe0VH) zl+S9xTA^(CPi@(YN7+7H|F)&A?Z7_~FJkX-U;id?9a*H0|L)TfTcy7(xzEJK$-cs4 zdTS+g1Z%=R5EuO^TuSKc4m?M19oDhvafHqU!>>?6H~I?Ipj0}i`)ck9oS!hEC6=1I z7vgH}XF^=f{aT2tx!(Y4Hd258`7PSl(XfI>}v~QZHxI zCTuwq`c?+4>@Xa2^J=>&w<~Thwo`B=>|D|Kk3jhEB delta 749 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 DTdWP@ delta 24529 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 delta 3615 zcmZ`+Z%kEX9KPoO;iag6@Nkx!u>3&O)~+;hIS2?6kgfu)^h%7Iz%**g(v1(&1#@m; ztJp7^rcSM`4~zo3CIS8Nav5SD8no8T%FVXc`p`mV^@VA__uqIpxBG7IIh^PDJ@5Pb z_a5$9d?RIU*?y+e%`g0+Cq8OY3d2Bx4wv; z32j;9cWj>=r)ca&$NE}a-CxsvzHFc68!cCyvAHE}T&mdPxTR~9&E?9~HZ_8^I;73( z+U9!IYMZyD&9Jn&qHX?uTG+xpX^|Gb=S^Xk#N_BWQ+&Xg%Wm2da+T)v~mJmB{Lz65v{;F+{A?6?I_ zGQ^{ZXC_ax+Geso=3Hr3>~$}&TPeV7Fp0KZYF|pCJt(x1SrwD$K9;xgdn$51GCR+ z)*Dg_^)cI?M0;3j`;usn3vJ{V&6*^dYX1f*thT>Wh9RTCYOe!4#3rT~#eid4>lCii zfq81CiV??sRm*H%N;IKauA90>oX8Pa^gdt*0pAVyFyQD!3~;RN65v?d+X}BtXwm$q zcsCy2+HL_HgWOI7qmJ95S(}v@WGBU#Jp@_|^03rW67310jeL+qd&{FT)m{dBtag5t z3@f49#el!SCZ;@f3RhLdOoypqjH{|;MJ&NrT0f03`!+0!ue6hNW6TK;1KR>y#sSA2 zuL6#>^;fTigtg73g-;!~1Xw(zjgsxqtk?e@vZ9WA1hnY?n-pM{l4ysdb|i^*LTDp* z>wjdXQB2u#G@i;KVcALm$3t2Ncq`;E3^?YnTj7a^w4ZKrNawZ8xMjNFBPzJyL`Gpz z?B6)xVQ}Ia;ON9(fMX%^x3ZXr2p$XhsKOHuX%GZh+kJrJA?=}x&m8w%&3c18NnOl- z2wDtsNNQtAw6j7RnNOnKSR>=|nH^2FpU}9s+9ANP+OGhk7?GpZmRJ( zEihXKTI{20sclZ84NGlj67542o^aepwyk)D?}%+Qz-$Y!4QyzCQExqEPj9kkgVYsUORdv|=FfBd=UkA!nJ%-dnnVrR5c3JjA1VmV|h4rf%*3F7`QC#)`S|zD9sNjO#g)*=oTt?Ak zwzdJ~!!P8BnrK?EO^i+2v>9I-W1?@qYJ6z6@xjDC_N{&K!StR16`Rh(@1FagbI$*M zw+~sme;=6gLqyOo-U)}RAD4IXG24pB=Y56v2!;j1Vfp&bd$!8|K8Itb> zqR@+f1#Ww~MIrAY_y|e-ZgomMPT^9)+KT%O68L*%qrIr41~c1>roE~OJa_FpB(jRO zoc?8+^C4`n-VB;KR(%g(7~S=oy%}nYz49 z!Vn=(2orV?tYzuuk)R21f*Lzdl1z;`xAxRkUoAVWdaEEzB3}9MqRS9DA+~I8sj-Gb zYJn=P&EZcCoy+G^-uR2GP!H^@4q$K8VZPk-Bfyfu?JZs4JWFLOT-SV*V9Wpcb_1MK z@Bhcrrh~Y@q5{X74mA!))6%5W4=FjNOq3Lu!3;^W8=%?8SvQ-O3bG;30(ao^rd=J1 z4&ze6W+;X&SvDmtcoroQ+4b@%X&gl46}3Jw`cgDwhE21_r5~1Ieig_4Nta%-uu69CGO>oTMOJvvlh*deRm9$vo?$@McS zWZTb~*mVLA)c&?%#kJB&Xawu-{1nm6UT3(cZmsKsgX!#Oj&Dtmoah_IOjRWd1+XjJ zqOMs|9@Is)94WQpq;941?AvBmTdU)Fp-l+;(N=sB4y;q;(lReoSrvGg7(oRGI9Q1_ zdS&xfI0o?8+!?VmXHn@pH8{#Ii3ZEJ?S?UST8Sqw(}w#;BQC}TbvGoS4a0&A18IkM3V#zfq;6lYt02FPH| z-ap|CK8)4k`qnf|;#6zzx=+Zq{eQea71~Wy_YI0ioZlBYvxjo_650qI1j^x^1nV7t zLD2|dlrTm(MWE-wzao51_=Ye+I7gTyOcQ1Zblm&`VU9p&z)`9Zyh`Cb;hI3PB+qmC XG}lk_>)0AU260@B*TM~a6hHbe$)6|Z delta 984 zcmZva+fP$L5XR4JOG|pQrBt8=xf!5>0W1`PP(VvXNYDf|P?d|eg>ow_D4>;#;0*!A z3Gu~5B*a9cMsrBi_*d{?Vh9gB=%a=g-i*3)ATbh8a(?se>^D2Rn|(A7PiDY$)@am8 z;#-`q4=qi+Gc6jyFRvIoRNzO0r2xEGVcCXST+6k}zPMgWui8rCB7V0mo0~-;_Yu$3%uYgPe!3&XET}dDyWE z(1E7RJXt+UA~ht$@J|seTfi2Yg)D@B%~j3tvDH5>^cmkeNLqU zPI=x%C?XUSl&$v3{&X22Le2F3l%{69M>%IxrbDGy>Xm{bO5#G+objsgIN9!ol&mNF zoMxCRl>_7J?9;n%Ras#^DvW6AtX5?U*?MMR5; zaU#ZvNFMPwN={V+XQG+UX+@j6Qey}XTxsvY0bK%arp4g;5nV|S?M1mU1@SbzB`_`O z26KdvSUfX;j_H^Q41#JgQZsqW?E!c$=a*_(tY6(e5+3At#bso%j|^u$9(kqA2XF^} zRoLMkYRa4KGqm!aMwdlygZ!29hw_@g{v=N`%wT$Td`xe+?>CcYJ*~hpRaj7ZNKU9q z2ja|+emd2dIO-vI}5Z>8sx7&qQ+64;OLMeZ(kccE21kr-xFCrlRfl(Wa5fs!Z5`UwK9=w=n z#)F9mR4$l!&@}Pj!T3isCMG5(9vWlf#gkt2;?bFHL3+?8@7wq0`{upQo0)gD=Ub0w zm&ao&{JTE0?Z~gkp6yO}kREmhRbL$&t9hgx3zJh6NA&2j*)y&gm zp1L`G!#4(2D;uZsh*sXFn8G#7sn6OK2r>7-fiC`_Ay6R@vIF{PeTF7_RNMzO1SMx- zC7my6fDp};oP|<`_ASi@ z;#MCoZ}XVHB@O%R4cg02=mR3%Y47r;>~O(ydnqtryZ4uOS^d16<}vT?#D3fTTlu=& z`5hJatwC1IE>dYYXscS5e&YDt!K2RI%s!$Yp&c|AjORQ3KRgA`=xw-n_w1MCBIuLaIMTc1*B)NK%k4^3%#iBxHO1Co}W`q^PCos z$75D5jiOpFAVrhW#uH~U&XH566UT9-ST_hrnd3%*O#)#7DLULNuti|2fL$FuJj*yw z;^@R=%*Ym{S5d|l+|I<1n-4fRl{kKSB;y!Qq>iEV2ksEcT%c0f2<%wwFqSa?fk!IJ zpg<9r;DsVJtOgyrTB;emGQqEU`D;Ka6w>p^YHL4B--(mt!2mAo{ke>tS(GjKMNewx zMw(a?EBwcua3LIE{{J@;(-(qWh;?)#8!H|X_g*O`_)WiSw_ZxH;U|y3e@-l%l~tlt z1&Rgap+kAOP#zi_@% delta 1275 zcmaJ>O-vI}5bo=?ODVKawy=O(+EV@&j75SXe_Dzt|4;}9XoL`~5e3v~Xds&4ff$eI zcrfu`q8GfOy?IoL#>9k##6#69H@$fB=FGMP8l#)cx9`n-GrRNi-u~dmpku<}u$$(-)%Y*IG@#J1kQpg_CgCG4i1)t^ihyd=Y zrNz~xtYa@Z<7Jbfn*2gIW}g?e#8&aMJxVKYj)pyXVOI?iJJ}=i9SauP!^u=$R45#% z=ZHyyIu4b5;Aj)Vi|?EpN$*@{NSVkov``o~-t`7?RGCmUvc0rG4!yv}P|lzk>Fg=rL<4TSM@1!y;x*BXcZzF; zhA)b5i4eL=W|TG}-y_MSXtzQe=YcyVJ>rZJqf(tLY9)p)K|j5~%AguG+EPQx2yLH` z9MnjH5hFl*=}kGp)G;H~lb7h_veC{iip)-|474gyqI@Db zoU>@AfQ|S%(0ina;wP3??Zg_0tmpqDdFst&vj0DIUH&_{h2?Z|FvIRc++Xnvr>pzd zXG!sc@I3(a419or4=eDQ0zMbO(+xbl!1D<_c)+6s VTn^w5q>E}d;a4>ugEgl`;164q?|=XR diff --git a/norch/autograd/functions.py b/norch/autograd/functions.py index e5b3c20..daa0668 100644 --- a/norch/autograd/functions.py +++ b/norch/autograd/functions.py @@ -216,6 +216,7 @@ class MaxBackward: grad_output = (grad_output * mask) / mask.sum().tensor.contents.data[0] else: + if not self.keepdim: # Remove dimensions of size 1 from the gradient tensor. input_shape = [s for i, s in enumerate(input_shape) if i != self.axis] @@ -225,8 +226,12 @@ class MaxBackward: grad_output_shape.insert(self.axis, 1) grad_output = gradient.reshape(grad_output_shape) grad_output = grad_output + self.input[0].zeros_like() - + + print(self.input[0]) max_value = self.input[0].max() + print(max_value) + max_values = self.input[0].max(axis=self.axis, keepdim=True) + print('\n\n', max_values, '@@@') mask = self.input[0].equal(max_value) grad_output = (grad_output * mask) / mask.sum().tensor.contents.data[0] diff --git a/norch/csrc/cpu.cpp b/norch/csrc/cpu.cpp index 6d65273..9584c23 100644 --- a/norch/csrc/cpu.cpp +++ b/norch/csrc/cpu.cpp @@ -306,6 +306,45 @@ void equal_tensor_cpu(Tensor* tensor1, Tensor* tensor2, float* result_data) { } } +void equal_broadcasted_tensor_cpu(Tensor* tensor1, Tensor* tensor2, float* result_data, int* broadcasted_shape, int broadcasted_size) { + int max_ndim = tensor1->ndim > tensor2->ndim ? tensor1->ndim : tensor2->ndim; + + // Calculate strides for broadcasting + int* strides1 = (int*)malloc(max_ndim * sizeof(int)); + int* strides2 = (int*)malloc(max_ndim * sizeof(int)); + if (strides1 == NULL || strides2 == NULL) { + fprintf(stderr, "Memory allocation failed\n"); + exit(1); + } + + int stride1 = 1, stride2 = 1; + for (int i = max_ndim - 1; i >= 0; i--) { + int dim1 = i < tensor1->ndim ? tensor1->shape[tensor1->ndim - max_ndim + i] : 1; + int dim2 = i < tensor2->ndim ? tensor2->shape[tensor2->ndim - max_ndim + i] : 1; + strides1[i] = dim1 == broadcasted_shape[i] ? stride1 : 0; + strides2[i] = dim2 == broadcasted_shape[i] ? stride2 : 0; + stride1 *= (dim1 == broadcasted_shape[i]) ? dim1 : 1; + stride2 *= (dim2 == broadcasted_shape[i]) ? dim2 : 1; + } + + // Perform element-wise equal with broadcasting + for (int i = 0; i < broadcasted_size; i++) { + int index1 = 0, index2 = 0; + int linear_index = i; + for (int j = max_ndim - 1; j >= 0; j--) { + int pos = linear_index % broadcasted_shape[j]; + linear_index /= broadcasted_shape[j]; + if (strides1[j] != 0) index1 += pos * strides1[j]; + if (strides2[j] != 0) index2 += pos * strides2[j]; + } + result_data[i] = (tensor1->data[index1] == tensor2->data[index2]) ? 1.0f : 0.0f; + } + + // Free strides + free(strides1); + free(strides2); +} + void ones_like_tensor_cpu(Tensor* tensor, float* result_data) { diff --git a/norch/csrc/cpu.h b/norch/csrc/cpu.h index b006ca7..83bd83f 100644 --- a/norch/csrc/cpu.h +++ b/norch/csrc/cpu.h @@ -22,6 +22,7 @@ void tensor_pow_scalar_cpu(Tensor* tensor, float exponent, float* result_data); void log_tensor_cpu(Tensor* tensor, float* result_data); void scalar_mul_tensor_cpu(Tensor* tensor, float scalar, float* result_data); void equal_tensor_cpu(Tensor* tensor1, Tensor* tensor2, float* result_data); +void equal_broadcasted_tensor_cpu(Tensor* tensor1, Tensor* tensor2, float* result_data, int* broadcasted_shape, int broadcasted_size); void ones_like_tensor_cpu(Tensor* tensor, float* result_data); void zeros_like_tensor_cpu(Tensor* tensor, float* result_data); void transpose_1D_tensor_cpu(Tensor* tensor, float* result_data); diff --git a/norch/csrc/cuda.cu b/norch/csrc/cuda.cu index 5726c69..7d056ab 100644 --- a/norch/csrc/cuda.cu +++ b/norch/csrc/cuda.cu @@ -564,6 +564,67 @@ __host__ void equal_tensor_cuda(Tensor* tensor1, Tensor* tensor2, float* result_ cudaDeviceSynchronize(); } +__global__ void equal_broadcasted_tensor_cuda_kernel(float* data1, float* data2, float* result_data, int* broadcasted_shape, int* strides1, int*strides2, int max_ndim, int size) { + int i = blockIdx.x * blockDim.x + threadIdx.x; + if (i >= size) return; + + int index1 = 0, index2 = 0; + int linear_index = i; + for (int j = max_ndim - 1; j >= 0; j--) { + int pos = linear_index % broadcasted_shape[j]; + linear_index /= broadcasted_shape[j]; + if (strides1[j] != 0) index1 += pos * strides1[j]; + if (strides2[j] != 0) index2 += pos * strides2[j]; + } + result_data[i] = (data1[index1] == data2[index2]) ? 1.0f : 0.0f; +} + +__host__ void equal_broadcasted_tensor_cuda(Tensor* tensor1, Tensor* tensor2, float* result_data, int* broadcasted_shape, int broadcasted_size) { + int max_ndim = tensor1->ndim > tensor2->ndim ? tensor1->ndim : tensor2->ndim; + + int* strides1 = (int*)malloc(max_ndim * sizeof(int)); + int* strides2 = (int*)malloc(max_ndim * sizeof(int)); + if (strides1 == NULL || strides2 == NULL) { + fprintf(stderr, "Memory allocation failed\n"); + exit(1); + } + + int stride1 = 1, stride2 = 1; + for (int i = max_ndim - 1; i >= 0; i--) { + int dim1 = i < tensor1->ndim ? tensor1->shape[tensor1->ndim - max_ndim + i] : 1; + int dim2 = i < tensor2->ndim ? tensor2->shape[tensor2->ndim - max_ndim + i] : 1; + strides1[i] = dim1 == broadcasted_shape[i] ? stride1 : 0; + strides2[i] = dim2 == broadcasted_shape[i] ? stride2 : 0; + stride1 *= (dim1 == broadcasted_shape[i]) ? dim1 : 1; + stride2 *= (dim2 == broadcasted_shape[i]) ? dim2 : 1; + } + + int* d_broadcasted_shape; + int* d_strides1; + int* d_strides2; + + cudaMalloc((void**)&d_broadcasted_shape, max_ndim * sizeof(int)); + cudaMemcpy(d_broadcasted_shape, broadcasted_shape, max_ndim * sizeof(int), cudaMemcpyHostToDevice); + + cudaMalloc((void**)&d_strides1, max_ndim * sizeof(int)); + cudaMemcpy(d_strides1, strides1, max_ndim * sizeof(int), cudaMemcpyHostToDevice); + + cudaMalloc((void**)&d_strides2, max_ndim * sizeof(int)); + cudaMemcpy(d_strides2, strides2, max_ndim * sizeof(int), cudaMemcpyHostToDevice); + + int number_of_blocks = (broadcasted_size + THREADS_PER_BLOCK - 1) / THREADS_PER_BLOCK; + equal_broadcasted_tensor_cuda_kernel<<>>(tensor1->data, tensor2->data, result_data, d_broadcasted_shape, d_strides1, d_strides2, max_ndim, broadcasted_size); + + cudaError_t error = cudaGetLastError(); + if (error != cudaSuccess) { + printf("CUDA error: %s\n", cudaGetErrorString(error)); + exit(-1); + } + + cudaDeviceSynchronize(); + cudaFree(d_broadcasted_shape); +} + __global__ void ones_like_tensor_cuda_kernel(float* data, float* result_data, int size) { diff --git a/norch/csrc/cuda.h b/norch/csrc/cuda.h index 712e600..73eb831 100644 --- a/norch/csrc/cuda.h +++ b/norch/csrc/cuda.h @@ -52,6 +52,9 @@ __global__ void equal_tensor_cuda_kernel(float* data1, float* data2, float* result_data, int size); __host__ void equal_tensor_cuda(Tensor* tensor1, Tensor* tensor2, float* result_data); + __global__ void equal_broadcasted_tensor_cuda_kernel(float* data1, float* data2, float* result_data, int* broadcasted_shape, int* strides1, int*strides2, int max_ndim, int size); + __host__ void equal_broadcasted_tensor_cuda(Tensor* tensor1, Tensor* tensor2, float* result_data, int* broadcasted_shape, int broadcasted_size); + __global__ void ones_like_tensor_cuda_kernel(float* data, float* result_data, int size); __host__ void ones_like_tensor_cuda(Tensor* tensor, float* result_data); diff --git a/norch/csrc/tensor.cpp b/norch/csrc/tensor.cpp index 8564b2a..570aefa 100644 --- a/norch/csrc/tensor.cpp +++ b/norch/csrc/tensor.cpp @@ -1084,6 +1084,54 @@ extern "C" { } } + Tensor* equal_broadcasted_tensor(Tensor* tensor1, Tensor* tensor2) { + + if (strcmp(tensor1->device, tensor2->device) != 0) { + fprintf(stderr, "Tensors must be on the same device: %s and %s\n", tensor1->device, tensor2->device); + exit(1); + } + + int max_ndim = tensor1->ndim > tensor2->ndim ? tensor1->ndim : tensor2->ndim; + + // Determine the broadcasted shape + int* broadcasted_shape = (int*)malloc(max_ndim * sizeof(int)); + if (broadcasted_shape == NULL) { + fprintf(stderr, "Memory allocation failed\n"); + exit(1); + } + for (int i = 0; i < max_ndim; i++) { + int dim1 = i < tensor1->ndim ? tensor1->shape[tensor1->ndim - 1 - i] : 1; + int dim2 = i < tensor2->ndim ? tensor2->shape[tensor2->ndim - 1 - i] : 1; + if (dim1 != dim2 && dim1 != 1 && dim2 != 1) { + fprintf(stderr, "Shapes are not compatible for broadcasting\n"); + exit(1); + } + broadcasted_shape[max_ndim - 1 - i] = dim1 > dim2 ? dim1 : dim2; + } + + int broadcasted_size = 1; + for (int i = 0; i < max_ndim; i++) { + broadcasted_size *= broadcasted_shape[i]; + } + + if (strcmp(tensor1->device, "cuda") == 0) { + float* result_data; + cudaMalloc((void **)&result_data, broadcasted_size * sizeof(float)); + equal_broadcasted_tensor_cuda(tensor1, tensor2, result_data, broadcasted_shape, broadcasted_size); + return create_tensor(result_data, broadcasted_shape, max_ndim, tensor1->device); + } + else { + float* result_data = (float*)malloc(broadcasted_size * sizeof(float)); + if (result_data == NULL) { + fprintf(stderr, "Memory allocation failed\n"); + exit(1); + } + + equal_broadcasted_tensor_cpu(tensor1, tensor2, result_data, broadcasted_shape, broadcasted_size); + return create_tensor(result_data, broadcasted_shape, max_ndim, tensor1->device); + } + } + Tensor* ones_like_tensor(Tensor* tensor) { char* device = (char*)malloc(strlen(tensor->device) + 1); diff --git a/norch/csrc/tensor.h b/norch/csrc/tensor.h index bdcf25d..5669d4f 100644 --- a/norch/csrc/tensor.h +++ b/norch/csrc/tensor.h @@ -31,6 +31,7 @@ extern "C" { Tensor* scalar_pow_tensor(float base, Tensor* tensor); Tensor* log_tensor(Tensor* tensor); Tensor* equal_tensor(Tensor* tensor1, Tensor* tensor2); + Tensor* equal_broadcasted_tensor(Tensor* tensor1, Tensor* tensor2); void to_device(Tensor* tensor, char* device); Tensor* ones_like_tensor(Tensor* tensor); Tensor* zeros_like_tensor(Tensor* tensor); diff --git a/norch/libtensor.so b/norch/libtensor.so index 90f234627e5a99a18bef507126d80646332a50f1..d1b1cd0abda25d6f7180b68a7f9c04a092883ca5 100755 GIT binary patch delta 46385 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 diff --git a/norch/tensor.py b/norch/tensor.py index e8963e8..22cc111 100644 --- a/norch/tensor.py +++ b/norch/tensor.py @@ -656,25 +656,59 @@ class Tensor: return self.equal(other) else: return False - - if self.shape != other.shape: - return False - Tensor._C.equal_tensor.argtypes = [ctypes.POINTER(CTensor), ctypes.POINTER(CTensor)] - Tensor._C.equal_tensor.restype = ctypes.POINTER(CTensor) + broadcasted_shape_add = [] - result_tensor_ptr = Tensor._C.equal_tensor(self.tensor, other.tensor) + # Function to determine if broadcasting is needed and get the broadcasted shape + def broadcast_shape(shape1, shape2): + if shape1 == shape2: + return shape1, False + + max_len = max(len(shape1), len(shape2)) + shape1 = [1] * (max_len - len(shape1)) + shape1 + shape2 = [1] * (max_len - len(shape2)) + shape2 - result_data = Tensor() - result_data.tensor = result_tensor_ptr - result_data.shape = self.shape.copy() - result_data.ndim = self.ndim - result_data.device = self.device - result_data.numel = self.numel + + for dim1, dim2 in zip(shape1, shape2): + if dim1 != dim2 and dim1 != 1 and dim2 != 1: + raise ValueError("Shapes are not compatible for broadcasting") + broadcasted_shape_add.append(max(dim1, dim2)) + return broadcasted_shape_add, True + + broadcasted_shape_add, needs_broadcasting = broadcast_shape(self.shape, other.shape) + if needs_broadcasting: + # Call equal_broadcasted_tensor if broadcasting is needed + Tensor._C.equal_broadcasted_tensor.argtypes = [ctypes.POINTER(CTensor), ctypes.POINTER(CTensor)] + Tensor._C.equal_broadcasted_tensor.restype = ctypes.POINTER(CTensor) + + result_tensor_ptr = Tensor._C.equal_broadcasted_tensor(self.tensor, other.tensor) + + result_data = Tensor() + result_data.tensor = result_tensor_ptr + result_data.shape = broadcasted_shape_add.copy() + result_data.ndim = len(broadcasted_shape_add) + + result_data.device = self.device + result_data.numel = 1 + for s in result_data.shape: + result_data.numel *= s + + else: + Tensor._C.equal_tensor.argtypes = [ctypes.POINTER(CTensor), ctypes.POINTER(CTensor)] + Tensor._C.equal_tensor.restype = ctypes.POINTER(CTensor) + + result_tensor_ptr = Tensor._C.equal_tensor(self.tensor, other.tensor) + + result_data = Tensor() + result_data.tensor = result_tensor_ptr + 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 log(self): Tensor._C.log_tensor.argtypes = [ctypes.POINTER(CTensor)] Tensor._C.log_tensor.restype = ctypes.POINTER(CTensor) diff --git a/tests/test_operations.py b/tests/test_operations.py index 008136d..6a12367 100644 --- a/tests/test_operations.py +++ b/tests/test_operations.py @@ -546,6 +546,21 @@ class TestTensorOperations(unittest.TestCase): self.assertTrue(utils.compare_torch(torch_result, torch_expected)) + def test_broadcasted_equal(self): + """ + Test broadcasted equal two tensors: tensor1.equal(tensor2) + """ + norch_tensor1 = norch.Tensor([[[10, 10], [-4, -4]], [[5., 6], [7, 8]]]).to(self.device) + norch_tensor2 = norch.Tensor([[[10, 10]], [[5, 6]]]).to(self.device) + norch_result = norch_tensor1.equal(norch_tensor2) + torch_result = utils.to_torch(norch_result).to(self.device) + + torch_tensor1 = torch.tensor([[[10, 10], [-4, -4]], [[5., 6], [7, 8]]]).to(self.device) + torch_tensor2 = torch.tensor([[[10, 10]], [[5, 6]]]).to(self.device) + torch_expected = (torch_tensor1 == torch_tensor2).float() + + self.assertTrue(utils.compare_torch(torch_result, torch_expected)) + def test_zeros_like(self): From cd57c39ff0da13355009186c87a0699f1550d084 Mon Sep 17 00:00:00 2001 From: lucasdelimanogueira Date: Mon, 20 May 2024 18:44:43 -0300 Subject: [PATCH 19/28] min max broadcasted autograd and small fix on add broadcasted --- norch/__pycache__/tensor.cpython-38.pyc | Bin 18194 -> 18460 bytes .../__pycache__/functions.cpython-38.pyc | Bin 9830 -> 10085 bytes norch/autograd/functions.py | 23 ++++--- norch/tensor.py | 20 ++++++ test.py | 18 ++++-- tests/test_autograd.py | 59 +++++++++++++++--- tests/test_operations.py | 25 ++++++-- 7 files changed, 119 insertions(+), 26 deletions(-) diff --git a/norch/__pycache__/tensor.cpython-38.pyc b/norch/__pycache__/tensor.cpython-38.pyc index 1705e208cd96ce49ff08d85fed2704db9611c2bc..0e09a81a352e9292f15a031df4e10fab02489361 100644 GIT binary patch delta 1603 zcmdUvU1$_n6vyxX&dly)-LLFNHrY)zTI)wutRK5Zku++E5~+gLhaz=tT_TJcbR7!P z*{%|hh##yuEs6^6f`a%}%7Dax(E8v*pQE$_)u)m?947nG;e+B!0b8q z%;DbO-v7P-(KEPIL3l?f6cGIUGQN56hZ8@AKTF96pno!*bR;#mTtRWU1w*rhp%s*N zu}>BUEmVC4WiPm{rbG=T49#)AJAGcyh$Xxp;q?g9U)9Z)8X9r0Gy}#ABbX00D&9Ek z#A=Xdj96tDk(w;2W?)#9G)uL#d}Ksaqcy>qYPODuVQ}W)#W=goNGLN6P3pd%CIid^>>ik87bs3S=x57P^B%i?$Y-iZ_q)f(`ld1sug?b ztM&2GmH_>nUPiB1MM%qxqz4>95k^p|N-z?WJ{d)V-V83aqEIAMB7f zC&}&X&HqL2iF1y29=ej+9;V@G~s^J)?7E8;?gJ|hQkANFV{J9T5TPT6ZXs8r@%9NX?Ir2my%BP z^w?L{JO*NPvZvP`Tl)ye+9!K z5^!M=Y$H~ync%Z@^}w+w^S5n-rErRMSNFuDA5=rqsG3rci^r)dDMgAoRGiCX=N3IC z%^Fdx8YtEjjftu>M`8v|)C?i(0!i9Z1V^H3Um$aUBL}W^VsuUj_?9(;A_mD3IcC%h zB2i?`7>E-}Lh|%2TKF^6CkrGJ}CjpVMLZAdhXVlW)NZea?7hn@>@|Lqh*h?#Ru+^O|Hd1k} zp;xap*uX+X!D5%GNXx-NWoiT?n88e?baYS~7`e0Z1wcFdSTzDiGm@^YotAW@U1w6AS=Xg+uWq9W-GgW! z#Z=%rimoGZbRC`k8a|QV>`x5$8Vq+RgMp4*9|QBI97y5K(lKzZdl);aW*CZMaoDHU zH*k?XZVN&Qll={Bz&`*IJMV8cp3!U9%kKLtJ?olA=HL8X01tT}&`RMIpX!h){A5!d zUHrk`r$D^|i*$7IXI);PeFAF_)N^kSjz=@&aPLikNnRCH0M4?7z8EZVWB)8v?32o* WZtV4vkZxU)8=Ft^NI{kwIQZwJbHPH7p_wwX8L)>5Rb)njDk+6xT6n3QcxbikZAhNr&;) zaZFZI{>b=lvV@8uKL^ly79eH@;$rGpo+ZM-2sDfhXd)XB zaxqkC_-5u!=20>Lx$3cs&E!@kIVPYx6;-7J;(@A){6K_1h)4htpfD+N1QFaIf*(kH x2I67`Ai=;Wz{thO2lRyklMFK(lNd7xkjKR+1o0Wu*=G3>Wk3yL!HN+w4r=`co3ZdFoY)R??V zNrsP!kCBg&gHeo;hf#n@YVsMSkBpxu?^8CkVgq`D8Hk;MxVQ>P)Ii;x#khb8=vpQq zpE-poo2jS-CIj(B&E#4IdB&Q_a}~727@%%q_A6op8d0PJBs6&@uTZs7)?_PU2MO_k z2wxx(#hII#7hjf`Q<_?o021_@JW*U+{0ER(3{u3vD8R@CbgTfA0+S5u Date: Mon, 20 May 2024 20:16:41 -0300 Subject: [PATCH 20/28] softmax v1 need fix some erros yet --- build/tensor.o | Bin 50416 -> 50768 bytes norch/__pycache__/tensor.cpython-38.pyc | Bin 18460 -> 18512 bytes norch/csrc/tensor.cpp | 28 +++++++++--- norch/libtensor.so | Bin 194792 -> 194800 bytes .../nn/__pycache__/activation.cpython-38.pyc | Bin 1201 -> 1616 bytes norch/nn/activation.py | 11 ++++- norch/nn/functional.py | 13 ++++++ norch/tensor.py | 15 ++++-- test.py | 15 ++++-- tests/test_nn.py | 43 ++++++++++++++++-- 10 files changed, 106 insertions(+), 19 deletions(-) diff --git a/build/tensor.o b/build/tensor.o index bcc09bcf292ec24c4a1201335bb53e4de60a905c..d12bc526f48941d9271926bd72e9a6df3e846ecd 100644 GIT binary patch literal 50768 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 W{nwXRw*4>PYWGO!j`G*t|9=3Qdz{Vy literal 50416 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 V2b)**V^YYXx~SlNd3)%ke*mFgX=?xg delta 1322 zcmchWNlcSL6vw~!wG=}wp+IQ@7K$`N5m7{0#X_}3*-9iv#f5EvNZA}0B&nhi_pSQe zF)|eUQ7NiOEz6Z?4ik*x$;@vH4t!WODbZe=7fTJT1SFtxMPH{eHyQFGc22JoXRH77P= z6x;g>BG{CA2P&~4Gac<|haiY!X?f72y-Iry(2Iv1dGq=>go|+3DBY|F7|R&T^-4ZE z-l%55^G-%LK6YkPph@R|9{5^@z9GoAK}K{dE4|QNTI~F10J}Mvc@HmhGATK>UbNL^ zH^2b8v&StVvbfc$GNv8yNA^0?KHd{;v^}SB>Wp6IJmj5$^7-1;ym0_MrnVt}BbX1* zlIY!HuR7(Ey)*>oV}JT+FA6`a8NW5k<{pOLM>4A*zAhT3wpSHzv-QuIbSK?@QhQuH z0Wq?KZ1}u9)#w5e=Sl+wp^z$jLrRZ1WNa}DwHsI`D>R$C)qH_Pt98ESc9Og`mMaMDwRPs$ztB2jXzJWVOwN!XZVgR@vzk%bQz znDCg#xwLJsx2+>M)FwsrbB4(=dQQI%tBckW4T`dJy3%6&Rx$5?RNCU%1Yx>A$(FwD z{mW-c&?Y?_K;2}(veI0wttu1L-l)Fsy$^5@x32yKBj>#t*zW6uF?{7~4b*X8HM2BV zPiqf2vWbKOCNQaPLxW7AiQf$kbc^{8`2QL)=r5sHecN9&Qb}f6#qcp|85~qLGW3Vt z!Dndim) * sizeof(int)); for (int i = 0; i < tensor->ndim; i++) { - shape[i] = tensor->shape[i]; + if (axis == -1) { + shape[i] = 1; + } else { + shape[i] = tensor->shape[i]; + } } shape[axis] = 1; ndim = tensor->ndim; - + Tensor* new_tensor = create_tensor(result_data, shape, ndim, device); + return reshape_tensor(new_tensor, shape, ndim); } return create_tensor(result_data, shape, ndim, device); + } } @@ -289,11 +295,16 @@ extern "C" { if (keepdim) { shape = (int*) malloc((tensor->ndim) * sizeof(int)); for (int i = 0; i < tensor->ndim; i++) { - shape[i] = tensor->shape[i]; + if (axis == -1) { + shape[i] = 1; + } else { + shape[i] = tensor->shape[i]; + } } shape[axis] = 1; ndim = tensor->ndim; - + Tensor* new_tensor = create_tensor(result_data, shape, ndim, device); + return reshape_tensor(new_tensor, shape, ndim); } return create_tensor(result_data, shape, ndim, device); @@ -353,11 +364,16 @@ extern "C" { if (keepdim) { shape = (int*) malloc((tensor->ndim) * sizeof(int)); for (int i = 0; i < tensor->ndim; i++) { - shape[i] = tensor->shape[i]; + if (axis == -1) { + shape[i] = 1; + } else { + shape[i] = tensor->shape[i]; + } } shape[axis] = 1; ndim = tensor->ndim; - + Tensor* new_tensor = create_tensor(result_data, shape, ndim, device); + return reshape_tensor(new_tensor, shape, ndim); } return create_tensor(result_data, shape, ndim, device); diff --git a/norch/libtensor.so b/norch/libtensor.so index d1b1cd0abda25d6f7180b68a7f9c04a092883ca5..5d6e518858f33e23124f274788b4c26e00a07c7e 100755 GIT binary patch delta 27770 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

5`hLyr?3Iu+DdnyyTP{K{z@1dBm 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 delta 26417 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 Le5T~TA@%+bFuzzu diff --git a/norch/nn/__pycache__/activation.cpython-38.pyc b/norch/nn/__pycache__/activation.cpython-38.pyc index ac065b476d96e84f7f2267f3d9318b15a8069e5a..0a2693bd37ebd5120abcdc7c9f3ff0a7df7ece86 100644 GIT binary patch delta 393 zcmY*UJxc>Y5S`h*cr&wa-p>69qEQ%nKrwp1%x=Ug>f_bwY;Z6H}5lnh4D&={|$c5HdszJjCDT761_#iFusXcbG5imPEsV`#fF123he@Y{jpl z)JN1A+j+fIi^N3LB7aKz#2r)Sl|7vAH8SVfOg~Mu(%t_{)uHB1v^wnOkT>}l2ZL7S YFLBMab&5SxS|<_v3ot-O#C~l50R9z0?EnA( delta 68 zcmcb>vyqc8l$V!_0SFX)ywm10PUMqebeX8_%$C9u%%I7+v1JA`qvm8;7B41Ej>#Wc WwI(aDDe-UsmGdz2Fmf;na03ANAq?37 diff --git a/norch/nn/activation.py b/norch/nn/activation.py index 5f93936..a1361f6 100644 --- a/norch/nn/activation.py +++ b/norch/nn/activation.py @@ -18,4 +18,13 @@ class Sigmoid(Activation): super().__init__() def forward(self, x): - return F.sigmoid(x) \ No newline at end of file + return F.sigmoid(x) + +class Softmax(Activation): + def __init__(self, dim): + super(Softmax, self).__init__() + + self.dim = dim + + def forward(self, x): + return F.softmax(x, self.dim) \ No newline at end of file diff --git a/norch/nn/functional.py b/norch/nn/functional.py index b865379..9b5c3ec 100644 --- a/norch/nn/functional.py +++ b/norch/nn/functional.py @@ -3,3 +3,16 @@ import math def sigmoid(x): return 1.0 / (1.0 + (math.e) ** (-x)) +def softmax(x, dim=None): + e = math.e ** x + s = e.sum(axis=dim, keepdim=True) + #l = s.log() + print('x', x) + print('\n\n') + #print('log', l) + + print('\n\n') + #print('x-log', x - l) + + + #return math.e ** (x - sum.log()) \ No newline at end of file diff --git a/norch/tensor.py b/norch/tensor.py index 7e60d7f..52a5558 100644 --- a/norch/tensor.py +++ b/norch/tensor.py @@ -697,7 +697,7 @@ class Tensor: elif self.ndim == other.ndim - 1: self = self.reshape([1] + self.shape) - + # Call equal_broadcasted_tensor if broadcasting is needed Tensor._C.equal_broadcasted_tensor.argtypes = [ctypes.POINTER(CTensor), ctypes.POINTER(CTensor)] Tensor._C.equal_broadcasted_tensor.restype = ctypes.POINTER(CTensor) @@ -748,7 +748,9 @@ class Tensor: return result_data - def sum(self, axis=-1, keepdim=False): + def sum(self, axis=None, keepdim=False): + if axis == None: + axis = -1 Tensor._C.sum_tensor.argtypes = [ctypes.POINTER(CTensor), ctypes.c_int, ctypes.c_bool] Tensor._C.sum_tensor.restype = ctypes.POINTER(CTensor) @@ -783,10 +785,13 @@ class Tensor: return result_data - def max(self, axis=-1, keepdim=False): + def max(self, axis=None, keepdim=False): + if axis == None: + axis = -1 Tensor._C.max_tensor.argtypes = [ctypes.POINTER(CTensor), ctypes.c_int, ctypes.c_bool] Tensor._C.max_tensor.restype = ctypes.POINTER(CTensor) + print(axis, keepdim) result_tensor_ptr = Tensor._C.max_tensor(self.tensor, axis, keepdim) result_data = Tensor() @@ -818,7 +823,9 @@ class Tensor: return result_data - def min(self, axis=-1, keepdim=False): + def min(self, axis=None, keepdim=False): + if axis == None: + axis = -1 Tensor._C.min_tensor.argtypes = [ctypes.POINTER(CTensor), ctypes.c_int, ctypes.c_bool] Tensor._C.min_tensor.restype = ctypes.POINTER(CTensor) diff --git a/test.py b/test.py index 2306f6a..f6125ae 100644 --- a/test.py +++ b/test.py @@ -6,11 +6,16 @@ import norch.optim as optim import random random.seed(1) -torch_tensor = norch.Tensor([[[10, 10], [-4, -4]], [[5., 6], [7, 8]]], requires_grad=True)#.to(self.device) -torch_tensor2 = norch.Tensor([[[10.0,], [-4.0,]],[[6.0,], [8.0,]]]) -torch_tensor3 = torch_tensor.max(axis=2, keepdim=True) -print(torch_tensor3) -print(torch_tensor.equal(torch_tensor3)) +torch_tensor = norch.Tensor([[[2, 2], [-1, -1]], [[1., 2], [3, 3]]], requires_grad=True)#.to(self.device) +b = norch.nn.functional.softmax(torch_tensor) + + +"""a = norch.Tensor([[[4.186502456665039]]]) +b = norch.Tensor([[[2.0, 2.0,],[-1.0, -1.0,]],[[1.0, 2.0,],[3.0, 3.0,]]]) + +print(b) +print(a.shape) +print(b-a)""" """print(torch_tensor.shape) print('\n\n') diff --git a/tests/test_nn.py b/tests/test_nn.py index e790fa9..daaac21 100644 --- a/tests/test_nn.py +++ b/tests/test_nn.py @@ -60,11 +60,11 @@ class TestNNModuleActivationFn(unittest.TestCase): sigmoid_fn_torch = torch.nn.Sigmoid() # Test case 1: Positive input - x = norch.Tensor([1, 2, 3]).to(self.device) + x = norch.Tensor([[1, 2, 3]]).to(self.device) sigmoid_norch = sigmoid_fn_norch.forward(x) sigmoid_torch_result = utils.to_torch(sigmoid_norch).to(self.device) - x = torch.tensor([1, 2, 3]).to(self.device) + x = torch.tensor([[1, 2, 3]]).to(self.device) sigmoid_torch_expected = sigmoid_fn_torch.forward(x) self.assertTrue(utils.compare_torch(sigmoid_torch_result, sigmoid_torch_expected)) @@ -87,4 +87,41 @@ class TestNNModuleActivationFn(unittest.TestCase): x = torch.tensor([0, 0, 0]).to(self.device) 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 + self.assertTrue(utils.compare_torch(sigmoid_torch_result, sigmoid_torch_expected)) + + def test_softmax_activation(self): + """ + Test Softmax activation function + """ + + # Test different axes + axes = [None, 0, 1, -1] + + # Define the input tensors for different test cases + test_cases = [ + (norch.Tensor([[1., 2, 3], [4, 5, 6]]), torch.tensor([[1., 2, 3], [4, 5, 6]])), + (norch.Tensor([[1., -1, 0], [2, -2, 0]]), torch.tensor([[1., -1, 0], [2, -2, 0]])), + (norch.Tensor([[0., 0, 0], [0, 0, 0]]), torch.tensor([[0., 0, 0], [0, 0, 0]])) + ] + + for dim in axes: + softmax_fn_norch = norch.nn.Softmax(dim=dim) + softmax_fn_torch = torch.nn.Softmax(dim=dim) + + for norch_input, torch_input in test_cases: + # Move tensors to the correct device + norch_input = norch_input.to(self.device) + torch_input = torch_input.to(self.device) + + # Forward pass using norch + softmax_norch = softmax_fn_norch.forward(norch_input) + softmax_torch_result = utils.to_torch(softmax_norch).to(self.device) + + # Forward pass using torch + softmax_torch_expected = softmax_fn_torch.forward(torch_input) + + # Compare the results + print(softmax_torch_result) + print(softmax_torch_expected) + self.assertTrue(utils.compare_torch(softmax_torch_result, softmax_torch_expected)) + From fd831f950d39e5dce67744f823fc4c81ed4c36da Mon Sep 17 00:00:00 2001 From: lucasdelimanogueira Date: Tue, 21 May 2024 00:38:34 -0300 Subject: [PATCH 21/28] fix add min max keepdim --- build/tensor.o | Bin 50768 -> 50320 bytes norch/csrc/tensor.cpp | 83 ++++++++++++++++++--------------------- norch/libtensor.so | Bin 194800 -> 194792 bytes tests/test_operations.py | 1 + 4 files changed, 39 insertions(+), 45 deletions(-) diff --git a/build/tensor.o b/build/tensor.o index d12bc526f48941d9271926bd72e9a6df3e846ecd..85b704a056485eb2ee0d1b300fba4502c79a5ca6 100644 GIT binary patch literal 50320 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 bvb}-y%Z(qOO9gFzUx(cyVPDd}-R=Ju4Zs$I literal 50768 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 W{nwXRw*4>PYWGO!j`G*t|9=3Qdz{Vy diff --git a/norch/csrc/tensor.cpp b/norch/csrc/tensor.cpp index a340c73..63568cc 100644 --- a/norch/csrc/tensor.cpp +++ b/norch/csrc/tensor.cpp @@ -183,11 +183,8 @@ extern "C" { int ndim; int* shape; if (axis == -1) { + shape = (int*) malloc(sizeof(int)); - if (shape == NULL) { - fprintf(stderr, "Memory allocation failed\n"); - exit(1); - } shape[0] = 1; ndim = 1; } else { @@ -198,7 +195,6 @@ extern "C" { } } ndim = tensor->ndim - 1; - } int size = 1; @@ -220,25 +216,27 @@ extern "C" { fprintf(stderr, "Memory allocation failed\n"); exit(1); } + sum_tensor_cpu(tensor, result_data, size, shape, axis); if (keepdim) { - shape = (int*) malloc((tensor->ndim) * sizeof(int)); - for (int i = 0; i < tensor->ndim; i++) { - if (axis == -1) { + if (axis == -1){ + ndim = tensor->ndim; + shape = (int*) malloc((tensor->ndim) * sizeof(int)); + for (int i = 0; i < tensor->ndim; i++) { shape[i] = 1; - } else { + } + } else { + shape = (int*) malloc((tensor->ndim) * sizeof(int)); + for (int i = 0; i < tensor->ndim; i++) { shape[i] = tensor->shape[i]; } + shape[axis] = 1; + ndim = tensor->ndim; } - shape[axis] = 1; - ndim = tensor->ndim; - Tensor* new_tensor = create_tensor(result_data, shape, ndim, device); - return reshape_tensor(new_tensor, shape, ndim); + } - return create_tensor(result_data, shape, ndim, device); - } } @@ -252,12 +250,8 @@ extern "C" { } int ndim; int* shape; - if (axis == -1) { + if (axis == -1) { shape = (int*) malloc(sizeof(int)); - if (shape == NULL) { - fprintf(stderr, "Memory allocation failed\n"); - exit(1); - } shape[0] = 1; ndim = 1; } else { @@ -268,7 +262,6 @@ extern "C" { } } ndim = tensor->ndim - 1; - } int size = 1; @@ -293,18 +286,20 @@ extern "C" { max_tensor_cpu(tensor, result_data, size, shape, axis); if (keepdim) { - shape = (int*) malloc((tensor->ndim) * sizeof(int)); - for (int i = 0; i < tensor->ndim; i++) { - if (axis == -1) { + if (axis == -1){ + ndim = tensor->ndim; + shape = (int*) malloc((tensor->ndim) * sizeof(int)); + for (int i = 0; i < tensor->ndim; i++) { shape[i] = 1; - } else { + } + } else { + shape = (int*) malloc((tensor->ndim) * sizeof(int)); + for (int i = 0; i < tensor->ndim; i++) { shape[i] = tensor->shape[i]; } - } - shape[axis] = 1; - ndim = tensor->ndim; - Tensor* new_tensor = create_tensor(result_data, shape, ndim, device); - return reshape_tensor(new_tensor, shape, ndim); + shape[axis] = 1; + ndim = tensor->ndim; + } } return create_tensor(result_data, shape, ndim, device); @@ -322,11 +317,8 @@ extern "C" { int ndim; int* shape; if (axis == -1) { + shape = (int*) malloc(sizeof(int)); - if (shape == NULL) { - fprintf(stderr, "Memory allocation failed\n"); - exit(1); - } shape[0] = 1; ndim = 1; } else { @@ -337,9 +329,8 @@ extern "C" { } } ndim = tensor->ndim - 1; - } - + int size = 1; for (int i = 0; i < ndim; i++) { size *= shape[i]; @@ -362,18 +353,20 @@ extern "C" { min_tensor_cpu(tensor, result_data, size, shape, axis); if (keepdim) { - shape = (int*) malloc((tensor->ndim) * sizeof(int)); - for (int i = 0; i < tensor->ndim; i++) { - if (axis == -1) { + if (axis == -1){ + ndim = tensor->ndim; + shape = (int*) malloc((tensor->ndim) * sizeof(int)); + for (int i = 0; i < tensor->ndim; i++) { shape[i] = 1; - } else { + } + } else { + shape = (int*) malloc((tensor->ndim) * sizeof(int)); + for (int i = 0; i < tensor->ndim; i++) { shape[i] = tensor->shape[i]; } - } - shape[axis] = 1; - ndim = tensor->ndim; - Tensor* new_tensor = create_tensor(result_data, shape, ndim, device); - return reshape_tensor(new_tensor, shape, ndim); + shape[axis] = 1; + ndim = tensor->ndim; + } } return create_tensor(result_data, shape, ndim, device); diff --git a/norch/libtensor.so b/norch/libtensor.so index 5d6e518858f33e23124f274788b4c26e00a07c7e..de7ec33a5d384ea53ebe16737bffeeb968ac13c6 100755 GIT binary patch delta 26250 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 I8@2ZT1D!$0tpET3 delta 26844 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 C{QM69 diff --git a/tests/test_operations.py b/tests/test_operations.py index 8a684e9..dffe0a1 100644 --- a/tests/test_operations.py +++ b/tests/test_operations.py @@ -272,6 +272,7 @@ class TestTensorOperations(unittest.TestCase): torch_tensor = torch.tensor([[[1, 2], [3, -4]], [[5, 6], [7, 8]]]).to(self.device) torch_expected = torch.sum(torch_tensor, dim=1, keepdim=True) + print(torch_result, torch_expected) self.assertTrue(utils.compare_torch(torch_result, torch_expected)) def test_max(self): From a39bd59a3f36a1fd048a2fd4e91a31a2da52cc83 Mon Sep 17 00:00:00 2001 From: lucasdelimanogueira Date: Tue, 21 May 2024 01:22:38 -0300 Subject: [PATCH 22/28] softmax operation --- build/tensor.o | Bin 50320 -> 50480 bytes norch/__pycache__/tensor.cpython-38.pyc | Bin 18512 -> 18654 bytes norch/csrc/tensor.cpp | 4 +-- norch/libtensor.so | Bin 194792 -> 194792 bytes norch/nn/functional.py | 22 ++++++++-------- norch/tensor.py | 16 +++++++++++- test.py | 3 ++- tests/test_nn.py | 10 +++---- tests/test_operations.py | 33 ++++++++++++++++++++++++ 9 files changed, 67 insertions(+), 21 deletions(-) diff --git a/build/tensor.o b/build/tensor.o index 85b704a056485eb2ee0d1b300fba4502c79a5ca6..10402c807d458bfecf83492a19979ee8a777d22e 100644 GIT binary patch delta 238 zcmbQx$-JS9d4dMxhK-ubLnbSP*^B0+=BDPA=$2;|rz)gmmSq-a=I3!uJ`-%u1LrX? zFl;^_Vrb2j!nB!b!Z${yJxrT_O`OKa$-)ej0)pVlf>XpdZA_YTnW^$;sztr9G1;ptFG|_RV40mXJ=9< zn7r!XF(z5o$wi0Em|DXpuR3JMv@v|LVYJw!!y1e`CTkwHXS^{v^RPWrz`n_=4%;za anEVpR;YgdTb;OR-;vmF=lF1(r$^!r{{Z;k= delta 196 zcmdnc#XO;td4dL`z(&pGA)58C6gx}k_Q0z14l*x diff --git a/norch/__pycache__/tensor.cpython-38.pyc b/norch/__pycache__/tensor.cpython-38.pyc index 1192401fdc0823e0bd6545facd8c0af2aa1078eb..141689fc6244ffff0b732112c60c98f8e693cfe5 100644 GIT binary patch delta 1547 zcmeHHPe>F|7=PcJSsmAPU3XV^w@4DjSOr@X)ZI-i{}ZMM3!;=^aW-=nSA9-p^fn3Y zuUhJ(Zo%5AE@JGd&JkTA51oWk=+ZHQphHIATg^jYr%urf@Atm{-+SMWnY9J@un5l6 z1qF73&sJ&o`A4(Qoc<785QjqPhF2Eh#LAtf3&0GN;g`(8EHWTL**i|s#i%*V64PhU z09l4UA~r+5fM2&;N?NsS%a~fVf&a6IjpJb&Fk$8^f1$eO((IO$_o59Fam@{z{1p-d7 zc{rdssUPg%rJ4RC3}7nrwr-2U6Y-}0lkFBpJ;=OhxCXEx!r`u|ZtTUuAc;J-4|hlq z+=+=cA4x&hLs_LkQ(!@wS_<9_Ocj@HNU?HEyq%u6>)F=r(w^3^N?H8wds!S0IQXr*zq!;*SF=BmKA`9nSnMFU{$>Vd}nJxwN|2? T&^-1azE$FVrw@eq+Ij9LOY46T delta 1355 zcmd6nOK1~O6o&7)Gik?UCa+0sQ$(oM+Mw37C|28QYfG()qWD<&U@VM$g*5< zcErw}v#i4$$LXMG;no4#=Vkjpy_D`d--mw{_2D^(V@TmpC} z&u<;fWPssOHXGwYx1j(oD}&2%Oy(%K)KSF^I5keKr7c$ETwnSi*ns+JkBbW)b3&eK zg={_wv`;X6((@nvXWZGGs<{)WeU5eA#?!9y6r*|_B6D;+oy(pbO~7SicWN|*xiI#G z{mzJ|I(k1l87~ApRb@`Z3v6YaS&{q#>Y}K_wu70kyy^$>fk?iYkc zAS0HBYVFv_$b_98Pn+_9nUK3WTS1fg&dpj?9lDU66gTA4&gRHxBHE4}3^Y(dj`ysF zsMNbQOm+PwE#$aJZF>`#&`<8sHd3K)sF%G{K6{S-XAO z2Ktd$@*}$B2Re}FD2<^MmlH!rI^6g#j+o?#m?kF@h(8H8<&*BFa5XR?vN>0@U_p=@ zE9HE53~rUS*Ci7QIndim != tensor2->ndim) { - fprintf(stderr, "Tensors must have the same number of dimensions %d and %d for element-wise multiplication\n", tensor1->ndim, tensor2->ndim); + fprintf(stderr, "Tensors must have the same number of dimensions %d and %d for element-wise division\n", tensor1->ndim, tensor2->ndim); exit(1); } @@ -664,7 +664,7 @@ extern "C" { for (int i = 0; i < ndim; i++) { if (tensor1->shape[i] != tensor2->shape[i]) { - fprintf(stderr, "Tensors must have the same shape %d and %d at index %d for subtraction\n", tensor1->shape[i], tensor2->shape[i], i); + fprintf(stderr, "Tensors must have the same shape %d and %d at index %d for division\n", tensor1->shape[i], tensor2->shape[i], i); exit(1); } shape[i] = tensor1->shape[i]; diff --git a/norch/libtensor.so b/norch/libtensor.so index de7ec33a5d384ea53ebe16737bffeeb968ac13c6..81254adae29b489efe3a4ff78e3b660f92ca5fc6 100755 GIT binary patch delta 4254 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 rhvq;2k5q5v0NwuqO`AhD delta 4203 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 Ny&X(ncb^9v{trN;GAsZ9 diff --git a/norch/nn/functional.py b/norch/nn/functional.py index 9b5c3ec..75654cf 100644 --- a/norch/nn/functional.py +++ b/norch/nn/functional.py @@ -4,15 +4,15 @@ def sigmoid(x): return 1.0 / (1.0 + (math.e) ** (-x)) def softmax(x, dim=None): - e = math.e ** x - s = e.sum(axis=dim, keepdim=True) - #l = s.log() - print('x', x) - print('\n\n') - #print('log', l) + if dim is not None and dim < 0: + dim = x.ndim + dim + + x_max = x.max(axis=dim, keepdim=True) + exp_x = math.e ** (x - x_max) - print('\n\n') - #print('x-log', x - l) - - - #return math.e ** (x - sum.log()) \ No newline at end of file + if dim is not None: + sum_exp_x = exp_x.sum(axis=dim, keepdim=True) + exp_x.zeros_like() + return exp_x / sum_exp_x + else: + sum_exp_x = exp_x.sum() + return exp_x / sum_exp_x \ No newline at end of file diff --git a/norch/tensor.py b/norch/tensor.py index 52a5558..49df15d 100644 --- a/norch/tensor.py +++ b/norch/tensor.py @@ -613,6 +613,9 @@ class Tensor: result_data.grad_fn = DivisionBackward(self, other) elif isinstance(self, Tensor) and isinstance(other, Tensor): + if other.numel == 1: + return self.__truediv__(other.tensor.contents.data[0]) + Tensor._C.tensor_div_tensor.argtypes = [ctypes.POINTER(CTensor), ctypes.POINTER(CTensor)] Tensor._C.tensor_div_tensor.restype = ctypes.POINTER(CTensor) @@ -749,8 +752,12 @@ class Tensor: return result_data def sum(self, axis=None, keepdim=False): + if axis is not None and axis < 0: + axis = self.ndim + axis + if axis == None: axis = -1 + Tensor._C.sum_tensor.argtypes = [ctypes.POINTER(CTensor), ctypes.c_int, ctypes.c_bool] Tensor._C.sum_tensor.restype = ctypes.POINTER(CTensor) @@ -786,12 +793,15 @@ class Tensor: return result_data def max(self, axis=None, keepdim=False): + if axis is not None and axis < 0: + axis = self.ndim + axis + if axis == None: axis = -1 + Tensor._C.max_tensor.argtypes = [ctypes.POINTER(CTensor), ctypes.c_int, ctypes.c_bool] Tensor._C.max_tensor.restype = ctypes.POINTER(CTensor) - print(axis, keepdim) result_tensor_ptr = Tensor._C.max_tensor(self.tensor, axis, keepdim) result_data = Tensor() @@ -824,8 +834,12 @@ class Tensor: return result_data def min(self, axis=None, keepdim=False): + if axis is not None and axis < 0: + axis = self.ndim + axis + if axis == None: axis = -1 + Tensor._C.min_tensor.argtypes = [ctypes.POINTER(CTensor), ctypes.c_int, ctypes.c_bool] Tensor._C.min_tensor.restype = ctypes.POINTER(CTensor) diff --git a/test.py b/test.py index f6125ae..6bd8b2a 100644 --- a/test.py +++ b/test.py @@ -7,8 +7,9 @@ import random random.seed(1) torch_tensor = norch.Tensor([[[2, 2], [-1, -1]], [[1., 2], [3, 3]]], requires_grad=True)#.to(self.device) +torch_tensor = norch.Tensor([[[2, 2], [2, 2]], [[2, 2.], [2, 2]]]) b = norch.nn.functional.softmax(torch_tensor) - +print(b) """a = norch.Tensor([[[4.186502456665039]]]) b = norch.Tensor([[[2.0, 2.0,],[-1.0, -1.0,]],[[1.0, 2.0,],[3.0, 3.0,]]]) diff --git a/tests/test_nn.py b/tests/test_nn.py index daaac21..1010c82 100644 --- a/tests/test_nn.py +++ b/tests/test_nn.py @@ -95,13 +95,13 @@ class TestNNModuleActivationFn(unittest.TestCase): """ # Test different axes - axes = [None, 0, 1, -1] + axes = [0, 1, 2, -1] # Define the input tensors for different test cases test_cases = [ - (norch.Tensor([[1., 2, 3], [4, 5, 6]]), torch.tensor([[1., 2, 3], [4, 5, 6]])), - (norch.Tensor([[1., -1, 0], [2, -2, 0]]), torch.tensor([[1., -1, 0], [2, -2, 0]])), - (norch.Tensor([[0., 0, 0], [0, 0, 0]]), torch.tensor([[0., 0, 0], [0, 0, 0]])) + (norch.Tensor([[[1., 2, 3], [4, 5, 6]]]), torch.tensor([[[1., 2, 3], [4, 5, 6]]])), + (norch.Tensor([[[1., -1, 0], [2, -2, 0]]]), torch.tensor([[[1., -1, 0], [2, -2, 0]]])), + (norch.Tensor([[[0., 0, 0], [0, 0, 0]]]), torch.tensor([[[0., 0, 0], [0, 0, 0]]])) ] for dim in axes: @@ -121,7 +121,5 @@ class TestNNModuleActivationFn(unittest.TestCase): softmax_torch_expected = softmax_fn_torch.forward(torch_input) # Compare the results - print(softmax_torch_result) - print(softmax_torch_expected) self.assertTrue(utils.compare_torch(softmax_torch_result, softmax_torch_expected)) diff --git a/tests/test_operations.py b/tests/test_operations.py index dffe0a1..961cb17 100644 --- a/tests/test_operations.py +++ b/tests/test_operations.py @@ -260,6 +260,17 @@ class TestTensorOperations(unittest.TestCase): self.assertTrue(utils.compare_torch(torch_result, torch_expected)) + # negative axis + + norch_tensor = norch.Tensor([[[1, 2], [3, -4]], [[5, 6], [7, 8]]]).to(self.device) + norch_result = norch_tensor.sum(axis=-2) + torch_result = utils.to_torch(norch_result).to(self.device) + + torch_tensor = torch.tensor([[[1, 2], [3, -4]], [[5, 6], [7, 8]]]).to(self.device) + torch_expected = torch.sum(torch_tensor, dim=-2) + + self.assertTrue(utils.compare_torch(torch_result, torch_expected)) + def test_sum_axis_keepdim(self): """ @@ -301,6 +312,17 @@ class TestTensorOperations(unittest.TestCase): self.assertTrue(utils.compare_torch(torch_result, torch_expected)) + # negative axis + + norch_tensor = norch.Tensor([[[1, 2], [3, -4]], [[5, 6], [7, 8]]]).to(self.device) + norch_result = norch_tensor.max(axis=-1) + torch_result = utils.to_torch(norch_result).to(self.device) + + torch_tensor = torch.tensor([[[1, 2], [3, -4]], [[5, 6], [7, 8]]]).to(self.device) + torch_expected, _ = torch.max(torch_tensor, dim=-1) + + self.assertTrue(utils.compare_torch(torch_result, torch_expected)) + def test_max_axis_keepdim(self): """ @@ -341,6 +363,17 @@ class TestTensorOperations(unittest.TestCase): self.assertTrue(utils.compare_torch(torch_result, torch_expected)) + # negative axis + + norch_tensor = norch.Tensor([[[1, 2], [3, -4]], [[5, 6], [7, 8]]]).to(self.device) + norch_result = norch_tensor.min(axis=-1) + torch_result = utils.to_torch(norch_result).to(self.device) + + torch_tensor = torch.tensor([[[1, 2], [3, -4]], [[5, 6], [7, 8]]]).to(self.device) + torch_expected, _ = torch.min(torch_tensor, dim=-1) + + self.assertTrue(utils.compare_torch(torch_result, torch_expected)) + def test_min_axis_keepdim(self): """ From a57298dfcb5bb3f54f7a76558eab40b20e3828c7 Mon Sep 17 00:00:00 2001 From: lucasdelimanogueira Date: Tue, 21 May 2024 02:03:24 -0300 Subject: [PATCH 23/28] softmax and small fixes --- .../__pycache__/functions.cpython-38.pyc | Bin 10085 -> 9661 bytes norch/autograd/functions.py | 34 ++++++----- test.py | 10 ++-- tests/test_autograd.py | 56 ++++++++++++++++++ 4 files changed, 80 insertions(+), 20 deletions(-) diff --git a/norch/autograd/__pycache__/functions.cpython-38.pyc b/norch/autograd/__pycache__/functions.cpython-38.pyc index 0b21229cce857c4b4c4e93794269a0aa31a74bcc..66ce27e5edd74698e2f12ebd7684abb0dc013b74 100644 GIT binary patch delta 1463 zcmds1OK1~O6rDGpWSTb9>ZF|{&1d?dHvW);i&ES4Y-ZxERs})?ha^M~2-Fx1BGw+=HKK8xn zcE{Xq7l+TY`ku3=vTo082ToFt5iHK+4C3QVlHs#ln#Z|d@Dn^eFfCfdluU=|WcfLg zXJ+Iq=i$;ql47KYnig-cD#B&n#~DI`Xq>fPMbJ2dV^Iz3CiRWHfvqUhE{mTdY1}qb zY>8Qt7A&E#X-Srq!AOD(auKnQ68w0&n?WT)#0^GyhbT^tr(n4}2CK5-y@K(S2Z1AH z#Kl(&4pPtSf((rnpUG`l)B+II+KUe&fl3TC2s;C00W(px69cz!q(lUn$O7$z>)TqP z$$QSx&B#4aC2J%LZ@dXd4-0hLtM9^quTAa3Kn6#CwM4t@)t`JP$vtTCC&&ZD3Rv>j zsJ+Nr+`tuRugz_!JVPD{U{~O<+K-%vn|S@mYlGK;A+iLa;8pSx?gyKtW5{_9 zU$j1G3JsH2Fds6>0Nl_tvI^dCvpR_Rl}&X9v5p6<@H}}3+x5l<+Z=v8;QZMPY7=7FUe+ z6%FYcpLSZXrz$wR==jwu$7uk6KT?(x9p7mS13$u=FcrVJCXJn+iBHn~EE;9d%Ak!w zI|H3TkO4a?YCGr_*4!yj;( z(h+Qv5`~oUIv)^oR^?%(xqCcG$#r?`>tKX6P3Sv2cpYJrK!v-+sEewjnEZ<4r#_?bw63|9-651+ o1(fwTWerSOcT(1Zl&MNb;k~B8-W~O&KEk delta 1878 zcmeH{OKclu5XX1+<+WRH+BnX}uAL@ng6gUYy>O_M2W~^5gt#4n4pHi34z8W)nNH2ogdZS!@29 z`M&?oKK$GHeDudm_)It)lIXa8Yjp10YPhYeP~onTT$UUu2QkTkid>aSGSx~>UX;Z^ zQT4#l9Nh`<{w1h8D@IjnlS;~RhE{^^)XUNw8{4@+BA{Z%Hoh;=H7KYGt?I zQ>)4#c~A*^Y8A>Q#Zwv$&8lQkLrIdJmN)e%OJ4xmxPkO9< zMlR9yJpcX{6`^|a>+(}-RucTgB1W>V)a3pn;sB*JaMwWu&heJNzl#?`14|2TadxHf z=wv7Oqja{H_cz zG;tc&_TGZgNkP zyA6Nnd=tLMXZIf+_YwV-EOk!)f@rb6Q&9=sEuJ(_sS6I4pDY-hedpw<5YM$1(LL{g`s#R#*+G075zjwt)MmT+dvb9Vz zZRg>(6C?bm#Yt*PVPRQ3&gFe9{~jA3h7E40(R7gdZoneWbK?d^;sTfcN6^gAFT{^k zAFrSF4o7S<{zS0_kBw&EU@s3=!P8RkC=@*B1P?L6vq>;=!p5(n7QWPXu%*Kcm_gGp JLllO2;7 Date: Tue, 21 May 2024 12:16:12 -0300 Subject: [PATCH 24/28] cross entropy loss --- norch/nn/__pycache__/loss.cpython-38.pyc | Bin 1420 -> 2620 bytes norch/nn/functional.py | 17 ++++- norch/nn/loss.py | 70 +++++++++++++++++++-- test.py | 19 ++++-- tests/test_nn.py | 77 +++++++++++++++++++++-- 5 files changed, 168 insertions(+), 15 deletions(-) diff --git a/norch/nn/__pycache__/loss.cpython-38.pyc b/norch/nn/__pycache__/loss.cpython-38.pyc index a39202c7182c730dfc171bf4b535752507f08ad9..df6fd5a931db4a8f313c204e140bf1b4c3b58460 100644 GIT binary patch literal 2620 zcmcIlPj4GV6rX>)-Z)Mg8Yn4+cFP|XOQ`OJ9;&LMv{hB9jSwjud|7RFCW)K%u9+DN z;;b)8q#`6xKZ1P>XD)mT&U@w53mCN2F(x$LLvIn2l^%=zul?<6>xefVTz zuE9W&HkAZW&^CWcHYlPx2--pl=^c*PLM&6GQzuj!xMx_E#a$IeWj%_z zj#w~mMA0|>IGt!}Q6%#uikwTGY5f+QSuRYjD^X}>=ARp&)%fEY0|T4$YCr{R@PIpM z0@7p8^g_&sAf7VTu@(3VY~_+m0W1}cr5>={u62}-O)gd zdzsaFZ|8sD$R&h5NiFA3+B%&q$hj9}`5tI$fdH01Vf)Nnv>eDHtXI%|I)=7KLCYrE zo1kS*3nZ$vKpkK5ZKi(%f6@OdmTO)Uvs$lNnB2f zX#L6Q{B(qc4h3Uv{EmbPAtGGK39q>9dL``kjSbgT z_*i8o*PpmJ+#L?CziL|*Q?J^mF-d7mc~D)r>~sg$9$*%C0bOlQgOGr3mvAB z2BeXyuTy**Y>}R2LbwFu#y)Sb2CHL(_2)phCBA46C2csJnvxLN;S(7nT}- E19Fl?YybcN delta 297 zcmX|5Jxjw-6n*!7Bx#zYSQP}nKoEQ&E`mCU4k87yE=7 class probabilities (one-hot encoded) + assert target.shape == input.shape, \ + "Input and target shape does not match: {} and {}".format(input.shape, target.shape) + logits = norch.softmax(input, dim=0) + cost = -(logits.log() * target).sum() + + return cost + + + elif input.ndim == 2: + # batched + if target.ndim == 1: + # target -> Ground truth class indices: + num_classes = input.shape[1] + + target = norch.one_hot_encode(target, num_classes) + + batch_size = input.shape[0] + logits = norch.softmax(input, dim=1) + cost = -(logits.log() * target).sum() / batch_size + + return cost + + else: + # target -> class probabilities (one-hot encoded) + assert target.shape == input.shape, \ + "Input and target shape does not match: {} and {}".format(input.shape, target.shape) + + batch_size = input.shape[0] + logits = norch.softmax(input, dim=1) + cost = -(logits.log() * target).sum() / batch_size + + return cost + + + diff --git a/test.py b/test.py index d61d329..54b67fb 100644 --- a/test.py +++ b/test.py @@ -6,12 +6,19 @@ import norch.optim as optim import random random.seed(1) -torch_tensor = norch.Tensor([[[1, 5], [2, -1]], [[5, 2.], [2, 2]]], requires_grad=True)#.to(self.device) -b = norch.nn.functional.softmax(torch_tensor, dim=0) -soma = b.sum() -print(soma) -soma.backward() -print(torch_tensor.grad) +"""one_hot_target = norch.one_hot_encode(norch.Tensor([5]), num_classes=10) +print(one_hot_target)""" + +logits = norch.Tensor([[2.0, 1.0, 0.1, 0.1], [2.0, 1.0, 0.1, 0.1], [2.0, 1.0, 0.1, 0.1]], requires_grad=True) + +# One-hot encoded target with shape (batch_size, num_classes) +one_hot_target = norch.Tensor([0, 1, 1]) + +criterion = nn.CrossEntropyLoss() + +loss = criterion(logits, one_hot_target) +print(loss) + """a = norch.Tensor([[[4.186502456665039]]]) b = norch.Tensor([[[2.0, 2.0,],[-1.0, -1.0,]],[[1.0, 2.0,],[3.0, 3.0,]]]) diff --git a/tests/test_nn.py b/tests/test_nn.py index 1010c82..cd623f3 100644 --- a/tests/test_nn.py +++ b/tests/test_nn.py @@ -21,13 +21,13 @@ class TestNNModuleLoss(unittest.TestCase): loss_fn_torch = torch.nn.MSELoss() # Test case 1: Predictions and labels are equal - predictions_norch = norch.Tensor([1.1, 2, 3, 4]).to(self.device) - labels_norch = norch.Tensor([1.1, 2, 3, 4]).to(self.device) + predictions_norch = norch.Tensor([[1.1, 2, 3, 4], [1.1, 2, 3, 4]]).to(self.device) + labels_norch = norch.Tensor([[1.1, 2, 3, 4], [1.1, 2, 3, 3]]).to(self.device) loss_norch = loss_fn_norch.forward(predictions_norch, labels_norch) loss_torch_result = utils.to_torch(loss_norch).to(self.device) - predictions_torch = torch.tensor([1.1, 2, 3, 4]).to(self.device) - labels_torch = torch.tensor([1.1, 2, 3, 4]).to(self.device) + predictions_torch = torch.tensor([[1.1, 2, 3, 4], [1.1, 2, 3, 4]]).to(self.device) + labels_torch = torch.tensor([[1.1, 2, 3, 4], [1.1, 2, 3, 3]]).to(self.device) loss_torch_expected = loss_fn_torch(predictions_torch, labels_torch) self.assertTrue(utils.compare_torch(loss_torch_result, loss_torch_expected)) @@ -44,6 +44,75 @@ class TestNNModuleLoss(unittest.TestCase): self.assertTrue(utils.compare_torch(loss_torch_result, loss_torch_expected)) + def test_cross_entropy_loss(self): + """ + Test the CrossEntropyLoss + """ + loss_fn_norch = norch.nn.CrossEntropyLoss() + loss_fn_torch = torch.nn.CrossEntropyLoss() + + # Test case 1: Single class, single sample + predictions_norch = norch.Tensor([2.0, 1.0, 0.1]).to(self.device) + labels_norch = norch.Tensor([0]).to(self.device) + loss_norch = loss_fn_norch.forward(predictions_norch, labels_norch) + + loss_torch_result = utils.to_torch(loss_norch).to(self.device) + + predictions_torch = torch.tensor([2.0, 1.0, 0.1]).to(self.device) + labels_torch = torch.tensor(0).to(self.device) + loss_torch_expected = loss_fn_torch(predictions_torch, labels_torch) + + self.assertTrue(utils.compare_torch(loss_torch_result, loss_torch_expected)) + + # Test case 2: Multiple classes, multiple samples + predictions_norch = norch.Tensor([[0.5, 1.5, 2.5], [1.0, 2.0, 3.0]]).to(self.device) + labels_norch = norch.Tensor([2, 1]).to(self.device) + loss_norch = loss_fn_norch.forward(predictions_norch, labels_norch) + loss_torch_result = utils.to_torch(loss_norch).to(self.device) + + + predictions_torch = torch.tensor([[0.5, 1.5, 2.5], [1.0, 2.0, 3.0]]).to(self.device) + labels_torch = torch.tensor([2, 1]).to(self.device) + loss_torch_expected = loss_fn_torch(predictions_torch, labels_torch) + + self.assertTrue(utils.compare_torch(loss_torch_result, loss_torch_expected)) + + # Test case 3: Edge case - all predictions are zero + predictions_norch = norch.Tensor([[0.0, 0.0, 0.0], [0.0, 0.0, 0.0]]).to(self.device) + labels_norch = norch.Tensor([1, 2]).to(self.device) + loss_norch = loss_fn_norch.forward(predictions_norch, labels_norch) + loss_torch_result = utils.to_torch(loss_norch).to(self.device) + + predictions_torch = torch.tensor([[0.0, 0.0, 0.0], [0.0, 0.0, 0.0]]).to(self.device) + labels_torch = torch.tensor([1, 2]).to(self.device) + loss_torch_expected = loss_fn_torch(predictions_torch, labels_torch) + + self.assertTrue(utils.compare_torch(loss_torch_result, loss_torch_expected)) + + # Test case 4: Class probabilities instead of class index + predictions_norch = norch.Tensor([0.5, 0.2, 0.1]).to(self.device) + labels_norch = norch.Tensor([1., 0, 0]).to(self.device) + loss_norch = loss_fn_norch.forward(predictions_norch, labels_norch) + loss_torch_result = utils.to_torch(loss_norch).to(self.device) + + predictions_torch = torch.tensor([0.5, 0.2, 0.1]).to(self.device) + labels_torch = torch.tensor([1., 0, 0]).to(self.device) + loss_torch_expected = loss_fn_torch(predictions_torch, labels_torch) + + self.assertTrue(utils.compare_torch(loss_torch_result, loss_torch_expected)) + + # Test case 4: Batched class probabilities instead of class index + predictions_norch = norch.Tensor([[0.5, 0.2, 0.1], [0.1, 0.5, 0.7]]).to(self.device) + labels_norch = norch.Tensor([[1., 0, 0], [0, 1, 0]]).to(self.device) + loss_norch = loss_fn_norch.forward(predictions_norch, labels_norch) + loss_torch_result = utils.to_torch(loss_norch).to(self.device) + + predictions_torch = torch.tensor([[0.5, 0.2, 0.1], [0.1, 0.5, 0.7]]).to(self.device) + labels_torch = torch.tensor([[1., 0, 0], [0, 1, 0]]).to(self.device) + loss_torch_expected = loss_fn_torch(predictions_torch, labels_torch) + + self.assertTrue(utils.compare_torch(loss_torch_result, loss_torch_expected)) + class TestNNModuleActivationFn(unittest.TestCase): From 9cfb1f622db12c1d7a511817f18b7c9783cefcf1 Mon Sep 17 00:00:00 2001 From: lucasdelimanogueira Date: Tue, 21 May 2024 13:52:05 -0300 Subject: [PATCH 25/28] softmax autograd --- .../__pycache__/functions.cpython-38.pyc | Bin 9661 -> 10296 bytes norch/autograd/functions.py | 22 +++ norch/nn/__pycache__/loss.cpython-38.pyc | Bin 2620 -> 2717 bytes norch/nn/loss.py | 12 +- tests/test_autograd.py | 155 +++++++++++++++--- 5 files changed, 158 insertions(+), 31 deletions(-) diff --git a/norch/autograd/__pycache__/functions.cpython-38.pyc b/norch/autograd/__pycache__/functions.cpython-38.pyc index 66ce27e5edd74698e2f12ebd7684abb0dc013b74..71b56620fce7e8792a0fc1d67c2e5de36dc993a3 100644 GIT binary patch delta 1140 zcmZwGZ)h8390&0GJ$IK}^ZzbM^RH=5nrzElCn5|KoGYapuBGc(eG~0!+o#RiZH-(4 z>uN%<^AE!b!`}<>4-VZ*an-(9hEr_Of`XuxDMKZwAij{{3-yhH2=#l`7lH=zeeUx- zzq@<*-QD`YJHzHfe!oX?exDCcf1Op7AJ_~h8>A>wk#tHaZjV%?Ly8nqeNvI?IZ+gl zCUr=MRLCXWkQ>qKl7ZSK0fH}r~Q1vdz}t(ukQ^y%s=^t=pf(cf0KrrKl)=t$9O9+NJn@u_#RC+ zjZhEK4DT}sXo?rjRhs9!t!L?RUblMb3~yPtXt8-Ke4XeSJ|7*SC-}SQ3cbvsSO-67 zMfqkzfXHTjR^ z3-mTWpE^v}_}kPnt@BeI7w7}F(!I3CBk5)Oi2qD4(#PEB?4=KRqw@xBHqUlN1bxAm za#6mV8K6)3=gbZIwYi-Ao#;1AKN|c6&8lb!gFL$ZFkdtenDIWlT&eWWRqgU)X9sb} z-@EtoH$fxhI#n*sR4Xlab=)pZRV#ck5lM&fj#mUOq0mC+Kc^nBIvZ}RIrr$pzcPWS zDYMEWqAu!c4bNB7Nn!7)ido3{G@cW6t)|t~xKh{W_WoU=p`wT*O^Sq4cl}dK|E{gM zQmC)#QYi_%RQiye!kuhvZbB(gOO-RHZ#%mz=(g@G>_xQnxw1WZqUElXr>m#O=k1tN z;I^F&+W{wzJ2B?OB%VR2l&2wI5c+hCzR)^xEq6oy3^*R!-olc%xS_B@d%JFe$&HqnMMMu}gR%xp*G4 z(jOniYw;&e;f-8Lsra$_<^Mf9 c_2OQtE>PSk=d=5zGaHbVtYJy>QvA8}KfpW5`Tzg` diff --git a/norch/autograd/functions.py b/norch/autograd/functions.py index f9ad107..c9a393e 100644 --- a/norch/autograd/functions.py +++ b/norch/autograd/functions.py @@ -1,4 +1,5 @@ import math +import norch class AddBackward: def __init__(self, x, y): @@ -270,3 +271,24 @@ class MinBackward: return [grad_output] +class CrossEntropyLossBackward: + def __init__(self, logits, targets): + self.input = [logits, targets] + + def backward(self, gradient): + logits, targets = self.input + + if logits.ndim == 1: + softmax = norch.softmax(logits, dim=0) + grad_logits = (softmax - targets) + + elif logits.ndim == 2: + # batched + batch_size = logits.shape[0] + softmax = norch.softmax(logits, dim=1) + + grad_logits = (softmax - targets) / batch_size + + return [grad_logits, None] # targets do not have a gradient + + diff --git a/norch/nn/__pycache__/loss.cpython-38.pyc b/norch/nn/__pycache__/loss.cpython-38.pyc index df6fd5a931db4a8f313c204e140bf1b4c3b58460..b8133b99ff9fe85bc2549c254e8f67c3b96ca772 100644 GIT binary patch delta 1113 zcma)5J#5oJ6!x9%v(q?fRMIwWQz$J|7@-cRVnU^~RJ2u#K+q)vmTNn08pjT2N6;#$ zLm3zticVrGdnY6$kPt#*XJBITz{J9W#LNKiIfz8+LZW+j@Bj0??>-b?4(2y_*#JGsHo524oVXSSQNnzC)AJ+V51x)FV2oA``9qt#hFGQ7XEiXHgcot6 zJT1a%L9-Q~$kQ#$v3=jN`o%}OvV#57G(Ln?bKv#uC~`au>FJrnIBd)r2wag4LMI9} zWL@S=+>swoAq{S#$fC?8oDhTT8aWgjY&rX>O?Na` zu2cS09J4F=U>*#5o9yUy8Xb!tY<4tw1uF2PP7eux1a5}$aK0eswHt-tb~odYM62S3 zwm3+3auDz=&bLXM9?-UG^|w{=ReQ(A0kK&SM|!z@29A6JM54sIO)pUbx7Og3Xgw{y z=#%?r(U;@igI0}uk+sEbHz_UhFp8Ffn1{{XH8?KVP93?DXy~`9fvI&kqNW_-Q&=P$ zv>Kh^dq}!)s|Msbhn~JoJ9f6MsJ7?zTy`FN&dG&^Q0&n)S!L1 zNS>6G0@`If4^bEg-8+GD<}ai9d8pUZs5{eXaNvz}XNndxldwkmx_C%Iuatt2JKHmM zD-Q8{XR58B^Om>??nJgX9j?MDr7}mNaIDkQ@*igt{c5N59KPcSd20|I{stY$6^uWB Ck?(B) delta 1036 zcma)5y=xRf6yKTIot?YeTjS-!Xf8RusCZzZ60IZ}laK^7ph1NzJoauj=90Y+W;aMe zBt=q)Rl-yv{sVS`7J`kH2o}~`3zEh@jq$xb$eq}@%l!6z{oZ>!Z|2S5rvdk2Hk&r! zDSW)U{42fh=C>b#CJjjfqe04q7?MgB&@zRu8L);~Hidtfx6~oXgtbhOOp_Kwz z=r80p;abqqYZ=hvOk4S~UW@9jW*GCptNKBxT<~<@1jg9QE%nS(GNT{UsR<6I5>fs5 zp(;tj>&^Ch^bb!|Dm5<%Div3MqYDdIKcT5X%$fyn;Duq$$B>?y4#r_&WdYy{1%MzX zb(mnv(ofj)aco1`2)NrSAtik$Osv(ff3qK#u|ahbAm)*$p88R2)>>iIQ!gRl?+j_z zx;Q(8gi{E^2;HvFpxWJ-?!*0 z40esGm-++0(YIwS8DJ7yWL2!u@U1@N6Juy>z6TSR_8Q$IY8OH{vOvC~=j@r>-@H8% zKG3i2X$OeXfKwwDH?QlfVp!+IE1rj@OIiI+E60<48(Zy{NWQ3G17F&psV*ZxLd`832R0#5poq2h#*!hq@$3 zP17ark(tPxIn<$PTo6A5zCQG^jjfoW`{qCcQADE96AK%PjlHm=MQsQ B#BBfo diff --git a/norch/nn/loss.py b/norch/nn/loss.py index bfc4390..8d7b0f9 100644 --- a/norch/nn/loss.py +++ b/norch/nn/loss.py @@ -1,4 +1,5 @@ from .module import Module +from norch.autograd.functions import * import norch from abc import ABC @@ -45,8 +46,6 @@ class CrossEntropyLoss(Loss): logits = norch.softmax(input, dim=0) cost = -(logits.log() * target).sum() - - return cost else: # target -> class probabilities (one-hot encoded) @@ -55,8 +54,6 @@ class CrossEntropyLoss(Loss): logits = norch.softmax(input, dim=0) cost = -(logits.log() * target).sum() - return cost - elif input.ndim == 2: # batched @@ -70,8 +67,6 @@ class CrossEntropyLoss(Loss): logits = norch.softmax(input, dim=1) cost = -(logits.log() * target).sum() / batch_size - return cost - else: # target -> class probabilities (one-hot encoded) assert target.shape == input.shape, \ @@ -81,7 +76,10 @@ class CrossEntropyLoss(Loss): logits = norch.softmax(input, dim=1) cost = -(logits.log() * target).sum() / batch_size - return cost + if input.requires_grad: + cost.grad_fn = CrossEntropyLossBackward(input, target) + + return cost diff --git a/tests/test_autograd.py b/tests/test_autograd.py index fb124d6..3c1b16e 100644 --- a/tests/test_autograd.py +++ b/tests/test_autograd.py @@ -604,40 +604,147 @@ class TestTensorAutograd(unittest.TestCase): self.assertTrue(utils.compare_torch(norch_tensor_grad, torch_tensor_grad)) - def test_softmax(self): + def test_mse_loss_autograd(self): """ - Test autograd from softmax + Test the MSELoss with autograd functionality """ - norch_tensor = norch.Tensor([[[-5, 10], [-4, -4]], [[5., 6], [7, 8]]], requires_grad=True).to(self.device) - norch_softmax = norch.softmax(norch_tensor, dim=1) - norch_result = norch_softmax.sum() + loss_fn_norch = norch.nn.MSELoss() + loss_fn_torch = torch.nn.MSELoss() - norch_result.backward() - norch_tensor_grad = utils.to_torch(norch_tensor.grad).to(self.device) + predictions_norch = norch.Tensor([1.1, 2, 3, 4], requires_grad=True).to(self.device) + labels_norch = norch.Tensor([4, 3, 2.1, 1]).to(self.device) + loss_norch = loss_fn_norch.forward(predictions_norch, labels_norch) + loss_norch.backward() # Backpropagate the loss + grad_norch = predictions_norch.grad - torch_tensor = torch.tensor([[[-5, 10], [-4, -4]], [[5., 6], [7, 8]]], requires_grad=True).to(self.device) - torch_softmax = torch.softmax(torch_tensor, dim=1) - torch_result = torch_softmax.sum() + predictions_torch = torch.tensor([1.1, 2, 3, 4], requires_grad=True).to(self.device) + labels_torch = torch.tensor([4, 3, 2.1, 1]).to(self.device) + loss_torch_expected = loss_fn_torch(predictions_torch, labels_torch) + loss_torch_expected.backward() # Backpropagate the loss + grad_torch_expected = predictions_torch.grad - torch_result.backward() - torch_tensor_grad = torch_tensor.grad + # Convert norch gradient to torch tensor for comparison + grad_norch_torch = utils.to_torch(grad_norch).to(self.device) + + self.assertTrue(utils.compare_torch(grad_norch_torch, grad_torch_expected)) + + def test_cross_entropy_loss_autograd(self): + """ + Test the CrossEntropyLoss with autograd functionality + """ + loss_fn_norch = norch.nn.CrossEntropyLoss() + loss_fn_torch = torch.nn.CrossEntropyLoss() + + # Test case 1: Single class, single sample + predictions_norch = norch.Tensor([2.0, 1.0, 0.1], requires_grad=True).to(self.device) + labels_norch = norch.Tensor([0]).to(self.device) + loss_norch = loss_fn_norch.forward(predictions_norch, labels_norch) + + loss_norch.backward() # Backpropagate the loss + grad_norch = predictions_norch.grad + + predictions_torch = torch.tensor([2.0, 1.0, 0.1], requires_grad=True).to(self.device) + labels_torch = torch.tensor(0).to(self.device) + loss_torch_expected = loss_fn_torch(predictions_torch, labels_torch) + loss_torch_expected.backward() # Backpropagate the loss + grad_torch_expected = predictions_torch.grad + + # Convert norch gradient to torch tensor for comparison + grad_norch_torch = utils.to_torch(grad_norch).to(self.device) + + self.assertTrue(utils.compare_torch(grad_norch_torch, grad_torch_expected)) + + # Test case 2: Multiple classes, multiple samples + predictions_norch = norch.Tensor([[0.5, 1.5, 2.5], [1.0, 2.0, 3.0]], requires_grad=True).to(self.device) + labels_norch = norch.Tensor([2, 1]).to(self.device) + loss_norch = loss_fn_norch.forward(predictions_norch, labels_norch) + loss_norch.backward() # Backpropagate the loss + grad_norch = predictions_norch.grad + + predictions_torch = torch.tensor([[0.5, 1.5, 2.5], [1.0, 2.0, 3.0]], requires_grad=True).to(self.device) + labels_torch = torch.tensor([2, 1]).to(self.device) + loss_torch_expected = loss_fn_torch(predictions_torch, labels_torch) + loss_torch_expected.backward() # Backpropagate the loss + grad_torch_expected = predictions_torch.grad + + # Convert norch gradient to torch tensor for comparison + grad_norch_torch = utils.to_torch(grad_norch).to(self.device) + + self.assertTrue(utils.compare_torch(grad_norch_torch, grad_torch_expected)) + + # Test case 3: Edge case - all predictions are zero + predictions_norch = norch.Tensor([[0.0, 0.0, 0.0], [0.0, 0.0, 0.0]], requires_grad=True).to(self.device) + labels_norch = norch.Tensor([1, 2]).to(self.device) + loss_norch = loss_fn_norch.forward(predictions_norch, labels_norch) + loss_norch.backward() # Backpropagate the loss + grad_norch = predictions_norch.grad + + predictions_torch = torch.tensor([[0.0, 0.0, 0.0], [0.0, 0.0, 0.0]], requires_grad=True).to(self.device) + labels_torch = torch.tensor([1, 2]).to(self.device) + loss_torch_expected = loss_fn_torch(predictions_torch, labels_torch) + loss_torch_expected.backward() # Backpropagate the loss + grad_torch_expected = predictions_torch.grad + + # Convert norch gradient to torch tensor for comparison + grad_norch_torch = utils.to_torch(grad_norch).to(self.device) + + self.assertTrue(utils.compare_torch(grad_norch_torch, grad_torch_expected)) + + # Test case 4: Batched class probabilities instead of class index + predictions_norch = norch.Tensor([[0.5, 0.2, 0.1], [0.1, 0.5, 0.7]], requires_grad=True).to(self.device) + labels_norch = norch.Tensor([[1., 0, 0], [0, 1, 0]]).to(self.device) + loss_norch = loss_fn_norch.forward(predictions_norch, labels_norch) + loss_norch.backward() # Backpropagate the loss + grad_norch = predictions_norch.grad + + predictions_torch = torch.tensor([[0.5, 0.2, 0.1], [0.1, 0.5, 0.7]], requires_grad=True).to(self.device) + labels_torch = torch.tensor([[1., 0, 0], [0, 1, 0]]).to(self.device) + loss_torch_expected = loss_fn_torch(predictions_torch, labels_torch) + loss_torch_expected.backward() # Backpropagate the loss + grad_torch_expected = predictions_torch.grad + + # Convert norch gradient to torch tensor for comparison + grad_norch_torch = utils.to_torch(grad_norch).to(self.device) + + self.assertTrue(utils.compare_torch(grad_norch_torch, grad_torch_expected)) + + + # implement grad pure softmax --> 0 + # def test_softmax(self): + # """ + # Test autograd from softmax + # """ + # norch_tensor = norch.Tensor([[[-5, 10], [-4, -4]], [[5., 6], [7, 8]]], requires_grad=True).to(self.device) + # norch_softmax = norch.softmax(norch_tensor, dim=1) + # norch_result = norch_softmax.sum() + + # norch_result.backward() + # norch_tensor_grad = utils.to_torch(norch_tensor.grad).to(self.device) + + # torch_tensor = torch.tensor([[[-5, 10], [-4, -4]], [[5., 6], [7, 8]]], requires_grad=True).to(self.device) + # torch_softmax = torch.softmax(torch_tensor, dim=1) + # torch_result = torch_softmax.sum() + + # torch_result.backward() + # torch_tensor_grad = torch_tensor.grad - self.assertTrue(utils.compare_torch(norch_tensor_grad, torch_tensor_grad)) + # self.assertTrue(utils.compare_torch(norch_tensor_grad, torch_tensor_grad)) - norch_tensor = norch.Tensor([[[10, 1], [-4, 0]], [[5., 50], [7, 8]]], requires_grad=True).to(self.device) - norch_softmax = norch.softmax(norch_tensor, dim=2) - norch_result = norch_softmax.sum() + # norch_tensor = norch.Tensor([[[10, 1], [-4, 0]], [[5., 50], [7, 8]]], requires_grad=True).to(self.device) + # norch_softmax = norch.softmax(norch_tensor, dim=2) + # norch_result = norch_softmax.sum() - norch_result.backward() - norch_tensor_grad = utils.to_torch(norch_tensor.grad).to(self.device) + # norch_result.backward() + # norch_tensor_grad = utils.to_torch(norch_tensor.grad).to(self.device) - torch_tensor = torch.tensor([[[10, 1], [-4, 0]], [[5., 50], [7, 8]]], requires_grad=True).to(self.device) + # torch_tensor = torch.tensor([[[10, 1], [-4, 0]], [[5., 50], [7, 8]]], requires_grad=True).to(self.device) - torch_softmax = torch.softmax(torch_tensor, dim=2) - torch_result = torch_softmax.sum() - torch_result.backward() - torch_tensor_grad = torch_tensor.grad - self.assertTrue(utils.compare_torch(norch_tensor_grad, torch_tensor_grad)) + # torch_softmax = torch.softmax(torch_tensor, dim=2) + # torch_result = torch_softmax.sum() + # torch_result.backward() + # torch_tensor_grad = torch_tensor.grad + + # self.assertTrue(utils.compare_torch(norch_tensor_grad, torch_tensor_grad)) def test_reshape(self): From db434ec197a80816ea4a9c317d51aaf5b5d14277 Mon Sep 17 00:00:00 2001 From: lucasdelimanogueira Date: Tue, 21 May 2024 14:21:35 -0300 Subject: [PATCH 26/28] unsqueeze operation --- norch/__pycache__/tensor.cpython-38.pyc | Bin 18654 -> 18972 bytes norch/tensor.py | 11 ++++ test.py | 43 +++------------ tests/test_autograd.py | 67 ++++++++++++++++++++++++ tests/test_operations.py | 25 +++++++++ 5 files changed, 109 insertions(+), 37 deletions(-) diff --git a/norch/__pycache__/tensor.cpython-38.pyc b/norch/__pycache__/tensor.cpython-38.pyc index 141689fc6244ffff0b732112c60c98f8e693cfe5..4b4c091ce96e47215b92ef1ae9ac4b5322700597 100644 GIT binary patch delta 1546 zcmZvcTTEMJ9Kg^2Yk`&uv$TNRw2%&3SGmcCxDCp<701mDZ!mW#e5cT{UijLM%V2Dy zcv-gT`0z5P3nUs{G$weAFMH94y$+3wW?f=@nGcw_s9D0p$o&2XxtQ3dzwmx`p&4?V|PcfRJ|42HGgRT(pTci|bRgg?hxbR4Jpa^tre+ z13l&RAbm{evM4z$IiFVtbnaA0@Q>4eoz{tplEkk;{32ZflR#luieyZ}q;yOg)-!t) zC|MwQHwlYm2F{{IN5$PN$&sjT5V9rIh)4-F+0vBoUeHgHMG(j7I~a((6RnN@fD(-O zLqS_87PW=fk{UYEu{%7XZqN74$GQxuJBCG<56j_c$nJnMsOIe4K zDe5t;l^0|i;GtTay+**p?YWMWgr52X6$ySgw}u#jU(7uQZhofPGG|QXS$S~?@GE&P z>+$WGy_Cu_0rgJaHGm+WEpX+B&~6vFreB}V2r8-&yYXD3EHf>vU0o^oj+lIC8ASN` zH@1@GfDi+CdNDT^Hf~igTsT#sr9ubB5bH@%(t19D(q_-Gep&XZIYpwdQ7p_fhGr~` zMYaCZ?FRUQuN3T2+wC`qmDLMVLLVIuE6$C|xwB*^+~A!h@w6#nb&`KO+UO$?J1)!j z_*zL@@+nNL?aL@89#jTp`7gfg$mFLTmFs&%eK-`6<@@Tg<5L}s@Gtik@yjj~tg4^5 zt^#0LmUXXDS*}+5N;|>y8s^0fiA@*%^M<;EqTRCvZDiK@A=Z@-^wJjXY38SOtv-uFP3qrS`u2!D{ zSW!o7%7`JM_m24^>}}CrFkQpgTK+(B2*er3fO1 z7)MMX@DX9th%<<@h*`wzh&K?p8g?Fmb7L0~^N1m-NbnsL-$lHKcwZo42(wUFVbO7R gnd?1;S=f~UtY9Unr3SP1bs9{(#?uNP@EK3a-DaK#Xm}n&ajEQFIJx?r4^u6TsPQLft zbI*_aUOWe%o&o#)RaKP+`K-7{CNC_$ZvPb^fhJ`O3}KHFtR7}-SxpfVDpQ%GXux^p zh4=`&hutf+7Pgc1v8~ebv0W_0!qRGG{cKy&kZ5DuSww#QYzG^V-}cldHprqy!%;{C zSd6WcS}jvX4ZgUTHij$Zp|4hbj_*`ohYRB5nzty6ftn6;M5cBH57(T74{(jT5283* zt>O{20zMQc)LDR!#P5!u$@}KIZu3LZ`v$f-FM1x77EcgV!ajmSqEw-2`2yFp5`O2r z&!UkY#UDGImQ+rvzlibmD+=tuhwED^5+;_;1QWPa-=q9#zzv=zyjdTC5MFY-Fz$L9 zCh=pJ-;r7!JIR=){bmrg8?FNE$9LWSnn!7Uqr_Dc_mMq?EAA%PhI_nSBGIs@*pj49 z5E%Z@*leDZ<^;Mt)3?rg*|QLwqGej((}XRAPJ&+F3|Zy+<|Z_4uNb{srjVl)Y_qgX zDe#P5Yo0tVi9Oyw6lcjYIm0u_LdjH^&!v3j9LHO{@FpH>DOMen8-2>ZxyxQvWabWN z+E;+d^|d(U>#`i8(E4cZkp0c3_G{WV_>yn%*0@%DR`^y}TCW&k3YXj4;d}8(`$d3^ zKFrerEBMhK^oq9wtzbJsvC6r+AS~>`1*I-SvRC~#d9WQ_wM*U*`<8N;Yyw=+)nzlEB^3*?eKC%q( zJbDJchR3n3w*i|6Q}7xt4UW5q=+Nl@oIEVG_t6$@h7Q~u?UcV$I@&c7BF``(LWmK_ zlkXzv|IRpB2MAdLC*%oqY4|+hFyRUOBC7V$!1yr&HISbmED@-3ewy$s;W@$?iIO$P lXL2cCnCE9P9BXupNYM%oa440VtZVhxhX-RZKzuK@_isJ_M?C-l diff --git a/norch/tensor.py b/norch/tensor.py index 49df15d..a4e8569 100644 --- a/norch/tensor.py +++ b/norch/tensor.py @@ -153,6 +153,17 @@ class Tensor: result_data.grad_fn = ReshapeBackward(self) return result_data + + def unsqueeze(self, dim): + # Ensure the dimension is valid + if dim < 0 or dim > self.ndim: + raise ValueError("Dimension out of range (expected to be in range of [0, {0}], but got {1})".format(self.ndim, dim)) + + # Create the new shape with an extra dimension of size 1 + new_shape = self.shape[:dim] + [1] + self.shape[dim:] + + return self.reshape(new_shape) + def to(self, device): self.device = device diff --git a/test.py b/test.py index 54b67fb..5622e3d 100644 --- a/test.py +++ b/test.py @@ -6,37 +6,6 @@ import norch.optim as optim import random random.seed(1) -"""one_hot_target = norch.one_hot_encode(norch.Tensor([5]), num_classes=10) -print(one_hot_target)""" - -logits = norch.Tensor([[2.0, 1.0, 0.1, 0.1], [2.0, 1.0, 0.1, 0.1], [2.0, 1.0, 0.1, 0.1]], requires_grad=True) - -# One-hot encoded target with shape (batch_size, num_classes) -one_hot_target = norch.Tensor([0, 1, 1]) - -criterion = nn.CrossEntropyLoss() - -loss = criterion(logits, one_hot_target) -print(loss) - - -"""a = norch.Tensor([[[4.186502456665039]]]) -b = norch.Tensor([[[2.0, 2.0,],[-1.0, -1.0,]],[[1.0, 2.0,],[3.0, 3.0,]]]) - -print(b) -print(a.shape) -print(b-a)""" - -"""print(torch_tensor.shape) -print('\n\n') -torch_tensor2 = torch_tensor.max(axis=1) -print(torch_tensor2.shape) -print('\n\n') -c = torch_tensor + torch_tensor2 -print(c) -""" - -""" to_tensor = lambda x: norch.Tensor(x) reshape = lambda x: x.reshape([-1, 784]) @@ -53,9 +22,9 @@ train_loader = Dataloader(train_data, batch_size = BATCH_SIZE) class MyModel(nn.Module): def __init__(self): super(MyModel, self).__init__() - self.fc1 = nn.Linear(784, 10) + self.fc1 = nn.Linear(784, 5) self.sigmoid = nn.Sigmoid() - self.fc2 = nn.Linear(10, 1) + self.fc2 = nn.Linear(5, 10) def forward(self, x): out = self.fc1(x) @@ -68,7 +37,7 @@ device = "cpu" epochs = 10 model = MyModel().to(device) -criterion = nn.MSELoss() +criterion = nn.CrossEntropyLoss() optimizer = optim.SGD(model.parameters(), lr=0.001) loss_list = [] @@ -77,13 +46,13 @@ for epoch in range(epochs): x, target = batch x = x.T - target = target.T + target = target x = x.to(device) target = target.to(device) outputs = model(x) - + print(outputs.shape, target.shape) loss = criterion(outputs, target) optimizer.zero_grad() @@ -103,4 +72,4 @@ for epoch in range(epochs): print('\n\n') print(f'Epoch [{epoch + 1}/{epochs}], Loss: {loss[0]:.4f}') - loss_list.append(loss[0])""" \ No newline at end of file + loss_list.append(loss[0]) \ No newline at end of file diff --git a/tests/test_autograd.py b/tests/test_autograd.py index 3c1b16e..c5a95f7 100644 --- a/tests/test_autograd.py +++ b/tests/test_autograd.py @@ -824,7 +824,74 @@ class TestTensorAutograd(unittest.TestCase): self.assertTrue(utils.compare_torch(norch_tensor_grad_reshape_matmul1, torch_tensor_grad_reshape_matmul1)) self.assertTrue(utils.compare_torch(norch_tensor_grad_reshape_matmul2, torch_tensor_grad_reshape_matmul2)) + def test_unsqueeze(self): + """ + Test autograd from unsqueezing a tensor: tensor.unsqueeze(dim) + """ + + # Unsqueeze at dim=0 + norch_tensor_unsqueeze = norch.Tensor([[1., 2], [3, 4]], requires_grad=True).to(self.device) + norch_result_unsqueeze_0 = norch_tensor_unsqueeze.unsqueeze(0).sum() + norch_result_unsqueeze_0.backward() + norch_tensor_grad_unsqueeze_0 = utils.to_torch(norch_tensor_unsqueeze.grad).to(self.device) + torch_tensor_unsqueeze = torch.tensor([[1., 2], [3, 4]], requires_grad=True).to(self.device) + torch_result_unsqueeze_0 = torch_tensor_unsqueeze.unsqueeze(0).sum() + torch_result_unsqueeze_0.backward() + torch_tensor_grad_unsqueeze_0 = torch_tensor_unsqueeze.grad + + self.assertTrue(utils.compare_torch(norch_tensor_grad_unsqueeze_0, torch_tensor_grad_unsqueeze_0)) + + # Unsqueeze at dim=1 + norch_tensor_unsqueeze = norch.Tensor([[1., 2.], [3, 4]], requires_grad=True).to(self.device) + norch_result_unsqueeze_1 = norch_tensor_unsqueeze.unsqueeze(1).sum() + norch_result_unsqueeze_1.backward() + norch_tensor_grad_unsqueeze_1 = utils.to_torch(norch_tensor_unsqueeze.grad).to(self.device) + + torch_tensor_unsqueeze = torch.tensor([[1., 2.], [3, 4]], requires_grad=True).to(self.device) + torch_result_unsqueeze_1 = torch_tensor_unsqueeze.unsqueeze(1).sum() + torch_result_unsqueeze_1.backward() + torch_tensor_grad_unsqueeze_1 = torch_tensor_unsqueeze.grad + + self.assertTrue(utils.compare_torch(norch_tensor_grad_unsqueeze_1, torch_tensor_grad_unsqueeze_1)) + + # Unsqueeze at dim=2 + norch_tensor_unsqueeze = norch.Tensor([[1., 2], [3, 4]], requires_grad=True).to(self.device) + norch_result_unsqueeze_2 = norch_tensor_unsqueeze.unsqueeze(2).sum() + norch_result_unsqueeze_2.backward() + norch_tensor_grad_unsqueeze_2 = utils.to_torch(norch_tensor_unsqueeze.grad).to(self.device) + + torch_tensor_unsqueeze = torch.tensor([[1., 2], [3, 4]], requires_grad=True).to(self.device) + torch_result_unsqueeze_2 = torch_tensor_unsqueeze.unsqueeze(2).sum() + torch_result_unsqueeze_2.backward() + torch_tensor_grad_unsqueeze_2 = torch_tensor_unsqueeze.grad + + self.assertTrue(utils.compare_torch(norch_tensor_grad_unsqueeze_2, torch_tensor_grad_unsqueeze_2)) + + def test_unsqueeze_then_matmul(self): + """ + Test autograd from unsqueezing a tensor then performing matrix multiplication: matmul(tensor1.unsqueeze(dim), tensor2) + """ + norch_tensor1 = norch.Tensor([[1, 2], [3, 4]], requires_grad=True).to(self.device) + norch_tensor2 = norch.Tensor([[1, 2], [3, 4]], requires_grad=True).to(self.device) + + # Unsqueeze at dim=0 then matmul + norch_result_unsqueeze_matmul = (norch_tensor1 @ norch_tensor2.unsqueeze(0)).sum() + norch_result_unsqueeze_matmul.backward() + norch_tensor_grad_unsqueeze_matmul1 = utils.to_torch(norch_tensor1.grad).to(self.device) + norch_tensor_grad_unsqueeze_matmul2 = utils.to_torch(norch_tensor2.grad).to(self.device) + + torch_tensor1 = torch.tensor([[1, 2], [3, 4]], dtype=torch.float32, requires_grad=True).to(self.device) + torch_tensor2 = torch.tensor([[1, 2], [3, 4]], dtype=torch.float32, requires_grad=True).to(self.device) + + torch_result_unsqueeze_matmul = (torch_tensor1 @ torch_tensor2.unsqueeze(0)).sum() + torch_result_unsqueeze_matmul.backward() + torch_tensor_grad_unsqueeze_matmul1 = torch_tensor1.grad + torch_tensor_grad_unsqueeze_matmul2 = torch_tensor2.grad + + self.assertTrue(utils.compare_torch(norch_tensor_grad_unsqueeze_matmul1, torch_tensor_grad_unsqueeze_matmul1)) + self.assertTrue(utils.compare_torch(norch_tensor_grad_unsqueeze_matmul2, torch_tensor_grad_unsqueeze_matmul2)) + def test_T_then_matmul(self): """ Test autograd from transposing a tensor then performing matrix multiplication: matmul(tensor.T, tensor) diff --git a/tests/test_operations.py b/tests/test_operations.py index 961cb17..e141066 100644 --- a/tests/test_operations.py +++ b/tests/test_operations.py @@ -207,6 +207,31 @@ class TestTensorOperations(unittest.TestCase): self.assertTrue(utils.compare_torch(torch_result, torch_expected)) + def test_unsqueeze(self): + """ + Test unsqueeze operation on a tensor + """ + norch_tensor = norch.Tensor([[1, 2], [3, 4]]).to(self.device) + + # Unsqueeze at dim=0 + norch_unsqueeze_0 = norch_tensor.unsqueeze(0) + torch_unsqueeze_0 = utils.to_torch(norch_unsqueeze_0).to(self.device) + torch_tensor = torch.tensor([[1, 2], [3, 4]]).to(self.device) + torch_expected_0 = torch_tensor.unsqueeze(0) + self.assertTrue(utils.compare_torch(torch_unsqueeze_0, torch_expected_0)) + + # Unsqueeze at dim=1 + norch_unsqueeze_1 = norch_tensor.unsqueeze(1) + torch_unsqueeze_1 = utils.to_torch(norch_unsqueeze_1).to(self.device) + torch_expected_1 = torch_tensor.unsqueeze(1) + self.assertTrue(utils.compare_torch(torch_unsqueeze_1, torch_expected_1)) + + # Unsqueeze at dim=2 + norch_unsqueeze_2 = norch_tensor.unsqueeze(2) + torch_unsqueeze_2 = utils.to_torch(norch_unsqueeze_2).to(self.device) + torch_expected_2 = torch_tensor.unsqueeze(2) + self.assertTrue(utils.compare_torch(torch_unsqueeze_2, torch_expected_2)) + def test_transpose(self): """ Test transposition of a tensor: tensor.transpose(dim1, dim2) From be4eef45ac7c85cc3394db093851158637ac58f0 Mon Sep 17 00:00:00 2001 From: lucasdelimanogueira Date: Tue, 21 May 2024 14:32:14 -0300 Subject: [PATCH 27/28] unittest unsqueeze and unsqueeze neg --- norch/__pycache__/tensor.cpython-38.pyc | Bin 18972 -> 18990 bytes norch/tensor.py | 5 ++++- test.py | 5 +++-- tests/test_operations.py | 13 ++++++++++++- 4 files changed, 19 insertions(+), 4 deletions(-) diff --git a/norch/__pycache__/tensor.cpython-38.pyc b/norch/__pycache__/tensor.cpython-38.pyc index 4b4c091ce96e47215b92ef1ae9ac4b5322700597..5e72f97e94ffc18be6e6f6d0dc458e6f2d6ad727 100644 GIT binary patch delta 400 zcmXAi&r6d59LC@0`&NW=@5HTMr44oQ&Nhj)euxcXn6s^jtvMnHRMZu;U1}LSw0S6u zh(?F+@E{Rp?Idl;mpVig-nt2e9)^EFBAN(-C##>{N)yV4y*Fo{sgGWzT*O6oqP0O z_{SUiBoSn+?SYDBcDlP|>x=;$7l+G6xWfsfSMGN#5gv=v-3vUDnAsaVcN=02C#BrB3vX z*yVQeXx}nEiT~;puql<`FhSvXC?d&g2Y^@Nq3eVpGl@#AzY~;Kxf=9|XP^u8?ueg; ZE5K)Q-$(;Lxfm(nk65A^K;zua@*y=OdOH9B delta 356 zcmXAhO(??w7>EDAcjR-MCLj4|Gd89SVMa;wE#HZov}_JrI}9Z?ZVoi3cL~YIK{>GE zRf>b{;EJN0963nHh08Gcot~$je&G-{Lr{;X)u~E+Uw6He?MLdMCT}oq0l45*rgZ@X z6p!Lfm@1ipi#V#uhl(FGW=_`jBu-JhG(kVhJD%0cn6MoMHj*i8#8Bd zD;t@9;9kHlU!8}0X<@xbjU90`ECA0EX&fVpk?5No1hY7sTL=T}Z0?d5hXFLF;%H;J i*jtN0!GB(C3j@1S+u;GudAoBS*HYN!1=4t_d+!%T*m7(D diff --git a/norch/tensor.py b/norch/tensor.py index a4e8569..26192e7 100644 --- a/norch/tensor.py +++ b/norch/tensor.py @@ -155,8 +155,11 @@ class Tensor: return result_data def unsqueeze(self, dim): + if dim < 0: + dim = self.ndim + dim + 1 + # Ensure the dimension is valid - if dim < 0 or dim > self.ndim: + if dim > self.ndim: raise ValueError("Dimension out of range (expected to be in range of [0, {0}], but got {1})".format(self.ndim, dim)) # Create the new shape with an extra dimension of size 1 diff --git a/test.py b/test.py index 5622e3d..182f3aa 100644 --- a/test.py +++ b/test.py @@ -45,14 +45,15 @@ for epoch in range(epochs): for idx, batch in enumerate(train_loader): x, target = batch - x = x.T + x = x + print(x.shape) target = target x = x.to(device) target = target.to(device) outputs = model(x) - print(outputs.shape, target.shape) + print(outputs.shape, target.shape, x.shape) loss = criterion(outputs, target) optimizer.zero_grad() diff --git a/tests/test_operations.py b/tests/test_operations.py index e141066..f5751dd 100644 --- a/tests/test_operations.py +++ b/tests/test_operations.py @@ -232,6 +232,18 @@ class TestTensorOperations(unittest.TestCase): torch_expected_2 = torch_tensor.unsqueeze(2) self.assertTrue(utils.compare_torch(torch_unsqueeze_2, torch_expected_2)) + # Unsqueeze at dim=-1 + norch_unsqueeze_neg_1 = norch_tensor.unsqueeze(-1) + torch_unsqueeze_neg_1 = utils.to_torch(norch_unsqueeze_neg_1).to(self.device) + torch_expected_neg_1 = torch_tensor.unsqueeze(-1) + self.assertTrue(utils.compare_torch(torch_unsqueeze_neg_1, torch_expected_neg_1)) + + # Unsqueeze at dim=-2 + norch_unsqueeze_neg_2 = norch_tensor.unsqueeze(-2) + torch_unsqueeze_neg_2 = utils.to_torch(norch_unsqueeze_neg_2).to(self.device) + torch_expected_neg_2 = torch_tensor.unsqueeze(-2) + self.assertTrue(utils.compare_torch(torch_unsqueeze_neg_2, torch_expected_neg_2)) + def test_transpose(self): """ Test transposition of a tensor: tensor.transpose(dim1, dim2) @@ -308,7 +320,6 @@ class TestTensorOperations(unittest.TestCase): torch_tensor = torch.tensor([[[1, 2], [3, -4]], [[5, 6], [7, 8]]]).to(self.device) torch_expected = torch.sum(torch_tensor, dim=1, keepdim=True) - print(torch_result, torch_expected) self.assertTrue(utils.compare_torch(torch_result, torch_expected)) def test_max(self): From ed05e57cf425f20783f8807c9fb216a9d75f7e74 Mon Sep 17 00:00:00 2001 From: lucasdelimanogueira Date: Tue, 21 May 2024 14:43:16 -0300 Subject: [PATCH 28/28] squeeze operation and autograd --- norch/__pycache__/tensor.cpython-38.pyc | Bin 18990 -> 19565 bytes norch/tensor.py | 22 +++++++++- test.py | 5 +-- tests/test_autograd.py | 42 +++++++++++++++++++ tests/test_operations.py | 51 ++++++++++++++++++++++++ 5 files changed, 116 insertions(+), 4 deletions(-) diff --git a/norch/__pycache__/tensor.cpython-38.pyc b/norch/__pycache__/tensor.cpython-38.pyc index 5e72f97e94ffc18be6e6f6d0dc458e6f2d6ad727..bcc1fe02e52395d2c59b2c4b8388922a6f43bdbb 100644 GIT binary patch delta 1730 zcmZvbYitx%6oBX4+1)NJeeRZJrO(1Of55Hf8l0C)6g$iDu-rKg%HStNw-vGnX#pW|jR)!fl072?-3C0nV;7(q>_borDvB zvYLgU!qtQ-deQDeJ50fxjicu&7F1^axEfHnLhFE%W63h^=AOJ`Yr}4G82P#@%ccN( zDd-j;-KDXkdRXfl%V-0sjCMGh(*?sK#~a&*M$Du&luBxc*W@&wGA%8M9)s}%re+O| zn_5(hC%M)iPm;P1m7`;(Id1ZP!2NR<#d6`Z8y{;iVgQSIy(|aL6jb5L!lDT_>cV=! zNi2Nmq%+Ydp*au}>v8ot?e!o$3RAqwc&G;pONq$igpu{bss8w|)q=v1mFZ87q+3KQ zx{FOT`$)lKzZ(?Bk5IsiccEGV6>15Ts3EnCjh8$QD!(ZG8NRfymOTXb7WDmAo~bww z4`j&q8f>R{UJo}j@&3Wz?90AUfIsYom0{*K(e;sR4tx-bqa^wW)QR8($~seE7*<9Y z#y|2=V3%ir^c2v)z%YcFM#CaoEPljbt4uE$1WiUlt0t@NLs~wl3j0z|hlz7;7(sjG z(i;Gy^6s*5g&^~C#I$Sn+snw(>XpFlwbgf6(GXep6NV)Vt#YSP%-fNW2cj9$(proW z>In@5r}#r8%@;q`XBau#d~OKtn+JL8=f}EFswe$op%*T6@GJ(2gJ083B-z_h-_P_<2id!8SO76qho%X|44cIwV z-f60_*KK+h*hjPSskWN{pUL|6U*M!{ZmO1z9VYxBKkRs8*-py3{eM#LLfw72v~x)r zbnC3Ueg1htZtfgC+D2P86WR&%kBJ=w+9&o9oPT4GqyvOE30XppaEx$*kSClXyhV72 z@GgN4O`IjrT*U<8eL_Oni@ZqUCBkLG6~v4;EmCPyWDbd|q;n?u1kR^WiH1^qEGpLv be(LvloCf*Pg?hy+84Rzz&8-_?}$p|op89_pMos?4rE#)=5 zr)j{W*zOd>^G#JC%*Cd^1@z*3Eu(Oo@wfT_a@gD!4UDfXFszi6ZnMAbeQ-^X1M|K* zYz+mQ4|7zQaLJ`@){~5_jBVU?b)}Tr$=+)Yg)R&40d^f^jA2)}-JK(uM}KQOo(lgG zbF$f_HRInptelySuv(d^I3nekS=@LDTt~@qt0nK#9K|i^0(XF?+pFLsJQTXueBJ(q z2*k-#b`_@cS^HMYcrmgOuHaXZl5dv0w)k%pPWSQvx#GB#e*n&Oq&&yDGB2=x-9+}F zls5r???}=a#iDnJKf$D<7b;*9qum>P)l%k$3r_N1V8UC@0~9E1z3Zry+s)Nj2D~qD zwKdJAI;-&K*d}wV^KsEwV5c*=1D;%;B8-^p+ok|FcUDt99&xtI2Xp4-uAfAB&3-xD zImOj%UhO9WYg(J}!uC%a^Bm%MS58~ck?kFGxjcm7%0t2e4|s%Kk=FI}khCFf zi*$137sJ`zlP7kPOD7oomFYbUuA&DR&exG;HN_}0rWwZ={O8ao7|$}EV?57zk?|6P zk4L}4;63Woj0Fa-L%(LV5MO8c2ICx|T$$I0^I2V()^Fl`DgS5kejh_(Iu F{{hQIIAH() diff --git a/norch/tensor.py b/norch/tensor.py index 26192e7..1121066 100644 --- a/norch/tensor.py +++ b/norch/tensor.py @@ -167,7 +167,27 @@ class Tensor: return self.reshape(new_shape) - + def squeeze(self, dim=None): + if dim is not None: + if dim < 0: + dim = self.ndim + dim + + # Ensure the dimension is valid + if dim >= self.ndim or dim < 0: + raise ValueError("Dimension out of range (expected to be in range of [0, {0}), but got {1})".format(self.ndim, dim)) + + # Only squeeze the specified dimension if its size is 1 + if self.shape[dim] != 1: + raise ValueError("Dimension {0} does not have size 1 and cannot be squeezed".format(dim)) + + # Create the new shape without the specified dimension + new_shape = self.shape[:dim] + self.shape[dim+1:] + else: + # Create the new shape by removing all dimensions of size 1 + new_shape = [s for s in self.shape if s != 1] + + return self.reshape(new_shape) + def to(self, device): self.device = device self.device_ctype = self.device.encode('utf-8') diff --git a/test.py b/test.py index 182f3aa..7ad7e2d 100644 --- a/test.py +++ b/test.py @@ -45,12 +45,11 @@ for epoch in range(epochs): for idx, batch in enumerate(train_loader): x, target = batch - x = x - print(x.shape) + x = x.unsqueeze(-1) target = target x = x.to(device) - target = target.to(device) + target = target.to(device).unsqueeze(-1) outputs = model(x) print(outputs.shape, target.shape, x.shape) diff --git a/tests/test_autograd.py b/tests/test_autograd.py index c5a95f7..4739e7c 100644 --- a/tests/test_autograd.py +++ b/tests/test_autograd.py @@ -891,7 +891,49 @@ class TestTensorAutograd(unittest.TestCase): self.assertTrue(utils.compare_torch(norch_tensor_grad_unsqueeze_matmul1, torch_tensor_grad_unsqueeze_matmul1)) self.assertTrue(utils.compare_torch(norch_tensor_grad_unsqueeze_matmul2, torch_tensor_grad_unsqueeze_matmul2)) + + def test_squeeze(self): + """ + Test autograd from squeezing a tensor: tensor.squeeze(dim) + """ + # Squeeze at dim=0 + norch_tensor_squeeze = norch.Tensor([[[1., 2], [3, 4]]], requires_grad=True).to(self.device) + norch_result_squeeze_0 = norch_tensor_squeeze.squeeze(0).sum() + norch_result_squeeze_0.backward() + norch_tensor_grad_squeeze_0 = utils.to_torch(norch_tensor_squeeze.grad).to(self.device) + + torch_tensor_squeeze = torch.tensor([[[1., 2], [3, 4]]], requires_grad=True).to(self.device) + torch_result_squeeze_0 = torch_tensor_squeeze.squeeze(0).sum() + torch_result_squeeze_0.backward() + torch_tensor_grad_squeeze_0 = torch_tensor_squeeze.grad + + self.assertTrue(utils.compare_torch(norch_tensor_grad_squeeze_0, torch_tensor_grad_squeeze_0)) + + def test_squeeze_then_matmul(self): + """ + Test autograd from squeezing a tensor then performing matrix multiplication: matmul(tensor1.squeeze(dim), tensor2) + """ + norch_tensor1 = norch.Tensor([[[1., 2], [3, 4]]], requires_grad=True).to(self.device) + norch_tensor2 = norch.Tensor([[[1., 2], [3, 4]]], requires_grad=True).to(self.device) + # Squeeze at dim=0 then matmul + norch_result_squeeze_matmul = (norch_tensor1.squeeze(0) @ norch_tensor2).sum() + norch_result_squeeze_matmul.backward() + norch_tensor_grad_squeeze_matmul1 = utils.to_torch(norch_tensor1.grad).to(self.device) + norch_tensor_grad_squeeze_matmul2 = utils.to_torch(norch_tensor2.grad).to(self.device) + + torch_tensor1 = torch.tensor([[[1., 2], [3, 4]]], dtype=torch.float32, requires_grad=True).to(self.device) + torch_tensor2 = torch.tensor([[[1., 2], [3, 4]]], dtype=torch.float32, requires_grad=True).to(self.device) + + torch_result_squeeze_matmul = (torch_tensor1.squeeze(0) @ torch_tensor2).sum() + torch_result_squeeze_matmul.backward() + torch_tensor_grad_squeeze_matmul1 = torch_tensor1.grad + torch_tensor_grad_squeeze_matmul2 = torch_tensor2.grad + + self.assertTrue(utils.compare_torch(norch_tensor_grad_squeeze_matmul1, torch_tensor_grad_squeeze_matmul1)) + self.assertTrue(utils.compare_torch(norch_tensor_grad_squeeze_matmul2, torch_tensor_grad_squeeze_matmul2)) + + def test_T_then_matmul(self): """ Test autograd from transposing a tensor then performing matrix multiplication: matmul(tensor.T, tensor) diff --git a/tests/test_operations.py b/tests/test_operations.py index f5751dd..868f982 100644 --- a/tests/test_operations.py +++ b/tests/test_operations.py @@ -244,6 +244,57 @@ class TestTensorOperations(unittest.TestCase): torch_expected_neg_2 = torch_tensor.unsqueeze(-2) self.assertTrue(utils.compare_torch(torch_unsqueeze_neg_2, torch_expected_neg_2)) + def test_squeeze(self): + """ + Test squeeze operation on a tensor + """ + # Create a tensor with some dimensions of size 1 + norch_tensor = norch.Tensor([[[1, 2], [3, 4]]]).to(self.device) # shape [1, 2, 2] + + # Squeeze at dim=0 + norch_squeeze_0 = norch_tensor.squeeze(0) + torch_squeeze_0 = utils.to_torch(norch_squeeze_0).to(self.device) + torch_tensor = torch.tensor([[[1, 2], [3, 4]]]).to(self.device) + torch_expected_0 = torch_tensor.squeeze(0) + self.assertTrue(utils.compare_torch(torch_squeeze_0, torch_expected_0)) + + # Squeeze at dim=2 (should raise an error because size is not 1) + with self.assertRaises(ValueError): + norch_tensor.squeeze(2) + + # Create a tensor with a dimension of size 1 in the middle + norch_tensor_middle_1 = norch.Tensor([[[1, 2]], [[3, 4]]]).to(self.device) # shape [2, 1, 2] + + # Squeeze at dim=1 + norch_squeeze_1 = norch_tensor_middle_1.squeeze(1) + torch_squeeze_1 = utils.to_torch(norch_squeeze_1).to(self.device) + torch_tensor_middle_1 = torch.tensor([[[1, 2]], [[3, 4]]]).to(self.device) + torch_expected_1 = torch_tensor_middle_1.squeeze(1) + self.assertTrue(utils.compare_torch(torch_squeeze_1, torch_expected_1)) + + # Squeeze at dim=-2 (same as dim=1 in this case) + norch_squeeze_neg_2 = norch_tensor_middle_1.squeeze(-2) + torch_squeeze_neg_2 = utils.to_torch(norch_squeeze_neg_2).to(self.device) + torch_expected_neg_2 = torch_tensor_middle_1.squeeze(-2) + self.assertTrue(utils.compare_torch(torch_squeeze_neg_2, torch_expected_neg_2)) + + # Squeeze all dimensions of size 1 (None) + norch_tensor_all_1 = norch.Tensor([[[[1, 2], [3, 4]]]]).to(self.device) # shape [1, 1, 2, 2] + norch_squeeze_all = norch_tensor_all_1.squeeze() + torch_squeeze_all = utils.to_torch(norch_squeeze_all).to(self.device) + torch_tensor_all_1 = torch.tensor([[[[1, 2], [3, 4]]]]).to(self.device) + torch_expected_all = torch_tensor_all_1.squeeze() + self.assertTrue(utils.compare_torch(torch_squeeze_all, torch_expected_all)) + + # Squeeze no dimensions (no dimensions of size 1) + norch_tensor_no_1 = norch.Tensor([[1, 2], [3, 4]]).to(self.device) # shape [2, 2] + norch_squeeze_none = norch_tensor_no_1.squeeze() + torch_squeeze_none = utils.to_torch(norch_squeeze_none).to(self.device) + torch_tensor_no_1 = torch.tensor([[1, 2], [3, 4]]).to(self.device) + torch_expected_none = torch_tensor_no_1.squeeze() + self.assertTrue(utils.compare_torch(torch_squeeze_none, torch_expected_none)) + + def test_transpose(self): """ Test transposition of a tensor: tensor.transpose(dim1, dim2)