Использование моделей GPT для преобразования естественного языка в SQL-запросы

Применение моделей GPT для трансформации естественного языка в SQL-запросы

 

Обработка естественного языка, или NLP, претерпела огромное развитие, и модели GPT находятся на передовой этой революции.

Сегодня модели LLM могут использоваться в широком спектре приложений. 

Чтобы избежать ненужных задач и улучшить рабочий процесс, я начал изучать возможность обучения GPT формулированию SQL-запросов для меня.

И вот тут появилась блестящая идея:

Использование силы моделей GPT в интерпретации естественного языка и преобразовании его в структурированные SQL-запросы.

Возможно ли такое?

Давайте все это узнаем вместе!

Итак, давайте начнем сначала…

 

Концепция “Few Shot Prompting”

 

Некоторые из вас могут уже знакомы с концепцией few shot prompting, в то время как другие могут никогда не слышали о ней раньше.

Итак… Что это такое?

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

Вот почему это называется few shot prompting.

Простыми словами, представляя некоторые примеры ввода пользователя-образцы подсказок-вместе с желаемым выводом LLM, мы можем обучить модель выдавать улучшенный вывод, соответствующий нашим предпочтениям.

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

Давайте разъясним это на примере!

На протяжении этого урока я буду использовать заранее определенную функцию под названием chatgpt_call() для подачи запросов модели GPT. Если вы хотите лучше разобраться в этом, вы можете прочитать следующую статью.

Представьте, что я хочу, чтобы ChatGPT описал термин “оптимизм”. 

Если я просто попрошу GPT описать его, я получу серьезное и скучное описание. 

## Code Blockresponse = chatgpt_call("Научи меня об оптимизме. Будь кратким.")print(response)

 

Соответствующий вывод будет: 

  

Однако представьте, что я хочу получить что-то более поэтичное. Я могу добавить в свою подсказку некоторые дополнительные детали, указав, что я хочу поэтичное определение.

## Code Blockresponse = chatgpt_call("Научи меня об оптимизме. Будь кратким. Попробуйте создать поэтическое определение.")print(response)

 

Однако этот второй вывод выглядит как стихотворение и никак не соответствует моему желаемому выводу.

  

Что можно сделать?

Я могу детализировать подсказку еще больше и продолжать итерировать, пока я не получу хороший результат. Однако это займет много времени.

Вместо этого я могу показать модели, какой вид поэтического описания я предпочитаю, создавая пример и показывая его модели.

## Code Blockprompt = """Ваша задача - отвечать в согласованном стиле с этим стилем. : Научи меня о стойкости.: Стойкость подобна дереву, которое гнется под ветром, но никогда не ломается. Это способность преодолевать трудности и продолжать двигаться вперед.: Научи меня об оптимизме."""response = chatgpt_call(prompt)print(response)

 

И вывод точно соответствует тому, что я искал.

  

Итак… как мы можем применить это к нашему конкретному случаю с SQL-запросами?

 

Использование NLP для генерации SQL-запросов

 

ChatGPT уже способен генерировать SQL-запросы из образцов естественного языка. Мы даже не должны показывать модели какие-либо таблицы, просто формулируйте гипотетическое вычисление, и она сделает это за нас.

## Блок кода
user_input = """Предположим, у меня есть таблицы с продуктами и заказами. Можете ли вы создать одну таблицу, содержащую всю информацию о каждом продукте вместе с тем, сколько раз он был продан?"""
prompt = f"""Исходя из следующей естественноязычной подсказки, сгенерируйте гипотетический запрос, который выполняет требуемую задачу на SQL.{user_input}"""
response = chatgpt_call(prompt)
print(response)

 

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

  

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

Давайте представим, что я работаю с двумя основными таблицами: PRODUCTS и ORDERS

  

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

## Блок кода
user_input = """Какая модель телевизора была продана больше всего в магазине?"""
prompt = f"""Исходя из следующих таблиц SQL, ваша задача - предоставить необходимые SQL-запросы для удовлетворения любого запроса пользователя. Таблицы: <{sql_tables}> Запрос пользователя: ```{user_input}```"""
response = chatgpt_call(prompt)
print(response)

 

Вы можете найти sql_tables в конце этой статьи!

И выходные данные выглядят следующим образом!

  

Однако, мы можем заметить некоторые проблемы в предыдущем выводе.

  1. Вычисление частично неверно, так как оно учитывает только те телевизоры, которые уже были доставлены. А любой оформленный заказ, доставленный или нет, должен считаться продажей.
  2. Запрос не отформатирован так, как мне хотелось бы.

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

 

#1. Исправление некоторых недоразумений модели

 

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

## Примеры few-shot
fewshot_examples = """-------------- ПЕРВЫЙ ПРИМЕР
Пользователь: Какая модель телевизора была продана больше всего в магазине при учете всех оформленных заказов?
Система: Сначала вам нужно объединить таблицы orders и products, отфильтровать только те заказы, которые соответствуют телевизорам, и подсчитать количество заказов, которые были оформлены: SELECT P.product_name AS model_of_tv, COUNT(*) AS total_sold
FROM products AS P
JOIN orders AS O ON P.product_id = O.product_id
WHERE P.product_type = 'Телевизоры'
GROUP BY P.product_name
ORDER BY total_sold DESC
LIMIT 1;

-------------- ВТОРОЙ ПРИМЕР
Пользователь: Какой продукт был продан больше всего и уже доставлен?
Система: Сначала вам нужно объединить таблицы orders и products, подсчитать количество заказов, уже доставленных, и сохранить только первый: SELECT P.product_name AS model_of_tv, COUNT(*) AS total_sold
FROM products AS P
JOIN orders AS O ON P.product_id = O.product_id
WHERE P.order_status = 'Доставлен'
GROUP BY P.product_name
ORDER BY total_sold DESC
LIMIT 1;"""

 

И теперь, если мы снова дадим модели подсказку и включим в нее предыдущие примеры, можно увидеть, что соответствующий запрос будет не только правильным – предыдущий запрос уже работал – но и будет учитывать продажи так, как мы хотим!

## Блок кода
user_input = """Какая модель телевизора была продана больше всего в магазине?"""
prompt = f"""Исходя из следующих SQL-таблиц, ваша задача - предоставить требуемые таблицы SQL, чтобы удовлетворить любой запрос пользователя. Таблицы: <{sql_tables}>. Следуйте этим примерам, чтобы сгенерировать ответ, обратите внимание на структуру запросов и их формат:<{fewshot_examples}>. Запрос пользователя: ```{user_input}```"""
response = chatgpt_call(prompt)
print(response)

Следующий вывод:

Снимок экрана моей записной книжки Jupyter. Prompting GPT.

Теперь, если мы проверим соответствующий запрос…

## Блок кодапysqldf("""SELECT P.product_name AS model_of_tv, COUNT(*) AS total_soldFROM PRODUCTS AS PJOIN ORDERS AS O ON P.product_id = O.product_idWHERE P.product_type = 'TVs'GROUP BY P.product_nameORDER BY total_sold DESCLIMIT 1;""")

Это работает отлично!

Снимок экрана моей записной книжки Jupyter. Prompting GPT.

#2. Форматирование SQL-запросов

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

Если мы вернемся к предыдущим примерам, запросы совсем не имели формата. И мы все знаем, что существуют хорошие практики-вместе с некоторыми личными странностями-которые позволяют нам лучше читать SQL-запросы.

Вот почему мы можем использовать few-shot prompting, чтобы показать модели, как мы хотим формулировать запросы – с нашими хорошими практиками или просто нашими странностями-и обучить модель давать нам наши отформатированные требуемые SQL-запросы.

Итак, теперь я подготовлю такие же примеры, как и раньше, но в соответствии с моими предпочтениями форматирования.

## Блок кодаfewshot_examples = """---- ПРИМЕР 1Пользователь: Какая модель телевизора была продана больше всего в магазине, учитывая все оформленные заказы. Система: Вам сначала нужно объединить таблицы заказов и продуктов, отфильтровать только те заказы, которые соответствуют телевизорам, и посчитать количество выполненных заказов: SELECT        P.product_name AS model_of_tv,        COUNT(*)       AS total_soldFROM products AS PJOIN orders   AS O  ON P.product_id = O.product_id  WHERE P.product_type = 'TVs'GROUP BY P.product_nameORDER BY total_sold DESCLIMIT 1;---- ПРИМЕР 2Пользователь: Какой последний заказ был оформлен?Система: Вам сначала нужно объединить таблицы заказов и продуктов, отфильтровать по последнему оформлению заказа datetime: SELECT       P.product_name AS model_of_tvFROM products AS PJOIN orders AS O   ON P.product_id = O.product_id  WHERE O.order_creation = (SELECT MAX(order_creation) FROM orders)GROUP BY p.product_nameLIMIT 1;"""

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

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

## Блок кодаuser_input = """Какая модель товара наиболее популярна в магазине?"""prompt = f"""Учитывая следующие таблицы SQL, ваша задача - предоставить необходимые таблицы SQL для выполнения любого запроса пользователя.Таблицы: <{sql_tables}>. Следуйте этим примерам, чтобы сгенерировать ответ, обращая внимание как на способ структурирования запросов, так и на их формат:<{fewshot_examples}>Запрос пользователя: ```{user_input}```"""response = chatgpt_call(prompt)print(response)

Как вы можете видеть в следующем выводе, это сработало!

Снимок экрана моей записной книжки Jupyter. Prompting GPT.

#3. Обучение модели для вычисления некоторой конкретной переменной.

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

## Блок кодаuser_input = """Какой товар доставляется дольше всего?"""prompt = f"""Учитывая следующие таблицы SQL, ваша задача - предоставить необходимые таблицы SQL для выполнения любого запроса пользователя.Таблицы: <{sql_tables}>. Следуйте этим примерам, чтобы сгенерировать ответ, обращая внимание как на способ структурирования запросов, так и на их формат:<{fewshot_examples}>Запрос пользователя: ```{user_input}```"""response = chatgpt_call(prompt)print(response)

 

