大模型 LMDeploy 量化部署

1 模型部署

定义:

  • 在软件工程中,部署通常指的是将开发完毕的软件投入使用的过程。
  • 在人工智能领域,模型部署是实现深度学习算法落地应用的关键步骤。简单来说,模型部署就是将训练好的深度学习模型在特定环境中运行的过程。

场景:

  • 服务器端:CPU部署,单GPU/TPU/NPU部署,多卡/集群
    部署.
  • 移动端边缘端:移动机器人,手机……

2 大模型缓存推理技术

在大模型中,Transformer模型扮演着至关重要的角色。它通过自注意力机制(Self-Attention)来捕捉文本中的上下文信息,实现对文本的深入理解和推理。这种机制允许模型在处理每个token时都考虑到整个输入序列的上下文信息,从而提高了模型的表达能力和准确性。
在这里插入图片描述

  • 对于新的请求Query,需要与历史的Key、Value计算注意力分数。
  • 如果每次都重新计算历史的Key、Value,会浪费大量计算资源。
  • 每轮新迭代时将Key、Value进行缓存,共下次迭代使用。

KV Cache的全称是key-value cache,可以简单理解为对大模型推理过程中的key-value缓存的优化。
在这里插入图片描述

LMDeploy 对 KV Cache 的实现:

  • 预先申请策略,减少运行时因申请/释放内存的消耗时间。
  • 通过设置cache_max_entry_count参数来调节 KV Cache 占用内存的大小,为占用剩余显存的比例。

下图0.2表示KV Cache 可以占用的显存大小是在加载玩模型权重后所剩余的显存的20%。
在这里插入图片描述

3 大模型量化技术

量化技术将传统的表示方法中的浮点数转换为整数或其他离散形式,以减轻深度学习模型的存储和计算负担。

为什么要做量化:

  • 提升推理的速度
  • 增加上下文长度
  • 速度更快的kernel
  • 降低I/0延迟
  • 降低推理成本

量化思路,将原来浮点数所在区间做一个线性映射,映射为一些列整数。以 INT8 的8位二进制数为例,通用公式:
Z P = min ⁡ + max ⁡ 2 量化:   q = r o u n d ( f − Z P S ) S = max ⁡ − min ⁡ 255 反量化:   f = q × S + Z P ZP=\frac{\min+\max} {2} \quad\text{量化:} \, q=\mathrm{round}\left(\frac{f-ZP}{S}\right) \\ S=\frac{\max-\min}{255}\quad \text{反量化:} \, f=q\times S+ZP ZP=2min+max量化:q=round(SfZP)S=255maxmin反量化:f=q×S+ZP

在这里插入图片描述

量化方法的分类
(1)按量化对象分:

  • KV Cache量化
  • 模型权重量化
  • 激活值量化 (例如对于一个线性层 y = w x y = wx y=wx w w w为权重, x x x为激活值)

(2)按量化阶段分

  • 量化感知训练(QAT)
  • 量化感知微调(QAF)
  • 训练后量化(PTQ,训练好模型后,在完成量化,常用这种方式)

由于QAT和QAF一般在模型训练后仍然需要做微调操作量化,通常不采用这两种方式。

3.1 LMDeploy 量化方案

LMDeploy 量化方式:KV Cache量化 + 模型权重量化 + 训练后量化

3.1.1 KV Cache量化

  • 在线KVCacheINT4/INT8量化,粒度为 per-head per-token
  • 与FP16相比,INT4/INT8的KVBlock数量分别可以提升4倍和2倍。意义:更长的上下文、更高的并发吞吐
  • 精度上INT8几乎无损,INT4略有损失

在这里插入图片描述

3.1.2 模型权重量化(W4A16量化)

  • 基于 AWQ 算法,对权重进行4bit量化,计算时返量化使用FP16
  • 性能是FP16的2.4倍以上
  • 权重大小、显存降为FP16的的1/4

3.1.3 AWQ 量化原理**:

参考论文:AWQ

核心观点1权重并不等同重要,仅有0.1~1%小部分显著权重对推理结果影响较大

如果有办法将这0.1~1%的小部分显著权重保持FP16,对其他权重进行低比特量化,可以大幅降低内存占用。那么问题来了:如果选出显著权重?
(1)随机挑选-听天由命
(2)基于权重分布挑选-好像应该这样
(3)基于激活值挑选-竟然是这样

AWQ 论文对三者做了实验对比,返实验结果表明基于激活值挑选显著权重效果显著。

在这里插入图片描述

基于激活值,按通道“组团”挑选显著权重权重。为了避免实现上过于复杂,在挑选显著权重时,并非在“元素”级别进行挑选,而是在“通道”级别进行挑选。 首先将激活值对每一列(channel)求绝对值的平均值,把平均值较大一列对应的通道视作显著权重

