Ricerca…


Per iniziare: una semplice ANN con Python

Il seguente elenco di codici tenta di classificare le cifre scritte a mano dal set di dati MNIST. Le cifre assomigliano a questo:

MNIST

Il codice eseguirà il preprocesso di queste cifre, convertendo ciascuna immagine in una matrice 2D di 0 e 1 e quindi utilizzando questi dati per addestrare una rete neurale con un'accuratezza del 97% (50 epoche).

"""
Deep Neural Net 

(Name: Classic Feedforward)

"""

import numpy as np
import pickle, json
import sklearn.datasets
import random
import time
import os


def sigmoid(z):
    return 1.0 / (1.0 + np.exp(-z))

def sigmoid_prime(z):
    return sigmoid(z) * (1 - sigmoid(z))

def relU(z):
    return np.maximum(z, 0, z)
    
def relU_prime(z):
    return z * (z <= 0)
    
def tanh(z):
    return np.tanh(z)

def tanh_prime(z):
    return 1 - (tanh(z) ** 2)

def transform_target(y):
    t = np.zeros((10, 1))
    t[int(y)] = 1.0
    return t

"""--------------------------------------------------------------------------------"""

class NeuralNet:

    def __init__(self, layers, learning_rate=0.05, reg_lambda=0.01):
        self.num_layers = len(layers)
        self.layers = layers
        self.biases = [np.zeros((y, 1)) for y in layers[1:]]
        self.weights = [np.random.normal(loc=0.0, scale=0.1, size=(y, x)) for x, y in zip(layers[:-1], layers[1:])]
        self.learning_rate = learning_rate
        self.reg_lambda = reg_lambda
        self.nonlinearity = relU
        self.nonlinearity_prime = relU_prime

    def __feedforward(self, x):
        """ Returns softmax probabilities for the output layer """
        for w, b in zip(self.weights, self.biases):
            x = self.nonlinearity(np.dot(w, np.reshape(x, (len(x), 1))) + b)

        return np.exp(x) / np.sum(np.exp(x))

    def __backpropagation(self, x, y):
        """
        :param x: input
        :param y: target
        """
        weight_gradients = [np.zeros(w.shape) for w in self.weights]
        bias_gradients = [np.zeros(b.shape) for b in self.biases]

        # forward pass
        activation = x
        hidden_activations = [np.reshape(x, (len(x), 1))]
        z_list = []

        for w, b in zip(self.weights, self.biases):    
            z = np.dot(w, np.reshape(activation, (len(activation), 1))) + b
            z_list.append(z)
            activation = self.nonlinearity(z)
            hidden_activations.append(activation)

        t = hidden_activations[-1] 
        hidden_activations[-1] = np.exp(t) / np.sum(np.exp(t))

        # backward pass
        delta = (hidden_activations[-1] - y) * (z_list[-1] > 0)
        weight_gradients[-1] = np.dot(delta, hidden_activations[-2].T)
        bias_gradients[-1] = delta

        for l in range(2, self.num_layers):
            z = z_list[-l]
            delta = np.dot(self.weights[-l + 1].T, delta) * (z > 0)
            weight_gradients[-l] = np.dot(delta, hidden_activations[-l - 1].T)
            bias_gradients[-l] = delta

        return (weight_gradients, bias_gradients)

    def __update_params(self, weight_gradients, bias_gradients):
        for i in xrange(len(self.weights)):
            self.weights[i] += -self.learning_rate * weight_gradients[i]
            self.biases[i] += -self.learning_rate * bias_gradients[i]

    def train(self, training_data, validation_data=None, epochs=10):
        bias_gradients = None
        for i in xrange(epochs):
            random.shuffle(training_data)
            inputs = [data[0] for data in training_data]
            targets = [data[1] for data in training_data]

            for j in xrange(len(inputs)):
                (weight_gradients, bias_gradients) = self.__backpropagation(inputs[j], targets[j])
                self.__update_params(weight_gradients, bias_gradients)

            if validation_data: 
                random.shuffle(validation_data)
                inputs = [data[0] for data in validation_data]
                targets = [data[1] for data in validation_data]

                for j in xrange(len(inputs)):
                    (weight_gradients, bias_gradients) = self.__backpropagation(inputs[j], targets[j])
                    self.__update_params(weight_gradients, bias_gradients)

            print("{} epoch(s) done".format(i + 1))

        print("Training done.")

    def test(self, test_data):
        test_results = [(np.argmax(self.__feedforward(x[0])), np.argmax(x[1])) for x in test_data]
        return float(sum([int(x == y) for (x, y) in test_results])) / len(test_data) * 100

    def dump(self, file):
        pickle.dump(self, open(file, "wb"))

