/** * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * ** * Routine for Viterbi backtrace. * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ double viterbi_backtrace(const Graph& graph, matrix<VitCell>& chart, vector<int>& outLabelList, bool doAlign) { int frmCnt = chart.size1() - 1; int stateCnt = chart.size2(); // Find best final state. vector<int> finalStates; int finalCnt = graph.get_final_state_list(finalStates); double bestLogProb = g_zeroLogProb; int bestFinalState = -1; for (int finalIdx = 0; finalIdx < finalCnt; ++finalIdx) { int stateIdx = finalStates[finalIdx]; if (chart(frmCnt, stateIdx).get_log_prob() == g_zeroLogProb) continue; double curLogProb = chart(frmCnt, stateIdx).get_log_prob() + graph.get_final_log_prob(stateIdx); if (curLogProb > bestLogProb) bestLogProb = curLogProb, bestFinalState = stateIdx; } if (bestFinalState < 0) throw runtime_error("No complete paths found."); // Do backtrace, collect appropriate labels. outLabelList.clear(); int stateIdx = bestFinalState; for (int frmIdx = frmCnt; --frmIdx >= 0; ) { assert((stateIdx >= 0) && (stateIdx < stateCnt)); int arcId = chart(frmIdx + 1, stateIdx).get_arc_id(); Arc arc; graph.get_arc(arcId, arc); assert((int) arc.get_dst_state() == stateIdx); if (doAlign) { if (arc.get_gmm() < 0) throw runtime_error("Expect all arcs to have GMM."); outLabelList.push_back(arc.get_gmm()); } else if (arc.get_word() > 0) outLabelList.push_back(arc.get_word()); stateIdx = graph.get_src_state(arcId); } if (stateIdx != graph.get_start_state()) throw runtime_error("Backtrace does not end at start state."); reverse(outLabelList.begin(), outLabelList.end()); return bestLogProb; }
/** * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * ** * Routine for Viterbi decoding. * * @param graph HMM/graph to operate on. * @param gmmProbs Matrix of log prob for each GMM for each frame. * @param chart Dynamic programming chart to fill in; already * allocated to be of correct size and initialized with default values. * @param outLabelList Indices of decoded output tokens are placed here. * @param acousWgt Acoustic weight. * @param doAlign If true, return GMM indices rather than word indices * in @p outLabelList. * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ double viterbi(const Graph& graph, const matrix<double>& gmmProbs, matrix<VitCell>& chart, vector<int>& outLabelList, double acousWgt, bool doAlign) { int frmCnt = chart.size1() - 1; int stateCnt = chart.size2(); // BEGIN_LAB // // Input: // An HMM stored in the object "graph" of type "Graph". // A matrix of doubles "gmmProbs" // // gmmProbs(0 .. (frmCnt - 1), 0 .. (#GMM's - 1)) // // that stores the log prob of each GMM in "gmmSet" // for each frame. // // Output: // A matrix "chart" of "VitCell" objects (declaration of // "VitCell" class above): // // chart(0 .. frmCnt, 0 .. stateCnt - 1) // // On exit, chart(frmIdx, stateIdx).get_log_prob() // should be set to the logarithm of the probability // of the best path from the start state to // state "stateIdx" given the // first "frmIdx" frames of observations; // and chart(frmIdx, stateIdx).get_arc_id() should be set // to the arc ID for the last arc of this best path (or -1 // if the best path is of length 0). // If a cell is unreachable from the start state, // these values should be set to "g_zeroLogProb" // and -1, respectively, which are what these values // are initialized to on entry. // The matrix "chart" has already been initialized to be // of the correct size. // // Notes: "g_zeroLogProb" is a large negative number we use // to represent "ln 0" instead of the actual // value negative infinity. // You can assume there are no skip arcs, i.e., // arc.get_gmm() >= 0 for all arcs "arc" in the graph. // Log probabilities should be base e, i.e., natural // logarithms. // // Here is an example of the syntax for accessing a chart // cell log prob: // // logProb = chart(frmIdx, stateIdx).get_log_prob(); // // Here is an example of setting the contents of a chart cell: // // chart(frmIdx, stateIdx).assign(logProb, arcId); // // Fill in Viterbi algorithm here. // // The code for calculating the final probability and // the best path is provided for you below. // Start Viterbi algorithm // Allocate a matrix for storing the log probability for the each current state giving the previous state // G(cur_state,pre_state) = chart(frmIdx-1,pre_state)*transformpro*gmmProb matrix<double> G; G.resize(stateCnt,stateCnt); for(int rowIndex=0; rowIndex<stateCnt; ++rowIndex) for(int colIndex=0; colIndex<stateCnt; ++colIndex) G(rowIndex,colIndex) = g_zeroLogProb; vector<double> maxProb(stateCnt); // store the max value for each row of the G matrix vector<int> maxIndex(stateCnt); // store the Index of the max element for each row of the G matrix // Initialize start state i.e. chart(0,(0 .. stateCnt - 1)) for(int stateIdx=0; stateIdx<stateCnt; ++stateIdx) if(stateIdx==graph.get_start_state()) chart(0,stateIdx).assign(0,-1); else chart(0,stateIdx).assign(g_zeroLogProb,-1); // End for(int frmIdx=0; frmIdx<frmCnt; ++frmIdx){ for(int stateIdx=0; stateIdx<stateCnt; ++stateIdx){ int arcCnt = graph.get_arc_count(stateIdx); // Get number of outgoing arcs for the stateIdx int arcId = graph.get_first_arc_id(stateIdx); // Get arc ID of the first outgoing arc for(int arcIdx=0; arcIdx<arcCnt;++arcIdx) { Arc arc; arcId = graph.get_arc(arcId,arc); int dstState = arc.get_dst_state(); // get the destination state of the stateIdx double transProb = arc.get_log_prob(); int gmmId = arc.get_gmm(); double gmmProb = gmmProbs(frmIdx,gmmId); G(dstState,stateIdx) = chart(frmIdx, stateIdx).get_log_prob() + transProb + gmmProb; } } for(int curStaIdx=0; curStaIdx<stateCnt; ++curStaIdx){ maxProb[curStaIdx] = g_zeroLogProb; maxIndex[curStaIdx] = -1; for(int preStaIdx=0; preStaIdx<stateCnt; ++preStaIdx) { if(G(curStaIdx,preStaIdx) > maxProb[curStaIdx]){ maxProb[curStaIdx] = G(curStaIdx,preStaIdx); int arcCnt = graph.get_arc_count(preStaIdx); // Get number of outgoing arcs for the stateIdx int arcId = graph.get_first_arc_id(preStaIdx); // Get arc ID of the first outgoing arc for(int arcIdx=0; arcIdx<arcCnt;++arcIdx) { Arc arc; arcId = graph.get_arc(arcId,arc); int dstState = arc.get_dst_state(); // get the destination state of the stateIdx if(dstState==curStaIdx) maxIndex[curStaIdx] = arcId-1; } } } } // assign the chart(frmIdx+1,0 .. stateCnt - 1) for(int stateIdx=0; stateIdx<stateCnt; ++stateIdx){ chart(frmIdx+1,stateIdx).assign(maxProb[stateIdx],maxIndex[stateIdx]); } // clear the matrix G and the vectors for(int rowIndex=0; rowIndex<stateCnt; ++rowIndex) for(int colIndex=0; colIndex<stateCnt; ++colIndex) G(rowIndex,colIndex) = g_zeroLogProb; for(int i=0; i<stateCnt; ++i){ maxProb[i] = g_zeroLogProb; maxIndex[i] = -1; } } // END_LAB // The code for calculating the final probability and // the best path is provided for you. return viterbi_backtrace(graph, chart, outLabelList, doAlign); }