Teorema del aproximador universal¶

CHAPTER 4. A visual proof that neural nets can compute any function

Dada una función continua cualquiera $f(\vec{x})$, existe una red neuronal de tres capas tal que para cada entrada $x$ la salida es $\hat{f}(\vec{x})$ con $|\hat{f}(\vec{x}) - f(x)| < \varepsilon$.

Comprendiendo la función de activación¶

Consideremos el funcionamiento de un solo perceptrón:

con las funciones: \begin{align} \sigma(z) &= \frac{1}{1 + e^{-z}} \\ z &= wx+b \end{align}

Juega con los parámetros $w$ y $b$ para ver su efecto en la función de salida.

In [1]:
import numpy as np
import matplotlib.pyplot as plt
plt.style.use('ggplot')
from ipywidgets import interact
import ipywidgets as widgets
In [2]:
@interact(
    w = widgets.FloatLogSlider(value=8, base=10,
        min=-2, # max exponent of base
        max=2.5, # min exponent of base
    ),
    b = widgets.FloatSlider(value=3, min=-15, max=15),
)
def plot_perceptron_function(w, b):
    fig, axes = plt.subplots(1, 2, figsize=(12,6))
    x = np.linspace(-2,2)
    z = w * x + b
    sigma_z = 1 / (1 + np.exp(-z))
    
    axes[0].plot(x,z)
    axes[1].plot(x,sigma_z)
    
    axes[0].set_title("$z = wx + b$")
    axes[1].set_title("$\sigma(x) = 1/(1 + e^{-(wx+b)})$")
    axes[0].set_xlabel("$x$")
    axes[1].set_xlabel("$x$")
    axes[0].set_ylabel("$z$")
    axes[1].set_ylabel("$\sigma(x)$")
interactive(children=(FloatLogSlider(value=8.0, description='w', max=2.5, min=-2.0), FloatSlider(value=3.0, de…

Parametrización por frontera¶

Para valores muy grandes de $w$ tenemos casi una función escalón. Su posición está dada por:

\begin{align} s = -\frac{b}{w} \end{align}
In [3]:
@interact(
    s = widgets.FloatSlider(value=0.1, min=-2, max=1.5),
)
def plot_perceptron_function(s):
    w = 200
    b = -s*w
    
    x = np.linspace(-2,2)
    z = w * x + b
    sigma_z = 1 / (1 + np.exp(-z))
    
    plt.plot(x, sigma_z, label=f"$s = {s}$")
    plt.title("$\sigma(x) = 1/(1 + e^{-(wx+b)})$")
    plt.xlabel("$x$")
    plt.ylabel("$\sigma(x)$")
    plt.legend()
interactive(children=(FloatSlider(value=0.1, description='s', max=1.5, min=-2.0), Output()), _dom_classes=('wi…

Interacción entre perceptrones¶

Utilizamos ahora dos perceptrones aproximando un escalón para generar los bloques constructores de esta muestra.

In [4]:
@interact(
    s1 = widgets.FloatSlider(value=0.4, min=-1, max=1.5, step=0.02),
    s2 = widgets.FloatSlider(value=0.6, min=-1, max=1.5, step=0.02),
    w1 = widgets.FloatSlider(value=0.6, min=-2, max=2, step=0.02),
    w2 = widgets.FloatSlider(value=1.2, min=-2, max=2, step=0.02),
)
def plot_two_perceptrons(s1,s2,w1,w2):
    w = 200
    b1_1 = -s1*w
    b2_1 = -s2*w
    
    x = np.linspace(-2,2)
    z1 = w * x + b1_1
    sigma_z1 = 1 / (1 + np.exp(-z1))
    z2 = w * x + b2_1
    sigma_z2 = 1 / (1 + np.exp(-z2))
    
    y = w1 * sigma_z1 + w2 * sigma_z2
    
    plt.plot(x, y, label=f"$s_1={s1},s_2={s2},w_1={w1},w_2={w2}$")
    plt.title("$f(x) = w_1 a_1 + w_2 a_2$")
    plt.xlabel("$x$")
    plt.ylabel("$y$")
    plt.legend()
interactive(children=(FloatSlider(value=0.4, description='s1', max=1.5, min=-1.0, step=0.02), FloatSlider(valu…

Aproximación¶

Propongamos como ejemplo la función de una dimensión: \begin{align} f(x) = \frac{x}{2} \left(\text{sen}(20x) - \text{cos}\left(\frac{20}{3}x\right)\right) \end{align}

In [5]:
def f(x):
    """
    Función objetivo.
    """
    return x/2 * (np.sin(20*x) - np.cos(20*x/3))
In [6]:
# Sólo por ilustrar el concepto,
# graficaremos barras directamente en lugar de 
# evaluar los pares de perceptrones.
@interact(
    h1 = widgets.FloatSlider(value=0.01, min=-1, max=1, step=0.02),
    h2 = widgets.FloatSlider(value=-0.1, min=-1, max=1, step=0.02),
    h3 = widgets.FloatSlider(value=0.38, min=-1, max=1, step=0.02),
    h4 = widgets.FloatSlider(value=-0.04, min=-1, max=1, step=0.02),
    h5 = widgets.FloatSlider(value=0.34, min=-1, max=1, step=0.02),
    h6 = widgets.FloatSlider(value=-0.82, min=-1, max=1, step=0.02),
)
def plot_approx(h1, h2, h3, h4, h5, h6):
    hs = [h1,h2,h3,h4,h5,h6]
    num_bars = len(hs)
    x = np.arange(0, 1, 0.02)
    y = f(x)
    
    for i, h in enumerate(hs):
        plt.bar((i+0.5)/num_bars,h, width=1/num_bars)
    plt.plot(x, y, label="f(x)")
    plt.title("$f(x) = x sin(x) - x cos(x/3)$")
    plt.xlabel("$x$")
    plt.ylabel("$y$")
    plt.legend()
interactive(children=(FloatSlider(value=0.01, description='h1', max=1.0, min=-1.0, step=0.02), FloatSlider(val…

@veroarriola

In [ ]: