Données d'angle d'encodage pour réseau neuronal

20

J'entraîne un réseau neuronal (détails non importants) où les données cibles sont un vecteur d'angles (entre 0 et 2 * pi). Je recherche des conseils sur la façon de coder ces données. Voici ce que j'essaie actuellement (avec un succès limité):

1) Encodage 1-of-C: je regroupe les angles possibles configurés dans environ 1000 angles discrets, puis indique un angle particulier en mettant un 1 à l'index approprié. Le problème avec cela, c'est que le réseau apprend simplement à sortir tous les 0 (car c'est presque exactement correct).

2) Mise à l'échelle simple: j'ai mis à l'échelle la plage de sortie des réseaux ([0,1]) à [0,2 * pi]. Le problème ici est que les angles ont naturellement une topologie circulaire (c'est-à-dire que 0,0001 et 2 * pi sont en fait l'un à côté de l'autre). Avec ce type d'encodage, ces informations sont perdues.

Toute suggestion serait appréciée!

Ari Herman
la source
1
Vous ne devriez pas avoir de problème avec le réseau qui génère tous les zéros, si vous utilisez une couche de sortie softmax - ce que vous devriez généralement faire, si vous utilisez une sortie catagorique (c'est-à-dire 1-of-C).
Lyndon White
7
Une idée d'encodage purement spéculative (je ne l'ai pas vue ni testée, mais je n'ai pas regardé) est l'encodage de votre angle ( ) sous forme de paire: . Je pense que ce serait alors une carte continue avec toutes les valeurs comme et proches l'une de l'autre. Je pense que je pourrais construire une démo de cela et le tester. θ ( sin ( θ ) , cos ( θ ) ) 0 2 πθθ(péché(θ),cos(θ))02π
Lyndon White
j'y ai réfléchi un peu plus, et je pense que cela pourrait en fait tout être dans votre fonction de perte. Je veux essayer un tas de choses. J'ai fait la démo, mais je n'ai pas terminé les tests. Attendez-vous à une réponse détaillée avec un soutien expérimental demain. (Piquez-moi si je ne le fais pas)
Lyndon White
Je n'utilise pas actuellement une couche softmax, et c'est probablement le problème. Je vais mettre en œuvre cela aujourd'hui si j'en ai l'occasion! Votre idée (cos, sin) est très intéressante et j'aime particulièrement qu'elle place automatiquement cette plage dans [-1,1] (bonne si vous travaillez avec une fonction d'activation tanh). J'ai hâte de voir vos résultats1
Ari Herman
Une mise à jour rapide: j'ai essayé d'implémenter une couche softmax, et je n'ai toujours pas de chance. Le problème, je pense, est que pour ce problème, il est essentiel que l '"angularité" des données soit représentée d'une manière ou d'une autre dans l'encodage. Avec un encodage catégoriel, la topologie des données cibles est perdue. Par conséquent, une erreur de 0,5 * pi et 0,05 * pi est identique pour le réseau (il considère les deux comme des catégorisations incorrectes).
Ari Herman

Réponses:

18

introduction

Je trouve cette question vraiment intéressante, je suppose que quelqu'un a publié un document à ce sujet, mais c'est mon jour de congé, donc je ne veux pas aller à la recherche de références.

Nous pourrions donc le considérer comme une représentation / encodage de la sortie, ce que je fais dans cette réponse. Je continue de penser qu'il existe une meilleure façon, où vous pouvez simplement utiliser une fonction de perte légèrement différente. (Peut-être somme des différences au carré, en utilisant la soustraction modulo 2 ).π

Mais en avant avec la réponse réelle.

Méthode

Je propose qu'un angle soit représenté par une paire de valeurs, son sinus et son cosinus.θ

La fonction d'encodage est donc: et la fonction de décodage est:θ(péché(θ),cos(θ))
Pourarctan2étant les tangentes inverses, préservant la direction dans tous les quadrants)(y1,y2)arctan2(y1,y2)

