简介

在之前的文章中,我们提到了可以在跟大模型交互的时候,给大模型提供一些具体的例子内容,方便大模型从这些内容中获取想要的答案。这种方便的机制在langchain中叫做FewShotPromptTemplate。

如果例子内容少的话,其实无所谓,我们可以把所有的例子都发送给大语言模型进行处理。

但是如果例子太多的话,每次都发送如此多的内容,会让我们的钱包承受不住。毕竟那些第三方的大语言模型是按token收费的。

怎么办呢? 能不能找到一个经济又有效的方法来完成我们的工作呢?

答案就是使用example selector。

使用和自定义example selector

我们回想一下在使用FewShotPromptTemplate的时候,实际上是可以同时传入example_selector和examples。

prompt = FewShotPromptTemplate(
example_selector=example_selector,
example_prompt=example_prompt,
suffix="Question: {input}",
input_variables=["input"]
)

这里我们使用了一个example_selector,那么什么是example_selector呢?

从名字上看他的主要作用就是从给定的examples中选择需要的examples出来,提供给大模型使用,从而减少会话的token数目。

langchain中提供了这样的example_selector的实现,我们先来看下它的基础类的定义是怎么样的:

class BaseExampleSelector(ABC):
"""Interface for selecting examples to include in prompts.""" @abstractmethod
def add_example(self, example: Dict[str, str]) -> Any:
"""Add new example to store for a key.""" @abstractmethod
def select_examples(self, input_variables: Dict[str, str]) -> List[dict]:
"""Select which examples to use based on the inputs."""

可以看到BaseExampleSelector继承自ABC,并且定义了两个需要实现的抽象方法。

一个方法叫做add_example。目的是向selector中添加一个example。

一个方法叫做select_examples,主要目的就是根据input,从examples中找出要select出来的内容。

那么什么是ABC呢?

ABC当然就是你了解到的ABC,但是他还有一些额外的含义。ABC的全称叫做Abstract Base Class,也叫做抽象基类。主要用于在Python程序中创建抽象基类。

他提供了一些@abstractmethod,@abstarctproperty这些装饰方法,来表明具体类的特征。

所以,如果我们想自定义一个ExampleSelector,只需要继承自BaseExampleSelector,然后实现这两个抽象方法即可。

langchain中的ExampleSelector实现

除了自定义实现之外,langchain已经为我们提供了几个常用的ExampleSelector实现,一起来看看吧。

LengthBasedExampleSelector

LengthBasedExampleSelector是根据example的长度来进行选择的选择器。

我们看下它的具体实现:

    def add_example(self, example: Dict[str, str]) -> None:
"""Add new example to list."""
self.examples.append(example)
string_example = self.example_prompt.format(**example)
self.example_text_lengths.append(self.get_text_length(string_example))

add_example的逻辑是先把example添加到examples这个list中。

然后使用example_prompt对example进行格式化,得到最终的输出。

最后再把最后输出的text长度添加到example_text_lengths数组中。

    def select_examples(self, input_variables: Dict[str, str]) -> List[dict]:
"""Select which examples to use based on the input lengths."""
inputs = " ".join(input_variables.values())
remaining_length = self.max_length - self.get_text_length(inputs)
i = 0
examples = []
while remaining_length > 0 and i < len(self.examples):
new_length = remaining_length - self.example_text_lengths[i]
if new_length < 0:
break
else:
examples.append(self.examples[i])
remaining_length = new_length
i += 1
return examples

select_examples方法实际上就是用max_length减去输入text的长度,然后再去匹配example_text的长度,匹配一个减去一个,最终得到特定长度的examples。

这个selector的最主要作用就是防止耗尽context window。因为对于大多数大语言模型来说,用户的输入是有长度限制的。

如果超出了输入长度,会产生意想不到的结果。

这个selector使用起来很简单,下面是具体的例子:

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,
max_length=25,
)

SemanticSimilarityExampleSelector和MaxMarginalRelevanceExampleSelector

这两个selector是根据相似度来进行example的查找的。

其中MaxMarginalRelevanceExampleSelector是SemanticSimilarityExampleSelector的字类,他是对SemanticSimilarityExampleSelector进行了一些算法上的优化。所以这里我们把他们两个放在一起介绍。

这两个selector和之前介绍的selector有所不同。因为他们用到了向量数据库。

