AutoGrad From Scrach

References

[121]:
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns

# Set the random seed for reproducibility
np.random.seed(1000)

Differentiability

  • A function f(x) is really differentiable when it has a well defined derivative at a given point x = a, and it’s derivative f’(a) exists and is finite. \begin{align*} f'(a) &= \lim_{h \rightarrow 0} \frac{f(a + h) - f(a)}{h} \\\\ &\text{or} \\ f'(a) &= \lim_{h \rightarrow 0} \frac{f(a + h) - f(a - h)}{2h} \\\\ \end{align*}

  • In simpler terms if function has a slope or rate of exchange than can be calculated (must not have any sharp edges)

  • If a function is not conitnuous at a point, it is not differentiable there, but not all conitnuous functions are differenciable

  • Examples -

    • Differentiable \(f(x) = x^2\)

    • Non differentiable \(f(x) = |x|\)

Derivative

[123]:
def diff_func_1(x):
    """Square function

    .. math::
        f(x) = x^2
    """
    return x**2

def diff_func_2(x):
    """Quadratic function

    .. math::
        f(x) = x^2 - 2x + 1
    """
    return x**2 - (2 * x) + 1

def diff_func_3(x):
    """Sine function

    .. math::
        f(x) = sin(x)
    """
    return np.sin(x)

def non_diff_func_1(x):
    """Absolute value function

    .. math::
        f(x) = |x|
    """
    return np.abs(x)

def non_diff_func_2(x):
    """Step function

    .. math::
        f(x) = 1 if x > 0, 0 otherwise
    """
    return x * np.abs(x)

def derivative(func, x):
    h = 1e-5
    f_x_plus_h = func(x + h)
    f_x_minus_h = func(x - h)
    return (f_x_plus_h - f_x_minus_h) / (2.0 * h)
[124]:
x = 1
h = 1e-5

print(derivative(diff_func_1, x))
2.000000000002
[125]:
def plot_differentiation(func):
    delta = 0.1  # Smaller step size for smooth curves
    lim = 5
    x = np.arange(-lim, lim + delta, delta)
    f_x = func(x)
    df_x = derivative(func, x)

    fig, ax = plt.subplots(1, 1, figsize=(4, 3))

    # Plot function and derivative
    ax.plot(x, f_x, label='f(x)', color='blue', linewidth=2)
    ax.plot(x, df_x, label="f'(x)", color='orange', linestyle='--', linewidth=2)

    # Highlight axes
    ax.axhline(0, color='black', linewidth=0.8, linestyle='--')
    ax.axvline(0, color='black', linewidth=0.8, linestyle='--')

    # Titles and labels
    ax.set_title("Function and its Derivative", fontsize=14)
    ax.set_xlabel("x", fontsize=12)
    ax.set_ylabel("y", fontsize=12)

    critical_points = x[np.abs(df_x) < 1e-3]  # Approximate zeros of the derivative
    ax.scatter(critical_points, func(critical_points), color='red', label='Critical Points', zorder=5)

    # Grid and legend
    ax.grid(True, linestyle='--', alpha=0.6)
    ax.legend(fontsize=12)

    fig.show();
[128]:
plot_differentiation(diff_func_1)
../_images/notebooks_autograd_6_0.png
[129]:
plot_differentiation(non_diff_func_1)
../_images/notebooks_autograd_7_0.png