Codificando dados de ângulo para redes neurais

20

Estou treinando uma rede neural (detalhes não importantes) em que os dados de destino são um vetor de ângulos (entre 0 e 2 * pi). Estou procurando conselhos sobre como codificar esses dados. Aqui está o que estou tentando atualmente (com sucesso limitado):

1) Codificação 1-de-C: divido os ângulos possíveis configurados em mais ou menos 1000 ângulos discretos e, em seguida, indico um ângulo específico colocando 1 no índice relevante. O problema disso é que a rede simplesmente aprende a emitir todos os 0s (já que isso é quase exatamente correto).

2) Escalonamento simples: escalonei o intervalo de saída da rede ([0,1]) para [0,2 * pi]. O problema aqui é que os ângulos têm naturalmente uma topologia circular (ou seja, 0,0001 e 2 * pi estão realmente próximos um do outro). Com esse tipo de codificação, essas informações são perdidas.

Todas as sugestões serão apreciadas!

Ari Herman
fonte
1
Você não deve ter problemas com a rede produzindo todos os zeros, se você usa uma camada de saída softmax - o que geralmente deve ser feito, se estiver usando uma saída catagórica (por exemplo, 1-de-C).
Lyndon White
7
Uma idéia de codificação puramente especulativa (eu não a vi fazer ou testá-la, mas não a observei) é codificar seu ângulo ( ) como um par: . Eu acho que então seria um mapa contínuo com todos os valores como e 2 \ pi próximos um do outro. Acho que posso criar uma demonstração disso e testá-lo. θ ( sin ( θ ) , cos ( θ ) ) 0 2 πθθ(sin(θ),cos(θ))02π
Lyndon White
Estive pensando um pouco mais sobre isso, e acho que pode estar na sua função de perda. Eu quero tentar um monte de coisas. Eu fiz a demo, mas não terminei os testes. Espere uma resposta detalhada com o apoio experimental amanhã em algum momento. (Pique-me se eu não fizer)
Lyndon Branca
Atualmente, não estou usando uma camada softmax, e esse provavelmente é o problema. Vou implementar isso hoje, se tiver uma chance! Sua ideia (cos, sin) é muito interessante e eu gosto especialmente que ela automaticamente coloque esse intervalo em [-1,1] (bom se você estiver trabalhando com uma função de ativação tanh). Estou ansioso para ver os resultados1 #
Ari Herman
Uma atualização rápida: tentei implementar uma camada softmax e ainda não estou tendo sorte. A questão, penso eu, é que, para esse problema, é essencial que a "angularidade" dos dados seja representada de alguma forma na codificação. Com uma codificação categórica, a topologia dos dados de destino é perdida. Portanto, um erro de 0,5 * pi e 0,05 * pi parece o mesmo para a rede (ele vê tanto como categorizações incorretas).
Ari Herman

Respostas:

18

Introdução

Acho essa pergunta realmente interessante, suponho que alguém tenha publicado um trabalho sobre ela, mas é meu dia de folga, então não quero ir atrás de referências.

Portanto, poderíamos considerá-lo como uma representação / codificação da saída, o que faço nesta resposta. Eu continuo pensando que existe uma maneira melhor, onde você pode simplesmente usar uma função de perda ligeiramente diferente. (Talvez soma das diferenças ao quadrado, usando o módulo de subtração 2 ).π

Mas em diante com a resposta real.

Método

Proponho que um ângulo seja representado como um par de valores, seu seno e seu cosseno.θ

Portanto, a função de codificação é: e a função de decodificação é: Para o arctan2 sendo as tangentes inversas, preservando a direção em todos os quadrantes)θ(sin(θ),cos(θ))
(y1,y2)arctan2(y1,y2)

