M3EChatGLM向量化构建本地知识库

news2024/9/27 7:25:44

M3E&ChatGLM向量化构建本地知识库

  • 整体步骤
  • 向量数据库
    • 向量数据库简介
    • 主流数据库
    • Milvus部署
  • 文本向量化
    • M3E介绍
    • 模型对比
    • M3E使用
    • 向量数据存储
  • 基于本地知识库的问答
    • 问句向量化
    • 向量搜索
    • 请求ChatGLM
    • 问答测试

整体步骤

  • 向量化:首先,你需要将语言模型的数据转化为向量。这通常通过嵌入模型(embedding models)完成,比如word2vec,GloVe,或者BERT等,这些模型可以将文本数据转化为向量形式。
  • 存储:向量化后的数据可以存储在向量数据库中。向量数据库提供了一种高效的方式来存储和索引大量的向量数据。
  • 查询:存储在向量数据库中的向量可以通过向量空间中的搜索和比较操作来查询。例如,你可以通过查找与给定向量最相近的向量来找到与给定文本最相关的文本。

向量数据库

向量数据库简介

向量数据库是一种特殊类型的数据库,它用于存储和处理向量数据。向量数据库的主要特点是能够高效地执行向量空间中的搜索和比较操作,比如最近邻搜索(nearest neighbor search)。向量数据库在许多领域都有应用,包括机器学习、人工智能、计算机视觉和自然语言处理等。

主流数据库

  1. Faiss:Faiss是Facebook AI研究所开发的一种用于高效相似度搜索和聚类的库。它可以处理大量数据,并且支持在GPU上运行。
  2. Annoy (Approximate Nearest Neighbors Oh Yeah):Annoy是Spotify开发的一种用于大规模近似最近邻搜索的C++库。Annoy的优点是它支持动态添加向量,这对于需要不断更新数据的应用来说非常有用。
  3. Milvus:Milvus是一款开源的向量数据库,支持在线向量相似度搜索和向量聚类。它提供了丰富的API接口,可以方便地与其他系统进行集成。
  4. Pinecone:Pinecone是一款托管型向量搜索服务,提供全托管的向量搜索引擎,用于构建和部署大规模向量搜索应用。

这里我们选择Milvus。

Milvus部署

Milvus是基于Docker部署的,你的Docker需要符合以下条件:

  • Docker 版本 > 19.03 部署docker
  • Docker Compose 版本 > 1.25.1 安装Compose

1、下载保存docker-compose.standalone.yml并保存为docker-compose.yml:

wget https://github.com/milvus-io/milvus/releases/download/v2.2.12/milvus-standalone-docker-compose.yml -O docker-compose.yml

2、启动单节点

docker-compose up -d

3、通过命令确定单节点安装完成

[root@slave2 docker]# sudo docker-compose psName                     Command                  State             Ports       
--------------------------------------------------------------------------------------
milvus-etcd         etcd -listen-peer-urls=htt ...   Up (healthy)   2379/tcp, 2380/tcp
milvus-minio        /usr/bin/docker-entrypoint ...   Up (healthy)   9000/tcp          
milvus-standalone   /tini -- milvus run standalone   Exit 132

4、关闭Milvus

docker-compose down

5、启动Milvus

docker-compose up -d

文本向量化

M3E介绍

M3E Models :Moka(北京希瑞亚斯科技)开源的系列文本嵌入模型。
模型地址:
https://huggingface.co/moka-ai/m3e-base

M3E Models 是使用千万级 (2200w+) 的中文句对数据集进行训练的 Embedding 模型,在文本分类和文本检索的任务上都超越了 openai-ada-002 模型(ChatGPT 官方的模型)。

M3E 是 Moka Massive Mixed Embedding 的缩写

  • Moka,此模型由 MokaAI 训练,开源和评测,训练脚本使用 uniem ,评测 BenchMark 使用 MTEB-zh
  • Massive,此模型通过千万级 (2200w+) 的中文句对数据集进行训练
  • Mixed,此模型支持中英双语的同质文本相似度计算,异质文本检索等功能,未来还会支持代码检索
  • Embedding,此模型是文本嵌入模型,可以将自然语言转换成稠密的向量

模型对比

在这里插入图片描述

M3E使用

1、首先需要先安装 sentence-transformers

pip install -U sentence-transformers

2、安装完成后,您可以使用以下代码来使用 M3E Models

from sentence_transformers import SentenceTransformer

model = SentenceTransformer('moka-ai/m3e-base')

