Можно сказать, что это часть 1.5 😅 моей мини-серии Создание комплексных приложений на базе LLM без API Open AI.

Прежде чем идти дальше, если вам интересно, что охватывает часть 1, проверьте это. Мы расскажем, как интегрировать пользовательский LLM с помощью langchain.

О чем пока идет речь в сериале

Давайте начнем

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

  1. Управление путями моделей. Возможно, вам потребуется изменить путь к модели или ссылку S3. И зависимости пути могут быть отражены в нескольких файлах проекта.
  2. Управление гиперпараметрами. Предположим, у вас есть конвейер тонкой настройки или конвейер вывода. Гиперпараметры, такие как скорость обучения, выбор оптимизатора и т. д., становятся трудными для управления, если они инициализируются в файлах проекта. В наших случаях такие конфиги, как надо стримить или нет, температура, top_p, top_k и т.д.
  3. Конфигурации действуют как настройки. Как и все приложения (даже ваши приложения для Android/iOS) имеют встроенные настройки. Точно так же файлы конфигурации также действуют как настройки, которые также могут быть изменены вне внутренней структуры кода, но могут сообщать о том, как должен работать ваш код. Даже файлы конфигурации очень полезны при написании кода инфраструктуры для обслуживания приложений.

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

То же самое касается структуры проекта. Создание модульной структуры проекта очень важно. Современные облачные приложения требуют надлежащей структуризации проектов, чтобы большинство модулей были отделены (независимы) друг от друга и могли обрабатываться или масштабироваться независимо друг от друга. Теперь, когда вы получили некоторое представление о написании файлов конфигурации, теперь давайте перейдем непосредственно к созданию структуры нашего проекта и напишем наши файлы конфигурации. Для структуры проекта мы будем использовать CookieClutter для создания шаблона нашего проекта, а для управления конфигурацией мы будем использовать Hydra.

Структурирование нашего проекта

CookieClutter — это утилита командной строки, которая создает структуру проекта из уже существующих шаблонов проекта. Вы можете установить cookiecutter с помощью

pip install cookiecutter

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

cookiecutter -c v1 https://github.com/drivendata/cookiecutter-data-science

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

.
├── configs
│   ├── config.yaml
│   ├── knowledge_base
│   │   └── default.yaml
│   └── model
│       └── default.yaml
├── data
│   ├── diff_lm.pdf
│   └── llama2.pdf
├── LICENSE
├── main.py
├── Makefile
├── README.md
├── requirements.txt
├── setup.py
├── src
│   ├── data
│   │   └── __init__.py
│   ├── __init__.py
│   └── models
│       └── __init__.py
├── test_environment.py
├── tests
└── tox.ini

Вот некоторая информация, которую вам нужно знать о том, для чего предназначена каждая папка.

  1. configs : Это в основном для хранения различных конфигураций для каждого из компонентов в наших приложениях. Примеры: модели, база знаний, векторная база данных и т. д.
  2. data : Это используется для сохранения всех наших документов для нашей базы знаний. Однако на практике документы должны храниться внутри такого сервиса, как Amazon S3.
  3. src : он содержит все наши исходные файлы с подпапками, такими как data , которые будут содержать все файлы для создания и управления нашей базой знаний и modelsдля создания и управления нашими LLM.
  4. tests : Здесь мы будем хранить все наши файлы для тестирования, такого как модульное тестирование, интеграционное тестирование и т. д.
  5. main.py: На данный момент это будет наш основной файл Python, который будет использовать исходные файлы в качестве помощника и запускать основной код. Мы запустим этот код для нашего чат-бота.
  6. Makefile полезен для быстрой настройки нашего проекта, такой как установка зависимостей, линтинг, форматирование, быстрое тестирование и т. д.

Код из нашего предыдущего блога будет находиться внутри src/models папки. Сегодня мы сосредоточимся на написании файлов конфигурации в папке configs. Я предполагаю, что читатели уже знакомы с моим предыдущим блогом о том, как мы интегрировали собственный LLM с открытым исходным кодом (используя gpt4all и langchain). Потому что сегодня мы будем писать файлы конфигурации для наших моделей.

Сценарий

Короче говоря, давайте представим себе этот сюжет. Предположим, ваше приложение позволяет пользователям общаться с различными типами данных и с различными вариантами LLM с открытым исходным кодом. Доступ к различным LLM с открытым исходным кодом осуществляется через разных поставщиков, таких как LLAMA.cpp , GPT4ALL , HugggingFace и т. д. Теперь каждая из моделей, написанных с использованием разных библиотек, может иметь разные оптимальные конфигурации. Как ты собираешься с ними справиться? Ответ заключается в написании файлов конфигурации. Здесь мы будем писать наши конфигурации для наших моделей gpt4all LLM.

Гидра 101

Начнем с установки Гидры. Мы можем сделать это с помощью этой команды

pip install hydra-core==1.1.0

Hydra работает поверх OmegaConf, которая представляет собой иерархическую систему конфигурации на основе YAML с поддержкой объединения конфигураций из нескольких источников (файлов, аргументов CLI, переменных среды), обеспечивая согласованный API независимо от того, как была создана конфигурация.