Em teoria, você poderia trabalhar de forma equivalente diretamente com os ângulos se sua ferramenta usar suporte atan2como uma função de camada (recebendo exatamente 2 entradas e produzindo 1 saída). O TensorFlow faz isso agora e oferece suporte à descida gradiente , embora não seja destinado a esse uso. Eu investiguei usando out = atan2(sigmoid(ylogit), sigmoid(xlogit)) uma função de perda min((pred - out)^2, (pred - out - 2pi)^2). Descobri que ele treinava muito pior do que usar outs = tanh(ylogit), outc = tanh(xlogit)) com uma função de perda 0.5((sin(pred) - outs)^2 + (cos(pred) - outc)^2. Que eu acho que pode ser atribuído ao gradiente ser descontínuo poratan2

Meu teste aqui o executa como uma função de pré-processamento

Para avaliar isso, defini uma tarefa:

Dada uma imagem em preto e branco que representa uma única linha em um plano de fundo em branco, indique qual é o ângulo dessa linha no "eixo x positivo"

Eu implementei uma função gerar essas imagens aleatoriamente, com linhas em ângulos aleatórios (NB: as versões anteriores deste post usavam inclinações aleatórias, em vez de ângulos aleatórios. Agradecemos a @Ari Herman por apontar isso. Agora está corrigido). Eu construí várias redes neurais para avaliar o desempenho da tarefa. Os detalhes completos da implementação estão neste caderno Jupyter . O código está todo em Julia e eu uso a biblioteca de rede neural Mocha .

Para comparação, apresento-o contra os métodos alternativos de escala para 0,1. e colocar em 500 escaninhos e usar softmax com etiqueta macia. Não estou particularmente feliz com o último e sinto que preciso ajustá-lo. É por isso que, diferentemente dos outros, eu apenas o testo por 1.000 iterações, contra os outros dois que foram executados por 1.000 e por 10.000

Configuração Experimental

As imagens eram pixels, com a linha voltada para o centro e indo para a borda. Não havia ruído, etc. na imagem, apenas uma linha "preta", sobre fundo branco.101×101

Para cada trilha, 1.000 treinamentos e 1.000 imagens de teste foram geradas aleatoriamente.

A rede de avaliação tinha uma única camada oculta de largura 500. Neurônios sigmóides foram usados ​​na camada oculta.

Foi treinado por Decocção de Gradiente Estocástico, com uma taxa de aprendizado fixa de 0,01 e um momento fixo de 0,9.

Não houve regularização ou abandono. Nem houve qualquer tipo de convolução, etc. Uma rede simples, que espero sugira que esses resultados generalizem

É muito fácil ajustar esses parâmetros no código de teste , e eu encorajo as pessoas a fazer isso. (e procure por erros no teste).

Resultados

Meus resultados são os seguintes:

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

Onde me refiro ao erro, esse é o valor absoluto da diferença entre o ângulo de saída da rede neural e o ângulo real. Portanto, o erro médio (por exemplo) é a média dos 1.000 casos de teste dessa diferença, etc. Não tenho certeza de que não devo escalá-lo novamente, cometendo um erro de dizer igual para um erro de ). π7π4π4

Apresento também a precisão em vários níveis de granularidade. A precisão é a parte dos casos de teste que foi corrigida. Isso accuracy_to_point01significa que foi contado como correto se a saída estivesse dentro de 0,01 do ângulo real. Nenhuma das representações obteve resultados perfeitos, mas isso não surpreende, dado o funcionamento da matemática de ponto flutuante.

Se você der uma olhada no histórico deste post, verá que os resultados têm um pouco de ruído para eles, um pouco diferente a cada vez que o executo. Mas a ordem geral e a escala de valores permanecem as mesmas; permitindo assim tirar algumas conclusões.

Discussão

Binning com softmax tem um desempenho de longe o pior, pois eu disse que não tenho certeza de que não estraguei nada na implementação. Porém, ele apresenta um desempenho ligeiramente acima da taxa de estimativa. se apenas estivéssemos supondo que estaríamos recebendo um erro médio deπ

A codificação sin / cos tem um desempenho significativamente melhor que a codificação em escala 0-1. A melhoria é na medida em que, em 1.000 iterações de treinamento, o sin / cos está apresentando um desempenho três vezes melhor na maioria das métricas do que o dimensionamento em 10.000 iterações.

Eu acho que, em parte, isso está relacionado à melhoria da generalização, pois ambos estavam obtendo um erro quadrático médio bastante semelhante no conjunto de treinamento, pelo menos uma vez que 10.000 iterações foram executadas.

Certamente, existe um limite superior para o melhor desempenho possível nessa tarefa, já que o ângulo pode ser mais ou menos qualquer número real, mas nem todos esses anjos produzem linhas diferentes na resolução de pixels. Portanto, como, por exemplo, os ângulos 45.0 e 45.0000001 estão vinculados à mesma imagem nessa resolução, nenhum método jamais obterá os dois perfeitamente corretos.101×101