在这里插入图片描述

但是,随之伴随问题:

  • 显著权重INT4,非显著权重FP16?
  • “显著”的阈值?
  • 如何实现一个通道粒度上混合精度的kernel?
  • 这是硬件不友好的行为!
  • 这更是开发者不友好的行为!

核心观点2量化前对显著权重进行放大可以降低量化误差
在这里插入图片描述

考虑权重矩阵w,线性运算写作y=wx。对权重矩阵进行量化后,可以写作y=Q(w)x。Q(·)定义如下:实验表现:

  • 随着s增大,假设成立的概率越来越低,但在s<2之前,概率还是很低的(<5%)
  • 在一定范围内,随着s的增大,误差比值越来越小,完全支持作者观点

所有权重均低比特量化。显著权重乘以较大s,等效于降低量化误差。非显著权重乘以较小的s,等效于给予更少的关注。

在这里插入图片描述

LMDeploy 采用分组计算每个通道的缩放系数:

在这里插入图片描述

4 大模型外推技术

大模型长度外推性是一个训练和预测的长度不一致的问题。

外推引发的两大问题:

  • 预测阶段用到了没训练过的位置编码
    模型不可避免地在一定程度上对位置编码“过拟合”
  • 预测注意力时注意力机制所处理的token数量远超训练时的数量
    导致计算注意力“熵”的差异较大

4.1 为什么需要位置编码

因为并行化的自注意力机制并不具备区分token相对位置的能力。位置编码用于为输入序列的每个位置添加一种表示其位置信息的编码。通过引入位置编码,Transformer模型可以更好地捕捉序列中不同位置之间的关系,从而更好地处理长距离依赖关系。

  • 如果直接使用一个整数作为位置信息提供给模型,则会造成数值跨度过大,对梯度优化器不友好,模型学习困难。

  • 如果将数值缩放到 [0-1] 区间,则会造成数值跨度过小,模型和优化器都难以分辨位置。

  • 使用向量表示位置:
    “10进制”为例,位置1234可以用4维向量[1 2 3 4]表示。一般地,位置N可以用如下向量表示:
    ∣ N 1 0 3 ∣ m o d 10 ∣ N 1 0 2 ∣ m o d 10 ∣ N 1 0 2 ∣ m o d 10 ∣ N 1 0 0 ∣ m o d 10 \left|\frac{N}{10^{3}}\right|\mathrm{mod}10\quad\left|\frac{N}{10^{2}}\right|\mathrm{mod}10\quad\left|\frac{N}{10^{2}}\right|\mathrm{mod}10\quad\left|\frac{N}{10^{0}}\right|\mathrm{mod}10 103N mod10 102N mod10 102N mod10 100N mod10
    更一般地,果不是10进制,是β进制呢?第i位数是为:
    ⌊ N β i ⌋ m o d   β \left\lfloor\frac{N}{\beta^i}\right\rfloor\mathrm{mod~}\beta βiNmod β
    这里的取余重在表示周期性。

Transformer使用的 Sinusoidal 位置编码
p i , 2 j = sin ⁡ ( i 1000 0 2 j / d ) , p i , 2 j + 1 = cos ⁡ ( i 1000 0 2 j / d ) p_{i,2j}=\sin\left(\frac i{10000^{2j/d}}\right),\quad p_{i,2j+1}=\cos\left(\frac i{10000^{2j/d}}\right) pi,2j=sin(100002j/di),pi,2j+1=cos(100002j/di)

  • "mod”的主要特性是周期性,因此与周期函数cos/sin具有一定的等效性
  • 因此,Sinusoidal 位置编码可以认为是一种特殊的β进制编码

4.2 从位置编码角度解决外推引发的问题

方案:训练阶段就预留好足够的位数

Transformer的原作者就是这么想的,认为预留好位数后模型就能具备对位置编码的泛化性。

在这里插入图片描述

  • 现实情况:模型并没有按照我们的期望进行泛化
  • 训练阶段大多数高位都是“0”,因此这部分位数没有被充分训练,模型无法处理这些新编码。

线性内插法

  • 方案:把“新长度范围”,等比例缩放至训练阶段的长度范围。
  • 如训练时使用1k训练,需外推至4k,就将 [0,4k] 的范围线性缩放至 [0,1k]。
    n / k β d / 2 − 1 \frac{n/k}{\beta^{d/2-1}} βd/21n/k在这里插入图片描述
    但是这会使最低位非常“拥挤”,通常需要进行微调,使模型适应拥挤的映射关系。各维度差异较大!其他位置差异为1,个位差异较小,模型不易分辨,效果不佳。