向量数据库是干什么用的呢?它的主要目的是把输入转换成各种向量然后存储起来。向量数据库可以方便的进行输入相识度的计算。

我们先来看下他们的add_example方法:

    def add_example(self, example: Dict[str, str]) -> str:
"""Add new example to vectorstore."""
if self.input_keys:
string_example = " ".join(
sorted_values({key: example[key] for key in self.input_keys})
)
else:
string_example = " ".join(sorted_values(example))
ids = self.vectorstore.add_texts([string_example], metadatas=[example])
return ids[0]

这个方法先把example的key加入到input_keys中,然后进行排序。最后通过调用vectorstore的add_texts,把key和value加入到向量数据库中。

这两个selector的add_example都是一样的。只有select_examples的方法不同。

其中SemanticSimilarityExampleSelector调用了vectorstore的similarity_search方法来实现相似度的搜索。

而MaxMarginalRelevanceExampleSelector则是调用vectorstore的max_marginal_relevance_search方法来实现搜索的。

两者的搜索算法不太一样。

因为使用了向量数据库,所以他们的调用方法和其他的也不太一样:

examples = [
{"input": "happy", "output": "sad"},
{"input": "tall", "output": "short"},
{"input": "energetic", "output": "lethargic"},
{"input": "sunny", "output": "gloomy"},
{"input": "windy", "output": "calm"},
] example_selector = SemanticSimilarityExampleSelector.from_examples(
examples,
# 使用的ebeddings
OpenAIEmbeddings(),
# 向量数据库
Chroma,
# 要返回的数目
k=1
)

NGramOverlapExampleSelector

最后一个要介绍的是NGramOverlapExampleSelector。这个selector使用的是ngram 重叠矩阵来选择相似的输入。

具体的实现算法和原理这里就不介绍了。大家有兴趣的可以自行探索。

这个selector也不需要使用向量数据库。

使用起来是这样的:

example_selector = NGramOverlapExampleSelector(
examples=examples,
example_prompt=example_prompt,
threshold=-1.0,
)

这里有个不太一样的参数叫做threshold。

对于负阈值:Selector按ngram重叠分数对示例进行排序,不排除任何示例。

对于大于1.0的阈值:选择器排除所有示例,并返回一个空列表。

对于等于0.0的阈值:选择器根据ngram重叠分数对示例进行排序,并且排除与输入没有ngram重叠的那些。

总结

有了这些selector我们就可以在提供的examples中进行特定的选择,然后再把选择的结果输入给大语言模型。

从而有效的减少token的浪费。

在langchain中使用自定义example selector的更多相关文章

  1. UWP中实现自定义标题栏

    UWP中实现自定义标题栏 0x00 起因 在UWP开发中,有时候我们希望实现自定义标题栏,例如在标题栏中加入搜索框.按钮之类的控件.搜了下资料居然在一个日文网站找到了一篇介绍这个主题的文章: http ...

  2. Entity Framework 6 Recipes 2nd Edition(10-5)译 -> 在存储模型中使用自定义函数

    10-5. 在存储模型中使用自定义函数 问题 想在模型中使用自定义函数,而不是存储过程. 解决方案 假设我们数据库里有成员(members)和他们已经发送的信息(messages) 关系数据表,如Fi ...

  3. Android XML中引用自定义内部类view的四个why

    今天碰到了在XML中应用以内部类形式定义的自定义view,结果遇到了一些坑.虽然通过看了一些前辈写的文章解决了这个问题,但是我看到的几篇都没有完整说清楚why,于是决定做这个总结. 使用自定义内部类v ...

  4. ASP.NET Core中显示自定义错误页面-增强版

    之前的博文 ASP.NET Core中显示自定义错误页面 中的方法是在项目中硬编码实现的,当有多个项目时,就会造成不同项目之间的重复代码,不可取. 在这篇博文中改用middleware实现,并且放在独 ...

  5. TP中关于自定义类库的添加和使用

    ThinkPHP的类库主要包括公共类库和应用类库,都是基于命名空间进行定义和扩展的.只要按照规范定义,都可以实现自动加载. 类库存放位置:Think目录:系统核心类库Org目录:第三方公共类库demo ...

  6. [asp.net mvc 奇淫巧技] 01 - 封装上下文 - 在View中获取自定义的上下文

    我们在asp.net 开发中已经封装了最强大的HttpContext,我们可以在HttpContext中可以获取到几乎任何想获取的东西,也可以在HttpContext写入需要返回客户端的信息.但是这些 ...

  7. ASP.NET中处理自定义错误的最佳方式

    要在ASP.NET中处理好自定义错误(Custom Errors)首先要抛弃使用web.config\customErrors. <customErrors mode="RemoteO ...

  8. iOS 在UITableViewCell中加入自定义view时view的frame设定注意

    由于需要重用同一个布局,于是在cellForRowAtIndexPath中把自定义view加在了cell上,我是这样设定view的frame的 var screenFrame = UIScreen.m ...

  9. 在ASP.NET中引用自定义提示框

    在html网页中自定义提示框 正文: 在一般的B/S架构中项目,与用户的交互信息是非常重要的.在一般的情况下,设计人员都在把用户信息呈现在html中,用div和span去弹出相关信息.对于一般的情况而 ...

  10. WPF中实现自定义虚拟容器(实现VirtualizingPanel)

    WPF中实现自定义虚拟容器(实现VirtualizingPanel) 在WPF应用程序开发过程中,大数据量的数据展现通常都要考虑性能问题.有下面一种常见的情况:原始数据源数据量很大,但是某一时刻数据容 ...

