Connect with us

Оптимизируйте LLM с DSPy: пошаговое руководство по созданию, оптимизации и оценке систем ИИ

Промпт-инжиниринг

Оптимизируйте LLM с DSPy: пошаговое руководство по созданию, оптимизации и оценке систем ИИ

mm
DSPy is a framework for algorithmically optimizing LM prompts and weights

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

Вступает DSPy, революционный фреймворк, предназначенный для упрощения разработки систем ИИ, работающих на LLM. DSPy вводит системный подход к оптимизации подсказок LM и весов, позволяя разработчикам создавать сложные приложения с минимальными ручными усилиями.

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

Что такое DSPy, и почему вам он нужен?

DSPy – это фреймворк, который отделяет поток вашей программы (модули) от параметров (подсказок LM и весов) каждого шага. Это разделение позволяет систематически оптимизировать подсказки LM и веса, позволяя вам создавать сложные системы ИИ с большей надежностью, предсказуемостью и соблюдением ограничений, специфичных для области.

Традиционно разработка систем ИИ с LLM включала в себя трудоемкий процесс разбиения проблемы на шаги, создания сложных подсказок для каждого шага, генерации синтетических примеров для тонкой настройки и ручного управления LМ для соблюдения конкретных ограничений. Этот подход не только был длительным, но и склонным к ошибкам, поскольку даже незначительные изменения в конвейере, LM или данных могли требовать обширной переработки подсказок и шагов тонкой настройки.

DSPy решает эти проблемы, вводя новую парадигму: оптимизаторы. Эти алгоритмы, управляемые LM, могут настраивать подсказки и веса ваших вызовов LM, учитывая метрику, которую вы хотите максимизировать. Автоматизируя процесс оптимизации, DSPy наделяет разработчиков возможностью создавать надежные системы ИИ с минимальным ручным вмешательством, повышая надежность и предсказуемость выходных данных LM.

Модульная архитектура DSPy

В основе DSPy лежит модульная архитектура, которая облегчает составление сложных систем ИИ. Фреймворк предоставляет набор встроенных модулей, которые абстрагируют различные методы подсказки, такие как dspy.ChainOfThought и dspy.ReAct. Эти модули можно комбинировать и составлять в более крупные программы, позволяя разработчикам создавать сложные конвейеры, адаптированные к их конкретным требованиям.

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

Оптимизация с DSPy

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

Некоторые из ключевых оптимизаторов, доступных в DSPy, включают:

  1. BootstrapFewShot: Этот оптимизатор расширяет сигнатуру, автоматически генерируя и включая оптимизированные примеры в подсказку, отправляемую в модель, реализуя метод few-shot обучения.
  2. BootstrapFewShotWithRandomSearch: Применяет BootstrapFewShot несколько раз с случайным поиском по сгенерированным демонстрациям, выбирая лучшую программу за оптимизацию.
  3. MIPRO: Генерирует инструкции и примеры few-shot на каждом шаге, с генерацией инструкций, осведомленной о данных и демонстрациях. Он использует байесовскую оптимизацию для эффективного поиска пространства инструкций генерации и демонстраций по модулям.
  4. BootstrapFinetune: Дистиллирует программу DSPy на основе подсказок в обновления весов для меньших LM, позволяя вам тонко настраивать основные LLM(ы) для повышения эффективности.

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

Начало работы с DSPy

Чтобы проиллюстрировать мощь DSPy, давайте пройдемся через практический пример создания системы генерации, дополненной извлечением (RAG), для вопросов и ответов.

Шаг 1: Настройка языковой модели и модели извлечения

Первым шагом является настройка языковой модели (LM) и модели извлечения (RM) в DSPy.

Чтобы установить DSPy, запустите:


pip install dspy-ai

DSPy поддерживает несколько API LM и RM, а также локальное размещение моделей, что делает его легко интегрируемым с вашими предпочитаемыми моделями.


import dspy

# Настройка LM и RM
turbo = dspy.OpenAI(model='gpt-3.5-turbo')
colbertv2_wiki17_abstracts = dspy.ColBERTv2(url='http://20.102.90.50:2017/wiki17_abstracts')

dspy.settings.configure(lm=turbo, rm=colbertv2_wiki17_abstracts)

Шаг 2: Загрузка набора данных

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


from dspy.datasets import HotPotQA

# Загрузка набора данных
dataset = HotPotQA(train_seed=1, train_size=20, eval_seed=2023, dev_size=50, test_size=0)