进制转换法

  • 大模型其实并不知道我们输入的位置编码具体是多少“进制”的,他只对相对大小关系敏感
  • 能否通过“进制转换”来等效“内插”?
    如:10进制下,3位表示范围是0~999;16进制下,3位表示范围是0 ~ FFF(FFF16=4095)
    在这里插入图片描述
    虽然每一位上都有可能出现大于“9”的数,但相对大小差异仍为“1”,模型有能力进行泛化。把内插的压力平均分摊到了每一位上。

4.2.1 NTK-aware 外推技术

预测阶段,计算系数,对位置编码的底数base进行缩放,使得与线性内插法值相等:
n ( β λ ) d / 2 − 1 = n / k β d / 2 − 1 \frac{n}{(\beta\lambda)^{d/2-1}}=\frac{n/k}{\beta^{d/2-1}} (βλ)d/21n=βd/21n/k
n 是实际预测长度,k 是实际长度与训练长度的比值。求得:
λ = k 2 / ( d − 2 ) \lambda=k^{2/(d-2)} λ=k2/(d2)

可得位置编码为:
sin ⁡ ( n ( β λ ) i ) = sin ⁡ ( n ( θ 2 / d k 2 / ( d − 2 ) ) i ) = sin ⁡ ( n ( θ k d / ( d − 2 ) ) 2 i ) \sin\left(\frac{n}{(\beta\lambda)^{i}}\right)=\sin\left(\frac{n}{(\theta^{2/d}k^{2/(d-2)})^{i}}\right)=\sin\left(\frac{n}{(\theta k^{d/(d-2)})^{2i}}\right) sin((βλ)in)=sin((θ2/dk2/(d2))in)=sin((θkd/(d2))2in)

5 Function Calling

什么是Function Calling?为什么要有FunctionCalling?

FunctionCalling,即为让LLM调用外部函数解决问题,从而拓展LLM的能力边界。

  • 指的是在语言模型中集成外部服务或API的调用能力。
  • 模型可以在生成文本的过程中调用外部函数或服务,获取额外的数据或执行特定的任务。

Function Calling的意义?

  • 解决时效性问题
    今天是哪年哪日?今天天气如何?
  • 拓展LLM能力边界
    帮我算一下 e 8 / 12345 e^8/12345 e8/12345 等于多少?帮我搜索一下这篇论文?

5.1 Function Calling 与 RAG

工作原理

  • Function Calling:
    指的是在语言模型中集成外部功能或API的调用能力;
    模型可以在生成文本的过程中调用外部函数或服务,获取额外的数据或执行特定的任务。
  • RAG:
    结合了信息检索和文本生成;
    它首先从一个大型的文档数据库中检索与输入查询相关的文档,然后将这些文档的信息融入到语言模型的生成过程中。

应用场景

  • Function Calling:
    适用于需要模型执行特定操作或与外部系统交互的场景。
    例如,模型可以调用天气API来回答关于当前天气的问题,或者调用翻译服务来提供翻译;
    在动态数据查询、任务自动化、系统集成以及结构化数据获取等方面有广泛应用。
  • RAG:
    适用于那些需要外部信息来提供准确回答的场景;
    如问答系统,它可以通过检索到的信息来丰富和支模型的回答;
    在医疗、法律、研究等领域,使用RAG可以保证提供的答案与权威文献一致。

6 LMDeploy 量化部署实践

6.1 配置LMDeploy环境

6.1.1 环境搭建

创建一个名为lmdeploy的conda环境,python版本为3.10,创建成功后激活环境并安装0.5.3版本的lmdeploy及相关包。

conda create -n lmdeploy  python=3.10 -y
conda activate lmdeploy
conda install pytorch==2.1.2 torchvision==0.16.2 torchaudio==2.1.2 pytorch-cuda=12.1 -c pytorch -c nvidia -y
pip install timm==1.0.8 openai==1.40.3 lmdeploy[all]==0.5.3

pip install datasets==2.19.2

6.1.2 InternStudio 环境获取模型

InternStudio统一把模型放置在/root/models/目录。
运行以下命令,创建文件夹并设置开发机共享目录的软链接。

mkdir /root/models
ln -s /root/share/new_models/Shanghai_AI_Laboratory/internlm2_5-7b-chat /root/models
ln -s /root/share/new_models/Shanghai_AI_Laboratory/internlm2_5-1_8b-chat /root/models
ln -s /root/share/new_models/OpenGVLab/InternVL2-26B /root/models

本实战使用internlm2_5-7b-chat和InternVL2-26B作为演示。由于上述模型量化会消耗大量时间(约8h),量化请使用internlm2_5-1_8b-chat模型完成。

6.1.3 LMDeploy 验证启动模型文件

在量化工作正式开始前,我们还需要验证一下获取的模型文件能否正常工作,以免竹篮打水一场空。

让我们进入创建好的conda环境并启动InternLM2_5-1_8b-chat

conda activate lmdeploy
lmdeploy chat /root/models/internlm2_5-7b-chat

启动成功后,可以提问:
在这里插入图片描述

启动大模型后查看资源监控:

现在显存占用约23GB。

我们要运行参数量为7B的InternLM2.5,由InternLM2.5的码仓查询InternLM2.5-7b-chat的config.json文件可知,该模型的权重被存储为bfloat16格式:

在这里插入图片描述
对于一个7B(70亿)参数的模型,每个参数使用16位浮点数(等于 2个 Byte)表示,则模型的权重大小约为:

7×10^9 parameters×2 Bytes/parameter=14GB

为什么现在显存占用约23GB呢?
这是因为LMDeploy设置了kv cache量化占剩余显存的80%:
在这里插入图片描述
此时对于24GB的显卡,即30%A100,权重占用14GB显存,剩余显存24-14=10GB,因此kv cache占用10GB*0.8=8GB,加上原来的权重14GB,总共占用14+8=22GB。
实际加载模型后,其他项也会占用部分显存,因此剩余显存比理论偏低,实际占用会略高于22GB。

6.2 LMDeploy与InternLM2.5

前面我们直接在本地部署InternLM2.5。而在实际应用中,我们有时会将大模型封装为API接口服务,供客户端访问。

6.2.1 LMDeploy API部署InternLM2.5

启动API服务器
首先让我们进入创建好的conda环境,并通下命令启动API服务器,部署InternLM2.5模型:

conda activate lmdeploy
lmdeploy serve api_server \
    /root/models/internlm2_5-7b-chat \
    --model-format hf \
    --quant-policy 0 \
    --server-name 0.0.0.0 \
    --server-port 23333 \
    --tp 1

命令解释:

  1. lmdeploy serve api_server:这个命令用于启动API服务器。
  2. /root/models/internlm2_5-7b-chat:这是模型的路径。
  3. --model-format hf:这个参数指定了模型的格式。hf代表“Hugging Face”格式。
  4. --quant-policy 0:这个参数指定了量化策略。
  5. --server-name 0.0.0.0:这个参数指定了服务器的名称。在这里,0.0.0.0是一个特殊的IP地址,它表示所有网络接口。
  6. --server-port 23333:这个参数指定了服务器的端口号。在这里,23333是服务器将监听的端口号。
  7. --tp 1:这个参数表示并行数量(GPU数量)。

这一步由于部署在远程服务器上,所以本地需要做一下ssh转发才能直接访问。在你本地打开一个cmd或powershell窗口,输入命令如下:

 ssh -CNg -L 23333:127.0.0.1:23333 root@ssh.intern-ai.org.cn -p 你的ssh端口号

打开浏览器,访问http://127.0.0.1:23333看到如下界面即代表部署成功:
在这里插入图片描述

6.2.2 以命令行形式连接API服务器

关闭http://127.0.0.1:23333网页,但保持终端和本地窗口不动,新建一个终端。

conda activate lmdeploy
lmdeploy serve api_client http://localhost:23333

稍待片刻,等出现double enter to end input >>>的输入提示即启动成功,此时便可以随意与InternLM2.5对话,同样是两下回车确定,输入exit退出。

6.2.3 以Gradio网页形式连接API服务器

保持启动23333端口的服务,在新建终端中输入exit退出。

输入以下命令,使用Gradio作为前端,启动网页:

lmdeploy serve gradio http://localhost:23333 \
    --server-name 0.0.0.0 \
    --server-port 6006

启动后,关闭之前的cmd/powershell窗口,重开一个,再次做一下ssh转发(因为此时端口不同)。在你本地打开一个cmd或powershell窗口,输入命令如下。

ssh -CNg -L 6006:127.0.0.1:6006 root@ssh.intern-ai.org.cn -p <你的ssh端口号>

打开浏览器,访问地址http://127.0.0.1:6006,然后就可以与模型尽情对话了:
在这里插入图片描述

6.3 LMDeploy Lite

随着模型变得越来越大,我们需要一些大模型压缩技术来降低模型部署的成本,并提升模型的推理性能。LMDeploy 提供了权重量化和 k/v cache两种策略。

6.3.1 设置最大kv cache缓存大小

kv cache是一种缓存技术,通过存储键值对的形式来复用计算结果,以达到提高性能和降低内存消耗的目的。在大规模训练和推理中,kv cache可以显著减少重复计算量,从而提升模型的推理速度。理想情况下,kv cache全部存储于显存,以加快访存速度。

模型在运行时,占用的显存可大致分为三部分:模型参数本身占用的显存、kv cache占用的显存,以及中间运算结果占用的显存。LMDeploy的kv cache管理器可以通过设置--cache-max-entry-count参数,控制kv缓存占用剩余显存的最大比例。默认的比例为0.8。

