Skip to content

Commit e8dcf44

Browse files
committed
sandbox/online: adding online endpointing code and example script. Various changes and fixes needed for that
git-svn-id: https://svn.code.sf.net/p/kaldi/code/sandbox/online@3644 5e6a8d80-dfce-4ca6-a32a-6e07a63d50c8
1 parent facc9ac commit e8dcf44

20 files changed

Lines changed: 420 additions & 65 deletions

‎egs/rm/s5/local/run_online_decoding.sh‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,9 @@ steps/online/prepare_online_decoding.sh --cmd "$train_cmd" data/train data/lang
99
steps/online/decode.sh --config conf/decode.config --cmd "$decode_cmd" --nj 20 exp/tri3b/graph \
1010
data/test exp/tri3b_online/decode
1111

12+
steps/online/decode.sh --do-endpointing true \
13+
--config conf/decode.config --cmd "$decode_cmd" --nj 20 exp/tri3b/graph \
14+
data/test exp/tri3b_online/decode_endpointing
1215

1316
steps/online/decode.sh --per-utt true --config conf/decode.config \
1417
--cmd "$decode_cmd" --nj 20 exp/tri3b/graph \
@@ -17,6 +20,9 @@ steps/online/decode.sh --per-utt true --config conf/decode.config \
1720
# grep WER exp/tri3b_online/decode/wer_* | utils/best_wer.sh
1821
# %WER 2.00 [ 251 / 12533, 28 ins, 45 del, 178 sub ] exp/tri3b_online/decode/wer_10
1922

23+
# grep WER exp/tri3b_online/decode_endpointing/wer_* | utils/best_wer.sh
24+
# %WER 2.27 [ 284 / 12533, 61 ins, 49 del, 174 sub ] exp/tri3b_online/decode_endpointing/wer_12
25+
2026
# Treating each one as a separate utterance, we get this:
2127
# grep WER exp/tri3b_online/decode_per_utt/wer_* | utils/best_wer.sh
2228
# %WER 2.37 [ 297 / 12533, 41 ins, 56 del, 200 sub ] exp/tri3b_online/decode_per_utt/wer_9

‎egs/wsj/s5/steps/online/prepare_online_decoding.sh‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -202,6 +202,7 @@ if [ $stage -le 3 ]; then
202202
echo "--rescore-model=$dir/final.rescore_mdl" >>$conf
203203
fi
204204
echo "--silence-phones=$silphonelist" >>$conf
205+
echo "--endpoint.silence-phones=$silphonelist" >>$conf
205206
echo "--global-cmvn-stats=$dir/global_cmvn.stats" >>$conf
206207
echo "$0: created config file $conf"
207208
fi

‎src/decoder/lattice-faster-decoder.cc‎

