Полное руководство по обучению модели машинного обучения переводу вопросов/инструкций на естественном языке в SQL-запросы с использованием Tensorflow.

Введение

В этом посте мы рассмотрим интересную задачу: преобразование естественного языка в SQL. Академический термин для этого — интерфейс естественного языка для базы данных (NLIDB). Несмотря на то, что NLIDB все еще является областью активных исследований, создание модели для одной простой таблицы на самом деле довольно просто. Мы сделаем это для таблицы сотрудников с 3 столбцами: имя, пол и зарплата (как показано на рисунке 1). К концу этого сообщения в блоге вы узнаете, как перейти от строки ввода на естественном языке, такой как show me the names of the employees whose income is higher than 50000, к выводу SQL-запроса select e.name from employee e where e.salary > 50000.

Обзор

В этом разделе мы объясняем основную идею на высоком уровне.

Ядром этой задачи является проблема машинного перевода. Хотя может показаться заманчивым просто добавить модель машинного перевода последовательности к последовательности, чтобы перейти непосредственно от входного естественного языка к выходному SQL-запросу, на практике это не работает. Основная причина заключается в том, что модель может столкнуться с маркерами вне словаря (OOV) для значений столбца. Хотя модель может научиться до некоторой степени терпеть другие второстепенные неизвестные слова, OOV для значений столбца является фатальным. Представьте, что у нас есть значение зарплаты в приведенном выше примере, отличное от вводного, которое не охватывает обучающий набор данных — 60 000, 70 000, 80 000, что угодно — всегда будет номер зарплаты, которого нет в словаре. То же самое касается столбца имени. Токены OOV будут сопоставлены с символом [UNK] и переданы в модель перевода. Таким образом, у модели нет возможности восстановить точные фактические значения столбцов в выводе SQL.

Типичный способ справиться с этим — запустить необработанный ввод через процесс, называемый связыванием схемы, который идентифицирует и кэширует значения столбца и заменяет их заполнителями, которые модель видела во время обучения. Например, входной пример из введения станет show me the names of the employees whose income is higher than [salary_1] после связывания схемы. Во время обучения модели присваивается вывод метки, такой как select e.name from employee e where e.salary > [salary_1]. Таким образом, модель фактически учится преобразовывать входные данные на естественном языке с заменой значений столбцов в выходные данные SQL с заменой значений столбцов. Последним шагом является просто заполнение заполнителей путем поиска соответствующих значений столбцов, кэшированных на этапе связывания схемы.

Теперь с пониманием высокого уровня, давайте углубимся в конкретные шаги.

Сбор данных

Начнем со сбора данных. Чтобы иметь полный контроль над сквозным процессом, а также сделать его более увлекательным, мы собираемся генерировать обучающие данные с нуля. Ожидаемый результат этого шага генерации данных — список пар предложений на естественном языке с заменой значений столбцов и SQL-запросов с заменой значений столбцов. Хотя вы можете просто создать десять тысяч обучающих пар вручную, это утомительно и может не включать множество лингвистических вариаций, с которыми модель столкнется во время логического вывода.

Вместо этого мы вручную создаем несколько десятков шаблонов обучающих пар, чтобы запустить генерацию данных, а затем пробуем возможные имена столбцов и условия для создания экземпляров обучающих пар. Шаблон может выглядеть следующим образом:

Show me [cols] of employees whose [cond_cols] order by [comp_cols] in descending order
select [cols] from employee where [cond_cols] order by [comp_cols] desc

Затем мы можем выбрать комбинацию столбцов сотрудников (имя, пол, зарплата) для заполнения части [cols], выбрать комбинацию предопределенных условий (например, пол = [gender_1], зарплата › [salary_1] и зарплата ‹ [salary_2]) для заполнения [cond_cols], и выберите комбинацию сопоставимых столбцов (в данном случае только зарплата), чтобы заполнить часть [comp_cols]. Очевидно, нам нужно заранее определить возможное содержимое для [cols], [cond_cols] и [comp_cols] как для предложений на естественном языке, так и для SQL-запросов. Это часто называют определением онтологии предметной области.

Создавая конкретные пары, мы можем легко перейти от десятков тренировочных пар к сотням тренировочных пар. Затем нам нужно увеличить обучающие пары, чтобы включить больше лингвистических вариаций. Мы можем сделать это, заменив фразы в предложениях естественного языка парафразами. По сути, для каждой фразы из одного или нескольких слов в предложении мы создаем новое предложение, превращая его в парафраз. SQL-запрос остается неизменным, поскольку мы фокусируемся на лингвистических вариациях в предложениях на естественном языке. Новое предложение и исходная пара SQL-запросов являются новым дополнением к набору обучающих данных. Мы получаем парафразы из базы данных парафраз. См. этот сообщение в блоге для получения конкретного кода того, как использовать базу данных парафраз для расширения естественного языка. Это позволяет нам перейти от сотен тренировочных пар к тысячам тренировочных пар, которых достаточно для этой задачи.

Естественный язык в модель SQL

Теперь у нас есть тысячи пар естественного языка и SQL-запросов с заменой значений столбцов, и мы можем построить нашу модель перевода. Мы используем модель последовательность к последовательности с механизмами внимания, подробно описанными в этом сообщении в блоге. См. Рисунок 2 для части архитектуры RNN.

Однако у нас есть одно изменение во встроенной части архитектуры. Исходная модель перевода из машинного перевода сообщение в блоге только изучает вложения входных слов с нуля. В этой модели мы по-прежнему собираемся изучать встраивания входных слов, но, кроме того, мы загрузим фиксированный предварительно обученный слой встраивания слов и объединим выходные данные этих двух слоев встраивания для передачи в оставшиеся части модели. . См. рисунок 3 для иллюстрации.

Причина в том, что у нас может не быть достаточных данных для обучения, чтобы представить лингвистическую вариацию ввода на естественном языке. Итак, нам нужен предварительно обученный слой встраивания слов. Но в то же время мы хотим поддерживать собственное встраивание, обученное с нуля, чтобы улавливать нюансы в этой конкретной области. Ниже приведен фрагмент кода для загрузки фиксированного предварительно обученного слоя встраивания слов.

Как только мы это получим, мы будем использовать аналогичный код в машинном переводе сообщение в блоге для машинного перевода. Единственное отличие состоит в том, что, как упоминалось выше, мы будем объединять выходные данные фиксированного слоя встраивания со слоем встраивания естественного языка и передавать их рекуррентному слою нейронной сети (вместо того, чтобы просто получать выходные данные из слоя встраивания естественного языка). Полный код модели см. в следующем фрагменте кода. Чтобы узнать, как создать параметры инициализации модели nl_text_processor, sql_text_processor и код цикла обучения, не стесняйтесь обращаться к сообщению в блоге машинного перевода.

Мы обучаем модель в течение 10 эпох и пробуем ее с помощью нескольких вводов на естественном языке:

Input: show me the name of the employee whose salary is higher than [salary_1]
Output: select e.name from employee e where e.salary > [salary_1]
Input: get me employee with name [name_1]
Output: select * from employee e where e.name like '%[name_1]%'
Input: which employee has the highest salary
Output: select * , e.salary from employee e order by e.salary desc limit 1

Модель связывания схемы

Теперь осталось только связать значения столбцов. Целью является предложение на естественном языке, такое как show me the names of the employees whose income is higher than 50000, привязка схемы должна выводить show me the names of the employees whose income is higher than [salary_1], которое может быть использовано моделью перевода. В то же время мы должны помнить, что [salary_1] равно 50000. Когда у нас будет вывод SQL из модели трансляции select e.name from employee e where e.salary > [salary_1], мы запишем фактическое значение столбца 50000, чтобы создать окончательный исполняемый SQL-запрос select e.name from employee e where e.salary > 50000.

Есть много способов связать необработанный ввод со схемой. Некоторые используют синтаксическое обнаружение на основе правил для определения потенциальных значений столбца. Другие сканируют диапазоны входных данных разного размера и определяют, насколько они похожи на значение столбца. Сходство можно измерить, вычислив евклидово расстояние между вложениями слов диапазона и совокупными вложениями столбца. Совокупное вложение столбца может быть предварительно вычислено путем суммирования всех вложений слов репрезентативной выборки значений столбца.

Здесь мы собираемся изучить новый подход. Мы будем использовать тот же метод машинного перевода, чтобы предсказать/сгенерировать выходную маску для любого заданного ввода. Давайте сопоставим предопределенные заполнители с целым числом, а остальные значения, не относящиеся к столбцам, — с нулем.

[name_1]: 1, [salary_1]: 2, [salary_2]: 3, [gender_1]: 4, others: 0

Затем давайте создадим схему, связывающую обучающие данные, заменив заполнители из обучающих пар перевода случайными значениями. См. следующий фрагмент кода для более подробной информации:

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

All employees with name John Joe and salary 50000 and gender male
 0      0       0    0    1   1   0     0     2    0     0     4

Мы тренируем его на 10 эпох и пробуем несколько входных примеров.

# [name_1]: 1, [salary_1]: 2, [salary_2]: 3, [gender_1]: 4, others: 0
Input: name of the employee whose salary is higher than 50000
Output: 0    0  0     0       0      0    0    0     0    2
Input: get me employee whose name is John Joe
Output: 0   0     0      0     0   0   1   1
Input: show me employees whose salary is between 450000 and 650000
Output: 0    0     0       0      0    0    0       2    0     3

Теперь нам просто нужно использовать вывод в качестве маски, чтобы отметить заполнители в необработанном вводе. Обратите внимание, что в случае значений из нескольких слов, таких как имя, нам нужно свернуть их в один заполнитель.

На данный момент у нас есть все шаги, необходимые для перехода от ввода на естественном языке к выводу исполняемого SQL.

Рекомендуемые документы

  • Сквозной нейронный интерфейс на естественном языке для баз данных (ссылка).
  • Нейронные подходы к естественно-языковым интерфейсам баз данных: обзор (ссылка).
  • Zero-shot Text-to-SQL Learning with Auxiliary Task (ссылка).
  • Обучаемый интерфейс естественного языка для баз данных (ссылка).
  • На пути к сложному преобразованию текста в SQL в междоменной базе данных с промежуточным представлением (ссылка).
  • PPDB: База данных Paraphrase (ссылка).
  • Изучение нейросемантического парсера на основе отзывов пользователей (ссылка).
  • Сравнительный обзор современных интерфейсов на естественном языке для баз данных (ссылка).