6.3.2 设置在线 kv cache int4/int8 量化

自 v0.4.0 起,LMDeploy 支持在线 kv cache int4/int8 量化,量化方式为 per-head per-token 的非对称量化。此外,通过 LMDeploy 应用 kv 量化非常简单,只需要设定 quant_policycache-max-entry-count参数。目前,LMDeploy 规定 quant_policy=4 表示 kv int4 量化,quant_policy=8 表示 kv int8 量化。

例如,输入以下指令,启动API服务器:

lmdeploy serve api_server \
    /root/models/internlm2_5-7b-chat \
    --model-format hf \
    --quant-policy 4 \
    --cache-max-entry-count 0.4\
    --server-name 0.0.0.0 \
    --server-port 23333 \
    --tp 1

相比使用BF16精度的kv cache,int4的Cache可以在相同4GB的显存下只需要4位来存储一个数值,而BF16需要16位。这意味着int4的Cache可以存储的元素数量是BF16的四倍。

6.3.3 W4A16 模型量化和部署

  • W4:这通常表示权重量化为4位整数(int4)。这意味着模型中的权重参数将从它们原始的浮点表示(例如FP32、BF16或FP16,Internlm2.5精度为BF16)转换为4位的整数表示。这样做可以显著减少模型的大小。
  • A16:这表示激活(或输入/输出)仍然保持在16位浮点数(例如FP16或BF16)。激活是在神经网络中传播的数据,通常在每层运算之后产生。

使用1.8B模型进行量化:

lmdeploy lite auto_awq \
   /root/models/internlm2_5-1_8b-chat \
  --calib-dataset 'ptb' \
  --calib-samples 128 \
  --calib-seqlen 2048 \
  --w-bits 4 \
  --w-group-size 128 \
  --batch-size 1 \
  --search-scale False \
  --work-dir /root/models/internlm2_5-1_8b-chat-w4a16-4bit

命令解释:

  1. lmdeploy lite auto_awq: lite这是LMDeploy的命令,用于启动量化过程,而auto_awq代表自动权重量化(auto-weight-quantization)。
  2. /root/models/internlm2_5-7b-chat: 模型文件的路径。
  3. --calib-dataset 'ptb': 这个参数指定了一个校准数据集,这里使用的是’ptb’(Penn Treebank,一个常用的语言模型数据集)。
  4. --calib-samples 128: 这指定了用于校准的样本数量—128个样本
  5. --calib-seqlen 2048: 这指定了校准过程中使用的序列长度—2048
  6. --w-bits 4: 这表示权重(weights)的位数将被量化为4位。
  7. --work-dir /root/models/internlm2_5-7b-chat-w4a16-4bit: 这是工作目录的路径,用于存储量化后的模型和中间结果。

等终端输出如下时,说明正在推理中,稍待片刻:

在这里插入图片描述

报错解决:

  1. 如果此处出现报错:TypeError: 'NoneType' object is not callable。原因datasets3.0 无法下载calibrate数据集,解决办法:pip install datasets==2.19.2

等待推理完成,便可以直接在你设置的目标文件夹看到对应的模型文件。

那么推理后的模型和原本的模型区别在哪里呢?最明显的两点是模型文件大小以及占据显存大小。

我们可以输入如下指令查看在当前目录中显示所有子目录的大小:

cd /root/models/
du -sh *

输出结果如下(其余文件夹都是以软链接的形式存在的,不占用空间,故显示为0):
在这里插入图片描述

那么原模型大小呢?输入以下指令查看。

cd /root/share/new_models/Shanghai_AI_Laboratory/
du -sh *

在这里插入图片描述

那么显存占用情况对比呢?

输入以下指令启动量化后的模型:在这里插入图片描述

lmdeploy chat /root/models/internlm2_5-7b-chat --cache-max-entry-count 0.4

显存占用情况:

对于修改kv cache默认占用之前,即如1.3 LMDeploy验证启动模型文件所示直接启动模型的显存占用情况(23GB):

  1. 在 BF16 精度下,7B模型权重占用14GB:70×10^9 parameters×2 Bytes/parameter=14GB
  2. kv cache占用8GB:剩余显存24-14=10GB,kv cache默认占用80%,即10*0.8=8GB
  3. 其他项1GB

故23GB=权重占用14GB+kv cache占用8GB+其它项1GB

对于修改kv cache占用之后的显存占用情况(19GB):

  1. bfloat16是16位的浮点数格式,占用2字节(16位)的存储空间。int4是4位的整数格式,占用0.5字节(4位)的存储空间。因此,从bfloat16到int4的转换理论上可以将模型权重的大小减少到原来的1/4,即7B个int4参数仅占用3.5GB的显存。
  2. kv cache占用16.4GB:剩余显存24-3.5=20.5GB,kv cache默认占用80%,即20.5*0.8=16.4GB
  3. 其他项约为1GB