#Our sentences we like to encode
sentences = [
    '* Moka 此文本嵌入模型由 MokaAI 训练并开源,训练脚本使用 uniem',
    '* Massive 此文本嵌入模型通过**千万级**的中文句对数据集进行训练',
    '* Mixed 此文本嵌入模型支持中英双语的同质文本相似度计算,异质文本检索等功能,未来还会支持代码检索,ALL in one'
]

#Sentences are encoded by calling model.encode()
embeddings = model.encode(sentences)

#Print the embeddings
for sentence, embedding in zip(sentences, embeddings):
    print("Sentence:", sentence)
    print("Embedding:", embedding)
    print("")

3、这里,我使用flask框架,将M3E以API的对外提供接口服务

import flask
from flask import Flask
import logging
from sentence_transformers import SentenceTransformer

app = Flask(__name__)
# 配置日志级别和输出格式
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')

@app.route('/embeddings',methods=['post'])
def embeddings():
    sentences = flask.request.form['text']
    model = SentenceTransformer('moka-ai_m3e-base')
    embeddings = model.encode(sentences)
    #print(embeddings)
    return embeddings.tolist()

if __name__ == '__main__':
    app.debug = False
    handler = logging.FileHandler('flask.log')
    app.logger.addHandler(handler)
    app.run(port=5000, debug=False, host='0.0.0.0')

4、使用POST请求访问http://xxx.xxx.xxx.xxx:5000/embeddings即可将文本转换为向量集合。

向量数据存储

1、创建Java springboot项目,添加maven依赖:

<dependency>
            <groupId>io.milvus</groupId>
            <artifactId>milvus-sdk-java</artifactId>
            <version>2.2.1</version>
</dependency>

2、在向量数据库中创建库pdf_data

@Test
void prepare() {
    dropCollection(milvusClient);
    createCollection(milvusClient);
    buildIndex(milvusClient);
}
void buildIndex(MilvusServiceClient client){
    final String INDEX_PARAM = "{\"nlist\":1024}";
    client.createIndex(
            CreateIndexParam.newBuilder()
                    .withCollectionName("pdf_data")
                    .withFieldName("content_vector")
                    .withIndexType(IndexType.IVF_FLAT)
                    .withMetricType(MetricType.L2)
                    .withExtraParam(INDEX_PARAM)
                    .withSyncMode(Boolean.FALSE)
                    .build()
    );
}
void dropCollection(MilvusServiceClient client){
    client.dropCollection(
            DropCollectionParam.newBuilder()
                    .withCollectionName("pdf_data")
                    .build()
    );
}
void createCollection(MilvusServiceClient client){
    FieldType fieldType1 = FieldType.newBuilder()
            .withName("id")
            .withDataType(DataType.Int64)
            .withPrimaryKey(true)
            .withAutoID(true)
            .build();
    FieldType fieldType2 = FieldType.newBuilder()
            .withName("content_word_count")
            .withDataType(DataType.Int32)
            .build();
    FieldType fieldType3 = FieldType.newBuilder()
            .withName("content")
            .withDataType(DataType.VarChar)
            .withMaxLength(1024)
            .build();
    FieldType fieldType4 = FieldType.newBuilder()
            .withName("content_vector")
            .withDataType(DataType.FloatVector)
            .withDimension(768)
            //.withDimension(1536)
            .build();
    CreateCollectionParam createCollectionReq = CreateCollectionParam.newBuilder()
            .withCollectionName("pdf_data")
            .withShardsNum(4)
            .addFieldType(fieldType1)
            .addFieldType(fieldType2)
            .addFieldType(fieldType3)
            .addFieldType(fieldType4)
            .build();
    client.createCollection(createCollectionReq);
}

3、根据不同的文档类型,解析得到文档知识字符串。

/**
 * 文件上传,支持PDF、Word、Xmind
 * @param file
 * @throws Exception
 */
@PostMapping("/upload")
public void upload(MultipartFile file) throws Exception {
    List<String> sentenceList = new ArrayList<>();
    String fileSuffix = file.getOriginalFilename().substring(file.getOriginalFilename().lastIndexOf("."));
    if (Constants.PDF.equalsIgnoreCase(fileSuffix)){
        sentenceList = PdfParseUtil.parse(file.getInputStream());
    }
    if (Constants.DOCX.equalsIgnoreCase(fileSuffix)){
        sentenceList = WordParseUtil.getContentDocx(file.getInputStream());
    }
    if (Constants.DOC.equalsIgnoreCase(fileSuffix)){
        sentenceList = WordParseUtil.getContentDoc(file.getInputStream());
    }
    if (Constants.XMIND.equalsIgnoreCase(fileSuffix)){
        sentenceList = XmindUtil.xmindToList(file.getInputStream());
    }
    chatService.save(sentenceList);
}

