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

  1. 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 
  2. 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