Codifica dei dati angolari per la rete neurale


20

Sto addestrando una rete neurale (dettagli non importanti) in cui i dati target sono un vettore di angoli (tra 0 e 2 * pi). Sto cercando consigli su come codificare questi dati. Ecco cosa sto provando attualmente (con successo limitato):

1) Codifica 1-of-C: inserisco i possibili angoli impostati in circa 1000 angoli discreti e quindi indico un angolo particolare inserendo un 1 nell'indice pertinente. Il problema è che la rete impara semplicemente a produrre tutti gli 0 (poiché questo è quasi esattamente corretto).

2) Semplice ridimensionamento: ho ridimensionato l'intervallo di output delle reti (da [0,1]) a [0,2 * pi]. Il problema qui è che gli angoli hanno naturalmente una topologia circolare (ovvero 0,0001 e 2 * pi sono in realtà uno accanto all'altro). Con questo tipo di codifica, tali informazioni vengono perse.

Tutti i suggerimenti sarebbero apprezzati!


1
Non dovresti avere problemi con la rete che emette tutti gli zeri, se usi un livello di output softmax - cosa che dovresti fare in genere, se stai usando un output catagorico (cioè 1-of-C).
Lyndon White l'

7
Un'idea di codifica puramente speculativa (non l'ho vista fatta o testata, ma non l'ho guardata) è codificare il tuo angolo ( ) in coppia: θ ( sin ( θ ) , cos ( θ ) ) . Penso che sarebbe una mappa continua con tutti i valori come 0 e vicini l'uno all'altro. Penso che potrei creare una demo di questo e provarlo. θθ(peccato(θ),cos(θ))02π
Lyndon White l'

ci ho pensato un po 'di più e penso che potrebbe davvero essere tutto nella tua funzione di perdita. Voglio provare un sacco di cose. Ho fatto la demo, ma non ho finito i test su di essa. Aspettatevi una risposta dettagliata con supporto sperimentale domani a volte. (Colpiscimi se non lo faccio)
Lyndon White l'

Attualmente non sto usando uno strato di softmax, e questo è probabilmente il problema. Lo implementerò oggi se ne avrò la possibilità! La tua idea (cos, sin) è molto interessante e mi piace soprattutto che metta automaticamente tale intervallo in [-1,1] (buono se stai lavorando con una funzione di attivazione tanh). Non vedo l'ora di vedere i tuoi risultati1
Ari Herman,

Un rapido aggiornamento: ho provato ad implementare un layer softmax e non ho ancora avuto fortuna. Il problema, penso, è che per questo problema è essenziale che l '"angolarità" dei dati sia in qualche modo rappresentata nella codifica. Con una codifica categoriale, la topologia dei dati di destinazione viene persa. Quindi un errore di 0,5 * pi e 0,05 * pi sembra lo stesso per la rete (vede entrambe come categorizzazioni errate).
Ari Herman,

Risposte:


18

introduzione

Trovo questa domanda davvero interessante, suppongo che qualcuno ci abbia messo un documento, ma è il mio giorno libero, quindi non voglio andare a cercare riferimenti.

Quindi potremmo considerarlo come una rappresentazione / codifica dell'output, cosa che faccio in questa risposta. Continuo a pensare che esiste un modo migliore, in cui puoi semplicemente usare una funzione di perdita leggermente diversa. (Forse somma delle differenze al quadrato, usando la sottrazione modulo 2 ).π

Ma in seguito con la risposta effettiva.

Metodo

Propongo che un angolo sia rappresentato come una coppia di valori, il suo seno e il suo coseno.θ

Quindi la funzione di codifica è: e la funzione di decodifica è:θ(peccato(θ),cos(θ))
Perarctan2essendo le tangenti inverse, preservando la direzione in tutti i quadranti)(y1,y2)arctan2(y1,y2)

