SingularCode

Redes Neurais – multilayer

Redes Neurais - Multilayer

As redes neurais multicamada (Multilayer Perceptrons – MLPs) são um tipo fundamental de rede neural artificial, que desempenham um papel crucial no campo do aprendizado de máquina e da inteligência artificial. Essas redes são compostas por múltiplas camadas de neurônios, organizadas em uma estrutura de camadas sucessivas, onde cada camada é conectada à próxima. Vamos explorar os conceitos principais que envolvem as redes neurais multicamada de maneira detalhada:

1. Estrutura Básica

Neurônios: Unidades básicas de computação que simulam os neurônios biológicos. Cada neurônio recebe entradas, as processa com base em uma função de ativação e produz uma saída.

Camadas: As MLPs são compostas por três tipos principais de camadas:

  • Camada de Entrada: Recebe os dados de entrada para a rede.
  • Camadas Ocultas: Camadas intermediárias entre a entrada e a saída, onde ocorre a maior parte do processamento através de pesos e funções de ativação. Uma MLP pode ter uma ou mais camadas ocultas.
  • Camada de Saída: Produz a saída final da rede, que é a previsão ou classificação realizada pelo modelo.

2. Funcionamento

  • Propagação para frente (Forward Propagation): O processo começa na camada de entrada, passa pelas camadas ocultas e termina na camada de saída. Em cada neurônio, a soma ponderada das entradas é calculada e passada por uma função de ativação para gerar a saída do neurônio.
  • Funções de Ativação: São cruciais para introduzir não-linearidade no modelo, permitindo que a rede aprenda relações complexas. Exemplos incluem a função sigmóide, ReLU (Unidade Linear Retificada) e tanh (Tangente Hiperbólica).

3. Aprendizado

  • Retropropagação (Backpropagation): Após a propagação para frente, o erro é calculado na saída. Esse erro é então propagado de volta pela rede, atualizando os pesos dos neurônios de forma a minimizar o erro. Este processo é facilitado por algoritmos de otimização, como o gradiente descendente.
  • Atualização dos Pesos: Os pesos são ajustados de acordo com o erro, com o objetivo de reduzir a diferença entre a saída prevista e a real. O tamanho do passo de atualização é determinado pela taxa de aprendizado.

4. Aplicações

As MLPs são versáteis e podem ser aplicadas em uma ampla gama de tarefas, incluindo:

  • Classificação e regressão
  • Reconhecimento de padrões e imagens
  • Previsão de séries temporais
  • Sistemas de recomendação

5. Desafios e Considerações

  • Overfitting: O modelo pode se tornar muito complexo, memorizando os dados de treinamento em vez de aprender as relações gerais. Técnicas como regularização e dropout são usadas para combater o overfitting.
  • Escolha da Arquitetura: Determinar o número de camadas ocultas e o número de neurônios em cada camada é crucial e pode depender muito do problema específico e dos dados disponíveis.

Função de Ativação:

Para as camadas oculta e de saída, você pode usar a função sigmóide, que é comum em redes neurais para classificação binária. A função sigmóide é definida como \(\sigma(x) = \frac{1}{1 + e^{-x}}\), onde \(e\) é a base do logaritmo natural.

Valores entre 0 e 1 no eixo \(y\)

Se x é um valor alto, ele fica próximo a 1.

Se y é um valor baixo, ele fica próximo a 0.

Não retorna valores negativos

Outras Funções: clique aqui

Operador XOR:

Para implementar uma rede neural que possa resolver o problema XOR, você precisará de uma arquitetura com pelo menos uma camada oculta, além da camada de entrada e saída. Isso é necessário porque o XOR é um problema não linearmente separável, o que significa que não é possível dividir as classes (0 e 1) com uma única linha reta no espaço de entrada. Uma rede com uma camada oculta pode criar fronteiras de decisão não lineares, permitindo resolver problemas como o XOR.

 

  • Entradas: [[0,0], [0,1], [1,0], [1,1]]
  • Saídas esperadas: [[0], [1], [1], [0]]
  • Pesos da camada de entrada para a camada oculta (pesos0): np.array([[-0.424, -0.740, -0.961],[0.358, -0.577, -0.469]])
  • Pesos da camada oculta para a camada de saída (pesos1): np.array([[-0.017], [-0.893], [0.148]])

Propagação para Frente (Forward Propagation)

A. Camada de Entrada para Camada Oculta

  1. Cálculo da Soma Ponderada na Camada Oculta: Para cada par de entrada, calculamos a soma ponderada das entradas utilizando pesos0. Isso é feito multiplicando as entradas pelos pesos correspondentes e somando os resultados para cada neurônio na camada oculta.

  2. Aplicação da Função de Ativação Sigmóide: A soma ponderada é passada pela função de ativação sigmóide para cada neurônio na camada oculta. A função sigmóide é dada por \(f(x) = \frac{1}{1 + e^{-x}}\), que mapeia qualquer valor de entrada para um valor entre 0 e 1.

