大语言模型RAG-将本地大模型封装为langchain的chat model(三)

大语言模型RAG-将本地大模型封装为langchain的chat model(三)

往期文章:
大语言模型RAG-技术概览 (一)
大语言模型RAG-langchain models (二)

上一期langchain还在0.1时代,这期使用的langchain v0.2已经与之前不兼容了。
本期介绍如何将开源模型封装为langchainchat模型 这是后面构建RAG应用的基础。

基于langchain_core.language_models.BaseChatModel创建类。这个类就是本地llm的langchain封装,需要定义以下方法或属性。

方法/属性作用是否必要
_generate根据prompt生成聊天结果必要
_llm_type (属性)模型的唯一标识,用于生成日志必要
_identifying_params(属性)用于返回模型的一些参数,用于日志和调试可选
_stream用于流式输出可选
_agenerate本机异步的_generate可选
_astream异步的_stream可选

示例如下,只实现比较常用的功能:

from typing import Any, AsyncIterator, Dict, Iterator, List, Optional
from threading import Thread
from langchain_core.callbacks import (AsyncCallbackManagerForLLMRun,CallbackManagerForLLMRun,
)
from langchain_core.language_models import BaseChatModel, SimpleChatModel
from langchain_core.messages import AIMessageChunk, BaseMessage, HumanMessage
from langchain_core.outputs import (
ChatGeneration, 
ChatGenerationChunk, 
ChatResult
)
from langchain_core.messages import (AIMessage,BaseMessage,HumanMessage,
)
from typing import AsyncIterator, Literal, Iterator, Optional, List, Dict, Any
from transformers import AutoTokenizer, AutoModelForCausalLM
from pydantic import BaseModel, Field, validator
import torchclass CustomChatModel(BaseChatModel):"""将本地模型封装为langchain的chat模型"""model_name: str  # 模型的本地地址device: str = "cpu"  # 计算设备n: int = 512  # 最大生成长度temperature: float = 1.0  # 温度model: Optional[AutoModelForCausalLM] = Nonetokenizer: Optional[AutoTokenizer] = Nonedef __init__(self, model_name: str, device: str, temperature: float = 1.0, **kwargs):"""我把模型加载放在初始化里面了。当然有更灵活的处理方式,这部分你可以随心所欲,只要在模型获得输入前加载好模型就OK。Params:model_name (str): 本地模型的路径device (str): 计算设备temperature (float): 模型温度"""super().__init__(model_name=model_name, device=device, **kwargs)self.model_name = model_nameself.device = deviceself.temperature = temperature# 加载本地模型。try:self.tokenizer = AutoTokenizer.from_pretrained(self.model_name, trust_remote_code=True, use_fast=False)self.model = AutoModelForCausalLM.from_pretrained(self.model_name, trust_remote_code=True).half().to(self.device)except Exception as e:raise RuntimeError(f"模型或分词器加载失败: {e}")def _generate(self, messages: List[BaseMessage], stop: Optional[List[str]] = ['.', '。', '?', '?', '!', '!'], run_manager: Optional[CallbackManagerForLLMRun] = None, **kwargs: Any) -> ChatResult:"""实现模型的文本生成逻辑这是langchain必须的类方法。Params:messages (BaseMessage): 输入模型的消息列表stop (List[str]): 这是一个字符列表,langchain会根据这个列表中的字符找到完整句子的结束位置。run_manager (CallbackManagerForLLMRun): 用于管理和追踪执行的过程,通常用于实现回调和异步处理。虽然这个功能很重要,但本文暂时不做示例(我没玩明白)Return:ChatResult类的输出,完全符合langchain风格。"""last_message = messages[-1].content  # 获取最后一条消息inputs = self.tokenizer.encode(last_message, return_tensors="pt").to(self.device)  # 将消息内容编码为张量outputs = self.model.generate(inputs, max_length=self.n + len(inputs[0]), temperature=self.temperature)  # 调用模型生成文本张量tokens = self.tokenizer.decode(outputs[0], skip_special_tokens=True)  # 将张量解码为文本# 截取合适的长度(找到完整句子的结束位置)end_positions = [tokens.find(c) for c in stop if tokens.find(c) != -1]if end_positions:end_pos = max(end_positions) + 1  # 包括结束符if end_pos < self.n:tokens = tokens[:end_pos]else:tokens = tokens[:self.n]else:tokens = tokens[:self.n]message = AIMessage(content=tokens)  # 将生成的文本封装为消息generation = ChatGeneration(message=message)  # 封装为ChatGenerationreturn ChatResult(generations=[generation])def _stream(self, messages: List[BaseMessage], stop: Optional[List[str]] = None, run_manager: Optional[CallbackManagerForLLMRun] = None, **kwargs: Any) -> Iterator[ChatGenerationChunk]:"""流式生成文本。这个方法不是必须的,但实际应用中流式输出能提高用户体验,从这个角度来说它是必须的。下面仅给出了最基本的流式输出的逻辑,实际工程中你可以自行添加你需要的逻辑。Params:messages (BaseMessage): 输入模型的消息列表stop (List[str]): 这是一个字符列表,langchain会根据这个列表中的字符找到完整句子的结束位置。run_manager (CallbackManagerForLLMRun): 用于管理和追踪执行的过程,通常用于实现回调和异步处理。Return:ChatGenerationChunk类的输出,完全符合langchain风格。"""last_message = messages[-1].contentinputs = self.tokenizer(last_message, return_tensors="pt").to(self.device)streamer = TextIteratorStreamer(tokenizer=self.tokenizer)  # transformers提供的标准接口,专为流式输出而生inputs.update({"streamer": streamer, "max_new_tokens": 512})  # 这是model.generate的参数,可以自由发挥thread = Thread(target=self.model.generate, kwargs=inputs)  # TextIteratorStreamer需要配合线程使用thread.start()for new_token in streamer:# 用迭代器的返回形式 。yield ChatGenerationChunk(message=AIMessage(content=new_token))@propertydef _llm_type(self) -> str:"""返回自定义模型的标记"""return "Custom"@propertydef _identifying_params(self) -> Dict[str, Any]:"""返回自定义的debug信息"""return {"model_path": self.model_name, "device": self.device}

