2 利用语言模型进行语义搜索(Semantic Search with Language Models)
现在,让我们深入探讨可以提升语言模型搜索能力的主要系统类别。我们先从高密度检索开始,然后再讨论重排和 RAG。
密集检索
回顾一下,嵌入(embeddings)将文本转化为数字表示。如图 8-4 所示,我们可以将其视为空间中的点。相邻的点意味着它们所代表的文本是相似的。因此,在本例中,文本 1 和文本 2 之间的相似度(因为它们彼此靠近)要高于文本 3(因为它距离较远)。
图 8-4. 嵌入的直觉:每个文本都是一个点,意思相近的文本彼此靠近
这是用于构建搜索系统的属性。在这种情况下,当用户输入搜索查询时,我们会嵌入查询,从而将其投射到与文本档案相同的空间中。然后,我们只需在该空间中找到与查询最近的文档,这些文档就是搜索结果(图 8-5)。
图 8-5. 密集检索依赖于搜索查询将靠近相关结果这一特性
从图 8-5 中的距离来看,"text 2"是该查询的最佳结果,其次是 "text 1"。不过,这里可能会出现两个问题:
- text 3是否应该作为结果返回?这要由系统设计者来决定。有时,最好设定一个相似度得分的最大阈值,以过滤掉不相关的结果(以防语料库中没有与查询相关的结果)。
- 查询和最佳结果在语义上相似吗?不一定。这就是为什么语言模型需要在问题-答案对上进行训练,以提高检索能力。第 10 章将详细介绍这一过程。图 8-6 显示了我们如何在嵌入每个分块之前对文档进行分块。然后,这些嵌入向量(embedding vectors)将被存储到向量数据库中,随时可供检索。
图 8-6. 将某个外部知识库转换为矢量数据库。然后,我们就可以查询该矢量数据库,获取有关知识库的信息
密集检索示例(Dense retrieval example)
让我们以密集检索为例,使用 Cohere 搜索电影《Interstellar》的维基百科页面。在这个示例中,我们将进行以下操作:
- 获取我们想要搜索的文本,并进行一些简单的处理,将其分块成句子。
- 嵌入句子。
- 建立搜索索引
- 搜索并查看结果。
登录 https://oreil.ly/GxrQ1 获取 Cohere API 密钥。将其粘贴到以下代码中。运行此示例无需支付任何费用。
导入我们需要的库:
python
import cohere
import numpy as np
import pandas as pd
from tqdm import tqdm
# Paste your API key here. Remember to not share publicly
api_key = ''
# Create and retrieve a Cohere API key from os.cohere.ai
co = cohere.Client(api_key)
获取文本存档并将其分块
让我们使用维基百科上关于电影《星际穿越》的第一部分。我们先获取文本,然后将其分成若干句子:
python
text = """
Interstellar is a 2014 epic science fiction film co-written,directed, and produced by Christopher Nolan.It stars Matthew McConaughey, Anne Hathaway, Jessica Chastain,Bill Irwin, Ellen Burstyn, Matt Damon, and Michael Caine.Set in a dystopian future where humanity is struggling tosurvive, the film follows a group of astronauts who travelthrough a wormhole near Saturn in search of a new home formankind.
Brothers Christopher and Jonathan Nolan wrote the screenplay,which had its origins in a script Jonathan developed in 2007.Caltech theoretical physicist and 2017 Nobel laureate inPhysics[4] Kip Thorne was an executive producer, acted as ascientific consultant, and wrote a tie-in book, The Science ofInterstellar.
Cinematographer Hoyte van Hoytema shot it on 35 mm movie film inthe Panavision anamorphic format and IMAX 70 mm.Principal photography began in late 2013 and took place inAlberta, Iceland, and Los Angeles.
Interstellar uses extensive practical and miniature effects andthe company Double Negative created additional digital effects.Interstellar premiered on October 26, 2014, in Los Angeles.In the United States, it was first released on film stock,expanding to venues using digital projectors.
The film had a worldwide gross over $677 million (and $773million with subsequent re-releases), making it the tenth-highestgrossing film of 2014.
It received acclaim for its performances, direction, screenplay,musical score, visual effects, ambition, themes, and emotionalweight.
It has also received praise from many astronomers for itsscientific accuracy and portrayal of theoretical astrophysics.Since its premiere, Interstellar gained a cult following,[5] andnow is regarded by many sci-fi experts as one of the bestscience-fiction films of all time.
Interstellar was nominated for five awards at the 87th AcademyAwards, winning Best Visual Effects, and received numerous otheraccolades"""
# Split into a list of sentences
texts = text.split('.')
# Clean up to remove empty spaces and new lines
texts = [t.strip(' \n') for t in texts]
计算文本块的嵌入向量(Embedding the text chunks)
现在让我们计算文本嵌入向量。我们将把它们发送到 Cohere API,并为每个文本返回一个向量:
python
# Get the embeddings
response = co.embed(
texts=texts,
input_type="search_document",
).embeddings
embeds = np.array(response)
print(embeds.shape)
输出结果为 (15, 4096),表示我们有 15 个向量,每个向量的大小为 4096。
建立搜索索引(Building the search index)
在搜索之前,我们需要建立一个搜索索引(search index)。索引可以存储嵌入信息,并经过优化,即使我们有大量的点,也能快速检索到最近的邻居:
python
import faiss
dim = embeds.shape[1]
index = faiss.IndexFlatL2(dim)
print(index.is_trained)
index.add(np.float32(embeds))
通过索引进行搜索(Search the index)
我们现在可以使用任何查询来搜索数据集。我们只需嵌入查询并将其嵌入到索引中,索引就会从维基百科文章中检索出最相似的句子。
让我们定义一下搜索函数:
python
def search(query, number_of_results=3):
# 1. Get the query's embedding
query_embed = co.embed(texts=[query],
input_type="search_query",).embeddings[0]
# 2. Retrieve the nearest neighbors
distances , similar_item_ids =
index.search(np.float32([query_embed]), number_of_results)
# 3. Format the results
texts_np = np.array(texts) # Convert texts list to numpy for easier indexing
results = pd.DataFrame(data={'texts':
texts_np[similar_item_ids[0]], 'distance': distances[0]})
# 4. Print and return the results
print(f"Query:'{query}'\nNearest neighbors:")
return results
现在我们可以编写查询并搜索文本了!
python
query = "how precise was the science"
results = search(query)
results
该过程产生以下输出:
texts distance
0 It has also received praise from
many astronomers for its
scientific accuracy and portrayal 10757.379883
of theoretical astrophysics
1 Caltech theoretical physicist and
2017 Nobel laureate in
Physics[4] Kip Thorne was an 11566.131836
executive producer, acted as a
scientific consultant, and wrote a
tie-in book, The Science of Interstellar
2 Interstellar uses extensive
practical and miniature effects 11922.833008
and the company Double
Negative created additional
digital effects
第一个结果的距离最小,因此与查询最相似。从这个结果来看,它完美地回答了问题。请注意,如果我们只进行关键词搜索,这是不可能的,因为最前面的结果并不包含查询中的相同关键词。
实际上,我们可以通过定义一个关键字搜索功能来验证这一点,将两者进行比较。我们将使用 BM25 算法,它是领先的词法搜索方法之一。有关这些代码片段的源代码,请参阅本笔记本:
python
from rank_bm25 import BM25Okapi
from sklearn.feature_extraction import _stop_words
import string
def bm25_tokenizer(text):
tokenized_doc = []
for token in text.lower().split():
token = token.strip(string.punctuation)
if len(token) > 0 and token not in _stop_words.ENGLISH_STOP_WORDS:
tokenized_doc.append(token)
return tokenized_doc
tokenized_corpus = []
for passage in tqdm(texts):
tokenized_corpus.append(bm25_tokenizer(passage))
bm25 = BM25Okapi(tokenized_corpus)
def keyword_search(query, top_k=3, num_candidates=15):
print("Input question:", query)
##### BM25 search (lexical search) #####
bm25_scores = bm25.get_scores(bm25_tokenizer(query))
top_n = np.argpartition(bm25_scores, -num_candidates)[-num_candidates:]
bm25_hits = [{'corpus_id': idx, 'score': bm25_scores[idx]} for idx in top_n]
bm25_hits = sorted(bm25_hits, key=lambda x: x['score'], reverse=True)
print(f"Top-3 lexical search (BM25) hits")
for hit in bm25_hits[0:top_k]:
print("\t{:.3f}\t{}".format(hit['score'],
texts[hit['corpus_id']].replace("\n", " ")))
现在,当我们搜索同一个查询时,我们会从密集检索中得到一组不同的结果:
python
keyword_search(query = "how precise was the science")
结果是:
shell
Input question: how precise was the science
Top-3 lexical search (BM25) hits
1.789 Interstellar is a 2014 epic science fiction film
co-written, directed, and produced by Christopher Nolan
1.373 Caltech theoretical physicist and 2017 Nobel
laureate in Physics[4] Kip Thorne was an executive producer,
acted as a scientific consultant, and wrote a tie-in book, The
Science of Interstellar
0.000 It stars Matthew McConaughey, Anne Hathaway,
Jessica Chastain, Bill Irwin, Ellen Burstyn, Matt Damon, and
Michael Caine
请注意,尽管第一个结果与查询中的"science"一词相同,但它并没有真正回答问题。在下一节中,我们将了解如何通过添加重排器来改进这一搜索系统。但在此之前,我们先来了解一下密集检索的注意事项,并介绍一些将文本分解成块的方法,从而完成对密集检索的概述。
密集检索的注意事项
了解密集检索的一些缺点以及如何解决这些缺点是非常有用的。例如,如果文本不包含答案,会发生什么情况?我们仍然可以得到结果及其距离。例如
Query:'What is the mass of the moon?' Nearest neighbors:
texts distance
0 The film had a worldwide gross
over $677 million (and $773
million with subsequent rereleases), 1.298275
making it the tenthhighest
grossing film of 2014
1 It has also received praise from
many astronomers for its 1.324389
scientific accuracy and portrayal
of theoretical astrophysics
2 Cinematographer Hoyte van
Hoytema shot it on 35 mm movie 1.328375
film in the Panavision
anamorphic format and IMAX 70 mm
在这种情况下,一种可能的启发式方法就是设定一个阈值级别(threshold leve)--例如相关性的最大距离。很多搜索系统都会向用户提供所能获得的最佳信息,然后由用户决定是否相关。
跟踪用户是否点击了结果(并对结果感到满意)的信息,可以改进搜索系统的未来版本。密集检索的另一个注意的场景是,当用户希望找到特定短语的精确匹配结果时。这种情况非常适合关键词匹配。这就是为什么建议使用混合搜索(hybrid search),包括语义搜索和关键字搜索,而不完全依赖密集检索的原因之一。
密集检索系统还发现,要在其训练过的领域之外的领域中正常工作也很困难。因此,举例来说,如果您在互联网和维基百科数据上训练了一个检索模型,然后将其部署到法律文本上(没有足够的法律数据作为训练集的一部分),那么该模型在法律领域就无法正常工作。
最后我们要指出的是,在这种情况下,每个句子都包含一条信息,而我们展示的是专门询问该信息的查询。那么答案跨越多个句子的问题呢?这就突出了密集检索系统的一个重要设计参数:对长文本进行分块的最佳方法是什么?为什么我们首先需要对它们进行分块?
长文本分块(Chunking long texts)
Transformer 语言模型的一个限制是,它们支持的上下文大小有限,这意味着我们无法向它们提供超过模型支持的单词或tokens数量的超长文本。那么我们该如何嵌入长文本呢?
有几种可能的方法,图 8-7 中显示了两种可能的方法,包括为每个文档建立一个向量索引和为每个文档建立多个向量索引。
图 8-7. 创建一个代表整个文档的向量是可能的,但对于较长的文档来说,最好将其分割成较小的块,并获得各自的嵌入。
每个文件一个向量(One vector per document)
在这种方法中,我们使用单一向量来表示整个文档。这里的可能性包括:
- 只嵌入文档的主要部分,而忽略文本的其他部分。这可能意味着只嵌入标题或文档开头。这对于快速开始制作演示很有用,但会留下大量未索引的信息,因此无法搜索。作为一种方案,它可能更适用于开头部分包含文档要点的文档(如维基百科文章)。但对于真正的系统来说,这确实不是最好的方法,因为大量信息将被排除在索引之外,无法搜索。
- 将文档以分块的方式嵌入,嵌入这些块,然后将这些块聚合成一个单一的向量。通常的聚合方法是平均(average)这些向量。这种方法的缺点是会产生一个高度压缩的向量,从而丢失文档中的大量信息。
这种方法可以满足某些信息需求,但不能满足其他需求。很多时候,搜索是为了获取一篇文章中包含的特定信息,如果有自己的向量,就能更好地搜索到这些信息。
每个文档多个向量
在这种方法中,我们将文档分割成小块,然后计算这些小块的向量。这样,我们的搜索索引就变成了块嵌入索引,而不是整个文档的嵌入索引。图 8-8 展示了多种可能的文本分块方法。
图 8-8. 几种分块方法及对输入文本的影响。重叠的分块对于防止上下文缺失非常重要。
文档分块是更好的方案,因为它能全面覆盖文本,而且向量倾向于捕捉文本中的单个概念。这使得搜索索引更具表现力。图 8-9 显示了一些可能的方法。
图 8-9. 对文档进行分块嵌入的多种可能选项。
对长文本进行分块的最佳方法取决于系统预期的文本和查询类型。方法包括:
- 每个句子一个分块。这里的问题是,这可能过于细化,向量无法捕捉到足够的上下文。
- 每个段落一个分块。如果文本由短小的段落组成,这种方法就很好。否则,可能每 3-8 句话就是一个分块。
- 有分块的意义主要来源于周围的文本。因此,我们可以通过以下方式加入一些上下文:
- 为分块添加文档标题。
- 为语块添加前后的一些文本。这样,语块就可以重叠,从而包含一些也出现在相邻语块中的周边文本。如图 8-10 所示。随着这一领域的发展,预计会有更多的分块策略出现,其中有些甚至会使用 LLM 来动态地将文本分割成有意义的块。
随着这一领域的发展,预计会出现更多的分块策略--其中有些策略甚至可以使用 LLM 将文本动态地分割成有意义的块。
图 8-10. 将文本分成重叠的片段是保留不同片段更多上下文的一种策略。
近邻搜索(Nearest neighbor search)与向量数据库
查询被向量化后,我们需要从文本档案中找到与之最近的向量,如图 8-11 所示。查找最近邻的最直接方法是计算查询和存档之间的距离。这可以用 NumPy 轻松完成,如果存档中有数千或数万个向量,这也是一种合理的方法。
图 8-11. 正如我们在第 3 章中看到的,我们可以通过比较嵌入来快速找到与查询最相似的文档
当你的向量的数量超过数百万时,一种优化的检索方法是依靠近似近邻搜索库,如 Annoy 或 FAISS。这些库可以让你在几毫秒内从海量索引中检索到结果,其中一些库还可以通过利用 GPU 和扩展到机器集群来为超大索引提供服务,从而提高性能。
另一类向量检索系统是向量数据库,如 Weaviate 或 Pinecone。向量数据库允许你添加或删除向量,而无需重建索引。它们还提供了过滤搜索或自定义搜索的方法,而不仅仅是向量距离。
微调密集检索的嵌入模型(Fine-tuning embedding models for dense retrieval)
正如我们在第 4 章文本分类中讨论的那样,我们可以通过微调来提高 LLM 在某项任务中的性能。在这种情况下,检索需要优化文本嵌入,而不仅仅是token嵌入。微调的过程是:获取由查询和相关结果组成的训练数据。
让我们来看看数据集中的一个例子,句子 "interstellar premiered on October 26, 2014, in Los Angeles"。与此句子相关的两个可能查询结果是:
- 相关查询 1:"Interstellar release date"
- 相关查询 2:"When did Interstellar premier"
微调过程的目的是使这些查询的嵌入结果接近结果句子的嵌入结果。它还需要查看与句子无关的查询的负面例子,例如:
- 无关查询: "interstellar cast"
我们现在就有了三对查询--两对正查询和一对负查询。让我们假设,如图 8-12 所示,在微调之前,所有三个查询与结果文档的距离相同。这并不牵强,因为它们都在谈论 "星际(Interstellar)"。
图 8-12. 在微调之前,相关查询和不相关查询的嵌入都可能接近于某个特定文档。
微调步骤的作用是使相关查询离文档更近,同时使不相关查询离文档更远。我们可以从图 8-13 中看到这种效果。
图 8-13. 经过微调后,文本嵌入模型通过使用我们提供的相关和不相关文档示例,结合我们在数据集上定义相关性的方法,在这项搜索任务中变得更加出色。
重排序(Reranking)
很多企业已经建立了搜索系统。对于这些企业来说,将语言模型作为搜索管道的最后一步是一种更简单的方法。这一步的任务是根据搜索查询的相关性改变搜索结果的顺序。这一步可以极大地改进搜索结果,事实上,微软必应就是利用类似 BERT 的模型来改进搜索结果的。图 8-14 显示了作为两阶段搜索系统第二阶段的 rerank 搜索系统的结构。
图 8-14. LLM 重排序器作为搜索管道的一部分运行,其目标是按照相关性对一些入围搜索结果重新排序
重新排名举例
Reranker 接收搜索查询和大量搜索结果,并返回这些文档的最佳排序,从而使与查询最相关的文档排名靠前。Cohere 的 Rerank 端点是开始使用第一个 reranker 的简单方法。我们只需将查询和文本传递给它,就能得到结果。我们不需要训练或调整它:
python
query = "how precise was the science"
results = co.rerank(query=query, documents=texts, top_n=3, return_documents=True)
results.results
我们能够打印以下结果:
python
for idx, result in enumerate(results.results):
print(idx, result.relevance_score , result.document.text)
输出结果:
python
0 0.1698185 It has also received praise from many astronomers
for its scientific accuracy and portrayal of theoretical
astrophysics
1 0.07004896 The film had a worldwide gross over $677 million
(and $773 million with subsequent re-releases), making it the
tenth-highest grossing film of 2014
2 0.0043994132 Caltech theoretical physicist and 2017 Nobel
laureate in Physics[4] Kip Thorne was an executive producer,
acted as a scientific consultant, and wrote a tie-in book, The
Science of Interstellar
这表明 reranker 对第一个结果更有信心,给它打出了 0.16 的相关性分数,而其他结果的相关性分数要低得多。
在这个基本例子中,我们将所有 15 个文档都交给了重整器。但更常见的情况是,我们的索引会有数千或数百万项,我们需要筛选出一百或一千个结果,然后将这些结果提交给检索器。这一筛选步骤被称为搜索管道的第一阶段。
第一阶段的检索器可以是关键词搜索,也可以是密集检索,或者更好的是同时使用这两种方式的混合搜索。
我们可以重温之前的例子,看看在关键词搜索系统之后添加检索器如何提高其性能
python
def keyword_and_reranking_search(query, top_k=3, num_candidates=10):
print("Input question:", query)
##### BM25 search (lexical search) #####
bm25_scores = bm25.get_scores(bm25_tokenizer(query))
top_n = np.argpartition(bm25_scores, -num_candidates)[-num_candidates:]
bm25_hits = [{'corpus_id': idx, 'score': bm25_scores[idx]} for idx in top_n]
bm25_hits = sorted(bm25_hits, key=lambda x: x['score'], reverse=True)
print(f"Top-3 lexical search (BM25) hits")
for hit in bm25_hits[0:top_k]:
print("\t{:.3f}\t{}".format(hit['score'], texts[hit['corpus_id']].replace("\n", " ")))
#Add re-ranking
docs = [texts[hit['corpus_id']] for hit in bm25_hits]
print(f"\nTop-3 hits by rank-API ({len(bm25_hits)} BM25 hits re-ranked)")
results = co.rerank(query=query, documents=docs, top_n=top_k, return_documents=True)
# print(results.results)
for hit in results.results:
# print(hit)
print("\t{:.3f}\t{}".format(hit.relevance_score, hit.document.text.replace("\n", " ")))
现在,我们可以发送查询并检查关键字搜索的结果,然后将关键字搜索的结果筛选出前 10 个结果,再将它们传递给搜索器:
python
keyword_and_reranking_search(query = "how precise was thecscience")
输出结果:
Input question: how precise was the scienceTop-3 lexical search (BM25) hits1.789 Interstellar is a 2014 epic science fiction film cowritten,directed, and produced by Christopher Nolan1.373 Caltech theoretical physicist and 2017 Nobel laureate inPhysics[4] Kip Thorne was an executive producer, acted as ascientific consultant, and wrote a tie-in book, The Science ofInterstellar0.000 Interstellar uses extensive practical and miniatureeffects and the company Double Negative created additionaldigital effectsTop-3 hits by rank-API (10 BM25 hits re-ranked)0.004 Caltech theoretical physicist and 2017 Nobel laureate inPhysics[4] Kip Thorne was an executive producer, acted as ascientific consultant, and wrote a tie-in book, The Science ofInterstellar0.004 Set in a dystopian future where humanity is struggling tosurvive, the film follows a group of astronauts who travelthrough a wormhole near Saturn in search of a new home formankind0.003 Brothers Christopher and Jonathan Nolan wrote thescreenplay, which had its origins in a script Jonathandeveloped in 2007
我们看到,关键词搜索只给两个共享部分关键词的结果打分。在第二组结果中,reranker 会将第二个结果适当提升为与查询最相关的结果。这只是一个玩具示例,让我们对效果略知一二,但在实践中,这样的管道能显著提高搜索质量。在 MIRACL 这样的多语言基准测试中,ranker 可以将性能从 36.5 提升到 62.8,以 nDCG@10 表示(本章稍后将详细介绍评估结果)。
利用句子transformers进行开源检索和重排
如果您想在自己的机器上本地设置检索和重排,则可以使用句子转换器库。有关设置,请参阅 https://oreil.ly/jJOhV 上的文档。查看 "检索和重排 "部分,了解如何在库中执行这些步骤的说明和代码示例。
重排模型的工作原理
构建 LLM 搜索重排器的一种常用方法是将查询和每个结果呈现给作为交叉编码器(cross-encoder)工作的 LLM。这意味着查询和可能的结果会同时呈现给模型,让模型在给出相关性评分之前查看这两个文本,如图 8-15 所示。所有文档将作为一个批次同时处理,但每份文档都将根据查询进行独立评估。然后,分数将决定结果的新顺序。这种方法在一篇题为 "使用 BERT 进行多阶段文档排序 "的论文中有更详细的描述,有时也被称为 monoBERT。
图 8-15. 重搜索器通过同时查看文档和查询,为每份文档分配相关性得分
将搜索表述为相关性评分,基本上可以归结为一个分类问题。鉴于这些输入,模型会输出一个 0-1 的分数,其中 0 表示不相关,1 表示高度相关。这在我们第 4 章的分类讨论中应该不陌生。要了解有关使用 LLMs 进行搜索的更多信息,请参阅 "Pretrained transformers for text tanking: BERT and beyond"(《预训练转换器用于文本过滤:BERT 及其他》)强烈推荐阅读,了解这些模型在 2021 年之前的发展情况。
检索评估指标(Retrieval Evaluation Metrics)
语义搜索使用信息检索(IR)领域的指标进行评估。让我们来讨论其中一个常用指标:平均精度(MAP)。
对搜索系统进行评估需要三个主要组成部分:文本档案、查询集和相关性判断,其中相关性判断表明哪些文档与每个查询相关。我们可以从图 8-16 中看到这些组成部分。
图 8-16. 要对搜索系统进行评估,我们需要一个测试套件,其中包括查询和相关性判断,说明档案中哪些文档与每个查询相关。
有了这个测试套件,我们就可以开始探索如何评估搜索系统了。让我们从一个简单的例子开始。假设我们将查询 1 传递给两个不同的搜索系统。并得到两组结果。假设我们将结果数限制为三个,如图 8-17 所示。
图 8-17. 为了比较两个搜索系统,我们将测试套件中的相同查询传递给两个系统,并查看它们的最高搜索结果。
要想知道哪个系统更好,我们就来看看查询的相关性判断。图 8-18 显示了哪些返回结果是相关的。
图 8-18. 从测试套件的相关性判断中,我们可以看出系统 1 比系统 2 做得更好。
这让我们清楚地看到了系统 1 优于系统 2 的情况。直观地说,我们可以只计算每个系统检索到多少相关结果。系统 1 得到了三个中的两个正确结果,而系统 2 只得到了三个中的一个正确结果。但是,如果出现图 8-19 这样的情况,两个系统在三个结果中都只得到一个相关结果,但它们处于不同的位置,又该怎么办呢?
图 8-19. 我们需要一个评分系统,它可以奖励系统 1 将相关结果分配到较高的位置,即使两个系统在前三个结果中都只检索到一个相关结果。
在这种情况下,我们可以直觉地认为系统 1 比系统 2 做得更好,因为第一个位置(最重要的位置)的结果是正确的。但是,我们怎样才能用一个数字或分数来表示这个结果好多少呢?平均值精度是一种能够量化这种区别的测量方法。
在这种情况下,分配数字分数的一种常见方法是平均精度,即系统 1 的查询结果为 1,系统 2 的查询结果为 0.3。因此,让我们来看看平均精度是如何计算的,以评估一组结果,然后又是如何汇总的,以评估测试套件中所有查询的系统。
用平均精度为单个查询评分
要对搜索系统进行评分,我们可以重点对相关文档进行评分。我们先来看看测试套件中只有一个相关文档的查询。第一个很简单:搜索系统将相关结果(该查询的唯一可用结果)放在了最前面。
图 8-20 显示了这一计算结果:从第一个位置看,我们有一个相关结果,导致位置 1 的精确度为 1.0(计算方法是位置 1 的相关结果数除以我们当前查看的位置)。
图 8-20. 要计算平均平均精度,我们首先计算每个位置的精度,从位置 1 开始。
由于我们只对相关文档进行评分,因此我们可以忽略非相关文档的评分,并在此停止计算。但是,如果系统真的把唯一相关的结果放在了第三位呢?这会对得分产生什么影响?图 8-21 显示了这是如何导致惩罚的。
图 8-21. 如果系统将非相关文档放在相关文档之前,其精确度得分就会受到影响。
现在我们来看看有多个相关文档的查询。图 8- 22 显示了计算结果以及平均值是如何产生的。
图 8-22. 有多个相关文档的文档的平均精确度考虑了所有相关文档的 k 结果的精确度
用平均精度对多个查询进行评分
既然我们已经熟悉了 k 时的精确度和平均精确度,我们就可以将这一知识扩展到一个指标,该指标可以根据测试套件中的所有查询对搜索系统进行评分。这个指标叫做平均精度。图 8-23 展示了如何通过求取每个查询的平均精度的平均值来计算这一指标。
图 8-23. 平均精度平均值考虑了测试套件中每个查询的系统平均精度得分。通过求平均值,我们可以得出一个单一指标,用来比较一个搜索系统和另一个搜索系统。
你可能想知道,为什么同样的运算被称为 "平均(mean)"和 "均值(average)"。这可能是一种美学选择,因为 MAP 听起来比平均值平均精度更好。
现在,我们有了一个可以用来比较不同系统的单一指标。如果您想了解更多有关评估指标的信息,请参阅 Christopher D. Manning、Prabhakar Raghavan 和 Hinrich Schütze 合著的《信息检索导论》(Introduction to Information Retrieval)(剑桥大学出版社)中的 "信息检索中的评估 "一章。
除平均精度外,另一个常用于搜索系统的指标是归一化折算累积增益(nDCG),该指标更为细致,因为文档的相关性不是二元对立的(相关与不相关),在测试套件和评分机制中,一个文档可以被标记为比另一个文档更相关。