然后将文本知识转换为向量数据。

public void save(List<String> sentenceList){
    List<Integer> contentWordCount = new ArrayList<>();
    List<List<Float>> contentVector = new ArrayList<>();
    for(String str : sentenceList){
        contentWordCount.add(str.length());
    }
    contentVector = embeddingModel.doEmbedding(sentenceList);

    List<InsertParam.Field> fields = new ArrayList<>();
    fields.add(new InsertParam.Field("content", sentenceList));
    fields.add(new InsertParam.Field("content_word_count", contentWordCount));
    fields.add(new InsertParam.Field("content_vector", contentVector));

    InsertParam insertParam = InsertParam.newBuilder()
            .withCollectionName("pdf_data")
            .withFields(fields)
            .build();
    //插入数据
    milvusClient.insert(insertParam);
}

基于本地知识库的问答

问句向量化

调用python的M3E接口服务,返回问句的向量化数据

/**
 * 通过python服务请求获取Embeddings,请求出错返回null
 * @param msg
 * @return
 */
public List<Float> doEmbedding(String msg){
    List<Float> floats = new ArrayList<>();;
    try {
        //表单数据参数填入
        RequestBody body = new FormBody.Builder().add("text", msg).build();
        Request request = new Request.Builder()
                //这里必须手动设置为json内容类型
                .addHeader("content-type", "multipart/form-data")
                //参数放到链接后面
                .url(embeddingUrl)
                .post(body)
                .build();
        Response response = openAiHttpClient.newCall(request).execute();
        if(response.code() == 200){
            String string = response.body().string().replace("[","").replace("]","");
            Arrays.stream(string.split(",")).forEach(num->floats.add(Float.parseFloat(num)));
        }
    }catch (Exception e){
        e.printStackTrace();
    }
    return floats;
}

向量搜索

传入问句向量数据,在向量数据库中进行搜索,得到存储到向量数据库中与之最为匹配的文本知识。

/**
 * 从向量数据库中搜索
 * @param search_vectors
 * @return
 */
private List<PDFData> search(List<List<Float>> search_vectors){
    milvusClient.loadCollection(
            LoadCollectionParam.newBuilder()
                    .withCollectionName("pdf_data")
                    .build()
    );
    final Integer SEARCH_K = 4;
    final String SEARCH_PARAM = "{\"nprobe\":10}";
    List<String> ids = Arrays.asList("id");
    List<String> contents = Arrays.asList("content");
    List<String> contentWordCounts = Arrays.asList("content_word_count");
    SearchParam searchParam = SearchParam.newBuilder()
            .withCollectionName("pdf_data")
            .withConsistencyLevel(ConsistencyLevelEnum.STRONG)
            .withOutFields(ids)
            .withOutFields(contents)
            .withOutFields(contentWordCounts)
            .withTopK(SEARCH_K)
            .withVectors(search_vectors)
            .withVectorFieldName("content_vector")
            .withParams(SEARCH_PARAM)
            .build();
    R<SearchResults> respSearch = milvusClient.search(searchParam);
    List<PDFData> pdfDataList = new ArrayList<>();
    if(respSearch.getStatus() == R.Status.Success.getCode()){
        //respSearch.getData().getStatus() == R.Status.Success
        SearchResults resp = respSearch.getData();
        //判断是否查到结果
        if(!resp.hasResults()){
            return new ArrayList<>();
        }
        for (int i = 0; i < search_vectors.size(); ++i) {
            SearchResultsWrapper wrapperSearch = new SearchResultsWrapper(resp.getResults());
            List<Long> id = (List<Long>) wrapperSearch.getFieldData("id", 0);
            List<String> content = (List<String>) wrapperSearch.getFieldData("content", 0);
            List<Integer> contentWordCount = (List<Integer>) wrapperSearch.getFieldData("content_word_count", 0);
            PDFData pdfData = new PDFData(id.get(0),content.get(0),contentWordCount.get(0));
            pdfDataList.add(pdfData);
        }
    }
    milvusClient.releaseCollection(
            ReleaseCollectionParam.newBuilder()
                    .withCollectionName("pdf_data")
                    .build());
    return pdfDataList;
}

