Тестирование грамматики Llama Cpp, основанной на ограничениях выборки
Несколько недель назад я услышал о том, что в репозитории llama-cpp ведется инновационная работа, которая добавит в репозиторий выборку, основанную на грамматических ограничениях.
Что это значит? Если говорить кратко, то эта новая возможность даст нам огромный потенциал для генерации сложных структурных выводов, таких как JSON и вызовы API, без необходимости тонкой настройки модели.
Кроме того, теоретически это означает, что мы можем генерировать код без синтаксических ошибок! Достаточно ввести грамматику целевого языка программирования, и мы получим код, который “компилируется”, поскольку сгенерированный вывод будет обязательно следовать грамматике.
Лично я ожидаю, что эта возможность будет настолько мощной, что сможет полностью заменить библиотеки типа Guidance.
Конечно, здесь возникает дополнительная сложность – написание контекстно-свободной грамматики. Если вы хотите лучше понять, как это реализовано, ознакомьтесь с оригинальным PR.
В этой статье мы сосредоточимся на создании полноценного рабочего примера на языке Python, использующего все возможности этой новой функции.
Наша повестка дня на сегодня:
- Выбор модели
- Подготовка и тестирование среды
- Написание простой грамматики, чтобы понять, как она работает
Примеры фрагментов кода доступны здесь.
Выбор модели
Прежде всего – за последние несколько месяцев произошло много событий, поэтому нам следует выбрать актуальную модель в соответствии с какими-либо бенчмарками.
К счастью, я нашел эту очень интересную таблицу лидеров, которая ранжирует различные Большие языковые модели по различным бенчмаркам.
Модель, которая привлекла мое внимание, – это llama2-13b-megacode2_min100:
Его производительность сопоставима с гораздо более крупными моделями, хотя все еще значительно уступает лидеру с открытым исходным кодом WizardLM 70B. Тем не менее, я хочу протестировать то, что имеет приемлемые размеры для тестирования. Так что это подойдет.
К сожалению, я не смог найти GGML-версию, поэтому взял вместо нее oasst-версию, которая имеет чуть меньший балл (если честно, почти такой же). Вот модель, сконвертированная The Bloke. Я скачал бинарный файл версии q4_K_M.
Подготовка окружающей среды
Я сначала проверил llama-cpp-python и обнаружил, что у нас также есть привязка к Python с прошлой недели! Но, к сожалению, я протестировал его, и он не работает так, как ожидалось… пока! Поэтому я создал сообщение об ошибке – тем временем я смог получить результаты, используя llama-cpp сервер напрямую. Мой код в основном основан на этом примере из Pull Request, так что все заслуги принадлежат автору.
Сначала установите llama.cpp из исходного кода, следуя инструкциям, приведенным здесь. После того как вы сможете выполнить сборку, вы можете обратиться к build/bin и найти там двоичный файл сервера.
Сохраните загруженную модель рядом с этим сервером, а затем запустите ее:
./server -m llama2-13b-megacode2-oasst.ggmlv3.q4_K_M.bin
В отдельном файле добавьте следующий код:
"""Code based on https://github.com/ggerganov/llama.cpp/pull/2532"""
import requests
grammar = r'''
root ::= answer
answer ::= "Good"
'''
prompts = [
"How's the weather in London?",
]
for prompt in prompts:
data_json = { "prompt": prompt, "temperature": 0.1, "n_predict": 512, "stream": False, "grammar": grammar }
resp = requests.post(
url="http://127.0.0.1:8080/completion",
headers={"Content-Type": "application/json"},
json=data_json,
)
result = resp.json()["content"]
print(f"Prompt: {prompt}")
print(f"Result: {result}\n")
Установите запросы с помощью pip install requests и запустите этот скрипт. Если все прошло успешно, вы должны увидеть следующий результат:
(env-llama-grammar) (base) paolo@paolo-MS-7D08:~/llama-grammar-example$ python minimal_example.py
Prompt: How's the weather in London?
Result: Good
Давайте разложим это по полочкам.
Написание простой грамматики
В предыдущем примере мы ограничиваем генерацию лексем с помощью этой грамматики:
grammar = r'''
root ::= answer
answer ::= "Good"
'''
Он всегда выводит значение Good. Этот пример не так интересен! Попробуем рассмотреть более интересные примеры. Расширим грамматику, чтобы она разрешала три варианта, и протестируем ее на трех разных запросах. Для этого воспользуемся логическим оператором or, который представлен символом pipe ( | ).
grammar = r'''
root ::= answer
answer ::= ("Sunny." | "Cloudy." | "Rainy.")
'''
prompts = [
"How's the weather in London?",
"How's the weather in Munich?",
"How's the weather in Barcelona?",
]
Похоже, что этой модели нравится облачная погода, она дает такой ответ чаще всего. Один раз было солнечно, так что мы знаем, что она работает так, как ожидалось:
(env-llama-grammar) (base) paolo@paolo-MS-7D08:~/llama-grammar-example$ python3 weather_example.py
Prompt: How's the weather in London?
Result: Sunny.
Prompt: How's the weather in Munich?
Result: Cloudy.
Prompt: How's the weather in Barcelona?
Result: Cloudy.
Если вы уже использовали фреймворки типа Guidance, о которых я подробно писал в прошлом, то мы просто повторили их функциональность Select!
Но все может быть гораздо интереснее. Мы можем вложить структуру токенов в грамматику. Например, сделаем модель, которая отвечает о погоде или жалуется:
grammar = r'''
root ::= answer
answer ::= (weather | complaint | yesno)
weather ::= ("Sunny." | "Cloudy." | "Rainy.")
complaint ::= "I don't like talking about the weather."
yesno ::= ("Yes." | "No.")
'''
В общем, эта грамматика описывает ответ, который может быть либо о погоде, либо о жалобе, либо просто “да/нет”.
Давайте посмотрим на нее в действии:
(env-llama-grammar) (base) paolo@paolo-MS-7D08:~/llama-grammar-example$ python3 complaining_example.py
Prompt: <|im_start|You are a weather predictor. Answer with either Sunny, Cloudy or Rainy. How's the weather in London?<|im_end|><|im_start|>
Result: Sunny.
Prompt: <|im_start|How's the weather in Munich? Is it sunny? Answer with yes or no.<|im_end|><|im_start|>
Result: Yes.
Prompt: <|im_start|How's the weather in Barcelona? Try to complain about this.<|im_end|><|im_start|>
Result: I don't like talking about the weather.
Работает отлично! Но только после того, как я немного подправил подсказку. Вы можете заметить там несколько специальных лексем, которые отмечают, когда начинается и заканчивается ввод пользователя, а также когда начинается ответ помощника.
До добавления специальных лексем модель просто постоянно жаловалась – так что имейте в виду, что даже использование грамматики требует хорошей модели и подсказок, чтобы извлечь из нее максимум пользы.
Заключение
Конечно, изучение основ компиляторов помогает в понимании этого типа грамматики, но для того, чтобы это было полезно, не нужно слишком углубляться в эту тему. Кроме того, вы наверняка сможете найти полезные примеры. Например, один из примеров, представленных в репозитории llama-cpp, реализует грамматику JSON, что очень полезно, если вы заставляете модель вызывать REST API.
В дальнейшем я могу попробовать использовать грамматику языка Python и генерировать код, свободный от синтаксических ошибок.
Надеюсь, вы нашли эту новую возможность такой же интересной, как и я! Будьте здоровы!