AI & Machine Learning Fundamentals

Welcome to a practical exploration of neural networks! This page provides a hands-on introduction to the fundamental concepts of forward propagation and backpropagation. We'll use Python with the NumPy library to illustrate these principles, making them easier to grasp. These examples are designed to give you a clear, practical understanding of how neural networks learn. Machine learning, as a field, encompasses a variety of algorithms and techniques that allow computers to learn from data without being explicitly programmed. At the heart of many advanced machine learning systems lie neural networks, which form the basis for deep learning. In our exploration, we focus on a simple neural network to illustrate these core principles.

Neural Network Basics

Let's start with a basic forward propagation example using Python and NumPy. Forward propagation is the initial step in the process of neural network computations.

Key Concept: Forward propagation is the process of taking input data, passing it through the neural network's layers, and calculating an output. Each neuron in a layer computes a weighted sum of its inputs, adds a bias, and applies an activation function. The activation function introduces non-linearity, which is crucial for the network to learn complex patterns. A typical choice for activation functions is the sigmoid function. The output of each layer serves as the input for the next layer, hence the term "forward" propagation.


import numpy as np

def sigmoid(x):
    """
    Applies the sigmoid activation function.
    Args:
      x (numpy.ndarray or float): The input value(s).
    Returns:
      numpy.ndarray or float: The result after applying the sigmoid function.
    """
    return 1 / (1 + np.exp(-x))

def forward_propagation(inputs, weights, bias):
    """
    Performs forward propagation for a single layer neural network.
    Args:
      inputs (numpy.ndarray): Input data for the layer.
      weights (numpy.ndarray): Weights for the layer's connections.
      bias (float): Bias value for the layer.
    Returns:
      numpy.ndarray: Output of the layer after applying the activation function.
    """
    # Calculate the weighted sum of inputs and add bias
    z = np.dot(inputs, weights) + bias
    # Apply the sigmoid activation function
    output = sigmoid(z)
    return output

# Example Usage
inputs = np.array([0.5, 0.3, 0.8]) # Example input data
weights = np.array([0.2, -0.3, 0.5]) # Example weights
bias = 0.1 # Example bias value

output = forward_propagation(inputs, weights, bias)
print("Output:", output)
        

Backpropagation Example

Now, let's delve into backpropagation, which is the fundamental algorithm for training neural networks. The following example illustrates how gradients are computed and used to update the network's weights. This process is iterative and continuous, driving the network to improve its performance.

Key Concept: Backpropagation calculates the gradient of the loss function with respect to each weight in the network. It leverages the chain rule of calculus to efficiently propagate error signals backward from the output layer to the input layer. The gradients are used to update the weights, usually using an optimization algorithm like gradient descent. The learning rate dictates the step size in this process. Through this iterative process, the network's parameters are continuously adjusted to minimize the error between the network's predictions and the actual target values, ultimately improving the network's ability to make accurate predictions.


import numpy as np

def sigmoid(x):
    """
    Applies the sigmoid activation function.
    Args:
      x (numpy.ndarray or float): The input value(s).
    Returns:
      numpy.ndarray or float: The result after applying the sigmoid function.
    """
    return 1 / (1 + np.exp(-x))

def sigmoid_derivative(x):
    """
    Calculates the derivative of the sigmoid function.
    Args:
        x (numpy.ndarray or float): The input value(s).
    Returns:
        numpy.ndarray or float: The derivative of the sigmoid function at the input value(s).
    """
    return sigmoid(x) * (1 - sigmoid(x))

def forward_propagation(inputs, weights, bias):
    """
    Performs forward propagation.
    Args:
      inputs (numpy.ndarray): Input data for the layer.
      weights (numpy.ndarray): Weights for the layer's connections.
      bias (float): Bias value for the layer.
    Returns:
      tuple: Output of the layer after applying the activation function, and the value of 'z'.
    """
    # Calculate the weighted sum of inputs and add bias
    z = np.dot(inputs, weights) + bias
    # Apply the sigmoid activation function
    output = sigmoid(z)
    return output, z

def backward_propagation(inputs, output, z, target, learning_rate, weights):
    """
    Performs backpropagation to update weights.
     Args:
        inputs (numpy.ndarray): Input data for the layer.
        output (numpy.ndarray or float): Output from forward propagation.
        z (numpy.ndarray or float): Weighted sum of inputs + bias.
        target (float): Target output value.
        learning_rate (float): Learning rate for the weight update.
        weights (numpy.ndarray): Current weights.
    Returns:
        numpy.ndarray: Updated weights after backpropagation.
    """
    # Calculate the error
    error = target - output
    # Calculate the delta (error * derivative of the activation function)
    delta = error * sigmoid_derivative(z)
    # Calculate the gradient of the weights
    weights_gradient = np.dot(inputs.reshape(-1,1), delta.reshape(1,-1))
    # Update the weights using the learning rate and gradient
    new_weights = weights + learning_rate * weights_gradient

    return new_weights

# Example Usage
inputs = np.array([0.5, 0.3, 0.8]) # Example input data
weights = np.array([0.2, -0.3, 0.5]) # Example initial weights
bias = 0.1 # Example bias value
target = 1.0 # Desired output
learning_rate = 0.1 # Learning rate for weight adjustment

output, z = forward_propagation(inputs, weights, bias)

new_weights = backward_propagation(inputs, output, z, target, learning_rate, weights)
print("Updated weights:", new_weights)
       

More Examples

This is just the beginning! We will continue adding more advanced examples in the future. Expect implementations for Convolutional Neural Networks (CNNs) and Recurrent Neural Networks (RNNs), along with practical tips on how to apply these concepts effectively. CNNs are particularly useful for image processing tasks, while RNNs are often employed for processing sequential data like text or time-series. We will also explore other optimization techniques, such as mini-batch gradient descent and Adam optimizer, to enhance the training process and speed up convergence.