Redes de Hopfield

On 24 de setembro de 2017 by Mateus Coelho

Redes de Hopfield, juntamente com as Máquinas de Boltzmann, são fundamentos de diversas arquiteturas e algoritmos modernos de redes neurais. Redes de Hopfield são Redes Neurais Artificiais que possuem arquitetura muito simples. Elas usam diversos recursos também utilizados por Redes Neurais Feedforward. Essas ferramentas são comumente utilizadas em problemas de visão computacional.

Introdução

Quando se discutem os paradigmas para a implementação de Inteligência Artificial e Reconhecimento de Padrões, muitas vezes o caminho utilizado passa pelo uso de Redes Neurais Artificiais. Essa associação é natural, pois as arquiteturas de redes neurais artificiais e as funções de aprendizado de máquina surgem inspiradas em observações do processo de aprendizado natural.

As unidades básicas das redes neurais são chamadas de neurônios. Elas recebem esse nome pois foram modeladas de acordo com a interpretação de cientistas acerca do funcionamento dos neurônios biológicos. A maneira como cada neurônio se relaciona com os valores de entrada e com a rede definirão as diferenças de cada arquitetura.

Contudo, diferentemente das Redes Neurais Biológicas, máquinas só podem “aprender” a realizar tarefas muito específicas. Diferentes cientistas propuseram arquiteturas para resolver as mais diversas classes de problemas apresentados. Essa gama de soluções busca maneiras para relizar tarefas como classificar dados em grupos. Outra aplicações buscam prever tendências, ou mesmo recuperar dados incompletos ou corrompidos.

Nesse contexto, uma das arquteturas de Redes Neurais Artificiais tida como base para algoritmos modernos é chamada de Rede de Hopfield. Ela é comumente utilizada para reconstruir dados degradados, ruidosos ou incompletos após ser treinada para reconhecer um padrão, como exemplificado na figura abaixo (Fonte).

Redes de Hopfield

Em 1982, Hopfield propôs uma arquitetura muito simples de Rede Neural Artificial, com apenas uma camada. Os neurônios que compõem essa camada são ligados a todos os outros. Isso que faz com que essa rede seja chamada de auto-associativa. Esse tipo de rede, após receber treinamento, busca reconhecer os padrões e retornar os próprios padrões. Isso torna esse tipo de aplicação ideal para preencher gaps de padrões incompletos ou corrompidos.

Na Figura abaixo é possível observar uma representação gráfica de uma Redes de Hopfield. Essa arquitetura de rede é facilmente representável por uma matriz n x n, onde n é o número de neurônios da rede. O valor n que também corresponde ao número de elementos de uma instância de entrada. Cada elemento dessa matriz representa o peso associado à ligação do neurônio j com o neurônio i. Nessa matriz, a diagonal principal terá sempre valor zero, pois os neurônios não estão ligados a si mesmos.

Como a saída dos neurônios está ligada à entrada de todos os outros, elas são aqui chamadas de estados. Esses estados seguem a lógica booleana. A saída dos estados é dada por estados verdadeiros (true = 1) ou falsos (false = -1).

Exemplo de implementação

A implementação que será apresentada aqui é baseada no livro Artificial Intelligence for Humans Vol.3 – Deep Learning and Neural Networks, além de exemplos da internet encontrados em:

https://www.alexbod.com/hopfield-neural-network/
http://iitkgp.vlab.co.in/sub=39&brch=125&sim=1163&cnt=2
https://gist.github.com/codecontemplator/7620852 .

Inicialização dos neurônios da Rede de Hopfield

O primeiro passo é criar a estrutura de dados que abriga o neurônio. Ela será a mais simples possível, com a matriz de pesos e o estado de cada neurônio. Além disso é preciso inicializar a rede.

//TIPO DE DADOS:
//ELEMENTOS REDE NEURAL IMPLEMENTADA
typedef struct t_network{
    double wmatrix [NET_SIZE][NET_SIZE];
    int states[NET_SIZE];
} network;

