Coletânea Python do ZERO às Redes Neurais Artificiais
Perceptron de Uma Camada – Tabela AND
Entendidos os conceitos lógicos de o que é um perceptron, como os mesmos são um modelo para geração de processamento de dados para por fim ser o parâmetro de uma função de ativação. Hora de, também de forma prática, entender de fato o que é aprendizagem de máquina.
Novamente se tratando de redes neurais serem uma abstração as redes
neurais biológicas, o mecanismo de aprendizado que faremos também é baseado em tal modelo. Uma das características de nosso sistema nervoso central é a de aprender criando padrões de conexões neurais para ativação de alguma função ou processamento de funções já realizadas (memória). Também é válido dizer que temos a habilidade de aprender e melhorar nosso desempenho com base em repetição de um determinado tipo de ação.
Da mesma forma, criaremos modelos computacionais de estruturas
neurais onde, programaremos determinadas funções de forma manual (fase chamada supervisionada) e posteriormente iremos treinar tal rede para que identifique o que é certo, errado, e memorize este processo. Em suma, a aprendizagem de máquina ocorre quando uma rede neural atinge uma solução generalizada para uma classe de problemas.
Partindo para prática, vamos criar do zero uma rede neural que aprenderá o que é o mecanismo lógico de uma tabela AND. Operador lógico muito usado em problemas que envolvem simples tomada de decisão. Inicialmente, como é de se esperar, iremos criar uma estrutura lógica que irá processar os valores mas de forma errada, sendo assim, na chamada fase supervisionada, iremos treinar nossa rede para que ela aprenda o padrão referencial correto e de fato solucione o que é o processamento das entradas e as respectivas saídas de uma tabela AND.
Tabela AND:
X1 X2
0 0 0 0 1 0 1 0 0 1 1 1
Todo mundo em algum momento de seu curso de computação teve de estudar operadores lógicos e o mais básico deles é o operador AND. Basicamente ele faz a relação entre duas proposições e apenas retorna VERDADEIRO caso os dois valores de entrada forem verdadeiros. A tabela acima nada mais é do que a tabela AND em operadores aritméticos, mas o mesmo conceito vale para True e False. Sendo 1 / True e 0 / False, apenas teremos como retorno 1 caso os dois operandos forem 1, assim como só será True se as duas proposições forem True.
Dessa forma, temos 4 operadores a primeira coluna, mais 4 operadores na segunda camada e na última coluna os resultados das relações entre os mesmos. Logo, temos o suficiente para criar um modelo de rede neural que aprenderá essa lógica.
Basicamente o que faremos é a criação de 4 perceptrons onde cada um terá 2 neurônios de entrada, pesos que iremos atribuir manualmente, uma função soma e uma função de ativação.
Montada a estrutura visual dos perceptrons, por hora, apenas para fins didáticos, hora de realizar as devidas operações de multiplicação entre os valores de entrada e seus respectivos pesos. Novamente, vale salientar que neste caso em particular, para fins de aprendizado, estamos iniciando essas operações com pesos zerados e definidos manualmente, esta condição não se repetirá em outros exemplos futuros.
Vamos ao modelo, criamos 4 estruturas onde X1 representa a primeira linha de nossa tabela AND (0 e 0), X2 que representa a segunda linha (0 e 1), X3 que representa a terceira linha (1 e 0) e por fim X4 que representa a quarta linha do operador (1 e 1).
Executando a função de soma dos mesmos, repare que para X1 a multiplicação das entradas pelos respectivos pesos e a soma destes resultou no valor 0, que era esperado nesse caso (Tabela AND - 0 e 0 = 0). O processo se repete exatamente igual aos perceptrons X2 e X3 onde a função soma resulta em 0. Porém, repare em X4, a multiplicação das entradas pelos respectivos pesos como manda a regra resultou em 0, mas sabemos que de acordo com a tabela verdade este resultado deveria ser 1. Isso se deu porque simplesmente os pesos 0 implicaram em uma multiplicação falha, qualquer valor multiplicado por 0 resulta em 0.
Precisamos então treinar nossa rede para que ao final do processo se chegue ao valor esperado (X4 = 1). Em redes neurais básicas como estas, o que faremos é o simples reajuste dos valores dos pesos e a repetição do processo para ver se o mesmo é corrigido.
Revisando apenas o perceptron X4, alterando os valores dos pesos de 0 para 0.5 e repetindo a função de soma, agora sim temos o valor esperado 1. (Tabela AND 1 e 1 = 1.
Uma vez encontrado o valor de pesos que solucionou o problema a nível de X4, hora de aplicar este mesmo valor de pesos a toda rede neural e verificar se este valor funciona para tudo.
Aplicando o valor de peso 0.5 a todos neurônios e refeitos os devidos cálculos dentro de nossa função soma, finalmente temos os valores corrigidos. Agora seguindo a lógica explicada anteriormente, como os valores de X2 e X3 são menores que 1, na step function os mesmos serão reduzidos a 0, sendo apenas X4 o perceptron ativado nessa rede. o que era esperado de acordo com a tabela AND.
Em X1 0 e 0 = 0, em X2 0 e 1 = 0, em X3 1 e 0 = 0 e em X4 1 e 1 = 1.
Partindo para o Código:
Por parte do código, todo processo sempre se inicia com a importação das bibliotecas e módulos necessários para que possamos fazer o uso deles. Nesse caso todas as operações que faremos serão operações nativas do Python ou funções internas da biblioteca Numpy. Para sua importação basta executar o comando import numpy, por convenção também as referenciaremos como np por meio do comando as, dessa forma, sempre que quisermos “chamar” alguma função sua basta usar np.
Em seguida criamos três variáveis que ficarão responsáveis por guardar os valores das entradas, saídas e dos pesos. Para isso declaramos uma nova variável de nome entradas que recebe como atributo uma array numpy através do comando np.array que por sua vez recebe como parâmetros os valores de entrada. Repare na sintaxe e também que para cada item dessa lista estão contidos os valores X1 e X2, ou seja, cada linha de nossa tabela AND. Da mesma forma criamos uma variável saidas que recebe uma array numpy com uma lista dos valores de saída. Por fim criamos uma variável pesos que recebe uma array numpy de valores iniciais zerados. Uma vez que aqui neste exemplo estamos explorando o que de fato é o aprendizado de máquina, usaremos pesos inicialmente zerados que serão corrigidos posteriormente.
Logo após criamos uma variável de nome taxaAprendizado que como próprio nome sugere, será a taxa com que os valores serão atualizados e testados novamente nesse perceptron. Você pode testar valores diferentes, menores, e posteriormente acompanhar o processo de aprendizagem.
Na sequência criamos a nossa função Soma, que tem como parâmetro as variáveis temporárias e e p, internamente ela simplesmente tem o comando de retornar o produto escalar de e sobre p, em outras palavras, a soma das multiplicações dos valores atribuídos as entradas com seus respectivos pesos.
Criamos uma variável de nome s que como atributo recebe a função Soma que agora finalmente possui como parâmetro as variáveis entradas e pesos. Dessa forma, podemos instanciar a variável s em qualquer bloco de código que ela imediatamente executará a soma das entradas pelos pesos, guardando seus dados internamente.
Seguindo com o código, criamos nossa stepFunction, em tradução livre, nossa função degrau, aquela que pegará os valores processados para consequentemente determinar a ativação deste perceptron ou não em meio a rede. Para isso definimos uma função stepFunction que tem como parâmetro a variável temporária soma. Internamente criamos uma estrutura condicional básica onde, se soma (o valor atribuído a ela) for igual ou maior que 1, retorna 1 (resultando na ativação), caso contrário retornará 0 (resultando na não ativação do mesmo).
Assim como as outras funções, é preciso criar também uma função que basicamente pegará o valor processado e atribuído como saída e fará a comparação com o valor que já sabemos de saída da tabela AND, para que assim se possa identificar onde está o erro, subsequentemente realizar as devidas modificações para se se aprenda qual os valores corretos de saída. Simplesmente criamos uma função, agora de nome calculoSaida que tem como parâmetro a variável temporária reg, internamente ela tem uma variável temporária s que recebe como atributo o produto escalar de reg sobre pesos, retornando esse valor processado pela função degrau.
Muita atenção a este bloco de código, aqui será realizado o processo de aprendizado de máquina propriamente dito.
Inicialmente criamos uma função de nome aprendeAtualiza, sem parâmetros mesmo. Internamente inicialmente criamos uma variável de nome erroTotal, com valor 1 atribuído simbolizando que existe um erro a ser corrigido.
Em seguida criamos um laço de repetição que inicialmente tem como parâmetro a condição de que se erroTotal for diferente de 0 será executado o bloco de código abaixo. Dentro desse laço erroTotal tem seu valor zerado, na sequência uma variável i percorre todos valores de saidas, assim como uma nova variável calcSaida recebe os valores de entrada em forma de array numpy, como atributo de calculoSaida. É criada também uma variável de nome erro que de forma absoluta (sem considerar o sinal negativo) recebe como valor os dados de saidas menos os de calcSaida, em outras palavras, estamos fazendo nesta linha de código a diferença entre os valores de saída reais que já conhecemos na tabela AND e os que foram encontrados inicialmente pelo perpeptron. Para finalizar esse bloco de código erroTotal soma a si próprio o valor encontrado e atribuído a erro.
Na sequência é criado um novo laço de repetição onde uma variável j irá percorrer todos valores de pesos, em sequida pesos recebe como atributo seus próprios valores multiplicados pelos de taxaAprendizado (definido manualmente anteriormente como 0.5), multiplicado pelos valores de entradas e de erro. Para finalizar ele recebe dois comandos print( ) para que no console/terminal sejam exibidos os valores dos pesos atualizados assim como o total de erros encontrados.
Por fim simplesmente executando a função aprendeAtualiza( ) podemos acompanhar via console/terminal o processo de atualização dos pesos (aprendizado de máquina) e o log de quando não existem mais erros, indicando que tal perceptron finalmente foi treinado e aprendeu de fato a interpretar uma tabela AND, ou em outras palavras, reconhecer o padrão correto de suas entradas e saídas.
Código Completo:
Usando Spyder:
Explorador de Variáveis:
Console: