Skip to content

Commit

Permalink
implemented GatherPackedNode and ScatterPackedNode, so far CPU only
Browse files Browse the repository at this point in the history
  • Loading branch information
frankseide committed Mar 13, 2016
1 parent a15d8d8 commit 2575da3
Show file tree
Hide file tree
Showing 10 changed files with 452 additions and 138 deletions.
25 changes: 17 additions & 8 deletions Source/CNTK/BrainScript/CNTKCoreLib/CNTK.core.bs
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ AveragePooling(input, windowWidth, windowHeight, horizontalSubsample, verticalSu
ColumnwiseCrossProduct = KhatriRaoProduct // deprecated
ClassificationError = ErrorPrediction
Delay = PastValue
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*/ ]
ClassBasedCrossEntropyWithSoftmax(labelClassDescriptorVectorSequence, mainInputInfo, mainWeight, classLogProbsBeforeSoftmax, tag='') = new ComputationNode [ operation = 'ClassBasedCrossEntropyWithSoftmax' ; inputs = (labelClassDescriptorVectorSequence : mainInputInfo : mainWeight : classLogProbsBeforeSoftmax) /*plus the function args*/ ]
ColumnElementTimes(aVectorSequence, anotherVectorSequence, tag='') = new ComputationNode [ operation = 'ColumnElementTimes' ; inputs = (aVectorSequence : anotherVectorSequence) /*plus the function args*/ ]
Expand All @@ -76,6 +77,7 @@ Dropout(activationVectorSequence, tag='') = new ComputationNode [ operation = 'D
ElementTimes(aMatrix, anotherMatrix, tag='') = new ComputationNode [ operation = 'ElementTimes' ; inputs = (aMatrix : anotherMatrix) /*plus the function args*/ ]
ErrorPrediction(labelVectorSequence, outVectorSequence, tag='') = new ComputationNode [ operation = 'ErrorPrediction' ; inputs = (labelVectorSequence : outVectorSequence) /*plus the function args*/ ]
Exp(x, tag='') = new ComputationNode [ operation = 'Exp' ; inputs = x /*plus the function args*/ ]
GatherPacked(indexSequence, sourceData, tag='') = new ComputationNode [ operation = 'GatherPacked' ; inputs = (indexSequence : sourceData) /*plus the function args*/ ]
GMMLogLikelihood(unnormalizedPriorVector, meansAsRows, logStdDevAsRows, dataVectorSequence, tag='') = new ComputationNode [ operation = 'GMMLogLikelihood' ; inputs = (unnormalizedPriorVector : meansAsRows : logStdDevAsRows : dataVectorSequence) /*plus the function args*/ ]
InvStdDev(dataVectorSequence, tag='') = new ComputationNode [ operation = 'InvStdDev' ; inputs = dataVectorSequence /*plus the function args*/ ]
KhatriRaoProduct(leftMatrix, rightMatrix, tag='') = new ComputationNode [ operation = 'KhatriRaoProduct' ; inputs = (leftMatrix : rightMatrix) /*plus the function args*/ ]
Expand All @@ -93,6 +95,7 @@ Plus(leftMatrix, rightMatrix, tag='') = new ComputationNode [ operation = 'Plus'
RectifiedLinear(z, tag='') = new ComputationNode [ operation = 'RectifiedLinear' ; inputs = z /*plus the function args*/ ]
Scale(scalarScalingFactor, matrix, tag='') = new ComputationNode [ operation = 'Scale' ; inputs = (scalarScalingFactor : matrix) /*plus the function args*/ ]
// TODO: Scale = ElementTimes
ScatterPacked(cond, indexSequence, sourceData, tag='') = new ComputationNode [ operation = 'ScatterPacked' ; inputs = (cond : indexSequence : sourceData) /*plus the function args*/ ]
Sigmoid(z, tag='') = new ComputationNode [ operation = 'Sigmoid' ; inputs = z /*plus the function args*/ ]
Softmax(z, tag='') = new ComputationNode [ operation = 'Softmax' ; inputs = z /*plus the function args*/ ]
Hardmax(z, tag='') = new ComputationNode [ operation = 'Hardmax' ; inputs = z /*plus the function args*/ ]
Expand Down Expand Up @@ -185,19 +188,21 @@ Sequences = [
Map (lambda, x) = lambda (x) // that one's easy
# Reverse (x) is a C++ node currently called TimeReverse

Filter (pred, x) = x // TODO: Implement this as a C++ node.
# Gather and Scatter
# We go through 3 nodes each to take advantage of x
Gather (cond, x) = GatherPacked ( PackedIndex (x, Where (cond)), x)
Scatter (cond, y) = ScatterPacked (cond, PackedIndex (y, Where (cond)), y)

# sequence-altering LINQ-like operators
# These generate new data packing (MBLayouts)

# TakeWhile and DropWhile
TakeWhile (predicate, x) = Filter ( _WhilePredicate (PastValue, predicate), x)
DropWhile (predicate, x) = Filter (!_WhilePredicate (PastValue, predicate), x)
# Skip, SkipWhile--same?
SkipWhile (predicate, x) = Filter (!_WhilePredicate (PastValue, predicate), x)
_WhilePredicate (DelayFn, predicate, input) =
[
whilePredicate = Boolean.And (DelayFn (whilePredicate, defaultHiddenActivation=Boolean.True), predicate)
].whilePredicate
whilePredicateRec = Boolean.And (DelayFn (whilePredicateRec, defaultHiddenActivation=Boolean.True), predicate)
].whilePredicateRec
# TODO: do we need operations from the back?

# First and Take
Expand All @@ -206,14 +211,18 @@ Sequences = [
Take (N, x) = _Take (PastValue, N, x)
_Take (DelayFn, N, x) = [
selected = Loop._IsWithin (DelayFn, N, x)
out = Filter (selected, x)
out = Gather (selected, x)
].out
Skip (N, x) = _Skip (PastValue, N, x)
_Skip (DelayFn, N, x) = [ // TODO: merge with _Take
selected = Loop._IsWithin (DelayFn, N, x)
out = Filter (!selected, x)
out = Gather (!selected, x)
].out
ElementAt (n, x) = First (Skip (n, x)) // not efficient, as it filters twice. Better AND the predicates. TODO: what if n is out of range? ElementAtOrDefault
ElementAt (n, x) = [ // not efficient, as it filters twice. Better AND the predicates. TODO: what if n is out of range? ElementAtOrDefault
startMask = Skip (n, x) // ...000111...
mask = startMask - PastValue (0, startMask) // ...000100...
out = Gather (mask, x)
]
Single (predicate, x) = x

#FirstOrDefault (x) = ? // can empty sequences exist or even be represented by CNTK?
Expand Down
61 changes: 58 additions & 3 deletions Source/Common/Include/Sequences.h
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,60 @@ struct MBLayout
m_writable = true;
}

// packing algorithm
// - width: maximum width of structure; set to maximum over sequence lengths
// - inputSequences: vector of input SequenceInfo records (only seqId and GetNumTimeSteps() are used)
// - [out] *pMBLayout: MBLayout that describes the created packed sequence set
// - placement, rowAllocations: temp buffers (passed in to be able to optimize memory allocations)
template<typename SequenceInfoVector>
void InitAsPackedSequences(const SequenceInfoVector& inputSequences,
/*temp buffer*/std::vector<std::pair<size_t, size_t>>& placement,
/*temp buffer*/std::vector<size_t> rowAllocations)
{
placement.resize(inputSequences.size()); // [sequence index] result goes here (entries are invalid for gaps)
// determine width of MBLayout
size_t width = 0;
for (size_t i = 0; i < inputSequences.size(); i++)
if (inputSequences[i].seqId == GAP_SEQUENCE_ID)
continue;
else if (width < inputSequences[i].GetNumTimeSteps())
width = inputSequences[i].GetNumTimeSteps();
// allocate
rowAllocations.clear(); // [row] we build rows one by one
for (size_t i = 0; i < inputSequences.size(); i++)
{
if (inputSequences[i].seqId == GAP_SEQUENCE_ID)
continue;
let len = inputSequences[i].GetNumTimeSteps();
// first see if we find a row that has enough space
// TODO: Should we use a proper priority_queue?
size_t s;
for (s = 0; s < rowAllocations.size(); s++)
if (rowAllocations[s] + len <= width)
break; // yep, it fits
// we did not find a s that fit then create a new one
if (s == rowAllocations.size())
rowAllocations.push_back(0);
// sequence goes to (s, rowAllocations[s])
placement[i] = make_pair(s, rowAllocations[s]);
// and allocate it
rowAllocations[s] += len;
}
// create MBLayout
Init(rowAllocations.size(), width);
for (size_t i = 0; i < inputSequences.size(); i++)
{
if (inputSequences[i].seqId == GAP_SEQUENCE_ID)
continue;
size_t s, tBegin; tie
(s, tBegin) = placement[i];
AddSequence(inputSequences[i].seqId, s, (ptrdiff_t)tBegin, tBegin + inputSequences[i].GetNumTimeSteps());
}
// need to fill the gaps as well
for (size_t s = 0; s < rowAllocations.size(); s++)
AddGap(s, (size_t)rowAllocations[s], width);
}

// -------------------------------------------------------------------
// accessors
// -------------------------------------------------------------------
Expand Down Expand Up @@ -1003,7 +1057,7 @@ static inline std::pair<DimensionVector, DimensionVector> TensorSliceWithMBLayou
// 'Reduce' style operations--the criterion nodes and gradient computation--call this.
// Warning: The layout used here must match the matrix. E.g. don't pass a child's matrix from a criterion node (use Input(x)->MaskMissing{Values,Gradient}ColumnsToZero() instead.
template <class ElemType>
static inline void MaskMissingColumnsTo(Matrix<ElemType> &matrixToMask, const MBLayoutPtr &pMBLayout, const FrameRange &fr, ElemType val)
static inline void MaskMissingColumnsTo(Matrix<ElemType>& matrixToMask, const MBLayoutPtr& pMBLayout, const FrameRange& fr, ElemType val)
{
if (pMBLayout && pMBLayout->HasGaps(fr))
{
Expand All @@ -1013,11 +1067,12 @@ static inline void MaskMissingColumnsTo(Matrix<ElemType> &matrixToMask, const MB
auto matrixSliceToMask = DataWithMBLayoutFor(matrixToMask, fr, pMBLayout);
TensorView<ElemType>(matrixSliceToMask).DoMaskNegativeOf(0, TensorView<ElemType>(matrixSliceToMask), TensorView<ElemType>(maskSlice), 1); val;
#else
const auto &maskMatrix = pMBLayout->GetColumnsValidityMask(matrixToMask.GetDeviceId());
const auto& maskMatrix = pMBLayout->GetColumnsValidityMask(matrixToMask.GetDeviceId());
auto maskSlice = DataWithMBLayoutFor(maskMatrix, fr, pMBLayout);
auto matrixSliceToMask = DataWithMBLayoutFor(matrixToMask, fr, pMBLayout);
matrixSliceToMask.MaskColumnsValue(maskSlice, val);
#endif
}
}
} } }

}}}
2 changes: 2 additions & 0 deletions Source/ComputationNetworkLib/ComputationNetworkBuilder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ static shared_ptr<ComputationNode<ElemType>> CreateStandardNode(const std::wstri
else if (nodeType == OperationNameOf(ErrorPredictionNode)) return New<ErrorPredictionNode<ElemType>>(forward<_Types>(_Args)...);
else if (nodeType == OperationNameOf(ExpNode)) return New<ExpNode<ElemType>>(forward<_Types>(_Args)...);
else if (nodeType == OperationNameOf(FutureValueNode)) return New<FutureValueNode<ElemType>>(forward<_Types>(_Args)...);
else if (nodeType == OperationNameOf(GatherPackedNode)) return New<GatherPackedNode<ElemType>>(forward<_Types>(_Args)...);
#ifdef COMING_SOON
else if (nodeType == OperationNameOf(GMMLogLikelihoodNode)) return New<GMMLogLikelihoodNode<ElemType>>(forward<_Types>(_Args)...);
#endif
Expand Down Expand Up @@ -79,6 +80,7 @@ static shared_ptr<ComputationNode<ElemType>> CreateStandardNode(const std::wstri
else if (nodeType == OperationNameOf(RowRepeatNode)) return New<RowRepeatNode<ElemType>>(forward<_Types>(_Args)...);
else if (nodeType == OperationNameOf(RowSliceNode)) return New<RowSliceNode<ElemType>>(forward<_Types>(_Args)...);
else if (nodeType == OperationNameOf(RowStackNode)) return New<RowStackNode<ElemType>>(forward<_Types>(_Args)...);
else if (nodeType == OperationNameOf(ScatterPackedNode)) return New<ScatterPackedNode<ElemType>>(forward<_Types>(_Args)...);
else if (nodeType == OperationNameOf(SequenceWithSoftmaxNode)) return New<SequenceWithSoftmaxNode<ElemType>>(forward<_Types>(_Args)...);
#ifdef COMING_SOON
else if (nodeType == OperationNameOf(SequenceDecoderNode)) return New<SequenceDecoderNode<ElemType>>(forward<_Types>(_Args)...);
Expand Down
6 changes: 6 additions & 0 deletions Source/ComputationNetworkLib/ComputationNode.h
Original file line number Diff line number Diff line change
Expand Up @@ -1057,6 +1057,12 @@ class ComputationNode : public ComputationNodeBase // abstract class that cannot
MaskMissingColumnsToZero(*m_gradient, m_pMBLayout, fr);
}

// for index vectors: Invalid entries must be set to -1.
void MaskMissingValueColumnsTo(const FrameRange& fr, ElemType val)
{
MaskMissingColumnsTo(*m_value, m_pMBLayout, fr, val);
}

// for debugging, set the gaps to NaN instead (to track whether it bubbles up somewhere)
void InvalidateMissingValueColumns(const FrameRange& fr) override final
{
Expand Down
Loading

0 comments on commit 2575da3

Please sign in to comment.