Codierungswinkeldaten für neuronales Netzwerk

20

Ich trainiere ein neuronales Netzwerk (Details nicht wichtig), bei dem die Zieldaten ein Winkelvektor sind (zwischen 0 und 2 * pi). Ich suche Rat, wie ich diese Daten verschlüsseln kann. Folgendes versuche ich derzeit (mit begrenztem Erfolg):

1) 1-von-C-Codierung: Ich bin die eingestellten möglichen Winkel in etwa 1000 diskrete Winkel und gebe dann einen bestimmten Winkel an, indem ich eine 1 an den entsprechenden Index stelle. Das Problem dabei ist, dass das Netzwerk einfach lernt, alle Nullen auszugeben (da dies fast genau richtig ist).

2) Einfache Skalierung: Ich habe den Ausgangsbereich des Netzwerks ([0,1]) auf [0,2 * pi] skaliert. Das Problem hierbei ist, dass Winkel naturgemäß eine kreisförmige Topologie haben (dh 0,0001 und 2 * pi liegen tatsächlich direkt nebeneinander). Bei dieser Art der Codierung gehen diese Informationen verloren.

Anregungen wäre dankbar!

Ari Herman
quelle
1
Wenn Sie eine Softmax-Ausgabeebene verwenden, sollten Sie kein Problem damit haben, dass das Netzwerk alle Nullen ausgibt. Dies sollten Sie im Allgemeinen tun, wenn Sie eine katagorische (dh 1-aus-C) Ausgabe verwenden.
Lyndon White
7
Eine rein spekulative Kodierungsidee (ich habe sie noch nicht gesehen oder getestet, aber ich habe sie noch nicht gesehen) ist die Kodierung Ihres Winkels ( ) als Paar: . Ich denke, dann wäre es eine kontinuierliche Karte mit allen Werten wie und nahe beieinander. Ich denke, ich könnte eine Demo davon bauen und es ausprobieren. θ ( sin ( θ ) , cos ( θ ) ) 0 2 πθθ(sin(θ),cos(θ))02π
Lyndon White
Ich habe darüber noch ein bisschen nachgedacht, und ich denke, es könnte tatsächlich nur in Ihrer Verlustfunktion liegen. Ich möchte ein paar Dinge ausprobieren. Ich habe die Demo gemacht, aber die Tests nicht beendet. Erwarten Sie morgen irgendwann eine ausführliche Antwort mit experimenteller Unterstützung. (Me Poke , wenn ich nicht)
Lyndon Weiß
Ich verwende derzeit keine Softmax-Schicht, und das ist wahrscheinlich das Problem. Ich werde das heute umsetzen, wenn ich eine Chance bekomme! Ihre (cos, sin) Idee ist sehr interessant und ich mag es besonders, dass sie diesen Bereich automatisch in [-1,1] setzt (gut, wenn Sie mit einer Tanh-Aktivierungsfunktion arbeiten). Ich freue mich auf Ihre Ergebnisse1
Ari Herman
Ein kurzes Update: Ich habe versucht, einen Softmax-Layer zu implementieren und habe immer noch kein Glück. Das Problem ist meines Erachtens, dass es für dieses Problem wesentlich ist, dass die "Winkligkeit" der Daten in der Codierung irgendwie dargestellt wird. Bei einer kategorialen Codierung geht die Topologie der Zieldaten verloren. Ein Fehler von 0,5 * pi und 0,05 * pi sieht für das Netzwerk gleich aus (beides wird als falsche Kategorisierung angesehen).
Ari Herman

Antworten:

18

Einführung

Ich finde diese Frage wirklich interessant, ich gehe davon aus, dass jemand eine Zeitung darüber herausgebracht hat, aber es ist mein freier Tag, also möchte ich nicht nach Referenzen suchen.

Wir könnten es also als Repräsentation / Codierung der Ausgabe betrachten, was ich in dieser Antwort tue. Ich denke immer noch, dass es einen besseren Weg gibt, wo man einfach eine etwas andere Verlustfunktion verwenden kann. (Vielleicht Summe der quadratischen Differenzen mit Subtraktionsmodulo 2 ).π

Aber weiter mit der eigentlichen Antwort.

Methode

