Новый многообещающий метод отбора проб

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

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

Но кроме того, это также означает, что теоретически мы можем генерировать код без синтаксических ошибок! Все, что нам нужно сделать, это ввести грамматику целевого языка программирования, и у нас будет код, который «компилируется», поскольку сгенерированный вывод обязательно будет следовать грамматике.

Лично я ожидаю, что эта функция будет настолько мощной, что сможет полностью заменить такие библиотеки, как Guidance.

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

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

Наша повестка дня на сегодня:

  1. Выбор модели
  2. Подготовка и тестирование нашей среды
  3. Написание простой грамматики, чтобы понять, как она работает

Фрагменты кода примера доступны здесь.

Выбор модели

Перво-наперво — за последние несколько месяцев многое произошло, поэтому стоит выбирать актуальную модель по каким-то критериям.

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

Мое внимание привлекла вот эта модель llama2–13b-megacode2_min100:

Его производительность сравнима с гораздо более крупными моделями, хотя все еще сильно отстает от лидера с открытым исходным кодомWizardLM 70B. Тем не менее, я хочу протестировать что-то разумного размера для тестирования. Так что это подойдет.

К сожалению, мне не удалось найти его версию GGML, поэтому я взял версию oasst, которая имеет немного меньший балл (честно говоря, почти такой же). Вот модель, конвертированная The Bloke. Я загрузил двоичный файл версии q4_K_M.

Подготовка нашей среды

Сначала я проверил llama-cpp-python и обнаружил, что с прошлой недели у нас также есть привязки к Python! Но, к сожалению, я это протестировал, и он не сработал должным образом… пока! Поэтому я создал отчет об ошибке — тем временем я мог получать результаты напрямую, используя сервер llama-cpp. Мой код в основном основан на этом примере запроса на включение, поэтому заслуги принадлежат первоначальному автору.

Сначала установите llama.cpp из исходного кода, следуя инструкциям здесь. Как только вы сможете собрать, вы сможете получить доступ к build/bin и найти там двоичный файл server.

Сохраните загруженную модель рядом с этим сервером, а затем запустите ее с помощью:

./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")

Установите requests с помощью 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, который представлен символом вертикальной черты ( | ).

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 и сгенерировать код без синтаксических ошибок.

Надеюсь, вы нашли эту новую функцию такой же захватывающей, как и я! Ваше здоровье!