сопоставление большого количества разных предложений (с использованием синтаксического анализа регулярных выражений) - PullRequest
3 голосов
/ 16 июня 2019

Я хочу использовать regexps для построения классификатора текстовых предложений (для обработки естественного языка чатбота).

У меня очень большое число (например, >> 100 ) различных типов текстовых предложений для соответствия шаблонам регулярных выражений.

Когда предложение соответствует регулярному выражению (скажем, намерение ), активирует определенное действие (обработчик функции).

Я предварительно настроил конкретноерегулярные выражения, чтобы соответствовать любому другому набору предложений, например:

     // I have a long list of regexps (also many regexp for a many intents)

    const regexps = [ 
      /from (?<fromCity>.+)/,  // ---> actionOne()
      /to (?<toCity>.+)/,      // ---> actionTwo()
      /.../,                   // ---> anotherAction()
      /.../                   // ---> yetAnotherAction()
    ]

   // I have a long list of actions (function handlers)

   const actions = [
     actionOne(),
     actionTwo(),
     ...,
     ...
   ]      

Как я могу построить самый быстрый (multi-regexp) классификатор (в Javascript)?

Мое текущее быстрое и грязное решение состоит в том, чтобы просто проверять каждое регулярное выражение последовательно:

    // at run time        
    ...
    sentence = 'from Genova'
    ...

    if (sentence.match(/from (?<fromCity>.+)/)
      actionOne()

    else if(sentence.match(/to (?<toCity>.+)/)
      actionTwo()

    else if ...
    else if ...
    else 
      fallback()

Приведенный выше последовательность if-then не очень масштабируема и, прежде всего, медленна с точки зрения производительности(даже если может помочь наиболее часто используемая сортировка регулярных выражений).

Альтернативный подход для улучшения производительности может быть следующим: создать одиночное (большое) регулярное выражение, составленное из названной группы (по одному на каждое совпадение-регулярное выражение) чередование ?

Как в минимальном примере:

   const regexp = /(?<one>from (?<toCity>.+))|(?<two>to (?<toCity>.+))/

Поэтому я создаю классификатор регулярных выражений просто с помощью (пожалуйста, используйте код ниже как псевдокод javascript):

    // at build time

    // I collect all possible regexps, each one as a named group
    const intents = [
      '(?<one>from (?<fromCity>.+))',
      '(?<two>to (?<toCity>.+))',
      '...',
      '...'
    ]

    const classifier = new RegExp(intents.join('|'))

    // collection of functions handlers, one for each regexp
    const Actions = {
     'one': 'actionOne',
     'two': 'actionTwo',
     ...,
     ...
    }

    // at run time

    const match = sentence.match(classifier)

    // if match, call corresponding function handler
    // match.groups contains the matching named group
    const action = Actions[match.groups]

    if ( action )
      action()
    else
      fallback() // no match

Имеет ли это смысл?Любое предложение для лучшего подхода?

1 Ответ

2 голосов
/ 04 июля 2019

Скорее всего, это зависит от нескольких вещей, таких как каждый отдельный RegExp (например, сколько групп захвата), фактический размер списка и длина вашего ввода.

Но при тестировании на очень большомколичество RegExp (10000 простых), любой вариант большого объединенного RegExp намного медленнее, чем просто выполнение отдельных по одному. JSPerf

Учитывая эту информацию и тот факт, что она в целом упрощает код, я бы предложил не использовать такой большой подход RegExp.

Чтобы сделать вещи болеелегко поддерживаемый, я бы предложил хранить каждый триггер и его действие в одном месте, например, массив объектов.Это также позволит вам добавить больше к этим объектам позже, если необходимо (например, назвать намерение):

const intents = [
    { regexp: /from (?<fromCity>.+)/, action: fromCity },
    { regexp: /to (?<toCity>.+)/, action: toCity },
    { regexp: /.../, action: anotherAction },
];

// We use find to stop as soon as we've got a result
let result = intents.find(intent => {
    let match = sentence.match(intent.regexp);
    if (match) {
        // You can include a default action in case the action is not specified in the intent object
        // Decide what you send to your action function here
        (match.action || defaultAction)(match, sentence, intent);
    }
    return match;
});
if (!result) {
    fallback();
}
...