本文使用chatglm3-6b模型为例,演示最基础使用方法。

import CustomChatModel
import os
from langchain_core.messages import HumanMessage# 模型实例化
model = LoadChatLLM(model_name="THUDM/chatglm-6b", device=“cuda”, temperature=float(1.0))def stream_generate_text(text):"""流式输出的方式打印到终端。原理就是每生成一个新token,就清空屏幕,然后把原先生成过的所有tokens都打印一次。"""generated_text = ''count = 0message = [HumanMessage(content=text)]for new_text in model._stream(message):generated_text += new_text.textcount += 1if count % 8 == 0:  # 避免刷新太频繁,每8个tokens刷新一次os.system("clear")print(generated_text)os.system("clear")print(generated_text)while True:ipt = input("请输入:")stream_generate_text(ipt)

实际效果:
我问它:如何安装Python?
在这里插入图片描述

它的输出:
在这里插入图片描述

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

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

相关文章

【调试笔记-20240604-Linux-为 OpenWrt LuCI 界面添加多语言支持】

调试笔记-系列文章目录 调试笔记-20240604-Linux-为 OpenWrt LuCI 界面添加多语言支持 文章目录 调试笔记-系列文章目录调试笔记-20240604-Linux-为 OpenWrt LuCI 界面添加多语言支持 前言一、调试环境操作系统&#xff1a;Ubuntu 22.04.4 LTS编译环境调试目标 二、调试步骤预…

mysql使用sorce、load 导入数据

