TipCo se naučíte

V této kapitole se naučíte:

  • Co je funkce více proměnných
  • Co jsou parciální derivace
  • Co je gradient a proč je důležitý
  • Jak gradient ukazuje směr největšího růstu
  • 3D vizualizace funkcí a gradientů

14.1 Proč funkce více proměnných?

V reálném světě závisí většina věcí na více faktorech:

  • Teplota závisí na zeměpisné šířce A nadmořské výšce
  • Cena domu závisí na rozloze A lokalitě A stáří
  • Loss neuronové sítě závisí na TISÍCÍCH vah

V strojovém učení máme funkce s miliony proměnných (vah)!


14.2 Funkce dvou proměnných

Funkce dvou proměnných \(f(x, y)\) přiřazuje každé dvojici \((x, y)\) jedno číslo.

Graf takové funkce je plocha ve 3D prostoru.

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

# Funkce dvou proměnných
def f(x, y):
    return x**2 + y**2

# Vytvoření mřížky
x = np.linspace(-3, 3, 50)
y = np.linspace(-3, 3, 50)
X, Y = np.meshgrid(x, y)
Z = f(X, Y)

# 3D graf
fig = plt.figure(figsize=(12, 5))

ax1 = fig.add_subplot(121, projection='3d')
ax1.plot_surface(X, Y, Z, cmap='viridis', alpha=0.8)
ax1.set_xlabel('x')
ax1.set_ylabel('y')
ax1.set_zlabel('z = f(x,y)')
ax1.set_title('f(x, y) = x² + y²')

# Vrstevnice (contour plot)
ax2 = fig.add_subplot(122)
contour = ax2.contourf(X, Y, Z, levels=20, cmap='viridis')
plt.colorbar(contour, ax=ax2, label='f(x,y)')
ax2.set_xlabel('x')
ax2.set_ylabel('y')
ax2.set_title('Vrstevnice (pohled shora)')
ax2.set_aspect('equal')

plt.tight_layout()
plt.show()


14.3 Parciální derivace

Parciální derivace je derivace podle jedné proměnné, zatímco ostatní držíme konstantní.

Pro funkci \(f(x, y)\):

  • \(\frac{\partial f}{\partial x}\) – derivace podle \(x\) (y je konstanta)
  • \(\frac{\partial f}{\partial y}\) – derivace podle \(y\) (x je konstanta)

14.3.1 Příklad

Pro \(f(x, y) = x^2 + xy + y^2\):

\[\frac{\partial f}{\partial x} = 2x + y\]

\[\frac{\partial f}{\partial y} = x + 2y\]

from sympy import symbols, diff

x, y = symbols('x y')
f = x**2 + x*y + y**2

df_dx = diff(f, x)
df_dy = diff(f, y)

print(f"f(x, y) = {f}")
print(f"∂f/∂x = {df_dx}")
print(f"∂f/∂y = {df_dy}")
f(x, y) = x**2 + x*y + y**2
∂f/∂x = 2*x + y
∂f/∂y = x + 2*y

14.3.2 Geometrická interpretace

Parciální derivace \(\frac{\partial f}{\partial x}\) je sklon plochy ve směru osy \(x\).

Kód
import numpy as np
import matplotlib.pyplot as plt

def f(x, y):
    return x**2 + y**2

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

# 3D pohled
ax1 = fig.add_subplot(121, projection='3d')

x = np.linspace(-2, 2, 30)
y = np.linspace(-2, 2, 30)
X, Y = np.meshgrid(x, y)
Z = f(X, Y)

ax1.plot_surface(X, Y, Z, cmap='viridis', alpha=0.5)

# Řez při y = 1
y_fixed = 1
x_line = np.linspace(-2, 2, 50)
z_line = f(x_line, y_fixed)
ax1.plot(x_line, np.ones_like(x_line) * y_fixed, z_line, 'r-', linewidth=3,
         label='Řez při y=1')

# Bod a tečna
x_bod = 1
z_bod = f(x_bod, y_fixed)
ax1.scatter([x_bod], [y_fixed], [z_bod], color='red', s=100)

# Tečna (parciální derivace ∂f/∂x = 2x = 2 v bodě x=1)
sklon = 2 * x_bod
x_tecna = np.linspace(x_bod - 0.5, x_bod + 0.5, 10)
z_tecna = z_bod + sklon * (x_tecna - x_bod)
ax1.plot(x_tecna, np.ones_like(x_tecna) * y_fixed, z_tecna, 'b-', linewidth=3)

ax1.set_xlabel('x')
ax1.set_ylabel('y')
ax1.set_zlabel('z')
ax1.set_title('∂f/∂x = sklon ve směru x')