Vous pourriez, en théorie, travailler de manière équivalente directement avec les angles si votre outil utilise pris atan2en charge comme fonction de couche (en prenant exactement 2 entrées et en produisant 1 sortie). TensorFlow le fait maintenant et prend en charge la descente de gradient dessus , bien qu'il ne soit pas destiné à cette utilisation. J'ai étudié l'utilisation out = atan2(sigmoid(ylogit), sigmoid(xlogit)) d'une fonction de perte min((pred - out)^2, (pred - out - 2pi)^2). J'ai trouvé qu'il s'entraînait bien pire que l'utilisation outs = tanh(ylogit), outc = tanh(xlogit)) avec une fonction de perte 0.5((sin(pred) - outs)^2 + (cos(pred) - outc)^2. Ce qui, je pense, peut être attribué au gradient étant discontinu pouratan2

Mes tests ici l'exécutent comme une fonction de prétraitement

Pour évaluer cela, j'ai défini une tâche:

Étant donné une image en noir et blanc représentant une seule ligne sur un arrière-plan vierge Afficher l'angle de cette ligne par rapport à "l'axe x positif"

J'ai implémenté une fonction générant aléatoirement ces images, avec des lignes à angles aléatoires (NB: les versions antérieures de ce post utilisaient des pentes aléatoires, plutôt que des angles aléatoires. Merci à @Ari Herman de le signaler. Il est maintenant corrigé). J'ai construit plusieurs réseaux de neurones pour y évaluer les performances de la tâche. Les détails complets de l'implémentation se trouvent dans ce bloc-notes Jupyter . Le code est entièrement dans Julia , et j'utilise la bibliothèque du réseau de neurones Mocha .

À titre de comparaison, je le présente par rapport aux méthodes alternatives de mise à l'échelle à 0,1. et mettre dans 500 bacs et utiliser softmax soft-label. Je ne suis pas particulièrement satisfait de ce dernier et je pense que je dois le modifier. C'est pourquoi, contrairement aux autres, je ne le teste que pour 1000 itérations, contre les deux autres qui ont été exécutées pour 1000 et pour 10000

Montage expérimental

101×101

Pour chaque piste, 1 000 formations et 1 000 images de test ont été générées de manière aléatoire.

Le réseau d'évaluation avait une seule couche cachée de largeur 500. Des neurones sigmoïdes ont été utilisés dans la couche cachée.

Il a été formé par Stochastic Gradient Decent, avec un taux d'apprentissage fixe de 0,01 et un moment fixe de 0,9.

Aucune régularisation ou abandon n'a été utilisé. Il n'y avait pas non plus de convolution, etc. Un réseau simple, qui j'espère suggère que ces résultats généraliseront

Il est très facile de modifier ces paramètres dans le code de test , et j'encourage les gens à le faire. (et recherchez les bogues dans le test).

Résultats

Mes résultats sont les suivants:

|                        |  500 bins    |  scaled to 0-1 |  Sin/Cos     |  scaled to 0-1 |  Sin/Cos     |
|                        | 1,000 Iter   | 1,000 Iter     | 1,000 iter   | 10,000 Iter    | 10,000 iter  |
|------------------------|--------------|----------------|--------------|----------------|--------------|
| mean_error             | 0.4711263342 | 0.2225284486   | 2.099914718  | 0.1085846429   | 2.1036656318 |
| std(errors)            | 1.1881991421 | 0.4878383767   | 1.485967909  | 0.2807570442   | 1.4891605068 |
| minimum(errors)        | 1.83E-006    | 1.82E-005      | 9.66E-007    | 1.92E-006      | 5.82E-006    |
| median(errors)         | 0.0512168533 | 0.1291033982   | 1.8440767072 | 0.0562908143   | 1.8491085947 |
| maximum(errors)        | 6.0749693965 | 4.9283551248   | 6.2593307366 | 3.735884823    | 6.2704853962 |
| accurancy              | 0.00%        | 0.00%          | 0.00%        | 0.00%          | 0.00%        |
| accurancy_to_point001  | 2.10%        | 0.30%          | 3.70%        | 0.80%          | 12.80%       |
| accurancy_to_point01   | 21.90%       | 4.20%          | 37.10%       | 8.20%          | 74.60%       |
| accurancy_to_point1    | 59.60%       | 35.90%         | 98.90%       | 72.50%         | 99.90%       |

septπ4π4

Je présente également la précision à différents niveaux de granularité. La précision étant la partie des cas de test, elle a été corrigée. accuracy_to_point01Cela signifie donc qu'il a été considéré comme correct si la sortie était à moins de 0,01 de l'angle réel. Aucune des représentations n'a obtenu de résultats parfaits, mais ce n'est pas du tout surprenant étant donné le fonctionnement des mathématiques en virgule flottante.

Si vous jetez un œil à l'historique de ce post, vous verrez que les résultats ont un peu de bruit, légèrement différents à chaque fois que je le relance. Mais l'ordre général et l'échelle des valeurs restent les mêmes; nous permettant ainsi de tirer quelques conclusions.

Discussion

π

Le codage sin / cos fonctionne bien mieux que le codage 0-1 mis à l'échelle. L'amélioration est dans la mesure où à 1 000 itérations d'entraînement, sin / cos fonctionne environ 3 fois mieux sur la plupart des mesures que la mise à l'échelle est à 10 000 itérations.

Je pense que cela est en partie lié à l'amélioration de la généralisation, car les deux obtenaient une erreur quadratique moyenne assez similaire sur l'ensemble d'entraînement, au moins une fois que 10 000 itérations ont été exécutées.

101×101

Il semble également probable qu'à une échelle absolue pour aller au-delà de cette performance, un meilleur réseau neuronal soit nécessaire. Plutôt que celui très simple décrit ci-dessus dans la configuration expérimentale.

Conclusion.

Il semble que la représentation sin / cos soit de loin la meilleure des représentations que j'ai étudiées ici. Cela a du sens, car il a une valeur lisse lorsque vous vous déplacez autour du cercle. J'aime aussi que l'inverse se fasse avec arctan2 , qui est élégant.

F(X)=y1y2X

Lyndon White
la source
C'est certainement la réponse la plus complète que j'ai jamais reçue sur l'échange de pile. Comme je ne connais pas Julia, il m'est difficile d'examiner votre code ... donc, à la place, je vais essayer de reproduire vos résultats en utilisant Python. Je posterai mes résultats plus tard dans la journée ou demain.
Ari Herman
Bien que je ne sois pas surpris du mauvais fonctionnement du binning, j'ai été surpris du degré de surclassement de l'échelle (0,1) par la méthode (cos, sin). J'ai remarqué que vous avez généré vos exemples en choisissant au hasard la montée et la course des lignes. Cela générerait, je pense, des lignes dont les angles ne sont pas uniformément répartis, mais dont les pentes le sont. Est-il possible que ce soit la raison pour laquelle la méthode (cos, sin) a donné de bien meilleurs résultats? Que se passerait-il si vous faisiez bronzer les cibles (angle) ...?
Ari Herman
tan(angle)π/4
Il devrait y avoir une carte proche de une à une entre julia et numpy, et entre Mocha et Caffe, si vous voulez vraiment la réimplémenter. Y a-t-il une partie particulière du code que vous trouvez difficile à lire? Julia devrait être une langue facile à comprendre. Alors j'ai peut-être fait quelque chose d'étrange.
Lyndon White
J'ai fini par lire votre code, et tout semble correct. Pourtant, je voulais écrire ma propre version, car cela est généralement instructif. Mon implémentation est légèrement différente de la vôtre, il sera donc intéressant de comparer les résultats. Je les posterai dans les prochaines heures.
Ari Herman
5

Voici une autre implémentation de Python comparant le codage proposé par Lyndon White à une approche groupée. Le code ci-dessous a produit la sortie suivante:

Training Size: 100
Training Epochs: 100
Encoding: cos_sin
Test Error: 0.017772154610047136
Encoding: binned
Test Error: 0.043398792553251526

Training Size: 100
Training Epochs: 500
Encoding: cos_sin
Test Error: 0.015376604917819397
Encoding: binned
Test Error: 0.032942592915322394

Training Size: 1000
Training Epochs: 100
Encoding: cos_sin
Test Error: 0.007544091937411164
Encoding: binned
Test Error: 0.012796594492198667

Training Size: 1000
Training Epochs: 500
Encoding: cos_sin
Test Error: 0.0038051515079569097
Encoding: binned
Test Error: 0.006180633805557207

(péché(θ),cos(θ))(péché(θ),cos(θ))

import matplotlib.pyplot as plt
import numpy as np
import torch
import torch.nn as nn
import torch.utils.data

device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")


class Net(nn.Module):
    def __init__(self, input_size, hidden_size, num_out):
        super(Net, self).__init__()
        self.fc1 = nn.Linear(input_size, hidden_size)
        self.sigmoid = nn.Sigmoid()
        self.fc2 = nn.Linear(hidden_size, num_out)

    def forward(self, x):
        out = self.fc1(x)
        out = self.sigmoid(out)
        out = self.fc2(out)
        return out


def gen_train_image(angle, side, thickness):
    image = np.zeros((side, side))
    (x_0, y_0) = (side / 2, side / 2)
    (c, s) = (np.cos(angle), np.sin(angle))
    for y in range(side):
        for x in range(side):
            if (abs((x - x_0) * c + (y - y_0) * s) < thickness / 2) and (
                    -(x - x_0) * s + (y - y_0) * c > 0):
                image[x, y] = 1

    return image.flatten()


def gen_data(num_samples, side, num_bins, thickness):
    angles = 2 * np.pi * np.random.uniform(size=num_samples)
    X = [gen_train_image(angle, side, thickness) for angle in angles]
    X = np.stack(X)

    y = {"cos_sin": [], "binned": []}
    bin_size = 2 * np.pi / num_bins
    for angle in angles:
        idx = int(angle / bin_size)
        y["binned"].append(idx)
        y["cos_sin"].append(np.array([np.cos(angle), np.sin(angle)]))

    for enc in y:
        y[enc] = np.stack(y[enc])

    return (X, y, angles)


def get_model_stuff(train_y, input_size, hidden_size, output_sizes,
                    learning_rate, momentum):
    nets = {}
    optimizers = {}

    for enc in train_y:
        net = Net(input_size, hidden_size, output_sizes[enc])
        nets[enc] = net.to(device)
        optimizers[enc] = torch.optim.SGD(net.parameters(), lr=learning_rate,
                                          momentum=momentum)

    criterions = {"binned": nn.CrossEntropyLoss(), "cos_sin": nn.MSELoss()}
    return (nets, optimizers, criterions)


def get_train_loaders(train_X, train_y, batch_size):
    train_X_tensor = torch.Tensor(train_X)

    train_loaders = {}

    for enc in train_y:
        if enc == "binned":
            train_y_tensor = torch.tensor(train_y[enc], dtype=torch.long)
        else:
            train_y_tensor = torch.tensor(train_y[enc], dtype=torch.float)

        dataset = torch.utils.data.TensorDataset(train_X_tensor, train_y_tensor)
        train_loader = torch.utils.data.DataLoader(dataset=dataset,
                                                   batch_size=batch_size,
                                                   shuffle=True)
        train_loaders[enc] = train_loader

    return train_loaders


def show_image(image, side):
    img = plt.imshow(np.reshape(image, (side, side)), interpolation="nearest",
                     cmap="Greys")
    plt.show()


def main():
    side = 101
    input_size = side ** 2
    thickness = 5.0
    hidden_size = 500
    learning_rate = 0.01
    momentum = 0.9
    num_bins = 500
    bin_size = 2 * np.pi / num_bins
    half_bin_size = bin_size / 2
    batch_size = 50
    output_sizes = {"binned": num_bins, "cos_sin": 2}
    num_test = 1000

    (test_X, test_y, test_angles) = gen_data(num_test, side, num_bins,
                                             thickness)

    for num_train in [100, 1000]:

        (train_X, train_y, train_angles) = gen_data(num_train, side, num_bins,
                                                    thickness)
        train_loaders = get_train_loaders(train_X, train_y, batch_size)

        for epochs in [100, 500]:

            (nets, optimizers, criterions) = get_model_stuff(train_y, input_size,
                                                             hidden_size, output_sizes,
                                                             learning_rate, momentum)

            for enc in train_y:
                optimizer = optimizers[enc]
                net = nets[enc]
                criterion = criterions[enc]

                for epoch in range(epochs):
                    for (i, (images, ys)) in enumerate(train_loaders[enc]):
                        optimizer.zero_grad()

                        outputs = net(images.to(device))
                        loss = criterion(outputs, ys.to(device))
                        loss.backward()
                        optimizer.step()


            print("Training Size: {0}".format(num_train))
            print("Training Epochs: {0}".format(epochs))
            for enc in train_y:
                net = nets[enc]
                preds = net(torch.tensor(test_X, dtype=torch.float).to(device))
                if enc == "binned":
                    pred_bins = np.array(preds.argmax(dim=1).detach().cpu().numpy(),
                                         dtype=np.float)
                    pred_angles = bin_size * pred_bins + half_bin_size
                else:
                    pred_angles = torch.atan2(preds[:, 1], preds[:, 0]).detach().cpu().numpy()
                    pred_angles[pred_angles < 0] = pred_angles[pred_angles < 0] + 2 * np.pi

                print("Encoding: {0}".format(enc))
                print("Test Error: {0}".format(np.abs(pred_angles - test_angles).mean()))

            print()


if __name__ == "__main__":
    main()
airalcorn2
la source
3

Voici ma version Python de votre expérience. J'ai conservé la plupart des détails de votre implémentation, en particulier j'utilise la même taille d'image, les tailles de couche réseau, le taux d'apprentissage, l'élan et les mesures de réussite.

Chaque réseau testé a une couche cachée (taille = 500) avec des neurones logistiques. Les neurones de sortie sont soit linéaires soit softmax comme indiqué. J'ai utilisé 1 000 images d'entraînement et 1 000 images de test qui ont été générées de manière indépendante et aléatoire (il peut donc y avoir des répétitions). La formation consistait en 50 itérations à travers l'ensemble de formation.

J'ai pu obtenir une assez bonne précision en utilisant le binning et le codage "gaussien" (un nom que j'ai inventé; similaire au binning sauf que le vecteur de sortie cible a la forme exp (-pi * ([1,2,3, ... , 500] - idx) ** 2) où idx est l'indice correspondant à l'angle correct). Le code est ci-dessous; voici mes résultats:

Erreur de test pour le codage (cos, sin):

1 000 images d'entraînement, 1 000 images de test, 50 itérations, sortie linéaire

  • Moyenne: 0,0911558142071

  • Médiane: 0,0429723541743

  • Minimum: 2.77769843793e-06

  • Maximum: 6.2608513539

  • Précision à 0,1: 85,2%

  • Précision à 0,01: 11,6%

  • Précision à 0,001: 1,0%

Erreur de test pour l'encodage [-1,1]:

1 000 images d'entraînement, 1 000 images de test, 50 itérations, sortie linéaire

  • Moyenne: 0,234181700523

  • Médiane: 0,17460197307

  • Minimum: 0,000473665840258

  • Maximum: 6.00637777237

  • Précision à 0,1: 29,9%

  • Précision à 0,01: 3,3%

  • Précision à 0,001: 0,1%

Erreur de test pour l'encodage 1 sur 500:

1000 images d'entraînement, 1000 images de test, 50 itérations, sortie softmax

  • Moyenne: 0,0298767021922

  • Médiane: 0,00388858079174

  • Minimum: 4.08712407829e-06

  • Maximum: 6.2784479965

  • Précision à 0,1: 99,6%

  • Précision à 0,01: 88,9%

  • Précision à 0,001: 13,5%

