You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
154 lines
5.7 KiB
154 lines
5.7 KiB
#!/usr/bin/env perl
|
|
use warnings; #sed replacement for -w perl parameter
|
|
# Copyright 2010-2011 Microsoft Corporation
|
|
# 2013 Johns Hopkins University (author: Daniel Povey)
|
|
|
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
# you may not use this file except in compliance with the License.
|
|
# You may obtain a copy of the License at
|
|
#
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
#
|
|
# THIS CODE IS PROVIDED *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
|
# KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED
|
|
# WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,
|
|
# MERCHANTABLITY OR NON-INFRINGEMENT.
|
|
# See the Apache 2 License for the specific language governing permissions and
|
|
# limitations under the License.
|
|
|
|
# makes lexicon FST, in text form, from lexicon (pronunciation probabilities optional).
|
|
|
|
$pron_probs = 0;
|
|
|
|
if ((@ARGV > 0) && ($ARGV[0] eq "--pron-probs")) {
|
|
$pron_probs = 1;
|
|
shift @ARGV;
|
|
}
|
|
|
|
if (@ARGV != 1 && @ARGV != 3 && @ARGV != 4) {
|
|
print STDERR "Usage: make_lexicon_fst.pl [--pron-probs] lexicon.txt [silprob silphone [sil_disambig_sym]] >lexiconfst.txt\n\n";
|
|
print STDERR "Creates a lexicon FST that transduces phones to words, and may allow optional silence.\n\n";
|
|
print STDERR "Note: ordinarily, each line of lexicon.txt is:\n";
|
|
print STDERR " word phone1 phone2 ... phoneN;\n";
|
|
print STDERR "if the --pron-probs option is used, each line is:\n";
|
|
print STDERR " word pronunciation-probability phone1 phone2 ... phoneN.\n\n";
|
|
print STDERR "The probability 'prob' will typically be between zero and one, and note that\n";
|
|
print STDERR "it's generally helpful to normalize so the largest one for each word is 1.0, but\n";
|
|
print STDERR "this is your responsibility.\n\n";
|
|
print STDERR "The silence disambiguation symbol, e.g. something like #5, is used only\n";
|
|
print STDERR "when creating a lexicon with disambiguation symbols, e.g. L_disambig.fst,\n";
|
|
print STDERR "and was introduced to fix a particular case of non-determinism of decoding graphs.\n\n";
|
|
exit(1);
|
|
}
|
|
|
|
$lexfn = shift @ARGV;
|
|
if (@ARGV == 0) {
|
|
$silprob = 0.0;
|
|
} elsif (@ARGV == 2) {
|
|
($silprob,$silphone) = @ARGV;
|
|
} else {
|
|
($silprob,$silphone,$sildisambig) = @ARGV;
|
|
}
|
|
if ($silprob != 0.0) {
|
|
$silprob < 1.0 || die "Sil prob cannot be >= 1.0";
|
|
$silcost = -log($silprob);
|
|
$nosilcost = -log(1.0 - $silprob);
|
|
}
|
|
|
|
|
|
open(L, "<$lexfn") || die "Error opening lexicon $lexfn";
|
|
|
|
|
|
if ( $silprob == 0.0 ) { # No optional silences: just have one (loop+final) state which is numbered zero.
|
|
$loopstate = 0;
|
|
$nextstate = 1; # next unallocated state.
|
|
while (<L>) {
|
|
@A = split(" ", $_);
|
|
@A == 0 && die "Empty lexicon line.";
|
|
foreach $a (@A) {
|
|
if ($a eq "<eps>") {
|
|
die "Bad lexicon line $_ (<eps> is forbidden)";
|
|
}
|
|
}
|
|
$w = shift @A;
|
|
if (! $pron_probs) {
|
|
$pron_cost = 0.0;
|
|
} else {
|
|
$pron_prob = shift @A;
|
|
if (! defined $pron_prob || !($pron_prob > 0.0 && $pron_prob <= 1.0)) {
|
|
die "Bad pronunciation probability in line $_";
|
|
}
|
|
$pron_cost = -log($pron_prob);
|
|
}
|
|
if ($pron_cost != 0.0) { $pron_cost_string = "\t$pron_cost"; } else { $pron_cost_string = ""; }
|
|
|
|
$s = $loopstate;
|
|
$word_or_eps = $w;
|
|
while (@A > 0) {
|
|
$p = shift @A;
|
|
if (@A > 0) {
|
|
$ns = $nextstate++;
|
|
} else {
|
|
$ns = $loopstate;
|
|
}
|
|
print "$s\t$ns\t$p\t$word_or_eps$pron_cost_string\n";
|
|
$word_or_eps = "<eps>";
|
|
$pron_cost_string = ""; # so we only print it on the first arc of the word.
|
|
$s = $ns;
|
|
}
|
|
}
|
|
print "$loopstate\t0\n"; # final-cost.
|
|
} else { # have silence probs.
|
|
$startstate = 0;
|
|
$loopstate = 1;
|
|
$silstate = 2; # state from where we go to loopstate after emitting silence.
|
|
print "$startstate\t$loopstate\t<eps>\t<eps>\t$nosilcost\n"; # no silence.
|
|
if (!defined $sildisambig) {
|
|
print "$startstate\t$loopstate\t$silphone\t<eps>\t$silcost\n"; # silence.
|
|
print "$silstate\t$loopstate\t$silphone\t<eps>\n"; # no cost.
|
|
$nextstate = 3;
|
|
} else {
|
|
$disambigstate = 3;
|
|
$nextstate = 4;
|
|
print "$startstate\t$disambigstate\t$silphone\t<eps>\t$silcost\n"; # silence.
|
|
print "$silstate\t$disambigstate\t$silphone\t<eps>\n"; # no cost.
|
|
print "$disambigstate\t$loopstate\t$sildisambig\t<eps>\n"; # silence disambiguation symbol.
|
|
}
|
|
while (<L>) {
|
|
@A = split(" ", $_);
|
|
$w = shift @A;
|
|
if (! $pron_probs) {
|
|
$pron_cost = 0.0;
|
|
} else {
|
|
$pron_prob = shift @A;
|
|
if (! defined $pron_prob || !($pron_prob > 0.0 && $pron_prob <= 1.0)) {
|
|
die "Bad pronunciation probability in line $_";
|
|
}
|
|
$pron_cost = -log($pron_prob);
|
|
}
|
|
if ($pron_cost != 0.0) { $pron_cost_string = "\t$pron_cost"; } else { $pron_cost_string = ""; }
|
|
$s = $loopstate;
|
|
$word_or_eps = $w;
|
|
while (@A > 0) {
|
|
$p = shift @A;
|
|
if (@A > 0) {
|
|
$ns = $nextstate++;
|
|
print "$s\t$ns\t$p\t$word_or_eps$pron_cost_string\n";
|
|
$word_or_eps = "<eps>";
|
|
$pron_cost_string = ""; $pron_cost = 0.0; # so we only print it the 1st time.
|
|
$s = $ns;
|
|
} elsif (!defined($silphone) || $p ne $silphone) {
|
|
# This is non-deterministic but relatively compact,
|
|
# and avoids epsilons.
|
|
$local_nosilcost = $nosilcost + $pron_cost;
|
|
$local_silcost = $silcost + $pron_cost;
|
|
print "$s\t$loopstate\t$p\t$word_or_eps\t$local_nosilcost\n";
|
|
print "$s\t$silstate\t$p\t$word_or_eps\t$local_silcost\n";
|
|
} else {
|
|
# no point putting opt-sil after silence word.
|
|
print "$s\t$loopstate\t$p\t$word_or_eps$pron_cost_string\n";
|
|
}
|
|
}
|
|
}
|
|
print "$loopstate\t0\n"; # final-cost.
|
|
} |