1、本机安装mysql https://blog.csdn.net/weixin_52687711/article/details/130509902 注意&#xff1a;修改安装路径 配置环境变量 2. 使用sorce命令导入数据 https://blog.csdn.net/2301_82257317/article/details/138332048 使用mysql命令框链接目标数据库 以管理员身份运…

新媒体运营主要都做哪些内容的工作?

1.新媒体运营的核心工作是转化。这包括通过活动运营、内容运营和社群运营等方式吸引和激活用户&#xff0c;从而增加产品的活跃度和留存率。具体来说&#xff0c;运营人员会撰写内容并发布到各大自媒体平台&#xff0c;以吸引和拉新用户。同时&#xff0c;他们还会通过各种手段…

游戏资源汇总

游戏平台资源汇总 668 游戏仓库 网站名称&#xff1a;668 游戏仓库网址&#xff1a;668 游戏仓库介绍&#xff1a;全球最大的游戏下载中心、游戏交流中心&#xff0c;提供单机游戏、Switch 游戏、PS4 游戏等资源。 PC 游戏下载 网站名称&#xff1a;PC 游戏下载网址&#x…

图像关键特征描述方法-小目标

图像关键特征描述方法主要包括以下几种: SIFT(尺度不变特征变换): SIFT是一种广泛使用的特征描述方法,它通过尺度空间和梯度方向直方图来描述图像中的关键点。SIFT特征描述具有尺度不变性和旋转不变性,对于光照和视角变化也具有一定的鲁棒性。 SURF(加速稳健特征): SURF…

Echarts柱状图数据太多,自定义长度之后,自适应浏览器缩放

不知道是不是最优解&#xff0c;但是当前解决了我遇到的问题&#xff0c;如有更好的方法&#xff0c;希望看到这篇文章的同学可以不吝指导一番&#xff0c;非常感谢 1、问题描述&#xff1a; 因Ecahrts柱状图数据有时多有时少&#xff0c;所以在数据达到一定程度之后&#xff…

第54期|GPTSecurity周报

GPTSecurity是一个涵盖了前沿学术研究和实践经验分享的社区&#xff0c;集成了生成预训练Transformer&#xff08;GPT&#xff09;、人工智能生成内容&#xff08;AIGC&#xff09;以及大语言模型&#xff08;LLM&#xff09;等安全领域应用的知识。在这里&#xff0c;您可以找…

Spring Boot 实现动态数据源配置

前言 之前在CSDN博客以及某站看了教程&#xff0c;不免觉得有点不知如何下手&#xff0c;好在最后融合了以下&#xff0c;得出了一个比较简单的配置动态数据源的过程。 首先项目是Spring Boot的单体项目&#xff0c;我们的需求是要连接多个数据库&#xff0c;那么就需要配置多个…

产品经理:做好有效的客户需求分析

需求分析是产品开发过程中的重要环节&#xff0c;它直接决定了产品是否能够满足市场需求和用户期望。通过深入了解客户需求&#xff0c;产品经理可以确保产品功能的设计符合用户的实际需求&#xff0c;从而提高产品的用户满意度和市场竞争力。 一、识别用户需求 识别用户需求…

目标检测数据集 - 海洋垃圾检测数据集下载「包含VOC、COCO、YOLO三种格式」

数据集介绍&#xff1a;海洋垃圾检测数据集&#xff0c;真实拍摄海洋海底场景高质量垃圾检测图片数据&#xff0c;涉及场景丰富&#xff0c;比如海底塑料垃圾数据、海底铁制品罐状垃圾数据、海底纸张垃圾数据、海洋生物和海底垃圾同框数据、海底探索仪器和海底垃圾同框数据、海…

【ArcGIS微课1000例】0114:基于DEM地形数据整体抬升或下降高程

相关阅读:【GlobalMapper精品教程】083:基于DEM整体抬升或下降地形高程的两种方式 文章目录 一、任务分析二、栅格计算器简介三、地形整体修改四、注意事项一、任务分析 打开软件,加载配套实验数据中的0112.rar中的dem数据,如下所示,dem的高程范围为256.75~342.37米,现在…

