// // Copyright (c) Microsoft. All rights reserved. // Licensed under the MIT license. See LICENSE.md file in the project root for full license information. // // ComputationNetworkBuilder -- helper class for constructing ComputationNetworks and ComputationNodes from C++ (internal and external) // #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 "Basics.h" #include "ComputationNetworkBuilder.h" #include "ComputationNode.h" #include "ConvolutionalNodes.h" #include "RNNNodes.h" #include "DeprecatedNodes.h" #include "EvaluationNodes.h" #include "InputAndParamNodes.h" #include "LinearAlgebraNodes.h" #include "NonlinearityNodes.h" #include "PreComputeNodes.h" #include "ReshapingNodes.h" #include "RecurrentNodes.h" #include "SpecialPurposeNodes.h" #include "TrainingNodes.h" #include namespace Microsoft { namespace MSR { namespace CNTK { using namespace std; // create a new node of a type given as a string, with var args so that this can be used at multiple places template static shared_ptr> CreateStandardNode(const std::wstring& nodeType, _Types&&... _Args) { // please keep this table sorted #ifdef COMING_SOON if (nodeType == OperationNameOf(CRFNode)) return New>(forward<_Types>(_Args)...); else #endif if (nodeType == OperationNameOf(AbsNode)) return New>(forward<_Types>(_Args)...); else if (nodeType == OperationNameOf(ClassBasedCrossEntropyWithSoftmaxNode))return New>(forward<_Types>(_Args)...); else if (nodeType == OperationNameOf(ClassificationErrorNode)) return New>(forward<_Types>(_Args)...); else if (nodeType == OperationNameOf(ClipNode)) return New>(forward<_Types>(_Args)...); else if (nodeType == OperationNameOf(CosDistanceNode)) return New>(forward<_Types>(_Args)...); else if (nodeType == OperationNameOf(CosDistanceWithNegativeSamplesNode)) return New>(forward<_Types>(_Args)...); else if (nodeType == OperationNameOf(CosineNode)) return New>(forward<_Types>(_Args)...); else if (nodeType == OperationNameOf(CropNode)) return New>(forward<_Types>(_Args)...); else if (nodeType == OperationNameOf(CrossEntropyNode)) return New>(forward<_Types>(_Args)...); else if (nodeType == OperationNameOf(CrossEntropyWithSoftmaxNode)) return New>(forward<_Types>(_Args)...); else if (nodeType == OperationNameOf(DiagonalNode)) return New>(forward<_Types>(_Args)...); else if (nodeType == OperationNameOf(DiagTimesNode)) return New>(forward<_Types>(_Args)...); else if (nodeType == OperationNameOf(DropoutNode)) return New>(forward<_Types>(_Args)...); else if (nodeType == OperationNameOf(DummyCriterionNode)) return New>(forward<_Types>(_Args)...); else if (nodeType == OperationNameOf(DynamicAxisNode)) return New>(forward<_Types>(_Args)...); else if (nodeType == OperationNameOf(ElementTimesNode)) return New>(forward<_Types>(_Args)...); else if (nodeType == OperationNameOf(EnvironmentInputNode)) return New>(forward<_Types>(_Args)...); else if (nodeType == OperationNameOf(EpochAccumulatorNode)) return New>(forward<_Types>(_Args)...); else if (nodeType == OperationNameOf(EqualNode)) return New>(forward<_Types>(_Args)...); else if (nodeType == OperationNameOf(ExpNode)) return New>(forward<_Types>(_Args)...); else if (nodeType == OperationNameOf(FloorNode)) return New>(forward<_Types>(_Args)...); else if (nodeType == OperationNameOf(FutureValueNode)) return New>(forward<_Types>(_Args)...); else if (nodeType == OperationNameOf(GatherPackedNode)) return New>(forward<_Types>(_Args)...); #ifdef COMING_SOON else if (nodeType == OperationNameOf(GMMLogLikelihoodNode)) return New>(forward<_Types>(_Args)...); #endif else if (nodeType == OperationNameOf(GreaterEqualNode)) return New>(forward<_Types>(_Args)...); else if (nodeType == OperationNameOf(GreaterNode)) return New>(forward<_Types>(_Args)...); else if (nodeType == OperationNameOf(HardmaxNode)) return New>(forward<_Types>(_Args)...); else if (nodeType == OperationNameOf(IfNode)) return New>(forward<_Types>(_Args)...); else if (nodeType == OperationNameOf(InvStdDevNode)) return New>(forward<_Types>(_Args)...); else if (nodeType == OperationNameOf(LambdaRankNode)) return New>(forward<_Types>(_Args)...); else if (nodeType == OperationNameOf(NDCG1EvalNode)) return New>(forward<_Types>(_Args)...); else if (nodeType == OperationNameOf(KhatriRaoProductNode)) return New>(forward<_Types>(_Args)...); else if (nodeType == OperationNameOf(LessEqualNode)) return New>(forward<_Types>(_Args)...); else if (nodeType == OperationNameOf(LessNode)) return New>(forward<_Types>(_Args)...); else if (nodeType == OperationNameOf(LogNode)) return New>(forward<_Types>(_Args)...); else if (nodeType == OperationNameOf(LogPlusNode)) return New>(forward<_Types>(_Args)...); else if (nodeType == OperationNameOf(LogSoftmaxNode)) return New>(forward<_Types>(_Args)...); else if (nodeType == OperationNameOf(LookupTableNode)) return New>(forward<_Types>(_Args)...); else if (nodeType == OperationNameOf(MatrixL1RegNode)) return New>(forward<_Types>(_Args)...); else if (nodeType == OperationNameOf(MatrixL2RegNode)) return New>(forward<_Types>(_Args)...); else if (nodeType == OperationNameOf(MeanNode)) return New>(forward<_Types>(_Args)...); else if (nodeType == OperationNameOf(MinusNode)) return New>(forward<_Types>(_Args)...); else if (nodeType == OperationNameOf(NegateNode)) return New>(forward<_Types>(_Args)...); else if (nodeType == OperationNameOf(NotEqualNode)) return New>(forward<_Types>(_Args)...); else if (nodeType == OperationNameOf(NoiseContrastiveEstimationNode)) return New>(forward<_Types>(_Args)...); else if (nodeType == OperationNameOf(OptimizedRNNStackNode)) return New>(forward<_Types>(_Args)...); else if (nodeType == OperationNameOf(PackedIndexNode)) return New>(forward<_Types>(_Args)...); else if (nodeType == OperationNameOf(PastValueNode)) return New>(forward<_Types>(_Args)...); else if (nodeType == OperationNameOf(PerDimMeanVarNormalizationNode)) return New>(forward<_Types>(_Args)...); 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(RandomSampleNode)) return New>(forward<_Types>(_Args)...); else if (nodeType == OperationNameOf(RandomSampleInclusionFrequencyNode)) 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(ReduceElementsNode)) return New>(forward<_Types>(_Args)...); else if (nodeType == OperationNameOf(ReshapeNode)) return New>(forward<_Types>(_Args)...); else if (nodeType == OperationNameOf(RowRepeatNode)) return New>(forward<_Types>(_Args)...); else if (nodeType == OperationNameOf(RowStackNode)) return New>(forward<_Types>(_Args)...); else if (nodeType == OperationNameOf(ScatterPackedNode)) return New>(forward<_Types>(_Args)...); else if (nodeType == OperationNameOf(SequenceWithSoftmaxNode)) return New>(forward<_Types>(_Args)...); #ifdef COMING_SOON else if (nodeType == OperationNameOf(SequenceDecoderNode)) return New>(forward<_Types>(_Args)...); #endif #ifdef COMING_SOON else if (nodeType == OperationNameOf(ShiftNode)) return New>(forward<_Types>(_Args)...); #endif else if (nodeType == OperationNameOf(SigmoidNode)) return New>(forward<_Types>(_Args)...); else if (nodeType == OperationNameOf(SinNode)) return New>(forward<_Types>(_Args)...); else if (nodeType == OperationNameOf(SliceNode)) return New>(forward<_Types>(_Args)...); else if (nodeType == OperationNameOf(SoftmaxNode)) return New>(forward<_Types>(_Args)...); else if (nodeType == OperationNameOf(SqrtNode)) return New>(forward<_Types>(_Args)...); else if (nodeType == OperationNameOf(SquareErrorNode)) return New>(forward<_Types>(_Args)...); else if (nodeType == OperationNameOf(LogisticNode)) return New>(forward<_Types>(_Args)...); else if (nodeType == OperationNameOf(SumColumnElementsNode)) return New>(forward<_Types>(_Args)...); else if (nodeType == OperationNameOf(SumElementsNode)) return New>(forward<_Types>(_Args)...); else if (nodeType == OperationNameOf(TanhNode)) return New>(forward<_Types>(_Args)...); else if (nodeType == OperationNameOf(TraceNode)) return New>(forward<_Types>(_Args)...); else if (nodeType == OperationNameOf(TimesNode)) return New>(forward<_Types>(_Args)...); else if (nodeType == OperationNameOf(TransposeDimensionsNode)) return New>(forward<_Types>(_Args)...); else if (nodeType == OperationNameOf(TransposeTimesNode)) return New>(forward<_Types>(_Args)...); else if (nodeType == OperationNameOf(QuantizedTimesNode)) return New>(forward<_Types>(_Args)...); else if (nodeType == OperationNameOf(WhereNode)) return New>(forward<_Types>(_Args)...); // legacy names we also support for back compat of model-files else if (nodeType == L"ColumnElementTimes") return New>(forward<_Types>(_Args)...); else if (nodeType == L"ErrorPrediction") return New>(forward<_Types>(_Args)...); else if (nodeType == L"Delay") return New>(forward<_Types>(_Args)...); // TODO: DiagTimes is also an alias of ElementTimes; current separate implementation is unnecessary. else if (nodeType == L"PerDimMeanVarNormalizationNode") return New>(forward<_Types>(_Args)...); else if (nodeType == L"PerDimMeanVarDeNormalizationNode") return New>(forward<_Types>(_Args)...); else if (nodeType == L"ReconcileMBLayout") return New>(forward<_Types>(_Args)...); else if (nodeType == L"RNN") return New>(forward<_Types>(_Args)...); else if (nodeType == L"RowElementTimes") return New>(forward<_Types>(_Args)...); else if (nodeType == L"RowSlice") return New>(forward<_Types>(_Args)...); else if (nodeType == L"Scale") return New>(forward<_Types>(_Args)...); else if (nodeType == L"Transpose") return New>(forward<_Types>(_Args)...); #if 1 else if (nodeType == OperationNameOf(LegacyReshapeNode)) return New>(forward<_Types>(_Args)...); #endif else if (nodeType == OperationNameOf(MaxUnpoolingNode)) return New>(forward<_Types>(_Args)...); else InvalidArgument("Attempted to instantiate undefined operation %ls.", nodeType.c_str()); } // create a new node of a type given as a string, with var args so that this can be used at multiple places // This function is used for loading, while the above is used for creating standard-type networks. template static shared_ptr> CreateNode(const std::wstring& nodeType, _Types&&... _Args) { // check more types if (nodeType == OperationNameOf(AveragePoolingNode)) return New>(forward<_Types>(_Args)...); else if (nodeType == OperationNameOf(BatchNormalizationNode)) return New>(forward<_Types>(_Args)...); else if (nodeType == OperationNameOf(ConvolutionNode)) return New>(forward<_Types>(_Args)...); else if (nodeType == OperationNameOf(PoolingNode)) return New>(forward<_Types>(_Args)...); else if (nodeType == OperationNameOf(SparseInputValue)) return New>(forward<_Types>(_Args)...); else if (nodeType == OperationNameOf(InputValue)) return New>(forward<_Types>(_Args)...); else if (nodeType == OperationNameOf(LearnableParameter)) return New>(forward<_Types>(_Args)...); else if (nodeType == OperationNameOf(MaxPoolingNode)) return New>(forward<_Types>(_Args)...); else if (nodeType == OperationNameOf(ROIPoolingNode)) return New>(forward<_Types>(_Args)...); else return CreateStandardNode(nodeType, forward<_Types>(_Args)...); } // this function is called from SimpleNetworkBuilder and old NDL template /*static*/ shared_ptr> ComputationNetworkBuilder::NewStandardNode(const std::wstring& nodeType, DEVICEID_TYPE deviceId, const wstring& name) { return CreateStandardNode(nodeType, deviceId, name); } // this function is used when loading from file template /*static*/ shared_ptr> ComputationNetworkBuilder::NewNode(const std::wstring& nodeType, DEVICEID_TYPE deviceId, const wstring& name) { return CreateNode(nodeType, deviceId, name); } shared_ptr NewComputationNodeFromConfig(const Microsoft::MSR::ScriptableObjects::IConfigRecordPtr configp) { wstring precision = configp->Get(L"precision"); // dispatch on ElemType wstring operationName = configp->Get(L"operation"); ComputationNodeBasePtr node; if (precision == L"float") node = CreateNode(operationName, configp); else if (precision == L"double") node = CreateNode(operationName, configp); else RuntimeError("NewStandardNode: Invalid value '%ls' for 'precision' parameter. Must be 'float' or 'double'.", precision.c_str()); // add a tag // Tags are used to declare special node types to ComputationNetwork. // For now we support only a single tag, but we could in the future easily extend this to an array of tags. wstring tag = configp->Get(L"tag"); if (!tag.empty()) node->SetTag(tag); return node; } // ----------------------------------------------------------------------- // node creation // ----------------------------------------------------------------------- // The following functions create nodes and add them to the net, but don't attach inputs (some don't have inputs). // There are special versions for nodes with custom constructors, and a catch-all, CreateComputationNode(), for all others. // TODO: Do we really need these? Folks who want to use C++ can instead say net->AddNodeToNet(New<>(...)), which is not that different. // TODO: separate into nodes that have inputs and those that duplicate functions with input adding except just not adding inputs. Clear? template shared_ptr> ComputationNetworkBuilder::CreateLearnableParameter(const std::wstring& paramName, const size_t rows, const size_t cols) { // TODO: in SimpleNetworkBuilder, this is very often followed by InitLearnableParameter()--we should have an overload that just does it right away return net.AddNodeToNetWithElemType(New>(net.GetDeviceId(), paramName, rows, cols)); } template shared_ptr> ComputationNetworkBuilder::CreateLearnableParameter(const std::wstring& paramName, const TensorShape& tensorShape) { return net.AddNodeToNetWithElemType(New>(net.GetDeviceId(), paramName, tensorShape)); } // TODO: change these to take an actual object instead of a name for dynamicAxis template shared_ptr> ComputationNetworkBuilder::CreateInputNode(const std::wstring& inputName, const size_t rows, const wstring& dynamicAxisName) { return net.AddNodeToNetWithElemType(New>(net.GetDeviceId(), inputName, rows, dynamicAxisName)); } template shared_ptr> ComputationNetworkBuilder::CreateSparseInputNode(const std::wstring& inputName, const size_t rows, const wstring& dynamicAxisName) { return net.AddNodeToNetWithElemType(New>(net.GetDeviceId(), inputName, rows, dynamicAxisName)); } template shared_ptr> ComputationNetworkBuilder::CreateInputNode(const std::wstring& inputName, const TensorShape& sampleLayout, const wstring& dynamicAxisName) { return net.AddNodeToNetWithElemType(New>(net.GetDeviceId(), inputName, sampleLayout, dynamicAxisName)); } template shared_ptr> ComputationNetworkBuilder::CreateSparseInputNode(const std::wstring& inputName, const TensorShape& imageLayout, const wstring& dynamicAxisName) { return net.AddNodeToNetWithElemType(New>(net.GetDeviceId(), inputName, imageLayout, dynamicAxisName)); } template shared_ptr> ComputationNetworkBuilder::CreateConvolutionNode(const std::wstring& nodeName, const size_t kernelWidth, const size_t kernelHeight, const size_t outputChannels, const size_t horizontalSubsample, const size_t verticalSubsample, ImageLayoutKind imageLayoutKind, const bool zeroPadding, const size_t maxTempMemSizeInSamples) { return net.AddNodeToNetWithElemType(New>(net.GetDeviceId(), nodeName, kernelWidth, kernelHeight, outputChannels, horizontalSubsample, verticalSubsample, imageLayoutKind, zeroPadding, maxTempMemSizeInSamples)); } template shared_ptr> ComputationNetworkBuilder::CreateConvolutionNode(const std::wstring& nodeName, const TensorShape& kernelShape, const TensorShape& mapCount, const TensorShape& strideShape, const std::vector& sharing, const std::vector& autoPadding, const TensorShape& lowerPad, const TensorShape& upperPad, bool transpose, ImageLayoutKind imageLayout, size_t maxTempMemSizeInSamples) { return net.AddNodeToNetWithElemType(New>(net.GetDeviceId(), nodeName, kernelShape, mapCount, strideShape, sharing, autoPadding, lowerPad, upperPad, transpose, imageLayout, maxTempMemSizeInSamples)); } template shared_ptr> ComputationNetworkBuilder::CreatePoolingNode(const std::wstring& nodeName, PoolKind poolKind, const TensorShape& kernelShape, const TensorShape& strideShape, const std::vector& autoPadding, const TensorShape& lowerPad, const TensorShape& upperPad, ImageLayoutKind imageLayout) { return net.AddNodeToNetWithElemType(New>(net.GetDeviceId(), nodeName, poolKind, kernelShape, strideShape, autoPadding, lowerPad, upperPad, imageLayout)); } template shared_ptr> ComputationNetworkBuilder::CreateMaxPoolingNode(const std::wstring& nodeName, const size_t windowWidth, const size_t windowHeight, const size_t horizontalSubsample, const size_t verticalSubsample, ImageLayoutKind imageLayoutKind) { return net.AddNodeToNetWithElemType(New>(net.GetDeviceId(), nodeName, windowWidth, windowHeight, horizontalSubsample, verticalSubsample, imageLayoutKind)); } template shared_ptr> ComputationNetworkBuilder::CreateAveragePoolingNode(const std::wstring& nodeName, const size_t windowWidth, const size_t windowHeight, const size_t horizontalSubsample, const size_t verticalSubsample, ImageLayoutKind imageLayoutKind) { return net.AddNodeToNetWithElemType(New>(net.GetDeviceId(), nodeName, windowWidth, windowHeight, horizontalSubsample, verticalSubsample, imageLayoutKind)); } template shared_ptr> ComputationNetworkBuilder::CreateROIPoolingNode(const std::wstring& nodeName, const TensorShape& roiOutputShape) { return net.AddNodeToNetWithElemType(New>(net.GetDeviceId(), nodeName, roiOutputShape)); } template shared_ptr> ComputationNetworkBuilder::CreateReconcileDynamicAxisNode(const std::wstring& nodeName) { return net.AddNodeToNetWithElemType(New>(net.GetDeviceId(), nodeName)); } // this is the catch-all for all cases not covered as special cases above // Unlike the specialized ones above, this one creates nodes by type given as a string. template shared_ptr> ComputationNetworkBuilder::CreateComputationNode(const std::wstring& nodeType, const std::wstring& nodeName) { return net.AddNodeToNetWithElemType(NewStandardNode(nodeType, net.GetDeviceId(), nodeName)); } // ----------------------------------------------------------------------- // node creation // ----------------------------------------------------------------------- // The following functions create nodes and link them to the network and their inputs. // TODO: Do we need both this set and the one above that does not add inputs? Can they share more code? template shared_ptr> ComputationNetworkBuilder::Convolution(const ComputationNodePtr weight, const ComputationNodePtr inputValues, const size_t kernelWidth, const size_t kernelHeight, const size_t outputChannels, const size_t horizontalSubsample, const size_t verticalSubsample, ImageLayoutKind imageLayoutKind, const bool zeroPadding, const size_t maxTempMemSizeInSamples, const std::wstring nodeName) { return net.AddNodeToNetAndAttachInputs(New>(net.GetDeviceId(), nodeName, kernelWidth, kernelHeight, outputChannels, horizontalSubsample, verticalSubsample, imageLayoutKind, zeroPadding, maxTempMemSizeInSamples), { weight, inputValues }); } template shared_ptr> ComputationNetworkBuilder::Convolution(const ComputationNodePtr weight, const ComputationNodePtr inputValues, const TensorShape& kernelShape, const TensorShape& mapCount, const TensorShape& strideShape, const std::vector& sharing, const std::vector& autoPadding, const TensorShape& lowerPad, const TensorShape& upperPad, bool transpose, ImageLayoutKind imageLayout, size_t maxTempMemSizeInSamples, const std::wstring nodeName) { return net.AddNodeToNetAndAttachInputs(New>(net.GetDeviceId(), nodeName, kernelShape, mapCount, strideShape, sharing, autoPadding, lowerPad, upperPad, transpose, imageLayout, maxTempMemSizeInSamples), { weight, inputValues }); } template shared_ptr> ComputationNetworkBuilder::Pooling(const ComputationNodePtr inputValues, PoolKind poolKind, const TensorShape& kernelShape, const TensorShape& strideShape, const std::vector& autoPadding, const TensorShape& lowerPad, const TensorShape& upperPad, ImageLayoutKind imageLayout, const std::wstring nodeName) { return net.AddNodeToNetAndAttachInputs(New>(net.GetDeviceId(), nodeName, poolKind, kernelShape, strideShape, autoPadding, lowerPad, upperPad, imageLayout), { inputValues }); } template shared_ptr> ComputationNetworkBuilder::MaxUnpooling(const ComputationNodePtr unpoolInputValues, const ComputationNodePtr poolInputValues, const TensorShape& kernelShape, const TensorShape& strideShape, const std::vector& autoPadding, const TensorShape& lowerPad, const TensorShape& upperPad, ImageLayoutKind imageLayout, const std::wstring nodeName) { return net.AddNodeToNetAndAttachInputs(New>(net.GetDeviceId(), nodeName, kernelShape, strideShape, autoPadding, lowerPad, upperPad, imageLayout), { unpoolInputValues, poolInputValues }); } template shared_ptr> ComputationNetworkBuilder::MaxPooling(const ComputationNodePtr inputValues, const size_t windowWidth, const size_t windowHeight, const size_t horizontalSubsample, const size_t verticalSubsample, ImageLayoutKind imageLayoutKind, const std::wstring nodeName) { return net.AddNodeToNetAndAttachInputs(New>(net.GetDeviceId(), nodeName, windowWidth, windowHeight, horizontalSubsample, verticalSubsample, imageLayoutKind), { inputValues }); } template shared_ptr> ComputationNetworkBuilder::AveragePooling(const ComputationNodePtr inputValues, const size_t windowWidth, const size_t windowHeight, const size_t horizontalSubsample, const size_t verticalSubsample, ImageLayoutKind imageLayoutKind, const std::wstring nodeName) { return net.AddNodeToNetAndAttachInputs(New>(net.GetDeviceId(), nodeName, windowWidth, windowHeight, horizontalSubsample, verticalSubsample, imageLayoutKind), { inputValues }); } template shared_ptr> ComputationNetworkBuilder::ROIPooling(const ComputationNodePtr inputValues, const ComputationNodePtr inputROIs, const TensorShape& roiOutputShape, const std::wstring nodeName) { return net.AddNodeToNetAndAttachInputs(New>(net.GetDeviceId(), nodeName, roiOutputShape), { inputValues, inputROIs }); } template shared_ptr> ComputationNetworkBuilder::ReconcileDynamicAxis(const ComputationNodePtr dataInput, const ComputationNodePtr layoutInput, const std::wstring nodeName) { return net.AddNodeToNetAndAttachInputs(New>(net.GetDeviceId(), nodeName), { dataInput, layoutInput }); } template shared_ptr> ComputationNetworkBuilder::Crop(const ComputationNodePtr input1, const ComputationNodePtr input2, const std::wstring nodeName) { return net.AddNodeToNetAndAttachInputs(New>(net.GetDeviceId(), nodeName), { input1, input2 }); } template shared_ptr> ComputationNetworkBuilder::Crop(const ComputationNodePtr input1, const ComputationNodePtr input2, size_t offsetX, size_t offsetY, const std::wstring nodeName) { return net.AddNodeToNetAndAttachInputs(New>(offsetX, offsetY, net.GetDeviceId(), nodeName), { input1, input2 }); } template shared_ptr> ComputationNetworkBuilder::Crop(const ComputationNodePtr input1, const ComputationNodePtr input2, const ComputationNodePtr eqNode1, const ComputationNodePtr eqNode2, const std::wstring nodeName) { return net.AddNodeToNetAndAttachInputs(New>(net.GetDeviceId(), nodeName), { input1, input2, eqNode1, eqNode2 }); } template shared_ptr> ComputationNetworkBuilder::ClassificationError(const ComputationNodePtr a, const ComputationNodePtr b, const std::wstring nodeName) { return net.AddNodeToNetAndAttachInputs(New>(net.GetDeviceId(), nodeName), { a, b }); } template shared_ptr> ComputationNetworkBuilder::PerDimMeanVarNormalization(const ComputationNodePtr feature, const ComputationNodePtr mean, const ComputationNodePtr InvStdDev, const std::wstring nodeName) { return net.AddNodeToNetAndAttachInputs(New>(net.GetDeviceId(), nodeName), { feature, mean, InvStdDev }); } template shared_ptr> ComputationNetworkBuilder::PerDimMeanVarDeNormalization(const ComputationNodePtr feature, const ComputationNodePtr mean, const ComputationNodePtr InvStdDev, const std::wstring nodeName) { return net.AddNodeToNetAndAttachInputs(New>(net.GetDeviceId(), nodeName), { feature, mean, InvStdDev }); } template shared_ptr> ComputationNetworkBuilder::SquareError(const ComputationNodePtr a, const ComputationNodePtr b, const std::wstring nodeName) { return net.AddNodeToNetAndAttachInputs(New>(net.GetDeviceId(), nodeName), { a, b }); } template shared_ptr> ComputationNetworkBuilder::Logistic(const ComputationNodePtr a, const ComputationNodePtr b, const std::wstring nodeName) { return net.AddNodeToNetAndAttachInputs(New>(net.GetDeviceId(), nodeName), { a, b }); } template shared_ptr> ComputationNetworkBuilder::Logistic(const ComputationNodePtr a, const ComputationNodePtr b, const ComputationNodePtr c, const std::wstring nodeName) { return net.AddNodeToNetAndAttachInputs(New>(net.GetDeviceId(), nodeName), { a, b, c }); } #ifdef COMING_SOON template shared_ptr> ComputationNetworkBuilder::SequenceDecoder(const ComputationNodePtr label, const ComputationNodePtr prediction, const ComputationNodePtr pairscore, const std::wstring nodeName) { return net.AddNodeToNetAndAttachInputs(New>(net.GetDeviceId(), nodeName), { label, prediction, pairscore }); } #endif template shared_ptr> ComputationNetworkBuilder::CrossEntropyWithSoftmax(const ComputationNodePtr label, const ComputationNodePtr prediction, const std::wstring nodeName) { return net.AddNodeToNetAndAttachInputs(New>(net.GetDeviceId(), nodeName), { label, prediction }); } template shared_ptr> ComputationNetworkBuilder::LambdaRank(const ComputationNodePtr gain, const ComputationNodePtr prediction, const ComputationNodePtr queryId, const std::wstring nodeName) { return net.AddNodeToNetAndAttachInputs(New>(net.GetDeviceId(), nodeName), { gain, prediction, queryId }); } template shared_ptr> ComputationNetworkBuilder::NDCG1Eval(const ComputationNodePtr gain, const ComputationNodePtr prediction, const ComputationNodePtr queryId, const std::wstring nodeName) { return net.AddNodeToNetAndAttachInputs(New>(net.GetDeviceId(), nodeName), { gain, prediction, queryId }); } template shared_ptr> ComputationNetworkBuilder::SequenceWithSoftmax(const ComputationNodePtr label, const ComputationNodePtr prediction, const ComputationNodePtr loglikelihood, const std::wstring nodeName) { return net.AddNodeToNetAndAttachInputs(New>(net.GetDeviceId(), nodeName), { label, prediction, loglikelihood }); } template shared_ptr> ComputationNetworkBuilder::NoiseContrastiveEstimation(const ComputationNodePtr label, const ComputationNodePtr prediction, const ComputationNodePtr input_weight, const ComputationNodePtr input_bias, const std::wstring nodeName, NCEEvalMode mode) { return net.AddNodeToNetAndAttachInputs(New>(net.GetDeviceId(), nodeName, mode), { label, prediction, input_weight, input_bias }); } template shared_ptr> ComputationNetworkBuilder::ClassCrossEntropyWithSoftmax(const ComputationNodePtr label, const ComputationNodePtr prediction, const ComputationNodePtr input_weight, const ComputationNodePtr cls_log_post_prob, const std::wstring nodeName) { return net.AddNodeToNetAndAttachInputs(New>(net.GetDeviceId(), nodeName), { label, prediction, input_weight, cls_log_post_prob }); } template shared_ptr> ComputationNetworkBuilder::Clip(const ComputationNodePtr a, const ComputationNodePtr b, const ComputationNodePtr c, const std::wstring nodeName) { return net.AddNodeToNetAndAttachInputs(New>(net.GetDeviceId(), nodeName), { a, b, c }); } #ifdef COMING_SOON template shared_ptr> ComputationNetworkBuilder::CRF(const ComputationNodePtr label, const ComputationNodePtr postDepScore, const ComputationNodePtr transition_score, const std::wstring nodeName) { return net.AddNodeToNetAndAttachInputs(New>(net.GetDeviceId(), nodeName), { label, postDepScore, transition_score }); } #endif template shared_ptr> ComputationNetworkBuilder::DummyCriterion(const ComputationNodePtr objectives, const ComputationNodePtr derivatives, const ComputationNodePtr prediction, const std::wstring nodeName) { return net.AddNodeToNetAndAttachInputs(New>(net.GetDeviceId(), nodeName), { objectives, derivatives, prediction }); } template shared_ptr> ComputationNetworkBuilder::CrossEntropy(const ComputationNodePtr label, const ComputationNodePtr prediction, const std::wstring nodeName) { return net.AddNodeToNetAndAttachInputs(New>(net.GetDeviceId(), nodeName), { label, prediction }); } template shared_ptr> ComputationNetworkBuilder::MatrixL1Reg(const ComputationNodePtr a, const std::wstring nodeName) { return net.AddNodeToNetAndAttachInputs(New>(net.GetDeviceId(), nodeName), { a }); } template shared_ptr> ComputationNetworkBuilder::MatrixL2Reg(const ComputationNodePtr a, const std::wstring nodeName) { return net.AddNodeToNetAndAttachInputs(New>(net.GetDeviceId(), nodeName), { a }); } template shared_ptr> ComputationNetworkBuilder::Mean(const ComputationNodePtr a, const std::wstring nodeName) { return net.AddNodeToNetAndAttachInputs(New>(net.GetDeviceId(), nodeName), { a }); } template shared_ptr> ComputationNetworkBuilder::Pass(const ComputationNodePtr a, const std::wstring& nodeName) { return net.AddNodeToNetAndAttachInputs(New>(net.GetDeviceId(), nodeName), { a }); } template shared_ptr> ComputationNetworkBuilder::DynamicAxis(const ComputationNodePtr a, const std::wstring& nodeName) { return net.AddNodeToNetAndAttachInputs(New>(net.GetDeviceId(), nodeName), { a }); } template shared_ptr> ComputationNetworkBuilder::InvStdDev(const ComputationNodePtr a, const std::wstring nodeName) { return net.AddNodeToNetAndAttachInputs(New>(net.GetDeviceId(), nodeName), { a }); } template shared_ptr> ComputationNetworkBuilder::Negate(const ComputationNodePtr a, const std::wstring nodeName) { return net.AddNodeToNetAndAttachInputs(New>(net.GetDeviceId(), nodeName), { a }); } template shared_ptr> ComputationNetworkBuilder::RectifiedLinear(const ComputationNodePtr a, const std::wstring nodeName) { return net.AddNodeToNetAndAttachInputs(New>(net.GetDeviceId(), nodeName), { a }); } template shared_ptr> ComputationNetworkBuilder::Sigmoid(const ComputationNodePtr a, const std::wstring nodeName) { return net.AddNodeToNetAndAttachInputs(New>(net.GetDeviceId(), nodeName), { a }); } template shared_ptr> ComputationNetworkBuilder::Tanh(const ComputationNodePtr a, const std::wstring nodeName) { return net.AddNodeToNetAndAttachInputs(New>(net.GetDeviceId(), nodeName), { a }); } template shared_ptr> ComputationNetworkBuilder::Exp(const ComputationNodePtr a, const std::wstring nodeName) { return net.AddNodeToNetAndAttachInputs(New>(net.GetDeviceId(), nodeName), { a }); } template shared_ptr> ComputationNetworkBuilder::Log(const ComputationNodePtr a, const std::wstring nodeName) { return net.AddNodeToNetAndAttachInputs(New>(net.GetDeviceId(), nodeName), { a }); } template shared_ptr> ComputationNetworkBuilder::Cos(const ComputationNodePtr a, const std::wstring nodeName) { return net.AddNodeToNetAndAttachInputs(New>(net.GetDeviceId(), nodeName), { a }); } template shared_ptr> ComputationNetworkBuilder::Sin(const ComputationNodePtr a, const std::wstring nodeName) { return net.AddNodeToNetAndAttachInputs(New>(net.GetDeviceId(), nodeName), { a }); } template shared_ptr> ComputationNetworkBuilder::Abs(const ComputationNodePtr a, const std::wstring nodeName) { return net.AddNodeToNetAndAttachInputs(New>(net.GetDeviceId(), nodeName), { a }); } template shared_ptr> ComputationNetworkBuilder::Floor(const ComputationNodePtr a, const std::wstring nodeName) { return net.AddNodeToNetAndAttachInputs(New>(net.GetDeviceId(), nodeName), { a }); } template shared_ptr> ComputationNetworkBuilder::Hardmax(const ComputationNodePtr a, const std::wstring nodeName) { return net.AddNodeToNetAndAttachInputs(New>(net.GetDeviceId(), nodeName), { a }); } template shared_ptr> ComputationNetworkBuilder::If(const ComputationNodePtr a, const ComputationNodePtr b, const ComputationNodePtr c, const std::wstring nodeName) { return net.AddNodeToNetAndAttachInputs(New>(net.GetDeviceId(), nodeName), { a, b, c }); } template shared_ptr> ComputationNetworkBuilder::Softmax(const ComputationNodePtr a, const std::wstring nodeName) { return net.AddNodeToNetAndAttachInputs(New>(net.GetDeviceId(), nodeName), { a }); } template shared_ptr> ComputationNetworkBuilder::LogSoftmax(const ComputationNodePtr a, const std::wstring nodeName) { return net.AddNodeToNetAndAttachInputs(New>(net.GetDeviceId(), nodeName), { a }); } template shared_ptr> ComputationNetworkBuilder::Reciprocal(const ComputationNodePtr a, const std::wstring nodeName) { return net.AddNodeToNetAndAttachInputs(New>(net.GetDeviceId(), nodeName), { a }); } template shared_ptr> ComputationNetworkBuilder::Sqrt(const ComputationNodePtr a, const std::wstring nodeName) { return net.AddNodeToNetAndAttachInputs(New>(net.GetDeviceId(), nodeName), { a }); } template shared_ptr> ComputationNetworkBuilder::Sum(const ComputationNodePtr a, const std::wstring nodeName) { return net.AddNodeToNetAndAttachInputs(New>(net.GetDeviceId(), nodeName), { a }); } template shared_ptr> ComputationNetworkBuilder::TransposeDimensions(const ComputationNodePtr matrix, int dim1, int dim2, const std::wstring nodeName) { return net.AddNodeToNetAndAttachInputs(New>(net.GetDeviceId(), nodeName, dim1, dim2), { matrix }); } template shared_ptr> ComputationNetworkBuilder::Times(const ComputationNodePtr a, const ComputationNodePtr b, size_t outputRank, const std::wstring nodeName) { return net.AddNodeToNetAndAttachInputs(New>(net.GetDeviceId(), nodeName, outputRank), { a, b }); } template shared_ptr> ComputationNetworkBuilder::TransposeTimes(const ComputationNodePtr a, const ComputationNodePtr b, const std::wstring nodeName) { return net.AddNodeToNetAndAttachInputs(New>(net.GetDeviceId(), nodeName), { a, b }); } template shared_ptr> ComputationNetworkBuilder::QuantizedTimes(const ComputationNodePtr a, const ComputationNodePtr b, size_t bitSmoothingA, size_t bitSmoothingB, size_t outputRank, const std::wstring nodeName) { return net.AddNodeToNetAndAttachInputs(New>(net.GetDeviceId(), nodeName, bitSmoothingA, bitSmoothingB, outputRank), { a, b }); } template shared_ptr> ComputationNetworkBuilder::ElementTimes(const ComputationNodePtr a, const ComputationNodePtr b, const std::wstring nodeName) { return net.AddNodeToNetAndAttachInputs(New>(net.GetDeviceId(), nodeName), { a, b }); } template shared_ptr> ComputationNetworkBuilder::DiagTimes(const ComputationNodePtr a, const ComputationNodePtr b, const std::wstring nodeName) { return net.AddNodeToNetAndAttachInputs(New>(net.GetDeviceId(), nodeName), { a, b }); } template shared_ptr> ComputationNetworkBuilder::CosDistance(const ComputationNodePtr a, const ComputationNodePtr b, const std::wstring nodeName) { return net.AddNodeToNetAndAttachInputs(New>(net.GetDeviceId(), nodeName), { a, b }); } template shared_ptr> ComputationNetworkBuilder::KhatriRaoProduct(const ComputationNodePtr a, const ComputationNodePtr b, const std::wstring nodeName) { return net.AddNodeToNetAndAttachInputs(New>(net.GetDeviceId(), nodeName), { a, b }); } template shared_ptr> ComputationNetworkBuilder::Plus(const ComputationNodePtr a, const ComputationNodePtr b, const std::wstring nodeName) { return net.AddNodeToNetAndAttachInputs(New>(net.GetDeviceId(), nodeName), { a, b }); } template shared_ptr> ComputationNetworkBuilder::LogPlus(const ComputationNodePtr a, const ComputationNodePtr b, const std::wstring nodeName) { return net.AddNodeToNetAndAttachInputs(New>(net.GetDeviceId(), nodeName), { a, b }); } template shared_ptr> ComputationNetworkBuilder::Less(const ComputationNodePtr a, const ComputationNodePtr b, const std::wstring nodeName) { return net.AddNodeToNetAndAttachInputs(New>(net.GetDeviceId(), nodeName), { a, b }); } template shared_ptr> ComputationNetworkBuilder::Equal(const ComputationNodePtr a, const ComputationNodePtr b, const std::wstring nodeName) { return net.AddNodeToNetAndAttachInputs(New>(net.GetDeviceId(), nodeName), { a, b }); } template shared_ptr> ComputationNetworkBuilder::Greater(const ComputationNodePtr a, const ComputationNodePtr b, const std::wstring nodeName) { return net.AddNodeToNetAndAttachInputs(New>(net.GetDeviceId(), nodeName), { a, b }); } template shared_ptr> ComputationNetworkBuilder::GreaterEqual(const ComputationNodePtr a, const ComputationNodePtr b, const std::wstring nodeName) { return net.AddNodeToNetAndAttachInputs(New>(net.GetDeviceId(), nodeName), { a, b }); } template shared_ptr> ComputationNetworkBuilder::NotEqual(const ComputationNodePtr a, const ComputationNodePtr b, const std::wstring nodeName) { return net.AddNodeToNetAndAttachInputs(New>(net.GetDeviceId(), nodeName), { a, b }); } template shared_ptr> ComputationNetworkBuilder::LessEqual(const ComputationNodePtr a, const ComputationNodePtr b, const std::wstring nodeName) { return net.AddNodeToNetAndAttachInputs(New>(net.GetDeviceId(), nodeName), { a, b }); } template shared_ptr> ComputationNetworkBuilder::Minus(const ComputationNodePtr a, const ComputationNodePtr b, const std::wstring nodeName) { return net.AddNodeToNetAndAttachInputs(New>(net.GetDeviceId(), nodeName), { a, b }); } template shared_ptr> ComputationNetworkBuilder::Dropout(const ComputationNodePtr a, const std::wstring nodeName) { return net.AddNodeToNetAndAttachInputs(New>(net.GetDeviceId(), nodeName), { a }); } template shared_ptr> ComputationNetworkBuilder::Reshape(const ComputationNodePtr a, const TensorShape& imageLayout, const std::wstring nodeName) { return net.AddNodeToNetAndAttachInputs(New>(net.GetDeviceId(), nodeName, imageLayout), { a }); } #if 1 template shared_ptr> ComputationNetworkBuilder::LegacyReshape(const ComputationNodePtr a, const size_t numRows, const TensorShape& imageLayout, const std::wstring nodeName) { return net.AddNodeToNetAndAttachInputs(New>(net.GetDeviceId(), nodeName, numRows, imageLayout), { a }); } #endif template shared_ptr> ComputationNetworkBuilder::RowRepeat(const ComputationNodePtr a, const size_t num_repeat, const std::wstring nodeName) { return net.AddNodeToNetAndAttachInputs(New>(net.GetDeviceId(), nodeName, num_repeat), { a }); } template shared_ptr> ComputationNetworkBuilder::Diagonal(const ComputationNodePtr a, const std::wstring nodeName) { return net.AddNodeToNetAndAttachInputs(New>(net.GetDeviceId(), nodeName), { a }); } template shared_ptr> ComputationNetworkBuilder::PastValue(const ComputationNodePtr a, const float initHiddenActivity, const size_t row_size, size_t timeStep, const std::wstring nodeName) { return net.AddNodeToNetAndAttachInputs(New>(net.GetDeviceId(), nodeName, initHiddenActivity, row_size, timeStep), { a }); } template shared_ptr> ComputationNetworkBuilder::FutureValue(const ComputationNodePtr a, const float initHiddenActivity, const size_t row_size, size_t timeStep, const std::wstring nodeName) { return net.AddNodeToNetAndAttachInputs(New>(net.GetDeviceId(), nodeName, initHiddenActivity, row_size, timeStep), { a }); } template shared_ptr> ComputationNetworkBuilder::RowSlice(const ComputationNodePtr a, const size_t start_index, const size_t num_rows, const std::wstring nodeName) { return net.AddNodeToNetAndAttachInputs(New>(net.GetDeviceId(), nodeName, (int)start_index, (int)(start_index + num_rows)), { a }); } template shared_ptr> ComputationNetworkBuilder::RowStack(const std::vector pinputs, const std::wstring nodeName) { vector inputs(pinputs.size()); for (size_t i = 0; i < inputs.size(); i++) inputs[i] = pinputs[i]; // convert to ComputationNodeBasePtr return net.AddNodeToNetAndAttachInputs(New>(net.GetDeviceId(), nodeName), { inputs }); } template shared_ptr> ComputationNetworkBuilder::RandomSample(const ComputationNodePtr a, const std::wstring nodeName) { return net.AddNodeToNetAndAttachInputs(New>(net.GetDeviceId(), nodeName), { a }); } template shared_ptr> ComputationNetworkBuilder::RandomSampleInclusionFrequency(const ComputationNodePtr a, const std::wstring nodeName) { return net.AddNodeToNetAndAttachInputs(New>(net.GetDeviceId(), nodeName), { a }); } #ifdef COMING_SOON template shared_ptr> ComputationNetworkBuilder::GMMLogLikelihood(const ComputationNodePtr unnormedPrior, const ComputationNodePtr mean, const ComputationNodePtr logStddev, const ComputationNodePtr feature, const std::wstring nodeName) { return net.AddNodeToNetAndAttachInputs(New>(net.GetDeviceId(), nodeName), { unnormedPrior, mean, logStddev, feature }); } #endif template shared_ptr> ComputationNetworkBuilder::LookupTable(const ComputationNodePtr dictionary, const ComputationNodePtr input, const std::wstring nodeName) { return net.AddNodeToNetAndAttachInputs(New>(net.GetDeviceId(), nodeName), { dictionary, input }); } template shared_ptr> ComputationNetworkBuilder::BatchNormalization(const ComputationNodePtr input, const ComputationNodePtr scale, const ComputationNodePtr bias, const ComputationNodePtr runMean, const ComputationNodePtr runVariance, bool spatial, double normalizationTimeConstant, double blendTimeConstant, double epsilon, bool useCntkEngine, ImageLayoutKind imageLayoutKind, const std::wstring nodeName) { return net.AddNodeToNetAndAttachInputs(New>(net.GetDeviceId(), nodeName, spatial, normalizationTimeConstant, blendTimeConstant, epsilon, useCntkEngine, imageLayoutKind), { input, scale, bias, runMean, runVariance }); } template class ComputationNetworkBuilder; template class ComputationNetworkBuilder; }}}