随机推荐

  1. 2022-09-25:给定一个二维数组matrix,数组中的每个元素代表一棵树的高度。 你可以选定连续的若干行组成防风带,防风带每一列的防风高度为这一列的最大值 防风带整体的防风高度为,所有列防风高度

    2022-09-25:给定一个二维数组matrix,数组中的每个元素代表一棵树的高度. 你可以选定连续的若干行组成防风带,防风带每一列的防风高度为这一列的最大值 防风带整体的防风高度为,所有列防风高度 ...

  2. 2020-02-24:arr是面值数组,其中的值都是正数且没有重复。再给定一个正数aim。每个值都认为是一种面值,且认为张数是无限的。返回组成aim的最少货币数。

    福哥答案2020-02-24: 自然智慧即可. 1.递归.有代码. 2.动态规划.dp是二维数组.有代码. 代码用golang编写,代码如下: package main import ( " ...

  3. [学习笔记]解决因C#8.0的语言特性导致EFCore实体类型映射的错误

    今天下午在排查一个EF问题时,遇到了个很隐蔽的坑,特此记录. 问题 使用ef执行Insert对象到某表时报错,此对象的Address为空: 不能将值 NULL 插入列 'Address',表 'dbo ...

  4. Netty实战(一)

    目录 第一章 Java网络编程 1.1 Java NIO 1.2 选择器 第二章 Netty是什么 2.1 Netty简介 2.2 Netty的特性 2.2.1 设计 2.2.2 易于使用 2.2.3 ...

  5. 探索JS中this的最终指向

    js 中的this 指向 一直是前端开发人员的一个痛点难点,项目中有很多bug往往是因为this指向不明确(this指向在函数定义时无法确定,只有在函数被调用时,才确定该this的指向为最终调用它的对 ...

  6. PowerShell脚本的基础知识

    参考文档https://learn.microsoft.com/zh-cn/powershell/scripting/samples/viewing-object-structure--get-mem ...

  7. jenkins中的坑_CreateProcess error=1392

    环境:windows11,jdk1.8,jenkins_2.346.war 起因 最近在使用jenkins部署项目的时候,填写仓库的url地址时,发现填完后报500这个错误,于是我打开jenkins的 ...

  8. rust cargo build一直出现 Blocking waiting for file lock on package cache

    如果确定没有多个程序占用,可以删除rm -rf ~/.cargo/.package-cache,然后再执行

  9. 深入浅出MySQL事务

    Photo by Lukas Hartmann from Pexels 辞职这段时间以来看见了很多工作之外的东西,我认为这是值得的.同时也有时间和机会来好好整理所学所想,准备开启下一段旅途. 事务的定 ...

  10. 【.NET深呼吸】用代码写WPF控件模板

    这一次咱们来探究一下怎么用纯代码写 WPF 模板.模板有个共同基类 FrameworkTemplate,数据模板.控件模板等是从此类派生的,因此,该类已定义了一些通用成员. 用代码构建模板,重要的成员 ...