Também parece provável que, em uma escala absoluta, para ir além desse desempenho, seja necessária uma rede neural melhor. Em vez do muito simples descrito acima na configuração experimental.

Conclusão.

Parece que a representação sin / cos é de longe a melhor das representações que investiguei aqui. Isso faz sentido, pois tem um valor suave à medida que você se move ao redor do círculo. Também gosto que o inverso possa ser feito com o arctan2 , que é elegante.

Acredito que a tarefa apresentada seja suficiente em sua capacidade de apresentar um desafio razoável para a rede. Embora eu ache que realmente esteja apenas aprendendo a fazer o ajuste de curva para , talvez seja muito fácil. E talvez pior ainda, esteja favorecendo a representação emparelhada. Eu não acho que seja, mas está ficando tarde aqui, então talvez eu tenha perdido algo que eu convido você novamente a examinar meu código . Sugira melhorias ou tarefas alternativas.f(x)=y1y2x

Lyndon White
fonte
Esta é certamente a resposta mais completa que já recebi sobre troca de pilhas. Como não estou familiarizado com Julia, é difícil para mim examinar seu código ... então, em vez disso, tentarei replicar seus resultados usando Python. Vou postar as conclusões mais tarde hoje ou amanhã.
Ari Herman
Embora não tenha me surpreendido que o binning tenha tido um desempenho ruim, fiquei surpreso com o grau em que (0,1) a escala foi superada pelo método (cos, sin). Percebi que você gerou seus exemplos escolhendo aleatoriamente a ascensão e o andamento das linhas. Penso que isso geraria linhas cujos ângulos não são uniformemente distribuídos, mas cujas inclinações são. É possível que seja por isso que o método (cos, sin) tenha se saído muito melhor? O que aconteceria se você deixasse os alvos bronzeados (ângulo) ...?
Ari Herman
você está certo sobre inclinação aleatória vs ângulo aleatório. mas não acho que a segmentação funcione tan(angle)tão bem, dado que tan não está definido para todos os ângulos (por exemplo, ). Vou executá-lo novamente com ângulos gerados aleatoriamente e editar as postagens. π/4
Lyndon White
Deve haver um mapa aproximado de um para um entre julia e numpy e entre Mocha e Caffe, se você realmente deseja reimplementá-lo. Existe uma parte específica do código que você acha difícil de ler? Julia deve ser uma linguagem fácil de entender. Então, talvez eu tenha feito algo estranho.
Lyndon White
Acabei lendo seu código e tudo parece correto. Ainda assim, eu queria escrever minha própria versão, pois isso geralmente é instrutivo. Minha implementação é um pouco diferente da sua, por isso será interessante comparar resultados. Vou publicá-los dentro das próximas duas horas.
Ari Herman
5

Aqui está outra implementação do Python comparando a codificação proposta por Lyndon White a uma abordagem em bin. O código abaixo produziu a seguinte saída:

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

Como você pode ver, enquanto a abordagem binned executa admiravelmente nesta tarefa de brinquedo, a codificação desempenho melhor em todas as configurações de treinamento, às vezes por uma margem considerável. Eu suspeito que a tarefa específica se tornou mais complexo, os benefícios da utilização Lyndon Branco 's representação se tornaria mais pronunciada.( sin ( θ ) , cos ( θ ) )(sin(θ),cos(θ))(sin(θ),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
fonte
3

Aqui está minha versão em Python do seu experimento. Mantive muitos detalhes da sua implementação iguais, em particular utilizo o mesmo tamanho de imagem, tamanhos de camada de rede, taxa de aprendizado, momento e métricas de sucesso.

Cada rede testada possui uma camada oculta (tamanho = 500) com neurônios logísticos. Os neurônios de saída são lineares ou softmax, conforme observado. Usei 1.000 imagens de treinamento e 1.000 imagens de teste que foram geradas de forma independente e aleatória (para que haja repetições). O treinamento consistiu em 50 iterações no conjunto de treinamento.

Consegui obter uma precisão muito boa usando binning e codificação "gaussiana" (um nome que eu criei; semelhante ao binning, exceto que o vetor de saída de destino tem o formato exp (-pi * ([1,2,3, ... , 500] - idx) ** 2) em que idx é o índice correspondente ao ângulo correto). O código está abaixo; Aqui estão meus resultados:

Erro de teste para codificação (cos, sin):

