ExampleSelector 示例选择器

ExampleSelector 的作用

ExampleSelector 是与 FewShotPromptTemplate 联合使用的,ExampleSelector的主要作用是从一个较大的示例样本库中选择最合适、最有代表性的样本来进行学习和推理,从而提高模型输出内容的质量。

ExampleSelector 四种类型选择器

类型名称 相关类 应用场景
按长度 LengthBasedExampleSelector 根据提示文本长度来选择示例
按最大边际相关性 (MMR) MaxMarginalRelevanceExampleSelector 从示例库中选择与输入即相关又具有多样性的示例,需要用到向量数据库
按 n-gram 重叠度 NGramOverlapExampleSelector 通过计算输入与示例之间n-gram(如单词、短语)的重叠度来选择最相关的示例
按相似度 SemanticSimilarityExampleSelector 基于语义相似性选择示例,需要用到向量数据库

基于长度的示例选择器 LengthBasedExampleSelector

根据示例的长度来选择示例,解决提示内容长度超出大模型处理长度。对于较长的输入,它会选择较少的示例,而对于较短输入,他会选择更多的示例。

  1. from langchain.prompts import FewShotPromptTemplate, PromptTemplate
  2. from langchain.prompts.example_selector import LengthBasedExampleSelector
  3. # 创建一个包含词语及其反义词的示例集合。
  4. examples = [
  5. {"input": "光明", "output": "黑暗"},
  6. {"input": "快乐", "output": "悲伤"},
  7. {"input": "上升", "output": "下降"},
  8. {"input": "胜利", "output": "失败"},
  9. {"input": "繁荣", "output": "衰败"},
  10. ]
  11. # 定义如何将示例数据格式化为字符串的模板。
  12. example_prompt = PromptTemplate(
  13. input_variables=["input", "output"],
  14. template="词语: {input}\n反义词: {output}",
  15. )
  16. # 使用LengthBasedExampleSelector选择符合长度要求的示例。
  17. example_selector = LengthBasedExampleSelector(
  18. examples=examples,
  19. example_prompt=example_prompt,
  20. # 假设我们希望每个示例的格式化字符串长度不超过10个字符。
  21. max_length=10,
  22. )
  23. # 创建FewShotPromptTemplate,动态地根据输入选择示例并生成提示。
  24. dynamic_prompt = FewShotPromptTemplate(
  25. example_selector=example_selector,
  26. example_prompt=example_prompt,
  27. prefix="根据词语说出反义词大赛",
  28. suffix="轮到你了,词语: {input}\n反义词:",
  29. input_variables=["input"],
  30. )
  31. print(dynamic_prompt.format(input="努力"))

上面代码中在构建LengthBasedExampleSelector实例时,通过更改max_length的大小,会直接影响到在示例提示中的数量。
LengthBasedExampleSelector的构造参数说明:

  • example_prompt (PromptTemplate) [必需]: 用于格式化示例的提示模板。这个模板定义了如何将提供的示例数据通过这个提示模版转换为提示字符串。
  • examples (List[dict]) [必需]: 示例列表,包含提示模板期望的所有示例。每个示例是一个字典,包含了与提示模板中定义的变量相对应的键值对。
  • get_text_length (Callable[[str], int] = ): 一个函数,用于测量提示长度,默认为计算字数的函数。这个函数的目的是根据自己需要来计算提示内容的长度,例如去掉空格的字符串长度。
  • max_length (int = 2048): 提示的最大长度,输入到模型中的文本总长度,长度单位是token,超过这个长度的示例会被裁剪。这个参数允许用户设置最大长度,用于处理模版生成的提示不会超过模型处理能力。

    基于最大边际相关性的示例选择器 MaxMarginalRelevanceExampleSelector

    MaxMarginalRelevanceExampleSelector 最大边际相关性示例选择器,可以让我们在构建少样本学习的提示模板时,确保选择的示例不仅与提问的问题高度相关,同时也尽可能地覆盖更广的信息范围,避免过度集中于某一特定类型或主题的示例。这样,即使在示例数量有限的情况下,也能最大化地利用这些示例来训练或指导大模型,提高大模型的输出质量。
    什么是最大边际相关性?
    Maximal Marginal Relevance (MMR) 是一种在信息检索和自然语言处理中常用的策略,旨在解决当我们面对大量相关信息时,如何选择最有价值的部分呈现给用户的问题。简单来说,MMR 试图在相关性(即信息与查询的匹配度)和多样性(不同信息之间的差异)之间找到一个最佳平衡。
    举个例子,如果你要组织一场关于“未来科技”的展览,并且你需要从一大堆的项目中选择几个来展示。你的目标是让参观者全方位的感受到未来科技带来的震撼。
  1. 从相关性进行选择:首先,你肯定想要展示那些最能代表“未来科技”主题的项目。比如,如果有一个项目是关于先进的人工智能(AI),而另一个项目是关于怎样种植更好的番茄,很明显,人工智能项目与“未来科技”的主题更加相关。
  2. 从多样性进行选择:但是,如果你只选择与人工智能相关的项目,那么展览就会显得单调,缺乏全面性。即使这些项目都非常优秀且紧贴主题,但整体上,它们可能覆盖的领域都很相似。因此,你可能还会选择一些关于空间探索、生物技术、可持续能源等其他领域的项目,以确保参观者能够接触到未来科技的多个方面。

这就是 MMR 策略的精髓所在,在选择要展示的项目时,MMR 会帮助你找到那些既高度相关(比如紧密贴合“未来科技”这一主题)又具有多样性(涵盖不同的未来科技领域)的项目组合。通过这种方式,你能够保证展览既丰富又全面,让参观者从多个角度了解未来科技,而不是仅仅从单一的视角。

  1. from langchain.prompts import FewShotPromptTemplate, PromptTemplate
  2. from langchain.prompts.example_selector import MaxMarginalRelevanceExampleSelector
  3. from langchain_community.vectorstores import FAISS
  4. from langchain_community.embeddings.openai import OpenAIEmbeddings
  5. import os
  6. example_prompt = PromptTemplate(
  7. input_variables=["input", "output"],
  8. template="输入: {input}\n输出: {output}",
  9. )
  10. # 创建反义词任务的示例
  11. examples = [
  12. {"input": "黑暗", "output": "光明"},
  13. {"input": "寒冷", "output": "温暖"},
  14. {"input": "干燥", "output": "湿润"},
  15. {"input": "安静", "output": "喧闹"},
  16. {"input": "平滑", "output": "粗糙"},
  17. ]
  18. # 配置 OpenAI 服务
  19. OPENAI_API_KEY = os.getenv('OPENAI_API_KEY') ## 设置openai的key
  20. OPENAI_API_BASE = os.getenv('OPENAI_BASE_URL') ## 更换为代理地址
  21. print(os.getenv("OPENAI_API_KEY"))
  22. print(os.getenv("OPENAI_BASE_URL"))
  23. example_selector = MaxMarginalRelevanceExampleSelector.from_examples(
  24. # 可供选择的示例列表
  25. examples,
  26. # 用于生成语义相似性嵌入的嵌入类
  27. OpenAIEmbeddings(openai_api_base=OPENAI_API_BASE, openai_api_key=OPENAI_API_KEY),
  28. # 用于存储嵌入并进行相似性搜索的VectorStore类
  29. FAISS,
  30. # 要生成的示例数量
  31. k=2,
  32. )
  33. mmr_prompt = FewShotPromptTemplate(
  34. # 我们提供一个示例选择器,而不是示例
  35. example_selector=example_selector,
  36. example_prompt=example_prompt,
  37. prefix="给出每个输入的反义词",
  38. suffix="输入: {adjective}\n输出:",
  39. input_variables=["adjective"],
  40. )
  41. # 如果输入是一个情感或属性,应该首先选择相关的示例
  42. print(mmr_prompt.format(adjective="快乐"))