Ich schlage vor, dass ein Winkel als ein Wertepaar dargestellt wird, sein Sinus und sein Cosinus.θ

Die Codierungsfunktion lautet also: und die Decodierungsfunktion lautet: Für arctan2 sind die inversen Tangenten, wobei die Richtung in allen Quadranten beibehalten wird.θ(Sünde(θ),cos(θ))
(y1,y2)arctan2(y1,y2)

Theoretisch könnten Sie auch direkt mit den Winkeln arbeiten, wenn Ihr Werkzeug atan2als Layer-Funktion unterstützt wird (genau 2 Eingaben nehmen und 1 Ausgabe erzeugen). TensorFlow macht dies jetzt und unterstützt den Gradientenabstieg , obwohl es nicht für diese Verwendung vorgesehen ist. Ich habe out = atan2(sigmoid(ylogit), sigmoid(xlogit)) mit einer Verlustfunktion nachgeforscht min((pred - out)^2, (pred - out - 2pi)^2). Ich fand, dass es viel schlimmer trainierte als outs = tanh(ylogit), outc = tanh(xlogit)) mit einer Verlustfunktion 0.5((sin(pred) - outs)^2 + (cos(pred) - outc)^2. Was meiner Meinung nach darauf zurückzuführen ist, dass der Gradient für unterbrochen istatan2

Mein Test hier führt es als Vorverarbeitungsfunktion aus

Um dies auszuwerten, habe ich eine Aufgabe definiert:

Bei einem Schwarzweißbild, das eine einzelne Linie auf einem leeren Hintergrund darstellt, wird ausgegeben, in welchem ​​Winkel diese Linie zur "positiven x-Achse" liegt.

Ich habe eine Funktion implementiert, mit der diese Bilder zufällig mit Linien in zufälligen Winkeln erzeugt werden (Hinweis: In früheren Versionen dieses Beitrags wurden eher zufällige Neigungen als zufällige Winkel verwendet. Dank an @Ari Herman für den Hinweis. Es ist jetzt behoben). Ich habe mehrere neuronale Netze aufgebaut, um die Leistung für die Aufgabe zu bewerten. Die vollständigen Details der Implementierung finden Sie in diesem Jupyter-Notizbuch . Der Code ist alles in Julia und ich benutze die Mocha- Bibliothek für neuronale Netze.

Zum Vergleich stelle ich es den alternativen Skalierungsmethoden von 0,1 gegenüber. und in 500 Behälter zu setzen und Soft-Label-Softmax zu verwenden. Ich bin mit dem letzten nicht besonders glücklich und denke, ich muss es optimieren. Aus diesem Grund probiere ich es im Gegensatz zu den anderen nur für 1.000 Iterationen aus, im Gegensatz zu den beiden anderen, die für 1.000 und für 10.000 ausgeführt wurden

Versuchsaufbau

Die Bilder hatten eine Pixel, wobei die Linie in der Mitte beginnt und bis zum Rand reicht. Das Bild enthielt kein Rauschen usw., nur eine "schwarze" Linie auf weißem Hintergrund.101×101

Für jeden Trail wurden 1.000 Trainings- und 1.000 Testbilder zufällig generiert.

Das Bewertungsnetzwerk hatte eine einzige verborgene Schicht mit einer Breite von 500. Sigmoidneuronen wurden in der verborgenen Schicht verwendet.

Es wurde von Stochastic Gradient Decent mit einer festen Lernrate von 0,01 und einem festen Impuls von 0,9 trainiert.

Es wurde keine Regularisierung oder Dropout verwendet. Es gab auch keine Art von Faltung usw. Ein einfaches Netzwerk, von dem ich hoffe, dass sich diese Ergebnisse verallgemeinern lassen

Es ist sehr einfach , diese Parameter in dem zwicken Test - Code , und ich ermutige die Menschen , dies zu tun. (und suchen Sie nach Fehlern im Test).

Ergebnisse

Meine Ergebnisse sind wie folgt:

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

Wo ich mich auf Fehler beziehe, ist dies der absolute Wert der Differenz zwischen dem vom neuronalen Netz ausgegebenen Winkel und dem wahren Winkel. So ist der mittlere Fehler (zum Beispiel) der Durchschnitt über die 1.000 Testfälle dieses Unterschieds usw. Ich bin nicht sicher, ob ich ihn nicht neu skalieren sollte, indem ich einen Fehler von say gleich mache auf einen Fehler von ). π7π4π4