Lines changed: 18 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,7 @@ bool LatticeFasterDecoder::GetBestPath(fst::MutableFst<LatticeArc> *ofst,
112112

113113
// Outputs an FST corresponding to the raw, state-level
114114
// tracebacks.
115-
bool LatticeFasterDecoder::GetRawLattice(fst::MutableFst<LatticeArc> *ofst,
115+
bool LatticeFasterDecoder::GetRawLattice(Lattice *ofst,
116116
bool use_final_probs) const {
117117
typedef LatticeArc Arc;
118118
typedef Arc::StateId StateId;
@@ -199,9 +199,9 @@ bool LatticeFasterDecoder::GetRawLattice(fst::MutableFst<LatticeArc> *ofst,
199199
}
200200

201201
bool LatticeFasterDecoder::GetRawLatticePruned(
202-
fst::MutableFst<LatticeArc> *ofst,
203-
bool use_final_probs,
204-
BaseFloat beam) const {
202+
Lattice *ofst,
203+
bool use_final_probs,
204+
BaseFloat beam) const {
205205
typedef LatticeArc Arc;
206206
typedef Arc::StateId StateId;
207207
typedef Arc::Weight Weight;
@@ -248,17 +248,17 @@ bool LatticeFasterDecoder::GetRawLatticePruned(
248248
ofst->SetStart(ofst->NumStates()-1);
249249

250250
// Next create states for "good" tokens
251-
StateId cur_state = 0;
252251
while (!tok_queue.empty()) {
253252
std::pair<Token*, int32> cur_tok_pair = tok_queue.front();
254253
tok_queue.pop();
255254
Token *cur_tok = cur_tok_pair.first;
256255
int32 cur_frame = cur_tok_pair.second;
257-
256+
KALDI_ASSERT(cur_frame >= 0 && cur_frame <= cost_offsets_.size());
257+
258258
unordered_map<Token*, StateId>::const_iterator iter =
259-
tok_map.find(cur_tok);
259+
tok_map.find(cur_tok);
260260
KALDI_ASSERT(iter != tok_map.end());
261-
cur_state = iter->second;
261+
StateId cur_state = iter->second;
262262

263263
for (ForwardLink *l = cur_tok->links;
264264
l != NULL;
@@ -267,23 +267,21 @@ bool LatticeFasterDecoder::GetRawLatticePruned(
267267
if (next_tok->extra_cost < beam) {
268268
// so both the current and the next token are good, create the arc
269269
int32 next_frame = l->ilabel == 0 ? cur_frame : cur_frame + 1;
270-
std::pair<Token*, int32> next_tok_pair(next_tok, next_frame);
271-
// creat state for new token and put it in queue for further processing
272-
tok_map[next_tok] = ofst->AddState();
273-
tok_queue.push(next_tok_pair);
274-
StateId nextstate = tok_map[next_tok];
275-
BaseFloat cost_offset = 0.0;
276-
if (l->ilabel != 0) { // emitting..
277-
KALDI_ASSERT(cur_frame >= 0 && cur_frame < cost_offsets_.size());
278-
cost_offset = cost_offsets_[cur_frame];
270+
StateId nextstate;
271+
if (tok_map.find(next_tok) == tok_map.end()) {
272+
nextstate = tok_map[next_tok] = ofst->AddState();
273+
tok_queue.push(std::pair<Token*, int32>(next_tok, next_frame));
274+
} else {
275+
nextstate = tok_map[next_tok];
279276
}
277+
BaseFloat cost_offset = (l->ilabel != 0 ? cost_offsets_[cur_frame] : 0);
280278
Arc arc(l->ilabel, l->olabel,
281279
Weight(l->graph_cost, l->acoustic_cost - cost_offset),
282280
nextstate);
283281
ofst->AddArc(cur_state, arc);
284282
}
285283
}
286-
284+
287285
if (cur_frame == num_frames) {
288286
unordered_map<Token*, BaseFloat>::const_iterator iter =
289287
final_costs.find(cur_tok);
@@ -296,15 +294,14 @@ bool LatticeFasterDecoder::GetRawLatticePruned(
296294
}
297295
}
298296

299-
KALDI_ASSERT(cur_state + 1 == ofst->NumStates());
300-
return (cur_state != 0);
297+
return (ofst->NumStates() != 0);
301298
}
302299

303300
// This function is now deprecated, since now we do determinization from outside
304301
// the LatticeFasterDecoder class.
305302
// Outputs an FST corresponding to the lattice-determinized
306303
// lattice (one path per word sequence).
307-
bool LatticeFasterDecoder::GetLattice(fst::MutableFst<CompactLatticeArc> *ofst,
304+
bool LatticeFasterDecoder::GetLattice(CompactLattice *ofst,
308305
bool use_final_probs) const {
309306
Lattice raw_fst;
310307
GetRawLattice(&raw_fst, use_final_probs);

‎src/decoder/lattice-faster-decoder.h‎

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -133,14 +133,14 @@ class LatticeFasterDecoder {
133133
// it will treat all final-probs as one.
134134
bool GetBestPath(fst::MutableFst<LatticeArc> *ofst,
135135
bool use_final_probs = true) const;
136-
136+
137137

138138
// Outputs an FST corresponding to the raw, state-level
139139
// tracebacks. Returns true if result is nonempty.
140140
// If "use_final_probs" is true AND we reached the final-state
141141
// of the graph then it will include those as final-probs, else
142142
// it will treat all final-probs as one.
143-
bool GetRawLattice(fst::MutableFst<LatticeArc> *ofst,
143+
bool GetRawLattice(Lattice *ofst,
144144
bool use_final_probs = true) const;
145145

146146
// Outputs an FST corresponding to the lattice-determinized lattice (one path
@@ -149,7 +149,7 @@ class LatticeFasterDecoder {
149149
// of the graph then it will include those as final-probs, else
150150
// it will treat all final-probs as one. [will become deprecated,
151151
// users should determinize themselves.]
152-
bool GetLattice(fst::MutableFst<CompactLatticeArc> *ofst,
152+
bool GetLattice(CompactLattice *ofst,
153153
bool use_final_probs = true) const;
154154

155155
/// InitDecoding initializes the decoding, and should only be used if you
@@ -189,7 +189,7 @@ class LatticeFasterDecoder {
189189
/// reasonable likelihood.
190190
BaseFloat FinalRelativeCost() const;
191191

192-
inline int32 NumFramesDecoded() { return active_toks_.size() - 1; }
192+
inline int32 NumFramesDecoded() const { return active_toks_.size() - 1; }
193193
private:
194194
struct Token;
195195
// ForwardLinks are the links from a token to a token on the next frame.
@@ -333,7 +333,7 @@ class LatticeFasterDecoder {
333333
// Behaves the same like GetRawLattice but only processes
334334
// tokens whose extra_cost is smaller than beam(plus the lowest
335335
// extra_cost of it's corresponding frame).
336-
bool GetRawLatticePruned(fst::MutableFst<LatticeArc> *ofst,
336+
bool GetRawLatticePruned(Lattice *ofst,
337337
bool use_final_probs = true,
338338
BaseFloat beam = 0.1) const;
339339

@@ -474,6 +474,9 @@ class DecodeUtteranceLatticeFasterClass {
474474
};
475475

476476

477+
478+
479+
477480
} // end namespace kaldi.
478481

479482
#endif

‎src/decoder/lattice-simple-decoder.cc‎

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ bool LatticeSimpleDecoder::Decode(DecodableInterface *decodable) {
6565

6666
// Outputs an FST corresponding to the single best path
6767
// through the lattice.
68-
bool LatticeSimpleDecoder::GetBestPath(fst::MutableFst<LatticeArc> *ofst,
68+
bool LatticeSimpleDecoder::GetBestPath(Lattice *ofst,
6969
bool use_final_probs) const {
7070
fst::VectorFst<LatticeArc> fst;
7171
GetRawLattice(&fst, use_final_probs);
@@ -75,7 +75,7 @@ bool LatticeSimpleDecoder::GetBestPath(fst::MutableFst<LatticeArc> *ofst,
7575

7676
// Outputs an FST corresponding to the raw, state-level
7777
// tracebacks.
78-
bool LatticeSimpleDecoder::GetRawLattice(fst::MutableFst<LatticeArc> *ofst,
78+
bool LatticeSimpleDecoder::GetRawLattice(Lattice *ofst,
7979
bool use_final_probs) const {
8080
typedef LatticeArc Arc;
8181
typedef Arc::StateId StateId;
@@ -155,8 +155,9 @@ bool LatticeSimpleDecoder::GetRawLattice(fst::MutableFst<LatticeArc> *ofst,
155155
// the LatticeSimpleDecoder class.
156156
// Outputs an FST corresponding to the lattice-determinized
157157
// lattice (one path per word sequence).
158-
bool LatticeSimpleDecoder::GetLattice(fst::MutableFst<CompactLatticeArc> *ofst,
159-
bool use_final_probs) const {
158+
bool LatticeSimpleDecoder::GetLattice(
159+
CompactLattice *ofst,
160+
bool use_final_probs) const {
160161
Lattice raw_fst;
161162
GetRawLattice(&raw_fst, use_final_probs);
162163
Invert(&raw_fst); // make it so word labels are on the input.

‎src/decoder/lattice-simple-decoder.h‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -143,7 +143,7 @@ class LatticeSimpleDecoder {
143143
// If "use_final_probs" is true AND we reached the final-state
144144
// of the graph then it will include those as final-probs, else
145145
// it will treat all final-probs as one.
146-
bool GetBestPath(fst::MutableFst<LatticeArc> *ofst,
146+
bool GetBestPath(Lattice *lat,
147147
bool use_final_probs = true) const;
148148

149149
// Outputs an FST corresponding to the raw, state-level
@@ -152,15 +152,15 @@ class LatticeSimpleDecoder {
152152
// If "use_final_probs" is true AND we reached the final-state
153153
// of the graph then it will include those as final-probs, else
154154
// it will treat all final-probs as one.
155-
bool GetRawLattice(fst::MutableFst<LatticeArc> *ofst,
155+
bool GetRawLattice(Lattice *lat,
156156
bool use_final_probs = true) const;
157157

158158
// This function is now deprecated, since now we do determinization from
159159
// outside the LatticeTrackingDecoder class.
160160
// Outputs an FST corresponding to the lattice-determinized
161161
// lattice (one path per word sequence). [will become deprecated,
162162
// users should determinize themselves.]
163-
bool GetLattice(fst::MutableFst<CompactLatticeArc> *ofst,
163+
bool GetLattice(CompactLattice *clat,
164164
bool use_final_probs = true) const;
165165

166166
inline int32 NumFramesDecoded() const { return active_toks_.size() - 1; }

‎src/decoder/simple-decoder.cc‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,7 @@ BaseFloat SimpleDecoder::FinalRelativeCost() const {
118118
return extra_cost;
119119
}
120120

121-
bool SimpleDecoder::GetBestPath(fst::MutableFst<LatticeArc> *fst_out) const {
121+
bool SimpleDecoder::GetBestPath(Lattice *fst_out) const {
122122
fst_out->DeleteStates();
123123
Token *best_tok = NULL;
124124
bool is_final = ReachedFinal();

‎src/decoder/simple-decoder.h‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ class SimpleDecoder {
5959
// fst_out will be empty (Start() == kNoStateId) if nothing was available due to
6060
// search error.
6161
// If Decode() returned true, it is safe to assume GetBestPath will return true.
62-
bool GetBestPath(fst::MutableFst<LatticeArc> *fst_out) const;
62+
bool GetBestPath(Lattice *fst_out) const;
6363

6464
/// *** The next functions are from the "new interface". ***
6565

‎src/online2/Makefile‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ include ../kaldi.mk
66
TESTFILES = online-feature-test
77

88
OBJFILES = online-feature.o online-gmm-decodable.o online-feature-pipeline.o \
9-
online-gmm-decoding.o online-timing.o onlinebin-util.o
9+
online-gmm-decoding.o online-timing.o online-endpoint.o onlinebin-util.o
1010

1111
LIBNAME = kaldi-online2
1212

‎src/online2/online-endpoint.cc‎

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
// online2/online-endpoing.cc
2+
3+
// Copyright 2014 Johns Hopkins University (author: Daniel Povey)
4+
5+
// See ../../COPYING for clarification regarding multiple authors
6+
//
7+
// Licensed under the Apache License, Version 2.0 (the "License");
8+
// you may not use this file except in compliance with the License.
9+
// You may obtain a copy of the License at
10+
//
11+
// http://www.apache.org/licenses/LICENSE-2.0
12+
//
13+
// THIS CODE IS PROVIDED *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14+
// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED
15+
// WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,
16+
// MERCHANTABLITY OR NON-INFRINGEMENT.
17+
// See the Apache 2 License for the specific language governing permissions and
18+
// limitations under the License.
19+
20+
#include "online2/online-endpoint.h"
21+
22+
namespace kaldi {
23+
24+
static inline void GetBestPathStats(const TransitionModel &tmodel,
25+
const Lattice &best_path,
26+
const OnlineEndpointConfig &config,
27+
BaseFloat frame_shift_in_seconds,
28+
BaseFloat *utterance_length,
29+
BaseFloat *trailing_silence) {
30+
std::vector<int32> silence_phones;
31+
if (!SplitStringToIntegers(config.silence_phones, ":", false, &silence_phones))
32+
KALDI_ERR << "Bad --silence-phones option in endpointing config: "
33+
<< config.silence_phones;
34+
std::sort(silence_phones.begin(), silence_phones.end());
35+
KALDI_ASSERT(IsSortedAndUniq(silence_phones) &&
36+
"Duplicates in --silence-phones option in endpointing config");
37+
KALDI_ASSERT(!silence_phones.empty() &&
38+
"Endpointing requires nonempty --endpoint.silence-phones option");
39+
ConstIntegerSet<int32> silence_set(silence_phones);
40+
41+
std::vector<int32> alignment;
42+
43+
fst::GetLinearSymbolSequence<LatticeArc,int32>(best_path, &alignment, NULL, NULL);
44+
45+
*utterance_length = alignment.size() * frame_shift_in_seconds;
46+
if (alignment.empty()) {
47+
*trailing_silence = 0.0;
48+
return;
49+
}
50+
51+
int32 num_sil_frames = 0;
52+
for (int32 n = alignment.size() - 1; n >= 0; n--) {
53+
int32 phone = tmodel.TransitionIdToPhone(alignment[n]);
54+
if (silence_set.count(phone) != 0)
55+
num_sil_frames++;
56+
else
57+
break;
58+
}
59+
*trailing_silence = num_sil_frames * frame_shift_in_seconds;
60+
}
61+
62+
63+
static bool RuleActivated(const OnlineEndpointRule &rule,
64+
const std::string &rule_name,
65+
BaseFloat trailing_silence,
66+
BaseFloat relative_cost,
67+
BaseFloat utterance_length) {
68+
bool contains_nonsilence = (utterance_length > trailing_silence);
69+
70+
bool ans = (contains_nonsilence || !rule.must_contain_nonsilence) &&
71+
trailing_silence >= rule.min_trailing_silence &&
72+
relative_cost <= rule.max_relative_cost &&
73+
utterance_length >= rule.min_utterance_length;
74+
if (ans) {
75+
KALDI_VLOG(2) << "Endpointing rule " << rule_name << " activated: "
76+
<< (contains_nonsilence ? "true" : "false" ) << ','
77+
<< trailing_silence << ',' << relative_cost << ','
78+
<< utterance_length;
79+
}
80+
return ans;
81+
}
82+
83+
bool EndpointDetected(const TransitionModel &tmodel,
84+
const Lattice &best_path,
85+
const OnlineEndpointConfig &config,
86+
BaseFloat frame_shift_in_seconds,
87+
BaseFloat final_relative_cost) {
88+
if (best_path.NumStates() == 0) return false;
89+
KALDI_ASSERT(final_relative_cost >= 0.0);
90+
BaseFloat utterance_length, trailing_silence;
91+
GetBestPathStats(tmodel, best_path, config, frame_shift_in_seconds,
92+
&utterance_length, &trailing_silence);
93+
94+
if (RuleActivated(config.rule1, "rule1",
95+
trailing_silence, final_relative_cost, utterance_length))
96+
return true;
97+
if (RuleActivated(config.rule2, "rule2",
98+
trailing_silence, final_relative_cost, utterance_length))
99+
return true;
100+
if (RuleActivated(config.rule3, "rule3",
101+
trailing_silence, final_relative_cost, utterance_length))
102+
return true;
103+
if (RuleActivated(config.rule4, "rule4",
104+
trailing_silence, final_relative_cost, utterance_length))
105+
return true;
106+
if (RuleActivated(config.rule5, "rule5",
107+
trailing_silence, final_relative_cost, utterance_length))
108+
return true;
109+
return false;
110+
}
111+
112+
113+
114+
} // namespace kaldi

0 commit comments

Comments
 (0)