请求ChatGLM

将得到的杂乱的文本知识,采用OpenAI方式访问ChatGLM,使用ChatGLM的语言组织能力,重新组织语言,返回给我们。
ChatGLM部署及访问参考:ChatGLM本地化部署

JSONObject params = new JSONObject();
params.put("model", "chatglm2-6b");
params.put("max_tokens", maxTokens);
params.put("stream", true);
params.put("temperature", temperature);
params.put("top_p", topP);
params.put("user", user);
JSONObject message = new JSONObject();
message.put("role", "user");
message.put("content", finalPrompt);
params.put("messages", Collections.singleton(message));
log.info("ChatGLM请求参数:"+message.toJSONString());
return webClient.post()
    .uri(chatGlmUrl)
    .header(HttpHeaders.AUTHORIZATION, "Bearer none")
    .bodyValue(params.toJSONString())
    .retrieve()
    .bodyToFlux(String.class)
    .onErrorResume(WebClientResponseException.class, ex -> {
        HttpStatus status = ex.getStatusCode();
        String res = ex.getResponseBodyAsString();
        log.error("ChatGLM error: {} {}", status, res);
        return Mono.error(new RuntimeException(res));
    });

问答测试

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/985242.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【深度学习】You Only Segment Once: Towards Real-Time Panoptic Segmentation,YOSO全景分割

论文&#xff1a;https://arxiv.org/abs/2303.14651 代码&#xff1a;https://github.com/hujiecpp/YOSO 文章目录 Abstract1. Introduction2. Related Work3. Method3.1. Task Formulation3.2. Feature Pyramid Aggregator3.3. Separable Dynamic Decoder 4. Experiments4.1. …

如何查看MySQL的安装位置

MySQL的安装位置 1、查看安装目录 参数 路径 解释 备注 --basedir /usr/bin 相关命令目录 mysqladmin mysqldump等命令 --datadir /var/lib/mysql/ mysql 数据库文件的存放路径 --plugin-dir /usr/lib64/mysql/plugin mysql插件存放路径 --log-error …

ARM DIY(十)LRADC 按键

前言 ARM SOC 有别于单片机 MCU 的一点就是&#xff0c;ARM SOC 的 GPIO 比较少&#xff0c;基本上引脚都有专用的功能&#xff0c;因为它很少去接矩阵键盘、众多继电器、众多 LED。 但有时 ARM SOC 又需要三五个按键&#xff0c;这时候 LRADC 就是一个不错的选择&#xff0c;…

[刷题记录]牛客面试笔刷TOP101

牛客笔试算法必刷TOP101系列,每日更新中~ 1.合并有序链表2023.9.3 合并两个排序的链表_牛客题霸_牛客网 (nowcoder.com) 题意大致为: 将两个链表中的元素按照从小到大的顺序合并成为一个链表. 所给予的条件: 给出的所要合并的链表都是从小到大顺序排列的. 思路: 创建一…

Vue3后台管理系统Element-plus_侧边栏制作_无限递归

在home.view中添加代码 <template><div><div class"common-layout"><el-container><el-header class"common-header flex-float"><div class"flex"><img class"logo" src"../assets/logo…

1. XAML简单的划分区域

1.运行效果 2.XAML程序 <Window x:Class="_1000_分区域.MainWindow"xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"xmlns:d="http://schemas.microsoft…

指针的应用与用法

指针的应用场景 从刚才的需求看&#xff0c;指针似乎并不是刚需啊&#xff0c;为什么一定要用指针呢&#xff0c;那么难理解&#xff0c;这是因为有些应用场景非他不可&#xff1a; 1.访问单片机的寄存器&#xff1b; 2.函数调用时内存共享&#xff1b; 3.常用数据结构链表&…

SpringBoot3快速入门

SpringBoot3快速入门 准备 知识准备 Spring、SpringMVC、MyBatis、Maven、IDEA 工具版本 java17 * SpringBoot:3.1.1 * IDEA:2021.2.1 * Maven:3.6.3 * Tomcat:10.0 * Servlet:5.0 * GraaIVM Community:22.3 Native Build Tools:0.9.23 springBoot简介 SpringBoot帮我们简单…

[Vue3 博物馆管理系统] 使用Vue3、Element-plus的Layout 布局构建组图文章

系列文章目录 第一章 定制上中下&#xff08;顶部菜单、底部区域、中间主区域显示&#xff09;三层结构首页 第二章 使用Vue3、Element-plus菜单组件构建菜单 第三章 使用Vue3、Element-plus走马灯组件构建轮播图 第四章 使用Vue3、Element-plus tabs组件构建选项卡功能 第五章…