# Укажите поле 'question' как вход
trainset = [x.with_inputs('question') for x in dataset.train]
devset = [x.with_inputs('question') for x in dataset.dev]

Шаг 3: Создание сигнатур

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


class GenerateAnswer(dspy.Signature):
"""Отвечайте на вопросы с короткими фактологическими ответами."""

context = dspy.InputField(desc="может содержать соответствующие факты")
question = dspy.InputField()
answer = dspy.OutputField(desc="часто между 1 и 5 словами")

Шаг 4: Создание конвейера

Мы создадим наш конвейер RAG как модуль DSPy, который состоит из метода инициализации (__init__) для объявления подмодулей (dspy.Retrieve и dspy.ChainOfThought) и метода forward (forward) для описания потока управления ответом на вопрос с помощью этих модулей.


class RAG(dspy.Module):
def __init__(self, num_passages=3):
super().__init__()

self.retrieve = dspy.Retrieve(k=num_passages)
self.generate_answer = dspy.ChainOfThought(GenerateAnswer)

def forward(self, question):
context = self.retrieve(question).passages
prediction = self.generate_answer(context=context, question=question)
return dspy.Prediction(context=context, answer=prediction.answer)

Шаг 5: Оптимизация конвейера

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


from dspy.teleprompt import BootstrapFewShot

# Метрика проверки
def validate_context_and_answer(example, pred, trace=None):
answer_EM = dspy.evaluate.answer_exact_match(example, pred)
answer_PM = dspy.evaluate.answer_passage_match(example, pred)
return answer_EM and answer_PM

# Настройка оптимизатора
teleprompter = BootstrapFewShot(metric=validate_context_and_answer)

# Компилируйте программу
compiled_rag = teleprompter.compile(RAG(), trainset=trainset)

Шаг 6: Оценка конвейера

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


from dspy.evaluate import Evaluate

# Настройка оценщика
evaluate = Evaluate(devset=devset, metric=validate_context_and_answer, num_threads=4, display_progress=True, display_table=0)

# Оценка скомпилированной программы RAG
evaluation_result = evaluate(compiled_rag)

print(f"Результат оценки: {evaluation_result}")

Шаг 7: Просмотр истории модели

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


# Просмотр истории модели
turbo.inspect_history(n=1)

Шаг 8: Сделать прогнозы

С оптимизированным и оцененным конвейером вы теперь можете использовать его для прогнозов на новых вопросах.


# Пример вопроса
question = "Какую награду получила первая книга Гэри Зукава?"

# Сделать прогноз, используя скомпилированную программу RAG
prediction = compiled_rag(question)

print(f"Вопрос: {question}")
print(f"Ответ: {prediction.answer}")
print(f"Извлеченные контексты: {prediction.context}")

Минимальный рабочий пример с DSPy

Теперь давайте пройдемся через другой минимальный рабочий пример, используя набор данных GSM8K и модель OpenAI GPT-3.5-turbo для симуляции задач подсказки в DSPy.

Настройка

Сначала убедитесь, что ваша среда правильно настроена:


import dspy
from dspy.datasets.gsm8k import GSM8K, gsm8k_metric

# Настройка LM
turbo = dspy.OpenAI(model='gpt-3.5-turbo-instruct', max_tokens=250)
dspy.settings.configure(lm=turbo)

# Загрузка математических вопросов из набора данных GSM8K
gsm8k = GSM8K()
gsm8k_trainset, gsm8k_devset = gsm8k.train[:10], gsm8k.dev[:10]

print(gsm8k_trainset)

Набор данных gsm8k_trainset и gsm8k_devset содержит список примеров, каждый из которых имеет вопрос и ответ.

Определение модуля

Далее определите пользовательскую программу, использующую модуль ChainOfThought для пошагового рассуждения:


class CoT(dspy.Module):
def __init__(self):
super().__init__()
self.prog = dspy.ChainOfThought("вопрос -> ответ")

def forward(self, question):
return self.prog(question=question)

Компиляция и оценка модели

Теперь скомпилируйте ее с помощью телепrompter BootstrapFewShot:


from dspy.teleprompt import BootstrapFewShot

# Настройка оптимизатора
config = dict(max_bootstrapped_demos=4, max_labeled_demos=4)

# Оптимизация с помощью метрики gsm8k
teleprompter = BootstrapFewShot(metric=gsm8k_metric, **config)
optimized_cot = teleprompter.compile(CoT(), trainset=gsm8k_trainset)

# Настройка оценщика
from dspy.evaluate import Evaluate