故20.9GB=权重占用3.5GB+kv cache占用16.4GB+其它项1GB

6.3.4 W4A16 量化+ KV cache+KV cache 量化

输入以下指令,让我们同时启用量化后的模型、设定kv cache占用和kv cache int4量化:

lmdeploy serve api_server \
    /root/models/internlm2_5-1_8b-chat-w4a16-4bit \
    --model-format awq \
    --quant-policy 4 \
    --cache-max-entry-count 0.4\
    --server-name 0.0.0.0 \
    --server-port 23333 \
    --tp 1

显存占用11.3GB:

在这里插入图片描述

7 LMDeploy之FastAPI与Function call

7.1 API开发

与之前一样,让我们进入创建好的conda环境并输入指令启动API服务器:

conda activate lmdeploy
lmdeploy serve api_server \
    /root/models/internlm2_5-1_8b-chat-w4a16-4bit \
    --model-format awq \
    --cache-max-entry-count 0.4 \
    --quant-policy 4 \
    --server-name 0.0.0.0 \
    --server-port 23333 \
    --tp 1

保持终端窗口不动,新建一个终端,新建文件:

touch /root/internlm2_5.py

写内容:

# 导入openai模块中的OpenAI类,这个类用于与OpenAI API进行交互
from openai import OpenAI


# 创建一个OpenAI的客户端实例,需要传入API密钥和API的基础URL
client = OpenAI(
    api_key='YOUR_API_KEY',  
    # 替换为你的OpenAI API密钥,由于我们使用的本地API,无需密钥,任意填写即可
    base_url="http://0.0.0.0:23333/v1"  
    # 指定API的基础URL,这里使用了本地地址和端口
)

# 调用client.models.list()方法获取所有可用的模型,并选择第一个模型的ID
# models.list()返回一个模型列表,每个模型都有一个id属性
model_name = client.models.list().data[0].id

# 使用client.chat.completions.create()方法创建一个聊天补全请求
# 这个方法需要传入多个参数来指定请求的细节
response = client.chat.completions.create(
  model=model_name,  
  # 指定要使用的模型ID
  messages=[  
  # 定义消息列表,列表中的每个字典代表一个消息
    {"role": "system", "content": "你是一个友好的小助手,负责解决问题."},  
    # 系统消息,定义助手的行为
    {"role": "user", "content": "帮我讲述一个关于狐狸和西瓜的小故事"},  
    # 用户消息,询问时间管理的建议
  ],
    temperature=0.8,  
    # 控制生成文本的随机性,值越高生成的文本越随机
    top_p=0.8  
    # 控制生成文本的多样性,值越高生成的文本越多样
)

# 打印出API的响应结果
print(response.choices[0].message.content)

执行代码:

conda activate lmdeploy
python /root/internlm2_5.py

终端会输出如下结果:
在这里插入图片描述

此时代表我们成功地使用本地API与大模型进行了一次对话,如果切回第一个终端窗口,会看到如下信息,这代表其成功的完成了一次用户问题GET与输出POST:
####

7.2 Function call

关于Function call,即函数调用功能,它允许开发者在调用模型时,详细说明函数的作用,并使模型能够智能地根据用户的提问来输入参数并执行函数。完成调用后,模型会将函数的输出结果作为回答用户问题的依据。

首先让我们进入创建好的conda环境并启动API服务器。

启动服务:

conda activate lmdeploy
lmdeploy serve api_server \
    /root/models/internlm2_5-7b-chat \
    --model-format hf \
    --quant-policy 0 \
    --server-name 0.0.0.0 \
    --server-port 23333 \
    --tp 1

让我们使用一个简单的例子作为演示。输入如下指令,新建internlm2_5_func.py,写入内容:

from openai import OpenAI


def add(a: int, b: int):
    return a + b


def mul(a: int, b: int):
    return a * b


tools = [{
    'type': 'function',
    'function': {
        'name': 'add',
        'description': 'Compute the sum of two numbers',
        'parameters': {
            'type': 'object',
            'properties': {
                'a': {
                    'type': 'int',
                    'description': 'A number',
                },
                'b': {
                    'type': 'int',
                    'description': 'A number',
                },
            },
            'required': ['a', 'b'],
        },
    }
}, {
    'type': 'function',
    'function': {
        'name': 'mul',
        'description': 'Calculate the product of two numbers',
        'parameters': {
            'type': 'object',
            'properties': {
                'a': {
                    'type': 'int',
                    'description': 'A number',
                },
                'b': {
                    'type': 'int',
                    'description': 'A number',
                },
            },
            'required': ['a', 'b'],
        },
    }
}]
messages = [{'role': 'user', 'content': 'Compute (3+5)*2'}]