"""--------------------------------------------------------------------------------"""

if __name__ == "__main__":
    total = 5000
    training = int(total * 0.7)
    val = int(total * 0.15)
    test = int(total * 0.15)

    mnist = sklearn.datasets.fetch_mldata('MNIST original', data_home='./data')

    data = zip(mnist.data, mnist.target)
    random.shuffle(data)
    data = data[:total]
    data = [(x[0].astype(bool).astype(int), transform_target(x[1])) for x in data]

    train_data = data[:training]
    val_data = data[training:training+val]
    test_data = data[training+val:]

    print "Data fetched"

    NN = NeuralNet([784, 32, 10]) # defining an ANN with 1 input layer (size 784 = size of the image flattened), 1 hidden layer (size 32), and 1 output layer (size 10, unit at index i will predict the probability of the image being digit i, where 0 <= i <= 9)  

    NN.train(train_data, val_data, epochs=5)

    print "Network trained"

    print "Accuracy:", str(NN.test(test_data)) + "%"

Questo è un esempio di codice autonomo e può essere eseguito senza ulteriori modifiche. Assicurati di aver installato numpy e scikit per la tua versione di python.

Backpropagation - The Heart of Neural Networks

L'obiettivo di backpropagation è ottimizzare i pesi in modo che la rete neurale possa imparare come mappare correttamente gli input e gli output arbitrari.

Ogni livello ha il proprio set di pesi e questi pesi devono essere calibrati per poter prevedere con precisione il giusto input dato in input.

Una panoramica di alto livello della propagazione del retro è la seguente:

  1. Forward pass - l'input viene trasformato in un output. Ad ogni livello, l'attivazione viene calcolata con un prodotto punto tra l'input e i pesi, seguito dalla somma del risultante con il bias. Infine, questo valore viene passato attraverso una funzione di attivazione, per ottenere l'attivazione di quel livello che diventerà l'input per il livello successivo.
  2. Nell'ultimo livello, l'output viene confrontato con l'etichetta effettiva corrispondente a quell'input e l'errore viene calcolato. Di solito, è l'errore quadratico medio.
  3. Passaggio all'indietro: l'errore calcolato nel passaggio 2 viene propagato ai livelli interni e i pesi di tutti i livelli vengono adattati per tenere conto di questo errore.

1. Inizializzazione dei pesi

Di seguito è riportato un esempio semplificato di inizializzazione dei pesi:

layers = [784, 64, 10] 
weights = np.array([(np.random.randn(y, x) * np.sqrt(2.0 / (x + y))) for x, y in zip(layers[:-1], layers[1:])])
biases = np.array([np.zeros((y, 1)) for y in layers[1:]])
  • Il livello nascosto 1 ha il peso della dimensione [64, 784] e il bias della dimensione 64.

  • Il livello di output ha il peso della dimensione [10, 64] e il bias della dimensione

Ci si potrebbe chiedere cosa sta succedendo quando si inizializzano i pesi nel codice sopra. Questa è chiamata inizializzazione Xavier ed è un passo migliore dell'inizializzazione casuale delle matrici di peso. Sì, l'inizializzazione conta. In base alla tua inizializzazione, potresti essere in grado di trovare un minimo locale migliore durante la discesa del gradiente (la propagazione posteriore è una versione glorificata della discesa del gradiente).

2. Passaggio in avanti

activation = x
hidden_activations = [np.reshape(x, (len(x), 1))]
z_list = []

for w, b in zip(self.weights, self.biases):    
    z = np.dot(w, np.reshape(activation, (len(activation), 1))) + b
    z_list.append(z)
    activation = relu(z)
    hidden_activations.append(activation)

t = hidden_activations[-1] 
hidden_activations[-1] = np.exp(t) / np.sum(np.exp(t))

Questo codice esegue la trasformazione sopra descritta. hidden_activations[-1] contiene le probabilità softmax - le previsioni di tutte le classi, la cui somma è 1. Se si prevedono cifre, l'output sarà un vettore di probabilità della dimensione 10, la cui somma è 1.

3. Passaggio a ritroso

weight_gradients = [np.zeros(w.shape) for w in self.weights]
bias_gradients = [np.zeros(b.shape) for b in self.biases]

