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$.
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.
import numpy as np
import matplotlib.pyplot as plt
plt.style.use('ggplot')
from ipywidgets import interact
import ipywidgets as widgets
@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…
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}@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…
Utilizamos ahora dos perceptrones aproximando un escalón para generar los bloques constructores de esta muestra.
@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…
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}
def f(x):
"""
Función objetivo.
"""
return x/2 * (np.sin(20*x) - np.cos(20*x/3))
# 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…