Modification de modèle avec BrainScript
(Remarque : Versions antérieures de CNTK utilisées « MEL » (langage d’édition de modèle) à cette fin. Nous sommes toujours en train de convertir les exemples. Pour obtenir de la documentation sur MEL, consultez cette page.)
CNTK permet aux modèles d’être modifiés après le fait. Pour ce faire, créez un modèle lors du clonage (parties de) d’un modèle existant avec des modifications appliquées. Pour cela, CNTK fournit trois fonctions de base :
BS.Network.Load()
pour charger un modèle existantBS.Network.CloneFunction()
pour extraire une section d’un modèle existant à réutiliserBS.Network.Edit()
pour cloner un modèle avec des modifications de nœud par nœud appliquées
L’opération d’édition n’est pas une étape distincte. Au lieu de cela, une commande qui doit fonctionner hors d’un modèle modifié ne spécifie pas un modelPath
pour charger le modèle, mais plutôt une BrainScriptNetworkBuilder
section qui charge le modèle à l’intérieur et construit un nouveau modèle à la volée.
Exemple : préentrînation discriminative
La pré-formation discriminative est une technique dans laquelle un réseau profond est créé par l’entraînement d’une séquence de réseaux peu profonds. Commencez par un réseau de couche 1 masqué, effectuez l’apprentissage vers une convergence partielle, puis supprimez la couche de sortie, ajoutez une nouvelle couche masquée et ajoutez une nouvelle couche de sortie. Répétez jusqu’à ce que le nombre souhaité de couches masquées soit atteint.
Supposons qu’il s’agit d’un modèle de démarrage très simple
BrainScriptNetworkBuilder = [
N = 40; M = 9000; H = 512
W1 = Parameter (H, N); b1 = Parameter (H)
Wout = Parameter (M, H); bout = Parameter (M)
x = Input (N, tag=‘feature’) ; labels = Input (M, tag=‘labels’)
h1 = Sigmoid (W1 * x + b1)
z = Wout * h1 + bout
ce = CrossEntropyWithSoftmax (labels, z, tag=‘criterion’)
]
Nous allons entraîner ce modèle et enregistrer sous « model.1.dnn ». Ensuite, nous voulons entraîner un modèle avec deux couches masquées, où la première couche masquée est initialisée à partir des valeurs entraînées ci-dessus. Pour ce faire, nous créons une action d’entraînement distincte qui crée un modèle, mais réutilisant des parties de la précédente, comme suit :
BrainScriptNetworkBuilder = {
# STEP 1: load 1-hidden-layer model
inModel = BS.Network.Load ("model.1.dnn")
# get its h1 variable --and also recover its dimension
h1 = inModel.h1
H = h1.dim
# also recover the number of output classes
M = inModel.z.dim
# STEP 2: create the rest of the extended network as usual
W2 = Parameter (H, H); b2 = Parameter (H)
Wout = Parameter (M, H); bout = Parameter (M)
h2 = Sigmoid (W2 * h1 + b2)
z = Wout * h2 + bout
ce = CrossEntropyWithSoftmax (labels, z, tag=‘criterion’)
}
Tout d’abord, STEP 1 utilise Load()
pour charger le réseau dans une variable BrainScript. Le réseau se comporte comme un enregistrement BrainScript, où tous les nœuds de niveau supérieur (tous les nœuds qui ne contiennent pas ou .
[
ne contiennent pas de noms de nœuds) sont accessibles via la syntaxe d’enregistrement. Un nouveau réseau peut référencer n’importe quel nœud dans un réseau chargé. Dans cet exemple, le réseau chargé contient un nœud h1
qui est la sortie de la première couche masquée et un nœud z
qui est la probabilité postérieure de journal nonnormalisée des classes de sortie (entrée de la fonction Softmax). Les deux nœuds sont accessibles à partir de BrainScript par le biais d’une syntaxe par points, par exemple inModel.h1
, et inModel.z
.
Notez que les constantes ne sont pas stockées dans les modèles, de sorte que ni elles ne N
M
sont disponibles à partir du modèle. Toutefois, il est possible de les reconstruire à partir du modèle chargé. À cet effet, les nœuds de calcul se comportent également comme des enregistrements BrainScript et exposent une dim
propriété.
Ensuite, STEP 2 construit le reste du nouveau réseau à l’aide de BrainScript normal. Notez que cette nouvelle section utilise simplement le nœud h1
du modèle d’entrée comme entrée, comme elle utiliserait tout autre nœud. Le référencement d’un nœud à partir du réseau d’entrée rend automatiquement tous les nœuds dont ce nœud dépend également d’une partie du réseau nouvellement créé. Par exemple, le nœud x
d’entrée fera automatiquement partie du nouveau réseau.
Notez également que la couche de sortie est construite à nouveau. De cette façon, ses paramètres de modèle seront créés à nouveau. (Pour ne pas le faire et réutiliser plutôt les paramètres existants, il est possible d’utiliser inModel.Wout
, mais notez que cela n’a pas de sens du point de vue de la conception réseau dans cet exemple particulier.)
Exemple : utilisation d’un modèle préentraîné
Voici un exemple d’utilisation d’un modèle préentraîné (à partir d’un fichier "./featext.dnn"
) comme extracteur de fonctionnalités :
BrainScriptNetworkBuilder = {
# STEP 1: load existing model
featExtNetwork = BS.Network.Load ("./featext.dnn")
# STEP 2: extract a read-only section that is the feature extractor function
featExt = BS.Network.CloneFunction (
featExtNetwork.input, # input node that AE model read data from
featExtNetwork.feat, # output node in AE model that holds the desired features
parameters="constant") # says to freeze that part of the network
# STEP 3: define the part of your network that uses the feature extractor
# from the loaded model, which above we isolated into featExt().
# featExt() can be used like any old BrainScript function.
input = Input (...)
features = featExt (input) # this will instantiate a clone of the above network
# STEP 4: and add the remaining bits of the network in BrainScript, e.g.
h = Sigmoid (W_hid * features + b_hid) # whatever your hidden layer looks like
z = W_out * h + b_out
ce = CrossEntropyWithSoftmax (labels, z)
criterionNodes = (ce)
}
ÉTAPE 1 utilise Load()
pour charger le réseau dans une variable BrainScript.
STEP 2 utilise CloneFunction()
pour cloner la section liée à l’extraction de caractéristiques à partir du réseau chargé, qui est le sous-graphique qui se connecte featExtNetwork.input
à featExtNetwork.feat
. Étant donné que nous avons spécifié parameters="constant"
, tous les paramètres qui featExtNetwork.feat
dépendent sont également clonés et créés en lecture seule.
Dans l’ÉTAPE 3 et 4, le nouveau réseau est défini. Cela s’effectue comme n’importe quel autre modèle BrainScript, mais nous pouvons maintenant utiliser la featExt()
fonction dans ce cas.
Problèmes liés aux noms de nœuds avec .
[
et ]
Pour référencer un nœud dans un réseau qui contient .
ou ]
[
, remplacez ces caractères par _
.
Par exemple, si network
contient un nœud appelé result.z
, network.result.z
échoue ; à la place, dites network.result_z
.
Exemple : modification des nœuds d’un réseau existant
Pour modifier les parties internes d’un réseau existant, il est en fait possible de cloner le réseau, tandis que les modifications sont appliquées dans le cadre du processus de clonage. Cette opération est effectuée par BS.Network.Edit()
. Edit()
itérera sur tous les nœuds d’un réseau et offrira chaque nœud, un par un, aux fonctions lambda passées par l’appelant. Ces fonctions lambda peuvent ensuite inspecter le nœud et retourner le nœud non modifié, ou retourner un nouveau nœud à sa place. Edit()
itérera sur les nœuds dans un ordre non spécifié. Si un nœud de remplacement fait référence à un nœud réseau qui à son tour a été remplacé, Edit()
met à jour toutes ces références aux remplacements respectifs (par exemple, « faites la bonne chose »).
TODO : Exemple.
Suivant : Informations de référence sur les fonctions complètes