当前位置:   article > 正文

【LLM大模型】你还在为写好Prompt而头疼吗,带你走进DSPy-Program LLMs之初体验

【LLM大模型】你还在为写好Prompt而头疼吗,带你走进DSPy-Program LLMs之初体验

前言

随着大模型的使用和应用越来越频繁,也越来越广泛,大家有没有陷入到无限制的研究、调优自己的prompt。

随之,市面上也出现了提示词工程师,更有专门的提示工程一说。

现在网上搜一搜,有各种各样的写提示词的技巧,包括我自己也专门写过相关的文章。

再聊prompt

不知道大家有没有感受,prompt的技巧和调优效果很明显,但是没有什么提示词策略是可以解决所有类型的问题的。

LLMs本身对提示词是非常敏感的,这意味着,在实际实验过程中,除了要求大模型输出某个内容,还需要约束它按照某种格式,各种条件约束下输出,以确保整体的稳定性。你稍微改变条件或者数据,都会导致结果的返回发生变化,甚至有时候天差地别,你又需要重新调整或者优化prompt。

关于prompt我有两个问题:

  1. 根据OpenAI的工程师自述,起先在开发大模型的过程中,并没有提示工程这一说,而是后来在使用过程中大家发现通过一些技巧可以提高大模型的涌现能力。实际上就是大量的玩家在试玩大模型之后总结出的技巧。

我什么时候能不费劲巴拉的写各种各样的prompt。

  1. 都说我们看待LLM要像对待“人”一样,什么时候我真正能像和一个“人”沟通,能理解我在说什么和我需要什么 。

Hello Programming-DSPy

在这个过程中,DSPy一种program的全新的大模型应用方式出现了。

在这里我们不讨论两者的好与坏,随着LLM的多模态能力的发展和Multil Agents的发展,没准哪天就可以傻瓜式的提问了。

DSPy是什么

DSPy [1] 是斯坦福大学 NLP 研究人员开发的 "基础模型编程 "框架, 是一个旨在解决基于语言模型(LM)的应用程序脆弱性问题的框架,它优先考虑编程而不是提示。它允许您重新编译整个流水线,以便根据您的特定任务进行优化,而不是每当您更改一个组件时,就重复一轮人工提示工程。

DSPy 还将程序的信息流与每一步的参数(提示和 LM 权重)分离开来,为构建基于 LM 的应用程序提供了更系统的方法。然后,DSPy 将根据您的程序,自动优化如何针对您的特定任务提示(或微调)LM。

DSPy的核心思想

  • Signatures:手动设计的提示 -> 用签名取代

是我们告诉DSPY需要做什么,而不是告诉他应该如何做。例如:输入是文档,输出是摘要;输入是上下文+问题,输出是问题回复。

  • Modules:提示技巧 -> 转变为模块化

在DSPY是一些模块化的组件,比如说:dspy.ChainOfThought、dspy.ProgramOfThought,类似于一些可调用的函数

  • Optimizers:复杂的提示工程 -> 通过提词器和DSPy编译器自动完成

根据某个指标对整个流程自动优化。

DSPy是如何工作的

image.png

使用DSPy构建基于LM的应用的工作流程如上图所示,与训练神经网络的工作流程类似。

  1. 收集数据集:收集一些程序输入和输出的示例(例如,问答对),这些示例将用于优化pipeline。
  2. 编写DSPy程序:用签名(signature)和模块(module)以及组件之间的信息流定义程序的逻辑,以解决你的任务。
  3. 定义验证逻辑:使用验证度量和优化器(teleprompter)定义优化程序的逻辑。
  4. 编译DSPy程序:DSPy编译器考虑训练数据、编写程序、优化器和验证度量,以优化程序(例如,提示或微调)。
  5. 迭代:通过改进数据、编写程序或验证来重复该过程,直到对pipeline的性能感到满意为止。

进一步了解DSPy请参考以下链接:

举个例子

可能理解起来还有点抽象,我们再举一个例子:

比如说,我们想解决“姚明的妻子的出生年龄?”这个问题。看到这个问题,大家脑子里肯定蹦出了一堆的解决方案。这是个多跳问题,几乎不可能通过单轮搜索来解决这个问题,大多数系统可以得出“姚明的妻子是谁?”,但是无法回答后续的年龄问题。多轮搜索的系统,通过生成额外的搜索,收集必要的信息,可以得出最终答案,整体上还是蛮复杂的。但是这里举个例子,如何用DSPY只需要几行代码来实现并优化这个问题。

原文链接:blog.csdn.net/weixin_5875… ,如有侵权请联系删除

