改进向量搜索-使用PostgresML和LlamaIndex重新排名
搜索和重新排名:提高结果相关性

搜索系统到底怎么精准找到你想要的内容?通常,这条路子分两种:关键词搜索和语义搜索。关键词搜索就是把你的查询词和数据库里的内容做精确匹配,而语义搜索则动用自然语言处理和机器学习,去理解你的真实意图。很多高效的系统都把这两种方法结合起来,效果才能最大化。
第一次检索之后,还要对结果重排一下,相关性才能更上一层楼。传统的重排序靠的是历史用户交互数据,但面对新内容就抓瞎了,而且训练起来需要海量数据。那有没有更先进的替代方案呢?交叉编码器(Cross-Encoder)就干这个事——它直接比较查询和结果对的相似度。
交叉编码器的工作方式,就是直接把两段文本扔进去,算出相似度得分。跟传统的语义搜索不同,你不能预先算好交叉编码器的向量嵌入然后重复用——每次都得对每一对要比较的文本单独跑一遍。这玩意儿计算开销极大,在大规模搜索场景下根本跑不动。不过,它特别适合对数据集的子集进行重排序,因为它擅长评估那些没见过的、全新的数据,不需要大量用户交互数据来微调。
交叉编码器补上了传统重排序系统在深度文本分析上的短板,尤其是面对新颖或高度特定内容的时候。它不依赖海量用户交互数据集来训练(虽然那些数据也有用),而且特别擅长处理新数据、之前从未见过的数据。这样一来,交叉编码器就成了在重排序环境里提升搜索结果相关性的利器。
实施重新排名
接下来,我们用 LlamaIndex 和 PostgresML 托管索引,搭一个简单的重排序示例。先装好必要的依赖:
pip install llama_index llama-index-indices-managed-postgresml
数据集用 Paul Graham 的文章,用 curl 下载:
mkdir data
curl -o data/paul_graham_essay.txt https://raw.githubusercontent.com/run-llama/llama_index/main/docs/docs/examples/data/paul_graham/paul_graham_essay.txt
PostgresML 托管索引会帮你处理存储、拆分、嵌入和查询这些事。你只需要一个数据库连接字符串。设置环境变量:
export PGML_DATABASE_URL="{YOUR_CONNCECTION_STRING}"
然后创建索引:
from llama_index.core.readers import SimpleDirectoryReader
from llama_index.indices.managed.postgresml import PostgresMLIndex
documents = SimpleDirectoryReader("data").load_data()
index = PostgresMLIndex.from_documents(
documents, collection_name="llama-index-rerank-example"
)
注意,collection_name 是唯一标识当前索引的名字。这里用 SimpleDirectoryReader 加载文档,然后直接构造 PostgresMLIndex。这个过程不需要你对文档做什么预处理——文档会直接发到 PostgresML,按管道规范自动存储、拆分和嵌入。这是 PostgresML 托管索引的独特之处。
现在试试搜索吧!先做一次语义搜索,从索引里取前 2 个结果:
retriever = index.as_retriever(limit=2)
docs = retriever.retrieve("What did the author do as a child?")
for doc in docs:
print("---------")
print(f"Id: {doc.id_}")
print(f"Score: {doc.score}")
print(f"Text: {doc.text}")
输出是这样的:
---------
Id: de01b7e1-95f8-4aa0-b4ec-45ef64816e0e
Score: 0.7793415653313153
Text: Wow, I thought, there's an audience. If I write something and put it on the web, anyone can read it. That may seem obvious now, but it was surprising then. In the print era there was a narrow channel to readers, guarded by fierce monsters known as editors. The only way to get an audience for anything you wrote was to get it published as a book, or in a newspaper or magazine. Now anyone could publish anything.
This had been possible in principle since 1993, but not many people had realized it yet. I had been intimately involved with building the infrastructure of the web for most of that time, and a writer as well, and it had taken me 8 years to realize it. Even then it took me several years to understand the implications. It meant there would be a whole new generation of essays. [11]
In the print era, the channel for publishing essays had been vanishingly small. Except for a few officially anointed thinkers who went to the right parties in New York, the only people allowed to publish essays were specialists writing about their specialties. There were so many essays that had never been written, because there had been no way to publish them. Now they could be, and I was going to write them. [12]
I've worked on several different things, but to the extent there was a turning point where I figured out what to work on, it was when I started publishing essays online. From then on I knew that whatever else I did, I'd always write essays too.
---------
Id: de01b7e1-95f8-4aa0-b4ec-45ef64816e0e
Score: 0.7770352826735559
Text: Asterix comics begin by zooming in on a tiny corner of Roman Gaul that turns out not to be controlled by the Romans. You can do something similar on a map of New York City: if you zoom in on the Upper East Side, there's a tiny corner that's not rich, or at least wasn't in 1993. It's called Yorkville, and that was my new home. Now I was a New York artist — in the strictly technical sense of making paintings and living in New York.
I was nervous about money, because I could sense that Interleaf was on the way down. Freelance Lisp hacking work was very rare, and I didn't want to ha ve to program in another language, which in those days would ha ve meant C++ if I was lucky. So with my unerring nose for financial opportunity, I decided to write another book on Lisp. This would be a popular book, the sort of book that could be used as a textbook. I imagined myself living frugally off the royalties and spending all my time painting. (The painting on the cover of this book, ANSI Common Lisp, is one that I painted around this time.)
The best thing about New York for me was the presence of Idelle and Julian Weber. Idelle Weber was a painter, one of the early photorealists, and I'd taken her painting class at Harvard. I've never known a teacher more beloved by her students. Large numbers of former students kept in touch with her, including me. After I moved to New York I became her de facto studio assistant.
结果看起来还行,但不够完美。试试用交叉编码器重排一下:
retriever = index.as_retriever(
limit=2,
rerank={
"model": "mixedbread-ai/mxbai-rerank-base-v1",
"num_documents_to_rerank": 100
}
)
docs = retriever.retrieve("What did the author do as a child?")
for doc in docs:
print("---------")
print(f"Id: {doc.id_}")
print(f"Score: {doc.score}")
print(f"Text: {doc.text}")
这里我们配置检索器仍然只返回前 2 个文档,但加了个 rerank 参数,指定使用 mixedbread-ai/mxbai-rerank-base-v1 模型。这意味着初始语义搜索会拿到 100 个结果,然后由这个模型对它们重新排序,最终只展示前 2 个。
运行输出:
Id: de01b7e1-95f8-4aa0-b4ec-45ef64816e0e
Score: 0.17803585529327393
Text: What I Worked On
February 2021
Before college the two main things I worked on, outside of school, were writing and programming. I didn't write essays. I wrote what beginning writers were supposed to write then, and probably still are: short stories. My stories were awful. They had hardly any plot, just characters with strong feelings, which I imagined made them deep.
The first programs I tried writing were on the IBM 1401 that our school district used for what was then called "data processing." This was in 9th grade, so I was 13 or 14. The school district's 1401 happened to be in the basement of our junior high school, and my friend Rich Dra ves and I got permission to use it. It was like a mini Bond villain's lair down there, with all these alien-looking machines — CPU, disk drives, printer, card reader — sitting up on a raised floor under bright fluorescent lights.
The language we used was an early version of Fortran. You had to type programs on punch cards, then stack them in the card reader and press a button to load the program into memory and run it. The result would ordinarily be to print something on the spectacularly loud printer.
---------
Id: de01b7e1-95f8-4aa0-b4ec-45ef64816e0e
Score: 0.1057136133313179
Text: I wanted not just to build things, but to build things that would last.
In this dissatisfied state I went in 1988 to visit Rich Dra ves at CMU, where he was in grad school. One day I went to visit the Carnegie Institute, where I'd spent a lot of time as a kid. While looking at a painting there I realized something that might seem obvious, but was a big surprise to me. There, right on the wall, was something you could make that would last. Paintings didn't become obsolete. Some of the best ones were hundreds of years old.
And moreover this was something you could make a living doing. Not as easily as you could by writing software, of course, but I thought if you were really industrious and lived really cheaply, it had to be possible to make enough to survive. And as an artist you could be truly independent. You wouldn't ha ve a boss, or even need to get research funding.
I had always liked looking at paintings. Could I make them? I had no idea. I'd never imagined it was even possible. I knew intellectually that people made art — that it didn't just appear spontaneously — but it was as if the people who made it were a different species. They either lived long ago or were mysterious geniuses doing strange things in profiles in Life magazine. The idea of actually being able to make art, to put that verb before that noun, seemed almost miraculous.
这次结果明显好多了!最上面的文档直接回答了用户的问题。注意,我们不需要指定第三方的重排序 API——PostgresML 直接在数据库里用交叉编码器搞定了重排序。
我们还可以把重排序直接用在 RAG 流程里:
query_engine = index.as_query_engine(
streaming=True,
vector_search_limit=2,
vector_search_rerank={
"model": "mixedbread-ai/mxbai-rerank-base-v1",
"num_documents_to_rerank": 100,
},
)
results = query_engine.query("What did the author do as a child?")
for text in results.response_gen:
print(text, end="", flush=True)
运行输出:
Based on the context information, as a child, the author worked on writing (writing short stories) and programming (on the IBM 1401 using Fortran) outside of school.
这正是我们想要的答案!
重新排序可带来更好的结果
搜索这事儿,有时候真挺复杂。用交叉编码器做重排序,能通过比较文本对、有效处理新数据,让搜索结果更精准。借助 LlamaIndex 和 PostgresML 实现重排序,能显著提升搜索结果的命中率,在检索增强生成(RAG)应用里给出更精确的回答。
-
- 关于宇宙的好的网名有哪些
- 角色扮演 | 1
- 网名