Skip to content

Commit d02e27b

Browse files
[pre-commit.ci] auto fixes from pre-commit.com hooks
for more information, see https://pre-commit.ci
1 parent 73b9f07 commit d02e27b

1 file changed

Lines changed: 68 additions & 68 deletions

File tree

machine_learning/multilayer_perceptron_classifier_from_scratch.py

Lines changed: 68 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -38,8 +38,9 @@ def __init__(self, features: list[list[float]], labels: list[int]) -> None:
3838
self.y = np.array(labels)
3939
self.class_weights = {0: 1.0, 1: 1.0} # Example class weights, adjust as needed
4040

41-
def get_train_test_data(self) -> tuple[list[np.ndarray],
42-
list[np.ndarray], list[np.ndarray], list[np.ndarray]]:
41+
def get_train_test_data(
42+
self,
43+
) -> tuple[list[np.ndarray], list[np.ndarray], list[np.ndarray], list[np.ndarray]]:
4344
"""
4445
Splits the data into training and testing sets.
4546
Here, we manually split the data.
@@ -52,16 +53,18 @@ def get_train_test_data(self) -> tuple[list[np.ndarray],
5253
- Test labels
5354
"""
5455
train_data = np.array([self.X[0], self.X[1], self.X[2]])
55-
train_labels = [np.array([self.y[0]]), np.array([self.y[1]]),
56-
np.array([self.y[2]])]
56+
train_labels = [
57+
np.array([self.y[0]]),
58+
np.array([self.y[1]]),
59+
np.array([self.y[2]]),
60+
]
5761
test_data = np.array([self.X[3]])
5862
test_labels = [np.array([self.y[3]])]
5963
return train_data, train_labels, test_data, test_labels
6064