delta = (hidden_activations[-1] - y) * (z_list[-1] > 0) # relu derivative
weight_gradients[-1] = np.dot(delta, hidden_activations[-2].T)
bias_gradients[-1] = delta

for l in range(2, self.num_layers):
    z = z_list[-l]
    delta = np.dot(self.weights[-l + 1].T, delta) * (z > 0) # relu derivative
    weight_gradients[-l] = np.dot(delta, hidden_activations[-l - 1].T)
    bias_gradients[-l] = delta

Le prime 2 righe inizializzano i gradienti. Questi gradienti sono calcolati e verranno utilizzati per aggiornare i pesi e i bias successivi.

Le 3 linee successive calcolano l'errore sottraendo la previsione dalla destinazione. L'errore viene quindi nuovamente propagato ai livelli interni.

Ora, traccia attentamente il funzionamento del ciclo. Le righe 2 e 3 trasformano l'errore dal layer[i] al layer[i - 1] . Traccia le forme delle matrici moltiplicate per capire.

4. Aggiornamento pesi / parametri

for i in xrange(len(self.weights)):
    self.weights[i] += -self.learning_rate * weight_gradients[i]
    self.biases[i] += -self.learning_rate * bias_gradients[i] 

self.learning_rate specifica la velocità con cui la rete impara. Non vuoi che impari troppo velocemente, perché potrebbe non convergere. Una discesa morbida è favorita per trovare una buona minima. Di solito, i tassi tra 0.01 e 0.1 sono considerati buoni.

Funzioni di attivazione

Le funzioni di attivazione, note anche come funzione di trasferimento, vengono utilizzate per mappare i nodi di input per i nodi di output in un determinato modo.

Sono utilizzati per impartire non linearità all'output di uno strato di rete neurale.

Di seguito alcune funzioni comunemente utilizzate e le loro curve: Funzioni di attivazione


Funzione sigmoide

Il sigmoid è una funzione di compressione il cui output è nell'intervallo [0, 1] .

inserisci la descrizione dell'immagine qui

Il codice per implementare sigmoid insieme alla sua derivata con numpy è mostrato di seguito:

def sigmoid(z):
    return 1.0 / (1.0 + np.exp(-z))

def sigmoid_prime(z):
    return sigmoid(z) * (1 - sigmoid(z))

Funzione iperbolica tangente (tanh)

La differenza fondamentale tra le funzioni tanh e sigmoide è che tanh è 0 centrato, schiaccia gli input nell'intervallo [-1, 1] ed è più efficiente da calcolare.

inserisci la descrizione dell'immagine qui

È possibile utilizzare facilmente le funzioni np.tanh o math.tanh per calcolare l'attivazione di un livello nascosto.

Funzione ReLU

Un'unità lineare rettificata non fa altro che max(0,x) . È una delle scelte più comuni per le funzioni di attivazione delle unità di rete neurali.

inserisci la descrizione dell'immagine qui

Le ReLU risolvono il problema del gradiente di fuga delle unità tangenti sigmoide / iperboliche, consentendo una propagazione graduale efficiente nelle reti profonde.

Il nome ReLU viene dalla carta di Nair e Hinton, le unità rettificate rettificate migliorano le macchine Boltzmann con restrizioni .

Ha alcune variazioni, ad esempio, riLU (leali) (LReLU) e unità lineari esponenziali (ELU).

Il codice per l'implementazione di ReLU vanilla insieme alla sua derivata con numpy è mostrato di seguito:

def relU(z):
    return z * (z > 0)

def relU_prime(z):
    return z > 0

Funzione Softmax

La regressione di Softmax (o regressione logistica multinomiale) è una generalizzazione della regressione logistica nel caso in cui vogliamo gestire più classi. È particolarmente utile per le reti neurali in cui vogliamo applicare la classificazione non binaria. In questo caso, la semplice regressione logistica non è sufficiente. Avremmo bisogno di una distribuzione di probabilità su tutte le etichette, che è ciò che ci offre Softmax.

Softmax è calcolato con la seguente formula:

Formula

___________________________Dove si adatta? _____________________________

SoftMax Per normalizzare un vettore applicando la funzione softmax ad esso con numpy , utilizzare:

np.exp(x) / np.sum(np.exp(x))

Dove x è l'attivazione dallo strato finale della RNA.



Modified text is an extract of the original Stack Overflow Documentation
Autorizzato sotto CC BY-SA 3.0
Non affiliato con Stack Overflow