# 2D pohled na řez
ax2 = fig.add_subplot(122)
ax2.plot(x_line, z_line, 'r-', linewidth=2, label='f(x, 1) = x² + 1')
ax2.plot(x_tecna, z_tecna, 'b-', linewidth=2, label='Tečna (sklon = 2)')
ax2.plot(x_bod, z_bod, 'ro', markersize=10)
ax2.set_xlabel('x')
ax2.set_ylabel('f(x, 1)')
ax2.set_title('Řez při y = 1')
ax2.legend()
ax2.grid(True, alpha=0.3)

plt.tight_layout()
plt.show()

Parciální derivace jako sklon ve směru osy

14.4 Gradient

Gradient je vektor všech parciálních derivací:

\[\nabla f = \begin{bmatrix} \frac{\partial f}{\partial x} \\ \frac{\partial f}{\partial y} \end{bmatrix}\]

Pro více proměnných:

\[\nabla f = \begin{bmatrix} \frac{\partial f}{\partial x_1} \\ \frac{\partial f}{\partial x_2} \\ \vdots \\ \frac{\partial f}{\partial x_n} \end{bmatrix}\]

VarováníKlíčová vlastnost!

Gradient ukazuje směr největšího růstu funkce. Záporný gradient ukazuje směr největšího poklesu – přesně to, co potřebujeme pro minimalizaci loss!

# Příklad: gradient pro f(x,y) = x² + y²
from sympy import symbols, diff, Matrix

x, y = symbols('x y')
f = x**2 + y**2

gradient = Matrix([diff(f, x), diff(f, y)])

print(f"f(x, y) = {f}")
print(f"∇f = {gradient.T}")  # Transpozice pro lepší zobrazení
f(x, y) = x**2 + y**2
∇f = Matrix([[2*x, 2*y]])

14.4.1 Vizualizace gradientu

import numpy as np
import matplotlib.pyplot as plt

def f(x, y):
    return x**2 + y**2

def gradient_f(x, y):
    return np.array([2*x, 2*y])

fig, ax = plt.subplots(figsize=(10, 8))

# Vrstevnice
x = np.linspace(-3, 3, 100)
y = np.linspace(-3, 3, 100)
X, Y = np.meshgrid(x, y)
Z = f(X, Y)

contour = ax.contour(X, Y, Z, levels=15, cmap='viridis')
ax.clabel(contour, inline=True, fontsize=8)

# Gradienty v několika bodech
body = [(-2, -2), (-2, 1), (0, 2), (1.5, -1.5), (2, 0.5)]

for bx, by in body:
    grad = gradient_f(bx, by)
    # Normalizujeme pro lepší vizualizaci
    grad_norm = grad / (np.linalg.norm(grad) + 0.001) * 0.5
    ax.arrow(bx, by, grad_norm[0], grad_norm[1],
             head_width=0.15, head_length=0.1, fc='red', ec='red')
    ax.plot(bx, by, 'ko', markersize=5)

ax.set_xlabel('x')
ax.set_ylabel('y')
ax.set_title('Gradient ∇f ukazuje směr největšího růstu\n(šipky ukazují směr od minima)')
ax.set_aspect('equal')
ax.plot(0, 0, 'g*', markersize=15, label='Minimum')
ax.legend()
plt.show()

Gradient ukazuje směr největšího růstu

14.4.2 Gradient Descent vizuálně

import numpy as np
import matplotlib.pyplot as plt

def f(x, y):
    return x**2 + y**2

def gradient_f(x, y):
    return np.array([2*x, 2*y])

# Gradient descent
start = np.array([2.5, 2.0])
learning_rate = 0.1
pozice = [start.copy()]

for _ in range(20):
    grad = gradient_f(pozice[-1][0], pozice[-1][1])
    nova_pozice = pozice[-1] - learning_rate * grad
    pozice.append(nova_pozice)

pozice = np.array(pozice)

# Vizualizace
fig, ax = plt.subplots(figsize=(10, 8))

x = np.linspace(-3, 3, 100)
y = np.linspace(-3, 3, 100)
X, Y = np.meshgrid(x, y)
Z = f(X, Y)

contour = ax.contourf(X, Y, Z, levels=20, cmap='viridis', alpha=0.7)
plt.colorbar(contour, ax=ax, label='f(x,y)')

# Trajektorie
ax.plot(pozice[:, 0], pozice[:, 1], 'ro-', markersize=8, linewidth=2)
ax.plot(pozice[0, 0], pozice[0, 1], 'r*', markersize=20, label='Start')
ax.plot(0, 0, 'g*', markersize=20, label='Minimum')

ax.set_xlabel('x')
ax.set_ylabel('y')
ax.set_title('Gradient Descent: sledujeme záporný gradient k minimu')
ax.legend()
ax.set_aspect('equal')
plt.show()