//FUNCAO
//INCIALIZACAO DOS VALORES NA REDE
void initializeNetwork(network * N)
{
    for (int i = 0; i < NET_SIZE; i++)
    {
        for (int j = 0; j < NET_SIZE; j++)
        {
            N->wmatrix[i][j] = 0;
        }
    }
}

A equação abaixo define o valor do estado de um neurônio. Esse tipo de equação pode ser chamada também de função de ativação, e é representada por:

Nessa função, o estado s de um neurônio i é atualizado pela soma estados dos demais neurônios, ponderados pelos pesos w das ligações de cada neurônio j com o neurônio i. O estado será atualizado se essa soma ponderada foi maior que um Threshold, que usualmente vale zero (0). As redes de Hopfield podem ser atualizadas de forma síncrona, onde todos os neurônios são atualizados ao mesmo tempo, ou assíncrona, onde aleatoriamente se seleciona um neurônio para atualizar até que todos sejam atualizados. Geralmente os valores são atualizados até que a rede estabilize a saída.

Estabilidade de uma Rede de Hopfield

Para analisar quão próxima está a rede de estabilizar, é necessário ter uma métrica. A função que é usada para isso é chamada função de energia. O valor dessa função decresce a medida que a função se aproxima de um estado mais estável. A função de energia de uma Rede de Hopfield é dada por:

Na implementação aqui construída, os valores de Threshold são considerados zero para simplificação.

//FUNCAO
//ENERGIA DA REDE
double networkEnergy(network N)
{
    double E = 0;
    for (int i = 0; i < NET_SIZE; i++)
    {
        for (int j = 0; j < NET_SIZE; j++)
        {
           E += N.wmatrix[i][j] * N.states[i] * N.states[j];
        }
    }
    return -0.5 * E;
}

Treinamento de uma Rede de Hopfield

O treinamento de Redes de Hopfield é realizado usando uma lista de padrões “desejados”. Os pesos da rede serão atualizados de acordo com uma função de treinamento para que padrões semelhantes a esses sejam reconhecidos. Essa atualização dará força às conexões de neurônios que reagem aos mesmos estímulos. As Redes de Hopfield trabalham com um conjunto de instâncias de entrada. Cada uma dessas instâncias é um vetor de variáveis booleanas.

Para treinar uma Rede Neural, é preciso definir a função matemática que guiará esse treinamento. Essa função é nomeada função de treinamento. Em Redes de Hopfield, geralmente há 2 tipos de treinamento que são utilizados. O primeiro deles é chamado de Hebbiano, e é um tipo de aprendizado muito popular em diversas arquiteturas de Redes Neurais. O outro tipo de aprendizado muito usado nesse caso é chamado de Storkey.

Aprendizado Hebbiano

O aprendizado Hebbiano é descrito por:

Onde epslon é a entrada do padrão no u-ésimo elemento de entrada, e o número de elementos no set de entrada é n. Essa entrada é um reflexo simples da frequência duas saídas na rede assumem valor +1 ou -1. A força da ligação é aumentada conforme ambos elementos produzem a mesma saída. Para implementar esse treinamento, é interessante dividir ele no efeito de cada instância da entrada na matriz de pesos.

//FUNCAO
//ADICIONA O EFEITO DE UM PADRAO NA REDE (HEBBIANA)
void addPatternH(network * N, int pattern[NET_SIZE], int size)
{
    for (int i = 0; i < NET_SIZE; i++)
    {
        for (int j = 0; j < NET_SIZE; j++)
        {
           if (i != j)
                N->wmatrix[i][j] += ((pattern[i] + pattern[j])/size);
        }
    }
}

Aprendizado de Storkey

Caso o treinamento desejado utilize o modelo proposto por Storkey, é necessário ampliar um pouco as funções de treinamento. O modelo de Storkey é mais complexo que o Hebbiano, contudo ele tem capacidade para reconhecer mais padrões. O primeiro passo ao utilizar esse algoritmo é calcular um valor chamado campo local, dado por:

