本文将了解到什么是矢量数据库,以及如何与LLMs进行集成。通过LLMs和向量数据库的结合,可以节省微调带来的开销和时间。
通常,LLM会在各种各样的数据上进行训练,这使它们具有广泛的理解能力,但可能会导致在特定的知识领域存在差距。有时,它们甚至可能产生与目标无关或带有偏见的信息——这是从广阔但未经筛选的web学习的副产品。为了解决该问题,我们引入了向量数据库(Vector Database)的概念。这些数据库以一种称为"向量嵌入"的独特格式存储数据,可以让LLMs掌握和使用的信息更连贯和准确。
本文给出了如何使用向量数据库构建一个LLM,并改进LLM对该流程的使用。我们将会看到二者的结合是如何让LLMs更加准确可靠(特别针对特定主题)。
下面我们简单总结了向量数据库,解释向量嵌入的概念以及它在增加AI和机器学习应用方面的角色。之后我们会展示这些数据库和传统数据库的不同之处,以及为什么他们更适合AI任务,特别是与非结构数据(如文本、图片和复杂模式)打交道时。
之后,我们会探索使用该技术来构建一个封闭式QA机器人应用,该机器人使用了Falcon-7B模型和ChromaDB向量数据库,证明了LLMs与正确的工具和技术相结合时的成效。
最后,你将清楚认识到如何使用LLMs和向量数据库来创建新颖、上下文相关并且可靠的应用。不管你是AI爱好者还是经验丰富的开发者,本指南将帮助你轻松自信地探索这个激动人心的领域。
在深入了解向量数据库之前,需要了解向量嵌入(vector embedding)的概念。向量嵌入是机器学习中用于将原始数据转换为AI系统可以理解的数值格式必不可少的一种技术,涉及数据转换,如将文本或图片转换为一系列数字,这些数字在高维度空间称之为向量。高纬度数据指包括很多属性或特征的数据,每个数据代表一个不同的维度,这些维度可以帮助捕获该数据的细微特征。
创建向量嵌入的过程开始于数据输入,可以是语句中的任意内容或是图片的像素等。大语言模型和其他AI算法会分析这些数据并确定其关键特征。例如,在文本数据中,可能涉及理解单词的意义和它在语句中的上下文。嵌入模型会将这些特征转换为一个数值格式,向量中的每个数值代表数据的一个特征,通过将这些特征数值封装到一起,就可以作为机器可以处理的输入。
之所以说这些向量是高纬度的,是因为它们包含很多数值,每个数值对应数据中的某个(不同)特征。这种高维度使得向量能够捕捉复杂、详细的信息,使之成为强大的AI模型工具。模型使用这些嵌入向量来识别数据中的模式、关系和潜在结构。
向量数据库针对向量嵌入的特性提供了优化存储和查询的能力。它们擅长提供高效搜索、高性能、可扩展性,并通过比较和识别数据点之间的相似性,实现数据的快速检索。
这些数值代表了复杂的高纬度信息,使之有别于传统的主要使用文本和数字存储数据的系统。向量数据库的主要能力是管理和查询如图片、视频和文本格式的数据,当这些数据转换为向量格式后,特别适用于机器学习和AI应用。
在下面示例中,我们将一段文本转换为词向量,这一步是神经语言处理的基本步骤,可以让我们量化和分析语言关系。例如,"puppy"的向量表达应该更接近"dog"的向量空间,而不是"house",反映了它们的语义接近性。这种方式还可以拓展到类似的关系中。"man"和"woman"的向量距离和方向类似于"king"和"queen"之间的距离和方向。下面展示了单词向量不仅仅代表单词,还可以在多维度向量空间中对它们的语义关系进行有意义的比较。
向量数据库用于处理向量嵌入,已经有一些关键使用场景,特别是在机器学习和AI领域:
相似搜索:这是向量数据库的关键功能。它们可以在高维度空间内找出与给定请求相似的数据点。特别适用于图形和音频检索(希望找出和特定输入类似的内容),下面是一些业界使用场景:
推荐系统:向量数据库通过处理用户和商品嵌入来支持推荐系统。它可以将用户和他们感兴趣或过去有过互动的物品(如产品、电影或文章)关联起来。下面是一个业务使用场景:
基于内容的检索:这里向量数据库用于基于实际内容而非传统元数据来检索内容。特别对于非结构化数据,如文本和图像,需要首先对内容本身进行分析。下面是一些业界使用场景:
关于基于内容的检索的最后一点越来越重要,并促进了一种新的应用:
使用上下文理解来增强LLMs:通过存储和处理文本嵌入,向量数据库可以帮助LLMs实现更精细化以及上下文相关的检索工作。它们可以帮助理解大量文本的语义内容,在回答复杂搜索、维护对话上下文或生成相关内容等任务中起着关键作用。这种应用正迅速成为向量数据库的一个重要场景,展示了其在增强高级AI系统(如LLM)能力方面起到的作用。
传统的SQL数据库在结构化数据管理方面表现出色,擅长处理精确匹配和明确定义的条件逻辑。它们维护数据的完整性,适合需要精确、结构化数据处理的应用程序。但这种刚性设计模式也使得它们不太适合处理非结构化数据的语义以及上下文之间的细微差别,而这正是LLM和生成式AI应用所需要的。
另一方面,相比传统的SQL系统,NoSQL数据库则更加灵活。它们可以处理半结构化和非结构化数据,如JSON文档,这使得它们更适用于AI和机器学习场景。但即便如此,相比用于LLMs和生成式的向量数据库来说,NoSQL数据库依然缺少某些方面的能力,如解析上下文、模式以及简单数据检索之外的语义内容。
向量数据库弥补了这一空白。它们以AI为中心,使用向量的方式处理数据,可以有效管理复杂的非结构化数据。当与LLMs协作时,向量数据库支持相似性查找和上下文理解,提供了超出传统SQL和NoSQL数据库的能力。它们擅长处理近似值和模式识别,使之特别适用于对数据的微妙理解比精确数据匹配更重要的AI应用。
对于依赖速度和精确检索高纬数据的应用来说,向量数据库的性能优化至关重要。这里面涉及提升查询速度、保证高准确性以及有效处理不断增长的数据量和用户请求。其中最重要的一部分是围绕索引策略进行优化(一种更有效组织和查询向量数据的技术)。下面,我们将介绍这些索引策略,以及它们是如何提升向量数据库性能的。
向量数据库的索引策略用于方便和准确检索与查询向量相似的向量。这些策略可以显著影响到查询操作的速度和准确性。
HNSW图很好地均衡了查询速度和准确性,使其非常适于需要立即响应的实时查询应用和推荐系统。它们在中大型数据集中表现良好,提供可扩展的搜索能力。但对于超大型数据集来说,其内存需求可能会成为瓶颈,因此这种方式不适用于内存资源受限或数据集大小远超实际内存容量的场景。
IVF方法适合处理可扩展查询环境中的高纬度数据,可以通过对相似项划分聚类来有效降低搜索空间,特别适用于相对静态的数据库,且可以允许偶尔重新聚类的场景。但对于低纬数据(可能存在过分分割)或要求尽可能低延迟的应用(聚类过程和跨多个聚类查询可能会引入额外的查询时间)来说,IVF可能不是最佳的选择。
通过这些索引策略和注意事项来提升向量数据库的性能需要深刻理解底层数据以及特定的应用需求。通过仔细选择并调节这些策略,开发者可以显著提高基于向量的应用的响应能力和可扩展性,保证其满足真实使用场景。
像Facebook的 LLama2 或 TIIUAE的 Falcon大语言模型具有先进的人工智能和类人类的文本生成能力,但由于它们是基于广泛、通用的数据集进行训练的,因此缺乏对特定上下文的处理能力。
可以使用如下两种方式解决上下文限制的问题:
第二种方式称为RAG,我们将在下一章中详细介绍。
本章中,我们将介绍如何使用向量数据库来构建一个LLM,是使用的模型是一个封闭式问题机器人(Closed Q&A bot),该机器人使用一组集成的技术组件来有效解答与科学相关的问题。
实现架构如下。使用multi-qa-MiniLM-L6-cos-v1作为嵌入模型,负责将数据集的数据导入数据库。 在用户提问时,会使用嵌入模型对提问文本进行编码,生成向量数据库可以理解的格式,并由向量数据库返回应答。Falcon 7B模型用于优化向量数据库的应答结果。 下图有点问题,看起来请求和应答是异步执行的,但代码中是串行的。
为了实现本文的代码,需要安装如下库:
!pip install -qU \
transformers==4.30.2 \
torch==2.0.1+cu118 \
einops==0.6.1 \
accelerate==0.20.3 \
datasets==2.14.5 \
chromadb \
sentence-transformers==2.2.2
本代码运行在Qwak的Workspaces的一台gpu.a10.2xl 实例上。需要注意的是运行Falcon-7B-Instruct模型的代码可能会根据硬件配置而变化。
一开始,我们需要拉取Databricks-Dolly数据集,重点关注closed_qa
一类的数据集。这些数据条目通常以对精确信息的需求为特征,这种转一性给通用性大语言模型的训练带来了一定挑战。
在databricks/databricks-dolly-15k的dataset card中可以查看支持的
split
和过滤字段。
from datasets import load_dataset
# Load only the training split of the dataset
# 加载 train 类型的数据集
train_dataset = load_dataset("databricks/databricks-dolly-15k", split='train')
# 通过过滤拉取 category为 closed_qa 的一组数据
closed_qa_dataset = train_dataset.filter(lambda example: example['category'] == 'closed_qa')
print(closed_qa_dataset[0])
数据库通常可以分为三类:training、validating和evaluating。分别用于训练、校验和评估模型。
典型的数据集条目的格式如下:
{
"instruction": "When was Tomoaki Komorida born?",
"context": "Komorida was born in Kumamoto Prefecture on July 10, 1981. After graduating from high school, he joined the J1 League club Avispa Fukuoka in 2000. His career involved various positions and clubs, from a midfielder at Avispa Fukuoka to a defensive midfielder and center back at clubs such as Oita Trinita, Montedio Yamagata, Vissel Kobe, and Rosso Kumamoto. He also played for Persela Lamongan in Indonesia before returning to Japan and joining Giravanz Kitakyushu, retiring in 2012.",
"response": "Tomoaki Komorida was born on July 10, 1981.",
"category": "closed_qa"
}
下面,我们将重点为每组指令及其各自的上下文生成词嵌入,并将它们集成到矢量数据库ChromaDB中。
Chroma DB是一个开源的向量数据库系统,擅长管理向量嵌入,专为语义查询引擎之类的应用量身定做,这种能力在自然语言处理和机器学习领域至关重要。Chroma DB作为一个内存型数据库,支持数据的快速访问和高速处理。其友好的Python设置增强了对我们项目的吸引力,简化了与我们工作流程的集成。更多参见Chroma DB 文档。
为了给回答生成嵌入向量,我们使用了multi-qa-MiniLM-L6-cos-v1 模型,该模型专为语义搜索场景而训练。给定一个问题/搜索请求,该模型可以找到相关的文本信息,这正是我们所需要的。
在下面例子中,解释了如何将嵌入向量存储在Chroma的内存集合中。
import chromadb
from sentence_transformers import SentenceTransformer
class VectorStore:
def __init__(self, collection_name):
# 初始化嵌入模型和向量数据库
self.embedding_model = SentenceTransformer('sentence-transformers/multi-qa-MiniLM-L6-cos-v1')
self.chroma_client = chromadb.Client()
self.collection = self.chroma_client.create_collection(name=collection_name)
# 该方法用于将输入的数据集转换为向量,并保存到向量数据库中
def populate_vectors(self, dataset):
for i, item in enumerate(dataset):
combined_text = f"{item['instruction']}. {item['context']}"
embeddings = self.embedding_model.encode(combined_text).tolist()
self.collection.add(embeddings=[embeddings], documents=[item['context']], ids=[f"id_{i}"])
# 该方法直接从向量数据库中与查询相关的上下文
def search_context(self, query, n_results=1):
query_embeddings = self.embedding_model.encode(query).tolist()
return self.collection.query(query_embeddings=query_embeddings, n_results=n_results)
# Example usage
if __name__ == "__main__":
# Initialize the handler with collection name
vector_store = VectorStore("knowledge-base")
# 输入上一步拉取的 closed_qa 数据集
vector_store.populate_vectors(closed_qa_dataset)
对于每个数据集条目,我们会生成一个结合了instruction
和context
字段的嵌入向量,在我们的LLM提示中,context
充当要检索的文档。
下面,我们使用Falcon-7b-instruct LLM来生成封闭式信息查询的应答(无需额外的上下文),并展示知识库的重要性。
为了实现文本生成任务,我们采用了来自Hugging Face的Falcon-7b-instruct模型。该模型是Falcon的革新系列,由Abu Dhabi的Technology Innovation Institute开发。
Falcon-7B-Instruct引人注目的一点是其有效平衡了高级功能和管理大小,用于理解复杂的文本和生成任务,其性能可以与更大的封闭源代码模型相媲美,但包更精简。这也是它成为我们理想选择的原因(可以深入语言理解,但不会带来跟更大模型一样的开销)。
如果你打算运行Falcon-7B-Instruct模型(无论是本地机器或远程服务器),需要注意硬件要求。在HuggingFace的文档中有提到,该模型最少需要16GB的内存来满足最佳的性能和更快的响应次数,最好使用GPU。
import transformers
import torch
from transformers import AutoTokenizer, AutoModelForCausalLM
class Falcon7BInstructModel:
def __init__(self):
# 初始化模型
model_name = "tiiuae/falcon-7b-instruct"
self.pipeline, self.tokenizer = self.initialize_model(model_name)
def initialize_model(self, model_name):
# 初始化 Tokenizer
tokenizer = AutoTokenizer.from_pretrained(model_name)
# Pipeline setup for text generation
pipeline = transformers.pipeline(
"text-generation",
model=model_name,
tokenizer=tokenizer,
torch_dtype=torch.bfloat16,
trust_remote_code=True,
device_map="auto",
)
return pipeline, tokenizer
def generate_answer(self, question, context=None):
# Preparing the input prompt
prompt = question if context is None else f"{context}\n\n{question}"
# 生成应答
sequences = self.pipeline(
prompt,
max_length=500,
do_sample=True,
top_k=10,
num_return_sequences=1,
eos_token_id=self.tokenizer.eos_token_id,
)
# Extracting and returning the generated text
return sequences['generated_text']
可以参考Hugging Face的文档来编写上述代码:
tokenizer
的设计,这些tokens可以是单词、子单词(subword)甚至是特征。在Falcon-7B-Instruct模型的上下文中,AutoTokenizer.from_pretrained(model)
调用会加载一个专为该模型设计的tokenizer
,保证能够遵循该模型训练的方式来将文本转化为token。
使用方式如下:
# 初始化 Falcon 模型类
falcon_model = Falcon7BInstructModel()
user_question = "When was Tomoaki Komorida born?"
# LLM使用LLM来为用户提问生成回答
answer = falcon_model.generate_answer(user_question)
print(f"Result: {answer}")
你可能已经猜到模型的输出:
{ answer: “I don't have information about Tomoaki Komorida's birthdate.” }
使用Falcon-7B-Instruct
时,如果没有相关的上下文,就会返回一个否定响应。这说明需要进一步丰富背景知识,为非一般性问题提供更有针对性和有用的答案。
下面我们通过向量存储来为模型提供相关上下文,从而提升模型能力。
有趣的是,我们使用相同的VectorStore类来同时生成嵌入向量和从用户提问中获取上下文。
# Assuming vector_store and falcon_model have already been initialized
# 从 VectorStore 中获取上下文(假设数据集已经导入数据库)
context_response = vector_store.search_context(user_question)
# 从向量数据库的响应中抽取上下文,上下文应该是 context 关键字的第一个元素
context = "".join(context_response['context'][0])
# Falcon模型结合提取的上下文生成应答
enriched_answer = falcon_model.generate_answer(user_question, context=context)
print(f"Result: {enriched_answer}")
最后可以得到准确的回答:
Tomoaki Komorida was born on July 10, 1981.