print("Trajektorie gradient descent:")
for i, pos in enumerate(pozice[:6]):
    print(f"Krok {i}: ({pos[0]:.3f}, {pos[1]:.3f}), f = {f(pos[0], pos[1]):.4f}")
print("...")
print(f"Krok {len(pozice)-1}: ({pozice[-1][0]:.6f}, {pozice[-1][1]:.6f}), f = {f(pozice[-1][0], pozice[-1][1]):.8f}")

Gradient Descent na 2D funkci
Trajektorie gradient descent:
Krok 0: (2.500, 2.000), f = 10.2500
Krok 1: (2.000, 1.600), f = 6.5600
Krok 2: (1.600, 1.280), f = 4.1984
Krok 3: (1.280, 1.024), f = 2.6870
Krok 4: (1.024, 0.819), f = 1.7197
Krok 5: (0.819, 0.655), f = 1.1006
...
Krok 20: (0.028823, 0.023058), f = 0.00136246

14.5 Gradient v NumPy

import numpy as np

def f(params):
    """Loss funkce s vektorem parametrů."""
    x, y = params
    return x**2 + y**2 + x*y

def gradient_f(params, h=1e-5):
    """Numerický gradient."""
    grad = np.zeros_like(params)
    for i in range(len(params)):
        params_plus = params.copy()
        params_plus[i] += h
        params_minus = params.copy()
        params_minus[i] -= h
        grad[i] = (f(params_plus) - f(params_minus)) / (2 * h)
    return grad

# Test
params = np.array([1.0, 2.0])
grad = gradient_f(params)

print(f"Parametry: {params}")
print(f"f(params) = {f(params)}")
print(f"Numerický gradient: {grad}")

# Analytický gradient pro srovnání: [2x + y, 2y + x]
print(f"Analytický gradient: [{2*params[0] + params[1]}, {2*params[1] + params[0]}]")
Parametry: [1. 2.]
f(params) = 7.0
Numerický gradient: [4. 5.]
Analytický gradient: [4.0, 5.0]

14.6 Loss funkce neuronové sítě

V praxi máme loss funkci závislou na tisících parametrech:

\[\mathcal{L}(w_1, w_2, ..., w_n)\]

Gradient má stejný počet složek:

\[\nabla \mathcal{L} = \begin{bmatrix} \frac{\partial \mathcal{L}}{\partial w_1} \\ \frac{\partial \mathcal{L}}{\partial w_2} \\ \vdots \\ \frac{\partial \mathcal{L}}{\partial w_n} \end{bmatrix}\]

# Simulace složitější loss funkce
import numpy as np
import matplotlib.pyplot as plt

def loss(x, y):
    return (np.sin(x) * np.cos(y) + 0.5 * x**2 + 0.3 * y**2
            + 0.2 * np.sin(3*x) * np.cos(2*y))