Ich präsentiere auch die Genauigkeit auf verschiedenen Ebenen der Granularität. Die Genauigkeit ist der Teil der Testfälle, die korreliert wurden. Das accuracy_to_point01bedeutet, dass es als korrekt gezählt wurde, wenn die Ausgabe innerhalb von 0,01 des wahren Winkels lag. Keine der Darstellungen lieferte perfekte Ergebnisse, aber das ist angesichts der Funktionsweise der Gleitkomma-Mathematik keineswegs überraschend.

Wenn Sie sich den Verlauf dieses Beitrags ansehen, werden Sie feststellen, dass die Ergebnisse ein wenig verrauscht sind und sich jedes Mal geringfügig unterscheiden, wenn ich ihn erneut ausführe. Die allgemeine Reihenfolge und Skala der Werte bleibt jedoch gleich. So können wir einige Schlussfolgerungen ziehen.

Diskussion

Binning mit Softmax funktioniert bei weitem am schlechtesten, da ich nicht sicher bin, ob ich bei der Implementierung etwas falsch gemacht habe. Die Leistung liegt jedoch geringfügig über der Rate. Wenn wir nur raten würden, bekämen wir einen mittleren Fehler vonπ

Die Sin / Cos-Codierung ist wesentlich leistungsfähiger als die skalierte 0-1-Codierung. Die Verbesserung besteht darin, dass sin / cos bei 1.000 Trainingsiterationen bei den meisten Metriken etwa dreimal so gut abschneidet wie bei 10.000 Iterationen.

Ich denke, dies hängt zum Teil mit der Verbesserung der Verallgemeinerung zusammen, da beide einen relativ ähnlichen mittleren quadratischen Fehler im Trainingssatz aufwiesen und mindestens einmal 10.000 Iterationen ausgeführt wurden.

Es gibt sicherlich eine Obergrenze für die bestmögliche Leistung bei dieser Aufgabe, da der Winkel mehr oder weniger eine reelle Zahl sein kann, aber nicht alle derartigen Winkel erzeugen unterschiedliche Linien bei einer Auflösung von × Pixeln. Da zum Beispiel die Winkel 45.0 und 45.0000001 bei dieser Auflösung beide an dasselbe Bild gebunden sind, wird keine Methode jemals beide perfekt korrigieren.101×101

Es scheint auch wahrscheinlich, dass auf einer absoluten Skala, um über diese Leistung hinauszugehen, ein besseres neuronales Netzwerk benötigt wird. Anstatt der sehr einfachen, die oben im Versuchsaufbau beschrieben wurde.

Fazit.

Es scheint, dass die Sin / Cos-Darstellung bei weitem die beste der Darstellungen ist, die ich hier untersucht habe. Dies ist sinnvoll, da es einen glatten Wert hat, wenn Sie sich im Kreis bewegen. Ich mag es auch, dass das Umkehren mit arctan2 gemacht werden kann , was elegant ist.

Ich glaube, dass die vorgelegte Aufgabe ausreicht, um eine angemessene Herausforderung für das Netzwerk darzustellen. Obwohl ich wirklich denke, dass es nur darum geht, die Kurvenanpassung für es vielleicht zu einfach. Und vielleicht noch schlimmer, es könnte die gepaarte Darstellung begünstigen. Ich glaube nicht, dass es so ist, aber es wird spät hier, also habe ich vielleicht etwas verpasst, das ich Sie noch einmal einlade, meinen Code durchzusehen . Schlagen Sie Verbesserungen oder alternative Aufgaben vor.f(x)=y1y2x