Na implementação, uma função auxiliar foi criada para calcular os valores dos campos locais.

//FUNCAO AUXILIAR
//CALCULA O CAMPO LOCAL DE STORKEY
double calculateLocalField(network N, int i, int j, int pattern[NET_SIZE])
{
    double sum = 0;
    for (int k = 0; k < NET_SIZE; k++)
    {
        if (k != i) sum += N.wmatrix[i][k] * pattern[k];
    }
    return sum;
}

A atualização dos pesos da rede por aprendizado de Storkey tem três termos. Dois deles baseados em campos locais, e um termo baseado apenas nos padrões. Esse termo de atualização é dado por:

sendo que nesse caso n significa o tamanho da rede, ou o comprimento do padrão. Esse valor de atualização é somado a cada peso w da matriz de pesos. Como dito anteriormente, a implementação da função auxiliar será utilizada para facilitar a implementação do aprendizado descrito na equação acima.

//FUNCAO
//ADICIONA O EFEITO DE UM PADRAO NA REDE (STORKEY)
void addPatternS(network * N, int pattern[NET_SIZE])
{
    for (int i = 0; i < NET_SIZE; i++)
    {
        for (int j = 0; j < NET_SIZE; j++)
        {
           //CALCULA OS TRES TERMOS DO TREINAMENTO
           double t1 = (pattern[i]*pattern[j])/NET_SIZE;
           double t2 = (pattern[i]*calculateLocalField(*N,j,i,pattern))/NET_SIZE;
           double t3 = (pattern[j]*calculateLocalField(*N,i,j,pattern))/NET_SIZE;
           
           N->wmatrix[i][j] += (t1-t2-t3);
        }
    }
}

Existe ainda uma variação das redes de Hopfield, usada para tratar problemas de otimização. Esse tipo de rede é chamada de Rede de Hopfield-Tank. Nela os valores da saída dos neurônios não são mais discretos, mas dentro do intervalo entre zero (0) e um (1). Nesse tipo de rede, a função de ativação do neurônio segue uma função matemática sigmoide. Contudo, tipicamente esse algoritmo tem performance pior do que algoritmos de propósito geral na otimização.

Aplicações Práticas de Redes de Hopfield

Detecção de Limites Tumorais em Tomografias Computadorizadas

Uma parte importante do diagnóstico médico por imagem é a determinação dos limites de uma área de interesse. A figura abaixo apresenta um exemplo da aplicação desse problema. Esse problema pode ser modelado como um problema de otimização, que busca minimizar a função de energia de uma rede baseada em um modelo de contorno ativo.

Uma aplicação importante para esse tipo de problema é a caracterização de tumores cerebrais a partir de imagens geradas em tomografias computadorizadas ou em exames de ressonância magnética. As informações obtidas nesse tipo de exame são importantes para o planejamento do tratamento. Nesse caso, pode-se utilizar uma versão modificada da Rede de Hopfield para tratar o problema.

fonte: http://dlia.ir/Scientific/IEEE/iel1/42/11977/00552055.pdf

Reconhecimento de Objetos

Existem diversas aplicações onde é importante reconhecer padrões em imagens. A maior parte das ferramentas de aprendizado segue um modelo feed-forward. Essas aplicações possuem como principal desvantagem a propagação de quaisquer falhas que aconteçam na camada inicial do modelo, devido à direção do fluxo de dados.

A figura acima exemplifica uma aplicação de reconhecimento de objetos. Uma solução proposta para o problema de redes feed-forward é o uso de diversas Redes de Hopfield interligadas em cascata, para garantir que os valores sejam bidirecionalmente atualizados a cada iteração.

fonte: http://www.dtic.mil/dtic/tr/fulltext/u2/a282417.pdf

Deixe um comentário

O seu endereço de e-mail não será publicado.