Perceptron
Aspeto
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