evaluate = Evaluate(devset=gsm8k_devset, metric=gsm8k_metric, num_threads=4, display_progress=True, display_table=0)
evaluate(optimized_cot)

# Просмотр истории модели
turbo.inspect_history(n=1)

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

Управление данными в DSPy

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

Создание объектов примеров

Объекты примеров в DSPy похожи на словари Python, но имеют полезные утилиты:


qa_pair = dspy.Example(question="Это вопрос?", answer="Это ответ.")

print(qa_pair)
print(qa_pair.question)
print(qa_pair.answer)

Выход:


Example({'question': 'Это вопрос?', 'answer': 'Это ответ.'}) (input_keys=None)
Это вопрос?
Это ответ.

Указание ключей входных данных

В DSPy объекты Example имеют метод with_inputs(), чтобы отметить конкретные поля как входные данные:


print(qa_pair.with_inputs("question"))
print(qa_pair.with_inputs("question", "answer"))

Значения можно получить, используя оператор точки, и методы, такие как inputs() и labels(), возвращают новые объекты Example, содержащие только входные или не-входные ключи соответственно.

Оптимизаторы в DSPy

Оптимизатор DSPy настраивает параметры программы DSPy (т. е. подсказки и/или веса LM) для максимизации указанных метрик. DSPy предлагает различные встроенные оптимизаторы, каждый из которых использует разные стратегии.

Доступные оптимизаторы

  • BootstrapFewShot: Генерирует примеры few-shot, используя предоставленные помеченные входные и выходные данные.
  • BootstrapFewShotWithRandomSearch: Применяет BootstrapFewShot несколько раз с случайным поиском по сгенерированным демонстрациям.
  • COPRO: Генерирует и совершенствует новые инструкции для каждого шага, оптимизируя их с помощью координатного восхождения.
  • MIPRO: Оптимизирует инструкции и примеры few-shot, используя байесовскую оптимизацию.

Выбор оптимизатора

Если вы не уверены, с чего начать, используйте BootstrapFewShotWithRandomSearch:

Для очень небольшого количества данных (10 примеров) используйте BootstrapFewShot.
Для немного большего количества данных (50 примеров) используйте BootstrapFewShotWithRandomSearch.
Для более крупных наборов данных (300+ примеров) используйте MIPRO.

Вот как использовать BootstrapFewShotWithRandomSearch:


from dspy.teleprompt import BootstrapFewShotWithRandomSearch

config = dict(max_bootstrapped_demos=4, max_labeled_demos=4, num_candidate_programs=10, num_threads=4)
teleprompter = BootstrapFewShotWithRandomSearch(metric=YOUR_METRIC_HERE, **config)
optimized_program = teleprompter.compile(YOUR_PROGRAM_HERE, trainset=YOUR_TRAINSET_HERE)

Сохранение и загрузка оптимизированных программ

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

optimized_program.save(YOUR_SAVE_PATH)

Загрузите сохраненную программу:


loaded_program = YOUR_PROGRAM_CLASS()
loaded_program.load(path=YOUR_SAVE_PATH)

Расширенные функции: Утверждения DSPy

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

Использование утверждений

Определите функции проверки и объявите утверждения, следующими за соответствующим поколением модели. Например:


dspy.Suggest(
len(query) <= 100,
"Запрос должен быть коротким и менее 100 символов",
)

dspy.Suggest(
validate_query_distinction_local(prev_queries, query),
"Запрос должен быть отличным от: " + "; ".join(f"{i+1}) {q}" for i, q in enumerate(prev_queries)),
)

Преобразование программ с помощью утверждений


from dspy.primitives.assertions import assert_transform_module, backtrack_handler

baleen_with_assertions = assert_transform_module(SimplifiedBaleenAssertions(), backtrack_handler)

Альтернативно, активируйте утверждения直接 на программе:


baleen_with_assertions = SimplifiedBaleenAssertions().activate_assertions()

Оптимизации, управляемые утверждениями

Утверждения DSPy работают с оптимизациями DSPy, особенно с BootstrapFewShotWithRandomSearch, включая настройки, такие как:

  • Компиляция с утверждениями
  • Компиляция + вывод с утверждениями

Заключение

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

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

Я провел последние пять лет, погружаясь в увлекательный мир Machine Learning и Deep Learning. Моя страсть и экспертиза привели меня к участию в более чем 50 различных проектах по разработке программного обеспечения, с особым акцентом на AI/ML. Мое непрекращающееся любопытство также привело меня к Natural Language Processing, области, которую я с нетерпением жду возможности изучить более подробно.