6165
def shuffle_data(
62-
self,
63-
paired_data: list[tuple[np.ndarray, int]]
64-
) -> list[tuple[np.ndarray, int]]:
66+
self, paired_data: list[tuple[np.ndarray, int]]
67+
) -> list[tuple[np.ndarray, int]]:
6568
"""
6669
Shuffles the data randomly.
6770
@@ -99,41 +102,43 @@ def one_hot_encode(labels: list[int], num_classes: int) -> np.ndarray:
99102

100103
class MLP:
101104
"""
102-
A custom MLP class for implementing a simple multi-layer perceptron with
103-
forward propagation, backpropagation.
104-
105-
Attributes:
106-
learning_rate (float): Learning rate for gradient descent.
107-
gamma (float): Parameter to control learning rate adjustment.
108-
epoch (int): Number of epochs for training.
109-
hidden_dim (int): Dimension of the hidden layer.
110-
batch_size (int): Number of samples per mini-batch.
111-
train_loss (List[float]): List to store training loss for each fold.
112-
train_accuracy (List[float]): List to store training accuracy for each fold.
113-
test_loss (List[float]): List to store test loss for each fold.
114-
test_accuracy (List[float]): List to store test accuracy for each fold.
115-
dataloader (Dataloader): DataLoader object for handling training data.
116-
inter_variable (dict): Dictionary to store intermediate variables
117-
for backpropagation.
118-
weights1_list (List[Tuple[np.ndarray, np.ndarray]]):
119-
List of weights for each fold.
120-
121-
Methods:
122-
get_inout_dim:obtain input dimension and output dimension.
123-
relu: Apply the ReLU activation function.
124-
relu_derivative: Compute the derivative of the ReLU function.
125-
forward: Perform a forward pass through the network.
126-
back_prop: Perform backpropagation to compute gradients.
127-
update_weights: Update the weights using gradients.
128-
update_learning_rate: Adjust the learning rate based on test accuracy.
129-
accuracy: Compute accuracy of the model.
130-
loss: Compute weighted MSE loss.
131-
train: Train the MLP over multiple folds with early stopping.
132-
133-
134-
"""
135-
def __init__(self, dataloader, epoch: int, learning_rate: float,
136-
gamma=1, hidden_dim=2):
105+
A custom MLP class for implementing a simple multi-layer perceptron with
106+
forward propagation, backpropagation.
107+
108+
Attributes:
109+
learning_rate (float): Learning rate for gradient descent.
110+
gamma (float): Parameter to control learning rate adjustment.
111+
epoch (int): Number of epochs for training.
112+
hidden_dim (int): Dimension of the hidden layer.
113+
batch_size (int): Number of samples per mini-batch.
114+
train_loss (List[float]): List to store training loss for each fold.
115+
train_accuracy (List[float]): List to store training accuracy for each fold.
116+
test_loss (List[float]): List to store test loss for each fold.
117+
test_accuracy (List[float]): List to store test accuracy for each fold.
118+
dataloader (Dataloader): DataLoader object for handling training data.
119+
inter_variable (dict): Dictionary to store intermediate variables
120+
for backpropagation.
121+
weights1_list (List[Tuple[np.ndarray, np.ndarray]]):
122+
List of weights for each fold.
123+
124+
Methods:
125+
get_inout_dim:obtain input dimension and output dimension.
126+
relu: Apply the ReLU activation function.
127+
relu_derivative: Compute the derivative of the ReLU function.
128+
forward: Perform a forward pass through the network.
129+
back_prop: Perform backpropagation to compute gradients.
130+
update_weights: Update the weights using gradients.
131+
update_learning_rate: Adjust the learning rate based on test accuracy.
132+
accuracy: Compute accuracy of the model.
133+
loss: Compute weighted MSE loss.
134+
train: Train the MLP over multiple folds with early stopping.
135+
136+
137+
"""
138+
139+
def __init__(
140+
self, dataloader, epoch: int, learning_rate: float, gamma=1, hidden_dim=2
141+
):
137142
self.learning_rate = learning_rate
138143
self.gamma = gamma # learning_rate decay hyperparameter gamma
139144
self.epoch = epoch
@@ -216,13 +221,12 @@ def relu_derivative(self, input_array: np.ndarray) -> np.ndarray:
216221
"""
217222
return (input_array > 0).astype(float)
218223

219-
220224
def forward(
221-
self,
222-
input_data: np.ndarray,
223-
w1: np.ndarray,
224-
w2: np.ndarray,
225-
no_gradient: bool = False
225+
self,
226+
input_data: np.ndarray,
227+
w1: np.ndarray,
228+
w2: np.ndarray,
229+
no_gradient: bool = False,
226230
) -> np.ndarray:
227231
"""
228232
Performs a forward pass through the neural network with one hidden layer.
@@ -264,10 +268,7 @@ def forward(
264268
return a2
265269

266270
def back_prop(
267-
self,
268-
input_data: np.ndarray,
269-
true_labels: np.ndarray,
270-
w2: np.ndarray
271+
self, input_data: np.ndarray, true_labels: np.ndarray, w2: np.ndarray
271272
) -> tuple[np.ndarray, np.ndarray]:
272273
"""
273274
Performs backpropagation to compute gradients for the weights.
@@ -315,12 +316,12 @@ def back_prop(
315316
return grad_w1, grad_w2
316317

317318
def update_weights(
318-
self,
319-
w1: np.ndarray,
320-
w2: np.ndarray,
321-
grad_w1: np.ndarray,
322-
grad_w2: np.ndarray,
323-
learning_rate: float
319+
self,
320+
w1: np.ndarray,
321+
w2: np.ndarray,
322+
grad_w1: np.ndarray,
323+
grad_w2: np.ndarray,
324+
learning_rate: float,
324325
) -> tuple[np.ndarray, np.ndarray]:
325326
"""
326327
Updates the weight matrices using the computed gradients and learning rate.
@@ -359,7 +360,6 @@ def update_weights(
359360
w2 -= learning_rate * grad_w2
360361
return w1, w2
361362

362-
363363
def update_learning_rate(self, learning_rate: float) -> float:
364364
"""
365365
Updates the learning rate by applying the decay factor gamma.
@@ -456,13 +456,13 @@ def train(self) -> None:
456456
"""
457457

458458
learning_rate = self.learning_rate
459-
train_data, train_labels, test_data, test_labels = \
460-
self.dataloader.get_train_test_data()
459+
train_data, train_labels, test_data, test_labels = (
460+
self.dataloader.get_train_test_data()
461+
)
461462

462463
train_data = np.c_[train_data, np.ones(train_data.shape[0])]
463464
test_data = np.c_[test_data, np.ones(test_data.shape[0])]
464465

465-
466466
_, total_label_num = self.dataloader.get_inout_dim()
467467

468468
train_labels = self.dataloader.one_hot_encode(train_labels, total_label_num)
@@ -476,14 +476,14 @@ def train(self) -> None:
476476

477477
for _j in tqdm(range(self.epoch)):
478478
for k in range(0, train_data.shape[0], batch_size): # retrieve every image
479-
480-
batch_imgs = train_data[k: k + batch_size]
481-
batch_labels = train_labels[k: k + batch_size]
479+
batch_imgs = train_data[k : k + batch_size]
480+
batch_labels = train_labels[k : k + batch_size]
482481

483482
self.forward(input_data=batch_imgs, w1=w1, w2=w2, no_gradient=False)
484483

485-
grad_w1, grad_w2 = self.back_prop(input_data=batch_imgs, \
486-
true_labels=batch_labels, w2=w2)
484+
grad_w1, grad_w2 = self.back_prop(
485+
input_data=batch_imgs, true_labels=batch_labels, w2=w2
486+
)
487487

488488
w1, w2 = self.update_weights(w1, w2, grad_w1, grad_w2, learning_rate)
489489

@@ -498,7 +498,7 @@ def train(self) -> None:
498498

499499
self.test_accuracy = test_accuracy_list
500500
self.test_loss = test_loss_list
501-
print("Test accuracy:", sum(test_accuracy_list)/len(test_accuracy_list))
501+
print("Test accuracy:", sum(test_accuracy_list) / len(test_accuracy_list))
502502

503503

504504
if __name__ == "__main__":

0 commit comments

Comments
 (0)