Compartilhar via


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 NDLNetworkBuilderde . 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 NDLNetworkBuilderBrainScriptNetworkBuilder é 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 um ComputationNetwork 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 como Parameter(N), agora ele precisa ser escrito explicitamente como um tensor ParameterTensor{N} ou uma matriz Parameter(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 nome LearnableParameter() herdado para ParameterTensor{}.

    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 exemplo RowStack (a:b:c) , em vez de RowStack (a, b, c).

  • Alguns padrões foram atualizados, principalmente o parâmetro opcional imageLayout de Convolution(), as operações de pooling e ImageInput(). Para NDL, esses padrão são legacy, 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 como cudnn 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 exemplo modelPath, , ), criterionNodesassim 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 ser Constant(), tag="eval" devem ser tag="evaluation"e evalNodes agora evaluationNodesé .

    • Alguns nomes mal escritos foram corrigidos: criteria agora criterion é (da mesma forma criterionNodes), defaultHiddenActivity agora defaultHiddenActivationé .

    • 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 de init=uniform. Sem as aspas, o BrainScript falhará com uma mensagem de erro dizendo que o símbolo uniform é desconhecido.

    • Os primitivos do BrainScript que criam parâmetros (Input{} e ParameterTensor{}) 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 preterido NDLNetworkBuilder, era personalizado usar a extensão .ndlde arquivo . Se não houver nenhum networkDescription parâmetro especificado, a descrição da rede será considerada embutida no mesmo NDLNetworkBuilder subbloco, especificado com o run parâmetro abaixo. Observe que apenas um caminho de arquivo pode ser especificado por meio do networkDescription parâmetro . Para carregar vários arquivos de macros, use o ndlMacros parâmetro .

  • run: o bloco do NDL que será executado. Se um arquivo NDL externo for especificado por meio do networkDescription parâmetro , o run parâmetro identificará um bloco nesse arquivo. Esse parâmetro substitui todos run os parâmetros que já podem existir no arquivo. Se nenhum networkDescription arquivo for especificado, o run 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 pelo load parâmetro normalmente contêm macros para uso pelo run bloco. Semelhante ao run parâmetro , o load parâmetro identifica blocos em um arquivo NDL externo e substitui todos load os parâmetros que já podem existir no arquivo, se um arquivo for especificado pelo networkDescription parâmetro . Se nenhum networkDescription 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 esse ndlMacros 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.