深入探讨当前信息搜索检索方法的工作原理, 绝对新手必读
搜索和搜索引擎的故事与语言密切相关. 直到最近, 计算机还无法模拟人类的理解能力, 早期的信息查找是一个繁琐且容易出错的过程. 早期的搜索引擎就像你使用图书馆的卡片目录一样, 将你输入的单词与信息目录(其索引)中的单词进行匹配. 这被称为词汇或关键词搜索. 这种查找信息的过程是反复的, 手动的, 而且常常令人沮丧.
然而, 2022年OpenAI的ChatGPT的出现大大改变了搜索的格局. ChatGPT 由一个大型语言模型(LLM)驱动, 可以非常准确地生成对文本问题的文本回复. ChatGPT 与其他AI助手, 代码生成器和代理一起, 极大地改变了人们对与数字工具交互的期望, 并激发了人们对数字领域信息检索的热情.
我们现在正处于搜索革命的风口浪尖, 这场革命将聊天机器人和AI助手的对话能力与搜索引擎的快速信息处理能力融为一体. 检索增强生成(RAG)等技术正被用于提高搜索结果的准确性和相关性.
使用 Meta.ai 生成的图片
本博客旨在让读者了解信息检索领域新出现的可能性, 为构建者, 产品经理和管理人员提供有关语义搜索和生成式AI的见解. 它将指导应用开发人员将这些技术融入应用中, 帮助产品经理制定产品集成战略, 并协助高管确定其团队利用生成式AI力量的方向.
什么是搜索和搜索引擎?
搜索的目的是查找与当前任务相关的信息. 广义地说, 无论你是在与AI聊天机器人聊天, 还是在搜索框中输入文字, 你都是在搜索.
搜索引擎是允许用户从互联网上的大量内容中搜索和检索信息的程序.
它生成一个文本(网页, 科学论文, 新闻文章, 推特等)的排序列表, 根据与用户查询的估计相关性进行排序.
搜索引擎有两个核心功能--索引和检索. 为了构建搜索体验, 应用程序构建者将信息作为结构化文档发送给引擎. 在这里, Document是一个专业术语, 指引擎索引和搜索查询检索的单一实体. 引擎将搜索文档字段中的信息编入索引, 为文本, 数字等提供快速匹配和检索.
现代搜索系统采用机器学习和其他策略来提高搜索结果的相关性. 这些策略包括行为跟踪, 查询重写和个性化, 它们利用点击和购买等用户行为信号来改善搜索体验.
搜索类型:
在信息检索(IR)中, 有两种类型的搜索是可能的, 也是实用的, 它们是词法搜索和语义搜索. 让我们分别探讨一下这两种搜索是如何工作的.
词法搜索:
词法搜索是最基本的搜索形式, 直接使用语言进行操作.
什么是词法搜索?
它将大段非结构化文本分解为单个词, 将这些词与查询中的文本进行匹配.
让我们以一个宠物鸟推荐网站为例. 该网站的创建者使用宠物描述作为搜索引擎的文档主体. 下面是两个描述鸡尾鹦鹉和非洲灰鹦鹉的文本块:
- 文本示例 A : 鸡尾鹦鹉是最善于交流, 最富有情感的鸟类之一. 它们因模仿周围的声音(包括电话, 警报器甚至室外鸟类的声音)而广为人知. 这些聪明的小鸟渴望社交互动, 需要主人给它们时间和关注, 它们才能茁壮成长, 避免孤独或抑郁.
- 文本示例 B: 非洲灰鹦鹉被认为是世界上最聪明的鸟类, 能够学习大量的词汇. 由于它们的智力超群, 这些鹦鹉每天需要大约 5 个小时的刺激才能避免陷入无聊或抑郁. 如果你希望与非洲灰鹦鹉成为永远的朋友, 那么非洲灰鹦鹉将是你的最佳伴侣.
当搜索在唱歌的鸟类时, 文本样本 A 似乎比文本样本 B 更适合匹配.
大多数搜索引擎通常分三个阶段解决搜索问题, 即匹配, 合并和排序. 让我们来探讨和了解这三个阶段的工作原理.
匹配:
为匹配而对文本进行规范化处理的过程通常是在索引时间(文档添加到索引时)和查询时间(用户运行搜索时)完成的. 在使用词法搜索的搜索引擎中, 匹配文本的规范化过程包括几个步骤.
分段 - 在分段过程中, 搜索引擎会去掉标点符号, 并将术语转换为小写. 这可确保"Run"与"run"匹配.
词干化 - 特定语言的词干化规则会去除常见的转折词. 因此,"Run"将与"run”,"runs"和"running"匹配.
停止词过滤 - 停止词过滤器可去除常见术语, 如冠词(a
, an
, the
等), 这些术语在几乎所有文档中都很常见, 因此匹配价值较低.
同义词匹配 - 最后, 添加同义词以匹配常见组别的术语. 例如,"happy"和"joyful"可用作"glad"的同义词.
本讨论主要以英语分析器为中心. 跨多种语言搜索的主题非常广泛, 包括索引设计, 语言分析以及索引, 群集和文档的租用等方面.
预处理后的文本示例如下:
Text sample A (cocktails): cockatiel among commun emot bird
much well known quirk mimick sound around includ phone alarm
even outdoor birdsthes smart littl bird crave social interact
requir owner provid time attent need order thrive prevent
loneli depress recommend keep cockatiel pair get lone leav
Text sample B (African Grey): african grey parrot believ
smartest bird world capabl learn huge vocabularybecaus outsiz
intellig parrot need somewher vicin 5 hour stimul everi day
keep fall boredom depress look make seriou commit forev friend
find intellig love companion african grey parrot
输出结果看起来很奇怪. 这是怎么回事?你可以看到, 停顿词被删除了. 我们没有使用同义词, 因此没有额外的词语. 例如, communicative变成了commun, emotional变成了emot..
当然, 文本查询也要经过类似的分析. 查询"在玩耍和唱歌的鸟"被翻译成"玩耍唱歌的鸟".
为了与查询相匹配, 搜索引擎使用了倒排索引. 一本书中的索引将单词映射到出现这些单词的页码上;要查找某样东西, 你需要去索引中查找一个词, 然后去查找所标示的页码, 直到找到你要找的东西为止. 同样, 搜索引擎中的倒排索引将术语映射到包含这些术语的文档 ID 上. 搜索引擎在索引中查找术语, 然后组合文档 ID 集来确定匹配.
示例语料库中的术语索引和文档索引
上图显示了示例语料库中可能存在的一些术语. 左边是术语索引;右边是每个条目的发布列表或文档索引. 在匹配过程中, 引擎会在术语索引中查找术语. 要匹配play sing bird, 它会查找分析术语--“play”, "sing"和"bird”--从而得到张贴列表 [21,34,23,12,18
,43
]和 [23
,11
,13
,18
].
合并
搜索的第二阶段是合并张贴列表, 以获得单个匹配集. 搜索引擎使用集合数学来计算匹配度. 如果应用程序希望匹配查询中的所有术语, 就会对查询使用"AND"运算符, 搜索引擎会计算张贴列表的交集--本例中为“[23,11]”或空集. 在大多数情况下, 应用程序将使用"OR"运算符--一个联合--来检索[21,34,23,12,18
, 43
,23
, 11
, 13
, 18
]. 它依靠评分和排序将最相关的文档排在最前面.
重新排序
在搜索过程的最后一步, 即排名, 搜索引擎使用一种特殊算法来确定文档与特定搜索查询的相关性. 这种算法被称为"词频-反向文档频率”(TF-IDF), 它是根据文本的整体统计数据进行操作的. 下面是它的工作原理:
- 文档中的罕见术语或单词会获得高分.
- 而普通术语或单词则获得低分.
- 针对特定搜索查询的文档得分是通过将与查询匹配的术语得分相加计算得出的. 然后再将总分乘以这些词在文档中出现的次数.
在排序阶段, 搜索引擎会根据得分排列与搜索查询匹配的所有文档. 得分最高的文档排在搜索结果的最前面, 而得分较低的文档则排在搜索结果的后面. 这一过程可确保最相关的文档首先呈现给用户, 使他们更容易找到所需的信息.
简单来说, 假设你正在寻找巧克力饼干的食谱. 搜索引擎会首先找到所有提到巧克力饼干的网页. 然后, 它会根据"巧克力饼干"一词出现的频率以及这些词在网页上下文中的重要程度对这些网页进行排序. 最相关, 最有用的网页将显示在搜索结果的顶部.
词法搜索的实现
让我们看看如何在实践中使用 python 中的 nltk 软件包进行简单的词法搜索.
import nltk
nltk.download('punkt')
nltk.download('stopwords')
from nltk.corpus import stopwords
from nltk.tokenize import word_tokenize
import re
from sklearn.feature_extraction.text import TfidfVectorizer
# Sample corpus of documents
corpus = ['Cockatiels are among the most communicative and emotional birds. they are much more well known for their quirk of mimicking sounds around them including phones, alarms, and even outdoor birds.these smart little birds crave social interaction and require an owner who can provide them with the time and attention they need in order to thrive and prevent loneliness, or depression. It's recommended to keep cockatiels in pairs so they do not get lonely when you have to leave the home; single cockatiels can be kept as pets, but they require near-constant attention from their owner to stay in high spirits.',
'African grey parrots are believed to be the smartest birds in the world and are capable of learning a huge vocabulary.Because of their outsized intelligence, these parrots need somewhere in the vicinity of 5 hours of stimulation every day to keep from falling into boredom or depression. Those looking to make a serious commitment to a forever friend can find an intelligent and loving companion in an African grey parrot.'
]
# Define a function to preprocess the text
def preprocess_text(text):
# Convert to lower case
text = text.lower()
# Remove punctuation
text = re.sub(r'[^\w\s]', '', text)
# Tokenize the text
tokens = word_tokenize(text)
# Remove stopwords
tokens = [token for token in tokens if token not in stopwords.words('english')]
# Perform stemming
stemmer = nltk.stem.PorterStemmer()
tokens = [stemmer.stem(token) for token in tokens]
# Perform lemmatization
lemmatizer = nltk.stem.WordNetLemmatizer()
tokens = [lemmatizer.lemmatize(token) for token in tokens]
# Join the tokens back into a string
text = ' '.join(tokens)
return text
# Preprocess the corpus
corpus = [preprocess_text(doc) for doc in corpus]
print('Corpus: \n{}'.format(corpus))
# Create a TfidfVectorizer object and fit it to the preprocessed corpus
vectorizer = TfidfVectorizer()
vectorizer.fit(corpus)
# Transform the preprocessed corpus into a TF-IDF matrix
tf_idf_matrix = vectorizer.transform(corpus)
# Get list of feature names that correspond to the columns in the TF-IDF matrix
print("Feature Names:\n", vectorizer.get_feature_names_out())
# Print the resulting matrix
print("TF-IDF Matrix:\n",tf_idf_matrix.toarray())Semantic search
步骤 1. 导入必要的库
- nltk(Natural Language Toolkit, 自然语言工具包)用于文本预处理任务, 如标记化, 删除停滞词, 词干化和词素化.
- sklearn.feature_extraction.text.TfidfVectorizer "用于计算已处理语料的 TF-IDF 矩阵, 这是表示文档中术语重要性的关键步骤.
2. 下载 NLTK 资源
nltk.download('punkt')
nltk.download('stopwords')
这些命令可下载 NLTK 的基本组件:
- 用于将句子标记为单词的
'punkt'
. - 用于删除"and”,"the"等常用词的"stopwords”.
3. 样本语料库
corpus
包含两个样本文档(关于鸟类特征的描述), 将对其进行进一步处理.
4. 文本预处理函数
preprocess_text(text)
函数执行一系列文本清理和转换步骤:
a. 转换为小写字母:- 通过将所有文本转换为小写字母来确保统一性.
text = text.lower()
b. 删除标点符号: 这个正则表达式会删除任何非字母数字或空白的字符, 从而有效地删除标点符号.
text = re.sub(r'[^\w\s]', '', text)
c. 标记化: 使用 word_tokenize()
, 将文本分割成单个单词(tokens).
tokens = word_tokenize(text)
d. 删除停止词: 从标记列表中删除常见的停止词, 以减少数据中的噪音.
tokens = [token for token in tokens if token not in stopwords.words('english')]
e. 词根提取: 使用波特词根提取器(Porter Stemmer)将单词还原为词根形式. 例如,"playing"变为"play”.
stemmer = nltk.stem.PorterStemmer()
tokens = [stemmer.stem(token) for token in tokens]
f. 词根化: 词根化根据上下文进一步将单词还原为词根形式. 例如, "better"可以词素化为"good”.
lemmatizer = nltk.stem.WordNetLemmatizer()
tokens = [lemmatizer.lemmatize(token) for token in tokens]
g. 重新连接词块: 将词块重新连接成字符串, 使其更加简洁明了.
text = ' '.join(tokens)
5. 应用预处理: 使用preprocess_text
函数处理corpus
中的每个文档, 为 TF-IDF 转换做好准备.
corpus = [preprocess_text(doc) for doc in corpus]
6. TF-IDF 矢量化
vectorizer = TfidfVectorizer()
vectorizer.fit(corpus)
- 初始化
TfidfVectorizer()
以将语料库转换为 TF-IDF 特征矩阵. vectorizer.fit(corpus)
根据预处理后的文本计算术语-文档矩阵.
7. 转换语料库
tf_idf_matrix = vectorizer.transform(corpus)
这一步将处理过的语料库转换为 TF-IDF 值的稀疏矩阵表示, 其中每一行对应一个文档, 每一列对应一个术语.
8. 提取并显示特征和 TF-IDF 矩阵: 接下来, 让我们打印从语料库中提取的特征名称(术语). 矩阵会转换成二维数组, 显示每个文档中每个术语的 TF-IDF 权重. 这些权重反映了术语在语料库上下文中的重要性
print("Feature Names:\n", vectorizer.get_feature_names_out())
print("TF-IDF Matrix:\n",tf_idf_matrix.toarray())
下面的流程图概括了 TF-IDF 或 BM25 等词法搜索方法的工作原理
词法搜索架构
2. 语义搜索:
语义搜索是计算机和机器学习(ML)模型用来理解和处理自然语言的一种方法.
***什么是语义搜索?
语义搜索背后的基本思想是使用向量来表示词语的含义, 以便进行有意义的比较和组合.
通过在大型数据集上训练 ML 模型, 我们可以创建捕捉语言细微差别的向量, 从而获得更准确, 更直观的搜索结果. 这些模型只能理解数字, 因此要处理语言, 它们需要将其转换为数字形式. 这就是"向量"发挥作用的地方.
向量和嵌入
向量在语义搜索中也称为"嵌入”, 是一种将自然语言表示为多维数值集合的方法. 为搜索引擎训练 ML 模型的目的是创建一个模型, 生成的向量在含义相似的文本中相距较近, 而在含义不同的文本中相距较远.
向量空间中的向量表示
想象一个 512 维的向量, 这意味着它在不同的轴上有 512 个值. 然而, 要将 512 维可视化并非易事!
让我们简化一下. 想想给每种动物分配一个唯一的数字:'cat' = 1, 'dog' = 2, 'elephant' = 3, 以此类推. 但如果说'cat' + 'dog' = 'elephant'就说不通了.
为了改善这种情况, 我们可以扩展为两个数字, 就像在地图上标注点一样:
cat = (1,0)
dog = (0,1)
elephant = (0,2)
tiger = (1,2)
但还是有问题. 将'cat'和'elephant'相加得到 (1,2), 等于'tiger'. 显然, 有必要增加维度, 但我们需要多少维度呢?
维度: 稀疏检索和密集检索
一种解决方案是, 词汇量有多少, 维数就用多少. 这通常被称为稀疏,关键字或单次编码. 然而, 稀疏向量无法进行有意义的组合. 例如, 将"animal"和"pet"相加就不会产生"elephant"或"lion”. 但如果等同于"dog"就有意义了, 因为狗也是一种常见的宠物.
为了更好地捕捉这种自然理解, 我们使用维数少于动物总数的向量. 这些向量被称为密度向量, 常用于现代机器学习模型. 例如, 想象一个二维空间, 其中一个轴代表"大小”, 另一个轴代表"驯化”. 在这个空间中,"家养动物"的向量可能靠近"dog"的向量, 而"野生动物"的向量可能离得较远, 靠近"lion”.
以"大小"和"驯化"为维度的向量表示示例
什么是语义搜索?
语义搜索的核心概念是使用向量来表示词语的含义, 以便进行有意义的比较和组合.
通过在大型数据集上训练 ML 模型, 我们可以创建捕捉语言细微差别的向量, 从而获得更准确, 更直观的搜索结果.
语义搜索流程:
在语义搜索中, 搜索应用程序使用 LLM 为语料库中的每个文档创建一个向量. 当用户运行搜索时, 应用会使用相同的 LLM 对搜索查询的文本进行编码, 从而在同一空间生成一个向量. 然后, 它使用一个距离函数为查询/文档对生成一个分数, 并根据该分数对结果进行排序.
语义搜索架构
将含义编码成向量的BERT模型
BERT模型可用于将意义编码成向量, 但为什么是BERT?为什么不是变换器模型的其他变体或深度学习模型?
我们知道, 变换器架构通常由编码器和解码器两部分组成. 而 BERT 只使用了变换器中的编码器部分. BERT 只是几个具有(双向, 多头)自注意和前馈变换的重复层, 每个层后都有层归一化 和残差连接.
Transformer结构
Transformer的编码器组件非常善于理解自然语言, 这也是 BERT 模型善于理解和表示语言的根本原因. 有两类 BERT 模型特别适用于搜索和检索目的--双编码器和交叉编码器.
双编码器是该领域使用的基本密集检索算法之一. 双编码器将文本序列作为输入, 并产生一个密集向量作为输出. 双编码器产生的向量具有语义意义--相似的文本序列产生的向量在向量空间中相邻. 当你运行搜索时, 它会将你的查询向量与具有最接近向量的文档进行匹配, 通常会使用余弦相似度等算法. 这样可以更快, 更有效地找到最相关的内容.
双编码器流程
我们还可以使用分层导航小词(HNSW) 等算法来加快这一过程. HNSW 可帮助执行"近似近邻"搜索, 因此它不会搜索所有内容, 而是将搜索范围缩小到可能与你的查询相似的较小群体, 这些群体被称为"候选集”. 就像我们在词法搜索中所做的那样, 我们可以在 Elastic 或 OpenSearch 等数据库中存储文档向量, 并建立 HNSW 搜索索引. 然后, 我们可以使用双编码器为用户的查询生成一个向量, 然后使用适当的距离度量执行向量搜索, 找出最相似的文档.
*交叉编码器**采用的是另一种方法. 它们不是为查询和文档创建单独的向量, 而是将两者一起处理. 请参阅下图进一步了解交叉编码器. 这样, 它们就能得出更准确的相似性得分. 缺点是什么?它的计算成本很高, 这意味着它并不总是适用于大规模搜索.
交叉编码器流程
双编码器和交叉编码器通常都采用 BERT 模型, 这是一种基于变压器的架构. 了解 BERT 的结构及其工作原理对改进搜索系统至关重要. 交叉编码器非常精确, 而双编码器则在速度和精确度之间取得了平衡, 因此成为当今大多数基于矢量的搜索系统的首选.
BERT 如何为理解文本做好准备?
要了解 BERT 或任何纯编码器模型的工作原理, 首先要了解它是如何处理输入的. 这看似简单--只需输入一个句子, 对吗?- 但这背后还有很多事情要做. 在 BERT 理解文本之前, 它必须将文本分解成它可以消化的形式. 这就是标记化的作用.
把标记化理解为把一个句子切割成一小块一小块的, 易于管理的片段, 这些片段被称为"标记”. 这些标记可以是整个单词, 甚至是单词的一部分. BPE 或SentencePiece 等工具可以帮助实现这一点. 它们将文本分割成标记, 将输入转化为 BERT 可以处理的内容.
但还有更多. BERT 还依赖一些特殊的标记来帮助它浏览文本:
[cls]
: 该标记位于文本的开头. 它就像是 BERT 的个人笔记工具--可以捕捉整个序列的整体含义.[sep]
: 当涉及多个句子时,[SEP]
标记作为边界, 将一个句子与下一个句子分开. 这有助于 BERT 区分不同的文本块.[eos]
: 在序列的末尾,[EOS]
标记表示文本已经结束. 这就像在段落末尾加上句号, 确保 BERT 知道文本在哪里结束.
这些标记在帮助 BERT 理解单个单词以及单词之间的关系方面起着至关重要的作用, 从而使 BERT 的文本表述更有意义.
嵌入标记:BERT 如何理解位置?
输入文本经过标记化处理后, 每个标记都需要嵌入, 即转换成模型可以理解的数字向量. 为此, BERT 使用了一个大型嵌入层, 它就像一个查找表, 将每个标记转换成唯一的向量. 但在模型处理这些向量之前, 还有一件重要的事情要做, 那就是位置嵌入.
BERT 中的嵌入层. 输入嵌入是标记嵌入, 分割嵌入和位置嵌入的总和.
为什么位置很重要?因为语言是有结构的, 单词的顺序对意义至关重要. 为了帮助 BERT 理解每个标记的位置, 我们在标记向量中添加了位置嵌入. 这种位置信息有助于 BERT 识别每个单词在句子中的位置, 使其不仅能捕捉单个单词的含义, 还能理解单词之间的关系.
双向自我关注: 洞察全局
自我关注是 BERT 能够如此有效地处理语言的秘诀. 虽然对自我注意的深入研究不在本篇文章的讨论范围之内, 但要点如下:自我注意允许 BERT 通过查看序列中的所有其他标记来转换每个标记. 但关键在于:BERT 使用双向自我注意, 这意味着它不仅会查看标记之前的单词, 还会考虑标记之后的单词.
试想一下, 一个字一个字地阅读一个句子, 而不是把整个句子摆在你面前. 双向自我关注为 BERT 提供了完整句子的视角, 使每个单词都能在整个序列的语境中得到理解.
前馈转换: 增强每个Token
接下来是前馈变换, 它的作用有所不同. 自我关注关注的是token之间如何相互作用, 而前馈变换则不同, 它关注的是单个token. 序列中的每个token都要经过相同的变换:几个线性运算, 中间使用 ReLU 激活函数. 可以认为这是在逐个打磨每个token的表征. 之后, 进行归一化处理, 以确保输出平衡, 为下一步做好准备.
将所有内容整合在一起: BERT 如何学习?
双向自我关注和前馈转换相互配合, 各自做出独特的贡献. 自我关注通过考虑所有token之间的关系来帮助模型理解大局, 而前馈转换则对每个token的单独表示进行微调. 这些过程结合在一起, 使 BERT 能够捕捉文本中复杂的模式和关系, 使其在搜索, 问题解答和文本分类等任务中表现出色.
BERT 的架构.
为了更好地理解注意力的工作原理, 我建议你阅读这篇杰伊的博客. 现在, 让我们尝试使用 bertviz python 软件包来可视化注意力在转换器中的工作原理. BertViz 是一款交互式工具, 用于可视化 BERT, GPT2 或 T5 等Transformer语言模型中的注意力. 它可以通过一个简单的 Python API 在 Jupyter 或 Colab 笔记本中运行.
# Load model and retrieve attention weights
from bertviz import head_view, model_view
from transformers import BertTokenizer, BertModel
model_version = 'bert-base-uncased'
model = BertModel.from_pretrained(model_version, output_attentions=True)
tokenizer = BertTokenizer.from_pretrained(model_version)
sentence_a = "The cat sat on the mat"
sentence_b = "It also lays on the rug sometime"
inputs = tokenizer.encode_plus(sentence_a, sentence_b, return_tensors='pt')
input_ids = inputs['input_ids']
token_type_ids = inputs['token_type_ids']
attention = model(input_ids, token_type_ids=token_type_ids)[-1]
sentence_b_start = token_type_ids[0].tolist().index(1)
input_id_list = input_ids[0].tolist() # Batch index 0
tokens = tokenizer.convert_ids_to_tokens(input_id_list)
### The head view visualizes attention in one or more heads from a single Transformer layer.
head_view(attention, tokens, sentence_b_start)
句子 A--“The cat sat on the mat”和句子 B--“It also lays on the rug sometime”的注意力可视化显示了第 6 层是如何理解单词"it”是如何指代"cat”的.
我想你现在已经了解了 BERT 模型的基本工作原理. 说到香草 BERT 模型在语义相似性和聚类任务中效果不佳, 你可能会想:*我们如何调整这些模型?*我们怎样才能调整这些模型, 为语义搜索提供更有用的嵌入?我们只需要使用连体网络或三重网络结构对这些模型进行微调, 就能得到具有语义意义的文本或文档表示--sBERT 模型或Sentence-BERT: Sentence Embeddings using Siamese BERT-Networks. sBERT 模型是在 PyTorch 和 HuggingFace 的基础上构建的一个 Python 库(名为 Sentence Transformers)中实现的. 这个软件包公开提供了大量最先进的模型--包括双编码器和交叉编码器--并且可以轻松使用这些模型, 并在自己的数据上对它们进行有效的微调. 由于该软件包基于 sBERT, 因此 SentenceTransformer 内嵌具有语义意义--相似的句子产生的内嵌在向量空间中接近.
使用 SentenceTransformer
进行语义搜索的示例:
"""This is a simple application for sentence embeddings: semantic searchWe have a corpus with various sentences. Then, for a given query sentence,we want to find the most similar sentence in this corpus.This script outputs for various queries the top 5 most similar sentences in the corpus."""
import torch from sentence_transformers
import SentenceTransformer
embedder = SentenceTransformer("all-MiniLM-L6-v2")
# Corpus with example sentencescorpus = [
"A man is eating food.",
"A man is eating a piece of bread.",
"The girl is carrying a baby.",
"A man is riding a horse.",
"A woman is playing violin.",
"Two men pushed carts through the woods.",
"A man is riding a white horse on an enclosed ground.",
"A monkey is playing drums.",
"A cheetah is running behind its prey.",
]
# Use "convert_to_tensor=True" to keep the tensors on GPU (if available)corpus_embeddings = embedder.encode(corpus, convert_to_tensor=True)
# Query sentences:queries = [
"A man is eating pasta.",
"Someone in a gorilla costume is playing a set of drums.",
"A cheetah chases prey on across a field.",
]
# Find the closest 5 sentences of the corpus for each query sentence based on cosine similaritytop_k = min(5, len(corpus))
for queryin queries:
query_embedding = embedder.encode(query, convert_to_tensor=True)
# We use cosine-similarity and torch.topk to find the highest 5 scoressimilarity_scores = embedder.similarity(query_embedding, corpus_embeddings)[0]
scores, indices = torch.topk(similarity_scores, k=top_k)
print("\nQuery:", query)
print("Top 5 most similar sentences in corpus:")
for score, idxin zip(scores, indices):
print(corpus[idx], "(Score:{:.4f})".format(score))
今天的内容就分享到这里啦!
一家之言, 欢迎拍砖!
Happy Coding! Stay GOLDEN!