Zoeken…


Aan de slag: een eenvoudige ANN met Python

De onderstaande codelijst probeert handgeschreven cijfers uit de MNIST-gegevensset te classificeren. De cijfers zien er zo uit:

MNIST

De code zal deze cijfers voorbewerken, waarbij elke afbeelding wordt omgezet in een 2D-reeks van 0'en en 1'en en vervolgens deze gegevens gebruiken om een neuraal netwerk te trainen met een nauwkeurigheid tot 97% (50 epochs).

"""
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)) + "%"

Dit is een zelfstandig codevoorbeeld en kan zonder verdere wijzigingen worden uitgevoerd. Zorg ervoor dat je numpy en scikit learn hebt geïnstalleerd voor je versie van python.

Backpropagation - The Heart of Neural Networks

Het doel van backpropagation is het optimaliseren van de gewichten zodat het neurale netwerk kan leren hoe willekeurige ingangen correct kunnen worden toegewezen aan uitgangen.

Elke laag heeft zijn eigen set gewichten en deze gewichten moeten worden afgestemd om de juiste output gegeven input nauwkeurig te kunnen voorspellen.

Een overzicht op hoog niveau van rugpropagatie is als volgt:

  1. Voorwaartse doorgang - de invoer wordt omgezet in een uitvoer. Bij elke laag wordt de activering berekend met een puntproduct tussen de invoer en de gewichten, gevolgd door het optellen van de resultante met de voorspanning. Ten slotte wordt deze waarde door een activeringsfunctie geleid om de activering van die laag te krijgen die de invoer voor de volgende laag wordt.
  2. In de laatste laag wordt de uitvoer vergeleken met het werkelijke label dat overeenkomt met die invoer en wordt de fout berekend. Meestal is dit de gemiddelde kwadratische fout.
  3. Achterwaartse doorgang - de fout berekend in stap 2 wordt teruggevoerd naar de binnenste lagen en de gewichten van alle lagen worden aangepast om deze fout te verklaren.

1. Initialisatie van gewichten

Een vereenvoudigd voorbeeld van initialisatie van gewichten wordt hieronder weergegeven:

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:]])
  • Verborgen laag 1 heeft het gewicht van afmeting [64, 784] en voorspanning van afmeting 64.

  • De uitvoerlaag heeft het gewicht van dimensie [10, 64] en een voorspanning van dimensie

Je vraagt je misschien af wat er aan de hand is bij het initialiseren van gewichten in de bovenstaande code. Dit wordt Xavier-initialisatie genoemd en het is een stap beter dan het willekeurig initialiseren van uw gewichtsmatrices. Ja, initialisatie is belangrijk. Op basis van uw initialisatie, kunt u mogelijk een betere lokale minima vinden tijdens verloopdaling (rugvoortplanting is een verheerlijkte versie van verloopdaling).

2. Voorwaartse pas

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))

Deze code voert de hierboven beschreven transformatie uit. hidden_activations[-1] bevat softmax-kansen - voorspellingen van alle klassen, waarvan de som 1 is. Als we cijfers voorspellen, is de uitvoer een vector van kansen van dimensie 10, waarvan de som 1 is.

3. Achteruitgang

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

De eerste 2 lijnen initialiseren de verlopen. Deze gradiënten worden berekend en zullen later worden gebruikt om de gewichten en vooroordelen bij te werken.

De volgende 3 regels berekenen de fout door de voorspelling af te trekken van het doel. De fout wordt vervolgens terug verspreid naar de binnenste lagen.

Volg nu voorzichtig de werking van de lus. Lijnen 2 en 3 transformeren de fout van layer[i] naar layer[i - 1] . Volg de vormen van de matrices die worden vermenigvuldigd om te begrijpen.

4. Update gewichten / parameters

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 geeft de snelheid aan waarmee het netwerk leert. Je wilt niet dat het te snel leert, omdat het misschien niet convergeert. Een soepele afdaling wordt aanbevolen voor het vinden van een goede minima. Gewoonlijk worden snelheden tussen 0.01 en 0.1 als goed beschouwd.

Activeringsfuncties

Activeringsfuncties, ook wel overdrachtsfunctie genoemd, worden gebruikt om ingangsknopen op een bepaalde manier aan uitgangsknopen toe te wijzen.

Ze worden gebruikt om niet-lineariteit te verlenen aan de output van een neurale netwerklaag.

Enkele veelgebruikte functies en hun curven worden hieronder gegeven: Activeringsfuncties


Sigmoïde functie

De sigmoïde is een squashfunctie waarvan de uitvoer in het bereik [0, 1] .

voer hier de afbeeldingsbeschrijving in

De code voor het implementeren van sigmoïde samen met de afgeleide met numpy wordt hieronder weergegeven:

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

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

Hyperbolische tangensfunctie (tanh)

Het basisverschil tussen de functies tanh en sigmoïde is dat tanh 0 gecentreerd is, ingangen platdrukken in het bereik [-1, 1] en efficiënter is om te berekenen.

voer hier de afbeeldingsbeschrijving in

U kunt eenvoudig de functies np.tanh of math.tanh gebruiken om de activering van een verborgen laag te berekenen.

ReLU-functie

Een gerectificeerde lineaire eenheid doet eenvoudig max(0,x) . Het is een van de meest voorkomende keuzes voor activeringsfuncties van neurale netwerkeenheden.

voer hier de afbeeldingsbeschrijving in

ReLU's pakken het verdwijnende gradiëntprobleem van sigmoïde / hyperbolische tangenseenheden aan, waardoor efficiënte gradiëntvoortplanting in diepe netwerken mogelijk wordt gemaakt.

De naam ReLU komt van het papier van Nair en Hinton, Rectified Linear Units Improve Restricted Boltzmann Machines .

Het heeft enkele variaties, bijvoorbeeld lekkende ReLU's (LReLU's) en Exponential Linear Units (ELU's).

De code voor het implementeren van vanille ReLU samen met de afgeleide met numpy wordt hieronder weergegeven:

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

def relU_prime(z):
    return z > 0

Softmax-functie

Softmax-regressie (of multinomiale logistieke regressie) is een generalisatie van logistieke regressie naar het geval waarin we meerdere klassen willen afhandelen. Het is vooral handig voor neurale netwerken waar we niet-binaire classificatie willen toepassen. In dit geval is eenvoudige logistieke regressie niet voldoende. We zouden een waarschijnlijkheidsverdeling over alle labels nodig hebben, wat softmax ons geeft.

Softmax wordt berekend met de onderstaande formule:

Formule

___________________________ Waar past het in? _____________________________

Softmax Om een vector te normaliseren door de softmax-functie erop toe te passen met numpy , gebruikt u:

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

Waarbij x de activering van de laatste laag van de ANN is.



Modified text is an extract of the original Stack Overflow Documentation
Licentie onder CC BY-SA 3.0
Niet aangesloten bij Stack Overflow