Поделиться через


Построитель сетей BrainScript

Пользовательские сети описаны в пользовательском языке описания сети CNTK "BrainScript". Чтобы определить пользовательскую сеть, включите раздел с именем BrainScriptNetworkBuilder в конфигурацию обучения. Подробное описание на языке описания сети можно найти на странице Основные понятия и на соответствующих подстраницах.

Существует две формы использования построителя сетей BrainScript: одна из которых использует круглые скобки (...)и короткая форма с фигурными скобками {...}. Чтобы описать сеть во внешнем файле, укажите блок, аналогичный следующему:

BrainScriptNetworkBuilder = (new ComputationNetwork {
    include "yourNetwork.bs"
})

где yourNetwork.bs содержит сеть, описанную с помощью BrainScript. Сначала файл yourNetwork.bs ищется в том же каталоге, что и файл конфигурации, а если он не найден, в каталоге исполняемого файла CNTK. Здесь принимаются как абсолютные, так и относительные имена путей. Например, означает файл, bs/yourNetwork.bs расположенный в каталоге bs рядом с файлом конфигурации (или каталог bs в исполняемом каталоге CNTK).

Примечание. До CNTK 1.6 в BrainScript использовались квадратные скобки [...] вместо фигурных скобок {...}. Квадратные скобки по-прежнему принимаются, но не рекомендуются.

Кроме того, можно определить встроенную сеть непосредственно в файле конфигурации. Это может упростить настройку, если вы не планируете совместно использовать один и тот же сценарий мозга в нескольких конфигурациях. Используйте эту форму:

BrainScriptNetworkBuilder = {
    # insert network description here
}

Так как выглядит код BrainScript, который входит в квадратные скобки? Чтобы узнать это, перейдите к разделу Основные понятия BrainScript.

Или оставайтесь на этой странице и читайте о некоторых менее часто необходимых сведениях для вас.

В приведенной {...} выше форме на самом деле просто короткие руки для этого:

BrainScriptNetworkBuilder = (new ComputationNetwork {
    # insert network description here
})

Наконец, в качестве расширенного (...) использования форма не ограничивается использованием new. Вместо этого в круглых скобках допускается любое выражение BrainScript, результатом которого ComputationNetwork является объект . Пример:

BrainScriptNetworkBuilder = ({
    include "myNetworks.bs"
    network = CreateMyNetworkOfType42()
}.network)

Это расширенное использование, которое также иногда встречается в контексте редактирования модели.

Далее: Основные понятия BrainScript.

Наследие NDLNetworkBuilder

В более ранних версиях CNTK построитель сети назывался NDLNetworkBuilder. Его язык определения является подмножеством BrainScript. Старый синтаксический анализатор был менее способным, но и более прощающим. Существуют и другие небольшие различия.

NDLNetworkBuilder В настоящее время является устаревшим, но из-за сходства не трудно выполнить обновление до BrainScriptNetworkBuilder. Ниже приведено руководство по преобразованию NDLNetworkBuilder описаний сети в BrainScriptNetworkBuilder.

Обновление с NDLNetworkBuilder до BrainScriptNetworkBuilder

Преобразование существующего определения сети для NDLNetworkBuilderBrainScriptNetworkBuilder в в большинстве случаев просто. Изменения main — это окружающий синтаксис. Само описание основной сети в значительной степени совместимо и, вероятно, идентично или почти идентично, если вы не используете преимущества новых функций языка.

Чтобы преобразовать описания, необходимо переключить построитель сети, адаптировать внешний синтаксис w.r.t. и, возможно, внести незначительные изменения в сам сетевой код.

Шаг 1. Переключение построителя сетей. Замените NDLNetworkBuilder соответствующим BrainScriptNetworkBuilder блоком в файле конфигурации CNTK. Если описание сети находится в отдельном файле:

# change from:
NDLNetworkBuilder = [
    ndlMacros = "shared.ndl"   # (if any)
    networkDescription = "yourNetwork.ndl"
]
# ...to:
BrainScriptNetworkBuilder = (new ComputationNetwork {
    include "shared.bs"        # (if any)
    include "yourNetwork.bs"
})