Написание ваших первых файлов конфигурации

Давайте начнем с создания простого файла yaml с именем config.yaml, а содержимое файла yaml будет таким:

gpt4all:
  gpt4all_model_name: ggml-gpt4all-j-v1.3-groovy.bin
  gpt4all_model_folder_path: /home/anindya/.local/share/nomic.ai/GPT4All/
  gpt4all_backend: llama

Выше приведен файл конфигурации, который я написал с помощью yaml. Теперь я мог бы также написать то же самое, как это

gpt4all_model_name: ggml-gpt4all-j-v1.3-groovy.bin
gpt4all_model_folder_path: /home/anindya/.local/share/nomic.ai/GPT4All/
gpt4all_backend: llama

Этот тоже действителен. Но все зависит. Давайте рассмотрим, что первый yaml — это формат 1, а второй — формат 2. Формат 1 позволяет мне одновременно добавлять конфигурации разных поставщиков моделей. Например, если я сейчас хочу добавить конфигурации обнимающих лиц, я мог бы сделать что-то вроде этого.

gpt4all:
  gpt4all_model_name: ggml-gpt4all-j-v1.3-groovy.bin
  gpt4all_model_folder_path: /home/anindya/.local/share/nomic.ai/GPT4All/
  gpt4all_backend: llama

hugging_face:
  hugging_face_model_id: some model name 
  hugging_face_adapter_name: some adapter id
  hugging_face_dataset: some dataset source id

В то время как в формате 2 я мог бы сделать то же самое, но в разных файлах. В этом случае один файл будет называться gpt4all_config.yaml, другой — huggingface_config.yaml. Все зависит от того, насколько сложны наши проекты, и исходя из этого мы должны выбрать нашу

Теперь давайте загрузим этот файл с помощью Hydra и воспользуемся печатью конфигураций.

# file name: main.py

import hydra

@hydra.main(config_path='.', config_name='config')
def main(cfg):
    print('GPT4ALL Configurations')
    print(' Model name: ', cfg.gpt4all.gpt4all_model_name)
    print(' Model stored in path: ', cfg.gpt4all.gpt4all_model_folder_path)
    print(' Model using LLM backend of: ', cfg.gpt4all.gpt4all_backend)
    print('\nHuggingFace Configurations')
    print(' Hugging face model name: ', cfg.hugging_face.hugging_face_model_id)
    print(' Hugging face adapter name: ', cfg.hugging_face.hugging_face_adapter_name)
    print(' Hugging face dataset name: ', cfg.hugging_face.hugging_face_dataset)

if __name__ == '__main__':
    main()

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

GPT4ALL Configurations
 Model name:  ggml-gpt4all-j-v1.3-groovy.bin
 Model stored in path:  /home/anindya/.local/share/nomic.ai/GPT4All/
 Model using LLM backend of:  llama

HuggingFace Configurations
 Hugging face model name:  some model name
 Hugging face adapter name:  some adapter id
 Hugging face dataset name:  some dataset source id

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

python3 main.py \
    gpt4all.gpt4all_model_name="my latest model name" \
    gpt4all.gpt4all_model_folder_path="my latest folder" \
    gpt4all.gpt4all_backend="GPTJ"

И это будет печатать

GPT4ALL Configurations
 Model name:  my latest model name
 Model stored in path:  my latest folder
 Model using LLM backend of:  GPTJ

HuggingFace Configurations
 Hugging face model name:  some model name
 Hugging face adapter name:  some adapter id
 Hugging face dataset name:  some dataset source id

Думаю, этой информации достаточно, чтобы вы все начали работу с нашим существующим проектом. Однако, если вам интересно узнать больше, ознакомьтесь с потрясающей документацией самой Hydra. Кроме того, большая часть этого 101 была вдохновлена ​​этим потрясающим блогом Raviraja Ganta. Он использовал Hydra, чтобы показать нам, как управлять конфигурациями при создании комплексных приложений машинного обучения.

Применение Hydra к нашему проекту для управления нашей моделью

Ранее я показал вам, как создать один файл yaml и использовать его в качестве наших конфигураций. Но в реальных сценариях нам придется обрабатывать гораздо более сложные вещи. Вы можете просто взять наши примеры приложений. Сейчас мы просто имеем дело с нашей моделью. В дальнейшем нам предстоит сделать то же самое для нашей векторной базы данных (базы знаний), API, сервировки и т. д. Поэтому лучше создавать конфиги для разных компонентов. Вот пример нашей структуры папок config.

configs
├── config.yaml
├── knowledge_base
│   └── default.yaml
└── model
    └── default.yaml

Думайте о config.yaml внутри configs как о оркестраторе конфигурации. Это означает, что config.yaml знает, где находятся другие конфигурации каждого компонента, и через config.yaml мы можем загружать и управлять другими компонентами. Теперь внутри model и knowledge_base есть этот default.yaml, который имеет конфигурации по умолчанию для нашей модели и нашей базы знаний (мы поговорим о базе знаний в последующих частях нашего блога). Теперь давайте посмотрим на наш файл config.yaml.

defaults:
  - model: default
  - knowledge_base: default

