Kỹ thuật prompt

Tối ưu hóa LLM với DSPy: Hướng dẫn từng bước để xây dựng, tối ưu hóa và đánh giá hệ thống AI

mm
Thêm Unite.AI vào các nguồn ưu tiên của bạn trên Google
DSPy is a framework for algorithmically optimizing LM prompts and weights

Khi khả năng của các mô hình ngôn ngữ lớn (LLM) tiếp tục mở rộng, việc phát triển các hệ thống AI mạnh mẽ để tận dụng tiềm năng của chúng đã trở nên ngày càng phức tạp. Các phương pháp truyền thống thường liên quan đến các kỹ thuật nhắc nhở phức tạp, tạo dữ liệu để tinh chỉnh và hướng dẫn thủ công để đảm bảo tuân thủ các ràng buộc cụ thể của miền. Tuy nhiên, quá trình này có thể tốn thời gian, dễ xảy ra lỗi và phụ thuộc nặng vào can thiệp của con người.

Đó là nơi DSPy xuất hiện, một khuôn khổ cách mạng được thiết kế để đơn giản hóa việc phát triển các hệ thống AI được cung cấp bởi LLM. DSPy giới thiệu một cách tiếp cận hệ thống để tối ưu hóa các nhắc nhở LM và trọng số, cho phép các nhà phát triển xây dựng các ứng dụng tinh vi với nỗ lực thủ công tối thiểu.

Trong hướng dẫn toàn diện này, chúng tôi sẽ khám phá các nguyên tắc cốt lõi của DSPy, kiến trúc mô-đun của nó và loạt tính năng mạnh mẽ mà nó cung cấp. Chúng tôi cũng sẽ đi sâu vào các ví dụ thực tế, chứng minh cách DSPy có thể biến đổi cách bạn phát triển các hệ thống AI với LLM.

DSPy là gì và Tại sao Bạn Cần Nó?

DSPy là một khuôn khổ tách luồng chương trình của bạn (mô-đun) khỏi các tham số (nhắc nhở LM và trọng số) của mỗi bước. Sự tách biệt này cho phép tối ưu hóa hệ thống các nhắc nhở LM và trọng số, cho phép bạn xây dựng các hệ thống AI phức tạp với độ tin cậy, dự đoán và tuân thủ các ràng buộc cụ thể của miền cao hơn.

Truyền thống, việc phát triển các hệ thống AI với LLM liên quan đến một quá trình phức tạp của việc chia nhỏ vấn đề thành các bước, tạo nhắc nhở phức tạp cho mỗi bước, tạo ví dụ tổng hợp để tinh chỉnh và hướng dẫn thủ công LM để tuân thủ các ràng buộc cụ thể. Cách tiếp cận này không chỉ tốn thời gian mà còn dễ xảy ra lỗi, vì ngay cả những thay đổi nhỏ đối với đường ống, LM hoặc dữ liệu có thể yêu cầu làm lại đáng kể các nhắc nhở và bước tinh chỉnh.

DSPy giải quyết những thách thức này bằng cách giới thiệu một mô hình mới: tối ưu hóa. Những thuật toán được điều khiển bởi LM này có thể điều chỉnh các nhắc nhở và trọng số của các cuộc gọi LM, cho một metric bạn muốn tối đa hóa. Bằng cách tự động hóa quá trình tối ưu hóa, DSPy trao quyền cho các nhà phát triển xây dựng các hệ thống AI mạnh mẽ với sự can thiệp thủ công tối thiểu, tăng cường độ tin cậy và dự đoán của đầu ra LM.

Kiến trúc Mô-đun của DSPy

Ở trung tâm của DSPy là một kiến trúc mô-đun giúp tạo thành các hệ thống AI phức tạp. Khuôn khổ cung cấp một tập hợp các mô-đun tích hợp sẵn để trừu tượng hóa các kỹ thuật nhắc nhở khác nhau, chẳng hạn như dspy.ChainOfThoughtdspy.ReAct. Những mô-đun này có thể được kết hợp và tạo thành các chương trình lớn hơn, cho phép các nhà phát triển xây dựng các đường ống tinh vi phù hợp với yêu cầu cụ thể của họ.

