Applying Grammar Weights

Ultimately, the Speech Engine is just a probability machine.

When the Engine decodes audio input, it compares the sounds in the audio to its phoneme tables to determine which phonemes are present. Using the grammar as a guide, it computes the probability that a series of sounds matches a word in the grammar.

You can modify those probabilities in an SRGS grammar by applying weights to words, phrases, and rules — making the Engine more or less likely to match audio to specific grammar items.

As an example, suppose we have a grammar that recognizes a four-digit number:

ABNF example:

#ABNF 1.0;
language en-US;
mode voice;
root $number;

$one_digit = zero | one | two | three | four | five | six | seven | eight | nine;

$teens = ten | eleven | twelve | thirteen | fourteen | fifteen | sixteen | seventeen | eighteen | nineteen;
$above_twenty = (twenty | thirty | forty | fifty | sixty | seventy | eighty | ninety)[$one_digit];
$double_digit = $teens | $above_twenty;

$single_digits = $one_digit<4>; //e.g. one two three four
$double_digits = $double_digit<2>; //e.g. twelve thirty four
$single_double = $one_digit<2> $double_digit; //e.g. one two thirty four
$double_single = $double_digit $one_digit<2>; //e.g. twelve three four

$number = $single_digits | $double_digits | $single_double | $double_single;

GrXML example:

<?xml version="1.0" encoding="UTF-8" ?>
<grammar xmlns="http://www.w3.org/2001/06/grammar" xml:lang="en-US" root="number" mode="voice">

  <rule id="one_digit">
    <one-of>
      <item>zero</item>
      <item>one</item>
      <item>two</item>
      <item>three</item>
      <item>four</item>
      <item>five</item>
      <item>six</item>
      <item>seven</item>
      <item>eight</item>
      <item>nine</item>
    </one-of>
  </rule>

  <rule id="teens">
    <one-of>
      <item>ten</item>
      <item>eleven</item>
      <item>twelve</item>
      <item>thirteen</item>
      <item>fourteen</item>
      <item>fifteen</item>
      <item>sixteen</item>
      <item>seventeen</item>
      <item>eighteen</item>
      <item>nineteen</item>
    </one-of>
  </rule>

  <rule id="above_twenty">
    <one-of>
      <item>twenty</item>
      <item>thirty</item>
      <item>forty</item>
      <item>fifty</item>
      <item>sixty</item>
      <item>seventy</item>
      <item>eighty</item>
      <item>ninety</item>
    </one-of>
    <item repeat="0-1"><ruleref uri="#one_digit"/></item>
  </rule>

  <rule id="double_digit">
    <one-of>
      <item><ruleref uri="#teens"/></item>
      <item><ruleref uri="#above_twenty"/></item>
    </one-of>
  </rule>

  <rule id="single_digits">  <!-- e.g. one two three four -->
    <item repeat="4"><ruleref uri="#one_digit"/></item>
  </rule>

  <rule id="double_digits">  <!-- e.g. twelve thirty four -->
    <item repeat="2"><ruleref uri="#double_digit"/></item>
  </rule>

  <rule id="single_double">  <!-- e.g. one two thirty four -->
    <item repeat="2"><ruleref uri="#one_digit"/></item>
    <item><ruleref uri="#double_digit"/></item>
  </rule>

  <rule id="double_single">  <!-- e.g. twelve three four -->
    <item><ruleref uri="#double_digit"/></item>
    <item repeat="2"><ruleref uri="#one_digit"/></item>
  </rule>

  <rule id="number">
    <one-of>
      <item><ruleref uri="#single_digits"/></item>
      <item><ruleref uri="#double_digits"/></item>
      <item><ruleref uri="#single_double"/></item>
      <item><ruleref uri="#double_single"/></item>
    </one-of>
  </rule>

</grammar>

This is a flexible grammar, but in practice you might be disappointed — you may notice that words like "four three" are too often misrecognized as "forty." In general, your callers may speak a sentence that matches single_digits the majority of the time, but the ASR too frequently returns a result matching one of the other three rules.

You can help the ASR get the right answer more often by adding a weight that predisposes it toward the single_digits rule.

Weights are numeric. In ABNF they are entered between two forward slashes (the / character); in GrXML they are set with the weight attribute on <item> elements. Weights specify how much more or less likely one item is to be matched than another — in this sense they are relative to other weights. Items are assumed to have a weight of 1 if no weight is specified.

So if one item is given a weight of 2 and a second a weight of 1, the first is twice as likely to be recognized as the second. You could equally assign weights of 200 and 100 for the same effect.

Suppose callers match the single_digits rule five times as often as the other rules. We could weight the grammar to reflect this:

ABNF example:

#ABNF 1.0;
language en-US;
mode voice;
root $number;

