Integrate vadimma/CTC into master
This commit is contained in:
Коммит
3063238829
1
Makefile
1
Makefile
|
@ -1132,6 +1132,7 @@ UNITTEST_NETWORK_SRC = \
|
||||||
$(SOURCEDIR)/../Tests/UnitTests/NetworkTests/OperatorEvaluation.cpp \
|
$(SOURCEDIR)/../Tests/UnitTests/NetworkTests/OperatorEvaluation.cpp \
|
||||||
$(SOURCEDIR)/../Tests/UnitTests/NetworkTests/stdafx.cpp \
|
$(SOURCEDIR)/../Tests/UnitTests/NetworkTests/stdafx.cpp \
|
||||||
$(SOURCEDIR)/../Tests/UnitTests/NetworkTests/TestHelpers.cpp \
|
$(SOURCEDIR)/../Tests/UnitTests/NetworkTests/TestHelpers.cpp \
|
||||||
|
$(SOURCEDIR)/../Tests/UnitTests/NetworkTests/EditDistanceTests.cpp \
|
||||||
$(SOURCEDIR)/CNTK/ModelEditLanguage.cpp \
|
$(SOURCEDIR)/CNTK/ModelEditLanguage.cpp \
|
||||||
$(SOURCEDIR)/ActionsLib/TrainActions.cpp \
|
$(SOURCEDIR)/ActionsLib/TrainActions.cpp \
|
||||||
$(SOURCEDIR)/ActionsLib/EvalActions.cpp \
|
$(SOURCEDIR)/ActionsLib/EvalActions.cpp \
|
||||||
|
|
|
@ -160,6 +160,7 @@ bool CheckFunction(std::string& p_nodeType, bool* allowUndeterminedVariable)
|
||||||
#endif
|
#endif
|
||||||
else if (EqualInsensitive(nodeType, OperationNameOf(ClassBasedCrossEntropyWithSoftmaxNode), L"CBCEWithSM")) ret = true;
|
else if (EqualInsensitive(nodeType, OperationNameOf(ClassBasedCrossEntropyWithSoftmaxNode), L"CBCEWithSM")) ret = true;
|
||||||
else if (EqualInsensitive(nodeType, OperationNameOf(ClassificationErrorNode), L"ErrorPrediction")) ret = true;
|
else if (EqualInsensitive(nodeType, OperationNameOf(ClassificationErrorNode), L"ErrorPrediction")) ret = true;
|
||||||
|
else if (EqualInsensitive(nodeType, OperationNameOf(EditDistanceErrorNode))) ret = true;
|
||||||
else if (EqualInsensitive(nodeType, OperationNameOf(EqualNode))) ret = true;
|
else if (EqualInsensitive(nodeType, OperationNameOf(EqualNode))) ret = true;
|
||||||
else if (EqualInsensitive(nodeType, OperationNameOf(GreaterEqualNode))) ret = true;
|
else if (EqualInsensitive(nodeType, OperationNameOf(GreaterEqualNode))) ret = true;
|
||||||
else if (EqualInsensitive(nodeType, OperationNameOf(GreaterNode))) ret = true;
|
else if (EqualInsensitive(nodeType, OperationNameOf(GreaterNode))) ret = true;
|
||||||
|
|
|
@ -294,6 +294,13 @@ public:
|
||||||
return GetNumTimeSteps() * GetNumParallelSequences();
|
return GetNumTimeSteps() * GetNumParallelSequences();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Get the number of frames of the input sequence that belong to the MB, i.e. disregarding sequence elements that are outside of the MB boundaries
|
||||||
|
// Input sequence is expected to belong to this MBLayout
|
||||||
|
size_t GetNumSequenceFramesInCurrentMB(const SequenceInfo& sequenceInfo) const
|
||||||
|
{
|
||||||
|
return min(sequenceInfo.tEnd, GetNumTimeSteps()) - max(sequenceInfo.tBegin, (ptrdiff_t)0);
|
||||||
|
}
|
||||||
|
|
||||||
// return all sequences stored in this minibatch
|
// return all sequences stored in this minibatch
|
||||||
const vector<SequenceInfo>& GetAllSequences() const
|
const vector<SequenceInfo>& GetAllSequences() const
|
||||||
{
|
{
|
||||||
|
@ -501,6 +508,18 @@ public:
|
||||||
return col;
|
return col;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// get the matrix-column indices for a given sequence
|
||||||
|
// sequence is expected to belong to this MB
|
||||||
|
vector<size_t> GetColumnIndices(const SequenceInfo& seq) const
|
||||||
|
{
|
||||||
|
size_t numFrames = GetNumSequenceFramesInCurrentMB(seq);
|
||||||
|
vector<size_t> res;
|
||||||
|
res.reserve(numFrames);
|
||||||
|
for (size_t i = 0; i < numFrames;++i)
|
||||||
|
res.push_back(GetColumnIndex(seq,i));
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// we are trying to access content--this verifies that the structure is consistent
|
// we are trying to access content--this verifies that the structure is consistent
|
||||||
// All frames must now be declared.
|
// All frames must now be declared.
|
||||||
|
@ -822,7 +841,7 @@ inline bool MBLayout::IsBeyondMinibatch(const FrameRange& fr) const
|
||||||
if (fr.IsAllFrames())
|
if (fr.IsAllFrames())
|
||||||
LogicError("MBLayout::IsBeyondStartOrEnd() cannot be applied to FrameRange that specifies more than a single time step.");
|
LogicError("MBLayout::IsBeyondStartOrEnd() cannot be applied to FrameRange that specifies more than a single time step.");
|
||||||
|
|
||||||
const auto beginTime = (ptrdiff_t)fr.timeIdxInSeq + fr.m_timeOffset; // we test off the frame without offset
|
const auto beginTime = (ptrdiff_t)fr.timeIdxInSeq + fr.m_timeOffset; // we test off the frame with offset
|
||||||
const auto endTime = beginTime + (ptrdiff_t)fr.m_timeRange;
|
const auto endTime = beginTime + (ptrdiff_t)fr.m_timeRange;
|
||||||
return beginTime < 0 || endTime > (ptrdiff_t)GetNumTimeSteps();
|
return beginTime < 0 || endTime > (ptrdiff_t)GetNumTimeSteps();
|
||||||
}
|
}
|
||||||
|
|
|
@ -446,6 +446,7 @@ bool ComputationNetwork::IsTypicalCriterionNode(ComputationNodeBasePtr nodePtr)
|
||||||
nodePtr->OperationName() == OperationNameOf(CrossEntropyNode) ||
|
nodePtr->OperationName() == OperationNameOf(CrossEntropyNode) ||
|
||||||
nodePtr->OperationName() == OperationNameOf(ClassBasedCrossEntropyWithSoftmaxNode) ||
|
nodePtr->OperationName() == OperationNameOf(ClassBasedCrossEntropyWithSoftmaxNode) ||
|
||||||
nodePtr->OperationName() == OperationNameOf(ClassificationErrorNode) ||
|
nodePtr->OperationName() == OperationNameOf(ClassificationErrorNode) ||
|
||||||
|
nodePtr->OperationName() == OperationNameOf(EditDistanceErrorNode) ||
|
||||||
#ifdef COMING_SOON
|
#ifdef COMING_SOON
|
||||||
nodePtr->OperationName() == OperationNameOf(CRFNode) ||
|
nodePtr->OperationName() == OperationNameOf(CRFNode) ||
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -54,6 +54,7 @@ static shared_ptr<ComputationNode<ElemType>> CreateStandardNode(const std::wstri
|
||||||
else if (nodeType == OperationNameOf(DropoutNode)) return New<DropoutNode<ElemType>>(forward<_Types>(_Args)...);
|
else if (nodeType == OperationNameOf(DropoutNode)) return New<DropoutNode<ElemType>>(forward<_Types>(_Args)...);
|
||||||
else if (nodeType == OperationNameOf(DummyCriterionNode)) return New<DummyCriterionNode<ElemType>>(forward<_Types>(_Args)...);
|
else if (nodeType == OperationNameOf(DummyCriterionNode)) return New<DummyCriterionNode<ElemType>>(forward<_Types>(_Args)...);
|
||||||
else if (nodeType == OperationNameOf(DynamicAxisNode)) return New<DynamicAxisNode<ElemType>>(forward<_Types>(_Args)...);
|
else if (nodeType == OperationNameOf(DynamicAxisNode)) return New<DynamicAxisNode<ElemType>>(forward<_Types>(_Args)...);
|
||||||
|
else if (nodeType == OperationNameOf(EditDistanceErrorNode)) return New<EditDistanceErrorNode<ElemType>>(forward<_Types>(_Args)...);
|
||||||
else if (nodeType == OperationNameOf(ElementTimesNode)) return New<ElementTimesNode<ElemType>>(forward<_Types>(_Args)...);
|
else if (nodeType == OperationNameOf(ElementTimesNode)) return New<ElementTimesNode<ElemType>>(forward<_Types>(_Args)...);
|
||||||
else if (nodeType == OperationNameOf(EnvironmentInputNode)) return New<EnvironmentInputNode<ElemType>>(forward<_Types>(_Args)...);
|
else if (nodeType == OperationNameOf(EnvironmentInputNode)) return New<EnvironmentInputNode<ElemType>>(forward<_Types>(_Args)...);
|
||||||
else if (nodeType == OperationNameOf(EpochAccumulatorNode)) return New<EpochAccumulatorNode<ElemType>>(forward<_Types>(_Args)...);
|
else if (nodeType == OperationNameOf(EpochAccumulatorNode)) return New<EpochAccumulatorNode<ElemType>>(forward<_Types>(_Args)...);
|
||||||
|
@ -427,6 +428,12 @@ shared_ptr<ComputationNode<ElemType>> ComputationNetworkBuilder<ElemType>::Class
|
||||||
return net.AddNodeToNetAndAttachInputs(New<ClassificationErrorNode<ElemType>>(net.GetDeviceId(), nodeName), { a, b });
|
return net.AddNodeToNetAndAttachInputs(New<ClassificationErrorNode<ElemType>>(net.GetDeviceId(), nodeName), { a, b });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <class ElemType>
|
||||||
|
shared_ptr<ComputationNode<ElemType>> ComputationNetworkBuilder<ElemType>::EditDistanceError(const ComputationNodePtr a, const ComputationNodePtr b, float subPen, float delPen, float insPen, bool squashInputs, vector<int> samplesToIgnore, const std::wstring nodeName)
|
||||||
|
{
|
||||||
|
return net.AddNodeToNetAndAttachInputs(New<EditDistanceErrorNode<ElemType>>(net.GetDeviceId(), nodeName, subPen, delPen, insPen, squashInputs, samplesToIgnore), { a, b });
|
||||||
|
}
|
||||||
|
|
||||||
template <class ElemType>
|
template <class ElemType>
|
||||||
shared_ptr<ComputationNode<ElemType>> ComputationNetworkBuilder<ElemType>::PerDimMeanVarNormalization(const ComputationNodePtr feature, const ComputationNodePtr mean,
|
shared_ptr<ComputationNode<ElemType>> ComputationNetworkBuilder<ElemType>::PerDimMeanVarNormalization(const ComputationNodePtr feature, const ComputationNodePtr mean,
|
||||||
const ComputationNodePtr InvStdDev, const std::wstring nodeName)
|
const ComputationNodePtr InvStdDev, const std::wstring nodeName)
|
||||||
|
|
|
@ -129,6 +129,7 @@ public:
|
||||||
ComputationNodePtr Diagonal(const ComputationNodePtr a, const std::wstring nodeName = L"");
|
ComputationNodePtr Diagonal(const ComputationNodePtr a, const std::wstring nodeName = L"");
|
||||||
ComputationNodePtr Dropout(const ComputationNodePtr a, const std::wstring nodeName = L"");
|
ComputationNodePtr Dropout(const ComputationNodePtr a, const std::wstring nodeName = L"");
|
||||||
ComputationNodePtr DummyCriterion(const ComputationNodePtr objectives, const ComputationNodePtr derivatives, const ComputationNodePtr prediction, const std::wstring nodeName = L"");
|
ComputationNodePtr DummyCriterion(const ComputationNodePtr objectives, const ComputationNodePtr derivatives, const ComputationNodePtr prediction, const std::wstring nodeName = L"");
|
||||||
|
ComputationNodePtr EditDistanceError(const ComputationNodePtr a, const ComputationNodePtr b, float subPen, float delPen, float insPen, bool squashInputs, vector<int> samplesToIgnore, const std::wstring nodeName = L"");
|
||||||
ComputationNodePtr ElementTimes(const ComputationNodePtr a, const ComputationNodePtr b, const std::wstring nodeName = L"");
|
ComputationNodePtr ElementTimes(const ComputationNodePtr a, const ComputationNodePtr b, const std::wstring nodeName = L"");
|
||||||
ComputationNodePtr DynamicAxis(const ComputationNodePtr a, const std::wstring& nodeName = L"");
|
ComputationNodePtr DynamicAxis(const ComputationNodePtr a, const std::wstring& nodeName = L"");
|
||||||
ComputationNodePtr ClassificationError(const ComputationNodePtr a, const ComputationNodePtr b, const std::wstring nodeName = L"");
|
ComputationNodePtr ClassificationError(const ComputationNodePtr a, const ComputationNodePtr b, const std::wstring nodeName = L"");
|
||||||
|
|
|
@ -7,7 +7,8 @@
|
||||||
#include "Basics.h"
|
#include "Basics.h"
|
||||||
#include "ComputationNode.h"
|
#include "ComputationNode.h"
|
||||||
#include "gammacalculation.h"
|
#include "gammacalculation.h"
|
||||||
|
#include "InputAndParamNodes.h"
|
||||||
|
#include "Sequences.h"
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
@ -15,6 +16,7 @@
|
||||||
#include <list>
|
#include <list>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
|
|
||||||
namespace Microsoft { namespace MSR { namespace CNTK {
|
namespace Microsoft { namespace MSR { namespace CNTK {
|
||||||
|
|
||||||
// -----------------------------------------------------------------------
|
// -----------------------------------------------------------------------
|
||||||
|
@ -456,6 +458,287 @@ protected:
|
||||||
template class NDCG1EvalNode<float>;
|
template class NDCG1EvalNode<float>;
|
||||||
template class NDCG1EvalNode<double>;
|
template class NDCG1EvalNode<double>;
|
||||||
|
|
||||||
|
// Edit distance error evaluation node with the option of specifying penalty of substitution, deletion and insertion, as well as squashing the input sequences and ignoring certain samples.
|
||||||
|
// Using the classic DP algorithm as described in https://en.wikipedia.org/wiki/Edit_distance, adjusted to take into account the penalties.
|
||||||
|
//
|
||||||
|
// The node allows to squash sequences of repeating labels and ignore certain labels. For example, if squashInputs is true and samplesToIgnore contains label '-' then
|
||||||
|
// given first input sequence as s1="a-ab-" and second as s2="-aa--abb" the edit distance will be computed against s1' = "aab" and s2' = "aab".
|
||||||
|
//
|
||||||
|
// The returned error is computed as: EditDistance(s1,s2) * length(s1') / length(s1)
|
||||||
|
//
|
||||||
|
// Just like ClassificationError and other evaluation nodes, when used as an evaluation criterion, the SGD process will aggregate all values over an epoch and report the average, i.e. the error rate.
|
||||||
|
// Primary objective of this node is for error evaluation of CTC training, see formula (1) in "Connectionist Temporal Classification: Labelling Unsegmented
|
||||||
|
// Sequence Data with Recurrent Neural Networks", http://machinelearning.wustl.edu/mlpapers/paper_files/icml2006_GravesFGS06.pdf
|
||||||
|
template<class ElemType>
|
||||||
|
class EditDistanceErrorNode : public ComputationNodeNonLooping/*ComputationNode*/<ElemType>, public NumInputs<2>
|
||||||
|
{
|
||||||
|
typedef ComputationNodeNonLooping<ElemType> Base; UsingComputationNodeMembersBoilerplate;
|
||||||
|
static const std::wstring TypeName() { return L"EditDistanceError"; }
|
||||||
|
|
||||||
|
public:
|
||||||
|
// subPen - substitution penalty
|
||||||
|
// delPen - deletion penalty
|
||||||
|
// insPen - insertion penalty
|
||||||
|
// squashInputs - whether to merge sequences of identical samples.
|
||||||
|
// samplesToIgnore - list of samples to ignore during edit distance evaluation
|
||||||
|
EditDistanceErrorNode(DEVICEID_TYPE deviceId, const wstring & name, float subPen, float delPen, float insPen, bool squashInputs, vector<int> samplesToIgnore)
|
||||||
|
: Base(deviceId, name), m_SubPen(subPen), m_DelPen(delPen), m_InsPen(insPen), m_SquashInputs(squashInputs), m_SamplesToIgnore(samplesToIgnore)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
EditDistanceErrorNode(const ScriptableObjects::IConfigRecordPtr configp)
|
||||||
|
: EditDistanceErrorNode(configp->Get(L"deviceId"), L"<placeholder>", configp->Get(L"subPen"), configp->Get(L"delPen"), configp->Get(L"insPen"), configp->Get(L"squashInputs"), configp->Get(L"samplesToIgnore"))
|
||||||
|
{
|
||||||
|
AttachInputsFromConfig(configp, this->GetExpectedNumInputs());
|
||||||
|
}
|
||||||
|
|
||||||
|
EditDistanceErrorNode(DEVICEID_TYPE deviceId, const wstring& name)
|
||||||
|
: Base(deviceId, name)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void BackpropToNonLooping(size_t /*inputIndex*/) override
|
||||||
|
{
|
||||||
|
LogicError("%ls operation is used for evaluation only.", OperationName().c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void ForwardPropNonLooping() override
|
||||||
|
{
|
||||||
|
bool isInput0Sparse = Input(0)->template Is<SparseInputValue<ElemType>>();
|
||||||
|
bool isInput1Sparse = Input(1)->template Is<SparseInputValue<ElemType>>();
|
||||||
|
if (isInput0Sparse || isInput1Sparse)
|
||||||
|
LogicError("EditDistanceError node was not tested for sparse inputs.");
|
||||||
|
|
||||||
|
FrameRange frameRange(Input(0)->GetMBLayout());
|
||||||
|
Input(0)->ValueFor(frameRange).VectorMax(*m_maxIndexes0, *m_maxValues, true);
|
||||||
|
Input(1)->ValueFor(frameRange).VectorMax(*m_maxIndexes1, *m_maxValues, true);
|
||||||
|
|
||||||
|
MaskMissingColumnsToZero(*m_maxIndexes0, Input(0)->GetMBLayout(), frameRange);
|
||||||
|
MaskMissingColumnsToZero(*m_maxIndexes1, Input(1)->GetMBLayout(), frameRange);
|
||||||
|
Value()(0, 0) = ComputeEditDistanceError(*m_maxIndexes0, *m_maxIndexes1, Input(0)->GetMBLayout(), m_SubPen, m_DelPen, m_InsPen, m_SquashInputs, m_SamplesToIgnore);
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void Validate(bool isFinalValidationPass) override
|
||||||
|
{
|
||||||
|
ValidateBinaryReduce(isFinalValidationPass);
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void UpdateFunctionMBSize() override
|
||||||
|
{
|
||||||
|
Base::UpdateFunctionMBSize();
|
||||||
|
|
||||||
|
// resize the temporaries to their proper size
|
||||||
|
size_t cols = Input(0)->Value().GetNumCols();
|
||||||
|
m_maxIndexes0->Resize(1, cols);
|
||||||
|
m_maxIndexes1->Resize(1, cols);
|
||||||
|
m_maxValues->Resize(1, cols);
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void CopyTo(ComputationNodeBasePtr nodeP, const std::wstring& newName, const CopyNodeFlags flags) const override
|
||||||
|
{
|
||||||
|
Base::CopyTo(nodeP, newName, flags);
|
||||||
|
|
||||||
|
if (flags & CopyNodeFlags::copyNodeValue)
|
||||||
|
{
|
||||||
|
auto node = dynamic_pointer_cast<EditDistanceErrorNode<ElemType>>(nodeP);
|
||||||
|
node->m_maxIndexes0 = m_maxIndexes0;
|
||||||
|
node->m_maxIndexes1 = m_maxIndexes1;
|
||||||
|
node->m_maxValues = m_maxValues;
|
||||||
|
node->m_SquashInputs = m_SquashInputs;
|
||||||
|
node->m_SubPen = m_SubPen;
|
||||||
|
node->m_DelPen = m_DelPen;
|
||||||
|
node->m_InsPen = m_InsPen;
|
||||||
|
node->m_SamplesToIgnore = m_SamplesToIgnore;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//request matrices needed to do node function value evaluation
|
||||||
|
virtual void RequestMatricesBeforeForwardProp(MatrixPool& matrixPool)
|
||||||
|
{
|
||||||
|
Base::RequestMatricesBeforeForwardProp(matrixPool);
|
||||||
|
RequestMatrixFromPool(m_maxIndexes0, matrixPool);
|
||||||
|
RequestMatrixFromPool(m_maxIndexes1, matrixPool);
|
||||||
|
RequestMatrixFromPool(m_maxValues, matrixPool);
|
||||||
|
}
|
||||||
|
|
||||||
|
//release temp matrices that are only used by forward computation
|
||||||
|
//don't release matrices that need to be used in the gradient computation
|
||||||
|
virtual void ReleaseMatricesAfterForwardProp(MatrixPool& matrixPool)
|
||||||
|
{
|
||||||
|
Base::ReleaseMatricesAfterForwardProp(matrixPool);
|
||||||
|
ReleaseMatrixToPool(m_maxIndexes0, matrixPool);
|
||||||
|
ReleaseMatrixToPool(m_maxIndexes1, matrixPool);
|
||||||
|
ReleaseMatrixToPool(m_maxValues, matrixPool);
|
||||||
|
}
|
||||||
|
|
||||||
|
// firstSeq - first sequence of samples
|
||||||
|
// secondSeq - second sequence of samples
|
||||||
|
// numParallelSequences - number of parallel sequences in the minibatch
|
||||||
|
// subPen - substitution penalty
|
||||||
|
// delPen - deletion penalty
|
||||||
|
// insPen - insertion penalty
|
||||||
|
// squashInputs - whether to merge sequences of identical samples.
|
||||||
|
// samplesToIgnore - list of samples to ignore during edit distance evaluation
|
||||||
|
static ElemType ComputeEditDistanceError(Matrix<ElemType>& firstSeq, const Matrix<ElemType> & secondSeq, MBLayoutPtr pMBLayout,
|
||||||
|
float subPen, float delPen, float insPen, bool squashInputs, const vector<int>& samplesToIgnore)
|
||||||
|
{
|
||||||
|
std::vector<int> firstSeqVec, secondSeqVec;
|
||||||
|
|
||||||
|
// Edit distance between subsequences
|
||||||
|
Matrix<float> grid(CPUDEVICE);
|
||||||
|
|
||||||
|
// Number of insertions between subsequences
|
||||||
|
Matrix<float> insMatrix(CPUDEVICE);
|
||||||
|
|
||||||
|
//Number of deletions between subsequences
|
||||||
|
Matrix<float> delMatrix(CPUDEVICE);
|
||||||
|
|
||||||
|
// Number of substitutions between subsequences
|
||||||
|
Matrix<float> subMatrix(CPUDEVICE);
|
||||||
|
|
||||||
|
float del, ins, sub;
|
||||||
|
ElemType wrongSampleNum = 0.0;
|
||||||
|
size_t totalSampleNum = 0, totalframeNum = 0;
|
||||||
|
size_t sequenceStartFrame = 0;
|
||||||
|
|
||||||
|
for (const auto& sequence : pMBLayout->GetAllSequences())
|
||||||
|
{
|
||||||
|
if (sequence.seqId == GAP_SEQUENCE_ID)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
auto numFrames = pMBLayout->GetNumSequenceFramesInCurrentMB(sequence);
|
||||||
|
|
||||||
|
if (numFrames > 0)
|
||||||
|
{
|
||||||
|
totalframeNum += numFrames;
|
||||||
|
|
||||||
|
auto columnIndices = pMBLayout->GetColumnIndices(sequence);
|
||||||
|
|
||||||
|
ExtractSampleSequence(firstSeq, columnIndices, squashInputs, samplesToIgnore, firstSeqVec);
|
||||||
|
ExtractSampleSequence(secondSeq, columnIndices, squashInputs, samplesToIgnore, secondSeqVec);
|
||||||
|
|
||||||
|
//calculate edit distance
|
||||||
|
size_t firstSize = firstSeqVec.size();
|
||||||
|
totalSampleNum += firstSize;
|
||||||
|
size_t secondSize = secondSeqVec.size();
|
||||||
|
grid.Resize(firstSize + 1, secondSize + 1);
|
||||||
|
insMatrix.Resize(firstSize + 1, secondSize + 1);
|
||||||
|
delMatrix.Resize(firstSize + 1, secondSize + 1);
|
||||||
|
subMatrix.Resize(firstSize + 1, secondSize + 1);
|
||||||
|
insMatrix.SetValue(0.0f);
|
||||||
|
delMatrix.SetValue(0.0f);
|
||||||
|
subMatrix.SetValue(0.0f);
|
||||||
|
|
||||||
|
for (size_t i = 0; i < firstSize + 1; i++)
|
||||||
|
{
|
||||||
|
grid(i, 0) = (float)(i * delPen);
|
||||||
|
delMatrix(i, 0) = (float)i;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (size_t j = 0; j < secondSize + 1; j++)
|
||||||
|
{
|
||||||
|
grid(0, j) = (float)(j * insPen);
|
||||||
|
insMatrix(0, j) = (float)j;
|
||||||
|
}
|
||||||
|
for (size_t i = 1; i < firstSize + 1; i++)
|
||||||
|
{
|
||||||
|
for (size_t j = 1; j < secondSize + 1; j++)
|
||||||
|
{
|
||||||
|
if (firstSeqVec[i - 1] == secondSeqVec[j - 1])
|
||||||
|
{
|
||||||
|
grid(i, j) = grid(i - 1, j - 1);
|
||||||
|
insMatrix(i, j) = insMatrix(i - 1, j - 1);
|
||||||
|
delMatrix(i, j) = delMatrix(i - 1, j - 1);
|
||||||
|
subMatrix(i, j) = subMatrix(i - 1, j - 1);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
del = grid(i - 1, j) + delPen; //deletion
|
||||||
|
ins = grid(i, j - 1) + insPen; //insertion
|
||||||
|
sub = grid(i - 1, j - 1) + subPen; //substitution
|
||||||
|
if (sub <= del && sub <= ins)
|
||||||
|
{
|
||||||
|
insMatrix(i, j) = insMatrix(i - 1, j - 1);
|
||||||
|
delMatrix(i, j) = delMatrix(i - 1, j - 1);
|
||||||
|
subMatrix(i, j) = subMatrix(i - 1, j - 1) + 1.0f;
|
||||||
|
grid(i, j) = sub;
|
||||||
|
}
|
||||||
|
else if (del < ins)
|
||||||
|
{
|
||||||
|
insMatrix(i, j) = insMatrix(i - 1, j);
|
||||||
|
subMatrix(i, j) = subMatrix(i - 1, j);
|
||||||
|
delMatrix(i, j) = delMatrix(i - 1, j) + 1.0f;
|
||||||
|
grid(i, j) = del;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
delMatrix(i, j) = delMatrix(i, j - 1);
|
||||||
|
subMatrix(i, j) = subMatrix(i, j - 1);
|
||||||
|
insMatrix(i, j) = insMatrix(i, j - 1) + 1.0f;
|
||||||
|
grid(i, j) = ins;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
wrongSampleNum += insMatrix(firstSize, secondSize) + delMatrix(firstSize, secondSize) + subMatrix(firstSize, secondSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
sequenceStartFrame += numFrames;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (ElemType)(wrongSampleNum * totalframeNum / totalSampleNum);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
shared_ptr<Matrix<ElemType>> m_maxIndexes0, m_maxIndexes1;
|
||||||
|
shared_ptr<Matrix<ElemType>> m_maxValues;
|
||||||
|
bool m_SquashInputs;
|
||||||
|
float m_SubPen;
|
||||||
|
float m_DelPen;
|
||||||
|
float m_InsPen;
|
||||||
|
std::vector<int> m_SamplesToIgnore;
|
||||||
|
|
||||||
|
// Clear out_SampleSeqVec and extract a vector of samples from the matrix into out_SampleSeqVec.
|
||||||
|
static void ExtractSampleSequence(const Matrix<ElemType>& firstSeq, vector<size_t>& columnIndices, bool squashInputs, const vector<int>& samplesToIgnore, std::vector<int>& out_SampleSeqVec)
|
||||||
|
{
|
||||||
|
out_SampleSeqVec.clear();
|
||||||
|
|
||||||
|
// Get the first element in the sequence
|
||||||
|
size_t lastId = (int)firstSeq(0, columnIndices[0]);
|
||||||
|
if (std::find(samplesToIgnore.begin(), samplesToIgnore.end(), lastId) == samplesToIgnore.end())
|
||||||
|
out_SampleSeqVec.push_back(lastId);
|
||||||
|
|
||||||
|
// Remaining elements
|
||||||
|
if (squashInputs)
|
||||||
|
{
|
||||||
|
//squash sequences of identical samples
|
||||||
|
for (size_t i = 1; i < columnIndices.size(); i++)
|
||||||
|
{
|
||||||
|
size_t refId = (int)firstSeq(0, columnIndices[i]);
|
||||||
|
if (lastId != refId)
|
||||||
|
{
|
||||||
|
lastId = refId;
|
||||||
|
if (std::find(samplesToIgnore.begin(), samplesToIgnore.end(), refId) == samplesToIgnore.end())
|
||||||
|
out_SampleSeqVec.push_back(refId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
for (size_t i = 1; i < columnIndices.size(); i++)
|
||||||
|
{
|
||||||
|
auto refId = (int)firstSeq(0, columnIndices[i]);
|
||||||
|
if (std::find(samplesToIgnore.begin(), samplesToIgnore.end(), refId) == samplesToIgnore.end())
|
||||||
|
out_SampleSeqVec.push_back(refId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template class EditDistanceErrorNode<float>;
|
||||||
|
template class EditDistanceErrorNode<double>;
|
||||||
|
|
||||||
#ifdef COMING_SOON
|
#ifdef COMING_SOON
|
||||||
|
|
||||||
// -----------------------------------------------------------------------
|
// -----------------------------------------------------------------------
|
||||||
|
|
|
@ -54,6 +54,12 @@ Test module "NetworkTests" has passed with:
|
||||||
Test case "CropNodeTestSuite/CropNodeBackwardTest" has passed with:
|
Test case "CropNodeTestSuite/CropNodeBackwardTest" has passed with:
|
||||||
40 assertions out of 40 passed
|
40 assertions out of 40 passed
|
||||||
|
|
||||||
|
Test suite "EditDistanceTests" has passed with:
|
||||||
|
1 test cases out of 1 passed
|
||||||
|
1 assertions out of 1 passed
|
||||||
|
|
||||||
|
Test case "EditDistanceTests/ComputeEditDistanceErrorTest" has passed
|
||||||
|
|
||||||
Test suite "NetworkTestSuite" has passed with:
|
Test suite "NetworkTestSuite" has passed with:
|
||||||
1 test case out of 1 passed
|
1 test case out of 1 passed
|
||||||
1 assertion out of 1 passed
|
1 assertion out of 1 passed
|
||||||
|
|
|
@ -0,0 +1,48 @@
|
||||||
|
//
|
||||||
|
// Copyright (c) Microsoft. All rights reserved.
|
||||||
|
// Licensed under the MIT license. See LICENSE.md file in the project root for full license information.
|
||||||
|
//
|
||||||
|
#include "stdafx.h"
|
||||||
|
#include "EvaluationNodes.h"
|
||||||
|
|
||||||
|
using namespace Microsoft::MSR::CNTK;
|
||||||
|
namespace Microsoft { namespace MSR { namespace CNTK { namespace Test {
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_SUITE(EditDistanceTests)
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(ComputeEditDistanceErrorTest)
|
||||||
|
{
|
||||||
|
Matrix<float> firstSeq(CPUDEVICE);
|
||||||
|
Matrix<float> secondSeq(CPUDEVICE);
|
||||||
|
vector<int> samplesToIgnore;
|
||||||
|
size_t seqSize = 10;
|
||||||
|
firstSeq.Resize(1, seqSize);
|
||||||
|
secondSeq.Resize(1, seqSize);
|
||||||
|
for (size_t i = 0; i < seqSize; i++)
|
||||||
|
{
|
||||||
|
firstSeq(0, i) = (float)i;
|
||||||
|
secondSeq(0, i) = (float)i - 1;
|
||||||
|
}
|
||||||
|
MBLayoutPtr pMBLayout = make_shared<MBLayout>(1, seqSize, L"X");
|
||||||
|
pMBLayout->AddSequence(0, 0, 0, seqSize);
|
||||||
|
|
||||||
|
float ed = EditDistanceErrorNode<float>::ComputeEditDistanceError(firstSeq, secondSeq, pMBLayout, 1, 1, 1, true, samplesToIgnore);
|
||||||
|
assert((int)ed == 2);
|
||||||
|
|
||||||
|
for (size_t i = 0; i < seqSize; i++)
|
||||||
|
{
|
||||||
|
secondSeq(0, i) = (float)i;
|
||||||
|
}
|
||||||
|
|
||||||
|
ed = EditDistanceErrorNode<float>::ComputeEditDistanceError(firstSeq, secondSeq, pMBLayout, 1, 1, 1, true, samplesToIgnore);
|
||||||
|
assert((int)ed == 0);
|
||||||
|
|
||||||
|
secondSeq(0, seqSize-1) = (float)123;
|
||||||
|
|
||||||
|
ed = EditDistanceErrorNode<float>::ComputeEditDistanceError(firstSeq, secondSeq, pMBLayout, 1, 1, 1, true, samplesToIgnore);
|
||||||
|
assert((int)ed == 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_SUITE_END()
|
||||||
|
|
||||||
|
} } } }
|
|
@ -56,7 +56,7 @@
|
||||||
<PreprocessorDefinitions>WIN32;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
<PreprocessorDefinitions>WIN32;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||||
<UseFullPaths>true</UseFullPaths>
|
<UseFullPaths>true</UseFullPaths>
|
||||||
<OpenMPSupport>true</OpenMPSupport>
|
<OpenMPSupport>true</OpenMPSupport>
|
||||||
<AdditionalIncludeDirectories>$(MSMPI_INC);$(SolutionDir)Source\Readers\ReaderLib;$(SolutionDir)Source\Common\Include;$(SolutionDir)Source\Math;$(SolutionDir)Source\ActionsLib;$(SolutionDir)Source\ComputationNetworkLib;$(SolutionDir)Source\CNTK\BrainScript;$(BOOST_INCLUDE_PATH)</AdditionalIncludeDirectories>
|
<AdditionalIncludeDirectories>$(MSMPI_INC);$(SolutionDir)Source\Readers\ReaderLib;$(SolutionDir)Source\SequenceTrainingLib;$(SolutionDir)Source\Common\Include;$(SolutionDir)Source\Math;$(SolutionDir)Source\ActionsLib;$(SolutionDir)Source\ComputationNetworkLib;$(SolutionDir)Source\CNTK\BrainScript;$(BOOST_INCLUDE_PATH)</AdditionalIncludeDirectories>
|
||||||
<DisableSpecificWarnings>4819</DisableSpecificWarnings>
|
<DisableSpecificWarnings>4819</DisableSpecificWarnings>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
<Link>
|
<Link>
|
||||||
|
@ -112,6 +112,7 @@
|
||||||
<ClCompile Include="..\..\..\Source\CNTK\BrainScript\BrainScriptParser.cpp" />
|
<ClCompile Include="..\..\..\Source\CNTK\BrainScript\BrainScriptParser.cpp" />
|
||||||
<ClCompile Include="AccumulatorNodeTests.cpp" />
|
<ClCompile Include="AccumulatorNodeTests.cpp" />
|
||||||
<ClCompile Include="CropNodeTests.cpp" />
|
<ClCompile Include="CropNodeTests.cpp" />
|
||||||
|
<ClCompile Include="EditDistanceTests.cpp" />
|
||||||
<ClCompile Include="OperatorEvaluation.cpp" />
|
<ClCompile Include="OperatorEvaluation.cpp" />
|
||||||
<ClCompile Include="stdafx.cpp">
|
<ClCompile Include="stdafx.cpp">
|
||||||
<PrecompiledHeader>Create</PrecompiledHeader>
|
<PrecompiledHeader>Create</PrecompiledHeader>
|
||||||
|
|
|
@ -20,6 +20,7 @@
|
||||||
<ClCompile Include="AccumulatorNodeTests.cpp" />
|
<ClCompile Include="AccumulatorNodeTests.cpp" />
|
||||||
<ClCompile Include="CropNodeTests.cpp" />
|
<ClCompile Include="CropNodeTests.cpp" />
|
||||||
<ClCompile Include="TestHelpers.cpp" />
|
<ClCompile Include="TestHelpers.cpp" />
|
||||||
|
<ClCompile Include="EditDistanceTests.cpp" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Filter Include="Config">
|
<Filter Include="Config">
|
||||||
|
@ -49,4 +50,4 @@
|
||||||
<Filter>Config</Filter>
|
<Filter>Config</Filter>
|
||||||
</Text>
|
</Text>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
Загрузка…
Ссылка в новой задаче