Equações que Aprendem:
Uma Introdução aos Fundamentos Matemáticos da Aprendizagem Artificial
A. P. Rodrigues
___________________________________________________________________ Dados Internacionais de Catalogação na Publicação (CIP) (Câmara Brasileira do Livro, SP, Brasil) Rodrigues, A. P. Equações que aprendem : uma introdução aos fundamentos matemáticos da aprendizagem artificial / A. P. Rodrigues. -- 1. ed. -- Passo de Torres, SC : Ed. do Autor, 2025. ISBN 978-65-01-49720-4 1. Aprendizagem de máquina 2. Ciência da computação 3. Inteligência artificial 4. Modelos matemáticos 5. Redes neurais (Ciência da computação) I. Título. 25-275449 CDD-006.3 Índices para catálogo sistemático: 1. Inteligência artificial 006.3 ___________________________________________________________________
Dedico este livro…
Ao Eduardo, cuja gentil indicação bibliográfica me trouxe às redes neurais artificiais.
Ao professor Giovane pela disposição e atenção cálidas, e ao seu irmão, Vinícius.
Ao Daniel pelo papo, dicas e conselhos de valor inestimável.
Ao meu amigo querido, paizão mesmo, Marcus Maia.
Ao Fabiano e à Laura pela gratidão que virou tamanha generosidade.
À professora Carmen Mandarino.
Àquele que criou o neurônio sob cuja inspiração o Perceptron foi inventado.
Prefácio
Este é um e-book de distribuição gratuita, mas se você quiser, poderá adquirir uma cópia física no website do autor. Este livro é sobre a aprendizagem artificial e como este dom é dado a uma das estruturas mais fundamentais de todas as redes neurais: o Perceptron.
Este livro apresenta o que há de mais básico sobre a aprendizagem artificial em redes neurais. Qualquer pessoa pode utilizá-lo para ter um primeiro contato com o fascinante mundo em que as máquinas são capazes de aprender quase qualquer coisa e simular aspectos importantes da inteligência humana, como ver, ler, falar, entender o que outro ser humano fala, e várias outras capacidades utilíssimas e que têm estado cada vez mais ao alcance de todos.
Os dois primeiros capítulos deste livro costumavam compor um outro livro que eu havia intitulado de "O Mais Básico do Básico do Básico sobre a Aprendizagem Artificial". A presente obra é uma formidável ampliação da anterior. Embora o tratamento e o alcance do conteúdo do presente volume possam ser ditos ainda serem bem básicos, ele vai mais a fundo na aprendizagem das máquinas e mostra como dotar modelos profundos com a capacidade de aprender.
A aprendizagem artificial é o admirável segredo que está por trás das maravilhas que vemos, hoje em dia, nas IA’s mais conhecidas, como, por exemplo, o ChatGPT ou o Gemini. Sem ela, estes verdadeiros subprodutos monumentais da tecnologia não teriam sido possíveis. Para se construir uma inteligência artificial não basta apenas saber escrever código em alguma boa linguagem de programação como Tensorflow ou Pytorch. Estas mesmas linguagens são resultado de se haver dominado o entendimento de como fazer uma máquina, um software ou uma equação apreenderem e manterem o que gostaríamos de ensinar a elas.
No entanto, a aprendizagem está normalmente encapsulada em métodos, funções, classes, etc, daquelas linguagens e um excelente programador nunca precisa entrar em contato com elas, se não o desejar, para descrever em código a estrutura que deseja construir. Deste modo, o dom mais precioso de toda a inteligência artificial, em minha opinião, fica como que escondido.
O ocultamento, que é em parte um efeito colateral da automatização das camadas programáticas responsáveis pela aprendizagem, não é sem motivo. Ela amplia grandemente o número de pessoas que são capazes de dar vida a uma ideia através de inteligência artificial, mesmo sem jamais ter conhecido nada a respeito de como a aprendizagem artificial funciona.
Infelizmente, a matemática que descreve o fenômeno da aprendizagem não é normalmente ensinada antes do nível superior de educação no Brasil. E, embora ela não seja difícil e possa mesmo ser considerada conhecimento matemático antigo, o Cálculo Diferencial e a derivada de funções, acaba ficando desconhecido para um grande número de pessoas.
Este livro inteiro é sobre derivar funções! As funções em questão são os Perceptrons! Mas, os perceptrons são funções de um tipo um tanto mais elaborado. Não são funções reais. Eles são funções que envolvem matrizes e vetores!
O aprendizado artificial, que se baseia em derivadas, está baseado em uma técnica conhecida como "Propagar para trás" (back propagation, em inglês). Ela é o jeito certo de derivar uma função de tipo vetorial que seja compositivamente profunda. Este livro é sobre isto. Este é o principal conteúdo deste livro.
O que apresento nesta obra não é a única coisa que você deverá saber de importante sobre o aprendizado de máquina, mas é o indispensável! Sem este cerne central, o aprendizado artificial e as peças de inteligência artificial mencionadas acima não existiriam.
Escrevi este livro muito como eu mesmo gostaria de ter lido sobre o assunto quando comecei a estuda-lo. Tentei mostrar o que é, em minha opinião, o mais importante dentro do conjunto das coisas mais importantes, mostrando, por exemplo, as equações da estrutura do Perceptron e as do funcionamento da aprendizagem, de modo explícito e direto-ao-ponto, a fim de que qualquer um saiba exatamente como codificá-las, na sua linguagem preferida, tão logo lance um olhar sobre elas. Era isto o que eu gostaria ter encontrado quando comecei a estudar o assunto. Um tratamento elementar, acessível a principiantes, unitemático relativamente à aprendizagem e que fosse desde a conceituação, passando pela descrição detalhada e indo até a aplicação. Este livro, como já mencionado, enfoca fortemente a etapa descritiva.
No apêndice Tópicos Fundamentais em Aprendizado de Redes Neurais Não Tratados Neste Livro, você poderá encontrar uma lista de tópicos importantes em aprendizagem de máquina que não foram tratados, ou que tenham sido apenas mencionados, ou insuficientemente abordados na presente versão deste livro. Ela serve como uma boa referência temática inicial para aqueles que desejarão continuar alargando o seu conhecimento sobre o assunto.
Por fim, há alguns códigos que escrevi para este livro e que disponibilizei no notebook que está no repositório do GitHub. É bem possível que no futuro mais material ainda seja push
(ado) para lá.
Tenha uma boa leitura!
- Prefácio
- Introdução
- 1. Vamos iniciar com uma brincadeira?
- 2. A Descrição Básica
- 3. O Aprendizado Artificial
- 4. Múltiplas Camadas
- 4.1. A Propagação de um Sinal x Através das Camadas da Rede
- 4.2. Um Perceptron de 2 Camadas
- 4.2.1. A Equação de um Perceptron de 2 Camadas
- 4.2.2. A Função de Erro de Um Perceptron de 2 Camadas
- 4.2.3. As Taxas de Variação do Erro
- 4.2.4. Derivada do Erro com Relação aos Pesos da Camada 2
- 4.2.5. Derivada do Erro com Relação aos Bias da Camada 2
- 4.2.6. Derivada do Erro com Relação aos Pesos da Camada 1
- 4.2.7. Derivada do Erro com Relação aos Bias da Camada 1
- 4.3. Um Perceptron de Múltiplas Camadas
- 4.4. A Função de Erro de um Perceptron de Múltiplas Camadas
- 4.5. A Derivada do Erro com Relação aos Pesos de uma Camada Qualquer
- 4.6. Processo Prático Para a Atualização dos Pesos e Bias
- 4.7. Analisando a Dimensão das Matrizes de \( \frac{\partial E}{\partial W^l}\) e de \( \frac{\partial E}{\partial b^l}\)
- 4.8. A Atualização dos Pesos e Bias
- 5. Treino em Lotes
- Apêndice A: Norma Sobre Um Espaço Vetorial
- Apêndice B: As Derivadas de \( y_i\)
- Apêndice C: Derivada de Funções Vetoriais
- Apêndice D: Algumas Observações sobre o Gradiente
- Apêndice E: Produto Externo
- Apêndice F: Aprendizado Contínuo
- Apêndice G: A Função de Custo sobre um Domínio Matricial é uma Norma
- Apêndice H: Tópicos Fundamentais em Aprendizado de Redes Neurais Não Tratados Neste Livro
- H.1. Inicialização de Pesos
- H.2. Normalização de Dados
- H.3. Taxa de Aprendizado e Otimizadores
- H.4. Regularização
- H.5. Arquiteturas de Rede
- H.6. Engenharia de Dados e Pré-processamento
- H.7. Técnicas de Treinamento
- H.8. Curvas de Aprendizado e Avaliação
- H.9. Transferência de Aprendizado (Transfer Learning)
Introdução
O Perceptron é, provavelmente, a mais básica de todas as arquiteturas de redes neurais. Embora possa ser utilizada sozinha em pequenos projetos e para pequenas tarefas, está presente, de um modo ou de outro, na grande maioria das peças de inteligência artificial mais conhecidas e badaladas atualmente, tais como o ChatGPT da OpenAI, ou o Transformer da Google.
Apresento a estrutura matemática mais básica do Perceptron, do seu funcionamento e, principalmente, do seu aprendizado, de forma rápida e direta-ao-ponto. O que abordo de teoria, aqui, é apenas o indispensável à apresentação, sem rodeios, desta peça fundamental da inteligência artificial atualmente. Assim, este livro não aborda a história do Perceptron, nem dá qualquer tratamento estatístico ou analítico geral aos objetos apresentados, nem aborda a teoria de matrizes ou conceitos de Álgebra Linear explicitamente, etc.
Algumas poucas demonstrações são feitas nos Apêndices, para o leitor interessado, mas só porque elas ajudam a esclarecer pontos importantes e basilares daquilo que este autor acredita ser o mais importante deste assunto inteiro: como é possível a uma rede neural aprender.
Assim, este livro foi escrito mais para exibir e operar com as fórmulas básicas, que trazem o entendimento primário e sólido do assunto, do que para prová-las ou demonstrá-las rigorosamente. Minha intenção primeira é apresentar e descrever, com boa clareza, aquilo que é o mais básico, matematicamente falando, a fim de que este primeiro contato dirija o leitor interessado a uma compreensão firme sobre o assunto e útil como base para leituras complementares ou mais avançadas, posteriormente.
As redes neurais e o Perceptron, em particular, são invenções humanas que foram baseadas no que se entendia sobre o funcionamento do neurônio. É um modelo que espelha longinquamente o funcionamento de um objeto natural vivo. Por mais distante que esteja o modelo do Perceptron daquilo que se conhece sobre a altíssima complexidade de um neurônio real, este modelo, não obstante, é de um sucesso grandioso.
Sendo uma invenção humana, a sua modelagem matemática mais fundamental é elegante e bela na medida mesma em que é demasiadamente simplista. O leitor provavelmente terá esta impressão em várias partes do livro, principalmente no primeiro capítulo e naquelas partes que tratam especificante da sua estrutura.
O conteúdo apresentado neste livro, principalmente, o que é exposto a partir do capítulo 2, foi criado com a intenção de colocar o leitor em condições de utilizar ou adequar facilmente os mesmos conceitos para o estudo de outras arquiteturas de redes neurais e de deep learning.
As modernas linguagens e frameworks tais como Tensorflow, baseiam-se e exploram o conceito de vetores e matrizes. Talvez, um dos méritos deste livro seja o de mostrar, explicitamente, a natureza matricial das equações que descrevem o Perceptron e, principalmente, daquelas que descrevem o seu aprendizado.
Por experiência, sabemos que para se construir redes neurais utilizando-se de ferramentas como o Tensorflow, não é imprescindível a um programador ter o conhecimento mais em profundidade que apresento aqui, pela simples razão de que ele está embutido como peça chave e, de certo modo, escondido debaixo de uma interface de altíssimo nível, intuitiva e simples de usar. Mas, a adequada, explícita e clara representação da natureza diferencial e matricial subjacente à aprendizagem artificial dará ao leitor a compreensão exata da beleza e do poder normalmente ocultos ao grande público maravilhado com os resultados luminosos advindos da aplicação de tais conhecimentos.
1. Vamos iniciar com uma brincadeira?
Vamos começar este livro com uma brincadeira!
Uma que você vai lembrar pelo resto da leitura deste livro. Talvez, pelo resto da vida.
Vamos "transformar" uma sequência de números no número \( \pi=3.14\). Isto mesmo que você leu: vamos transformar!
"Mas qual sequência de números?", você poderia perguntar. Qualquer uma serve, eu responderia! Você escolhe a sua!
A minha, vai ser a 1, 2, 3, 4!
Mas, eu poderia ter escolhido qualquer outra, como 3, 2, 1, 0, -1, -2; ou \(\frac{1}{2}, \frac{3}{4}, 100, -\frac{1256}{100}, 8^3, 0.67,\sqrt{\frac{1}{6}}\), não importando quais números estão na sequência ou quantos!
Mas, vamos precisar de uma outra sequencia de números! A que vai aprender a transformar [1, 2, 3, 4] no número único 3.14! Sim, precisamos de uma outra sequência! E, esta segunda sequencia é a sequencia mais importante.
Esta outra também pode começar com quaisquer números, mas ajuda muito se ela tiver, inicialmente, apenas números pequenos próximos de zero! E, esta outra sequência deve ter o mesmo número de elementos que a primeira.
Eu escolhi a seguinte: [0.9, 1.5, -0.1, 0.3].
Não há como frisar demais a importância desta sequência! Ela guardará o aprendizado responsável por transformar [1, 2, 3, 4] em 3.14.
Os quatro valores iniciais, que vemos no vetor de aprendizado, ainda não são tão importantes, mas os quatro valores que obtivermos, no fim, quando acabarmos de brincar, eles sim, são os mais importantes!
Pra começar, vamos combinar [1, 2, 3, 4] com [0.9, 1.5, -0.1, 0.3]! Isto mesmo! Vamos combinar! E, combinar linearmente! Ou seja, nós vamos encarar estas sequências como se fossem vetores e vamos multiplicar um vetor pelo outro, e ver no que dá:
Agora, 4.8 ainda não é 3.14 e nem próximo o bastante de 3.14!
Então, temos que fazer algo a respeito!
A versão inicial da nossa sequência de aprendizado tem um número negativo. Ele pode ser visto na primeira linha de 1. E se mexêssemos neste número de modo a obtermos, no final, um valor menor que 4.8 e mais próximo de 3.14?!?
O que poderíamos fazer com -0.1 de modo a transformar os 4.8 em um valor mais próximo de 3.14?
Veja que para se chegar em 3.14, a partir de 4.8, poderíamos fazer 4.8 - 1.66! Mas como mexer nos valores do vetor [0.9, 1.5, -0.1, 0.3] de modo a obtermos a diferença, -1.66, que precisamos, no valor final?
Vamos "chutar", como se diz, e se não der certo ainda, nós ajustamos mais tarde!
Então, vamos fazer o seguinte: vamos alterar -0.1 para -0.3; e vamos alterar também o 1.5 para 0.97.
Assim, nosso vetor de aprendizado inicial já está aprendendo (ou tentando!), pois ele foi de [0.9, 1.5, -0.1, 0.3] para [0.9, 0.97, -0.3, 0.3]!
E, vejam só!
Veja que alteramos apenas dois dos quatro números em nosso vetor inicial de aprendizagem. Mas, poderíamos ter alterado a todos!
O que exatamente fizemos àqueles dois valores do vetor original? Nós subtraímos 0.53 do 1.5 para obtermos 0.97 na segunda posição, e somamos -0.2 ao -0.1 para obtermos -0.3 na terceira posição. Achei o -0.53 e o -0.2 em repetidas tentativas. Observei o efeito que cada tentativa tinha em aproximar ou distanciar do resultado desejado, até encontrar valores cada vez mais próximos de 3.14!
Neste pequeno exemplo, acabamos de usar nossas inteligências reais para fazer algo parecido com o que modelos de inteligência artificial fazem corriqueiramente quando estão aprendendo!
Elas fazem pequenos ajustes em uma multidão de números distribuídos em muitos vetores. Estes vetores são, normalmente, muito grandes e os ajustes são feitos muitas e muitas vezes de modo que, a cada vez, as pequenas mudanças contribuam para levar o modelo inteiro a uma resposta mais próxima da resposta que se deseja que o modelo produza.
Este livro é sobre o modo belo, engenhoso e preciso em que estes tais ajustes são calculados e aplicados!
Veja que poderíamos ter feito ajustes em vários vetores ao mesmo tempo, cada um produzindo um resultado próprio! As nossas inteligências naturais achariam cansativo lidar com números em vários vetores a um nível de detalhe ínfimo e enfadonho! Mas, é exatamente o que os modelos de inteligência artificial podem fazer por nós!
Na verdade, estes modelos, também chamados de redes neurais artificiais, fazem muito mais do que só aproximar números. Eles são capazes de aproximar curvas e superfícies e, de um modo geral, eles podem aproximar ou mapear conjuntos de dados de natureza complicada e que descrevem, entre outras coisas, características da inteligência humana, como visão, audição ou fala.
Mencionei o conceito de mapeamento acima. Em nossa brincadeira, nós criamos um mapeamento! Um muito simples, mas, ainda assim, funcional. O mapeamento que criamos, usa o aprendizado que armazenamos no vetor [0.9, 0.97, -0.3, 0.3] para criar uma relação funcional entre um vetor, o [1,2,3,4] e o número 3.14, de modo que podemos simbolizar esta relação funcional, y=f(x), assim: \( f(x)=[0.9, 0.97, -0.3, 0.3] \cdot x\), de modo que \( f([1,2,3,4])=[0.9, 0.97, -0.3, 0.3] \cdot [1,2,3,4]=3.14\)!
Prepare-se, pois no restante do livro veremos muito mais sobre estes mapeamentos fabulosos e sobre como aplicar neles processos de otimização matemática automáticos que tornarão o aprendizado artificial uma verdadeira brincadeira de crianças.
2. A Descrição Básica
Entre as redes neurais artificiais, o perceptron é a mais simples.
O Perceptron é, digamos assim, o tijolo de construção da maioria dos modelos de redes neurais.
Sua estrutura é simples e fácil de entender.
Matematicamente, o Perceptron é uma equação que aprende.
Mas, o que a equação do Perceptron aprende? Qualquer coisa! Ela aprende a dar as respostas que desejemos que ela dê aos elementos de um conjunto qualquer de dados ou informações. Deste ponto de vista, o Perceptron aprende a criar um relacionamento matemático, ponto-a-ponto, entre um conjunto, \( D\), de dados e um outro conjunto, \( Z\), de respostas desejadas que assume a forma funcional \( P: D\longrightarrow Z\) e age como a equação \( P(d)=z\) entre pontos específicos.
2.1. Pesos e viéses ou Parâmetros Treináveis
Ele está baseado em uma matriz, \( W\), de parâmetros treináveis e também num vetor, \( b\), de outros parâmetros treináveis.
e
Estes parâmetros são ditos treináveis, pois eles se alteram durante o treino do perceptron, acumulando misteriosamente o aprendizado da rede, até atingirem um valor ótimo, quando a rede está pronta para desempenhar a tarefa para a qual foi criada.
Os elementos da matriz \( W\) são os pesos do perceptron, enquanto que o vetor \( b\) é o bias ou, em português, o viés. Os elementos de \( b\) são os viéses de cada neurônio.
Para colocar de forma muito simples e direta, o número de linhas de \( W\) é o número de neurônios do Perceptron e o número de colunas é o número de pesos de cada neurônio. Todos os neurônios (da mesma camada) têm o mesmo número de pesos.
Me referi ao perceptron como tijolo de construção da maioria das redes neurais. As redes neurais são construídas com estruturas básicas, chamadas camadas, e o Perceptron é esta camada numa grande maioria das arquiteturas de rede. Além disto, o Perceptron, ele próprio, pode ter camadas como veremos no Capítulo 3.
Veja que se o Perceptron fosse constituído de um único neurônio, então, a matriz, \( W\), representando esta camada seria, de fato, um vetor-linha! Ou seja, \( W\) seria a matriz \( 1\times m\):
Só para dar um exemplo, por incrível que possa parecer, mesmo projetos de Deep Learning, com arquiteturas mais complexas e profundas, mas que realizem classificação binária, provavelmente, estão utilizando um Perceptron com uma única linha de pesos como sua última camada. Este seria o caso de uma rede cujo objetivo fosse ler radiografias ósseas e dizer se elas indicam fraturas ou não. Uma tal rede, bem desenvolvida, poderia ser treinada para identificar fissuras ou fraturas que fossem difíceis de constatar pelo olho humano.
2.2. A Forma Geral do Perceptron
Um perceptron, assim como qualquer outra rede neural, é criado para desempenhar uma tarefa. Ele deve aprender a desempenhar a sua tarefa. Ele deve produzir um resultado, \( z\), condizente com cada elemento, \( x\), ambos em um conjunto, \( D\), de dados ou informações. Dizemos que \( D\) é um conjunto de vetores ou de tensores e que o perceptron é treinado sobre este conjunto. Falaremos mais sobre \( D\) quando chegarmos à Seção O Treino.
Se \( x\in D\) é um dos vetores de treino, então, a resposta, \( y\), do perceptron a este vetor é
O vetor \( x\) é uma das entradas do perceptron e \( y\) é a saída correspondente.
Qualquer das expressões em (6) são, às vezes, chamadas, simplesmente, de linearidade do perceptron.
2.3. Duas Representações Alternativas
Abaixo, apresento, rapidamente, duas formas alternativas em que o Perceptron pode ser considerado. O leitor poderá encontrá-las em outros livros, e saber que elas existem poderá alargar a sua capacidade de manipular as ferramentas matemáticas que o descrevem e o modelam. Eu as coloco aqui, de passagem, mas neste livro não as utilizaremos.
2.3.1. A forma \(\large xW\) do Produto Matricial
Note o leitor que a primeira equação de 6 poderia ter sido escrita com o vetor \( x\) multiplicando a matrix \( W\) desde a esquerda, assim
caso em que o vetor \( x^T\) seria um vetor-linha, as colunas de \( W^T\) seriam os neurônios do Perceptron, enquanto que o número de linhas de \( W^T\) seria o número de pesos em cada nerônio.
2.3.2. Pesos e Bias em uma Mesma Matriz
Podemos embutir o vetor de bias, de cada camada, em sua respectiva matriz de pesos, de modo que ele será a última coluna destas matrizes. Esta possibilidade já está dada nas equações do Perceptron. Considere, por exemplo, a terceira equação em 6 e repare que ela pode ser reescrita da seguinte forma
com \( b_i=w_{i(m+1)}\) e com \( x_{m+1}=1\).
Agora, note que a segunda equação em 8 é o mesmo que
Deste modo, só precisaríamos embutir a unidade escalar como a última posição de cada vetor entrante, \( x\), de modo que agora ele passe a ter \( m+1\) elementos.
Deste modo, a equação acima é simplesmente
2.4. Funções de Ativação
É extremamente comum entregar cada um dos valores de \( y\) a uma mesma função de ativação. Esta função de ativação também é conhecida como "não-linearidade", por "quebrar", de algum modo, o comportamento linear que é produzido em 6. Ela pode assumir uma de várias formas comumente utilizadas. No momento, vamos apenas designá-las por um símbolo: \( a\). Assim, em sua forma mais geral, o perceptron de uma camada é
Esta última expressão pode parecer um pouco confusa, no momento, talvez por causa das expressões que estão dentro dos colchetes, mas não estranhe. Ela exibe os símbolos matemáticos que espelham a estrutura conceitual de um Perceptron de uma camada. Ela também mostra como o vetor \( x\) é "absorvido" e processado pela rede. Os vetores \( W_i\) são as linhas de \( W\) e os escalares \( b_i\) são elementos de \( b\). Veja como o vetor \( x\) é processado por cada uma das linhas de \( W\). A equação mostra como o sinal \( x\) "flui" através do perceptron até ser transformado na sua resposta, \( P\).
Procure fixar bem o fato de que \( a\) é um vetor e que seus elementos são funções cujas variáveis independentes são, respectivamente, os elementos do vetor \( y\). Na Subseção, abaixo, coloquei uma tabela com algumas funções de ativação bem conhecidas e utilizadas.
2.4.1. Algumas Funções de Ativação
A tabela, abaixo, exibe algumas das funções de ativação mais conhecidas. Elas são mostradas com a notação que utilizamos durante todo o livro, revelando a sua natureza de função real de domínio real, com exceção da Função Softmax. A função Softmax utiliza todas as componentes de um vetor de linearidades para gerar um percentual relativo à \( i\)-ésima componente.
Nome | Fórmula |
---|---|
Sigmoid |
\( a_i(y_i)=\frac{1}{1+e^{-y_i}}\) |
Tangente Hiperbólica |
\( a_i(y_i)=\tanh(y_i)\) |
Softmax |
\( a_i(y) = \frac{e^{y_i}}{\sum_{k=1}^{n}e^{y_k}}\) |
ReLU |
\( a_i(y_i) = \max\{0,y_i\}\) |

