Contents
Deep Breakdown
What it tells me (Situation Awareness): The derivative of (exponential with arbitrary base , ) is scaled by the natural log of the base: . The factor encodes how "fast" base grows relative to base : when , and you recover . When , , so grows at the rate of . In DA/DS: this rule applies when differentiating learning rate schedules based on exponential decay ( where ), computing gradients through custom activation functions with non- bases, and differentiating log-probability expressions after change-of-base. The chain rule extension applies in any case where the exponent is a function of .
Flowchart
Real-world use
Where it is used
Differentiating learning rate decay schedules of the form (e.g., per epoch) to find the rate of LR change; computing gradients through log-base-2 entropy expressions (bits); differentiating any power function with a non- base that arises in custom ML architectures; proof that is the "natural" base because it is the unique for which the scaling factor .
Problem it solves
When the base of an exponential is not , the derivative is not simply the function itself. Without this rule, one cannot correctly differentiate expressions like , , or that appear in decay schedules and information-theoretic quantities.
How it solves the problem
Rewrite , then apply the chain rule: . The key insight is that any base- exponential is just a base- exponential with a rescaled exponent, and the rescaling factor enters as a constant multiplier.
Example in a business
A DS implements a step decay learning rate schedule: where . To analyze how fast the LR drops per epoch, they compute . The negative sign confirms decay; the magnitude means a per-step relative drop rate.
The formula, piece by piece
- : the base; any positive real ,
- : the exponential function; grows when , decays when
- : natural log of the base; the constant multiplier introduced by differentiation
- : the derivative; proportional to the original function, scaled by
- Derivation: , so
- Chain rule:
- Special cases: ; ;
- Note: when (decaying base), confirming derivative is negative
The formula in words
"The derivative of is itself, multiplied by the natural log of the base. The factor is constant — it does not depend on . It scales the growth rate: larger means faster growth. When , this factor is 1, which is why is the natural base for calculus." Tiny examples: . . . .
Critical Concepts
- Why appears: . Differentiating via chain rule brings down as the derivative of the inner function . This is the fundamental reason the natural log is called "natural" — it is the constant that arises when differentiating exponentials.
- as the natural base: is the unique base for which , so with no extra constant. Every other base introduces a factor. This simplicity makes the default in calculus and ML.
- Decaying bases (): for , so . The function is decreasing; its derivative correctly reflects this. Example: .
- Change of base formula connection: , so . The derivative can also be written .
- Integral: (for ), derived by reversing the derivative rule.
Step-by-step calculation (tiny example)
Differentiate (learning rate decay schedule) and find the decay rate at . Use , .
Step 1: Identify the form. . Here , the base.
Step 2: Apply the rule . .
Step 3: Compute . .
Step 4: Evaluate at . . Interpretation: at epoch 5, the learning rate is decreasing at per epoch.
Code examples
Beginner example (Python)
import numpy as np
def exp_base_a_deriv(a: float, x: float) -> float:
"""d/dx(a^x) = a^x * ln(a). Valid for a > 0, a != 1."""
return a**x * np.log(a)
def exp_base_a_deriv_chain(a: float, x: float, f_x: float, f_prime_x: float) -> float:
"""d/dx(a^f(x)) = a^f(x) * ln(a) * f'(x)."""
return a**f_x * np.log(a) * f_prime_x # Verify numerically for several bases
h = 1e-7
x0 = 1.5
print(f"{'Base a':>8} {'Analytic':>12} {'Numerical':>12} {'Match':>7}")
for a in [2.0, np.e, 10.0, 0.5, 0.95]:
analytic = exp_base_a_deriv(a, x0)
numerical = (a**(x0 + h) - a**(x0 - h)) / (2 * h)
match = abs(analytic - numerical) < 1e-5
print(f"{a:>8.4f} {analytic:>12.6f} {numerical:>12.6f} {str(match):>7}") # Special case: a = e should match d/dx(e^x) = e^x
print(f"\nAt a=e, x=1: d/dx(e^x)={np.e**1:.6f}, rule gives={exp_base_a_deriv(np.e, 1):.6f}")Beginner example (R)
# d/dx(a^x) = a^x * ln(a)
exp_base_a_deriv <- function(a, x) a^x * log(a) # log() in R is natural log
x0 <- 1.5
h <- 1e-7
bases <- c(2, exp(1), 10, 0.5, 0.95)
cat(sprintf("%-8s %-12s %-12s\n", "Base a", "Analytic", "Numerical"))
for (a in bases) { analytic <- exp_base_a_deriv(a, x0)
numerical <- (a^(x0 + h) - a^(x0 - h)) / (2 * h)
cat(sprintf("%-8.4f %-12.6f %-12.6f\n", a, analytic, numerical))
}Professional example (Python)
import numpy as np
np.random.seed(42) # -- Learning rate schedule analysis: gamma^t decay and its derivative --
# Use case: a DS wants to understand how quickly LR decays under different gamma values
# and choose gamma so that LR reaches a target fraction after N epochs.
INITIAL_LR = 1e-3
TARGET_FRAC = 0.1 # want LR to reach 10% of initial after N_EPOCHS
N_EPOCHS = 50
GAMMAS = [0.99, 0.97, 0.95, 0.90, 0.85]
def lr_schedule(initial_lr: float, gamma: float, t: int) -> float:
"""LR at epoch t: alpha_0 * gamma^t. d/dt = alpha_0 * gamma^t * ln(gamma)."""
return initial_lr * gamma**t
def lr_decay_rate(initial_lr: float, gamma: float, t: int) -> float:
"""d/dt of LR: alpha_0 * gamma^t * ln(gamma). Always <= 0 for gamma < 1."""
return initial_lr * gamma**t * np.log(gamma)
def epochs_to_target(initial_lr: float, gamma: float, target_frac: float) -> float:
"""Solve gamma^t = target_frac: t = log(target_frac) / log(gamma)."""
return np.log(target_frac) / np.log(gamma)
print("Learning Rate Decay Schedule Analysis: d/dt(alpha_0 * gamma^t) = alpha_0 * gamma^t * ln(gamma)")
print(f"\nInitial LR: {INITIAL_LR:.4f}, Target: {TARGET_FRAC*100:.0f}% of initial\n")
print(f"{'gamma':>7} {'LR@ep50':>10} {'Decay rate@ep0':>16} {'Decay rate@ep50':>16} {'Epochs to target':>18}")
for gamma in GAMMAS:
lr_at_50 = lr_schedule(INITIAL_LR, gamma, N_EPOCHS)
rate_at_0 = lr_decay_rate(INITIAL_LR, gamma, 0)
rate_at_50 = lr_decay_rate(INITIAL_LR, gamma, N_EPOCHS)
t_target = epochs_to_target(INITIAL_LR, gamma, TARGET_FRAC)
print(f"{gamma:>7.2f} {lr_at_50:>10.6f} {rate_at_0:>16.6f} {rate_at_50:>16.8f} {t_target:>18.1f}") # Verify derivative with finite difference
gamma_test = 0.95
t_test = 10.0
h = 1e-6
analytic = lr_decay_rate(INITIAL_LR, gamma_test, t_test)
numerical = (lr_schedule(INITIAL_LR, gamma_test, t_test + h) - lr_schedule(INITIAL_LR, gamma_test, t_test - h)) / (2 * h)
print(f"\nDerivative check (gamma={gamma_test}, t={t_test}): analytic={analytic:.8f}, numerical={numerical:.8f}")
print(f"Key: d/dt(gamma^t) = gamma^t * ln(gamma); ln({gamma_test}) = {np.log(gamma_test):.5f}")Source: 1300 p. 221