20  Hledání minima

20.1 Motivace: Proč hledáme minimum?

Trénování neuronové sítě je v jádru optimalizační problém. Máme:

  1. Model s parametry (váhy, biasy)
  2. Loss funkci, která měří, jak špatně model predikuje
  3. Cíl: najít parametry, které minimalizují loss

Představte si loss funkci jako krajinu - hory a údolí. Chceme najít nejnižší bod (globální minimum). Tato kapitola vysvětlí základní koncepty optimalizace, které jsou klíčové pro pochopení, jak se neuronové sítě učí.

import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D

# Vizualizace loss landscape
def loss_landscape(x, y):
    return (x - 1)**2 + (y - 2)**2 + 0.5 * np.sin(3*x) * np.cos(3*y)

x = np.linspace(-2, 4, 100)
y = np.linspace(-1, 5, 100)
X, Y = np.meshgrid(x, y)
Z = loss_landscape(X, Y)

fig = plt.figure(figsize=(14, 5))

# 3D pohled
ax1 = fig.add_subplot(121, projection='3d')
ax1.plot_surface(X, Y, Z, cmap='viridis', alpha=0.8)
ax1.set_xlabel('Parametr w₁')
ax1.set_ylabel('Parametr w₂')
ax1.set_zlabel('Loss')
ax1.set_title('Loss Landscape (3D)')

# Kontury
ax2 = fig.add_subplot(122)
contour = ax2.contour(X, Y, Z, levels=20, cmap='viridis')
ax2.clabel(contour, inline=True, fontsize=8)
ax2.plot(1, 2, 'r*', markersize=15, label='Globální minimum')
ax2.set_xlabel('Parametr w₁')
ax2.set_ylabel('Parametr w₂')
ax2.set_title('Loss Landscape (kontury)')
ax2.legend()
ax2.grid(True, alpha=0.3)

plt.tight_layout()
plt.show()

20.2 Co je optimalizace?

PoznámkaDefinice: Optimalizační problém

Minimalizace: Najít \(\mathbf{x}^*\) takové, že: \[\mathbf{x}^* = \arg\min_{\mathbf{x}} f(\mathbf{x})\]

kde \(f\) je účelová funkce (objective function), v ML typicky loss funkce.

V kontextu neuronových sítí: - \(\mathbf{x}\) = všechny váhy a biasy sítě (může jich být miliony!) - \(f(\mathbf{x})\) = průměrná loss na trénovacích datech

# Jednoduchý příklad: Kvadratická funkce
import numpy as np
import matplotlib.pyplot as plt

def f(x):
    return (x - 3)**2 + 1

x = np.linspace(-2, 8, 100)

plt.figure(figsize=(10, 5))
plt.plot(x, f(x), 'b-', lw=2, label='f(x) = (x-3)² + 1')
plt.axvline(x=3, color='red', linestyle='--', label='Minimum při x=3')
plt.scatter([3], [1], color='red', s=100, zorder=5)
plt.xlabel('x')
plt.ylabel('f(x)')
plt.title('Jednoduchý optimalizační problém')
plt.legend()
plt.grid(True, alpha=0.3)
plt.show()

print("Minimum funkce f(x) = (x-3)² + 1:")
print(f"  x* = 3")
print(f"  f(x*) = 1")

Minimum funkce f(x) = (x-3)² + 1:
  x* = 3
  f(x*) = 1

20.3 Lokální vs globální minimum

VarováníDůležitý rozdíl
  • Globální minimum: Nejnižší bod celé funkce
  • Lokální minimum: Nejnižší bod v okolí (ale ne nutně celkově nejnižší)

V hlubokém učení často končíme v lokálním minimu, ale to může být dostatečně dobré!

import numpy as np
import matplotlib.pyplot as plt

def f_complex(x):
    return x**4 - 5*x**2 + 4 + 2*np.sin(3*x)

x = np.linspace(-3, 3, 500)

plt.figure(figsize=(12, 5))
plt.plot(x, f_complex(x), 'b-', lw=2)

# Najdeme lokální minima numericky (bez scipy)
def find_local_minima(y, order=10):
    """Najde indexy lokálních minim."""
    minima = []
    for i in range(order, len(y) - order):
        if all(y[i] <= y[i-j] for j in range(1, order+1)) and \
           all(y[i] <= y[i+j] for j in range(1, order+1)):
            minima.append(i)
    return np.array(minima)

