Tecnica Jedi per ridurre le reti convoluzionali — pruning

Tecnica Jedi per ridurre le reti convoluzionali — pruning

Hai di nuovo un compito di rilevamento degli oggetti. La priorità è la velocità operativa mantenendo una precisione accettabile. Utilizzi l'architettura YOLOv3 e fai un ulteriore addestramento. La precisione (mAp75) è superiore a 0,95. Ma la velocità di esecuzione è ancora bassa. Accidenti.

Oggi eviteremo la quantizzazione. Sotto il post daremo uno sguardo a Model Pruning — potatura delle parti ridondanti della rete per velocizzare l'Inference senza perdita di precisione. In modo visivo — da dove, quanto e come possiamo rimuovere. Analizzeremo come farlo manualmente e dove possiamo automatizzare. Alla fine — un repository su keras.

Introduzione

Nel mio precedente lavoro presso Macroscop a Perm, ho acquisito un'abitudine: monitorare sempre i tempi di esecuzione degli algoritmi. E i tempi di esecuzione delle reti devono sempre essere controllati tramite un filtro di adeguatezza. Di solito, i modelli state-of-the-art non superano questo filtro, il che mi ha portato al Pruning.

Il Pruning è un tema vecchio, di cui si parlava nelle lezioni di Stanford del 2017. L'idea principale è quella di ridurre le dimensioni di una rete addestrata senza perdere precisione attraverso la rimozione di vari nodi. Sembra interessante, ma raramente sento parlare del suo utilizzo. Forse mancano implementazioni, non ci sono articoli in russo o semplicemente tutti considerano il pruning come un know-how e tacciono.
Ma andiamo a esplorare

Uno sguardo alla biologia

Mi piace quando nel Deep Learning si introducono idee derivate dalla biologia. A queste, come all'evoluzione, ci si può fidare (sapevi che ReLU è molto simile alla funzione di attivazione dei neuroni nel cervello?)

Il processo di Model Pruning è anch'esso vicino alla biologia. La reazione della rete qui può essere paragonata alla plasticità del cervello. Ci sono alcuni esempi interessanti nel libro di Norman Doidge:

  1. Il cervello di una donna, nata con solo metà, ha riprogrammato se stesso per eseguire le funzioni della metà mancante.
  2. Un ragazzo si è sparato in testa, colpendo la parte del cervello responsabile della vista. Col tempo, altre parti del cervello hanno preso in carico tali funzioni. (non cerchiamo di ripetere)

Così puoi rimuovere parte delle convoluzioni dalla tua modella. In casi estremi, le convoluzioni rimanenti potranno sostituire quelle rimosse.

Preferisci il Transfer Learning o addestri da zero?

Opzione numero uno. Utilizzi il Transfer Learning su Yolov3. Retina, Mask-RCNN o U-Net. Ma molto spesso non abbiamo bisogno di riconoscere 80 classi di oggetti, come nel COCO. Nella mia esperienza, ci limitiamo a 1-2 classi. Si potrebbe supporre che l'architettura per 80 classi sia eccessiva qui. Il pensiero di ridurre l'architettura si impone. Inoltre, si desidera fare ciò senza perdere i pesi pre-addestrati esistenti.

Opzione numero due. Potresti avere molti dati e risorse computazionali o semplicemente una architettura super personalizzata. Non importa. Ma stai addestrando la rete da zero. L'ordine abituale è analizzare la struttura dei dati, selezionare un'architettura sovrabbondante in potenza e spingere i dropout contro il sovra-adattamento. Ho visto dropout a 0,6, Carl.

In entrambi i casi la rete può essere ridotta. Ti abbiamo motivato. Ora andiamo a capire che cos'è la potatura.

Algoritmo generale

Abbiamo deciso che possiamo rimuovere convoluzioni. Sembra abbastanza semplice:

Tecnica Jedi per ridurre le reti convoluzionali — pruning

