MMR
MMR
为最大边际相关性,MaxMarginalRelevanceExampleSelector
根据示例与输入之间的相似度以及多样性来选择示例
通过寻找与输入具有最大余弦相似度的嵌入的示例,并在迭代中进行添加;同时对与已选择示例的相似度进行惩罚来进行实现
import
1 2 3 4 5 6 7 from langchain.prompts.example_selector import ( MaxMarginalRelevanceExampleSelector, SemanticSimilarityExampleSelector, ) from langchain.vectorstores import FAISSfrom langchain.embeddings import OpenAIEmbeddingsfrom langchain.prompts import FewShotPromptTemplate, PromptTemplate
构造提示模板和示例集合
1 2 3 4 5 6 7 8 9 10 11 12 13 example_prompt = PromptTemplate( input_variables=["input" , "output" ], template="Input: {input}\nOutput: {output}" , ) examples = [ {"input" : "happy" , "output" : "sad" }, {"input" : "tall" , "output" : "short" }, {"input" : "energetic" , "output" : "lethargic" }, {"input" : "sunny" , "output" : "gloomy" }, {"input" : "windy" , "output" : "calm" }, ]
创建 MaxMarginalRelevanceExampleSelector
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 example_selector = MaxMarginalRelevanceExampleSelector.from_examples( examples, OpenAIEmbeddings(), FAISS, k=2 , ) mmr_prompt = FewShotPromptTemplate( example_selector=example_selector, example_prompt=example_prompt, prefix="Give the antonym of every input" , suffix="Input: {adjective}\nOutput:" , input_variables=["adjective" ], )
FewShotPromptTemplate fomat
1 2 3 print (mmr_prompt.format (adjective="worried" ))
1 2 3 4 5 6 7 8 9 10 Give the antonym of every input Input : happyOutput : sadInput : windyOutput : calmInput : worriedOutput :
SemanticSimilarity
根据与输入的相似度选择示例,通过找到与输入具有最大余弦相似度的
embedding 例子来实现此目的
Embedding
embedding
中文翻译是“嵌入”
在⾃然语⾔处理和机器学习领域,embedding
是指将单词、短语或⽂本等离散变量转换成连续向量空间的过程
向量空间通常被称为嵌⼊空间 embedding
space ,⽣成的向量则称为嵌⼊向量 embedding vector
或向量嵌⼊vector embedding
为什么需要 embedding
LLM 有一些无法解决的问题:
LLM 的训练数据是有时间约束的,无法获取到最新的一些信息
LLM 不知道答案后开始放飞自我,出现 hallucination
应用化(例如客服),数据需要自主提供
针对上述问题,LLM
的供应商一般提供了如下机制:维护自己的数据集,用户输入 prompt
之后首先匹配自主维护的数据集,找到相关性强的部分,再结合成最终的 prompt
交给 LLM
新的问题出现了 怎么在我们的数据集中检索到和 prompt
相关的内容
数据向量是将数据表示为数值向量的形式,用于方便地进行计算、分析和处理,处理为向量后计算机才可以更好的对内容机进行相关性的判断
LangChain 这里使用了 OpenAI 的 embedding API 生成内容对应的 embedding
vectors
1 2 3 4 5 6 7 example_selector = MaxMarginalRelevanceExampleSelector.from_examples( examples, OpenAIEmbeddings(), FAISS, k=2 , )
也提供了很多 LLM 的 API,例如微软 Azure 的有
AzureOpenAIEmbeddings
不过需要部署相应的模型才能使用,模型不匹配会提示
1 The embeddings operation does not work with the specified model, gpt-35 -turbo-16 k. Please choose different model and try again.
N-gram overlap
NGramOverlapExampleSelector
根据示例与输入之间的 n-gram
重叠程度选择和排序示例
N-gram 重叠得分是一个在 0.0 到 1.0 之间(闭区间)的浮点数
选择器允许设置阈值分数,n-gram
重叠得分小于或等于阈值的示例将被排除在外;默认情况下,阈值设置为
-1.0,因此不会排除任何示例
设置提示模板和示例
1 2 3 4 5 6 7 8 9 10 11 example_prompt = PromptTemplate( input_variables=["input" , "output" ], template="Input: {input}\nOutput: {output}" , ) examples = [ {"input" : "See Spot run." , "output" : "Ver correr a Spot." }, {"input" : "My dog barks." , "output" : "Mi perro ladra." }, {"input" : "Spot can run." , "output" : "Spot puede correr." }, ]
创建 NGramOverlapExampleSelector 选择器
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 example_selector = NGramOverlapExampleSelector( examples=examples, example_prompt=example_prompt, threshold=-1.0 , ''' 对于负阈值: Selector 按 ngram 重叠分数对示例进行排序,不排除任何示例 对于大于 1.0 的阈值: 选择器排除所有示例,并返回一个空列表 对于等于 0.0 的阈值: Selector 根据 ngram 重叠分数对示例进行排序,并且排除与输入没有 ngram 重叠的那些 ''' ) dynamic_prompt = FewShotPromptTemplate( example_selector=example_selector, example_prompt=example_prompt, prefix="Give the Spanish translation of every input" , suffix="Input: {sentence}\nOutput:" , input_variables=["sentence" ], )
FewShotPromptTemplate fomat
1 2 3 print (dynamic_prompt.format (sentence="Spot can run fast." ))
1 2 3 4 5 6 7 8 9 10 11 12 13 Give the Spanish translation of every input Input : Spot can run.Output : Spot puede correr.Input : See Spot run.Output : Ver correr a Spot.Input : My dog barks.Output : Mi perro ladra.Input : Spot can run fast.Output :
添加新的示例到选择器
1 2 3 4 5 new_example = {"input" : "Spot plays fetch." , "output" : "Spot juega a buscar." } example_selector.add_example(new_example) print (dynamic_prompt.format (sentence="Spot can run fast." ))
N-gram 操作
N-gram(n
元语法)是自然语言处理中的一种常用技术,用于将文本分割成连续的、固定长度的片段
一个 N-gram 是由 N 个连续的词或字符组成的序列
在N-gram中,N代表片段中的词或字符的数量。例如:
2-gram(也称为 bigram)由两个连续的词组成,例如
natural language
、machine learning
3-gram(也称为 trigram)由三个连续的词组成,例如
the quick brown
、learn from data
需要注意的是,较小的 N 值可能无法捕捉到长距离的语言依赖关系,而较大的
N 值可能会导致数据稀疏问题;因此,选择合适的 N
值对于具体任务和数据集是需要仔细考虑的
示例,将 "Today is a good day" 使用 2-gram 拆分多个字符串:
"Today is"
"is a"
"a good"
"good day"
Elasticsearch 中的模糊(wildcard)搜索一般就是使用 N-gram
分词器来实现
创建一个测试索引,配置 N-gram 分词器
关于 N-gram 分词器支持的参数可以参考 [N-gram
tokenizer | Elasticsearch Guide [8.10] | Elastic]
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 PUT /test-ngram-index { "settings" : { "analysis" : { "analyzer" : { "my_analyzer" : { "tokenizer" : "my_ngram_tokenizer" } } , "tokenizer" : { "my_ngram_tokenizer" : { "type" : "ngram" , "min_gram" : 2 , "max_gram" : 3 , "token_chars" : [ "letter" ] } } } } }
分词
1 2 3 4 { "analyzer" : "my_analyzer" , "text" : "今天是个好日子!" }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 { "tokens" : [ { "token" : "今天" , "start_offset" : 0 , "end_offset" : 2 , "type" : "word" , "position" : 0 } , { "token" : "今天是" , "start_offset" : 0 , "end_offset" : 3 , "type" : "word" , "position" : 1 } , { "token" : "天是" , "start_offset" : 1 , "end_offset" : 3 , "type" : "word" , "position" : 2 } ... ] }
自定义示例选择器
ExampleSelector
必须实现两个方法:
add_example
方法,该方法接受一个示例并将其添加到
ExampleSelector
中
select_examples
方法,该方法接受输入变量(用于用户输入)并返回要在 few shot
提示中使用的示例列表
实现一个自定义的 ExampleSelector
,效果是在示例中随机选择
n 个,n 作为构造参数传入
自定义选择器实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 from langchain.prompts.example_selector.base import BaseExampleSelectorfrom typing import Dict , List import numpy as npclass CustomExampleSelector (BaseExampleSelector ): def __init__ (self, examples: List [Dict [str , str ]], size: int ): self.examples = examples self.size = size def add_example (self, example: Dict [str , str ] ) -> None : """添加新示例""" self.examples.append(example) def select_examples (self, input_variables: Dict [str , str ] ) -> List [dict ]: """根据输入选择要使用的示例""" return np.random.choice(self.examples, size=self.size, replace=False )
使用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 examples = [ {"魏" : "曹丕" }, {"蜀" : "刘备" }, {"吴" : "孙权" } ] example_selector = CustomExampleSelector(examples, 2 ) print (example_selector.select_examples({"unuseful" : "unuseful" }))example_selector.add_example({"晋" : "司马睿" }) print (example_selector.select_examples({"unuseful" : "unuseful" }))
选择长度
选择器可以根据长度对示例进行选择来避免构建的提示长度超过上下文窗口
token 的限制
对于较长的输入,它会选择较少的示例,而对于较短的输入,它会选择更多的示例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 from langchain.prompts import PromptTemplatefrom langchain.prompts import FewShotPromptTemplatefrom langchain.prompts.example_selector import LengthBasedExampleSelectorexamples = [ {"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, max_length=25 , ''' 这是用于获取字符串长度的函数以确定要包括哪些示例 它被注释掉是因为,如果未指定,则将其作为默认值提供 get_text_length: Callable [[str], int] = lambda x: len(re.split("\n| ", x)) 默认逻辑是按照换行符或空格进行分割来判断长度 ''' ) dynamic_prompt = FewShotPromptTemplate( example_selector=example_selector, example_prompt=example_prompt, prefix="Give the antonym of every input" , suffix="Input: {adjective}\nOutput:" , input_variables=["adjective" ], )
使用
1 print (dynamic_prompt.format (adjective="big" ))
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 Give the antonym of every input Input : happyOutput : sadInput : tallOutput : shortInput : energeticOutput : lethargicInput : sunnyOutput : gloomyInput : windyOutput : calmInput : bigOutput :
因为输入的内容很短(big),所以根据生于长度会取更多的示例
1 2 long_string = "big and huge and massive and large and gigantic and tall and much much much much much bigger than everything else" print (dynamic_prompt.format (adjective=long_string))
1 2 3 4 5 6 7 Give the antonym of every input Input: happyOutput: sadInput: big and huge and massive and large and gigantic and tall and much much much much much bigger than everything elseOutput:
因为输入的内容很长(big and huge and massive and large and gigantic
and tall and much much much much much bigger than everything
else),所以选择的示例就比较少
参考
例子选择器
| 🦜️🔗 Langchain
OpenAI体验3 ——
embedding和向量数据库(pinecone) - 知乎 (zhihu.com)