Référence de couches avec BrainScript
CNTK prédéfinis un certain nombre de « couches » communes, ce qui facilite l’écriture de réseaux simples qui se composent de couches standard superposées les unes sur les autres.
Les couches sont des objets de fonction qui peuvent être utilisés comme des fonctions BrainScript normales, mais qui contiennent des paramètres appris et qui ont une paire supplémentaire de {}
paramètres de construction ou d’attributs supplémentaires.
Par exemple, il s’agit de la description réseau d’un modèle de couche simple à 1 masqué à l’aide de la DenseLayer{}
couche :
h = DenseLayer {1024, activation=ReLU} (features)
p = DenseLayer {9000, activation=Softmax} (h)
qui peut ensuite être utilisé pour l’entraînement par rapport à un critère d’entropie croisée :
ce = CrossEntropy (labels, p)
Si votre réseau est une concaténation droite d’opérations (plusieurs sont), vous pouvez utiliser l’alternative
Sequential()
notation :
myModel = Sequential (
DenseLayer {1024, activation=ReLU} :
DenseLayer {9000, activation=Softmax}
)
et appelez-le comme suit :
p = myModel (features)
Exemples de modèles
L’exemple suivant montre un balisage d’emplacement qui incorpore une séquence de mots, le traite avec un LSTM récurrent, puis classifie chaque mot :
taggingModel = Sequential (
EmbeddingLayer {150} : # embed into a 150-dimensional vector
RecurrentLSTMLayer {300} : # forward LSTM
DenseLayer {labelDim} # word-wise classification
)
Et voici un réseau convolutionnel simple pour la reconnaissance d’images :
convNet = Sequential (
# 3 layers of convolution and dimension reduction by pooling
ConvolutionalLayer {32, (5:5), pad=true, activation=ReLU} :
MaxPoolingLayer {(3:3), stride=(2:2)} :
ConvolutionalLayer {32, (5:5), pad=true, activation=ReLU} :
MaxPoolingLayer {(3:3), stride=(2:2)} :
ConvolutionalLayer {64, (5:5), pad=true, activation=ReLU} :
MaxPoolingLayer {(3:3), stride=(2:2)} :
# 2 dense layers for classification
DenseLayer {64, activation=ReLU} :
LinearLayer {10}
)
Partage de paramètres
Si vous affectez une couche à une variable et que vous l’utilisez à plusieurs emplacements, les paramètres seront partagés. Si vous dites
lay = DenseLayer {1024, activation=Sigmoid}
h1 = lay (x)
h2 = lay (h1) # same weights as `h1`
h1
et h2
partage les mêmes paramètres, comme lay()
c’est la même fonction dans les deux cas.
Dans le cas ci-dessus, ce n’est probablement pas ce qui a été souhaité, donc soyez conscient.
Si les deux appels ci-dessus lay()
sont destinés à avoir des paramètres différents, n’oubliez pas de définir deux instances distinctes, par exemple lay1 = DenseLayer{...}
et lay2 = DenseLayer{...}
.
Pourquoi ce comportement ?
Les couches permettent de partager des paramètres entre les sections d’un modèle.
Considérez un modèle DSSM qui traite deux images d’entrée, par exemple doc
query
et de manière identique avec la même chaîne de traitement, et compare les vecteurs masqués résultants :
imageToVec = Sequential (
ConvolutionalLayer {32, (5:5), pad = true, activation = ReLU} :
MaxPoolingLayer {(3:3), stride = (2:2)} :
ConvolutionalLayer {64, (5:5), pad = true, activation = ReLU} :
MaxPoolingLayer {(3:3), stride = (2:2)} :
DenseLayer {64, activation = ReLU} :
LinearLayer {10}
)
zDoc = imageToVec (doc)
zQuery = imageToVec (query) # same model as for zDoc
sim = CosDistance (zdoc, zQuery)
où imageToVec
est la partie du modèle qui convertit les images en vecteur plat.
imageToVec
est un objet de fonction qui contient à son tour plusieurs objets de fonction (par exemple, trois instances de ConvolutionalLayer{}
).
imageToVec
est instancié une seule fois, et cette instance contient les paramètres appris de tous les objets de fonction inclus. Les deux appels partageront model()
ces paramètres dans l’application, et leurs dégradés seront la somme des deux appels.
Enfin, notez que si dans l’exemple query
ci-dessus et doc
doit avoir les mêmes dimensions, car elles sont traitées par le biais du même objet de fonction, et que la première couche de cet objet de fonction a sa dimension d’entrée déduite pour correspondre à celle des deux query
et doc
.
Si leurs dimensions diffèrent, ce réseau est mal formé et l’inférence/validation de dimension échoue avec un message d’erreur.
Note d’implémentation
De nombreuses couches sont des wrappers autour des primitives CNTK sous-jacentes, ainsi que les paramètres accessibles en fonction des besoins respectifs. Par exemple, ConvolutionalLayer{}
encapsule la Convolution()
primitive.
Les avantages de l’utilisation de couches sont les suivants :
- les couches contiennent des paramètres apprenants de la dimension correcte
- les couches sont composables (cf.
Sequential()
)
DenseLayer{}, LinearLayer{}
Fonction Factory pour créer une couche entièrement connectée.
DenseLayer{}
prend avec une non-linéarité facultative.
DenseLayer {outDim, activation=Identity, init='glorotUniform', initValueScale=1, bias=true}
LinearLayer {outDim, init='glorotUniform', initValueScale=1, bias=true}
Paramètres
-
outDim
: dimension de sortie de cette couche -
activation
(DenseLayer{}
uniquement) : passez ici une fonction à utiliser comme fonction d’activation, telle queactivation=ReLU
-
init
('heNormal'
|'glorotUniform'
|...): type d’initialisation pour les pondérations. Consultez cette page pour obtenir la liste complète des options d’initialisation. -
initValueScale
: l’initialisation aléatoire de variance est multipliée par ce -
bias
: si la valeur est false, n’incluez pas de paramètre de biais
Valeur renvoyée
Fonction qui implémente la couche entièrement connectée souhaitée. Consultez la description.
Description
Utilisez ces fonctions d’usine pour créer une couche entièrement connectée.
Utilisez DenseLayer{}
si vous souhaitez qu’une fonction d’activation soit incluse; sinon LinearLayer{}
.
Chacune de ces fonctions de fabrique crée un objet de fonction qui contient une matrice de poids appris et, sauf si bias=false
, un biais appris. L’objet de fonction peut être utilisé comme une fonction, qui implémente l’une de ces formules :
DenseLayer{...} (v) = activation (W * v + b)
LinearLayer{...} (v) = W * v + b
où W
est une matrice de poids de dimension [outDim x (dimension of v)]
, b
est le biais de la dimension [outdim]
, et la valeur résultante a une dimension (ou des dimensions de tenseur) comme indiqué par outDim
.
Prise en charge de Tensor
Si la fonction retournée est appliquée à une entrée d’un rang > de tenseur 1, par exemple une image 2D, W
aura la dimension [outDim x (first dimension of input) x (second dimension of input) x ...]
.
En revanche, outDim
il peut s’agir d’un vecteur qui spécifie des dimensions de tenseur, par exemple (10:10)
.
Dans ce cas, W
aura la dimension [outDim[0] x outDim[1] x ... x (dimension of input)]
, et b
aura les dimensions [outDim[0] x outDim[1] x ...]
de tenseur .
CNTK produit de matrice interprétera ces dimensions de sortie ou d’entrée supplémentaires comme s’ils étaient aplati en un long vecteur.
Pour plus d’informations sur ce sujet, consultez la documentation de Times()
Exemple :
h = DenseLayer {1024, activation=Sigmoid) (v)
ou alternativement :
Layer = DenseLayer {1024, activation=Sigmoid)
h = Layer (v)
ConvolutionalLayer{}
Crée une couche de convolution avec une non-linéarité facultative.
ConvolutionalLayer {numOutputChannels, filterShape,
activation = Identity,
init = 'glorotUniform', initValueScale = 1,
stride = 1, pad = false, lowerPad = 0, upperPad = 0,
bias = true}
Paramètres
-
numOutputChannels
: nombre de canaux de sortie (nombre de filtres) -
filterShape
: étendue spatiale du filtre, par exemple(5:5)
pour un filtre 2D. La dimension de canal d’entrée n’est pas incluse ici. -
activation
: non linéaire facultative, par exempleactivation=ReLU
-
init
('heNormal'
|'glorotUniform'
|...): type d’initialisation aléatoire pour les pondérations. Consultez cette page pour obtenir la liste complète des options d’initialisation aléatoire. -
initValueScale
: l’initialisation aléatoire de variance est multipliée par ce -
stride
: incrémente lors du glissement du filtre sur l’entrée. Par exemple(2:2)
, pour réduire les dimensions de 2 -
pad
: si elle n’est pas définie (valeur par défaut), le filtre est décalé sur la zone d’entrée « valide », autrement dit, aucune valeur en dehors de la zone n’est utilisée. S’ilpad
est défini d’un autre côté, le filtre est appliqué à toutes les positions d’entrée, et les valeurs en dehors de la région valide sont considérées comme zéro. -
lowerPad
,upperPad
: spécifiez explicitement différentes marges pour le remplissage. Les filtres seront déplacés sur une région valide qui est (pratiquement) augmentée avec des zéros. Par exemple,lowerPad=(1:2)
ajoute une colonne de zéros et deux lignes de zéros. La dimension de la sortie est étendue en conséquence. -
bias
: si la valeur est false, n’incluez pas de paramètre de biais
Valeur renvoyée
Fonction qui implémente la couche entièrement connectée souhaitée. Consultez la description.
Description
Utilisez ces fonctions de fabrique pour créer une couche de convolution.
La couche résultante applique une opération de convolution sur un tenseur ndimensionnel.
L’appelant spécifie l’extension spatiale du filtre.
Un ensemble de filtres d’une étendue spatiale donnée (par exemple (5:5)
) est corrélé avec chaque emplacement de l’entrée (par exemple, une [640 x 480]
image dimensionnée).
En supposant que le remplissage est activé (pad
) et que les strides sont 1, cela génère une région de sortie de la même dimension ([640 x 480]
).
En règle générale, de nombreux filtres sont appliqués en même temps.
numOutputChannels
spécifie le nombre. Par conséquent, pour chaque emplacement d’entrée, un vecteur entier est numOutputChannels
produit.
Pour notre exemple ci-dessus, la valeur numOutputChannels
64 se trouve dans un [640 x 480 x 64]
tenseur dimensionné.
Ce dernier axe est appelé dimension de canal.
Lorsque la convolution est appliquée à une entrée avec une dimension de canal, chaque filtre se compose également de vecteurs de la dimension de canal de l’entrée.
Par exemple, lors de l’application d’une convolution avec une étendue de filtre spatial spécifiée d’une (5:5)
[640 x 480 x 3]
image de couleur dimensionnée, chaque filtre est un [5 x 5 x 3]
tenseur.
Tous les numOutputChannels
filtres empilés ensemble sont appelés noyau.
Dans notre exemple, la forme du noyau sera [5 x 5 x 3 x 64]
.
L’exemple suivant récapitule la relation entre les différentes dimensions et formes :
input shape : [ (spatial dims) x (#input channels) ]
spatial extent : [ (filterShape) ]
output shape : [ (spatial dims) x x numOutputChannels ]
kernel shape : [ (filterShape) x (#input channels) x numOutputChannels ]
dans notre exemple :
input shape : [ 640 x 480 x 3 ]
spatial extent : [ 5 x 5 ]
output shape : [ 640 x 480 x x numOutputChannels ]
kernel shape : [ 5 x 5 x 3 x numOutputChannels ]
Remplissage
Si le remplissage n’est pas activé, la région de sortie est réduite par les emplacements de limite auxquels l’étendue de filtre complète ne peut pas être appliquée. Par exemple, l’application d’un (5:5)
filtre -extent à une image sans remplissage, les 2 lignes et colonnes de pixels les plus externes entraînent l’application du filtre hors limites.
Par conséquent, ConvolutionalLayer{}
réduit les dimensions en conséquence.
Une [640 x 480]
image associée à un (5:5)
filtre sans remplissage laisse une [636 x 476]
région de sortie dimensionnée.
Progrès
Les stride
paramètres spécifient l’incrément des filtres.
Les valeurs stridentes supérieures à un entraînent un sous-échantillonnage de la région de sortie.
Par exemple, le filtrage d’une [640 x 480]
image avec un pas à (2:2)
pas entraîne une [320 x 240]
zone dimensionnée avec remplissage et [318 x 238]
sans remplissage.
Notes
Cette couche est un wrapper autour de la Convolution()
primitive.
Le nom des paramètres du noyau de filtre, comme indiqué dans la section de validation du journal, se termine par .W
.
La dimension n’est pas affichée comme [ (filterShape) x (#input channels) x numOutputChannels ]
décrit ci-dessus, mais à la place [ numOutputChannels x ((product over filter shape) * (#input channels)) ]'.
Exemple :
c = ConvolutionalLayer {64, (3:3), pad = true, stride = (1:1), bias=false} (x)
DeconvLayer{}
Crée une couche de décovolution.
DeconvLayer {numOutputChannels,
filterShape, numInputChannels,
bias = true,
activation = (x=>x),
init = 'glorotUniform',
initValueScale = 0.001,
initBias = 0,
stride = 1, autoPadding = false,
lowerPad = 0, upperPad = 0,
maxTempMemSizeInSamples = 0}
Paramètres
-
numOutputChannels
: nombre de canaux de sortie (nombre de filtres) -
filterShape
: étendue spatiale du filtre, par exemple(5:5)
pour un filtre 2D. La dimension de canal d’entrée n’est pas incluse ici. -
numInputChannels
: nombre de canaux d’entrée (nombre de filtres du volume d’entrée) -
bias
: si la valeur est false, n’incluez pas de paramètre de biais -
activation
: non linéaire facultative, par exempleactivation=ReLU
-
init
('heNormal'
|'glorotUniform'
|...): type d’initialisation aléatoire pour les pondérations. Consultez cette page pour obtenir la liste complète des options d’initialisation aléatoire. -
initValueScale
: l’initialisation aléatoire de variance est multipliée par ce -
initBias
: valeur initiale pour le biais -
stride
: incrémente lors du glissement du filtre sur l’entrée. Par exemple(2:2)
, pour réduire les dimensions de 2 -
autoPadding
: si elle n’est pas définie (valeur par défaut), le filtre est décalé sur la zone d’entrée « valide », autrement dit, aucune valeur en dehors de la zone n’est utilisée. S’ilautoPadding
est défini d’un autre côté, le filtre est appliqué à toutes les positions d’entrée, et les valeurs en dehors de la région valide sont considérées comme zéro. -
lowerPad
,upperPad
: spécifiez explicitement différentes marges pour le remplissage pour le volume de sortie, c’est-à-dire celles utilisées pour l’entrée dans la couche convolutionnelle correspondante. Il est important de les définir en correspondance avec la couche convolutionnelle pour obtenir les mêmes dimensions de tenseur.
Valeur renvoyée
Fonction qui implémente la couche entièrement connectée souhaitée. Consultez la description.
Description
Utilisez ces fonctions de fabrique pour créer une couche de déconvolution.
La couche résultante applique une opération de déconvolution sur un tenseur ndimensionnel.
Cette couche est un wrapper autour de la Convolution()
primitive avec deconv=true
.
Une casse populaire pour la déconvolution est la reconstruction d’une image (voir ici par exemple). Lorsque la convolution prend une région de champ réceptive 2D d’entrée et calcule la corrélation avec un filtre 2D, la décovolution prend un pixel et la répartit sur une région 2D.
Considérez une image p(.,.), un emplacement de pixel (x,y) et un filtre centré [3 x 3] avec le contenu suivant (aucune dimension de profondeur de carte de caractéristiques pour l’instant, c’est-à-dire un seul canal) :
[ . . c
a b .
. . . ]
Ici, un b et c sont des poids du filtre, '.' Correspond à un poids nul. Convolution() calcule le pixel de sortie q(x, y) à l’emplacement (x, y) comme suit :
q(x,y) = b * p(x,y) + a * p(x-1,y) + c * p(x+1,y-1)
La déconvolution prend des pixels q(x,y) et les répartit sur une région autour (x,y). Si nous avons utilisé le même filtre, cela apporterait les contributions suivantes à la sortie r(x,y) :
r(x,y) += b * q(x,y)
r(x-1,y) += a * q(x,y)
r(x+1,y-1) += c * q(x,y)
Sachant que la même chose s’applique à tous les x et y sur le plan, nous pouvons exprimer ceci pour r(x,y) :
r(x,y) += b * q(x,y)
r(x,y) += a * q(x+1,y)
r(x,y) += c * q(x-1,y+1)
ou au total,
r(x,y) = b * q(x,y) + a * q(x+1,y) + c * q(x-1,y+1)
Cela a la même forme que la Convolution ci-dessus, sauf que le filtre est mis en miroir le long des deux axes.
Maintenant, nous introduisons des mappages de fonctionnalités dans la combinaison. C’est facile : au lieu d’aller de la profondeur d’entrée à la profondeur de sortie, nous allons dans l’autre sens.
En résumé, Convolution (W, x) == Deconvolution (W', x), où
W : [W x H x C x K]
et
W’ = W
avec ses valeurs réorganisés comme suit : [(W mirrored) x (H mirrored) x K x C]
C’est-à-dire ce que deconvolution() fait implicitement :
- échanger les deux dimensions de profondeur (transposer)
- les dimensions spatiales (inverser l’ordre des données)
- Convolution() avec ces
Exemple :
deconv_A = DeconvLayer {inputDim, (5:5), cMap1, lowerPad=(2:2:0), upperPad=(2:2:0)}(unpool_A)
Consultez l’encodeur automatique d’image à l’aide de Deconvolution et Unpooling pour obtenir un exemple détaillé et parcourir.
MaxPoolingLayer{}, AveragePoolingLayer{}
Fonctions d’usine pour créer une couche max- ou moyenne de regroupement.
MaxPoolingLayer {poolShape, stride = 1, pad = false, lowerPad = 0, upperPad = 0}
AveragePoolingLayer {poolShape, stride = 1, pad = false, lowerPad = 0, upperPad = 0} =
Paramètres
-
poolShape
: forme de la région à mettre en pool, par exemple(2:2)
-
stride
: incrémente lors du glissement du pool sur l’entrée. Par exemple(2:2)
, pour réduire les dimensions de 2 -
pad
: si elle n’est pas définie (valeur par défaut), le pool est décalé sur la zone d’entrée « valide », autrement dit, aucune valeur en dehors de la zone n’est utilisée. Sipad
elle est définie d’un autre côté, le pool est appliqué à toutes les positions d’entrée, et les valeurs en dehors de la région valide sont considérées comme zéro. Pour le regroupement moyen, le nombre de valeurs moyennes n’inclut pas de valeurs rembourrées. -
lowerPad
,upperPad
: spécifiez explicitement différentes marges pour le remplissage. Les filtres seront déplacés sur une région valide qui est (pratiquement) augmentée avec des zéros. Par exemple,lowerPad=(1:2)
ajoute une colonne de zéros et deux lignes de zéros. La dimension de la sortie est étendue en conséquence.
Valeur renvoyée
Fonction qui implémente la couche de regroupement souhaitée. Consultez la description.
Description
Utilisez cette fonction de fabrique pour créer une opération de regroupement. Permet MaxPoolingLayer{}
de calculer le maximum sur les valeurs de la zone du pool et AveragePoolingLayer{}
de prendre leur moyenne.
L’opération de regroupement fait glisser une « fenêtre de pool » sur les emplacements d’une région d’entrée et calcule la valeur maximale ou la moyenne des valeurs dans la région de pool correspondante.
Cette opération est structurellement très similaire à la convolution, sauf que l’opération appliquée à la fenêtre glissante est d’une nature différente.
Toutes les considérations relatives aux dimensions d’entrée, au remplissage et aux strides s’appliquent de manière identique. Pour plus d’informations, consultez ConvolutionalLayer{}
cette rubrique.
Exemple :
p = MaxPoolingLayer {(3:3), stride=(2:2)} (c)
MaxUnpoolingLayer{}
Crée une couche max-unooling.
MaxUnpoolingLayer {poolShape,
stride = 1, pad = false,
lowerPad = 0, upperPad = 0}
Paramètres
-
poolShape
: forme de la région à dépooler (taille de la région de sortie ), par exemple(2:2)
-
stride
: incrémente lors du glissement du pool sur la sortie. Par exemple,(2:2)
pour augmenter les dimensions de 2 -
pad
: s’il n’est pas défini (valeur par défaut), le pool est décalé sur la zone de sortie « valide », autrement dit, aucune valeur en dehors de la zone n’est utilisée. -
lowerPad
,upperPad
: spécifiez explicitement différentes marges pour le remplissage. Les filtres supposent une région de sortie valide qui est (pratiquement) augmentée.
Valeur renvoyée
Fonction qui implémente la couche de mise en pool souhaitée. Consultez la description.
Description
Utilisez cette fonction de fabrique pour créer une opération de dépooling.
L’opération de dépooling est l’inverse d’une opération de regroupement. Il nécessite deux entrées : la sortie de sa couche de regroupement correspondante, par exemple p1
et l’entrée de sa couche de regroupement correspondante, par exemple r1
. Il fait glisser une « fenêtre de pool inverse » sur les emplacements de son entrée p1
, et projette la valeur à cette position de la région de sortie qui avait la valeur maximale dans l’opération de regroupement correspondante, c’est-à-dire dans r1
. La deuxième entrée r1
est requise dans CNTK pour déterminer la cible de l’opération Depooling, car CNTK ne stocke pas les variables de commutateur (voir ici pour plus d’informations).
Exemple :
unpool_A = MaxUnpoolingLayer {(2:2), stride=(2:2)}(deconv_B, relu_A)
Consultez l’encodeur automatique d’image à l’aide de Deconvolution et Unpooling pour obtenir un exemple détaillé et parcourir.
EmbeddingLayer{}
EmbeddingLayer {outDim,
init='glorotUniform', initValueScale=1,
embeddingPath = '', transpose = false}
Paramètres
-
outDim
: dimension du vecteur d’incorporation souhaité -
init
('heNormal'
|'glorotUniform'
|...): type d’initialisation pour les pondérations. Consultez cette page pour obtenir la liste complète des options d’initialisation. -
initValueScale
: l’initialisation aléatoire de variance est multipliée par ce -
embeddingPath
: s’il est donné, les incorporations ne sont pas apprises, mais chargées à partir d’un fichier et non mises à jour au cours de l’entraînement -
transpose
: permet de charger des incorporations stockées sous forme transposeuse
Valeur renvoyée
Fonction qui implémente la couche d’incorporation. Consultez la description.
Description
« Incorporation » fait référence à la représentation de mots ou d’autres éléments discrets par des vecteurs continus denses. Cette couche suppose que l’entrée est sous forme à chaud. Par exemple, pour une taille de vocabulaire de 10 000, chaque vecteur d’entrée doit avoir la dimension 10 000 et comporter des zéros, sauf pour une position contenant un 1. L’index de cet emplacement est l’index du mot ou de l’élément qu’il représente.
Dans CNTK, les vecteurs d’incorporation correspondants sont stockés sous forme de colonnes d’une matrice. Par conséquent, le mappage d’un mot d’entrée à son incorporation est implémenté en tant que produit de matrice. Pour que cela soit très efficace, il est important que les vecteurs d’entrée soient stockés au format épars.
Fait amusant : le dégradé d’une matrice d’incorporation a la forme de vecteurs de dégradé qui ne sont que zéro pour les mots vus dans un minibatch. Étant donné que pour des vocabulaires réalistes de dizaines ou de centaines de milliers, la grande majorité des colonnes serait zéro, CNTK implémente a une optimisation spécifique pour représenter le dégradé sous forme « éparse de colonne ».
Problème connu : la forme de dégradé éparse de colonne mentionnée ci-dessus n’est actuellement pas prise en charge par notre technique de parallélisation SGD 1 bits . Utilisez plutôt la technique block-momentum .
Exemple
Incorporation apprise qui représente les mots d’un vocabulaire de 87636 sous la forme d’un vecteur 300 dimensionnel :
input = Input{87636, sparse=true} # word sequence, as one-hot vector, sparse format
embEn = EmbeddingLayer{300} (input) # embed word as a 300-dimensional continuous vector
En plus de sparse=true
, on doit également déclarer une entrée en tant qu’entrée éparse dans le bloc de reader
configuration.
Voici un exemple de lecture d’entrée de texte éparse avec :CNTKTextFormatReader
reader = {
readerType = "CNTKTextFormatReader"
file = "en2fr.txt"
randomize = true
input = {
input = { alias = "E" ; dim = 87636 ; format = "sparse" }
labels = { alias = "F" ; dim = 98624 ; format = "sparse" }
}
}
Si, au lieu de cela, les vecteurs d’incorporation existent déjà et doivent être chargés à partir d’un fichier, il se présente comme suit :
input = Input{87636, sparse=true} # word sequence, as one-hot vector, sparse format
embEn = EmbeddingLayer{300, embeddingPath="embedding-en.txt", transpose=true} (w) # embedding from disk
où le fichier "embedding-en.txt"
devrait être composé de 87 636 lignes de texte, chacune comprenant 300 nombres séparés par espace.
Dans la mesure où ce fichier enregistre les incorporations sous forme de lignes plutôt que de colonnes, transpose=true
transpose la matrice à la volée.
RecurrentLSTMLayer{}, RecurrentLSTMLayerStack{}
Fonctions de fabrique pour créer une LSTM récurrente à couche unique ou multicouche.
RecurrentLSTMLayer {outDim, cellShape = None,
goBackwards = false,
usePeepholes = false,
init = 'glorotUniform', initValueScale = 1,
enableSelfStabilization = false,
allowOptimizedEngine = false}
RecurrentLSTMLayerStack {layerDims,
cellShapes = None,
usePeepholes = false,
init = 'glorotUniform', initValueScale = 1,
enableSelfStabilization = false,
allowOptimizedEngine = false}
Paramètres
-
outDim
(RecurrentLSTMLayer{}
) : dimension de la sortie du réseau. Pour désigner un tenseur de rang>1, il peut s’agir d’un vecteur, par exemple(40:2)
-
layerDims
(RecurrentLSTMLayerStack{}
) : tableau de dimensions des couches internes et de la sortie du réseau -
cellShape
( (RecurrentLSTMLayer{}
facultatif) : dimension de la cellule de la LSTM. Normalement, cela est identique àoutDim
. Si une valeur différente est donnée, une projection linéaire supplémentaire est insérée pour convertir la dimension de cellule en sortie. -
cellShapes
( (RecurrentLSTMLayerStack{}
facultatif) : tableau de valeurs commecellShape
pourRecurrentLSTMLayer()
indiquer la projection -
goBackwards
(facultatif) : si la valeur est true, la périodicité est régressé -
usePeepholes
(facultatif) : si la valeur est true, utilisez des connexions peephole dans le LSTM -
init
('heNormal'
|'glorotUniform'
|...): type d’initialisation pour les pondérations. Consultez cette page pour obtenir la liste complète des options d’initialisation. -
initValueScale
: l’initialisation aléatoire de variance est multipliée par ce -
enableSelfStabilization
(facultatif) : si la valeur est true, insérez une opération « stabilisateur » similaire àStabilizerLayer{}
-
allowOptimizedEngine
(facultatif, false par défaut) : si la valeur est true, utilisez le moteur RNN optimisé de cuDNN si possible
Valeur renvoyée
Fonction qui implémente la ou les couches souhaitées qui applique/applique une LSTM récurrente à sa séquence d’entrée. Cette couche (pile) mappe une séquence d’entrée à une séquence d’états masqués de la même longueur.
Description
Cela implémente la LSTM récurrente à appliquer à une séquence d’entrées, en deux variantes : une couche unique et une pile multicouche. Cette opération gère automatiquement les entrées de longueur variable. La valeur initiale de l’état masqué et de la cellule est 0.
L’application de cette couche à une séquence d’entrée retourne la séquence des états masqués du LSTM récurrent (haut de pile) (la valeur de la cellule de mémoire de LSTM n’est pas retournée).
La séquence retournée a la même longueur que l’entrée.
Si seul le dernier état est souhaité, comme dans la classification de séquence ou dans certains scénarios de séquence à séquence, utilisez cette option BS.Sequences.Last()
pour extraire uniquement l’état masqué du dernier élément.
(Dans une périodicité descendante, vous utiliseriez BS.Sequences.First()
.)
Pour créer un modèle bidirectionnel avec RecurrentLSTMLayer()
, utilisez deux couches, une avec goBackwards=true
, et Splice()
les deux sorties ensemble.
RecurrentLSTMLayerStack()
actuellement ne prend pas en charge les modèles bidirectionnels, vous devez le construire manuellement à l’aide de plusieurs RecurrentLSTMLayer()/Splice()
combos.
Utilisation du moteur RNN CuDNN5
Cette fonction utilise automatiquement le moteur RNN optimisé de CuDNN5 si possible, autrement dit, si
- le modèle spécifié est un modèle qui peut être implémenté par la fonction de CuDNN5
- aucune projection (aucun
cellShape
paramètre) - aucune connexion peep-trou
- aucune auto-stabilisation
- ne pas revenir en arrière
- pour
RecurrentLSTMLayerStack{}
, toutes les dimensions de couche ont la même valeur
- aucune projection (aucun
allowOptimizedEngine=true
Plus précisément, CNTK nécessite d’activer allowOptimizedEngine=true
.
En effet, cuDNN5 RNN est implémenté en tant qu’opération primitive CNTK qui nécessite un GPU.
Toutefois, de nombreux systèmes réels utilisent des GPU pour l’entraînement, mais uniquement les serveurs en cours de déploiement.
Le RÉSEAU RNN CuDNN5 ne convient pas ici.
(Il est théoriquement possible d’utiliser le RÉSEAU RNN CuDNN5 pour l’entraînement et de le remplacer pour le déploiement par une opération d’édition par une implémentation LSTM explicite équivalente dans BrainScript.)
Notes
Si allowOptimizedEngine=true
ces deux variantes de couche sont des wrappers autour de la OptimizedRNNStack()
primitive.
Exemple
Un classifieur de texte simple, qui exécute une séquence de mots à travers une périodicité, puis passe le dernier état masqué du LSTM à un classifieur softmax, peut avoir cette forme :
w = Input{...} # word sequence (one-hot vectors)
e = EmbeddingLayer {150} (w) # embed as a 150-dimensional dense vector
h = RecurrentLSTMLayer {300} (e) # left-to-right LSTM with hidden and cell dim 300
t = BS.Sequences.Last (h) # extract last hidden state
z = DenseLayer {10000, activation=Softmax} (t) # softmax classifier
Pour remplacer l’exemple ci-dessus par une pile de 3 couches qui utilise le moteur RNN CuDNN5, modifiez cette ligne :
h = RecurrentLSTMLayerStack {(300:300:300), allowOptimizedEngine=true} (e)
Pour créer un LSTM bidirectionnel unidirectionnel (par exemple, en utilisant la moitié de la dimension masquée par rapport à ci-dessus), utilisez ceci :
hFwd = RecurrentLSTMLayer {150} (e)
hBwd = RecurrentLSTMLayer {150, goBackwards=true} (e)
h = Splice (hFwd:hBwd)
DelayLayer{}
Fonction factory pour créer une couche qui retarde son entrée.
DelayLayer {T=1, defaultHiddenActivation=0}
Paramètres
-
T
: nombre d’étapes de temps à retarder. Pour accéder aux valeurs futures, utilisez une valeur négative -
defaultHiddenActivation
: valeur à utiliser pour les images retardées aux limites
Valeur renvoyée
Fonction qui implémente l’opération de délai souhaitée.
Description
Cette opération retarde une séquence d’entrée par T
étapes (par défaut 1).
Cela est utile, par exemple, pour transformer une séquence de mots en séquence de triples de mots qui se chevauchent.
Considérez une séquence d’entrée « a b c b », qui doit être encodée sous la forme d’une séquence de vecteurs à chaud comme suit :
1 0 0 0
0 1 0 1
0 0 1 0
Ici, chaque colonne est un vecteur à chaud et correspond à un mot.
L’application DelayLayer{T=1}
à cette entrée génère cette séquence :
0 1 0 0
0 0 1 0
0 0 0 1
Tous les jetons sont retardés par un, et la première position est remplie sous la forme d’un vecteur 0.
De même, l’utilisation DelayLayer{T=-1}
(délai négatif) donne accès aux valeurs futures, et le remplissage à partir de la droite avec un zéro :
0 0 0 0
1 0 1 0
0 1 0 0
Notes
Cette couche est un wrapper autour des PastValue()
primitives et FutureValue()
des primitives.
Exemple
L’exemple suivant montre comment empiler trois mots voisins dans un vecteur trigramme :
x = ... # input value, e.g. a N-dimensional one-hot vector
xp = DelayLayer{} (x) # previous value
xn = DelayLayer{T-1} (x) # next value (negative delay)
tg = Splice (xp : x : xn) # concatenate all into a 3N-dimensional three-hot vector
BatchNormalizationLayer{}, LayerNormalizationLayer{}, StabilisateurLayer{}
Fonctions de fabrique pour créer des couches pour la normalisation par lots, la normalisation des couches et la stabilisation automatique.
BatchNormalizationLayer {spatialRank = 0,
normalizationTimeConstant = 5000,
initialScale = 1, epsilon = 0.00001, useCntkEngine = true}
LayerNormalizationLayer {initialScale = 1, initialBias = 0}
StabilizerLayer{}
Paramètres
BatchNormalizationLayer
:
-
spatialRank
: les paramètres de normalisation sont regroupés sur les premièresspatialRank
dimensions. Les valeurs actuellement autorisées sont 0 (aucun regroupement) et 2 (regroupement sur toutes les positions de pixels d’une image) -
normalizationTimeConstant
(valeur par défaut 5000) : constante de temps dans les échantillons du filtre à faible passe de premier ordre utilisé pour calculer les statistiques moyennes/variance pour l’utilisation de l’inférence -
initialScale
: valeur initiale du paramètre d’échelle -
epsilon
: petite valeur ajoutée à l’estimation de variance lors de l’calcul de l’inverse -
useCntkEngine
: si la valeur est true, utilisez l’implémentation native de CNTK. Si la valeur est false, utilisez l’implémentation de cuDNN (GPU uniquement).
LayerNormalizationLayer
:
-
initialScale
: valeur initiale du paramètre d’échelle -
initialBias
: valeur initiale du paramètre de biais
Valeur renvoyée
Fonction qui implémente une couche qui effectue l’opération de normalisation.
Description
BatchNormalizationLayer{}
implémente la technique décrite dans la normalisation par lots de papier : accélération de la formation en réseau profond en réduisant le décalage de covarié interne (Sergey Ioffe, Christian Szegedy).
Elle normalise ses entrées pour chaque minibatch par la moyenne/variance de minibatch, et la désactive avec un facteur de mise à l’échelle et un biais appris.
En inférence, au lieu d’utiliser la moyenne/variance minibatch, la normalisation par lot utilise une estimation moyenne/var à long terme.
Cette estimation est calculée lors de l’entraînement par filtrage à faible passe des statistiques de minibatch.
La constante de temps du filtre à faible passe peut être modifiée par le normalizationTimeConstant
paramètre.
Nous vous recommandons de commencer par la valeur par défaut (5 000), mais d’expérimenter d’autres valeurs, généralement sur l’ordre de plusieurs milliers à des dizaines de milliers.
LayerNormalizationLayer{}
implémente Layer Normalization (Jimmy Lei Ba, Jamie Ryan Kiros, Geoffrey E. Hinton).
Il normalise chaque échantillon d’entrée en soustrayant la moyenne sur tous les éléments de l’échantillon, puis en divisant par l’écart type sur tous les éléments de l’échantillon.
StabilizerLayer{}
implémente un auto-stabilisateur par réseau neuronal profond auto-stabilisé (P. Ghahremani, J. Droppo).
Cette technique simple mais efficace multiplie son entrée par un scalaire appris (mais contrairement à la normalisation de couche, elle ne normalise pas d’abord l’entrée, ni ne soustrait une moyenne).
Notez que par rapport au papier original, qui propose une scalaire beta
linéaire ou exponentielle Exp (beta)
, nous avons constaté qu’il était bénéfique d’utiliser une opération softplus aiguisée par la suggestion du second auteur, ce qui évite les valeurs négatives et l’instabilité de l’exponentiel.
Notes
BatchNormalizationLayer{}
est un wrapper autour de la BatchNormalization()
primitive.
LayerNormalizationLayer{}
et StabilizerLayer{}
sont exprimés directement dans BrainScript.
Exemple
Couche classique dans un réseau convolutionnel avec normalisation par lots :
MyLayer (x, depth, initValueScale) =
{
c = ConvolutionalLayer {depth, (5:5), pad=true, initValueScale=initValueScale} (x)
b = BatchNormalizationLayer {spatialRank = 2} (c) #####
r = ReLU (b)
p = MaxPoolingLayer {(3:3), stride = (2:2)} (r)
}.p
FeatureMVNLayer{}
Fonction de fabrique pour créer une couche qui normalise l’entrée des caractéristiques par leur moyenne et leur écart type.
FeatureMVNLayer{}
Paramètres
Liste d’arguments {}
vide .
Valeur renvoyée
Fonction qui implémente une couche qui effectue l’opération de normalisation.
Description
Cette couche normalise l’entrée sur un réseau neuronal en fonction de son biais et de sa variance. Ces valeurs sont estimées à l’avance en effectuant une transmission complète des données d’apprentissage, puis enregistrées et figées. Cela se produit automatiquement.
Étant donné que les paramètres de cette couche sont précalculés dans une passe distincte avant l’entraînement principal, il ne peut être appliqué qu’aux variables déclarées en tant que Input{}
.
Exemple
Il s’agit d’un début typique d’un réseau neuronal pour la modélisation acoustique de la parole :
features = Input{40} # e.g. speech features
featNorm = FeatureMVNLayer{} (features)
h = DenseLayer{2048} (featNorm)
...