Added more features

This commit is contained in:
Panupong Pasupat 2017-08-30 18:21:22 -07:00
parent c475bddba2
commit 8a987add98
9 changed files with 222 additions and 10 deletions

12
run
View File

@ -675,7 +675,7 @@ addMode('tables', 'QA on HTML tables', lambda { |e| l(
}),
# Parser
letDefault(:parser, 'floatsize'),
o('beamSize', 100),
o('beamSize', 50),
o('useSizeInsteadOfDepth'),
sel(:parser, {
'floatsize' => l(
@ -728,8 +728,8 @@ addMode('tables', 'QA on HTML tables', lambda { |e| l(
nil),
'editdist-fuzzy' => l(
o('FuzzyMatcher.fuzzyMatcher', 'tables.match.EditDistanceFuzzyMatcher'),
o('fuzzyMatchSubstring'), o('fuzzyMatchMaxEditDistanceRatio', 0.3),
o('alsoReturnUnion'), o('alsoMatchPart'),
o('fuzzyMatchSubstring'), o('fuzzyMatchMaxEditDistanceRatio', 0.15),
o('alsoMatchPart'),
nil),
}),
letDefault(:normalize, 1),
@ -814,10 +814,14 @@ addMode('tables', 'QA on HTML tables', lambda { |e| l(
'some' => l( # Add your own features! (only set up the feature computers)
o('FeatureExtractor.featureComputers', 'tables.features.PhrasePredicateFeatureComputer tables.features.PhraseDenotationFeatureComputer'.split),
nil),
'all' => l( # All features
'all' => l( # All ACL 2015 features
o('FeatureExtractor.featureDomains', 'custom-denotation phrase-predicate phrase-denotation headword-denotation missing-predicate'.split),
o('FeatureExtractor.featureComputers', 'tables.features.PhrasePredicateFeatureComputer tables.features.PhraseDenotationFeatureComputer'.split),
nil),
'more' => l( # All ACL 2015 features + more experimental features
o('FeatureExtractor.featureDomains', 'custom-denotation phrase-predicate phrase-denotation headword-denotation missing-predicate anchored-entity'.split),
o('FeatureExtractor.featureComputers', 'tables.features.PhrasePredicateFeatureComputer tables.features.PhraseDenotationFeatureComputer tables.features.AnchorFeatureComputer'.split),
nil),
'baseline' => l( # For the baseline classifier
o('FeatureExtractor.featureDomains', 'custom-denotation phrase-denotation headword-denotation table-baseline'.split),
o('FeatureExtractor.featureComputers', 'tables.baseline.TableBaselineFeatureComputer tables.features.PhraseDenotationFeatureComputer'.split),

View File

@ -39,6 +39,9 @@ public class FuzzyMatchFn extends SemanticFn {
}
}
public FuzzyMatchFnMode getMode() { return mode; }
public boolean getMatchAny() { return matchAny; }
@Override
public DerivationStream call(Example ex, Callable c) {
return new LazyFuzzyMatchFnDerivs(ex, c, mode, matchAny);

View File

@ -660,6 +660,18 @@ public class TableKnowledgeGraph extends KnowledgeGraph implements FuzzyMatchabl
return answer;
}
/**
* Return the index of the column with the specified ID. Return -1 if not found.
*/
public int getColumnIndex(String nameValueId) {
if (nameValueId.startsWith("!"))
nameValueId = nameValueId.substring(1);
for (int j = 0; j < columns.size(); j++) {
if (columns.get(j).relationNameValue.id.equals(nameValueId)) return j;
}
return -1;
}
// ============================================================
// Test
// ============================================================

View File

@ -0,0 +1,68 @@
package edu.stanford.nlp.sempre.tables.features;
import java.util.*;
import edu.stanford.nlp.sempre.*;
import edu.stanford.nlp.sempre.tables.StringNormalizationUtils;
import edu.stanford.nlp.sempre.tables.TableKnowledgeGraph;
import edu.stanford.nlp.sempre.tables.TableCell;
import edu.stanford.nlp.sempre.tables.TableColumn;
import fig.basic.LogInfo;
public class AnchorFeatureComputer implements FeatureComputer {
@Override
public void extractLocal(Example ex, Derivation deriv) {
if (!(FeatureExtractor.containsDomain("anchored-entity"))) return;
if (!(deriv.rule.sem instanceof FuzzyMatchFn)) return;
FuzzyMatchFn sem = (FuzzyMatchFn) deriv.rule.sem;
if (sem.getMatchAny() || sem.getMode() != FuzzyMatchFn.FuzzyMatchFnMode.ENTITY) return;
String phrase = ((StringValue) ((ValueFormula<?>) deriv.child(0).formula).value).value;
NameValue predicate = (NameValue) ((ValueFormula<?>) deriv.formula).value;
TableKnowledgeGraph graph = (TableKnowledgeGraph) ex.context.graph;
extractMatchingFeatures(graph, deriv, phrase, predicate);
}
private void extractMatchingFeatures(TableKnowledgeGraph graph,
Derivation deriv, String phrase, NameValue predicate) {
LogInfo.logs("%s -> %s", phrase, predicate);
// Type of match
String predicateString = graph.getOriginalString(predicate);
predicateString = StringNormalizationUtils.simpleNormalize(predicateString).toLowerCase();
if (predicateString.equals(phrase)) {
deriv.addFeature("a-e", "exact");
LogInfo.logs("%s %s exact", phrase, predicateString);
} else if (predicateString.startsWith(phrase + " ")) {
deriv.addFeature("a-e", "prefix");
LogInfo.logs("%s %s prefix", phrase, predicateString);
} else if (predicateString.endsWith(" " + phrase)) {
deriv.addFeature("a-e", "suffix");
LogInfo.logs("%s %s suffix", phrase, predicateString);
} else if (predicateString.contains(" " + phrase + " ")){
deriv.addFeature("a-e", "substring");
LogInfo.logs("%s %s substring", phrase, predicateString);
} else {
deriv.addFeature("a-e", "other");
LogInfo.logs("%s %s other", phrase, predicateString);
}
// Does the phrase match other cells?
Set<String> matches = new HashSet<>();
for (TableColumn column : graph.columns) {
for (TableCell cell : column.children) {
String s = StringNormalizationUtils.simpleNormalize(cell.properties.originalString).toLowerCase();
if (s.contains(phrase) && !cell.properties.id.equals(predicate.id)) {
matches.add(s);
}
}
}
LogInfo.logs(">> %s", matches);
if (matches.size() == 0) {
deriv.addFeature("a-e", "unique");
} else if (matches.size() < 3) {
deriv.addFeature("a-e", "multiple;" + matches.size());
} else {
deriv.addFeature("a-e", "multiple;>=3");
}
}
}

View File

@ -0,0 +1,84 @@
package edu.stanford.nlp.sempre.tables.features;
import java.io.*;
import java.util.*;
import edu.stanford.nlp.sempre.Example;
import edu.stanford.nlp.sempre.tables.TableKnowledgeGraph;
import fig.basic.*;
public class ColumnCategoryInfo {
public static class Options {
@Option(gloss = "Read category information from this file")
public String tableCategoryInfo = null;
}
public static Options opts = new Options();
// ============================================================
// Singleton access
// ============================================================
private static ColumnCategoryInfo singleton;
public static ColumnCategoryInfo getSingleton() {
if (opts.tableCategoryInfo == null)
return null;
else if (singleton == null)
singleton = new ColumnCategoryInfo();
return singleton;
}
// ============================================================
// Read data from file
// ============================================================
// tableId -> columnIndex -> list of (category, weight)
protected static Map<String, List<List<Pair<String, Double>>>> allCategoryInfo = null;
private ColumnCategoryInfo() {
LogInfo.begin_track("Loading category information from %s", opts.tableCategoryInfo);
allCategoryInfo = new HashMap<>();
try {
BufferedReader reader = IOUtils.openIn(opts.tableCategoryInfo);
String line;
while ((line = reader.readLine()) != null) {
String[] tokens = line.split("\t");
String tableId = tokens[0];
List<List<Pair<String, Double>>> categoryInfoForTable = allCategoryInfo.get(tableId);
if (categoryInfoForTable == null)
allCategoryInfo.put(tableId, categoryInfoForTable = new ArrayList<>());
int columnIndex = Integer.parseInt(tokens[1]);
// Assume that the columns are ordered
assert categoryInfoForTable.size() == columnIndex;
// Read the category-weight pairs
List<Pair<String, Double>> categories = new ArrayList<>();
for (int i = 2; i < tokens.length; i++) {
String[] pair = tokens[i].split(":");
categories.add(new Pair<>(pair[0], Double.parseDouble(pair[1])));
}
categoryInfoForTable.add(categories);
}
reader.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
LogInfo.end_track();
}
// ============================================================
// Getters
// ============================================================
public List<Pair<String, Double>> get(String tableId, int columnIndex) {
return allCategoryInfo.get(tableId).get(columnIndex);
}
public List<Pair<String, Double>> get(Example ex, String columnId) {
TableKnowledgeGraph graph = (TableKnowledgeGraph) ex.context.graph;
String tableId = graph.filename;
int columnIndex = graph.getColumnIndex(columnId);
if (columnIndex == -1) return null;
return allCategoryInfo.get(tableId).get(columnIndex);
}
}

View File

@ -113,6 +113,7 @@ public class PhraseDenotationFeatureComputer implements FeatureComputer {
LogInfo.logs("%s %s %s", deriv.value, deriv.type, denotationTypes);
for (String denotationType : denotationTypes) {
for (PhraseInfo phraseInfo : phraseInfos) {
if (PhraseInfo.opts.forbidBorderStopWordInLexicalizedFeatures && phraseInfo.isBorderStopWord) continue;
deriv.addFeature("p-d", phraseInfo.lemmaText + ";" + denotationType);
}
// Check original column text

View File

@ -2,6 +2,7 @@ package edu.stanford.nlp.sempre.tables.features;
import java.util.*;
import java.util.concurrent.ExecutionException;
import java.util.regex.Pattern;
import com.google.common.cache.*;
@ -23,6 +24,8 @@ public class PhraseInfo {
public int maxPhraseLength = 3;
@Option(gloss = "Fuzzy match predicates")
public boolean computeFuzzyMatchPredicates = false;
@Option(gloss = "Do not produce lexicalized features if the phrase begins or ends with a stop word")
public boolean forbidBorderStopWordInLexicalizedFeatures = false;
}
public static Options opts = new Options();
@ -35,6 +38,7 @@ public class PhraseInfo {
public final List<String> nerTags;
public final String canonicalPosSeq;
public final List<String> fuzzyMatchedPredicates;
public final boolean isBorderStopWord; // true if the first or last word is a stop word
public PhraseInfo(Example ex, int start, int end) {
this.start = start;
@ -49,6 +53,7 @@ public class PhraseInfo {
lemmaText = languageInfo.lemmaPhrase(start, end).toLowerCase();
canonicalPosSeq = languageInfo.canonicalPosSeq(start, end);
fuzzyMatchedPredicates = opts.computeFuzzyMatchPredicates ? getFuzzyMatchedPredicates(ex.context) : null;
isBorderStopWord = isStopWord(languageInfo.lemmaTokens.get(start)) || isStopWord(languageInfo.lemmaTokens.get(end - 1));
}
private List<String> getFuzzyMatchedPredicates(ContextValue context) {
@ -71,6 +76,17 @@ public class PhraseInfo {
return matchedPredicates;
}
static final Pattern ALL_PUNCT = Pattern.compile("^[^A-Za-z0-9]*$");
static final Set<String> STOP_WORDS = new HashSet<>(Arrays.asList(
"a", "an", "the", "be", "of", "in", "on", "do"
));
static boolean isStopWord(String x) {
if (ALL_PUNCT.matcher(x).matches()) return true;
if (STOP_WORDS.contains(x)) return true;
return false;
}
@Override
public String toString() {
return "\"" + text + "\"";

View File

@ -35,6 +35,11 @@ public class PhrasePredicateFeatureComputer implements FeatureComputer {
public boolean lexicalizedPhrasePredicate = true;
@Option(gloss = "Maximum ngram length for lexicalize all pair features")
public int maxNforLexicalizeAllPairs = Integer.MAX_VALUE;
@Option(gloss = "phrase-category: Weight threshold")
public double phraseCategoryWeightThreshold = 0.8;
@Option(gloss = "phrase-category: Use binary features instead of continuous ones")
public boolean phraseCategoryBinary = true;
}
public static Options opts = new Options();
@ -47,7 +52,8 @@ public class PhrasePredicateFeatureComputer implements FeatureComputer {
@Override
public void extractLocal(Example ex, Derivation deriv) {
if (!(FeatureExtractor.containsDomain("phrase-predicate")
|| FeatureExtractor.containsDomain("missing-predicate"))) return;
|| FeatureExtractor.containsDomain("phrase-formula")
|| FeatureExtractor.containsDomain("phrase-category"))) 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);
@ -58,7 +64,8 @@ public class PhrasePredicateFeatureComputer implements FeatureComputer {
LogInfo.logs("Derivation: %s", deriv);
LogInfo.logs("Predicates: %s", predicateInfos);
}
if (FeatureExtractor.containsDomain("phrase-predicate")) {
if (FeatureExtractor.containsDomain("phrase-predicate")
|| FeatureExtractor.containsDomain("phrase-category")) {
if (opts.defineOnPartialDerivs) {
deriv.getTempState().put("p-p", new ArrayList<>(predicateInfos));
// Subtract predicates from children
@ -109,7 +116,7 @@ public class PhrasePredicateFeatureComputer implements FeatureComputer {
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 (FeatureExtractor.containsDomain("phrase-predicate") && opts.unlexicalizedPhrasePredicate) {
if (phraseString.equals(predicateString)) {
defineFeatures(ex, deriv, phraseInfo, predicateInfo, phraseType + "=" + predicateType,
phraseString, predicateString, factor);
@ -132,10 +139,27 @@ public class PhrasePredicateFeatureComputer implements FeatureComputer {
}
}
}
if (opts.lexicalizedPhrasePredicate && phraseInfo.end - phraseInfo.start <= maxNforLexicalizeAllPairs) {
if (FeatureExtractor.containsDomain("phrase-predicate") && opts.lexicalizedPhrasePredicate
&& phraseInfo.end - phraseInfo.start <= maxNforLexicalizeAllPairs
&& (!PhraseInfo.opts.forbidBorderStopWordInLexicalizedFeatures || !phraseInfo.isBorderStopWord)) {
deriv.addFeature("p-p",
phraseType + phraseString + ";" + predicateType + predicateString, factor);
}
if (FeatureExtractor.containsDomain("phrase-category") && predicateInfo.type == PredicateType.BINARY
&& (!PhraseInfo.opts.forbidBorderStopWordInLexicalizedFeatures || !phraseInfo.isBorderStopWord)) {
ColumnCategoryInfo catInfo = ColumnCategoryInfo.getSingleton();
List<Pair<String, Double>> categories = catInfo.get(ex, predicateInfo.predicate);
if (categories != null) {
for (Pair<String, Double> pair : categories) {
if (pair.getSecond() >= opts.phraseCategoryWeightThreshold) {
if (opts.phraseCategoryBinary)
deriv.addFeature("p-c", phraseType + phraseString + ";" + pair.getFirst());
else
deriv.addFeature("p-c", phraseType + phraseString + ";" + pair.getFirst(), pair.getSecond());
}
}
}
}
}
private void defineFeatures(Example ex, Derivation deriv, PhraseInfo phraseInfo, PredicateInfo predicateInfo,

View File

@ -238,7 +238,7 @@ public final class DenotationUtils {
*/
public static UnaryDenotation superlativeUnary(int rank, int count, List<Pair<Value, Value>> pairs,
SuperlativeFormula.Mode mode, TypeProcessor processor) {
if (rank <= 0 || count <= 0)
if (rank <= 0 || count <= 0 || rank >= 1000000 || count >= 100000)
LogInfo.fails("Invalid superlative (rank = %d, count = %d)", rank, count);
if (pairs.isEmpty()) {
if (LambdaDCSExecutor.opts.superlativesFailOnEmptyLists)
@ -269,7 +269,7 @@ public final class DenotationUtils {
default: throw new LambdaDCSException(Type.invalidFormula, "Unknown superlative mode: %s", mode);
}
int from = Math.min(rank - 1, indices.size()),
to = Math.min(Math.max(from, from + count), indices.size());
to = Math.min(from + count, indices.size());
for (int index : indices.subList(from, to))
answer.add(pairs.get(index).getSecond());
}