In teoria, potresti lavorare in modo equivalente direttamente con gli angoli se il tuo strumento utilizza supportato atan2come una funzione di livello (prendendo esattamente 2 input e producendo 1 output). TensorFlow lo fa ora e supporta la discesa gradiente su di esso , anche se non è previsto per questo uso. Ho studiato usando out = atan2(sigmoid(ylogit), sigmoid(xlogit)) con una funzione di perdita min((pred - out)^2, (pred - out - 2pi)^2). Ho scoperto che si è allenato molto peggio che usare outs = tanh(ylogit), outc = tanh(xlogit)) con una funzione di perdita 0.5((sin(pred) - outs)^2 + (cos(pred) - outc)^2. Il che penso possa essere attribuito al gradiente di essere discontinuo peratan2

I miei test qui vengono eseguiti come una funzione di preelaborazione

Per valutare questo ho definito un'attività:

Data un'immagine in bianco e nero che rappresenta una singola linea su uno sfondo bianco Output quale angolo è quella linea rispetto all '"asse x positivo"

Ho implementato una funzione per generare casualmente queste immagini, con linee ad angoli casuali (NB: le versioni precedenti di questo post utilizzavano pendenze casuali, piuttosto che angoli casuali. Grazie a @Ari Herman per averlo sottolineato. Ora è stato risolto). Ho costruito diverse reti neurali per valutare le prestazioni sull'attività. I dettagli completi dell'implementazione si trovano in questo notebook Jupyter . Il codice è tutto in Julia e utilizzo la libreria di rete neurale Mocha .

Per confronto, lo presento contro i metodi alternativi di ridimensionamento a 0,1. e mettere in 500 contenitori e usare softmax soft-label. Non sono particolarmente soddisfatto dell'ultimo e sento di doverlo modificare. Ecco perché, a differenza degli altri, l'ho provato solo per 1.000 iterazioni, rispetto agli altri due che sono stati eseguiti per 1.000 e per 10.000

Setup sperimentale

Le immagini erano pixel, con la linea che si spostava al centro e si spostava verso il bordo. Non c'erano rumori, ecc. Nell'immagine, solo una linea "nera", su uno sfondo bianco.101×101

Per ogni percorso sono stati generati in modo casuale 1.000 allenamenti e 1.000 immagini di prova.

La rete di valutazione aveva un singolo strato nascosto di larghezza 500. I neuroni sigmoidi venivano usati nello strato nascosto.

È stato addestrato da Stochastic Gradient Decent, con un tasso di apprendimento fisso di 0,01 e un momento fisso di 0,9.

Non è stata utilizzata alcuna regolarizzazione o dropout. Né c'era alcun tipo di convoluzione ecc. Una rete semplice, che spero suggerisca che questi risultati si generalizzeranno

È molto facile modificare questi parametri nel codice di test e incoraggio le persone a farlo. (e cerca bug nel test).

risultati

I miei risultati sono i seguenti:

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

7π4π4

Presento anche l'accuratezza a vari livelli di granularità. L'accuratezza è la parte dei casi di test che è stata corretta. Quindi accuracy_to_point01significa che è stato considerato corretto se l'uscita si trovava entro 0,01 dall'angolo reale. Nessuna delle rappresentazioni ha ottenuto risultati perfetti, ma ciò non è affatto sorprendente dato il funzionamento della matematica in virgola mobile.

Se dai un'occhiata alla storia di questo post vedrai che i risultati avranno un po 'di rumore, leggermente diverso ogni volta che lo riesco. Ma l'ordine generale e la scala dei valori rimangono gli stessi; permettendoci così di trarre alcune conclusioni.

Discussione

π

La codifica sin / cos ha prestazioni significativamente migliori rispetto alla codifica 0-1 in scala. Il miglioramento è nella misura in cui a 1.000 allenamenti iterazioni sin / cos sta eseguendo circa 3 volte meglio sulla maggior parte delle metriche rispetto al ridimensionamento a 10.000 iterazioni.

Penso che, in parte, ciò sia legato al miglioramento della generalizzazione, poiché entrambi stavano ottenendo un errore quadratico medio abbastanza simile sul set di addestramento, almeno una volta sono state eseguite 10.000 iterazioni.

101×101

Sembra anche probabile che su una scala assoluta per andare oltre queste prestazioni, sia necessaria una migliore rete neurale. Piuttosto che quello molto semplice descritto sopra nell'impostazione sperimentale.

Conclusione.

Sembra che la rappresentazione peccato / cos sia di gran lunga la migliore, delle rappresentazioni che ho studiato qui. Questo ha senso, in quanto ha un valore uniforme mentre ti muovi nel cerchio. Mi piace anche che l'inverso possa essere fatto con arctan2 , che è elegante.

f(X)=y1y2X


Questa è sicuramente la risposta più completa che abbia mai ricevuto in cambio di stack. Dal momento che non ho familiarità con Julia, è difficile per me esaminare il tuo codice ... quindi, invece, proverò a replicare i tuoi risultati usando Python. Pubblicherò i risultati più tardi oggi o domani.
Ari Herman,

Mentre non ero sorpreso dal fatto che il binning funzionasse male, sono rimasto sorpreso dal grado in cui (0,1) il ridimensionamento è stato sovraperformato dal metodo (cos, sin). Ho notato che hai generato i tuoi esempi selezionando casualmente l'ascesa e la corsa delle linee. Ciò, a mio avviso, genererebbe linee i cui angoli non sono distribuiti uniformemente, ma le cui pendenze lo sono. È possibile che questo sia il motivo per cui il metodo (cos, sin) ha funzionato molto meglio? Cosa accadrebbe se abbronzassi gli obiettivi (angolo) ...?
Ari Herman,

tan(angle)π/4

Dovrebbe esserci una mappa ravvicinata tra julia e numpy e tra Moka e Caffe, se vuoi davvero reimplementarla. C'è una parte particolare del codice che trovi difficile da leggere? Julia dovrebbe essere una lingua facile da capire. Quindi forse ho fatto qualcosa di strano.
Lyndon White,

Ho finito per leggere il tuo codice e tutto sembra corretto. Tuttavia, volevo scrivere la mia versione, dato che farlo di solito è istruttivo. La mia implementazione è leggermente diversa dalla tua, quindi sarà interessante confrontare i risultati. Li posterò entro un paio d'ore.
Ari Herman,

5

Ecco un'altra implementazione di Python che confronta la codifica proposta da Lyndon White con un approccio integrato. Il codice seguente ha prodotto il seguente output:

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

(peccato(θ),cos(θ))(peccato(θ),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()

3

Ecco la mia versione di Python del tuo esperimento. Ho mantenuto molti dei dettagli della tua implementazione allo stesso modo, in particolare utilizzo le stesse dimensioni dell'immagine, dimensioni del livello di rete, velocità di apprendimento, quantità di moto e metriche di successo.

Ogni rete testata ha uno strato nascosto (dimensione = 500) con neuroni logistici. I neuroni in uscita sono lineari o softmax come indicato. Ho usato 1.000 immagini di allenamento e 1.000 immagini di test che sono state generate in modo indipendente e casuale (quindi potrebbero esserci delle ripetizioni). La formazione consisteva in 50 iterazioni attraverso il set di formazione.

Sono stato in grado di ottenere una precisione abbastanza buona usando il binning e la codifica "gaussiana" (un nome che ho inventato; simile al binning tranne per il fatto che il vettore di output di destinazione ha la forma exp (-pi * ([1,2,3, ... , 500] - idx) ** 2) dove idx è l'indice corrispondente all'angolo corretto). Il codice è sotto; ecco i miei risultati:

Errore di test per la codifica (cos, sin):

1.000 immagini di allenamento, 1.000 immagini di prova, 50 iterazioni, output lineare

  • Media: 0,0911558142071

  • Mediana: 0,0429723541743

  • Minimo: 2.77769843793e-06

  • Massimo: 6.2608513539

  • Precisione allo 0,1: 85,2%

  • Precisione a 0,01: 11,6%

  • Precisione a 0,001: 1,0%

Errore di test per la codifica [-1,1]:

1.000 immagini di allenamento, 1.000 immagini di prova, 50 iterazioni, output lineare

  • Media: 0,234181700523

  • Mediana: 0,17460197307

  • Minimo: 0.000473665840258

  • Massimo: 6.00637777237

  • Precisione allo 0,1: 29,9%

  • Precisione a 0,01: 3,3%

  • Precisione a 0,001: 0,1%

Errore di test per la codifica 1 su 500:

1.000 immagini di allenamento, 1.000 immagini di prova, 50 iterazioni, uscita softmax

  • Media: 0,0298767021922

  • Mediana: 0,00388858079174

  • Minimo: 4.08712407829e-06

  • Massimo: 6.2784479965

  • Precisione allo 0,1: 99,6%

  • Precisione a 0,01: 88,9%

  • Precisione a 0,001: 13,5%

Errore di test per la codifica gaussiana:

1.000 immagini di allenamento, 1.000 immagini di prova, 50 iterazioni, uscita softmax

  • Media: 0,0296905377463
  • Mediana: 0,00365867335107
  • Minimo: 4.08712407829e-06
  • Massimo: 6.2784479965
  • Precisione allo 0,1: 99,6%
  • Precisione a 0,01: 90,8%
  • Precisione a 0,001: 14,3%

Non riesco a capire perché i nostri risultati sembrano essere in contraddizione l'uno con l'altro, ma sembra che valga la pena indagare ulteriormente.

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

Fantastico, con chiave diversa. Ti stai allenando su ogni immagine solo una volta. Mi sto allenando su ogni immagine 1.000 volte o 10.000 volte. Iterazioni multiple sebbene i dati di allenamento siano normali, in particolare quando ci si allena su quantità relativamente ridotte di dati (e mi è bastata una tesi di laurea non pubblicabile per imparare questo, ma questa è un'altra storia). Detto questo, dovrei aggiungere una colonna di 1 iter alla mia tabella. sarebbe informativo
Lyndon White,

Penserei che allenarsi su immagini simili (ma non identiche) con obiettivi simili influenzerebbe la rete in modo simile. Se questo è vero, dovrebbe funzionare bene per aumentare semplicemente il numero di immagini casuali su cui si sta allenando, piuttosto ripetendo più volte attraverso un set di addestramento più piccolo. Stai dicendo che non è così?
Ari Herman,

È simile, ma per questo compito di esempio non ha il problema che alla fine mostrerai tutte le immagini possibili in modo che il tuo test si sovrapponga al tuo treno, quindi il test generalistion non funzionerà. Più significativamente se si eseguono 100.000 immagini di allenamento, ovvero <1000 * 1000 immagini di allenamento * Iterazioni.
Lyndon White,

Hai ragione, risolverò quel problema. C'è un problema ancora più significativo con il mio codice: sto usando neuroni logistici che non sono in grado di produrre i valori negativi richiesti dalla rappresentazione (cos, sin). D'oh! Revisionerò il mio codice e ripubblicherò il prima possibile.
Ari Herman,

Potresti (se non l'hai già fatto) essere interessato a fare un Graident Check , che vale la pena implementare reti neurali da zero, poiché è molto facile fare un piccolo errore e far funzionare la tua rete per lo più. Ri: Neurone: sì, ho uno strato di output lineare, sullo strato nascosto sigmoideo
Lyndon White

1

Un altro modo per codificare l'angolo è come un insieme di due valori:

y1 = max (0, theta)

y2 = max (0, -theta)

theta_out = y1 - y2

Ciò avrebbe lo stesso problema di arctan2 in quanto il gradiente non è definito a theta = 0. Non ho il tempo di addestrare una rete e confrontarmi con le altre codifiche ma in questo documento la tecnica sembrava ragionevolmente riuscita.


1
Sembra una risposta unita a un'altra domanda in un post. Questo sito funziona in modo leggermente diverso rispetto al forum. Qui le risposte dovrebbero concentrarsi sulla risposta alla domanda originale. E se hai un'altra domanda o un commento, dovrebbe essere pubblicato come tale.
Karolis Koncevičius,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.