Erreur de test pour le codage gaussien:

1000 images d'entraînement, 1000 images de test, 50 itérations, sortie softmax

  • Moyenne: 0,0296905377463
  • Médiane: 0,00365867335107
  • Minimum: 4.08712407829e-06
  • Maximum: 6.2784479965
  • Précision à 0,1: 99,6%
  • Précision à 0,01: 90,8%
  • Précision à 0,001: 14,3%

Je ne peux pas comprendre pourquoi nos résultats semblent être en contradiction les uns avec les autres, mais cela semble mériter une enquête plus approfondie.

# -*- coding: utf-8 -*-
"""
Created on Mon Jun 13 16:59:53 2016

@author: Ari
"""

from numpy import savetxt, loadtxt, round, zeros, sin, cos, arctan2, clip, pi, tanh, exp, arange, dot, outer, array, shape, zeros_like, reshape, mean, median, max, min
from numpy.random import rand, shuffle
import matplotlib.pyplot as plt

###########
# Functions
###########

# Returns a B&W image of a line represented as a binary vector of length width*height
def gen_train_image(angle, width, height, thickness):
    image = zeros((height,width))
    x_0,y_0 = width/2, height/2
    c,s = cos(angle),sin(angle)
    for y in range(height):
        for x in range(width):
            if abs((x-x_0)*c + (y-y_0)*s) < thickness/2 and -(x-x_0)*s + (y-y_0)*c > 0:
                image[x,y] = 1
    return image.flatten()