python
复制代码
import dspy      

turbo = dspy.OpenAI(model='gpt-3.5-turbo')   
colbertv2 = dspy.ColBERTv2(url='http://20.102.90.50:2017/wiki17_abstracts')      
dspy.settings.configure(lm=turbo, rm=colbertv2 )   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

加载测试数据,多跳问题,可以使用HotPotQA测试

python
复制代码
from dspy.datasets import HotPotQA      

dataset = HotPotQA(train_seed=1, train_size=20, eval_seed=2023, dev_size=50, test_size=0)      trainset = [x.with_inputs('question') for x in dataset.train]   
devset = [x.with_inputs('question') for x in dataset.dev]      
len(trainset), 
len(devset)   
#(20, 50)   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

设置Signatures

签名是最小形式的输入和输出字段的元组。下面,您可以看到一些简写语法签名的示例。

与手写提示相比,签名可以通过为每个签名引导示例编译成自完善和自适应管道的prompt或fine-tune。

arduino
复制代码
"question -> answer"

"long-document -> summary"

"context, question -> answer"
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
python
复制代码
class GenerateAnswer(dspy.Signature):       
context = dspy.InputField()       
question = dspy.InputField()       
answer = dspy.OutputField()         

class GenerateSearchQuery(dspy.Signature):          
context = dspy.InputField()       
question = dspy.InputField()       
query = dspy.OutputField()   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

构建DSPY pipeline

您可能熟悉几种不同的提示技巧,例如添加Your task is to …之类的句子。或“You are a …”在提示开头,思维链(“让我们一步一步地思考”),或者在提示结尾添加 “Don’t make anything up” 或“Only use the provided context”这样的句子。

对DSPy中的模块进行模板化和参数化,以抽象这些提示技术。这意味着它们用于通过应用提示、微调、增强和推理技术使DSPy签名适应任务。

将签名传递给ChainOfThought模块,然后使用输入字段contextquestion的值进行调用。

使用ChainOfThought初步实施签名“context, question -> answer”

在撰写本文时,DSPy实现了以下六个Module:

  • dspy.Predict:处理输入和输出字段,生成指令,并为指定的signature创建模板。
  • dspy.ChainOfThought:继承了Predict模块,并增加了“ Chain of Thought “处理的功能。
  • dspy.ChainOfThoughtWithHint:继承了Predict模块,并增强了ChainOfThought模块,增加了提供推理提示的选项。
  • dspy.MultiChainComparison:继承了Predict模块,并增加了多链比较的功能。
  • dspy.Retrieve:从检索器模块检索段落。
  • dspy.ReAct:旨在组成思想,行动和观察的交错步骤。
python
复制代码
from dsp.utils import deduplicate      

class SimplifiedBaleen(dspy.Module):       
def __init__(self, passages_per_hop=3, max_hops=2):           
super().__init__()              
self.generate_query = [dspy.ChainOfThought(GenerateSearchQuery) for _ in range(max_hops)]  
# 多跳,每一跳都使用一个dspy.ChainOfThought           
self.retrieve = dspy.Retrieve(k=passages_per_hop)           
self.generate_answer = dspy.ChainOfThought(GenerateAnswer)           
self.max_hops = max_hops          

def forward(self, question):           
context = []                      
for hop in range(self.max_hops):               
query = self.generate_query[hop](context=context, question=question).query       
passages = self.retrieve(query).passages               
context = deduplicate(context + passages)              
pred = self.generate_answer(context=context, question=question)           
return dspy.Prediction(context=context, answer=pred.answer)   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21

测试

python
复制代码
my_question = "How many storeys are in the castle that David Gregory inherited?"  
uncompiled_baleen = SimplifiedBaleen()     
pred = uncompiled_baleen(my_question)      
print(f"Question: {my_question}")   
print(f"Predicted Answer: {pred.answer}")   
print(f"Retrieved Contexts (truncated): {[c[:200] + '...' for c in pred.context]}")   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

优化

上面提到过,可以用某个打分来优化DSPY的结果,定义一个评估函数

  • 预测答案与真实答案相符。
  • 检索到的上下文包含真实答案
  • 生成的搜索查询不能太杂乱,小于100个字符
  • 生成的搜索查询尽量不要重复(跟历史的相比,不能超过0.8)。
python
复制代码
def validate_context_and_answer_and_hops(example, pred, trace=None):       
if not dspy.evaluate.answer_exact_match(example, pred): return False       
if not dspy.evaluate.answer_passage_match(example, pred): return False          
hops = [example.question] + [outputs.query for *_, outputs in trace if 'query' in outputs]          
if max([len(h) for h in hops]) > 100: return False       
if any(dspy.evaluate.answer_exact_match_str(hops[idx], hops[:idx], frac=0.8) for idx in range(2, len(hops))): return False          
return True   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