Mỗi mô-đun bao gồm các tham số có thể học được, bao gồm hướng dẫn, ví dụ vài lần và trọng số LM. Khi một mô-đun được gọi, các tối ưu hóa của DSPy có thể tinh chỉnh các tham số này để tối đa hóa metric mong muốn, đảm bảo rằng đầu ra của LM tuân thủ các ràng buộc và yêu cầu cụ thể.

Tối ưu hóa với DSPy

DSPy giới thiệu một loạt các tối ưu hóa mạnh mẽ được thiết kế để tăng cường hiệu suất và độ tin cậy của các hệ thống AI của bạn. Những tối ưu hóa này sử dụng các thuật toán được điều khiển bởi LM để điều chỉnh các nhắc nhở và trọng số của các cuộc gọi LM, tối đa hóa metric cụ thể trong khi tuân thủ các ràng buộc cụ thể của miền.

Một số tối ưu hóa chính có sẵn trong DSPy bao gồm:

  1. BootstrapFewShot: Tối ưu hóa này mở rộng chữ ký bằng cách tự động tạo và bao gồm các ví dụ được tối ưu hóa trong nhắc nhở gửi đến mô hình, thực hiện học vài lần.
  2. BootstrapFewShotWithRandomSearch: Áp dụng BootstrapFewShot nhiều lần với tìm kiếm ngẫu nhiên trên các bản demo được tạo.
  3. MIPRO: Tạo hướng dẫn và ví dụ vài lần ở mỗi bước, với việc tạo hướng dẫn được nhận thức về dữ liệu và bản demo. Nó sử dụng tối ưu hóa Bayesian để tìm kiếm hiệu quả không gian tạo hướng dẫn và bản demo trên các mô-đun của bạn.
  4. BootstrapFinetune: Chưng cất một chương trình DSPy dựa trên nhắc nhở thành cập nhật trọng số cho các mô hình LM nhỏ hơn, cho phép bạn tinh chỉnh mô hình LM cơ bản để tăng hiệu quả.

Bằng cách tận dụng những tối ưu hóa này, các nhà phát triển có thể tối ưu hóa hệ thống AI của mình một cách hệ thống, đảm bảo đầu ra chất lượng cao trong khi tuân thủ các ràng buộc và yêu cầu cụ thể của miền.

Bắt đầu với DSPy

Để minh họa sức mạnh của DSPy, hãy cùng chúng tôi đi qua một ví dụ thực tế về xây dựng một hệ thống tạo và thu hồi (RAG) cho câu hỏi và trả lời.

Bước 1: Thiết lập Mô hình Ngôn ngữ và Mô hình Thu hồi

Bước đầu tiên liên quan đến việc cấu hình mô hình ngôn ngữ (LM) và mô hình thu hồi (RM) trong DSPy.

Để cài đặt DSPy, chạy:


pip install dspy-ai

DSPy hỗ trợ nhiều API LM và RM, cũng như lưu trữ mô hình cục bộ, giúp dễ dàng tích hợp mô hình yêu thích của bạn.


import dspy

<p># Cấu hình LM và 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>

Bước 2: Tải Tập Dữ liệu

Tiếp theo, chúng tôi sẽ tải tập dữ liệu HotPotQA, chứa một bộ câu hỏi và trả lời phức tạp thường được trả lời theo nhiều bước.


from dspy.datasets import HotPotQA

<p># Tải tập dữ liệu
dataset = HotPotQA(train_seed=1, train_size=20, eval_seed=2023, dev_size=50, test_size=0)</p>

<p># Chỉ định trường 'question' làm đầu vào
trainset = [x.with_inputs('question') for x in dataset.train]
devset = [x.with_inputs('question') for x in dataset.dev]</p>

Bước 3: Xây dựng Chữ ký

DSPy sử dụng chữ ký để định nghĩa hành vi của mô-đun. Trong ví dụ này, chúng tôi sẽ định nghĩa một chữ ký cho nhiệm vụ tạo câu trả lời, chỉ định các trường đầu vào (context và question) và trường đầu ra (answer).


