Промпт-інжиніринг
Оптимізуйте LLM з DSPy: крок за кроком керівництво з будівництва, оптимізації та оцінки систем штучного інтелекту
Як можливості великомасштабних мовних моделей (LLM) продовжують розширюватися, розробка надійних систем штучного інтелекту, які використовують їх потенціал, стала все більш складною. Традиційні підходи часто включають складні техніки промптингу, генерацію даних для тонкої настройки та ручне керівництво для забезпечення дотримання домен-специфічних обмежень. Однак цей процес може бути трудомістким, схильним до помилок та сильно залежним від людського втручання.
Вступає DSPy, революційний фреймворк, призначений для спрощення розробки систем штучного інтелекту, що працюють на основі LLM. DSPy вводить системний підхід до оптимізації промптів LM та ваг і дозволяє розробникам створювати складні програми з мінімальними ручними зусиллями.
У цьому всебічному керівництві ми дослідимо основні принципи DSPy, його модульну архітектуру та ряд потужних функцій, які він пропонує. Ми також розглянемо практичні приклади, демонструючи, як DSPy може змінити спосіб розробки систем штучного інтелекту з LLM.
Що таке DSPy, і чому вам це потрібно?
DSPy – це фреймворк, який розділяє потік вашої програми (модулів) від параметрів (промптів LM та ваг) кожного кроку. Це розділення дозволяє систематично оптимізувати промпти LM та ваги, дозволяючи вам створювати складні системи штучного інтелекту з більшою надійністю, передбачуваністю та дотриманням домен-специфічних обмежень.
Традиційно, розробка систем штучного інтелекту з LLM включала трудомісткий процес розбиття проблеми на кроки, створення складних промптів для кожного кроку, генерації синтетичних прикладів для тонкої настройки та ручного керівництва для забезпечення дотримання конкретних обмежень. Цей підхід не тільки був часу, але й схильний до помилок, оскільки навіть незначні зміни в трубопроводі, LM чи даних могли вимагати великої переробки промптів та кроків тонкої настройки.
DSPy вирішує ці проблеми, вводячи новий парадигму: оптимізатори. Ці алгоритми, керовані LM, можуть налаштовувати промпти та ваги ваших викликів LM, дано метрику, яку ви хочете максимізувати. Автоматизуючи процес оптимізації, DSPy дозволяє розробникам створювати надійні системи штучного інтелекту з мінімальними ручними зусиллями, підвищуючи надійність та передбачуваність виходів LM.
Модульна архітектура DSPy
У серці DSPy лежить модульна архітектура, яка спрощує створення складних систем штучного інтелекту. Фреймворк пропонує набір вбудованих модулів, які абстрагують різні техніки промптингу, такі як dspy.ChainOfThought та dspy.ReAct. Ці модулі можна поєднувати та складати в більші програми, дозволяючи розробникам створювати складні трубопроводи, адаптовані до їхніх конкретних вимог.
Кожен модуль інкапсулює навчальні параметри, включаючи інструкції, приклади та ваги LM. Коли модуль викликається, оптимізатори DSPy можуть тонко налаштовувати ці параметри для максимізації бажаної метрики, забезпечуючи, щоб виходи LM відповідали заданим обмеженням та вимогам.
Оптимізація з DSPy
DSPy вводить ряд потужних оптимізаторів, призначених для підвищення продуктивності та надійності ваших систем штучного інтелекту. Ці оптимізатори використовують алгоритми, керовані LM, для налаштування промптів та ваг LM, максимізуючи задану метрику та дотримуючись домен-специфічних обмежень.
Деякі з ключових оптимізаторів, доступних у DSPy, включають:
- BootstrapFewShot: цей оптимізатор розширює підпис, автоматично генеруючи та включаючи оптимізовані приклади у промпт, надісланий до моделі, реалізуючи few-shot навчання.
- BootstrapFewShotWithRandomSearch: застосовує
BootstrapFewShotкілька разів з випадковим пошуком над згенерованими демонстраціями, вибираючи найкращу програму над оптимізацією. - MIPRO: генерує інструкції та приклади на кожному кроці, з інструкціями генерації, які залежать від даних та демонстрацій. Він використовує баєсівську оптимізацію для ефективного пошуку простору генерації інструкцій та демонстрацій через ваші модулі.
- BootstrapFinetune: дистилує програму DSPy на основі промптів у оновлення ваг для менших LM, дозволяючи вам тонко налаштовувати базові LLM для підвищення ефективності.
Використовуючи ці оптимізатори, розробники можуть систематично оптимізувати свої системи штучного інтелекту, забезпечуючи високоякісні виходи та дотримання домен-специфічних обмежень та вимог.
Початок роботи з DSPy
Для ілюстрації потужності DSPy пройдемо через практичний приклад створення системи генерації з підтримкою пошукової системи (RAG) для питання-відповідь.
Крок 1: Налаштування мови та моделі пошукової системи
Перший крок включає конфігурацію мови (LM) та моделі пошукової системи (RM) у DSPy.
Для встановлення DSPy виконайте:
pip install dspy-ai
DSPy підтримує кілька LM та RM API, а також локальне розміщення моделей, що робить його легко інтегрувати з вашими моделями.
import dspy <p># Конфігуруйте LM та RM turbo = dspy.OpenAI(model='gpt-3.5-turbo') colbertv2_wiki17_abstracts = dspy.ColBERTv2(url='http://20.102.90.50:2017/wiki17_abstracts')</p> <p>dspy.settings.configure(lm=turbo, rm=colbertv2_wiki17_abstracts)</p>
Крок 2: Завантаження набору даних
Далі завантажимо набір даних HotPotQA, який містить колекцію складних питання-відповідь пар, які зазвичай відповідаються у багатоступеневий спосіб.
from dspy.datasets import HotPotQA
<p># Завантажте набір даних
dataset = HotPotQA(train_seed=1, train_size=20, eval_seed=2023, dev_size=50, test_size=0)</p>
<p># Специфікуйте поле 'question' як вхід</p>
<p>trainset = [x.with_inputs('question') for x in dataset.train]
devset = [x.with_inputs('question') for x in dataset.dev]</p>
Крок 3: Будування підписів
DSPy використовує підписи для визначення поведінки модулів. У цьому прикладі ми визначимо підпис для завдання генерації відповідей, вказавши вхідні поля (контекст та питання) та поле виходу (відповідь).
<p>class GenerateAnswer(dspy.Signature): """Відповідає на питання короткими фактичними відповідями.""" context = dspy.InputField(desc="може містити відповідні факти") question = dspy.InputField() answer = dspy.OutputField(desc="часто між 1 та 5 словами")</p>
Крок 4: Будування трубопроводу
Ми побудуємо наш трубопровід RAG як модуль DSPy, який складається з методу ініціалізації (__init__) для оголошення підмодулів (dspy.Retrieve та dspy.ChainOfThought) та методу прямої дії (forward) для опису контролю потоку відповіді на питання за допомогою цих модулів.
<p>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)</p> <p>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)</p>
Крок 5: Оптимізація трубопроводу
З визначеним трубопроводом ми можемо тепер оптимізувати його за допомогою оптимізаторів DSPy. У цьому прикладі ми використаємо оптимізатор BootstrapFewShot, який генерує та вибирає ефективні промпти для наших модулів на основі тренувального набору та метрики для валідації.
<p>from dspy.teleprompt import BootstrapFewShot</p> <p># Метрика валідації 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</p> <p># Налаштування оптимізатора teleprompter = BootstrapFewShot(metric=validate_context_and_answer)</p> <p># Компіляція програми compiled_rag = teleprompter.compile(RAG(), trainset=trainset)</p>
Крок 6: Оцінка трубопроводу
Після компіляції програми необхідно оцінити її продуктивність на наборі розробки, щоб забезпечити її відповідність бажаній точності та надійності.
from dspy.evaluate import Evaluate
<p># Налаштування оцінювача
evaluate = Evaluate(devset=devset, metric=validate_context_and_answer, num_threads=4, display_progress=True, display_table=0)</p>
<p># Оцінка скомпільованої програми RAG
evaluation_result = evaluate(compiled_rag)</p>
<p>print(f"Оцінка результату: {evaluation_result}")</p>
Крок 7: Інспекція історії моделі
Для глибшого розуміння взаємодій моделі можна переглянути останні генерації, інспектуючи історію моделі.
<p># Інспекція історії моделі turbo.inspect_history(n=1)</p>
Крок 8: Робота з прогнозами
З оптимізованим трубопроводом та оціненою продуктивністю ми можемо тепер використовувати його для прогнозів на нових питаннях.
<p># Приклад питання
question = "Який нагород отримав перший книгу Гері Зукава?"</p>
<p># Робота з прогнозами за допомогою скомпільованої програми RAG
prediction = compiled_rag(question)</p>
<p>print(f"Питання: {question}")
print(f"Відповідь: {prediction.answer}")
print(f"Відібрані контексти: {prediction.context}")</p>
Мінімальний робочий приклад з DSPy
Тепер пройдемо через ще один мінімальний робочий приклад, використовуючи набір даних GSM8K та модель OpenAI GPT-3.5-turbo для симуляції завдань промптингу в DSPy.
Налаштування
Спочатку забезпечте, що ваша середовище правильно налаштоване:
<p>import dspy from dspy.datasets.gsm8k import GSM8K, gsm8k_metric</p> <p># Налаштування LM turbo = dspy.OpenAI(model='gpt-3.5-turbo-instruct', max_tokens=250) dspy.settings.configure(lm=turbo)</p> <p># Завантаження математичних питань з набору даних GSM8K gsm8k = GSM8K() gsm8k_trainset, gsm8k_devset = gsm8k.train[:10], gsm8k.dev[:10]</p> print(gsm8k_trainset)
Набори даних gsm8k_trainset та gsm8k_devset містять список прикладів, кожен з яких має питання та відповідь.
Визначення модуля
Далі визначимо власну програму, використовуючи модуль ChainOfThought для крок за кроком розуміння:
<p>class CoT(dspy.Module):
def __init__(self):
super().__init__()
self.prog = dspy.ChainOfThought("question -> answer")</p>
<p>def forward(self, question):
return self.prog(question=question)</p>
Компіляція та оцінка моделі
Тепер скомпілюємо її з телепромптером BootstrapFewShot:
<p>from dspy.teleprompt import BootstrapFewShot</p> <p># Налаштування оптимізатора config = dict(max_bootstrapped_demos=4, max_labeled_demos=4)</p> <p># Оптимізація за допомогою метрики gsm8k teleprompter = BootstrapFewShot(metric=gsm8k_metric, **config) optimized_cot = teleprompter.compile(CoT(), trainset=gsm8k_trainset)</p> <p># Налаштування оцінювача from dspy.evaluate import Evaluate</p> <p>evaluate = Evaluate(devset=gsm8k_devset, metric=gsm8k_metric, num_threads=4, display_progress=True, display_table=0) evaluate(optimized_cot)</p> <p># Інспекція історії моделі turbo.inspect_history(n=1)</p>
Цей приклад демонструє, як налаштувати середовище, визначити власний модуль, скомпілювати модель та ретельно оцінити її продуктивність за допомогою наданого набору даних та конфігурації телепромптера.
Управління даними в DSPy
DSPy працює з тренувальними, розробними та тестовими наборами даних. Для кожного прикладу у ваших даних ви зазвичай маєте три типи значень: вхідні дані, проміжні мітки та кінцеві мітки. Хоча проміжні або кінцеві мітки є необов’язковими, наявність кількох прикладів вхідних даних є необхідною.
Створення об’єктів прикладів
Об’єкти прикладів у DSPy подібні до словників Python, але мають корисні утиліти:
<p>qa_pair = dspy.Example(question="Це питання?", answer="Це відповідь.")</p> <p>print(qa_pair) print(qa_pair.question) print(qa_pair.answer)</p>
Вихід:
<p>Example({'question': 'Це питання?', 'answer': 'Це відповідь.'}) (input_keys=None)
Це питання?
Це відповідь.</p>
Специфікація вхідних ключів
У DSPy об’єкти прикладів мають метод with_inputs(), щоб позначити певні поля як вхідні:
<p>print(qa_pair.with_inputs("question"))
print(qa_pair.with_inputs("question", "answer"))</p>
Значення можна отримувати за допомогою оператора крапки, а методи, такі як inputs() та labels(), повертають нові об’єкти прикладів, що містять лише вхідні або невхідні ключі відповідно.
Оптимізатори в DSPy
Оптимізатор DSPy налаштовує параметри програми DSPy (тобто промпти та/або ваги LM) для максимізації заданих метрик. DSPy пропонує різні вбудовані оптимізатори, кожен з яких використовує різні стратегії.
Доступні оптимізатори
- BootstrapFewShot: генерує приклади few-shot за допомогою наданих мітованих вхідних та вихідних даних.
- BootstrapFewShotWithRandomSearch: застосовує BootstrapFewShot кілька разів з випадковим пошуком над згенерованими демонстраціями.
- COPRO: генерує та уточнює нові інструкції для кожного кроку, оптимізуючи їх за допомогою координатного підйому.
- MIPRO: оптимізує інструкції та приклади за допомогою баєсівської оптимізації.
Вибір оптимізатора
Якщо ви не впевнені, з чого почати, використовуйте BootstrapFewShotWithRandomSearch:
Для дуже малих даних (10 прикладів) використовуйте BootstrapFewShot.
Для трохи більших даних (50 прикладів) використовуйте BootstrapFewShotWithRandomSearch.
Для великих наборів даних (300+ прикладів) використовуйте MIPRO.
Ось, як використовувати BootstrapFewShotWithRandomSearch:
<p>from dspy.teleprompt import BootstrapFewShotWithRandomSearch</p> <p>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)</p>
Збереження та завантаження оптимізованих програм
Після виконання програми через оптимізатор збережіть її для майбутнього використання:
optimized_program.save(YOUR_SAVE_PATH)
Завантажте збережену програму:
<p>loaded_program = YOUR_PROGRAM_CLASS() loaded_program.load(path=YOUR_SAVE_PATH)</p>
Розширені функції: твердження DSPy
Твердження DSPy автоматизують виконання обчислювальних обмежень на LM, підвищуючи надійність, передбачуваність та правильність виходів LM.
Використання тверджень
Визначте функції валідації та оголосіть твердження після відповідної генерації моделі. Наприклад:
<p>dspy.Suggest(
len(query) <= 100,
"Запит повинен бути коротким та менше 100 символів",
)</p>
<p>dspy.Suggest(
validate_query_distinction_local(prev_queries, query),
"Запит повинен бути відмінним від: " + "; ".join(f"{i+1}) {q}" for i, q in enumerate(prev_queries)),
)</p>
Трансформація програм з твердженнями
<p>from dspy.primitives.assertions import assert_transform_module, backtrack_handler</p> <p>baleen_with_assertions = assert_transform_module(SimplifiedBaleenAssertions(), backtrack_handler)</p>
Альтернативно, активуйте твердження безпосередньо на програмі:
<p>baleen_with_assertions = SimplifiedBaleenAssertions().activate_assertions()</p>
Оптимізації, керовані твердженнями
Твердження DSPy працюють з оптимізаціями DSPy, особливо з BootstrapFewShotWithRandomSearch, включаючи такі налаштування, як:
- Компіляція з твердженнями
- Компіляція + Інференція з твердженнями
Висновок
DSPy пропонує потужний та системний підхід до оптимізації мовних моделей та їхніх промптів. Використовуючи ці приклади, ви можете створювати, оптимізувати та оцінювати складні системи штучного інтелекту з легкістю. Модульний дизайн та розширені оптимізатори DSPy дозволяють ефективно та ефективно інтегрувати різні мовні моделі, роблячи його цінним інструментом для всіх, хто працює в галузі NLP та штучного інтелекту.
Чи створюєте ви просту систему питання-відповідь, чи більш складний трубопровід, DSPy пропонує гнучкість та надійність, необхідні для досягнення високої продуктивності та надійності.












