void PackedValue::Unpack() const { if (m_packedDataLayout && (m_packedDataLayout->GetNumTimeSteps() != 1) && (m_packedDataLayout->GetNumSequences() != 1) && Internal::IsAutomaticUnpackingOfPackedValuesDisabled()) LogicError("PackedValue::Unpack: Automatic unpacking of PackedValue objects is disabled"); if (m_isPacked) { ValuePtr valueObject; auto dataType = m_packedData->GetDataType(); switch (dataType) { case DataType::Float: valueObject = CompositeFunction::GetValueObjectFromCNTKImplMatrixAndMBLayout(m_sampleShape, *(m_packedData->GetMatrix<float>()), m_packedDataLayout, m_isReadOnly); break; case DataType::Double: valueObject = CompositeFunction::GetValueObjectFromCNTKImplMatrixAndMBLayout(m_sampleShape, *(m_packedData->GetMatrix<double>()), m_packedDataLayout, m_isReadOnly); break; default: LogicError("Unsupported DataType %s", DataTypeName(dataType)); } m_data = valueObject->Data(); m_mask = valueObject->Mask(); m_packedData = nullptr; m_packedDataLayout = nullptr; m_isPacked = false; if (m_unpackedShape != m_data->Shape()) LogicError("The computed unpacked shape of the PackedValue object does not match the actual Data NDArrayView's shape after unpacking"); } }
void TestReduceSum(size_t sampleRank, const DeviceDescriptor& device) { size_t numSequences = 7; size_t maxAllowedSequenceLength = 11; size_t maxDimSize = 23; NDShape inputShape(sampleRank); for (size_t i = 0; i < sampleRank; ++i) inputShape[i] = (rand() % maxDimSize) + 1; auto sequenceLengths = GenerateSequenceLengths(numSequences, maxAllowedSequenceLength); auto sequences = GenerateSequences<float>(sequenceLengths, inputShape); ValuePtr sequencesValue = Value::Create(inputShape, sequences, device, true); // Test ReduceSum along a static axis { auto testReduceSum = [&sequences, &sequenceLengths, inputShape, sequencesValue, device, sampleRank](int reductionAxis, bool useNegativeAxisIndex) { size_t maxActualSequenceLength = sequencesValue->Shape()[inputShape.Rank()]; size_t numSequences = sequencesValue->Shape()[inputShape.Rank() + 1]; auto inputVar = InputVariable(inputShape, DataType::Float, L"input"); FunctionPtr reduceSumFunc; bool reduceAll = (reductionAxis < 0); if (reduceAll) reduceSumFunc = ReduceSum(inputVar); else reduceSumFunc = ReduceSum(inputVar, Axis(useNegativeAxisIndex ? (reductionAxis - (int)sampleRank) : reductionAxis)); NDShape outputShape = reduceSumFunc->Output().Shape(); NDShape outputDataShape = outputShape; if (!reduceAll) outputDataShape = outputDataShape.AppendShape({ maxActualSequenceLength, numSequences }); std::vector<float> outputData(outputDataShape.TotalSize()); ValuePtr outputValue = MakeSharedObject<Value>(MakeSharedObject<NDArrayView>(outputDataShape, outputData, false), reduceAll ? nullptr : sequencesValue->Mask()->DeepClone()); std::unordered_map<Variable, ValuePtr> outputs = { { reduceSumFunc->Output(), outputValue } }; reduceSumFunc->Forward({ { inputVar, sequencesValue } }, outputs, device); std::vector<size_t> inputShapeStrides = GetStrides(inputShape); std::vector<size_t> outputShapeStrides = GetStrides(outputShape); std::vector<float> expectedPerFrameTotals(outputShape.TotalSize() * maxActualSequenceLength * numSequences, 0.0f); float expectedTotal = 0.0f; for (size_t i = 0; i < numSequences; ++i) { size_t currentSequenceLength = sequenceLengths[i]; for (size_t j = 0; j < currentSequenceLength; ++j) { for (size_t k = 0; k < inputShape.TotalSize(); ++k) { auto inputIdx = UnflattenedShape(k, inputShapeStrides); auto outputIdx = inputIdx; if (!reduceAll) outputIdx[reductionAxis] = 0; else outputIdx = {}; auto flatOutputIdx = FlattenedIndex(outputIdx, outputShapeStrides); float value = sequences[i][(j * inputShape.TotalSize()) + k]; expectedPerFrameTotals[(((i * maxActualSequenceLength) + j) * outputShape.TotalSize()) + flatOutputIdx] += value; expectedTotal += value; } } } if (reduceAll) FloatingPointVectorCompare(outputData, std::vector<float>({ expectedTotal }), "testReduceSum: Forward prop results do not match expected results"); else FloatingPointVectorCompare(outputData, expectedPerFrameTotals, "testReduceSum: Forward prop results do not match expected results"); }; // Reduce over all axes testReduceSum(-1, false); int reductionAxis = 0; testReduceSum(reductionAxis, true); if (reductionAxis < (inputShape.Rank() - 1)) reductionAxis++; testReduceSum(reductionAxis, false); if (reductionAxis < (inputShape.Rank() - 1)) reductionAxis++; testReduceSum(reductionAxis, true); } // Test ReduceSum along a dynamic axis { auto testReduceSum = [&sequences, &sequenceLengths, inputShape, sequencesValue, device](const Axis& axis) { if (!axis.IsDynamicAxis()) RuntimeError("Called the dynamic axis ReduceSum test with a static axis"); size_t maxActualSequenceLength = sequencesValue->Shape()[inputShape.Rank()]; size_t numSequences = sequencesValue->Shape()[inputShape.Rank() + 1]; auto inputVar = InputVariable({ inputShape }, DataType::Float, L"input"); FunctionPtr reduceSumFunc = ReduceSum(inputVar, axis); NDShape maskShape = { ((axis == Axis::DefaultBatchAxis()) ? maxActualSequenceLength : 1), ((axis == Axis::DefaultBatchAxis()) ? 1 : numSequences) }; NDShape outputShape = reduceSumFunc->Output().Shape(); auto outputDataShape = outputShape.AppendShape(maskShape); std::vector<float> outputData(outputDataShape.TotalSize()); auto maskPtr = MakeSharedObject<NDMask>(maskShape, device); ValuePtr outputValue = MakeSharedObject<Value>(MakeSharedObject<NDArrayView>(outputDataShape, outputData, false), maskPtr); std::unordered_map<Variable, ValuePtr> outputs = { { reduceSumFunc->Output(), outputValue } }; reduceSumFunc->Forward({ { inputVar, sequencesValue } }, outputs, device); std::vector<float> expectedTotals(outputDataShape.TotalSize(), 0.0f); for (size_t i = 0; i < numSequences; ++i) { size_t currentSequenceLength = sequenceLengths[i]; for (size_t j = 0; j < currentSequenceLength; ++j) { for (size_t k = 0; k < inputShape.TotalSize(); ++k) { float value = sequences[i][(j * inputShape.TotalSize()) + k]; if (axis == Axis::DefaultBatchAxis()) expectedTotals[(j * inputShape.TotalSize()) + k] += value; else expectedTotals[(i * inputShape.TotalSize()) + k] += value; } } } FloatingPointVectorCompare(outputData, expectedTotals, "testReduceSum: Forward prop results do not match expected results"); }; testReduceSum(Axis::DefaultDynamicAxis()); } }
void TestSlice(size_t sampleRank, const DeviceDescriptor& device) { size_t numSequences = 7; size_t maxAllowedSequenceLength = 11; size_t maxDimSize = 23; size_t minDimSize = 5; NDShape inputShape(sampleRank); for (size_t i = 0; i < sampleRank; ++i) inputShape[i] = (rand() % maxDimSize) + minDimSize; auto sequenceLengths = GenerateSequenceLengths(numSequences, maxAllowedSequenceLength); auto sequences = GenerateSequences<float>(sequenceLengths, inputShape); ValuePtr sequencesValue = Value::Create(inputShape, sequences, device, true); // Test slice along a static axis { auto testStaticAxisSlice = [&sequences, &sequenceLengths, inputShape, sequencesValue, device, sampleRank](int sliceAxis, int beginOffset, int endOffset, bool useNegativeAxisIndex) { size_t maxActualSequenceLength = sequencesValue->Shape()[inputShape.Rank()]; size_t numSequences = sequencesValue->Shape()[inputShape.Rank() + 1]; auto inputVar = InputVariable(inputShape, DataType::Float, L"input"); auto sliceFunc = Slice(inputVar, Axis(useNegativeAxisIndex ? (sliceAxis - (int)sampleRank) : sliceAxis), beginOffset, endOffset); NDShape outputShape = sliceFunc->Output().Shape(); auto outputDataShape = outputShape.AppendShape({ maxActualSequenceLength, numSequences }); std::vector<float> outputData(outputDataShape.TotalSize()); ValuePtr outputValue = MakeSharedObject<Value>(MakeSharedObject<NDArrayView>(outputDataShape, outputData, false), sequencesValue->Mask()->DeepClone()); std::unordered_map<Variable, ValuePtr> outputs = { { sliceFunc->Output(), outputValue } }; sliceFunc->Forward({ { inputVar, sequencesValue } }, outputs, device); std::vector<size_t> inputShapeStrides = GetStrides(inputShape); std::vector<size_t> outputShapeStrides = GetStrides(outputShape); size_t sliceStartOffset = (beginOffset >= 0) ? beginOffset : (inputShape[sliceAxis] + beginOffset); std::vector<float> expectedOutputValues(outputShape.TotalSize() * maxActualSequenceLength * numSequences); for (size_t i = 0; i < numSequences; ++i) { size_t currentSequenceLength = sequenceLengths[i]; for (size_t j = 0; j < currentSequenceLength; ++j) { for (size_t k = 0; k < outputShape.TotalSize(); ++k) { auto outputIdx = UnflattenedShape(k, outputShapeStrides); auto inputIdx = outputIdx; inputIdx[sliceAxis] += sliceStartOffset; auto flatInputIdx = FlattenedIndex(inputIdx, inputShapeStrides); expectedOutputValues[(((i * maxActualSequenceLength) + j) * outputShape.TotalSize()) + k] = sequences[i][(j * inputShape.TotalSize()) + flatInputIdx]; } } } FloatingPointVectorCompare(outputData, expectedOutputValues, "testStaticAxisSlice: Forward prop results do not match expected results"); }; int sliceAxis = 0; testStaticAxisSlice(sliceAxis, 3, 5, true); if (sliceAxis < (inputShape.Rank() - 1)) sliceAxis++; testStaticAxisSlice(sliceAxis, -1, 0, false); if (sliceAxis < (inputShape.Rank() - 1)) sliceAxis++; testStaticAxisSlice(sliceAxis, -3, -1, true); } // Test slice along a dynamic axis { auto testDynamicAxisSlice = [&sequences, &sequenceLengths, inputShape, sequencesValue, device](const Axis& axis, int beginOffset, int endOffset) { if (!axis.IsDynamicAxis()) RuntimeError("Called the dynamic axis slice test with a static axis"); size_t maxActualSequenceLength = sequencesValue->Shape()[inputShape.Rank()]; size_t numSequences = sequencesValue->Shape()[inputShape.Rank() + 1]; int endAndBeginOffsetDiff = endOffset - beginOffset; size_t maxSliceLength = (endAndBeginOffsetDiff > 0) ? endAndBeginOffsetDiff : maxActualSequenceLength + endAndBeginOffsetDiff; auto inputVar = InputVariable(inputShape, DataType::Float, L"input"); auto sliceFunc = Slice(inputVar, axis, beginOffset, endOffset); sliceFunc = sliceFunc + sliceFunc; size_t outputSequenceAxisLength = (axis == Axis::DefaultDynamicAxis()) ? maxSliceLength : maxActualSequenceLength; size_t outputBatchAxisLength = (axis == Axis::DefaultBatchAxis()) ? maxSliceLength : numSequences; NDShape outputShape = sliceFunc->Output().Shape().AppendShape({ outputSequenceAxisLength, outputBatchAxisLength }); std::vector<float> outputData(outputShape.TotalSize(), 0); NDMaskPtr mask; if (endAndBeginOffsetDiff < 0) { ValuePtr outputValue = MakeSharedObject<Value>(MakeSharedObject<NDArrayView>(outputShape, outputData, false)); mask = MakeSharedObject<NDMask>(std::initializer_list<size_t>({ outputSequenceAxisLength, outputBatchAxisLength }), device); } ValuePtr outputValue = MakeSharedObject<Value>(MakeSharedObject<NDArrayView>(outputShape, outputData, false), mask); std::unordered_map<Variable, ValuePtr> outputs = { { sliceFunc->Output(), outputValue } }; sliceFunc->Forward({ { inputVar, sequencesValue } }, outputs, device); size_t startSequenceIdx = (axis == Axis::DefaultBatchAxis()) ? ((beginOffset >= 0) ? beginOffset : (numSequences + beginOffset)) : 0; size_t endSequenceIdx = (axis == Axis::DefaultBatchAxis()) ? ((endOffset > 0) ? endOffset : (numSequences + endOffset)) : numSequences; std::vector<float> expectedOutputValues(inputShape.TotalSize() * outputSequenceAxisLength * outputBatchAxisLength); for (size_t i = startSequenceIdx; i < endSequenceIdx; ++i) { size_t currentSequenceLength = sequenceLengths[i]; size_t startFrameIdx = (axis == Axis::DefaultDynamicAxis()) ? ((beginOffset >= 0) ? beginOffset : (currentSequenceLength + beginOffset)) : 0; size_t endFrameIdx = (axis == Axis::DefaultDynamicAxis()) ? ((endOffset > 0) ? endOffset : (currentSequenceLength + endOffset)) : currentSequenceLength; size_t j = startFrameIdx; for (; j < endFrameIdx; ++j) { for (size_t k = 0; k < inputShape.TotalSize(); ++k) expectedOutputValues[((((i - startSequenceIdx) * outputSequenceAxisLength) + (j - startFrameIdx)) * inputShape.TotalSize()) + k] = 2 * sequences[i][(j * inputShape.TotalSize()) + k]; } // Zero out the invalid portions of the actual output for (; j < (outputSequenceAxisLength + startFrameIdx); ++j) for (size_t k = 0; k < inputShape.TotalSize(); ++k) outputData[((((i - startSequenceIdx) * outputSequenceAxisLength) + (j - startFrameIdx)) * inputShape.TotalSize()) + k] = 0; } FloatingPointVectorCompare(outputData, expectedOutputValues, "testDynamicAxisSlice: Forward prop results do not match expected results"); }; testDynamicAxisSlice(Axis::DefaultDynamicAxis(), 0, 1); testDynamicAxisSlice(Axis::DefaultDynamicAxis(), 0, 2); testDynamicAxisSlice(Axis::DefaultDynamicAxis(), -1, 0); testDynamicAxisSlice(Axis::DefaultDynamicAxis(), -2, 0); testDynamicAxisSlice(Axis::DefaultDynamicAxis(), 0, -1); testDynamicAxisSlice(Axis::DefaultDynamicAxis(), 1, 0); } }