<p>class GenerateAnswer(dspy.Signature):
"""Trả lời câu hỏi với câu trả lời ngắn."""

context = dspy.InputField(desc="có thể chứa các事 thực liên quan")
question = dspy.InputField()
answer = dspy.OutputField(desc="thường giữa 1 và 5 từ")

Bước 4: Xây dựng Đường ống

Chúng tôi sẽ xây dựng đường ống RAG của mình như một mô-đun DSPy, bao gồm một phương thức khởi tạo (__init__) để khai báo các mô-đun con (dspy.Retrieve và dspy.ChainOfThought) và một phương thức tiến hành (forward) để mô tả luồng điều khiển của việc trả lời câu hỏi bằng các mô-đun này.


<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)

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)

Bước 5: Tối ưu hóa Đường ống

Với đường ống được định nghĩa, chúng tôi có thể tối ưu hóa nó bằng cách sử dụng các tối ưu hóa của DSPy. Trong ví dụ này, chúng tôi sẽ sử dụng tối ưu hóa BootstrapFewShot, tạo và chọn các nhắc nhở hiệu quả cho các mô-đun của chúng tôi dựa trên một tập dữ liệu huấn luyện và một metric để xác thực.


<p>from dspy.teleprompt import BootstrapFewShot</p>

<p># Metric xác thực
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># Cài đặt tối ưu hóa
teleprompter = BootstrapFewShot(metric=validate_context_and_answer)

<p># Biên dịch chương trình
compiled_rag = teleprompter.compile(RAG(), trainset=trainset)

Bước 6: Đánh giá Đường ống

Sau khi biên dịch chương trình, điều quan trọng là phải đánh giá hiệu suất của nó trên một tập dữ liệu phát triển để đảm bảo nó đáp ứng độ chính xác và độ tin cậy mong muốn.


from dspy.evaluate import Evaluate

<p># Cài đặt bộ đánh giá
evaluate = Evaluate(devset=devset, metric=validate_context_and_answer, num_threads=4, display_progress=True, display_table=0)

<p># Đánh giá chương trình RAG đã biên dịch
evaluation_result = evaluate(compiled_rag)

<p>print(f"Kết quả đánh giá: {evaluation_result}")

Bước 7: Kiểm tra Lịch sử Mô hình

Để có hiểu biết sâu hơn về tương tác của mô hình, bạn có thể xem lại các thế hệ gần đây nhất bằng cách kiểm tra lịch sử của mô hình.


<p># Kiểm tra lịch sử mô hình
turbo.inspect_history(n=1)

Bước 8: Dự đoán

Với đường ống được tối ưu hóa và đánh giá, bạn có thể sử dụng nó để dự đoán trên các câu hỏi mới.


<p># Ví dụ câu hỏi
question = "Giải thưởng nào mà cuốn sách đầu tiên của Gary Zukav nhận được?"

<p># Dự đoán bằng chương trình RAG đã biên dịch
prediction = compiled_rag(question)

<p>print(f"Câu hỏi: {question}")
print(f"Trả lời: {prediction.answer}")
print(f"Context thu hồi: {prediction.context}")

Ví dụ Hoạt động Tối thiểu với DSPy

Bây giờ, hãy cùng chúng tôi đi qua một ví dụ hoạt động tối thiểu khác sử dụng tập dữ liệu GSM8K và mô hình OpenAI GPT-3.5-turbo để mô phỏng các nhiệm vụ nhắc nhở trong DSPy.

Cài đặt

Trước tiên, hãy đảm bảo môi trường của bạn được cấu hình đúng:


<p>import dspy
from dspy.datasets.gsm8k import GSM8K, gsm8k_metric</p>

<p># Cài đặt LM
turbo = dspy.OpenAI(model='gpt-3.5-turbo-instruct', max_tokens=250)
dspy.settings.configure(lm=turbo)

<p># Tải câu hỏi toán từ tập dữ liệu GSM8K
gsm8k = GSM8K()
gsm8k_trainset, gsm8k_devset = gsm8k.train[:10], gsm8k.dev[:10]

print(gsm8k_trainset)

Tập dữ liệu gsm8k_trainsetgsm8k_devset chứa danh sách các ví dụ với mỗi ví dụ có trường câu hỏi và trả lời.

Định nghĩa Mô-đun

Tiếp theo, hãy định nghĩa một chương trình tùy chỉnh sử dụng mô-đun ChainOfThought cho lý luận từng bước:


<p>class CoT(dspy.Module):
def __init__(self):
super().__init__()
self.prog = dspy.ChainOfThought("question -&gt; answer")

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

Biên dịch và Đánh giá Mô hình

Bây giờ, hãy biên dịch nó với teleprompter BootstrapFewShot:


<p>from dspy.teleprompt import BootstrapFewShot</p>

<p># Cài đặt tối ưu hóa
config = dict(max_bootstrapped_demos=4, max_labeled_demos=4)

<p># Tối ưu hóa bằng cách sử dụng gsm8k_metric
teleprompter = BootstrapFewShot(metric=gsm8k_metric, **config)
optimized_cot = teleprompter.compile(CoT(), trainset=gsm8k_trainset)

<p># Cài đặt bộ đánh giá
from dspy.evaluate import Evaluate

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

<p># Kiểm tra lịch sử mô hình
turbo.inspect_history(n=1)

Ví dụ này chứng minh cách thiết lập môi trường, định nghĩa mô-đun tùy chỉnh, biên dịch mô hình và đánh giá hiệu suất của nó bằng cách sử dụng tập dữ liệu và cấu hình teleprompter được cung cấp.

Quản lý Dữ liệu trong DSPy

DSPy hoạt động với các tập dữ liệu huấn luyện, phát triển và kiểm tra. Đối với mỗi ví dụ trong dữ liệu của bạn, bạn thường có ba loại giá trị: đầu vào, nhãn trung gian và nhãn cuối cùng. Trong khi nhãn trung gian hoặc cuối cùng là tùy chọn, việc có một vài ví dụ đầu vào là điều cần thiết.

Tạo Đối tượng Ví dụ

Đối tượng ví dụ trong DSPy tương tự như từ điển Python nhưng đi kèm với các tiện ích hữu ích:


<p>qa_pair = dspy.Example(question="Đây là một câu hỏi?", answer="Đây là một câu trả lời.")</p>

<p>print(qa_pair)
print(qa_pair.question)
print(qa_pair.answer)

Kết quả:


<p>Example({'question': 'Đây là một câu hỏi?', 'answer': 'Đây là một câu trả lời.'}) (input_keys=None)
Đây là một câu hỏi?
Đây là một câu trả lời.

Chỉ định Khóa Đầu vào

Trong DSPy, đối tượng ví dụ có phương thức with_inputs() để đánh dấu các trường cụ thể là đầu vào:


<p>print(qa_pair.with_inputs("question"))
print(qa_pair.with_inputs("question", "answer"))

Giá trị có thể được truy cập bằng cách sử dụng toán tử chấm, và các phương thức như inputs() và labels() trả về các đối tượng ví dụ mới chứa chỉ các khóa đầu vào hoặc không phải đầu vào, tương ứng.

Tối ưu hóa trong DSPy

Một tối ưu hóa của DSPy điều chỉnh các tham số của một chương trình DSPy (tức là nhắc nhở và/hoặc trọng số LM) để tối đa hóa các metric được chỉ định. DSPy cung cấp nhiều tối ưu hóa tích hợp sẵn, mỗi tối ưu hóa sử dụng các chiến lược khác nhau.

Tối ưu hóa Có sẵn

  • BootstrapFewShot: Tạo ví dụ vài lần bằng cách sử dụng dữ liệu đầu vào và đầu ra được dán nhãn.
  • BootstrapFewShotWithRandomSearch: Áp dụng BootstrapFewShot nhiều lần với tìm kiếm ngẫu nhiên trên các bản demo được tạo.
  • COPRO: Tạo và tinh chỉnh hướng dẫn mới cho mỗi bước, tối ưu hóa chúng bằng cách tăng dần.
  • MIPRO: Tối ưu hóa hướng dẫn và ví dụ vài lần bằng cách sử dụng tối ưu hóa Bayesian.