y = f_complex(x)
local_min_idx = find_local_minima(y, order=10)

for i, idx in enumerate(local_min_idx):
    color = 'green' if y[idx] == min(y[local_min_idx]) else 'orange'
    label = 'Globální minimum' if color == 'green' else 'Lokální minimum'
    if i > 0 and color == 'orange':
        label = None
    plt.scatter([x[idx]], [y[idx]], color=color, s=100, zorder=5, label=label)

plt.xlabel('x')
plt.ylabel('f(x)')
plt.title('Lokální vs Globální minimum')
plt.legend()
plt.grid(True, alpha=0.3)
plt.show()

20.3.1 Sedlové body

V mnoha dimenzích existují také sedlové body - místa, kde je gradient nulový, ale nejsou to minima:

import numpy as np
import matplotlib.pyplot as plt

def sedlova_funkce(x, y):
    return x**2 - y**2

x = np.linspace(-2, 2, 100)
y = np.linspace(-2, 2, 100)
X, Y = np.meshgrid(x, y)
Z = sedlova_funkce(X, Y)

fig = plt.figure(figsize=(14, 5))

# 3D
ax1 = fig.add_subplot(121, projection='3d')
ax1.plot_surface(X, Y, Z, cmap='RdBu', alpha=0.8)
ax1.scatter([0], [0], [0], color='red', s=100, label='Sedlový bod')
ax1.set_xlabel('x')
ax1.set_ylabel('y')
ax1.set_zlabel('f(x,y)')
ax1.set_title('Sedlová funkce: f(x,y) = x² - y²')

# Kontury
ax2 = fig.add_subplot(122)
contour = ax2.contour(X, Y, Z, levels=20, cmap='RdBu')
ax2.clabel(contour, inline=True, fontsize=8)
ax2.scatter([0], [0], color='red', s=100, label='Sedlový bod')
ax2.set_xlabel('x')
ax2.set_ylabel('y')
ax2.set_title('Kontury sedlové funkce')
ax2.legend()
ax2.set_aspect('equal')
ax2.grid(True, alpha=0.3)

plt.tight_layout()
plt.show()

print("V sedlovém bodě (0, 0):")
print("  Gradient = (0, 0) - vypadá jako minimum")
print("  Ale ve směru y funkce klesá!")

V sedlovém bodě (0, 0):
  Gradient = (0, 0) - vypadá jako minimum
  Ale ve směru y funkce klesá!

20.4 Konvexní funkce

Některé funkce mají speciální vlastnost - jsou konvexní. Pro konvexní funkce platí:

  • Každé lokální minimum je zároveň globální
  • Optimalizace je “snadná” - gradient descent vždy konverguje
PoznámkaDefinice: Konvexní funkce

Funkce \(f\) je konvexní, pokud pro libovolné body \(x_1, x_2\) a \(\lambda \in [0, 1]\):

\[f(\lambda x_1 + (1-\lambda) x_2) \leq \lambda f(x_1) + (1-\lambda) f(x_2)\]

Geometricky: úsečka mezi dvěma body na grafu leží nad grafem.

import numpy as np
import matplotlib.pyplot as plt

fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(12, 5))

# Konvexní funkce
x = np.linspace(-3, 3, 100)
f_convex = x**2

ax1.plot(x, f_convex, 'b-', lw=2, label='f(x) = x²')
# Úsečka mezi dvěma body
x1, x2 = -2, 2
ax1.plot([x1, x2], [f_convex[25], f_convex[75]], 'r--', lw=2, label='Úsečka')
ax1.fill_between(x[(x >= x1) & (x <= x2)],
                  f_convex[(x >= x1) & (x <= x2)],
                  alpha=0.3, color='green', label='Graf pod úsečkou')
ax1.set_xlabel('x')
ax1.set_ylabel('f(x)')
ax1.set_title('Konvexní funkce')
ax1.legend()
ax1.grid(True, alpha=0.3)

# Nekonvexní funkce
f_nonconvex = np.sin(2*x) + 0.5*x**2

