Técnica Jedi para redução de redes convolucionais - poda

Técnica Jedi para redução de redes convolucionais - poda

Antes de você novamente está a tarefa de detectar objetos. A prioridade é a velocidade de operação com precisão aceitável. Você pega a arquitetura YOLOv3 e a treina ainda mais. A precisão (mAp75) é superior a 0.95. Mas a taxa de execução ainda é baixa. Besteira.

Hoje vamos ignorar a quantização. E sob o corte vamos olhar Poda de modelo — cortar partes redundantes da rede para acelerar a inferência sem perda de precisão. Está claro onde, quanto e como cortar. Vamos descobrir como fazer isso manualmente e onde você pode automatizá-lo. No final há um repositório no keras.

Introdução

No meu local de trabalho anterior, Macroscop em Perm, adquiri um hábito - monitorar sempre o tempo de execução dos algoritmos. E verifique sempre o tempo de execução da rede através de um filtro de adequação. Geralmente o que há de mais moderno em produção não passa por esse filtro, o que me levou ao Pruning.

A poda é um tema antigo que foi discutido em Palestras de Stanford em 2017. A ideia principal é reduzir o tamanho da rede treinada sem perder precisão, removendo vários nós. Parece legal, mas raramente ouço falar de seu uso. Provavelmente, não há implementações suficientes, não há artigos em russo ou simplesmente todos consideram isso um know-how de poda e permanecem em silêncio.
Mas vamos desmontar isso

Um vislumbre da biologia

Adoro quando o Deep Learning analisa ideias que vêm da biologia. Eles, assim como a evolução, são confiáveis ​​(você sabia que o ReLU é muito semelhante ao função de ativação de neurônios no cérebro?)

O processo de Poda Modelo também está próximo da biologia. A resposta da rede aqui pode ser comparada à plasticidade do cérebro. Existem alguns exemplos interessantes no livro. Norman Doidge:

  1. O cérebro de uma mulher que nasceu com apenas uma metade se reprogramou para desempenhar as funções da metade que faltava.
  2. O cara disparou na parte do cérebro responsável pela visão. Com o tempo, outras partes do cérebro assumiram essas funções. (não estamos tentando repetir)

Da mesma forma, você pode eliminar algumas das convoluções fracas do seu modelo. Como último recurso, os feixes restantes ajudarão a substituir os cortados.

Você adora o Transfer Learning ou está aprendendo do zero?

Opção número um. Você usa o Transfer Learning no Yolov3. Retina, Máscara-RCNN ou U-Net. Mas na maioria das vezes não precisamos reconhecer 80 classes de objetos como no COCO. Na minha prática, tudo se limita às séries 1-2. Pode-se supor que a arquitetura para 80 classes seja redundante aqui. Isso sugere que a arquitetura precisa ser menor. Além disso, gostaria de fazer isso sem perder os pesos pré-treinados existentes.

Opção número dois. Talvez você tenha muitos dados e recursos computacionais ou apenas precise de uma arquitetura superpersonalizada. Não importa. Mas você está aprendendo a rede do zero. O procedimento usual é observar a estrutura de dados, selecionar uma arquitetura com potência EXCESSIVA e eliminar as desistências do retreinamento. Eu vi 0.6 desistências, Karl.

Em ambos os casos, a rede pode ser reduzida. Motivado. Agora vamos descobrir que tipo de poda de circuncisão é

Algoritmo geral

Decidimos que poderíamos remover os pacotes. Parece bem simples:

Técnica Jedi para redução de redes convolucionais - poda

A remoção de qualquer convolução é estressante para a rede, o que geralmente leva a algum aumento no erro. Por um lado, esse aumento no erro é um indicador de quão corretamente removemos as convoluções (por exemplo, um grande aumento indica que estamos fazendo algo errado). Mas um pequeno aumento é bastante aceitável e muitas vezes é eliminado pelo treinamento adicional leve subsequente com um LR pequeno. Adicione uma etapa de treinamento adicional:

Técnica Jedi para redução de redes convolucionais - poda

Agora precisamos descobrir quando queremos interromper nosso loop de Aprendizagem<->Remoção. Pode haver opções exóticas aqui quando precisarmos reduzir a rede a um determinado tamanho e velocidade (por exemplo, para dispositivos móveis). Contudo, a opção mais comum é continuar o ciclo até que o erro se torne superior ao aceitável. Adicione uma condição:

Técnica Jedi para redução de redes convolucionais - poda

Então, o algoritmo fica claro. Resta descobrir como determinar as convoluções excluídas.

Procure por pacotes excluídos

Precisamos remover algumas convoluções. Apressar-se e “atirar” em alguém é uma má ideia, embora funcione. Mas como você tem cabeça, pode pensar e tentar selecionar convoluções “fracas” para remoção. Existem várias opções:

  1. Menor medida L1 ou poda_de_magnitude baixa. A ideia de que convoluções com pesos pequenos contribuem pouco para a decisão final
  2. Menor medida L1 levando em consideração a média e o desvio padrão. Complementamos com uma avaliação da natureza da distribuição.
  3. Mascarando convoluções e excluindo aquelas que menos influenciam a precisão final. Determinação mais precisa de convoluções insignificantes, mas que consome muito tempo e recursos.
  4. Outros

Cada uma das opções tem direito à vida e características próprias de implementação. Aqui consideramos a opção com a menor medida L1

Processo manual para YOLOv3

A arquitetura original contém blocos residuais. Mas não importa o quão legais sejam para redes profundas, elas nos atrapalharão um pouco. A dificuldade é que você não pode excluir reconciliações com índices diferentes nestas camadas:

Técnica Jedi para redução de redes convolucionais - poda

Portanto, vamos selecionar camadas das quais podemos excluir livremente as reconciliações:

Técnica Jedi para redução de redes convolucionais - poda

Agora vamos construir um ciclo de trabalho:

  1. Fazendo upload de ativações
  2. Descobrir quanto cortar
  3. Cortar fora
  4. Aprendendo 10 épocas com LR = 1e-4
  5. Teste

Descarregar convoluções é útil para estimar quanta peça podemos remover em uma determinada etapa. Exemplos de descarregamento:

Técnica Jedi para redução de redes convolucionais - poda

Vemos que quase em todos os lugares 5% das convoluções têm uma norma L1 muito baixa e podemos removê-las. A cada etapa, esse descarregamento era repetido e era feita uma avaliação de quais camadas e quantas poderiam ser recortadas.

Todo o processo foi concluído em 4 etapas (números aqui e em todos os lugares para o RTX 2060 Super):

Passo mapa75 Número de parâmetros, milhões Tamanho da rede, MB Do inicial, % Tempo de execução, senhora Condição de circuncisão
0 0.9656 60 241 100 180 -
1 0.9622 55 218 91 175 5% de tudo
2 0.9625 50 197 83 168 5% de tudo
3 0.9633 39 155 64 155 15% para camadas com mais de 400 convoluções
4 0.9555 31 124 51 146 10% para camadas com mais de 100 convoluções

Um efeito positivo foi adicionado à etapa 2 - o tamanho do lote 4 cabe na memória, o que acelerou bastante o processo de treinamento adicional.
Na etapa 4, o processo foi interrompido porque mesmo o treinamento adicional de longo prazo não elevou o mAp75 aos valores antigos.
Como resultado, conseguimos acelerar a inferência 15%, reduza o tamanho em 35% e não perder exatamente.

Automação para arquiteturas mais simples

Para arquiteturas de rede mais simples (sem adição condicional, concatenação e blocos residuais), é bem possível focar no processamento de todas as camadas convolucionais e automatizar o processo de corte de convoluções.

Eu implementei esta opção aqui.
É simples: você só precisa de uma função de perda, um otimizador e geradores de lote:

import pruning
from keras.optimizers import Adam
from keras.utils import Sequence

train_batch_generator = BatchGenerator...
score_batch_generator = BatchGenerator...

opt = Adam(lr=1e-4)
pruner = pruning.Pruner("config.json", "categorical_crossentropy", opt)

pruner.prune(train_batch, valid_batch)

Se necessário, você pode alterar os parâmetros de configuração:

{
    "input_model_path": "model.h5",
    "output_model_path": "model_pruned.h5",
    "finetuning_epochs": 10, # the number of epochs for train between pruning steps
    "stop_loss": 0.1, # loss for stopping process
    "pruning_percent_step": 0.05, # part of convs for delete on every pruning step
    "pruning_standart_deviation_part": 0.2 # shift for limit pruning part
}

Além disso, é implementada uma limitação baseada no desvio padrão. O objetivo é limitar a parte que é removida, excluindo convoluções com medidas L1 já “suficientes”:

Técnica Jedi para redução de redes convolucionais - poda

Assim, permitimos remover apenas convoluções fracas de distribuições semelhantes à direita e não afetar a remoção de distribuições semelhantes à esquerda:

Técnica Jedi para redução de redes convolucionais - poda

Quando a distribuição se aproxima do normal, o coeficiente pruning_standart_deviation_part pode ser selecionado entre:

Técnica Jedi para redução de redes convolucionais - poda
Eu recomendo uma suposição de 2 sigma. Ou você pode ignorar esse recurso, deixando o valor <1.0.

A saída é um gráfico do tamanho da rede, perda e tempo de execução da rede para todo o teste, normalizado para 1.0. Por exemplo, aqui o tamanho da rede foi reduzido quase 2 vezes sem perda de qualidade (pequena rede convolucional com pesos de 100k):

Técnica Jedi para redução de redes convolucionais - poda

A velocidade de funcionamento está sujeita a flutuações normais e permanece praticamente inalterada. Existe uma explicação para isso:

  1. O número de circunvoluções muda de conveniente (32, 64, 128) para não o mais conveniente para placas de vídeo - 27, 51, etc. Posso estar errado aqui, mas provavelmente isso tem um efeito.
  2. A arquitetura não é ampla, mas consistente. Ao reduzir a largura, não afetamos a profundidade. Assim, reduzimos a carga, mas não alteramos a velocidade.

Portanto, a melhoria foi expressa em uma redução na carga CUDA durante a execução em 20-30%, mas não em uma redução no tempo de execução

Resultados de

Vamos refletir. Consideramos 2 opções de poda - para YOLOv3 (quando você precisa trabalhar com as mãos) e para redes com arquiteturas mais simples. Pode-se observar que em ambos os casos é possível obter redução e aceleração do tamanho da rede sem perda de precisão. Resultados:

  • Downsizing
  • Execução de aceleração
  • Reduzindo a carga CUDA
  • Como resultado, respeito ao meio ambiente (otimizamos o uso futuro dos recursos de computação. Em algum lugar alguém fica feliz Greta Tunberg)

Apêndice

  • Após a etapa de poda, você pode adicionar quantização (por exemplo, com TensorRT)
  • Tensorflow fornece recursos para poda_de_baixa_magnitude. Funciona.
  • repositório Quero desenvolver e ficarei feliz em ajudar

Fonte: habr.com

Adicionar um comentário