# Display training image    
def display_image(image,height, width):    
    img = plt.imshow(reshape(image,(height,width)), interpolation = 'nearest', cmap = "Greys")
    plt.show()    

# Activation function
def sigmoid(X):
    return 1.0/(1+exp(-clip(X,-50,100)))

# Returns encoded angle using specified method ("binned","scaled","cossin","gaussian")
def encode_angle(angle, method):
    if method == "binned": # 1-of-500 encoding
        X = zeros(500)
        X[int(round(250*(angle/pi + 1)))%500] = 1
    elif method == "gaussian": # Leaky binned encoding
        X = array([i for i in range(500)])
        idx = 250*(angle/pi + 1)
        X = exp(-pi*(X-idx)**2)
    elif method == "scaled": # Scaled to [-1,1] encoding
        X = array([angle/pi])
    elif method == "cossin": # Oxinabox's (cos,sin) encoding
        X = array([cos(angle),sin(angle)])
    else:
        pass
    return X

# Returns decoded angle using specified method
def decode_angle(X, method):
    if method == "binned" or method == "gaussian": # 1-of-500 or gaussian encoding
        M = max(X)
        for i in range(len(X)):
            if abs(X[i]-M) < 1e-5:
                angle = pi*i/250 - pi
                break
#        angle = pi*dot(array([i for i in range(500)]),X)/500  # Averaging
    elif method == "scaled": # Scaled to [-1,1] encoding
        angle = pi*X[0]
    elif method == "cossin": # Oxinabox's (cos,sin) encoding
        angle = arctan2(X[1],X[0])
    else:
        pass
    return angle

