Perceptron
O perceptron foi inventado em 1943 por Warren McCulloch e Walter Pitts. A primeira implementação de hardware foi a máquina Mark I Perceptron construída em 1957 por Frank Rosenblatt no Cornell Aeronautical Laboratory.[1][2] Ele pode ser visto como o tipo mais simples de rede neural feedforward: um classificador linear.[1][2]
Definição[editar | editar código-fonte]
O perceptron é um classificador binário que mapeia sua entrada (um vetor de valor real) para um valor de saída (uma valor binário simples) através da matriz.
Onde é um vetor de peso real e é o produto escalar (que computa uma soma com pesos) e é o viés (do inglês "bias"), um termo constante que não depende de qualquer valor de entrada.
Implementação em Python[editar | editar código-fonte]
''' Este projeto esta disponivel no GiHub de Marcos castro de Sousa Implementação da rede neural Perceptron w = w + N * (d(k) - y) * x(k) ''' import random, copy class Perceptron: def __init__(self, amostras, saidas, taxa_aprendizado=0.1, epocas=1000, limiar=-1): self.amostras = amostras # todas as amostras self.saidas = saidas # saídas respectivas de cada amostra self.taxa_aprendizado = taxa_aprendizado # taxa de aprendizado (entre 0 e 1) self.epocas = epocas # número de épocas self.limiar = limiar # limiar self.num_amostras = len(amostras) # quantidade de amostras self.num_amostra = len(amostras[0]) # quantidade de elementos por amostra self.pesos = [] # vetor de pesos # função para treinar a rede def treinar(self): # adiciona -1 para cada uma das amostras for amostra in self.amostras: amostra.insert(0, -1) # inicia o vetor de pesos com valores aleatórios for i in range(self.num_amostra): self.pesos.append(random.random()) # insere o limiar no vetor de pesos self.pesos.insert(0, self.limiar) # inicia o contador de epocas num_epocas = 0 while True: erro = False # o erro inicialmente inexiste # para todas as amostras de treinamento for i in range(self.num_amostras): u = 0 ''' realiza o somatório, o limite (self.amostra + 1) é porque foi inserido o -1 para cada amostra ''' for j in range(self.num_amostra + 1): u += self.pesos[j] * self.amostras[i][j] # obtém a saída da rede utilizando a função de ativação y = self.sinal(u) # verifica se a saída da rede é diferente da saída desejada if y != self.saidas[i]: # calcula o erro: subtração entre a saída desejada e a saída da rede erro_aux = self.saidas[i] - y # faz o ajuste dos pesos para cada elemento da amostra for j in range(self.num_amostra + 1): self.pesos[j] = self.pesos[j] + self.taxa_aprendizado * erro_aux * self.amostras[i][j] erro = True # ainda existe erro # incrementa o número de épocas num_epocas += 1 # critério de parada é pelo número de épocas ou se não existir erro if num_epocas > self.epocas or not erro: break # função utilizada para testar a rede # recebe uma amostra a ser classificada e os nomes das classes # utiliza a função sinal, se é -1 então é classe1, senão é classe2 def testar(self, amostra, classe1, classe2): # insere o -1 amostra.insert(0, -1) # utiliza o vetor de pesos que foi ajustado na fase de treinamento u = 0 for i in range(self.num_amostra + 1): u += self.pesos[i] * amostra[i] # calcula a saída da rede y = self.sinal(u) # verifica a qual classe pertence if y == -1: print('A amostra pertence a classe %s' % classe1) else: print('A amostra pertence a classe %s' % classe2) # função de ativação: degrau bipolar (sinal) def sinal(self, u): return 1 if u >= 0 else -1 print('\nA ou B?\n') # amostras: um total de 4 amostras amostras = [[0.1, 0.4, 0.7], [0.3, 0.7, 0.2], [0.6, 0.9, 0.8], [0.5, 0.7, 0.1]] # saídas desejadas de cada amostra saidas = [1, -1, -1, 1] # conjunto de amostras de testes testes = copy.deepcopy(amostras) # cria uma rede Perceptron rede = Perceptron(amostras=amostras, saidas=saidas, taxa_aprendizado=0.1, epocas=1000) # treina a rede rede.treinar() # testando a rede for teste in testes: rede.testar(teste, 'A', 'B')
Implementação em C#[editar | editar código-fonte]
using System; using System.Linq; namespace perceptron { public class Perceptron { static readonly double[] w = new double[3]; private readonly int[,] _matrizAprendizado = new int[4, 3]; private double _net; Perceptron() { //tabela AND _matrizAprendizado[0, 0] = 0; _matrizAprendizado[0, 1] = 0; _matrizAprendizado[0, 2] = 0; _matrizAprendizado[1, 0] = 0; _matrizAprendizado[1, 1] = 1; _matrizAprendizado[1, 2] = 0; _matrizAprendizado[2, 0] = 1; _matrizAprendizado[2, 1] = 0; _matrizAprendizado[2, 2] = 0; _matrizAprendizado[3, 0] = 1; _matrizAprendizado[3, 1] = 1; _matrizAprendizado[3, 2] = 1; w[0] = 0; w[1] = 0; w[2] = 0; } public static void Main(string[] args) { //pesos antes do treinamento w.ToList().ForEach(x => Console.WriteLine(x + ",")); Console.WriteLine("\n"); //efetua-se o treinamento da rede new Perceptron().Treinar(); Console.WriteLine("\n"); //pesos ajustados após treinamento w.ToList().ForEach(x => Console.WriteLine(x + ",")); //dados de entrada para rede treinada, 0 e 0 resulta em 0 (tabela and) -1 corresponde ao BIAS int[] amostra1 = { 0, 1, -1 }; // 0 e 1 -> 0 Classe B int[] amostra2 = { 1, 0, -1 }; // 1 e 0 -> 0 Classe B int[] amostra3 = { 0, 0, -1 }; // 0 e 0 -> 0 Classe B int[] amostra4 = { 1, 1, -1 }; // 1 e 1 -> 1 Classe A ClassificarAmostra(amostra1); ClassificarAmostra(amostra2); ClassificarAmostra(amostra3); ClassificarAmostra(amostra4); Console.ReadKey(); } public static void ClassificarAmostra(int[] amostra) { //pesos encontrados após o treinamento int[] pesos = { 2, 1, 3 }; //aplicação da separação dos dados linearmente após aprendizado var u = amostra.Select((t, k) => pesos[k] * t).Sum(); var y = LimiarAtivacao(u); Console.WriteLine(y > 0 ? "Amostra da classe A >= 0" : "HelloWorld < 0"); } private static int LimiarAtivacao(double u) { return (u >= 0) ? 1 : 0; } int Executar(int x1, int x2) { _net = (x1 * w[0]) + (x2 * w[1]) + ((-1) * w[2]); return (_net >= 0) ? 1 : 0; } public void Treinar() { var treinou = true; for (var i = 0; i < _matrizAprendizado.GetLength(0); i++) { var saida = Executar(_matrizAprendizado[i, 0], _matrizAprendizado[i, 1]); if (saida != _matrizAprendizado[i, 2]) { CorrigirPeso(i, saida); treinou = false; } } if (!treinou) Treinar(); } void CorrigirPeso(int i, int saida) { w[0] = w[0] + (1 * (_matrizAprendizado[i, 2] - saida) * _matrizAprendizado[i, 0]); w[1] = w[1] + (1 * (_matrizAprendizado[i, 2] - saida) * _matrizAprendizado[i, 1]); w[2] = w[2] + (1 * (_matrizAprendizado[i, 2] - saida) * (-1)); } } }
Implementação em Ruby[editar | editar código-fonte]
# # Classe PERCEPTRON responsável para aprendizado e resolução da tabela AND # class Perceptron def initialize # pesos sinápticos [0] entrada 1, [1] entrada 2, [3]BIAS @w = [] # variável responsável pelo somatório(rede). @net = 0 # variavél responsável pelo número máximo de épocas @epocasMax = 30 # variável responsável pela contagem das épocas durante o treinamento @count = 0 # declara o vetor da matriz de aprendizado @matriz_aprendizado = [] self.inicia_matriz end def inicia_matriz # Primeiro valor @matriz_aprendizado[0] = [] @matriz_aprendizado[0][0] = 0; # entrada 1 @matriz_aprendizado[0][1] = 0; # entrada 2 @matriz_aprendizado[0][2] = 0; # valor esperado # Segundo Valor @matriz_aprendizado[1] = [] @matriz_aprendizado[1][0] = 0; # entrada 1 @matriz_aprendizado[1][1] = 1; # entrada 2 @matriz_aprendizado[1][2] = 0; # valor esperado # terceiro valor @matriz_aprendizado[2] = [] @matriz_aprendizado[2][0] = 1; # entrada 1 @matriz_aprendizado[2][1] = 0; # entrada 2 @matriz_aprendizado[2][2] = 0; # valor esperado # quarto valor @matriz_aprendizado[3] = [] @matriz_aprendizado[3][0] = 1; # entrada 1 @matriz_aprendizado[3][1] = 1; # entrada 2 @matriz_aprendizado[3][2] = 1; # valor esperado # inicialização dos pesos sinápticos # Peso sináptico para primeira entrada. @w[0] = 0; # Peso sináptico para segunda entrada. @w[1] = 0; # Peso sináptico para o BIAS @w[2] = 0; end # Método responsávelpelo somatório e a função de ativação. def executar(x1, x2) # Somatório (NET) @net = (x1 * @w[0]) + (x2 * @w[1]) + ((-1) * @w[2]); # Função de Ativação return 1 if (@net >= 0) return 0; end # Método para treinamento da rede def treinar() # variavel utilizada responsável pelo controlede treinamento recebefalso treinou = true; # varável responsável para receber o valor da saída (y) saida = nil; # laço usado para fazer todas as entradas @matriz_aprendizado.length.times do |i| # A saída recebe o resultado da rede que no caso é 1 ou 0 saida = self.executar(@matriz_aprendizado[i][0], @matriz_aprendizado[i][1]); if (saida != @matriz_aprendizado[i][2]) # Caso a saída seja diferente do valor esperado # os pesos sinápticos serão corrigidos self.corrigirPeso(i, saida); # a variavél responsável pelo controlede treinamento recebe falso treinou = false; end end # acrescenta uma época @count+=1; # teste se houve algum erro duranteo treinamento e o número de epocas #é menor qe o definido if(not treinou and (@count < @epocasMax)) # chamada recursiva do método self.treinar(); end end # fim do método para treinamento # Método para a correção de pesos def corrigirPeso(i, saida) @w[0] = @w[0] + (1 * (@matriz_aprendizado[i][2] - saida) * @matriz_aprendizado[i][0]); @w[1] = @w[1] + (1 * (@matriz_aprendizado[i][2] - saida) * @matriz_aprendizado[i][1]); @w[2] = @w[2] + (1 * (@matriz_aprendizado[i][2] - saida) * (-1)); end end
Implementação em Java[editar | editar código-fonte]
/* * Classe PERCEPTRON responsável para aprendizado e resolução da tabela AND */ public class Perceptron { // pesos sinápticos [0] entrada 1, [1] entrada 2, [3]BIAS private double[] w = new double[3]; // variável responsável pelo somatório(rede). private double NET = 0; // variavél responsável pelo número máximo de épocas private final int epocasMax = 30; // variável responsável pela contagem das épocas durante o treinamento private int count = 0; // declara o vetor da matriz de aprendizado private int[][] matrizAprendizado = new int[4][3]; // MÉTODO DE RETORNO DO CONTADOR public int getCount(){ return this.count; } // metodo de inicialização inicia o vetor da matriz de aprendizado Perceptron() { // Primeiro valor this.matrizAprendizado[0][0] = 0; // entrada 1 this.matrizAprendizado[0][1] = 0; // entrada 2 this.matrizAprendizado[0][2] = 0; // valor esperado // Segundo Valor this.matrizAprendizado[1][0] = 0; // entrada 1 this.matrizAprendizado[1][1] = 1; // entrada 2 this.matrizAprendizado[1][2] = 0; // valor esperado // terceiro valor this.matrizAprendizado[2][0] = 1; // entrada 1 this.matrizAprendizado[2][1] = 0; // entrada 2 this.matrizAprendizado[2][2] = 0; // valor esperado // quarto valor this.matrizAprendizado[3][0] = 1; // entrada 1 this.matrizAprendizado[3][1] = 1; // entrada 2 this.matrizAprendizado[3][2] = 1; // valor esperado // inicialização dos pesos sinápticos // Peso sináptico para primeira entrada. w[0] = 0; // Peso sináptico para segunda entrada. w[1] = 0; // Peso sináptico para o BIAS w[2]= 0; } // Método responsávelpelo somatório e a função de ativação. int executar(int x1, int x2) { // Somatório (NET) NET = (x1 * w[0]) + (x2 * w[1]) + ((-1) * w[2]); // Função de Ativação if (NET >= 0) { return 1; } return 0; } // Método para treinamento da rede public void treinar() { // variavel utilizada responsável pelo controlede treinamento recebefalso boolean treinou= true; // varável responsável para receber o valor da saída (y) int saida; // laço usado para fazer todas as entradas for (int i = 0; i < matrizAprendizado.length; i++) { // A saída recebe o resultado da rede que no caso é 1 ou 0 saida = executar(matrizAprendizado[i][0], matrizAprendizado[i][1]); if (saida != matrizAprendizado[i][2]) { // Caso a saída seja diferente do valor esperado // os pesos sinápticos serão corrigidos corrigirPeso(i, saida); // a variavél responsável pelo controlede treinamento recebe falso treinou = false; } } // acrescenta uma época this.count++; // teste se houve algum erro duranteo treinamento e o número de epocas //é menor qe o definido if((treinou == false) && (this.count < this.epocasMax)) { // chamada recursiva do método treinar(); } } // fim do método para treinamento // Método para a correção de pesos void corrigirPeso(int i, int saida) { w[0] = w[0] + (1 * (matrizAprendizado[i][2] - saida) * matrizAprendizado[i][0]); w[1] = w[1] + (1 * (matrizAprendizado[i][2] - saida) * matrizAprendizado[i][1]); w[2] = w[2] + (1 * (matrizAprendizado[i][2] - saida) * (-1)); }}
Referências
- ↑ a b Freund, Yoav; Schapire, Robert E. (1999). «Large Margin Classification: Using the Perceptron Algorithm» (PDF). UC San Diego. 37 (3): 2. Consultado em 23 de junho de 2020
- ↑ a b Lefkowitz, Melanie (25 de setembro de 2019). «Professor's perceptron paved the way for AI – 60 years too soon». Cornell Chronicle (em inglês). Consultado em 23 de junho de 2020