(Изменение расширения имени файла не является обязательным, но рекомендуется.)

Если описание сети находится в самом файле конфигурации .cntk :

# 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")
}

Шаг 2. Удалите load и run блоки. При BrainScriptNetworkBuilderиспользовании объединяются определения макросов и функций и код main. Блоки load и run должны быть просто удалены. Например,

load = ndlMnistMacros
run = DNN
ndlMnistMacros = [
    featDim = 784
    ...
    labels = InputValue(labelDim)
]
DNN = [
    hiddenDim = 200
    ...
    outputNodes = (ol)
]

просто становится:

featDim = 784
...
labels = InputValue(labelDim)
hiddenDim = 200
...
outputNodes = (ol)

Возможно, вы использовали run переменную для выбора одной из нескольких конфигураций с внешней переменной, например:

NDLNetworkBuilder = [
    run = $whichModel$   # outside parameter selects model, must be either "model1" or "model2"
    model1 = [ ... (MODEL 1 DEFINITION) ]
    model2 = [ ... (MODEL 1 DEFINITION) ]
]

Этот шаблон был в основном необходим, так как в NDL не было условных выражений. В BrainScript теперь это будет записано с помощью 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$'")
)

Однако часто выбранные модели очень похожи, поэтому лучше объединить их описания и вместо этого использовать условные условия внутри только для того, где они различаются. Ниже приведен пример, в котором параметр используется для выбора между однонаправленным и двунаправленным LSTM:

encoderFunction =
    if useBidirectionalEncoder
    then BS.RNNs.RecurrentBirectionalLSTMPStack
    else BS.RNNs.RecurrentLSTMPStack
encoder = encoderFunction (encoderDims, inputEmbedded, inputDim=inputEmbeddingDim)

Шаг 3. Настройка описания сети. Что касается описания сети (формул), BrainScript в значительной степени совместим с NDL. Ниже перечислены main различия.

  • Возвращаемое значение макросов (функций) больше не является последней переменной, определенной в них, а всем набором переменных. Необходимо явно выбрать выходное значение в конце. Пример:

      # 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
    

    Без этого изменения возвращаемым значением функции будет вся запись, а типичная ошибка заключается в том, что ComputationNode ожидалось, где ComputationNetwork был найден объект .

  • BrainScript не разрешает функции с переменным числом параметров. Это важно в первую очередь для Parameter() функции: векторный параметр больше не может быть записан как Parameter(N), теперь он должен быть явно записан в виде тензора ParameterTensor{N} или матрицы Parameter(N, 1)из 1 столбца . Без этого изменения вы получите ошибку о несоответствии количества позиционных параметров. Эта нотация также работает с NDL, поэтому вы можете сначала внести это изменение и протестировать его с помощью NDL перед преобразованием. Это также хорошая возможность переименовать любое использование устаревшего имени LearnableParameter() в ParameterTensor{}.

    Это также важно для RowStack() функции, которая в BrainScript принимает один параметр, который является массивом входных данных. Входные данные должны быть разделены двоеточием (:) вместо запятой, например RowStack (a:b:c) вместо RowStack (a, b, c).

  • Были обновлены некоторые значения по умолчанию, в первую очередь необязательный imageLayout параметр Convolution(), операции пула и ImageInput(). Для NDL по умолчанию используется legacyзначение , в то время как теперь используется cudnn значение по умолчанию, которое требуется для совместимости с примитивами свертки cuDNN. (Все примеры кода NDL явно указывают этот параметр как cudnn уже.)

  • Средство синтаксического анализа BrainScript является более строгим:

    • Теперь в идентификаторах учитывается регистр. Встроенные функции используют PascalCase (например RectifiedLinear(), ), а встроенные переменные и имена параметров используют camelCase (например modelPath, , criterionNodes), как и строки параметров (init="fixedValue", tag="criterion"). Обратите внимание, что для имен необязательных параметров неправильное написание не всегда отображается как ошибка. Вместо этого некоторые необязательные параметры, неправильно написанные, просто игнорируются. Примером являются определения "специальных узлов". Их правильное написание для этих теперь:

        featureNodes    = ...
        labelNodes      = ...
        criterionNodes  = ...
        evaluationNodes = ...
        outputNodes     = ...
      
    • Сокращенные альтернативные имена больше не допускаются, например Const() должно быть Constant(), tag="eval" должно быть tag="evaluation", и evalNodes теперь evaluationNodesимеет значение .

    • Исправлены некоторые неправильно написанные имена: criteria теперь является criterion (аналогично criterionNodes), defaultHiddenActivity имеет значение defaultHiddenActivation.

    • Знак = больше не является необязательным для определений функций.

    • Хотя для блоков () разрешено использовать квадратные скобки,[ ... ] он не рекомендуется. Используйте фигурные скобки ({ ... }).

    • Метки параметров должны быть заключены в кавычки в виде строк, например init="uniform" , а init=uniformне . Без кавычек BrainScript завершится ошибкой с сообщением об ошибке о том, что символ uniform неизвестен.

    • Примитивы BrainScript, создающие параметры (Input{} и ParameterTensor{}), должны использовать фигурные скобки для аргументов (например, f = Input{42}). Это соглашение, которое не применяется, но рекомендуется в дальнейшем.

    Этот более ограниченный синтаксис по-прежнему принимается NDLNetworkBuilder, поэтому мы рекомендуем сначала внести эти синтаксические изменения и протестировать их с помощью NDL, прежде чем переходить на BrainScript.

