Riconoscimento delle immagini di Lab pratico
Si noti che questa esercitazione richiede la versione master più recente o il prossimo CNTK 1.7 che verrà rilasciato presto. Un download binario intermedio è disponibile nelle istruzioni per l'esercitazione CNTK Hands-On KDD che questa esercitazione è stata originariamente progettata per.
lab Hands-On: riconoscimento delle immagini con reti convoluzionali, normalizzazione batch e reti residui
Questo lab pratico illustra come implementare il riconoscimento delle immagini basate sulla convoluzione con CNTK. Si inizierà con un'architettura comune di riconoscimento delle immagini convoluzionali, si aggiunge la normalizzazione batch e si estenderà in una rete residuale (ResNet-20).
Le tecniche che si praticano includono:
- modifica di una definizione di rete CNTK per aggiungere un'operazione predefinita (elenco a discesa)
- creazione di funzioni definite dall'utente per estrarre parti ripetitive in una rete in un modulo riutilizzabile
- implementazione di strutture di rete personalizzate (connessione ignora resNet)
- creazione di molti livelli contemporaneamente tramite cicli ricorsivi
- training parallelo
- reti convolutzionali
- normalizzazione batch
Prerequisiti
Si supponga di aver già installato CNTK e di poter eseguire il comando CNTK. Questa esercitazione è stata tenuta a KDD 2016 e richiede una compilazione recente, vedere qui per istruzioni sulla configurazione. È sufficiente seguire le istruzioni per scaricare un pacchetto di installazione binaria da tale pagina. Per le attività correlate all'immagine, è consigliabile eseguire questa operazione in un computer con una GPU compatibile con CUDA.
Scaricare quindi un archivio ZIP (circa 12 MB): fare clic su questo collegamento e quindi sul pulsante Scarica.
L'archivio contiene i file per questa esercitazione. Archiviare e impostare la directory di lavoro su ImageHandsOn
.
Verranno usati i file seguenti:
ImageHandsOn.cntk
: il file di configurazione CNTK verrà presentato di seguito e verrà introdotto.cifar10.pretrained.cmf
: modello risultante della configurazione che verrà avviata.cifar10.ResNet.cmf
: modello risultante della versione resnet che verrà creata di seguito.
Infine, è necessario scaricare e convertire il set di dati CIFAR-10. Il passaggio di conversione richiede circa 10 minuti. Eseguire i due script Python seguenti che saranno disponibili anche nella directory di lavoro:
wget -rc http://www.cs.toronto.edu/~kriz/cifar-10-python.tar.gz
tar xvf www.cs.toronto.edu/~kriz/cifar-10-python.tar.gz
python CifarConverter.py cifar-10-batches-py
In questo modo le immagini vengono convertite in file PNG, 50000 per il training e 10000 per il test, che verranno inserite rispettivamente nelle due directory seguenti: cifar-10-batches-py/data/train
e cifar-10-batches-py/data/test
.
Struttura del modello
Verrà avviata questa esercitazione con un semplice modello convoluzionale. È costituito da 3 livelli di 5x5 convoluzioni + non linearità + riduzione della dimensione 2x per 3x3 max-pooling, che vengono quindi seguiti da un livello nascosto denso e una trasformazione densa per formare l'input a un classificatore softmax a 10 vie.
In alternativa, come descrizione di rete CNTK. Si prega di avere un'occhiata rapida e di corrispondervi con la descrizione precedente:
featNorm = features - Constant (128)
l1 = ConvolutionalLayer {32, (5:5), pad = true, activation = ReLU,
init = "gaussian", initValueScale = 0.0043} (featNorm)
p1 = MaxPoolingLayer {(3:3), stride = (2:2)} (l1)
l2 = ConvolutionalLayer {32, (5:5), pad = true, activation = ReLU,
init = "gaussian", initValueScale = 1.414} (p1)
p2 = MaxPoolingLayer {(3:3), stride = (2:2)} (l2)
l3 = ConvolutionalLayer {64, (5:5), pad = true, activation = ReLU,
init = "gaussian", initValueScale = 1.414} (p2)
p3 = MaxPoolingLayer {(3:3), stride = (2:2)} (l3)
d1 = DenseLayer {64, activation = ReLU, init = "gaussian", initValueScale = 12} (p3)
z = LinearLayer {10, init = "gaussian", initValueScale = 1.5} (d1)
È possibile trovare altre informazioni su questi operatori qui: ConvolutionalLayer{}
, , MaxPoolingLayer{}
, LinearLayer{}
DenseLayer{}
.
configurazione CNTK
File di configurazione
Per eseguire il training e testare un modello in CNTK, è necessario fornire un file di configurazione che indica CNTK le operazioni da eseguire (command
variabile) e una sezione dei parametri per ogni comando.
Per il comando di training, CNTK deve essere detto:
- come leggere i dati (
reader
sezione) - la funzione del modello e i relativi input e output nel grafico di calcolo (
BrainScriptNetworkBuilder
sezione) - parametri hyper-parameters per il learner (
SGD
sezione)
Per il comando di valutazione, CNTK deve conoscere:
- come leggere i dati di test (
reader
sezione) - metriche da valutare (
evalNodeNames
parametro)
Di seguito è riportato il file di configurazione che verrà avviato. Come si vede, un file di configurazione CNTK è un file di testo costituito da definizioni di parametri, organizzati in una gerarchia di record. È anche possibile vedere come CNTK supporta la sostituzione dei parametri di base usando la $parameterName$
sintassi. Il file effettivo contiene solo alcuni parametri più indicati in precedenza, ma analizzarlo e individuare gli elementi di configurazione appena menzionati:
# CNTK Configuration File for training a simple CIFAR-10 convnet.
# During the hands-on tutorial, this will be fleshed out into a ResNet-20 model.
command = TrainConvNet:Eval
makeMode = false ; traceLevel = 0 ; deviceId = "auto"
rootDir = "." ; dataDir = "$rootDir$" ; modelDir = "$rootDir$/Models"
modelPath = "$modelDir$/cifar10.cmf"
# Training action for a convolutional network
TrainConvNet = {
action = "train"
BrainScriptNetworkBuilder = {
imageShape = 32:32:3
labelDim = 10
model (features) = {
featNorm = features - Constant (128)
l1 = ConvolutionalLayer {32, (5:5), pad=true, activation=ReLU,
init="gaussian", initValueScale=0.0043} (featNorm)
p1 = MaxPoolingLayer {(3:3), stride=(2:2)} (l1)
l2 = ConvolutionalLayer {32, (5:5), pad=true, activation=ReLU,
init="gaussian", initValueScale=1.414} (p1)
p2 = MaxPoolingLayer {(3:3), stride=(2:2)} (l2)
l3 = ConvolutionalLayer {64, (5:5), pad=true, activation=ReLU,
init="gaussian", initValueScale=1.414} (p2)
p3 = MaxPoolingLayer {(3:3), stride=(2:2)} (l3)
d1 = DenseLayer {64, activation=ReLU, init="gaussian", initValueScale=12} (p3)
z = LinearLayer {10, init="gaussian", initValueScale=1.5} (d1)
}.z
# inputs
features = Input {imageShape}
labels = Input {labelDim}
# apply model to features
z = model (features)
# connect to system
ce = CrossEntropyWithSoftmax (labels, z)
errs = ErrorPrediction (labels, z)
featureNodes = (features)
labelNodes = (labels)
criterionNodes = (ce)
evaluationNodes = (errs)
outputNodes = (z)
}
SGD = {
epochSize = 50000
maxEpochs = 30 ; minibatchSize = 64
learningRatesPerSample = 0.00015625*10:0.000046875*10:0.000015625
momentumAsTimeConstant = 600*20:6400
L2RegWeight = 0.03
firstMBsToShowResult = 10 ; numMBsToShowResult = 100
}
reader = {
verbosity = 0 ; randomize = true
deserializers = ({
type = "ImageDeserializer" ; module = "ImageReader"
file = "$dataDir$/cifar-10-batches-py/train_map.txt"
input = {
features = { transforms = (
{ type = "Crop" ; cropType = "RandomSide" ; sideRatio = 0.8 ; jitterType = "UniRatio" } :
{ type = "Scale" ; width = 32 ; height = 32 ; channels = 3 ; interpolations = "linear" } :
{ type = "Transpose" }
)}
labels = { labelDim = 10 }
}
})
}
}
# Eval action
Eval = {
action = "eval"
minibatchSize = 16
evalNodeNames = errs
reader = {
verbosity = 0 ; randomize = true
deserializers = ({
type = "ImageDeserializer" ; module = "ImageReader"
file = "$dataDir$/cifar-10-batches-py/test_map.txt"
input = {
features = { transforms = (
{ type = "Scale" ; width = 32 ; height = 32 ; channels = 3 ; interpolations = "linear" } :
{ type = "Transpose" }
)}
labels = { labelDim = 10 }
}
})
}
}
Lettura dati e dati
Dopo aver scaricato i dati CIFAR-10 ed eseguire lo CifarConverter.py
script come richiesto all'inizio di questa esercitazione, è disponibile una directory denominata cifar-10-batches-py/data
, che contiene due sottodirectory train
e test
, pieno di file PNG.
L'CNTK ImageDeserializer
usa i formati di immagine standard.
Sono disponibili anche due file train_map.txt
e test_map.txt
. Guardando a quest'ultimo,
% more cifar-10-batches-py/test_map.txt
cifar-10-batches-py/data/test/00000.png 3
cifar-10-batches-py/data/test/00001.png 8
cifar-10-batches-py/data/test/00002.png 8
...
Entrambi i file sono costituiti da due colonne, in cui il primo contiene il percorso del file di immagine e la seconda etichetta di classe, come indice numerico. Queste colonne corrispondono agli input features
del lettore e labels
che sono stati definiti come:
features = { transforms = (
{ type = "Crop" ; cropType = "RandomSide" ; sideRatio = 0.8 ; jitterType = "UniRatio" } :
{ type = "Scale" ; width = 32 ; height = 32 ; channels = 3 ; interpolations = "linear" } :
{ type = "Transpose" }
)}
labels = { labelDim = 10 }
La sezione aggiuntiva transforms
indica all'utente di ImageDeserializer
applicare una sequenza di trasformazioni (comuni) alle immagini durante la lettura.
Per altre informazioni, vedere qui.
Esecuzione
È possibile trovare il file di configurazione precedente sotto il nome ImageHandsOn.cntk
nella cartella di lavoro.
Per eseguirlo, eseguire la configurazione precedente tramite questo comando:
cntk configFile=ImageHandsOn.cntk
Lo schermo verrà vivo con un flurry di messaggi di log (CNTK può essere parlativo a volte), ma se tutto è andato bene, verrà presto visualizzato questo:
Training 116906 parameters in 10 out of 10 parameter tensors and 28 nodes with gradient
seguito dall'output simile al seguente:
Finished Epoch[ 1 of 10]: [Training] ce = 1.66950797 * 50000; errs = 61.228% * 50000
Finished Epoch[ 2 of 10]: [Training] ce = 1.32699016 * 50000; errs = 47.394% * 50000
Finished Epoch[ 3 of 10]: [Training] ce = 1.17140398 * 50000; errs = 41.168% * 50000
Questo indica che sta imparando. Ogni epoca rappresenta un passaggio attraverso le immagini di training 50000.
Indica inoltre che dopo la seconda epoca, il criterio di training, che la configurazione denominata ce
, ha raggiunto 1,33 come misurato sui 50000 campioni di questo periodo, e che la frequenza di errore è del 47% su quelli stessi 50000 campioni di training.
Si noti che i computer solo CPU sono circa 20 volte più lente. Sarà necessario molti minuti finché non viene visualizzato anche il primo output del log. Per assicurarsi che il sistema sia in corso, è possibile abilitare la traccia per visualizzare i risultati parziali, che dovrebbero essere visualizzati ragionevolmente rapidamente:
cntk configFile=ImageHandsOn.cntk traceLevel=1
Epoch[ 1 of 10]-Minibatch[-498- 1, 0.13%]: ce = 2.30260658 * 64; errs = 90.625% * 64
...
Epoch[ 1 of 10]-Minibatch[ 1- 100, 12.80%]: ce = 2.10434176 * 5760; errs = 78.472% * 5760
Epoch[ 1 of 10]-Minibatch[ 101- 200, 25.60%]: ce = 1.82372971 * 6400; errs = 68.172% * 6400
Epoch[ 1 of 10]-Minibatch[ 201- 300, 38.40%]: ce = 1.69708496 * 6400; errs = 62.469% * 6400
Al termine del training (che richiede circa 3 minuti su un Surface Book e in un computer desktop con una GPU Titan-X), il messaggio finale sarà simile al seguente:
Finished Epoch[10 of 10]: [Training] ce = 0.74679766 * 50000; errs = 25.486% * 50000
che mostra che la rete ha ridotto correttamente la ce
perdita e ha raggiunto un errore di classificazione del 25,5% nel set di training. Poiché la command
variabile specifica un secondo comando Eval
, CNTK procederà con tale azione. Misura la frequenza di errore di classificazione sulle immagini 10000 del set di test.
Final Results: Minibatch[1-625]: errs = 24.180% * 10000
La frequenza di errore di test è vicina al training. Poiché CIFAR-10 è un set di dati piuttosto piccolo, questo è un indicatore che il modello non ha ancora convergente completamente (e in effetti, l'esecuzione per 30 epoche otterrà circa il 20%).
Se non si vuole attendere il completamento, è possibile eseguire un modello intermedio, ad esempio.
cntk configFile=ImageHandsOn.cntk command=Eval modelPath=Models/cifar10.cmf.5
Final Results: Minibatch[1-625]: errs = 31.710% * 10000
o eseguire anche il modello pre-sottoposto a training:
cntk configFile=ImageHandsOn.cntk command=Eval modelPath=cifar10.pretrained.cmf
Final Results: Minibatch[1-625]: errs = 24.180% * 10000
Modifica del modello
Di seguito verranno fornite attività per praticare la modifica delle configurazioni di CNTK. Le soluzioni vengono fornite alla fine di questo documento... ma provare senza!
Attività 1: Aggiunta dell'eliminazione
Una tecnica comune per migliorare la generalizzabilità dei modelli è a discesa. Per aggiungere l'eliminazione a un modello di CNTK, è necessario
- aggiungere una chiamata alla funzione
Dropout()
CNTK in cui si vuole inserire l'operazione di rilascio - aggiungere un parametro
dropoutRate
allaSGD
sezione chiamata per definire la probabilità di eliminazione
In questa attività specifica specifica specificare nessun dropout per il primo periodo 1, seguito da un tasso di rilascio del 50%.
Per informazioni su come eseguire questa operazione, vedere la Dropout()
documentazione.
Se tutto è andato bene, si osserverà nessun cambiamento per la prima 1 epoca, ma un miglioramento molto minore di ce
una volta a dropout calci in con la seconda epoca. Si tratta di un comportamento previsto. Per questa configurazione specifica, l'accuratezza del riconoscimento non migliora, in realtà. Il risultato finale quando l'allenamento ha circa 10 epoche è circa il 32%.
10 epoche non sono sufficienti per l'eliminazione.
Vedere la soluzione qui.
Attività 2: Semplificare la definizione del modello estraendo parti ripetitive in una funzione
Nell'esempio la sequenza (Convolution >> ReLU >> Pooling) viene ripetuta tre volte. L'attività consiste nel scrivere una funzione BrainScript che raggruppa queste tre operazioni in un modulo riutilizzabile. Si noti che tutti e tre gli usi di questa sequenza usano parametri diversi (dimensioni di output, peso di inizializzazione). Pertanto, la funzione scritta deve accettare questi due parametri, oltre ai dati di input. Ad esempio, potrebbe essere definito come
MyLayer (x, depth, initValueScale)
Quando si esegue questa operazione, si prevede che i valori e errs
risultanti ce
siano uguali.
Tuttavia, se si esegue sulla GPU, non determinism nell'implementazione back-propagazione di cuDNN causerà variazioni minori.
Vedere la soluzione qui.
Attività 3: Aggiunta di BatchNormalization
Questa attività richiede una GPU, poiché l'implementazione della normalizzazione batch di CNTK è basata su cuDNN.
La normalizzazione batch è una tecnica popolare per velocizzare e migliorare la convergenza.
In CNTK la normalizzazione batch viene implementata come BatchNormalizationLayer{}
.
Il modulo spaziale (in cui tutte le posizioni pixel vengono normalizzate con parametri condivisi) viene richiamato da un parametro facoltativo: BatchNormalizationLayer{spatialRank=2}
.
Aggiungere la normalizzazione batch a tutti e tre i livelli di convoluzione e tra i due livelli densi.
Si noti che la normalizzazione batch deve essere inserita direttamente prima della non linearità.
Di conseguenza, è necessario rimuovere il activation
parametro e invece inserire chiamate esplicite alla funzione ReLU()
CNTK .
Inoltre, la normalizzazione batch cambia la velocità di convergenza. Quindi, aumentare i tassi di apprendimento per i primi 7 periodi 3 volte e disabilitare la regolarizzazione e L2 impostando i parametri su 0.
Quando si esegue, verranno visualizzati altri parametri appresi elencati nel log di training. Il risultato finale sarà intorno al 28%, ovvero 4 punti migliori rispetto a senza normalizzazione batch dopo lo stesso numero di iterazioni. La convergenza accelera effettivamente.
Vedere la soluzione qui.
Attività 4: Convertire in rete rimanente
La configurazione precedente è un esempio "toy" per ottenere le mani sporche con l'esecuzione e la modifica di configurazioni CNTK e non è stato intenzionalmente eseguito a una convergenza completa per mantenere bassi i tempi di turnaround. Quindi ora passiamo avanti a una configurazione più reale--a Residual Net. Residual Net (https://arxiv.org/pdf/1512.03385v1.pdf) è una struttura di rete profonda modificata in cui i livelli, anziché imparare un mapping dall'input all'output, apprendere un termine di correzione.
Questa attività richiede anche una GPU per l'operazione di normalizzazione batch, anche se si dispone di un sacco di tempo, è possibile provare a eseguirla in una CPU modificando le chiamate di normalizzazione batch, in caso di perdita di accuratezza.
Per iniziare, modificare la configurazione precedente. Sostituire prima di tutto la funzione model(features)
del modello con questa:
MySubSampleBN (x, depth, stride) =
{
s = Splice ((MaxPoolingLayer {(1:1), stride = (stride:stride)} (x) : ConstantTensor (0, (1:1:depth/stride))), axis = 3) # sub-sample and pad: [W x H x depth/2] --> [W/2 x H/2 x depth]
b = BatchNormalizationLayer {spatialRank = 2, normalizationTimeConstant = 4096} (s)
}.b
MyConvBN (x, depth, initValueScale, stride) =
{
c = ConvolutionalLayer {depth, (3:3), pad = true, stride = (stride:stride), bias = false,
init = "gaussian", initValueScale = initValueScale} (x)
b = BatchNormalizationLayer {spatialRank = 2, normalizationTimeConstant = 4096} (c)
}.b
ResNetNode (x, depth) =
{
c1 = MyConvBN (x, depth, 7.07, 1)
r1 = ReLU (c1)
c2 = MyConvBN (r1, depth, 7.07, 1)
r = ReLU (c2)
}.r
ResNetIncNode (x, depth) =
{
c1 = MyConvBN (x, depth, 7.07, 2) # note the 2
r1 = ReLU (c1)
c2 = MyConvBN (r1, depth, 7.07, 1)
r = ReLU (c2)
}.r
model (features) =
{
conv1 = ReLU (MyConvBN (features, 16, 0.26, 1))
rn1 = ResNetNode (ResNetNode (ResNetNode (conv1, 16), 16), 16)
rn2_1 = ResNetIncNode (rn1, 32)
rn2 = ResNetNode (ResNetNode (rn2_1, 32), 32)
rn3_1 = ResNetIncNode (rn2, 64)
rn3 = ResNetNode (ResNetNode (rn3_1, 64), 64)
pool = AveragePoolingLayer {(8:8)} (rn3)
z = LinearLayer {labelDim, init = "gaussian", initValueScale = 0.4} (pool)
}.z
e modificare la configurazione SGD in:
SGD = {
epochSize = 50000
maxEpochs = 160 ; minibatchSize = 128
learningRatesPerSample = 0.0078125*80:0.00078125*40:0.000078125
momentumAsTimeConstant = 1200
L2RegWeight = 0.0001
firstMBsToShowResult = 10 ; numMBsToShowResult = 500
}
L'attività consiste nel modificare ResNetNode()
e ResNetNodeInc()
in modo da implementare la struttura disposta nell'arte ASCII seguente:
ResNetNode ResNetNodeInc
| |
+------+------+ +---------+----------+
| | | |
V | V V
+----------+ | +--------------+ +----------------+
| Conv, BN | | | Conv x 2, BN | | SubSample, BN |
+----------+ | +--------------+ +----------------+
| | | |
V | V |
+-------+ | +-------+ |
| ReLU | | | ReLU | |
+-------+ | +-------+ |
| | | |
V | V |
+----------+ | +----------+ |
| Conv, BN | | | Conv, BN | |
+----------+ | +----------+ |
| | | |
| +---+ | | +---+ |
+--->| + |<---+ +------>+ + +<-------+
+---+ +---+
| |
V V
+-------+ +-------+
| ReLU | | ReLU |
+-------+ +-------+
| |
V V
Confermare con l'output di convalida nel log a cui è stato eseguito correttamente.
Questa operazione richiederà molto tempo per il completamento. L'output previsto sarà simile al seguente:
Finished Epoch[ 1 of 160]: [Training] ce = 1.57037109 * 50000; errs = 58.940% * 50000
Finished Epoch[ 2 of 160]: [Training] ce = 1.06968234 * 50000; errs = 38.166% * 50000
Finished Epoch[ 3 of 160]: [Training] ce = 0.85858969 * 50000; errs = 30.316% * 50000
mentre il modello non corretto senza le connessioni ignora è più simile al seguente:
Finished Epoch[ 1 of 160]: [Training] ce = 1.72901219 * 50000; errs = 66.232% * 50000
Finished Epoch[ 2 of 160]: [Training] ce = 1.30180430 * 50000; errs = 47.424% * 50000
Finished Epoch[ 3 of 160]: [Training] ce = 1.04641961 * 50000; errs = 37.568% * 50000
Vedere la soluzione qui.
Attività 5: Generazione automatica di molti livelli
Infine, resnet con prestazioni ottimali ha 152 livelli. Poiché sarebbe molto noioso ed errore soggetto a scrittura di 152 singole espressioni, verrà ora modificata la definizione per generare automaticamente uno stack di ResNetNode()
.
L'attività consiste nel scrivere una funzione con questa firma:
ResNetNodeStack (x, depth, L)
dove L
indica quanti ResNetNodes
devono essere in pila, in modo che sia possibile sostituire l'espressione per rn1
sopra con una chiamata con parametri:
rn1 = ResNetNodeStack (conv1, 16, 3) # 3 means 3 such nodes
e analogamente per rn2
e rn3
.
Gli strumenti necessari sono l'espressione condizionale :
z = if cond then x else y
e ricorsione.
Questo training verrà eseguito per circa mezzo nostro su un Titan-X. Se l'hai fatto correttamente, all'inizio del log conterrà questo messaggio:
Training 200410 parameters in 51 out of 51 parameter tensors and 112 nodes with gradient:
Per informazioni di riferimento, è inclusa una versione pre-training di questo modello. È possibile misurare la frequenza di errore con questo comando:
cntk configFile=ImageHandsOn.ResNet.cntk command=Eval
e dovrebbe visualizzare un risultato simile al seguente:
Final Results: Minibatch[1-625]: errs = 8.400% * 10000; top5Errs = 0.290% * 10000
Questa frequenza di errore è molto vicina a quella segnalata nel documento ResNet originale (https://arxiv.org/pdf/1512.03385v1.pdftabella 6).
Vedere la soluzione qui.
Attività 6: Training parallelo
Infine, se sono presenti più GPU, CNTK consente di parallelizzare il training usando MPI (Interfaccia di passaggio messaggi). Questo modello è troppo piccolo da aspettare molto velocità senza ulteriore ottimizzazione, ad esempio di dimensioni minibatch (l'impostazione corrente minibatch-size è troppo piccola per ottenere l'utilizzo completo dei core GPU disponibili). Tuttavia, passiamo attraverso i movimenti, in modo da sapere come farlo una volta che si passa ai carichi di lavoro reali.
Aggiungere la riga seguente al SGD
blocco:
SGD = {
...
parallelTrain = {
parallelizationMethod = "DataParallelSGD"
parallelizationStartEpoch = 2
distributedMBReading = true
dataParallelSGD = { gradientBits = 1 }
}
}
e quindi eseguire questo comando:
mpiexec -np 4 cntk configFile=ImageHandsOn.cntk stderr=Models/log parallelTrain=true
in ordine di priorità
Questa esercitazione ha praticato per eseguire una configurazione esistente e modificarla in modi specifici:
- aggiunta di un'operazione predefinita (dropout)
- estrazione di parti ripetitive in moduli riutilizzabili (funzioni)
- refactoring (per inserire la normalizzazione batch)
- strutture di rete personalizzate (ignora connessione ResNet)
- parametrizzazione di strutture ripetitive tramite ricorsione
e abbiamo visto come velocizzare il training in parallelo.
Dove andiamo da qui? Potrebbe essere già stato scoperto che il modello usato in questi esempi, che chiamiamo stile di compilazione grafico , può essere abbastanza soggetto a errori. Individuare l'errore?
model (features) =
{
l1 = ConvolutionalLayer {32, (5:5), pad = true, activation = ReLU,
init = "gaussian", initValueScale = 0.0043} (featNorm)
p1 = MaxPoolingLayer {(3:3), stride = (2:2)} (l1)
l2 = ConvolutionalLayer {64, (5:5), pad = true, activation = ReLU,
init = "gaussian", initValueScale = 1.414} (p1)
p2 = MaxPoolingLayer {(3:3), stride = (2:2)} (l1)
d1 = DenseLayer {64, activation = ReLU, init = "gaussian", initValueScale = 12} (p2)
z = LinearLayer {10, init = "gaussian", initValueScale = 1.5} (d1)
}.z
Un modo per evitare questo errore consiste nell'usare la composizione delle funzioni. Di seguito è riportato un modo più conciso di scrivere lo stesso:
model = Sequential (
ConvolutionalLayer {32, (5:5), pad = true, activation = ReLU,
init = "gaussian", initValueScale = 0.0043} :
MaxPoolingLayer {(3:3), stride = (2:2)} :
ConvolutionalLayer {64, (5:5), pad = true, activation = ReLU,
init = "gaussian", initValueScale = 1.414} :
MaxPoolingLayer {(3:3), stride = (2:2)} :
DenseLayer {64, activation = ReLU, init = "gaussian", initValueScale = 12} :
LinearLayer {10, init = "gaussian", initValueScale = 1.5}
)
Questo stile verrà introdotto e usato nell'esercitazione pratica successiva, Analisi del testo con reti ricorrenti.
Soluzioni
Soluzione 1: Aggiunta dell'elenco a discesa
Modificare la definizione del modello come indicato di seguito:
p3 = MaxPoolingLayer {(3:3), stride = (2:2)} (l3)
d1 = DenseLayer {64, activation = ReLU, init = "gaussian", initValueScale = 12} (p3)
d1_d = Dropout (d1) ##### added
z = LinearLayer {10, init = "gaussian", initValueScale = 1.5} (d1_d) ##### d1 -> d1_d
e la sezione SGD:
SGD = {
...
dropoutRate = 0*5:0.5 ##### added
...
}
Soluzione 2: Semplificare la definizione del modello estraendo parti ripetitive in una funzione
Aggiungere una definizione di funzione come indicato di seguito:
MyLayer (x, depth, initValueScale) =
{
c = ConvolutionalLayer {depth, (5:5), pad = true, activation = ReLU,
init = "gaussian", initValueScale = initValueScale} (x)
p = MaxPoolingLayer {(3:3), stride = (2:2)} (c)
}.p
e aggiornare la definizione del modello per usarla
featNorm = features - Constant (128)
p1 = MyLayer (featNorm, 32, 0.0043) ##### replaced
p2 = MyLayer (p1, 32, 1.414) ##### replaced
p3 = MyLayer (p2, 64, 1.414) ##### replaced
d1 = DenseLayer {64, activation = ReLU, init = "gaussian", initValueScale = 12} (p3)
Soluzione 3: Aggiunta di BatchNormalization
Modificare MyLayer()
:
MyLayer (x, depth, initValueScale) =
{
c = ConvolutionalLayer {depth, (5:5), pad = true, ##### no activation=ReLU
init = "gaussian", initValueScale = initValueScale} (x)
b = BatchNormalizationLayer {spatialRank = 2} (c)
r = ReLU (b) ##### now called explicitly
p = MaxPoolingLayer {(3:3), stride = (2:2)} (r)
}.p
e usarlo. Inserire anche la normalizzazione batch prima z
di :
d1 = DenseLayer {64, init = "gaussian", initValueScale = 12} (p3)
d1_bnr = ReLU (BatchNormalizationLayer {} (d1)) ##### added BN and explicit ReLU
d1_d = Dropout (d1_bnr) ##### d1 -> d1_bnr
z = LinearLayer {10, init = "gaussian", initValueScale = 1.5} (d1_d)
Aggiornare questi parametri nella sezione SGD:
SGD = {
....
learningRatesPerSample = 0.00046875*7:0.00015625*10:0.000046875*10:0.000015625
momentumAsTimeConstant = 0
L2RegWeight = 0
...
}
Soluzione 4: Convertire in rete rimanente
Le implementazioni corrette per ResNetNode()
e ResNetNodeInc()
sono:
ResNetNode (x, depth) =
{
c1 = MyConvBN (x, depth, 7.07, 1)
r1 = ReLU (c1)
c2 = MyConvBN (r1, depth, 7.07, 1)
r = ReLU (x + c2) ##### added skip connection
}.r
ResNetIncNode (x, depth) =
{
c1 = MyConvBN (x, depth, 7.07, 2) # note the 2
r1 = ReLU (c1)
c2 = MyConvBN (r1, depth, 7.07, 1)
xs = MySubSampleBN (x, depth, 2)
r = ReLU (xs + c2) ##### added skip connection
}.r
Soluzione 5: generazione automatica di molti livelli
Questa è l'implementazione:
ResNetNodeStack (x, depth, L) =
{
r = if L == 0
then x
else ResNetNode (ResNetNodeStack (x, depth, L-1), depth)
}.r
o, più breve:
ResNetNodeStack (x, depth, L) =
if L == 0
then x
else ResNetNode (ResNetNodeStack (x, depth, L-1), depth)
È anche necessario modificare la funzione del modello:
conv1 = ReLU (MyConvBN (features, 16, 0.26, 1))
rn1 = ResNetNodeStack (conv1, 16, 3) ##### replaced
rn2_1 = ResNetIncNode (rn1, 32)
rn2 = ResNetNodeStack (rn2_1, 32, 2) ##### replaced
rn3_1 = ResNetIncNode (rn2, 64)
rn3 = ResNetNodeStack (rn3_1, 64, 2) ##### replaced