# Train and test neural network with specified angle encoding method
def test_encoding_method(train_images,train_angles,test_images, test_angles, method, num_iters, alpha = 0.01, alpha_bias = 0.0001, momentum = 0.9, hid_layer_size = 500):
    num_train,in_layer_size = shape(train_images)
    num_test = len(test_angles)

    if method == "binned":
        out_layer_size = 500
    elif method == "gaussian":
        out_layer_size = 500
    elif method == "scaled":
        out_layer_size = 1
    elif method == "cossin":
        out_layer_size = 2
    else:
        pass

    # Initial weights and biases
    IN_HID = rand(in_layer_size,hid_layer_size) - 0.5 # IN --> HID weights
    HID_OUT = rand(hid_layer_size,out_layer_size) - 0.5 # HID --> OUT weights
    BIAS1 = rand(hid_layer_size) - 0.5 # Bias for hidden layer
    BIAS2 = rand(out_layer_size) - 0.5 # Bias for output layer

    # Initial weight and bias updates
    IN_HID_del = zeros_like(IN_HID)
    HID_OUT_del = zeros_like(HID_OUT)
    BIAS1_del = zeros_like(BIAS1)
    BIAS2_del = zeros_like(BIAS2)

    # Train
    for j in range(num_iters):
        for i in range(num_train):
            # Get training example
            IN = train_images[i]
            TARGET = encode_angle(train_angles[i],method) 

            # Feed forward and compute error derivatives
            HID = sigmoid(dot(IN,IN_HID)+BIAS1)

            if method == "binned" or method == "gaussian": # Use softmax
                OUT = exp(clip(dot(HID,HID_OUT)+BIAS2,-100,100))
                OUT = OUT/sum(OUT)
                dACT2 = OUT - TARGET
            elif method == "cossin" or method == "scaled": # Linear
                OUT = dot(HID,HID_OUT)+BIAS2 
                dACT2 = OUT-TARGET 
            else:
                print("Invalid encoding method")

            dHID_OUT = outer(HID,dACT2)
            dACT1 = dot(dACT2,HID_OUT.T)*HID*(1-HID)
            dIN_HID = outer(IN,dACT1)
            dBIAS1 = dACT1
            dBIAS2 = dACT2

            # Update the weight updates 
            IN_HID_del = momentum*IN_HID_del + (1-momentum)*dIN_HID
            HID_OUT_del = momentum*HID_OUT_del + (1-momentum)*dHID_OUT
            BIAS1_del = momentum*BIAS1_del + (1-momentum)*dBIAS1
            BIAS2_del = momentum*BIAS2_del + (1-momentum)*dBIAS2

            # Update the weights
            HID_OUT -= alpha*dHID_OUT
            IN_HID -= alpha*dIN_HID
            BIAS1 -= alpha_bias*dBIAS1
            BIAS2 -= alpha_bias*dBIAS2

    # Test
    test_errors = zeros(num_test)
    angles = zeros(num_test)
    target_angles = zeros(num_test)
    accuracy_to_point001 = 0
    accuracy_to_point01 = 0
    accuracy_to_point1 = 0

    for i in range(num_test):

        # Get training example
        IN = test_images[i]
        target_angle = test_angles[i]

        # Feed forward
        HID = sigmoid(dot(IN,IN_HID)+BIAS1)

        if method == "binned" or method == "gaussian":
            OUT = exp(clip(dot(HID,HID_OUT)+BIAS2,-100,100))
            OUT = OUT/sum(OUT)
        elif method == "cossin" or method == "scaled":
            OUT = dot(HID,HID_OUT)+BIAS2 

        # Decode output 
        angle = decode_angle(OUT,method)

        # Compute errors
        error = abs(angle-target_angle)
        test_errors[i] = error
        angles[i] = angle

        target_angles[i] = target_angle
        if error < 0.1:
            accuracy_to_point1 += 1
        if error < 0.01: 
            accuracy_to_point01 += 1
        if error < 0.001:
            accuracy_to_point001 += 1

    # Compute and return results
    accuracy_to_point1 = 100.0*accuracy_to_point1/num_test
    accuracy_to_point01 = 100.0*accuracy_to_point01/num_test
    accuracy_to_point001 = 100.0*accuracy_to_point001/num_test

    return mean(test_errors),median(test_errors),min(test_errors),max(test_errors),accuracy_to_point1,accuracy_to_point01,accuracy_to_point001