ax2.plot(x, f_nonconvex, 'b-', lw=2, label='f(x) = sin(2x) + 0.5x²')
ax2.set_xlabel('x')
ax2.set_ylabel('f(x)')
ax2.set_title('Nekonvexní funkce')
ax2.legend()
ax2.grid(True, alpha=0.3)

plt.tight_layout()
plt.show()

print("Konvexní: x², ||x||², logistická regrese loss")
print("Nekonvexní: loss funkce neuronových sítí!")

Konvexní: x², ||x||², logistická regrese loss
Nekonvexní: loss funkce neuronových sítí!

20.5 Podmínky optima

Jak poznáme, že jsme v minimu? Derivace nám pomůže:

PoznámkaNutné podmínky pro minimum

1D funkce \(f(x)\): - Nutná podmínka: \(f'(x^*) = 0\) - Postačující: \(f''(x^*) > 0\)

Více proměnných \(f(\mathbf{x})\): - Nutná podmínka: \(\nabla f(\mathbf{x}^*) = \mathbf{0}\) (gradient je nulový) - Postačující: Hessova matice je pozitivně definitní

# Příklad: f(x) = x³ - 3x
import numpy as np
import matplotlib.pyplot as plt

def f(x):
    return x**3 - 3*x

def f_deriv(x):
    return 3*x**2 - 3

def f_deriv2(x):
    return 6*x

x = np.linspace(-2.5, 2.5, 100)

fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(12, 5))

# Funkce
ax1.plot(x, f(x), 'b-', lw=2, label='f(x) = x³ - 3x')
ax1.axhline(y=0, color='gray', linestyle='-', alpha=0.3)

# Stacionární body kde f'(x) = 0
stat_points = [-1, 1]
for sp in stat_points:
    color = 'green' if f_deriv2(sp) > 0 else 'red'
    label = 'Minimum' if f_deriv2(sp) > 0 else 'Maximum'
    ax1.scatter([sp], [f(sp)], color=color, s=100, zorder=5, label=label)

ax1.set_xlabel('x')
ax1.set_ylabel('f(x)')
ax1.set_title('Funkce a její extrémy')
ax1.legend()
ax1.grid(True, alpha=0.3)

# Derivace
ax2.plot(x, f_deriv(x), 'b-', lw=2, label="f'(x) = 3x² - 3")
ax2.axhline(y=0, color='gray', linestyle='-', alpha=0.3)
ax2.scatter(stat_points, [0, 0], color='red', s=100, zorder=5, label="f'(x) = 0")
ax2.set_xlabel('x')
ax2.set_ylabel("f'(x)")
ax2.set_title('Derivace')
ax2.legend()
ax2.grid(True, alpha=0.3)

plt.tight_layout()
plt.show()

print("Stacionární body (f'(x) = 0):")
for sp in stat_points:
    typ = "minimum" if f_deriv2(sp) > 0 else "maximum"
    print(f"  x = {sp}: f''({sp}) = {f_deriv2(sp)}{typ}")

