From ca4afe5c67c6dd5c4d219ca5b8aea91953dc574b Mon Sep 17 00:00:00 2001 From: Clemens Marschner Date: Wed, 13 Apr 2016 14:30:06 +0200 Subject: [PATCH] Address review comments --- Makefile | 1 - Source/ActionsLib/NDLNetworkBuilder.cpp | 4 +- .../CNTK/BrainScript/CNTKCoreLib/CNTK.core.bs | 10 +- .../ExperimentalNetworkBuilder.cpp | 134 ------------------ Source/CNTK/CNTK.vcxproj | 3 +- Source/CNTK/CNTK.vcxproj.filters | 3 - Source/Common/Include/Sequences.h | 4 +- .../ComputationNetworkBuilder.cpp | 2 +- .../ComputationNetworkEvaluation.cpp | 10 +- .../ComputationNetworkLib/ComputationNode.cpp | 23 ++- .../ComputationNetworkLib/ComputationNode.h | 23 +-- .../InputAndParamNodes.h | 12 +- .../NetworkTests/NetworkTests.vcxproj | 1 - .../NetworkTests/NetworkTests.vcxproj.filters | 3 - 14 files changed, 50 insertions(+), 183 deletions(-) delete mode 100644 Source/CNTK/BrainScript/ExperimentalNetworkBuilder.cpp diff --git a/Makefile b/Makefile index f46c74f50..3fa6e2c6e 100644 --- a/Makefile +++ b/Makefile @@ -586,7 +586,6 @@ CNTK_SRC =\ $(SOURCEDIR)/CNTK/BrainScript/BrainScriptEvaluator.cpp \ $(SOURCEDIR)/CNTK/BrainScript/BrainScriptParser.cpp \ $(SOURCEDIR)/CNTK/BrainScript/BrainScriptTest.cpp \ - $(SOURCEDIR)/CNTK/BrainScript/ExperimentalNetworkBuilder.cpp \ $(SOURCEDIR)/Common/BestGpu.cpp \ $(SOURCEDIR)/Common/MPIWrapper.cpp \ diff --git a/Source/ActionsLib/NDLNetworkBuilder.cpp b/Source/ActionsLib/NDLNetworkBuilder.cpp index c584cd08a..c8bdf47b6 100644 --- a/Source/ActionsLib/NDLNetworkBuilder.cpp +++ b/Source/ActionsLib/NDLNetworkBuilder.cpp @@ -73,7 +73,7 @@ void NDLNodeEvaluatorImpl::Evaluate(NDLNode* node, const wst size_t i = 0; auto tensorShape = ProcessTensorShapeParameters(node, params, i, /*isImage=*/false, cnNodeType); - wstring dynamicAxis = node->GetOptionalParameter("dynamic", ""); + wstring dynamicAxis = node->GetOptionalParameter("dynamicAxis", ""); // first look for this node already existing in the network // BUGBUG: How does this set the dimensions then? if (m_net->NodeNameExists(name)) @@ -98,7 +98,7 @@ void NDLNodeEvaluatorImpl::Evaluate(NDLNode* node, const wst size_t imageHeight = ((NDLNode*) params[1])->GetScalar(); size_t imageChannels = ((NDLNode*) params[2])->GetScalar(); ImageLayoutKind imageLayoutKind = ImageLayoutKindFrom(node->GetOptionalParameter("imageLayout", "HWC")); - wstring dynamicAxis = node->GetOptionalParameter("dynamic", ""); + wstring dynamicAxis = node->GetOptionalParameter("dynamicAxis", ""); if (isSparse) nodePtr = builder.CreateSparseInputNode(name, ImageDimensions::AsTensorShape(imageWidth, imageHeight, imageChannels, imageLayoutKind), dynamicAxis); diff --git a/Source/CNTK/BrainScript/CNTKCoreLib/CNTK.core.bs b/Source/CNTK/BrainScript/CNTKCoreLib/CNTK.core.bs index 85be15737..9ad26ec07 100644 --- a/Source/CNTK/BrainScript/CNTKCoreLib/CNTK.core.bs +++ b/Source/CNTK/BrainScript/CNTKCoreLib/CNTK.core.bs @@ -34,11 +34,11 @@ Parameter = LearnableParameter // deprecated # TODO: make Parameter take tensor dims? ParameterTensor(dims, learningRateMultiplier = 1.0, init = 'uniform'/*|fixedValue|gaussian|fromFile*/, initValueScale = 1, value = 0, initFromFilePath = '', initFromLiteral = '', initOnCPUOnly=true, randomSeed=-1, tag='') = new ComputationNode [ operation = 'LearnableParameter' ; shape = new TensorShape [ /*dims*/ ] /*plus the function args*/ ] ConstantFromString(literal, tag='') = ParameterTensor((0)/*dim, will be inferred*/, init = 'fromLiteral', initFromLiteral = literal, learningRateMultiplier = 0.0) -DynamicAxis(Name='', tag='') = new ComputationNode [ operation = 'DynamicAxis' ; name = Name ] -Input(dims, dynamicAxis='', tag='feature') = new ComputationNode [ operation = 'InputValue' ; shape = new TensorShape [ /*dims*/ ] ; dynamicAxes = dynamicAxis ; isImage = false /*plus the function args*/ ] -SparseInput(dims, dynamicAxis='', tag='feature') = new ComputationNode [ operation = 'SparseInputValue' ; shape = new TensorShape [ /*dims*/ ] ; dynamicAxes = dynamicAxis ; isImage = false /*plus the function args*/ ] -ImageInput(imageWidth, imageHeight, imageChannels, imageLayout='CHW', dynamicAxis='', tag='feature') = new ComputationNode [ operation = 'InputValue' ; dynamicAxes = dynamicAxis ; isImage = true /*plus the function args*/ ] -SparseImageInput(imageWidth, imageHeight, imageChannels, imageLayout='CHW', dynamicAxis='', tag='feature') = new ComputationNode [ operation = 'SparseInputValue' ; dynamicAxes = dynamicAxis ; isImage = true /*plus the function args*/ ] +DynamicAxis(name='', tag='') = new ComputationNode [ operation = 'DynamicAxis' ; /*plus the function args*/ ] +Input(dims, dynamicAxis='', tag='feature') = new ComputationNode [ operation = 'InputValue' ; shape = new TensorShape [ /*dims*/ ] ; isImage = false /*plus the function args*/ ] +SparseInput(dims, dynamicAxis='', tag='feature') = new ComputationNode [ operation = 'SparseInputValue' ; shape = new TensorShape [ /*dims*/ ] ; isImage = false /*plus the function args*/ ] +ImageInput(imageWidth, imageHeight, imageChannels, imageLayout='CHW', dynamicAxis='', tag='feature') = new ComputationNode [ operation = 'InputValue' ; isImage = true /*plus the function args*/ ] +SparseImageInput(imageWidth, imageHeight, imageChannels, imageLayout='CHW', dynamicAxis='', tag='feature') = new ComputationNode [ operation = 'SparseInputValue' ; isImage = true /*plus the function args*/ ] EnvironmentInput(propertyName, tag='') = new ComputationNode [ operation = 'EnvironmentInput' /*plus the function args*/ ] ConstantTensor(val, dims, tag='') = ParameterTensor(dims, learningRateMultiplier = 0, init = 'fixedValue', value = val) Constant(val, rows = 1, cols = 1, tag='') = Parameter(rows, cols, learningRateMultiplier = 0, init = 'fixedValue', value = val) diff --git a/Source/CNTK/BrainScript/ExperimentalNetworkBuilder.cpp b/Source/CNTK/BrainScript/ExperimentalNetworkBuilder.cpp deleted file mode 100644 index 8ce3d5c14..000000000 --- a/Source/CNTK/BrainScript/ExperimentalNetworkBuilder.cpp +++ /dev/null @@ -1,134 +0,0 @@ -#if 0 // this entire file can be removed once CNTK.core.bs works -// ExperimentalNetworkBuilder.cpp -- interface to new version of NDL (and config) parser --fseide - -#define _CRT_NONSTDC_NO_DEPRECATE // make VS accept POSIX functions without _ -#define _CRT_SECURE_NO_WARNINGS // "secure" CRT not available on all platforms --add this at the top of all CPP files that give "function or variable may be unsafe" warnings - -#include - -using namespace std; - -// TODO: move to actual text files to be included - -wstring standardFunctions = - L"Print(value, format='') = new PrintAction [ what = value /*; how = format*/ ] \n" - L"Debug(value, say = '', enabled = true) = new Debug [ /*macro arg values*/ ] \n" - L"Format(value, format) = new StringFunction [ what = 'Format' ; arg = value ; how = format ] \n" - L"Replace(s, from, to) = new StringFunction [ what = 'Replace' ; arg = s ; replacewhat = from ; withwhat = to ] \n" - L"Substr(s, begin, num) = new StringFunction [ what = 'Substr' ; arg = s ; pos = begin ; chars = num ] \n" - L"Chr(c) = new StringFunction [ what = 'Chr' ; arg = c ] \n" - L"Floor(x) = new NumericFunction [ what = 'Floor' ; arg = x ] \n" - L"Length(x) = new NumericFunction [ what = 'Length' ; arg = x ] \n" - L"Ceil(x) = -Floor(-x) \n" - L"Round(x) = Floor(x+0.5) \n" - L"Sign(x) = if x > 0 then 1 else if x < 0 then -1 else 0 \n" - L"Min(a,b) = if a < b then a else b \n" - L"Max(a,b) = if a > b then a else b \n" - L"Fac(n) = if n > 1 then Fac(n-1) * n else 1 \n"; - -wstring commonMacros = - L"BFF(in, rows, cols) = [ B = Parameter(rows, 1, init = 'fixedValue', value = 0) ; W = Parameter(rows, cols) ; z = W*in+B ] \n" - L"SBFF(in, rows, cols) = [ Eh = Sigmoid(BFF(in, rows, cols).z) ] \n " - L"MeanVarNorm(feat) = PerDimMeanVarNormalization(feat, Mean(feat), InvStdDev(feat)) \n" - L"LogPrior(labels) = Log(Mean(labels)) \n"; - -wstring computationNodes = // TODO: use actual TypeName() here? would first need to make it a wide string; we should also extract those two methods into the base macro -L"LearnableParameter(rows, cols, learningRateMultiplier = 1.0, init = 'uniform'/*|fixedValue|gaussian|fromFile*/, initValueScale = 1, value = 0, initFromFilePath = '', initOnCPUOnly=true, randomSeed=-1, tag='') = new ComputationNode [ operation = 'LearnableParameter' ; shape = new TensorShape [ dims = (rows : cols) ] /*plus the function args*/ ]\n" - L"Parameter = LearnableParameter // deprecated \n" -L"ParameterTensor(dims, learningRateMultiplier = 1.0, init = 'uniform'/*|fixedValue|gaussian|fromFile*/, initValueScale = 1, value = 0, initFromFilePath = '', initOnCPUOnly=true, randomSeed=-1, tag='') = new ComputationNode [ operation = 'LearnableParameter' ; shape = new TensorShape [ /*dims*/ ] /*plus the function args*/ ]\n" - // TODO: ImageParameter? - // ^^ already works; vv untested - L"Input(dims, tag='feature') = new ComputationNode [ operation = 'InputValue' ; shape = new TensorShape [ /*dims*/ ] ; isImage = false /*plus the function args*/ ]\n" // note: naming a little inconsistent // TODO: re-test after flag change - L"SparseInput(dims, tag='feature') = new ComputationNode [ operation = 'SparseInputValue' ; shape = new TensorShape [ /*dims*/ ] ; isImage = false /*plus the function args*/ ]\n" - L"ImageInput(imageWidth, imageHeight, imageChannels, imageLayout='CHW', tag='feature') = new ComputationNode [ operation = 'InputValue' ; isImage = true /*plus the function args*/ ]\n" - L"SparseImageInput(imageWidth, imageHeight, imageChannels, imageLayout='CHW', tag='feature') = new ComputationNode [ operation = 'SparseInputValue' ; isImage = true /*plus the function args*/ ]\n" - L"Constant(val, rows = 1, cols = 1, tag='') = Parameter(rows, cols, learningRateMultiplier = 0, init = 'fixedValue', value = val) \n" - L"PastValue(dims, input, timeStep = 1, defaultHiddenActivation = 0.1, tag='') = new ComputationNode [ operation = 'PastValue' ; inputs = input ; shape = new TensorShape [ /*dims*/ ] /*plus the function args*/ ]\n" - L"FutureValue(dims, input, timeStep = 1, defaultHiddenActivation = 0.1, tag='') = new ComputationNode [ operation = 'FutureValue' ; inputs = input ; shape = new TensorShape [ /*dims*/ ] /*plus the function args*/ ]\n" - // TODO: ^^ DelayedValues no longer need to know their dimension. That is inferred in Validation. - L"Shift(input, fromOffset, boundaryValue, boundaryMode=-1/*context*/, dim=-1, tag='') = new ComputationNode [ operation = 'Shift' ; inputs = (input : boundaryValue) /*plus the function args*/ ]\n" - L"RowSlice(startIndex, numRows, input, tag='') = new ComputationNode [ operation = 'RowSlice' ; inputs = input /*plus the function args*/ ]\n" - L"RowRepeat(input, numRepeats, tag='') = new ComputationNode [ operation = 'RowRepeat' ; inputs = input /*plus the function args*/ ]\n" - L"RowStack(inputs, tag='') = new ComputationNode [ operation = 'RowStack' /*plus the function args*/ ]\n" - L"Reshape(input, numRows, imageWidth = 0, imageHeight = 0, imageChannels = 0, tag='') = new ComputationNode [ operation = 'LegacyReshape' ; inputs = input /*plus the function args*/ ]\n" - L"NewReshape(input, dims, beginDim=0, endDim=0, tag='') = new ComputationNode [ operation = 'Reshape' ; inputs = input ; shape = new TensorShape [ /*dims*/ ] /*plus the function args*/ ]\n" - L"ReshapeDimension(x, dim, tensorShape) = NewReshape(x, tensorShape, beginDim=dim, endDim=dim + 1) \n" - L"FlattenDimensions(x, dim, num) = NewReshape(x, 0, beginDim=dim, endDim=dim + num) \n" - L"SplitDimension(x, dim, N) = ReshapeDimension(x, dim, 0:N) \n" - L"TransposeDimensions(input, dim1, dim2, tag='') = new ComputationNode [ operation = 'TransposeDimensions' ; inputs = input /*plus the function args*/ ]\n" - L"Transpose(x) = TransposeDimensions(x, 1, 2)\n" - L"Times(A, B, outputRank=1, tag='') = new ComputationNode [ operation = 'Times' ; inputs = ( A : B ) /*plus the function args*/ ]\n" - // TODO: Logistic should be generated with with BinaryStandardNode macro below. - L"Logistic(label, probability, tag='') = new ComputationNode [ operation = 'Logistic' ; inputs = (label : probability) /*plus the function args*/ ]\n" - L"WeightedLogistic(label, probability, instanceWeight, tag='') = new ComputationNode [ operation = 'Logistic' ; inputs = (label : probability : instanceWeight) /*plus the function args*/ ]\n" - L"ReconcileDynamicAxis(dataInput, layoutInput, tag='') = new ComputationNode [ operation = 'ReconcileDynamicAxis' ; inputs = (dataInput : layoutInput) /*plus the function args*/ ]\n" - L"Convolution(weightNode, inputValueNode, kernelWidth, kernelHeight, outputChannels, horizontalSubsample, verticalSubsample, zeroPadding = false, maxTempMemSizeInSamples = 0, imageLayout='CHW', tag='') = new ComputationNode [ operation = 'Convolution' ; inputs = (weightNode : inputValueNode) /*plus the function args*/ ]\n" - L"MaxPooling(input, windowWidth, windowHeight, horizontalSubsample, verticalSubsample, imageLayout='CHW', tag='') = new ComputationNode [ operation = 'MaxPooling' ; inputs = input /*plus the function args*/ ]\n" - L"AveragePooling(input, windowWidth, windowHeight, horizontalSubsample, verticalSubsample, imageLayout='CHW', tag='') = new ComputationNode [ operation = 'AveragePooling' ; inputs = input /*plus the function args*/ ]\n" - // TODO: define DelayedValue, with negative delay for future; cannot do this yet, need to be able to say something like delay = -(^.delay) - // aliases - L"ColumnwiseCrossProduct = KhatriRaoProduct // deprecated \n" // TODO: should it be deprecated? It is described as easier to understand in the CNTKBook. - L"ClassificationError = ErrorPrediction \n" - L"Delay = PastValue \n" // TODO: should it allow negative offsets and an if test here? - L"BatchNormalization(input, scale, bias, runMean, runInvStdDev, eval, spatial, normalizationTimeConstant = 0, epsilon = 0.00001, useCntkEngine = true, imageLayout='CHW', tag='') = new ComputationNode [ operation = 'BatchNormalization' ; inputs = (input : scale : bias : runMean : runInvStdDev) /*plus the function args*/ ]\n" -// standard nodes. We use macros to define these strings. -#define UnaryStandardNode(Op, a) L## #Op L"(" L## #a L", tag='') = new ComputationNode [ operation = '" L## #Op L"' ; inputs = " L## #a L" /*plus the function args*/ ]\n" -#define BinaryStandardNode(Op, a, b) L## #Op L"(" L## #a L", " L## #b L", tag='') = new ComputationNode [ operation = '" L## #Op L"' ; inputs = (" L## #a L" : " L## #b L") /*plus the function args*/ ]\n" -#define TernaryStandardNode(Op, a, b, c) L## #Op L"(" L## #a L", " L## #b L", " L## #c L", tag='') = new ComputationNode [ operation = '" L## #Op L"' ; inputs = (" L## #a L" : " L## #b L" : " L## #c L") /*plus the function args*/ ]\n" -#define QuaternaryStandardNode(Op, a, b, c, d) L## #Op L"(" L## #a L", " L## #b L", " L## #c L", " L## #d L", tag='') = new ComputationNode [ operation = '" L## #Op L"' ; inputs = (" L## #a L" : " L## #b L" : " L## #c L" : " L## #d L") /*plus the function args*/ ]\n" -#ifdef COMING_SOON - TernaryStandardNode(CRF, labelVectorSequence, positionDependenScoreVectorSequence, transitionScores) // TODO: better names -#endif - UnaryStandardNode(Abs, x) - QuaternaryStandardNode(ClassBasedCrossEntropyWithSoftmax, labelClassDescriptorVectorSequence, mainInputInfo, mainWeight, classLogProbsBeforeSoftmax) - // BUGBUG: the commented-out ones are not mentioned in the CNTK book, nor are their parameters documented in the source code - BinaryStandardNode(ColumnElementTimes, aVectorSequence, anotherVectorSequence) - BinaryStandardNode(CosDistance, aVectorSequence, anotherVectorSequence) - QuaternaryStandardNode(CosDistanceWithNegativeSamples, aVectorSequence, anotherVectorSequence, numShifts, numNegSamples) - //BinaryStandardNode(CosDistanceWithNegativeSamplesNode) - UnaryStandardNode(Cosine, x) - BinaryStandardNode(CrossEntropy, refProbVectorSequence, outProbVectorSequence) - BinaryStandardNode(CrossEntropyWithSoftmax, labelVectorSequence, outProbVectorSequence) - BinaryStandardNode(DiagTimes, diagonalMatrixAsColumnVector, matrix) - UnaryStandardNode(Dropout, activationVectorSequence) - //BinaryStandardNode(DummyCriterionNode) - BinaryStandardNode(ElementTimes, aMatrix, anotherMatrix) - BinaryStandardNode(ErrorPrediction, labelVectorSequence, outVectorSequence) // CNTKBook: ClassificationError? - UnaryStandardNode(Exp, x) - QuaternaryStandardNode(GMMLogLikelihood, unnormalizedPriorVector, meansAsRows, logStdDevAsRows, dataVectorSequence) - UnaryStandardNode(InvStdDev, dataVectorSequence) - BinaryStandardNode(KhatriRaoProduct, leftMatrix, rightMatrix) - //BinaryStandardNode(LSTMNode) - UnaryStandardNode(Log, x) - UnaryStandardNode(LogSoftmax, z) - //BinaryStandardNode(LookupTableNode) - UnaryStandardNode(MatrixL1Reg, matrix) - UnaryStandardNode(MatrixL2Reg, matrix) - // BUGBUG: CNTKBook also mentions L1Norm and L2Norm - UnaryStandardNode(Mean, dataVectorSequence) - BinaryStandardNode(Minus, leftMatrix, rightMatrix) - UnaryStandardNode(Negate, input) - TernaryStandardNode(PerDimMeanVarDeNormalization, dataVectorSequence, meanVector, invStdDevVector) // TODO: correct? - TernaryStandardNode(PerDimMeanVarNormalization, dataVectorSequence, meanVector, invStdDevVector) - BinaryStandardNode(Plus, leftMatrix, rightMatrix) - UnaryStandardNode(RectifiedLinear, z) - //BinaryStandardNode(RowElementTimesNode) - BinaryStandardNode(Scale, scalarScalingFactor, matrix) -#ifdef COMING_SOON - //BinaryStandardNode(SequenceDecoderNode) -#endif - UnaryStandardNode(Sigmoid, z) - UnaryStandardNode(Softmax, z) - UnaryStandardNode(Hardmax, z) - BinaryStandardNode(SquareError, aMatrix, anotherMatrix) - UnaryStandardNode(SumColumnElements, z) - UnaryStandardNode(SumElements, matrix) - UnaryStandardNode(Tanh, z) - UnaryStandardNode(TimeReverse, vectorSequence) - BinaryStandardNode(TransposeTimes, leftMatrix, rightMatrix) - // those nodes are deprecated, we won't implement them in BS: - //BinaryStandardNode(NoiseContrastiveEstimationNode) - //BinaryStandardNode(ParallelNode) - //BinaryStandardNode(StrideTimesNode) - ; -#endif diff --git a/Source/CNTK/CNTK.vcxproj b/Source/CNTK/CNTK.vcxproj index 5221a0798..2d7d7f927 100644 --- a/Source/CNTK/CNTK.vcxproj +++ b/Source/CNTK/CNTK.vcxproj @@ -203,7 +203,6 @@ - @@ -226,4 +225,4 @@ - + \ No newline at end of file diff --git a/Source/CNTK/CNTK.vcxproj.filters b/Source/CNTK/CNTK.vcxproj.filters index 012499f3f..f75404459 100644 --- a/Source/CNTK/CNTK.vcxproj.filters +++ b/Source/CNTK/CNTK.vcxproj.filters @@ -44,9 +44,6 @@ BrainScript - - BrainScript - Common diff --git a/Source/Common/Include/Sequences.h b/Source/Common/Include/Sequences.h index 6abb596b4..891a69c0d 100644 --- a/Source/Common/Include/Sequences.h +++ b/Source/Common/Include/Sequences.h @@ -120,6 +120,8 @@ struct MBLayout // copy the content of another MBLayoutPtr over // Use this instead of actual assignment to make it super-obvious that this is not copying the pointer but actual content. The pointer is kept fixed. + // Use "keepName" if the "identity" of the target is to be preserved, e.g. + // while copying from reader space to network space. void CopyFrom(const MBLayoutPtr& other, bool keepName=false) { m_numTimeSteps = other->m_numTimeSteps; @@ -521,7 +523,7 @@ private: size_t m_numFramesDeclared; size_t m_numGapFrames; - // Lookup tables for determining whether any sequence at time t is a identical layouts, do noboundary or gap. + // Lookup tables for determining whether any sequence at time t is a boundary or gap. // An optional time delay can be given, then the test is whether going from t to (t + time delay) crosses a boundary. // The purpose is for knowing when to reset state of a recurrent node. // diff --git a/Source/ComputationNetworkLib/ComputationNetworkBuilder.cpp b/Source/ComputationNetworkLib/ComputationNetworkBuilder.cpp index 5358abcf5..3f8abdd2d 100644 --- a/Source/ComputationNetworkLib/ComputationNetworkBuilder.cpp +++ b/Source/ComputationNetworkLib/ComputationNetworkBuilder.cpp @@ -76,7 +76,7 @@ static shared_ptr> CreateStandardNode(const std::wstri else if (nodeType == OperationNameOf(PerDimMeanVarDeNormalizationNode)) return New>(forward<_Types>(_Args)...); else if (nodeType == OperationNameOf(PassNode)) return New>(forward<_Types>(_Args)...); else if (nodeType == OperationNameOf(PlusNode)) return New>(forward<_Types>(_Args)...); - else if (nodeType == OperationNameOf(ReconcileDynamicAxisNode)) return New>(forward<_Types>(_Args)...); + else if (nodeType == OperationNameOf(ReconcileDynamicAxisNode)) return New>(forward<_Types>(_Args)...); else if (nodeType == OperationNameOf(ReciprocalNode)) return New>(forward<_Types>(_Args)...); else if (nodeType == OperationNameOf(RectifiedLinearNode)) return New>(forward<_Types>(_Args)...); else if (nodeType == OperationNameOf(ReshapeNode)) return New>(forward<_Types>(_Args)...); diff --git a/Source/ComputationNetworkLib/ComputationNetworkEvaluation.cpp b/Source/ComputationNetworkLib/ComputationNetworkEvaluation.cpp index 476cf7f2d..012e116f8 100644 --- a/Source/ComputationNetworkLib/ComputationNetworkEvaluation.cpp +++ b/Source/ComputationNetworkLib/ComputationNetworkEvaluation.cpp @@ -525,13 +525,19 @@ void ComputationNetwork::ResetMBLayouts() for (const auto& node : GetAllNodesForRoot(nullptr)) node->LinkToMBLayout(nullptr); - // DynamicAxis nodes are (apart from the network-wide MBLayout) the only holders of MBLayouts. Initialize them. + // DynamicAxis nodes are (apart from the network-wide MBLayout) the main holders of MBLayouts. Initialize them. + // The only other instances are nodes that change the MBLayout, like WhereNode. for (auto node : GetNodesWithType(L"DynamicAxis")) node->LinkToMBLayout(make_shared(1, 0, node->GetName())); // This is now initialized inside of the Input nodes, with the proper connections. for (auto node : InputNodes(nullptr)) - node->AttachDynamicAxis([&](std::wstring name) { return GetNodeFromName(name); }, /* default */ m_pMBLayoutOfNetwork); + { + auto n = dynamic_pointer_cast(node); + if (!n) + LogicError("Expected %ls to implement IDynamic, but it doesn't.", node->NodeDescription().c_str()); + n->AttachDynamicAxis([&](std::wstring name) { return GetNodeFromName(name); }, /* default */ m_pMBLayoutOfNetwork); + } } // ----------------------------------------------------------------------- diff --git a/Source/ComputationNetworkLib/ComputationNode.cpp b/Source/ComputationNetworkLib/ComputationNode.cpp index 3e43a7ca6..a64e02fc2 100644 --- a/Source/ComputationNetworkLib/ComputationNode.cpp +++ b/Source/ComputationNetworkLib/ComputationNode.cpp @@ -332,28 +332,25 @@ TensorShape ComputationNodeBase::GetOneSampleTensorSliceFor(size_t rank, const F prototype += "NULL"; continue; } - - const char* mbSizeMark = child->m_pMBLayout ? " x " : ""; -#if 0 - if (child->m_sampleLayout.GetRank() == 3 && (child->m_sampleLayout[1] != 1 || child->m_sampleLayout[0] != 1)) // looks like an image: use WHC notation - prototype += msra::strfun::strprintf("%ls[%s%s {W=%lu, H=%lu, C=%lu}]", child->NodeName().c_str(), string(child->m_sampleLayout).c_str(), mbSizeMark, - child->m_sampleLayout[1], child->m_sampleLayout[2], child->m_sampleLayout[0]); - // BUGBUG: This ^^ will print based on the old legacy layout, and we have no way of knowing here whether that is correct. - else -#endif - prototype += msra::strfun::strprintf("[%s%s%ls]", string(child->m_sampleLayout).c_str(), mbSizeMark, - child->HasMBLayout() ? child->GetMBLayout()->GetAxisName().c_str() : L""); + prototype += child->ShapeDescription().c_str(); } prototype += extraArgs; //prototype += ")"; } - prototype += msra::strfun::strprintf(" -> [%s%s%ls]", string(GetSampleLayout()).c_str(), HasMBLayout() ? " x " : "", - HasMBLayout() ? GetMBLayout()->GetAxisName().c_str() : L""); + prototype += msra::strfun::strprintf(" -> %s", ShapeDescription().c_str()); return prototype; } +const std::string ComputationNodeBase::ShapeDescription() const +{ + return msra::strfun::strprintf("[%s%s%ls]", + string(m_sampleLayout).c_str(), + HasMBLayout() ? " x " : "", + HasMBLayout() ? GetMBLayout()->GetAxisName().c_str() : L""); +} + template /*virtual*/ void ComputationNode::DumpNodeInfo(const bool /*printValues*/, const bool printMetadata, File& fstream) const { diff --git a/Source/ComputationNetworkLib/ComputationNode.h b/Source/ComputationNetworkLib/ComputationNode.h index 65ebe98c4..55343b4d9 100644 --- a/Source/ComputationNetworkLib/ComputationNode.h +++ b/Source/ComputationNetworkLib/ComputationNode.h @@ -473,12 +473,6 @@ public: VerifyDims(node->GetSampleLayout(), node->HasMBLayout()); } - virtual void AttachDynamicAxis(std::function nodeLookup, MBLayoutPtr defaultLayout) - { - // Applies only to input nodes. - NOT_IMPLEMENTED; - } - // MBLayout (minibatch structure) void LinkToMBLayout(MBLayoutPtr pMBLayout) { @@ -551,7 +545,7 @@ public: return GetInputsFromConfig(configp, L"inputs"); } - static vector GetInputsFromConfig(const ScriptableObjects::IConfigRecordPtr configp, const std::wstring property) + static vector GetInputsFromConfig(const ScriptableObjects::IConfigRecordPtr configp, const std::wstring& property) { vector inputs; const auto* inputsArg = configp->Find(property); @@ -810,6 +804,9 @@ public: return std::wstring(L"Node '") + NodeName().c_str() + L"' (" + OperationName().c_str() + L" operation)"; }; + // Helper that returns [a x b x c], including dynamic axes. + const std::string ShapeDescription() const; + protected: // ----------------------------------------------------------------------- @@ -844,7 +841,8 @@ protected: typedef ComputationNodeBase::ComputationNodeBasePtr ComputationNodeBasePtr; // ======================================================================= -// NumInputs -- little helper interface to allow derived Node classes to specify how many inputs they expect +// NumInputs -- little helper interface to allow derived Node classes to +// specify how many inputs they expect // ======================================================================= struct INumInputs { virtual size_t GetExpectedNumInputs() const = 0; }; @@ -857,6 +855,14 @@ struct NumInputs : public INumInputs // e.g. derive from NumInputs<2> } }; +// ======================================================================= +// +// ======================================================================= +struct IDynamic +{ + virtual void AttachDynamicAxis(std::function nodeLookup, MBLayoutPtr defaultLayout) = 0; +}; + // ======================================================================= // ComputationNode -- abstract base class for computation nodes, deriving // from CompuationNodeBase, parameterized by float vs. double @@ -1921,7 +1927,6 @@ protected: public: \ using Base::AttachInputs; \ using Base::AttachInputsFromConfig; \ - using Base::AttachDynamicAxis; \ using Base::CreateGradientMatrixIfNull; \ using Base::NodeName; \ using Base::RequiresPreCompute; \ diff --git a/Source/ComputationNetworkLib/InputAndParamNodes.h b/Source/ComputationNetworkLib/InputAndParamNodes.h index e7cfe56a2..8560f16a7 100644 --- a/Source/ComputationNetworkLib/InputAndParamNodes.h +++ b/Source/ComputationNetworkLib/InputAndParamNodes.h @@ -129,7 +129,7 @@ public: m_displayName = name; } DynamicAxisNode(const ScriptableObjects::IConfigRecordPtr configp) - : DynamicAxisNode(configp->Get(L"deviceId"), (const std::wstring&)configp->Get(L"name")) + : DynamicAxisNode(configp->Get(L"deviceId"), (const std::wstring&)configp->Get(L"axisName")) { } @@ -168,7 +168,7 @@ template class DynamicAxisNode; // ----------------------------------------------------------------------- template -class InputValueBase : public ComputationNode, public NumInputs<0> +class InputValueBase : public ComputationNode, public NumInputs<0>, public IDynamic { typedef ComputationNode Base; UsingComputationNodeMembers; @@ -205,11 +205,11 @@ protected: { AttachInputsFromConfig(configp, this->GetExpectedNumInputs()); wstring axisName = L""; - if (configp->Exists(L"dynamicAxes")) + if (configp->Exists(L"dynamicAxis")) { - if (configp->Find(L"dynamicAxes")->Is()) + if (configp->Find(L"dynamicAxis")->Is()) { - ComputationNodeBasePtr axis = configp->Get(L"dynamicAxes"); + ComputationNodeBasePtr axis = configp->Get(L"dynamicAxis"); axisName = axis->GetName(); } // Else: Use default axis. @@ -245,7 +245,7 @@ protected: // more dependencies on the order in which things are evaluated, though. if (!node->Is>()) RuntimeError("%ls: dynamicAxis argument must be of type DynamicAxis(), but got %ls.", - NodeDescription().c_str(), node->NodeDescription().c_str()); + NodeDescription().c_str(), node->NodeDescription().c_str()); m_dynamicAxisNode = node->As>(); if (!m_dynamicAxisNode->HasMBLayout()) LogicError("%ls: Expected %ls to have MBLayout, but it doesn't.", NodeDescription().c_str(), node->NodeDescription().c_str()); diff --git a/Tests/UnitTests/NetworkTests/NetworkTests.vcxproj b/Tests/UnitTests/NetworkTests/NetworkTests.vcxproj index 8b0e9442c..93cc1f442 100644 --- a/Tests/UnitTests/NetworkTests/NetworkTests.vcxproj +++ b/Tests/UnitTests/NetworkTests/NetworkTests.vcxproj @@ -122,7 +122,6 @@ - diff --git a/Tests/UnitTests/NetworkTests/NetworkTests.vcxproj.filters b/Tests/UnitTests/NetworkTests/NetworkTests.vcxproj.filters index 359a25534..19572d2f1 100644 --- a/Tests/UnitTests/NetworkTests/NetworkTests.vcxproj.filters +++ b/Tests/UnitTests/NetworkTests/NetworkTests.vcxproj.filters @@ -34,9 +34,6 @@ From BrainScript - - From BrainScript -