client = OpenAI(api_key='YOUR_API_KEY', base_url='http://0.0.0.0:23333/v1')
model_name = client.models.list().data[0].id
response = client.chat.completions.create(
    model=model_name,
    messages=messages,
    temperature=0.8,
    top_p=0.8,
    stream=False,
    tools=tools)
print(response)
func1_name = response.choices[0].message.tool_calls[0].function.name
func1_args = response.choices[0].message.tool_calls[0].function.arguments
func1_out = eval(f'{func1_name}(**{func1_args})')
print(func1_out)

messages.append({
    'role': 'assistant',
    'content': response.choices[0].message.content
})
messages.append({
    'role': 'environment',
    'content': f'3+5={func1_out}',
    'name': 'plugin'
})
response = client.chat.completions.create(
    model=model_name,
    messages=messages,
    temperature=0.8,
    top_p=0.8,
    stream=False,
    tools=tools)
print(response)
func2_name = response.choices[0].message.tool_calls[0].function.name
func2_args = response.choices[0].message.tool_calls[0].function.arguments
func2_out = eval(f'{func2_name}(**{func2_args})')
print(func2_out)

执行代码:

python /root/internlm2_5_func.py

输出结果:

在这里插入图片描述

我们可以看出InternLM2.5将输入'Compute (3+5)*2'根据提供的function拆分成了"加"和"乘"两步,第一步调用function add实现加,再于第二步调用function mul实现乘,再最终输出结果16:

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Github 2024-12-01 开源项目月报 Top20

根据Github Trendings的统计,本月(2024-12-01统计)共有20个项目上榜。根据开发语言中项目的数量,汇总情况如下: 开发语言项目数量Python项目10TypeScript项目9Go项目2HTML项目1Shell项目1Jupyter Notebook项目1屏幕截图转代码应用 创建周期:114 天开发语言:TypeScript, Py…

调试玲珑应用

文章目录 一、在终端中使用 gdb 进行调试二、QtCreator 配置 以下教程以“构建工具”一节中提到的 linglong-builder-demo 项目为例。我们将项目放在 /path/to/project。参考教程操作时注意对路径进行替换。 由于玲珑应用运行在容器中&#xff0c;想要在宿主机上对其进行调试&…

Linux笔记-现场实施记录(找网口、挂载u盘)

2024-10-08 在项目现场&#xff0c;进行实施&#xff0c;在此记录下&#xff0c;方便以后查阅。记录2个点&#xff1b; 找网口 服务器开机后查下ifconfig。 看下网卡配的标识如eth0 再使用如下命令 ethtool -p eth0 30 此时物理网口会闪烁&#xff0c;此时再看下是哪一个…

C# (WinForms) 使用 iTextSharp 库将图片转换为 PDF

iTextSharp简介 iTextSharp 是一个开源的 .NET 库&#xff0c;主要用于创建和操作 PDF 文档。它是 iText 的 .NET 版本&#xff0c;iText 是一个广泛使用的 Java 库。iTextSharp 继承了 iText 的核心功能并进行了适应 .NET 平台的调整。 iTextSharp 的主要功能包括&#xff1a…

2020 年“泰迪杯”数据分析职业技能大赛A 题教育平台的线上课程智能推荐策略

2020 年“泰迪杯”数据分析职业技能大赛A 题教育平台的线上课程智能推荐策略 完整代码请私聊 博主 一、 背景 近年来&#xff0c;随着互联网与通信技术的高速发展&#xff0c;学习资源的建设与共享呈现出新的发展趋势&#xff0c;各种网课、慕课、直播课等层出不穷&#xff0c…

QT 中基于 TCP 的网络通信

基础 基于 TCP 的套接字通信需要用到两个类&#xff1a; 1&#xff09;QTcpServer&#xff1a;服务器类&#xff0c;用于监听客户端连接以及和客户端建立连接。 2&#xff09;QTcpSocket&#xff1a;通信的套接字类&#xff0c;客户端、服务器端都需要使用。 这两个套接字通信类…

企业级日志分析系统ELK之ELK概述

ELK 概述 ELK 介绍 什么是 ELK 早期IT架构中的系统和应用的日志分散在不同的主机和文件&#xff0c;如果应用出现问题&#xff0c;开发和运维人员想排 查原因&#xff0c;就要先找到相应的主机上的日志文件再进行查找和分析&#xff0c;所以非常不方便&#xff0c;而且还涉及…

SpringBoot教程(十四) SpringBoot之集成Redis

SpringBoot教程&#xff08;十四&#xff09; | SpringBoot之集成Redis 一、Redis集成简介二、集成步骤 2.1 添加依赖2.2 添加配置2.3 项目中使用之简单使用 &#xff08;举例讲解&#xff09;2.4 项目中使用之工具类封装 &#xff08;正式用这个&#xff09;2.5 序列化 &…