$one_digit = zero | one | two | three | four | five | six | seven | eight | nine;
$teens = ten | eleven | twelve | thirteen | fourteen | fifteen | sixteen | seventeen | eighteen | nineteen;
$above_twenty = (twenty | thirty | forty | fifty | sixty | seventy | eighty | ninety)[$one_digit];
$double_digit = $teens | $above_twenty;

$single_digits = $one_digit<4>; //one two three four
$double_digits = $double_digit<2>; //twelve thirty four
$single_double = $one_digit<2> $double_digit; //one two thirty four
$double_single = $double_digit $one_digit<2>; //twelve three four

$number = /50/ $single_digits | /10/ ($double_digits | $single_double | $double_single);

/**********************************************************
 * You could also write the weights as:
 * /5/ $single_digits | $double_digits | $single_double | $double_single;
 **********************************************************/

GrXML example:

<?xml version="1.0" encoding="UTF-8" ?>
<grammar xmlns="http://www.w3.org/2001/06/grammar" xml:lang="en-US" root="number" mode="voice">

  <rule id="one_digit">
    <one-of>
      <item>zero</item>
      <item>one</item>
      <item>two</item>
      <item>three</item>
      <item>four</item>
      <item>five</item>
      <item>six</item>
      <item>seven</item>
      <item>eight</item>
      <item>nine</item>
    </one-of>
  </rule>

  <rule id="teens">
    <one-of>
      <item>ten</item>
      <item>eleven</item>
      <item>twelve</item>
      <item>thirteen</item>
      <item>fourteen</item>
      <item>fifteen</item>
      <item>sixteen</item>
      <item>seventeen</item>
      <item>eighteen</item>
      <item>nineteen</item>
    </one-of>
  </rule>

  <rule id="above_twenty">
    <one-of>
      <item>twenty</item>
      <item>thirty</item>
      <item>forty</item>
      <item>fifty</item>
      <item>sixty</item>
      <item>seventy</item>
      <item>eighty</item>
      <item>ninety</item>
    </one-of>
    <item repeat="0-1"><ruleref uri="#one_digit"/></item>
  </rule>

  <rule id="double_digit">
    <one-of>
      <item><ruleref uri="#teens"/></item>
      <item><ruleref uri="#above_twenty"/></item>
    </one-of>
  </rule>

  <rule id="single_digits">  <!-- e.g. one two three four -->
    <item repeat="4"><ruleref uri="#one_digit"/></item>
  </rule>

  <rule id="double_digits">  <!-- e.g. twelve thirty four -->
    <item repeat="2"><ruleref uri="#double_digit"/></item>
  </rule>

  <rule id="single_double">  <!-- e.g. one two thirty four -->
    <item repeat="2"><ruleref uri="#one_digit"/></item>
    <item><ruleref uri="#double_digit"/></item>
  </rule>

  <rule id="double_single">  <!-- e.g. twelve three four -->
    <item><ruleref uri="#double_digit"/></item>
    <item repeat="2"><ruleref uri="#one_digit"/></item>
  </rule>

  <rule id="number">
    <one-of>
      <item weight="50"><ruleref uri="#single_digits"/></item>
      <item weight="10">
        <item><ruleref uri="#double_digits"/></item>
        <item><ruleref uri="#single_double"/></item>
        <item><ruleref uri="#double_single"/></item>
      </item>
    </one-of>
  </rule>

  <!-- You could also write the weights as:

  <item weight="5"><ruleref uri="#single_digits"/></item>
  <item><ruleref uri="#double_digits"/></item>
  <item><ruleref uri="#single_double"/></item>
  <item><ruleref uri="#double_single"/></item>

  -->
</grammar>

Now, when the Engine faces a borderline decision between matching single_digits or one of the others, it will more frequently choose single_digits. We weighted the rules with a 5:1 ratio because we had actual data showing that callers said one rule five times as often as the others.

Weights are most useful when two items sound similar and are therefore likely to be confused — applied properly, they affect the outcome only when the Engine had a close choice between two items. For this reason, avoid very high or very low weights unless you are weighting all the rules accordingly. If you weighted one rule at 10,000 and left the others at the default of 1, the Engine would likely match every utterance to that rule, regardless of what was said.

If you give rules weights below 1, it can become very difficult for the Engine to match them, as this is effectively a negative weight. In addition to matching sounds to the phonemes in a grammar, the Engine also tries to match audio to noise, which it discards. If you apply very strong negative weights to rules, the Engine can end up almost always favoring noise over the negatively weighted rules.

Do Not Apply Weights Without Data

Applying grammar weights should never be the first thing you do to a grammar. Initially you do not know how often each rule will be matched, so you are better off letting all rules be treated equally. Apply weights only after you have a compelling amount of data showing that they will help the application — as we did above — and after you apply them, test their effects on real call data. Badly applied weights are worse than no weights at all.


Related Articles


Was this article helpful?