package edu.stanford.nlp.sempre.tables.features; import java.util.*; import edu.stanford.nlp.sempre.*; import edu.stanford.nlp.sempre.tables.features.PredicateInfo.PredicateType; import fig.basic.*; /** * Extract features based on (phrase, predicate) pairs. * * - |phrase| is an n-gram from the utterance (usually n = 1) * - |predicate| is a predicate (LispTree leaf) from the formula * Example: fb:cell_name.barack_obama, fb:row.row.name, argmax * * Properties of phrases: POS tags, length, word shapes, ... * Properties of predicates: category (entity / binary / keyword), ... * Properties of alignment: exact match, prefix match, suffix match, string contains, ... * * @author ppasupat */ public class PhrasePredicateFeatureComputer implements FeatureComputer { public static class Options { @Option(gloss = "Verbosity") public int verbose = 0; @Option(gloss = "Define features on partial derivations as well") public boolean defineOnPartialDerivs = true; @Option(gloss = "Also define features on prefix and suffix matches") public boolean usePrefixSuffixMatch = true; @Option(gloss = "Also define features with POS tags") public boolean usePosFeatures = true; @Option(gloss = "Define unlexicalized phrase-predicate features") public boolean unlexicalizedPhrasePredicate = true; @Option(gloss = "Define lexicalized phrase-predicate features") public boolean lexicalizedPhrasePredicate = true; @Option(gloss = "Maximum ngram length for lexicalize all pair features") public int maxNforLexicalizeAllPairs = Integer.MAX_VALUE; } public static Options opts = new Options(); public final int maxNforLexicalizeAllPairs; public PhrasePredicateFeatureComputer() { maxNforLexicalizeAllPairs = Math.min(opts.maxNforLexicalizeAllPairs, PhraseInfo.opts.maxPhraseLength); } @Override public void extractLocal(Example ex, Derivation deriv) { if (!(FeatureExtractor.containsDomain("phrase-predicate") || FeatureExtractor.containsDomain("missing-predicate"))) return; // Only compute features at the root, except when the partial option is set. if (!opts.defineOnPartialDerivs && !deriv.isRoot(ex.numTokens())) return; List<PhraseInfo> phraseInfos = PhraseInfo.getPhraseInfos(ex); List<PredicateInfo> predicateInfos = PredicateInfo.getPredicateInfos(ex, deriv); if (opts.verbose >= 2) { LogInfo.logs("Example: %s", ex.utterance); LogInfo.logs("Phrases: %s", phraseInfos); LogInfo.logs("Derivation: %s", deriv); LogInfo.logs("Predicates: %s", predicateInfos); } if (FeatureExtractor.containsDomain("phrase-predicate")) { if (opts.defineOnPartialDerivs) { deriv.getTempState().put("p-p", new ArrayList<>(predicateInfos)); // Subtract predicates from children Map<PredicateInfo, Integer> predicateInfoCounts = new HashMap<>(); for (PredicateInfo predicateInfo : predicateInfos) MapUtils.incr(predicateInfoCounts, predicateInfo); if (deriv.children != null) { for (Derivation child : deriv.children) { @SuppressWarnings("unchecked") List<PredicateInfo> childPredicateInfos = (List<PredicateInfo>) child.getTempState().get("p-p"); for (PredicateInfo predicateInfo : childPredicateInfos) MapUtils.incr(predicateInfoCounts, predicateInfo, -1); } } for (PhraseInfo phraseInfo : phraseInfos) { for (Map.Entry<PredicateInfo, Integer> entry : predicateInfoCounts.entrySet()) { if (entry.getValue() != 0) extractMatch(ex, deriv, phraseInfo, entry.getKey(), entry.getValue()); } } } else { for (PhraseInfo phraseInfo : phraseInfos) { for (PredicateInfo predicateInfo : predicateInfos) { extractMatch(ex, deriv, phraseInfo, predicateInfo, 1); } } } } if (FeatureExtractor.containsDomain("missing-predicate")) { extractMissing(ex, deriv, phraseInfos, predicateInfos); } } // ============================================================ // Matching // ============================================================ private void extractMatch(Example ex, Derivation deriv, PhraseInfo phraseInfo, PredicateInfo predicateInfo, double factor) { if (predicateInfo.originalString != null) { extractMatch(ex, deriv, phraseInfo, phraseInfo.lemmaText, "", predicateInfo, predicateInfo.originalString, "(o)", factor); } else { extractMatch(ex, deriv, phraseInfo, phraseInfo.lemmaText, "", predicateInfo, predicateInfo.predicate, "(i)", factor); } } private void extractMatch(Example ex, Derivation deriv, PhraseInfo phraseInfo, String phraseString, String phraseType, PredicateInfo predicateInfo, String predicateString, String predicateType, double factor) { if (opts.unlexicalizedPhrasePredicate) { if (phraseString.equals(predicateString)) { defineFeatures(ex, deriv, phraseInfo, predicateInfo, phraseType + "=" + predicateType, phraseString, predicateString, factor); } else if (opts.usePrefixSuffixMatch) { if (predicateString.startsWith(phraseString)) { defineFeatures(ex, deriv, phraseInfo, predicateInfo, "*_" + phraseType + "=" + predicateType, phraseString, predicateString, factor); } if (predicateString.endsWith(phraseString)) { defineFeatures(ex, deriv, phraseInfo, predicateInfo, "_*" + phraseType + "=" + predicateType, phraseString, predicateString, factor); } if (phraseString.startsWith(predicateString)) { defineFeatures(ex, deriv, phraseInfo, predicateInfo, phraseType + "=_*" + predicateType, phraseString, predicateString, factor); } if (phraseString.endsWith(predicateString)) { defineFeatures(ex, deriv, phraseInfo, predicateInfo, phraseType + "=*_" + predicateType, phraseString, predicateString, factor); } } } if (opts.lexicalizedPhrasePredicate && phraseInfo.end - phraseInfo.start <= maxNforLexicalizeAllPairs) { deriv.addFeature("p-p", phraseType + phraseString + ";" + predicateType + predicateString, factor); } } private void defineFeatures(Example ex, Derivation deriv, PhraseInfo phraseInfo, PredicateInfo predicateInfo, String featurePrefix, String phraseString, String predicateString, double factor) { defineFeatures(ex, deriv, phraseInfo, predicateInfo, featurePrefix, factor); if (opts.usePosFeatures) defineFeatures(ex, deriv, phraseInfo, predicateInfo, featurePrefix + "," + phraseInfo.canonicalPosSeq, factor); } private void defineFeatures(Example ex, Derivation deriv, PhraseInfo phraseInfo, PredicateInfo predicateInfo, String featurePrefix, double factor) { if (opts.verbose >= 2) LogInfo.logs("defineFeatures: %s %s %s %s", featurePrefix, phraseInfo, predicateInfo, predicateInfo.type); deriv.addFeature("p-p", featurePrefix, factor); deriv.addFeature("p-p", featurePrefix + "," + predicateInfo.type, factor); } // ============================================================ // Missing predicate features // ============================================================ private void extractMissing(Example ex, Derivation deriv, List<PhraseInfo> phraseInfos, List<PredicateInfo> predicateInfos) { // Only makes sense at the root if (!deriv.isRoot(ex.numTokens())) return; // Get the list of all relevant predicates Set<String> relevantPredicates = new HashSet<>(); for (PredicateInfo predicateInfo : predicateInfos) { if (predicateInfo.type == PredicateType.BINARY || predicateInfo.type == PredicateType.ENTITY) { String predicate = predicateInfo.predicate; if (predicate.charAt(0) == '!') predicate = predicate.substring(1); relevantPredicates.add(predicate); } } // See which predicates are missing! Set<String> missingPredicates = new HashSet<>(); for (PhraseInfo phraseInfo : phraseInfos) { if (phraseInfo.fuzzyMatchedPredicates == null) continue; for (String predicate : phraseInfo.fuzzyMatchedPredicates) { if (!relevantPredicates.contains(predicate)) { missingPredicates.add(predicate); missingPredicates.add("type=" + PredicateInfo.inferType(predicate)); } } } if (opts.verbose >= 2) { LogInfo.logs("have %s", relevantPredicates); LogInfo.logs("missing %s", missingPredicates); } for (String missing : missingPredicates) { deriv.addFeature("m-p", missing); } } }