Primeira vez aqui? Seja bem vindo e cheque o FAQ!
x

Como implementar um perceptron multicamadas para aprender uma função de R² em R³ em Python?

+1 voto
603 visitas
perguntada Jul 8, 2016 em Aprendizagem de Máquinas por Lyw (21 pontos)  
recategorizado Jul 8, 2016 por Lyw

Implemente um perceptron multicamada no python e aplique para aprender a seguinte função:

f (x, y) = [y ∗ exp(sin(x)) + u, sin(x) ∗ cos(y) + v , sin(x) + cos(y) + w ],

onde u,v e w são independentes e identicamente distribuídos com distribuição normal N(0, 0.09).

Compartilhe

1 Resposta

0 votos
respondida Jul 8, 2016 por Lyw (21 pontos)  

Foi implementado um perceptron com uma camada oculta de 12 neurônios por meio do PyBrain. O código foi baseado na solução do professor para aproximação da função seno. As linhas seguintes importam as bibliotecas e constroem a rede:

import matplotlib.pyplot as plt
import numpy as np
import math

from pybrain.tools.shortcuts import buildNetwork
from pybrain.structure import TanhLayer
from pybrain.supervised.trainers import BackpropTrainer
from pybrain.datasets import SupervisedDataSet
from pybrain.tools.validation import ModuleValidator
from matplotlib import cm
from mpl_toolkits.mplot3d import *

# Definicao da classe de rede neural. Após varios testes, 12 neuronios
# na camada escondida tiveram performance razoavel.
dimensaoDaEntrada = 2
dimensaoDaCamadaEscondida = 12
dimensaoDaSaida = 3

rn = buildNetwork(dimensaoDaEntrada, dimensaoDaCamadaEscondida,
                  dimensaoDaSaida, bias=True, hiddenclass=TanhLayer)

Cria-se, então, os dados. O código permite a opção de colocar ruído ou não nos dados. Vamos utilizar dados com ruído:

# Criacao dos dados
tamanhoDaAmostra = 100
dados = SupervisedDataSet(dimensaoDaEntrada, dimensaoDaSaida)

comRuido = True

for i in range(tamanhoDaAmostra):
    if(comRuido):
        x = np.random.uniform(0, 2 * math.pi, 1)
        y = np.random.uniform(0, 2 * math.pi, 1)
        dados.addSample((x, y), (y * math.exp(math.sin(x)) +
                        np.random.normal(0, 0.09, 1),
                        math.sin(x) * math.cos(y) +
                        np.random.normal(0, 0.09, 1),
                        math.sin(x) + math.cos(y) +
                        np.random.normal(0, 0.09, 1)))
    else:
        x = np.random.uniform(0, 2*math.pi, 1)
        y = np.random.uniform(0, 2*math.pi, 1)
        dados.addSample((x, y), (y * math.exp(math.sin(x)),
                        math.sin(x) * math.cos(y),
                        math.sin(x) + math.cos(y)))

Inicia-se o treinador e os parâmetros de treinamento:

treinadorSupervisionado = BackpropTrainer(rn, dados)

numeroDeAcessos = 10
numeroDeEpocasPorAcesso = 50

Em seguida, a aprendizagem em si. O código plota em tempo real a evolução do erro:

# Inicio dos graficos q mostram a evolucao do erro no processo
# de aprendizagem
fig0 = plt.figure()
ax0 = fig0.add_subplot(111)
ax0.axis([-50, numeroDeAcessos * numeroDeEpocasPorAcesso+50, 0.00001, 4])
ax0.set_yscale('log')
fig0.hold()
meansq = ModuleValidator()
erro = meansq.MSE(treinadorSupervisionado.module, dados)
print erro
ax0.plot([0], [erro], 'bo')

tempoPausa = 1
for i in range(numeroDeAcessos):
    treinadorSupervisionado.trainEpochs(numeroDeEpocasPorAcesso)
    meansq = ModuleValidator()
    erro = meansq.MSE(treinadorSupervisionado.module, dados)
    print erro
    ax0.plot([numeroDeEpocasPorAcesso * (i + 1)], [erro], 'bo')
    plt.pause(tempoPausa)