Lyndon White
quelle
Dies ist mit Sicherheit die gründlichste Antwort, die ich je auf den Stapelaustausch erhalten habe. Da ich mit Julia nicht vertraut bin, fällt es mir schwer, Ihren Code zu untersuchen. Stattdessen werde ich versuchen, Ihre Ergebnisse mit Python zu replizieren. Ich werde meine Erkenntnisse später heute oder morgen veröffentlichen.
Ari Herman
Obwohl ich nicht überrascht war, dass das Binning schlecht ablief, war ich überrascht, inwieweit die (0,1) -Skalierung durch die (cos, sin) -Methode übertroffen wurde. Mir ist aufgefallen, dass Sie Ihre Beispiele generiert haben, indem Sie die Steigung und den Verlauf der Linien zufällig ausgewählt haben. Dies würde meiner Meinung nach Linien erzeugen, deren Winkel nicht gleichmäßig verteilt sind, deren Steigungen jedoch. Ist es möglich, dass die (cos, sin) -Methode deshalb so viel besser abschneidet? Was würde passieren, wenn Sie die Ziele bräunen (Winkel) ...?
Ari Herman
tan(angle)π/4
Zwischen Julia und Numpy sowie zwischen Mocha und Caffe sollte es eine Eins-zu-Eins-Karte geben, wenn Sie sie wirklich neu implementieren möchten. Gibt es einen bestimmten Teil des Codes, den Sie schwer lesen können? Julia sollte eine leicht verständliche Sprache sein. Vielleicht habe ich etwas Seltsames getan.
Lyndon White
Am Ende habe ich Ihren Code gelesen und alles scheint korrekt zu sein. Trotzdem wollte ich meine eigene Version schreiben, da dies normalerweise lehrreich ist. Meine Implementierung unterscheidet sich geringfügig von Ihrer, daher ist es interessant, die Ergebnisse zu vergleichen. Ich werde sie in den nächsten Stunden veröffentlichen.
Ari Herman
5

Hier ist eine weitere Python-Implementierung, die Lyndon Whites vorgeschlagene Codierung mit einem Binned-Ansatz vergleicht. Der folgende Code erzeugte die folgende Ausgabe:

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