【Transformer序列预测】Pytorch中构建Transformer对序列进行预测源代码

Python&#xff0c;Pytorch中构建Transformer进行序列预测源程序。包含所有的源代码和数据&#xff0c;程序能够一键运行。此程序是完整的Transformer&#xff0c;即使用了Encoder、Decoder和Embedding所有模块。源程序是用jupyterLab所写&#xff0c;建议分块运行。也整理了.p…

基于LLM智能问答系统【阿里云:天池比赛】

流程&#xff1a; 1、分别识别问题及提供的资料文件中的公司名实体&#xff0c;有公司名的走语义检索&#xff0c;无公司名的走结构化召回 2、结构化召回&#xff1a;Qwen根据问题生成sql&#xff0c;执行sql获取结果数值&#xff0c;把结果数值与问题给到Qwen生成最终结果 …

商品期权开户条件是什么?

商品期权开户条件是什么&#xff1f; 商品期权是一种金融衍生品&#xff0c;它赋予期权持有者在特定日期&#xff08;欧式期权&#xff09;或在特定日期之前&#xff08;美式期权&#xff09;&#xff0c;以特定价格&#xff08;行权价格&#xff09;买入或卖出一定数量的某种…

大文件分块上传后端服务器

一、背景&#xff1a; 后台系统需要上传大文件、大视频等数据&#xff0c;耗时过长&#xff0c;接口等待超时&#xff0c;故需优化通过前端多线程分片方式进行文件上传&#xff0c;显著提升上传速度。 二、流程&#xff1a; 前端逻辑&#xff1a; 前端使用分片技术&#xff…

docker部署seata

1.准备数据库表 Seata支持多种存储模式&#xff0c;但考虑到持久化的需要&#xff0c;我们一般选择基于数据库存储。 先准备seata-tc.sql脚本&#xff0c;在你的数据库中运行&#xff0c;内容复制粘贴即可。 CREATE DATABASE IF NOT EXISTS seata; USE seata;CREATE TABLE I…

java+ssm+mysql美妆论坛

项目介绍&#xff1a; 使用javassmmysql开发的美妆论坛&#xff0c;系统包含超级管理员&#xff0c;系统管理员、用户角色&#xff0c;功能如下&#xff1a; 用户&#xff1a;主要是前台功能使用&#xff0c;包括注册、登录&#xff1b;查看论坛板块和板块下帖子&#xff1b;…

Openlayers基础知识回顾(五)

1、GeoJSON数据的加载 GeoJSON是一种基于JSON的地理空间数据交换格式&#xff0c;它定义了几种类型JSON对象以及它们组合在一起的方法&#xff0c;以表示有关地理要素、属性和它们的空间范围的数据 2、GeoJSON转化为ol要素 new ol.format.GeoJSON().readFeatures() 一、canv…

使用 ASP.NET Core HttpLoggingMiddleware 记录 http 请求/响应

我们发布了一个应用程序&#xff0c;该应用程序运行在一个相当隐蔽的 WAF 后面。他们向我们保证&#xff0c;他们的产品不会以任何方式干扰我们的应用程序。这是错误的。他们删除了我们几乎所有的“自定义”标头。为了“证明”这一点&#xff0c;我构建了一个中间件&#xff0c…

后端工程搭建

后端工程通过maven聚合工程的形式来搭建 1.1创建spzx-parent工程(父工程) 存放公共依赖 锁定公共依赖版本 1.2创建spzx-common工程(公共模块) 存放一些工具类/公共服务 1.3创建spzx-model工程(数据模型) 存放实体类 1.4创建spzx-menager工程(后台管理系统) 后台管理系统服务模…

Flink Python作业快速入门

Flink Python快速入门_实时计算 Flink版(Flink)-阿里云帮助中心 import argparse # 用于处理命令行参数和选项&#xff0c;使程序能够接收用户通过命令行传递的参数 import logging import sysfrom pyflink.common import WatermarkStrategy, Encoder, Types from pyflink.data…

数字图像处理(15):图像平移

&#xff08;1&#xff09;图像平移的基本原理&#xff1a;计算每个像素点的移动向量&#xff0c;并将这些像素按照指定的方向和距离进行移动。 &#xff08;2&#xff09;平移向量包括水平和垂直分量&#xff0c;可以表示为&#xff08;dx&#xff0c;dy&#xff09;&#xff…

JAVA秋招面试题精选-第一天总结

目录 分栏简介&#xff1a; 问题一&#xff1a;订单表每天新增500W条数据&#xff0c;分库分表应该怎么设计&#xff1f; 问题难度以及频率&#xff1a; 问题导向&#xff1a; 满分答案&#xff1a; 举一反三&#xff1a; 问题总结&#xff1a; 问题二&#xff1a;解释…