gradforge 2.0.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- gradforge-2.0.1/PKG-INFO +54 -0
- gradforge-2.0.1/README.md +40 -0
- gradforge-2.0.1/gradforge/_exceptions/__init__.py +4 -0
- gradforge-2.0.1/gradforge/_exceptions/_errors.py +6 -0
- gradforge-2.0.1/gradforge/_tests/profiling_stats.py +6 -0
- gradforge-2.0.1/gradforge/_tests/test_linear.py +47 -0
- gradforge-2.0.1/gradforge/_tests/test_operation.py +11 -0
- gradforge-2.0.1/gradforge/autograd/__init__.py +2 -0
- gradforge-2.0.1/gradforge/autograd/_operations/add.py +20 -0
- gradforge-2.0.1/gradforge/autograd/_operations/div.py +23 -0
- gradforge-2.0.1/gradforge/autograd/_operations/matmul.py +22 -0
- gradforge-2.0.1/gradforge/autograd/_operations/mul.py +25 -0
- gradforge-2.0.1/gradforge/autograd/_operations/pow.py +26 -0
- gradforge-2.0.1/gradforge/autograd/_operations/sub.py +19 -0
- gradforge-2.0.1/gradforge/autograd/engine.py +23 -0
- gradforge-2.0.1/gradforge/autograd/operation.py +15 -0
- gradforge-2.0.1/gradforge/autograd/standard_engine.py +35 -0
- gradforge-2.0.1/gradforge/backend/__init__.py +5 -0
- gradforge-2.0.1/gradforge/backend/backend.py +85 -0
- gradforge-2.0.1/gradforge/backend/backend_manager.py +13 -0
- gradforge-2.0.1/gradforge/backend/cupy_backend/cuda_backend.py +4 -0
- gradforge-2.0.1/gradforge/backend/numpy_backend/numpy_backend.py +72 -0
- gradforge-2.0.1/gradforge/core/tensor.py +71 -0
- gradforge-2.0.1/gradforge/data/preprocessing/__init__.py +2 -0
- gradforge-2.0.1/gradforge/data/preprocessing/preprocess.py +22 -0
- gradforge-2.0.1/gradforge/metrics/__init__.py +4 -0
- gradforge-2.0.1/gradforge/metrics/std_metrics.py +34 -0
- gradforge-2.0.1/gradforge/optimizers/__init__.py +0 -0
- gradforge-2.0.1/gradforge/optimizers/adam.py +0 -0
- gradforge-2.0.1/gradforge/optimizers/optimizer.py +14 -0
- gradforge-2.0.1/gradforge/optimizers/sgd.py +13 -0
- gradforge-2.0.1/gradforge/training/__init__.py +0 -0
- gradforge-2.0.1/gradforge/training/callbacks/__init__.py +2 -0
- gradforge-2.0.1/gradforge/training/callbacks/std_callbacks.py +38 -0
- gradforge-2.0.1/gradforge/training/layers/__init__.py +0 -0
- gradforge-2.0.1/gradforge/training/layers/layer.py +15 -0
- gradforge-2.0.1/gradforge/training/layers/linear.py +17 -0
- gradforge-2.0.1/gradforge/training/models/model.py +0 -0
- gradforge-2.0.1/gradforge/training/models/sequential.py +10 -0
- gradforge-2.0.1/gradforge/training/trainer.py +2 -0
- gradforge-2.0.1/gradforge.egg-info/PKG-INFO +54 -0
- gradforge-2.0.1/gradforge.egg-info/SOURCES.txt +45 -0
- gradforge-2.0.1/gradforge.egg-info/dependency_links.txt +1 -0
- gradforge-2.0.1/gradforge.egg-info/requires.txt +5 -0
- gradforge-2.0.1/gradforge.egg-info/top_level.txt +1 -0
- gradforge-2.0.1/pyproject.toml +35 -0
- gradforge-2.0.1/setup.cfg +4 -0
gradforge-2.0.1/PKG-INFO
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: gradforge
|
|
3
|
+
Version: 2.0.1
|
|
4
|
+
Summary: Machinelearning library
|
|
5
|
+
Author-email: insane-machines <forshnikita2@gmail.com>
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/nikitaforsh/gradforge
|
|
8
|
+
Requires-Python: >=3.11
|
|
9
|
+
Description-Content-Type: text/markdown
|
|
10
|
+
Requires-Dist: numpy
|
|
11
|
+
Requires-Dist: pytest>=9.1.1
|
|
12
|
+
Provides-Extra: cuda
|
|
13
|
+
Requires-Dist: cupy; extra == "cuda"
|
|
14
|
+
|
|
15
|
+
<h1 align="center">
|
|
16
|
+
<img src="https://github.com/insane-machines/fsh/blob/main/fsh/fsh.egg-info/logo.jpg"></img>
|
|
17
|
+
</h1>
|
|
18
|
+
<h1 align="center">
|
|
19
|
+
<img src="https://readme-typing-svg.herokuapp.com?font=Fira+Code&pause=1000&color=00FFAA¢er=true&vCenter=true&width=500&lines=Hi,+I'm+nikitaforsh!;Welcome+to++our+machinelearning+library;named+GradForge!" alt="Typing SVG" />
|
|
20
|
+
</h1>
|
|
21
|
+
|
|
22
|
+
<p align="center">
|
|
23
|
+
<a href="https://t.me/insane_machines" target="_blank">
|
|
24
|
+
<img src="https://img.shields.io/badge/Insane%20Machines-Telegram-blue?style=for-the-badge&logo=telegram" alt="Telegram link" />
|
|
25
|
+
</a>
|
|
26
|
+
</p>
|
|
27
|
+
Features
|
|
28
|
+
|
|
29
|
+
Linear model with:
|
|
30
|
+
• **Callbacks**
|
|
31
|
+
• **Metrics**
|
|
32
|
+
• **Preprocessing functions**
|
|
33
|
+
• **Simple integration**
|
|
34
|
+
|
|
35
|
+
---
|
|
36
|
+
|
|
37
|
+
Quick Start
|
|
38
|
+
|
|
39
|
+
```python
|
|
40
|
+
import fsh
|
|
41
|
+
|
|
42
|
+
X = [[1, 2], [3, 4]]
|
|
43
|
+
y = [[5], [6]]
|
|
44
|
+
|
|
45
|
+
#array converting is integrated! but you can do it yourself!
|
|
46
|
+
X = fsh.fsh.preprocessing.to_array(X)
|
|
47
|
+
y = fsh.fsh.preprocessing.to_array(y)
|
|
48
|
+
|
|
49
|
+
callback = fsh.fsh.callbacks.History()
|
|
50
|
+
model = fsh.fsh.Linear(n_features=2, lr=0.01)
|
|
51
|
+
|
|
52
|
+
# Training
|
|
53
|
+
model.train(X, y, epochs=100, callbacks=[callback], loss_fn=fsh.fsh.metrics.mse)
|
|
54
|
+
model.predict(X)
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
<h1 align="center">
|
|
2
|
+
<img src="https://github.com/insane-machines/fsh/blob/main/fsh/fsh.egg-info/logo.jpg"></img>
|
|
3
|
+
</h1>
|
|
4
|
+
<h1 align="center">
|
|
5
|
+
<img src="https://readme-typing-svg.herokuapp.com?font=Fira+Code&pause=1000&color=00FFAA¢er=true&vCenter=true&width=500&lines=Hi,+I'm+nikitaforsh!;Welcome+to++our+machinelearning+library;named+GradForge!" alt="Typing SVG" />
|
|
6
|
+
</h1>
|
|
7
|
+
|
|
8
|
+
<p align="center">
|
|
9
|
+
<a href="https://t.me/insane_machines" target="_blank">
|
|
10
|
+
<img src="https://img.shields.io/badge/Insane%20Machines-Telegram-blue?style=for-the-badge&logo=telegram" alt="Telegram link" />
|
|
11
|
+
</a>
|
|
12
|
+
</p>
|
|
13
|
+
Features
|
|
14
|
+
|
|
15
|
+
Linear model with:
|
|
16
|
+
• **Callbacks**
|
|
17
|
+
• **Metrics**
|
|
18
|
+
• **Preprocessing functions**
|
|
19
|
+
• **Simple integration**
|
|
20
|
+
|
|
21
|
+
---
|
|
22
|
+
|
|
23
|
+
Quick Start
|
|
24
|
+
|
|
25
|
+
```python
|
|
26
|
+
import fsh
|
|
27
|
+
|
|
28
|
+
X = [[1, 2], [3, 4]]
|
|
29
|
+
y = [[5], [6]]
|
|
30
|
+
|
|
31
|
+
#array converting is integrated! but you can do it yourself!
|
|
32
|
+
X = fsh.fsh.preprocessing.to_array(X)
|
|
33
|
+
y = fsh.fsh.preprocessing.to_array(y)
|
|
34
|
+
|
|
35
|
+
callback = fsh.fsh.callbacks.History()
|
|
36
|
+
model = fsh.fsh.Linear(n_features=2, lr=0.01)
|
|
37
|
+
|
|
38
|
+
# Training
|
|
39
|
+
model.train(X, y, epochs=100, callbacks=[callback], loss_fn=fsh.fsh.metrics.mse)
|
|
40
|
+
model.predict(X)
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
from gradforge.core.tensor import Tensor
|
|
2
|
+
from gradforge.optimizers.sgd import SGD
|
|
3
|
+
from gradforge.autograd.standard_engine import StandardEngine
|
|
4
|
+
from gradforge.backend.backend_manager import BackendManager
|
|
5
|
+
from gradforge.training.layers.linear import Linear
|
|
6
|
+
import tracemalloc
|
|
7
|
+
|
|
8
|
+
#tracemalloc.start()
|
|
9
|
+
|
|
10
|
+
def train(model, x, y, epochs):
|
|
11
|
+
engine = StandardEngine()
|
|
12
|
+
optimizer = SGD(learning_rate=1e-3)
|
|
13
|
+
for epoch in range(epochs):
|
|
14
|
+
y_pred = model.predict(x)
|
|
15
|
+
error = y - y_pred
|
|
16
|
+
loss = error ** 2
|
|
17
|
+
|
|
18
|
+
optimizer.zero_grad(model.params)
|
|
19
|
+
engine.backward(loss)
|
|
20
|
+
print('eror grad: ', error.grad)
|
|
21
|
+
try:
|
|
22
|
+
optimizer.step(model.params)
|
|
23
|
+
except Exception as e:
|
|
24
|
+
print('Error: ', e)
|
|
25
|
+
|
|
26
|
+
print(x.data @ model.w.data)
|
|
27
|
+
|
|
28
|
+
x = Tensor([[4], [8], [9]])
|
|
29
|
+
y = Tensor([[8], [16], [18]])
|
|
30
|
+
test = Tensor([[40], [60]])
|
|
31
|
+
assert x.data.shape == y.data.shape
|
|
32
|
+
|
|
33
|
+
#before = tracemalloc.take_snapshot()
|
|
34
|
+
|
|
35
|
+
model = Linear(1, 1)
|
|
36
|
+
train(model, x, y, epochs=10000)
|
|
37
|
+
# after = tracemalloc.take_snapshot()
|
|
38
|
+
|
|
39
|
+
print(test.data @ model.w.data)
|
|
40
|
+
# print(model.w.data.shape)
|
|
41
|
+
|
|
42
|
+
# stats = after.compare_to(before, "lineno")
|
|
43
|
+
# for result in stats[:50]:
|
|
44
|
+
# print(result)
|
|
45
|
+
|
|
46
|
+
# 0.757 seconds
|
|
47
|
+
# Backend allocations needs to optimize
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import timeit
|
|
2
|
+
|
|
3
|
+
setup = "from gradforge.core.tensor import Tensor; a = Tensor([5]); b = Tensor([6])"
|
|
4
|
+
|
|
5
|
+
result = timeit.repeat("c = a + b", setup=setup, number=100000, repeat=10)
|
|
6
|
+
|
|
7
|
+
best_time = min(result)
|
|
8
|
+
time_per_op = best_time / 100_000
|
|
9
|
+
print(time_per_op * 1000000)
|
|
10
|
+
|
|
11
|
+
#0.8082507399922179
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
from ...autograd.operation import Operation
|
|
2
|
+
|
|
3
|
+
class Add(Operation):
|
|
4
|
+
def __init__(self, backend) -> None:
|
|
5
|
+
super().__init__(backend)
|
|
6
|
+
|
|
7
|
+
def forward(self, a, b):
|
|
8
|
+
|
|
9
|
+
result = self._backend.add(a, b)
|
|
10
|
+
|
|
11
|
+
return result
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def backward(self, result_grad) -> tuple:
|
|
15
|
+
|
|
16
|
+
a_grad = result_grad
|
|
17
|
+
b_grad = result_grad
|
|
18
|
+
|
|
19
|
+
return (a_grad, b_grad)
|
|
20
|
+
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
from ...autograd.operation import Operation
|
|
2
|
+
|
|
3
|
+
class Div(Operation):
|
|
4
|
+
def __init__(self, backend) -> None:
|
|
5
|
+
super().__init__(backend)
|
|
6
|
+
self.parent_data = (0, 0)
|
|
7
|
+
|
|
8
|
+
def forward(self, a, b):
|
|
9
|
+
|
|
10
|
+
result = self._backend.div(a, b)
|
|
11
|
+
self.parent_data = (a, b)
|
|
12
|
+
|
|
13
|
+
return result
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def backward(self, result_grad) -> tuple:
|
|
17
|
+
a_data, b_data = self.parent_data
|
|
18
|
+
|
|
19
|
+
a_grad = result_grad / b_data if b_data != 0 else 0
|
|
20
|
+
b_grad = (- a_data / b_data ^ 2) * result_grad if b_data != 0 else 0
|
|
21
|
+
|
|
22
|
+
return (a_grad, b_grad)
|
|
23
|
+
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
from ...autograd.operation import Operation
|
|
2
|
+
class Matmul(Operation):
|
|
3
|
+
def __init__(self, backend) -> None:
|
|
4
|
+
super().__init__(backend)
|
|
5
|
+
self.parent_data = (0, 0)
|
|
6
|
+
|
|
7
|
+
def forward(self, a, b):
|
|
8
|
+
|
|
9
|
+
self.parent_data = (a, b)
|
|
10
|
+
result = self._backend.matmul(a, b)
|
|
11
|
+
|
|
12
|
+
return result
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def backward(self, result_grad) -> tuple:
|
|
16
|
+
|
|
17
|
+
a_data, b_data = self.parent_data
|
|
18
|
+
|
|
19
|
+
a_grad = result_grad @ self._backend.transpose(b_data)
|
|
20
|
+
b_grad = self._backend.transpose(a_data) @ result_grad
|
|
21
|
+
|
|
22
|
+
return (a_grad, b_grad)
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
from ...autograd.operation import Operation
|
|
2
|
+
from typing import TYPE_CHECKING
|
|
3
|
+
if TYPE_CHECKING:
|
|
4
|
+
from ...core.tensor import Tensor
|
|
5
|
+
class Mul(Operation):
|
|
6
|
+
def __init__(self, backend) -> None:
|
|
7
|
+
super().__init__(backend)
|
|
8
|
+
self.parent_data = (0, 0)
|
|
9
|
+
|
|
10
|
+
def forward(self, a, b):
|
|
11
|
+
|
|
12
|
+
self.parent_data = (a, b)
|
|
13
|
+
result = self._backend.mul(a, b)
|
|
14
|
+
|
|
15
|
+
return result
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def backward(self, result_grad) -> tuple:
|
|
19
|
+
a_data, b_data = self.parent_data
|
|
20
|
+
|
|
21
|
+
a_grad = b_data * result_grad
|
|
22
|
+
b_grad = a_data * result_grad
|
|
23
|
+
|
|
24
|
+
return (a_grad, b_grad)
|
|
25
|
+
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
from ...autograd.operation import Operation
|
|
2
|
+
class Pow(Operation):
|
|
3
|
+
def __init__(self, backend) -> None:
|
|
4
|
+
super().__init__(backend)
|
|
5
|
+
self.n = 0
|
|
6
|
+
self.parent_data = 0
|
|
7
|
+
|
|
8
|
+
def forward(self, x, n):
|
|
9
|
+
|
|
10
|
+
self.parent_data = x
|
|
11
|
+
self.n = n
|
|
12
|
+
|
|
13
|
+
result = x ** n
|
|
14
|
+
|
|
15
|
+
return result
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def backward(self, result_grad) -> list:
|
|
19
|
+
x_data = self.parent_data
|
|
20
|
+
n = self.n
|
|
21
|
+
|
|
22
|
+
x_grad = result_grad * n * x_data**(n - 1)
|
|
23
|
+
|
|
24
|
+
return [x_grad]
|
|
25
|
+
|
|
26
|
+
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
from ...autograd.operation import Operation
|
|
2
|
+
class Sub(Operation):
|
|
3
|
+
def __init__(self, backend) -> None:
|
|
4
|
+
super().__init__(backend)
|
|
5
|
+
|
|
6
|
+
def forward(self, a, b):
|
|
7
|
+
|
|
8
|
+
result = self._backend.sub(a.data, b.data)
|
|
9
|
+
|
|
10
|
+
return result
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def backward(self, result_grad) -> tuple:
|
|
14
|
+
|
|
15
|
+
a_grad = result_grad
|
|
16
|
+
b_grad = -result_grad
|
|
17
|
+
|
|
18
|
+
return (a_grad, b_grad)
|
|
19
|
+
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
from abc import ABC, abstractmethod
|
|
2
|
+
from ..backend.backend_manager import BackendManager
|
|
3
|
+
|
|
4
|
+
class Engine(ABC):
|
|
5
|
+
def __init__(self) -> None:
|
|
6
|
+
super().__init__()
|
|
7
|
+
self._backend = BackendManager.get_backend()
|
|
8
|
+
|
|
9
|
+
@abstractmethod
|
|
10
|
+
def _prepare_root_grad():
|
|
11
|
+
pass
|
|
12
|
+
|
|
13
|
+
@abstractmethod
|
|
14
|
+
def _tensor_backward():
|
|
15
|
+
pass
|
|
16
|
+
|
|
17
|
+
@abstractmethod
|
|
18
|
+
def _start_backward_recursion():
|
|
19
|
+
pass
|
|
20
|
+
|
|
21
|
+
@abstractmethod
|
|
22
|
+
def backward():
|
|
23
|
+
pass
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
from abc import ABC, abstractmethod
|
|
3
|
+
from ..backend.backend import Backend
|
|
4
|
+
class Operation(ABC):
|
|
5
|
+
def __init__(self, backend: Backend) -> None:
|
|
6
|
+
super().__init__()
|
|
7
|
+
self._backend: Backend = backend
|
|
8
|
+
|
|
9
|
+
@abstractmethod
|
|
10
|
+
def forward(self, a, b):
|
|
11
|
+
pass
|
|
12
|
+
|
|
13
|
+
@abstractmethod
|
|
14
|
+
def backward(self, result_grad) -> tuple:
|
|
15
|
+
pass
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
from .engine import Engine
|
|
2
|
+
from ..core.tensor import Tensor
|
|
3
|
+
|
|
4
|
+
class StandardEngine(Engine):
|
|
5
|
+
def __init__(self) -> None:
|
|
6
|
+
super().__init__()
|
|
7
|
+
|
|
8
|
+
def _prepare_root_grad(self, tensor: Tensor):
|
|
9
|
+
root_grad = self._backend.ones_like(tensor.data)
|
|
10
|
+
return root_grad
|
|
11
|
+
|
|
12
|
+
def _tensor_backward(self, tensor: Tensor, grad):
|
|
13
|
+
if tensor.grad_fn is None:
|
|
14
|
+
return ()
|
|
15
|
+
|
|
16
|
+
parents = tensor.grad_fn[0]
|
|
17
|
+
grads = tensor.grad_fn[1].backward(grad)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
return zip(parents, grads)
|
|
21
|
+
|
|
22
|
+
def _start_backward_recursion(self, tensor_backward):
|
|
23
|
+
for parent, parent_grad in tensor_backward:
|
|
24
|
+
if parent.requires_grad:
|
|
25
|
+
parent.grad += parent_grad
|
|
26
|
+
|
|
27
|
+
if parent.grad_fn is not None:
|
|
28
|
+
self.backward(parent, parent.grad)
|
|
29
|
+
|
|
30
|
+
def backward(self, tensor: Tensor, grad=None):
|
|
31
|
+
if grad is None:
|
|
32
|
+
grad = self._prepare_root_grad(tensor)
|
|
33
|
+
|
|
34
|
+
tensor_backward = self._tensor_backward(tensor, grad)
|
|
35
|
+
self._start_backward_recursion(tensor_backward)
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
from abc import ABC, abstractmethod
|
|
2
|
+
class Backend(ABC):
|
|
3
|
+
|
|
4
|
+
@abstractmethod
|
|
5
|
+
def convert(self, data):
|
|
6
|
+
return data
|
|
7
|
+
|
|
8
|
+
#Binary operations
|
|
9
|
+
@abstractmethod
|
|
10
|
+
def add(self, a, b):
|
|
11
|
+
pass
|
|
12
|
+
|
|
13
|
+
@abstractmethod
|
|
14
|
+
def div(self, a, b):
|
|
15
|
+
pass
|
|
16
|
+
|
|
17
|
+
@abstractmethod
|
|
18
|
+
def mul(self, a, b):
|
|
19
|
+
pass
|
|
20
|
+
|
|
21
|
+
@abstractmethod
|
|
22
|
+
def sub(self, a, b):
|
|
23
|
+
pass
|
|
24
|
+
|
|
25
|
+
#Matrix operations
|
|
26
|
+
@abstractmethod
|
|
27
|
+
def matmul(self, a, b):
|
|
28
|
+
pass
|
|
29
|
+
|
|
30
|
+
@abstractmethod
|
|
31
|
+
def transpose(self, matrix):
|
|
32
|
+
return matrix
|
|
33
|
+
|
|
34
|
+
#Unary operations
|
|
35
|
+
@abstractmethod
|
|
36
|
+
def relu(self, x):
|
|
37
|
+
return x
|
|
38
|
+
|
|
39
|
+
@abstractmethod
|
|
40
|
+
def sigmoid(self, x):
|
|
41
|
+
return x
|
|
42
|
+
|
|
43
|
+
@abstractmethod
|
|
44
|
+
def tanh(self, x):
|
|
45
|
+
return x
|
|
46
|
+
|
|
47
|
+
@abstractmethod
|
|
48
|
+
def softmax(self, x):
|
|
49
|
+
return x
|
|
50
|
+
|
|
51
|
+
@abstractmethod
|
|
52
|
+
def mean(self, array):
|
|
53
|
+
return array
|
|
54
|
+
|
|
55
|
+
@abstractmethod
|
|
56
|
+
def std(self, array):
|
|
57
|
+
return array
|
|
58
|
+
|
|
59
|
+
@abstractmethod
|
|
60
|
+
def abs(self, array):
|
|
61
|
+
return array
|
|
62
|
+
|
|
63
|
+
@abstractmethod
|
|
64
|
+
def zeros_like(self, array):
|
|
65
|
+
return array
|
|
66
|
+
|
|
67
|
+
@abstractmethod
|
|
68
|
+
def ones_like(self, array):
|
|
69
|
+
return array
|
|
70
|
+
|
|
71
|
+
@abstractmethod
|
|
72
|
+
def array_zeros(self, shape):
|
|
73
|
+
return shape
|
|
74
|
+
|
|
75
|
+
@abstractmethod
|
|
76
|
+
def array_ones(self, shape):
|
|
77
|
+
return shape
|
|
78
|
+
|
|
79
|
+
@abstractmethod
|
|
80
|
+
def is_native(self, data) -> bool:
|
|
81
|
+
pass
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
from .backend import Backend
|
|
2
|
+
from .numpy_backend.numpy_backend import NumpyBackend
|
|
3
|
+
class BackendManager():
|
|
4
|
+
_backend: Backend = NumpyBackend()
|
|
5
|
+
|
|
6
|
+
@staticmethod
|
|
7
|
+
def set_backend(backend_adapter: Backend):
|
|
8
|
+
BackendManager._backend = backend_adapter
|
|
9
|
+
|
|
10
|
+
@staticmethod
|
|
11
|
+
def get_backend() -> Backend:
|
|
12
|
+
return BackendManager._backend
|
|
13
|
+
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
from ..backend import Backend
|
|
2
|
+
import numpy as np
|
|
3
|
+
|
|
4
|
+
class NumpyBackend(Backend):
|
|
5
|
+
|
|
6
|
+
def convert(self, data) -> np.ndarray:
|
|
7
|
+
if not self.is_native(data):
|
|
8
|
+
return np.array(data, dtype=np.float32)
|
|
9
|
+
|
|
10
|
+
return data
|
|
11
|
+
|
|
12
|
+
#Binary operations
|
|
13
|
+
def add(self, a, b):
|
|
14
|
+
return np.add(a, b)
|
|
15
|
+
|
|
16
|
+
def div(self, a, b):
|
|
17
|
+
return np.divide(a, b)
|
|
18
|
+
|
|
19
|
+
def mul(self, a, b):
|
|
20
|
+
return np.multiply(a, b)
|
|
21
|
+
|
|
22
|
+
def sub(self, a, b):
|
|
23
|
+
return np.subtract(a, b)
|
|
24
|
+
|
|
25
|
+
#Matrix operations
|
|
26
|
+
def matmul(self, a, b):
|
|
27
|
+
return np.matmul(a, b)
|
|
28
|
+
|
|
29
|
+
def transpose(self, matrix):
|
|
30
|
+
return matrix.T
|
|
31
|
+
|
|
32
|
+
#Unary operations
|
|
33
|
+
def relu(self, x):
|
|
34
|
+
return max(0, x)
|
|
35
|
+
|
|
36
|
+
def sigmoid(self, x):
|
|
37
|
+
pass
|
|
38
|
+
|
|
39
|
+
def tanh(self, x):
|
|
40
|
+
pass
|
|
41
|
+
|
|
42
|
+
def softmax(self, x):
|
|
43
|
+
pass
|
|
44
|
+
|
|
45
|
+
def mean(self, array):
|
|
46
|
+
return np.mean(array)
|
|
47
|
+
|
|
48
|
+
def std(self, array):
|
|
49
|
+
return np.std(array)
|
|
50
|
+
|
|
51
|
+
def abs(self, array):
|
|
52
|
+
return np.abs(array)
|
|
53
|
+
|
|
54
|
+
def zeros_like(self, array):
|
|
55
|
+
return np.zeros_like(array)
|
|
56
|
+
|
|
57
|
+
def ones_like(self, array):
|
|
58
|
+
return np.ones_like(array)
|
|
59
|
+
|
|
60
|
+
def array_zeros(self, shape):
|
|
61
|
+
return np.zeros(shape)
|
|
62
|
+
|
|
63
|
+
def array_ones(self, shape):
|
|
64
|
+
return np.ones(shape)
|
|
65
|
+
|
|
66
|
+
def is_native(self, data) -> bool:
|
|
67
|
+
if type(data) is np.ndarray:
|
|
68
|
+
return True
|
|
69
|
+
else:
|
|
70
|
+
return False
|
|
71
|
+
|
|
72
|
+
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
from ..backend.backend_manager import BackendManager
|
|
3
|
+
from ..backend.backend import Backend
|
|
4
|
+
from ..autograd._operations import mul, matmul, sub, div, add, pow
|
|
5
|
+
from typing import Optional
|
|
6
|
+
|
|
7
|
+
class Tensor():
|
|
8
|
+
__slots__ = ['data', 'requires_grad', 'grad', 'grad_fn', 'backend']
|
|
9
|
+
|
|
10
|
+
def __init__(self, data, requires_grad: bool = False):
|
|
11
|
+
self.requires_grad: bool = requires_grad
|
|
12
|
+
self.grad = 0
|
|
13
|
+
self.grad_fn: Optional[list] = None
|
|
14
|
+
self.backend: Backend = BackendManager.get_backend()
|
|
15
|
+
self.data = self.backend.convert(data)
|
|
16
|
+
|
|
17
|
+
def __mul__(self, other):
|
|
18
|
+
mul_op = mul.Mul(self.backend)
|
|
19
|
+
mul_forward = mul_op.forward(self.data, other.data)
|
|
20
|
+
|
|
21
|
+
result = Tensor(mul_forward, requires_grad=self.requires_grad or other.requires_grad)
|
|
22
|
+
result.grad_fn = [[self, other], mul_op]
|
|
23
|
+
|
|
24
|
+
return result
|
|
25
|
+
|
|
26
|
+
def __matmul__(self, other: Tensor):
|
|
27
|
+
mat_mul_op = matmul.Matmul(self.backend)
|
|
28
|
+
mul_forward = mat_mul_op.forward(self.data, other.data)
|
|
29
|
+
|
|
30
|
+
result = Tensor(mul_forward, requires_grad=self.requires_grad or other.requires_grad)
|
|
31
|
+
result.grad_fn = [[self, other], mat_mul_op]
|
|
32
|
+
|
|
33
|
+
return result
|
|
34
|
+
|
|
35
|
+
def __add__(self, other: Tensor):
|
|
36
|
+
add_op = add.Add(self.backend)
|
|
37
|
+
add_forward = add_op.forward(self.data, other.data)
|
|
38
|
+
|
|
39
|
+
result = Tensor(add_forward, requires_grad=self.requires_grad or other.requires_grad)
|
|
40
|
+
result.grad_fn = [[self, other], add_op]
|
|
41
|
+
|
|
42
|
+
return result
|
|
43
|
+
|
|
44
|
+
def __sub__(self, other: Tensor):
|
|
45
|
+
sub_op = sub.Sub(self.backend)
|
|
46
|
+
sub_forward = sub_op.forward(self.data, other.data)
|
|
47
|
+
|
|
48
|
+
result = Tensor(sub_forward, requires_grad=self.requires_grad or other.requires_grad)
|
|
49
|
+
result.grad_fn = [[self, other], sub_op]
|
|
50
|
+
|
|
51
|
+
return result
|
|
52
|
+
|
|
53
|
+
def __truediv__(self, other):
|
|
54
|
+
div_op = div.Div(self.backend)
|
|
55
|
+
div_forward = div_op.forward(self.data, other.data)
|
|
56
|
+
|
|
57
|
+
result = Tensor(div_forward, requires_grad=self.requires_grad or other.requires_grad)
|
|
58
|
+
result.grad_fn = [[self, other], div_op]
|
|
59
|
+
|
|
60
|
+
return result
|
|
61
|
+
|
|
62
|
+
def __pow__(self, other):
|
|
63
|
+
pow_op = pow.Pow(self.backend)
|
|
64
|
+
pow_forward = pow_op.forward(self.data, other)
|
|
65
|
+
|
|
66
|
+
result = Tensor(pow_forward, requires_grad=self.requires_grad or other.requires_grad)
|
|
67
|
+
result.grad_fn = [[self], pow_op]
|
|
68
|
+
|
|
69
|
+
return result
|
|
70
|
+
|
|
71
|
+
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
from ..._exceptions._errors import DataError
|
|
2
|
+
from ...backend.backend_manager import BackendManager
|
|
3
|
+
class preprocessing():
|
|
4
|
+
|
|
5
|
+
@staticmethod
|
|
6
|
+
def normalize(array, mean=None, std=None):
|
|
7
|
+
backend = BackendManager.get_backend()
|
|
8
|
+
if mean is not None and std is not None:
|
|
9
|
+
arr_mean = mean
|
|
10
|
+
arr_std = std
|
|
11
|
+
elif (mean is None and std is not None) or (mean is not None and std is None):
|
|
12
|
+
raise DataError('one of the normalize components is none, and another is not none')
|
|
13
|
+
else:
|
|
14
|
+
arr_mean = backend.mean(array)
|
|
15
|
+
arr_std = backend.std(array)
|
|
16
|
+
arr_norm = (array-arr_mean)/arr_std
|
|
17
|
+
return arr_norm, arr_mean, arr_std
|
|
18
|
+
@staticmethod
|
|
19
|
+
|
|
20
|
+
def denormalize(normalized, mean, std):
|
|
21
|
+
array = normalized * std + mean
|
|
22
|
+
return array
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
from math import sqrt
|
|
2
|
+
from ..backend.backend_manager import BackendManager
|
|
3
|
+
class metrics():
|
|
4
|
+
|
|
5
|
+
@staticmethod
|
|
6
|
+
def mse(y, y_pred, training=False):
|
|
7
|
+
backend = BackendManager.get_backend()
|
|
8
|
+
y = backend.convert(y)
|
|
9
|
+
y_pred = backend.convert(y_pred)
|
|
10
|
+
|
|
11
|
+
loss = backend.mean((y.flatten()-y_pred.flatten())**2)
|
|
12
|
+
if not training:
|
|
13
|
+
print(f'MSE: {loss}')
|
|
14
|
+
return loss
|
|
15
|
+
|
|
16
|
+
@staticmethod
|
|
17
|
+
def rmse(y, y_pred, training=False):
|
|
18
|
+
y = BackendManager.get_backend().convert(y)
|
|
19
|
+
y_pred = BackendManager.get_backend().convert(y_pred)
|
|
20
|
+
|
|
21
|
+
loss = sqrt(metrics.mse(y, y_pred, training=True))
|
|
22
|
+
if not training:
|
|
23
|
+
print(f'RMSE: {loss}')
|
|
24
|
+
return loss
|
|
25
|
+
|
|
26
|
+
@staticmethod
|
|
27
|
+
def mae(y, y_pred, training=False):
|
|
28
|
+
backend = BackendManager.get_backend()
|
|
29
|
+
mae = backend.mean(backend.abs(y - y_pred))
|
|
30
|
+
|
|
31
|
+
if not training:
|
|
32
|
+
print('MAE:', mae)
|
|
33
|
+
|
|
34
|
+
return mae
|
|
File without changes
|
|
File without changes
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
from abc import ABC, abstractmethod
|
|
2
|
+
|
|
3
|
+
class Optimizer(ABC):
|
|
4
|
+
def __init__(self, learning_rate=1e-3) -> None:
|
|
5
|
+
super().__init__()
|
|
6
|
+
self.learning_rate = learning_rate
|
|
7
|
+
|
|
8
|
+
@abstractmethod
|
|
9
|
+
def step():
|
|
10
|
+
pass
|
|
11
|
+
|
|
12
|
+
@abstractmethod
|
|
13
|
+
def zero_grad():
|
|
14
|
+
pass
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
from .optimizer import Optimizer
|
|
2
|
+
from ..core.tensor import Tensor
|
|
3
|
+
class SGD(Optimizer):
|
|
4
|
+
def __init__(self, learning_rate=0.001) -> None:
|
|
5
|
+
super().__init__(learning_rate)
|
|
6
|
+
|
|
7
|
+
def step(self, params: list[Tensor]):
|
|
8
|
+
for param in params:
|
|
9
|
+
param.data -= param.grad * self.learning_rate
|
|
10
|
+
|
|
11
|
+
def zero_grad(self, params: list[Tensor]):
|
|
12
|
+
for param in params:
|
|
13
|
+
param.grad = param.backend.zeros_like(param.data)
|
|
File without changes
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
class callbacks():
|
|
2
|
+
|
|
3
|
+
class EarlyStop():
|
|
4
|
+
def __init__(self, patience=10, delta=1e-3):
|
|
5
|
+
self.best_loss = None
|
|
6
|
+
self.delta = delta
|
|
7
|
+
self.patience = patience
|
|
8
|
+
self.wait = 0
|
|
9
|
+
def __call__(self, model, log):
|
|
10
|
+
val_loss = log.get('val_loss')
|
|
11
|
+
if val_loss is None:
|
|
12
|
+
return
|
|
13
|
+
|
|
14
|
+
if self.best_loss is None:
|
|
15
|
+
self.best_loss = val_loss
|
|
16
|
+
|
|
17
|
+
else:
|
|
18
|
+
if val_loss < self.best_loss - self.delta:
|
|
19
|
+
self.best_loss = val_loss
|
|
20
|
+
self.wait = 0
|
|
21
|
+
else:
|
|
22
|
+
self.wait += 1
|
|
23
|
+
|
|
24
|
+
if self.wait >= self.patience:
|
|
25
|
+
model.stop_training = True
|
|
26
|
+
else:
|
|
27
|
+
model.stop_training = False
|
|
28
|
+
|
|
29
|
+
class History():
|
|
30
|
+
def __init__(self):
|
|
31
|
+
self.history = {'epoch': [], 'loss': [], 'val_loss': []}
|
|
32
|
+
def __call__(self, model, log):
|
|
33
|
+
epoch = log.get('epoch')
|
|
34
|
+
loss = log.get('loss')
|
|
35
|
+
val_loss = log.get('val_loss')
|
|
36
|
+
self.history['epoch'].append(epoch)
|
|
37
|
+
self.history['loss'].append(loss)
|
|
38
|
+
self.history['val_loss'].append(val_loss)
|
|
File without changes
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
from .layer import Layer
|
|
2
|
+
from ...core.tensor import Tensor
|
|
3
|
+
from ...backend.backend_manager import BackendManager
|
|
4
|
+
class Linear(Layer):
|
|
5
|
+
def __init__(self, in_shape, out_shape) -> None:
|
|
6
|
+
super().__init__()
|
|
7
|
+
self._backend = BackendManager.get_backend()
|
|
8
|
+
self.w: Tensor = Tensor(self._backend.array_zeros((in_shape, out_shape)), requires_grad=True)
|
|
9
|
+
|
|
10
|
+
def predict(self, x: Tensor) -> Tensor:
|
|
11
|
+
return x @ self.w
|
|
12
|
+
|
|
13
|
+
@property
|
|
14
|
+
def params(self) -> list[Tensor]:
|
|
15
|
+
return [attr_val\
|
|
16
|
+
for attr_val in self.__dict__.values()\
|
|
17
|
+
if (type(attr_val) is Tensor and attr_val.requires_grad)]
|
|
File without changes
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: gradforge
|
|
3
|
+
Version: 2.0.1
|
|
4
|
+
Summary: Machinelearning library
|
|
5
|
+
Author-email: insane-machines <forshnikita2@gmail.com>
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/nikitaforsh/gradforge
|
|
8
|
+
Requires-Python: >=3.11
|
|
9
|
+
Description-Content-Type: text/markdown
|
|
10
|
+
Requires-Dist: numpy
|
|
11
|
+
Requires-Dist: pytest>=9.1.1
|
|
12
|
+
Provides-Extra: cuda
|
|
13
|
+
Requires-Dist: cupy; extra == "cuda"
|
|
14
|
+
|
|
15
|
+
<h1 align="center">
|
|
16
|
+
<img src="https://github.com/insane-machines/fsh/blob/main/fsh/fsh.egg-info/logo.jpg"></img>
|
|
17
|
+
</h1>
|
|
18
|
+
<h1 align="center">
|
|
19
|
+
<img src="https://readme-typing-svg.herokuapp.com?font=Fira+Code&pause=1000&color=00FFAA¢er=true&vCenter=true&width=500&lines=Hi,+I'm+nikitaforsh!;Welcome+to++our+machinelearning+library;named+GradForge!" alt="Typing SVG" />
|
|
20
|
+
</h1>
|
|
21
|
+
|
|
22
|
+
<p align="center">
|
|
23
|
+
<a href="https://t.me/insane_machines" target="_blank">
|
|
24
|
+
<img src="https://img.shields.io/badge/Insane%20Machines-Telegram-blue?style=for-the-badge&logo=telegram" alt="Telegram link" />
|
|
25
|
+
</a>
|
|
26
|
+
</p>
|
|
27
|
+
Features
|
|
28
|
+
|
|
29
|
+
Linear model with:
|
|
30
|
+
• **Callbacks**
|
|
31
|
+
• **Metrics**
|
|
32
|
+
• **Preprocessing functions**
|
|
33
|
+
• **Simple integration**
|
|
34
|
+
|
|
35
|
+
---
|
|
36
|
+
|
|
37
|
+
Quick Start
|
|
38
|
+
|
|
39
|
+
```python
|
|
40
|
+
import fsh
|
|
41
|
+
|
|
42
|
+
X = [[1, 2], [3, 4]]
|
|
43
|
+
y = [[5], [6]]
|
|
44
|
+
|
|
45
|
+
#array converting is integrated! but you can do it yourself!
|
|
46
|
+
X = fsh.fsh.preprocessing.to_array(X)
|
|
47
|
+
y = fsh.fsh.preprocessing.to_array(y)
|
|
48
|
+
|
|
49
|
+
callback = fsh.fsh.callbacks.History()
|
|
50
|
+
model = fsh.fsh.Linear(n_features=2, lr=0.01)
|
|
51
|
+
|
|
52
|
+
# Training
|
|
53
|
+
model.train(X, y, epochs=100, callbacks=[callback], loss_fn=fsh.fsh.metrics.mse)
|
|
54
|
+
model.predict(X)
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
README.md
|
|
2
|
+
pyproject.toml
|
|
3
|
+
gradforge.egg-info/PKG-INFO
|
|
4
|
+
gradforge.egg-info/SOURCES.txt
|
|
5
|
+
gradforge.egg-info/dependency_links.txt
|
|
6
|
+
gradforge.egg-info/requires.txt
|
|
7
|
+
gradforge.egg-info/top_level.txt
|
|
8
|
+
gradforge/_exceptions/__init__.py
|
|
9
|
+
gradforge/_exceptions/_errors.py
|
|
10
|
+
gradforge/_tests/profiling_stats.py
|
|
11
|
+
gradforge/_tests/test_linear.py
|
|
12
|
+
gradforge/_tests/test_operation.py
|
|
13
|
+
gradforge/autograd/__init__.py
|
|
14
|
+
gradforge/autograd/engine.py
|
|
15
|
+
gradforge/autograd/operation.py
|
|
16
|
+
gradforge/autograd/standard_engine.py
|
|
17
|
+
gradforge/autograd/_operations/add.py
|
|
18
|
+
gradforge/autograd/_operations/div.py
|
|
19
|
+
gradforge/autograd/_operations/matmul.py
|
|
20
|
+
gradforge/autograd/_operations/mul.py
|
|
21
|
+
gradforge/autograd/_operations/pow.py
|
|
22
|
+
gradforge/autograd/_operations/sub.py
|
|
23
|
+
gradforge/backend/__init__.py
|
|
24
|
+
gradforge/backend/backend.py
|
|
25
|
+
gradforge/backend/backend_manager.py
|
|
26
|
+
gradforge/backend/cupy_backend/cuda_backend.py
|
|
27
|
+
gradforge/backend/numpy_backend/numpy_backend.py
|
|
28
|
+
gradforge/core/tensor.py
|
|
29
|
+
gradforge/data/preprocessing/__init__.py
|
|
30
|
+
gradforge/data/preprocessing/preprocess.py
|
|
31
|
+
gradforge/metrics/__init__.py
|
|
32
|
+
gradforge/metrics/std_metrics.py
|
|
33
|
+
gradforge/optimizers/__init__.py
|
|
34
|
+
gradforge/optimizers/adam.py
|
|
35
|
+
gradforge/optimizers/optimizer.py
|
|
36
|
+
gradforge/optimizers/sgd.py
|
|
37
|
+
gradforge/training/__init__.py
|
|
38
|
+
gradforge/training/trainer.py
|
|
39
|
+
gradforge/training/callbacks/__init__.py
|
|
40
|
+
gradforge/training/callbacks/std_callbacks.py
|
|
41
|
+
gradforge/training/layers/__init__.py
|
|
42
|
+
gradforge/training/layers/layer.py
|
|
43
|
+
gradforge/training/layers/linear.py
|
|
44
|
+
gradforge/training/models/model.py
|
|
45
|
+
gradforge/training/models/sequential.py
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
gradforge
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=61.0"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "gradforge"
|
|
7
|
+
version = "2.0.1"
|
|
8
|
+
description = "Machinelearning library"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
license = {text = "MIT"}
|
|
11
|
+
authors = [{name = "insane-machines", email = "forshnikita2@gmail.com"}]
|
|
12
|
+
requires-python = ">=3.11"
|
|
13
|
+
dependencies = [
|
|
14
|
+
"numpy",
|
|
15
|
+
"pytest>=9.1.1",
|
|
16
|
+
]
|
|
17
|
+
|
|
18
|
+
[project.urls]
|
|
19
|
+
Homepage = "https://github.com/nikitaforsh/gradforge"
|
|
20
|
+
|
|
21
|
+
[project.optional-dependencies]
|
|
22
|
+
cuda = [
|
|
23
|
+
"cupy"
|
|
24
|
+
]
|
|
25
|
+
|
|
26
|
+
[tool.setuptools.packages.find]
|
|
27
|
+
include = ["gradforge*"]
|
|
28
|
+
|
|
29
|
+
[dependency-groups]
|
|
30
|
+
dev = [
|
|
31
|
+
"memray>=1.20.0",
|
|
32
|
+
"mypy>=2.3.0",
|
|
33
|
+
"pytest>=9.1.1",
|
|
34
|
+
"ruff>=0.15.22",
|
|
35
|
+
]
|