x = np.linspace(-3, 3, 100)
y = np.linspace(-3, 3, 100)
X, Y = np.meshgrid(x, y)
Z = loss(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('w₁')
ax1.set_ylabel('w₂')
ax1.set_zlabel('Loss')
ax1.set_title('Loss landscape (3D)')

# Vrstevnice
ax2 = fig.add_subplot(122)
contour = ax2.contourf(X, Y, Z, levels=30, cmap='viridis')
plt.colorbar(contour, ax=ax2, label='Loss')
ax2.set_xlabel('w₁')
ax2.set_ylabel('w₂')
ax2.set_title('Loss landscape (vrstevnice)')
ax2.set_aspect('equal')

plt.tight_layout()
plt.show()

Loss landscape neuronové sítě (zjednodušeno na 2D)

14.7 Řešené příklady

14.7.1 Příklad 1: Parciální derivace

Najděte parciální derivace \(f(x, y) = x^3y + xy^2 - 5xy\)

from sympy import symbols, diff

x, y = symbols('x y')
f = x**3 * y + x * y**2 - 5*x*y

print(f"f(x, y) = {f}")
print(f"∂f/∂x = {diff(f, x)}")
print(f"∂f/∂y = {diff(f, y)}")
f(x, y) = x**3*y + x*y**2 - 5*x*y
∂f/∂x = 3*x**2*y + y**2 - 5*y
∂f/∂y = x**3 + 2*x*y - 5*x

14.7.2 Příklad 2: Gradient v bodě

Vypočítejte gradient funkce \(f(x, y) = x^2 + 2xy + y^2\) v bodě \((1, 2)\).

from sympy import symbols, diff, Matrix

x, y = symbols('x y')
f = x**2 + 2*x*y + y**2

df_dx = diff(f, x)  # 2x + 2y
df_dy = diff(f, y)  # 2x + 2y

print(f"f(x, y) = {f}")
print(f"∂f/∂x = {df_dx}")
print(f"∂f/∂y = {df_dy}")

# V bodě (1, 2)
grad_x = df_dx.subs([(x, 1), (y, 2)])
grad_y = df_dy.subs([(x, 1), (y, 2)])

print(f"\nGradient v bodě (1, 2): [{grad_x}, {grad_y}]")
f(x, y) = x**2 + 2*x*y + y**2
∂f/∂x = 2*x + 2*y
∂f/∂y = 2*x + 2*y

Gradient v bodě (1, 2): [6, 6]

14.7.3 Příklad 3: Směr největšího růstu

V jakém směru roste funkce \(f(x, y) = x^2 - y^2\) nejrychleji v bodě \((2, 1)\)?

# ∂f/∂x = 2x, ∂f/∂y = -2y
# V bodě (2, 1): gradient = [4, -2]

import numpy as np

gradient = np.array([4, -2])
smer = gradient / np.linalg.norm(gradient)

print(f"Gradient v (2, 1): {gradient}")
print(f"Směr největšího růstu: {smer.round(3)}")
print(f"Velikost gradientu: {np.linalg.norm(gradient):.3f}")
Gradient v (2, 1): [ 4 -2]
Směr největšího růstu: [ 0.894 -0.447]
Velikost gradientu: 4.472

14.7.4 Příklad 4: Gradient descent krok

Máme \(f(x, y) = x^2 + 4y^2\), jsme v bodě \((3, 2)\), learning rate je \(0.1\). Kam se posuneme?

# Gradient: [2x, 8y]
import numpy as np

pozice = np.array([3.0, 2.0])
lr = 0.1

gradient = np.array([2 * pozice[0], 8 * pozice[1]])
nova_pozice = pozice - lr * gradient

print(f"Aktuální pozice: {pozice}")
print(f"Gradient: {gradient}")
print(f"Nová pozice: {nova_pozice}")
print(f"f před: {pozice[0]**2 + 4*pozice[1]**2}")
print(f"f po: {nova_pozice[0]**2 + 4*nova_pozice[1]**2}")
Aktuální pozice: [3. 2.]
Gradient: [ 6. 16.]
Nová pozice: [2.4 0.4]
f před: 25.0
f po: 6.3999999999999995

14.8 Cvičení

VarováníCvičení 1: Parciální derivace

Najděte \(\frac{\partial f}{\partial x}\) a \(\frac{\partial f}{\partial y}\) pro \(f(x,y) = 3x^2y - y^3 + 2x\)

Výsledky: \(6xy + 2\) a \(3x^2 - 3y^2\)

VarováníCvičení 2: Gradient

Vypočítejte gradient \(f(x, y) = e^{x+y}\) v bodě \((0, 0)\).

Výsledek: \([1, 1]\)

Řešení

\(\frac{\partial f}{\partial x} = e^{x+y}\), \(\frac{\partial f}{\partial y} = e^{x+y}\)

V \((0, 0)\): gradient = \([e^0, e^0] = [1, 1]\)
VarováníCvičení 3: Kritický bod

Najděte bod, kde je gradient funkce \(f(x, y) = x^2 + y^2 - 2x - 4y\) nulový.

Výsledek: \((1, 2)\)

Řešení

\(\nabla f = [2x - 2, 2y - 4] = [0, 0]\)

\(x = 1\), \(y = 2\)
VarováníCvičení 4: Gradient descent

Funkce \(f(x, y) = x^2 + y^2\). Startujeme v \((4, 3)\) s learning rate \(0.2\). Jaká je pozice po 2 krocích?

Řešení

Krok 1: \(\nabla f = [8, 6]\), nová pozice: \((4 - 0.2 \cdot 8, 3 - 0.2 \cdot 6) = (2.4, 1.8)\)

Krok 2: \(\nabla f = [4.8, 3.6]\), nová pozice: \((2.4 - 0.96, 1.8 - 0.72) = (1.44, 1.08)\)
VarováníCvičení 5: Tří proměnné

Vypočítejte gradient \(f(x, y, z) = x^2 + y^2 + z^2\).

Výsledek: \(\nabla f = [2x, 2y, 2z]\)


14.9 Shrnutí

PoznámkaCo si zapamatovat
  • Funkce více proměnných: \(f(x_1, x_2, ..., x_n) \rightarrow \mathbb{R}\)
  • Parciální derivace \(\frac{\partial f}{\partial x_i}\): derivace podle jedné proměnné, ostatní konstantní
  • Gradient: \(\nabla f = [\frac{\partial f}{\partial x_1}, ..., \frac{\partial f}{\partial x_n}]\)
  • Gradient ukazuje směr největšího růstu
  • Gradient descent: \(\mathbf{x}_{new} = \mathbf{x}_{old} - \alpha \nabla f\)
  • V neuronových sítích gradient loss podle vah říká, jak je upravit

V další kapitole se naučíme o automatické derivaci – jak počítače počítají gradienty automaticky pomocí PyTorch.