# Dispaly results
def display_results(results,method):
    MEAN,MEDIAN,MIN,MAX,ACC1,ACC01,ACC001 = results
    if method == "binned":
        print("Test error for 1-of-500 encoding:")
    elif method == "gaussian":
        print("Test error for gaussian encoding: ")
    elif method == "scaled":
        print("Test error for [-1,1] encoding:")
    elif method == "cossin":
        print("Test error for (cos,sin) encoding:")
    else:
        pass
    print("-----------")
    print("Mean: "+str(MEAN))
    print("Median: "+str(MEDIAN))
    print("Minimum: "+str(MIN))
    print("Maximum: "+str(MAX))
    print("Accuracy to 0.1: "+str(ACC1)+"%")
    print("Accuracy to 0.01: "+str(ACC01)+"%")
    print("Accuracy to 0.001: "+str(ACC001)+"%")
    print("\n\n")


##################
# Image parameters
##################
width = 100 # Image width
height = 100 # Image heigth
thickness = 5.0 # Line thickness

#################################
# Generate training and test data
#################################
num_train = 1000
num_test = 1000
test_images = []
test_angles = []
train_images = []
train_angles = []
for i in range(num_train):
    angle = pi*(2*rand() - 1)
    train_angles.append(angle)
    image = gen_train_image(angle,width,height,thickness)
    train_images.append(image)
