Skip to main content

Someone wanted to learn this too, so Grasp built them a personal learning path.

You can keep exploring this one or create your own

Implementing Gradient Descent

Hello! Let's dive into the practical side of optimization.

Introduction

In our previous lesson, we rigorously derived the gradient descent update rule from first principles:

We established that moving in the direction of the negative gradient, , is the path of steepest descent, providing the theoretical justification for this algorithm.

Today, we will translate this mathematical theory into practice. Your goal for this lesson is to implement the gradient descent algorithm to find the minimum of a simple convex function. This will connect the abstract derivations directly with your Python programming experience. You'll write the code to make a point "roll downhill" on a function's surface to find its minimum, solidifying your understanding of this fundamental optimization workhorse.

Understanding Gradient Descent Visualization
This image provides a concise visual recap of the process. We start at some point, calculate the gradient (the slope of the tangent), and take a step in the opposite direction to move towards the minimum.

Part 1: Defining the Optimization Problem

Before we write any code, we must first define the function we want to minimize. As per the learning outcome, we'll use a simple, multivariable convex function. This ensures that the local minimum we find is also the global minimum, a property you proved in a prior lesson.

Let's choose the following quadratic function:

This function is a paraboloid, but stretched more steeply along the -axis. Its minimum is clearly at .

The first step in implementing gradient descent is always to compute the gradient of the objective function. Given your calculus background, you can quickly find the partial derivatives:

So, the gradient vector is:

With and defined, we have all the mathematical components needed to implement our algorithm.


Part 2: Implementing Gradient Descent in Python

Now, we will write the Python code to perform the optimization. We will use NumPy for numerical operations and Matplotlib for visualization, tools you are already familiar with.

The following video provides an excellent walkthrough of implementing gradient descent from scratch. It starts with a simple 1D case and then moves to a 2D (or 3D surface) case. While the video uses a different, non-convex function for its 2D example, the implementation logic—defining the function, its gradient, and the iterative update loop—is universal.

Gradient Descent From Scratch in Python - Visual Explanation

Watch this video, "Gradient Descent From Scratch in Python" by NeuralNine, to see a live demonstration of the implementation process.

Please watch these two key segments: 1D Implementation (03:44 - 10:31): Focus on how the code translates the math for f(x) = x^2. Notice the y_derivative function, the initial current_position, the learning rate, and the core update loop. 3D Implementation (16:51 - 24:35): Pay close attention to the general structure. See how it defines the z_function (our f), the calculate_gradient function (our grad_f), and the main loop that updates both x and y coordinates according to their respective partial derivatives. The logic here is directly applicable to our problem.

Now, it's your turn to build it. Using the patterns from the video and the structured approach outlined in the following text, you will implement gradient descent for our specific function, .

Introduction to Optimization and Visualizing algorithms

The document "Introduction to Optimization and Visualizing algorithms" provides another clear example, including a self-contained Gradient_Descent function. The section on the simpler quadratic function is particularly relevant.

Review the sections "Gradient Descent implementation" and "Gradient descent on a simpler function (quadratic)". You don't need to use the Rosenbrock function. Instead, notice how they structure their Gradient_Descent function and apply it to the simple quadratic f(x,y) = .01x^2 + .1y^2. This will serve as a great template for your own implementation.

Your Task: Implement Gradient Descent

Follow these steps in a Jupyter Notebook or Python script:

  1. Import libraries: numpy and matplotlib.pyplot.
  2. Define the functions:
    • f(x): Takes a 2-element NumPy array x and returns .
    • grad_f(x): Takes a 2-element NumPy array x and returns the gradient, np.array([2*x[0], 10*x[1]]).
  3. Set up the algorithm:
    • Initialize a starting point, e.g., x_current = np.array([10.0, 2.0]).
    • Choose a learning rate, e.g., eta = 0.05.
    • Set the number of iterations, e.g., n_iterations = 100.
    • Create an empty list history to store the position x_current at each iteration.
  4. Create the main loop:
    • Loop for n_iterations.
    • In each iteration:
      • Calculate the gradient at the current position: gradient = grad_f(x_current).
      • Update the position using the rule: x_current = x_current - eta * gradient.
      • Append x_current to your history list.
  5. Visualize the results:
    • After the loop, you can print the final position x_current to see how close it got to [0, 0].
    • Use matplotlib to create a contour plot of the function and overlay the path taken by the algorithm (from your history list). The resources above contain examples of how to generate these plots.

Part 3: The Critical Role of the Learning Rate

In the last lesson, we noted the importance of the step size . Now you can see its effect firsthand. A poorly chosen can lead to slow convergence or even cause the algorithm to fail completely.

The video you watched earlier has an excellent segment demonstrating this.

Gradient Descent From Scratch in Python - Visual Explanation

Let's revisit the NeuralNine video to see a visual explanation of what happens when the learning rate is too small or too large.

Watch the segment from 10:31 to 15:09. Observe the three scenarios: Too Small: The algorithm makes painfully slow progress. Too Large (Oscillation): The algorithm overshoots the minimum and bounces back and forth. Way Too Large (Divergence): The values explode, and the algorithm fails.

Now, experiment with your own implementation. Run your code with three different learning rates:

  • A small learning rate (e.g., eta = 0.001)
  • A good learning rate (e.g., eta = 0.05)
  • A large learning rate (e.g., eta = 0.19)
  • An even larger learning rate (e.g., eta = 0.21)

Visualize the path for each. You will see the behavior change dramatically.

Gradient Descent with Varying Learning Rates and Momentum
This image illustrates how different learning rates affect the optimization path on a 2D contour plot. Notice how a small rate takes many tiny steps, while a large rate can overshoot and oscillate before converging.
Test your understanding!

Our objective function is . The gradient is . Notice that for the same point , the gradient component is larger in the direction.

If you use a learning rate that is slightly too large, in which direction ( or ) would you expect the algorithm to start oscillating or diverging first? Why?

Show answer

You would expect the algorithm to start oscillating or diverging in the direction first.

The update for each component is . Since the gradient component for () is five times larger than for () at any given point, the step taken in the direction is much larger. This larger step makes it more likely to overshoot the minimum along that axis, leading to oscillation or divergence. This illustrates how the function's curvature (which is steeper along the -axis) interacts with the learning rate.


Conclusion

Congratulations! You have successfully implemented one of the most fundamental algorithms in machine learning. By translating the mathematical formula into working code, you have bridged the gap between theory and practice.

Key Takeaways:

  • Gradient descent is implemented as an iterative loop that repeatedly calculates the gradient and updates the current parameters.
  • The essential ingredients are the objective function, its gradient, a starting point, a learning rate, and a number of iterations.
  • The learning rate () is a critical hyperparameter. If it's too small, convergence is slow. If it's too large, the algorithm can oscillate or diverge.
  • Visualizing the optimization path on a contour plot is a powerful tool for understanding the algorithm's behavior.

Preview of the Next Lesson:
We've built an intuition for how convergence works, but our choice of and the number of iterations was based on trial and error. To be more rigorous, we need to analyze the algorithm's convergence properties mathematically. In the next lesson, we will define two key properties of functions—smoothness and strong convexity—which will allow us to derive formal guarantees on the convergence rate of gradient descent. This will help us answer the question: "How fast does gradient descent find the minimum?"

Can't find a good explanation? Sign up and we'll make it for you

Sign up