Skip to content

Commit 38ee6e2

Browse files
authored
Update multilayer_perceptron_classifier_from_scratch.py
1 parent ce9da8f commit 38ee6e2

File tree

1 file changed

+44
-22
lines changed

1 file changed

+44
-22
lines changed

machine_learning/multilayer_perceptron_classifier_from_scratch.py

Lines changed: 44 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,9 @@ def __init__(self, features: list[list[float]], labels: list[int]) -> None:
5151
self.y = np.array(labels)
5252
self.class_weights = {0: 1.0, 1: 1.0}
5353

54-
def get_train_test_data(self) -> tuple[list[np.ndarray], list[np.ndarray], list[np.ndarray], list[np.ndarray]]:
54+
def get_train_test_data(
55+
self
56+
) -> tuple[list[np.ndarray], list[np.ndarray], list[np.ndarray], list[np.ndarray]]:
5557
"""
5658
Splits the data into training and testing sets.
5759
Here, we manually split the data.
@@ -63,13 +65,17 @@ def get_train_test_data(self) -> tuple[list[np.ndarray], list[np.ndarray], list[
6365
- Test data
6466
- Test labels
6567
"""
66-
train_data = np.array([self.X[0], self.X[1], self.X[2]]) # First 3 samples for training
67-
train_labels = [np.array([self.y[0]]), np.array([self.y[1]]), np.array([self.y[2]])]
68+
train_data = np.array([self.X[0], self.X[1], self.X[2]])
69+
train_labels = \
70+
[np.array([self.y[0]]), np.array([self.y[1]]), np.array([self.y[2]])]
6871
test_data = np.array([self.X[3]]) # Last sample for testing
6972
test_labels = [np.array([self.y[3]])] # Labels as np.ndarray
7073
return train_data, train_labels, test_data, test_labels
7174

72-
def shuffle_data(self, paired_data: list[tuple[np.ndarray, int]]) -> list[tuple[np.ndarray, int]]:
75+
def shuffle_data(
76+
self,
77+
paired_data: list[tuple[np.ndarray, int]]
78+
) -> list[tuple[np.ndarray, int]]:
7379
"""
7480
Shuffles the data randomly.
7581
@@ -84,7 +90,8 @@ def shuffle_data(self, paired_data: list[tuple[np.ndarray, int]]) -> list[tuple[
8490
return paired_data
8591

8692
def get_inout_dim(self) -> tuple[int, int]:
87-
train_data, train_labels, test_data, test_labels = self.get_train_test_data()
93+
train_data, train_labels, test_data, test_labels = (
94+
self.get_train_test_data())
8895
in_dim = train_data[0].shape[0]
8996
out_dim = len(train_labels)
9097
return in_dim, out_dim
@@ -203,9 +210,11 @@ def initialize(self) -> tuple[np.ndarray, np.ndarray]:
203210
(2, 3)
204211
"""
205212

206-
in_dim, out_dim = self.dataloader.get_inout_dim() # in_dim here is image dim
207-
w1 = rng.standard_normal((in_dim + 1, self.hidden_dim)) * np.sqrt(2.0 / in_dim)
208-
w2 = rng.standard_normal((self.hidden_dim, out_dim)) * np.sqrt(2.0 / self.hidden_dim)
213+
in_dim, out_dim = self.dataloader.get_inout_dim()
214+
w1 = (rng.standard_normal((in_dim + 1, self.hidden_dim))
215+
* np.sqrt(2.0 / in_dim))
216+
w2 = (rng.standard_normal((self.hidden_dim, out_dim))
217+
* np.sqrt(2.0 / self.hidden_dim))
209218
return w1, w2
210219

211220
def relu(self, input_array: np.ndarray) -> np.ndarray:
@@ -256,7 +265,8 @@ def forward(
256265
no_gradient: If True, returns output without storing intermediates.
257266
258267
Returns:
259-
Output of the network after forward pass, shape (batch_size, output_dim).
268+
Output of the network after forward pass,
269+
shape (batch_size, output_dim).
260270
261271
Examples:
262272
>>> mlp = MLP(None, 1, 0.1, hidden_dim=2)
@@ -334,11 +344,11 @@ def back_prop(
334344

335345
grad_w2 = (
336346
np.dot(a1.T, delta_k) / batch_size
337-
) # (hidden, batch).dot(batch, output) = (hidden, output)
338-
input_data_flat = input_data.reshape(input_data.shape[0], -1) # (batch_size, input_dim)
347+
)
348+
input_data_flat = input_data.reshape(input_data.shape[0], -1)
339349
grad_w1 = (
340350
np.dot(input_data_flat.T, delta_j) / batch_size
341-
) # (input_dim, batch_size).dot(batch, hidden) = (input, hidden)
351+
)
342352