在上方的代码示例中,MaxMarginalRelevanceExampleSelector的构造方法参数说明如下:

  1. examples:这是一个列表,包含了所有可供选择的示例。每个示例通常是一个字典,包含了一组键值对。
  2. embeddings:embedding示例嵌入向量方式对应类实例,上方代码示例用的是OpenAIEmbeddings。
  3. vectorstore_cls:向量数据库对应的类,例如 FAISS。
  4. k:这是一个整数,指定了要从示例集合中选择的示例数量。
  5. input_keys(可选):这是一个字符串列表,指定了示例数据中应该考虑哪些键(字段)来进行相似性搜索,例如上方代码示例中的examples中的input字段,那么在进行相似性搜索的时候,会主要在input这个字段。
  6. fetch_k(可选):这是一个整数,指定了在进行重新排名(reranking)之前,初步从向量存储中检索的示例数量。
  7. vectorstore_kwargs(可选):这是一个字典,这些参数可以用于进一步定制向量检索的方式,例如指定搜索算法的参数。

    基于相似性的示例选择器 SemanticSimilarityExampleSelector

    基于语义相似性选择示例,它与MaxMarginalRelevanceExampleSelector的用法一样,如下为SemanticSimilarityExampleSelector实例代码,他与MaxMarginalRelevanceExampleSelector代码示例的区别就是类名不一样,其他的参数完全相同。
    1. example_selector = SemanticSimilarityExampleSelector.from_examples(
    2. # 可供选择的示例列表
    3. examples,
    4. # 用于生成语义相似性嵌入的嵌入类
    5. OpenAIEmbeddings(openai_api_base=OPENAI_API_BASE, openai_api_key=OPENAI_API_KEY),
    6. # 用于存储嵌入并进行相似性搜索的VectorStore类
    7. FAISS,
    8. # 要生成的示例数量
    9. k=2,
    10. )

    基于按 n-gram 重叠度的示例选择器

    通过计算输入与示例之间n-gram(如单词、短语)的重叠度来选择最相关的示例,举例来说:
    n-gram,例如你在一个图书馆里寻找关于“怎样种植苹果树”的书籍,但图书馆太大,书太多,你不可能一本本查看。这时,n-gram 就像一个聪明的图书管理员,它通过检查书籍的目录和你的问题中关键词的搭配(比如“种植”和“苹果树”),快速帮你找到几本最相关的书。
  1. from langchain.prompts import FewShotPromptTemplate, PromptTemplate
  2. from langchain.prompts.example_selector.ngram_overlap import NGramOverlapExampleSelector
  3. # 示例 prompt 模版
  4. example_prompt = PromptTemplate(
  5. input_variables=["input", "output"],
  6. template="输入: {input}\n输出: {output}",
  7. )
  8. # 示例列表
  9. examples = [
  10. {"input": "See Spot run.", "output": "Ver correr a Spot."},
  11. {"input": "My dog barks.", "output": "Mi perro ladra."},
  12. {"input": "Spot can run.", "output": "Spot puede correr."},
  13. ]
  14. # 中文提示貌似不支持
  15. example_selector = NGramOverlapExampleSelector(
  16. # 示例列表。
  17. examples=examples,
  18. # 用于格式化示例的PromptTemplate。
  19. example_prompt=example_prompt,
  20. # 词语重叠度,0.0-1.0区间
  21. # 通过调整threshold在-1.0,0.0,1.0,以及0.0-1.0区间,观察示例的选择的变化
  22. threshold=0.0,
  23. )
  24. dynamic_prompt = FewShotPromptTemplate(
  25. # 我们提供一个ExampleSelector而不是示例。
  26. example_selector=example_selector,
  27. example_prompt=example_prompt,
  28. prefix="将输入的英语翻译成西班牙语",
  29. suffix="输入: {sentence}\n输出:",
  30. input_variables=["sentence"],
  31. )
  32. print(dynamic_prompt.format(sentence="Spot can run fast."))

参数 threshold

在 NGramOverlapExampleSelector 类中用于控制示例选择的阈值。这个阈值决定了哪些示例将被选中用于进一步处理,基于它们与输入的n-gram重叠分数。n-gram重叠分数是一个浮点数,介于0.0到1.0之间,用于衡量两段文本之间的n-gram重叠度,数值越大,选择出来的示例重叠度越高。

  • threshold = -1.0:默认值,所有的示例都会包含,但会根据它们与输入的n-gram重叠分数进行排序。
  • threshold > 1.0:所有示例都被排除。
  • threshold = 0.0:只有那些至少与输入提示有一个n-gram重叠的示例会被选中,这意味着完全不相关的示例会被排除。

    举例说明

    假设我们有以下输入句子和三个示例:

  • 输入句子:”我喜欢吃苹果。”

  • 示例1:”我不喜欢吃香蕉。”(与输入有一些词的重叠)
  • 示例2:”我喜欢吃葡萄。”(与输入有较多词的重叠)
  • 示例3:”太阳在东方升起。”(与输入没有重叠)

