05.0 Redes Neuronales Utilidades¶
En esta carpeta se encuentran una serie de utilidades necesarias para los cuadernos sobre Redes Neuronales (RNN)
Utilidad que dibuja las curvas de nivel que determina el valor y en la region X¶
from matplotlib.colors import ListedColormap
import matplotlib.pyplot as plt
def plot_decision_regions(X, y, classifier, test_idx=None,
resolution=0.02):
# Se define el generador de marcadores y el mapa de colores
markers = ('s', 'x', 'o', '^', 'v')
colors = ('red', 'blue', 'lightgreen', 'gray', 'cyan')
cmap = ListedColormap(colors[:len(np.unique(y))])
# Se imprime la superficie de decisión
x1_min, x1_max = X[:, 0].min() - 1, X[:, 0].max() + 1
x2_min, x2_max = X[:, 1].min() - 1, X[:, 1].max() + 1
xx1, xx2 = np.meshgrid(np.arange(x1_min, x1_max, resolution),
np.arange(x2_min, x2_max, resolution))
Z = classifier.predict(np.array([xx1.ravel(), xx2.ravel()]).T)
Z = Z.reshape(xx1.shape)
# contourf describe las curvas de nivel de una función sobre la malla
plt.contourf(xx1, xx2, Z, alpha=0.3, cmap=cmap)
plt.xlim(xx1.min(), xx1.max())
plt.ylim(xx2.min(), xx2.max())
for idx, cl in enumerate(np.unique(y)):
plt.scatter(x=X[y == cl, 0], y=X[y == cl, 1],
alpha=0.8, c=colors[idx],
marker=markers[idx], label=cl,
edgecolor='black')
# highlight test samples
if test_idx:
# plot all samples
X_test, y_test = X[test_idx, :], y[test_idx]
plt.scatter(X_test[:, 0], X_test[:, 1],
c='', edgecolor='black', alpha=1.0,
linewidth=1, marker='o',
s=100, label='test set')
Las funciones contour y contourf¶
La función contour(X, Y, Z) tiene los siguientes parámetros de entrada
X : Matriz 2D con los valores X de los puntos de una malla creada con numpy.meshgrid()
Y : Matriz 2D con los valores Y de los puntos de una malla creada con numpy.meshgrid()
Z : Matriz 2D con el valor de la función
Contour dibuja la curva de nivel. Contourf dibuja la curva de nivel rellenada con color
Se dibujan las curvas de nivel del paraboloide elíptico \(y=x^2+y^2\) y del paraboloide hiperbólico \(y=x^2-y^2\)
## Curvas de nivel de la función
import matplotlib.pyplot as plt
import numpy as np
A=np.array([-3,-2,-1,0,1,2,3])
B=A
A,B=np.meshgrid(A,B)
fig = plt.figure()
#plt.contour(A,B,A**2+B**2) # Paraboloide elíptico. Curvas de nivel estándar
#plt.contour(A,B,A**2-B**2) # Paraboloide hiperbólico. Curvas de nivel estándar
#plt.contourf(A,B,A**2+B**2) # Paraboloide elíptico. Curvas de nivel con contorno relleno de color
#plt.contourf(A,B,A**2-B**2) # Paraboloide hiperbólico. Curvas de nivel con contorno relleno de color
#plt.show()
<Figure size 432x288 with 0 Axes>
Maqueta de neurona multicapa¶
def sigmoid(x):
#return 1.0/(1.0 + np.exp(-x)) ## versión básica con problemas de desbordamiento en valores x<<<0
#return np.where(x < 0, np.exp(x)/(1.0 + np.exp(x)), 1.0/(1.0 + np.exp(-x)))
#return 1. / (1. + np.exp(-np.clip(x, -250, 250)))
from scipy.special import expit
return expit(x) ##Función sigmoidea de scipy; algo más lenta
def sigmoid_derivada(x):
return sigmoid(x)*(1.0-sigmoid(x))
def tanh(x):
return np.tanh(x)
def tanh_derivada(x):
return 1.0 - np.tanh(x)**2
def ReLU(x):
return np.maximum(0, x)
def ReLU_derivada(x):
return np.where(x <= 0, 0, 1)
class NeuralNetwork(object):
"""MultiLayer LInear NEuron classifier.
Parametros
------------
eta : float Ratio de aprendizaje (entre 0.0 y 1.0)
n_iter : int Pasos sobre el conjunto de datos de entrenamiento.
capas: capas[0] las neuronas de entrada, capas[1] las neuronas de salida
random_state : int Generador de semillas de números aleatorios para inicializar los pesos.
shuffle : boolean. Hace una mezcla o barajado aleatorio del conjunto de entrenamiento en los minibatch
minibatch_size: tamaño del minibatch o subconjuntos en que se divide el conjunto de entrada para el entrenamiento
hiddenLayers: Neuronas en cada capa oculta. Aquí se excluye la capa de entrada y la de salida que la obtiene
automáticamente el proceso fit() de X e y
Atributos
-----------
w_ : Array de dimensión 1 con los pesos después del ajuste.
cost_ : lista de Suma-de-cuadrados de los valores de la función coste en cada Paso del algoritmo.
"""
def __init__(self, eta=0.01, epocas=50, hiddenLayers=[], shuffle=False, minibatch_size=0, seed=None, activacion='logistic'):
self.eta = eta
self.epocas = epocas
##self.random_state = random_state
#self.L = len(capas)-1 ### Número de Capas
#self.capas = capas
self.hiddenLayers = hiddenLayers
self.shuffle = shuffle
self.minibatch_size = minibatch_size
self.random = np.random.RandomState(seed)
if activacion == 'logistic':
self.f_activacion = sigmoid
self.f_activacion_deriv = sigmoid_derivada
#elif activacion == 'ReLU':
# self.f_activacion = ReLU
# self.f_activacion_deriv = ReLU_derivada
else:
self.f_activacion = tanh
self.f_activacion_deriv = tanh_derivada
def _onehot(self, y, n_classes):
"""Convierte las etiquetas en una representación de vectores de base canónica R^K siendo K el total de etiquetas
Parameters
------------
y : array, shape = [n_samples]
Valores objetivo.
Returns
-----------
onehot : array, shape = (n_samples, n_labels)
"""
if n_classes == 2:
onehot = np.zeros((1, y.shape[0]))
for idx, val in enumerate(y.astype(int)):
ilabel = self.Clases_y.tolist().index(val)
onehot[0, idx] = int(ilabel)
else:
onehot = np.zeros((n_classes, y.shape[0]))
for idx, val in enumerate(y.astype(int)):
ilabel = self.Clases_y.tolist().index(val)
onehot[ilabel, idx] = 1.
return onehot.T
def fit(self, X, y_inicial):
""" Ajuste con los datos de entrenamiento.
Parametros
----------
X : {Tipo array}, shape = [n_ejemplo, n_caracteristicas]
Vectores de entrenamiento, donde n_ejemplo es el numero de ejemplos y
n_caracteristicas es el número de características.
y : tipo array, shape = [n_ejemplo] Valores Objetivo.
Retorno
-------
self : objecto
"""
## Se obtiene las capas finales concatenando la de entrada y la de salida a las ocultas
#self.L = len(capas)-1 ### Número de Capas
#self.capas = capas
self.capas = [X.shape[1]] + self.hiddenLayers + [len(np.unique(y_inicial))]
self.L = len(self.capas)-1
self.minibatch_size = len(X) if (self.minibatch_size == 0) else self.minibatch_size
self.Clases_y = np.unique(y_inicial)
y = self._onehot(y_inicial, len(self.Clases_y))
self.weights = [[self.random.normal(loc=0.0, scale=0.01,size= self.capas[c])
for m in range(self.capas[c+1])] for c in range(self.L)]
self.bias = [self.random.normal(loc=0.0, scale=0.01,size= self.capas[c+1]) for c in range(self.L)]
##print("weights", self.weights)
##print("bias", self.bias)
self.coste = []
for t in range(self.epocas):
# iterate over minibatches
cost=0
indices = np.arange(X.shape[0])
if self.shuffle:
self.random.shuffle(indices)
for start_idx in range(0, indices.shape[0] - self.minibatch_size + 1, self.minibatch_size):
batch_idx = indices[start_idx:start_idx + self.minibatch_size]
Z = [] ## lista donde guarda las salidas del sumatorio en cada capa
A = [X[batch_idx]] ## lista para guardar la activación de cada capa. En la capa 1 es la X, en resto f(W(X))
##print("y", y)
### Primero se hace el avance hacia adelante
for l in range(self.L):
##print("FORWARD - CAPA ====>", l+1)
##print("entrada", A[l])
sumatorioPesos = self.aplicaPesosLinealmente(l, A[l])
activacion = self.activation(sumatorioPesos)
Z.append(sumatorioPesos) ## Se guarda la lista de sumatorios en A
A.append(activacion) ## La activación de esta capa es la entrada de la próxima
##print("sumatorioPesos", sumatorioPesos)
##print("activacion", activacion)
## Se obtiene la matriz Delta en la capa L con la tasa de cambio del error
errors = activacion - y[batch_idx] ## Los errores y - la activacion de la ultima capa
##print("errors", errors)
##print("sigmoide Prima", self.activation_prima(Z[-1]))
deltas = [errors * self.activation_prima(Z[-1])] ## delta de la capa última, la capa L
##print("deltas[ult]", len(deltas), len(deltas[0]), deltas)
### Retropropagación. Calcula desde la capa L-1 hacia atrás la matriz Delta
### propagando la tasa de cambio del error obtenido en la capa L
for l in reversed(range(self.L - 1)):
###print("self.weights[", l+1, "]=", self.weights[l+1])
###print("activacion_prima-cap[", l, "]", self.activation_prima(Z[l]))
delt_prop = np.dot(deltas[-1], self.weights[l+1])
###print("delta propagado=", delt_prop)
delta = delt_prop * self.activation_prima(Z[l])
deltas.append(delta)
###print("deltas[", l, "]=", delta)
# invertir
# Se han calculado las deltas de L hasta 1 (de salida a 1ª oculta).
# Se ponen al reves de 1(1º oculta hasta salida)
deltas.reverse()
# Recalculo de pesos y bias usando la matriz delta de cada capa
# Este proceso se puede hacer de la capa 1 a la L.
# 1. Multiplcar los delta de salida con las activaciones de entrada
# para obtener el gradiente del peso.
# 2. actualizo el peso restandole un porcentaje del gradiente
for l in range(self.L):
A_ = np.atleast_2d(A[l])
delta = np.atleast_2d(deltas[l])
##print("Ajuste - Weights Incremental[", l, "]=", -self.eta * np.transpose(A_.T.dot(delta)))
##print("Ajuste - Bias Incremental[", l, "]", -self.eta * np.sum(delta, axis=0))
self.weights[l] -= self.eta * np.transpose(A_.T.dot(delta))
self.bias[l] -= self.eta * np.sum(delta, axis=0)
##print("Ajuste - self.weights[", l, "]=", self.weights[l])
##print("Ajuste - self.bias[", l, "]=", self.bias[l])
cost_minbatch = (errors**2).sum() / (2.0 * X[batch_idx].shape[0])
cost += cost_minbatch
if t % 5000 == 0: print("Epoca =====>", t+1, "Coste ====>", cost)
self.coste.append(cost)
print("Epoca =====>", self.epocas, "Coste ====>", cost)
return self
def aplicaPesosLinealmente(self, l, X):
"""Calculate net input"""
# Con esto agregamos la unidad de Bias a la capa de entrada
return np.dot(X, np.transpose(self.weights[l])) + np.transpose(self.bias[l])
def activation(self, X):
"""Compute linear activation"""
return self.f_activacion(X)
def activation_prima(self, X):
"""Compute linear activation"""
return self.f_activacion_deriv(X)
def predict(self, X):
"""Return class label after unit step"""
# Se aplica un forward a través de todas las capas
A_=X
for l in range(self.L):
A_ = self.activation(self.aplicaPesosLinealmente(l, A_))
i_labels = np.argmax(A_, axis=1)
y_pred = [self.Clases_y[i] for i in i_labels]
return np.array(y_pred)
def predict_proba(self, X):
"""Return la probabilidad de cada clase"""
# Se aplica un forward a través de todas las capas
A_=X
for l in range(self.L):
A_ = self.activation(self.aplicaPesosLinealmente(l, A_))
return A_
Función para imprimir una cifra manuscrita y su predicción¶
import matplotlib.pyplot as plt
import numpy as np
def plotCifra(matImag, probs):
##ps = probs.data.numpy().squeeze()
fig, (ax1, ax2) = plt.subplots(figsize=(6,9), ncols=2)
ax1.imshow(matImag.reshape(28, 28))
ax1.axis('off')
ax2.barh(np.arange(10), probs)
ax2.set_aspect(0.1)
ax2.set_yticks(np.arange(10))
ax2.set_yticklabels(np.arange(10))
ax2.set_title('Probabilidad')
ax2.set_xlim(0, 1.1)
plt.tight_layout()
import matplotlib.pyplot as plt
def plotCifra_old(matImag, probs):
fig1, ax1 = plt.subplots()
img = matImag.reshape(28, 28)
ax1.imshow(img, cmap='Greys')
ax1.set_xticks([])
ax1.set_yticks([])
plt.tight_layout()
plt.show()
fig, ax = plt.subplots()
# Example data
cifras = ('0', '1', '2', '3', '4', '5', '6', '7', '8', '9')
y_pos = np.arange(len(cifras))
ax.barh(y_pos, probs, align='center')
ax.set_yticks(y_pos)
ax.set_yticklabels(cifras)
ax.invert_yaxis() # labels read top-to-bottom
ax.set_xlabel('Probabilidad')
ax.set_title('Predicción de cifra manuscrita')
plt.show()
Utilidades para pruebas con pytorch¶
import matplotlib.pyplot as plt
import numpy as np
from torch import nn, optim
from torch.autograd import Variable
def test_network(net, trainloader):
criterion = nn.MSELoss()
optimizer = optim.Adam(net.parameters(), lr=0.001)
dataiter = iter(trainloader)
images, labels = dataiter.next()
# Create Variables for the inputs and targets
inputs = Variable(images)
targets = Variable(images)
# Clear the gradients from all Variables
optimizer.zero_grad()
# Forward pass, then backward pass, then update weights
output = net.forward(inputs)
loss = criterion(output, targets)
loss.backward()
optimizer.step()
return True
def imshow(image, ax=None, title=None, normalize=True):
"""Imshow for Tensor."""
if ax is None:
fig, ax = plt.subplots()
image = image.numpy().transpose((1, 2, 0))
if normalize:
mean = np.array([0.485, 0.456, 0.406])
std = np.array([0.229, 0.224, 0.225])
image = std * image + mean
image = np.clip(image, 0, 1)
ax.imshow(image)
ax.spines['top'].set_visible(False)
ax.spines['right'].set_visible(False)
ax.spines['left'].set_visible(False)
ax.spines['bottom'].set_visible(False)
ax.tick_params(axis='both', length=0)
ax.set_xticklabels('')
ax.set_yticklabels('')
return ax
def view_recon(img, recon):
''' Function for displaying an image (as a PyTorch Tensor) and its
reconstruction also a PyTorch Tensor
'''
fig, axes = plt.subplots(ncols=2, sharex=True, sharey=True)
axes[0].imshow(img.numpy().squeeze())
axes[1].imshow(recon.data.numpy().squeeze())
for ax in axes:
ax.axis('off')
ax.set_adjustable('box-forced')
def view_classify(img, ps, version="MNIST"):
''' Function for viewing an image and it's predicted classes.
'''
ps = ps.data.numpy().squeeze()
fig, (ax1, ax2) = plt.subplots(figsize=(6,9), ncols=2)
ax1.imshow(img.resize_(1, 28, 28).numpy().squeeze())
ax1.axis('off')
ax2.barh(np.arange(10), ps)
ax2.set_aspect(0.1)
ax2.set_yticks(np.arange(10))
if version == "MNIST":
ax2.set_yticklabels(np.arange(10))
elif version == "Fashion":
ax2.set_yticklabels(['Camiseta/top',
'Pantalones',
'Jersey',
'Vestido',
'Abrigo',
'Sandalia',
'Camisa',
'Deportivas',
'Maleta',
'Bota'], size='small');
ax2.set_title('Probabilidad')
ax2.set_xlim(0, 1.1)
plt.tight_layout()