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.
Files changed (31) hide show
  1. {neuralnetworknumpy-0.2.0 → neuralnetworknumpy-0.2.1}/PKG-INFO +1 -1
  2. {neuralnetworknumpy-0.2.0 → neuralnetworknumpy-0.2.1}/neuralnetworknumpy/layers/BatchNorm.py +2 -2
  3. {neuralnetworknumpy-0.2.0 → neuralnetworknumpy-0.2.1}/neuralnetworknumpy/model.py +13 -14
  4. {neuralnetworknumpy-0.2.0 → neuralnetworknumpy-0.2.1}/neuralnetworknumpy/utils.py +31 -13
  5. {neuralnetworknumpy-0.2.0 → neuralnetworknumpy-0.2.1}/neuralnetworknumpy.egg-info/PKG-INFO +1 -1
  6. {neuralnetworknumpy-0.2.0 → neuralnetworknumpy-0.2.1}/pyproject.toml +1 -1
  7. {neuralnetworknumpy-0.2.0 → neuralnetworknumpy-0.2.1}/tests/test_model.py +2 -6
  8. {neuralnetworknumpy-0.2.0 → neuralnetworknumpy-0.2.1}/README.md +0 -0
  9. {neuralnetworknumpy-0.2.0 → neuralnetworknumpy-0.2.1}/neuralnetworknumpy/__init__.py +0 -0
  10. {neuralnetworknumpy-0.2.0 → neuralnetworknumpy-0.2.1}/neuralnetworknumpy/layers/Activation.py +0 -0
  11. {neuralnetworknumpy-0.2.0 → neuralnetworknumpy-0.2.1}/neuralnetworknumpy/layers/AveragePooling2D.py +0 -0
  12. {neuralnetworknumpy-0.2.0 → neuralnetworknumpy-0.2.1}/neuralnetworknumpy/layers/BatchNorm2D.py +0 -0
  13. {neuralnetworknumpy-0.2.0 → neuralnetworknumpy-0.2.1}/neuralnetworknumpy/layers/Conv2D.py +0 -0
  14. {neuralnetworknumpy-0.2.0 → neuralnetworknumpy-0.2.1}/neuralnetworknumpy/layers/Dense.py +0 -0
  15. {neuralnetworknumpy-0.2.0 → neuralnetworknumpy-0.2.1}/neuralnetworknumpy/layers/DepthwiseConv2D.py +0 -0
  16. {neuralnetworknumpy-0.2.0 → neuralnetworknumpy-0.2.1}/neuralnetworknumpy/layers/DepthwiseSeparableConv2D.py +0 -0
  17. {neuralnetworknumpy-0.2.0 → neuralnetworknumpy-0.2.1}/neuralnetworknumpy/layers/Dropout.py +0 -0
  18. {neuralnetworknumpy-0.2.0 → neuralnetworknumpy-0.2.1}/neuralnetworknumpy/layers/Flatten.py +0 -0
  19. {neuralnetworknumpy-0.2.0 → neuralnetworknumpy-0.2.1}/neuralnetworknumpy/layers/GlobalAveragePooling2D.py +0 -0
  20. {neuralnetworknumpy-0.2.0 → neuralnetworknumpy-0.2.1}/neuralnetworknumpy/layers/GroupConv2D.py +0 -0
  21. {neuralnetworknumpy-0.2.0 → neuralnetworknumpy-0.2.1}/neuralnetworknumpy/layers/Layer.py +0 -0
  22. {neuralnetworknumpy-0.2.0 → neuralnetworknumpy-0.2.1}/neuralnetworknumpy/layers/MaxPooling2D.py +0 -0
  23. {neuralnetworknumpy-0.2.0 → neuralnetworknumpy-0.2.1}/neuralnetworknumpy/layers/ResidualBlock.py +0 -0
  24. {neuralnetworknumpy-0.2.0 → neuralnetworknumpy-0.2.1}/neuralnetworknumpy/layers/SpatiallySeparableConv2D.py +0 -0
  25. {neuralnetworknumpy-0.2.0 → neuralnetworknumpy-0.2.1}/neuralnetworknumpy/layers/__init__.py +0 -0
  26. {neuralnetworknumpy-0.2.0 → neuralnetworknumpy-0.2.1}/neuralnetworknumpy.egg-info/SOURCES.txt +0 -0
  27. {neuralnetworknumpy-0.2.0 → neuralnetworknumpy-0.2.1}/neuralnetworknumpy.egg-info/dependency_links.txt +0 -0
  28. {neuralnetworknumpy-0.2.0 → neuralnetworknumpy-0.2.1}/neuralnetworknumpy.egg-info/requires.txt +0 -0
  29. {neuralnetworknumpy-0.2.0 → neuralnetworknumpy-0.2.1}/neuralnetworknumpy.egg-info/top_level.txt +0 -0
  30. {neuralnetworknumpy-0.2.0 → neuralnetworknumpy-0.2.1}/setup.cfg +0 -0
  31. {neuralnetworknumpy-0.2.0 → neuralnetworknumpy-0.2.1}/tests/test_load.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: neuralnetworknumpy
3
- Version: 0.2.0
3
+ Version: 0.2.1
4
4
  Summary: A neural network framework built completely from scratch using NumPy
5
5
  Author: Itamar Senderovitz
6
6
  License: MIT
@@ -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, 1, 2), keepdims=True)
120
- + dvar * np.sum(-2. * (self.A_prev - self.mean), axis=(0, 1, 2), keepdims=True) / m
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(self.layers[-1].A, y_true) / m
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] if x.ndim > 2 else 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[1] == y.size, f"X has {X.shape[1]} samples but y has {y.size}"
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[:, :8].astype(np.float64) # <-- float64 is critical for numerical grad
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] if X.ndim > 2 else 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[1]
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
- """if X.shape[1] != y.size:
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" for Z-score (Mean=0, Std=1)
22
- "minmax" for range scaling (0 to 1)
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
- """Calculates parameters from training data. X shape: (features, samples)"""
35
+ """Compute statistics from X. Call only on training data."""
32
36
  if self.mode == "standard":
33
- self.mean = np.mean(X, axis=1, keepdims=True)
34
- self.std = np.std(X, axis=1, keepdims=True)
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=1, keepdims=True)
39
- self.max = np.max(X, axis=1, keepdims=True)
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
- else:
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
- m = X.shape[1]
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[:, perm]
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[:, :test_size]
77
+ X_test = X[:test_size]
67
78
  y_test = y[:test_size]
68
79
 
69
- X_train = X[:, test_size:]
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
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: neuralnetworknumpy
3
- Version: 0.2.0
3
+ Version: 0.2.1
4
4
  Summary: A neural network framework built completely from scratch using NumPy
5
5
  Author: Itamar Senderovitz
6
6
  License: MIT
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "neuralnetworknumpy"
7
- version = "0.2.0"
7
+ version = "0.2.1"
8
8
  description = "A neural network framework built completely from scratch using NumPy"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.8"
@@ -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[1]) # random sample index
53
+ idx = np.random.randint(test_X.shape[0]) # random sample index
58
54
 
59
- x = test_X[:, idx:idx+1] # shape (784, 1)
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)