根据不同的 threshold 设置,示例的选择如下:

  • threshold = -1.0:示例都包含,并根据与输入句子的n-gram重叠分数排序。因此,示例2、示例1和示例3按这个顺序被排序。
  • threshold = 0.0:只包含示例1和示例2,因为它们至少与输入有一个n-gram重叠。示例3被排除,因为它与输入没有任何n-gram重叠。

    四种类型选择器的区别和应用场景

    | 选择器 | 场景 | 举例 | | —- | —- | —- | | LengthBasedExampleSelector | 在限定提示长度前提下,尽可能多的选择示例数量 | 常规的提示,限定提问大模型的提示内容长度。 | | MaxMarginalRelevanceExampleSelector | 示例选择的相关性和多样性 | 在为研究人员提供综述文章或文献综合分析的服务中,用户希望探索一个主题的不同方面和视角,例如,“可持续能源的最新研究趋势是什么?”
    此时,MaxMarginalRelevanceExampleSelector被用来选择覆盖该主题不同子领域(如太阳能、风能、生物质能源)的文献示例,确保提供给用户的信息既全面又具有多样性,避免内容的单一化。 | | NGramOverlapExampleSelector | N-Gram词语的重叠性 | 在提供技术支持或解决具体问题的服务中,用户可能会提出非常具体的技术问题,如,“如何在Ubuntu 20.04上安装NVIDIA驱动?”
    在这种情况下会选择与用户问题在词汇层面上有高度重叠的技术文档或先前问答示例。这种方法确保了模型能够提供准确、针对性的操作指南,直接解答用户的具体疑问。 | | SemanticSimilarityExampleSelector | 示例语义上的相关性 | 在心理咨询或健康问答服务中,用户可能会表达一些含糊的感受或症状,例如,“最近总是感到焦虑和失眠。”
    通过分析这些查询的深层语义内容,选择与用户描述在情感或症状上最相似的健康咨询示例。这允许模型以更加细腻和同理心的方式回应用户的感受,提供个性化且贴切的建议或解决方案。 |

N-Gram 和相似性的区别

示例文本

  • 文本A: “快乐的孩子在海边玩耍。”
  • 文本B: “在海滩上,欢乐的儿童正在嬉戏。”

    N-Gram分析

    假设我们使用bigram(2-gram)来分析这两段文本,从相同词语重叠的角度分析:

  • 文本A的部分2-grams包括:”快乐的 孩子”,”孩子 在”,”在 海边”,”海边 玩耍”。

  • 文本B的部分2-grams包括:”在 海滩”,”海滩 上”,”欢乐的 儿童”,”儿童 正在”,”正在 嬉戏”。

从N-Gram的角度看,这两段文本没有共享的bigrams,因此根据N-Gram分析,它们之间的相似度可能被认为是低的。

语义相似性分析

然而,当我们从语义相似性的角度来看:

  • 尽管使用了不同的词语(如“快乐的”与“欢乐的”,“孩子”与“儿童”,“海边”与“海滩”,“玩耍”与“嬉戏”),两段文本都表达了一个非常相似的场景:快乐的小孩在海边玩耍。
  • 一个深度学习模型,如基于BERT的语义相似性分析工具,能够识别这两段文本在意义上的高度一致性。这是因为这样的模型能够理解词语的同义关系和上下文中的语义内容。

    结论

    尽管N-Gram分析显示文本A和文本B在表面结构上的相似度很低(几乎没有重叠的bigrams),语义相似性分析却能够揭示出两者在深层意义上的强烈相似性。这个例子展示了N-Gram分析和语义相似性分析在文本处理上的根本区别:前者依赖于表面的字词结构,而后者深入到语义层面,理解文本的真正含义。