343353
return grad_w1, grad_w2
344354

@@ -351,11 +361,14 @@ def update_weights(
351361
learning_rate: float
352362
) -> tuple[np.ndarray, np.ndarray]:
353363
"""
354-
Updates the weight matrices using the computed gradients and learning rate.
364+
Updates the weight matrices using
365+
the computed gradients and learning rate.
355366
356367
Args:
357-
w1: Weight matrix for input to hidden layer, shape (input_dim + 1, hidden_dim).
358-
w2: Weight matrix for hidden to output layer, shape (hidden_dim, output_dim).
368+
w1: Weight matrix for input to hidden layer, shape
369+
(input_dim + 1, hidden_dim).
370+
w2: Weight matrix for hidden to output layer, shape
371+
(hidden_dim, output_dim).
359372
grad_w1: Gradient for w1, shape (input_dim + 1, hidden_dim).
360373
grad_w2: Gradient for w2, shape (hidden_dim, output_dim).
361374
learning_rate: Learning rate for weight updates.
@@ -405,7 +418,8 @@ def update_learning_rate(self, learning_rate: float) -> float:
405418
@staticmethod
406419
def accuracy(label: np.ndarray, y_hat: np.ndarray) -> float:
407420
"""
408-
Computes the accuracy of predictions by comparing predicted and true labels.
421+
Computes the accuracy of predictions
422+
by comparing predicted and true labels.
409423
410424
Args:
411425
label: True labels, shape (batch_size, num_classes).
@@ -426,7 +440,8 @@ def accuracy(label: np.ndarray, y_hat: np.ndarray) -> float:
426440
@staticmethod
427441
def loss(output: np.ndarray, label: np.ndarray) -> float:
428442
"""
429-
Computes the mean squared error loss between predictions and true labels.
443+
Computes the mean squared error loss
444+
between predictions and true labels.
430445
431446
Args:
432447
output: Predicted outputs, shape (batch_size, num_classes).
@@ -465,9 +480,11 @@ def get_acc_loss(self) -> tuple[list[float], list[float]]:
465480

466481
def train(self) -> None:
467482
"""
468-
Trains the MLP model using the provided dataloader for multiple folds and epochs.
483+
Trains the MLP model using the provided dataloader
484+
for multiple folds and epochs.
469485
470-
Saves the best model parameters for each fold and records accuracy/loss.
486+
Saves the best model parameters
487+
for each fold and records accuracy/loss.
471488
472489
Examples:
473490
>>> X = [[0.0, 0.0], [1.0, 1.0], [1.0, 0.0], [0.0, 1.0]]
@@ -479,7 +496,8 @@ def train(self) -> None:
479496
"""
480497

481498
learning_rate = self.learning_rate
482-
train_data, train_labels, test_data, test_labels = self.dataloader.get_train_test_data()
499+
train_data, train_labels, test_data, test_labels = (
500+
self.dataloader.get_train_test_data())
483501

484502
train_data = np.c_[train_data, np.ones(train_data.shape[0])]
485503
test_data = np.c_[test_data, np.ones(test_data.shape[0])]
@@ -498,12 +516,16 @@ def train(self) -> None:
498516
batch_size = 1
499517

500518
for j in tqdm(range(self.epoch)):
501-
for k in range(0, train_data.shape[0], batch_size): # retrieve every image
519+
for k in range(0, train_data.shape[0], batch_size):
502520

503521
batch_imgs = train_data[k: k + batch_size]
504522
batch_labels = train_labels[k: k + batch_size]
505523

506-
output = self.forward(input_data=batch_imgs, w1=w1, w2=w2, no_gradient=False)
524+
output = self.forward(
525+
input_data=batch_imgs,
526+
w1=w1,
527+
w2=w2,
528+
no_gradient=False)
507529

508530
grad_w1, grad_w2 = self.back_prop(
509531
input_data=batch_imgs,

0 commit comments

Comments
 (0)