heroch 0.1.0__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.
- heroch-0.1.0/LICENSE +21 -0
- heroch-0.1.0/PKG-INFO +107 -0
- heroch-0.1.0/README.md +87 -0
- heroch-0.1.0/heroch/__init__.py +3 -0
- heroch-0.1.0/heroch/nn/__init__.py +4 -0
- heroch-0.1.0/heroch/nn/activations.py +31 -0
- heroch-0.1.0/heroch/nn/linear.py +28 -0
- heroch-0.1.0/heroch/nn/loss.py +17 -0
- heroch-0.1.0/heroch/nn/module.py +28 -0
- heroch-0.1.0/heroch/optim/__init__.py +2 -0
- heroch-0.1.0/heroch/optim/optimizer.py +11 -0
- heroch-0.1.0/heroch/optim/sgd.py +18 -0
- heroch-0.1.0/heroch/tensor.py +195 -0
- heroch-0.1.0/heroch.egg-info/PKG-INFO +107 -0
- heroch-0.1.0/heroch.egg-info/SOURCES.txt +19 -0
- heroch-0.1.0/heroch.egg-info/dependency_links.txt +1 -0
- heroch-0.1.0/heroch.egg-info/requires.txt +1 -0
- heroch-0.1.0/heroch.egg-info/top_level.txt +1 -0
- heroch-0.1.0/pyproject.toml +33 -0
- heroch-0.1.0/setup.cfg +4 -0
- heroch-0.1.0/tests/test_heroch.py +67 -0
heroch-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024 Death Legion Team
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
heroch-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: heroch
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: A flexible deep learning library for machine learning and scientific computing with GPU acceleration.
|
|
5
|
+
Author-email: Death Legion Team <contact@deathlegion.team>
|
|
6
|
+
Project-URL: Homepage, https://github.com/death-legion-team/heroch
|
|
7
|
+
Project-URL: Bug Tracker, https://github.com/death-legion-team/heroch/issues
|
|
8
|
+
Keywords: machine learning,deep learning,tensors,autograd,neural networks,GPU acceleration,CUDA,scientific computing
|
|
9
|
+
Classifier: Programming Language :: Python :: 3
|
|
10
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
11
|
+
Classifier: Operating System :: OS Independent
|
|
12
|
+
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
13
|
+
Classifier: Intended Audience :: Science/Research
|
|
14
|
+
Classifier: Development Status :: 3 - Alpha
|
|
15
|
+
Requires-Python: >=3.8
|
|
16
|
+
Description-Content-Type: text/markdown
|
|
17
|
+
License-File: LICENSE
|
|
18
|
+
Requires-Dist: numpy>=1.20.0
|
|
19
|
+
Dynamic: license-file
|
|
20
|
+
|
|
21
|
+
# Heroch: High-Performance Deep Learning and Scientific Computing Library
|
|
22
|
+
|
|
23
|
+
[](https://badge.fury.io/py/heroch)
|
|
24
|
+
[](https://opensource.org/licenses/MIT)
|
|
25
|
+
|
|
26
|
+
**Heroch** is a powerful, flexible, and intuitive Python library designed for **machine learning**, **deep learning**, and **scientific computing**. It provides a high-performance tensor computation platform with seamless **GPU acceleration** (via CUDA/CuPy) and a dynamic **automatic differentiation** (autograd) engine.
|
|
27
|
+
|
|
28
|
+
## Why Heroch?
|
|
29
|
+
|
|
30
|
+
Heroch is built to feel like a native Python library, integrating perfectly with the SciPy ecosystem. Whether you are conducting academic research or building production-level neural networks, Heroch offers the flexibility to experiment and the speed to scale.
|
|
31
|
+
|
|
32
|
+
### Key Features
|
|
33
|
+
|
|
34
|
+
* **⚡ High-Performance Tensor Computations**: Native NumPy interface with optional GPU acceleration for massive speedups.
|
|
35
|
+
* **🧠 Dynamic Autograd Engine**: Flexible computational graphs that allow you to change network structures on the fly.
|
|
36
|
+
* **🛠️ Complete Deep Learning Platform**: Built-in modules for linear layers, common activations (ReLU, Sigmoid), and loss functions (MSELoss).
|
|
37
|
+
* **🚀 Optimized Training**: Robust optimizers including Stochastic Gradient Descent (SGD) with momentum.
|
|
38
|
+
* **🐍 Pythonic API**: Intuitive design that is easy to debug and executes code as you write it.
|
|
39
|
+
|
|
40
|
+
## Installation
|
|
41
|
+
|
|
42
|
+
Install Heroch easily via pip:
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
pip install heroch
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
*Note: For GPU support, ensure you have an NVIDIA GPU and the appropriate CUDA drivers installed.*
|
|
49
|
+
|
|
50
|
+
## Quick Start Example
|
|
51
|
+
|
|
52
|
+
Build and train a simple neural network in minutes:
|
|
53
|
+
|
|
54
|
+
```python
|
|
55
|
+
import heroch
|
|
56
|
+
import heroch.nn as nn
|
|
57
|
+
import heroch.optim as optim
|
|
58
|
+
from heroch import Tensor
|
|
59
|
+
|
|
60
|
+
# Define your flexible model structure
|
|
61
|
+
class MyNeuralNetwork(nn.Module):
|
|
62
|
+
def __init__(self):
|
|
63
|
+
super().__init__()
|
|
64
|
+
self.fc1 = nn.Linear(2, 4)
|
|
65
|
+
self.relu = nn.ReLU()
|
|
66
|
+
self.fc2 = nn.Linear(4, 1)
|
|
67
|
+
|
|
68
|
+
def forward(self, x):
|
|
69
|
+
x = self.relu(self.fc1(x))
|
|
70
|
+
return self.fc2(x)
|
|
71
|
+
|
|
72
|
+
# Initialize model, loss, and optimizer
|
|
73
|
+
model = MyNeuralNetwork()
|
|
74
|
+
criterion = nn.MSELoss()
|
|
75
|
+
optimizer = optim.SGD(model.parameters(), lr=0.01, momentum=0.9)
|
|
76
|
+
|
|
77
|
+
# Training data
|
|
78
|
+
inputs = Tensor([[1.0, 2.0], [3.0, 4.0]])
|
|
79
|
+
targets = Tensor([[5.0], [11.0]])
|
|
80
|
+
|
|
81
|
+
# Training loop
|
|
82
|
+
for epoch in range(100):
|
|
83
|
+
optimizer.zero_grad()
|
|
84
|
+
predictions = model(inputs)
|
|
85
|
+
loss = criterion(predictions, targets)
|
|
86
|
+
loss.backward()
|
|
87
|
+
optimizer.step()
|
|
88
|
+
|
|
89
|
+
if epoch % 10 == 0:
|
|
90
|
+
print(f"Epoch {epoch}, Loss: {loss.data}")
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
## Advanced Usage: GPU Acceleration
|
|
94
|
+
|
|
95
|
+
Heroch makes it easy to move your computations to the GPU:
|
|
96
|
+
|
|
97
|
+
```python
|
|
98
|
+
# Move model parameters and data to GPU
|
|
99
|
+
x_gpu = Tensor([1.0, 2.0, 3.0]).to_gpu()
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
## Contributing
|
|
103
|
+
|
|
104
|
+
We welcome contributions from the community! Check out our [GitHub repository](https://github.com/death-legion-team/heroch) to get involved.
|
|
105
|
+
|
|
106
|
+
---
|
|
107
|
+
Developed with 🔥 by **Death Legion Team**.
|
heroch-0.1.0/README.md
ADDED
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
# Heroch: High-Performance Deep Learning and Scientific Computing Library
|
|
2
|
+
|
|
3
|
+
[](https://badge.fury.io/py/heroch)
|
|
4
|
+
[](https://opensource.org/licenses/MIT)
|
|
5
|
+
|
|
6
|
+
**Heroch** is a powerful, flexible, and intuitive Python library designed for **machine learning**, **deep learning**, and **scientific computing**. It provides a high-performance tensor computation platform with seamless **GPU acceleration** (via CUDA/CuPy) and a dynamic **automatic differentiation** (autograd) engine.
|
|
7
|
+
|
|
8
|
+
## Why Heroch?
|
|
9
|
+
|
|
10
|
+
Heroch is built to feel like a native Python library, integrating perfectly with the SciPy ecosystem. Whether you are conducting academic research or building production-level neural networks, Heroch offers the flexibility to experiment and the speed to scale.
|
|
11
|
+
|
|
12
|
+
### Key Features
|
|
13
|
+
|
|
14
|
+
* **⚡ High-Performance Tensor Computations**: Native NumPy interface with optional GPU acceleration for massive speedups.
|
|
15
|
+
* **🧠 Dynamic Autograd Engine**: Flexible computational graphs that allow you to change network structures on the fly.
|
|
16
|
+
* **🛠️ Complete Deep Learning Platform**: Built-in modules for linear layers, common activations (ReLU, Sigmoid), and loss functions (MSELoss).
|
|
17
|
+
* **🚀 Optimized Training**: Robust optimizers including Stochastic Gradient Descent (SGD) with momentum.
|
|
18
|
+
* **🐍 Pythonic API**: Intuitive design that is easy to debug and executes code as you write it.
|
|
19
|
+
|
|
20
|
+
## Installation
|
|
21
|
+
|
|
22
|
+
Install Heroch easily via pip:
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
pip install heroch
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
*Note: For GPU support, ensure you have an NVIDIA GPU and the appropriate CUDA drivers installed.*
|
|
29
|
+
|
|
30
|
+
## Quick Start Example
|
|
31
|
+
|
|
32
|
+
Build and train a simple neural network in minutes:
|
|
33
|
+
|
|
34
|
+
```python
|
|
35
|
+
import heroch
|
|
36
|
+
import heroch.nn as nn
|
|
37
|
+
import heroch.optim as optim
|
|
38
|
+
from heroch import Tensor
|
|
39
|
+
|
|
40
|
+
# Define your flexible model structure
|
|
41
|
+
class MyNeuralNetwork(nn.Module):
|
|
42
|
+
def __init__(self):
|
|
43
|
+
super().__init__()
|
|
44
|
+
self.fc1 = nn.Linear(2, 4)
|
|
45
|
+
self.relu = nn.ReLU()
|
|
46
|
+
self.fc2 = nn.Linear(4, 1)
|
|
47
|
+
|
|
48
|
+
def forward(self, x):
|
|
49
|
+
x = self.relu(self.fc1(x))
|
|
50
|
+
return self.fc2(x)
|
|
51
|
+
|
|
52
|
+
# Initialize model, loss, and optimizer
|
|
53
|
+
model = MyNeuralNetwork()
|
|
54
|
+
criterion = nn.MSELoss()
|
|
55
|
+
optimizer = optim.SGD(model.parameters(), lr=0.01, momentum=0.9)
|
|
56
|
+
|
|
57
|
+
# Training data
|
|
58
|
+
inputs = Tensor([[1.0, 2.0], [3.0, 4.0]])
|
|
59
|
+
targets = Tensor([[5.0], [11.0]])
|
|
60
|
+
|
|
61
|
+
# Training loop
|
|
62
|
+
for epoch in range(100):
|
|
63
|
+
optimizer.zero_grad()
|
|
64
|
+
predictions = model(inputs)
|
|
65
|
+
loss = criterion(predictions, targets)
|
|
66
|
+
loss.backward()
|
|
67
|
+
optimizer.step()
|
|
68
|
+
|
|
69
|
+
if epoch % 10 == 0:
|
|
70
|
+
print(f"Epoch {epoch}, Loss: {loss.data}")
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
## Advanced Usage: GPU Acceleration
|
|
74
|
+
|
|
75
|
+
Heroch makes it easy to move your computations to the GPU:
|
|
76
|
+
|
|
77
|
+
```python
|
|
78
|
+
# Move model parameters and data to GPU
|
|
79
|
+
x_gpu = Tensor([1.0, 2.0, 3.0]).to_gpu()
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
## Contributing
|
|
83
|
+
|
|
84
|
+
We welcome contributions from the community! Check out our [GitHub repository](https://github.com/death-legion-team/heroch) to get involved.
|
|
85
|
+
|
|
86
|
+
---
|
|
87
|
+
Developed with 🔥 by **Death Legion Team**.
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
from heroch.tensor import Tensor
|
|
3
|
+
from heroch.nn.module import Module
|
|
4
|
+
|
|
5
|
+
class ReLU(Module):
|
|
6
|
+
def forward(self, x):
|
|
7
|
+
data = np.maximum(0, x.data)
|
|
8
|
+
requires_grad = x.requires_grad
|
|
9
|
+
depends_on = []
|
|
10
|
+
|
|
11
|
+
if x.requires_grad:
|
|
12
|
+
def backward_func(grad):
|
|
13
|
+
# dReLU(x)/dx = 1 if x > 0 else 0
|
|
14
|
+
return grad * (x.data > 0).astype(np.float32)
|
|
15
|
+
depends_on.append({'tensor': x, 'backward_func': backward_func})
|
|
16
|
+
|
|
17
|
+
return Tensor(data, requires_grad, depends_on)
|
|
18
|
+
|
|
19
|
+
class Sigmoid(Module):
|
|
20
|
+
def forward(self, x):
|
|
21
|
+
data = 1 / (1 + np.exp(-x.data))
|
|
22
|
+
requires_grad = x.requires_grad
|
|
23
|
+
depends_on = []
|
|
24
|
+
|
|
25
|
+
if x.requires_grad:
|
|
26
|
+
def backward_func(grad):
|
|
27
|
+
# dSigmoid(x)/dx = Sigmoid(x) * (1 - Sigmoid(x))
|
|
28
|
+
return grad * (data * (1 - data))
|
|
29
|
+
depends_on.append({'tensor': x, 'backward_func': backward_func})
|
|
30
|
+
|
|
31
|
+
return Tensor(data, requires_grad, depends_on)
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
from heroch.tensor import Tensor
|
|
3
|
+
from heroch.nn.module import Module
|
|
4
|
+
|
|
5
|
+
class Linear(Module):
|
|
6
|
+
def __init__(self, in_features, out_features, bias=True):
|
|
7
|
+
super().__init__()
|
|
8
|
+
# Xavier/Glorot initialization
|
|
9
|
+
limit = np.sqrt(6 / (in_features + out_features))
|
|
10
|
+
self.weight = Tensor(
|
|
11
|
+
np.random.uniform(-limit, limit, (in_features, out_features)).astype(np.float32),
|
|
12
|
+
requires_grad=True
|
|
13
|
+
)
|
|
14
|
+
if bias:
|
|
15
|
+
self.bias = Tensor(
|
|
16
|
+
np.zeros((1, out_features), dtype=np.float32),
|
|
17
|
+
requires_grad=True
|
|
18
|
+
)
|
|
19
|
+
else:
|
|
20
|
+
self.bias = None
|
|
21
|
+
|
|
22
|
+
def forward(self, x):
|
|
23
|
+
# x is (batch_size, in_features)
|
|
24
|
+
# weight is (in_features, out_features)
|
|
25
|
+
out = x @ self.weight
|
|
26
|
+
if self.bias is not None:
|
|
27
|
+
out = out + self.bias
|
|
28
|
+
return out
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
from heroch.tensor import Tensor
|
|
3
|
+
from heroch.nn.module import Module
|
|
4
|
+
|
|
5
|
+
class MSELoss(Module):
|
|
6
|
+
def forward(self, input, target):
|
|
7
|
+
# input: Tensor (batch_size, ...)
|
|
8
|
+
# target: Tensor (batch_size, ...)
|
|
9
|
+
diff = input - target
|
|
10
|
+
# Mean squared error: sum((input - target)^2) / n
|
|
11
|
+
n = np.prod(input.shape)
|
|
12
|
+
|
|
13
|
+
# We need a pow operation for Tensor, or just use mul
|
|
14
|
+
sq_diff = diff * diff
|
|
15
|
+
loss = sq_diff.sum() * (1.0 / n)
|
|
16
|
+
|
|
17
|
+
return loss
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
from heroch.tensor import Tensor
|
|
2
|
+
|
|
3
|
+
class Module:
|
|
4
|
+
def __init__(self):
|
|
5
|
+
self._parameters = {}
|
|
6
|
+
|
|
7
|
+
def parameters(self):
|
|
8
|
+
for name, value in self.__dict__.items():
|
|
9
|
+
if isinstance(value, Tensor) and value.requires_grad:
|
|
10
|
+
yield value
|
|
11
|
+
elif isinstance(value, Module):
|
|
12
|
+
yield from value.parameters()
|
|
13
|
+
elif isinstance(value, (list, tuple)):
|
|
14
|
+
for item in value:
|
|
15
|
+
if isinstance(item, Tensor) and item.requires_grad:
|
|
16
|
+
yield item
|
|
17
|
+
elif isinstance(item, Module):
|
|
18
|
+
yield from item.parameters()
|
|
19
|
+
|
|
20
|
+
def __call__(self, *args, **kwargs):
|
|
21
|
+
return self.forward(*args, **kwargs)
|
|
22
|
+
|
|
23
|
+
def forward(self, *args, **kwargs):
|
|
24
|
+
raise NotImplementedError
|
|
25
|
+
|
|
26
|
+
def zero_grad(self):
|
|
27
|
+
for p in self.parameters():
|
|
28
|
+
p.zero_grad()
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
from heroch.optim.optimizer import Optimizer
|
|
2
|
+
|
|
3
|
+
class SGD(Optimizer):
|
|
4
|
+
def __init__(self, parameters, lr=0.01, momentum=0.0):
|
|
5
|
+
super().__init__(parameters, lr)
|
|
6
|
+
self.momentum = momentum
|
|
7
|
+
self.velocities = [0.0 for _ in self.parameters]
|
|
8
|
+
|
|
9
|
+
def step(self):
|
|
10
|
+
for i, p in enumerate(self.parameters):
|
|
11
|
+
if p.grad is None:
|
|
12
|
+
continue
|
|
13
|
+
|
|
14
|
+
if self.momentum > 0:
|
|
15
|
+
self.velocities[i] = self.momentum * self.velocities[i] + (1 - self.momentum) * p.grad
|
|
16
|
+
p.data -= self.lr * self.velocities[i]
|
|
17
|
+
else:
|
|
18
|
+
p.data -= self.lr * p.grad
|
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
|
|
3
|
+
try:
|
|
4
|
+
import cupy as cp
|
|
5
|
+
HAS_CUPY = True
|
|
6
|
+
except ImportError:
|
|
7
|
+
HAS_CUPY = False
|
|
8
|
+
|
|
9
|
+
def get_array_module(data):
|
|
10
|
+
if HAS_CUPY:
|
|
11
|
+
return cp.get_array_module(data)
|
|
12
|
+
return np
|
|
13
|
+
|
|
14
|
+
class Tensor:
|
|
15
|
+
def __init__(self, data, requires_grad=False, depends_on=None):
|
|
16
|
+
xp = cp if HAS_CUPY and isinstance(data, cp.ndarray) else np
|
|
17
|
+
|
|
18
|
+
if isinstance(data, (int, float)):
|
|
19
|
+
data = xp.array(data, dtype=np.float32)
|
|
20
|
+
elif isinstance(data, list):
|
|
21
|
+
data = xp.array(data, dtype=np.float32)
|
|
22
|
+
|
|
23
|
+
self.data = data
|
|
24
|
+
self.requires_grad = requires_grad
|
|
25
|
+
self.depends_on = depends_on or []
|
|
26
|
+
self.grad = None
|
|
27
|
+
self.shape = self.data.shape
|
|
28
|
+
|
|
29
|
+
if self.requires_grad:
|
|
30
|
+
self.zero_grad()
|
|
31
|
+
|
|
32
|
+
def zero_grad(self):
|
|
33
|
+
xp = get_array_module(self.data)
|
|
34
|
+
self.grad = xp.zeros_like(self.data, dtype=np.float32)
|
|
35
|
+
|
|
36
|
+
def to_gpu(self):
|
|
37
|
+
if HAS_CUPY:
|
|
38
|
+
self.data = cp.asarray(self.data)
|
|
39
|
+
if self.grad is not None:
|
|
40
|
+
self.grad = cp.asarray(self.grad)
|
|
41
|
+
return self
|
|
42
|
+
|
|
43
|
+
def to_cpu(self):
|
|
44
|
+
if HAS_CUPY and isinstance(self.data, cp.ndarray):
|
|
45
|
+
self.data = cp.asnumpy(self.data)
|
|
46
|
+
if self.grad is not None:
|
|
47
|
+
self.grad = cp.asnumpy(self.grad)
|
|
48
|
+
return self
|
|
49
|
+
|
|
50
|
+
def __repr__(self):
|
|
51
|
+
return f"Tensor({self.data}, requires_grad={self.requires_grad})"
|
|
52
|
+
|
|
53
|
+
def backward(self, grad=None):
|
|
54
|
+
xp = get_array_module(self.data)
|
|
55
|
+
if grad is None:
|
|
56
|
+
if self.shape == ():
|
|
57
|
+
grad = xp.array(1.0, dtype=np.float32)
|
|
58
|
+
else:
|
|
59
|
+
raise RuntimeError("backward() can only be called on scalar tensors if no gradient is provided.")
|
|
60
|
+
|
|
61
|
+
if isinstance(grad, Tensor):
|
|
62
|
+
grad = grad.data
|
|
63
|
+
|
|
64
|
+
if self.grad is None:
|
|
65
|
+
self.grad = grad
|
|
66
|
+
else:
|
|
67
|
+
self.grad += grad
|
|
68
|
+
|
|
69
|
+
for dependency in self.depends_on:
|
|
70
|
+
backward_func = dependency['backward_func']
|
|
71
|
+
tensor = dependency['tensor']
|
|
72
|
+
grad_to_propagate = backward_func(grad)
|
|
73
|
+
tensor.backward(grad_to_propagate)
|
|
74
|
+
|
|
75
|
+
def __add__(self, other):
|
|
76
|
+
other = ensure_tensor(other)
|
|
77
|
+
data = self.data + other.data
|
|
78
|
+
requires_grad = self.requires_grad or other.requires_grad
|
|
79
|
+
depends_on = []
|
|
80
|
+
|
|
81
|
+
if self.requires_grad:
|
|
82
|
+
def backward_func(grad):
|
|
83
|
+
# Gradient of sum is just the gradient (with broadcast handling)
|
|
84
|
+
ndims_added = grad.ndim - self.data.ndim
|
|
85
|
+
for _ in range(ndims_added):
|
|
86
|
+
grad = grad.sum(axis=0)
|
|
87
|
+
for i, dim in enumerate(self.data.shape):
|
|
88
|
+
if dim == 1:
|
|
89
|
+
grad = grad.sum(axis=i, keepdims=True)
|
|
90
|
+
return grad
|
|
91
|
+
depends_on.append({'tensor': self, 'backward_func': backward_func})
|
|
92
|
+
|
|
93
|
+
if other.requires_grad:
|
|
94
|
+
def backward_func(grad):
|
|
95
|
+
ndims_added = grad.ndim - other.data.ndim
|
|
96
|
+
for _ in range(ndims_added):
|
|
97
|
+
grad = grad.sum(axis=0)
|
|
98
|
+
for i, dim in enumerate(other.data.shape):
|
|
99
|
+
if dim == 1:
|
|
100
|
+
grad = grad.sum(axis=i, keepdims=True)
|
|
101
|
+
return grad
|
|
102
|
+
depends_on.append({'tensor': other, 'backward_func': backward_func})
|
|
103
|
+
|
|
104
|
+
return Tensor(data, requires_grad, depends_on)
|
|
105
|
+
|
|
106
|
+
def __mul__(self, other):
|
|
107
|
+
other = ensure_tensor(other)
|
|
108
|
+
data = self.data * other.data
|
|
109
|
+
requires_grad = self.requires_grad or other.requires_grad
|
|
110
|
+
depends_on = []
|
|
111
|
+
|
|
112
|
+
if self.requires_grad:
|
|
113
|
+
def backward_func(grad):
|
|
114
|
+
# d(x*y)/dx = y
|
|
115
|
+
res = grad * other.data
|
|
116
|
+
ndims_added = res.ndim - self.data.ndim
|
|
117
|
+
for _ in range(ndims_added):
|
|
118
|
+
res = res.sum(axis=0)
|
|
119
|
+
for i, dim in enumerate(self.data.shape):
|
|
120
|
+
if dim == 1:
|
|
121
|
+
res = res.sum(axis=i, keepdims=True)
|
|
122
|
+
return res
|
|
123
|
+
depends_on.append({'tensor': self, 'backward_func': backward_func})
|
|
124
|
+
|
|
125
|
+
if other.requires_grad:
|
|
126
|
+
def backward_func(grad):
|
|
127
|
+
# d(x*y)/dy = x
|
|
128
|
+
res = grad * self.data
|
|
129
|
+
ndims_added = res.ndim - other.data.ndim
|
|
130
|
+
for _ in range(ndims_added):
|
|
131
|
+
res = res.sum(axis=0)
|
|
132
|
+
for i, dim in enumerate(other.data.shape):
|
|
133
|
+
if dim == 1:
|
|
134
|
+
res = res.sum(axis=i, keepdims=True)
|
|
135
|
+
return res
|
|
136
|
+
depends_on.append({'tensor': other, 'backward_func': backward_func})
|
|
137
|
+
|
|
138
|
+
return Tensor(data, requires_grad, depends_on)
|
|
139
|
+
|
|
140
|
+
def __sub__(self, other):
|
|
141
|
+
return self + (-other)
|
|
142
|
+
|
|
143
|
+
def __neg__(self):
|
|
144
|
+
return self * (-1.0)
|
|
145
|
+
|
|
146
|
+
def __matmul__(self, other):
|
|
147
|
+
# self @ other
|
|
148
|
+
other = ensure_tensor(other)
|
|
149
|
+
data = self.data @ other.data
|
|
150
|
+
requires_grad = self.requires_grad or other.requires_grad
|
|
151
|
+
depends_on = []
|
|
152
|
+
|
|
153
|
+
if self.requires_grad:
|
|
154
|
+
def backward_func(grad):
|
|
155
|
+
# d(X@W)/dX = grad @ W.T
|
|
156
|
+
return grad @ other.data.T
|
|
157
|
+
depends_on.append({'tensor': self, 'backward_func': backward_func})
|
|
158
|
+
|
|
159
|
+
if other.requires_grad:
|
|
160
|
+
def backward_func(grad):
|
|
161
|
+
# d(X@W)/dW = X.T @ grad
|
|
162
|
+
return self.data.T @ grad
|
|
163
|
+
depends_on.append({'tensor': other, 'backward_func': backward_func})
|
|
164
|
+
|
|
165
|
+
return Tensor(data, requires_grad, depends_on)
|
|
166
|
+
|
|
167
|
+
def sum(self):
|
|
168
|
+
data = self.data.sum()
|
|
169
|
+
requires_grad = self.requires_grad
|
|
170
|
+
depends_on = []
|
|
171
|
+
|
|
172
|
+
if self.requires_grad:
|
|
173
|
+
def backward_func(grad):
|
|
174
|
+
# Gradient of sum is just the gradient broadcasted to original shape
|
|
175
|
+
xp = get_array_module(self.data)
|
|
176
|
+
return grad * xp.ones_like(self.data)
|
|
177
|
+
depends_on.append({'tensor': self, 'backward_func': backward_func})
|
|
178
|
+
|
|
179
|
+
return Tensor(data, requires_grad, depends_on)
|
|
180
|
+
|
|
181
|
+
@property
|
|
182
|
+
def T(self):
|
|
183
|
+
data = self.data.T
|
|
184
|
+
requires_grad = self.requires_grad
|
|
185
|
+
depends_on = []
|
|
186
|
+
if self.requires_grad:
|
|
187
|
+
def backward_func(grad):
|
|
188
|
+
return grad.T
|
|
189
|
+
depends_on.append({'tensor': self, 'backward_func': backward_func})
|
|
190
|
+
return Tensor(data, requires_grad, depends_on)
|
|
191
|
+
|
|
192
|
+
def ensure_tensor(obj):
|
|
193
|
+
if isinstance(obj, Tensor):
|
|
194
|
+
return obj
|
|
195
|
+
return Tensor(obj)
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: heroch
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: A flexible deep learning library for machine learning and scientific computing with GPU acceleration.
|
|
5
|
+
Author-email: Death Legion Team <contact@deathlegion.team>
|
|
6
|
+
Project-URL: Homepage, https://github.com/death-legion-team/heroch
|
|
7
|
+
Project-URL: Bug Tracker, https://github.com/death-legion-team/heroch/issues
|
|
8
|
+
Keywords: machine learning,deep learning,tensors,autograd,neural networks,GPU acceleration,CUDA,scientific computing
|
|
9
|
+
Classifier: Programming Language :: Python :: 3
|
|
10
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
11
|
+
Classifier: Operating System :: OS Independent
|
|
12
|
+
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
13
|
+
Classifier: Intended Audience :: Science/Research
|
|
14
|
+
Classifier: Development Status :: 3 - Alpha
|
|
15
|
+
Requires-Python: >=3.8
|
|
16
|
+
Description-Content-Type: text/markdown
|
|
17
|
+
License-File: LICENSE
|
|
18
|
+
Requires-Dist: numpy>=1.20.0
|
|
19
|
+
Dynamic: license-file
|
|
20
|
+
|
|
21
|
+
# Heroch: High-Performance Deep Learning and Scientific Computing Library
|
|
22
|
+
|
|
23
|
+
[](https://badge.fury.io/py/heroch)
|
|
24
|
+
[](https://opensource.org/licenses/MIT)
|
|
25
|
+
|
|
26
|
+
**Heroch** is a powerful, flexible, and intuitive Python library designed for **machine learning**, **deep learning**, and **scientific computing**. It provides a high-performance tensor computation platform with seamless **GPU acceleration** (via CUDA/CuPy) and a dynamic **automatic differentiation** (autograd) engine.
|
|
27
|
+
|
|
28
|
+
## Why Heroch?
|
|
29
|
+
|
|
30
|
+
Heroch is built to feel like a native Python library, integrating perfectly with the SciPy ecosystem. Whether you are conducting academic research or building production-level neural networks, Heroch offers the flexibility to experiment and the speed to scale.
|
|
31
|
+
|
|
32
|
+
### Key Features
|
|
33
|
+
|
|
34
|
+
* **⚡ High-Performance Tensor Computations**: Native NumPy interface with optional GPU acceleration for massive speedups.
|
|
35
|
+
* **🧠 Dynamic Autograd Engine**: Flexible computational graphs that allow you to change network structures on the fly.
|
|
36
|
+
* **🛠️ Complete Deep Learning Platform**: Built-in modules for linear layers, common activations (ReLU, Sigmoid), and loss functions (MSELoss).
|
|
37
|
+
* **🚀 Optimized Training**: Robust optimizers including Stochastic Gradient Descent (SGD) with momentum.
|
|
38
|
+
* **🐍 Pythonic API**: Intuitive design that is easy to debug and executes code as you write it.
|
|
39
|
+
|
|
40
|
+
## Installation
|
|
41
|
+
|
|
42
|
+
Install Heroch easily via pip:
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
pip install heroch
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
*Note: For GPU support, ensure you have an NVIDIA GPU and the appropriate CUDA drivers installed.*
|
|
49
|
+
|
|
50
|
+
## Quick Start Example
|
|
51
|
+
|
|
52
|
+
Build and train a simple neural network in minutes:
|
|
53
|
+
|
|
54
|
+
```python
|
|
55
|
+
import heroch
|
|
56
|
+
import heroch.nn as nn
|
|
57
|
+
import heroch.optim as optim
|
|
58
|
+
from heroch import Tensor
|
|
59
|
+
|
|
60
|
+
# Define your flexible model structure
|
|
61
|
+
class MyNeuralNetwork(nn.Module):
|
|
62
|
+
def __init__(self):
|
|
63
|
+
super().__init__()
|
|
64
|
+
self.fc1 = nn.Linear(2, 4)
|
|
65
|
+
self.relu = nn.ReLU()
|
|
66
|
+
self.fc2 = nn.Linear(4, 1)
|
|
67
|
+
|
|
68
|
+
def forward(self, x):
|
|
69
|
+
x = self.relu(self.fc1(x))
|
|
70
|
+
return self.fc2(x)
|
|
71
|
+
|
|
72
|
+
# Initialize model, loss, and optimizer
|
|
73
|
+
model = MyNeuralNetwork()
|
|
74
|
+
criterion = nn.MSELoss()
|
|
75
|
+
optimizer = optim.SGD(model.parameters(), lr=0.01, momentum=0.9)
|
|
76
|
+
|
|
77
|
+
# Training data
|
|
78
|
+
inputs = Tensor([[1.0, 2.0], [3.0, 4.0]])
|
|
79
|
+
targets = Tensor([[5.0], [11.0]])
|
|
80
|
+
|
|
81
|
+
# Training loop
|
|
82
|
+
for epoch in range(100):
|
|
83
|
+
optimizer.zero_grad()
|
|
84
|
+
predictions = model(inputs)
|
|
85
|
+
loss = criterion(predictions, targets)
|
|
86
|
+
loss.backward()
|
|
87
|
+
optimizer.step()
|
|
88
|
+
|
|
89
|
+
if epoch % 10 == 0:
|
|
90
|
+
print(f"Epoch {epoch}, Loss: {loss.data}")
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
## Advanced Usage: GPU Acceleration
|
|
94
|
+
|
|
95
|
+
Heroch makes it easy to move your computations to the GPU:
|
|
96
|
+
|
|
97
|
+
```python
|
|
98
|
+
# Move model parameters and data to GPU
|
|
99
|
+
x_gpu = Tensor([1.0, 2.0, 3.0]).to_gpu()
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
## Contributing
|
|
103
|
+
|
|
104
|
+
We welcome contributions from the community! Check out our [GitHub repository](https://github.com/death-legion-team/heroch) to get involved.
|
|
105
|
+
|
|
106
|
+
---
|
|
107
|
+
Developed with 🔥 by **Death Legion Team**.
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
LICENSE
|
|
2
|
+
README.md
|
|
3
|
+
pyproject.toml
|
|
4
|
+
heroch/__init__.py
|
|
5
|
+
heroch/tensor.py
|
|
6
|
+
heroch.egg-info/PKG-INFO
|
|
7
|
+
heroch.egg-info/SOURCES.txt
|
|
8
|
+
heroch.egg-info/dependency_links.txt
|
|
9
|
+
heroch.egg-info/requires.txt
|
|
10
|
+
heroch.egg-info/top_level.txt
|
|
11
|
+
heroch/nn/__init__.py
|
|
12
|
+
heroch/nn/activations.py
|
|
13
|
+
heroch/nn/linear.py
|
|
14
|
+
heroch/nn/loss.py
|
|
15
|
+
heroch/nn/module.py
|
|
16
|
+
heroch/optim/__init__.py
|
|
17
|
+
heroch/optim/optimizer.py
|
|
18
|
+
heroch/optim/sgd.py
|
|
19
|
+
tests/test_heroch.py
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
numpy>=1.20.0
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
heroch
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=61.0", "wheel"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "heroch"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
authors = [
|
|
9
|
+
{ name="Death Legion Team", email="contact@deathlegion.team" },
|
|
10
|
+
]
|
|
11
|
+
description = "A flexible deep learning library for machine learning and scientific computing with GPU acceleration."
|
|
12
|
+
readme = "README.md"
|
|
13
|
+
requires-python = ">=3.8"
|
|
14
|
+
classifiers = [
|
|
15
|
+
"Programming Language :: Python :: 3",
|
|
16
|
+
"License :: OSI Approved :: MIT License",
|
|
17
|
+
"Operating System :: OS Independent",
|
|
18
|
+
"Topic :: Scientific/Engineering :: Artificial Intelligence",
|
|
19
|
+
"Intended Audience :: Science/Research",
|
|
20
|
+
"Development Status :: 3 - Alpha",
|
|
21
|
+
]
|
|
22
|
+
keywords = ["machine learning", "deep learning", "tensors", "autograd", "neural networks", "GPU acceleration", "CUDA", "scientific computing"]
|
|
23
|
+
dependencies = [
|
|
24
|
+
"numpy>=1.20.0",
|
|
25
|
+
]
|
|
26
|
+
|
|
27
|
+
[project.urls]
|
|
28
|
+
"Homepage" = "https://github.com/death-legion-team/heroch"
|
|
29
|
+
"Bug Tracker" = "https://github.com/death-legion-team/heroch/issues"
|
|
30
|
+
|
|
31
|
+
[tool.setuptools.packages.find]
|
|
32
|
+
where = ["."]
|
|
33
|
+
include = ["heroch*"]
|
heroch-0.1.0/setup.cfg
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import unittest
|
|
2
|
+
import numpy as np
|
|
3
|
+
from heroch import Tensor
|
|
4
|
+
from heroch.nn.linear import Linear
|
|
5
|
+
from heroch.nn.activations import ReLU, Sigmoid
|
|
6
|
+
from heroch.nn.loss import MSELoss
|
|
7
|
+
from heroch.optim.sgd import SGD
|
|
8
|
+
|
|
9
|
+
class TestHeroch(unittest.TestCase):
|
|
10
|
+
def test_tensor_ops(self):
|
|
11
|
+
a = Tensor([1, 2, 3])
|
|
12
|
+
b = Tensor([4, 5, 6])
|
|
13
|
+
c = a + b
|
|
14
|
+
np.testing.assert_array_equal(c.data, [5, 7, 9])
|
|
15
|
+
|
|
16
|
+
d = a * b
|
|
17
|
+
np.testing.assert_array_equal(d.data, [4, 10, 18])
|
|
18
|
+
|
|
19
|
+
e = a - b
|
|
20
|
+
np.testing.assert_array_equal(e.data, [-3, -3, -3])
|
|
21
|
+
|
|
22
|
+
def test_autograd_complex(self):
|
|
23
|
+
x = Tensor(2.0, requires_grad=True)
|
|
24
|
+
y = Tensor(3.0, requires_grad=True)
|
|
25
|
+
|
|
26
|
+
# f(x, y) = x^2 + 3xy
|
|
27
|
+
# df/dx = 2x + 3y = 4 + 9 = 13
|
|
28
|
+
# df/dy = 3x = 6
|
|
29
|
+
|
|
30
|
+
f = x * x + Tensor(3.0) * x * y
|
|
31
|
+
f.backward()
|
|
32
|
+
|
|
33
|
+
self.assertEqual(x.grad, 13.0)
|
|
34
|
+
self.assertEqual(y.grad, 6.0)
|
|
35
|
+
|
|
36
|
+
def test_linear_layer(self):
|
|
37
|
+
layer = Linear(3, 2)
|
|
38
|
+
x = Tensor([[1, 2, 3]], requires_grad=True)
|
|
39
|
+
out = layer(x)
|
|
40
|
+
self.assertEqual(out.shape, (1, 2))
|
|
41
|
+
|
|
42
|
+
out.sum().backward()
|
|
43
|
+
self.assertIsNotNone(layer.weight.grad)
|
|
44
|
+
self.assertIsNotNone(layer.bias.grad)
|
|
45
|
+
self.assertIsNotNone(x.grad)
|
|
46
|
+
|
|
47
|
+
def test_relu(self):
|
|
48
|
+
relu = ReLU()
|
|
49
|
+
x = Tensor([-1, 0, 1], requires_grad=True)
|
|
50
|
+
out = relu(x)
|
|
51
|
+
np.testing.assert_array_equal(out.data, [0, 0, 1])
|
|
52
|
+
|
|
53
|
+
out.sum().backward()
|
|
54
|
+
np.testing.assert_array_equal(x.grad, [0, 0, 1])
|
|
55
|
+
|
|
56
|
+
def test_sigmoid(self):
|
|
57
|
+
sigmoid = Sigmoid()
|
|
58
|
+
x = Tensor(0.0, requires_grad=True)
|
|
59
|
+
out = sigmoid(x)
|
|
60
|
+
self.assertAlmostEqual(out.data.item(), 0.5)
|
|
61
|
+
|
|
62
|
+
out.backward()
|
|
63
|
+
# dSigmoid(0)/dx = 0.5 * (1 - 0.5) = 0.25
|
|
64
|
+
self.assertAlmostEqual(x.grad.item(), 0.25)
|
|
65
|
+
|
|
66
|
+
if __name__ == '__main__':
|
|
67
|
+
unittest.main()
|