1-Chroma-起步

系列文章目录

1-Chroma-起步



概述

Chroma是一款流行的开源嵌入(embedding)数据库。Chroma通过将知识、事实和技能等可插拔(pluggable)到 LLM(大语言模型)中,从而使得构建 LLM 应用变得很容易。这里需要说明的是:
(1)什么是嵌入(embed和embedding)?可以理解成将数据嵌入到向量里,想象有一个布满格子的模具,这些格子被排列成一行,现在将一个橡皮泥材质的小猫摁入(embed)到模具中,小猫就变成了模具的形状,这个塑形的过程就是嵌入,小猫代表一条数据,每个格子代表小猫的一个特征,一排格子就是一个特征向量,一个特征向量就被称为一个embedding,嵌入的本质就是将数据表示为特征向量,即数据向量化,这里的数据包括但不限于:单词,句子,段落,文章,公式,图片,语音,视频等任何可在计算机中表示的事物。
(2)什么是可插拔(pluggable)?经过Chroma处理或存储的数据可以做为上下文发送给 LLM,并被LLM识别,这些数据一旦存储后就可以被重复使用。
Chrome与App,LLM通常会被搭配使用,它们之间的配合流程如下图,该图摘自Chrome官网。首先App会将查询内容(一般是用户的提出的问题)发送给Chroma,Chroma查找与查询内容最相似的文档内容,App将查找的文档内容与查询内容一起作为LLM的上下文窗口(context window)发送给LLM,LLM回答问题并返回给App。
实际开发中,大多会与LangChain结合使用,LangChain对Chroma进行了集成封装,具体结合方式可以查看这个demo:https://github.com/hwchase17/chroma-langchain在这里插入图片描述
Chroma是基于CPU的,默认使用all-MiniLM-L6-v2嵌入模型,可以将嵌入模型更换为支持GPU的模型来支持GPU,能在Chroma中使用的支持GPU的模型包括:hkunlp/instructor-base,hkunlp/instructor-large和hkunlp/instructor-xl。
Chroma提供了以下工具:

  • 存储嵌入和它们的元数据
  • 嵌入文档和查询嵌入
  • 搜索嵌入
    Chroma的优势在于:
  • 简单,提高了开发者的生产率,API较少,调用容易,上手快,且足够灵活,能满足各种场景
  • 在搜索基础上提供了分析和处理功能,比如:where筛选,计数等
  • 具有快速的数据存储和查询能力,可以在短时间内处理大规模的向量数据
  • 被集成到了LangChain中,可以快速构建基于Chroma的AI应用

参考资料

官网主页:https://www.trychroma.com/
官网文档:https://docs.trychroma.com/
github主页:https://github.com/chroma-core/
github项目:https://github.com/chroma-core/chroma/

版本发布

Chroma是一个比较新的项目,通过github的可以看到,在2022-10-16创建了第一个版本0.0.1,2022-10-23发布了第一个预发布版本0.0.2,2023-07-18发布了第一个正式发布版本0.4.0,此后每次发布基本上都是正式发布,鲜有预发布。Chrome的迭代速度很快,平均下来几乎每周都会有发布,有时会密集发布。需要注意的是,预发布的版本通常未经过充分测试和验证,可能存在不稳定性或不完全符合预期的行为。

架构

Chroma采用C/S(Client/Server)架构模式,包括三部分:Python客户端SDK,JavaScript/TypeScript客户端SDK和服务端应用,服务端应用是基于Python的。开发者可以通过使用这三种编程语言去调用Chroma的服务端查询或存储数据。
Chroma提供了两种运行模式:in-memory和client/server,即内存模式和C/S模式。两者的区别在于是否通过url和端口号访问服务端,C/S模式可以请求其它机器上部署的Chroma服务端的服务,而in-memory则只能调用本地的Chroma服务端的服务。对于Python的C/S模式尚处于alpha开发阶段。
对于in-memory,也分成两种模式:ephemeral和persistent,即短暂模式和持久化模式。短暂模式下程序一旦结束数据就被销毁,持久化模式下数据会被存储到本地硬盘文件中供以后重复使用,再次启动程序会自动加载数据,关闭程序会自动保存数据。
不同的编程语言支持的模式不同,具体如下表格,该表格摘自官网。可以看出,Python同时支持两种模式,JavaScript仅支持客户端,而其它语言需要借助中间件才能使用客户端。

in-memoryclient
Python✅(by Chroma)
Javascript✅ (by Chroma)
Rubyfrom @mariochavez
Javafrom @t_azarov
Gofrom @t_azarov
C#from @microsoft
Rustfrom @Anush008
Elixirfrom @3zcurdia
Dartfrom @davidmigloz
Other?

开发和运行环境

以下安装和示例程序都是针对Python编程,在win10的conda虚拟环境中运行过,chromadb被安装在conda虚拟环境中。

包安装

如果网速不快,可以使用清华源。

pip install chromadb -i https://pypi.tuna.tsinghua.edu.cn/simple

编码流程及示例

这里只演示in-memory的ephemeral模式,该模式仅支持Python,对于其它模式请关注后续文章。in-memory模式的编码流程包含四步:

1.获取客户端 > 2.创建或获取被查询集合 > 3.往集合中添加被查询数据 > 4.查询集合中的数据

在ephemeral模式下数据并没有持久化到硬盘中,程序运行结束数据就会销毁。如果要持久化,则需要在Client()中设置setting参数,或使用PersistentClient(path)创建客户端(Chroma的版本要大于0.4.0),将数据保存到指定的文件中。持久化后,collection可以被重复使用,之后再次启动客户端,会自动加载collection的数据,若添加数据,关闭客户端会自动保存。持久化会放到后续文章介绍。

步骤1:获取客户端

import chromadb
# in-memory ephemeral model
client = chromadb.Client()

如果Chroma版本大于0.4.0,可以这样调用。

import chromadb
# in-memory ephemeral model
client = chromadb.EphemeralClient()

步骤2:创建或获取被查询集合

集合是一个用来存放嵌入,文档和元数据的容器。允许同时创建多个集合,集合名称是必须的,用来区分多个不同的集合,且名称长度限制在3-63个字符之间,还有一些其他限制,后续文章会详细介绍。存贮在集合中的数据会提供给用户进行查询。嵌入即特征向量,文档即文本内容,元数据是用来描述文档的属性,包括:名称,来源,类型,作者等自定义属性,具体可以参考第三步的添加数据。

collection = client.create_collection(name="my_collection")

对于已存在的集合,可以直接通过名称获取,如果集合不存在会报错。

collection = client.get_collection(name="my_collection")

也可以通过以下方式调用,如果给定名称的集合已经存在则直接获取,否则创建。

collection = client.get_or_create_collection(name="my_collection")

步骤3:往集合中添加被查询数据

添加被查询数据后,就可以通过查询方法找出与用户输入信息最相近的数据。add方法的参数有:

  1. ids:多个文档的唯一标识列表,必传参数。
  2. embeddings:多个文档的嵌入列表,嵌入也是数组,选传参数,但是与documents参数至少有一个要传。
  3. documents:多个文档的文本内容字符串列表,选传参数,但是与embeddings参数至少有一个要传。
  4. metadatas:多个文档的元数据字典列表,元数据的数据可以是任何自定义文档属性,包括:名称,来源,类型,作者等。

这四个参数都是列表,且长度要一致,有几个文档就要有几个元素。
如果传入的是文档的内容文本,Chroma会自动处理分词(tokenization),嵌入(embedding)和索引(indexing),后续的查询方法需要使用query_texts参数。

collection.add(
    ids=["id1", "id2", "id3", "id4", "id5"],
    documents=["浙江的省会是杭州", "河北的省会是石家庄", "山东的省会是济南", "杭州是个美丽的城市", "杭州位于浙江省"],
    metadatas=[{"source": "myDoc"}, {"source": "myDoc"}, {"source": "myDoc"}, {"source": "myDoc"}, {"source": "myDoc"}],
)

如果已经有生成好的嵌入,可以通过embeddings参数直接传入,后续的查询方法需要使用query_embeddings参数。

collection.add(
    ids=["id1", "id2", "id3", "id4", "id5"],
    # 注意如果使用了embedings参数,且嵌入维度不是384(当前是3),则第四步中的查询需要使用query_embeddings传参查询
    # 其余情况,可以使用query_texts传参查询
    embeddings=[[1.2, 2.3, 4.5], [3.3, 5.1, 2.7], [6.1, 8.2, 9.2], [1.8, 0.2, 2.1], [5.5, 6.6, 9.9]],
    documents=["浙江的省会是杭州", "河北的省会是石家庄", "山东的省会是济南", "杭州是个美丽的城市", "杭州位于浙江省"],
    metadatas=[{"source": "myDoc"}, {"source": "myDoc"}, {"source": "myDoc"}, {"source": "myDoc"}, {"source": "myDoc"}],
)

步骤4:查询集合中的数据

查询集合中与用户给定的文本或嵌入(特征向量)最相似的n个文档,n由用户指定,这n条文档会按照相似度从高到低排列被返回。这里用到的查询策略不是常规的模式匹配,而是采用相似度(K-近邻算法),相似度愈高,返回数据中的距离(distance)值越小,如果值为0,表明内容一模一样。query方法的参数有:

  1. query_texts:查询文本列表
  2. query_embeddings:查询嵌入列表
  3. n_results:与每个查询文本或嵌入最相近的文档数
results = collection.query(
    query_texts=["西湖在浙江杭州"],
    n_results=5,
)
# 运行结果如下:
{'ids': [['id5', 'id4', 'id1', 'id3', 'id2']], 'distances': [[0.43458253145217896, 0.48921626806259155, 0.5748544931411743, 0.9643734097480774, 1.0526437759399414]], 'metadatas': [[{'source': 'myDoc'}, {'source': 'myDoc'}, {'source': 'myDoc'}, {'source': 'myDoc'}, {'source': 'myDoc'}]], 'embeddings': None, 'documents': [['杭州位于浙江省', '杭州是个美丽的城市', '浙江的省会是杭州', '山东的省会是济南', '河北的省会是石家庄']]}
  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值