BrainScript Network Builder
As redes personalizadas são descritas na linguagem de descrição de rede personalizada "BrainScript" da CNTK. Para definir uma rede personalizada, inclua uma secção com o nome BrainScriptNetworkBuilder
na configuração de preparação. Pode encontrar uma descrição detalhada no idioma de descrição da rede na página Conceitos Básicos e nas subpáginas correspondentes.
Existem duas formas de utilizar o construtor de rede BrainScript, uma com parênteses (...)
e uma forma de mão curta com chavetas {...}
. Para descrever a sua rede num ficheiro externo, especifique um bloco semelhante ao seguinte:
BrainScriptNetworkBuilder = (new ComputationNetwork {
include "yourNetwork.bs"
})
onde yourNetwork.bs
contém a rede descrita com o BrainScript. O ficheiro yourNetwork.bs
é apresentado primeiro no mesmo diretório que o ficheiro de configuração e, se não for encontrado, no diretório do executável CNTK. Os nomes de caminho absolutos e relativos são aceites aqui. Por exemplo, significa um ficheiro localizado num diretório bs
junto ao ficheiro de configuração (ou, em alternativa, bs/yourNetwork.bs
um diretório bs
dentro do diretório executável CNTK).
Nota: até ao CNTK 1.6, o BrainScript utilizou parênteses [...]
em vez de chavetas {...}
. Os parênteses ainda são aceites, mas preteridos.
Em alternativa, pode definir a sua rede inline, diretamente dentro do ficheiro de configuração. Isto pode simplificar a configuração se não planear partilhar o mesmo script cerebral em várias configurações. Utilize este formulário:
BrainScriptNetworkBuilder = {
# insert network description here
}
Então, qual é o aspeto do código BrainScript que vai para os parênteses? Para descobrir, avance para os Conceitos Básicos do BrainScript.
Em alternativa, mantenha-se nesta página e leia sobre alguns detalhes menos frequentemente necessários para si.
O {...}
formulário acima é, na verdade, apenas uma mão curta para isto:
BrainScriptNetworkBuilder = (new ComputationNetwork {
# insert network description here
})
Por fim, como uma utilização avançada, o (...)
formulário não se limita a utilizar new
. Em vez disso, qualquer expressão BrainScript que seja avaliada como um objeto de ComputationNetwork
é permitida dentro dos parênteses. Por exemplo:
BrainScriptNetworkBuilder = ({
include "myNetworks.bs"
network = CreateMyNetworkOfType42()
}.network)
Esta é uma utilização avançada que, por vezes, também ocorre no contexto da edição de modelos.
Seguinte: Conceitos Básicos do BrainScript.
Legado NDLNetworkBuilder
Nas versões mais antigas do CNTK, o construtor de rede chamava-se NDLNetworkBuilder
. A linguagem de definição é um subconjunto do BrainScript. O velho analisador era menos capaz, mas também mais clemente. Há também outras pequenas diferenças.
NDLNetworkBuilder
foi preterido, mas devido à semelhança, não é difícil atualizar para BrainScriptNetworkBuilder
. Segue-se um guia sobre como converter NDLNetworkBuilder
descrições de rede em BrainScriptNetworkBuilder
"s".
Atualizar de NDLNetworkBuilder
para BrainScriptNetworkBuilder
A conversão de uma definição de rede existente para o NDLNetworkBuilder
para BrainScriptNetworkBuilder
é simples na maioria dos casos. As principais alterações são a sintaxe circundante. A descrição da rede principal em si é compatível em grande parte para cima e provavelmente idêntica ou quase idêntica se não tirar partido das novas funcionalidades de idioma.
Para converter as suas descrições, tem de mudar o construtor de rede, adaptar a sintaxe externa w.r.t. e, possivelmente, fazer pequenas adaptações ao próprio código de rede.
Passo 1. Mudar o construtor de rede. Substitua o NDLNetworkBuilder
pelo bloco correspondente BrainScriptNetworkBuilder
no ficheiro de configuração CNTK. Se a descrição da rede estiver num ficheiro 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 de nome de ficheiro não é estritamente necessária, mas recomendada.)
Se a descrição da rede estiver no .cntk
próprio ficheiro 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")
}
Passo 2. Remover load
e run
bloquear. Com BrainScriptNetworkBuilder
, as definições de macro/função e o código principal são combinados. Os load
blocos e run
têm de ser simplesmente removidos. Por exemplo, isto:
load = ndlMnistMacros
run = DNN
ndlMnistMacros = [
featDim = 784
...
labels = InputValue(labelDim)
]
DNN = [
hiddenDim = 200
...
outputNodes = (ol)
]
torna-se simplesmente:
featDim = 784
...
labels = InputValue(labelDim)
hiddenDim = 200
...
outputNodes = (ol)
Pode ter utilizado a run
variável para selecionar uma de múltiplas 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) ]
]
Este padrão era maioritariamente necessário porque o NDL não tinha expressões condicionais. No BrainScript, isto seria agora 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, pelo que uma forma melhor seria intercalar as suas descrições e, em vez disso, utilizar condicionais no interior apenas para onde diferem. Eis um exemplo em que um parâmetro é utilizado para escolher entre um LSTM unidirecional e bidirecional:
encoderFunction =
if useBidirectionalEncoder
then BS.RNNs.RecurrentBirectionalLSTMPStack
else BS.RNNs.RecurrentLSTMPStack
encoder = encoderFunction (encoderDims, inputEmbedded, inputDim=inputEmbeddingDim)
Passo 3. Ajustar a descrição da rede. Relativamente à descrição da rede (fórmulas), o BrainScript é em grande parte compatível com o NDL. Estas são as principais diferenças:
O valor devolvido das macros (funções) já não é a última variável definida nas mesmas, mas todo o conjunto de variáveis. Tem de 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 esta alteração, o valor de retorno da função seria o registo completo e o erro típico que obterá é que era esperado um
ComputationNode
onde foi encontrado umComputationNetwork
.O BrainScript não permite funções com números variáveis de parâmetros. Isto é importante principalmente para a
Parameter()
função: um parâmetro de vetor já não pode ser escrito comoParameter(N)
, agora tem de ser explicitamente escrito como um tensorParameterTensor{N}
ou uma matrizParameter(N, 1)
de 1 coluna . Sem esta alteração, obterá um erro sobre o número de parâmetros posicionais que não correspondem. Esta notação também funciona com o NDL, para que possa efetuar esta alteração primeiro e testá-la com o NDL antes de a converter. Esta é também uma boa oportunidade para mudar o nome de qualquer utilização do nomeLearnableParameter()
legado paraParameterTensor{}
.Também é importante para a
RowStack()
função, que no BrainScript utiliza um único parâmetro que é uma matriz de entradas. As entradas têm de ser separadas por dois pontos (:
) em vez de uma vírgula, por exemploRowStack (a:b:c)
, em vez deRowStack (a, b, c)
.Algumas predefinições foram atualizadas, principalmente o parâmetro opcional
imageLayout
deConvolution()
, as operações de agrupamento eImageInput()
. Para NDL, estes predefiniram paralegacy
, enquanto agora a predefinição écudnn
a que é necessária para ser compatível com os primitivos de convolução cuDNN. (Todos os exemplos de código NDL especificam explicitamente este parâmetro comocudnn
já.)O analisador do BrainScript é mais restritivo:
Os identificadores são agora sensíveis às maiúsculas e minúsculas. As funções incorporadas utilizam PascalCase (por exemplo,
RectifiedLinear()
), e as variáveis incorporadas e os nomes de parâmetros utilizam camelCase (por exemplomodelPath
, ,criterionNodes
), tal como as cadeias de opção (init="fixedValue"
,tag="criterion"
). Tenha em atenção que, para nomes de parâmetros opcionais, as ortografias incorretas nem sempre são apanhadas como um erro. Em vez disso, alguns parâmetros opcionais escritos incorretamente são simplesmente ignorados. Um exemplo são as definições de "nós especiais". A ortografia correta para estes é agora:featureNodes = ... labelNodes = ... criterionNodes = ... evaluationNodes = ... outputNodes = ...
Os nomes alternativos abreviados já não são permitidos, como
Const()
deve serConstant()
,tag="eval"
deve sertag="evaluation"
, eevalNodes
é agoraevaluationNodes
.Alguns nomes mal escritos foram corrigidos: é agora
criterion
(da mesma formacriterionNodes
),defaultHiddenActivity
é agoradefaultHiddenActivation
.criteria
O
=
sinal já não é opcional para definições de funções.Embora seja permitido utilizar parênteses para blocos (
[ ... ]
), é preterido. Utilize chavetas ({ ... }
).As etiquetas de opção têm de ser citadas como cadeias, por exemplo
init="uniform"
, em vez deinit=uniform
. Sem as aspas, o BrainScript falhará com uma mensagem de erro a indicar que o símbolouniform
é desconhecido.Os primitivos do BrainScript que criam parâmetros (
Input{}
eParameterTensor{}
) devem utilizar chavetas para os respetivos argumentos (por exemplo,f = Input{42}
). Esta é uma convenção que não é imposta, mas recomendada para avançar.
Esta sintaxe mais restrita ainda é aceite pelo , pelo
NDLNetworkBuilder
que recomendamos que faça primeiro estas alterações sintacticas e as teste com NDL, antes de mudar para o BrainScript.
Passo 4: Remover NDLNetworkBuilder
das secções "escrever" e "testar". Reveja as secções "escrever" e "testar" para NDLNetworkBuilder
as secções e remova-as. Alguns dos nossos exemplos de NDL de ações têm secções estranhas NDLNetworkBuilder
. Não são utilizados e não devem estar lá. Se a configuração se basear num destes exemplos, também poderá ter essas secções. Costumavam ser ignorados, mas com a atualização do BrainScript, definir uma nova rede nestas secções tem agora um significado (edição de modelo), pelo que já não são ignorados e, por conseguinte, devem ser removidos.
NDLNetworkBuilder
referência (preterida)
A sintaxe dos preteridos NDLNetworkBuilder
é:
NDLNetworkBuilder = [
networkDescription = "yourNetwork.ndl"
]
O NDLNetworkBuilder
bloco tem os seguintes parâmetros:
networkDescription
: o caminho do ficheiro de descrição da rede. Com o preteridoNDLNetworkBuilder
, era costume utilizar a extensão.ndl
de ficheiro . Se não existir nenhumnetworkDescription
parâmetro especificado, assume-se que a descrição da rede está indicada no mesmoNDLNetworkBuilder
subbloqueio, especificado com orun
parâmetro abaixo. Tenha em atenção que apenas um caminho de ficheiro pode ser especificado através donetworkDescription
parâmetro . Para carregar vários ficheiros de macros, utilize ondlMacros
parâmetro .run
: o bloco do NDL que será executado. Se for especificado um ficheiro NDL externo através donetworkDescription
parâmetro , orun
parâmetro identifica um bloco nesse ficheiro. Este parâmetro substitui quaisquerrun
parâmetros que possam já existir no ficheiro. Se não for especificado nenhumnetworkDescription
ficheiro, orun
parâmetro identifica um bloco no ficheiro de configuração atual.load
: os blocos de scripts NDL a carregar. Podem ser especificados vários blocos através de uma lista separada por ":". Normalmente, osload
blocos especificados pelo parâmetro contêm macros para utilização pelorun
bloco. Semelhante aorun
parâmetro , oload
parâmetro identifica os blocos num ficheiro NDL externo e substitui quaisquerload
parâmetros que possam já existir no ficheiro, se um ficheiro for especificado pelonetworkDescription
parâmetro . Se não for especificado nenhumnetworkDescription
ficheiro,load
identifica um bloco no ficheiro de configuração atual.ndlMacros
: o caminho do ficheiro no qual as macros NDL podem ser carregadas. Normalmente, este parâmetro é utilizado para carregar um conjunto predefinido de macros NDL que podem ser utilizadas por todos os scripts NDL. Vários ficheiros NDL, cada um especificando diferentes conjuntos de macros, podem ser carregados ao especificar uma lista separada "+" de caminhos de ficheiro para estendlMacros
parâmetro. Para partilhar macros com outros blocos de comandos, como blocos mel (model-editing language) do NDL, deve defini-lo ao nível da raiz do ficheiro de configuração.randomSeedOffset
: um valor de desvio aleatório não negativo ao inicializar os parâmetros de aprendizagem. O valor predefinido é0
. Isto permite que os utilizadores executem experimentações com inicialização aleatória diferente.