B. Camada Oculta para Camada de Saída

  1. Cálculo da Soma Ponderada: As saídas da camada oculta, agora transformadas pela função sigmóide, são multiplicadas pelos pesos1 para calcular a soma ponderada na camada de saída.
  2. Aplicação da Função de Ativação Sigmóide: A soma ponderada na camada de saída é novamente passada pela função de ativação sigmóide para obter a saída final da rede para cada entrada.

C. Calculo do Erro

  • Erro: O erro é calculado como a diferença entre a saída esperada e a saída produzida pela rede. 

D. Retropropagação (Backpropagation)

  • Durante a retropropagação, os gradientes do erro são calculados em relação a cada peso na rede. Isso envolve a aplicação da regra da cadeia para derivar o gradiente do erro em relação aos pesos entre a camada oculta e a camada de saída (pesos1) e, em seguida, os pesos entre a camada de entrada e a camada oculta (pesos0).
  • Função Sigmoide: \(f(x) = \frac{1}{1 + e^{-x}}\)
  • Derivada Parcial da Função Sigmoide: \(D = y \times (1-y)\)

Cálculo do Delta de Saída para calcular o novo peso de peso1:

Cálculo do Delta Escondido para calcular o novo peso de peso0:

Atualização do Peso1:

peso(n+1) = [peso(n)*Momento]+[entrada*delta* Taxa de Aprendizagem]

Taxa de Aprendizagem (learning rate)

  • Define quão rápido o algoritmo vai aprender
  • Alto: A convergência é rápida mas pode perder o mínimo global
  • Baixo: Será mais lento mas tem mais chances de chegar no mínimo global

Momento (momentum)

  • Escapar de mínimos locais
  • Define o quão confiável é a última alteração
  • Alto: aumenta a velocidade da convergência
  • Baixo: pode evitar mínimos globais global

Atualização do Peso0:

Exemplo:

Explicação

  • Inicialização: Os pesos são inicializados aleatoriamente.
  • Forward Propagation: Calcula a saída da rede neural passando as entradas através das camadas.
  • Cálculo do Erro: A diferença entre a saída esperada e a obtida.
  • Backpropagation: Ajusta os pesos com base no gradiente do erro.
  • Atualização dos Pesos: Os pesos são ajustados em direção à minimização do erro.

Esse exemplo é uma introdução básica à implementação de uma rede neural para o problema XOR. Na prática, frameworks de aprendizado profundo como TensorFlow ou PyTorch fornecem ferramentas muito mais poderosas e flexíveis para construir e treinar redes neurais complexas.

import numpy as np

def sigmoid(soma):
    return 1 / (1 + np.exp(-soma))

def sigmoidDerivada(sig):
    return sig * (1 - sig)

entradas = np.array([[0,0],[0,1],[1,0],[1,1]])
saidas = np.array([[0],[1],[1],[0]])

pesos0 = 2*np.random.random((2,3)) - 1
pesos1 = 2*np.random.random((3,1)) - 1

# Mostrar os pesos
print("\nPesos da Camada de Entrada:")
print(pesos0)
print("\nPesos da Camada Oculta:")
print(pesos1)

epocas = 10000
taxaAprendizagem = 0.3
momento = 1

for j in range(epocas):
    camadaEntrada = entradas
    somaSinapse0 = np.dot(camadaEntrada, pesos0)
    camadaOculta = sigmoid(somaSinapse0)

    somaSinapse1 = np.dot(camadaOculta, pesos1)
    camadaSaida = sigmoid(somaSinapse1)

    erroCamadaSaida = saidas - camadaSaida
    mediaAbsoluta = np.mean(np.abs(erroCamadaSaida))
    #print(str(j) + " -> Erro: " + str(mediaAbsoluta))

    derivadaSaida = sigmoidDerivada(camadaSaida)
    deltaSaida = erroCamadaSaida * derivadaSaida

    pesos1Transposta = pesos1.T
    deltaSaidaXPeso = deltaSaida.dot(pesos1Transposta)
    deltaCamadaOculta = deltaSaidaXPeso * sigmoidDerivada(camadaOculta)

    camadaOcultaTransposta = camadaOculta.T
    pesosNovo1 = camadaOcultaTransposta.dot(deltaSaida)
    pesos1 = (pesos1 * momento) + (pesosNovo1 * taxaAprendizagem)

    camadaEntradaTransposta = camadaEntrada.T
    pesosNovo0 = camadaEntradaTransposta.dot(deltaCamadaOculta)
    pesos0 = (pesos0 * momento) + (pesosNovo0 * taxaAprendizagem)


# Erro final
print("\nErro Final:")
print(str(j) + " -> Erro: " + str(mediaAbsoluta))

# Mostrar os pesos
print("\nPesos da Camada de Entrada:")
print(pesos0)
print("\nPesos da Camada Oculta:")
print(pesos1)

# Teste da rede após treinamento
print("\nSaídas após o treinamento:")
for x in entradas:
    camada_oculta = sigmoid(np.dot(x, pesos0))
    camada_saida = sigmoid(np.dot(camada_oculta, pesos1))
    print(x, "->", camada_saida)