介绍
近两天学习了一点深度学习的内容。学的比较浅,想将自己的收获和理解记录下来,本文是基于langchain框架使用embedding实现简单的内容查找。话入正题。
embedding在深度学习中通常用于NLP(自然语言处理)。其作用是将文本处理成一个张量。
思路
通过将一个待查找文本使用分词器划分,然后使用embedding处理成张量。询问者输入问题也被处理一个张量此时进行匹配,程序将相似文本返回。
在embedding将文本处理为一个张量步骤中,产生的张量的好坏对程序的性能起决定性作用。自己电脑肯定是难以训练好一个embedding模型的,因此本次使用embedding模型是m3e-base,该模型在hungface中下载,当然模型的使用也是需要一定算力的,如果电脑无法使用该模型也可以使用市面上一些大模型的api。本文以百度的千帆大模型为例。
实现
程序通过langchain框架结合hungface模型简单实现。主要需要下载的包是langchain包
pip install langchain
如果是调用千帆接口还需要安装qianfan库
pip install qianfan
使用的库
from langchain.text_splitter import CharacterTextSplitter
from langchain.vectorstores import Chroma
from langchain.embeddings.huggingface import HuggingFaceEmbeddings
from langchain.embeddings.baidu_qianfan_endpoint import QianfanEmbeddingsEndpoint
import os
这些库分别是:
- langchain中的分词器
- langchain的chroma作用是作为一个数据库,存储通过embedding得到的张量
- 第三和第四个分别是加载hungface模型的库和百度千帆大模型的embedding调用库,这两个库只需要调用一个,如果想要使用本地下载的模型就使用第一个,想要使用大模型api就会使用第四个
- os库这个是python自带的库,在此处目的主要是为了设置环境变量
加载文本内容
此处是为了加载需要查找的目标文本。这里我随便找了一个网页将该网页内容直接复制到data.txt文件中
f=pen('data.txt','r',encoding='utf-8') #以读模式编码为utf-8打开文件
state_of_the_union = f.read() #读取文件内容
f.close() #关闭文件
使用分词器对文本划分
#划分器对象,第一个参数是通过回车符号划分,第二个参数是划分长度是60,最后参数是重叠个数
text_splitter = CharacterTextSplitter(separator="\n",chunk_size=60, chunk_overlap=2)
#通过划分器划分文件,并将结果返回
texts = text_splitter.split_text(state_of_the_union)
划分器同也是十分重要。其结果也一定程度关系到后面的结果好坏。就像是汉语的标点符号,划分错误可能会曲解原意。
加载模型对象
#下载的模型保存路径
m = r"m3e"
#加载模型对象
embeddings = HuggingFaceEmbeddings(model_name=m)
上面使用的是下载模型如果不想使用该模型可以直接调用千帆的api,当然也可以使用openai等大模型的api。这里只是作为一个示例。
二者各有优劣,使用本地模型好处是免费,缺点是如果模型的规模太大家用电脑可能无法带动,并且运算速度上无法比拟企业的服务器。调用企业大模型的api好处是速度快不需要太多的本地算力,坏处是这是付费的,不过好在费用不算太高。
这里以调用千帆的embedding为例
#将api的appkey添加到环境变量
os.environ['QIANFAN_AK']='yourak'
#将api的serectkey添加到环境变量
os.environ['QIANFAN_SK']='yoursk'
#使用千帆大模型的embedding
embeddings = QianfanEmbeddingsEndpoint()
部署数据
这里使用的是chorma作为数据库
#将划分后的文本和embedding作为参数构造数据库
docesearch = Chroma.from_texts(texts, embeddings)
查询内容
在以下代码中查询数据中返回的是多个数据,这些数据以概率排序,第一个是程序认为最大可能是答案的。这些数据存储在一个个列表中(元素类型不是字符串而是一个documen对象),如果只想要第一个数据可以直接用索引输出第一个数据。
#输入问题
question =input('usr:')
#查询数据
ans = docsearch.similarity_search(question)
#输出数据
print(ans)
效果
usr:第一个手持计算机谁发明的
[Document(page_content='1972年,惠普(Hewlett-Packard)发明了第一个手持计算器。'), Document(page_content='1820年,法国人 查尔斯·泽维尔·托马斯·德·科尔马(Charles Xavier Thomas de Colmar)制作成功第一台成品机械式计算机。'), Document(page_content='1946年2月14日,美国人莫克利(JohnW.Mauchly)和艾克特(J.PresperEckert)在美国宾夕法尼亚大学研制成功世界上第一台通用电子计算机“ENIAC”。'), Document(page_content='1886年,美国芝加哥的多尔·菲尔特(Dorr Eugene Felt)制造了第一台用按键操作 的计算器。')]
需要注意的是该程序中,ai模型作用仅仅是作为embedding,作用是将文本转化为张量,最后的答案是程序通过对比数据库中张量和问题张量相似度得出,相似度越大是答案概率越大。因此如果询问一个和读入的文本数据毫不相关的问题,程序还是会按概率给出一个概率较高的答案,尽管该答案概率可能并不高,例如以下例子:
usr:1+1=?
[Document(page_content='1976年,Cray 1诞生。它是第一台商用超级计算机,集成了20万个晶体管,每秒进行1.5亿次浮点运算。'), Document(page_content='1939年11月,美国的约翰·阿塔那索夫(John V. Atanasoff)和他的学生克利福特·贝瑞(Clifford Berry) 完成了一台16位的加法器,这是第一台真空管计算机。'), Document(page_content='1642年 ,法国数学家 布莱士·帕斯卡(Blaise Pascal) 在计算尺的基础上加以改进,能进行八位计算。'), Document(page_content='1938年,德国柏林的康拉德·楚泽(Konrad Zuse)和 他的助手们完成了一个机械可编程二进制形式的计算机,并将其命名为Z1。')]