Chọn Tối ưu hóa

Nếu bạn không chắc chắn về nơi bắt đầu, hãy sử dụng BootstrapFewShotWithRandomSearch:

Đối với rất ít dữ liệu (10 ví dụ), sử dụng BootstrapFewShot.
Đối với một chút nhiều dữ liệu (50 ví dụ), sử dụng BootstrapFewShotWithRandomSearch.
Đối với các tập dữ liệu lớn hơn (300+ ví dụ), sử dụng MIPRO.

Dưới đây là cách sử dụng 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)

Lưu và Tải Chương trình Tối ưu hóa

Sau khi chạy một chương trình qua một tối ưu hóa, hãy lưu nó để sử dụng trong tương lai:

optimized_program.save(YOUR_SAVE_PATH)

Tải một chương trình đã lưu:


<p>loaded_program = YOUR_PROGRAM_CLASS()
loaded_program.load(path=YOUR_SAVE_PATH)

Tính năng Nâng cao: Tuyên bố DSPy

Tuyên bố DSPy tự động hóa việc thực thi các ràng buộc tính toán trên LM, tăng cường độ tin cậy, dự đoán và chính xác của đầu ra LM.

Sử dụng Tuyên bố

Định nghĩa các hàm xác thực và tuyên bố sau khi tạo mô hình. Ví dụ:


<p>dspy.Suggest(
len(query) &lt;= 100,
&quot;Query nên ngắn và ít hơn 100 ký tự&quot;,
)

<p>dspy.Suggest(
validate_query_distinction_local(prev_queries, query),
"Query nên khác biệt so với: " + "; ".join(f"{i+1}) {q}" for i, q in enumerate(prev_queries)),
)

Chuyển đổi Chương trình với Tuyên bố


<p>from dspy.primitives.assertions import assert_transform_module, backtrack_handler</p>

<p>baleen_with_assertions = assert_transform_module(SimplifiedBaleenAssertions(), backtrack_handler)

Hoặc, kích hoạt tuyên bố trực tiếp trên chương trình:


<p>baleen_with_assertions = SimplifiedBaleenAssertions().activate_assertions()

Tối ưu hóa Được dẫn dắt bởi Tuyên bố

Tuyên bố DSPy hoạt động với các tối ưu hóa của DSPy, đặc biệt là với BootstrapFewShotWithRandomSearch, bao gồm các thiết lập như:

  • Biên dịch với Tuyên bố
  • Biên dịch + suy luận với Tuyên bố

Kết luận

DSPy cung cấp một cách tiếp cận mạnh mẽ và hệ thống để tối ưu hóa các mô hình ngôn ngữ và nhắc nhở của chúng. Bằng cách làm theo các bước được phác thảo trong những ví dụ này, bạn có thể xây dựng, tối ưu hóa và đánh giá các hệ thống AI phức tạp một cách dễ dàng. Thiết kế mô-đun của DSPy và các tối ưu hóa tiên tiến cho phép tích hợp hiệu quả và hiệu quả các mô hình ngôn ngữ khác nhau, làm cho nó trở thành một công cụ quý giá cho bất kỳ ai làm việc trong lĩnh vực NLP và AI.

Bất kể bạn đang xây dựng một hệ thống trả lời câu hỏi đơn giản hay một đường ống phức tạp hơn, DSPy cung cấp sự linh hoạt và độ tin cậy cần thiết để đạt được hiệu suất cao và độ tin cậy.

Tôi đã dành 5 năm qua để đắm mình trong thế giới hấp dẫn của Máy học và Học sâu. Đam mê và chuyên môn của tôi đã dẫn tôi đến việc đóng góp vào hơn 50 dự án kỹ thuật phần mềm đa dạng, với sự tập trung đặc biệt vào AI/ML. Sự tò mò liên tục của tôi cũng đã thu hút tôi đến với Xử lý Ngôn ngữ Tự nhiên, một lĩnh vực tôi渴望 khám phá thêm.