Construtor de Rede Do BrainScript
As redes personalizadas são descritas na linguagem de descrição de rede personalizada do CNTK "BrainScript". Para definir uma rede personalizada, inclua uma seção chamada BrainScriptNetworkBuilder
em sua configuração de treinamento. A descrição detalhada na linguagem de descrição da rede pode ser encontrada na página Conceitos Básicos e nas sub-páginas correspondentes.
Há duas formas de usar o construtor de rede BrainScript, uma usando parênteses (...)
e uma forma de mão curta usando chaves {...}
. Para descrever sua rede em um arquivo externo, especifique um bloco semelhante a este:
BrainScriptNetworkBuilder = (new ComputationNetwork {
include "yourNetwork.bs"
})
em que yourNetwork.bs
contém a rede descrita usando o BrainScript. O arquivo yourNetwork.bs
é procurar primeiro no mesmo diretório que o arquivo de configuração e, se não for encontrado, no diretório do executável CNTK. Nomes de caminho absolutos e relativos são aceitos aqui. Por exemplo, bs/yourNetwork.bs
significa um arquivo localizado em um diretório bs
ao lado do arquivo de configuração (ou, como alternativa, um diretório bs
dentro do diretório executável CNTK).
Observação: até o CNTK 1.6, o BrainScript usava colchetes [...]
em vez de chaves {...}
. Colchetes ainda são aceitos, mas preteridos.
Como alternativa, você pode definir sua rede embutida, diretamente dentro do arquivo de configuração. Isso poderá simplificar a configuração se você não planeja compartilhar o mesmo script cerebral em várias configurações. Use este formulário:
BrainScriptNetworkBuilder = {
# insert network description here
}
Então, qual é a aparência do código BrainScript que vai para os colchetes? Para descobrir, vá logo à frente para Conceitos Básicos do BrainScript.
Ou fique nesta página e leia sobre alguns detalhes menos necessários para você.
O {...}
formulário acima é realmente apenas uma mão curta para isso:
BrainScriptNetworkBuilder = (new ComputationNetwork {
# insert network description here
})
Por fim, como um uso avançado, o (...)
formulário não se limita a usar new
. Em vez disso, qualquer expressão BrainScript avaliada como um objeto de ComputationNetwork
é permitida dentro dos parênteses. Por exemplo:
BrainScriptNetworkBuilder = ({
include "myNetworks.bs"
network = CreateMyNetworkOfType42()
}.network)
Esse é um uso avançado que também ocorre às vezes no contexto de edição de modelo.
Próximo: Conceitos básicos do BrainScript.
Legado NDLNetworkBuilder
Em versões mais antigas do CNTK, o construtor de rede era chamado NDLNetworkBuilder
de . Sua linguagem de definição é um subconjunto de BrainScript. O velho analisador era menos capaz, mas também mais indulgente. Há também outras pequenas diferenças.
NDLNetworkBuilder
agora está preterido, mas devido à semelhança, não é difícil atualizar para BrainScriptNetworkBuilder
. Veja a seguir um guia sobre como converter NDLNetworkBuilder
descrições de rede em BrainScriptNetworkBuilder
.
Atualizando de NDLNetworkBuilder
para BrainScriptNetworkBuilder
A conversão de uma definição de rede existente para para NDLNetworkBuilder
BrainScriptNetworkBuilder
é simples na maioria dos casos. As alterações main são a sintaxe ao redor. A descrição da rede principal em si é compatível em grande parte para cima e provavelmente idêntica ou quase idêntica se você não aproveitar os novos recursos de linguagem.
Para converter suas descrições, você deve alternar o construtor de rede, adaptar a sintaxe externa w.r.t. e, possivelmente, fazer pequenas adaptações ao próprio código de rede.
Etapa 1. Alternar o construtor de rede. Substitua o NDLNetworkBuilder
pelo bloco correspondente BrainScriptNetworkBuilder
no arquivo de configuração CNTK. Se a descrição da rede estiver em um arquivo separado:
# change from:
NDLNetworkBuilder = [
ndlMacros = "shared.ndl" # (if any)
networkDescription = "yourNetwork.ndl"
]
# ...to:
BrainScriptNetworkBuilder = (new ComputationNetwork {
include "shared.bs" # (if any)
include "yourNetwork.bs"
})
(A alteração da extensão filename não é estritamente necessária, mas recomendada.)
Se a descrição da rede estiver no .cntk
próprio arquivo de configuração:
# change from:
NDLNetworkBuilder = [
# macros
load = [
SigmoidNetwork (x, W, b) = Sigmoid (Plus (Times (W, x), b))
]
# network description
run = [
feat = Input (13)
...
ce = CrossEntropyWithSoftmax (labels, z, tag="criterion")
]
]
# ...to:
BrainScriptNetworkBuilder = {
# macros are just defined inline
SigmoidNetwork (x, W, b) = Sigmoid (Plus (Times (W, x), b)) # or: Sigmoid (W * x + b)
# network description
feat = Input {13}
...
ce = CrossEntropyWithSoftmax (labels, z, tag="criterion")
}
Etapa 2. Remover load
e run
bloquear. Com BrainScriptNetworkBuilder
, definições de macro/função e main código são combinados. Os load
blocos e run
devem simplesmente ser removidos. Por exemplo, isso:
load = ndlMnistMacros
run = DNN
ndlMnistMacros = [
featDim = 784
...
labels = InputValue(labelDim)
]
DNN = [
hiddenDim = 200
...
outputNodes = (ol)
]
simplesmente se torna:
featDim = 784
...
labels = InputValue(labelDim)
hiddenDim = 200
...
outputNodes = (ol)
Você pode ter usado a run
variável para selecionar uma das várias configurações com uma variável externa, por exemplo:
NDLNetworkBuilder = [
run = $whichModel$ # outside parameter selects model, must be either "model1" or "model2"
model1 = [ ... (MODEL 1 DEFINITION) ]
model2 = [ ... (MODEL 1 DEFINITION) ]
]
Esse padrão era necessário principalmente porque o NDL não tinha expressões condicionais. No BrainScript, isso agora seria escrito com uma expressão if
:
BrainScriptNetworkBuilder = (new ComputationNetwork
if "$whichModel$" == "model1" then { ... (MODEL 1 DEFINITION) }
else if "$whichModel$" == "model2" then { ... (MODEL 2 DEFINITION) }
else Fail ("Invalid model selector value '$whichModel$'")
)
No entanto, muitas vezes, os modelos selecionados são muito semelhantes, portanto, uma maneira melhor seria mesclar suas descrições e, em vez disso, usar condicionais dentro apenas para onde eles diferem. Aqui está um exemplo em que um parâmetro é usado para escolher entre um LSTM unidirecional e um LSTM bidirecional:
encoderFunction =
if useBidirectionalEncoder
then BS.RNNs.RecurrentBirectionalLSTMPStack
else BS.RNNs.RecurrentLSTMPStack
encoder = encoderFunction (encoderDims, inputEmbedded, inputDim=inputEmbeddingDim)
Etapa 3. Ajustar a descrição da rede. Em relação à própria descrição da rede (fórmulas), o BrainScript é amplamente compatível com o NDL. Estas são as diferenças main:
O valor retornado das macros (funções) não é mais a última variável definida nelas, mas todo o conjunto de variáveis. Você deve selecionar explicitamente o valor de saída no final. Por exemplo:
# NDL: f(x) = [ x2 = Times (x, x) y = Plus (x2, Constant (1)) ] # <-- return value defaults to last entry, i.e. y # BrainScript: f(x) = { x2 = x*x y = x2 + 1 }.y # <-- return value y must be explicitly dereferenced
Sem essa alteração, o valor retornado da função seria o registro inteiro e o erro típico que você receberá é que um
ComputationNode
era esperado onde umComputationNetwork
foi encontrado.O BrainScript não permite funções com números variáveis de parâmetros. Isso importa principalmente para a
Parameter()
função: um parâmetro vetor não pode mais ser gravado comoParameter(N)
, agora ele precisa ser escrito explicitamente como um tensorParameterTensor{N}
ou uma matrizParameter(N, 1)
de 1 coluna. Sem essa alteração, você receberá um erro sobre a incompatibilidade do número de parâmetros posicionais. Essa notação também funciona com NDL, para que você possa fazer essa alteração primeiro e testá-la com NDL antes de converter. Essa também é uma boa oportunidade para renomear qualquer uso do nomeLearnableParameter()
herdado paraParameterTensor{}
.Ele também é importante para a
RowStack()
função , que no BrainScript usa um único parâmetro que é uma matriz de entradas. As entradas devem ser separadas por dois-pontos (:
) em vez de uma vírgula, por exemploRowStack (a:b:c)
, em vez deRowStack (a, b, c)
.Alguns padrões foram atualizados, principalmente o parâmetro opcional
imageLayout
deConvolution()
, as operações de pooling eImageInput()
. Para NDL, esses padrão sãolegacy
, enquanto agora o padrão é que écudnn
necessário ser compatível com os primitivos de convolução cuDNN. (Todos os exemplos de código NDL especificam explicitamente esse parâmetro comocudnn
já.)O analisador do BrainScript é mais restritivo:
Os identificadores agora diferenciam maiúsculas de minúsculas. As funções internas usam PascalCase (por exemplo,
RectifiedLinear()
), e variáveis internas e nomes de parâmetro usam camelCase (por exemplomodelPath
, , ),criterionNodes
assim como cadeias de caracteres de opção (init="fixedValue"
,tag="criterion"
). Observe que, para nomes de parâmetros opcionais, ortografias incorretas nem sempre são capturadas como um erro. Em vez disso, alguns parâmetros opcionais escritos incorretamente são ignorados. Um exemplo são as definições de 'nós especiais'. A ortografia correta para eles agora é:featureNodes = ... labelNodes = ... criterionNodes = ... evaluationNodes = ... outputNodes = ...
Nomes alternativos abreviados não são mais permitidos, como
Const()
deve serConstant()
,tag="eval"
devem sertag="evaluation"
eevalNodes
agoraevaluationNodes
é .Alguns nomes mal escritos foram corrigidos:
criteria
agoracriterion
é (da mesma formacriterionNodes
),defaultHiddenActivity
agoradefaultHiddenActivation
é .O
=
sinal não é mais opcional para definições de função.Embora seja permitido usar colchetes para blocos (
[ ... ]
), ele é preterido. Use chaves ({ ... }
).Os rótulos de opção devem ser citados como cadeias de caracteres, por exemplo
init="uniform"
, em vez deinit=uniform
. Sem as aspas, o BrainScript falhará com uma mensagem de erro dizendo que o símbolouniform
é desconhecido.Os primitivos do BrainScript que criam parâmetros (
Input{}
eParameterTensor{}
) devem usar chaves para seus argumentos (por exemplo,f = Input{42}
). Esta é uma convenção que não é imposta, mas recomendada daqui para frente.
Essa sintaxe mais restrita ainda é aceita pelo , portanto
NDLNetworkBuilder
, recomendamos primeiro fazer essas alterações sintaticas e testá-las com NDL, antes de realmente mudar para BrainScript.
Etapa 4. Remova NDLNetworkBuilder
das seções "gravar" e "testar". Examine as seções "gravar" e "testar" para NDLNetworkBuilder
seções e remova-as. Alguns de nossos exemplos de NDL de ações têm seções desnecessárias NDLNetworkBuilder
. Eles não são usados e não devem estar lá. Se sua configuração for baseada em um desses exemplos, você também poderá ter essas seções. Eles costumavam ser ignorados, mas com a atualização do BrainScript, a definição de uma nova rede nessas seções agora tem um significado (edição de modelo), portanto, elas não são mais ignoradas e, portanto, devem ser removidas.
NDLNetworkBuilder
reference (preterido)
A sintaxe do preterido NDLNetworkBuilder
é:
NDLNetworkBuilder = [
networkDescription = "yourNetwork.ndl"
]
O NDLNetworkBuilder
bloco tem os seguintes parâmetros:
networkDescription
: o caminho do arquivo de descrição da rede. Com o preteridoNDLNetworkBuilder
, era personalizado usar a extensão.ndl
de arquivo . Se não houver nenhumnetworkDescription
parâmetro especificado, a descrição da rede será considerada embutida no mesmoNDLNetworkBuilder
subbloco, especificado com orun
parâmetro abaixo. Observe que apenas um caminho de arquivo pode ser especificado por meio donetworkDescription
parâmetro . Para carregar vários arquivos de macros, use ondlMacros
parâmetro .run
: o bloco do NDL que será executado. Se um arquivo NDL externo for especificado por meio donetworkDescription
parâmetro , orun
parâmetro identificará um bloco nesse arquivo. Esse parâmetro substitui todosrun
os parâmetros que já podem existir no arquivo. Se nenhumnetworkDescription
arquivo for especificado, orun
parâmetro identificará um bloco no arquivo de configuração atual.load
: os blocos de scripts NDL a serem carregados. Vários blocos podem ser especificados por meio de uma lista separada ":". Os blocos especificados peloload
parâmetro normalmente contêm macros para uso pelorun
bloco. Semelhante aorun
parâmetro , oload
parâmetro identifica blocos em um arquivo NDL externo e substitui todosload
os parâmetros que já podem existir no arquivo, se um arquivo for especificado pelonetworkDescription
parâmetro . Se nenhumnetworkDescription
arquivo for especificado,load
identificará um bloco no arquivo de configuração atual.ndlMacros
: o caminho do arquivo em que as macros NDL podem ser carregadas. Esse parâmetro geralmente é usado para carregar um conjunto padrão de macros NDL que podem ser usadas por todos os scripts NDL. Vários arquivos NDL, cada um especificando diferentes conjuntos de macros, podem ser carregados especificando uma lista separada "+" de caminhos de arquivo para essendlMacros
parâmetro. Para compartilhar macros com outros blocos de comando, como blocos MEL (linguagem de edição de modelo) do NDL, você deve defini-lo no nível raiz do arquivo de configuração.randomSeedOffset
: um valor de deslocamento de semente aleatória não negativo na inicialização dos parâmetros de aprendizado. O valor padrão é0
. Isso permite que os usuários executem experimentos com inicialização aleatória diferente.