Rimuovere qualsiasi convoluzione è uno stress per la rete, che di solito porta a un certo aumento dell'errore. Da un lato, questo aumento dell'errore è un indicatore di quanto correttamente stiamo rimuovendo le convoluzioni (ad esempio, un grande aumento indica che stiamo facendo qualcosa di sbagliato). Ma un lieve aumento è piuttosto accettabile e spesso viene eliminato con un successivo leggero riaddestramento con un LR ridotto. Aggiungiamo un passo per il riaddestramento:

Tecnica Jedi per ridurre le reti convoluzionali — pruning

Ora dobbiamo capire quando vogliamo interrompere il nostro ciclo Learning<->Pruning. Qui possono esserci varianti esotiche, quando dobbiamo ridurre la rete a una certa dimensione e velocità di esecuzione (ad esempio, per dispositivi mobili). Tuttavia, la variante più comune è continuare il ciclo finché l'errore non supera i limiti accettabili. Aggiungiamo una condizione:

Tecnica Jedi per ridurre le reti convoluzionali — pruning

Quindi, l'algoritmo diventa chiaro. Resta da capire come determinare le convoluzioni da rimuovere.

Ricerca delle convoluzioni da rimuovere

Dobbiamo rimuovere alcune convoluzioni. Andare a colpo sicuro e "sparare" a qualsiasi convoluzione è un'idea sbagliata, anche se funzionerà. Ma poiché abbiamo la testa, possiamo pensare e provare a identificare le convoluzioni "deboli" da rimuovere. Ci sono diverse opzioni:

  1. Minimo L1 o low_magnitude_pruning. L'idea è che le convoluzioni con valori di pesi bassi contribuiscono poco alla decisione finale.
  2. Minimo L1 con medio e deviazione standard. Completiamo la valutazione della distribuzione.
  3. Mascheramento delle convoluzioni ed esclusione delle meno influenti sulla precisione finale.. Una definizione più precisa delle convoluzioni insignificanti, ma piuttosto costosa in termini di tempo e risorse.
  4. Altre

Ognuna delle opzioni ha diritto di esistere e le proprie caratteristiche di implementazione. Qui analizzeremo l'opzione con il minimo L1.

Processo manuale per YOLOv3

Nell'architettura originale ci sono blocchi residui. Ma per quanto possano essere utili per le reti profonde, possono darci qualche problema. La difficoltà sta nel non poter rimuovere le convoluzioni con indici diversi in questi strati:

Tecnica Jedi per ridurre le reti convoluzionali — pruning

Pertanto, evidenziamo gli strati da cui possiamo rimuovere liberamente le convoluzioni:

Tecnica Jedi per ridurre le reti convoluzionali — pruning

Ora creiamo un ciclo di lavoro:

  1. Scarichiamo le attivazioni
  2. Stabiliamo quanto tagliare
  3. Tagliamo
  4. Alleniamo per 10 epoche con LR=1e-4
  5. Testiamo

Scaricare le convoluzioni è utile per valutare quale parte possiamo rimuovere in un determinato passo. Esempi di scarico:

Tecnica Jedi per ridurre le reti convoluzionali — pruning

Osserviamo che quasi ovunque il 5% delle convoluzioni ha una norma L1 molto bassa e possiamo rimuoverle. A ogni passo, questo scarico veniva ripetuto e veniva effettuata una valutazione di quali strati e quanto si poteva rimuovere.

L'intero processo si è svolto in 4 passi (qui e ovunque i numeri sono per RTX 2060 Super):

PassomAp75Numero di parametri, mlnDimensione della rete, mbDall'iniziale, %Tempo di esecuzione, msCondizione di pruning
00.965660241100180
10.962255218911755% del totale
20.962550197831685% del totale
30.9633391556415515% per strati con oltre 400 convoluzioni
40.9555311245114610% per strati con oltre 100 convoluzioni

Al secondo passo è emerso un effetto positivo: è riuscito a far entrare nella memoria un batch size di 4, che ha notevolmente accelerato il processo di riaddestramento.
Nel quarto passo, il processo si è fermato, poiché anche un lungo riaddestramento non ha aumentato il mAp75 ai valori precedenti.
In definitiva, siamo riusciti ad accelerare l'inferenza di 15%, ridurre la dimensione di 35% e non perdere in precisione.

Automazione per architetture più semplici

Per architetture di rete più semplici (senza blocchi condizionali add, concatenate e residual) è possibile orientarsi al trattamento di tutti gli strati convoluzionali e automatizzare il processo di rimozione delle convoluzioni.

Questa opzione l'ho implementata qui.
È semplice: a voi serve solo la funzione di perdita, l'ottimizzatore e i generatori di batch:

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)

È possibile modificare i parametri di configurazione se necessario:

{
    "input_model_path": "model.h5",
    "output_model_path": "model_pruned.h5",
    "finetuning_epochs": 10, # il numero di epoche per l'allenamento tra i passi di pruning
    "stop_loss": 0.1, # perdita per fermare il processo
    "pruning_percent_step": 0.05, # parte delle convoluzioni da eliminare a ogni passo di pruning
    "pruning_standart_deviation_part": 0.2 # limite per la parte di pruning
}

In aggiunta, è stato implementato un limite basato sulla deviazione standard. L'obiettivo è limitare la parte rimossa, escludendo le convoluzioni con misure L1 già "sufficienti":

Tecnica Jedi per ridurre le reti convoluzionali — pruning

In questo modo, permettiamo di rimuovere solo le convoluzioni deboli dalle distribuzioni simili a quella di destra, senza influenzare la rimozione dalle distribuzioni simili a quella di sinistra:

Tecnica Jedi per ridurre le reti convoluzionali — pruning

Avvicinandosi a una distribuzione normale, il coefficiente pruning_standart_deviation_part può essere scelto da:

Tecnica Jedi per ridurre le reti convoluzionali — pruning
Consiglio di utilizzare una tolleranza di 2 sigma. Oppure si può non fare riferimento a questa caratteristica, mantenendo il valore < 1.0.

In output, arriva un grafico della dimensione della rete, delle perdite e del tempo di esecuzione della rete durante tutto il test, normalizzati a 1.0. Ad esempio, qui la dimensione della rete è stata ridotta di quasi la metà senza perdita di qualità (una piccola rete convoluzionale con 100k pesi):

Tecnica Jedi per ridurre le reti convoluzionali — pruning

La velocità di esecuzione è soggetta a normali fluttuazioni e in sostanza non è cambiata. Ciò ha una spiegazione:

  1. Il numero di convoluzioni cambia da valori possibili (32, 64, 128) a valori non così comodi per le schede grafiche — 27, 51 ecc. Potrei sbagliarmi, ma è probabile che questo influisca.
  2. L'architettura non è ampia, ma sequenziale. Riducendo la larghezza, non tocchiamo la profondità. Di conseguenza, riduciamo il carico senza modificare la velocità.

Pertanto, il miglioramento si è tradotto in una riduzione del carico CUDA durante l'esecuzione dal 20 al 30%, ma non in una riduzione del tempo di esecuzione.

Risultati

Riflettiamo. Abbiamo esaminato 2 opzioni di pruning — per YOLOv3 (quando è necessario lavorare manualmente) e per reti con architetture più semplici. È evidente che in entrambi i casi si possono ottenere dimensioni di rete inferiori e velocizzazione senza perdere precisione. Risultati:

  • Riduzione delle dimensioni
  • Accelerazione dell'esecuzione
  • Riduzione del carico CUDA
  • Di conseguenza, sostenibilità (Optimize utilization of computational resources. Celebrates one Greta Thunberg)

Appendice

  • Dopo il passo di pruning è possibile effettuare anche la quantizzazione (ad esempio con TensorRT)
  • Tensorflow offre opportunità per low_magnitude_pruning. Funziona.
  • Repository Voglio sviluppare e sarei felice di ricevere aiuto

Fonte: habr.com

Acquista hosting affidabile per siti web con protezione DDoS, server VPS VDS 🔥 Acquista hosting affidabile per siti web con protezione DDoS, server VPS VDS | ProHoster