文章目录
- 少样本提示(few-shorting)
-
- [示例选择器(Example selectors)](#示例选择器(Example selectors))
-
- 1.概念
- 2.按长度选择示例(Length)
- 3.按语义相似性选择示例 (Similarity)
- [4. 按最大边际相关性选择示例(MMR)](#4. 按最大边际相关性选择示例(MMR))
- 5.通过ngram重叠选择示例(Ngram)
少样本提示(few-shorting)
示例选择器(Example selectors)
1.概念
一旦我们有了示例数据集,就需要考虑提示中应该有多少个示例。关键的权衡是,更多的示例通常会提高性能,但更大的提示会增加成本和延迟。超过某个阈值,太多示例可能会开始混淆模型。
找到正确数量的示例在很大程度上取决于模型、任务、示例的质量以及成本和延迟限制。有趣的是,模型越好,它需要精准的示例就越少。但其实,最佳的方法是使用不同数量的示例进行一些实验。
若此时我们有【大量】的示例数据集。对于大模型来说,就没必要全部使用与参考。我们需要有一种方法可以根据给定的输入,从数据集中选择示例。
在 LangChain 中,示例选择器就可以帮我们从一组【示例的集合】中根据具体策略选择正确的【示例子集】构建少样本提示
选择策略有:
-
Length:根据特定【长度】内可以容纳的数量选择示例。 -
Similarity:使用输入和示例之间的【语义相似性】来决定选择哪些示例。 -
MMR:使用输入和示例之间的【最大边际相关性】来决定要选择哪些示例。 -
Ngram:使用输入和示例之间的【ngram重叠】来决定要选择哪些示例。
这些其实都是自然语言处理(NLP)里的相似性衡量问题。
2.按长度选择示例(Length)
当我们担心构造提示时,将超过上下文窗口长度,根据特定长度内可以容纳的数量选择示例。对于较长的输入,它将选择更少的示例来包含;而对于较短的输入,它将选择更多示例。
实现按长度选择示例的示例选择器是:
class langchain_core.example_selectors.length_based.LengthBasedExampleSelectr类,其参数如下:
example_prompt: PromptTemplate,用于格式化示例的提示模板。examples: 模板所需的示例列表。max_length: 提示的最大长度,超过该长度将剪切示例。get_text_length: 测量提示长度的方法。默认为字数统计。
内置方法:
add_example(example: dict[str, str]):将新示例添加到列表中。输入:一个字典,其中键作为输入变量,值作为其值。select_examples(input_variables: dict[str, str]) → list[dict]:根据输入长度选择要使用的示例。输入:一个字典,其中键作为输入变量,值作为其值。输出:要包含在提示中的示例列表。
接下来,演示一下如何使用长度示例选择器,让我们:
- 给一个示例集,输入和输出互为反义词
- 定义
PromptTemplate字符串模板,包含输入和输出两个"占位符" - 定义
LengthBasedExampleSelector长度示例选择器,设置初始示例集与最大长度 - 定义一个
FewShotPromptTemplate模板对象,用于实例化示例,将示例转化为聊天消息 - 打印消息结果
python
from langchain_core.example_selectors import LengthBasedExampleSelector
from langchain_core.prompts import FewShotPromptTemplate, PromptTemplate
# 反义词示例集合
examples = [
{"input": "happy", "output": "sad"},
{"input": "tall", "output": "short"},
{"input": "energetic", "output": "lethargic"},
{"input": "sunny", "output": "gloomy"},
{"input": "windy", "output": "calm"},
]
# 字符串模板
example_prompt = PromptTemplate(
input_variables=["input", "output"],
template="Input: {input}\nOutput: {output}",
)
# 长度示例选择器
example_selector = LengthBasedExampleSelector(
examples=examples,
example_prompt=example_prompt,
# 格式化示例的最大长度。
# 长度由下面的get_text_length函数测量。
max_length=25,
# 用于获取字符串长度的函数,用于确定包含哪些示例。
# 如果没有指定,它是作为默认值提供的。
# 该函数返回一个整数,表示字符串中由换行符或空格分隔的"单词"数量
# get_text_length: Callable[[str], int] = lambda x: len(re.split("\n| ", x))
)
# 用于实例化示例的模板
dynamic_prompt = FewShotPromptTemplate(
# 提供了一个ExampleSelector而不是examples。
example_selector=example_selector,
example_prompt=example_prompt,
prefix="给出每个输入的反义词",
suffix="Input: {adjective}\nOutput:",
input_variables=["adjective"],
)
print(
dynamic_prompt.invoke({"adjective": "big"}).to_messages()[0].content
)
输出结果:
python
给出每个输入的反义词
Input: happy
Output: sad
Input: tall
Output: short
Input: energetic
Output: lethargic
Input: sunny
Output: gloomy
Input: windy
Output: calm
Input: big
Output:
可以看到,示例选择器将所有的示例都选择了,因为还没有超过设置的示例最大长度,长度计算是根据字符串中由换行符或空格分隔的"单词"数量得到的(默认)。
接下来让我们加入长输入,验证长度选择器筛选示例。代码如下:
python
# 一个长的示例输入,所有示例超出最大长度
long_string = "非常非常非常非常非常非常非常非常非常非常非常非常非常非常非常非常非常非常非常非常非常大,比其他任何东西都要大得多"
print()
dynamic_prompt.invoke({"adjective": long_string}).to_messages()[0].content)
结果如下:
python
给出每个输入的反义词
Input: happy
Output: sad
Input: tall
Output: short
Input: 非常 非常 非常 非常 非常 非常 非常 非常 非常 非常 非常大,比其他任何东西都要大得多
Output:
3.按语义相似性选择示例 (Similarity)
概念
什么是语义相似?它是衡量文本在【含义上】的接近程度。例如下述两段文本:
python
text1 = "我喜欢猫"
text2 = "我讨厌狗"
这两段文本表面相似度低,但语义上都是表达对动物的态度。
再例如:
python
text1 = "苹果很甜"
text2 = "苹果市值创新高"
"苹果"可以指水果或公司,语义相似可以解决一词多义问题,因此这两段文本语义上不相似。
LangChain 能根据输入和示例之间的语义相似性来决定选择哪些示例,它通过查找与输入具有最大余弦相似性的嵌入示例来实现这一点。
实现
实现按语义相似性选择示例的示例选择器是:class langchain_core.example_selectors.semantic_similarity.SemanticSimilarityExampleSelector 类,
内置方法:
from_examples():根据示例集生成语义相似示例选择器- 输入:
examples示例列表;embeddings初始化的嵌入API接口,如OpenAIEmbeddings();vectorstore_cls向量存储数据库接口类;k最终要选择的示例的数量,默认值为4。 - 输出:语义相似性示例选择器
- 输入:
add_example(example: dict[str, str]):将新示例添加到列表中。select_examples(input_variables: dict[str, str]) → list[dict]:根据输入选择要使用的示例。
接下来,演示一下如何使用语义相似示例选择器,让我们:
- 给一个示例集,输入和输出互为反义词
- 定义
PromptTemplate字符串模板,包含输入和输出两个"占位符" - 定义
SemanticSimilarityExampleSelector语义相似示例选择器,设置初始示例集与嵌入API接口 - 定义一个
FewShotPromptTemplate模板对象,用于实例化示例,将示例转化为聊天消息 - 打印消息结果
注意:运行前先安装 langchain-chroma 库:pip install -U langchain-chroma
python
from langchain_chroma import Chroma
from langchain_core.example_selectors import SemanticSimilarityExampleSelector
from langchain_core.prompts import FewShotPromptTemplate, PromptTemplate
from langchain_openai import OpenAIEmbeddings
# 反义词示例集合
examples = [
{"input": "happy", "output": "sad"},
{"input": "tall", "output": "short"},
{"input": "energetic", "output": "lethargic"},
{"input": "sunny", "output": "gloomy"},
{"input": "windy", "output": "calm"},
]
# 字符串模板
example_prompt = PromptTemplate(
input_variables=["input", "output"],
template="Input: {input}\nOutput: {output}",
)
# 语义相似示例选择器
example_selector = SemanticSimilarityExampleSelector.from_examples(
examples,
# 嵌入类用于生成用于度量语义相似度的嵌入。
OpenAIEmbeddings(),
# VectorStore类, 用于存储嵌入并对其进行相似性搜索。
Chroma,
# 生成示例的数量。
k=1,
)
# 用于实例化示例的模板
similar_prompt = FewShotPromptTemplate(
# 提供了一个ExampleSelector而不是examples。
example_selector=example_selector,
example_prompt=example_prompt,
prefix="给出每个输入的反义词",
suffix="Input: {adjective}\nOutput:",
input_variables=["adjective"],
)
print(
similar_prompt.invoke({"adjective": "worried"}).to_messages()[0].content
)
输出结果 :
python
给出每个输入的反义词
Input: happy
Output: sad
Input: worried
Output:
我们输入的内容和最后选择的示例都表示心情,在语义上最相似
4. 按最大边际相关性选择示例(MMR)
概念
什么是最大边际相关性?它是一种重新排序算法,它使用语义相似性作为基础工具,从一个候选集中挑选出一组既能代表查询主题又彼此多样化的结果。
用一个例子看下两者的区别:
【语义相似性】就像面试官衡量每个应聘者与职位要求的匹配度。他会给每个应聘者打一个分数。
【最大边际相关性】就像团队经理(MMR算法)要组建一个团队。目标是选出一组"精华"结果,而不是一个单一结果 :
每个成员都要满足基本职位要求(满足相关性)。但经理不希望团队里全是只会一种技能的程序员。他需要前端、后端、算法、测试等不同专长的人,以确保团队能力全面、减少冗余(新颖性/多样性)。经理的策略是:先招一个最匹配的技术大牛(第一步),然后接下来招的人,既要技术达标,又要和已招的人技能互补(迭代过程)。
了解下使用最大边际相关性的场景,更能让我们理解其概念:
语义相似性使用场景:搜索引擎的基础排序、重复检测、聚类、语义搜索。
MMR使用场景:
- 推荐系统:推荐与用户兴趣相关但又不同类型的物品,避免"信息茧房"。
- 文档摘要:从长文档中选择能代表主旨又包含不同信息的句子,避免摘要内容重复。
- RAG(检索增强生成):在从知识库检索完一堆相关文档后,使用MMR进行去重和多样化筛选,再交给LLM生成答案,能有效提升答案质量和减少幻觉。
实现
了解了相关概念后,LangChain提供了按最大边际相关性选择示例的能力,该示例选择器是:
class langchain_core.example_selectors.semantic_similarity.MaxMarginalRelevanceExampleSelector类,
内置方法:
from_examples():根据示例集生成MMR示例选择器- 输入:
examples示例列表;embeddings初始化的嵌入API接口,如OpenAIEmbeddings();vectorstore_cls向量存储数据库接口类;k最终要选择的示例的数量,默认值为4。 - 输出:MMR示例选择器
- 输入:
add_example(example: dict[str, str]):将新示例添加到列表中。select_examples(input_variables: dict[str, str]) → list[dict]:根据输入选择要使用的示例。
接下来,演示一下如何使用MMR示例选择器,让我们:
- 给一个示例集,输入和输出互为反义词
- 定义
PromptTemplate字符串模板,包含输入和输出两个"占位符" - 定义
MaxMarginalRelevanceExampleSelectorMMR示例选择器,设置初始示例集与嵌入API接口 - 定义一个
FewShotPromptTemplate模板对象,用于实例化示例,将示例转化为聊天消息 - 打印消息结果
python
from langchain_chroma import Chroma
from langchain_core.example_selectors import MaxMarginalRelevanceExampleSelector
from langchain_core.prompts import FewShotPromptTemplate, PromptTemplate
from langchain_openai import OpenAIEmbeddings
# 反义词示例集合
examples = [
{"input": "happy", "output": "sad"},
{"input": "tall", "output": "short"},
{"input": "energetic", "output": "lethargic"},
{"input": "sunny", "output": "gloomy"},
{"input": "windy", "output": "calm"},
]
# 字符串模板
example_prompt = PromptTemplate(
input_variables=["input", "output"],
template="Input: {input}\nOutput: {output}",
)
# MMR示例选择器
example_selector = MaxMarginalRelevanceExampleSelector.from_examples(
examples,
OpenAIEmbeddings(),
Chroma,
k=2,
)
# 用于实例化示例的模板
similar_prompt = FewShotPromptTemplate(
# 提供了一个ExampleSelector而不是examples.
example_selector=example_selector,
example_prompt=example_prompt,
prefix="给出每个输入的反义词",
suffix="Input: {adjective}\nOutput:",
input_variables=["adjective"],
)
print(
similar_prompt.invoke({"adjective": "worried"}).to_messages()[0].content
)
输出结果:
python
给出每个输入的反义词
Input: happy
Output: sad
Input: windy
Output: calm
Input: worried
Output:
"happy"(情绪)、"windy"(天气)分别代表了不同的反义关系类型,这样组合能提高模型的泛化能力
5.通过ngram重叠选择示例(Ngram)
概念
什么是【ngram】?ngram指一个文本序列中连续的n个词(word)或字符(character)。
什么是【ngram重叠】?通过计算它们之间共同拥有的ngram数量来衡量两段文本相似度的方法。
例如下述两段文本:
text1="苹果手机很好用"(分词后:苹果 手机 很好 用)
text2="这款手机很好用"(分词后:这款 手机 很好 用)
这两段文本单词重复度很高,连续三个词相同的情况也存在,因此ngram重叠高。
再看个例子:
text1 = "苹果手机很好用"(分词后:苹果 手机 很好 用)
text2 = "iPhone 非常不错"(分词后:iPhone 非常 不错)
这两段文本在含义上非常相似,但它们的ngram重叠度为0。
因此,传统ngram重叠是一种表面形式的匹配。它只关心词是否完全一样,但对于同义词却无法处理。
什么是【语义ngram重叠】?不再比较词本身,而是比较词背后的语义向量(Embedding)。也就是说,它不是看两个词[苹果]和[iPhone]的字面是否相同,而是计算它们在语义空间中的向量是否相似。如果相似度超过某个阈值,就认为它们"重叠"了。还是看这个例子:
text1 = "苹果手机很好用"(分词后:苹果 手机 很好 用)
text2 = "iPhone 非常不错"(分词后:iPhone 非常 不错)
- 计算苹果和iPhone的向量相似度 → 得分0.95(很高,视为重叠)
- 计算手机和iPhone的向量相似度 → 得分0.88(很高,但可能不会同时计分,取决于算法设计,避免重复计算)
- 计算很和非常的向量相似度 → 得分0.90(很高,视为重叠)
- 计算好用和不错的向量相似度 → 得分0.82(很高,视为重叠)
最终,语义上的unigram重叠度可能为3或4(非常相似!)。
语义ngram重叠常用于需要更精准语义评估的场景,例如剽窃检测,能够发现那些改换了词汇但保留了核心思想的"智能"剽窃。
实现
LangChain实现按语义ngram重叠选择示例的示例选择器是:
class langchain_community.example_selectors.ngram_overlap.NGramOverlapExampleSelector类,
其参数如下:
example_prompt: PromptTemplate,用于格式化示例的提示模板。examples: 模板所需的示例列表。threshold: 算法停止的阈值。默认设置为 - 1.0。- 对于负阈值:按[重叠分数]对示例进行排序,但不排除任何示例。
- 对于等于0.0的阈值:按[重叠分数]进行排序,并排除与输入没有ngram重叠的示例。
- 对于大于1.0的阈值:排除所有示例,并返回一个空列表。
内置方法:
add_example(example: dict[str, str]): 将新示例添加到列表中。select_examples(input_variables: dict[str, str]) → list[dict]: 返回根据输入得到的重叠分数排序的降序示例列表。
接下来,演示一下如何使用语义相似示例选择器,让我们:
-
给一个示例集,设置相关文本
-
定义
PromptTemplate字符串模板,包含输入和输出两个"占位符" -
定义
NGramOverlapExampleSelector示例选择器,设置初始示例集与阈值(-1),表示对示例进行排序,但不排除任何示例。 -
定义一个
FewShotPromptTemplate模板对象,用于实例化示例,将示例转化为聊天消息 -
打印消息排序结果
注意:
- 运行前先安装
nltk库:pip install nltk - 运行前先安装
langchain_community库:pip install -U langchain_community
python
from langchain_community.example_selectors import NGramOverlapExampleSelector
from langchain_core.prompts import FewShotPromptTemplate, PromptTemplate
# 翻译示例
examples = [
{"input": "See Spot run.", "output": "看见Spot跑。"},
{"input": "My dog barks.", "output": "我的狗叫。"},
{"input": "Spot can run.", "output": "Spot可以跑。"},
]
# 字符串模板
example_prompt = PromptTemplate(
input_variables=["input", "output"],
template="Input: {input}\nOutput: {output}",
)
# NGram 示例选择器
example_selector = NGramOverlapExampleSelector(
examples=examples,
example_prompt=example_prompt,
threshold=-1.0
)
# 用于实例化示例的模板
dynamic_prompt = FewShotPromptTemplate(
example_selector=example_selector,
example_prompt=example_prompt,
prefix="给出每个输入的中文翻译",
suffix="Input: {sentence}\nOutput:",
input_variables=["sentence"],
)
# 按照重叠分数排序
print(
dynamic_prompt.invoke({"sentence": "Spot can run fast."}).to_messages()[0].content
)
结果如下:
给出每个输入的中文翻译
Input: Spot can run.
Output: Spot可以跑。
Input: See Spot run.
Output: 看见Spot跑。
Input: My dog barks.
Output: 我的狗叫。
Input: Spot can run fast.
Output:
分别设置 threshold = 0.0 和 1.0 进行测试
threshold = 0.0 (表示排除不相关的示例)
给出每个输入的中文翻译
Input: Spot can run.
Output: Spot可以跑。
Input: See Spot run.
Output: 看见Spot跑。
Input: Spot can run fast.
Output:
threshold = 1.0 (排除所有示例)
给出每个输入的中文翻译
Input: Spot can run fast.
Output: