neuralnetworknumpy 0.2.0__tar.gz → 0.2.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.
- {neuralnetworknumpy-0.2.0 → neuralnetworknumpy-0.2.1}/PKG-INFO +1 -1
- {neuralnetworknumpy-0.2.0 → neuralnetworknumpy-0.2.1}/neuralnetworknumpy/layers/BatchNorm.py +2 -2
- {neuralnetworknumpy-0.2.0 → neuralnetworknumpy-0.2.1}/neuralnetworknumpy/model.py +13 -14
- {neuralnetworknumpy-0.2.0 → neuralnetworknumpy-0.2.1}/neuralnetworknumpy/utils.py +31 -13
- {neuralnetworknumpy-0.2.0 → neuralnetworknumpy-0.2.1}/neuralnetworknumpy.egg-info/PKG-INFO +1 -1
- {neuralnetworknumpy-0.2.0 → neuralnetworknumpy-0.2.1}/pyproject.toml +1 -1
- {neuralnetworknumpy-0.2.0 → neuralnetworknumpy-0.2.1}/tests/test_model.py +2 -6
- {neuralnetworknumpy-0.2.0 → neuralnetworknumpy-0.2.1}/README.md +0 -0
- {neuralnetworknumpy-0.2.0 → neuralnetworknumpy-0.2.1}/neuralnetworknumpy/__init__.py +0 -0
- {neuralnetworknumpy-0.2.0 → neuralnetworknumpy-0.2.1}/neuralnetworknumpy/layers/Activation.py +0 -0
- {neuralnetworknumpy-0.2.0 → neuralnetworknumpy-0.2.1}/neuralnetworknumpy/layers/AveragePooling2D.py +0 -0
- {neuralnetworknumpy-0.2.0 → neuralnetworknumpy-0.2.1}/neuralnetworknumpy/layers/BatchNorm2D.py +0 -0
- {neuralnetworknumpy-0.2.0 → neuralnetworknumpy-0.2.1}/neuralnetworknumpy/layers/Conv2D.py +0 -0
- {neuralnetworknumpy-0.2.0 → neuralnetworknumpy-0.2.1}/neuralnetworknumpy/layers/Dense.py +0 -0
- {neuralnetworknumpy-0.2.0 → neuralnetworknumpy-0.2.1}/neuralnetworknumpy/layers/DepthwiseConv2D.py +0 -0
- {neuralnetworknumpy-0.2.0 → neuralnetworknumpy-0.2.1}/neuralnetworknumpy/layers/DepthwiseSeparableConv2D.py +0 -0
- {neuralnetworknumpy-0.2.0 → neuralnetworknumpy-0.2.1}/neuralnetworknumpy/layers/Dropout.py +0 -0
- {neuralnetworknumpy-0.2.0 → neuralnetworknumpy-0.2.1}/neuralnetworknumpy/layers/Flatten.py +0 -0
- {neuralnetworknumpy-0.2.0 → neuralnetworknumpy-0.2.1}/neuralnetworknumpy/layers/GlobalAveragePooling2D.py +0 -0
- {neuralnetworknumpy-0.2.0 → neuralnetworknumpy-0.2.1}/neuralnetworknumpy/layers/GroupConv2D.py +0 -0
- {neuralnetworknumpy-0.2.0 → neuralnetworknumpy-0.2.1}/neuralnetworknumpy/layers/Layer.py +0 -0
- {neuralnetworknumpy-0.2.0 → neuralnetworknumpy-0.2.1}/neuralnetworknumpy/layers/MaxPooling2D.py +0 -0
- {neuralnetworknumpy-0.2.0 → neuralnetworknumpy-0.2.1}/neuralnetworknumpy/layers/ResidualBlock.py +0 -0
- {neuralnetworknumpy-0.2.0 → neuralnetworknumpy-0.2.1}/neuralnetworknumpy/layers/SpatiallySeparableConv2D.py +0 -0
- {neuralnetworknumpy-0.2.0 → neuralnetworknumpy-0.2.1}/neuralnetworknumpy/layers/__init__.py +0 -0
- {neuralnetworknumpy-0.2.0 → neuralnetworknumpy-0.2.1}/neuralnetworknumpy.egg-info/SOURCES.txt +0 -0
- {neuralnetworknumpy-0.2.0 → neuralnetworknumpy-0.2.1}/neuralnetworknumpy.egg-info/dependency_links.txt +0 -0
- {neuralnetworknumpy-0.2.0 → neuralnetworknumpy-0.2.1}/neuralnetworknumpy.egg-info/requires.txt +0 -0
- {neuralnetworknumpy-0.2.0 → neuralnetworknumpy-0.2.1}/neuralnetworknumpy.egg-info/top_level.txt +0 -0
- {neuralnetworknumpy-0.2.0 → neuralnetworknumpy-0.2.1}/setup.cfg +0 -0
- {neuralnetworknumpy-0.2.0 → neuralnetworknumpy-0.2.1}/tests/test_load.py +0 -0
{neuralnetworknumpy-0.2.0 → neuralnetworknumpy-0.2.1}/neuralnetworknumpy/layers/BatchNorm.py
RENAMED
|
@@ -116,8 +116,8 @@ class BatchNorm(Layer):
|
|
|
116
116
|
# Mean derivative
|
|
117
117
|
# dmean = Σ (dX̂ * - (σ² + ε)^(1/2)) + dvar * Σ (-2 * (X - μ)) / m
|
|
118
118
|
dmean = (
|
|
119
|
-
np.sum(dX_hat * -var_inv, axis=(0,
|
|
120
|
-
+ dvar * np.sum(-2. * (self.A_prev - self.mean), axis=(0,
|
|
119
|
+
np.sum(dX_hat * -var_inv, axis=(0, ), keepdims=True)
|
|
120
|
+
+ dvar * np.sum(-2. * (self.A_prev - self.mean), axis=(0, ), keepdims=True) / m
|
|
121
121
|
)
|
|
122
122
|
|
|
123
123
|
# Total derivative
|
|
@@ -324,11 +324,11 @@ class NeuralNetwork:
|
|
|
324
324
|
|
|
325
325
|
|
|
326
326
|
# Backward function - locates the origin of the loss and tweaks it
|
|
327
|
-
def _backward(self, y_true):
|
|
327
|
+
def _backward(self, y_pred, y_true):
|
|
328
328
|
m = y_true.size
|
|
329
329
|
|
|
330
330
|
|
|
331
|
-
dA = self._loss_derivative(
|
|
331
|
+
dA = self._loss_derivative(y_pred, y_true) / m
|
|
332
332
|
dA = self.layers[-1]._backward(dA)
|
|
333
333
|
|
|
334
334
|
# Remaining layers
|
|
@@ -478,7 +478,7 @@ class NeuralNetwork:
|
|
|
478
478
|
@staticmethod
|
|
479
479
|
def shuffle_data(x, y):
|
|
480
480
|
perm = np.random.permutation(y.size)
|
|
481
|
-
x = x[perm]
|
|
481
|
+
x = x[perm]
|
|
482
482
|
y = y[perm]
|
|
483
483
|
return x, y
|
|
484
484
|
|
|
@@ -488,10 +488,10 @@ class NeuralNetwork:
|
|
|
488
488
|
|
|
489
489
|
|
|
490
490
|
def check_gradient(self, X, y):
|
|
491
|
-
assert X.shape[
|
|
491
|
+
assert X.shape[0] == y.size, f"X has {X.shape[0]} samples but y has {y.size}"
|
|
492
492
|
|
|
493
493
|
# Use a small batch to avoid numerical issues
|
|
494
|
-
X = X[
|
|
494
|
+
X = X[:8].astype(np.float64) # <-- float64 is critical for numerical grad
|
|
495
495
|
y = y[:8]
|
|
496
496
|
|
|
497
497
|
rel_diff = []
|
|
@@ -499,8 +499,8 @@ class NeuralNetwork:
|
|
|
499
499
|
self.lambda_ = 0.0
|
|
500
500
|
epsilon = 1e-5 # smaller epsilon can help
|
|
501
501
|
|
|
502
|
-
self._forward(X, training=False)
|
|
503
|
-
self._backward(y)
|
|
502
|
+
y_pred = self._forward(X, training=False)
|
|
503
|
+
self._backward(y_pred, y)
|
|
504
504
|
|
|
505
505
|
# Snapshot ALL analytical gradients before any weight perturbation
|
|
506
506
|
analytical_grads = {}
|
|
@@ -577,18 +577,17 @@ class NeuralNetwork:
|
|
|
577
577
|
optimizer_t += 1
|
|
578
578
|
|
|
579
579
|
# get batch
|
|
580
|
-
x_batch = x_shuffled[i:i + batch_size]
|
|
580
|
+
x_batch = x_shuffled[i:i + batch_size]
|
|
581
581
|
y_batch = y_shuffled[i:i + batch_size]
|
|
582
582
|
# feed model
|
|
583
|
-
self._forward(x_batch)
|
|
584
|
-
self._backward(y_batch)
|
|
583
|
+
y_pred = self._forward(x_batch)
|
|
584
|
+
self._backward(y_pred, y_batch)
|
|
585
585
|
self._update(optimizer_t)
|
|
586
586
|
|
|
587
|
-
y_pred = self.layers[-1].A
|
|
588
587
|
# Monitor loss - epoch_loss = Avg(batches_loss)
|
|
589
588
|
predictions.append(self._decode_output(y_pred))
|
|
590
589
|
batch_loss = self._compute_loss(y_pred, y_batch)
|
|
591
|
-
epoch_loss += batch_loss * n_samples / X.shape[
|
|
590
|
+
epoch_loss += batch_loss * n_samples / X.shape[0]
|
|
592
591
|
|
|
593
592
|
# Check Gradient - make sure backpropagation works well
|
|
594
593
|
#self.check_gradient(x_batch, y_batch)
|
|
@@ -644,8 +643,8 @@ class NeuralNetwork:
|
|
|
644
643
|
|
|
645
644
|
# Train the model
|
|
646
645
|
def fit(self, X, y, X_val=None, y_val=None, epochs=10, batch_size=1):
|
|
647
|
-
|
|
648
|
-
raise ValueError("Mismatch between samples and labels")
|
|
646
|
+
if X.shape[0] != y.size:
|
|
647
|
+
raise ValueError("Mismatch between samples and labels")
|
|
649
648
|
|
|
650
649
|
# Find last layer - last outsize is num classes
|
|
651
650
|
for layer in reversed(self.layers):
|
|
@@ -18,8 +18,12 @@ class History:
|
|
|
18
18
|
class Scaler:
|
|
19
19
|
def __init__(self, mode="standard"):
|
|
20
20
|
"""
|
|
21
|
-
mode: "standard"
|
|
22
|
-
"minmax"
|
|
21
|
+
mode: "standard" — Z-score normalisation (mean=0, std=1 per feature)
|
|
22
|
+
"minmax" — scales each feature to [0, 1]
|
|
23
|
+
|
|
24
|
+
Works with any row-major input shape:
|
|
25
|
+
(N, features) — dense / flattened data
|
|
26
|
+
(N, H, W, channels) — image data (statistics per pixel-channel)
|
|
23
27
|
"""
|
|
24
28
|
self.mode = mode
|
|
25
29
|
self.mean = None
|
|
@@ -28,15 +32,17 @@ class Scaler:
|
|
|
28
32
|
self.max = None
|
|
29
33
|
|
|
30
34
|
def fit(self, X):
|
|
31
|
-
"""
|
|
35
|
+
"""Compute statistics from X. Call only on training data."""
|
|
32
36
|
if self.mode == "standard":
|
|
33
|
-
|
|
34
|
-
|
|
37
|
+
# mean/std shape: (1, features) or (1, H, W, C)
|
|
38
|
+
# keepdims=True lets them broadcast against any (N, ...) input
|
|
39
|
+
self.mean = np.mean(X, axis=0, keepdims=True)
|
|
40
|
+
self.std = np.std(X, axis=0, keepdims=True)
|
|
35
41
|
self.std[self.std == 0] = 1e-8 # Avoid division by zero
|
|
36
42
|
|
|
37
43
|
elif self.mode == "minmax":
|
|
38
|
-
self.min = np.min(X, axis=
|
|
39
|
-
self.max = np.max(X, axis=
|
|
44
|
+
self.min = np.min(X, axis=0, keepdims=True)
|
|
45
|
+
self.max = np.max(X, axis=0, keepdims=True)
|
|
40
46
|
# Avoid division by zero if all values in a feature are the same
|
|
41
47
|
self.diff = self.max - self.min
|
|
42
48
|
self.diff[self.diff == 0] = 1e-8
|
|
@@ -47,40 +53,52 @@ class Scaler:
|
|
|
47
53
|
return (X - self.mean) / self.std
|
|
48
54
|
elif self.mode == "minmax":
|
|
49
55
|
return (X - self.min) / self.diff
|
|
50
|
-
|
|
51
|
-
raise NotImplementedError
|
|
56
|
+
raise NotImplementedError(f"Unknown mode: {self.mode}")
|
|
52
57
|
|
|
53
58
|
def fit_transform(self, X):
|
|
59
|
+
"""Fit on X, then transform and return it. Use only on training data."""
|
|
54
60
|
self.fit(X)
|
|
55
61
|
return self.transform(X)
|
|
56
62
|
|
|
57
63
|
def split_train_test(X, y, test_ratio=0.2):
|
|
58
|
-
|
|
64
|
+
"""
|
|
65
|
+
Randomly split (X, y) into train and test sets.
|
|
66
|
+
X shape: (N, ...) - any row-major layout.
|
|
67
|
+
Returns: X_train, y_train, X_test, y_test
|
|
68
|
+
"""
|
|
69
|
+
m = X.shape[0]
|
|
59
70
|
perm = np.random.permutation(m)
|
|
60
71
|
|
|
61
|
-
X = X[
|
|
72
|
+
X = X[perm]
|
|
62
73
|
y = y[perm]
|
|
63
74
|
|
|
64
75
|
test_size = int(m * test_ratio)
|
|
65
76
|
|
|
66
|
-
X_test = X[
|
|
77
|
+
X_test = X[:test_size]
|
|
67
78
|
y_test = y[:test_size]
|
|
68
79
|
|
|
69
|
-
X_train = X[
|
|
80
|
+
X_train = X[test_size:]
|
|
70
81
|
y_train = y[test_size:]
|
|
71
82
|
|
|
72
83
|
return X_train, y_train, X_test, y_test
|
|
73
84
|
|
|
74
85
|
|
|
75
86
|
def split_train_validation(X, y, val_ratio=0.2):
|
|
87
|
+
"""
|
|
88
|
+
Randomly split (X, y) into train and validation sets.
|
|
89
|
+
X shape: (N, ...) - any row-major layout.
|
|
90
|
+
Returns: X_train, y_train, X_val, y_val
|
|
91
|
+
"""
|
|
76
92
|
m = X.shape[0]
|
|
77
93
|
perm = np.random.permutation(m)
|
|
78
94
|
X = X[perm]
|
|
79
95
|
y = y[perm]
|
|
80
96
|
|
|
81
97
|
val_size = int(m * val_ratio)
|
|
98
|
+
|
|
82
99
|
X_val = X[:val_size]
|
|
83
100
|
y_val = y[:val_size]
|
|
101
|
+
|
|
84
102
|
X_train = X[val_size:]
|
|
85
103
|
y_train = y[val_size:]
|
|
86
104
|
|
|
@@ -35,10 +35,6 @@ test_X = scaler.fit_transform(test_X)
|
|
|
35
35
|
# Split into training and validation
|
|
36
36
|
train_X, train_y, val_X, val_y = split_train_validation(train_X, train_y, 0.1)
|
|
37
37
|
|
|
38
|
-
# Transpose to match network input (features, samples)
|
|
39
|
-
train_X = train_X.T # (784, 54000)
|
|
40
|
-
val_X = val_X.T # (784, 6000)
|
|
41
|
-
test_X = test_X.T # (784, 10000)
|
|
42
38
|
|
|
43
39
|
print("Train X:", train_X.shape, "Train y:", train_y.shape)
|
|
44
40
|
print("Validation X:", val_X.shape, "Validation y:", val_y.shape)
|
|
@@ -54,9 +50,9 @@ model.save("model.h5")
|
|
|
54
50
|
print(f"Training time: {time.time() - start_time}")
|
|
55
51
|
|
|
56
52
|
|
|
57
|
-
idx = np.random.randint(test_X.shape[
|
|
53
|
+
idx = np.random.randint(test_X.shape[0]) # random sample index
|
|
58
54
|
|
|
59
|
-
x = test_X[
|
|
55
|
+
x = test_X[idx:idx+1] # shape (1, 784)
|
|
60
56
|
y_true = test_y[idx]
|
|
61
57
|
|
|
62
58
|
y_pred = model.predict(x)
|
|
File without changes
|
|
File without changes
|
{neuralnetworknumpy-0.2.0 → neuralnetworknumpy-0.2.1}/neuralnetworknumpy/layers/Activation.py
RENAMED
|
File without changes
|
{neuralnetworknumpy-0.2.0 → neuralnetworknumpy-0.2.1}/neuralnetworknumpy/layers/AveragePooling2D.py
RENAMED
|
File without changes
|
{neuralnetworknumpy-0.2.0 → neuralnetworknumpy-0.2.1}/neuralnetworknumpy/layers/BatchNorm2D.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{neuralnetworknumpy-0.2.0 → neuralnetworknumpy-0.2.1}/neuralnetworknumpy/layers/DepthwiseConv2D.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{neuralnetworknumpy-0.2.0 → neuralnetworknumpy-0.2.1}/neuralnetworknumpy/layers/GroupConv2D.py
RENAMED
|
File without changes
|
|
File without changes
|
{neuralnetworknumpy-0.2.0 → neuralnetworknumpy-0.2.1}/neuralnetworknumpy/layers/MaxPooling2D.py
RENAMED
|
File without changes
|
{neuralnetworknumpy-0.2.0 → neuralnetworknumpy-0.2.1}/neuralnetworknumpy/layers/ResidualBlock.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{neuralnetworknumpy-0.2.0 → neuralnetworknumpy-0.2.1}/neuralnetworknumpy.egg-info/SOURCES.txt
RENAMED
|
File without changes
|
|
File without changes
|
{neuralnetworknumpy-0.2.0 → neuralnetworknumpy-0.2.1}/neuralnetworknumpy.egg-info/requires.txt
RENAMED
|
File without changes
|
{neuralnetworknumpy-0.2.0 → neuralnetworknumpy-0.2.1}/neuralnetworknumpy.egg-info/top_level.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|