如何在C ++中用CNTK创建LSTM层?

时间:2017-08-07 20:25:04

标签: cntk

CNTK通常提供了很棒的C ++ API,但我很难找到如何从C ++ API构建LSTM或GRU层。我能找到的唯一功能是OptimizedRNNStack。除weights变量外,该函数似乎不言自明。到目前为止,我还没有设法弄清楚如何初始化weights变量。查看CNTK.core.bs,似乎用以下内容初始化权重:

ParameterTensor {0:0, initFilterRank=0, initOutputRank=-1, init=init, 
initValueScale=initValueScale}`

但我无法弄清楚如何将其转换为C ++。对于上下文 - 我正在尝试使用CTC来构建OCR管道。使用C ++构建所有内容非常棒,因为我可以使用所有本机数据综合工具,并且可以对整个管道进行端到端的培训和测试。但是,如果我必须在Brainscript中构建模型,我想这也很好。

1 个答案:

答案 0 :(得分:1)

C ++ API没有等效的图层库。我一直在努力做到这一点,因为C ++的静态类型性质使得很难支持所有这些选项。让我分享一段私有的C ++代码,创建一个类似于图层库的GRU(没有所有选项)。

对不起,这不是直接可复制的;请尝试将返回值更改为Function,并通过创建两个PlaceholderVariables,dh和x来更改lambda签名。有趣的letconst auto的缩写。

static BinaryModel GRU(size_t outputDim, const DeviceDescriptor& device)
{
    let activation = [](const Variable& x) { return Tanh(x); };
    auto W  = Parameter({ outputDim * 3, NDShape::InferredDimension }, DataType::Float, GlorotUniformInitializer(), device, L"W");
    auto R  = Parameter({ outputDim * 2, outputDim }, DataType::Float, GlorotUniformInitializer(), device, L"R");
    auto R1 = Parameter({ outputDim    , outputDim }, DataType::Float, GlorotUniformInitializer(), device, L"R1");
    auto b  = Parameter({ outputDim * 3 }, 0.0f, device, L"b");
    let stackAxis = vector<Axis>{ Axis(0) };
    let stackedDim = (int)outputDim;
    let one = Constant::Scalar(1.0f, device); // for "1 -"...
    // e.g. https://en.wikipedia.org/wiki/Gated_recurrent_unit
    return BinaryModel({ W, R, R1, b }, [=](const Variable& dh, const Variable& x)
    {
        let& dhs = dh;
        // projected contribution from input(s), hidden, and bias
        let projx3 = b + Times(W, x);
        let projh2 = Times(R, dh);
        let zt_proj = Slice(projx3, stackAxis, 0 * stackedDim, 1 * stackedDim) + Slice(projh2, stackAxis, 0 * stackedDim, 1 * stackedDim);
        let rt_proj = Slice(projx3, stackAxis, 1 * stackedDim, 2 * stackedDim) + Slice(projh2, stackAxis, 1 * stackedDim, 2 * stackedDim);
        let ct_proj = Slice(projx3, stackAxis, 2 * stackedDim, 3 * stackedDim);

        let zt = Sigmoid(zt_proj)->Output();        // fun update gate z(t)

        let rt = Sigmoid(rt_proj);                  // reset gate r(t)

        let rs = dhs * rt;                          // "cell" c
        let ct = activation(ct_proj + Times(R1, rs));

        let ht = (one - zt) * ct + zt * dhs; // hidden state ht / output

        return ht;
    });
}