android framework之Applicataion启动流程分析(四)

本文主要学习并了解Application的Activity启动流程。 这边先分析一下Launcher是如何启动进程的Acitivity流程。从Launcher启动Acitivity的时候&#xff0c;它是把启动任务丢给instrumentation模块去协助完成&#xff0c;由它进一步调用AMS的startActivity()方法 去启动&#xf…

基于resnet网络架构训练图像分类模型

数据预处理部分&#xff1a; 数据增强&#xff1a;torchvision中transforms模块自带功能&#xff0c;比较实用数据预处理&#xff1a;torchvision中transforms也帮我们实现好了&#xff0c;直接调用即可DataLoader模块直接读取batch数据 网络模块设置&#xff1a; 加载预训练…

ARM DIY(九)陀螺仪调试

前言 今天调试六轴陀螺仪 MPU6050 硬件 硬件很简单&#xff0c;使用 I2C 接口&#xff0c;并且没有使用中断引脚。 焊接上 MPU6050 芯片和上拉电阻、滤波电容。 检测 MPU6050 是挂在 I2C-0 上的&#xff0c;I2C-0 控制器的驱动已 OK&#xff0c;所以直接使用 I2C-0 检测 …

2023 年前端编程 NodeJs 包管理工具 npm 安装和使用详细介绍

npm 基本概述 npm is the world’s largest software registry. Open source developers from every continent use npm to share and borrow packages, and many organizations use npm to manage private development as well. npm 官方网站&#xff1a;https://www.npmjs.…

程序员做哪些副业可以日赚一百?

日赚一百&#xff1f;对程序员来说简直不要太容易&#xff01;下面给程序员们推荐一些日赚100的副业&#xff1a; ①外包接单 程序员简单粗暴赚钱的副业之一。 外包接单的类型包括但不限于&#xff1a;软件开发、硬件开发、小程序功能开发、web开发……大到一个系统的开发、…

外包干了三年,我承认我确实废了……

没错&#xff0c;我也干过外包&#xff0c;一干就是三年&#xff0c;三年后&#xff0c;我废了…… 虽说废的不是很彻底&#xff0c;但那三年我几乎是出差了三年、玩了三年、荒废了三年&#xff0c;那三年&#xff0c;我的技术能力几乎是零成长的。 说起这段三年的外包经历&a…

对象临时中间状态的条件竞争覆盖

Portswigger练兵场之条件竞争 &#x1f984;条件竞争之对象临时中间状态的条件竞争 Lab: Partial construction race conditions&#x1f680;实验前置必要知识点 某些框架尝试通过使用某种形式的请求锁定来防止意外的数据损坏。例如&#xff0c;PHP 的本机会话处理程序模块…

任务管理系统所需功能概述

"任务管理需要有哪些功能&#xff1f;清晰的任务创建与编辑、智能分类和标签系统、提醒与通知功能、进度跟踪与报告、协作与共享功能、集成与兼容性。" 一款优秀的任务管理工具可以帮助我们有效地规划、执行和监控各项任务&#xff0c;提高工作效率。本文将探讨一款理…

深度学习(十一)---zed 调用yolov5 进行识别目标并实时测距

1. 前言 zed 相机测距有2种方式&#xff1a;一种是根据点云数据进行测试&#xff0c;二是根据zed获取深度值进行测距。上篇文章 调用yolov5模型进行实时图像推理及网页端部署 我们讲述了zed调用yolov5进行目标识别&#xff0c;我们在此基础上进一步实现目标测距功能。 2.深度…

Apache httpd漏洞复现

文章目录 未知后缀名解析漏洞多后缀名解析漏洞启动环境漏洞复现 换行解析漏洞启动环境漏洞复现 未知后缀名解析漏洞 该漏洞与Apache、php版本无关&#xff0c;属于用户配置不当造成的解析漏洞。在有多个后缀的情况下&#xff0c;只要一个文件含有.php后缀的文件即将被识别成PHP…

微信多开bat代码

目录标题 创建txt文件右键 点击新建文本文档 复制如下代码进去&#xff0c;将里面的微信地址改成自己的微信地址&#xff08;查看地址方法&#xff1a;右击微信图标->属性->目标&#xff09;复制如下代码 创建txt文件 右键 点击新建文本文档 复制如下代码进去&#xff0…