for i in range(num_test):
    angle = pi*(2*rand() - 1)
    test_angles.append(angle)
    image = gen_train_image(angle,width,height,thickness)
    test_images.append(image)
train_angles,train_images,test_angles,test_images = array(train_angles),array(train_images),array(test_angles),array(test_images)



###########################
# Evaluate encoding schemes
###########################
num_iters = 50

# Train with cos,sin encoding
method = "cossin"
results1 = test_encoding_method(train_images, train_angles, test_images, test_angles, method, num_iters)
display_results(results1,method)

# Train with scaled encoding
method = "scaled"
results3 = test_encoding_method(train_images, train_angles, test_images, test_angles, method, num_iters)
display_results(results3,method)

# Train with binned encoding
method = "binned"
results2 = test_encoding_method(train_images, train_angles, test_images, test_angles, method, num_iters)
display_results(results2,method)

# Train with gaussian encoding
method = "gaussian"
results4 = test_encoding_method(train_images, train_angles, test_images, test_angles, method, num_iters)
display_results(results4,method)
Ari Herman
la source
Cool, sur une touche différente. Vous ne vous entraînez sur chaque image qu'une seule fois. Je m'entraîne sur chaque image 1 000 fois ou 10 000 fois. Plusieurs itérations bien que les données de formation soient normales, en particulier lors de la formation sur des quantités relativement petites de données (et il ne m'a fallu qu'une thèse de premier cycle non publiée pour l'apprendre, mais c'est une autre histoire). Cela dit, je devrais ajouter une colonne 1 iter à ma table. ce serait informatif
Lyndon White
Je pense que l'entraînement sur des images similaires (mais pas identiques) avec des cibles similaires affecterait ce réseau de la même manière. Si cela est vrai, cela devrait fonctionner correctement pour simplement augmenter le nombre d'images aléatoires en cours de formation, plutôt que de répéter plusieurs fois via un ensemble de formation plus petit. Voulez-vous dire que ce n'est pas le cas?
Ari Herman
C'est similaire, mais pour cet exemple de tâche, il n'a pas le problème de montrer finalement toutes les images possibles afin que votre test se chevauche avec votre train, donc les tests de généralisation ne fonctionneront pas. Plus important encore, si vous effectuez 100 000 images d'entraînement, ce qui représente <1 000 * 1 000 images d'entraînement * Itérations.
Lyndon White
Vous avez raison, je vais résoudre ce problème. Il y a un problème encore plus important avec mon code: j'utilise des neurones logistiques qui sont incapables de produire les valeurs négatives requises par la représentation (cos, sin). Oh! Je réviserai mon code et le publierai dès que possible.
Ari Herman
Vous pourriez (si vous ne l'avez pas déjà fait) être intéressé à faire un Graident Check , qui vaut la peine d'être implémenté à partir de zéro, car il est très facile de faire une erreur mineure et de faire fonctionner votre réseau principalement. Re: Neuron: oui, j'ai une couche de sortie linéaire, sur la couche cachée sigmoïde
Lyndon White
1

Une autre façon de coder l'angle est un ensemble de deux valeurs:

y1 = max (0, thêta)

y2 = max (0, -thêta)

theta_out = y1 - y2

Cela aurait le même problème avec arctan2 en ce que le gradient n'est pas défini à thêta = 0. Je n'ai pas le temps de former un réseau et de comparer avec les autres encodages mais dans cet article, la technique semblait raisonnablement réussie.

DerekG
la source
1
Cela semble être une réponse mélangée à une autre question dans un message. Ce site fonctionne un peu différemment du forum. Ici, les réponses doivent se concentrer sur la réponse à la question d'origine. Et si vous avez une autre question ou un commentaire - il doit être affiché comme tel.
Karolis Koncevičius