Впрочем, ответ, который мы получаем, далек от правильного.

Снимок экрана моей тетради Jupyter. Подсказка GPT.

Что пошло не так?

GPT-модель пытается вычислить разницу между двумя переменными даты SQL напрямую. Это вычисление несовместимо с большинством версий SQL, вызывая проблемы, особенно для пользователей SQLite.

Как мы можем исправить эту проблему?

Решение прямо перед нами – мы возвращаемся к небольшим фрагментам.

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

Например, пользователи SQLite могут использовать функцию julianday (). Эта функция преобразует любую дату в количество дней, прошедших с начальной эпохи в юлианском календаре.

Это может помочь модели GPT лучше обрабатывать разницу в датах в базе данных SQLite.

  ## Добавление еще одного примераfewshot_examples + = """------ EXAMPLE 4User: Compute the time that it takes to delivery every product?System: You first need to join both orders and products tables, filter only those orders that have been delivered and compute the difference between both order_creation and delivery_date.: SELECT P.product_name AS product_with_longest_delivery, julianday(O.delivery_date) - julianday(O.order_creation) AS TIME_DIFF FROM products AS PJOIN orders AS O ON P.product_id = O.product_idWHERE O.order_status = 'Delivered';""" 

 

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

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

  ## Блок кодаuser_input = """What product is the one that takes longer to deliver?"""prompt = f"""Given the following SQL tables, your job is to provide the required SQL tablesto fulfill any user request.Tables: <{sql_tables}>. Follow those examples the generate the answer, paying attention to boththe way of structuring queries and its format:<{fewshot_examples}>User request: ```{user_input}```"""response = chatgpt_call(prompt)print(response) 

Снимок экрана моей тетради Jupyter. Подсказка GPT.

 

Резюме

 

В заключение, модель GPT – отличный инструмент для преобразования естественного языка в SQL-запросы.

Однако она не идеальна.

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

Используя небольшие фрагменты ввода, мы можем научить модель понимать наш стиль запроса и предпочтения в вычислениях.

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

От неразвернутых запросов до идеально настроенных SQL-запросов, модели GPT привносят волшебство персонализации прямо в наши руки!

Вы можете перейти и проверить мой код напрямую в GitHub.

  ## SQL TABLESsql_tables = """CREATE TABLE PRODUCTS ( product_name VARCHAR(100), price DECIMAL(10, 2), discount DECIMAL(5, 2), product_type VARCHAR(50), rating DECIMAL(3, 1), product_id VARCHAR(100));INSERT INTO PRODUCTS (product_name, price, discount, product_type, rating, product_id)VALUES ('UltraView QLED TV', 2499.99, 15, 'TVs', 4.8, 'K5521'), ('ViewTech Android TV', 799.99, 10, 'TVs', 4.6, 'K5522'), ('SlimView OLED TV', 3499.99, 5, 'TVs', 4.9, 'K5523'), ('PixelMaster Pro DSLR', 1999.99, 20, 'Cameras and Camcorders', 4.7, 'K5524'), ('ActionX Waterproof Camera', 299.99, 15, 'Cameras and Camcorders', 4.4, 'K5525'), ('SonicBlast Wireless Headphones', 149.99, 10, 'Audio and Headphones', 4.8, 'K5526'), ('FotoSnap DSLR Camera', 599.99, 0, 'Cameras and Camcorders', 4.3, 'K5527'), ('CineView 4K TV', 599.99, 10, 'TVs', 4.5, 'K5528'), ('SoundMax Home Theater', 399.99, 5, 'Audio and Headphones', 4.2, 'K5529'), ('GigaPhone 12X', 1199.99, 8, 'Smartphones and Accessories', 4.9, 'K5530');CREATE TABLE ORDERS ( order_number INT PRIMARY KEY, order_creation DATE, order_status VARCHAR(50), product_id VARCHAR(100));INSERT INTO ORDERS (order_number, order_creation, order_status, delivery_date, product_id)VALUES (123456, '2023-07-01', 'Shipped','', 'K5521'), (789012, '2023-07-02', 'Delivered','2023-07-06', 'K5524'), (345678, '2023-07-03', 'Processing','', 'K5521'), (901234, '2023-07-04', 'Shipped','', 'K5524'), (567890, '2023-07-05', 'Delivered','2023-07-15', 'K5521'), (123789, '2023-07-06', 'Processing','', 'K5526'), (456123, '2023-07-07', 'Shipped','', 'K5529'), (890567, '2023-07-08', 'Delivered','2023-07-12', 'K5522'), (234901, '2023-07-09', 'Processing','', 'K5528'), (678345, '2023-07-10', 'Shipped','', 'K5530');""" 

  Josep Ferrer – инженер-аналитик из Барселоны. Он окончил физико-техническое образование и в настоящее время работает в области науки о данных, применяемой к человеческой мобильности. Он является создателем контента в свободное время и фокусируется на науке о данных и технологиях. С ним можно связаться через LinkedIn, Twitter или VoAGI.