Saltar para o conteúdo

Perceptron

Origem: Wikipédia, a enciclopédia livre.

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]

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