Finalmente, calcula-se a resposta da rede às entradas para fins de comparação com os dados. Gráficos são gerados para a comparação. Eles mostram os dados em azul, os resultados da rede sobre as entradas após o treinamento em vermelho e também as superfícies que representam a função alvo na saída sem ruído.

# Calcula a saida da rede a partir dos dados de entrada
inputOutputRede = np.array([rn.activate(datax) for datax, _ in dados])

# Os graficos abaixo mostram os dados em azul, os resultados da rede sobre
# as entradas apos o terinamento em vermelho e tambem as superficies
# que representam a funcao alvo na saida sem ruido para comparacao
fig1 = plt.figure()
ax1 = fig1.gca(projection='3d')
plt.hold(True)
x_surf = np.arange(0, 2 * math.pi, 0.05)
y_surf = np.arange(0, 2 * math.pi, 0.05)
x_surf, y_surf = np.meshgrid(x_surf, y_surf)
z_surf = y_surf * np.exp(np.sin(x_surf))
ax1.plot_surface(x_surf, y_surf, z_surf, cmap=cm.hot, alpha=0.2)
ax1.scatter(dados['input'][:, 0], dados['input'][:, 1],
            dados['target'][:, 0], c='b')
ax1.scatter(dados['input'][:, 0], dados['input'][:, 1],
            inputOutputRede[:, 0], c='r')
plt.show()

fig2 = plt.figure()
ax2 = fig2.gca(projection='3d')
plt.hold(True)
x_surf = np.arange(0, 2 * math.pi, 0.05)
y_surf = np.arange(0, 2 * math.pi, 0.05)
x_surf, y_surf = np.meshgrid(x_surf, y_surf)
z_surf = np.sin(x_surf) * np.cos(y_surf)
ax2.plot_surface(x_surf, y_surf, z_surf, cmap=cm.hot, alpha=0.2)
ax2.scatter(dados['input'][:, 0], dados['input'][:, 1],
            dados['target'][:, 1], c='b')
ax2.scatter(dados['input'][:, 0], dados['input'][:, 1],
            inputOutputRede[:, 1], c='r')
plt.show()

fig3 = plt.figure()
ax3 = fig3.gca(projection='3d')
plt.hold(True)
x_surf = np.arange(0, 2 * math.pi, 0.05)
y_surf = np.arange(0, 2 * math.pi, 0.05)
x_surf, y_surf = np.meshgrid(x_surf, y_surf)
z_surf = np.sin(x_surf) + np.cos(y_surf)
ax3.plot_surface(x_surf, y_surf, z_surf, cmap=cm.hot, alpha=0.2)
ax3.scatter(dados['input'][:, 0], dados['input'][:, 1],
            dados['target'][:, 2], c='b')
ax3.scatter(dados['input'][:, 0], dados['input'][:, 1],
            inputOutputRede[:, 2], c='r')
plt.show()

A figura seguinte mostra a evolução do erro. Note que a escala é logarítmica:

A imagem será apresentada aqui.

A primeira componente corresponde à função y ∗ exp(sin(x)). Ela parece complicada, mas é a mais fácil de aproximar, porque seu comportamento no domínio considerado não é tão não-linear. O gráfico deixa isso claro:

A imagem será apresentada aqui.

A segunda componente, sin(x) ∗ cos(y), é a mais difícil. Apesar da rede possuir uma boa capacidade para aproximar funções trigonométricas, o produto nesta saída torna a tarefa muito difícil. A amostra também é relativamente pequena:

A imagem será apresentada aqui.

Finalmente, a terceira componente, sin(x) + cos(y), apresenta dificuldade intermediária. A rede aproxima bem funções trigonométricas e a soma é uma operação bem natural:

A imagem será apresentada aqui.

...