.. _sec_mlp: *Perceptrons* Multicamada ========================= Em :numref:`chap_linear`,, apresentamos regressão *softmax* (:numref:`sec_softmax`), implementando o algoritmo do zero (:numref:`sec_softmax_scratch`) e usando APIs de alto nível (:numref:`sec_softmax_concise`), e classificadores de treinamento para reconhecer 10 categorias de roupas a partir de imagens de baixa resolução. Ao longo do caminho, aprendemos como organizar dados, coagir nossos resultados em uma distribuição de probabilidade válida, aplicar uma função de perda apropriada, e minimizá-la em relação aos parâmetros do nosso modelo. Agora que dominamos essa mecânica no contexto de modelos lineares simples, podemos lançar nossa exploração de redes neurais profundas, a classe comparativamente rica de modelos com o qual este livro se preocupa principalmente. Camadas Ocultas --------------- Descrevemos a transformação afim em :numref:`subsec_linear_model`, que é uma transformação linear adicionada por um *bias. Para começar, relembre a arquitetura do modelo correspondendo ao nosso exemplo de regressão*\ softmax\ *, ilustrado em :numref:`fig_softmaxreg`. Este modelo mapeou nossas entradas diretamente para nossas saídas por meio de uma única transformação afim, seguido por uma operação*\ softmax\ *. Se nossos*\ labels\* realmente estivessem relacionados aos nossos dados de entrada por uma transformação afim, então esta abordagem seria suficiente. Mas a linearidade nas transformações afins é uma suposição *forte*. Modelos Lineares Podem Dar Errado ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Por exemplo, linearidade implica a *mais fraca* suposição de *monotonicidade*: que qualquer aumento em nosso recurso deve sempre causar um aumento na saída do nosso modelo (se o peso correspondente for positivo), ou sempre causa uma diminuição na saída do nosso modelo (se o peso correspondente for negativo). Às vezes, isso faz sentido. Por exemplo, se estivéssemos tentando prever se um indivíduo vai pagar um empréstimo, podemos razoavelmente imaginar que mantendo tudo o mais igual, um candidato com uma renda maior sempre estaria mais propenso a retribuir do que um com uma renda mais baixa. Embora monotônico, esse relacionamento provavelmente não está linearmente associado à probabilidade de reembolso. Um aumento na receita de 0 a 50 mil provavelmente corresponde a um aumento maior em probabilidade de reembolso do que um aumento de 1 milhão para 1,05 milhão. Uma maneira de lidar com isso pode ser pré-processar nossos dados de forma que a linearidade se torne mais plausível, digamos, usando o logaritmo da receita como nosso recurso. Observe que podemos facilmente encontrar exemplos que violam a monotonicidade. Digamos, por exemplo, que queremos prever a probabilidade de morte com base na temperatura corporal. Para indivíduos com temperatura corporal acima de 37 ° C (98,6 ° F), temperaturas mais altas indicam maior risco. No entanto, para indivíduos com temperatura corporal abaixo de 37 ° C, temperaturas mais altas indicam risco menor! Também neste caso, podemos resolver o problema com algum pré-processamento inteligente. Ou seja, podemos usar a distância de 37 ° C como nossa *feature*. Mas que tal classificar imagens de cães e gatos? Aumentar a intensidade do pixel no local (13, 17) deveria sempre aumentar (ou sempre diminuir) a probabilidade de que a imagem retrate um cachorro? A confiança em um modelo linear corresponde à implícita suposição de que o único requisito para diferenciar gatos vs. cães é avaliar o brilho de pixels individuais. Esta abordagem está fadada ao fracasso em um mundo onde inverter uma imagem preserva a categoria. E ainda, apesar do aparente absurdo da linearidade aqui, em comparação com nossos exemplos anteriores, é menos óbvio que poderíamos resolver o problema com uma correção de pré-processamento simples. Isso ocorre porque o significado de qualquer pixel depende de maneiras complexas de seu contexto (os valores dos pixels circundantes). Embora possa existir uma representação de nossos dados isso levaria em consideração as interações relevantes entre nossas características, no topo das quais um modelo linear seria adequado, nós simplesmente não sabemos como calculá-lo à mão. Com redes neurais profundas, usamos dados observacionais para aprender conjuntamente uma representação por meio de camadas ocultas e um preditor linear que atua sobre essa representação. Incorporando Camadas Ocultas ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Podemos superar essas limitações dos modelos lineares e lidar com uma classe mais geral de funções incorporando uma ou mais camadas ocultas. A maneira mais fácil de fazer isso é empilhar muitas camadas totalmente conectadas umas sobre as outras. Cada camada alimenta a camada acima dela, até gerarmos resultados. Podemos pensar nas primeiras :math:`L-1` camadas como nossa representação e a camada final como nosso preditor linear. Esta arquitetura é comumente chamada um *perceptron multicamadas*, frequentemente abreviado como *MLP*. Abaixo, representamos um MLP em diagrama (:numref:`fig_mlp`). |Um MLP com uma camada oculta de 5 unidades ocultas.| .. _fig_mlp: Este MLP tem 4 entradas, 3 saídas, e sua camada oculta contém 5 unidades ocultas. Uma vez que a camada de entrada não envolve nenhum cálculo, produzindo saídas com esta rede requer a implementação dos cálculos para as camadas ocultas e de saída; assim, o número de camadas neste MLP é 2. Observe que essas camadas estão totalmente conectadas. Cada entrada influencia cada neurônio na camada oculta, e cada um deles, por sua vez, influencia cada neurônio na camada de saída. No entanto, conforme sugerido por :numref:`subsec_parameterization-cost-fc-layers`, o custo de parametrização de MLPs com camadas totalmente conectadas pode ser proibitivamente alto, o que pode motivar compensação entre o salvamento do parâmetro e a eficácia do modelo, mesmo sem alterar o tamanho de entrada ou saída :cite:`Zhang.Tay.Zhang.ea.2021`. De Linear Para não Linear ~~~~~~~~~~~~~~~~~~~~~~~~~ Como antes, pela matriz :math:`\mathbf{X} \in \mathbb{R}^{n \times d}`, denotamos um *minibatch* de :math:`n` exemplos em que cada exemplo tem :math:`d` entradas (*features*). Para um MLP de uma camada oculta, cuja camada oculta tem :math:`h` unidades ocultas, denotamos por :math:`\mathbf{H} \in \mathbb{R}^{n \times h}` as saídas da camada oculta, que são *representações ocultas*. Em matemática ou código, :math:`\mathbf{H}` também é conhecido como uma *variável de camada oculta* ou uma *variável oculta*. Uma vez que as camadas ocultas e de saída estão totalmente conectadas, temos pesos de camada oculta :math:`\mathbf{W}^{(1)} \in \mathbb{R}^{d \times h}` e *bias* :math:`\mathbf{b}^{(1)} \in \mathbb{R}^{1 \times h}` e pesos da camada de saída :math:`\mathbf{W}^{(2)} \in \mathbb{R}^{h \times q}` e *bias* :math:`\mathbf{b}^{(2)} \in \mathbb{R}^{1 \times q}`. Formalmente, calculamos as saídas :math:`\mathbf{O} \in \mathbb{R}^{n \times q}` do MLP de uma camada oculta da seguinte maneira: .. math:: \begin{aligned} \mathbf{H} & = \mathbf{X} \mathbf{W}^{(1)} + \mathbf{b}^{(1)}, \\ \mathbf{O} & = \mathbf{H}\mathbf{W}^{(2)} + \mathbf{b}^{(2)}. \end{aligned} Observe que depois de adicionar a camada oculta, nosso modelo agora exige que rastreemos e atualizemos conjuntos adicionais de parâmetros. Então, o que ganhamos em troca? Você pode se surpreender ao descobrir que — no modelo definido acima — *nós não ganhamos nada pelos nossos problemas*! O motivo é claro. As unidades ocultas acima são fornecidas por uma função afim das entradas, e as saídas (pré-*softmax*) são apenas uma função afim das unidades ocultas. Uma função afim de uma função afim é em si uma função afim. Além disso, nosso modelo linear já era capaz de representar qualquer função afim. Podemos ver a equivalência formalmente provando que para quaisquer valores dos pesos, podemos apenas recolher a camada oculta, produzindo um modelo de camada única equivalente com parâmetros :math:`\mathbf{W} = \mathbf{W}^{(1)}\mathbf{W}^{(2)}` and :math:`\mathbf{b} = \mathbf{b}^{(1)} \mathbf{W}^{(2)} + \mathbf{b}^{(2)}`: .. math:: \mathbf{O} = (\mathbf{X} \mathbf{W}^{(1)} + \mathbf{b}^{(1)})\mathbf{W}^{(2)} + \mathbf{b}^{(2)} = \mathbf{X} \mathbf{W}^{(1)}\mathbf{W}^{(2)} + \mathbf{b}^{(1)} \mathbf{W}^{(2)} + \mathbf{b}^{(2)} = \mathbf{X} \mathbf{W} + \mathbf{b}. Para perceber o potencial das arquiteturas multicamadas, precisamos de mais um ingrediente chave: a *função de ativação* não linear :math:`\sigma` a ser aplicada a cada unidade oculta seguindo a transformação afim. As saídas das funções de ativação (por exemplo, :math:`\sigma(\cdot)`) são chamadas de *ativações*. Em geral, com as funções de ativação implementadas, não é mais possível reduzir nosso MLP em um modelo linear: .. math:: \begin{aligned} \mathbf{H} & = \sigma(\mathbf{X} \mathbf{W}^{(1)} + \mathbf{b}^{(1)}), \\ \mathbf{O} & = \mathbf{H}\mathbf{W}^{(2)} + \mathbf{b}^{(2)}.\\ \end{aligned} Uma vez que cada linha em :math:`\mathbf{X}` corresponde a um exemplo no *minibatch*, com algum abuso de notação, definimos a não linearidade :math:`\sigma` para aplicar às suas entradas de uma forma em linha, ou seja, um exemplo de cada vez. Observe que usamos a notação para *softmax* da mesma forma para denotar uma operação nas linhas em :numref:`subsec_softmax_vectorization`. Frequentemente, como nesta seção, as funções de ativação que aplicamos a camadas ocultas não são apenas nas linhas, mas elemento a elemento. Isso significa que depois de calcular a parte linear da camada, podemos calcular cada ativação sem olhar para os valores assumidos pelas outras unidades ocultas. Isso é verdadeiro para a maioria das funções de ativação. Para construir MLPs mais gerais, podemos continuar empilhando tais camadas escondidas, por exemplo, :math:`\mathbf{H}^{(1)} = \sigma_1(\mathbf{X} \mathbf{W}^{(1)} + \mathbf{b}^{(1)})` e :math:`\mathbf{H}^{(2)} = \sigma_2(\mathbf{H}^{(1)} \mathbf{W}^{(2)} + \mathbf{b}^{(2)})`, uma sobre a outra, rendendo modelos cada vez mais expressivos. Aproximadores Universais ~~~~~~~~~~~~~~~~~~~~~~~~ MLPs podem capturar interações complexas entre nossas entradas por meio de seus neurônios ocultos, que dependem dos valores de cada uma das entradas. Podemos projetar nós ocultos facilmente para realizar cálculos arbitrários, por exemplo, operações lógicas básicas em um par de entradas. Além disso, para certas escolhas da função de ativação, é amplamente conhecido que os MLPs são aproximadores universais. Mesmo com uma rede de camada única oculta, dados nós suficientes (possivelmente absurdamente muitos deles), e o conjunto certo de pesos, podemos modelar qualquer função, embora realmente aprender essa função seja a parte difícil. Você pode pensar em sua rede neural como sendo um pouco como a linguagem de programação C. A linguagem, como qualquer outra linguagem moderna, é capaz de expressar qualquer programa computável. Mas, na verdade, criar um programa que atenda às suas especificações é a parte difícil. Além disso, só porque uma rede de camada única oculta *pode* aprender qualquer função não significa que você deve tentar resolver todos os seus problemas com redes de camada única oculta. Na verdade, podemos aproximar muitas funções de forma muito mais compacta, usando redes mais profundas (em comparação com redes mais amplas). Trataremos de argumentos mais rigorosos nos capítulos subsequentes. .. _subsec_activation-functions: Funções de Ativação ------------------- As funções de ativação decidem se um neurônio deve ser ativado ou não por calcular a soma ponderada e adicionar ainda o *bias* com ela. Eles são operadores diferenciáveis para transformar sinais de entrada em saídas, enquanto a maioria deles adiciona não linearidade. Como as funções de ativação são fundamentais para o *deep learning*, vamos examinar brevemente algumas funções de ativação comuns. .. |Um MLP com uma camada oculta de 5 unidades ocultas.| image:: ../img/mlp.svg .. raw:: html