Шаг 4. Удалите NDLNetworkBuilder из разделов write и test. Просмотрите разделы "запись" и "тестирование" для NDLNetworkBuilder разделов и удалите их. Некоторые из наших стандартных примеров NDL содержат лишние NDLNetworkBuilder разделы. Они не используются и не должны быть там. Если конфигурация основана на одном из этих примеров, могут быть и такие разделы. Раньше они пропускались, но с обновлением BrainScript определение новой сети в этих разделах теперь имеет смысл (редактирование модели), поэтому они больше не игнорируются и, следовательно, должны быть удалены.

NDLNetworkBuilder reference (не рекомендуется)

Синтаксис нерекомендуемого NDLNetworkBuilder объекта:

NDLNetworkBuilder = [
    networkDescription = "yourNetwork.ndl"
]

Блок NDLNetworkBuilder имеет следующие параметры:

  • networkDescription: путь к файлу описания сети. В устаревшей NDLNetworkBuilderверсии было принято использовать расширение .ndlфайла . Если параметр не networkDescription указан, предполагается, что описание сети встраиваются в тот же NDLNetworkBuilder подблок, указанный run с помощью параметра ниже. Обратите внимание, что с помощью параметра можно указать только один путь к файлу networkDescription . Чтобы загрузить несколько файлов макросов, используйте ndlMacros параметр .

  • run: блок NDL, который будет выполнен. Если внешний NDL-файл указан с помощью networkDescription параметра , run параметр определяет блок в этом файле. Этот параметр переопределяет все run параметры, которые уже могут существовать в файле. Если файл не networkDescription указан, run параметр определяет блок в текущем файле конфигурации.

  • load: блоки скриптов NDL для загрузки. С помощью разделенного списка ":" можно указать несколько блоков. Блоки, заданные параметром load , обычно содержат макросы для использования блоком run . Аналогично параметру run , load параметр определяет блоки во внешнем NDL-файле и переопределяет все load параметры, которые уже могут существовать в файле, если файл указан параметром networkDescription . Если файл не networkDescription указан, load определяет блок в текущем файле конфигурации.

  • ndlMacros: путь к файлу, по которому могут быть загружены макросы NDL. Этот параметр обычно используется для загрузки набора макросов NDL по умолчанию, которые могут использоваться всеми скриптами NDL. Несколько NDL-файлов, каждый из которых задает разные наборы макросов, можно загрузить, указав разделенный "+" список путей к файлам для этого ndlMacros параметра. Для совместного использования макросов с другими командными блоками, такими как блоки языка редактирования моделей (MEL) NDL, необходимо определить их на корневом уровне файла конфигурации.

  • randomSeedOffset: неотрицательное значение случайного смещения начального значения при инициализации изучаемых параметров. Значение по умолчанию — 0. Это позволяет пользователям выполнять эксперименты с разными случайными инициализациями.