1.000 imagens de treinamento, 1.000 imagens de teste, 50 iterações, saída linear

  • Média: 0,0911558142071

  • Mediana: 0.0429723541743

  • Mínimo: 2.77769843793e-06

  • Máximo: 6.2608513539

  • Precisão de 0,1: 85,2%

  • Precisão em 0,01: 11,6%

  • Precisão em 0.001: 1.0%

Erro de teste para codificação [-1,1]:

1.000 imagens de treinamento, 1.000 imagens de teste, 50 iterações, saída linear

  • Média: 0.234181700523

  • Mediana: 0.17460197307

  • Mínimo: 0.000473665840258

  • Máximo: 6.00637777237

  • Precisão em 0.1: 29.9%

  • Precisão em 0,01: 3,3%

  • Precisão de 0,001: 0,1%

Erro de teste para codificação 1-de-500:

1.000 imagens de treinamento, 1.000 imagens de teste, 50 iterações, saída softmax

  • Média: 0,0298767021922

  • Mediana: 0.00388858079174

  • Mínimo: 4.08712407829e-06

  • Máximo: 6.2784479965

  • Precisão de 0,1: 99,6%

  • Precisão em 0,01: 88,9%

  • Precisão de 0,001: 13,5%

Erro de teste para codificação gaussiana:

1.000 imagens de treinamento, 1.000 imagens de teste, 50 iterações, saída softmax

  • Média: 0,0296905377463
  • Mediana: 0.00365867335107
  • Mínimo: 4.08712407829e-06
  • Máximo: 6.2784479965
  • Precisão de 0,1: 99,6%
  • Precisão em 0,01: 90,8%
  • Precisão em 0.001: 14,3%

Não consigo descobrir por que nossos resultados parecem estar em contradição um com o outro, mas parece valer uma investigação mais aprofundada.

# -*- 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
fonte
Legal, na tecla diferente. Você está treinando apenas em cada imagem uma vez. Estou treinando em cada imagem 1.000 vezes ou 10.000 vezes. Múltiplas iterações, embora os dados de treinamento sejam normais, principalmente quando se treina em quantidades relativamente pequenas de dados (e levou apenas uma tese de graduação não publicada para aprender isso, mas isso é outra história). Com isso dito, devo adicionar uma coluna de 1 iter à minha tabela. que seria informativa
Lyndon Branca
Eu pensaria que o treinamento em imagens semelhantes (mas não idênticas) com objetivos semelhantes afetaria essa rede da mesma forma. Se isso for verdade, deve funcionar bem para simplesmente aumentar o número de imagens aleatórias sendo treinadas, repetindo várias vezes através de um conjunto de treinamento menor. Você está dizendo que não é esse o caso?
Ari Herman
É semelhante, mas para esta tarefa de exemplo, não há o problema de que, eventualmente, você mostrará todas as imagens possíveis para que seu teste se sobreponha ao seu trem, para que a generalização de teste não funcione. Mais significativamente, você faz 100.000 imagens de treinamento, que são <1000 * 1000 imagens de treinamento * Iterações.
Lyndon White
Você está correto, eu vou corrigir esse problema. Existe um problema ainda mais significativo no meu código: estou usando neurônios logísticos que são incapazes de produzir os valores negativos exigidos pela representação (cos, sin). D'oh! Estarei revisando meu código e republicando o mais rápido possível.
Ari Herman
Você pode (se ainda não o fez) estar interessado em fazer uma verificação Graident , que vale a pena quando implementamos redes neurais do zero, pois é muito fácil cometer um pequeno erro e sua rede ainda funciona. Re: Neuron: sim, eu tenho uma camada de saída linear, sobre da camada escondida sigmóide
Lyndon Branca
1

Outra maneira de codificar o ângulo é como um conjunto de dois valores:

y1 = max (0, teta)

y2 = max (0, teta)

theta_out = y1 - y2

Isso teria o mesmo problema que o arctan2, pois o gradiente é indefinido em theta = 0. Não tenho tempo para treinar uma rede e comparar com outras codificações, mas neste artigo a técnica parecia razoavelmente bem-sucedida.

DerekG
fonte
1
Parece uma resposta misturada com outra pergunta em um post. Este site funciona um pouco diferente do que o fórum faria. Aqui, as respostas devem se concentrar em responder à pergunta original. E se você tiver outra pergunta ou comentário - ele deve ser publicado como tal.
Karolis Koncevičius