-
-
Notifications
You must be signed in to change notification settings - Fork 46.9k
Adding 2-hidden layer neural network with back propagation using sigmoid activation function #4037
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
dhruvmanila
merged 16 commits into
TheAlgorithms:master
from
raman77768:2_hidden_layer_neural_network
Dec 26, 2020
Merged
Changes from all commits
Commits
Show all changes
16 commits
Select commit
Hold shift + click to select a range
fa4e2ac
added neural network with 2 hidden layers
raman77768 1219f8c
Revert "added neural network with 2 hidden layers"
raman77768 97556ab
added neural network with 2 hidden layers
raman77768 a783a4d
passing pre-commit requirements
raman77768 07fd20e
doctest completed
raman77768 3f17bfc
added return hints
raman77768 ff55363
added example
raman77768 0454f72
example added
raman77768 0034458
completed doctest's
raman77768 4536f59
changes made as per the review
raman77768 c5f71eb
changes made
raman77768 527109d
changes after review
raman77768 1033bd1
changes
raman77768 481d7b7
spacing
raman77768 1285ca3
changed return type
raman77768 3e2e723
changed dtype
raman77768 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,295 @@ | ||
""" | ||
References: | ||
- http://neuralnetworksanddeeplearning.com/chap2.html (Backpropagation) | ||
- https://en.wikipedia.org/wiki/Sigmoid_function (Sigmoid activation function) | ||
- https://en.wikipedia.org/wiki/Feedforward_neural_network (Feedforward) | ||
""" | ||
|
||
import numpy | ||
|
||
|
||
class TwoHiddenLayerNeuralNetwork: | ||
def __init__(self, input_array: numpy.ndarray, output_array: numpy.ndarray) -> None: | ||
""" | ||
This function initializes the TwoHiddenLayerNeuralNetwork class with random | ||
weights for every layer and initializes predicted output with zeroes. | ||
|
||
input_array : input values for training the neural network (i.e training data) . | ||
output_array : expected output values of the given inputs. | ||
""" | ||
|
||
# Input values provided for training the model. | ||
self.input_array = input_array | ||
|
||
# Random initial weights are assigned where first argument is the | ||
# number of nodes in previous layer and second argument is the | ||
# number of nodes in the next layer. | ||
|
||
# Random initial weights are assigned. | ||
# self.input_array.shape[1] is used to represent number of nodes in input layer. | ||
# First hidden layer consists of 4 nodes. | ||
self.input_layer_and_first_hidden_layer_weights = numpy.random.rand( | ||
self.input_array.shape[1], 4 | ||
) | ||
|
||
# Random initial values for the first hidden layer. | ||
# First hidden layer has 4 nodes. | ||
# Second hidden layer has 3 nodes. | ||
self.first_hidden_layer_and_second_hidden_layer_weights = numpy.random.rand( | ||
4, 3 | ||
) | ||
|
||
# Random initial values for the second hidden layer. | ||
# Second hidden layer has 3 nodes. | ||
# Output layer has 1 node. | ||
self.second_hidden_layer_and_output_layer_weights = numpy.random.rand(3, 1) | ||
|
||
# Real output values provided. | ||
self.output_array = output_array | ||
|
||
# Predicted output values by the neural network. | ||
# Predicted_output array initially consists of zeroes. | ||
self.predicted_output = numpy.zeros(output_array.shape) | ||
|
||
def feedforward(self) -> numpy.ndarray: | ||
""" | ||
The information moves in only one direction i.e. forward from the input nodes, | ||
through the two hidden nodes and to the output nodes. | ||
There are no cycles or loops in the network. | ||
|
||
Return layer_between_second_hidden_layer_and_output | ||
(i.e the last layer of the neural network). | ||
|
||
>>> input_val = numpy.array(([0, 0, 0], [0, 0, 0], [0, 0, 0]), dtype=float) | ||
>>> output_val = numpy.array(([0], [0], [0]), dtype=float) | ||
>>> nn = TwoHiddenLayerNeuralNetwork(input_val, output_val) | ||
>>> res = nn.feedforward() | ||
>>> array_sum = numpy.sum(res) | ||
>>> numpy.isnan(array_sum) | ||
False | ||
""" | ||
# Layer_between_input_and_first_hidden_layer is the layer connecting the | ||
# input nodes with the first hidden layer nodes. | ||
self.layer_between_input_and_first_hidden_layer = sigmoid( | ||
numpy.dot(self.input_array, self.input_layer_and_first_hidden_layer_weights) | ||
) | ||
|
||
# layer_between_first_hidden_layer_and_second_hidden_layer is the layer | ||
# connecting the first hidden set of nodes with the second hidden set of nodes. | ||
self.layer_between_first_hidden_layer_and_second_hidden_layer = sigmoid( | ||
numpy.dot( | ||
self.layer_between_input_and_first_hidden_layer, | ||
self.first_hidden_layer_and_second_hidden_layer_weights, | ||
) | ||
) | ||
|
||
# layer_between_second_hidden_layer_and_output is the layer connecting | ||
# second hidden layer with the output node. | ||
self.layer_between_second_hidden_layer_and_output = sigmoid( | ||
numpy.dot( | ||
self.layer_between_first_hidden_layer_and_second_hidden_layer, | ||
self.second_hidden_layer_and_output_layer_weights, | ||
) | ||
) | ||
|
||
return self.layer_between_second_hidden_layer_and_output | ||
|
||
def back_propagation(self) -> None: | ||
dhruvmanila marked this conversation as resolved.
Show resolved
Hide resolved
dhruvmanila marked this conversation as resolved.
Show resolved
Hide resolved
dhruvmanila marked this conversation as resolved.
Show resolved
Hide resolved
|
||
""" | ||
Function for fine-tuning the weights of the neural net based on the | ||
error rate obtained in the previous epoch (i.e., iteration). | ||
Updation is done using derivative of sogmoid activation function. | ||
|
||
>>> input_val = numpy.array(([0, 0, 0], [0, 0, 0], [0, 0, 0]), dtype=float) | ||
>>> output_val = numpy.array(([0], [0], [0]), dtype=float) | ||
>>> nn = TwoHiddenLayerNeuralNetwork(input_val, output_val) | ||
>>> res = nn.feedforward() | ||
>>> nn.back_propagation() | ||
>>> updated_weights = nn.second_hidden_layer_and_output_layer_weights | ||
>>> (res == updated_weights).all() | ||
dhruvmanila marked this conversation as resolved.
Show resolved
Hide resolved
|
||
False | ||
""" | ||
|
||
updated_second_hidden_layer_and_output_layer_weights = numpy.dot( | ||
self.layer_between_first_hidden_layer_and_second_hidden_layer.T, | ||
2 | ||
* (self.output_array - self.predicted_output) | ||
* sigmoid_derivative(self.predicted_output), | ||
) | ||
updated_first_hidden_layer_and_second_hidden_layer_weights = numpy.dot( | ||
self.layer_between_input_and_first_hidden_layer.T, | ||
numpy.dot( | ||
2 | ||
* (self.output_array - self.predicted_output) | ||
* sigmoid_derivative(self.predicted_output), | ||
self.second_hidden_layer_and_output_layer_weights.T, | ||
) | ||
* sigmoid_derivative( | ||
self.layer_between_first_hidden_layer_and_second_hidden_layer | ||
), | ||
) | ||
updated_input_layer_and_first_hidden_layer_weights = numpy.dot( | ||
self.input_array.T, | ||
numpy.dot( | ||
numpy.dot( | ||
2 | ||
* (self.output_array - self.predicted_output) | ||
* sigmoid_derivative(self.predicted_output), | ||
self.second_hidden_layer_and_output_layer_weights.T, | ||
) | ||
* sigmoid_derivative( | ||
self.layer_between_first_hidden_layer_and_second_hidden_layer | ||
), | ||
self.first_hidden_layer_and_second_hidden_layer_weights.T, | ||
) | ||
* sigmoid_derivative(self.layer_between_input_and_first_hidden_layer), | ||
) | ||
|
||
self.input_layer_and_first_hidden_layer_weights += ( | ||
updated_input_layer_and_first_hidden_layer_weights | ||
) | ||
self.first_hidden_layer_and_second_hidden_layer_weights += ( | ||
updated_first_hidden_layer_and_second_hidden_layer_weights | ||
) | ||
self.second_hidden_layer_and_output_layer_weights += ( | ||
updated_second_hidden_layer_and_output_layer_weights | ||
) | ||
|
||
def train(self, output: numpy.ndarray, iterations: int, give_loss: bool) -> None: | ||
""" | ||
Performs the feedforwarding and back propagation process for the | ||
given number of iterations. | ||
Every iteration will update the weights of neural network. | ||
|
||
output : real output values,required for calculating loss. | ||
iterations : number of times the weights are to be updated. | ||
give_loss : boolean value, If True then prints loss for each iteration, | ||
If False then nothing is printed | ||
|
||
>>> input_val = numpy.array(([0, 0, 0], [0, 1, 0], [0, 0, 1]), dtype=float) | ||
>>> output_val = numpy.array(([0], [1], [1]), dtype=float) | ||
>>> nn = TwoHiddenLayerNeuralNetwork(input_val, output_val) | ||
>>> first_iteration_weights = nn.feedforward() | ||
>>> nn.back_propagation() | ||
>>> updated_weights = nn.second_hidden_layer_and_output_layer_weights | ||
>>> (first_iteration_weights == updated_weights).all() | ||
False | ||
""" | ||
for iteration in range(1, iterations + 1): | ||
self.output = self.feedforward() | ||
self.back_propagation() | ||
if give_loss: | ||
loss = numpy.mean(numpy.square(output - self.feedforward())) | ||
print(f"Iteration {iteration} Loss: {loss}") | ||
|
||
def predict(self, input: numpy.ndarray) -> int: | ||
""" | ||
Predict's the output for the given input values using | ||
the trained neural network. | ||
|
||
The output value given by the model ranges in-between 0 and 1. | ||
The predict function returns 1 if the model value is greater | ||
than the threshold value else returns 0, | ||
as the real output values are in binary. | ||
|
||
>>> input_val = numpy.array(([0, 0, 0], [0, 1, 0], [0, 0, 1]), dtype=float) | ||
>>> output_val = numpy.array(([0], [1], [1]), dtype=float) | ||
>>> nn = TwoHiddenLayerNeuralNetwork(input_val, output_val) | ||
>>> nn.train(output_val, 1000, False) | ||
>>> nn.predict([0,1,0]) | ||
1 | ||
""" | ||
|
||
# Input values for which the predictions are to be made. | ||
self.array = input | ||
|
||
self.layer_between_input_and_first_hidden_layer = sigmoid( | ||
numpy.dot(self.array, self.input_layer_and_first_hidden_layer_weights) | ||
) | ||
|
||
self.layer_between_first_hidden_layer_and_second_hidden_layer = sigmoid( | ||
numpy.dot( | ||
self.layer_between_input_and_first_hidden_layer, | ||
self.first_hidden_layer_and_second_hidden_layer_weights, | ||
) | ||
) | ||
|
||
self.layer_between_second_hidden_layer_and_output = sigmoid( | ||
numpy.dot( | ||
self.layer_between_first_hidden_layer_and_second_hidden_layer, | ||
self.second_hidden_layer_and_output_layer_weights, | ||
) | ||
) | ||
|
||
return int(self.layer_between_second_hidden_layer_and_output > 0.6) | ||
|
||
|
||
def sigmoid(value: numpy.ndarray) -> numpy.ndarray: | ||
""" | ||
Applies sigmoid activation function. | ||
|
||
return normalized values | ||
|
||
>>> sigmoid(numpy.array(([1, 0, 2], [1, 0, 0]), dtype=numpy.float64)) | ||
array([[0.73105858, 0.5 , 0.88079708], | ||
[0.73105858, 0.5 , 0.5 ]]) | ||
""" | ||
return 1 / (1 + numpy.exp(-value)) | ||
|
||
|
||
def sigmoid_derivative(value: numpy.ndarray) -> numpy.ndarray: | ||
""" | ||
Provides the derivative value of the sigmoid function. | ||
|
||
returns derivative of the sigmoid value | ||
|
||
>>> sigmoid_derivative(numpy.array(([1, 0, 2], [1, 0, 0]), dtype=numpy.float64)) | ||
array([[ 0., 0., -2.], | ||
[ 0., 0., 0.]]) | ||
""" | ||
return (value) * (1 - (value)) | ||
|
||
|
||
def example() -> int: | ||
dhruvmanila marked this conversation as resolved.
Show resolved
Hide resolved
raman77768 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
""" | ||
Example for "how to use the neural network class and use the | ||
respected methods for the desired output". | ||
Calls the TwoHiddenLayerNeuralNetwork class and | ||
provides the fixed input output values to the model. | ||
Model is trained for a fixed amount of iterations then the predict method is called. | ||
In this example the output is divided into 2 classes i.e. binary classification, | ||
the two classes are represented by '0' and '1'. | ||
|
||
>>> example() | ||
1 | ||
""" | ||
# Input values. | ||
input = numpy.array( | ||
( | ||
[0, 0, 0], | ||
[0, 0, 1], | ||
[0, 1, 0], | ||
[0, 1, 1], | ||
[1, 0, 0], | ||
[1, 0, 1], | ||
[1, 1, 0], | ||
[1, 1, 1], | ||
), | ||
dtype=numpy.float64, | ||
) | ||
|
||
# True output values for the given input values. | ||
output = numpy.array(([0], [1], [1], [0], [1], [0], [0], [1]), dtype=numpy.float64) | ||
|
||
# Calling neural network class. | ||
neural_network = TwoHiddenLayerNeuralNetwork(input_array=input, output_array=output) | ||
|
||
# Calling training function. | ||
# Set give_loss to True if you want to see loss in every iteration. | ||
neural_network.train(output=output, iterations=10, give_loss=False) | ||
|
||
return neural_network.predict(numpy.array(([1, 1, 1]), dtype=numpy.float64)) | ||
|
||
|
||
if __name__ == "__main__": | ||
example() |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.