|
| 1 | +import numpy as np |
| 2 | + |
| 3 | + |
| 4 | +class SimpleANN: |
| 5 | + """ |
| 6 | + Simple Artificial Neural Network (ANN) |
| 7 | +
|
| 8 | + - Feedforward Neural Network with 1 hidden layer and Sigmoid activation. |
| 9 | + - Uses Gradient Descent for backpropagation and Mean Squared Error (MSE) as the loss function. |
| 10 | + - Example demonstrates solving the XOR problem. |
| 11 | + """ |
| 12 | + |
| 13 | + def __init__( |
| 14 | + self, |
| 15 | + input_size: int, |
| 16 | + hidden_size: int, |
| 17 | + output_size: int, |
| 18 | + learning_rate: float = 0.1, |
| 19 | + ) -> None: |
| 20 | + """ |
| 21 | + Initialize the neural network with random weights and biases. |
| 22 | +
|
| 23 | + Args: |
| 24 | + input_size (int): Number of input features. |
| 25 | + hidden_size (int): Number of neurons in the hidden layer. |
| 26 | + output_size (int): Number of neurons in the output layer. |
| 27 | + learning_rate (float): Learning rate for gradient descent. |
| 28 | +
|
| 29 | + Example: |
| 30 | + >>> ann = SimpleANN(2, 2, 1) |
| 31 | + >>> isinstance(ann, SimpleANN) |
| 32 | + True |
| 33 | + """ |
| 34 | + rng = np.random.default_rng() |
| 35 | + self.weights_input_hidden = rng.standard_normal((input_size, hidden_size)) |
| 36 | + self.weights_hidden_output = rng.standard_normal((hidden_size, output_size)) |
| 37 | + self.bias_hidden = np.zeros((1, hidden_size)) |
| 38 | + self.bias_output = np.zeros((1, output_size)) |
| 39 | + self.learning_rate = learning_rate |
| 40 | + |
| 41 | + def sigmoid(self, value: np.ndarray) -> np.ndarray: |
| 42 | + """ |
| 43 | + Sigmoid activation function. |
| 44 | +
|
| 45 | + Args: |
| 46 | + value (ndarray): Input value for activation. |
| 47 | +
|
| 48 | + Returns: |
| 49 | + ndarray: Activated output using sigmoid function. |
| 50 | +
|
| 51 | + Example: |
| 52 | + >>> ann = SimpleANN(2, 2, 1) |
| 53 | + >>> ann.sigmoid(np.array([0])) |
| 54 | + array([0.5]) |
| 55 | + """ |
| 56 | + return 1 / (1 + np.exp(-value)) |
| 57 | + |
| 58 | + def sigmoid_derivative(self, sigmoid_output: np.ndarray) -> np.ndarray: |
| 59 | + """ |
| 60 | + Derivative of the sigmoid function. |
| 61 | +
|
| 62 | + Args: |
| 63 | + sigmoid_output (ndarray): Output after applying the sigmoid function. |
| 64 | +
|
| 65 | + Returns: |
| 66 | + ndarray: Derivative of the sigmoid function. |
| 67 | +
|
| 68 | + Example: |
| 69 | + >>> ann = SimpleANN(2, 2, 1) |
| 70 | + >>> output = ann.sigmoid(np.array([0.5])) |
| 71 | + >>> ann.sigmoid_derivative(output) |
| 72 | + array([0.25]) |
| 73 | + """ |
| 74 | + return sigmoid_output * (1 - sigmoid_output) |
| 75 | + |
| 76 | + def feedforward(self, inputs: np.ndarray) -> np.ndarray: |
| 77 | + """ |
| 78 | + Perform forward propagation through the network. |
| 79 | +
|
| 80 | + Args: |
| 81 | + inputs (ndarray): Input features for the network. |
| 82 | +
|
| 83 | + Returns: |
| 84 | + ndarray: Output from the network after feedforward pass. |
| 85 | +
|
| 86 | + Example: |
| 87 | + >>> ann = SimpleANN(2, 2, 1) |
| 88 | + >>> inputs = np.array([[0, 0], [1, 1]]) |
| 89 | + >>> ann.feedforward(inputs).shape |
| 90 | + (2, 1) |
| 91 | + """ |
| 92 | + self.hidden_input = np.dot(inputs, self.weights_input_hidden) + self.bias_hidden |
| 93 | + self.hidden_output = self.sigmoid(self.hidden_input) |
| 94 | + self.final_input = ( |
| 95 | + np.dot(self.hidden_output, self.weights_hidden_output) + self.bias_output |
| 96 | + ) |
| 97 | + self.final_output = self.sigmoid(self.final_input) |
| 98 | + return self.final_output |
| 99 | + |
| 100 | + def backpropagation( |
| 101 | + self, inputs: np.ndarray, targets: np.ndarray, outputs: np.ndarray |
| 102 | + ) -> None: |
| 103 | + """ |
| 104 | + Perform backpropagation to adjust the weights and biases. |
| 105 | +
|
| 106 | + Args: |
| 107 | + inputs (ndarray): Input features. |
| 108 | + targets (ndarray): True output labels. |
| 109 | + outputs (ndarray): Output predicted by the network. |
| 110 | +
|
| 111 | + Example: |
| 112 | + >>> ann = SimpleANN(2, 2, 1) |
| 113 | + >>> inputs = np.array([[0, 0], [1, 1]]) |
| 114 | + >>> outputs = ann.feedforward(inputs) |
| 115 | + >>> targets = np.array([[0], [1]]) |
| 116 | + >>> ann.backpropagation(inputs, targets, outputs) |
| 117 | + """ |
| 118 | + error = targets - outputs |
| 119 | + output_gradient = error * self.sigmoid_derivative(outputs) |
| 120 | + hidden_error = output_gradient.dot(self.weights_hidden_output.T) |
| 121 | + hidden_gradient = hidden_error * self.sigmoid_derivative(self.hidden_output) |
| 122 | + |
| 123 | + self.weights_hidden_output += ( |
| 124 | + self.hidden_output.T.dot(output_gradient) * self.learning_rate |
| 125 | + ) |
| 126 | + self.bias_output += ( |
| 127 | + np.sum(output_gradient, axis=0, keepdims=True) * self.learning_rate |
| 128 | + ) |
| 129 | + |
| 130 | + self.weights_input_hidden += inputs.T.dot(hidden_gradient) * self.learning_rate |
| 131 | + self.bias_hidden += ( |
| 132 | + np.sum(hidden_gradient, axis=0, keepdims=True) * self.learning_rate |
| 133 | + ) |
| 134 | + |
| 135 | + def train( |
| 136 | + self, inputs: np.ndarray, targets: np.ndarray, epochs: int = 10000 |
| 137 | + ) -> None: |
| 138 | + """ |
| 139 | + Train the neural network on the given input and target data. |
| 140 | +
|
| 141 | + Args: |
| 142 | + inputs (ndarray): Input features for training. |
| 143 | + targets (ndarray): True labels for training. |
| 144 | + epochs (int): Number of training iterations. |
| 145 | +
|
| 146 | + Example: |
| 147 | + >>> ann = SimpleANN(2, 2, 1) |
| 148 | + >>> inputs = np.array([[0, 0], [1, 1]]) |
| 149 | + >>> targets = np.array([[0], [1]]) |
| 150 | + >>> ann.train(inputs, targets, epochs=1) |
| 151 | + """ |
| 152 | + for epoch in range(epochs): |
| 153 | + outputs = self.feedforward(inputs) |
| 154 | + self.backpropagation(inputs, targets, outputs) |
| 155 | + if epoch % 1000 == 0: |
| 156 | + loss = np.mean(np.square(targets - outputs)) |
| 157 | + print(f"Epoch {epoch}, Loss: {loss}") |
| 158 | + |
| 159 | + def predict(self, inputs: np.ndarray) -> np.ndarray: |
| 160 | + """ |
| 161 | + Predict the output for new input data. |
| 162 | +
|
| 163 | + Args: |
| 164 | + inputs (ndarray): Input data for prediction. |
| 165 | +
|
| 166 | + Returns: |
| 167 | + ndarray: Predicted output from the network. |
| 168 | +
|
| 169 | + Example: |
| 170 | + >>> ann = SimpleANN(2, 2, 1) |
| 171 | + >>> inputs = np.array([[0, 0], [1, 1]]) |
| 172 | + >>> ann.predict(inputs).shape |
| 173 | + (2, 1) |
| 174 | + """ |
| 175 | + return self.feedforward(inputs) |
0 commit comments