Запрос меток части речи с Lucene 7 OpenNLP - PullRequest
0 голосов
/ 16 сентября 2018

Для удовольствия и обучения я пытаюсь создать тег POS для Open-LP и Lucene 7.4. Цель состоит в том, чтобы после индексации я действительно мог найти последовательность POS-тегов и найти все предложения, которые соответствуют последовательности. Я уже получил часть индексации, но я застрял в части запроса. Я знаю, что SolR может иметь некоторую функциональность для этого, и я уже проверил код (который, в конце концов, не был настолько самоуверенным). Но моя цель - понять и реализовать в Lucene 7, а не в SolR, так как я хочу быть независимым от любой поисковой системы сверху.

Идея Введите предложение 1: Быстрая коричневая лиса перепрыгнула через ленивых собак. Прикладной токенайзер Lucene OpenNLP приводит к: [The] [быстрый] [коричневый] [лиса] [перепрыгнул] [над] [the] [ленивый] [собаки] [.] Далее, применение тегов POS Lucene OpenNLP приводит к: [DT] [JJ] [JJ] [NN] [VBD] [IN] [DT] [JJ] [NNS] [.]

Введите предложение 2: Дайте это мне, детка! Применение токенайзера Lucene OpenNLP приводит к: [Give] [it] [to] [me] [,] [baby] [!] Затем, применение тегов POS Lucene OpenNLP приводит к: [VB] [PRP] [TO] [PRP] [,] [UH] [.]

Запрос: JJ NN VBD соответствует части предложения 1, поэтому предложение 1 должно быть возвращено. (На данный момент меня интересуют только точные совпадения, т.е. давайте оставим в стороне частичные совпадения, символы подстановки и т. Д.)

Индексация Сначала я создал свой собственный класс com.example.OpenNLPAnalyzer:

public class OpenNLPAnalyzer extends Analyzer {
  protected TokenStreamComponents createComponents(String fieldName) {
    try {

        ResourceLoader resourceLoader = new ClasspathResourceLoader(ClassLoader.getSystemClassLoader());


        TokenizerModel tokenizerModel = OpenNLPOpsFactory.getTokenizerModel("en-token.bin", resourceLoader);
        NLPTokenizerOp tokenizerOp = new NLPTokenizerOp(tokenizerModel);


        SentenceModel sentenceModel = OpenNLPOpsFactory.getSentenceModel("en-sent.bin", resourceLoader);
        NLPSentenceDetectorOp sentenceDetectorOp = new NLPSentenceDetectorOp(sentenceModel);

        Tokenizer source = new OpenNLPTokenizer(
                AttributeFactory.DEFAULT_ATTRIBUTE_FACTORY, sentenceDetectorOp, tokenizerOp);

        POSModel posModel = OpenNLPOpsFactory.getPOSTaggerModel("en-pos-maxent.bin", resourceLoader);
        NLPPOSTaggerOp posTaggerOp = new NLPPOSTaggerOp(posModel);

        // Perhaps we should also use a lower-case filter here?

        TokenFilter posFilter = new OpenNLPPOSFilter(source, posTaggerOp);

        // Very important: Tokens are not indexed, we need a store them as payloads otherwise we cannot search on them
        TypeAsPayloadTokenFilter payloadFilter = new TypeAsPayloadTokenFilter(posFilter);

        return new TokenStreamComponents(source, payloadFilter);
    }
    catch (IOException e) {
        throw new RuntimeException(e.getMessage());
    }              

}

Обратите внимание, что мы используем TypeAsPayloadTokenFilter, обернутый вокруг OpenNLPPOSFilter. Это означает, что наши POS-теги будут проиндексированы как полезные нагрузки, и наш запрос - как бы он ни выглядел - должен будет также выполнять поиск по полезным нагрузкам.

* 1025 Запросы * Вот где я застрял. Я понятия не имею, как запросить полезные нагрузки, и все, что я пытаюсь, не работает. Обратите внимание, что я использую Lucene 7, похоже, что в старых версиях запросы на полезную нагрузку менялись несколько раз. Документация крайне скудная. Даже не ясно, какое имя поля теперь нужно запрашивать - это «слово» или «тип» или что-то еще? Например, я пробовал этот код, который не возвращает результатов поиска:

    // Step 1: Indexing
    final String body = "The quick brown fox jumped over the lazy dogs.";
    Directory index = new RAMDirectory();
    OpenNLPAnalyzer analyzer = new OpenNLPAnalyzer();
    IndexWriterConfig indexWriterConfig = new IndexWriterConfig(analyzer);
    IndexWriter writer = new IndexWriter(index, indexWriterConfig);
    Document document = new Document();
    document.add(new TextField("body", body, Field.Store.YES));
    writer.addDocument(document);
    writer.close();


    // Step 2: Querying
    final int topN = 10;
    DirectoryReader reader = DirectoryReader.open(index);
    IndexSearcher searcher = new IndexSearcher(reader);

    final String fieldName = "body"; // What is the correct field name here? "body", or "type", or "word" or anything else?
    final String queryText = "JJ";
    Term term = new Term(fieldName, queryText);
    SpanQuery match = new SpanTermQuery(term);
    BytesRef pay = new BytesRef("type"); // Don't understand what to put here as an argument
    SpanPayloadCheckQuery query = new SpanPayloadCheckQuery(match, Collections.singletonList(pay));

    System.out.println(query.toString());

    TopDocs topDocs = searcher.search(query, topN);

Любая помощь очень ценится здесь.

1 Ответ

0 голосов
/ 18 сентября 2018

Почему бы вам не использовать TypeAsSynonymFilter вместо TypeAsPayloadTokenFilter и просто сделать обычный запрос.Итак, в вашем анализаторе:

:
TokenFilter posFilter = new OpenNLPPOSFilter(source, posTaggerOp);
TypeAsSynonymFilter typeAsSynonymFilter = new TypeAsSynonymFilter(posFilter);
return new TokenStreamComponents(source, typeAsSynonymFilter);

И сторона индексации:

static Directory index() throws Exception {
  Directory index = new RAMDirectory();
  OpenNLPAnalyzer analyzer = new OpenNLPAnalyzer();
  IndexWriterConfig indexWriterConfig = new IndexWriterConfig(analyzer);
  IndexWriter writer = new IndexWriter(index, indexWriterConfig);
  writer.addDocument(doc("The quick brown fox jumped over the lazy dogs."));
  writer.addDocument(doc("Give it to me, baby!"));
  writer.close();

  return index;
}

static Document doc(String body){
  Document document = new Document();
  document.add(new TextField(FIELD, body, Field.Store.YES));
  return document;
}

И сторона поиска:

static void search(Directory index, String searchPhrase) throws Exception {
  final int topN = 10;
  DirectoryReader reader = DirectoryReader.open(index);
  IndexSearcher searcher = new IndexSearcher(reader);

  QueryParser parser = new QueryParser(FIELD, new WhitespaceAnalyzer());
  Query query = parser.parse(searchPhrase);
  System.out.println(query);

  TopDocs topDocs = searcher.search(query, topN);
  System.out.printf("%s => %d hits\n", searchPhrase, topDocs.totalHits);
  for(ScoreDoc scoreDoc: topDocs.scoreDocs){
    Document doc = searcher.doc(scoreDoc.doc);
    System.out.printf("\t%s\n", doc.get(FIELD));
  }
}

А затем используйте их следующим образом:

public static void main(String[] args) throws Exception {
  Directory index = index();
  search(index, "\"JJ NN VBD\"");    // search the sequence of POS tags
  search(index, "\"brown fox\"");    // search a phrase
  search(index, "\"fox brown\"");    // search a phrase (no hits)
  search(index, "baby");             // search a word
  search(index, "\"TO PRP\"");       // search the sequence of POS tags
}

Результат выглядит так:

body:"JJ NN VBD"
"JJ NN VBD" => 1 hits
    The quick brown fox jumped over the lazy dogs.
body:"brown fox"
"brown fox" => 1 hits
    The quick brown fox jumped over the lazy dogs.
body:"fox brown"
"fox brown" => 0 hits
body:baby
baby => 1 hits
    Give it to me, baby!
body:"TO PRP"
"TO PRP" => 1 hits
    Give it to me, baby!
...