用PlantUML描绘C++世界:通过文本描述精准控制UML图的生成

往期本博主的 C 精讲优质博文可通过这篇导航进行查找&#xff1a; Lemo 的C精华博文导航&#xff1a;进阶、精讲、设计模式文章全收录 前言 在编写程序时&#xff0c;可视化的工具可以极大地帮助我们理解和设计复杂的系统。对于C程序员来说&#xff0c;一个强大的工具是UML&am…

Flutter 中的 RawImage 小部件:全面指南

Flutter 中的 RawImage 小部件&#xff1a;全面指南 Flutter 是一个流行的跨平台 UI 框架&#xff0c;由 Google 开发&#xff0c;它允许开发者使用 Dart 语言构建高性能、美观的应用。在 Flutter 的丰富组件库中&#xff0c;RawImage 是一个低级的图像组件&#xff0c;它允许…

[JS]学习笔记1 -- JAVAScript输入输出+变量

目录 1、组成&#xff1a; 2、书写位置&#xff1a; 2.1 内联JAVAScript&#xff1a; 2.2 内部JAVAScript&#xff1a; 2.3 外部JAVAScript&#xff1a; 3、注释 3.1 单行注释&#xff1a; 3.2 块注释&#xff1a; 4、输入输出语法 4.1 输入&#xff1a; 4.2 输出&a…

怎么用微信小程序实现远程控制空调

怎么用微信小程序实现远程控制空调呢&#xff1f; 本文描述了使用微信小程序调用HTTP接口&#xff0c;实现控制空调&#xff0c;通过不同规格的通断器&#xff0c;来控制不同功率的空调的电源。 可选用产品&#xff1a;可根据实际场景需求&#xff0c;选择对应的规格 序号设备…

最大似然参数估计、贝叶斯参数估计、最小二乘估计与贝叶斯学习的本质区别

最大似然参数估计和贝叶斯参数估计的区别在于最大似然估计认为参数是确定性的量&#xff0c;而贝叶斯估计认为参数是随机变量。之所以命名贝叶斯估计是因为用到了贝叶斯公式。根据贝叶斯公式&#xff0c;从先验概率密度转换到后验概率密度。 统计学中的贝叶斯参数估计是&#x…

python创建项目时关于new conda environment的解释

“New Conda Environment” 指的是创建一个新的 Conda 环境。Conda 是一个开源的跨平台包管理和环境管理系统&#xff0c;它可以帮助用户方便地安装、运行和升级软件包及其依赖关系。特别适用于数据科学、机器学习等领域&#xff0c;因为它对Python及非Python的包管理都有很好的…

three.js能实现啥效果?看过来,这里都是它的菜(16)

不知不觉已经分享16期&#xff0c;在这个过程中得到了很多好朋友的支持&#xff0c;所以我决定坚持分享36期&#xff0c;让大家对three.js有更加直观的认知。

机器学习知识点总结

简介&#xff1a;随着人工智能&#xff08;AI&#xff09;蓬勃发展&#xff0c;也有越来越多的人涌入到这一行业。下面简单介绍一下机器学习的各大领域&#xff0c;机器学习包含深度学习以及强化学习&#xff0c;在本节的机器学习中主要阐述一下机器学习的线性回归逻辑回归&…

数据库讲解---(关系规范化)【二】

目录 前言 一.函数依赖相关 1.1函数依赖集F的逻辑蕴涵 1.2函数依赖集闭包 1.3函数依赖的推理规则 1.3.1独立推理规则 自反律 增广律 传递律 1.3.2其他推理规则 合并规则 分解规则 伪传递规则 二.数据集闭包与F逻辑蕴涵的充要条件 2.1属性集闭包 2.2F逻辑蕴涵的…