1 Embedding技术
简单地说,嵌入(Embedding)思想可以视为一种尝试通过用向量来表示所有东西的“本质”的方法,其特性是“相近的事物”由相近的数表示。
1.1 文本向量(Text Embedding)
在GPT中,文本嵌入(Text Embedding)是通过将输入文本中的每个词汇或词语转换为高维向量表示的方法。这些向量捕捉了词汇的语义信息,使得模型能够理解文本的含义并生成相关的输出。通过预训练的方式,GPT模型能够学习得到通用的文本表示,从而在各种自然语言处理任务中表现出色,包括文本生成、文本分类、问答等。
1.2 使用GPT进行Embedding
目前OpenAI官方提供的3个文本向量模型为:
模型 | 最大向量维度 | 可否缩减维度 |
---|---|---|
text-embedding-3-small | 1536 | 是 |
text-embedding-3-large | 3072 | 是 |
text-embedding-ada-002 | 1536 | 否 |
text-embedding-3-small
和text-embedding-3-large
可以通过embeddings.create
中的dimensions
参数调整输出的文本向量的维度。一般而言,文本向量维度越小搜索越快,而维度越大搜索越准。embeddings.create
接口中的参数主要包括以下几个:
input
: string或array。当为array时,其元素可以是字符串、整数或整数组成的array。整数会被视为一个token标记,整数组成的array会被视为一个完整的字符串。一个字符串输出一个embedding向量。model
: OpenAI提供的embedding模型。dimension
: embedding向量的维度;encode-format
: embedding向量的编码方式,float
(默认值)或base64
具体代码举例如下(关于文本向量的相似度,GPT官方建议使用余弦相似度):
from openai import OpenAI
import os
from dotenv import load_dotenv, find_dotenv
_ = load_dotenv(find_dotenv())
client = OpenAI()
import numpy as np
from numpy.linalg import norm
def cos_sim(a, b):
'''余弦距离 -- 值越接近1越相似'''
return np.dot(a, b)/(norm(a)*norm(b))
def get_embeddings(texts, model="text-embedding-ada-002", dimensions=None):
'''封装 OpenAI 的 Embedding 模型接口'''
if model == "text-embedding-ada-002":
dimensions = None
if dimensions:
data = client.embeddings.create(
input=texts, model=model, dimensions=dimensions).data
else:
data = client.embeddings.create(input=texts, model=model).data
return [x.embedding for x in data]
if __name__=="__main__":
vec1=np.reshape(get_embeddings(texts="你好啊")[0],(-1,1536))
vec2=np.reshape(get_embeddings(texts="hello")[0],(1536,-1))
print(cos_sim(vec1,vec2))
代码运行结果如下:
[[0.84943191]]
2 向量数据库
向量数据库是一种特殊类型的数据库,专门用于存储和检索向量数据,是为了有效处理高维数据而设计的。这里要注意一点:向量数据库本身不生成向量,向量是由 Embedding 模型产生的。目前主流的向量数据库有多种:chroma、milvus等。这里以chroma为例简单介绍。
2.1 chroma安装
chroma
的安装非常简单,在Python中直接使用pip install
命令安装即可。具体如下:
pip install chromadb
2.2 chroma基本操作
chroma向量数据库中的集合(collection)保存一组相关文件,可以看作一个容器,用于存储和组织特定类别或主题的数据。与collection相关的操作包括以下内容:
- 创建集合:
create_collection
- 删除集合:
delete_collection
- 获取集合:
get_collection
- 获取并创建集合:
get_or_create_collection
(最常用)
在collection中可以添加add
、删除delect
、修改modify
、插入insert
、更新update
向量。
2.3 chroma添加向量及查找相似向量
def get_embeddings(texts, model="text-embedding-ada-002", dimensions=None):
if model == "text-embedding-ada-002":
dimensions = None
if dimensions:
data = client.embeddings.create(
input=texts, model=model, dimensions=dimensions).data
else:
data = client.embeddings.create(input=texts, model=model).data
return [x.embedding for x in data]
chroma_client=chromadb.Client(Settings(allow_reset=True))
chroma_client.reset()
collection=chroma_client.get_or_create_collection(name="my_collection")
documents=['hello','你吃早饭了吗?',"今天考了多少分?","侬好吗?"]
#向collection中添加向量
collection.add(embeddings=get_embeddings(documents),
documents=documents,
ids=["id1", "id2",'id3','id4'])
#查找与“你好”最相近的向量
results=collection.query(
query_embeddings=get_embeddings(["你好"]),
n_results=2)
print(results['documents'][0])
其输出内容为:
[‘侬好吗?’, ‘hello’]
注意:chroma等其他向量数据库能快速定位相似变量,是因为这类数据库通常执行的是近似查找,而非精确查找。
3 检索增强生成
检索增强生成(Retrieval-Augmented Generation, RAG)是一种结合了信息检索和自然语言生成的技术。其基本思想是利用信息检索的技术,从大规模语料库(存储在向量数据库)中检索出与当前任务相关的文本片段,并将这些文本片段作为输入提供给生成模型,以引导生成模型产生更准确、更相关的文本输出。其基本框架为:
下面给出一个具体的代码案例:
from pdfminer.high_level import extract_pages
from pdfminer.layout import LTTextContainer
import re
import chromadb
from chromadb.config import Settings
from openai import OpenAI
from dotenv import load_dotenv, find_dotenv
_ = load_dotenv(find_dotenv())
client=OpenAI()
#从pdf中提取文档
def extract_text_from_pdf(filename,page_numbers=None,min_line_length=5):
paragaphs=[]
full_text=''
for i,page_layout in enumerate(extract_pages(filename)):
if page_numbers and i not in page_numbers:
continue
for element in page_layout:
if isinstance(element,LTTextContainer):
full_text+=element.get_text()+'\n'
buffer=''
for text in full_text.split('\n'):
if len(text)>1 and bool(re.search(r'\b[A-Za-z]+\b',text)):
buffer+=(' '+text) if not text.endswith('-') else text.strip('-')
elif buffer:
paragaphs.append(buffer.lstrip(' '))
buffer=''
if buffer:
paragaphs.appen(buffer)
return paragaphs
#定义embedding函数
def get_embeddings(texts, model="text-embedding-ada-002", dimensions=None):
if model == "text-embedding-ada-002":
dimensions = None
if dimensions:
data = client.embeddings.create(
input=texts, model=model, dimensions=dimensions).data
else:
data = client.embeddings.create(input=texts, model=model).data
return [x.embedding for x in data]
#修改prompt模版
def bulid_prompt(prompt_template, **kwargs):
prompt=prompt_template
for k,v in kwargs.items():
if isinstance(v,str):
val=v
elif isinstance(v,list) and all(isinstance(x,str) for x in v):
val='\n'.join(v)
else:
val=str(v)
prompt=prompt.replace(f"__{k.upper()}__",val)
return prompt
#调用大模型
def get_completions(prompt,model='gpt-3.5-turbo'):
response=client.chat.completions.create(
model=model,
messages=[{"role":"user","content":prompt}],
temperature=0.5,
)
return response.choices[0].message.content
prompt_template = """
你是一个问答机器人。
你的任务是根据下述给定的已知信息回答用户问题。
确保你的回复完全依据下述已知信息。不要编造答案。
如果下述已知信息不足以回答用户的问题,请直接回复"我无法回答您的问题"。
已知信息:
__INFO__
用户问:
__QUERY__
请用中文回答用户问题。
"""
class RAG_BOT():
def __init__(self):
chroma_client=chromadb.Client(Settings(allow_reset=True))
self.collection=chroma_client.get_or_create_collection(name="my_collection")
def add_documents(self,documents):
self.collection.add(documents=documents,
embeddings=get_embeddings(documents),
ids=["id"+str(i) for i in range(len(documents))])
def query(self,user_query,top_n=2):
results=self.collection.query(query_embeddings=get_embeddings([user_query]),n_results=top_n)
prompt=bulid_prompt(prompt_template,query=user_query,info=results['documents'])
return get_completions(prompt)
documents=extract_text_from_pdf('/Users/sherry/Downloads/llama2.pdf')
rag_bot=RAG_BOT()
rag_bot.add_documents(documents)
print(rag_bot.query("llama2有多少参数?"))
其输出结果为:
根据已知信息,Llama 2 模型有三个变种,参数量分别为 7B、13B 和 70B。
参考资料
- https://zhuanlan.zhihu.com/p/647646322
- https://guangzhengli.com/blog/zh/vector-database/
- https://blog.csdn.net/shebao3333/article/details/130593519