2.5. O Treino
Já dissemos que um perceptron deve aprender a realizar uma certa tarefa e que ele é treinado sobre um conjunto de dados ou informações, \( D\). Este conjunto pode ser considerado um conjunto de duplas ordenadas, \( (x,z)\in D\), de modo que \( z\in Z\subset D\) e \( x\in X\subset D\) são, respectivamente, o conjunto das respostas desejadas e o conjunto dos vetores que representem um domínio qualquer de coisas que nos interesse relacionar com as respostas desejadas.
Nós já vimos que \( x\) é um vetor. A segunda componente da dupla ordenada, \( (x,z)\), é a resposta que desejamos que o perceptron aprenda a dar ao vetor de entrada \( x\). A ordenada \( z\) pode ser um número escalar, um vetor, uma matriz, ou um tensor. Isto vai depender de como codificamos a tarefa, matematicamente. Neste livro, as nossas respostas desejadas, \( z\), serão apenas escalares ou vetores.
Inicialmente, o perceptron dá uma resposta qualquer, \( P\), ao vetor \( x\). Ao longo do treino, esta resposta vai se aproximando da resposta correta ou desejada, \( z\). Verificamos esta aproximação com uma Função de Custo, que indicaremos com o símbolo \( E\), (veremos mais sobre ela na Seção A Função de Custo), a qual nos diz quão longe ou quão perto \( P(x)\) está da resposta desejada \( z\). Ao longo do treino, o valor da Função de Custo vai diminuindo pois \( P(x)\) vai se tornando cada vez mais próxima de \( z\). O nosso objetivo durante um treino é fazer com que o erro vá para zero, ou seja, que a igualdade \( P(x)=z\) seja verdadeira, ou quase verdadeira, pois, normalmente um erro suficientemente pequeno já é o bastante.
Sob certo ponto de vista, este livro inteiro é sobre como reintegrar no Perceptron, durante o treino, a informação contida no erro, \( E\), de modo a fazer o Perceptron acertar cada vez mais em sua tarefa. Isto é, criar o relacionamento que desejamos, \( P(x_i)=z_i\), entre
Há várias funções que podem ser utilizadas como função de custo ou de perda. A escolha depende do projeto e, às vezes, do gosto de quem treine a rede. Na Seção Algumas Funções de Custo, há uma tabela com algumas funções de custo úteis e comumente utilizadas. O importante a saber é que a Função de Custo, seja ela qual for, deve cumprir com a definição matemática de norma, assunto no qual não entraremos neste livro, mas cuja definição pode ser encontrada pelo leitor interessado no Apêndice Norma Sobre Um Espaço Vetorial, junto com uma pequena demonstração de que se uma norma é nula, então o seu argumento também o é.
Veremos na Seção Atualizando os Parâmetros Treináveis que o processo que utilizaremos para aproximar \( P(x)\) e \( z\) baseia-se no gradiente da Função de Erro. Este processo, chamado de Descida do Gradiente ou Descida Estocástica do Gradiente, indica gradualmente a direção do menor valor de \( E\) e, deste modo, também indica o caminho de um menor valor de separação entre \( P\) e \( z\).
Podemos dizer que o Perceptron é um mapeamento que aprende a dar uma resposta \( z\) adequada a cada \( x\) num processo de aprendizado que é realizado durante uma ou várias sessões de treino. Normalmente, várias! Numa sessão de treino, o Perceptron recebe todos os elementos \( x\) de \( D\), um após o outro e, para cada \( x\), calcula-se o \( P(x)\) correspondente. Após isto, calcula-se a função de erro sobre \( P(x)\) e \( z\), de modo que podemos expressa-la assim: \( E(P,z)\). Realiza-se tantas sessões de treino quantas forem necessárias para fazer \( E(P,z)\) suficientemente próxima de zero. Esta é a razão para se exigir que a Função de Erro ou de Custo seja uma norma, pois aí, quando \( E(P,z)\longrightarrow 0\), também será verdade que \( P-z\longrightarrow 0\), ou seja, a resposta da rede estará se tornando igual à resposta desejada.
3. O Aprendizado Artificial
3.1. Otimização
O aprendizado artificial é um processo de otimização.
O que é otimizado no aprendizado artificial? Uma função que normalmente é chamada de Função de Custo ou de Perda, ou, ainda, Função de Erro! Eu, pessoalmente, a chamo, neste contexto de aprendizado artificial, de Função Pedagógica, uma vez que ela mede o quão distante está a resposta do Perceptron da resposta desejada e, por este meio, sabemos se a rede está aprendendo ou não. É a partir da derivação da Função de Perda que o aprendizado acontece.
Qualquer um que já tenha derivado uma função para, logo em seguida, encontrar o seu máximo ou o seu mínimo, está em perfeitas condições de saber como o aprendizado artificial acontece.
O aprendizado ocorre em uma ou mais sessões de treino, em que os parâmetros treináveis do Perceptron são repetidamente atualizados (Ver Seção O Treino). Estes parâmetros são atualizados a cada passo do treino, ou seja, depois que cada lote de treino é apresentado à rede. Veremos mais sobre o treino em lotes na Seção Treinando Em Lotes.
A descrição de como o aprendizado artificial acontece é a parte mais importante e interessante das redes neurais na opinião deste autor. Sem isto, não há aprendizado de máquina.
3.2. A Função de Custo
Quando vamos à escola, o nosso aprendizado é medido por avaliações. O aprendizado das redes neurais também é aferido por avaliações de desempenho.
A escola do Perceptron é a sessão de treino.
Assim como a nota escolar final é obtida a partir de uma fórmula, as redes neurais também se utilizam de fórmulas que “dão nota” ao seu desempenho.
No caso das redes neurais, tais fórmulas são conhecidas como funções de custo ou de perda ou, ainda, de erro. Neste livro, me refiro a elas muito mais como funções de erro. Elas, de fato, medem o erro cometido pela rede ao tentar prever uma resposta a uma entrada correspondente.
A função de erro pode ter várias formas, mas não abordarei nenhuma forma específica agora, pois estamos interessados no encaixe que a sua forma geral tem nas fórmulas do aprendizado. Neste momento, vamos apenas simbolizar uma função de erro qualquer com a letra \( E\). Na Subseção Algumas Funções de Custo, logo abaixo, é possível encontrar uma tabela com algumas funções de custo.
Há muito o que dizer sobre \( E\) mas, por ora, vamos nos ater aos aspectos operacionais que tornam possível ao perceptron aprender.
A função \( E\) toma como argumento a saída do Perceptron. Então,
Ma, veja, a saída do perceptron depende dos seus parâmetros treináveis, ou seja, \( E\) também depende dos pesos e bias. Então, é mais comum escrevermos
Esta notação é muito útil, pois o aprendizado do perceptron depende da derivada de \( E\) com relação aos seus pesos e bias.
Aqui, precisamos considerar a estrutura compositiva da função de erro,
e, manter em mente que \( a\) e \( y\) são vetores, os dados em 6 e 11, e que \( E\) é uma função real.
3.2.1. Algumas Funções de Custo
Abaixo, estão algumas das Funções de Custo (de domínio vetorial) mais conhecidas.
Nome | Fórmula |
---|---|
Erro Quadrado Médio |
\( E=\frac{1}{n}\sum_{i=1}^n(a_i-z_i)^2\) |
Erro Médio Absoluto |
\( E=\frac{1}{n}\sum_{i=1}^n \begin{vmatrix}a_i-z_i\end{vmatrix}\) |
Entropia Cruzada |
\( E=-\frac{1}{n}\sum_{i=1}^n z_i\cdot \log a_i\) |
Entropia Cruzada Binária |
\( E=-\frac{1}{n}\sum_{i=1}^n \begin{bmatrix}z_i\cdot \log a_i+(1-z_i)\cdot \log (1-a_i)\end{bmatrix}\) |
3.3. Gradiente do Erro com Relação a W
Vamos calcular a derivada da função de erro com relação aos pesos do Perceptron. Esta derivada também é conhecida como o gradiente do Erro.
Sabemos que \(\frac{\partial E}{\partial a}\) é o gradiente de \( E\) com relação ao vetor de ativações \( a\), pois \( E\) é uma função real com domínio vetorial. Assim, \(\frac{\partial E}{\partial a}=\nabla_a E\).
A derivada \(\frac{\partial a}{\partial y}\) gera uma matriz. Isto vem do fato de que tanto \( a\) como \( y\) sejam funções vetoriais. Veja o apêndice Derivada de Funções Vetoriais para mais detalhes sobre derivadas de funções vetoriais.
Por esta razão, as derivadas \(\frac{da_i}{d y_i}\) ainda não podem ser calculadas ou completamente reduzidas. Isto acontecerá quando estivermos tratando de exemplos ou de arquiteturas específicas.
Se analisarmos a equação 11, veremos que cada \( a_i\) depende apenas de \( y_i\). Portanto, devemos ter \(\frac{d a_i}{d y_j}=0\) se \( i \ne j\). Portanto, \(\frac{\partial a}{\partial y}\) será uma matriz diagonal. Os elementos, \(\frac{da_i}{d y_i}\), desta matriz diagonal dependerão da forma específica de \( a\).
A diferencial \(\frac{\partial y}{\partial W}\) tem a forma de um vetor-coluna de \( n\) elementos. Só que estes elementos, por sua vez, são matrizes \( n\times m\).
Agora, veja que cada elemento, \( i\), do vetor-coluna que está no segundo membro de 16 é a derivada de uma função real cujos argumentos coincidem apenas com a linha \( i\) de \( W\). Estas funções reais estão definidas na equação 6, de onde sabemos que \( y_i(W_i)=\sum_{j=1}^{m} w_{ij}x_j +b_i\) (Veja o Apêndice As Derivadas de \( y_i\) para relembrar o procedimento de derivação desta equação). Portanto, os elementos do vetor-coluna são matrizes com entradas nulas, com a única exceção de sua linha \( i\).
Vetores-coluna ou vetores-linha de matrizes aparecerão muitas vezes nesta apresentação. Isto decorre do fato de estarmos derivando o erro, \( E\), com relação à matriz inteira, de pesos de uma só vez.
Deste modo, a fórmula 15, que calcula a derivada parcial do erro \( E\) do perceptron com relação aos seus pesos, \( W\), é
3.4. Gradiente do Erro com Relação ao bias b
Nós, agora, precisamos calcular a derivada de E com relação ao vetor de viéses, \( b\).
De 6, nós vemos que os viéses estão entranhados no nível mais profundo do perceptron, juntamente com os pesos.
Antes, consideramos o erro como função apenas dos pesos. Agora, vamos considerá-los como função apenas dos viéses ou bias.
Pelos cálculos que já fizemos na seção anterior, podemos muito simplesmente escrever.
Se necessário, veja o Apêndice As Derivadas de \( y_i\) para mais considerações sobre o cálculo de \(\frac{\partial y}{\partial b}\).
3.5. Algumas Funções de Custo e de Ativação e suas Derivadas
Nome | Fórmula | Derivada \(\large\left(\frac{d a_i}{d y_i}\right)\) |
---|---|---|
Sigmoid |
\( a_i(y_i)=\frac{1}{1+e^{-y_i}}\) |
\(\begin{aligned} &\frac{e^{-y_i}}{(1+e^{-y_i})^2}\\ &\text{ou}\\ &a_i(1-a_i) \end{aligned}\) |
Tangente Hiperbólica |
\( a_i(y_i)=\tanh(y_i)\) |
\(\begin{aligned} &1-tanh^2 y_i\\ &\text{ou}\\ &1-a_i^2 \end{aligned}\) |
Softmax |
\( a_i(y) = \frac{e^{y_i}}{\sum_{i=1}^{n}e^{y_i}}\) |
\(\begin{aligned} &\frac{e^{y_i}}{\sum_{j=1}^{n}e^{y_j}} \left( 1- \frac{e^{y_i}}{\sum_{j=1}^{n}e^{y_j}} \right )\\ &\text{ou}\\ &a_i(1-a_i) \end{aligned}\) |
ReLU |
\( a_i(y_i) = \max\{0,y_i\}\) |
\(\max\{0,1\}\) |
Nome | Fórmula | Derivada \(\left(\frac{d E}{d a_i}\right)\) |
---|---|---|
Erro Quadrado Médio |
\( E=\frac{1}{n}\sum_{i=1}^n(z_i-a_i)^2\) |
\( -\frac{2(z_i-a_i)}{n}\) |
Erro Médio Absoluto |
\( E=\frac{1}{n}\sum_{i=1}^n \begin{vmatrix}a_i-z_i\end{vmatrix}\) |
\(\begin{equation*} \begin{aligned} &\frac{1}{n} \frac{a_i-z_i}{\begin{vmatrix}a_i-z_i\end{vmatrix}}\\ &\text{ou}\\ &\frac{1}{n} \begin{cases} 1 & \text{se}\ \ a_i>z_i \\ -1 & \text{se}\ \ a_i<z_i \\ \nexists & \text{se }\ \ a_i=z_i \end{cases} \end{aligned} \end{equation*}\) |
Entropia Cruzada |
\( E=-\frac{1}{n}\sum_{i=1}^n z_i\cdot \log a_i\) |
\( -\frac{z_i}{na_i}\) |
Entropia Cruzada Binária |
\( E=-\frac{1}{n}\sum_{i=1}^n \begin{bmatrix}z_i\cdot \log a_i +(1-z_i)\cdot \log (1-a_i)\end{bmatrix}\) |
\(\frac{a_i-z_i}{a_i(1-a_i)}\) |
3.6. Atualizando os Parâmetros Treináveis
Por fim, chegamos aonde queríamos: \( \frac{\partial E}{\partial W}\) e \( \frac{\partial E}{\partial b}\) serão utilizados para atualizar os pesos e bias do perceptron. O processo em que isto é feito é chamado de retro-propagação e se baseia na técnica da Descida Estocástica do Gradiente. Esta técnica se baseia, por sua vez, no fato de que \( \frac{\partial E}{\partial W}\) é um vetor gradiente e que, portanto, sempre se orienta na direção da maior taxa de variação de \( E\). Portanto, o seu negativo, \( -\frac{\partial E}{\partial W}\), (ver nas fórmulas 21 e 23) apontará na direção da menor variação. Não entrarei em maiores detalhes sobre este ponto aqui, mas o leitor interessado poderá ler algumas outras poucas observações interessantes e pertinentes ao uso que fazemos, no Apêndice Algumas Observações sobre o Gradiente. Apenas adiantarei que o mencionado sinal negativo é muito importante. Se você já codificou esta fórmula, digamos em Python ou em Tensorflow ou qualquer outra linguagem ou framework, para treinar um Perceptron, mas, por engano, usou um sinal positivo no lugar do negativo, pode constatar que o erro cometido pelo Perceptron de fato só aumenta ao invés de diminuir!
No momento, já temos tudo o que é necessário à apresentação da fórmula que permite que o aprendizado aconteça. Esta fórmula tem tal simplicidade e beleza, que só é igualada por seu poder de tornar possível o aprendizado do Perceptron.
Durante uma sessão de treino, os pesos do Perceptron são atualizados muitas vezes. Cada atualização acontece em um momento, \( t\), do treino. À medida que o treino evolui, os pesos vão sendo alterados na busca de um melhor desempenho, ou seja, na busca de um custo, \( E\), menor. Em um dado momento, \( t\), do treino, o perceptron tem a matriz \( W_t\) de pesos que sofre a adição de \( \Delta W_t\) cujo resultado passa a ser a nova matriz atual de pesos do Perceptron, \( W_{t+1}\).
em que
O símbolo \( \eta\) é chamado de taxa de aprendizagem. É um parâmetro cuja importância está em ditar a cadência do treino, pois com ele é possível ajustar a “velocidade” do treino. No entanto, é difícil saber qual é a velocidade ótima para cada passo do treino de uma rede neural, embora haja orientações gerais e métodos úteis de cálculo, sobre os quais não falaremos neste momento. É um parâmetro de manejo algo delicado como, aliás, são também outros parâmetros definidores de redes neurais. Na prática, valores pequenos como \( \eta=0.01\) ou \( \eta=0.001\) são sempre utilizados como uma primeira alternativa. Outras abordagens alteram o valor de \( \eta\) ao longo do treino de modo que o seu valor vai diminuindo à medida em que o treino avança.
A atualização do bias é feita com fórmulas bem similares as da atualização dos pesos:
em que
4. Múltiplas Camadas
Um Perceptron pode ter mais de uma camada e, geralmente, tem, principalmente, em modelos de Deep Learning.
É possível "empilhar" camadas! Isto é feito para melhorar a aprendizagem.
Quanto mais um perceptron associa corretamente os dados de treino, \( x\), com a sua correspondente resposta desejada, \( z\), melhor ele está aprendendo. O aumento estratégico do número de pesos da rede, através do aumento do número de camadas, pode melhorar o desempenho do treino, ou seja, \( E\) diminui, o que se traduz como melhora do aprendizado, isto é, mais pares \( (x,z)\in D\) são corretamente associados pelo perceptron.
4.1. A Propagação de um Sinal x Através das Camadas da Rede
Um sinal de entrada, \( x\), "fluirá" através das camadas da rede, entrando na primeira camada, passando por cada uma delas até sair através das funções de ativação da última camada.
Já vimos que um perceptron de uma única camada se define por seus pesos e bias, então, podemos encará-lo como sendo o objeto
Vamos representar o empilhamento de camadas, ou seja, a justaposição de vários perceptrons de uma camada só, simplesmente, assim:
em que
Os sobrescritos em 26 indicam o número da camada a qual pertencem os pesos e bias.
4.2. Um Perceptron de 2 Camadas
Vamos, por ora, considerar um perceptron de duas camadas, \( P=P_1\rightarrow P_2\). O perceptron 1 tem \( n\) neurônios, enquanto que o perceptron 2 terá \( p\) neurônios. Consideraremos um vetor de entrada, \( x\), com \( m\) elementos.
O perceptron \( P_1\) receberá o sinal \( x\), mas \( P_2\) receberá a saída de \( P_1\), isto é, as funções de ativação, \( a^1\), de \( P_1\).
A saída de \( P_2\) é entregue à função de erro. Ou seja, as funções de ativação, \( a^2\), de \( P_2\) são os argumentos da Função de Erro.
4.2.1. A Equação de um Perceptron de 2 Camadas
Escrevamos uma versão simplificada para a equação de \( P\). Utilizarei o mesmo símbolo \( P\), como em 11, para designar a saída da rede. Para maior clareza, utilizaremos o símbolo, \( \circ\), que é às vezes utilizado na representação da composição de funções.
As expressões de 27 exibem detalhes da estrutura compositiva de \( P\). Continuando,
As expressões em 28 são a continuação do desenvolvimento iniciado em 27 e elas mostram como o sinal de entrada, \( x\), é absorvido na linearidade, \( y^1\) e como, posteriormente, esta linearidade é absorvida pelo vetor de ativações de \( P_1\). Note, na primeira e última linhas, como \( y^1\) e \( a^1\) são vetores-coluna.
Já, as expressões em 29 mostram como as ativações, \( a^1\), da primeira camada entram na linearidade, \( y^2\), da camada 2.
4.2.2. A Função de Erro de Um Perceptron de 2 Camadas
Assim, escrevamos a função de erro de \( P\), explicitando a sua estrutura compositiva.
Novamente, os sobrescritos em 31 designam o número da camada a que pertencem \( W\), \( b\) ou \( a\), respectivamente. Esta equação nos dá a forma como a função do erro de \( P\) está composta.
Poderíamos expressá-la, de modo mais incompleto e menos informativo, mas mais compacto assim:
Embora, nenhuma das expressões em 32 explicite a localização e relações dos pesos e bias, elas deixam apreender, num só lance, a profundidade e a ordem da composição.
Preparando-se para Derivar a Função de Erro de Um Perceptron de 2 Camadas
A aprendizagem de um perceptron acontece através do ajuste dos seus pesos e este ajuste é feito ao final de um processo, que se repete muitas vezes, e que se inicia com o cálculo da derivada do estado atual da função de erro com relação a todos os pesos de uma rede, \(\frac{\partial E}{\partial W}\).
-
É importante, agora, frisarmos que:
-
O ajuste dos pesos acontece durante um processo chamado de retro-propagação ou propagação para trás. Quando um sinal \( x\) é apresentado à rede, \( P(x)\), ele "flui" para adiante ou para frente, através da rede, indo da primeira camada até a última. Por outro lado, quando o ajuste dos parâmetros treináveis é feito, o sinal de ajuste flui ou propaga-se para trás. Isto tem a ver com o fato de que quando derivamos, derivamos para trás. A derivação é aplicada às camadas mais externas da rede primeiro, ou seja, é aplicada às últimas camadas primeiro, e, daí, retrograde até a camada inicial. Isto ficará muito claro quando explicarmos o processo inteiro, em sua generalidade, a partir da Seção A Derivada do Erro com Relação aos Pesos de uma Camada Qualquer.
-
Queremos as derivadas de \( E\) com relação aos parâmetros treináveis, \( W=\{W^1, W^2\}\), de \( P=P_1\rightarrow P_2\) para que possamos retro-propagar o erro e realizar o aprendizado do perceptron.
-
Estes parâmetros encontram-se em profundidades diferentes dentro da rede. No nosso caso presente, \( W^2\) está na segunda ou última camada, enquanto que \( W^1\) são os pesos da primeira camada.
-
A derivação e a retro-propagação têm uma direção: elas vão da última camada para a primeira.
-
Assim, o cálculo da derivada de um perceptron de duas camadas é feito em duas partes. Primeiro calculamos \(\frac{\partial E}{\partial W^2}\) para só então calcularmos \(\frac{\partial E}{\partial W^1}\).
-
4.2.3. As Taxas de Variação do Erro
Acabamos de mencionar que há um conjunto de todos os pesos \( W=\{W^1 ,W^2\}\). O objetivo é derivar com relação a todos os pesos do Perceptron, mas, em etapas, de modo a ser possível calcular as atualizações dos pesos de \( W^2\) e depois as de \( W^1\) e, posteriormente, fazer o mesmo para os bias.
4.2.4. Derivada do Erro com Relação aos Pesos da Camada 2
Sem mais delongas, passemos à derivação de \( E(W^1,W^2,b^1,b^2)\) com relação aos pesos e bias da camada 2: \( W^2\) e \( b^2\). Os cálculos a seguir e comentários sobre os seus detalhes já foram feitos na Seção Gradiente do Erro com Relação a W. Portanto, aqui, a equação 15 é reescrita, adequando-se a sua notação a este caso de 2 camadas. Em ambos os casos, estamos lidando com a última camada da rede.
Note a diferença sutil entre 17 e a terceira linha de 33. Em 17, o vetor-coluna de matrizes mais à direita, continha as componentes de \( x\) ao longo da única linha não nula de cada matriz do vetor-coluna. Agora, a equação, na terceira linha de 33 contém, naquelas mesmas posições, os elementos do vetor de ativações, \( a^1\), da camada 1.
Note, também, que o processo de derivação \( \frac{\partial E}{\partial W^2}\) se estende apenas à camada 2, onde estão os pesos \( W^2\), entranhados na linearidade \( y^2\). Então, levando-se em conta a segunda linha de 27, podemos frisar que
Por fim, note que a última linha de 33, pode ser desenvolvida ainda mais, de modo a se obter uma forma final que não contenha aquele, desajeitado e difícil de manipular, vetor-coluna de matrizes.
Como vemos, abaixo, a forma final de \(\large \frac{\partial E}{\partial W^2}\) é bem reduzida e emprega a operação de produto externo, o que indicamos com o símbolo \(\large \otimes\).
O leitor, assim como este autor, provavelmente não acha natural ter, na última linha de 36, o vetor de derivadas de \( E\) sucedendo a matriz diagonal das derivadas de \( a^2\). Este é um pequeno preço a ser pago pela redução da forma e o aumento da facilidade de manipulação de 33. A comutação envolvida ali vem da transposição efetuada na segunda linha. Esta transposição se faz sentir na matriz diagonal e em \( \nabla_{a^2} E\), sendo que a matriz diagonal é idêntica à sua transposta.
Por um lado, as bibliotecas de manipulação de matrizes e vetores, como Numpy ou Tensorflow, proveem um método nativo para o produto externo. Por outro, a codificação de um vetor-coluna de matrizes, embora não seja difícil, pode consumir tempo em sua escrita e testes prévios de correção e bom funcionamento.
Mas, por fim, a efetivação do produto, que está dentro dos parênteses, na penúltima ou na última linhas de 36, nos leva a um vetor-coluna cujas entradas escalares são produtos de derivadas que podem ser ajeitadas de modo a exibir a ordem correta dos fatores, como se vê no vetor-coluna que está na primeira linha.
4.2.5. Derivada do Erro com Relação aos Bias da Camada 2
Agora, a derivada \( \frac{\partial E}{\partial b^2}\) tem a mesma forma que a primeira e segunda linhas de 19, com a exceção dos sobrescritos e também apenas atinge a primeira parte da rede.
4.2.6. Derivada do Erro com Relação aos Pesos da Camada 1
Agora, vamos calcular a derivada com relação a \( W^1\) e, logo em seguida, comentar sobre os seus elementos.
Já mencionamos que as linearidades \( y^1(x)\) e \( y^2(a^1)\) absorvem os seus respectivos sinais entrantes, \( x\) e \( a^1\), do mesmo modo. Isto pode ser visto claramente em 28 e 30. Elas são muito parecidas.
Mas, no cálculo de \( \frac{\partial E}{\partial W^1}\), elas acabam por ser derivadas com relação a elementos diferentes da estrutura de P. A linearidade \( y^2\) é derivada com relação às ativações da camada 1, enquanto que \( y^1\) é derivado com relação a todos os pesos, \( W^1\), de sua própria camada, 1. É assim que o objetivo de derivar \( E\) com relação a \( W_1\) é alcançado.
Por esta razão, \( \frac{\partial y^2}{\partial a^1}\) é uma matriz \( p\times n\), enquanto que \( \frac{\partial y^1}{\partial W_1}\) é um vetor-coluna com \( n\) elementos, cada um dos quais é uma matriz \( n\times m\).
Aliás, a matriz \( p\times n\), resultante do cálculo de \( \frac{\partial y^2}{\partial a^1}\), é precisamente a matriz de pesos \( W^2\). Isto pode ser visto em 40.
Mais uma vez, podemos simplificar a expressão final da derivada de \( E\). Consideremos o seguinte desenvolvimento a partir da penúltima linha de 40.
Tal desenvolvimento também levará a uma forma envolvendo um produto externo com o sinal entrante que, neste caso, é \( x\). Então, o cálculo pode continuar como feito abaixo, em 42.
Fazendo a soma indicada, obtemos
Note que na passagem da segunda para a terceira linhas de 43, reconhecemos que a expressão que foi transposta é a mesmíssima que aparece na terceira linha! Daí bastou "desempacotar" os fatores já conhecidos. No momento, a deixaremos como está. Mas, logo veremos que esta expressão pode ser ainda mais trabalhada e que ela fará parte da metodologia recursiva que utilizaremos para calcular as taxas de variação do Erro em Perceptrons de várias camadas.
Nós veremos que, em perceptrons de mais de 2 camadas, o padrão \( \frac{\partial a^{l+1}}{\partial y^{l+1}}\cdot W^{l+1}\cdot \frac{\partial a^l}{\partial y^l}\), em que \( l\) é o número de uma camada, se repete. Sempre haverá \( L\) repetições deste padrão, encaixadas entre o \( \nabla_{a^L} E\) inicial e a \( \frac{\partial y^1}{\partial W^1}\) final, para um perceptron de L camadas. Esta constatação nos ajudará a produzir uma fórmula geral para o cálculo das derivadas da função de erro, \( E\), para um perceptron de um número qualquer de camadas.
4.2.7. Derivada do Erro com Relação aos Bias da Camada 1
Por fim, a derivada de E com relação aos bias da 1ª camada. Novamente, o cálculo com relação aos viéses segue bem de perto o cálculo com relação aos pesos da mesma camada, sendo apenas mais simples, uma vez que \( \frac{\partial y^1}{\partial b^1}\) produz uma matriz unitária.
4.3. Um Perceptron de Múltiplas Camadas
Agora que já ganhamos uma melhor compreensão sobre a estrutura de um Perceptron, vamos, rapidamente, escrever a equação para um, \( P\), com um número qualquer de camadas, \( L\). Utilizamos novamente o símbolo, \( \circ\), de composição de funções.
As reticências, naturalmente, indicam que, em seu lugar podem estar um número qualquer de camadas e cada par \( a^l\circ y^l\) indica os elementos da camada \( l\), a saber, respectivamente, o vetor de ativações cujo argumento é o seu vetor de linearidades, \( a^l( y^l)\).
Com a exceção da linearidade da camada 1, cada outra linearidade, \( L\ge l\ge 2\), tem a seguinte forma
em que \( n_l\) e \( p_l\) são, respectivamente, o número de linhas e de colunas de \( W^l\). Como o número de colunas da matriz \( W^l\) e o número de linhas do vetor \( a^{l-1}\) coincidem, o número de elementos de \( a^{l-1}\) também é \( p_l\).
A linearidade da camada 1 tem a forma muito parecida com a das demais linearidades, com a exceção do seu sinal entrante, \( x\).
4.4. A Função de Erro de um Perceptron de Múltiplas Camadas
A função de erro para o caso de \( L\) camadas é a mesma que a dos demais casos. Ela toma como argumento a saída, \( P\), do Perceptron.
4.5. A Derivada do Erro com Relação aos Pesos de uma Camada Qualquer
Vamos, a seguir, exibir a fórmula da derivada de \( E\) com relação aos pesos de uma camada \( l\). A sua forma é perfeitamente apreensível quando se tem em vista a expressão 48, pois, a partir disto, sabemos que temos que utilizar a regra da cadeia como método de derivação para obtermos
enquanto que a derivada com relação aos pesos da camada 1, \( W^1\), é
Acontece que, por mais belas e elegantes que sejam 49 e 50, em muitos casos, elas não poderiam ser calculadas inteiras a cada passo do treino de um Perceptron de várias camadas!
Quanto mais camadas um perceptron tem, mais longas são 49 e 50. Lembremos que cada derivada nestas fórmulas é uma matriz ou vetor ou, ainda, um vetor de matrizes cujas dimensões podem tomar valores bem altos. Isto torna impraticável o uso destas fórmulas na forma em que estão.
Considere dois cálculos sucessivos, o de \( \frac{\partial E}{\partial W^{l+1}}\) e \( \frac{\partial E}{\partial W^{l}}\). Se fôssemos utilizar a fórmula 49 para estes dois cálculos, teríamos calculado todas as primeiras \( L-(l+1)+1=L-l\) taxas de variação de 49 duas vezes!
Felizmente, há uma solução prática para este problema.
4.6. Processo Prático Para a Atualização dos Pesos e Bias
A solução ao problema apresentado na seção anterior é calcular a derivada dos pesos de uma camada, \( l\), aproveitando todos os cálculos já feitos para as camadas \( L\) até \( l+1\). A cada passo na descida pelas camadas, guarda-se o último cálculo realizado na memória.
4.6.1. Derivada de E com Relação aos Pesos
Isto é feito da seguinte forma. Considere as expressões a seguir, todas equivalentes à derivada de E com relação aos pesos da camada L
De modo que, da segunda linha de 52, temos, necessariamente, 53 que é a parte que nos importa salvar, por ora, na memória para os próximos cálculos.
Agora, preste atenção no que farei com 53, pois vou multiplicá-la por
para obter
Analise com atenção o lado esquerdo da primeira equação de 55 e certifique-se de que ele realmente se reduz ao lado esquerdo da quarta equação, pois é vital entendermos que a multiplicação de matrizes que acabamos de fazer, realmente, produz a derivada de E com relação aos pesos da camada seguinte da rede, da última para a primeira, a saber, \( \frac{\partial E}{\partial W^{L-1}}\).
Do lado direito, na quarta linha de 55, temos a parte que devemos salvar na memória para realizarmos o próximo cálculo da derivada de \( E\), que será com relação a \( W^{L-2}\).
Antes de mais nada, utilizemos 53 para escrever
A segunda equação em 56 resulta de se realizar o produto matricial \( \frac{\partial E}{\partial a^L}\frac{\partial a^L}{\partial y^L}\) e mostra a natureza recursiva da derivada de \( E\) com relação às linearidades do Perceptron, pois ela mostra a dependência que \( \frac{\partial E}{\partial y^{L-1}}\) tem de \( \frac{\partial E}{\partial y^L}\). O método que estamos desenvolvendo é um método recursivo.
Este método prático funciona, pois o que estamos salvando na memória é apenas o resultado dos cálculos e não as matrizes cujo produto dá este resultado. E, ele continua deste modo até que calculemos a derivada do Erro com relação aos pesos da camada 1.
Então, raciocinando de forma indutiva, sempre que tivermos calculado a derivada do Erro com relação aos pesos de uma camada \( l+1\), já teremos obtido a derivada do Erro com relação à linearidade desta camada
Aí, neste ponto, calculamos a quantidade correspondente a 54, só que, agora, com relação à camada \( l\) e em dois passos. Primeiro, calculamos apenas a quantidade
cujo produto com 57 produz
Veja que, em 57, já realizamos o produto matricial \( \frac{\partial E}{\partial y^{l+2}}\frac{\partial y^{l+2}}{\partial a^{l+1}}\frac{\partial a^{l+1}}{\partial y^{l+1}}\) que esta indicado em 59.
Por fim, multiplicamos ambos os membros da segunda equação em 59 por \( \frac{\partial y^{l}}{\partial W^{l}}\) para obtermos
4.6.2. Derivada de E com Relação aos Bias
O processo de dedução da derivada da função de erro com relação aos bias de uma camada qualquer é basicamente o mesmo que seguimos até aqui para a derivação quanto aos pesos.
Seguindo-se os mesmos procedimentos, é possível constatar que, também no caso dos viéses, a derivada de \( E\) com relação à linearidade da camada \( l\), ou seja, \( \frac{\partial E}{\partial y^l}\), é a mesmíssima que encontramos em 59. Não deveria haver surpresa alguma quanto a este fato, já que os pesos e os bias da camada \( l\) estão entranhados na única e mesma linearidade desta camada da rede.
Por fim, para encontrarmos \( \frac{\partial E}{\partial b^l}\), multiplicamos, como antes, a equação 59, só que agora, por \( \frac{\partial y^l}{\partial b^l}\) para obtermos
Mas, já vimos em 19 que sempre teremos
de modo que a segunda equação em 61 é, simplesmente, idêntica a \( \frac{\partial E}{\partial y^l}\) como expresso abaixo
4.6.3. A Fórmula Geral
Ufa! Agora, estamos em condições de sumarizar o que deduzimos até aqui em uma fórmula geral para o cálculo da derivada do Erro com relação à linearidade de uma camada \( l\). Com ela se tornará muito simples calcularmos a derivada do Erro com relação aos pesos e bias de qualquer camada, seguindo o processo que descrevemos.
Veja que o caso \( l=L\) vem diretamente de 53, enquanto que o caso \( L>l\ge 1\) é a expressão à direita de 59.
De acordo com 63, a equação acima é a exata expressão que calcula \( \frac{\partial E}{\partial b^{l}}\) para qualquer camada de um Perceptron.
Para acharmos \( \frac{\partial E}{\partial W^l}\), as equações em 60 nos dizem que basta multiplicar 64, em ambos os lados, por \( \frac{\partial y^l}{\partial W^l}\) e ajeitar o lado esquerdo da expressão para chegarmos em
4.7. Analisando a Dimensão das Matrizes de \( \frac{\partial E}{\partial W^l}\) e de \( \frac{\partial E}{\partial b^l}\)
Vamos fazer uma rápida análise da dimensão das matrizes envolvidas em 65 e, em seguida, efetuar explicitamente os produtos indicados nela. Isto nos dará a imagem do resultado final dos cálculos que temos estado a realizar até este ponto. Além disto, este resultado será utilizado em 20 para a atualização dos pesos e, para isto, é preciso que as dimensões de \( W^l\) e de \( \frac{\partial E}{\partial W^l}\) sejam iguais.
Considerando os elementos estruturais e as camadas indicadas em 65, vamos supor que a camada \( l+1\) tenha \( n\) neurônios, que a camada \( l\) tenha \( p\) neurônios e que o número de elementos do vetor entrante, \( s\), seja \( m\). O vetor \( s\) é o sinal que entra na camada \( l\). Este sinal pode ser tanto o vetor de ativações da camada \( l-1\), como pode ser o vetor \( x\) sobre o qual a rede está sendo treinada. Se a camada \( l\) for a última camada da rede, então, \( s=x\), caso contrário, \( s=a^{l-1}\). Por esta razão, utilizarei o símbolo \( s\) no restante desta Seção para indicar que podemos estar tratando de qualquer um destes casos.
Neste caso, \( \frac{\partial E}{\partial a^l}\) é um vetor linha de \( p\) elementos, \( \frac{\partial a^{l}}{\partial y^{l}}\) é uma matriz quadrada \( p\times p\), enquanto que \( \frac{\partial y^l}{\partial W^l}\) é um vetor-coluna de \( p\) posições cujos elementos são matrizes com a mesma dimensão de \( W^l\), ou seja, \( p\times m\).
Agora, \( \frac{\partial E}{\partial y^{l+1}}\) é um vetor linha de \( n\) elementos, \( \frac{\partial y^{l+1}}{\partial a^{l}}\) é uma matriz \( n\times p\) e, por fim mas não menos importante, \( \frac{\partial E}{\partial W^l}\) é uma matriz com as dimensões de \( W^l\).
Havendo a necessidade, consulte a Seção Derivada de Funções Vetoriais no Apêndice para uma pequena explicação sobre a dimensão de objetos resultantes da derivação de funções vetoriais.
Então, no caso em que \( l=L\), temos um produto de três matrizes com as seguintes dimensões: \( 1\times p\), \( p\times p\) e \( p\times 1\). Isto é o mínimo que esperaríamos para que o produto fosse possível, que é, o número de colunas da matriz à esquerda ser igual ao número de linhas da matriz à direita.
No caso em que \( L>l\ge 1\), temos quatro matrizes com as seguintes dimensões, da esquerda para a direita: \( 1\times n\), \( n\times p\), \( p\times p\) e, por fim, \( p\times 1\). Novamente, temos o mínimo que precisaríamos ter.
Em ambos os casos, a dimensão final \( p\times 1\) é a de um vetor-coluna de \( p\) linhas e \( 1\) coluna, cujas \( p\) posições são matrizes que têm a dimensão de \( W^l\), como já vimos várias vezes até agora. Assim, em ambos os casos, a dimensão \( 1\times 1\) do resultado dos produtos matriciais não é um escalar, mas, antes, uma única matriz que, como vimos, tem a dimensão da matriz de pesos da camada \( l\), ou seja, \( p\times m\).
Então, quando \( l\) é a última camada da rede
É fácil de se perceber que a última linha em 67, e de fato todo o seu desenvolvimento desde 66, é, essencialmente, aquele que foi iniciado em 35, com a exceção dos símbolos do sinal entrante e do número da camada.
O produto externo não é tão comum quanto o produto normal de vetores, mas o seu conceito é tão simples quanto ele. Apresento a sua definição no Apêndice Produto Externo.
Agora, partamos para a explicitação de \( \frac{\partial E}{\partial W^l}\) para uma camada \( l\) qualquer, com exceção da última.
Na equação 38 e seguintes já havíamos visto que a derivada de uma linearidade com relação ao seu sinal de entrada é a matriz de pesos da camada desta linearidade. Então, em 68, já usamos o fato de que \( \frac{\partial y^{l+1}}{\partial a^l}=W^{l+1}\). Continuando,
Até aqui, fizemos o produto indicado de matrizes ou vetores, da esquerda para a direita. O leitor deve acompanhar estes desenvolvimentos bem de perto, já que eles são os responsáveis por produzir as formas matemáticas que tornam a aprendizagem possível em princípio. O vetor-coluna mais à direita é, em verdade o vetor vertical de matrizes, sobre o qual já temos estado a falar. Estas matrizes aparecerão explicitamente, logo abaixo.
A seguir, nós reencontraremos o vetor linha que encontramos na segunda expressão de 69, só que desta vez, ele estará encerrado dentro de uma operação de transposição. Esta expressão transposta é a responsável pela forma final à que chegaremos a abaixo.
Agora, vejamos as fórmulas correspondentes para os bias. Já vimos que a forma de \( \frac{\partial E}{\partial b^l}\) é mais simples que a de \( \frac{\partial E}{\partial W^l}\). Isto acontece pois \( \frac{\partial y^l_i}{\partial b^l}\) são matrizes unitárias e podem ser "desprezadas" no cálculo de \( \frac{\partial E}{\partial b^l}\). Assim, abaixo, apresento as fórmulas relativas aos viéses sem detalhar todo o seu desenvolvimento já que ele é análogo e mais simples do que o que acabamos de fazer.
Assim, a fórmula para os bias, se \( l\) é a última camada, é
Se \( l\) for qualquer outra camada exceto a última, então,
4.8. A Atualização dos Pesos e Bias
Já vimos em 20, 21, 22 e 23 como a atualização dos pesos e bias é feita. Vamos reproduzir aquelas fórmulas, aqui, carregadas com a informação que recém obtivemos sobre \( \frac{\partial E}{\partial W^l}\) e \( \frac{\partial E}{\partial b^l}\).
ou, ainda,
A primeira vez que vemos esta fórmula, podemos ter alguma surpresa em constatar a presença de pesos da camada \( l+1\) na atualização dos pesos da camada \( l\). E não são só os pesos. Os elementos do vetor \( \frac{d E}{d y^{l+1}}\) estão ali também. A natureza recursiva de \( \frac{d E}{d y^{l+1}}\), como mostrado na segunda equação de 59, nos faz compreender que, implicitamente, ela contém elementos de todas as camadas desde a última camada \( L\).
Isto é devido à retro-propagação do erro. A derivada de \( E\) começa a ser calculada com relação aos elementos da camada \( L\) e vai descendo, em cadeia, até a camada \( l\) desejada, e, no processo, evoca as precisas quantidades adequadas das outras partes da rede.
A fórmula para a atualização dos bias é, mais uma vez, mais simples do que a dos pesos.
ou seja,
Adicionei uma pequena continuação ao que foi exposto nesta seção aos Apêndices, na Seção Aprendizado Contínuo. Nela coloco algumas observações sobre o que pode ser chamado, provisoriamente, de aprendizado contínuo. O leitor interessado a terá à mão, mas ela não é indispensável ao entendimento do que apresentamos neste livro.
5. Treino em Lotes
Neste capítulo descreveremos a matemática do treino em lotes que generaliza a descrição matemática da aprendizagem.
Ao invés de propagar um único sinal, x, a cada etapa de treino, podemos propagar vários vários deles reunidos em uma matriz que chamamos lote de vetores de treino. Daí, a expressão treinar em lotes.
Os modernos processadores computacionais permitem a realização simultânea, ou paralela, de muitos cálculos. Assim, no treino em lotes, pode-se calcular em paralelo a combinação linear de um sinal entrante com cada um dos vetores de pesos de uma mesma camada perceptrônica. Ou seja, é possível calcular a saída de todos os neurônios de uma mesma camada.
5.1. Treinando em Lotes
O treino em lotes consiste da utilização de lotes de vários vetores de treino, \( X\), ao invés de vetores únicos, \( x\), a cada etapa de propagação. O lote, \( X\) é, em verdade, uma matriz já que os \( x\) são vetores. Tenhamos em mente que um \( j-\text{ésimo}\) vetor \( x\) é a coluna, \( X^j\), da matriz \( X\).
5.1.1. O Efeito do Lote X na Primeira Camada
Vejamos qual é a expressão matemática disto. Reescrevamos a equação 9 com um lote (“batch” em inglês) de \( \beta\) vetores \( x\) para a primeira camada da rede. Este momento requer atenção para o fato de estarmos considerando a primeira camada da rede.
É claro que, agora, o vetor de linearidades da primeira camada é uma matriz e, para marcar esta diferença, usei outro símbolo, \( Y^1\).
Também, temos agora uma matriz de viézes e não mais apenas um vetor. Note que todas as colunas da matriz de viéses são iguais! Precisa ser! Pois, a rede neural ainda é a mesma, com a diferença de ser treinada sobre mais de um vetor de entrada ao mesmo tempo. Cada vetor de entrada no lote combina-se ainda com os mesmos pesos e o mesmo bias definidores da rede!
Usa-se, agora, uma matriz de viéses com \( \beta\) vetores-coluna idênticos, ou seja, \( B^1=\cdots=B^k=\cdots=B^{\beta}\). Isto é assim, para se manter a coerência com o jeito que matrizes e vetores se operam matematicamente, mas a igualdade entre os vetores coluna da matriz B já nos mostra que ainda temos um único vetor de bias aprendendo e que este vetor único é aplicado a cada um dos \( \beta\) conjunto de cálculos ocorrendo em paralelo para se tratar os \( \beta\) vetores distintos do lote de treino.
onde, agora, os elementos \( Y^j\) são as \( \beta\) colunas de \( Y^1\); \( W_i\) são as linhas de \( W\) e \( X^j\) é a \( j-\text{ésima}\) coluna do lote \( X\). O sobrescrito \( 1\), nos membros à esquerda de 79, indica o número da camada. Assim, qualquer um dos elementos da matriz \( Y^1\) pode ser representado por
As expressões à esquerda em 79 nos mostram que a linearidade da camada 1 deixou de ser um vetor-coluna para ser uma matriz de \( \beta\) vetores-coluna.
Então, agora, devemos aplicar as funções de ativação à saída de 79, ou seja, calcularemos as funções de ativação sobre cada um dos \( \beta\) vetores coluna \( Y^j\). Com a exceção de tratar-se de um lote de vetores, o cálculo das ativações está como antes, isto é, a ativação é aplicada à cada vetor de linearidades, \( Y^j\), de uma camada da rede que, por sua vez, é a resposta a um vetor de treino \( X^j\).
onde \( A\) é o novo símbolo para as ativações, que não são mais um único vetor-coluna, mas uma matriz de vetores-coluna. O mesmo vale para \( P\) como mostram as três equações acima.
Resumindo, treinar a rede em lotes, significa calcular simultaneamente a resposta da rede para cada um dos vetores do lote de entrada. Se a entrada, \( X\), é constituída de \( \beta\) vetores, então, naturalmente, a saída da rede é uma matriz, também, de \( \beta\) vetores \( Y^j\) - uma resposta para cada entrada \( X^j\).
5.1.2. O Efeito do Lote X Numa Camada Qualquer
Acontece que, como já destacamos na segunda e terceira expressões em 81, a saída da camada 1 é uma matriz. Isto significa que a entrada da camada 2 é esta mesma matriz. É claro que isto acontecerá a todas as demais camadas da rede e, em verdade, podemos mostrar isto apenas trocando o lote de entradas, \( X\), por uma matriz de sinais entrantes, \( S\), em 79 para obtermos
em que a notação sobrescrita aos colchetes, \( l\), indica o número da camada do elemento de rede.
A fórmula 82 é a mesma que vemos na última expressão de 79 e elas mostram que se uma camada, \( l\), receber uma matriz de \( \beta\) vetores-coluna como argumento, então, esta camada \( l\) dará outra matriz de \( \beta\) vetores-coluna como resposta que, por sua vez, será o sinal de entrada da camada \( l+1\), e assim por diante.
Além disto, das duas expressões em 82, vê-se que a contabilidade dimensional do produto matricial é respeitada em cada camada da rede, sendo este um dos aspectos que possibilita o uso de lotes. A matriz de pesos é \( n\times m\) enquanto que \( S^{l-1}\) é \( m\times \beta\), onde \( m\) é o número de elementos no vetor entrante, o qual coincide com o número de colunas de \( W^l\). Cada vetor-coluna \( B^k\) tem, naturalmente, n elementos.
Deste modo, mostramos como é que um sinal matricial de entrada, \( S\), flui através de um Perceptron, mantendo constante o número de colunas até sair pela última camada da rede, ponto em que será entregue à Função de Erro.
Não é demais destacar que uma matriz de sinais entrantes, \( S\), pode ser tanto uma matriz de vetores de treino, \( X\), como uma matriz de vetores-coluna de ativações, \( A\), advinda da camada anterior. Se a camada em questão for a camada 1, então, \( S=X\), caso contrário, \( S=A^{l-1}\).
5.1.3. A Equação do Perceptron Teinado em Lotes
Então, se fôssemos escrever uma equação geral para um Perceptron de Múltiplas Camadas, inspirada na fórmula 45, mas com a notação matricial que utilizamos neste capítulo, teríamos
onde, como em 45, os sobrescritos indicam o número das camadas.
5.1.4. O Erro como Uma Média Aritmética
Como já mencionamos, no caso de treino em lotes, a função de erro agirá sobre uma matriz de várias colunas e não mais apenas sobre um vetor-coluna. Como veremos, em seguida, a Função de Erro aplicada sobre uma matriz (cujas colunas são os vetores de ativação da última camada), acaba por ser calculada como a média aritmética dos erros cometidos por cada vetor-coluna na matriz de ativações. Lembremos que cada vetor de ativações é a resposta da rede a um dos vetores-coluna do lote.
Embora, a Função de Erro neste caso de treino em lotes seja diferente da Função de Erro que vínhamos vendo até agora, no aspecto geral, como antes, ela é
onde \( W=\{W^L, W^{L-1},\dots, W^1\}\).
Embora diferente, a função de Erro, \( E\), no caso de treino em lotes ainda é uma medida de quão próximos os vetores-coluna da saída, \( P\), da rede estão das respostas corretas e correspondentes a cada vetor-coluna do lote de treino, \( X\). Mostramos, no Apêndice A Função de Custo sobre um Domínio Matricial é uma Norma, que esta nova Função de Erro é, de fato, também uma norma, se \( E_j\) (ver abaixo) for uma norma. Agora, como antes, \( E\) é uma função de valor real, mas, diferente de antes, com domínio matricial. Podemos simbolizar isto assim: \( E:\mathbb{R}^{r\times \beta}\longrightarrow \mathbb{R}\), em que \( \mathbb{R}^{r\times \beta}\) simboliza o conjunto de matrizes com \( r\) linhas e \( \beta\) colunas, cujas entradas são números reais.
Deste modo, podemos mostrar 84 sob uma forma ainda mais reduzida, sem ressaltar a sua natureza compositiva, mas realçando o fato de que a função de ativações da última camada da rede, \( A^L\), é uma matriz, que \( E\) depende dos pesos atuais de \( W\) e, principalmente, que \( E\), agora, toma a forma de uma média aritmética dos erros calculados sobre cada uma das colunas de \( A^L\).
Aqui, os sobrescritos designam o número de uma coluna da matriz \( A\) da última camada da rede. \( E_j\) é o erro como o vínhamos vendo até o capítulo anterior, ou seja, uma função real sobre um vetor-coluna de ativações. Deste modo, vemos que a nova Função de Erro, necessária no treino em lotes, está intimamente relacionada à Função de Erro que havíamos considerado até agora, ou seja, a equação 48.
5.1.5. Treino em Lotes e a Taxa de Variação do Erro
Como se vê em 85, a aferição do desempenho do Perceptron, ainda vem de se conhecer o valor de cada erro, \( E_j\), associado a cada vetor de entrada, \( X^j\), do lote.
Tal aferição nos leva ao cálculo da derivada do erro e ao posterior processo de retro-propagação que torna possível ao Perceptron aprender. Então, passemos à derivada de 85 com relação aos parâmetros treináveis, \( W^l\), da camada \( l\).
Na segunda e terceira equações de 86, empregamos a utilíssima fórmula geral, 65, para o cálculo da derivada do erro com relação aos parâmetros treináveis de qualquer camada, \( \frac{\partial E_j(A^j)}{\partial W^l}\) (expressão a qual chegamos em 86), quando a rede é treinada sobre um único vetor se entrada, \( x\), pois vimos que o erro em domínio matricial se baseia no erro, \( E_j\), de domínio vetorial, o qual já havíamos calculado em 65.
Por fim, com base em 64 e com raciocínios análogos aos feitos logo acima, chegamos à forma abaixo para a taxa de variação do erro com relação aos bias de qualquer camada no treino em lotes.
5.2. A Atualização dos Parâmetros Treináveis no Treino em Lotes
Aqui, mais uma vez, a forma contida em 20 é a receita para a atualização dos pesos e a taxa de variação do erro com relação a eles tem a forma contida em 86, ou seja,
o que, de acordo com 75, nos leva a
É muito fácil de se ver que a equação 89 utiliza a forma de 75. A única diferença é que, agora, marcamos \( E\) e \( s\) com o número do vetor-coluna, \( j\), a que estes elementos correspondem no lote de treino.
As terceira expressão, em 88, nos deixa constatar que as \( \beta\) instâncias da rede, calculadas em paralelo, cada uma sobre um dos elementos do lote, \( X\), utilizam os mesmos elementos da rede. Vemos que elementos "concretos" como os pesos ou elementos dinâmicos como a forma da variação das ativações, por exemplo, são as mesmas para as \( \beta\) parcelas, embora o valor numérico, no caso das variações dinâmicas, possa mudar de acordo com o elemento do lote.
Mais uma vez, a forma da atualização respectiva ao bias é obtida por meio de procedimentos muito análogos aos feitos acima, mas a partir das equações 64, 77 e 87.
e
Apêndice A: Norma Sobre Um Espaço Vetorial
Definição 1:
Norma é qualquer função real \( N(x)\) definida sobre um espaço vetorial \( V\) e que obedeça às três condições abaixo:
-
\( N(x)> 0,\ se\ x\ne 0\)
-
\( N(a\cdot x)=N(a)\cdot N(x)=|a|\cdot N(x)\)
-
\( N(x+y)\le N(x)+N(y)\) (Desigualdade Triangular)
com \( x,y\in V\) e \( a\in \mathbb{R}\).
Não definirei, aqui, o que seja um espaço vetorial por fugir muito do escopo deste livro. Basta que tenhamos utilizado vetores e matrizes pesadamente neste livro por seu significado direto e utilidade imediata. A sua definição pode ser encontrada online e em um sem número de livros de Álgebra Linear.
Prova:
Pelo item 1, \( N(x)\) é sempre positiva, exceto quando \( x=0\). Se \( x=0\), então, podemos escrevê-lo assim: \( 0\cdot x=0\). Neste caso, o item 2 da definição 1, nos dá \( N(0\cdot x)=N(0)\cdot N(x)=0\cdot N(x)=0\). Logo, \( N(x)\) só pode assumir valores positivos ou ser nula. Então o mínimo valor de \( N\) é \( 0\) e ele, como vimos, ocorre no vetor \( 0\).
Corolário:
A diferença \( x-y\) deve ser nula se a norma \( N(x-y)\) atinge o seu valor mínimo.
Prova:
A validade deste corolário é evidentíssima à luz da Proposição 1. Mas, suponhamos, por contradição, que \( x-y\) fosse diferente de zero quando \( N\) atingisse o seu mínimo. Então, teríamos \( N(x-y)=0\) com \( x-y\ne 0\). Mas, isto contrariaria o requisito 1 da Definição 1. Logo, \( N\) não seria uma norma.
Apêndice B: As Derivadas de \( y_i\)
B.1. A Derivada de \( y_i\) com Relação aos pesos \( W_i\)
Comecemos recordando que \( W_i=[w_{i1},\dots ,w_{ip}]\) é o vetor que está na linha \( i\) de \( W\).
Deste modo, também fica claro porquê \( \frac{\partial y_i}{\partial W_l}=0\) com \( i\ne l\). Pois, \( y_i\) simplesmente não depende de \( W_l\) e, assim, \( \frac{\partial (w_{ij}s_j +b_i)}{\partial w_{lj}}\) precisa ser nulo para todo o \( j\).
B.2. A Derivada de \( y_i\) com Relação aos bias \( b\)
Considere o vetor de bias \( b=[b_{1},\dots ,b_{n}]\). Desde que \( b\) é um vetor, podemos, naturalmente, calcular \( \nabla_b y_I\) para um \( i=I\) fixo. Só que este cálculo não é o mais adequado a ser realizado.
O Perceptron tem uma estrutura e, de acordo com esta estrutura, ele tem um único viés por neurônio. Isto quer dizer que a cada vetor \( W_I\), numa linha de \( W\), corresponde apenas um escalar \( b_I\). Isto produz o interessante resultado de os elementos não nulos de \( \nabla_b y_i\), com \( 1\le i\le n\), não estarem, de fato, ao longo de uma linha ou de uma coluna de \( \frac{\partial y}{\partial b}\), mas sim ao longo de sua diagonal principal (como mostrado na segunda e terceira expressões em 19)! Assim,
onde \( \frac{\partial y_I}{\partial b_i}=0\) com \( i\ne I\), enquanto que \( \frac{\partial y_I}{\partial b_i}=1\) se \( i=I\). Deste modo, escrevemos \( 1_I\) para indicar que a unidade só ocorre na posição \( I\) e que todas as demais posições são nulas.
Apêndice C: Derivada de Funções Vetoriais
Este pequeno resumo não trata da teoria das derivadas de funções vetoriais. Eu apenas apresento algo de notações e fatos relevantes para o assunto deste livro na esperança de que a memória do leitor complete o resto.
Para começar, considere uma função \( f:\mathbb{R}\rightarrow\mathbb{R}\), tal que \( y=f(x)\), de modo que \( f\) tem apenas um argumento.
Podemos representar a derivada de \( f\) como \( \frac{dy}{dx}\) ou \( \frac{df(x)}{dx}\), ou ainda de outras maneiras.
Agora, se a função for tal que \( y=f(x_1,\dots,x_n)\), ou seja, \( f: \mathbb{R}^n\rightarrow \mathbb{R}\), então, \( f\) tem \( n\) argumentos e, neste texto, representamos a sua derivada muito simplesmente assim \( \frac{\partial f}{\partial x}\), ou assim \( \nabla f\). Estes dois últimos símbolos representam um vetor e \( x=[x_1, \dots ,x_n\)].
A derivação transforma, de fato, uma \( f: \mathbb{R}^n\rightarrow \mathbb{R}\) em um vetor de modo que
Note o fato importante de que a \( f\) é uma função que leva pontos de um espaço de \( n\) dimensões para um espaço de uma dimensão e que - observe bem este fato - a derivada \( \frac{\partial f}{\partial x}\) é um vetor de uma linha e \( n\) colunas. Guarde bem esta observação.
Agora, considere uma função que leve um espaço de \( n\) dimensões num espaço de \( m\) dimensões, \( f: \mathbb{R}^n\rightarrow \mathbb{R}^m\), tal que
A derivação desta função resulta na seguinte matriz
Note que esta matriz tem \( m\) linhas e \( n\) colunas, pois o domínio da função é um espaço de \( n\) dimensões e o contra-domínio é um espaço de outras \( m\) dimensões.
Assim, o número de dimensões do domínio, determina o número de colunas, enquanto que o número de dimensões do contra-domínio determina o número de linhas da matriz resultante da derivação.
Veja que este fato vale para funções comuns, cujo domínio e contra-domínio são espaços de única dimensão, como no primeiro exemplo. A derivada \( \frac{df(x)}{dx}\) é uma matriz de uma linha e uma coluna, ou seja, um número escalar.
Neste texto, acabamos lidando com alguns objetos mais interessantes, tal como a derivada de uma função vetorial com relação a uma matriz inteira.
A função de uma rede neural pode ser um objeto grandioso e intrincado. Assim, temos que utilizar o poderio matemático a fim de descrever e operar com e sobre ele, de modo que a sua manipulação fique facilitada e o seu sentido esclarecido.
Neste caso, claramente, a matriz \( W\) é o argumento da função vetorial:
A derivada desta função segue a mesma “regrinha” que já usamos até aqui.
Consideramos, num primeiro momento, a matriz \( W\), de \( q\) linhas e \( r\) colunas, como um bloco único. Assim, a derivada da função acima, gerará uma matriz de \( m\) linhas e 1 coluna. Deste modo,
Note que os elementos do vetor-coluna acima são, de fato, matrizes.
Note bem que uma matriz é um vetor-coluna cujos elementos são as linhas desta matriz.
Por esta razão, podemos escrever qualquer um dos elementos do vetor-coluna 98 como
onde \( W_i\) representa o vetor que está na linha \( i\) da matriz \( W\).
Deste modo, 98 assume a seguinte bela e interessante forma:
Apêndice D: Algumas Observações sobre o Gradiente
Resultados muito interessantes surgem no entrecruzamento de conceitos de Álgebra Linear e de Cálculo.
Primeiro, do trato com vetores, sabemos que o produto interno entre dois vetores, \( c\) e \( d\) é
e que, portanto, o lado direito é máximo quando \( \cos\theta=1\), uma vez que \( -1\le \cos\theta\le 1\).
Mas, \( \cos\theta=1\), quando \( \theta=0\), ou seja, \( c\cdot d\) é máximo quando c e d são paralelos ou colineares ou, ainda, de mesma direção.
Por outro lado, a derivada direcional, \( \nabla_d f\), de uma função real \( f:\mathbb{R}^n\rightarrow\mathbb{R}\) na direção de um vetor unitário, \( d\), pode ser apresentada como o produto interno entre o gradiente de \( f\) e o vetor \( d\), assim
onde, usamos o fato assumido de que \( |d|=1\).
Agora, da discussão em torno de 101, sabemos que a segunda expressão em 102 será máxima quando \( \theta=0\) e, neste caso, teremos que quando a derivada direcional de uma função é máxima, esta derivada direcional coincide com o módulo do gradiente desta mesma função. Além disto, como \( \theta=0\), \( \nabla_d f\) e \( d\) têm ambos a mesma direção. Disto decorre que \( \nabla_d f\) tem a mesma direção de \( \nabla f\), uma vez que podemos escrever o vetor unitário \( d\) assim \( d=\frac{\nabla f}{|\nabla f|}\).
Vimos várias vezes neste livro que o vetor gradiente é composto das derivadas parciais de uma função. A soma destas derivadas parciais dá, de algum modo, a magnitude da taxa de variação total da função.
Pensando geometricamente, no familiar espaço tridimensional, quanto mais íngrime é a direção da reta tangente à uma função, maior é a sua taxa de variação. Assim, se, como acabamos de ver, \( \nabla f\) aponta na direção da maior taxa de variação de \( f\), então, \( -\nabla f\) apontará na direção da menor taxa, contanto que o ponto em que ela estiver sendo calculada não seja um ponto de sela ou um ponto de cunha.
Apêndice E: Produto Externo
Por fim, há também o produto externo cuja operação tem o seguinte símbolo: \( \otimes\). Tomemos, agora, os vetores \( c\) e \( e\) e os consideremos, respectivamente, como uma matriz de \( 1\) linha e \( m\) colunas; e outra matriz de \( n\) linhas e \( 1\) coluna. Note que, para o produto externo, os números de elementos em cada vetor não precisam ser iguais.
Então, a definição do produto externo é simplesmente
Assim, o resultado de um produto externo é uma matriz. Isto é assim pois os números escalares de \( e\) são as linhas da matriz \( e\), enquanto que os escalares de \( c\) são as colunas da matriz \( c\).
Apêndice F: Aprendizado Contínuo
O que vamos expor, aqui, é muito mais a colocação de um problema e a representação, em linhas muito gerais, de um princípio de solução. Há uma conjectura que está implicitamente sugerida nas fórmulas, 75 e 77, dos pesos e bias. Esta conjectura é a seguinte: elas podem ser mostradas, como veremos a seguir, sob a forma explícita de uma somatória cujas parcelas têm todas um pequeno coeficiente, \( \eta\). Então, é possível indagar se o aumento do número de parcelas é acompanhado pela diminuição, ou tenha o efeito de diminuir correspondentemente o valor absoluto de \( \eta\).
Se isto for assim, a expressão para cada um dos pesos em 74 ou 75 assume a forma de uma integral. Mas, uma integral é uma soma cujo número de parcelas é indefinidamente grande. O treino normal de um Perceptron é finito, tem um número finito de atualizações de peso, embora este número finito possa ser grande. Ou seja, o aprendizado padrão de um Perceptron é limitado à sessão de treino após a qual ele é posto a desempenhar a sua tarefa produtiva.
Uma tal forma integral não tem como ser vista dentro de um quadro de aprendizado já concluído ou com término definido. Ela pressupõe um número infinito de passos de treino, o que significa, concretamente, que o treino nunca terminaria, ou seja, aprendizado contínuo. A beleza disto, é que o aprendizado contínuo não afasta, de modo algum, o trabalho produtivo, mas revela a possibilidade de atualização e adequação do Perceptron a tarefas com características que se alterem com o tempo ou até mesmo adequação a tarefas completamente diferentes como é o caso em se tratando de aprendizado por transferência.
e
Se, como dissemos, quando \( t\rightarrow \infty\), temos \( \eta_t\rightarrow 0\), mas mantém-se não nulo, então, podemos avançar para a seguinte fórmula
onde fizemos \( \eta_{\tau+1}=d\tau\), pois se \( \eta\) for pequeno o suficiente, ele pode se considerado como a diferença entre dois instantes \( \tau+1\) e \( \tau\), de tempos bem próximos. Também, considerei \( W^l_1=W^l_{t_0}\), pois, como dissemos, o aprendizado contínuo compreende uma quantidade indeterminada de sessões de treino de prazo determinado. Então, cada sessão de treino culmina em um conjunto de pesos que passa a ser o \( W^l_{t_0}\) da próxima sessão.
Há alguns desenvolvimentos sugeridos na segunda equação de 105, mas nós não nos ocuparemo deles neste livro. Fica, no entanto, a forma dada nesta integral que assenta a possibilidade matemática para o aprendizado continuado cuja realização já se efetivou, haja vista a vantajosa técnica do aprendizado por transferência que é utilizada com tanto sucesso hoje em dia.
Apêndice G: A Função de Custo sobre um Domínio Matricial é uma Norma
Aqui, precisamos mostrar que o erro, \( E\), como definido em 85, é uma norma, de acordo com a definição dada em Norma Sobre Um Espaço Vetorial. A Função de Erro definida, aí, tem domínio matricial e está definida sobre uma outra norma, \( E_j\), que por sua vez tem domínio vetorial.
Proposição 1:
Se \( E_j\) é uma norma, então \( E=\frac{1}{\beta}\sum_{j=1}^{\beta}E_j(A^j)\) também o é.
Prova:
Precisamos mostrar que \( E\) atende aos 3 requisitos dados na Definição 1.
Como \( E_j\) é uma norma e \( \beta>0\), segue-se que se \( A^j\ne 0\),
Logo, \( E\) atende ao primeiro requisito da Definição Norma Sobre Um Espaço Vetorial.
O Erro \( E\) também atende ao segundo requisito pois, se \( a\in \mathbb{R}\), então,
Por fim, a desigualdade triangular também é satisfeita. Vejamos:
Apêndice H: Tópicos Fundamentais em Aprendizado de Redes Neurais Não Tratados Neste Livro
Perguntei ao ChatGPT quais seriam os tópicos mais importantes para a aprendizagem de máquinas, além da propagação para trás. A sua resposta está abaixo, com edições minhas.
Embora, você mesmo possa fazer a mesma pergunta a ele, coloquei-a aqui para que você possa acessar e ler prontamente o conteúdo da resposta.
Abaixo estão 9 tópicos essenciais para tornar o aprendizado de redes neurais possível ou para aprimorá-lo, além da já conhecida retropropagação (backpropagation).
H.1. Inicialização de Pesos
A maneira como os pesos da rede são inicializados pode impactar fortemente o sucesso do treinamento. Inicializações inadequadas podem levar ao desvanecimento (gradientes muito pequenos) ou à explosão (valores muito grandes) dos gradientes durante a retropropagação, dificultando ou impedindo o aprendizado. Inicializações modernas foram desenvolvidas para manter os valores das ativações e dos gradientes dentro de faixas estáveis, desde as primeiras até as últimas camadas.
Exemplos:
-
Inicialização de Xavier (Glorot) – ideal para funções de ativação simétricas como tanh.
-
Inicialização de He – recomendada para redes com ReLU.
H.2. Normalização de Dados
Normalizar os dados antes de inseri-los na rede é uma prática crítica para melhorar a convergência do treinamento. Dados com escalas muito distintas podem causar instabilidades ou tornar o treinamento lento. Além disso, normalizar as ativações internas das redes (durante o treinamento) ajuda a manter distribuições estáveis e acelera o aprendizado.
Exemplos:
-
Batch Normalization – normaliza as ativações em mini-lotes, além de introduzir dois parâmetros treináveis de escala e deslocamento.
-
Layer Normalization – usada em RNNs e Transformers, pois não depende do tamanho do lote.
H.3. Taxa de Aprendizado e Otimizadores
A taxa de aprendizado é o hiperparâmetro mais sensível de uma rede. Ela determina o tamanho dos passos dados no espaço de parâmetros a cada atualização. Se for muito alta, a rede pode nunca convergir; se for muito baixa, o treinamento pode ser lento ou ficar preso em mínimos locais. Otimizadores modernos melhoram esse processo adaptando automaticamente os passos com base nos gradientes acumulados ou momentos anteriores.
Exemplos:
-
SGD (Stochastic Gradient Descent) – versão básica, com ou sem momentum.
-
Adam – combina RMSprop com momentum; muito usado por sua robustez.
-
RMSprop – adapta a taxa de aprendizado dividindo os gradientes por uma média móvel dos quadrados dos gradientes passados.
H.4. Regularização
Regularização é o conjunto de técnicas que combatem o overfitting, que ocorre quando a rede memoriza os dados de treinamento e falha em generalizar para novos exemplos. Isso é especialmente importante em redes profundas, com grande capacidade de parametrização. A regularização impõe restrições sobre os pesos ou o comportamento da rede para favorecer modelos mais simples e robustos.
Exemplos:
-
L1 e L2 regularization – adicionam penalidades sobre o valor absoluto ou o quadrado dos pesos, respectivamente.
-
Dropout – desliga aleatoriamente unidades da rede durante o treinamento, forçando redundância e evitando coadaptação excessiva.
H.5. Arquiteturas de Rede
A arquitetura da rede determina sua capacidade expressiva e sua adequação à tarefa. A escolha da arquitetura afeta diretamente o desempenho, tempo de treinamento e interpretabilidade. Com o avanço das pesquisas, surgiram modelos especializados para diferentes tipos de dados (imagens, texto, som, séries temporais etc.), cada um aproveitando estruturas e propriedades específicas desses dados.
Exemplos:
-
Feedforward (MLP) – redes básicas com camadas densas (exatamente as que vimos neste livro, MLP ou Feedforward são apenas outros nomes).
-
CNNs (Convolutional Neural Networks) – exploram a estrutura espacial de imagens.
-
RNNs, LSTMs, GRUs – boas para dados sequenciais, como textos e sinais.
-
Transformers – arquitetura dominante para NLP e também aplicada em visão.
H.6. Engenharia de Dados e Pré-processamento
O desempenho de uma rede está diretamente relacionado à qualidade e representatividade dos dados que ela recebe. Dados ruidosos, incompletos ou enviesados podem comprometer todo o processo de aprendizado. Pré-processar e enriquecer os dados é um passo essencial, e muitas vezes mais importante do que ajustes finos nos hiperparâmetros.
Exemplos:
-
Limpeza de dados – remoção de duplicatas, tratamento de valores ausentes.
-
Aumento de dados (data augmentation) – criação de novas amostras artificiais, comum em visão computacional (ex: espelhamento, rotação).
-
Extração de características – como PCA ou t-SNE, para melhorar a representação dos dados.
H.7. Técnicas de Treinamento
O modo como a rede é treinada influencia fortemente sua capacidade de convergir para uma boa solução. Técnicas eficazes de treinamento ajudam a evitar problemas como ruído excessivo nas atualizações ou sobreajuste aos dados de treino. Algumas delas são estratégias para parar o treinamento no momento certo ou dividir os dados de forma que as atualizações sejam mais estáveis.
Exemplos:
-
Mini-batch training – divisão do conjunto de dados em pequenos blocos; equilibra precisão e velocidade.
-
Early stopping – monitora a performance em validação e interrompe o treino se esta piorar.
-
Learning rate schedules – reduzem a taxa de aprendizado ao longo do tempo para facilitar a convergência.
H.8. Curvas de Aprendizado e Avaliação
Acompanhar o comportamento da rede ao longo do tempo é crucial para diagnosticar problemas e guiar decisões. As curvas de perda e acurácia durante o treinamento e a validação revelam sinais de overfitting, underfitting ou erro de modelagem. Além disso, métricas apropriadas ajudam a avaliar corretamente a rede de acordo com o contexto do problema.
Exemplos:
-
Loss vs. Accuracy – duas curvas básicas a serem monitoradas.
-
F1-score, Precision, Recall – importantes em tarefas desequilibradas como detecção de fraude.
-
AUC-ROC – útil em classificadores binários para medir separabilidade.
H.9. Transferência de Aprendizado (Transfer Learning)
Embora haja um apêndice abordando alguns aspectos do Aprendizado por Transferência, aquele conteúdo está longe de cobrir o assunto e, em verdade, representa muito mais um ensaio feito por este autor. Transfer learning permite reaproveitar conhecimento de uma rede treinada em uma grande base de dados e adaptá-la a uma nova tarefa com menos dados. Isso é extremamente útil quando os dados disponíveis são escassos, mas a tarefa é semelhante a outra já bem explorada. Ele reduz o tempo de treinamento e melhora a generalização, sendo hoje uma prática comum, especialmente em NLP e visão computacional.
Exemplos:
-
Fine-tuning de redes como ResNet, BERT, GPT – adaptando os pesos finais a uma nova tarefa.
-
Congelamento de camadas – mantendo pesos já treinados e treinando apenas as camadas finais.
-
Uso de embeddings pré-treinados – como word2vec ou GloVe para texto.