搭建知识库-DataWhale笔记

词向量及向量知识库介绍

词向量

词向量定义

在机器学习和自然语言处理(NLP)中,词向量(Embeddings)是一种将非结构化数据,如单词、句子或者整个文档,转化为实数向量的技术。这些实数向量可以被计算机更好地理解和处理。

嵌入背后的主要想法是,相似或相关的对象在嵌入空间中的距离应该很近。

词向量优势

在RAG(Retrieval Augmented Generation,检索增强生成)方面词向量的优势主要有两点:

  • 词向量比文字更适合检索。当我们在数据库检索时,如果数据库存储的是文字,主要通过检索关键词(词法搜索)等方法找到相对匹配的数据,匹配的程度是取决于关键词的数量或者是否完全匹配查询句的;但是词向量中包含了原文本的语义信息,可以通过计算问题与数据库中数据的点积、余弦距离、欧几里得距离等指标,直接获取问题与数据在语义层面上的相似度;

  • 词向量比其它媒介的综合信息能力更强,当传统数据库存储文字、声音、图像、视频等多种媒介时,很难去将上述多种媒介构建起关联与跨模态的查询方法;但是词向量却可以通过多种向量模型将多种数据映射成统一的向量形式。

这里补充一下RAG的介绍

RAG(Retrieval Augmented Generation,检索增强生成)是一种结合了检索(Retrieval)和生成(Generation)的人工智能模型架构,旨在提高文本生成任务的性能和相关性。这种方法通过先检索相关信息,然后基于这些信息生成文本,从而允许模型在生成回答、文章或其他类型的文本内容时,利用大量的外部知识。RAG模型通常用于需要广泛背景知识的任务,如问答系统、内容推荐、文章撰写等。

一般构建词向量的方法

在搭建 RAG 系统时,我们往往可以通过使用嵌入模型来构建词向量,我们可以选择:

  • 使用各个公司的 Embedding API;

  • 在本地使用嵌入模型将数据构建为词向量。

向量数据库

向量数据库定义

向量数据库是用于高效计算和管理大量向量数据的解决方案。向量数据库是一种专门用于存储和检索向量数据(embedding)的数据库系统。它与传统的基于关系模型的数据库不同,它主要关注的是向量数据的特性和相似性。

在向量数据库中,数据被表示为向量形式,每个向量代表一个数据项。这些向量可以是数字、文本、图像或其他类型的数据。向量数据库使用高效的索引和查询算法来加速向量数据的存储和检索过程。

使用Embedding API(OpenAI)

我这里使用的是OpenAI的API,

GPT embedding mode使用的是text-embedding-3-small

def openai_embedding(text: str, model: str=None):
    # 获取环境变量 OPENAI_API_KEY
    api_key=os.environ['OPENAI_API_KEY']
    client = OpenAI(api_key=api_key)
​
    # embedding model:'text-embedding-3-small', 'text-embedding-3-large', 'text-embedding-ada-002'
    if model == None:
        model="text-embedding-3-small"
​
    response = client.embeddings.create(
        input=text,
        model=model
    )
    return response
​
response = openai_embedding(text='要生成 embedding 的输入文本,字符串形式。')

API返回格式为json,除object向量类型外还有存放数据的data、embedding model 型号model以及本次 token 使用情况usage等数据,具体如下所示:

{
  "object": "list",
  "data": [
    {
      "object": "embedding",
      "index": 0,
      "embedding": [
        -0.006929283495992422,
        ... (省略)
        -4.547132266452536e-05,
      ],
    }
  ],
  "model": "text-embedding-3-small",
  "usage": {
    "prompt_tokens": 5,
    "total_tokens": 5
  }
}

我们可以调用response的object来获取embedding的类型。

print(f'返回的embedding类型为:{response.object}')Copy to clipboardErrorCopied
返回的embedding类型为:listCopy to clipboardErrorCopied

embedding存放在data中,我们可以查看embedding的长度及生成的embedding。

print(f'embedding长度为:{len(response.data[0].embedding)}')
print(f'embedding(前10)为:{response.data[0].embedding[:10]}')Copy to clipboardErrorCopied
embedding长度为:1536
embedding(前10)为:[0.03884002938866615, 0.013516489416360855, -0.0024250170681625605, -0.01655769906938076, 0.024130908772349358, -0.017382603138685226, 0.04206013306975365, 0.011498954147100449, -0.028245486319065094, -0.00674333656206727]Copy to clipboardErrorCopied

我们也可以查看此次embedding的模型及token使用情况。

print(f'本次embedding model为:{response.model}')
print(f'本次token使用情况为:{response.usage}')Copy to clipboardErrorCopied
本次embedding model为:text-embedding-3-small
本次token使用情况为:Usage(prompt_tokens=12, total_tokens=12)

数据处理

数据读取

pdf文档

我们可以使用 LangChain 的 PyMuPDFLoader 来读取知识库的 PDF 文件。PyMuPDFLoader 是 PDF 解析器中速度最快的一种,结果会包含 PDF 及其页面的详细元数据,并且每页返回一个文档。

from langchain.document_loaders.pdf import PyMuPDFLoader
​
# 创建一个 PyMuPDFLoader Class 实例,输入为待加载的 pdf 文档路径
loader = PyMuPDFLoader("../../data_base/knowledge_db/pumkin_book/pumpkin_book.pdf")
​
# 调用 PyMuPDFLoader Class 的函数 load 对 pdf 文件进行加载
pdf_pages = loader.load()

文档加载后储存在 pages 变量中:

  • page 的变量类型为 List

  • 打印 pages 的长度可以看到 pdf 一共包含多少页