Если мы просто воспользуемся своей интуицией, то понять это будет несложно. В нем говорится, что наши default конфигурации следующие. Конфигурации модели по умолчанию будут использовать default.yaml внутри configs/models и то же самое для нашей базы знаний. Теперь аналогичным образом configs/models может содержать другие файлы конфигурации, такие как model_dev.yaml (все конфигурации модели для этапа разработки), model_production.yaml (нетронутые конфигурации для производства, мы также можем сделать его доступным только для чтения с помощью Hydra, если захотим). И затем мы можем использовать это в нашей ссылке config.yaml. Но пока оставим эту настройку.

После этого давайте посмотрим, как выглядит наш configs/models/default.yaml.

gpt4all_model:
  gpt4all_model_name: ggml-gpt4all-j-v1.3-groovy.bin
  gpt4all_model_folder_path: /home/anindya/.local/share/nomic.ai/GPT4All/
  gpt4all_backend: llama
  gpt4all_allow_streaming: true
  gpt4all_allow_downloading: false
  gpt4all_temperature: 1
  gpt4all_top_p: 0.1
  gpt4all_top_k: 40
  gpt4all_n_batch: 8
  gpt4all_n_threads: 4
  gpt4all_n_predict: 256
  gpt4all_max_tokens: 200
  gpt4all_repeat_last_n: 64
  gpt4all_penalty: 1.18

Теперь мы записали все изменяемые параметры внутри конфига, и мы также сделали ссылки на наш config.yaml. Теперь внутри нашего файла main.py давайте используем его, чтобы проверить, работают ли наши конфиги. А также во время выполнения main.py мы настроим некоторые параметры, как это было сделано ранее в нашем фиктивном примере, чтобы увидеть, работает ли это и в этом случае.

Вот наш файл main.py.

import hydra
from src.models.gpt4all_model import MyGPT4ALL

# reference the ./configs folder to tell hydra where the configs are located
# also tell hydra that our master config (which manages all other config)
# name is config.

@hydra.main(config_path='./configs', config_name='config')
def main(cfg):

    # instantiate the model and populate the arguments using hydra

    chat_model = MyGPT4ALL(
        model_folder_path=cfg.model.gpt4all_model.gpt4all_model_folder_path,
        model_name=cfg.model.gpt4all_model.gpt4all_model_name,
        allow_download=cfg.model.gpt4all_model.gpt4all_allow_downloading,
        allow_streaming=cfg.model.gpt4all_model.gpt4all_allow_streaming,
        
    )

    while True:
        query = input('Enter your Query: ')
        if query == 'exit':
            break
        # use hydra to fill the **kwargs
        response = chat_model(
            query,
            n_predict=cfg.model.gpt4all_model.gpt4all_n_predict,
            temp=cfg.model.gpt4all_model.gpt4all_temperature,
            top_p=cfg.model.gpt4all_model.gpt4all_top_p,
            top_k=cfg.model.gpt4all_model.gpt4all_top_k,
            n_batch=cfg.model.gpt4all_model.gpt4all_n_batch,
            repeat_last_n=cfg.model.gpt4all_model.gpt4all_repeat_last_n,
            repeat_penalty=cfg.model.gpt4all_model.gpt4all_penalty,
            max_tokens=cfg.model.gpt4all_model.gpt4all_max_tokens,
        )
        print()

if __name__ == '__main__':
    main()

Я считаю, что этот файл достаточно прост для понимания. Все, что он делает, это создает экземпляр chat_model, используя наши конфигурации гидры, а также вызывает **kwargs при использовании модели в чате с нашими конфигурациями гидры. И если мы просто запустим python3 main.py, он должен работать, как и ожидалось. Но предположим, что мы хотим настроить некоторые параметры во время выполнения. Взгляните на это, например.

PYTHONPATH=. python3 main.py \
    model.gpt4all_model.gpt4all_model_name=ggml-mpt-7b-instruct.bin \
    model.gpt4all_model.gpt4all_temperature=1 \
    model.gpt4all_model.gpt4all_top_k=50 \
    model.gpt4all_model.gpt4all_max_tokens=10 \
    model.gpt4all_model.gpt4all_penalty=1.00

Я изменил имя модели, температуру, значения top_k, max_tokens и штраф, и, следовательно, он изменит параметры перед запуском кода. Тоже лучшая часть!!! Это дает нам отличный клиентский интерфейс, не позволяя нам его создавать. Разве это не потрясающе?

Заключение

Поздравляю 🥳🥳. Вы только что завершили очень важную часть общих жизненных циклов машинного обучения производственного уровня, то есть управление конфигурацией. И мы можем применить эти знания при создании наших приложений на базе LLM. Сейчас это может выглядеть не очень круто, но уверяю вас, это упрощает жизнь, когда приложения становятся слишком сложными для обработки. В следующей части нашего блога мы узнаем, как соединять документы и создавать из них базу знаний, чтобы сделать LLM более надежными и отвечать на вопросы по невидимым, частным документам. Все коды, использованные здесь и ранее, сброшены в этот репозиторий GitHub. Не стесняйтесь проверить это. До скорого. 💪

Ссылки и благодарности