Perceptron
algoritmo para aprendizagem supervisionada de classificadores binários
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
editarO 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'''
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
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 #
# 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
/*
* 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