// // Copyright (c) Microsoft. All rights reserved. // Licensed under the MIT license. See LICENSE.md file in the project root for full license information. // #include "LinearAlgebraNodes.h" using namespace Microsoft::MSR::CNTK; // ----------------------------------------------------------------------- // EpochAccumulatorNode calculates mean values of all samples used in forward pass. // ----------------------------------------------------------------------- // TODO: can this be static? template void Microsoft::MSR::CNTK::UpdateRunningAverage(ComputationNode& newInput, TensorView& runningAverage, size_t& runningCount) { FrameRange fr(newInput.GetMBLayout()); // Set gaps to zero, since we are reducing in time. newInput.MaskMissingValueColumnsToZero(fr); size_t newSamplesCount = newInput.GetMBLayout()->GetActualNumSamples(); size_t totalSamplesCount = runningCount + newSamplesCount; if (totalSamplesCount == 0) totalSamplesCount = 1; ElemType alpha = 1.0f / totalSamplesCount; ElemType beta = (ElemType) runningCount / totalSamplesCount; size_t rank = runningAverage.GetShape().GetRank(); auto input = newInput.ValueTensorFor(rank, fr); // runningMean = beta * accumulator + alpha * input. runningAverage.DoCopyOf(beta, input, alpha); runningCount += newSamplesCount; } template void Microsoft::MSR::CNTK::UpdateRunningAverage(ComputationNode& newInput, TensorView& runningAverage, size_t& runningCount); template void Microsoft::MSR::CNTK::UpdateRunningAverage(ComputationNode& newInput, TensorView& runningAverage, size_t& runningCount); template EpochAccumulatorNode::EpochAccumulatorNode(DEVICEID_TYPE deviceId, const wstring& name) : Base(deviceId, name), m_numSamples(0) { m_accumulator = make_shared>(deviceId); } template EpochAccumulatorNode::EpochAccumulatorNode(const Microsoft::MSR::ScriptableObjects::IConfigRecordPtr configp) : EpochAccumulatorNode(configp->Get(L"deviceId"), L"") { AttachInputsFromConfig(configp, this->GetExpectedNumInputs()); } template void EpochAccumulatorNode::BackpropToNonLooping(size_t /*inputIndex*/) { LogicError("%ls operation is used for forward only.", OperationName().c_str()); } template void EpochAccumulatorNode::OnEpochStart() { Reset(); } template void EpochAccumulatorNode::ForwardPropNonLooping() { TensorView accumulator = EnsureAccumlator(); UpdateRunningAverage(InputRef(0), accumulator, m_numSamples); CopyAccumulatorToValue(); } // Copies internal accumulator to the output. template void EpochAccumulatorNode::CopyAccumulatorToValue() { // Value gets resized in UpdateFunctionValuesSize that is called in BeforeForwardProp. Resize fills matrix with NaN // values, so m_value matrix cannot be used as persistent storage between ForwardProp calls. Value().SetValue(*m_accumulator); } template void EpochAccumulatorNode::CopyTo(ComputationNodeBasePtr nodeP, const std::wstring& newName, const CopyNodeFlags flags) const { Base::CopyTo(nodeP, newName, flags); if (flags & CopyNodeFlags::copyNodeValue) { auto node = nodeP->As>(); node->m_numSamples = m_numSamples; node->m_accumulator->SetValue(*m_accumulator); } } template void EpochAccumulatorNode::Validate(bool isFinalValidationPass) { Base::Validate(isFinalValidationPass); SetDims(Input(0)->GetSampleLayout(), HasMBLayout()); } template TensorView EpochAccumulatorNode::EnsureAccumlator() { if (m_accumulator->HasNoElements()) { // Accumulator has not been resized yet, allocate with necessary size. const size_t sampleSize = GetSampleLayout().GetNumElements(); m_accumulator->Resize(sampleSize, 1); Reset(); } size_t rank = DetermineElementwiseTensorRank(); return DataTensorFor(m_accumulator, rank, FrameRange()); } template void EpochAccumulatorNode::Reset() { m_accumulator->SetValue(0); m_numSamples = 0; } template class EpochAccumulatorNode; template class EpochAccumulatorNode;