print(f"载入后的变量类型为:{type(pdf_pages)},",  f"该 PDF 一共包含 {len(pdf_pages)} 页")Copy to clipboardErrorCopied
载入后的变量类型为:<class 'list'>, 该 PDF 一共包含 196 页Copy to clipboardErrorCopied

page 中的每一元素为一个文档,变量类型为 langchain_core.documents.base.Document, 文档变量类型包含两个属性

  • page_content 包含该文档的内容。

  • meta_data 为文档相关的描述性数据。

pdf_page = pdf_pages[1]
print(f"每一个元素的类型:{type(pdf_page)}.", 
    f"该文档的描述性数据:{pdf_page.metadata}", 
    f"查看该文档的内容:\n{pdf_page.page_content}", 
    sep="\n------\n")
每一个元素的类型:<class 'langchain_core.documents.base.Document'>.
------
该文档的描述性数据:{'source': './data_base/knowledge_db/pumkin_book/pumpkin_book.pdf', 'file_path': './data_base/knowledge_db/pumkin_book/pumpkin_book.pdf', 'page': 1, 'total_pages': 196, 'format': 'PDF 1.5', 'title': '', 'author': '', 'subject': '', 'keywords': '', 'creator': 'LaTeX with hyperref', 'producer': 'xdvipdfmx (20200315)', 'creationDate': "D:20230303170709-00'00'", 'modDate': '', 'trapped': ''}
------
查看该文档的内容:
前言
……省略
md文档
from langchain.document_loaders.markdown import UnstructuredMarkdownLoader

loader = UnstructuredMarkdownLoader("../../data_base/knowledge_db/prompt_engineering/1. 简介 Introduction.md")
md_pages = loader.load()

读取出来的对象和pf文档读出来的是一致的,这里不再赘述

数据清理

我们期望知识库的数据尽量是有序的、优质的、精简的,因此我们要删除低质量的、甚至影响理解的文本数据。

不同的文本数据情况不同,所要采取的清理方法也不同,例如上文中读取的pdf文件不仅将一句话按照原文的分行添加了换行符\n,也在原本两个符号中间插入了\n,我们可以使用正则表达式匹配并删除掉\n

import re
pattern = re.compile(r'[^\u4e00-\u9fff](\n)[^\u4e00-\u9fff]', re.DOTALL)
pdf_page.page_content = re.sub(pattern, lambda match: match.group(0).replace('\n', ''), pdf_page.page_content)
print(pdf_page.page_content)

文档分割

由于单个文档的长度往往会超过模型支持的上下文,导致检索得到的知识太长超出模型的处理能力,因此,在构建向量知识库的过程中,我们往往需要对文档进行分割,将单个文档按长度或者按固定的规则分割成若干个 chunk,然后将每个 chunk 转化为词向量,存储到向量数据库中。

在检索时,我们会以 chunk 作为检索的元单位,也就是每一次检索到 k 个 chunk 作为模型可以参考来回答用户问题的知识,这个 k 是我们可以自由设定的。

Langchain 中文本分割器都根据 chunk_size (块大小)和 chunk_overlap (块与块之间的重叠大小)进行分割。

  • chunk_size 指每个块包含的字符或 Token (如单词、句子等)的数量

  • chunk_overlap 指两个块之间共享的字符数量,用于保持上下文的连贯性,避免分割丢失上下文信息

Langchain 提供多种文档分割方式,区别在怎么确定块与块之间的边界、块由哪些字符/token组成、以及如何测量块大小

  • RecursiveCharacterTextSplitter(): 按字符串分割文本,递归地尝试按不同的分隔符进行分割文本。

  • CharacterTextSplitter(): 按字符来分割文本。

  • MarkdownHeaderTextSplitter(): 基于指定的标题来分割markdown 文件。

  • TokenTextSplitter(): 按token来分割文本。

  • SentenceTransformersTokenTextSplitter(): 按token来分割文本

  • Language(): 用于 CPP、Python、Ruby、Markdown 等。

  • NLTKTextSplitter(): 使用 NLTK(自然语言工具包)按句子分割文本。

  • SpacyTextSplitter(): 使用 Spacy按句子的切割文本。

注:如何对文档进行分割,其实是数据处理中最核心的一步,其往往决定了检索系统的下限。但是,如何选择分割方式,往往具有很强的业务相关性——针对不同的业务、不同的源数据,往往需要设定个性化的文档分割方式。

搭建并使用向量数据库

明天补充

相关推荐

  1. 服务器 - 笔记

    2024-04-23 06:24:02       19 阅读
  2. 笔记:Mysql 主从

    2024-04-23 06:24:02       22 阅读
  3. AIGC笔记--VAE模型的

    2024-04-23 06:24:02       42 阅读

最近更新

  1. TCP协议是安全的吗?

    2024-04-23 06:24:02       18 阅读
  2. 阿里云服务器执行yum,一直下载docker-ce-stable失败

    2024-04-23 06:24:02       19 阅读
  3. 【Python教程】压缩PDF文件大小

    2024-04-23 06:24:02       18 阅读
  4. 通过文章id递归查询所有评论(xml)

    2024-04-23 06:24:02       20 阅读

热门阅读

  1. 《前端面试题》- TypeScript - TypeScript的优/缺点

    2024-04-23 06:24:02       14 阅读
  2. Android Studio 快捷键及使用技巧汇总

    2024-04-23 06:24:02       14 阅读
  3. k8s whereabouts 使用

    2024-04-23 06:24:02       13 阅读
  4. Github 2024-04-22 开源项目日报Top10

    2024-04-23 06:24:02       16 阅读
  5. C# 异步编程模式详解

    2024-04-23 06:24:02       16 阅读
  6. 图像哈希:DCT篇

    2024-04-23 06:24:02       19 阅读