package edu.stanford.nlp.sempre.tables.features; import java.io.*; import java.util.*; import edu.stanford.nlp.sempre.*; import fig.basic.*; /** * Compute vector space paraphrase features as in the paraphrase paper (Berant and Liang, 2014). * * The features are computed on all (phrase n-gram, predicate n-gram) pairs. * * @author ppasupat */ public class WordEmbeddingFeatureComputer implements FeatureComputer { public static class Options { @Option(gloss = "Verbosity") public int verbosity = 0; @Option(gloss = "Path to file containing word vectors, one per line") public String wordVectorFile; @Option(gloss = "Vector dimension") public int vecCapacity = 50; } public static Options opts = new Options(); private Map<String, double[]> wordVectors; public WordEmbeddingFeatureComputer() { wordVectors = new HashMap<>(); LogInfo.begin_track("Loading word embedding files ..."); try (BufferedReader reader = IOUtils.openInHard(opts.wordVectorFile)) { String header = null, line; while ((line = reader.readLine()) != null) { String[] tokens = line.split("\\s+"); // Some word embedding files have a header which includes the number of // words and the number of dimensions. Ignore this. if (header == null && tokens.length == 2) { header = line; continue; } if (tokens.length - 1 != opts.vecCapacity) throw new RuntimeException("Expected " + opts.vecCapacity + " tokens, but got " + (tokens.length - 1) + ": " + line); double[] vector = new double[opts.vecCapacity]; for (int i = 1; i < tokens.length; ++i) vector[i - 1] = Double.parseDouble(tokens[i]); wordVectors.put(tokens[0], vector); } } catch (IOException e) { e.printStackTrace(); LogInfo.fail(e); } LogInfo.logs("%d words loaded (%d dimensions)", wordVectors.size(), opts.vecCapacity); LogInfo.end_track(); } @Override public void extractLocal(Example ex, Derivation deriv) { if (!(FeatureExtractor.containsDomain("word-embedding"))) return; if (!deriv.isRoot(ex.numTokens())) return; // Utterance double[] utteranceVector = new double[opts.vecCapacity]; int numUtteranceTokens = 0; for (String token : ex.languageInfo.tokens) { double[] tokenVector = wordVectors.get(token); if (tokenVector == null) { if (opts.verbosity >= 2) LogInfo.logs("Utterance word not found: %s", token); continue; } ListUtils.addMut(utteranceVector, tokenVector); numUtteranceTokens++; } if (numUtteranceTokens == 0) return; ListUtils.multMut(utteranceVector, 1.0 / numUtteranceTokens); // Predicates double[] predicateVector = new double[opts.vecCapacity]; int numPredicateTokens = 0; for (PredicateInfo predicateInfo : PredicateInfo.getPredicateInfos(ex, deriv)) { if (predicateInfo.originalString == null) continue; String s = predicateInfo.originalString.toLowerCase().replaceAll("[^a-z0-9]+", " ").trim(); if (opts.verbosity >= 2) LogInfo.logs("|%s| ==> |%s|", predicateInfo.originalString, s); if (s.isEmpty()) continue; for (String token : s.split(" ")) { double[] tokenVector = wordVectors.get(token); if (tokenVector == null) { if (opts.verbosity >= 2) LogInfo.logs("Predicate word not found: %s", token); continue; } ListUtils.addMut(predicateVector, tokenVector); numPredicateTokens++; } } if (numPredicateTokens == 0) return; ListUtils.multMut(predicateVector, 1.0 / numPredicateTokens); // Outer product for (int i = 0; i < opts.vecCapacity; i++) { for (int j = 0; j < opts.vecCapacity; j++) { deriv.addFeature("w-e", "" + i + "," + j, utteranceVector[i] * predicateVector[j]); } } } }