Stacionární body (f'(x) = 0):
  x = -1: f''(-1) = -6 → maximum
  x = 1: f''(1) = 6 → minimum

20.6 Loss landscape neuronových sítí

Loss landscape skutečných neuronových sítí je velmi složitý:

import numpy as np
import matplotlib.pyplot as plt

np.random.seed(42)

def realistic_loss(x, y):
    """Simulace realistického loss landscape."""
    # Hlavní "údolí"
    base = 0.5 * ((x - 1)**2 + (y - 1)**2)
    # Lokální minima
    local = 0.3 * np.sin(4*x) * np.cos(4*y)
    # Sedlové body
    saddle = 0.2 * np.sin(2*x) * np.sin(2*y)
    # Náhodný šum
    noise = 0.1 * np.sin(10*x) * np.cos(8*y)
    return base + local + saddle + noise

x = np.linspace(-2, 4, 200)
y = np.linspace(-2, 4, 200)
X, Y = np.meshgrid(x, y)
Z = realistic_loss(X, Y)

fig, axes = plt.subplots(1, 3, figsize=(15, 5))

# 3D pohled
ax1 = fig.add_subplot(131, projection='3d')
ax1.plot_surface(X, Y, Z, cmap='viridis', alpha=0.8)
ax1.set_xlabel('w₁')
ax1.set_ylabel('w₂')
ax1.set_zlabel('Loss')
ax1.set_title('Loss Landscape (3D)')

# Kontury
ax2 = axes[1]
contour = ax2.contourf(X, Y, Z, levels=30, cmap='viridis')
plt.colorbar(contour, ax=ax2)
ax2.set_xlabel('w₁')
ax2.set_ylabel('w₂')
ax2.set_title('Loss Landscape (kontury)')

# Řez
ax3 = axes[2]
mid_y = len(y) // 2
ax3.plot(x, Z[mid_y, :], 'b-', lw=2)
ax3.set_xlabel('w₁')
ax3.set_ylabel('Loss')
ax3.set_title('Řez při w₂ = 1')
ax3.grid(True, alpha=0.3)

plt.tight_layout()
plt.show()

20.6.1 Vlastnosti loss landscape v deep learning

import numpy as np
import matplotlib.pyplot as plt

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

# 1. Mnoho lokálních minim
ax1 = axes[0]
x = np.linspace(-5, 5, 500)
y1 = np.sin(3*x) + 0.1*x**2
ax1.plot(x, y1, 'b-', lw=2)
ax1.set_title('1. Mnoho lokálních minim')
ax1.set_xlabel('Parametr')
ax1.set_ylabel('Loss')
ax1.grid(True, alpha=0.3)

# 2. Plochá minima vs ostrá minima
ax2 = axes[1]
x = np.linspace(-3, 3, 200)
# Ostré minimum
sharp = np.where(np.abs(x) < 0.5, 0, (np.abs(x) - 0.5)**2)
# Ploché minimum
flat = np.where(np.abs(x - 1) < 1, 0.1, 0.1 + (np.abs(x - 1) - 1)**2 * 0.2)
ax2.plot(x, sharp + 0.5, 'r-', lw=2, label='Ostré minimum')
ax2.plot(x, flat, 'g-', lw=2, label='Ploché minimum')
ax2.set_title('2. Plochá vs ostrá minima')
ax2.set_xlabel('Parametr')
ax2.set_ylabel('Loss')
ax2.legend()
ax2.grid(True, alpha=0.3)

# 3. Sedlové body
ax3 = axes[2]
x = np.linspace(-2, 2, 100)
y = x**3
ax3.plot(x, y, 'b-', lw=2)
ax3.scatter([0], [0], color='red', s=100, zorder=5, label='Sedlový bod')
ax3.axhline(y=0, color='gray', linestyle='--', alpha=0.5)
ax3.set_title('3. Sedlové body')
ax3.set_xlabel('Parametr')
ax3.set_ylabel('Loss')
ax3.legend()
ax3.grid(True, alpha=0.3)

plt.tight_layout()
plt.show()

print("Zajímavý fakt: Plochá minima často generalizují lépe!")
print("Ostrá minima mohou vést k přetrénování.")

Zajímavý fakt: Plochá minima často generalizují lépe!
Ostrá minima mohou vést k přetrénování.

20.7 Metody optimalizace

Existuje mnoho metod pro hledání minima:

20.7.1 1. Analytické řešení (vzácné)

Pro jednoduché případy můžeme minimum najít analyticky:

import sympy as sp

x = sp.Symbol('x')
f = (x - 3)**2 + 2*x + 5

# Derivace
f_prime = sp.diff(f, x)
print(f"f(x) = {f}")
print(f"f'(x) = {f_prime}")

# Řešení f'(x) = 0
kriticke_body = sp.solve(f_prime, x)
print(f"Kritické body: {kriticke_body}")

# Druhá derivace pro ověření
f_double_prime = sp.diff(f_prime, x)
print(f"f''(x) = {f_double_prime} > 0 → minimum")
f(x) = 2*x + (x - 3)**2 + 5
f'(x) = 2*x - 4
Kritické body: [2]
f''(x) = 2 > 0 → minimum

20.7.2 2. Grid Search (hrubá síla)

Vyhodnotíme funkci na mřížce bodů:

import numpy as np

def f_2d(x, y):
    return (x - 1)**2 + (y + 0.5)**2

# Grid search
x_grid = np.linspace(-3, 5, 50)
y_grid = np.linspace(-4, 3, 50)

best_x, best_y = None, None
best_loss = float('inf')

for x in x_grid:
    for y in y_grid:
        loss = f_2d(x, y)
        if loss < best_loss:
            best_loss = loss
            best_x, best_y = x, y

print(f"Grid Search výsledek:")
print(f"  Nejlepší bod: ({best_x:.3f}, {best_y:.3f})")
print(f"  Loss: {best_loss:.6f}")
print(f"\nSkutečné minimum: (1.0, -0.5) s loss = 0")
Grid Search výsledek:
  Nejlepší bod: (0.918, -0.429)
  Loss: 0.011766

Skutečné minimum: (1.0, -0.5) s loss = 0

20.7.3 3. Náhodné hledání

import numpy as np

np.random.seed(42)

n_samples = 1000
x_random = np.random.uniform(-3, 5, n_samples)
y_random = np.random.uniform(-4, 3, n_samples)

losses = [f_2d(x, y) for x, y in zip(x_random, y_random)]
best_idx = np.argmin(losses)

print(f"Náhodné hledání ({n_samples} vzorků):")
print(f"  Nejlepší bod: ({x_random[best_idx]:.3f}, {y_random[best_idx]:.3f})")
print(f"  Loss: {losses[best_idx]:.6f}")
Náhodné hledání (1000 vzorků):
  Nejlepší bod: (1.047, -0.414)
  Loss: 0.009539

20.7.4 4. Gradient Descent (nejdůležitější!)

Klíčová metoda pro strojové učení - využívá gradient k “sestupu” do minima:

import numpy as np
import matplotlib.pyplot as plt

def gradient_descent_demo(f, grad_f, x0, learning_rate=0.1, n_steps=50):
    """Demonstrace gradient descent."""
    path = [x0]
    x = x0

    for _ in range(n_steps):
        grad = grad_f(x)
        x = x - learning_rate * grad
        path.append(x)

    return np.array(path)

# Funkce a její gradient
def f(x):
    return (x - 3)**2 + 1

def grad_f(x):
    return 2 * (x - 3)

# Gradient descent
x0 = -1
path = gradient_descent_demo(f, grad_f, x0, learning_rate=0.1, n_steps=30)

# Vizualizace
x = np.linspace(-2, 6, 100)

plt.figure(figsize=(12, 5))

# Funkce a cesta
plt.subplot(1, 2, 1)
plt.plot(x, f(x), 'b-', lw=2, label='f(x)')
plt.plot(path, f(path), 'ro-', markersize=5, label='Gradient descent')
plt.scatter([path[0]], [f(path[0])], color='green', s=100, zorder=5, label='Start')
plt.scatter([path[-1]], [f(path[-1])], color='red', s=100, zorder=5, label='Konec')
plt.xlabel('x')
plt.ylabel('f(x)')
plt.title('Gradient Descent')
plt.legend()
plt.grid(True, alpha=0.3)

# Konvergence
plt.subplot(1, 2, 2)
plt.plot(f(path), 'b.-', markersize=8)
plt.xlabel('Iterace')
plt.ylabel('f(x)')
plt.title('Konvergence')
plt.grid(True, alpha=0.3)

plt.tight_layout()
plt.show()

print(f"Start: x = {path[0]:.4f}, f(x) = {f(path[0]):.4f}")
print(f"Konec: x = {path[-1]:.4f}, f(x) = {f(path[-1]):.4f}")
print(f"Optimum: x = 3.0, f(x) = 1.0")

Start: x = -1.0000, f(x) = 17.0000
Konec: x = 2.9950, f(x) = 1.0000
Optimum: x = 3.0, f(x) = 1.0

20.8 Řešené příklady

20.8.1 Příklad 1: Klasifikace typu kritického bodu

Zadání: Pro funkci \(f(x) = x^4 - 4x^2 + 3\) najděte všechny kritické body a určete jejich typ.

Řešení:

import numpy as np
import matplotlib.pyplot as plt

import sympy as sp

x = sp.Symbol('x')
f = x**4 - 4*x**2 + 3

f_prime = sp.diff(f, x)
f_double_prime = sp.diff(f_prime, x)

print(f"f(x) = {f}")
print(f"f'(x) = {f_prime}")
print(f"f''(x) = {f_double_prime}")

kriticke_body = sp.solve(f_prime, x)
print(f"\nKritické body: {kriticke_body}")

for kb in kriticke_body:
    hodnota_f = f.subs(x, kb)
    hodnota_f2 = f_double_prime.subs(x, kb)
    if hodnota_f2 > 0:
        typ = "lokální minimum"
    elif hodnota_f2 < 0:
        typ = "lokální maximum"
    else:
        typ = "inflexní bod"
    print(f"  x = {kb}: f''({kb}) = {hodnota_f2}{typ}, f({kb}) = {hodnota_f}")

# Vizualizace
x_num = np.linspace(-2.5, 2.5, 100)
f_num = x_num**4 - 4*x_num**2 + 3

plt.figure(figsize=(10, 5))
plt.plot(x_num, f_num, 'b-', lw=2)

for kb in kriticke_body:
    kb_float = float(kb)
    f_val = float(f.subs(x, kb))
    f2_val = float(f_double_prime.subs(x, kb))
    color = 'green' if f2_val > 0 else 'red'
    plt.scatter([kb_float], [f_val], color=color, s=100, zorder=5)

plt.xlabel('x')
plt.ylabel('f(x)')
plt.title('Kritické body funkce f(x) = x⁴ - 4x² + 3')
plt.grid(True, alpha=0.3)
plt.show()
f(x) = x**4 - 4*x**2 + 3
f'(x) = 4*x**3 - 8*x
f''(x) = 12*x**2 - 8

Kritické body: [0, -sqrt(2), sqrt(2)]
  x = 0: f''(0) = -8 → lokální maximum, f(0) = 3
  x = -sqrt(2): f''(-sqrt(2)) = 16 → lokální minimum, f(-sqrt(2)) = -1
  x = sqrt(2): f''(sqrt(2)) = 16 → lokální minimum, f(sqrt(2)) = -1

20.8.2 Příklad 2: Optimalizace ve 2D

Zadání: Najděte minimum funkce \(f(x, y) = (x-1)^2 + (y-2)^2 + xy\) pomocí gradient descent.

Řešení:

import numpy as np
import matplotlib.pyplot as plt

def f_2d(x, y):
    return (x - 1)**2 + (y - 2)**2 + x*y

def grad_f_2d(x, y):
    df_dx = 2*(x - 1) + y
    df_dy = 2*(y - 2) + x
    return np.array([df_dx, df_dy])

# Gradient descent
pos = np.array([-2.0, -1.0])  # Start
learning_rate = 0.1
path = [pos.copy()]

for _ in range(50):
    grad = grad_f_2d(pos[0], pos[1])
    pos = pos - learning_rate * grad
    path.append(pos.copy())

path = np.array(path)

# Vizualizace
x = np.linspace(-3, 4, 100)
y = np.linspace(-2, 5, 100)
X, Y = np.meshgrid(x, y)
Z = f_2d(X, Y)

plt.figure(figsize=(10, 8))
contour = plt.contour(X, Y, Z, levels=30, cmap='viridis')
plt.clabel(contour, inline=True, fontsize=8)
plt.plot(path[:, 0], path[:, 1], 'ro-', markersize=4, label='Gradient descent')
plt.scatter([path[0, 0]], [path[0, 1]], color='green', s=100, zorder=5, label='Start')
plt.scatter([path[-1, 0]], [path[-1, 1]], color='red', s=100, zorder=5, label='Konec')
plt.xlabel('x')
plt.ylabel('y')
plt.title('Gradient Descent ve 2D')
plt.legend()
plt.grid(True, alpha=0.3)
plt.show()

print(f"Start: ({path[0, 0]:.4f}, {path[0, 1]:.4f})")
print(f"Konec: ({path[-1, 0]:.4f}, {path[-1, 1]:.4f})")
print(f"Finální loss: {f_2d(path[-1, 0], path[-1, 1]):.6f}")

Start: (-2.0000, -1.0000)
Konec: (0.0026, 1.9974)
Finální loss: 1.000007

20.8.3 Příklad 3: Porovnání learning rate

Zadání: Ukažte vliv learning rate na konvergenci gradient descent.

Řešení:

import numpy as np
import matplotlib.pyplot as plt

def f(x):
    return (x - 2)**2

def grad_f(x):
    return 2 * (x - 2)

learning_rates = [0.01, 0.1, 0.5, 0.9, 1.1]
x0 = -3

fig, axes = plt.subplots(1, len(learning_rates), figsize=(20, 4))

for ax, lr in zip(axes, learning_rates):
    x = x0
    path = [x]

    for _ in range(30):
        x = x - lr * grad_f(x)
        path.append(x)
        if abs(x) > 100:  # Divergence
            break

    path = np.array(path)

    x_range = np.linspace(-5, 8, 100)
    ax.plot(x_range, f(x_range), 'b-', lw=2)

    if len(path) > 1 and not np.any(np.isnan(path)) and np.max(np.abs(path)) < 100:
        ax.plot(path, f(path), 'ro-', markersize=4)

    ax.set_title(f'lr = {lr}')
    ax.set_xlabel('x')
    ax.set_ylabel('f(x)')
    ax.set_ylim(-1, 30)
    ax.grid(True, alpha=0.3)

    status = "OK" if np.abs(path[-1] - 2) < 0.1 else ("Pomalé" if np.abs(path[-1] - 2) < 1 else "Diverguje!")
    ax.text(0.5, 0.95, status, transform=ax.transAxes, ha='center', fontsize=12,
            color='green' if status == "OK" else 'orange' if status == "Pomalé" else 'red')

plt.tight_layout()
plt.show()

20.8.4 Příklad 4: Lineární regrese jako optimalizace

Zadání: Formulujte lineární regresi jako optimalizační problém a řešte gradient descent.

Řešení:

import numpy as np
import matplotlib.pyplot as plt

np.random.seed(42)

# Generování dat
n = 50
X = np.random.uniform(0, 10, n)
y_true = 2 * X + 3 + np.random.normal(0, 1, n)

# Loss funkce: MSE
def mse_loss(w, b, X, y):
    predictions = w * X + b
    return np.mean((predictions - y)**2)

# Gradient MSE
def mse_gradient(w, b, X, y):
    predictions = w * X + b
    errors = predictions - y
    dw = 2 * np.mean(errors * X)
    db = 2 * np.mean(errors)
    return dw, db

# Gradient descent
w, b = 0.0, 0.0
learning_rate = 0.01
losses = []
params = [(w, b)]

for _ in range(200):
    loss = mse_loss(w, b, X, y_true)
    losses.append(loss)

    dw, db = mse_gradient(w, b, X, y_true)
    w -= learning_rate * dw
    b -= learning_rate * db
    params.append((w, b))

# Vizualizace
fig, axes = plt.subplots(1, 3, figsize=(15, 4))

# Data a fit
ax1 = axes[0]
ax1.scatter(X, y_true, alpha=0.7, label='Data')
x_line = np.linspace(0, 10, 100)
ax1.plot(x_line, w * x_line + b, 'r-', lw=2, label=f'Fit: y = {w:.2f}x + {b:.2f}')
ax1.plot(x_line, 2 * x_line + 3, 'g--', lw=2, label='Skutečná: y = 2x + 3')
ax1.set_xlabel('x')
ax1.set_ylabel('y')
ax1.set_title('Lineární regrese')
ax1.legend()
ax1.grid(True, alpha=0.3)

# Loss
ax2 = axes[1]
ax2.plot(losses, 'b-', lw=2)
ax2.set_xlabel('Iterace')
ax2.set_ylabel('MSE Loss')
ax2.set_title('Konvergence')
ax2.grid(True, alpha=0.3)

# Trajektorie parametrů
ax3 = axes[2]
params = np.array(params)
ax3.plot(params[:, 0], params[:, 1], 'b.-', markersize=3, alpha=0.5)
ax3.scatter([0], [0], color='green', s=100, zorder=5, label='Start')
ax3.scatter([w], [b], color='red', s=100, zorder=5, label='Konec')
ax3.scatter([2], [3], color='orange', s=100, marker='*', zorder=5, label='Optimum')
ax3.set_xlabel('w')
ax3.set_ylabel('b')
ax3.set_title('Trajektorie parametrů')
ax3.legend()
ax3.grid(True, alpha=0.3)

plt.tight_layout()
plt.show()

print(f"Naučené parametry: w = {w:.4f}, b = {b:.4f}")
print(f"Skutečné parametry: w = 2.0, b = 3.0")
print(f"Finální MSE: {losses[-1]:.4f}")

Naučené parametry: w = 2.1168, b = 2.2301
Skutečné parametry: w = 2.0, b = 3.0
Finální MSE: 1.0445

20.9 Python v praxi: Gradient Descent optimalizace

# 1D optimalizace pomocí gradient descent
import numpy as np

def f_1d(x):
    return (x - 3)**2 + np.sin(2*x)

def grad_f_1d(x):
    return 2*(x - 3) + 2*np.cos(2*x)

def optimize_1d(f, grad_f, x0, lr=0.1, n_iter=100):
    x = x0
    for _ in range(n_iter):
        x = x - lr * grad_f(x)
    return x, f(x)

x_min, f_min = optimize_1d(f_1d, grad_f_1d, x0=0.0)
print("1D optimalizace (gradient descent):")
print(f"  Minimum při x = {x_min:.4f}")
print(f"  f(x) = {f_min:.4f}")

# 2D optimalizace
def f_2d(params):
    x, y = params
    return (x - 1)**2 + (y - 2)**2 + 0.5*x*y

def grad_f_2d(params):
    x, y = params
    dx = 2*(x - 1) + 0.5*y
    dy = 2*(y - 2) + 0.5*x
    return np.array([dx, dy])

def optimize_2d(f, grad_f, x0, lr=0.1, n_iter=100):
    x = np.array(x0, dtype=float)
    for i in range(n_iter):
        x = x - lr * grad_f(x)
    return x, f(x), i+1

x_min, f_min, n_iter = optimize_2d(f_2d, grad_f_2d, x0=[0.0, 0.0])
print(f"\n2D optimalizace (gradient descent):")
print(f"  Minimum při (x, y) = ({x_min[0]:.4f}, {x_min[1]:.4f})")
print(f"  f(x, y) = {f_min:.4f}")
print(f"  Počet iterací: {n_iter}")
1D optimalizace (gradient descent):
  Minimum při x = 2.5754
  f(x) = -0.7251

2D optimalizace (gradient descent):
  Minimum při (x, y) = (0.5333, 1.8667)
  f(x, y) = 0.7333
  Počet iterací: 100

20.10 Cvičení

PoznámkaCvičení 1: Kritické body

Najděte všechny kritické body funkce \(f(x) = x^3 - 6x^2 + 9x + 1\) a určete, které jsou lokální minima, maxima nebo inflexní body.

PoznámkaCvičení 2: Konvexnost

Dokažte, že funkce \(f(x) = e^x\) je konvexní. Hint: Spočítejte druhou derivaci.

PoznámkaCvičení 3: Grid search vs random search

Porovnejte grid search a random search pro funkci \(f(x, y) = (x-\pi)^2 + (y-e)^2\) na oblasti \([0, 5] \times [0, 5]\). Kolik evaluací potřebuje každá metoda k nalezení minima s přesností 0.1?

PoznámkaCvičení 4: Sedlové body

Pro funkci \(f(x, y) = x^2 - y^2 + 2xy\) najděte všechny stacionární body a klasifikujte je pomocí Hessovy matice.

PoznámkaCvičení 5: Polynomiální regrese

Rozšiřte příklad lineární regrese na polynomiální regresi stupně 2: \(y = w_2 x^2 + w_1 x + w_0\). Implementujte gradient descent pro nalezení optimálních koeficientů.

20.11 Shrnutí

TipCo jsme se naučili
  1. Optimalizace je hledání parametrů minimalizujících loss funkci
  2. Lokální minimumglobální minimum - ale v deep learning to často nevadí
  3. Konvexní funkce mají jediné globální minimum
  4. Sedlové body jsou místa s nulovým gradientem, která nejsou extrémy
  5. Nutná podmínka pro minimum: \(\nabla f = 0\)
  6. Gradient descent je klíčová metoda - následuje záporný gradient
  7. Learning rate určuje velikost kroků a ovlivňuje konvergenci
DůležitéKlíčové pojmy
  • Loss funkce: Měří kvalitu modelu
  • Globální/lokální minimum: Nejnižší bod celkově/v okolí
  • Sedlový bod: Stacionární bod, který není extrém
  • Konvexní funkce: Každé lokální minimum je globální
  • Gradient descent: Iterativní metoda sledující záporný gradient
  • Learning rate: Velikost kroku v gradient descent

V další kapitole se podrobně podíváme na gradient descent - nejdůležitější optimalizační algoritmus v strojovém učení.