package edu.stanford.nlp.sempre.tables.alignment; import java.io.PrintWriter; import java.util.*; import edu.stanford.nlp.sempre.Formula; import edu.stanford.nlp.sempre.Learner; import edu.stanford.nlp.sempre.tables.alignment.AlignerMain.NullWordHandling; import edu.stanford.nlp.sempre.tables.alignment.BitextData.BitextDataGroup; import edu.stanford.nlp.sempre.tables.alignment.BitextData.BitextDatum; import fig.basic.*; import fig.exec.Execution; /** * IBM Model 1. * * Each target word independently aligns to a source word (or NULL). * * @author ppasupat */ public abstract class IBM1AlignmentComputer implements AlignmentComputer { public static class Options { @Option(gloss = "When computing p(t|s), use the sum over all alignments instead of argmax") public boolean marginalizeAlignments = false; } public static Options opts = new Options(); // genProbs[w_s, w_t] // = P_theta(w_t|w_s) = probability of aligning target word w_t to source word w_s protected DoubleMap genProbs; protected abstract Collection<String> getAllSources(BitextData bitextData); protected abstract Collection<String> getAllTargets(BitextData bitextData); protected abstract List<String> getSource(BitextDatum datum); protected abstract List<String> getTarget(BitextDatum datum); protected double getNullProb(String target, int sourcesSize) { switch (AlignerMain.opts.nullWordHandling) { case FIXED: return AlignerMain.opts.fixedNullWordProb; case UNIFORM: return 1.0 / sourcesSize; case TRAINED: return genProbs.get(null, target); default: throw new RuntimeException("Unknown nullWordHandling " + AlignerMain.opts.nullWordHandling); } } @Override public void align(BitextData bitextData) { LogInfo.begin_track("IBM1AlignmentComputer.align()"); NullWordHandling nullWordHandling = AlignerMain.opts.nullWordHandling; // Initialize LogInfo.begin_track("Initialize ..."); Set<String> allSources = new HashSet<>(getAllSources(bitextData)); Set<String> allTargets = new HashSet<>(getAllTargets(bitextData)); allSources.add(null); genProbs = new DoubleMap.ConstantDoubleMap(1.0 / allTargets.size()); LogInfo.end_track(); // Run EM for (int iter = 0; iter < Learner.opts.maxTrainIters; iter++) { LogInfo.begin_track("EM Iteration %d", iter); Execution.putOutput("iter", iter); // parameters --> alignments // alignmentSoftCounts[w_s, w_t] // = soft count of the number of times w_t aligns to w_s DoubleMap alignmentSoftCounts = new DoubleMap(); // sourceToMarginalSoftCounts[w_s] // sum of alignmentSoftCounts[w_s, w_t] over all w_t Map<String, Double> sourceToMarginalSoftCounts = new HashMap<>(); for (int groupIndex = 0; groupIndex < bitextData.bitextDataGroups.size(); groupIndex++) { BitextDataGroup group = bitextData.bitextDataGroups.get(groupIndex); LogInfo.logs("Example %s (%d): %s", group.id, groupIndex, group.tokens); Execution.putOutput("example", groupIndex); double weight = 1.0 / group.bitextDatums.size(); for (BitextDatum datum : group.bitextDatums) { List<String> sources = new ArrayList<>(getSource(datum)); // Add a null word in front sources.add(0, null); List<String> targets = getTarget(datum); double[] probs = new double[sources.size()]; for (String target : targets) { double normalizer = 1e-10; for (int i = 1; i < sources.size(); i++) { probs[i] = genProbs.get(sources.get(i), target); normalizer += probs[i]; } // Handle NULL source probs[0] = getNullProb(target, sources.size()); normalizer += probs[0]; for (int i = 0; i < sources.size(); i++) { double softCount = probs[i] * weight / normalizer; alignmentSoftCounts.incr(sources.get(i), target, softCount); MapUtils.incr(sourceToMarginalSoftCounts, sources.get(i), softCount); } } } } // alignments --> parameters genProbs = new DoubleMap(); for (Map.Entry<Pair<String, String>, Double> entry : alignmentSoftCounts.entrySet()) { String source = entry.getKey().getFirst(), target = entry.getKey().getSecond(); double alignmentSoftCount = entry.getValue(); if (nullWordHandling != NullWordHandling.TRAINED && source == null) continue; if (alignmentSoftCount <= 0) continue; double prob = alignmentSoftCount / sourceToMarginalSoftCounts.get(source); if (prob > AlignerMain.epsilon) genProbs.put(source, target, prob); } LogInfo.end_track(); } LogInfo.end_track(); } @Override public void dump(PrintWriter out) { for (Map.Entry<Pair<String, String>, Double> entry : genProbs.entrySet()) { double value = entry.getValue(); if (value < AlignerMain.epsilon) continue; out.printf("%s\t%s\t%.6f\n", entry.getKey().getFirst(), entry.getKey().getSecond(), value); } } @Override public List<Pair<Formula, Double>> score(BitextDataGroup group) { List<Pair<Formula, Double>> scores = new ArrayList<>(); for (BitextDatum datum : group.bitextDatums) { scores.add(new Pair<>(datum.formula, score(datum))); } return scores; } @Override public double score(BitextDatum datum) { List<String> sources = new ArrayList<>(getSource(datum)); // Add a null word in front sources.add(0, null); List<String> targets = getTarget(datum); if (AlignerMain.opts.verbose >= 2) LogInfo.begin_track("%s | %s", sources, targets); double score = 0; for (String target : targets) { double alignmentScore = getNullProb(target, sources.size()); if (opts.marginalizeAlignments) { // Sum over all alignments for (int i = 1; i < sources.size(); i++) { String source = sources.get(i); alignmentScore += genProbs.get(source, target); } if (AlignerMain.opts.verbose >= 2) LogInfo.logs("[%10.3f] %20s", alignmentScore, target); } else { // Use only the best alignment String bestSource = null; for (int i = 1; i < sources.size(); i++) { String source = sources.get(i); double challenger = genProbs.get(source, target); if (challenger > alignmentScore) { alignmentScore = challenger; bestSource = source; } } if (AlignerMain.opts.verbose >= 2) LogInfo.logs("[%10.3f] %20s : %s", alignmentScore, target, bestSource); } score += Math.log(alignmentScore); } if (AlignerMain.opts.verbose >= 2) LogInfo.end_track(); // Normalize by length score -= targets.size() * Math.log(sources.size()); return score; } } /** * IBM Model 1 that models p(formula|utterance) [x ==> z] */ class IBM1XToZAlignmentComputer extends IBM1AlignmentComputer { @Override protected Collection<String> getAllSources(BitextData bitextData) { return bitextData.allTokens; } @Override protected Collection<String> getAllTargets(BitextData bitextData) { return bitextData.allPredicates; } @Override protected List<String> getSource(BitextDatum datum) { return datum.group.tokens; } @Override protected List<String> getTarget(BitextDatum datum) { return datum.predicates; } } /** * IBM Model 1 that models p(utterance|formula). [z ==> x] */ class IBM1ZToXAlignmentComputer extends IBM1AlignmentComputer { @Override protected Collection<String> getAllSources(BitextData bitextData) { return bitextData.allPredicates; } @Override protected Collection<String> getAllTargets(BitextData bitextData) { return bitextData.allTokens; } @Override protected List<String> getSource(BitextDatum datum) { return datum.predicates; } @Override protected List<String> getTarget(BitextDatum datum) { return datum.group.tokens; } }