AI-For-Beginners/translations/pt-PT/lessons/4-ComputerVision/08-TransferLearning/TrainingTricks.md

108 lines
9.9 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Truques para Treino em Deep Learning
À medida que as redes neurais se tornam mais profundas, o processo de treino torna-se cada vez mais desafiador. Um dos principais problemas é o chamado [vanishing gradients](https://en.wikipedia.org/wiki/Vanishing_gradient_problem) ou [exploding gradients](https://deepai.org/machine-learning-glossary-and-terms/exploding-gradient-problem#:~:text=Exploding%20gradients%20are%20a%20problem,updates%20are%20small%20and%20controlled.). [Este artigo](https://towardsdatascience.com/the-vanishing-exploding-gradient-problem-in-deep-neural-networks-191358470c11) oferece uma boa introdução a esses problemas.
Para tornar o treino de redes profundas mais eficiente, existem algumas técnicas que podem ser utilizadas.
## Manter valores num intervalo razoável
Para tornar os cálculos numéricos mais estáveis, é importante garantir que todos os valores dentro da rede neural estejam numa escala razoável, tipicamente [-1..1] ou [0..1]. Não é um requisito muito rígido, mas a natureza dos cálculos em ponto flutuante é tal que valores de magnitudes diferentes não podem ser manipulados com precisão juntos. Por exemplo, se somarmos 10<sup>-10</sup> e 10<sup>10</sup>, é provável que obtenhamos 10<sup>10</sup>, porque o valor menor seria "convertido" para a mesma ordem do maior, e assim a mantissa seria perdida.
A maioria das funções de ativação tem não-linearidades em torno de [-1..1], e por isso faz sentido escalar todos os dados de entrada para o intervalo [-1..1] ou [0..1].
## Inicialização de Pesos
Idealmente, queremos que os valores permaneçam na mesma faixa após passarem pelas camadas da rede. Assim, é importante inicializar os pesos de forma a preservar a distribuição dos valores.
A distribuição normal **N(0,1)** não é uma boa ideia, porque se tivermos *n* entradas, o desvio padrão da saída seria *n*, e os valores provavelmente sairão do intervalo [0..1].
As seguintes inicializações são frequentemente utilizadas:
* Distribuição uniforme -- `uniform`
* **N(0,1/n)** -- `gaussian`
* **N(0,1/√n_in)** garante que para entradas com média zero e desvio padrão de 1, a mesma média/desvio padrão será mantida
* **N(0,√2/(n_in+n_out))** -- chamada **Xavier initialization** (`glorot`), ajuda a manter os sinais na faixa durante a propagação direta e reversa
## Normalização por Lote
Mesmo com uma inicialização adequada dos pesos, estes podem ficar arbitrariamente grandes ou pequenos durante o treino, levando os sinais para fora da faixa apropriada. Podemos trazer os sinais de volta utilizando uma das técnicas de **normalização**. Embora existam várias delas (Normalização de Pesos, Normalização de Camadas), a mais utilizada é a Normalização por Lote.
A ideia da **normalização por lote** é considerar todos os valores do minibatch e realizar a normalização (ou seja, subtrair a média e dividir pelo desvio padrão) com base nesses valores. É implementada como uma camada na rede que realiza essa normalização após aplicar os pesos, mas antes da função de ativação. Como resultado, é provável que se obtenha maior precisão final e treino mais rápido.
Aqui está o [artigo original](https://arxiv.org/pdf/1502.03167.pdf) sobre normalização por lote, a [explicação na Wikipedia](https://en.wikipedia.org/wiki/Batch_normalization), e [um bom artigo introdutório](https://towardsdatascience.com/batch-normalization-in-3-levels-of-understanding-14c2da90a338) (e outro [em russo](https://habrahabr.ru/post/309302/)).
## Dropout
**Dropout** é uma técnica interessante que remove uma certa percentagem de neurónios aleatórios durante o treino. É implementada como uma camada com um parâmetro (percentagem de neurónios a remover, tipicamente 10%-50%), e durante o treino zera elementos aleatórios do vetor de entrada antes de passá-lo para a próxima camada.
Embora possa parecer uma ideia estranha, pode-se observar o efeito do dropout no treino de um classificador de dígitos MNIST no notebook [`Dropout.ipynb`](../../../../../lessons/4-ComputerVision/08-TransferLearning/Dropout.ipynb). Ele acelera o treino e permite alcançar maior precisão em menos épocas de treino.
Este efeito pode ser explicado de várias formas:
* Pode ser considerado como um fator de choque aleatório para o modelo, que o tira de mínimos locais
* Pode ser considerado como *média implícita de modelos*, porque durante o dropout estamos a treinar modelos ligeiramente diferentes
> *Algumas pessoas dizem que quando uma pessoa bêbada tenta aprender algo, ela lembra-se melhor no dia seguinte, comparando com uma pessoa sóbria, porque um cérebro com alguns neurónios a funcionar mal tenta adaptar-se melhor para captar o significado. Nunca testámos se isto é verdade ou não.*
## Prevenir overfitting
Um dos aspetos muito importantes do deep learning é ser capaz de prevenir o [overfitting](../../3-NeuralNetworks/05-Frameworks/Overfitting.md). Embora possa ser tentador usar um modelo de rede neural muito poderoso, devemos sempre equilibrar o número de parâmetros do modelo com o número de amostras de treino.
> Certifique-se de que compreende o conceito de [overfitting](../../3-NeuralNetworks/05-Frameworks/Overfitting.md) que introduzimos anteriormente!
Existem várias formas de prevenir o overfitting:
* Paragem antecipada -- monitorizar continuamente o erro no conjunto de validação e parar o treino quando o erro de validação começar a aumentar.
* Decaimento explícito de pesos / Regularização -- adicionar uma penalização extra à função de perda para valores absolutos altos de pesos, o que previne resultados muito instáveis no modelo
* Média de Modelos -- treinar vários modelos e depois fazer a média dos resultados. Isto ajuda a minimizar a variância.
* Dropout (Média Implícita de Modelos)
## Otimizadores / Algoritmos de Treino
Outro aspeto importante do treino é escolher um bom algoritmo de treino. Embora o **gradient descent** clássico seja uma escolha razoável, pode por vezes ser demasiado lento ou resultar em outros problemas.
Em deep learning, utilizamos **Stochastic Gradient Descent** (SGD), que é um gradient descent aplicado a minibatches, selecionados aleatoriamente do conjunto de treino. Os pesos são ajustados utilizando esta fórmula:
w<sup>t+1</sup> = w<sup>t</sup> - η∇ℒ
### Momentum
No **momentum SGD**, mantemos uma parte do gradiente dos passos anteriores. É semelhante a quando nos movemos com inércia e recebemos um empurrão numa direção diferente; a nossa trajetória não muda imediatamente, mas mantém parte do movimento original. Aqui introduzimos outro vetor v para representar a *velocidade*:
* v<sup>t+1</sup> = γ v<sup>t</sup> - η∇ℒ
* w<sup>t+1</sup> = w<sup>t</sup>+v<sup>t+1</sup>
Aqui o parâmetro γ indica até que ponto consideramos a inércia: γ=0 corresponde ao SGD clássico; γ=1 é uma equação de movimento puro.
### Adam, Adagrad, etc.
Como em cada camada multiplicamos os sinais por uma matriz W<sub>i</sub>, dependendo de ||W<sub>i</sub>||, o gradiente pode diminuir e ser próximo de 0, ou crescer indefinidamente. Este é o cerne do problema de Exploding/Vanishing Gradients.
Uma das soluções para este problema é usar apenas a direção do gradiente na equação e ignorar o valor absoluto, ou seja:
w<sup>t+1</sup> = w<sup>t</sup> - η(∇ℒ/||∇ℒ||), onde ||∇ℒ|| = √∑(∇ℒ)<sup>2</sup>
Este algoritmo é chamado **Adagrad**. Outros algoritmos que utilizam a mesma ideia: **RMSProp**, **Adam**
> **Adam** é considerado um algoritmo muito eficiente para muitas aplicações, por isso, se não tiver certeza de qual usar - use Adam.
### Gradient clipping
Gradient clipping é uma extensão da ideia acima. Quando ||∇ℒ|| ≤ θ, consideramos o gradiente original na otimização dos pesos, e quando ||∇ℒ|| > θ - dividimos o gradiente pela sua norma. Aqui θ é um parâmetro; na maioria dos casos podemos usar θ=1 ou θ=10.
### Decaimento da taxa de aprendizagem
O sucesso do treino muitas vezes depende do parâmetro de taxa de aprendizagem η. É lógico assumir que valores maiores de η resultam em treino mais rápido, algo que normalmente queremos no início do treino, e depois valores menores de η permitem ajustar melhor a rede. Assim, na maioria dos casos queremos diminuir η durante o processo de treino.
Isto pode ser feito multiplicando η por um número (ex. 0.98) após cada época de treino, ou utilizando um **cronograma de taxa de aprendizagem** mais complicado.
## Diferentes Arquiteturas de Redes
Selecionar a arquitetura certa para o seu problema pode ser complicado. Normalmente, escolhemos uma arquitetura que já tenha demonstrado funcionar para a nossa tarefa específica (ou uma semelhante). Aqui está uma [boa visão geral](https://www.topbots.com/a-brief-history-of-neural-network-architectures/) de arquiteturas de redes neurais para visão computacional.
> É importante selecionar uma arquitetura que seja suficientemente poderosa para o número de amostras de treino que temos. Escolher um modelo demasiado poderoso pode resultar em [overfitting](../../3-NeuralNetworks/05-Frameworks/Overfitting.md).
Outra boa abordagem seria usar uma arquitetura que se ajuste automaticamente à complexidade necessária. Até certo ponto, as arquiteturas **ResNet** e **Inception** são autoajustáveis. [Mais sobre arquiteturas de visão computacional](../07-ConvNets/CNN_Architectures.md)
**Aviso Legal**:
Este documento foi traduzido utilizando o serviço de tradução por IA [Co-op Translator](https://github.com/Azure/co-op-translator). Embora nos esforcemos para garantir a precisão, esteja ciente de que traduções automáticas podem conter erros ou imprecisões. O documento original no seu idioma nativo deve ser considerado a fonte autoritativa. Para informações críticas, recomenda-se a tradução profissional humana. Não nos responsabilizamos por quaisquer mal-entendidos ou interpretações incorretas resultantes do uso desta tradução.