你当前正在访问 Microsoft Azure Global Edition 技术文档网站。 如果需要访问由世纪互联运营的 Microsoft Azure 中国技术文档网站,请访问 https://docs.azure.cn

BrainScript 网络生成器

自定义网络在 CNTK 的自定义网络描述语言“BrainScript”中介绍。若要定义自定义网络,请在训练配置中包含名为 的部分 BrainScriptNetworkBuilder 。 有关网络描述语言的详细说明,请参阅 “基本概念 ”页和相应的子页。

使用 BrainScript 网络生成器有两种形式,一种使用括号 (...),另一种是使用大括号的 {...}短手形式。 若要在外部文件中描述网络,请指定如下所示的块:

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

其中 yourNetwork.bs 包含使用 BrainScript 描述的网络。 该文件 yourNetwork.bs 首先在配置文件所在的目录中查找,如果未找到,请在 CNTK 可执行文件的 目录中查找。 此处接受绝对路径名和相对路径名。 例如, bs/yourNetwork.bs 表示位于配置文件旁边的目录中 bs 的文件 (或 CNTK 可执行目录 bs) 中的目录。

注意:在 CNTK 1.6 之前,BrainScript 使用括号 [...] 而不是大括号 {...}。 括号仍被接受,但已弃用。

或者,可以直接在配置文件中以内联方式定义网络。 如果你不打算跨多个配置共享相同的大脑脚本,则可以简化配置。 使用以下形式:

BrainScriptNetworkBuilder = {
    # insert network description here
}

那么,进入括号的 BrainScript 代码是什么样子的呢? 若要了解,请直接跳转到 BrainScript 基本概念

或停留在此页面上,阅读一些不太经常需要的详细信息。

上面的 {...} 形式实际上只是一个简明的简明:

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

最后,作为高级用途,窗体 (...) 不限于使用 new。 相反,任何计算结果为 的对象的 ComputationNetwork BrainScript 表达式都允许在括号内使用。 例如:

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

这是一种高级用法,有时也发生在模型编辑的上下文中。

下一步: BrainScript 基本概念

遗产 NDLNetworkBuilder

在旧版 CNTK 中,网络生成器称为 NDLNetworkBuilder。 其定义语言是 BrainScript 的子集。 老分析员能力较差,但也更宽容。 还有其他小差异。

NDLNetworkBuilder 现已弃用,但由于相似性,升级到 并不难 BrainScriptNetworkBuilder。 下面是有关如何将网络说明BrainScriptNetworkBuilder转换为 NDLNetworkBuilder 的指南。

NDLNetworkBuilder 更新为 BrainScriptNetworkBuilder

在大多数情况下,将 NDLNetworkBuilder 的现有网络定义转换为 BrainScriptNetworkBuilder 非常简单。 main更改是周围的语法。 如果不利用新的语言功能,核心网络描述本身基本上向上兼容,并且可能完全相同或几乎相同。

若要转换说明,必须切换网络生成器,调整 w.r.t. 外部语法,并可能对网络代码本身进行少量调整。

步骤 1。 切换网络生成器. 将 NDLNetworkBuilder 替换为 CNTK 配置文件中的相应 BrainScriptNetworkBuilder 块。 如果网络说明位于单独的文件中:

# 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. 删除 loadrun 块。 使用 BrainScriptNetworkBuilder,宏/函数定义和main代码组合在一起。 loadrun和 块必须直接删除。 例如,以下代码:

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 和双向 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} 或 1 列矩阵 Parameter(N, 1)。 如果不进行此更改,将收到有关位置参数数量不匹配的错误。 此表示法也适用于 NDL,因此可以在转换之前先进行此更改并使用 NDL 对其进行测试。 这也是将旧名称 LearnableParameter() 的任何用法重命名为 ParameterTensor{}的好机会。

    它对于 RowStack() 函数也很重要,该函数在 BrainScript 中采用单个参数,该参数是一个输入数组。 输入必须用冒号分隔 (:) 而不是逗号,例如 RowStack (a:b:c) ,而不是 RowStack (a, b, c)

  • 某些默认值已更新,主要是 、 池操作 和 ImageInput()Convolution()可选imageLayout参数。 对于 NDL,这些默认为 legacy,而现在默认为 cudnn ,需要与 cuDNN 卷积基元兼容。 (所有 NDL 代码示例均将此参数显式指定为 cudnn already.)

  • BrainScript 分析程序的限制性更强:

    • 标识符现在区分大小写。 内置函数使用 PascalCase (例如 RectifiedLinear()) ,而内置变量和参数名称使用 camelCase (例如 modelPathcriterionNodes) ,选项字符串 (init="fixedValue"tag="criterion") 也是如此。 请注意,对于可选参数的名称,错误拼写并不总是被捕获为错误。 相反,某些拼写错误的可选参数将被忽略。 例如,“特殊节点”定义。 现在,它们的正确拼写为:

        featureNodes    = ...
        labelNodes      = ...
        criterionNodes  = ...
        evaluationNodes = ...
        outputNodes     = ...
      
    • 不再允许使用缩写的替代名称,例如 Const() 应为 Constant()tag="eval" 应为 tag="evaluation",现在 evalNodesevaluationNodes为 。

    • 更正了一些拼写错误的名称: criteria 现在 criterion (同样 criterionNodes) , defaultHiddenActivity 现在是 defaultHiddenActivation

    • 对于 = 函数定义,符号不再是可选的。

    • 虽然允许对块使用括号 ([ ... ]) ,但它已被弃用。 使用大括号 ({ ... }) 。

    • 选项标签必须用字符串括起,例如 init="uniform" ,而不是 init=uniform。 如果没有引号,BrainScript 将失败,并显示一条错误消息,指出符号 uniform 未知。

    • 创建参数的 Input{} BrainScript 基元 (和 ParameterTensor{}) 应使用大括号作为参数 (,例如 f = Input{42}) 。 这是一个不强制执行但建议今后执行的约定。

    此更受限的语法仍被 NDLNetworkBuilder接受,因此我们建议先进行这些语法更改,并使用 NDL 对其进行测试,然后再实际更改为 BrainScript。

步骤 4. 从“写入”和“测试”部分删除 NDLNetworkBuilder 。 请查看节的“写入”和“测试”部分 NDLNetworkBuilder ,并将其删除。 我们的一些常用 NDL 示例具有无关 NDLNetworkBuilder 部分。 它们未使用,也不应存在。 如果配置基于其中一个示例,则也可能具有此类部分。 它们过去会被忽略,但通过 BrainScript 更新,在这些部分中定义新网络现在具有 (模型编辑) 的意义,因此它们不再被忽略,因此应将其删除。

NDLNetworkBuilder 引用 (已弃用)

已弃用 NDLNetworkBuilder 的 语法为:

NDLNetworkBuilder = [
    networkDescription = "yourNetwork.ndl"
]

NDLNetworkBuilder 具有以下参数:

  • networkDescription:网络说明文件的文件路径。 弃用 后 NDLNetworkBuilder,习惯使用文件扩展名 .ndl。 如果未 networkDescription 指定参数,则假定网络说明内联在同一 NDLNetworkBuilder 子块中,并使用以下 run 参数指定。 请注意,只能通过 networkDescription 参数指定一个文件路径。 若要加载宏的多个文件,请使用 ndlMacros 参数。

  • run:将执行的 NDL 块。 如果通过 networkDescription 参数指定外部 NDL 文件,则 run 参数将标识该文件中的块。 此参数替代文件中可能已存在的任何 run 参数。 如果未 networkDescription 指定文件, run 参数将标识当前配置文件中的块。

  • load:要加载的 NDL 脚本块。 可以通过“:”分隔列表指定多个块。 参数指定的 load 块通常包含供 块使用的 run 宏。 与 run 参数类似, load 参数标识外部 NDL 文件中的块,并覆盖文件中可能已存在的任何 load 参数(如果 文件由 networkDescription 参数指定)。 如果未 networkDescription 指定文件, load 则 标识当前配置文件中的块。

  • ndlMacros:可以加载 NDL 宏的文件路径。 此参数通常用于加载可由所有 NDL 脚本使用的默认 NDL 宏集。 可以通过为此参数 ndlMacros 指定“+”分隔的文件路径列表来加载多个 NDL 文件,每个文件指定不同的宏集。 若要与其他命令块(例如 NDL 的模型编辑语言 (MEL) 块)共享宏,应在配置文件的根级别定义它。

  • randomSeedOffset:初始化可学习参数时的非负随机种子偏移值。 默认值为 0。 这允许用户运行具有不同随机初始化的试验。