2017-08-07 3 views
0

CNTKは一般的に優れたC++ APIを提供しますが、私はC++ APIからLSTMまたはGRUレイヤを構築する方法を見つけるのに苦労しています。私が見つけることができる唯一の機能はOptimizedRNNStackです。この関数は、変数weightsを除き、全く自明のようです。今のところ私はその変数weightsを初期化する方法を考え出すことはできませんでした。 CNTK.core.bsを見ると、と重みを初期化するようだ:CNTKでLSTMレイヤをC++から作成するにはどうすればよいですか?

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

が、私はC++にそれを翻訳する方法を見つけ出すことはできません。コンテキストについては、私はCTCを使用してOCRパイプラインを構築しようとしています。すべてのネイティブデータ合成ツールを使用でき、パイプライン全体をエンドツーエンドで訓練し、テストすることができるので、C++のすべてをビルドすることは素晴らしいことです。しかし、私がBrainscriptでモデルを構築しなければならない場合、私はそれも良いと思います。

答えて

0

C++ APIには、同等のレイヤーライブラリがありません。私はC++の静的型付けの性質がすべてのオプションをサポートするのを難しくしているので、これを行うには苦労しました。 Layersライブラリと同じようにGRUを作成するC++コードのプライベートな部分を共有してみましょう(すべてのオプションなし)。

申し訳ありませんが、これは直接コピー可能です。戻り値をFunctionに変更し、2つのPlaceholderVariables、dh、およびxを作成してラムダシグネチャを変更してください。その面白い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; 
    }); 
} 
+0

申し訳ありませんが、私は、スライス()関数は、ベクトル {0 * stackeDim}のように、ベクトルとしてすべての3つの引数を渡す必要があることに気づきました。小さなラッパー関数静的スライス(const変数&、const Axis&、int、int)を書くことをお勧めします。 –

関連する問題