使用 DSPy 中的BootstrapFewShot,通过少量示例来优化流程的预测器。 在撰写本文时,DSPy实现了以下五个提词器:

  • dspy.LabeledFewShot:定义预测器使用的k个样本数。
  • dspy.BootstrapFewShot:引导Few-Shot。
  • dspy.BootstrapFewShotWithRandomSearch:继承了BootstrapFewShot提词器,并为随机搜索过程引入了额外的属性。
  • dspy.BootstrapFinetune:t将提词器定义为用于微调编译的BootstrapFewShot实例。
  • dspy.Ensemble:创建多个程序的集成版本,将不同程序的各种输出减少到单个输出。 还有SignatureOptimizer和BayesianSignatureOptimizer,它们在zero/few-shot 设置中改善模块中签名的输出前缀和指令。

不同的提词器在优化成本和质量等方面提供了不同的权衡。

python
复制代码
from dspy.teleprompt import BootstrapFewShot      
teleprompter = BootstrapFewShot(metric=validate_context_and_answer_and_hops)   
compiled_baleen = teleprompter.compile(SimplifiedBaleen(), teacher=SimplifiedBaleen(passages_per_hop=2), trainset=trainset)   
  • 1
  • 2
  • 3
  • 4
  • 5

评估

python
复制代码
from dspy.evaluate.evaluate import Evaluate         
def gold_passages_retrieved(example, pred, trace=None):       
gold_titles = set(map(dspy.evaluate.normalize_text, example["gold_titles"]))       
found_titles = set(           
map(dspy.evaluate.normalize_text, [c.split(" | ")[0] for c in pred.context])       
)       
return gold_titles.issubset(found_titles)      
evaluate_on_hotpotqa = Evaluate(devset=devset, num_threads=1, display_progress=True, display_table=5)      
uncompiled_baleen_retrieval_score = evaluate_on_hotpotqa(uncompiled_baleen, metric=gold_passages_retrieved, display=False)      
compiled_baleen_retrieval_score = evaluate_on_hotpotqa(compiled_baleen, metric=gold_passages_retrieved)      
print(f"## Retrieval Score for uncompiled Baleen: {uncompiled_baleen_retrieval_score}")   
print(f"## Retrieval Score for compiled Baleen: {compiled_baleen_retrieval_score}")         
#Output   
## Retrieval Score for uncompiled Baleen: 36.0   
## Retrieval Score for compiled Baleen: 60.0   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

DSPy与LangChain或LlamaIndex有何不同?

LangChain、LlamaIndex和DSPy都是帮助开发人员轻松构建基于LM的应用的框架。使用LangChain和LlamaIndex的典型pipeline通常使用prompt template来实现,这使得整个pipeline对组件更改非常敏感。相比之下,DSPy将构建基于LM的管道从操作prompt 转移到更贴近编程。

DSPy中新引入的编译器在更改基于LM的应用程序(如LM或数据)中的部件时,消除了任何额外的Prompt工程或微调工作。相反,开发人员可以简单地重新编译程序,以优化pipeline适应新添加的更改。因此,与LangChain或LlamaIndex相比,DSPy可以帮助开发人员以更少的努力获得pipeline的性能。

总结

本文我们一是回顾了prompt使用过程中的痛点和问题,同时介绍了DSPy一种program的全新的大模型应用方式,并通过一个例子初步了解了DSPy是如何工作的,目前生成式人工智能社区对该框架非常感兴趣。DSPy 框架引入了一系列概念,将构建基于 LM 的应用程序从手动提示工程转向编程。

如何系统的去学习AI大模型LLM ?

作为一名热心肠的互联网老兵,我意识到有很多经验和知识值得分享给大家,也可以通过我们的能力和经验解答大家在人工智能学习中的很多困惑,所以在工作繁忙的情况下还是坚持各种整理和分享。

但苦于知识传播途径有限,很多互联网行业朋友无法获得正确的资料得到学习提升,故此将并将重要的 AI大模型资料 包括AI大模型入门学习思维导图、精品AI大模型学习书籍手册、视频教程、实战学习等录播视频免费分享出来

所有资料 ⚡️ ,朋友们如果有需要全套 《LLM大模型入门+进阶学习资源包》,扫码获取~

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/知新_RL/article/detail/866020
推荐阅读
相关标签