neuralplex 0.3.0__tar.gz → 0.3.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.
- {neuralplex-0.3.0 → neuralplex-0.3.1}/PKG-INFO +30 -7
- {neuralplex-0.3.0 → neuralplex-0.3.1}/README.md +29 -6
- neuralplex-0.3.1/src/neuralplex/__about__.py +1 -0
- {neuralplex-0.3.0 → neuralplex-0.3.1}/src/neuralplex/layer.py +3 -2
- {neuralplex-0.3.0 → neuralplex-0.3.1}/src/neuralplex/network.py +3 -2
- {neuralplex-0.3.0 → neuralplex-0.3.1}/src/neuralplex/neuron.py +3 -3
- {neuralplex-0.3.0 → neuralplex-0.3.1}/tests/test.py +16 -0
- neuralplex-0.3.0/src/neuralplex/__about__.py +0 -1
- {neuralplex-0.3.0 → neuralplex-0.3.1}/.gitignore +0 -0
- {neuralplex-0.3.0 → neuralplex-0.3.1}/LICENSE +0 -0
- {neuralplex-0.3.0 → neuralplex-0.3.1}/docs/README.md +0 -0
- {neuralplex-0.3.0 → neuralplex-0.3.1}/docs/generate_images.py +0 -0
- {neuralplex-0.3.0 → neuralplex-0.3.1}/docs/pyproject.toml +0 -0
- {neuralplex-0.3.0 → neuralplex-0.3.1}/examples/the_digits_experiment/README.md +0 -0
- {neuralplex-0.3.0 → neuralplex-0.3.1}/examples/the_digits_experiment/main.ipynb +0 -0
- {neuralplex-0.3.0 → neuralplex-0.3.1}/examples/the_digits_experiment/pyproject.toml +0 -0
- {neuralplex-0.3.0 → neuralplex-0.3.1}/examples/the_digits_experiment/visualizations.py +0 -0
- {neuralplex-0.3.0 → neuralplex-0.3.1}/examples/the_nibble_challenge/README.md +0 -0
- {neuralplex-0.3.0 → neuralplex-0.3.1}/examples/the_nibble_challenge/main.ipynb +0 -0
- {neuralplex-0.3.0 → neuralplex-0.3.1}/examples/the_nibble_challenge/pyproject.toml +0 -0
- {neuralplex-0.3.0 → neuralplex-0.3.1}/examples/the_nibble_challenge/visualizations.py +0 -0
- {neuralplex-0.3.0 → neuralplex-0.3.1}/images/digits_confusion_matrix.png +0 -0
- {neuralplex-0.3.0 → neuralplex-0.3.1}/images/digits_predictions.png +0 -0
- {neuralplex-0.3.0 → neuralplex-0.3.1}/images/nibble_multiweight.png +0 -0
- {neuralplex-0.3.0 → neuralplex-0.3.1}/pyproject.toml +0 -0
- {neuralplex-0.3.0 → neuralplex-0.3.1}/src/neuralplex/__init__.py +0 -0
- {neuralplex-0.3.0 → neuralplex-0.3.1}/tests/__init__.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: neuralplex
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.1
|
|
4
4
|
Summary: An object oriented neural network implementation.
|
|
5
5
|
Project-URL: Homepage, https://github.com/adamjpatterson/neuralplex
|
|
6
6
|
Project-URL: Issues, https://github.com/adamjpatterson/neuralplex/issues
|
|
@@ -36,13 +36,23 @@ An object oriented educational/experimental neural network implementation.
|
|
|
36
36
|
|
|
37
37
|
## Installation
|
|
38
38
|
|
|
39
|
+
To use the published package:
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
python -m pip install neuralplex
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
To explore the notebooks or modify the package, clone the repository and install
|
|
46
|
+
the checkout:
|
|
47
|
+
|
|
39
48
|
```bash
|
|
40
49
|
git clone https://github.com/adamjpatterson/neuralplex.git
|
|
41
50
|
cd neuralplex
|
|
42
51
|
python -m pip install -e .
|
|
43
52
|
```
|
|
44
53
|
|
|
45
|
-
|
|
54
|
+
Follow each example’s setup instructions below to install its notebook and
|
|
55
|
+
visualization dependencies.
|
|
46
56
|
|
|
47
57
|
## How it works
|
|
48
58
|
|
|
@@ -58,6 +68,12 @@ from neuralplex import Network, Layer, Neuron
|
|
|
58
68
|
|
|
59
69
|
#### Set a step.
|
|
60
70
|
|
|
71
|
+
`step` is the learning rate: it controls the size of weight and bias updates.
|
|
72
|
+
Each update subtracts `step` times the corresponding gradient.
|
|
73
|
+
`Layer(step=...)` supplies an initial default only to neurons whose `step` is
|
|
74
|
+
`None`. Changing `layer.step` later does not change the neurons; update each
|
|
75
|
+
`neuron.step` directly to change its learning rate.
|
|
76
|
+
|
|
61
77
|
```python
|
|
62
78
|
STEP = 0.01
|
|
63
79
|
```
|
|
@@ -97,9 +113,11 @@ hidden layers. Linear layers are sufficient for the nibble conversion task.
|
|
|
97
113
|
|
|
98
114
|
`train` minimizes half the sum of squared output errors. It runs the forward
|
|
99
115
|
pass, accumulates gradients backward through every connection, and only then
|
|
100
|
-
updates weights and biases.
|
|
116
|
+
updates weights and biases. `delta` is the derivative of the loss with respect
|
|
117
|
+
to the neuron's weighted sum plus bias, before activation.
|
|
118
|
+
A weight's gradient is `delta * source.value`;
|
|
101
119
|
the bias gradient is `delta`. The code uses explicit loops, not automatic
|
|
102
|
-
differentiation. Every non-input neuron needs a positive step.
|
|
120
|
+
differentiation. Every non-input neuron needs a finite, positive `step` for training.
|
|
103
121
|
|
|
104
122
|
`Neuron(bias=...)` sets the bias. Each incoming connection has its own weight,
|
|
105
123
|
stored in `receiver.weights[source]`. `Network(..., seed=42)` initializes
|
|
@@ -110,6 +128,11 @@ weights. Visualization modules read connections directly from
|
|
|
110
128
|
|
|
111
129
|
### Detach and reconnect between training steps
|
|
112
130
|
|
|
131
|
+
You can change an existing connection weight through `receiver.weights[source]`.
|
|
132
|
+
To add or remove connections, use `connectRHS`/`connectLHS` and
|
|
133
|
+
`disconnectRHS`/`disconnectLHS` rather than inserting or deleting dictionary
|
|
134
|
+
entries. These methods keep incoming weights and outgoing connections consistent.
|
|
135
|
+
|
|
113
136
|
```python
|
|
114
137
|
hidden = n1.layers[1].neurons[0]
|
|
115
138
|
incoming = dict(hidden.weights)
|
|
@@ -126,9 +149,9 @@ for target, weight in outgoing.items():
|
|
|
126
149
|
|
|
127
150
|
The neuron's bias, activation, and learning rate remain on the object. For one
|
|
128
151
|
edge, use `source.disconnectRHS(target)` and `source.connectRHS(target, weight)`.
|
|
129
|
-
Removing an edge does not cascade. A
|
|
130
|
-
|
|
131
|
-
run from earlier to later layers; input/output neurons cannot be detached.
|
|
152
|
+
Removing an edge does not cascade. A non-input neuron without incoming
|
|
153
|
+
connections outputs `activation(bias)`; an empty hidden layer left by detachment
|
|
154
|
+
is allowed. Connections must run from earlier to later layers; input/output neurons cannot be detached.
|
|
132
155
|
Change structure only between training steps, not concurrently with training.
|
|
133
156
|
|
|
134
157
|
## Examples
|
|
@@ -22,13 +22,23 @@ An object oriented educational/experimental neural network implementation.
|
|
|
22
22
|
|
|
23
23
|
## Installation
|
|
24
24
|
|
|
25
|
+
To use the published package:
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
python -m pip install neuralplex
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
To explore the notebooks or modify the package, clone the repository and install
|
|
32
|
+
the checkout:
|
|
33
|
+
|
|
25
34
|
```bash
|
|
26
35
|
git clone https://github.com/adamjpatterson/neuralplex.git
|
|
27
36
|
cd neuralplex
|
|
28
37
|
python -m pip install -e .
|
|
29
38
|
```
|
|
30
39
|
|
|
31
|
-
|
|
40
|
+
Follow each example’s setup instructions below to install its notebook and
|
|
41
|
+
visualization dependencies.
|
|
32
42
|
|
|
33
43
|
## How it works
|
|
34
44
|
|
|
@@ -44,6 +54,12 @@ from neuralplex import Network, Layer, Neuron
|
|
|
44
54
|
|
|
45
55
|
#### Set a step.
|
|
46
56
|
|
|
57
|
+
`step` is the learning rate: it controls the size of weight and bias updates.
|
|
58
|
+
Each update subtracts `step` times the corresponding gradient.
|
|
59
|
+
`Layer(step=...)` supplies an initial default only to neurons whose `step` is
|
|
60
|
+
`None`. Changing `layer.step` later does not change the neurons; update each
|
|
61
|
+
`neuron.step` directly to change its learning rate.
|
|
62
|
+
|
|
47
63
|
```python
|
|
48
64
|
STEP = 0.01
|
|
49
65
|
```
|
|
@@ -83,9 +99,11 @@ hidden layers. Linear layers are sufficient for the nibble conversion task.
|
|
|
83
99
|
|
|
84
100
|
`train` minimizes half the sum of squared output errors. It runs the forward
|
|
85
101
|
pass, accumulates gradients backward through every connection, and only then
|
|
86
|
-
updates weights and biases.
|
|
102
|
+
updates weights and biases. `delta` is the derivative of the loss with respect
|
|
103
|
+
to the neuron's weighted sum plus bias, before activation.
|
|
104
|
+
A weight's gradient is `delta * source.value`;
|
|
87
105
|
the bias gradient is `delta`. The code uses explicit loops, not automatic
|
|
88
|
-
differentiation. Every non-input neuron needs a positive step.
|
|
106
|
+
differentiation. Every non-input neuron needs a finite, positive `step` for training.
|
|
89
107
|
|
|
90
108
|
`Neuron(bias=...)` sets the bias. Each incoming connection has its own weight,
|
|
91
109
|
stored in `receiver.weights[source]`. `Network(..., seed=42)` initializes
|
|
@@ -96,6 +114,11 @@ weights. Visualization modules read connections directly from
|
|
|
96
114
|
|
|
97
115
|
### Detach and reconnect between training steps
|
|
98
116
|
|
|
117
|
+
You can change an existing connection weight through `receiver.weights[source]`.
|
|
118
|
+
To add or remove connections, use `connectRHS`/`connectLHS` and
|
|
119
|
+
`disconnectRHS`/`disconnectLHS` rather than inserting or deleting dictionary
|
|
120
|
+
entries. These methods keep incoming weights and outgoing connections consistent.
|
|
121
|
+
|
|
99
122
|
```python
|
|
100
123
|
hidden = n1.layers[1].neurons[0]
|
|
101
124
|
incoming = dict(hidden.weights)
|
|
@@ -112,9 +135,9 @@ for target, weight in outgoing.items():
|
|
|
112
135
|
|
|
113
136
|
The neuron's bias, activation, and learning rate remain on the object. For one
|
|
114
137
|
edge, use `source.disconnectRHS(target)` and `source.connectRHS(target, weight)`.
|
|
115
|
-
Removing an edge does not cascade. A
|
|
116
|
-
|
|
117
|
-
run from earlier to later layers; input/output neurons cannot be detached.
|
|
138
|
+
Removing an edge does not cascade. A non-input neuron without incoming
|
|
139
|
+
connections outputs `activation(bias)`; an empty hidden layer left by detachment
|
|
140
|
+
is allowed. Connections must run from earlier to later layers; input/output neurons cannot be detached.
|
|
118
141
|
Change structure only between training steps, not concurrently with training.
|
|
119
142
|
|
|
120
143
|
## Examples
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "0.3.1"
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
from math import isfinite
|
|
1
2
|
from typing import Self
|
|
2
3
|
from random import Random
|
|
3
4
|
|
|
@@ -6,8 +7,8 @@ from neuralplex.neuron import Neuron
|
|
|
6
7
|
|
|
7
8
|
class Layer:
|
|
8
9
|
def __init__(self, neurons: list[Neuron], step: float | None = None):
|
|
9
|
-
if step is not None and step <= 0:
|
|
10
|
-
raise ValueError("step must be positive")
|
|
10
|
+
if step is not None and (not isfinite(step) or step <= 0):
|
|
11
|
+
raise ValueError("step must be finite and positive")
|
|
11
12
|
self.neurons = neurons
|
|
12
13
|
self.step = step
|
|
13
14
|
for neuron in neurons:
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
from math import isfinite
|
|
1
2
|
from random import Random
|
|
2
3
|
|
|
3
4
|
from neuralplex.layer import Layer
|
|
@@ -48,8 +49,8 @@ class Network:
|
|
|
48
49
|
raise ValueError("Supply one target value per output neuron")
|
|
49
50
|
for layer in self.layers[1:]:
|
|
50
51
|
for neuron in layer.neurons:
|
|
51
|
-
if neuron.step is None or neuron.step <= 0:
|
|
52
|
-
raise ValueError("Every computational neuron needs a positive step")
|
|
52
|
+
if neuron.step is None or not isfinite(neuron.step) or neuron.step <= 0:
|
|
53
|
+
raise ValueError("Every computational neuron needs a finite, positive step")
|
|
53
54
|
self.predict(X_train)
|
|
54
55
|
|
|
55
56
|
# Clear all gradients, including those accumulated at input neurons.
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
from math import tanh
|
|
1
|
+
from math import isfinite, tanh
|
|
2
2
|
from random import uniform
|
|
3
3
|
from typing import Self
|
|
4
4
|
|
|
@@ -10,8 +10,8 @@ class Neuron:
|
|
|
10
10
|
name=None, activation: str = "linear"):
|
|
11
11
|
if activation not in ("linear", "tanh"):
|
|
12
12
|
raise ValueError("activation must be 'linear' or 'tanh'")
|
|
13
|
-
if step is not None and step <= 0:
|
|
14
|
-
raise ValueError("step must be positive")
|
|
13
|
+
if step is not None and (not isfinite(step) or step <= 0):
|
|
14
|
+
raise ValueError("step must be finite and positive")
|
|
15
15
|
self.bias = bias
|
|
16
16
|
self.step = step
|
|
17
17
|
self.name = name
|
|
@@ -89,6 +89,22 @@ class TestCorrectness(unittest.TestCase):
|
|
|
89
89
|
with self.assertRaises(ValueError):
|
|
90
90
|
model.predict([1, 2, 3])
|
|
91
91
|
|
|
92
|
+
def test_invalid_learning_rates(self):
|
|
93
|
+
for step in [0, -0.1, float('nan'), float('inf'), float('-inf')]:
|
|
94
|
+
with self.subTest(step=step):
|
|
95
|
+
with self.assertRaises(ValueError):
|
|
96
|
+
Neuron(step=step)
|
|
97
|
+
with self.assertRaises(ValueError):
|
|
98
|
+
Layer([Neuron()], step=step)
|
|
99
|
+
|
|
100
|
+
model = Network([Layer([Neuron()]), Layer([Neuron()], step=0.1)], seed=42)
|
|
101
|
+
output = model.output_layer.neurons[0]
|
|
102
|
+
before = (dict(output.weights), output.bias)
|
|
103
|
+
output.step = step # Manual changes must also be checked before training.
|
|
104
|
+
with self.assertRaises(ValueError):
|
|
105
|
+
model.train([1], [1])
|
|
106
|
+
self.assertEqual((output.weights, output.bias), before)
|
|
107
|
+
|
|
92
108
|
def test_missing_step_and_external_connections(self):
|
|
93
109
|
a, b, outside = Neuron(), Neuron(), Neuron()
|
|
94
110
|
model = Network([Layer([a]), Layer([b])])
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
__version__ = "0.3.0"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|