Difference between revisions of "Ambiguity resolution"
m (→Initial Documentation) |
(→Constraints) |
||
| Line 89: | Line 89: | ||
Contextual tests come together in ()s. All tests must match in order for the constraint to apply. Inside the parentheses, the first definition is a position: <code>0</code> means the current cohort, <code>-1</code> means the previous cohort, etc. The rest of the test is what should be matched. If you want to match any member of the <code>Nominals</code> set in the previous position, it would be <code>(-1 Nominals)</code> You can match tag sequences by adding an extra set of parentheses, e.g. <code>(1 (n pl))</code> would match a plural noun in the following position. You can match specific baseforms with quotation marks, e.g. <code>(1 ("house" n))</code> would match any form of the noun "house" in the following position. | Contextual tests come together in ()s. All tests must match in order for the constraint to apply. Inside the parentheses, the first definition is a position: <code>0</code> means the current cohort, <code>-1</code> means the previous cohort, etc. The rest of the test is what should be matched. If you want to match any member of the <code>Nominals</code> set in the previous position, it would be <code>(-1 Nominals)</code> You can match tag sequences by adding an extra set of parentheses, e.g. <code>(1 (n pl))</code> would match a plural noun in the following position. You can match specific baseforms with quotation marks, e.g. <code>(1 ("house" n))</code> would match any form of the noun "house" in the following position. | ||
| − | '''Classroom activity''': implement "houses this" example (at top of page). [https://github.swarthmore.edu/Ling073-sp26/ling073-eng/blob/master/apertium-eng.eng.rlx Solution]. | + | '''Classroom activity''': implement "houses this" example (at top of page). '''[https://github.swarthmore.edu/Ling073-sp26/ling073-eng/blob/master/apertium-eng.eng.rlx Solution] to this in-class example'''. |
=== Useful commands === | === Useful commands === | ||
Revision as of 15:18, 26 March 2026
There are two primary types of ambiguity that an Apertium pipeline can deal with:
- Morphological ambiguity — when a single form has multiple analyses. The solution to this is #Morphological disambiguation.
- Lexical ambiguity — when a single lexical item (lemma+POS+subcategory) has multiple translations. The solution to this is #Lexical selection.
Contents
Distinguishing between the two
Morphological ambiguity
Imagine you have a word that has two different tagsets, e.g.
^houses/house<n><pl>/house<v><tv><p3><sg>$ ^this/this<det><dem><sg>/this<prn><dem><sg>$
Normally your analyser will randomly output ("choose") one of these. Now imagine you have a sentence where the wrong analysis is "chosen" (in this case, twice!):
^The/The<det><def>$ ^motel/motel<n><sg>$ ^houses/house<n><pl>$ ^this/this<prn><dem><sg>$ ^dog/dog<n><sg>$^./.<sent>$
In both of these examples, there is a different lemma+POS+subcategory. However, the following is also an example of morphological ambiguity:
^deer/deer<n><sg>/deer<n><pl>$
As with the previous examples, both analyses are correct output from the transducer, but which one is correct in a given context will vary. (The assumption is that one and only one analysis will be correct for a given context.)
The goal of morphological disambiguation is to choose the correct analysis in cases like these based on the surrounding words.
Lexical ambiguity
An example of lexical ambiguity is the following:
- yard (a unit of length)
- yard (an enclosed area)
What's different about these words? Spelling, pronunciation, part of speech? What [different] contexts might each occur in?
Can we decide which translation is right from the surrounding words?
In both cases, these two words would receive the same analysis: ^yard/yard<n><sg>$. There would likely even be only one entry in a transducer that covers both nouns. But in a bilingual dictionary, depending on the target language, you could easily need two different entries to translate these correctly. Without further mechanisms, one of the options would be "chosen" randomly during translation.
To demonstrate this point, let's take an example from Spanish-to-English translation ("Estoy en la estación de tren" = "I'm at the train station"):
^Estar<vblex><pri><p1><sg>/Be<vbser><pri><p1><sg>$ ^en<pr>/in<pr>/at<pr>/on<pr>$ ^el<det><def><f><sg>/the<det><def><f><sg>$ ^estación<n><f><sg>/season<n><sg>/station<n><sg>$ ^de<pr>/of<pr>/from<pr>$ ^tren<n><m><sg>/train<n><sg>$^.<sent>/.<sent>$
Given the output of the bilingual dictionary, this sentence could end up translated as "I'm in the train season". Given the surrounding context, however, it is possilbe to decide the correct translation alternatives.
There are two main scenarios when lexical ambiguity may occur:
- There are two words in Language A that happen to have the same lemma and POS tags
- (kir) жаш → (eng) age; tear (eye water)
- (kir) ат → (eng) horse; name
- (eng) right → (kir) оң (side); туура (correct)
- (eng) lie → (kir) жат (lie down); калп айт (state a falsehood)
- (eng) bat → (kir) жарганат (animal); таякча (stick for hitting a ball)
- (eng) bank → (kir) банк (financial institution); жээк (shore)
- There is a semantic distinction in Language B that is not expressed in Language A
- (kir) кол → (eng) hand; arm
- (kir) күн → (eng) sun; day
- (kir) бир → (eng) one; a/an
- (kir) кара → (eng) look at; watch
- (eng) paper → (kir) кагаз (material); доклад (written work)
- (eng) horse → (kir) ат (for riding, working); жылкы (for herding, eating)
- (eng) sister → (kir) сиңди (younger, of female); карындаш (younger, of male); эже (older)
- (eng) move → (kir) жыл/жылдыр (location iv/tv); кыймылда/кыймылдат (movement iv/tv); көч/көчүр (residence, files iv/tv)
- (eng) fall → (kir) түш (drop from a height / fall down); кула (to the side / fall over); жыгыл (due to loss of footing)
Choosing between the two
Based on the above, which problem does each of the following example represent?
- (eng) tear → (kir) жаш (eye water); айыр (rip)
- (eng) plant → (kir) өсүмдүк (growing thing); отургуз (put in the ground to grow)
- (eng) fall → (kir) күз (season, <n>); күзгү (relating to the season, <adj>); [the verbs above]
- (kir) күзгү → (eng) fall (<adj>), mirror
- (kir) өч → (eng) revenge; be extinguished
- (kir) кыл → (eng) strand of hair; string (of instrument); do; make
- (kir) кара → (eng) black; look at; watch
- (kir) жыл → (eng) year; move
Morphological disambiguation
The goal of a morphological disambiguator is to choose the correct analysis based on the surrounding words.
Constraint Grammar (CG) is a formalism that can be used to formalise context-sensitive constraints to select or remove analyses from the list of possible analyses.
The structure of a CG file
At the top you define delimiters. You can then add lists (which are kind of like sets in twol). After that you can have any number of constraints. You can also put lists and constraints in sections. In the original specification for constraint grammar, all the constraints in each section are applied simultaneously, but the sections are applied in order and lists are always global. In the version we use (VISL CG3), the constraints are applied in strict order within the sections.
Constraints
Think of constraints as a series of heuristics to whittle down the number of analyses until you have as little ambiguity as possible.
The most commonly used constraints are to either remove a reading under a certain condition or select a reading under a certain condition. The syntax is e.g., REMOVE <target> [contextual tests] ;.
Targets can be defined as list names—like Nominals—or individual tag names, in parentheses—e.g., (n pl). This target identifies the reading to select or remove.
Contextual tests come together in ()s. All tests must match in order for the constraint to apply. Inside the parentheses, the first definition is a position: 0 means the current cohort, -1 means the previous cohort, etc. The rest of the test is what should be matched. If you want to match any member of the Nominals set in the previous position, it would be (-1 Nominals) You can match tag sequences by adding an extra set of parentheses, e.g. (1 (n pl)) would match a plural noun in the following position. You can match specific baseforms with quotation marks, e.g. (1 ("house" n)) would match any form of the noun "house" in the following position.
Classroom activity: implement "houses this" example (at top of page). Solution to this in-class example.
Useful commands
The file that your disambiguation constraints are defined in is the apertium-xyz.xyz.rlx file.
Getting output before disambiguation
example command:
echo "The motel houses this dog" | apertium -d . eng-morph
example output:
^The/the<det><def>$ ^motel/motel<n><sg>$ ^houses/house<n><pl>/house<v><tv><p3><sg>$ ^this/this<det><dem><sg>/this<prn><dem><sg>$ ^dog/dog<n><sg>$^./.<sent>$
Getting output after disambiguation
example command:
echo "The motel houses this dog" | apertium -d . eng-tagger
example output:
^The<det><def>$ ^motel<n><sg>$ ^house<v><tv><p3><sg>$ ^this<det><dem><sg>$ ^dog<n><sg>$^.<sent>$
Seeing which constraints are doing what
example command:
echo "The motel houses this dog" | apertium -d . eng-disam
example output:
"<The>" "the" det def "<motel>" "motel" n sg "<houses>" "house" v tv pres p3 sg ; "house" n pl REMOVE:26 "<this>" "this" det dem sg ; "this" prn dem sg REMOVE:29 "<dog>" "dog" n sg "<.>" "." sent
Calculating ambiguity
Note: `lt-proc` and `.bin` can be replaced with `hfst-proc` and `.hfst` throughout if there are problems with tokenisation or similar
The ambiguity of a corpus is the average number of analyses provided per token. You can get the number of tokens like this:
cat corpus.txt | apertium-destxt | lt-proc /path/to/xyz.automorf.bin | apertium-retxt | sed 's/$\W*\^/$\n^/g' | wc -l
You can get the total number of analyses in your corpus like this:
cat corpus.txt | apertium-destxt | lt-proc /path/to/xyz.automorf.bin | apertium-retxt | sed 's/$\W*\^/$\n^/g' | cut -f2- -d'/' | sed 's/\//\n/g' | wc -l
You can get the total number of analyses in your corpus after disambiguation like this:
cat corpus.txt | apertium-destxt | lt-proc /path/to/xyz.automorf.bin | apertium-retxt | cg-proc /path/to/xyz.rlx.bin | sed 's/$\W*\^/$\n^/g' | cut -f2- -d'/' | sed 's/\//\n/g' | wc -l
The commands apertium-destxt and apertium-retxt strip and readd (respectively) characters that could be parsed in problematic ways.
Automating it a little further, you can put something like the following in a file and run it as a shell script. Note that the coverage-hfst script already implements this. The first three lines need to be replaced with the path to your corpus, your compiled analyser, and your compiled disambiguator, respectively.
CORPUS=/path/to/xyz.corpus.txt
MORPH=/path/to/xyz.automorf.bin
RLX=/path/to/xyz.rlx.bin # you might need to remove ".bin"
TOKENS=`apertium-destxt ${CORPUS} | lt-proc ${MORPH} | apertium-retxt | sed 's/$\W*\^/$\n^/g' | wc -l`
ANALYSES=`apertium-destxt ${CORPUS} | lt-proc ${MORPH} | apertium-retxt | sed 's/$\W*\^/$\n^/g' | cut -f2- -d'/' | sed 's/\//\n/g' | wc -l`
DISAMB=`apertium-destxt ${CORPUS} | lt-proc ${MORPH} | cg-proc ${RLX} | apertium-retxt | sed 's/$\W*\^/$\n^/g' | cut -f2- -d'/' | sed 's/\//\n/g' | wc -l`
AMBIGPRE=`calc $ANALYSES/$TOKENS`
AMBIGPOST=`calc $DISAMB/$TOKENS`
echo "Ambiguity before disambiguation: ${AMBIGPRE}"
echo "Ambiguity after disambiguation: ${AMBIGPOST}"
It's recommended that you put this script in tests/ and name it disambiguation-test.sh. Don't forget to git add it, git commit, and git push.
Checking what forms are ambiguous in the corpus and transducer
You can get all of the words in the corpus along with their analyses (or lack thereof) like this:
cat corpus.txt | apertium-destxt | lt-proc /path/to/xyz.automorf.bin | apertium-retxt | sed 's/$\W*\^/$\n^/g'
You can get all of the words in the corpus that have more than one analysis like this:
cat corpus.txt | apertium-destxt | lt-proc /path/to/xyz.automorf.bin | apertium-retxt | sed 's/$\W*\^/$\n^/g' | grep '\/.*\/'
You can find all forms in your transducer like this:
hfst-expand -c0 xyz.automorf.hfst
You can count ambiguity in your transducer by comparing the number of all forms to the number of unique forms.
Number of all forms:
hfst-expand -c0 xyz.automorf.hfst | wc -l
Number of unique forms:
hfst-expand -c0 xyz.automorf.hfst | cut -f1 -d':' | sort -u | wc -l
If the number's different you'll want to compare the lists:
hfst-expand -c0 xyz.automorf.hfst | cut -f1 -d':' | sort > /tmp/totalforms hfst-expand -c0 xyz.automorf.hfst | cut -f1 -d':' | sort -u > /tmp/uniqforms diff /tmp/totalforms /tmp/uniqforms
Then you'll want to see what the multiple analyses are of any forms that show up there.
More resources
- Constraint Grammar on the Apertium wiki
- Apertium and Constraint Grammar on the Apertium wiki
- Constraint Grammar manual
Lexical selection
Lexical selection rules are in the apertium-xyz-abc.xyz-abc.lrx file in your language pair. You can find information about and examples of the formalism on the Apertium wiki: constraint-based lexical selection module, how to get started with lexical selection rules.
In short, you use the surrounding words (with morphological analyses) as context to decide which translation is the correct one.
The structure of lrx files
An lrx file is written in xml. It consists of a list of rules surrounded by <rules>...</rules>. Each <rule>...<rule> block contains some combination of <match><select ... /></match> blocks and <match .../> blocks. <match ...> tags can contain attributes like lemma="" and tags="". Multiple <match .../> blocks may be included within <or>...</or> so that multiple rules don't have to be written in cases of multiple matching criteria.
Besides the additional documentation linked to above, countless examples can be found in the Apertium codebase, and some examples from class will also be posted.
Here is an example to try in class:
- Көзүнөн жаш акты.
- Анын жашын билбейм.
Additional guidelines
You may wish to make a "default" translation and then some more specific rules. Because of how lrx-proc works, the less specific rule (the "default") may override any more specific rules.
Currently, the best way to get the less specific rule to be the default seems to be with weights—i.e., give a lower weight to the rule you wish to be default than to the specific ones (e.g., by just giving a weight above 1 to the specific rule).
More resources
The assignment
This assignment is generally due at the end of the day on Friday of the 10th week of class (this semester, 23:59, Friday, April 3rd, 2026).
The assignment is to identify two or three instances of ambiguity and write code to resolve them.
- You only need to identify two instances of ambiguity if one requires morphological disambiguation to solve and the other requires lexical selection
- If you only identify examples for morphological disambiguation or lexical selection, then you will need three instances of ambiguity.
Prepping
- If you're still working on your generator, finish that first.
- Commit and push your transducer at this point, and then add a tag "generator":
-
git tag generator - Then push the tag to the repo:
git push origin generator
-
- If you're still working on lexical transfer, finish that too.
- Commit and push your language pair at this point, and then add a tag "lexical-transfer":
-
git tag lexical-transfer - Then push the tag to the repo:
git push origin lexical-transfer
-
Initial Documentation
- Make a new page on the wiki,
Language1_and_Language2/Ambiguity resolution, linking to it from the main page for your language pair, and adding it to the category Category:Sp26_AmbiguityResolution and the categories for the two languages. - Add a section
xyzfor your language andxyz → abcfor your language pair. - In the
xyzsection, document your initial evaluation of ambiguity over two corpora in a bullet point (or similar) with this name- Find the "xyz-tagger" section of your
modes.xmlfile. Changecg-proc -wtocg-proc -1 -n -w(but not in the "xyz-disam" section!). Recompile, commit, push. - Add some of the most common stems identified by a coverage test of your parallel sentences corpus (
xyz.sentences.txt) to your transducer. - Measure the level of ambiguity on your parallel sentences corpus and the largest monolingual corpus in your corpus repository. (See above; the
coverage-hfstscript will also return coverage.)
- Find the "xyz-tagger" section of your
Morphological ambiguity
Identifying and documenting
- If ambiguity is 1 (i.e., there is no ambiguity), then find any form in the language that can have more than one analysis. Check a grammar book and dictionary and think creatively. For example, demonstratives are ambiguous in many languages between determiners and pronouns, colour and material terms can often be ambiguous between adjectives and nouns, etc.
- Implement this into your transducer.
- Recompile, and measure level of ambiguity again.
- If ambiguity is greater than 1 (i.e., there are at least some ambiguous forms), then figure out what form is ambiguous. Once you get ambiguity of this sort, update your initial evaluation of ambiguity to reflect this.
- Find two sentences for two possible readings of any ambiguous term. Make sure all the words are in your transducer with correct analyses. Put them in the
xyzsection on the wiki page, in a subsection called "Identified ambiguity" (or similar), highlighting the correct reading of the ambiguous form in each sentence. For example, if the word was "this", examples might be:-
^This/this<det><dem><sg>/this<prn><dem><sg>$ ^is/be<v><iv><pres><p3><sg>$ ^a/a<det><ind>$ ^dog/dog<n><sg>$^./.<sent>$ -
^I/I<prn><pers><p1><sg><sbj>$ ^saw/see<v><tv><past>$ ^this/this<det><dem><sg>/this<prn><dem><sg>$ ^dog/dog<n><sg>$^./.<sent>$ - Note that it's best to use real sentences, e.g. from your corpus (or any of the sources that went into the corpus). If you absolutely must, you may create examples, but make note of this!
-
Implementing
- Once you have identified examples of ambiguity, come up with prose forms of the constraints you'll need to write to get the correct form in each of the two sentences.
- Be as linguistically sound as you can, so that the constraints will apply in the broadest possible range of scenarios.
- Put the prose forms of the constraints as comments in your code.
- Using them as guidance, implement CG versions of the constraints.
- Be sure to define any sets/lists you might need.
Finish up
- Once you get your disambiguator working as expected, commit your code
- Also, add a link to the git repository on in the
xyzsection of the wiki page, and vice versa (i.e., a link to the wiki page as a comment in the.rlxfile in the git repo) - Measure ambiguity again and add that number to the wiki page under something like "final evaluation of ambiguity".
Lexical selection
- Find and document at least one case of a one-to-many mapping for translation.
- First check any dictionary resources you have for words with multiple translations. Things like lacking a hand/arm or sun/day or moon/month distinction in the source language but having it in the target language are potentially good places to start.
- If you're translating to English and are having trouble coming up with examples for this assignment, consider words in English that are used in lexically specific ways, like in/on/at (e.g., one rides in a car but one rides on a bus) or watch/look at or say/tell.
- See more examples under the second bullet point above where the need for lexical selection is discussed ("There are two main scenarios when lexical ambiguity may occur").
- For each ambiguous translation you work on, determine some generalisation for how to decide which translation is right, documenting these generalisations on the wiki in the
xyz → abcsection.- It may be that there is no obvious generalisation that can be made for a given lexical selection problem; in this case, choose a default form to select and find some other one-to-many translation that can be solved with lexical selection. You must have at least one [mostly] solvable problem.
- For each translation of the word, add an example sentence (or phrase if necessary—but check with me) where the meaning is unambiguous (minimum: two sentences).
- In the appropriate
lrxfile (for thexyz-abcdirection), set up any necessary default translations, and at least one set of lexical selection rules (at least two rules) for one solvable lexical selection problem. Add the example sentences/phrases in a comment before the relevant rule. - Compile and test that the sentences are translated with the correct stem.
- Commit and push your code!