UNIVERSIDADE FEDERAL DO CEARÁ
CENTRO DE TECNOLOGIA
DEPARTAMENTO DE ENGENHARIA ELÉTRICA
PRISCILA CAVALCANTE HOLANDA
DESENVOLVIMENTO DE UMA REDE NEURAL MULTILAYER PERCEPTRON
EMBARCADA EM FPGA
FORTALEZA
2013
PRISCILA CAVALCANTE HOLANDA
DESENVOLVIMENTO DE UMA REDE NEURAL MULTILAYER PERCEPTRON
EMBARCADA EM FPGA
Monografia apresentada ao Curso de
Engenharia Elétrica do Centro de Tecnologia da
Universidade Federal do Ceará, como requisito
parcial para obtenção do título de Graduada em
Engenharia Elétrica.
Orientador: Prof. Dr. Luiz Henrique Silva
Colado Barreto.
FORTALEZA
2013
Dados Internacionais de Catalogação na Publicação
Universidade Federal do Ceará
Biblioteca de Ciências e Tecnologia
H669d Holanda, Priscila Cavalcante.
Desenvolvimento de uma rede neural multilayer perceptron embarcada em Fpga / Priscila
Cavalcante Holanda. – 2013.
66 f. : enc. ; 30 cm.
Monografia(graduação) – Universidade Federal do Ceará, Centro de Tecnologia, Departamento de
Engenharia Elétrica, Graduação em Engenharia Elétrica, Fortaleza, 2013.
Orientação: Prof. Dr. Luiz Henrique Silva Colado Barreto.
1. Redes Neurais(computação). 2. Inteligência computacional. I. Título.
CDD 621.3
PRISCILA CAVALCANTE HOLANDA
DESENVOLVIMENTO DE UMA REDE NEURAL MULTILAYER PERCEPTRON
EMBARCADA EM FPGA
Monografia apresentada ao Curso de
Engenharia Elétrica do Centro de Tecnologia da
Universidade Federal do Ceará, como requisito
parcial para obtenção do título de Graduada em
Engenharia Elétrica.
Aprovada em: ___/___/______.
BANCA EXAMINADORA
________________________________________
Prof. Dr. Luiz Henrique Silva Colado Barreto (Orientador)
Universidade Federal do Ceará (UFC)
_________________________________________
Prof. Dr. Paulo Peixoto Praça
Universidade Federal do Ceará (UFC)
_________________________________________
Eng. Nícolas de Araújo Moreira
Universidade Federal do Ceará (UFC)
À minha família.
AGRADECIMENTOS
Muitas foram as pessoas, a quem ressalto reconhecimento, que contribuíram direta
ou indiretamente na persecução deste sonho que, por meio deste trabalho, se concretiza. Este é,
sem dúvidas, o resultado do auxílio de todos, conquista que jamais poderia ser alcançada de
maneira diversa. Por isso, gostaria de agradecer, ainda que nesse singelo momento, as
grandiosas e inestimáveis contribuições de cada desses indivíduos, que tenho a honra de chamar
de amigos.
Às referencias maternas de minha vida: Ana Maria Cavalcante Holanda, minha
mãe; Terezinha Cavalcante de Oliveira, para sempre minha vovó materna; Maria Carmen
Monteiro de Holanda, para sempre minha vovó paterna; e às minhas tias, maternas e paternas,
que com seus exemplos me ensinaram a ser sempre uma pessoa melhor.
Às referencias paternas de minha vida: José Aldemir de Holanda Filho, meu pai;
Manoel Cazé de Oliveira (in memoriam), para sempre meu vovô materno; José Aldemir de
Holanda Pinheiro, para sempre meu vovô paterno; ao meu “pai adotivo”, André Jalles
Monteiro; e aos meus tios e demais familiares, pela presença, pelas lições e pelo apoio que
sempre me deram.
Ao meu querido irmão, José Aldemir de Holanda Neto, pelos momentos de alegria,
e até pelas brigas, que me ensinaram a ser mais paciente.
Aos maiores colaboradores para minha formação, o corpo docente do Departamento
de Engenharia Elétrica da UFC, em especial ao Prof. Dr. Demercil de Souza Oliveira Junior,
pelos conselhos e pela dedicação quando coordenador do curso. Às amizades proporcionadas
por este curso, em especial à Jéssica Santos Guimarães, ao Lucas Colares Augusto Gonçalves,
ao Vitor Moreira Xavier, ao Raphael Fernandes Sales Costa, ao Higor Pontes Diniz de Almeida
Sergio, ao Janailson Rodrigues Lima e ao Vitor Bruno de Lima Benigno.
Ao Laboratório de Engenharia de Sistemas de Computação, em especial ao Prof.
Dr. Helano de Sousa Castro e ao Prof. Jarbas Aryel Nunes da Silveira, por me terem dado a
oportunidade de participar da equipe Brazil-IP; aos colegas de trabalho do laboratório, que com
tanta paciência me ensinaram e acolheram.
À CAPES, que através do programa Ciência sem Fronteiras me proporcionou a
realização do sonho de estudar no exterior; aos professores e conselheiros do Stevens Institute
of Technology, em especial ao Prof. Dr. Bryan Ackland, por seus ensinamentos, pelos valiosos
conselhos e pelo incentivo, e à Ilona Castro, por me ter recebido e acolhido de forma tão
cuidadosa. Às amizades proporcionadas por este intercâmbio, em especial à Amira Fátima
Kessler Annahas, ao Henrique Ewbank de Miranda Vieira, ao Pedro Wang de Faria Barros, à
Jaclyn Knöri, ao Carlos Vinicius Andrade Silva e a todos os demais amigos, que me fizeram
sentir parte de uma nova família.
Aos amigos que fiz durante toda a vida, alguns mais próximos que outros, sendo
todos indiscutivelmente importantes, em especial ao Caio César Mota Magalhães, ao Adan
Hillery Vidal da Silva, à Joana Maia Fernandes Barroso, à Halina Teixeira Lima, à Beatriz
Mesquita Gadelha, à Neuza Amirati Uchoa, à Raysa Quaresma Nogueira, ao Ádamo de
Figueiredo Nogueira Mesquita, ao Francisco Cristóvão Mota Lima Junior e à Simone Mota de
Holanda Lima que tanto contribuíram para a minha formação pessoal. À Fernanda Sousa
Vasconcelos pela cuidadosa correção ortográfica deste trabalho, pela atenção, carinho, alegria,
e pelo apoio em cada momento.
Ao Prof. Dr. Luiz Henrique Silva Colado Barreto, por me ter dado a oportunidade
de ser sua orientanda, e aos participantes da Banca Examinadora, Prof. Dr. Paulo Peixoto Praça
e ao mestrando Eng. Nícolas de Araújo Moreira, pelo tempo despendido e pelas valiosas
sugestões.
“Our greatest weakness lies in giving up. The
most certain way to succeed is always to try just
one more time.” (Thomas A. Edison)
RESUMO
Com o intuito de possuir comportamentos que de certa forma se aproximam ao processamento
de um cérebro humano, faz-se necessário que as redes neurais artificiais implementadas
possuam graus de paralelismo, situação a qual não é possível quando estas são desenvolvidas
utilizando-se de softwares nos processadores de uso geral, uma vez que estes possuem
processamento sequencial de instruções. Assim, o presente trabalho tem como objetivo a
implementação de uma rede neural Multilayer Perceptron em hardware configurável baseado
na tecnologia FPGA. Utilizando-se a linguagem VHDL, utilizou-se o paralelismo de neurônio
de modo a obter um maior aproveitamento do poder de processamento concorrente atribuído às
implementações de hardware. Ademais, foi implementado um circuito o mais flexível e
adaptável possível, sendo a função XOR a escolhida para testar o funcionamento do circuito
proposto. Por fim, utilizando uma placa de desenvolvimento Basys2, na qual está contida uma
FPGA Xilinx Spartan-3E-250, comprovou-se que o projeto implementado possui
comportamento condizente com o esperado.
Palavras-chave: Redes neurais. MLP. FPGA. Inteligência Computacional. Hardware
Configurável.
ABSTRACT
The aspiration of obtaining behaviors that somehow approach the processing of a human brain
leads implemented artificial neural networks to the necessity in having degrees of parallelism.
This situation is not possible when those networks are developed using software in general
purpose processors, since its instructions are processed sequentially. Thus, a neural network
Multilayer Perceptron was implemented in configurable hardware, based on the FPGA
technology. Using VHDL, the node parallelism was used in order to further exploit the power
of concurrent processing attributed to hardware implementations. Furthermore, a flexible and
adaptable circuit was implemented, which may be used in different applications, and the XOR
function was chosen to test the operation of the proposed circuit. Finally, using a Basys2
development board, which contains an FPGA Xilinx Spartan-3E-250, it was shown that the
implemented project is consistent with its expected behavior.
Keywords: Neural Networks. MLP. FPGA. Machine Learning. Configurable Hardware.
LISTA DE ILUSTRAÇÕES
Figura 1 – Estrutura básica de um neurônio .......................................................................... 21
Figura 2 – O Perceptron ....................................................................................................... 22
Figura 3 – Função Threshold ................................................................................................ 24
Figura 4 – Representação Geométrica do Hiperplano ........................................................... 25
Figura 5 – Representação geométrica das funções AND e OR .............................................. 25
Figura 6 – Função XOR ....................................................................................................... 26
Figura 7 – Solução do problema da função XOR .................................................................. 28
Figura 8 – Estrutura de uma MLP ........................................................................................ 29
Figura 9 – MAC ................................................................................................................... 33
Figura 10 – Função Sigmoide............................................................................................... 34
Figura 11 – Aproximação Linear da Função Sigmoid ........................................................... 36
Figura 12 – Modelo de uma camada da rede neural implementada ....................................... 38
Figura 13 – Mapeamento do paralelismo de neurônio e registradores de deslocamento ........ 38
Figura 14 – Unidades do código desenvolvido em VHDL .................................................... 40
Figura 15 – Topologia da rede utilizada para resolver o problema XOR ............................... 42
Figura 16 – Procedimento de Teste ...................................................................................... 43
Figura 17 – Diagrama e características da placa Basys2 ....................................................... 47
Figura 18 – Imagem da placa Basys2 ................................................................................... 47
Figura 19 – I/O da placa Basys2 ........................................................................................... 48
LISTA DE TABELAS
Tabela 1 – Tabela Verdade da Função XOR ......................................................................... 26
Tabela 2 – Número de problemas binários linearmente separáveis ....................................... 27
Tabela 3 – Algoritmo Backpropagation para redes feedforward contendo duas camadas de
unidades sigmoide................................................................................................................ 30
Tabela 4 – Requisitos de precisão em MLPs ........................................................................ 32
Tabela 5 – Formas e alcance do paralelismo nas RNAs ........................................................ 37
Tabela 6 – Configurações de Projeto no ISE Design Suite 14.4 ............................................ 44
Tabela 7 – Relatório Avançado da Síntese............................................................................ 44
Tabela 8 – Temporização na Síntese .................................................................................... 45
Tabela 9 – Utilização da FPGA ............................................................................................ 45
Tabela 10 – Resultados de Abdu-Aljabar (2012) .................................................................. 46
LISTA DE ABREVIATURAS E SIGLAS
ASIC
CMOS
CORDIC
e.g.
etc
FPGA
HDL
LUT
MAC
MLP
RNA
VHDL
VHSIC
VLSI
Application Specific Integrated Circuit
Complementary Metal-Oxide-Semiconductor
COordinate Rotation DIgital Computer
exempli gratia (por exemplo)
et cetera (e outras coisas)
Field-Programmable Gate Array
Hardware Description Language
Look-Up Table
Multiplier Accumulator
Perceptron Multi-Camadas (Multilayer Perceptron)
Redes Neurais Artificiais
VHSIC Hardware Description Language
Very High Speed Integrated Circuit
Very-Large-Scale Integration
SUMÁRIO
1 INTRODUÇÃO ................................................................................................................ 15
2 REDES NEURAIS ARTIFICIAIS .................................................................................... 21
2.1 O Perceptron ................................................................................................................. 21
2.2 Representação como Hiperplano .................................................................................... 24
2.3 Multilayer Perceptron .................................................................................................... 28
2.4 Implementação ............................................................................................................... 31
2.4.1 Representação e Precisão ....................................................................................................... 31
2.4.2 Operação MAC ....................................................................................................................... 32
2.4.3 Função de Ativação ................................................................................................................ 33
2.4.4 Paralelismo ............................................................................................................................. 36
2.4.5 A Rede Neural ........................................................................................................................ 39
3 RESULTADOS ................................................................................................................ 42
3.1 Implementação da Função XOR ..................................................................................... 42
3.2 Considerações Finais e Trabalhos Futuros ...................................................................... 48
4 CONCLUSÃO .................................................................................................................. 50
REFERÊNCIAS .................................................................................................................. 51
ANEXO A – my_data_types.vhd ......................................................................................... 53
ANEXO B – SPerceptron.vhd .............................................................................................. 54
ANEXO C – HPerceptron.vhd ............................................................................................. 57
ANEXO D – MLP.vhd ......................................................................................................... 59
ANEXO E – XOR.m ............................................................................................................ 63
ANEXO F – MLP_Tb.vhd ................................................................................................... 65
ANEXO G – FPGA.vhd ....................................................................................................... 67
15
1 INTRODUÇÃO
O desenvolvimento e aperfeiçoamento de técnicas de captação de imagens, sinais
elétricos etc., somado a incontáveis pesquisas e experimentos realizados nos mais diversos
institutos e universidades do planeta, não foram, até o momento, capazes de explicar em sua
totalidade o funcionamento do cérebro humano, o qual continua sendo um mistério em suas
mais diversas características e atividades. Mitchell (1997, p.82) afirma que é necessário apenas
10-1 segundos para que alguém possa reconhecer visualmente sua mãe. Esta habilidade,
desenvolvida por seres humanos desde muito cedo, de reconhecer rostos e sons em tempo
infinitesimal, bem como a capacidade de aprender e tomar decisões altamente complexas em
tempo igualmente diminuto, estão ainda fora do alcance dos sistemas computacionais
desenvolvidos até o momento.
Pesquisas na área de inteligência computacional levaram ao desenvolvimento de
algoritmos, idealizados com o objetivo de realizar atividades semelhantes àquelas praticadas
pelo cérebro humano, tais como reconhecer padrões, palavras faladas, caligrafias, imagens para
diagnósticos médicos etc. Tais algoritmos são formulados com a comum intenção de adquirir
aprendizado, se adaptar, generalizar, fragmentar ou organizar dados.
Em coadunação com este objetivo, então, em 1943, surgiu a primeira concepção
matemática do funcionamento de células nervosas biológicas, a qual segundo Kröse e Smagt
(1996, p.13) foi introduzida por McCulloch e Pitts. Estas células eram apresentadas como
componentes conceituais para circuitos que poderiam executar tarefas computacionais, de
modo que estas conduzissem a um aprendizado aproximado ao biológico.
Um neurônio biológico, conforme explica Dayan e Abbott (2005, p.1.02-1.04)
consiste primeiramente em um corpo celular, ou soma, o qual inclui o núcleo do neurônio. Ao
contrário do que é encontrado em outras partes de um organismo, os neurônios são células
extremamente especializadas, as quais são capazes de gerar sinais elétricos em resposta a
agentes químicos e outros fatores, e disseminá-los através dos axônios, os quais transmitem os
impulsos nervosos para outros neurônios, e dos dendritos, os quais recebem estes sinais de
outras células neuronais através de conexões denominadas sinapses. Ainda conforme explicado
pelos autores, o sinal elétrico relevante para o sistema nervoso é a diferença de potencial elétrico
de aproximadamente -70mV entre o interior de um neurônio e o meio extracelular adjacente,
mantida por bombas de íons localizadas na membrana destas células. Assim, quando uma
corrente de íons flui para dentro de um neurônio, este pode ficar menos negativo ou até positivo,
16
de modo que, ao atingir um limiar é gerado um potencial de ação, também denominado impulso
nervoso.
Em 1958 Frank Rosenblatt propôs o primeiro modelo de neurônio artificial,
consoante aponta Graupe (2007, p.10). Denominado Perceptron, este modelo é, ainda hoje, o
componente básico na maior parte das implementações de RNAs, incluindo-se a deste trabalho,
e será melhor explanado em momento subsequente. Entretanto, diante do exposto sobre células
neuronais biológicas, e sabendo-se que há muito mais detalhes a respeito destas, os quais não
foram comtemplados por não fazerem parte do escopo deste trabalho, ou ainda por não serem
até o momento completamente conhecidos, é imperioso frisar que se deve entender os modelos
de neurônio artificial apenas como uma aproximação simplista, e até grosseira, destas. A
intenção dos modelos não é, portanto, simular o funcionamento do neurônio humano ou animal,
e sim concretizar o desenvolvimento de máquinas úteis, capazes de realizar aprendizado.
Conforme explana Graupe (2007, p.05-08), consideram-se os axônios como fios de
conexão, e os dendritos como as entradas, as quais, de modo semelhante ao funcionamento do
neurônio, possuem prioridades, ou pesos, diferentes para cada conexão. Ademais, as saídas, que
no modelo biológico consistem em séries de impulsos nervosos, agora será considerada como
um valor numérico.
Kröse e Smagt (1996, p.29-30) alegam que, após a famosa publicação feita por
Minsky e Papert em 1969, na qual as deficiências do modelo Perceptron foram demonstradas,
houve um enorme abandono e retirada de recursos da área de estudo. Os autores explicam que
limitações na representação de mapeamentos simples, tendo a impossibilidade de um
Perceptron representar a função XOR como um dos resultados mais desencorajadores,
culminou em um período sem significativas publicações ou inovações.
Os autores, então, elucidam que o interesse na área apenas saiu do momentâneo
arrefecimento quando, no começo dos anos oitenta, novos desenvolvimentos em hardware
aumentaram a capacidade de processamento dos computadores, além de resultados teóricos
importantes terem sido alcançados, sendo exemplo destes a criação das redes de neurônios (e.g.
Perceptron Multi-Camadas, ou MLP) e, o mais significativo destes, o descobrimento da
retropropagação do erro, ou backpropagation error, os quais serão explanados de forma
detalhada em momento ulterior neste trabalho.
Hoje, a maior parte das universidades do mundo possuem grupos de redes neurais,
os quais podem se enquadrar em vários departamentos, como de física, ciências da computação
17
e biologia. Pesquisadores como Josh Tenenbaum1 do Massachusetts Institute of Technology,
por exemplo, publicam anualmente sobre o aprendizado e razão humanas vinculadas a estudos
em inteligência computacional e artificial, criando assim poderosos sistemas e paradigmas
teóricos para o entendimento da cognição humana.
Em face do exposto, entretanto, pode-se questionar a própria escolha da utilização
do neurônio artificial como solução para este tipo de problemática. Para sanar tal
questionamento, aponta-se inicialmente à lúcida analogia ilustrada por Alpaydin (2009, p.234),
na qual é aludida a diferença entre máquinas de voo natural, na forma de um pássaro, e artificial,
sendo este um avião comercial. O primeiro, para realizar um voo, utiliza-se do bater de suas
asas; o segundo, não obstante, utiliza-se de motores a jato; podendo-se afirmar que o pássaro e
o avião são duas implementações construídas para propósitos diferentes, mas que aplicam a
mesma teoria, a da aerodinâmica.
Em comparação com o exprimido, segue que o cérebro é, consoante
supramencionado, uma implementação a qual concretiza atividades de reconhecimento de
padrões e aprendizado. Tal como as tentativas de construção de máquinas de voo que se
assemelhavam aos pássaros, é de esperar que as primeiras tentativas de desenvolver estruturas
possuidoras das habilidades do cérebro sejam também semelhantes a redes com grande número
de unidades de processamento (como a aqui citada MLP), até que seja descoberta uma teoria
computacional da inteligência.
Acompanhando-se a linha de raciocínio do autor, assim como se descobriu que as
penas são irrelevantes ao voo, pode-se com o tempo descobrir que neurônios e sinapses são
também irrelevantes à inteligência. Entretanto, até que este tempo chegue, há ainda uma razão
ainda mais influente pela qual persiste o interesse na extração de características atribuídas ao
cérebro, relacionada com o processamento altamente paralelo realizado por este.
Mitchell (1997, p.82) indica ser estimado que o cérebro humano contém uma rede
densamente interconectada de aproximadamente 1011 neurônios, cada um dos quais, segundo
aponta o autor, conectados em média a outras 104 células nervosas. Desta maneira, a partir do
fato de que o tempo de chaveamento dos mais rápidos neurônios não passa da ordem de l0-3
segundos, o que demonstra ser demasiado longo se comparado aos resultados obtidos com a
atual tecnologia de circuitos integrados, é possível se concluir que seu funcionamento é
extremamente paralelo e distribuído, sendo este um dos fatos que torna possível sua indiscutível
superioridade em relação aos computadores atuais.
1 Website do pesquisador Josh Tenenbaum <http://web.mit.edu/cocosci/josh.html>. Acesso em: 20 Set., 2013
18
Outra clara vantagem acarretada pelo comportamento paralelo do cérebro, a qual é
mencionada por Graupe (2007, p.02), trata-se da sua insensibilidade a danos. O autor explica
que no sistema nervoso central de um humano adulto, milhares de neurônios morrem todos os
anos sem que as funções cerebrais sejam afetadas, a não ser em situações extremas como casos
de AVC severos. Em contrapartida, a arquitetura sequencial dos computadores digitais não
suporta que um transistor falhe, já que apenas um dentre os milhões de transistores dos quais
consistem estes sistemas pode levar toda a máquina à condição de inútil para os casos em que
não há um rigoroso estudo de tolerância a falhas.
Este mesmo paralelismo, e consequente insensibilidade anteriormente mencionada,
teoricamente se aplicam também a RNAs. Entretanto, a maior parte das implementações destas
redes são ainda realizadas em software nos computadores digitais convencionais, o que invalida
tais desejados aspectos.
O motivo por trás da escolha na implementação de RNAs em software, consoante
explica Misra e Saha (2010, p. 250), é devido ao rápido desenvolvimento dos processadores de
propósito geral, impossibilitando que implementações de redes neurais em hardware sejam
competitivas a ponto de se tornarem comercialmente bem sucedidas. Dias, Antunes e Mota
(2004, p.951) complementam afirmando que o alto custo destas implementações em relação a
tempo e recursos diminuem mais ainda sua competitividade, relatando uma outra razão para o
moroso progresso das redes neurais em hardware, a qual pode estar no número e variedade de
novos paradigmas na área, ainda em acelerado crescimento.
Destaca-se aqui, também, que os previamente mencionados autores veem o futuro
de forma otimista para implementações de RNAs em hardware. Misra e Saha (2010, p. 250)
acrescentam à explicação que a chave para este crescimento está no mapeamento das redes
neurais em estruturas paralelas de hardware, as quais ainda possuem grandes restrições de
custo-desempenho devido às despesas associadas à implementação das numerosas
interconexões, controle e mapeamento envolvidos. Dentre as novas soluções, os últimos autores
citam as interconexões óticas, a tecnologia de VLSI 3D2, a tecnologia híbrida
semicondutor/nanofio/molecular a qual combina CMOS e nanotecnologia denominada
CMOL3, dentre outras.
2 AL-SARAWI, S.; ABBOTT, D.; FRANZON, P. A review of 3-D packaging technology. IEEE Transactions on Components, Packaging, and Manufacturing Technology, Part B: Advanced Packaging 21 p. 2-14, fev. 1998. 3 LIKHAREV K.K.; STRUKOV, D.B. CMOL: Devices Circuits and Architectures. Springer, Berlin, Heidelberg, Germany, p. 447-477, c. 16, 2005.
19
Misra e Saha (2010, p. 251) concluem seu artigo comentando que modelos em
hardware de RNAs terão mais espaço nos próximos anos, quando a indústria enfrentará
exigências impostas pela computação como a capacidade de aprendizagem e de autonomia de
decisão em áreas como robótica autônoma e tecnologias assistivas. Estas aplicações exigem
lidar com grandes volumes de dados multimídia em tempo real a partir de ambientes interativos,
ao mesmo tempo que devem utilizar-se de hardware com rigorosas restrições de uso de energia,
sem que a eficiência computacional decline.
Para Omondi, Rajapakse e Bajger (2006, p.9-12), entretanto, o futuro das
implementações de RNAs não está nem em circuitos integrados de aplicação específica, nem
em softwares rodando nos processadores de uso geral, mas em FPGAs. É sabido que estes
hardwares programáveis não alcançam em performance os processadores ASIC ou os
microprocessadores convencionais. Entretanto, os autores explicam, após vários casos de
estudo, que as FPGAs podem oferecer uma melhor relação custo/performance que as duas
formas de implementação anteriores, além de proporcionar uma maior flexibilidade gerada pela
sua alta capacidade de reconfiguração.
Assim, à vista de todo o conteúdo discutido até então, buscou-se justificar a escolha
de implementação deste projeto, propondo o presente trabalho o desenvolvimento de uma rede
neural Multilayer Perceptron em hardware, a qual deverá ser embarcada na FPGA Spartan3E-
250 CP132, fabricado pela empresa Xilinx. A linguagem de descrição de hardware VHDL será
utilizada para desenvolver a rede neural proposta.
Trabalhos com semelhantes propostas podem ser encontrados na literatura.
Muthuramalingam, Himavathi e Srinivasan (2008, p. 86-92) discutem a implementação de um
único neurônio em FPGA, enquanto Mohammed e Ali (2013, p. 780-783), Abdu-aljabar (2012,
p. 73-90) e Won (2007, p. 816-820) apresentam suas propostas arquiteturas de redes neurais
multicamadas feedforward, contendo duas ou três camadas de neurônios. Este trabalho,
entretanto, pretende diferenciar-se ao fazer uso do denominado paralelismo de neurônio, o qual
garante uma maior rapidez ao circuito final, uma vez que todos os neurônios de uma mesma
camada trabalhando simultaneamente. Ademais, este trabalho propõe uma arquitetura cuja
modificação no número de neurônios em cada camada seja facilmente realizável, garantindo
assim que diversas implementações possam fazer uso dos códigos em VHDL implementados.
A maior parte das implementações encontradas realiza o treinamento das RNAs de
modo off-line, utilizando-se de ferramentas de software, sendo os trabalhos supramencionados
exemplos de realizações de tais treinamentos. Apesar de perderem em flexibilidade ao não
20
poderem ser treinados durante o uso, estes ganham em tempo ao diminuírem a latência dos
cálculos computacionais. Neste trabalho, portanto, a implementação do algoritmo de
treinamento Backpropagation será realizada de forma off-line, utilizando-se a ferramenta
Neural Network Toolbox e um script do software Matlab.
Com propostas mais específicas, em que se diferenciam implementações da função
sigmoide, parte de substancial relevância para o presente projeto, pode-se encontrar, por
exemplo, os trabalhos de Tommiska (2003, p.403-411), Basterretxea, Tarela, e del Campo
(2004, p. 18-24), dentre outros, os quais serão citados em momento oportuno.
Ademais, cita-se as importantes pesquisas de Nordström e Svensson (1992, p. 260-
285) e de Omondi, Rajapakse e Bajger (2006, p.1-36), as quais contêm estudos detalhados sobre
implementações em hardware de RNAs. Por fim, mencionam-se os estudos de Misra e Saha
(2010, p. 239-255) e de Dias, Antunes e Mota (2004, p. 945-952), artigos de datas recentes, os
quais contêm levantamentos de atuais trabalhos, comerciais e acadêmicos, na área de estudo
desta pesquisa.
Este trabalho será, então, apresentado da seguinte forma: a primeira seção conterá
todo o conteúdo teórico necessário ao entendimento das redes neurais artificiais. Ainda na
primeira seção, serão também apresentados os detalhes do projeto proposto, incluindo-se as
escolhas necessárias para uma boa implementação em hardware e suas respectivas justificações
através de estudos bibliográficos.
A segunda seção conterá a implementação da função XOR, utilizada neste trabalho
para testar o funcionamento da rede neural proposta. Em seguida, após uma discussão de todos
os métodos utilizados e resultados obtidos, serão mencionados os trabalhos e melhorias cujas
implementações podem ser de interesse para complementar o atual projeto.
21
2 REDES NEURAIS ARTIFICIAIS
Nesta seção, objetiva-se estabelecer toda a base teórica necessária para a
compreensão do trabalho proposto. Assim, será explanada a teoria de redes neurais artificiais
iniciando-se pelo modelo Perceptron, além de realizar um estudo sobre o seu poder e limitações
de representação. Como caminho natural de raciocínio, será, em seguida, introduzida a teoria
das redes MLP, fazendo-se uma breve explicação de seu treinamento, o algoritmo
Backpropagation.
Ademais, intenta-se, nesta seção, explicar de forma minuciosa a implementação do
trabalho proposto, bem como todos os desafios superados e as escolhas de projeto necessárias.
Portanto, ao final da seção, serão definidos os detalhes de precisão, de paralelismo, da
aproximação da função sigmoide realizada e dos demais aspectos envolvidos, a fim de se obter
uma visão completa da estrutura proposta neste trabalho.
2.1 O Perceptron
O Perceptron, consoante já mencionado, é possivelmente o primeiro neurônio
artificial proposto, além de ser o bloco básico o qual constitui a maioria das redes neurais
desenvolvidas ulteriormente. Ele consiste em um modelo aproximado à estrutura fundamental
de um neurônio biológico, cuja representação pode ser visualizada na Figura 1.
Figura 1 – Estrutura básica de um neurônio
Fonte: traduzida de NORDSTRÖM e SVENSSON (1992, p.261)
22
A estrutura neuronal, como se pode observar, é composta pelo corpo celular, por
uma quantidade de entradas, denominadas dendritos, por uma saída, a qual se divide a fim de
conectar-se a diversas outras células, denominada axônio, e pelas conexões, as quais funcionam
como uma espécie de resistor químico, denominadas sinapses. Além disso, destaca-se que as
conexões sinápticas não possuem a mesma intensidade, sendo estas diferenças representadas
pela neurociência através de um vetor de pesos sinápticos, os quais correspondem ao fluxo de
íons nestas conexões, consoante explicam Dayan e Abbott (2005, p.7.05). Os mencionados
autores explicam que, dependendo deste fluxo, as sinapses podem ser caracterizadas em
excitatórias ou inibitórias. Por fim, cita-se que a saída das células neurais, caracterizada pelos
denominados potenciais de ação, é gerada em função das entradas e de suas respectivas
influências (ou pesos), além de outros fatores os quais não são levados em consideração no
modelo simplista apresentado, como temporização, fluxo de íons através das membranas das
células etc.
Seguindo, portanto, a estrutura fundamental de um neurônio biológico, o modelo
Perceptron é também constituído por diversas entradas, as quais possuem pesos diferentes para
cada uma destas, e uma saída, a qual, simplificando o modelo biológico, é uma função das
entradas ponderadas da célula. A visualização deste modelo encontra-se na Figura 2.
Figura 2 – O Perceptron
Fonte: Elaborado pela autora.
Observa-se, na Figura 2, não apenas as entradas, saídas e funções, mas também a
nomenclatura que será utilizada no presente trabalho. As entradas serão, portanto, denominadas
23
��, ��, … , ��, os pesos, ��, ��, … , ��, e a saída, �. Observa-se também a adição de um offset,
ou bias, aqui representado pela letra θ.
Ademais, pode-se observar que há, de fato, o somatório das entradas ponderadas,
sendo esta função denominada Multiplier Accumulator, ou MAC. Por fim, observa-se, antes da
saída, uma função denominada função de ativação, sendo esta necessária para manter a saída
do neurônio entre um limite determinado.
Também denominada função squashing, a operação de ativação é uma função a
qual, segundo Graupe (2007, p.19), pode assumir diversas formas, apesar de todas possuírem a
mencionada propriedade de limitação. Em sua forma mais simples, pode-se encontrar a função
de ativação threshold, a qual representa a seguinte relação:
�� = � 1 �� � > 00 �� � ≤ 0 � 1�
Assim, de acordo com Kröse e Smagt (1996, p.23-24), a saída do Perceptron é 1 ou
0 dependendo das entradas, podendo agora este ser utilizado para resolver um problema de
classificação e assim decidir se determinado padrão de entradas pertencem a uma de duas
classes. Se o total de entradas é positivo, o padrão deverá ser atribuído à classe 1, ao passo que
se este total for negativo, a classe atribuída será a 0.
A função threshold, representada pela equação (1), pode ser observada através da
Figura 3. Outras funções de ativação podem também, consoante mencionado, ser utilizadas.
Graupe (2007, p.19) afirma que a mais comum função squashing é a função sigmoide logística,
representada pela seguinte equação:
�� = 11 + ��� 2�
A importância desta função de ativação, bem como sua detalhada especificação,
serão explanadas em momento posterior.
24
Figura 3 – Função Threshold
Fonte: Elaborado pela autora.
Considerando-se portanto todo o exposto, tem-se que a saída � do modelo
Perceptron deverá ser, por conseguinte:
� = �� = ���� ∙ ���
��� + ! 3�
na qual f(u) representa a função de ativação.
2.2 Representação como Hiperplano
Faz-se importante explanar que se pode ver o modelo Perceptron com função de
ativação threshold como a representação de um hiperplano, ou superfície de decisão, no espaço
n-dimensional de instâncias. Mitchell (1997, p.86-88) explica que a saída � do Perceptron será
1 para instâncias que se encontram de um lado do hiperplano e 0 (ou -1 em algumas
implementações) para as instâncias localizadas no outro lado. A equação para este hiperplano
é:
�##$ ∙ �$ = 0 4�
Pode-se visualizar a explanação realizada através da Figura 4, a qual representa dois
conjuntos linearmente separáveis por um hiperplano.
25
Figura 4 – Representação Geométrica do Hiperplano
Fonte: KRÖSE e SMAGT (1996, p.24).
Um exemplo bastante didático da representação dos Perceptrons são as funções
booleanas. Mitchell (1997, p.86-88) exemplifica que, para representar a função AND bastaria
definirem-se os pesos como �1 = �2 = 0.5 e o bias como = −0.8. Já para a função OR,
seria apenas necessário mudar o bias para = −0.3 e permanecerem iguais os valores dos
pesos. Suas representações podem ser conferidas a seguir na Figura 5.
Figura 5 – Representação geométrica das funções AND e OR
Fonte: KRÖSE e SMAGT (1996, p.29).
26
Utilizando-se como partida os exemplos aqui descritos, pode-se inferir que um
único Perceptron é capaz de representar também as funções NAND e NOR, uma vez que estas
são apenas variações das primeiras funções, sendo também linearmente separáveis.
Entretanto, Kröse e Smagt (1996, p.29) explanam que algumas funções não podem
ser representadas por um único Perceptron, como é o caso da função XOR, cuja tabela verdade
pode ser analisada a seguir:
Tabela 1 – Tabela Verdade da Função XOR
Estado Entradas Saída
x1 x2 z A 0 0 0 B 0 1 1 C 1 0 1 D 1 1 0
Fonte: Elaborado pela autora.
Consoante mencionado em seção anterior, a descoberta do problema de
representação do modelo Perceptron, ao se constatar que este não é capaz de representar uma
função tão simples quanto a função XOR, foi um fator significantemente desencorajador para
os estudiosos da época, tendo causado grande desilusão em relação a sua capacidade. Pode-se
examinar a impossibilidade de separação de forma linear da função XOR na Figura 6.
Figura 6 – Função XOR
Fonte: Adaptada de KRÖSE e SMAGT (1996, p.29).
27
De fato, existem muitos problemas que o modelo proposto não pode resolver.
Graupe (2007, p.23) explica que, à medida que se aumenta o número de entradas, o número de
problemas que podem ser classificados torna-se uma fração pequena do número de problemas
que podem ser formulados. Para entradas binárias, por exemplo, podem-se formar 2� padrões
de entrada diferentes, enquanto cada um destes padrões pode formar 2 saídas diferentes, o que
resulta um total de 2�* funções diferentes de n variáveis. Entretanto, o número destes problemas
que são linearmente separáveis se resume a uma pequena fração deste total, consoante se pode
observar na Tabela 2.
Tabela 2 – Número de problemas binários linearmente separáveis
No de entradas + 2�*
No de problemas linearmente
separáveis
1 4 4 2 16 14 (todas menos XOR/XNOR) 3 256 104 4 65000 1900 5 4.3 x 10, 95000 . . . . . . . . .
n > 7 � < Fonte: Traduzida e adaptada de GRAUPE (2007, p.23).
A solução para este problema viria anos mais tarde. Para se obter total entendimento
destas, parte-se do fato de se poder representar qualquer função booleana a partir de uma rede
de unidades interconectadas baseadas nas funções primitivas AND e OR.
Graupe (2007, p.23) explica, então, que basta adicionar uma outra camada de
neurônios para que a representação de modelos não-convexos, incluindo-se a função XOR,
sejam resolvidos. O autor ainda complementa que, ao se estender a três ou mais camadas, as
classes de problemas representáveis, e consequentemente solucionáveis, tornam-se
essencialmente ilimitadas. Mitchell (1997, p.86-88) explica que esta adição resolve o problema
pelo simples fato de se poder representar uma função booleana da forma normal disjuntiva, ou
seja, como a disjunção (OR) de um conjunto de junções (ANDs) das entradas e suas negações.
28
Figura 7 – Solução do problema da função XOR
Fonte: KRÖSE e SMAGT (1996, p.30).
Assim, torna-se imperioso tratar da teoria necessária para o entendimento desta
nova forma de rede neural, o Perceptron Multicamadas.
2.3 Multilayer Perceptron
Uma rede MLP consiste, basicamente, em uma estrutura em camadas, em que cada
uma é formada por neurônios cujas entradas são provenientes da camada anterior, o que
constitui a principal característica deste tipo de rede, denominada feedforward, ou “de
alimentação para a frente” em tradução livre.
Ademais, cada elemento da camada anterior possui ligação com todos os elementos
da camada seguinte, possuindo todas estas ligações diferentes pesos. A saída de cada neurônio
é, portanto, a função de ativação aplicada sobre a soma ponderada das entradas, do mesmo
modo que acontece no modelo Perceptron.
Observa-se que não há qualquer conexão entre os neurônios de uma mesma camada,
bem como entre camadas que não são consecutivas, como se pode verificar na Figura 8.
Também, ressalta-se que, na camada de entrada, não há qualquer processamento, possuindo
esta tantos elementos (e não neurônios) quanto o número de entradas da rede. As camadas
localizadas entre a camada de entrada e a camada de saída são denominadas ocultas, ou, em
inglês, hidden layers. Segundo Graupe (2007, p.33), utiliza-se, na maioria das implementações,
funções de ativação não lineares para as camadas ocultas, sendo a sigmoide a principal delas.
29
Figura 8 – Estrutura de uma MLP
Fonte: traduzida de BENGTSSON et al (2006, p.351).
Consoante explanado na seção anterior, a adição de camadas pode resolver várias
das restrições encontradas pelo modelo Perceptron. Entretanto, esta adição introduziu também
uma nova dificuldade, uma vez que agora as saídas das camadas ocultas não são acessíveis.
Esta indisponibilidade, consequentemente, impede um treinamento direto, ou seja, impede que
sejam definidos os pesos para as ligações intermediárias da rede através da comparação e
correção de erros entre entradas e saídas desejadas.
Kröse e Smagt (1996, p.33) explicam que a solução para tal dificuldade foi
apresentada por Rumelhart, Hintonand Williams em 1986. A ideia central desta solução,
explicam os autores, é que os pesos para as unidades das camadas ocultas podem ser
determinados através de uma retropropagação dos erros das unidades da camada de saída. Por
esta razão, o método proposto foi denominado Backpropagation learning rule, ou “regra da
retropropagação do erro” em tradução livre. A Tabela 3 apresenta o algoritmo para a aplicação
da regra da retropropagação do erro em determinada rede feedforward.
30
Tabela 3 – Algoritmo Backpropagation para redes feedforward contendo duas camadas de unidades sigmoide
Backpropagation (training_samples, -, +.�, +/�0, +1.223�)
Cada amostra é um par da forma {�$, 5$}, em que �$ é o vetor dos valores de
entrada, e 5$ é o vetor cujos valores são desejados para a saída da rede para
cada determinada entrada.
- é a taxa de aprendizado.
+.� é o número de entradas, +1.223� e +/�0 são o número de unidades na camada
oculta e na camada de saída, respectivamente.
� Criar uma rede feedforward com +.� entradas, +1.223� unidades na camada oculta e +/�0
unidades na saída.
� Inicializar todos os pesos da rede com números pequenos e aleatórios (e.g., entre -0,05 e 0.05).
� Até que a condição de término aconteça, fazer:
• Para cada par {�$, 5$} em training_samples, fazer:
Propagar as entradas, no sentido direto, através da rede:
1. Introduzir a instância �$ na rede neural e computer a saída �� de cada unidade � na rede.
Propagar os erros, em sentido reverso, através da rede.
2. Para cada unidade 7 de saída da rede, calcular o termo do erro:
89 ← �91 − �9�59 − �9� (Utilizou-se aqui o fato de que a função sigmoide possui derivada ; = 1 − �.) 3. Para cada unidade ℎ da camada oculta, calular o termo do erro:
81 ← �11 − �1� � �9189�9∈/�0>.
4. Atualizar cada peso e bias da rede:
��. ← ��. + ∆��. θ� ← θ� + ∆θ� Onde ∆��. = -8���. ∆θ� = -8�
Fonte: Traduzido e adaptado de MITCHELL (1997, p.98).
31
O processo de retropropagação é de fácil entendimento. Conforme
supramencionado, ao se definirem as entradas do sistema, estas se propagam adiante até a
camada de saída da rede. Logo após, a saída obtida é comparada com os valores desejados de
saída, resultando em um erro �A em cada unidade desta camada.
Em seguida, segundo explicam de forma lacônica Kröse e Smagt (1996, p.35-36),
deve-se realizar mudanças na rede de modo a zerar o erro da saída no momento em que cada
determinado padrão de entrada seja inserido. Todavia, como esta alteração não interfere nos
pesos das camadas ocultas, deve-se aplicar a regra da cadeia, na qual o erro da saída é
distribuído para todas as camadas ocultas com as quais é conectada, sendo este ponderado pelo
peso de cada ligação. A soma ponderada do erro é, depois, multiplicada pela derivada da função
de ativação, para que, por fim, as devidas alterações de pesos e offsets sejam realizadas.
2.4 Implementação
Por diversas vezes, já foi aqui citado que o Perceptron é o elemento básico de
processamento da rede neural proposta pelo presente trabalho. Destarte, serão observadas nas
seções a seguir, após breve comentário sobre a representação e as precisões escolhidas, os
detalhes de implementação do referido modelo de neurônio artificial.
Em seguida, explicar-se-á a estrutura da rede neural MLP completa, incluindo
explicações sobre paralelismo, algoritmos utilizados, dentre outros detalhes de projeto.
2.4.1 Representação e Precisão
A escolha da representação e, principalmente, da precisão dos dados deve ser
analisada com cuidado quando se trata de uma implementação em hardware, especialmente ao
se considerar as limitações impostas pelo projeto em questão, além das ainda existentes
limitações de hardware em FPGAs.
Outrossim, quanto se trata de precisão, deve-se realizar uma cuidadosa avaliação
de custo computacional versus performance, sobre a qual será provavelmente necessário tender
para uma das características, de forma a estar sempre de acordo com os objetivos do projeto a
ser realizado.
Neste trabalho, considerou-se como prioridade a obtenção de um circuito de baixo
custo computacional, de modo a ser realista com as opções disponíveis de hardware. Assim,
32
escolheu-se utilizar a representação em ponto fixo, em formato complemento de dois, com
diferentes precisões para os dados de entrada e pesos.
Ademais, optou-se pela utilização de dados com baixas precisões, escolha a qual
também garante um menor tempo de execução. Esta decisão foi fundamentada no estudo de
Holt e Hwang (1993, p.290), cujos resultados encontram-se resumidos na Tabela 4. Assim, de
acordo com os autores, valores de até 8 bits para entradas, e 16 para pesos são considerados
suficientes para uma boa representação.
Tabela 4 – Requisitos de precisão em MLPs
Backpropagation
Conv. binária
Conv. regressão
Pesos e offsets 13 - 15 bits 15 - 16 bits
Entradas, saídas e ativação 8 - 9 bits 9 - 10 bits
Fonte: traduzida e adaptada de Holt e Hwang (1993, p.290).
Complementando-se a justificativa, Nordström e Svensson (1992, p.266) explicam
que baixas precisões são interessantes para RNAs uma vez que tornam os algoritmos mais
plausíveis biologicamente. Para ilustrar, os autores explicam que um limite superior de exatidão
que o cérebro necessita para seus cálculos pode ser estimado em 7-8 bits.
2.4.2 Operação MAC
Foi explicado, em seção anterior, que cada unidade neuronal deve realizar uma série
de multiplicações e somas, representadas pela equação (5), a qual considera o offset incorporado
aos pesos.
� = � �.�.B
.�� 5�
Omondi, Rajapakse e Bajger (2006, p.4) discutem que não só as multiplicações e
adições, mas também a alternância entre sucessivas multiplicações e adições, consistem em
algumas das operações aritméticas mais importantes a serem realizadas na implementação de
uma rede neural em hardware. Neste trabalho, o Multiplier Accumulator, ou MAC, foi
33
desenvolvido tendo como base as lições de Pedroni (2004, p.285-289), cuja forma é ilustrada
na Figura 9.
Figura 9 – MAC
Fonte: Adaptado de PEDRONI (2004, p.286).
O MAC, basicamente, multiplica os dois valores de entrada (no caso, a entrada e o
peso desta) e a seguir adiciona o resultado ao valor acumulado por um registrador. Entretanto,
como o multiplicador e a posterior soma devem possuir a mesma quantidade de bits, é imperioso
realizar uma verificação de overflow, principalmente após grande quantidade de operações.
Ademais, decidiu-se que, para o caso de haver overflow, o valor da soma deverá ser truncado
para o maior valor de representação da quantidade de bits escolhida.
2.4.3 Função de Ativação
A função de ativação é, sem dúvidas, uma das partes mais complexas na realização
de uma rede neural feedforwad. Isso porque esta função envolve, quase sempre, funções
transcendentais, cujas implementações implicam em uma vasta quantidade de operações
aritméticas, podendo então constituir em uma grande limitação do circuito a ser desenvolvido.
Entretanto, felizmente, já foram estudadas e desenvolvidas por diversos autores formas de
aproximações deste tipo de função, algumas das quais serão discutidas a seguir.
Diversos são os autores que apontam a função sigmoide do tipo logística como a
mais utilizada em aplicações de redes MLP, dentre os quais cita-se Kröse e Smagt (1996, p.33),
Canas et al (2006, p.274) e Alpaydin (2009, p.228). Esta função, consoante previamente
mostrada, é dada pela equação (6).
34
�� = 11 � ��� (6)
Urge registrar que a escolha desta função de ativação em detrimento de outras como
a tangente hiperbólica ou arco-tangente se deu exatamente por sua vasta utilização, a qual
garante que a rede neural implementada seja altamente reutilizável, além de facilmente
adaptável. Pode-se ainda complementar a justificativa na utilização desta função de ativação ao
mencionar a útil propriedade de expressar sua derivada, necessária ao algoritmo
Backpropagation, em termos de sua saída. Mais precisamente,
;(�) = (�) ∙ (1 ( (�)) (7)
A função sigmoide pode ser observada graficamente na Figura 10.
Figura 10 – Função Sigmoide
Fonte: Elaborado pela autora.
A implementação em hardware da função sigmoide deve ser previamente
planejada, de modo que se possa desenvolver um circuito adequado aos objetivos do projeto.
Omondi, Rajapakse e Bajger (2006, p.21) explicam que, para este tipo de implementação,
precisão, desempenho e custo são de igual importância, o que implica que as melhores técnicas
de análise numérica, as quais são facilmente implementáveis em software, não são adequadas
em hardware.
35
No desenvolvimento deste trabalho, foram levadas em consideração diversas
formas de implementação, incluindo-se aproximações polinomiais, o algoritmo CORDIC,
aproximações racionais, look-up tables (LUTs) etc. Em detalhado estudo, entretanto, Omondi,
Rajapakse e Bajger (2006, p.21) explicam que o algoritmo CORDIC, cuja utilização foi
considerada para este projeto, principalmente por ser a técnica mais estudada para
implementações em hardware, é raramente implementado. Isto porque, apesar de ter a grande
vantagem de se poder utilizar o mesmo hardware para várias funções, o resultado obtido é
normalmente bastante pobre.
Para aproximações polinomiais de altas ordens, o problema é o contrário, conforme
explicam os mencionados autores. Apesar de as implementações que utilizam-se deste método
poderem chegar a erros bastante diminutos, tais aproximações fazem uso de um elevado número
de operações aritméticas para cada valor, o que implica em uma grande quantidade de hardware
utilizado, não se mostrando adequado para implementações em hardware, além de implicar em
um aumento no tempo de execução do sistema.
Outro método considerado para este projeto, a LUT, poderia ser considerada uma
boa solução para a precisão aqui determinada. Para maiores precisões, entretanto, o tamanho da
memória utilizada pode se tornar proibitiva, consoante dita Nordström (1995, p.14).
Optou-se, então, por implementar uma aproximação do tipo linear, a qual utiliza-se
de apenas algumas operações aritméticas simples. A operação escolhida, que simula a função
sigmoide por uma aproximação linear por partes, foi criada por Brown, Garber e Vanable (1988
apud NORDSTRÖM; SVENSSON, 1992, p.266). A equação (8) representa a mencionada
aproximação.
(�) =
EFFGFFH
0.000976 � ≤ (5� � 516 (5 < � ≤ (1
� � 24 (1 < � ≤ 1
� � 119 1 < � ≤ 5
0.99902 � ≥ 5
LFFMFFN
(8)
O resultado obtido pela aproximação ao simulá-la no software Matlab não difere de
forma significativa da função sigmoide original, tendo o maior erro o valor de 0,0809 para
números em ponto flutuante e de 0,0811 considerando-se também as diferenças de precisão.
Pode-se observar a forma gráfica da aproximação a partir da Figura 11.
36
Figura 11 – Aproximação Linear da Função Sigmoid
Fonte: Elaborado pela autora.
2.4.4 Paralelismo
Apontado como uma das maiores vantagens na implementação de redes neurais em
hardware, o paralelismo pode se apresentar de diversas formas. Uma cuidadosa análise destas
possíveis formas se faz necessária, segundo apontam Omondi, Rajapakse e Bajger (2006, p.12-
13), a fim de se determinar tanto as estruturas de hardware mais apropriadas, como os melhores
mapeamentos das estruturas de redes neurais para um dado hardware.
Os autores também explicam que, em geral, exceto para redes de tamanhos triviais,
a implementação de estruturas de hardware completamente paralelas não é algo viável, o que
implica que algum processamento sequencial deverá ser necessário.
Nordström e Svensson (1992, p.266) citam, a partir de um desdobramento dos
possíveis comportamentos computacionais, que podem haver, no mínimo, seis diferentes
formas de se obter paralelismo em redes neurais feedforward com treinamento
Backpropagation. São elas, em tradução livre4: paralelismo da sessão de treinamento;
paralelismo de exemplo do treinamento; paralelismo de camada (forward-backward);
paralelismo dos neurônios; paralelismo no peso (sinapses); paralelismo de bit. A Tabela 5 indica
4 Do texto original: Training session parallelism; Training example parallelism; Layer and Forward-Backward parallelism; Node (neuron) parallelism; Weight (synapse) parallelism; Bit parallelism.
37
a quantidade de operações elementares que cada uma destas formas de paralelismo pode
alcançar, sendo possível observar grande variação de alcance entre elas.
Tabela 5 – Formas e alcance do paralelismo nas RNAs
Paralelismo Alcance típico
Sessão de treinamento 10 – 1 000
Exemplo de treinamento 10 – 1 000 000
Camada (forward-backward) 1 – 6
Neurônio 100 – 1 000 000
Peso (sinapse) 10 – 100 000
Bit 1 – 64
Fonte: traduzido de BENGTSSON et al (2006, p.351).
Destaca-se que, para cada implementação, as dimensões de paralelismo escolhidas
dependerão das restrições impostas pela plataforma de hardware e do algoritmo a ser
implementado. Então, explicam Bengtsson et al (2006, p.351), para que uma aplicação de RNA
possa utilizar eficientemente os recursos computacionais, esta deve utilizar pelo menos uma das
seguintes quatro formas de paralelismo: paralelismo da sessão de treinamento, paralelismo de
exemplo do treinamento, paralelismo dos neurônios ou paralelismo no peso.
Deste modo, o presente trabalho foi realizado de modo a obter grande
aproveitamento do paralelismo de neurônio. Neste nível, consoante explanam Nordström e
Svensson (1992, p.266), o processamento paralelo é realizado por todos os neurônios, de modo
que a soma ponderada e a função de ativação sejam realizadas concomitantemente em cada
camada, e, como consequência, as saídas de todos os neurônios possam ser obtidas
simultaneamente. Outra forma de entender este paralelismo é ver os cálculos como operações
de matrizes, de modo que cada linha da matriz seja mapeada a uma unidade processadora.
De fato, este trabalho utilizou a analogia mencionada de modo quase literal, de
forma que os pesos foram armazenados na forma de matrizes, mapeadas a cada linha para um
neurônio da camada. As Figuras Figura 12 e Figura 13 demonstram a arquitetura proposta neste
trabalho.
38
Figura 12 – Modelo de uma camada da rede neural implementada
Fonte: Elaborado pela autora.
Na Figura 12, pode-se verificar uma camada exemplo da arquitetura proposta e,
apesar de constituir visualização bastante autoexplicativa, vale salientar alguns aspectos da
figura para um melhor entendimento. Observa-se que, na arquitetura, apenas uma entrada é
computada por vez, de modo que, na próxima borda de subida do clock, um registrador de
deslocamento será responsável por direcionar uma nova entrada às unidades neurais.
Figura 13 – Mapeamento do paralelismo de neurônio e registradores de deslocamento
Fonte: Elaborado pela autora.
Ademais, verifica-se, na Figura 13, a utilização de um outro deslocamento, desta
vez de toda uma linha da matriz dos pesos, de modo que a cada determinada entrada a respectiva
soma ponderada na sinapse em questão seja computada.
Ainda sobre o paralelismo de neurônio, Omondi, Rajapakse e Bajger (2006, p.12-
13) explicam que, se plenamente explorado, todos os níveis de paralelismo acima deste podem
também ser utilizados, o que torna o paralelismo de neurônio a mais importante das formas
disponíveis. Além disso, os autores citam que esta forma de paralelismo combina bastante com
39
o uso de FPGAs, uma vez que típicas estruturas de FPGAs envolvem um grande número de
unidades ou “células”, as quais operam em paralelo e para as quais os neurônios podem,
inclusive, ser mapeados diretamente.
2.4.5 A Rede Neural
Nas seções anteriores, foram explicados todos os blocos e implementações
necessários para a construção deste trabalho. Da precisão ao grau de paralelismo envolvidos,
foram mencionados os mais gerais detalhes de projeto considerados na implementação da rede
neural em hardware aqui proposta. Entretanto, uma explanação geral do conjunto final está
ainda por ser discutido.
Misra e Saha (2010, p.241) consideram que, para se especificar uma RNA, utilizam-
se geralmente termos de topologia da rede, função de ativação, algoritmo de aprendizado,
número e tipo de entradas e saídas, número de neurônios e interconexões sinápticas e número
de camadas utilizadas. Destas, apenas as últimas três definições não foram ainda mencionadas,
porém de forma proposital.
Intentou-se com este trabalho produzir um código mais geral possível, consoante
mencionado após a explicação a respeito da escolha da função de ativação sigmoide. Por esta
mesma razão, o número de neurônios em cada camada, e consequentemente o número de
interconexões sinápticas, dependerá da aplicação a ser realizada pela rede implementada, sendo
a mudança de fácil adaptação.
O número de camadas, entretanto, não pôde, por limitações de algoritmo, seguir o
mesmo raciocínio. Entretanto, consoante demonstrado por Hornik, Stinchcombe, e White,
1989; Funahashi, 1989; Cybenko, 1989; Hartman, Keeler e Kowalski, 1990 (apud KRÖSE;
SMAGT, 1996, p.33), apenas uma camada oculta é suficiente para aproximar qualquer função
com um número finito de descontinuidades e precisão arbitrária, desde que as funções de
ativação das unidades ocultas sejam não-lineares (teorema da aproximação universal aplicado
às redes neurais). Assim, uma vez que a função escolhida para as camadas intermediárias da
rede é não-linear, preserva-se a ambição de implementação de uma RNA cujo código seja
facilmente adaptável às mais diversas aplicações.
Uma vez que, finalmente, foram apresentadas todas as especificações da rede neural
proposta, faz-se também necessário comentar algumas escolhas de algoritmo, as quais podem
40
ser fundamentais para o entendimento dos códigos desenvolvidos. A representação básica de
seus principais blocos pode ser observada na Figura 14.
Figura 14 – Unidades do código desenvolvido em VHDL
Fonte: Elaborado pela autora.
Primeiramente, foi determinado que os dados de entrada seriam inseridos na forma
de vetores. Para tal, foi necessário criar, inicialmente, o PACKAGE denominado
my_data_types.vhd, apresentado no ANEXO A. Neste arquivo encontram-se também as
definições dos pesos, contidos no código na forma de matrizes. Uma terceira definição, também
referente aos pesos, representa uma linha da matriz dos pesos, necessária para a realização do
deslocamento no algoritmo explanado sobre paralelismo de neurônio.
A implementação do Perceptron se deu em arquivo separado do principal.
Denominado SPerceptron.vhd, o código contido no ANEXO B apresenta os cálculos da
operação MAC e da função de ativação sigmoide. Este modelo deve aqui ser utilizado para as
camadas ocultas da rede neural. Além deste, o código contido no ANEXO C, HPerceptron.vhd,
representa os mesmos cálculos da operação MAC, mas possui função de ativação threshold,
sendo esta necessária para algoritmos de classificação, uma vez que suas saídas devem ser
41
binárias. Vale ressaltar que ambos os modelos são controlados pelo sinal READY, o qual
determinará qual função deverá ser calculada entre as operações MAC e função de ativação.
Finalmente, o arquivo principal da rede neural, denominado MAC.vhd está contido
no ANEXO D. Neste código, podem ser visualizados, além da utilização dos códigos já
explanados através de port maps, todo o algoritmo de controle e deslocamento dos dados, além
do armazenamento de todos os valores de pesos e bias. Observa-se que a ordem de utilização
dos Perceptrons segue uma máquina de estados controlada pelo sinal control, o qual a cada
mudança de valor pode representar a modificação de valor do sinal READY ou a mudança de
camada a ser computada na rede.
Inserida em alguns estados da máquina de estados estão também contidos os
registradores e vetores de deslocamento previamente explanados. O sinal counter_in, neste
contexto, representa, basicamente, o controle destes deslocamentos. Ademais, após os valores
de entradas e seus respectivos pesos serem computados, os valores de bias são inseridos de
modo que possam também ser estes calculados.
Observa-se por fim que o número escolhido de neurônios da camada oculta,
mostrados no código apresentado, representa um valor suficiente para obter êxito no cálculo da
função XOR. Ademais, os valores contidos nas matrizes de pesos e vetores de bias são também
específicos para esta função. Maiores detalhes sobre estes fatos serão tratados na próxima seção.
42
3 RESULTADOS
Nesta seção será explicado o procedimento e os resultados obtidos nas simulações
computacionais, síntese e implementação em FPGA do circuito proposto. Em seguida, serão
descritos trabalhos e melhorias cuja realização pode ser considerada interessante para
complementar o presente projeto.
3.1 Implementação da Função XOR
Moussa, Areibi e Nichols (2006, p.48-49) descrevem a implementação do problema
da função XOR como uma maneira clássica utilizada para aferir a capacidade de aprendizagem
de uma RNA. Tendo isto em vista, conforme citado na seção anterior, adaptou-se a rede neural
proposta de modo que esta fosse capaz de resolver o mencionado problema. A topologia da
RNA utilizada pode ser visualizada na Figura 15.
Figura 15 – Topologia da rede utilizada para resolver o problema XOR
Fonte: Elaborado pela autora.
Incluídos nos anexos A a D, bem como explanados na seção anterior, os códigos
em VHDL utilizados para implementar a RNA proposta foram descritos e simulados através do
software ModelSim-Altera Starter Edition 10.1d5, desenvolvido pela Mentor Graphics e
5 Download disponível em <http://www.altera.com/products/software/quartus-ii/modelsim/qts-modelsim-index.html>. Acesso em: 22 Nov., 2013
43
disponibilizado gratuitamente pela Altera. Este software, o qual constitui importante ferramenta
na simulação de HDLs, é caracterizado por sua alta performance de simulação e grande
flexibilidade para automatização de processos.
Os valores de pesos e bias apresentados no arquivo MLP.vhd foram obtidos através
da implementação off-line do algoritmo Backpropagation, realizada utilizando-se o software
Matlab. O script contendo a mencionada implementação, XOR.m, pode ser observado no
ANEXO E. Faz-se importante observar que os pesos obtidos são, por este script, convertidos
para a forma binária e então transcrevidos para um arquivo de texto, de modo que se possa
transferir de forma manual os valores obtidos para o código em VHDL.
Uma vez em posse dos pesos necessários para um adequado funcionamento da rede
neural, criou-se então um testbench, código em VHDL o qual fornece os estímulos de entradas
e clocks necessários para se testar o circuito implementado. Além disso, o testbench aqui
realizado, disponível para visualização no ANEXO F, possui uma verificação de erro ao se
utilizar as funções ASSERT e REPORT, as quais geram mensagens na ferramenta de simulação
caso o resultado do código seja diferente do desejado.
A Figura 16 demonstra, de forma simplificada, o funcionamento do testbench
desenvolvido.
Figura 16 – Procedimento de Teste
Fonte: Elaborado pela autora.
Deste modo, com o uso do testbench como entidade top-level da implementação, e
utilizando-se das ferramentas e interface de simulação do software Modelsim, foi possível
realizar a verificação sintática e semântica da codificação em VHDL para situações ideais de
44
temporização. Por conseguinte, após a realização das quatro combinações possíveis da função
XOR de duas entradas, foi possível verificar o funcionamento correto do código implementado.
A validação da rede neural proposta, a partir da simulação, tornou então possível a
realização da síntese destes códigos, objetivando-se, finalmente, transferir o circuito resultante
para a FPGA. Assim, de modo a realizar a síntese, foi utilizado o software ISE Design Suite
14.4 (Webpack)6, disponibilizado em versão gratuita pela Xilinx, utilizando-se a configuração
conforme descrito na Tabela 6.
Tabela 6 – Configurações de Projeto no ISE Design Suite 14.4
Product Category: All Family: Spartan3E Device: XC3S250E Package: CP132 Speed Grade: -4 Top-Level Source Type: HDL Synthesis Tool: XST (VHDL/Verilog) Simulator: ISim (VHDL/Verilog) Preferred Language: VHDL
Fonte: Elaborado pela autora.
Após a realização da síntese, obtiveram-se importantes resultados, os quais podem
ser utilizados para caracterizar o circuito desenvolvido de forma objetiva. A Tabela 7,
primeiramente, descreve qualitativa e quantitativamente os circuitos digitais básicos formados
através da síntese do código descrito em VHDL. Destaca-se nesta a utilização de ROMs, as
quais representam os pesos inseridos no código principal (MLP.vhd).
Tabela 7 – Relatório Avançado da Síntese
# ROMs : 1 4x18-bit ROM : 1 # Multipliers : 4 12x9-bit registered multiplier : 4 # Adders/Subtractors : 14 18-bit adder : 3 20-bit adder : 6
6 Download disponível em <http://www.xilinx.com/support/download.html>. Acesso em: 28 Nov., 2013
45
21-bit adder : 4 32-bit adder : 1 # Registers : 393 Flip-Flops : 393 # Comparators : 18 21-bit comparator greatequal : 3 21-bit comparator lessequal : 10 24-bit comparator less : 3 32-bit comparator greater : 2 # Xors : 8 1-bit xor2 : 8
Fonte: Dados obtidos através do software ISE Design Suite 14.4
A provável característica de maior relevância obtida na síntese está contida na
Tabela 8. Nesta, importantes descrições de temporização do circuito gerado podem ser
analisadas, como o período mínimo e frequência máxima que podem ser utilizados pelo
circuito.
Tabela 8 – Temporização na Síntese
Timing Summary: --------------- Speed Grade: -4 Minimum period: 20.011ns (Maximum Frequency: 49.973MHz) Minimum input arrival time before clock: 6.725ns Maximum output required time after clock: 4.283ns Maximum combinational path delay: No path found
Fonte: Dados obtidos através do software ISE Design Suite 14.4
Por fim, a Tabela 9 contém o resumo obtido referente à utilização do chip da FPGA
pelo circuito gerado.
Tabela 9 – Utilização da FPGA
Device utilization summary: --------------------------- Selected Device : 3s250ecp132-4
46
Number of Slices: 409 out of 2448 16% Number of Slice Flip Flops: 386 out of 4896 7% Number of 4 input LUTs: 749 out of 4896 15% Number of IOs: 5 Number of bonded IOBs: 5 out of 92 5% Number of MULT18X18SIOs: 4 out of 12 33% Number of GCLKs: 1 out of 24 4%
Fonte: Dados obtidos através do software ISE Design Suite 14.4
Ressalta-se que, comparado a projetos de proposta semelhante como Abdu-Aljabar
(2012, p.87), pode-se considerar que o uso dos recursos do chip foi relativamente diminuto,
resultado que pode demonstrar a qualidade de uma implementação em hardware. O autor, o
qual desenvolveu uma rede neural de quatro entradas, quatro neurônios na camada oculta e um
neurônio na saída, e utilizou uma FPGA Virtex2, obteve os resultados mostrados na Tabela 10.
Tabela 10 – Resultados de Abdu-Aljabar (2012)
Number of Slices: 1,726 out of 5,120 33% Number of Slice Flip Flops: 3,113 out of 10,240 30% Number of 4 input LUTs: 1,372 out of 10,240 13% Number of bonded IOBs: 233 out of 328 71% Total equivalent gate count for design: 40,731 Number of RPM macros: 36
Fonte: adaptado de Abdu-Aljabar (2012, p.87)
Finalmente, após a realização da síntese, o circuito implementado pôde ser gravado
na FPGA, a qual está inserida no módulo de desenvolvimento Basys2, fabricado pela Digilent
Inc. Esta placa contém, além da FPGA Spartan 3E-250 CP132, portas VGA e PS/2, 4 displays
de 7 segmentos, 4 botões, 8 chaves deslizantes, 8 LEDs etc (DIGILENT INC, 2010, p.1),
consoante se pode conferir no diagrama apresentado na Figura 17 e na imagem da placa,
apresentada na Figura 18.
47
Figura 17 – Diagrama e características da placa Basys2
Fonte: traduzido e adaptado de DIGILENT INC (2010, p.1).
Figura 18 – Imagem da placa Basys2
Fonte: DIGILENT INC website.7
A Figura 19 apresenta a configuração das entradas e saídas disponíveis na placa
Basys2. Assim, a partir do acréscimo do código de adaptação FPGA.vhd, apresentado no
7 Disponível em: <http://www.digilentinc.com/press/pics/BASYS2.png>. Acesso em: 17 Dez., 2013
48
ANEXO G, o qual transforma as entradas da MLP em bits, foi possível utilizar dois botões
comutadores e um LED para testar o bom funcionamento da implementação proposta, além de
um botão do tipo push-button para o reset do sistema.
Figura 19 – I/O da placa Basys2
Fonte: DIGILENT INC (2010, p.4).
3.2 Considerações Finais e Trabalhos Futuros
Na seção anterior foi demonstrada a atuação da rede neural aqui proposta. Provou-
se, através de experimentos, o bom funcionamento do circuito, além de relevantes resultados
obtidos na síntese dos códigos implementados. Entretanto, alguns aspectos, os quais não
fizeram parte do escopo deste trabalho, merecem atenção por seu potencial para trabalhos
futuros.
Foi mencionado que a escolha da realização do treinamento Backpropagation de
modo off-line foi aqui escolhida por garantir uma melhor temporização do circuito final. Porém,
acredita-se que o uso intenso dos paralelismos de treinamento poderia melhorar este quadro,
49
tornando a implementação on-line deste treinamento altamente desejável, uma vez que
aumentará ainda mais a flexibilidade e adaptabilidade do projeto.
Ademais, a utilização da função XOR neste trabalho teve como intenção testar o
circuito implementado, além de demonstrar seu potencial para aplicações mais complexas.
Assim, a implementação de uma aplicação de reconhecimento e classificação de padrões
representará uma aplicação realista e útil para RNA proposta, o que a levará a cumprir o
propósito para o qual foi criada.
Por fim, citam-se como melhorias e propostas futuras a otimização do código
implementado, a realização de comparações de desempenho e utilização de recursos em outras
arquiteturas de FPGA e o cálculo de temporizações necessárias para descrever o poder de
processamento da rede neural implementada de formas comercialmente utilizadas, como
medidas de conexões por segundo8, atualização de conexão por segundo9 etc (DIAS,
ANTUNES e MOTA, 2004, p.946).
8 Do texto original: Connection per second (CPS). 9 Do texto original: Connection update per second (CUPS).
50
4 CONCLUSÃO
Neste trabalho, foi implementada uma rede neural feedforward Multilayer
Perceptron, utilizando-se a linguagem de descrição de hardware VHDL implementada na
FPGA Spartan-3E-250 CP132 da fabricante Xilinx. O treinamento da rede foi realizado de
forma off-line, utilizando-se da ferramenta Neural Network Toolbox e de um script do software
Matlab.
Para o teste da RNA implementada, utilizou-se o problema da função XOR como
parâmetro, demonstrando-se, através de simulações, que o circuito comportou-se da forma
inicialmente proposta, garantindo assim estar apto a realizar classificações mais complexas,
como reconhecimentos de padrões.
Foram também mensurados diversos aspectos da implementação e síntese,
especialmente levando-se em consideração a temporização e os recursos utilizados do chip da
FPGA. A partir destes resultados, e ao se realizar uma comparação com implementações de
propostas semelhantes, concluiu-se que o circuito implementado pode ser considerado
competitivo, com capacidade semelhante a alguns daqueles estudados.
Ademais, uma vez realizada extensa pesquisa bibliográfica, efetivada com a
finalidade de realizar e, de modo mais seguro, justificar todas as escolhas enfrentadas neste
projeto, foi possível obter-se não apenas um bom resultado na implementação, mas um
relevante estudo teórico escrito na língua portuguesa, haja vista ser substancialmente de língua
estrangeira as referências bibliográficas.
Por fim, pode-se considerar que o resultado final do projeto proposto foi uma rede
neural compacta, eficiente e, principalmente, flexível, cujo número de neurônios pode ser
facilmente adaptado às mais diversas implementações, tendo sido esta considerada a
característica mais importante na concepção deste trabalho.
51
REFERÊNCIAS
ABDU-ALJABAR, Rana D. Design and Implementation of Neural Network in FPGA. Journal of Engineering and Development, v. 16, no. 3, p. 73-90, set. 2012. ALPAYDIN, Ethem. Introduction to machine learning. Adaptive Computation and Machine Learning series, 2nd ed. The MIT Press, 2009. BASTERRETXEA, K.; TARELA, J.M.; DEL CAMPO, I. Approximation of sigmoid function and the derivative for hardware implementation of artificial neurons. Circuits, Devices and Systems, IEE Proceedings, v. 151, no. 1, p. 18-24, fev. 2004. BENGTSSON, Lars; LINDE, Arne; NORDSTRÖM, Tomas; SVENSSON, Bertil; TAVENIKU, Mikael. The REMAP reconfigurable architecture: a retrospective. In: OMONDI, Amos R.; RAJAPAKSE, Jagath C. (Edit.). FPGA Implementations of Neural Networks. p. 325-360, Holanda: Springer, 2006. CANAS, Antonio; ORTIGOSA, Eva M.; ROS, Eduardo; ORTIGOSA, Pilar M. FPGA implementation of a fully and partially connected MLP: Application to Automatic Speech Recognition. In: OMONDI, Amos R.; RAJAPAKSE, Jagath C. (Edit.). FPGA Implementations of Neural Networks. p. 271-296, Holanda: Springer, 2006. DAYAN, Peter; ABBOTT, Laurence F. Theoretical neuroscience: computational and mathematical modeling of neural systems. The MIT Press, 2005. DIAS, Fernando M.; ANTUNES, Ana; MOTA, Alexandre M. Artificial neural networks: a review of commercial hardware. Engineering Applications of Artificial Intelligence, Elsevier, v. 17, no. 8, p. 945-952, dez. 2004. DIGILENT INC. Basys2 Board Reference Manual. Pullman, WA, 11 nov. 2010. Disponível em: <http://www.digilentinc.com/Data/Products/BASYS2/Basys2_rm.pdf>. Acesso em: 1 dez. 2013. GRAUPE, Daniel. Principles of artificial neural networks. 2nd ed. Advanced Series in Circuits and Systems, v. 6. World Scientific Publishing, 2007. HOLT, J. L.; HWANG, Jenq-Neng. Finite precision error analysis of neural network hardware implementations. IEEE Transactions on Computers, v. 42, no. 3, p.281-290, mar. 1993. KRÖSE, Ben. SMAGT, P. van der. An introduction to neural network. 8th ed. The University of Amsterdam, 1996. MOUSSA, Medhat; AREIBI, Shawki; NICHOLS, Kristian. On the arithmetic precision for implementing Back-propagation networks on FPGA: a case study. In: OMONDI, Amos R.; RAJAPAKSE, Jagath C. (Edit.). FPGA Implementations of Neural Networks. p. 37-61, Holanda: Springer, 2006.
52
MISRA, Janardan; SAHA, Indranil. Artificial neural networks in hardware: A survey of two decades of progress. Neurocomputing, Elsevier, v. 74, p. 239-255, dez. 2010. MITCHELL, Tom M. Machine learning. McGraw Hill, 1997. MOHAMMED, Esraa Zeki; ALI, Haitham Kareem. Hardware Implementation of Artificial Neural Network Using Field Programmable Gate Array. International Journal of Computer Theory and Engineering, v. 5, no. 5, p. 780-783, out. 2013. MUTHURAMALINGAM, A.; HIMAVATHI, S.; SRINIVASAN, E. Neural Network Implementation Using FPGA: Issues and Application. International Journal of Information and Communication Engineering, v. 4, no. 2, p. 86-92, abr. 2008. NORDSTRÖM, Tomas; SVENSSON, Bertil. Using and designing massively parallel computers for artificial neural networks. Journal of Parallel and Distributed Computing, Elsevier, v. 14, no. 3, p. 260-285, mar. 1992. NORDSTRÖM, Tomas. On-Line Localized Learning Systems Part II – Parallel Computer Implementation. Research Report, no. TULEA 1995:02, Division of Computer Science and Engineering, Lulea University of Technology, Sweden, 1995. OMONDI, Amos R.; RAJAPAKSE, Jagath C.; BAJGER, Mariusz. FPGA Neurocomputers. In: OMONDI, Amos R.; RAJAPAKSE, Jagath C. (Edit.). FPGA Implementations of Neural Networks. p. 1-36, Holanda: Springer, 2006. PEDRONI, Volnei A. Circuit design with VHDL. The MIT Press, 2004. TOMMISKA, M.T. Efficient digital implementation of the sigmoid function for reprogrammable logic. Computers and Digital Techniques, v. 150, no 6, p. 403-411, 2003.
53
ANEXO A – my_data_types.vhd
----------------------------------------------------------------------------- -- Título/Title Package my_data_types -- Projeto/Project MLP -- Autora/Author Priscila C. Holanda -- Arquivo/File my_data_types.vhd -- Entidade/Entity my_data_types -- Rev. Data/Date -- 001 08/07/13 (m/d/y) -- -- -- Notas / Notes: -- adaptado de: Pedroni, Volnei A. Circuit Design With VHDL (2004, p.299-300) ----------------------------------------------------------------------------- library ieee; use ieee.std_logic_1164.all; use ieee.numeric_std.all; package my_data_types is constant size_m: INTEGER := 9; -- # of bits per input constant size_w: INTEGER := 12; -- # of bits per weight ---TESTE type vector_array_sizew is array (natural range <>) of signed(size_m+size_w-1 downto 0); type vector_array_io is array (natural range <>) of signed(size_m-1 downto 0); type row_w is array(natural range <>) of signed(size_w-1 downto 0); type vector_matrix_w is array (natural range <>, natural range <>) of signed(size_w-1 downto 0); end my_data_types;
54
ANEXO B – SPerceptron.vhd
----------------------------------------------------------------------------- -- Título/Title Hardlimit Perceptron -- Projeto/Project MLP -- Autora/Author Priscila C. Holanda -- Arquivo/File Perceptron.vhd -- Entidade/Entity Perceptron -- Rev. Data/Date -- 001 10/07/13 (m/d/y) -- -- -- Notas / Notes: -- Input/Output Data format = Fixed Point Value 2's complement -- MAC function adapt. from: Pedroni, Volnei A. Circuit Design With VHDL (2004, p.299-300) ----------------------------------------------------------------------------- library ieee; use ieee.std_logic_1164.all; use ieee.numeric_std.all; entity SPerceptron is generic ( size_m : integer := 9;
-- 08(sign) 07 . 06 05 04 03 02 01 00 # of bits per input/output size_m_fract: integer := 7; size_w : integer := 12;
--11(sign) 10 09 08 07 . 06 05 04 03 02 01 00 # of bits per weight size_w_fract: integer := 7; size_int : integer := 6; size_fract: integer := 14 ); port( IN_X : in signed(size_m-1 downto 0); -- Input x IN_W : in signed(size_w-1 downto 0); -- Weight w CLK : in std_logic; -- System Clock XRST, ZERO : in std_logic; -- Reset READY : in std_logic; -- All inputs inside => '1' OUT_Y : out signed(size_m-1 downto 0) -- Output f(x) ); end SPerceptron; architecture behavioral of SPerceptron is signal reg : signed(size_m+size_w-1 downto 0); begin neuron: process (CLK,XRST,READY) is --Variables MAC variable prod: signed(size_m+size_w-1 downto 0); variable sum : signed(size_m+size_w-1 downto 0) := (others => '0'); variable sign : std_logic; variable overf : signed(size_m+size_w-2 downto 0) := (others => '0'); --Variables Sig variable sig : signed(size_m-1 downto 0); variable atr : signed(1 downto 0); variable atr2 : signed(size_m-3 downto 0);
55
variable middle_x : signed(size_m+size_w-1 downto 0); --variable size_int : integer := size_m+size_w-size_m_fract-size_w_fract; --variable size_fract: integer := size_m_fract+size_w_fract; begin prod := IN_X*IN_W; if XRST = '1' or ZERO = '1' then sum := (others => '0'); sig := (others => '0'); elsif rising_edge(CLK) then case READY is when '0' => sign := sum(sum'left); sum := sum + prod; if (sign = prod(prod'left)) and (sum(sum'left) /= sign) then -- Overflow check --sum := (sum'left => sign, others => not sign); overf := (others => '0'); sum := sign & overf; end if; reg <= sum; when '1' => if reg <= (to_signed(-5,size_int) & to_signed(0,size_fract)) then -- -5 sig := (others => '0'); -- =0 elsif (reg > (to_signed(-5,size_int) & to_signed(0,size_fract))) and (reg <= (to_signed(-1,size_int) & to_signed(0,size_fract))) then -- -5 and -1 middle_x := (reg + (to_signed(5,size_int) & to_signed(0,size_fract))) srl 4; -- (reg+5)/16; sig := middle_x(size_fract+1 downto size_fract-size_m+2); elsif (reg > (to_signed(-1,size_int) & to_signed(0,size_fract))) and (reg <= (to_signed(1,size_int) & to_signed(0,size_fract))) then -- -1 and 1 middle_x := (reg + (to_signed(2,size_int) & to_signed(0,size_fract))) srl 2; --(reg+2)/4; sig := middle_x(size_fract+1 downto size_fract-size_m+2); elsif (reg > (to_signed(1,size_int) & to_signed(0,size_fract))) and (reg < (to_signed(5,10) & to_signed(0,size_fract))) then -- 1 and 5 middle_x := (reg + (to_signed(11,size_int) & to_signed(0,size_fract))) srl 4; sig := middle_x(size_fract+1 downto size_fract-size_m+2); elsif reg >= (to_signed(5,size_int) & to_signed(0,size_fract)) then -- 5 atr := "01"; atr2 := (others => '0');
56
sig := atr & atr2; else sig := (others => '1'); end if; sum := (others => '0'); when others => NULL; end case; end if; OUT_Y <= sig; end process neuron; end behavioral;
57
ANEXO C – HPerceptron.vhd
----------------------------------------------------------------------------- -- Título/Title Hardlimit Perceptron -- Projeto/Project MLP -- Autora/Author Priscila C. Holanda -- Arquivo/File Perceptron.vhd -- Entidade/Entity Perceptron -- Rev. Data/Date -- 001 10/07/13 (m/d/y) -- -- -- Notas / Notes: -- Input/Output Data format = Fixed Point Value 2's complement -- MAC function adapt. from: Pedroni, Volnei A. Circuit Design With VHDL (2004, p.299-300) ----------------------------------------------------------------------------- library ieee; use ieee.std_logic_1164.all; use ieee.numeric_std.all; entity HPerceptron is generic ( size_m : integer := 9; -- 08(sign) 07 . 06 05 04 03 02 01 00 # of bits per input/output size_m_fract: integer := 7; size_w : integer := 12; -- 11(sign) 10 09 08 07 . 06 05 04 03 02 01 00 # of bits per weight size_w_fract: integer := 7 ); port( IN_X : in signed(size_m-1 downto 0); -- Input x IN_W : in signed(size_w-1 downto 0); -- Weight w CLK : in std_logic; -- System Clock XRST, ZERO : in std_logic; -- Reset READY : in std_logic; -- All inputs inside => '1' OUT_Y : out signed(size_m-1 downto 0) -- Output f(x) ); end HPerceptron; architecture behavioral of HPerceptron is signal reg : signed(size_m+size_w-1 downto 0); begin neuron: process (CLK,XRST,READY) is --Variables MAC variable prod: signed(size_m+size_w-1 downto 0); variable sum : signed(size_m+size_w-1 downto 0) := (others => '0'); variable sign : std_logic; variable overf : signed(size_m+size_w-2 downto 0) := (others => '0'); --Variables Sig variable sig : signed(size_m-1 downto 0); variable atr : signed(1 downto 0); variable atr2 : signed(size_m-3 downto 0);
58
begin prod := IN_X*IN_W; if XRST = '1' or ZERO = '1' then sum := (others => '0'); sig := (others => '0'); elsif falling_edge(CLK) then case READY is when '0' => sign := sum(sum'left); sum := sum + prod; if (sign = prod(prod'left)) and (sum(sum'left) /= sign) then -- Overflow check overf := (others => '0'); sum := sign & overf; end if; reg <= sum; when '1' => if reg <= "000000010000000000000" then -- 0.5 sig := (others => '0'); -- =0 elsif reg > "000000010000000000000" then -- 0.5 atr := "01"; atr2 := (others => '0'); sig := atr & atr2; end if; sum := (others => '0'); when others => NULL; end case; end if; OUT_Y <= sig; end process neuron; end behavioral;
59
ANEXO D – MLP.vhd
----------------------------------------------------------------------------- -- Título/Title MLP XOR -- Projeto/Project MLP -- Autora/Author Priscila C. Holanda -- Arquivo/File MLP.vhd -- Entidade/Entity MLP -- Rev. Data/Date -- 003 11/07/13 (m/d/y) -- -- -- Notas / Notes: -- Input/Output Data format = Signed Fixed Point Value 2's complement ----------------------------------------------------------------------------- library ieee; use ieee.std_logic_1164.all; use ieee.numeric_std.all; library work; use work.my_data_types.all; entity MLP is generic ( ni: integer := 2; -- # of neurons @ input layer --5 nh: integer := 3; -- # of neurons @ hidden layer --3 no: integer := 1; -- # of neurons @ output layer --1 size_m: integer := 9; -- # of bits per input size_w: integer := 12 -- # of bits per weight ); port ( IN_X : in VECTOR_ARRAY_IO(1 TO ni); CLK : in std_logic; XRST : in std_logic; OUT_Y : out signed(size_m-1 downto 0) ); end MLP; architecture behavioral of MLP is component SPerceptron port ( IN_X : in signed(size_m-1 downto 0); -- Input x IN_W : in signed(size_w-1 downto 0); -- Weight w CLK : in std_logic; -- System Clock XRST, ZERO : in std_logic; -- Reset READY : in std_logic; -- All inputs inside => '1' OUT_Y : out signed(size_m-1 downto 0) -- Output f(x) ); end component; component HPerceptron port ( IN_X : in signed(size_m-1 downto 0); -- Input x IN_W : in signed(size_w-1 downto 0); -- Weight w CLK : in std_logic; -- System Clock XRST, ZERO : in std_logic; -- Reset READY : in std_logic; -- All inputs inside => '1' OUT_Y : out signed(size_m-1 downto 0) -- Output f(x) ); end component;
60
--Signals --Pipelining and/or intermediate signal reg_in_hid: VECTOR_ARRAY_IO(1 to ni) := (others => (others => '0')); signal out_hid: VECTOR_ARRAY_IO(1 to nh) := (others => (others => '0')); signal reg_out_hid: VECTOR_ARRAY_IO(1 to nh) := (others => (others => '0')); --ROM weights and biases signal rom_weights_hid: VECTOR_MATRIX_W(1 to ni, 1 to nh) := (("101011111010","010110101010","001111100011"), ("001010101101","001010111000","110011111011")); signal rom_bias_hid: ROW_W(1 to nh) := ("010011000110","111001011010","000111110001"); signal rom_weights_out: ROW_W(1 to nh) := (("111100101001","000000000001","111101101111")); signal rom_bias_out: signed(size_w-1 downto 0) := "000101100101"; --Weights signals signal weights_hid: VECTOR_MATRIX_W(1 to ni, 1 to nh); signal weights_out: ROW_W(1 to nh); --Control signal ready: std_logic := '0'; signal zero: std_logic := '0'; ------------------------------------------------------------------------- function extract_column( m : VECTOR_MATRIX_W; column : integer; ranged : integer) return ROW_W is variable aux : ROW_W(1 to ranged); begin for i in 1 to ranged loop aux(i) := m(i, column); end loop; return aux; end function; function return_column( m : VECTOR_MATRIX_W; row : ROW_W; column : integer; ranged, ranged2 : integer) return VECTOR_MATRIX_W is variable matrix : VECTOR_MATRIX_W(1 to ranged, 1 to ranged2) := m; begin for i in 1 to ranged loop matrix(i,column) := row(i); end loop; return matrix; end function; ------------------------------------------------------------------------- begin Hid_Layer : for i in 1 to nh generate hid_neurons: SPerceptron port map(reg_in_hid(1), weights_hid(1,i), CLK, XRST, zero, ready, out_hid(i));
61
end generate Hid_Layer; --Out_Layer : for i in 1 to no generate --out_neurons: HPerceptron port map(reg_out_hid(i), weights_out(i), CLK, XRST, ready, OUT_Y(i)); --end generate In_Layer; out_neurons: HPerceptron port map(reg_out_hid(1), weights_out(1), CLK, XRST, zero, ready, OUT_Y); process (CLK) variable control, counter_in: integer := 0; variable w_column_h : ROW_W(1 to ni); variable w_matrix_aux : VECTOR_MATRIX_W(1 to ni, 1 to nh); begin if XRST = '1' then control := 0; ready <= '0'; elsif rising_edge(CLK) then case control is when 0 => if counter_in > ni+1 then -- all multiplications were performed counter_in := 0; control := control + 1; reg_in_hid <= (others => "000000000"); ready <= '1'; elsif counter_in = ni+1 then -- bias reg_in_hid <= (others => "010000000"); for j in 1 to nh loop weights_hid(1,j) <= rom_bias_hid(j); end loop; counter_in := counter_in + 1; elsif counter_in = 0 then zero <= '1'; reg_in_hid <= IN_X; weights_hid <= rom_weights_hid; w_matrix_aux := rom_weights_hid; counter_in := counter_in + 1; elsif counter_in = 1 then zero <= '0'; counter_in := counter_in + 1; else -- shift registers reg_in_x and weights_in reg_in_hid <= reg_in_hid(2 to ni) & reg_in_hid(1); for j in 1 to nh loop w_column_h := extract_column(w_matrix_aux, j, ni); w_column_h := w_column_h(2 to ni) & w_column_h(1); w_matrix_aux := return_column(w_matrix_aux, w_column_h, j, ni, nh); end loop; weights_hid <= w_matrix_aux; counter_in := counter_in + 1; end if; when 1 => ready <= '0';
62
control := control + 1; when 2 => if counter_in > nh+1 then -- all multiplications were performed counter_in := 0; control := control + 1; reg_out_hid <= (others => "000000000"); ready <= '1'; elsif counter_in = nh+1 then -- bias reg_out_hid <= (others => "010000000"); --for j in 1 to nh loop weights_out(1) <= rom_bias_out; --end loop; counter_in := counter_in + 1; elsif counter_in = 0 then zero <= '1'; reg_out_hid <= out_hid; weights_out <= rom_weights_out; counter_in := counter_in + 1; elsif counter_in = 1 then zero <= '0'; counter_in := counter_in + 1; else -- shift registers reg_in_x and weights_in reg_out_hid <= reg_out_hid(2 to nh) & reg_out_hid(1); --for j in 1 to no loop --w_column := extract_column(weights_out, j, nh); --w_column := w_column(nh) & w_column(1 to nh-1); --weights_out <= return_column(weights_out, w_column, j, nh, no); --end loop; weights_out <= weights_out(2 to nh) & weights_out(1); counter_in := counter_in + 1; end if; when others => NULL; end case; end if; end process; end behavioral;
63
ANEXO E – XOR.m
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % Título/Title Matlab XOR Offline Backpropagation % Projeto/Project MLP % Autora/Author Priscila C. Holanda % Arquivo/File XOR.m % Rev. Data/Date % 001 10/24/13 (m/d/y) %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% close all clear all clc p = [0 0 1 1;0 1 0 1]; t = [0 1 1 0]; net = feedforwardnet([3]); net = configure(net,p, t); net.trainParam.epochs=1500; net.trainParam.lr = 0.8; net.trainParam.goal = 0.01; net.divideFcn=''; net.layers{1}.transferFcn = 'logsig'; net.inputs{1}.processFcns = {'fixunknowns' 'removeconstantrows'}; net.outputs{2}.processFcns = {'removeconstantrows'}; net=train(net,p,t); a = sim(net,p) aa = round(sim(net,p)) %% int_bits = 5; fract_bits = 7; word_length = int_bits + fract_bits; output = fopen('weights.vec', 'w'); %WEIGHTS HIDDEN weights1 = net.IW{1,1}; fprintf(output,'HIDDEN WEIGHTS\n'); for i = 1:3 for j = 1:2 w1 = fi(weights1(i,j), 1, word_length, fract_bits); a = bin(w1); fwrite(output, a); fprintf(output,' '); end fprintf(output,'\n'); end %BIAS INPUT fprintf(output,'\nHIDDEN BIAS\n'); bias1 = net.b{1}; for j = 1:3 w1 = fi(bias1(j), 1, word_length, fract_bits);
64
a = bin(w1); fwrite(output, a); fprintf(output,'\n'); end %WEIGHTS OUTPUT weights3 = net.LW{2,1}; fprintf(output,'\nOUTPUT WEIGHTS\n'); %for i = 1:3 for j = 1:3 w1 = fi(weights3(1,j), 1, word_length, fract_bits); a = bin(w1); fwrite(output, a); fprintf(output,' '); end fprintf(output,'\n'); %end %BIAS OUTPUT fprintf(output,'\nOUTPUT BIAS\n'); bias3 = net.b{2}; %for j = 1:3 w1 = fi(bias3, 1, word_length, fract_bits); a = bin(w1); fwrite(output, a); fprintf(output,'\n'); %end fclose(output);
65
ANEXO F – MLP_Tb.vhd
----------------------------------------------------------------------------- -- Título/Title MLP Testbench -- Projeto/Project MLP -- Autora/Author Priscila C. Holanda -- Arquivo/File MLP_Tb.vhd -- Entidade/Entity MLP_Tb -- Rev. Data/Date -- 001 10/24/13 (m/d/y) -- -- -- Notas / Notes: -- Input/Output Data format = Signed Fixed Point Value 2's complement ----------------------------------------------------------------------------- LIBRARY ieee; USE ieee.std_logic_1164.ALL; USE std.textio.ALL; USE ieee.std_logic_textio.ALL; use ieee.numeric_std.all; library work; use work.my_data_types.all; ENTITY MLP_Tb IS generic ( ni: integer := 2; -- # of neurons @ input layer nh: integer := 3; -- # of neurons @ hidden layer no: integer := 1; -- # of neurons @ output layer size_m: integer := 9; -- # of bits per input size_w: integer := 12 -- # of bits per weight ); END MLP_Tb; ARCHITECTURE behavior OF MLP_Tb IS -- Component Declaration for the Unit Under Test (UUT) COMPONENT MLP PORT( IN_X : in VECTOR_ARRAY_IO(1 TO ni); CLK : in std_logic; XRST : in std_logic; OUT_Y : out signed(size_m-1 downto 0) ); END COMPONENT; --Inputs signal IN_X : VECTOR_ARRAY_IO(1 TO ni) := (others => (others => '0')); signal CLK : std_logic := '0'; signal XRST : std_logic := '0'; --Output signal OUT_Y : signed(size_m-1 downto 0);
66
-- Clock period definitions constant CLK_period : time := 10 ns; BEGIN -- Instantiate the Unit Under Test (UUT) uut: MLP PORT MAP ( IN_X => IN_X, CLK => CLK, XRST => XRST, OUT_Y => OUT_Y ); -- Clock process definitions CLK_process :process begin CLK <= '0'; wait for CLK_period/2; CLK <= '1'; wait for CLK_period/2; end process; -- Stimulus process stim_proc: process constant PERIOD: time:=10ns; begin IN_X<=("010000000","000000000"); wait for 13*PERIOD; assert(OUT_Y="010000000") report "Test FAILED" severity error; wait; end process; END;
67
ANEXO G – FPGA.vhd
----------------------------------------------------------------------------- -- Título/Title FPGA -- Projeto/Project MLP -- Autora/Author Priscila C. Holanda -- Arquivo/File FPGA.vhd -- Entidade/Entity FPGA -- Rev. Data/Date -- 001 11/30/13 (m/d/y) ----------------------------------------------------------------------------- LIBRARY ieee; USE ieee.std_logic_1164.ALL; USE std.textio.ALL; USE ieee.std_logic_textio.ALL; use ieee.numeric_std.all; library work; use work.my_data_types.all; entity FPGA is generic ( ni: integer := 2; -- # of neurons @ input layer nh: integer := 3; -- # of neurons @ hidden layer no: integer := 1; -- # of neurons @ output layer size_m: integer := 9; -- # of bits per input size_w: integer := 12 -- # of bits per weight ); port ( IN_X : in std_logic_vector(1 downto 0); CLK : in std_logic; XRST : in std_logic; OUT_Y : out std_logic ); end FPGA; architecture behavior of FPGA is component MLP port( IN_X : in VECTOR_ARRAY_IO(1 to ni); CLK : in std_logic; XRST : in std_logic; OUT_Y : out signed(size_m-1 downto 0) ); end component; signal in_x_aux : VECTOR_ARRAY_IO(1 to ni) := (others => (others => '0')); signal out_y_aux : signed(8 downto 0) := (others => '0'); begin mlp_inst: MLP port map ( IN_X => in_x_aux, CLK => CLK, XRST => XRST,
68
OUT_Y => out_y_aux ); with IN_X select in_x_aux <= ("000000000","000000000") when "00", ("000000000","010000000") when "01", ("010000000","000000000") when "10", ("010000000","010000000") when "11", ("111111111","111111111") when others; with out_y_aux select OUT_Y <= '0' when "000000000", '1' when "010000000", 'Z' when others; end;