(Sünde(θ),cos(θ))(Sünde(θ),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
quelle
3

Hier ist meine Python-Version Ihres Experiments. Ich habe viele Details Ihrer Implementierung gleich gehalten, insbesondere die gleiche Bildgröße, Netzwerkebenengröße, Lernrate, Dynamik und Erfolgsmetrik.

Jedes getestete Netzwerk verfügt über eine verborgene Schicht (Größe = 500) mit logistischen Neuronen. Die Ausgangsneuronen sind, wie angegeben, entweder linear oder softmax. Ich habe 1.000 Trainingsbilder und 1.000 Testbilder verwendet, die unabhängig voneinander zufällig generiert wurden (daher kann es zu Wiederholungen kommen). Das Training bestand aus 50 Iterationen durch den Trainingssatz.

Mit Binning und "Gauß" -Codierung (ein Name, den ich mir ausgedacht habe; ähnlich wie Binning, außer dass der Zielausgabevektor die Form exp (-pi * ([1,2,3, ... , 500] - idx) ** 2) wobei idx der Index ist, der dem korrekten Winkel entspricht). Der Code ist unten; Hier sind meine Ergebnisse:

Testfehler für (cos, sin) Kodierung:

1.000 Trainingsbilder, 1.000 Testbilder, 50 Iterationen, lineare Ausgabe

  • Mittelwert: 0,0911558142071

  • Median: 0,0429723541743

  • Minimum: 2.77769843793e-06

  • Maximum: 6.2608513539

  • Genauigkeit auf 0,1: 85,2%

  • Genauigkeit auf 0,01: 11,6%

  • Genauigkeit bis 0,001: 1,0%

Testfehler für [-1,1] -Codierung:

1.000 Trainingsbilder, 1.000 Testbilder, 50 Iterationen, lineare Ausgabe

  • Mittelwert: 0,234181700523

  • Median: 0,17460197307

  • Minimum: 0,000473665840258

  • Maximum: 6.00637777237

  • Genauigkeit auf 0,1: 29,9%

  • Genauigkeit auf 0,01: 3,3%

  • Genauigkeit bis 0,001: 0,1%

Testfehler bei 1-von-500-Codierung:

1.000 Trainingsbilder, 1.000 Testbilder, 50 Iterationen, Softmax-Ausgabe

  • Mittelwert: 0,0298767021922

  • Median: 0,00388858079174

  • Minimum: 4.08712407829e-06

  • Maximum: 6.2784479965

  • Genauigkeit auf 0,1: 99,6%

  • Genauigkeit auf 0,01: 88,9%

  • Genauigkeit bis 0,001: 13,5%

Testfehler für Gaußsche Codierung:

1.000 Trainingsbilder, 1.000 Testbilder, 50 Iterationen, Softmax-Ausgabe

  • Mittelwert: 0,0296905377463
  • Median: 0,00365867335107
  • Minimum: 4.08712407829e-06
  • Maximum: 6.2784479965
  • Genauigkeit auf 0,1: 99,6%
  • Genauigkeit auf 0,01: 90,8%
  • Genauigkeit bis 0,001: 14,3%

Ich kann nicht verstehen, warum unsere Ergebnisse im Widerspruch zueinander zu stehen scheinen, aber es scheint eine weitere Untersuchung wert zu sein.

# -*- 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
quelle
Cool, auf Schlüssel anders. Sie trainieren nur einmal an jedem Bild. Ich trainiere auf jedem Bild 1.000 Mal oder 10.000 Mal. Mehrere Iterationen, obwohl die Trainingsdaten normal sind, insbesondere wenn mit relativ kleinen Datenmengen trainiert wird (und ich brauchte nur eine unveröffentlichte Diplomarbeit, um dies zu lernen, aber das ist eine andere Geschichte). Vor diesem Hintergrund sollte ich meiner Tabelle eine 1-Iter-Spalte hinzufügen. das wäre informativ
Lyndon White
Ich würde denken, dass das Training auf ähnlichen (aber nicht identischen) Bildern mit ähnlichen Zielen das Netzwerk in ähnlicher Weise beeinflussen würde. Wenn dies zutrifft, sollte es problemlos funktionieren, die Anzahl der zufälligen Bilder, auf die trainiert wird, zu erhöhen, anstatt sie mit einem kleineren Trainingssatz mehrmals zu wiederholen. Wollen Sie damit sagen, dass dies nicht der Fall ist?
Ari Herman
Es ist ähnlich, aber für diese Beispielaufgabe gibt es nicht das Problem, dass Sie schließlich alle möglichen Bilder anzeigen, sodass sich Ihr Test mit Ihrem Zug überschneidet und das Testen von Generalistion nicht funktioniert. Noch wichtiger ist, dass Sie 100.000 Trainingsbilder erstellen, dh <1000 * 1000 Trainingsbilder * Iterationen.
Lyndon White
Sie haben Recht, ich werde das Problem beheben. Es gibt ein noch größeres Problem mit meinem Code: Ich verwende logistische Neuronen, die nicht in der Lage sind, die für die Darstellung (cos, sin) erforderlichen negativen Werte zu erzeugen. D'oh! Ich werde meinen Code überarbeiten und ihn so schnell wie möglich erneut veröffentlichen.
Ari Herman
Möglicherweise möchten Sie (sofern Sie dies noch nicht getan haben) einen Graident-Check durchführen. Dies lohnt sich, wenn Sie neuronale Netze von Grund auf neu implementieren, da es sehr einfach ist, einen kleinen Fehler zu machen und Ihr Netzwerk meistens noch funktioniert. Betreff: Neuron: Ja, ich habe eine lineare Ausgabeebene auf der versteckten Sigmoid-Ebene
Lyndon White,
1

Eine andere Möglichkeit, den Winkel zu codieren, besteht darin, zwei Werte festzulegen:

y1 = max (0, Theta)

y2 = max (0, -theta)

theta_out = y1 - y2

Dies hätte das ähnliche Problem wie arctan2, da der Gradient bei Theta = 0 undefiniert ist. Ich habe nicht die Zeit, ein Netzwerk zu trainieren und mit den anderen Codierungen zu vergleichen, aber in diesem Artikel schien die Technik einigermaßen erfolgreich zu sein.

DerekG
quelle
1
Dies scheint eine Antwort zu sein, die mit einer anderen Frage in einem Beitrag vermischt ist. Diese Seite funktioniert ein bisschen anders als das Forum. Hier sollten sich die Antworten auf die Beantwortung der ursprünglichen Frage konzentrieren. Und wenn Sie eine andere Frage oder einen Kommentar haben, sollten Sie diese als solche veröffentlichen.
Karolis Koncevičius